# Montana — Network State Snapshot & Recovery Runbook **Версия слепка:** 1.1.0 **Дата:** 2026-05-27 **Назначение:** зафиксировать рабочее состояние сети + точные процедуры восстановления + все известные причины падений. ВНУТРЕННИЙ документ (содержит IP и ссылки на секреты — НЕ публиковать в efir369999/Montana по правилу no-IP-in-public). > Версионирование: при любом изменении топологии/ключей/каскадов — bump версии слепка (1.0.0 → 1.1.0) и обновить соответствующий раздел. --- ## 1. Топология (6 узлов) | alias | IP | хостинг | роль | сервисы | |-------|----|---------|------|---------| | moscow | 176.124.208.93 | Timeweb (RU) | genesis, control-plane, VPN own-key | montana-node :8444, xray :443 (Reality masq montana.quest), nginx :8443/:80, orchestrator :5020, sub-gen :5008 | | frankfurt | 89.19.208.158 | Timeweb (DE) | genesis, **VPN cascade FRONT**, VPN direct | montana-node :8444, xray :443 (Reality universal) + cascade outbounds | | helsinki | 91.132.142.42 | THE.Hosting (FI) | genesis, VPN exit | montana-node :8444, xray :443 (Reality universal) | | yerevan | 149.154.184.205 | WorkTitans (AM) | VPN exit (docker) | docker: montana-node :8444, xray :443, nginx-decoy :80 | | vilnius | 149.154.185.5 | LT | VPN exit (docker) | docker: montana-node :8444, xray :443, nginx-decoy :80 | | nicosia | 45.9.13.170 | VMmanager (CY) | VPN exit (docker) | docker: montana-node :8444, xray :443, nginx-decoy :80 | SSH-алиасы: `montana-moscow`/`my-timeweb`, `montana-frankfurt`, `montana-finland`, `montana-armenia`, `montana-lithuania`, `montana-nicosia`. --- ## 2. Архитектура VPN (на момент слепка) **Достижимые у домашних RU-провайдеров фронты:** Frankfurt (89.19.208.158), Moscow (176.124.208.93) — диапазоны Timeweb не режутся. **Заблокированные напрямую у домашних RU-ISP:** Helsinki, Yerevan, Vilnius (диапазоны THE.Hosting / 149.154.0.0/16). **Решение — каскад:** все заблокированные города ходят через достижимый фронт Frankfurt (raw double-crypto в текущей версии), выход — в правильной стране. ``` клиент → de.montana.quest:443 (Frankfurt, Reality) → routing по cascade-UUID → → exit-страна ``` | Город в подписке | Коннект (host) | UUID | flow | Выход (IP / страна) | |---|---|---|---|---| | 🇩🇪 Франкфурт | de.montana.quest | e6d355e2 (universal) | vision | 89.19.208.158 / DE (direct) | | 🇷🇺 Москва | montana.quest | c83d4d13 (own) | vision | 176.124.208.93 / RU (direct) | | 🇫🇮 Хельсинки | de.montana.quest | 094f9073 (cascade) | none | 91.132.142.42 / FI (via FRA) | | 🇦🇲 Ереван | de.montana.quest | 43ba0c0e (cascade) | none | 149.154.184.205 / AM (via FRA) | | 🇱🇹 Вильнюс | de.montana.quest | fc8a174d (cascade) | none | 149.154.185.5 / LT (via FRA) | | 🇨🇾 Никосия | de.montana.quest | dad79315 (cascade) | none | 45.9.13.170 / CY (via FRA) | --- ## 3. Ключи Reality **Universal Montana key** (Helsinki/Frankfurt/Yerevan/Vilnius exit + клиентская подписка): - UUID `e6d355e2-2d79-4c96-a373-3b0e6b6f4b0d` - PBK `EkTs2aGKnFNgFZ0f7wgft2sJp3VjwFQqIrwkZKM4gD8` - SID `302805bc0c25e504` - SNI `www.googletagmanager.com` - privateKey `cL7D6FCqH5nWcQlHCKH9uNr-RNwCt5peRAqt8tl9mXs` (секрет; на каждом exit-узле в xray config; pre-stage для install: `/etc/montana-vpn/privkey`) **Moscow own key** (masq под свой сайт montana.quest, т.к. :443 делит с сайтом): - UUID `c83d4d13-fce9-4c07-85f0-c152d4bda3ee` - PBK `svxjTnEZxk6aStkaHSYd2b-br3Pe4yqGcNrugokjEgg` - SID `f976f81b29f78c1f` - SNI `montana.quest` - privateKey `iMWS9kMDTBsvRqXMdjXdoRg50DgB3ZRjvJEZ2LxPm3g` (секрет; в xray config Moscow) **Cascade-UUID** (на Frankfurt-фронте, маршрутизируют к exit-outbound; flow пустой): - yerevan `43ba0c0e-c1e3-4e30-8ae8-c2e68d24d7c7` → outbound `armenia-out` - helsinki `094f9073-aff0-4c07-a4af-6ca4c924f6a9` → outbound `helsinki-out` - vilnius `fc8a174d-f42b-4945-8548-ab5c9f448f81` → outbound `vilnius-out` - nicosia `dad79315-0b80-5eca-9703-afee839e0131` → outbound `nicosia-out` Orchestrator admin token: Moscow `/etc/montana/orchestrator-admin-token`. CF API token: Keychain `cloudflare-api-token` / `montana-quest`. --- ## 4. Cloudflare DNS (зона montana.quest, все DNS-only, не proxied) ``` fi.montana.quest → 91.132.142.42 (Helsinki) de.montana.quest → 89.19.208.158 (Frankfurt — cascade front) am.montana.quest → 149.154.184.205 (Yerevan) lt.montana.quest → 149.154.185.5 (Vilnius) cy.montana.quest → 45.9.13.170 (Nicosia) cdn.montana.quest → multi-A: 89.19.208.158, 91.132.142.42, 149.154.185.5, 149.154.184.205, 45.9.13.170 (watchdog auto-prune) montana.quest → 176.124.208.93 (Moscow — сайт + own-key VPN) ``` --- ## 5. Control-plane (Moscow) - **orchestrator** `/opt/montana-orchestrator/server.py`, systemd `montana-orchestrator.service`, :5020. Registry `/var/lib/montana-orchestrator/nodes.json`. API `/register /deregister /nodes /pool` (нужен `secret`=admin token). Watchdog probe Reality :443 каждые 30с, prune dead из cdn multi-A. - **sub-генератор** `/opt/montana-vpn-balance/app.py`, gunicorn :5008, отдаёт `/vpn/sub`. Карты: `ALIAS_HOST`, `CASCADE` (yerevan/helsinki/vilnius → de.montana.quest), `OWN_KEY` (moscow → montana.quest). Ключи из `/etc/montana-vpn/keys.json` (sync с Helsinki, таймер `montana-vpn-key-sync`). - **nginx** :443 → SNI-роутинг montana.quest+hub+efir; ВНИМАНИЕ: на Moscow xray держит :443, nginx на 127.0.0.1:8443 (Reality dest=local nginx). Все enabled на boot. - **сайт-данные**: build-скрипты `montana-net-pull`, `montana-cities-build`, `aggregator.sh`, `collector.sh`, `peer-health.py` (на mos/fra/fin). US удалён отовсюду. --- ## 6. Frankfurt cascade-config ``` inbound: reality-in :443 (universal key) clients: montana-universal(vision), yerevan-cascade(none), helsinki-cascade(none), vilnius-cascade(none), nicosia-cascade(none) outbounds: direct, blocked, dns-out, armenia-out(→149.154.184.205:443), helsinki-out(→91.132.142.42:443), vilnius-out(→149.154.185.5:443), nicosia-out(→45.9.13.170:443) routing: user= → соответствующий -out (Reality client universal к exit) ``` Бэкапы конфига: `/usr/local/etc/xray/config.json.bak-*` (последний рабочий — без dokodemo). --- ## 7. ВСЕ известные причины падений + восстановление ### 7.1 Frankfurt xray restart-race (СЛУЧАЛОСЬ 2026-05-26) **Симптом:** xray `failed`, journal `bind: address already in use`, `Start request repeated too quickly`. Все каскады + клиентский VPN вниз. **Причина:** правка live-конфига (новый inbound/порт) + `Restart=always` → зомби-инстанс держит порт, новый не биндит, systemd крутит рестарты до лимита. **Восстановление:** ``` ssh montana-frankfurt 'systemctl stop xray; pkill -9 xray; for p in 443 2096 2097 2098; do fuser -k $p/tcp; done; sleep 3; \ cp $(ls -t /usr/local/etc/xray/config.json.bak-* | head -1) /usr/local/etc/xray/config.json; \ xray -test -config /usr/local/etc/xray/config.json; systemctl reset-failed xray; systemctl start xray' ``` **Профилактика:** НЕ править live-xray на проде хирургией; правильный путь load-распределения — deploy mt-egress relay, не dokodemo на живой xray. ### 7.2 ufw блокирует :8444 (узел не пирится входящими) **Симптом:** montana-node только исходящие ESTAB, входящих нет; не виден peer-ам. **Причина:** install-vps-full поднимал ufw с 22/80/443, забывал 8444. **Восстановление:** `ssh 'ufw allow 8444/tcp; ufw reload'`. (install-docker.sh уже открывает 8444.) ### 7.3 Reality-ключи рассинхрон (только Frankfurt работал) **Симптом:** клиент TLS-handshake проходит, но прокси не работает на части узлов; работает только один город. **Причина:** privateKey на узле ≠ PBK в подписке. **Восстановление:** сверить `xray x25519 -i ` каждого узла → PBK должен == подписочный. Universal privkey `cL7D6FCq…` на всех exit. Команда сверки: ``` ssh 'docker exec montana-xray xray x25519 -i $(jq -r .inbounds[0].streamSettings.realitySettings.privateKey /etc/montana-vpn/xray-config.json)' ``` ### 7.4 Узел недостижим у конкретного провайдера (IP в РКН-блоке) **Симптом:** город не открывается у одного ISP, работает у других / из дата-центра. **Причина:** IP узла в блок-листе оператора (149.154.0.0/16 = легаси-Telegram; THE.Hosting). НЕ баг сервера. **Восстановление:** завернуть город каскадом через достижимый фронт (Frankfurt/Moscow) — см. §2. Долгосрочно: T1–T3 транспорты (spec B1, не готовы) либо чистый IP. ### 7.5 Moscow :443 конфликт (сайт vs VPN) **Симптом:** упал сайт montana.quest ИЛИ /vpn/sub ИЛИ orchestrator. **Причина:** xray и nginx делят :443; неверный порядок старта / занятый порт. **Восстановление:** nginx должен слушать 127.0.0.1:8443 (не :443); xray :443 dest=127.0.0.1:8443. Бэкапы nginx: `/root/nginx-bak-*`. Проверка: `curl -sk --resolve montana.quest:443:127.0.0.1 https://montana.quest/vpn/sub` → 200. ### 7.6 docker build OOM / провал (Armenia/Vilnius при install) **Симптом:** install-docker.sh падает на cargo build. **Причины:** (а) RAM < 1.5G без swap → OOM; (б) `Montana wordlist.txt` вне build-context (фикс: context = repo root); (в) glibc mismatch (фикс: runtime debian:trixie-slim); (г) volume permission (фикс: chown+runuser); (д) xray x25519 формат вывода (фикс: awk `/Password|ublic/`). **Восстановление:** все фиксы уже в install-docker.sh main; при OOM добавить swap. ### 7.7 cdn.montana.quest содержит мёртвый/блокируемый IP **Симптом:** «Auto» попадает на нерабочий узел. **Причина:** watchdog probe только с Moscow (один vantage) — держит IP, недостижимый у клиента. **Восстановление:** deregister руками: `curl -X POST -d '{"ip":"","secret":""}' https://montana.quest/vpn/node/deregister`. Долгосрочно: reachability-sensing (новый код mt-net, не задеплоен). --- ## 8. Команды проверки здоровья (health-check) ``` # узлы (фаза + пиры) for n in moscow frankfurt finland armenia lithuania; do ssh montana-$n '...montana-node status...'; done # подписка (5 городов) curl -sk https://montana.quest/vpn/sub | base64 -d # каскады выходят правильной страной (с Moscow) ssh montana-moscow 'bash /tmp/test-all-cascades.sh' # helsinki→FI, vilnius→LT, yerevan→AM # Reality :443 фронта извне echo Q | openssl s_client -connect de.montana.quest:443 -servername www.googletagmanager.com -brief # orchestrator curl -sk https://montana.quest/vpn/node/health ``` --- ## 9. Что задеплоено vs что в коде (на момент слепка) - **Прод (живое):** montana-node v1.0.0 (mos/fra/fin/am/lt/cy), статический каскад через Frankfurt, install-docker.sh (авто-join + VPN-ключи). - **В коде, НЕ задеплоено:** mt-net reachability/steering (M10 A-C), mt-egress (M11 A-B + client + e2e) — юнит-протестировано локально (30+ тестов), вшивание в montana-node (M11 C) pending. - **Не построено:** T1-T3 транспорты (spec B1), English-перевод Network спеки (B5), M7 fast-sync, M8 multi-node apply, авто-апдейт флота.