Удалить stacks/astra-marusia: интеграция с Марусей отменена (остаётся в истории git)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
4775d59a4e
commit
89c9563453
@@ -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 закрыт. Не открывать автоматически первого пользователя.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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=
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# Вебхук Маруси (vhost astra.ruzzy.dev): не более 1 запроса/с с адреса
|
||||
limit_req_zone $binary_remote_addr zone=marusia_webhook:1m rate=1r/s;
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user