montana/_internal-private/NETWORK-STATE-RUNBOOK.md
2026-05-27 16:44:03 +03:00

183 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 → <out> → 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=<cascade-email> → соответствующий <city>-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 <node> 'ufw allow 8444/tcp; ufw reload'`. (install-docker.sh уже открывает 8444.)
### 7.3 Reality-ключи рассинхрон (только Frankfurt работал)
**Симптом:** клиент TLS-handshake проходит, но прокси не работает на части узлов; работает только один город.
**Причина:** privateKey на узле ≠ PBK в подписке.
**Восстановление:** сверить `xray x25519 -i <priv>` каждого узла → PBK должен == подписочный. Universal privkey `cL7D6FCq…` на всех exit. Команда сверки:
```
ssh <node> '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. Долгосрочно: T1T3 транспорты (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":"<ip>","secret":"<token>"}' 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, авто-апдейт флота.