Разговор с Астрой через Марусю
Код: 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_req1 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 закрыт. Не открывать автоматически первого пользователя.