From 89c9563453116ceeeb6595d800cbb08df8719a0b Mon Sep 17 00:00:00 2001 From: gilfanovr Date: Sun, 11 Oct 2026 21:04:30 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82=D1=8C=20st?= =?UTF-8?q?acks/astra-marusia:=20=D0=B8=D0=BD=D1=82=D0=B5=D0=B3=D1=80?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=20=D0=9C=D0=B0=D1=80=D1=83?= =?UTF-8?q?=D1=81=D0=B5=D0=B9=20=D0=BE=D1=82=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20(=D0=BE=D1=81=D1=82=D0=B0=D1=91=D1=82=D1=81=D1=8F=20?= =?UTF-8?q?=D0=B2=20=D0=B8=D1=81=D1=82=D0=BE=D1=80=D0=B8=D0=B8=20git)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- servers/rpi.md | 2 +- stacks/astra-marusia/README.md | 71 ------------------- .../astra-marusia-tunnel.service | 19 ----- stacks/astra-marusia/astra-marusia.service | 35 --------- stacks/astra-marusia/env.example | 5 -- .../fr1-nginx-astra.ruzzy.dev.conf | 56 --------------- .../fr1-nginx-conf.d-marusia-webhook.conf | 2 - .../fr1-sshd-70-marusia-tunnel.conf | 16 ----- 8 files changed, 1 insertion(+), 205 deletions(-) delete mode 100644 stacks/astra-marusia/README.md delete mode 100644 stacks/astra-marusia/astra-marusia-tunnel.service delete mode 100644 stacks/astra-marusia/astra-marusia.service delete mode 100644 stacks/astra-marusia/env.example delete mode 100644 stacks/astra-marusia/fr1-nginx-astra.ruzzy.dev.conf delete mode 100644 stacks/astra-marusia/fr1-nginx-conf.d-marusia-webhook.conf delete mode 100644 stacks/astra-marusia/fr1-sshd-70-marusia-tunnel.conf diff --git a/servers/rpi.md b/servers/rpi.md index 58e5d48..6c18c60 100644 --- a/servers/rpi.md +++ b/servers/rpi.md @@ -156,7 +156,7 @@ Docker CE 29.9 + Compose v5.6 из репо download.docker.com (в Debian ст ## Разговорный навык Маруси — отменён и удалён (2026-10-11) - Подготовленную за день интеграцию (разговорный сервис Astra для колонки Маруся через вебхук VK → nginx fr1 → SSH-туннель → rpi) владелец отменил в тот же вечер. Навык в VK так и не регистрировался. - Удалено с rpi: `astra-marusia.service`, `astra-marusia-tunnel.service`, `/opt/astra-marusia`, `/etc/astra-marusia.env` (секрет пути вебхука), `/var/lib/astra-marusia` (отдельный CODEX_HOME), ключ туннеля `~astra/.ssh/marusia_tunnel{,.pub}` и `known_hosts_marusia`. На fr1 — vhost, сертификат, sshd-правило, пользователь `marusia-tunnel` (см. `servers/fr1.md`). -- Код остался в репозиториях: `ruzzy/astra-bot` (`marusia/server.py`, `test_marusia.py`) и `stacks/astra-marusia/` — если вернёмся к идее, начинать оттуда (⚠️ ключ туннеля и пользователь на fr1 — заново). +- Код удалён и из репозиториев (2026-10-11): `marusia/`, `test_marusia.py` в `ruzzy/astra-bot`, `stacks/astra-marusia/` в `ruzzy/infra` — остаётся в истории git. Если вернёмся к идее — восстановить оттуда; ключ туннеля и пользователь на fr1 — заново. - Реестр устройств уточнён 2026-10-11 по сообщению владельца: `Xiaomi-Pad-7`, `ce:14:42:fd:a2:d5`, при обнаружении `192.168.10.45` — «Планшет подруги Полины (Xiaomi Pad 7)». Записано через `router known add`; MAC частный, при его смене потребуется новая отметка. diff --git a/stacks/astra-marusia/README.md b/stacks/astra-marusia/README.md deleted file mode 100644 index e1b25f0..0000000 --- a/stacks/astra-marusia/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Разговор с Астрой через Марусю - -Код: `ruzzy/astra-bot`, `marusia/server.py`, тесты `test_marusia.py`. -Сервис на rpi слушает только `127.0.0.1:8788`; доступ через fr1 подготовлен; SSH-туннель `astra-marusia-tunnel.service` на rpi включён и проверен 2026-10-11. -Webhook: `POST /webhook/<секрет из /etc/astra-marusia.env>`. -Проверка: `GET /health`. - -Отдельный разговор на идентификатор пользователя Маруси, до 16 реплик в памяти; -при перезапуске история очищается, через сутки простоя удаляется. Беседа семейная, -ответы подходят ребёнку 10 лет. Тексты не пишутся в access log. Голос распознаёт -Маруся, Астра отвечает текстом, Маруся озвучивает `response.tts`. - -Codex использует существующую авторизацию ChatGPT: файл auth.json привязан -только для чтения в отдельный CODEX_HOME. Пользовательская конфигурация, правила, -документы проекта и инструменты shell/apps/browser отключены. systemd скрывает -домашние каталоги, запрещает повышение привилегий и запись за пределами своего -StateDirectory. Не подключать этот сервис к административному треду Telegram. -После обновления Codex повторно проверять список отключаемых feature flags. - -Быстрый ответ ожидается до 2 секунд. Если ответ ещё готовится, колонка просит -сказать «готово?»; новая реплика во время ожидания не запускает вторую задачу. -Повтор webhook с теми же session_id/message_id получает кешированный ответ. -Одно обращение к модели одновременно, не более 50 разговоров. Глобальный бюджет модели: 10 новых вопросов/мин и 60/час; webhook: 30 POST/мин, в том числе отклонённые. Счётчики в памяти, при рестарте сбрасываются. - -## Публичный вход на fr1 (с 2026-10-11) - -Цепочка: VK → `https://astra.ruzzy.dev/marusia/webhook/<секрет>` → nginx на fr1 → -`127.0.0.1:18788` (обратный SSH-туннель, поднимает rpi) → rpi `127.0.0.1:8788/webhook/<секрет>`. -Домашние порты наружу не открыты, 18788 на fr1 слушает только loopback. - -- `fr1-nginx-astra.ruzzy.dev.conf` → `/etc/nginx/sites-available/astra.ruzzy.dev.conf`, - ссылка `sites-enabled/zz-astra.ruzzy.dev.conf`. ⚠️ Префикс `zz-` нужен: на :443 у fr1 - нет `default_server`, и первый по алфавиту vhost отвечает на неизвестный SNI - (`ruzzy.dev`, `fr1.ruzzy.dev`) — без префикса им стал бы этот vhost вместо git. -- `fr1-nginx-conf.d-marusia-webhook.conf` → `/etc/nginx/conf.d/marusia-webhook.conf` - (зона `limit_req` 1 r/s, burst 5, отказ — 429). -- `fr1-sshd-70-marusia-tunnel.conf` → `/etc/ssh/sshd_config.d/70-marusia-tunnel.conf`; - плюс `marusia-tunnel` дописан в `AllowUsers` в `00-hardening.conf` — ⚠️ без этого - sshd пользователя молча не пустит («Permission denied» без причины на клиенте). -- Пользователь fr1 `marusia-tunnel`: nologin, пароль заблокирован, групп нет; - `authorized_keys` принадлежит root, ключ с `restrict,port-forwarding,permitlisten="127.0.0.1:18788"`. -- Отпечаток host-ключа fr1 (ED25519): `SHA256:M5J9nH+svyA3+beD7ZllUn1tayMMO2cxw/t3Z/kj6KU`. - -⚠️ Секрет — часть пути, поэтому на fr1 для этого location `access_log off`, а -`error_log` отдельный и только `crit`: на уровне `error` nginx пишет строку запроса -(с секретом) при 502 от упавшего туннеля и при срабатывании `limit_req`. -Не поднимать уровень этого лога «для отладки» — секрет окажется на диске. -По HTTP `/marusia/` отдаёт 404 без лога и без редиректа. - -Ожидаемые ответы до заполнения skill_id: GET → 403 (nginx), POST с неверным -секретом → 404, с верным → 403 (сервис), туннель лежит → 502. - -Ключ Gitea — не ключ сервера; для туннеля отдельный ed25519 на rpi. -Не открывать домашний порт в интернет напрямую. -Регистрация/проверка: https://platform.vk.com/ и -https://skill-tester.marusia.mail.ru/ (актуальность интерфейса проверить при входе). -До заполнения skill_id все POST закрыты (403), знание случайного пути обязательно. -skill_id не является криптографическим доказательством отправителя; секрет URL -и приватный доступ навыка обеспечивают первичное ограничение доступа. - -С 2026-10-11 обязателен `MARUSIA_ALLOWED_USER_IDS`: список точных идентификаторов пользователя Маруси из проверенного запроса отладчика VK. Пустой список закрывает доступ всем; автоматически первого пришедшего пользователя не добавлять. Это не Telegram ID. Schema: version 1.0, SimpleUtterance, ограниченные session_id/message_id, вопрос до 1000 символов, Content-Type application/json, без chunked transfer. TTS экранирует разметку. Только разговор, любые действия подключаются отдельно по решению владельца. systemd: MemoryMax 512M, CPUQuota 100%, TasksMax 64. На fr1 агент уже установил nginx 1 r/s с burst 5; дополнительное ужесточение до 30 r/m и тела 16 KiB рекомендовано, но ещё не установлено на fr1. Локальный сервис уже применяет 30 POST/мин и 16 KiB. - -Проверка 2026-10-11 с rpi через публичный HTTPS: неверный секрет → 404; правильный секрет + посторонние skill/user → 403. SSH-ключ fr1 сверён с указанным отпечатком; туннель active с ExitOnForwardFailure. Тем же клиентским ключом попытки shell, другого reverse-порта (18789) и direct forwarding (`ssh -W`) отклонены сервером. До регистрации навыка и разрешённых пользователей разговор через колонку недоступен. Проверка не даёт административного доступа к fr1 для чтения его firewall/ss/logs; live loopback bind на fr1 проверяет его агент. - -## Повторная проверка безопасности 2026-10-11 - -Код astra-bot `7294ea8`, 59 тестов. Исправления: backend получает только PATH/HOME/CODEX_HOME/LANG, без переменных webhook/Telegram; HTTP-сервер ограничен четырьмя соединениями, очередь 8, таймаут чтения заголовков/тела 5 с; глубокий JSON отклоняется без traceback, сравнение секретного пути устойчиво к Unicode. systemd InaccessiblePaths закрывает бэкапы, router/bot/metrics/server-backup/env и другие приложения; ProtectProc=invisible. Авторизация Codex остаётся доступной только через ранее описанный read-only bind, она нужна модели. - -Живая проверка внутри mount namespace службы от UID astra с NoNewPrivileges: home/infra, backup, router.env, bot.env, marusia.env, media скрыты; sudo denied; настоящая модель ответила «Два плюс два — четыре». Через публичный HTTPS: root/health 404, GET webhook 403, неправильный секрет 404, посторонние skill/user 403, неправильная schema 400. HTTP /marusia/ не редиректит. Порты fr1 8788/18788 недоступны с rpi по публичному IPv4; wildcard reverse forward 0.0.0.0:18789 denied. Ранее проверены запреты shell, другого reverse-порта и direct forwarding. - -Границы проверки: административного SSH на fr1 нет, поэтому живые nginx/sshd/firewall, IPv6 bind и отсутствие секрета в логах проверяет агент fr1. Подпись VK не подтверждена: знание URL позволяет подделывать metadata, включая user_id/skill_id; allowlist не заменяет подпись. Сейчас skill_id и список пользователей пусты, доступ к модели через webhook закрыт. Не открывать автоматически первого пользователя. diff --git a/stacks/astra-marusia/astra-marusia-tunnel.service b/stacks/astra-marusia/astra-marusia-tunnel.service deleted file mode 100644 index 44cae40..0000000 --- a/stacks/astra-marusia/astra-marusia-tunnel.service +++ /dev/null @@ -1,19 +0,0 @@ -[Unit] -Description=Private reverse SSH tunnel for Marusia webhook -After=network-online.target astra-marusia.service -Wants=network-online.target - -[Service] -User=astra -Group=astra -ExecStart=/usr/bin/ssh -NT -i /home/astra/.ssh/marusia_tunnel -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=/home/astra/.ssh/known_hosts_marusia -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ConnectTimeout=10 -R 127.0.0.1:18788:127.0.0.1:8788 marusia-tunnel@fr1.ruzzy.dev -Restart=always -RestartSec=10 -NoNewPrivileges=yes -ProtectSystem=strict -ProtectHome=read-only -PrivateTmp=yes -UMask=0077 - -[Install] -WantedBy=multi-user.target diff --git a/stacks/astra-marusia/astra-marusia.service b/stacks/astra-marusia/astra-marusia.service deleted file mode 100644 index cfd8b99..0000000 --- a/stacks/astra-marusia/astra-marusia.service +++ /dev/null @@ -1,35 +0,0 @@ -[Unit] -Description=Astra conversation skill for Marusia -After=network-online.target -Wants=network-online.target - -[Service] -User=astra -Group=astra -WorkingDirectory=/var/lib/astra-marusia -EnvironmentFile=/etc/astra-marusia.env -Environment=CODEX_HOME=/var/lib/astra-marusia -Environment=HOME=/var/lib/astra-marusia -ExecStart=/usr/bin/python3 /opt/astra-marusia/server.py -Restart=on-failure -RestartSec=5 -StateDirectory=astra-marusia -StateDirectoryMode=0700 -UMask=0077 -NoNewPrivileges=yes -ProtectSystem=strict -ProtectHome=tmpfs -InaccessiblePaths=-/mnt/backup -/mnt/router -/etc/router.env -/etc/astra-bot.env -/etc/metrics-push -/etc/server-backup -/etc/astra-marusia.env -/opt/media -/opt/astra-bot -ProtectProc=invisible -BindReadOnlyPaths=/home/astra/.codex/auth.json:/var/lib/astra-marusia/auth.json -PrivateTmp=yes -ProtectKernelTunables=yes -ProtectKernelModules=yes -ProtectControlGroups=yes -RestrictSUIDSGID=yes -MemoryMax=512M -CPUQuota=100% -TasksMax=64 - -[Install] -WantedBy=multi-user.target diff --git a/stacks/astra-marusia/env.example b/stacks/astra-marusia/env.example deleted file mode 100644 index 5cfa103..0000000 --- a/stacks/astra-marusia/env.example +++ /dev/null @@ -1,5 +0,0 @@ -# Root-owned /etc/astra-marusia.env, mode 600. Never commit actual secret. -MARUSIA_WEBHOOK_SECRET=GENERATE_RANDOM_SECRET_AT_LEAST_32_CHARACTERS -MARUSIA_SKILL_ID= -# Marusia user IDs from trusted debugger requests; these are NOT Telegram IDs. -MARUSIA_ALLOWED_USER_IDS= diff --git a/stacks/astra-marusia/fr1-nginx-astra.ruzzy.dev.conf b/stacks/astra-marusia/fr1-nginx-astra.ruzzy.dev.conf deleted file mode 100644 index 2fc886f..0000000 --- a/stacks/astra-marusia/fr1-nginx-astra.ruzzy.dev.conf +++ /dev/null @@ -1,56 +0,0 @@ -# Публичный вход в разговорный сервис «Астра» для Маруси (stacks/astra-marusia). -# Цепочка: VK → nginx → 127.0.0.1:18788 (обратный SSH-туннель от rpi, пользователь marusia-tunnel) → rpi 127.0.0.1:8788. -# ⚠️ Секрет — часть пути. Поэтому в location нет access_log, а error_log только crit: -# на уровне error nginx пишет строку запроса (с секретом) при 502/limit_req. -server { - server_name astra.ruzzy.dev; - server_tokens off; - - location ^~ /marusia/webhook/ { - access_log off; - error_log /var/log/nginx/marusia-webhook.error.log crit; - - client_max_body_size 64k; - client_body_timeout 5s; - - limit_except POST { - deny all; - } - - limit_req zone=marusia_webhook burst=5 nodelay; - limit_req_status 429; - - proxy_connect_timeout 1s; - proxy_send_timeout 5s; - proxy_read_timeout 5s; - - # /marusia/webhook/<секрет> → /webhook/<секрет> - proxy_pass http://127.0.0.1:18788/webhook/; - } - - location / { - return 404; - } - - listen 443 ssl; - ssl_certificate /etc/letsencrypt/live/astra.ruzzy.dev/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/astra.ruzzy.dev/privkey.pem; - include /etc/letsencrypt/options-ssl-nginx.conf; - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; -} - -server { - listen 80; - server_name astra.ruzzy.dev; - server_tokens off; - - # по HTTP секрет уже утёк в сеть; хотя бы не пишем его в лог и не редиректим - location ^~ /marusia/ { - access_log off; - return 404; - } - - location / { - return 301 https://$host$request_uri; - } -} diff --git a/stacks/astra-marusia/fr1-nginx-conf.d-marusia-webhook.conf b/stacks/astra-marusia/fr1-nginx-conf.d-marusia-webhook.conf deleted file mode 100644 index 8d0a375..0000000 --- a/stacks/astra-marusia/fr1-nginx-conf.d-marusia-webhook.conf +++ /dev/null @@ -1,2 +0,0 @@ -# Вебхук Маруси (vhost astra.ruzzy.dev): не более 1 запроса/с с адреса -limit_req_zone $binary_remote_addr zone=marusia_webhook:1m rate=1r/s; diff --git a/stacks/astra-marusia/fr1-sshd-70-marusia-tunnel.conf b/stacks/astra-marusia/fr1-sshd-70-marusia-tunnel.conf deleted file mode 100644 index e7a2dd2..0000000 --- a/stacks/astra-marusia/fr1-sshd-70-marusia-tunnel.conf +++ /dev/null @@ -1,16 +0,0 @@ -# Только удалённый проброс 127.0.0.1:18788 для туннеля Маруси с rpi (stacks/astra-marusia) -Match User marusia-tunnel - AuthenticationMethods publickey - PasswordAuthentication no - KbdInteractiveAuthentication no - AllowTcpForwarding remote - PermitListen 127.0.0.1:18788 - PermitOpen none - GatewayPorts no - AllowAgentForwarding no - AllowStreamLocalForwarding no - PermitTunnel no - X11Forwarding no - PermitTTY no - PermitUserRC no - MaxSessions 0