7.1 KiB
7.1 KiB
Спецификация готовности к внешнему аудиту
Версия: 2026-05-18
Что мы гарантируем (must hold)
A. Архитектурные инварианты
| # | Инвариант | Как проверить |
|---|---|---|
| A1 | На каждом VPN-узле один inbound xray vless+reality :443, без посредников (нет haproxy, нет xray-pinned cascade) |
ss -tlnp | grep :443 → один процесс xray; systemctl is-active haproxy → inactive |
| A2 | Все VPN-узлы используют один и тот же Reality keypair (privateKey, publicKey, shortId) и один UUID | TLS-probe с тем же pbk к каждому IP проходит handshake → Verification: OK |
| A3 | cdn.montana.quest resolves в multi-A pool из живых VPN-узлов, TTL ≤ 120s, не proxied через CF |
dig @1.1.1.1 cdn.montana.quest A показывает несколько IP; TTL ≤ 120 |
| A4 | /vpn/sub отдаёт ровно одну vless-ссылку с cdn.montana.quest:443 |
curl /vpn/sub | base64 -d — одна строка vless://… |
| A5 | Клиент с одним профилем подключается к любому из IP pool без переустановки профиля | iPhone Happ → connect; toggle airplane → reconnect; в xray access.log видно соединение |
B. Самовосстановление
| # | Инвариант | Как проверить |
|---|---|---|
| B1 | При отказе узла его IP выпадает из multi-A не позже чем за 2 минуты | E2E test: systemctl stop xray на узле → /vpn/node/pool через 60-90s не содержит IP |
| B2 | При восстановлении узла IP возвращается в multi-A не позже чем за 2 минуты | Обратный E2E |
| B3 | Watchdog запускается автоматически при старте orchestrator; падение Flask → systemd restart | systemctl is-enabled montana-orchestrator = enabled; Restart=on-failure в unit |
| B4 | xray на узле имеет Restart=always с StartLimitBurst=10/300s (защита от крэш-цикла) |
cat /etc/systemd/system/xray.service.d/30-autorestart.conf |
C. Секреты
| # | Инвариант | Как проверить |
|---|---|---|
| C1 | Reality privateKey отсутствует в публичных артефактах: git, hub.montana.quest, sub-endpoint, /vpn/, /net/ карта |
grep -r 'cL7D6FCqH5' Montana/ | grep -v External-Audit | grep -v memory ⇒ 0 совпадений (allowed только в Memory/internal) |
| C2 | На узле privateKey лежит в /usr/local/etc/xray/config.json с правами 600 root:root |
ls -la /usr/local/etc/xray/config.json |
| C3 | Cloudflare API token хранится только в macOS Keychain (cloudflare-api-token) и в /etc/montana/cf-api-token (0600) на orchestrator-узле |
ls -la /etc/montana/cf-api-token |
| C4 | Admin secret для /register /deregister — random 32-byte hex, в Keychain montana-orchestrator-admin и /etc/montana/orchestrator-admin-token (0600) |
то же |
| C5 | join.sh не содержит секретов; VPN_PRIVKEY передаётся через env или защищённый файл |
`grep -E 'privateKey |
D. Минимальные привилегии и изоляция
| # | Инвариант | Как проверить |
|---|---|---|
| D1 | xray работает под dedicated user xray:xray, не nobody/root |
ps -ef | grep [x]ray run — owner = xray |
| D2 | На узле ufw default deny incoming; открыты только 22, 80, 443, 8444 (p2p) | ufw status verbose |
| D3 | fail2ban активен с jail для sshd (maxretry=3, bantime≥1h) | systemctl is-active fail2ban && fail2ban-client status sshd |
| D4 | orchestrator слушает только 127.0.0.1:5020, наружу — через nginx | ss -tlnp | grep :5020 — bound to 127.0.0.1 |
E. Производственное качество кода
| # | Инвариант | Как проверить |
|---|---|---|
| E1 | xray версия pinned (XRAY_PIN) в join.sh |
grep XRAY_PIN Montana/Node/join.sh |
| E2 | Все systemd-units включены (enabled) |
systemctl is-enabled xray montana-orchestrator fail2ban |
| E3 | Reality serverNames указывает на реальный публичный сайт с валидной CA-цепочкой |
openssl s_client -connect <IP>:443 -servername <SNI> → Verification: OK |
Что мы НЕ гарантируем (out of scope)
| # | Не гарантия | Причина |
|---|---|---|
| N1 | Защита от компрометации единого privateKey | Reality по дизайну требует shared keypair для нескольких backends. Mitigation в OPERATIONS.md#rotation |
| N2 | Защита от блокировки SNI www.googletagmanager.com |
Один SNI = single point. В roadmap — SNI pool (см. OPEN-RISKS.md R-1) |
| N3 | Защита от DDoS на узлы | Опирается на анти-DDoS хостинга. Mitigation: BGP-уровень провайдера |
| N4 | Защита от компрометации Cloudflare API token | Token — единственная точка управления DNS. Mitigation: scoped permissions, rotation |
| N5 | Анонимность пользователя относительно exit-IP | Узлы видят destination, как любой VPN |
| N6 | Защита от атак на montana-node p2p :8444 (Montana TimeChain) | Отдельный аудит TimeChain protocol (Montana-Protocol/Code/AUDIT.md) |
| N7 | Защита от компрометации хостинга (image dump) | Дано на уровне договора с провайдером, не на уровне кода |
Критерии «mainnet-ready» (must pass)
- A1–A5, B1–B4, C1–C5, D1–D4, E1–E3 — все проверки зелёные (см.
scripts/audit-probe.sh). - E2E auto-prune при отказе узла занимает ≤ 120 секунд (B1).
- E2E auto-restore при возврате узла занимает ≤ 120 секунд (B2).
- 0 секретов утекло в публичные артефакты (C1).
- Все P0/P1 риски из OPEN-RISKS.md закрыты или явно приняты с обоснованием.
История проверки
| Дата | Что проверено | Результат |
|---|---|---|
| 2026-05-18 | E2E auto-prune US (down) | ✅ 2 секунды |
| 2026-05-18 | E2E auto-restore US (up) | ✅ 29 секунд |
| 2026-05-18 | Reality probe на всех 3 IP | ✅ Verification: OK |
| 2026-05-18 | C1 grep на privateKey в публичных артефактах | ✅ 0 (см. scripts/check-leaked-secrets.sh) |