19 KiB
Montana — Network State Snapshot v2.0.0
Дата: 2026-05-27
Предыдущий: v1.1.0 (../NETWORK-STATE-RUNBOOK.md)
Статус: ВНУТРЕННИЙ — содержит IP, ключи, токены. НЕ публиковать (правило no-IP-in-public, reference_metzdowd).
Назначение: полное рабочее состояние сети + точные процедуры восстановления + все известные причины падений на момент крупной переделки VPN-слоя (Москва-мастер, stream-demux, anti-block DNS, failover-watchdog, фикс маршрутизации городов).
Версионирование слепка — ТОЛЬКО по явной команде автора (feedback_network_snapshot_on_command).
0. Что изменилось v1.1.0 → v2.0.0 (крупно)
- Москва стала мастер-точкой входа (
de.montana.quest → 176.124.208.93), домашний РФ-вход. Раньше мастером был Франкфурт. - Moscow :443 = nginx stream ssl_preread демультиплекс (SNI googletagmanager → xray :8444 Reality; montana.quest/hub → nginx-web :8443). Совмещает VPN-вход и сайт/подписку на одном порту.
- Автоматический failover точки входа (3-зеркало): цепочка
[Москва#1 → Франкфурт#2 → Хельсинки#3],montana-entry-watchdogна всех трёх, health = Reality-рукопожатие (не TCP), идемпотентная записьde.montana.quest. - Anti-block DNS: в публичном DNS виден только
de→Москва + веб;cdn(светил все IP) и пер-узловые имена удалены;messза Cloudflare; оркестратор больше не публикует IP узлов. - Фикс маршрутизации городов: каждый город привязан к своему cascade-выходу (front-independent); Москва убрана как город-выход (только вход).
- Test-mode в установщике (фикс-мнемоника / кастомный генезис / d-override) + тестовая сеть
montana-testnet-01.
1. Топология (6 узлов)
| alias | IP | хостинг | роль (VPN) | роль (протокол montana-node) |
|---|---|---|---|---|
| moscow | 176.124.208.93 | Timeweb (RU) | МАСТЕР-ВХОД (фронт #1), сайт+подписка+хаб | Active (singleton genesis) |
| frankfurt | 89.19.208.158 | Timeweb (DE) | фронт #2 (резерв) + exit DE | CandidateVdf |
| helsinki | 91.132.142.42 | THE.Hosting (FI) | фронт #3 (резерв) + exit FI | CandidateVdf |
| vilnius | 149.154.185.5 | LT | exit LT (docker) | CandidateVdf (test-режим в процессе) |
| yerevan | 149.154.184.205 | WorkTitans (AM) | exit AM (docker) | CandidateVdf (test-режим в процессе) |
| nicosia | 45.9.13.170 | VMmanager (CY) | exit CY (docker) | CandidateVdf |
SSH: montana-moscow/my-timeweb (через джамп -J montana-frankfurt если прямой ssh недоступен), montana-frankfurt, montana-finland, montana-vilnius/montana-lithuania, Армения -i ~/.ssh/montana-frankfurt root@149.154.184.205, montana-nicosia.
Удалённые ранее: Amsterdam, Almaty, SPb, Novosibirsk, US/NYC (newyork запись удалена из DNS 2026-05-27).
2. Архитектура входа и failover
Принцип: домашний РФ-вход (Москва) → заграничный выход в выбранной стране. Клиент всегда коннектится на фиксированный de.montana.quest; сеть привязывает его к живому фронту и переключает при падении — пользователь ничего не делает.
клиент → de.montana.quest:443 (= текущий мастер, сейчас Москва)
→ nginx stream demux: SNI googletagmanager → xray :8444
→ Reality, routing по cascade-UUID → <city>-out → exit-страна
Цепочка failover (приоритет): moscow:176.124.208.93 → frankfurt:89.19.208.158 → helsinki:91.132.142.42.
montana-entry-watchdog(systemd) на всех трёх фронтах:/opt/montana-entry-watchdog.sh.- Health = Reality-рукопожатие:
openssl s_client -connect <ip>:443 -servername www.googletagmanager.com→ cert содержитgoogle(Reality steal googletagmanager). НЕ голый TCP (nginx/левый сервис прошёл бы TCP-проверку — это роняло сеть). - Логика: первый живой в цепочке владеет
de.montana.quest; пишут идемпотентно (только при отличии cur≠target) → без гонок/флапа. ГистерезисFAIL_THRESHOLD=3×CHECK_INTERVAL=15с. TTL записи 60с. - CF-запись
de.montana.questidbe42d2634fe62b7c1fd1f54058f24e27. de.montana.questавто-управляемая — вручную НЕ ставить (watchdog перепишет).- Старый
/opt/montana-failover.shна Франкфурте — disabled, устарел (был дефектный TCP-health + master=Москва).
3. Moscow :443 — stream-demux (вход + сайт на одном порту)
nginx stream (:443, ssl_preread) /etc/nginx/stream.d/montana-demux.conf
map $ssl_preread_server_name:
www.googletagmanager.com → 127.0.0.1:8444 (xray Reality, VPN)
default (montana.quest/hub/www) → 127.0.0.1:8443 (nginx-web: сайт, /vpn/sub, hub)
xray: listen 127.0.0.1:8444, Reality serverNames=[googletagmanager], dest=googletagmanager:443
(сильная маскировка), универсальный ключ, 8 cascade-клиентов + outbounds
nginx-web: 127.0.0.1:8443 (montana.quest cert + hub.montana.quest cert; /vpn/sub → :5008)
xray Restart=always — при падении возможен зомби на :8444 (bind: address already in use). Лечить: systemctl stop xray; pkill -9 xray; fuser -k 8444/tcp; sleep 2; systemctl reset-failed xray; systemctl start xray.
4. Маршрутизация городов (cascade, front-independent)
Каждый город → de.montana.quest (= текущий фронт) + свой cascade-UUID → фронт роутит на <city>-out → выход в стране. Москва — только вход, НЕ выход (нет города «Москва» в подписке).
| Город в подписке | host | cascade-UUID | outbound на фронте | Выход |
|---|---|---|---|---|
| 🇫🇮 Хельсинки Монтана | de.montana.quest | 094f9073-aff0-4c07-a4af-6ca4c924f6a9 |
helsinki-out | 91.132.142.42 / FI |
| 🇩🇪 Франкфурт Монтана | de.montana.quest | 75e281f1-b702-5eb9-ba5c-8a5d38fa3c31 |
frankfurt-out→direct | 89.19.208.158 / DE |
| 🇦🇲 Ереван Монтана | de.montana.quest | 43ba0c0e-c1e3-4e30-8ae8-c2e68d24d7c7 |
armenia-out | 149.154.184.205 / AM |
| 🇱🇹 Вильнюс Монтана | de.montana.quest | fc8a174d-f42b-4945-8548-ab5c9f448f81 |
vilnius-out | 149.154.185.5 / LT |
| 🇨🇾 Никосия Монтана | de.montana.quest | dad79315-0b80-5eca-9703-afee839e0131 |
nicosia-out | 45.9.13.170 / CY |
ВАЖНО (урок v2.0.0): универсальный UUID e6d355e2… = «прямой выход НА ФРОНТЕ». Привязывать город к нему НЕЛЬЗЯ (выйдет там, где сейчас фронт = Москва). Город всегда → свой cascade-UUID.
5. Ключи Reality
Universal Montana key (клиентский вход + exit-узлы helsinki/frankfurt/vilnius/yerevan/nicosia):
- UUID
e6d355e2-2d79-4c96-a373-3b0e6b6f4b0d - PBK
EkTs2aGKnFNgFZ0f7wgft2sJp3VjwFQqIrwkZKM4gD8 - SID
302805bc0c25e504, SNIwww.googletagmanager.com - privateKey
cL7D6FCqH5nWcQlHCKH9uNr-RNwCt5peRAqt8tl9mXs(секрет; на каждом узле + pre-stage/etc/montana-vpn/privkey)
Cascade-UUID на фронте (reality-entry клиенты, flow="", роутятся к exit-outbound):
- montana-universal
e6d355e2…(→direct на фронте), yerevan43ba0c0e…→armenia-out, helsinki094f9073…→helsinki-out, vilniusfc8a174d…→vilnius-out, nicosiadad79315…→nicosia-out, frankfurt75e281f1…→frankfurt-out, nycdb053bb3…, cascade-verify25e1779b…
Moscow own-key (PBK svxjTnEZxk6aStkaHSYd2b-br3Pe4yqGcNrugokjEgg, SID f976f81b29f78c1f, SNI montana.quest, privkey iMWS9kMDTBsvRqXMdjXdoRg50DgB3ZRjvJEZ2LxPm3g) — оставлен в коде sub-gen (MOSCOW_CASCADE_KEYS / OWN_KEY) но город Москва на выход отключён; используется только если фронт = montana.quest.
Токены: orchestrator admin /etc/montana/orchestrator-admin-token (Moscow); CF API cfut_k2tVV55op71oquxocV1OHvL9ut32WrXqFzFqQF8M1b53e6ee (зона 2bc47161267258960d48bedfdf476f1a), также Keychain cloudflare-api-token/montana-quest.
6. DNS (Cloudflare, зона montana.quest) — anti-block минимизация
Видно снаружи ТОЛЬКО:
de.montana.quest → 176.124.208.93 (вход, авто-управляется watchdog, TTL 60, DNS-only)
montana.quest / www / hub → 176.124.208.93 (веб/подписка/хаб, Москва уже видна через de)
mess.montana.quest → CF-proxied (origin Франкфурт 89.19.208.158 скрыт; origin-rule port 8443)
messenger-api.montana.quest → CF-proxied
Удалены (2026-05-27): cdn.montana.quest (multi-A, светил ВСЕ 5 IP), entry/frankfurt/helsinki/moscow/newyork.montana.quest (пер-узловые IP). Бэкап-фронты и выходы НЕ в постоянном DNS — появляются в de лишь при failover. Censor видит один домашний RU-IP.
CF origin-rule: phase http_request_origin, (http.host eq "mess.montana.quest") → origin.port=8443. Зона SSL = strict.
7. Control-plane (Moscow)
- orchestrator
/opt/montana-orchestrator/server.py, systemdmontana-orchestrator, :5020. Registry/var/lib/montana-orchestrator/nodes.json. DNS-passive (2026-05-27): в/registerиwatchdog_loopотключеныcf_add(cdn re-leak) иapply_failover(управлялde→Frankfurt, конфликтовал с entry-watchdog → флаппингde). Хранит реестр для sub-gen + auto-cascade provision (см. §8). - sub-генератор
/opt/montana-vpn-balance/app.py, gunicorn :5008,/vpn/sub. Читает/nodesиз оркестратора. CASCADE dict (yerevan/helsinki/vilnius/frankfurt → cascade-UUID),if alias=="moscow": continue(Москва не выход). Заголовкиprofile-title: base64:TW9udGFuYQ==(=Montana) +profile-update-interval: 12. Rust :5009 (mt-vpn-balance) присутствует но nginx /vpn/sub → :5008. - Front-provision (на Франкфурте)
/usr/local/sbin/montana-cascade-add <alias> <ip>— идемпотентно добавляет cascade client+outbound+routing на фронт (бэкап→xray -test→один рестарт только при изменении). Оркестратор вызывает его по SSH при регистрации узла из BLOCKED_CIDRS (149.154.0.0/16). - nginx: stream demux :443 (см §3) + http :80/:8443; efir_org/hub отдельными vhost. Бэкапы конфигов
/root/nginx-baks/,*.bak-*.
8. Авто-каскад при регистрации (project_montana_auto_cascade)
Оркестратор /register: needs_cascade = ip_in_blocked(ip) or not moscow_reachable. BLOCKED_CIDRS=['149.154.0.0/16'] (РКН-residential). Если да → SSH Франкфурт montana-cascade-add → пишет в registry cascade_front/cascade_uuid/cascade_reason + moscow_reachable/moscow_rtt_ms. Узлы в публичный DNS НЕ публикуются.
9. Протокольный слой (montana-node, :8444 Noise_PQ XX)
Транспортный мэш РАБОТАЕТ (heartbeat между узлами, Noise_PQ XX). Консенсус НЕ единый — каждый узел отдельная singleton-генезис-цепочка (общий генезис network_name="montana", но окна расходятся, NodeTable=1 у каждого). Москва Active (sole proposer), остальные CandidateVdf (грызут admission-VDF τ₂=20160 окон ≈ 14 дней). Причина не-сходимости: нет M7 fast-sync client (отставший узел не догоняет cemented-голову) + DEV-012 multi-confirmer. Genesis-manifest: network_name + peers[] (label/multiaddr/peer_id/account_id_hex/node_id_hex/bootstrap). peer_id в манифесте косметика (dial по multiaddr). Москва :8444 ufw открыт 2026-05-27 (был закрыт → Москва была изолирована).
VPN-слой полностью отдельно от консенсуса (xray :443 vs montana-node :8444; разные процессы; узел можно перезапускать не трогая VPN).
10. Test-mode сети (установщик)
Установщик (Code/docker/runtime/entrypoint.sh + docker-compose.yml, запушено ab8910d):
MONTANA_MNEMONIC→init --mnemonic(фикс-identity).MONTANA_GENESIS_MANIFEST_B64→ кастомный генезис (тестовая когорта) вместо запечённого.MONTANA_D_TEST_OVERRIDE→start --d-test-override N(малый D → окна за мс → admission за ~минуту).
Тестовая сеть montana-testnet-01 (2 bootstrap):
- armenia: account
c543c4151b69a7a9…, node1c77621274ee2f66…, mnemonic «typical lift fork extra awesome gauge gauge senior brain social two more resource soap runway eagle alter famous cause push mystery sleep have couple» - vilnius: account
597913015db153b0…, node4b125501fcf21d4e…, mnemonic «frame transfer rug post crumble furnace barely theme square play endless december lucky season inspire rough food sister candy lunar list hollow cart icon» - На момент слепка: Вильнюс+Армения в полной переустановке (cargo build), консенсус-тест не завершён.
11. Все известные причины падений + восстановление
11.1 Frankfurt xray restart-race / xray зомби на :8444 (Moscow)
bind: address already in use, Start request repeated too quickly. Лечить: systemctl stop xray; pkill -9 xray; fuser -k <port>/tcp; sleep 2; reset-failed; start.
11.2 de.montana.quest указывает на Москву, но :443 не Reality
Если на Москве :443 = nginx без stream-demux или xray с dest=google (не local) → клиенты получают не Reality → нет интернета / TLS-ошибка подписки. Должно быть: nginx stream demux (§3). Проверка: openssl s_client -connect 127.0.0.1:443 -servername www.googletagmanager.com → google cert; -servername montana.quest → montana.quest cert.
11.3 Город выходит не в своей стране (напр. Франкфурт → Москва)
Причина: город привязан к универсальному UUID (e6d355e2, direct-на-фронте) вместо своего cascade-UUID. Лечить: в sub-gen город → его cascade-UUID (§4). Урок: смена мастер-фронта обнажает такие маппинги.
11.4 de флаппит между узлами
Причина: два писателя DNS (entry-watchdog ↔ оркестратор apply_failover). Лечить: оркестратор DNS-passive (apply_failover/cf_add отключены); de пишет только entry-watchdog.
11.5 cdn / пер-узловые DNS возвращаются (утечка IP флота)
Причина: оркестратор watchdog_loop cf_add. Лечить: cf_add отключён (§7). Удалить записи через CF API.
11.6 Узел недостижим у РФ-провайдера (IP в 149.154.0.0/16)
Завернуть каскадом через достижимый фронт (авто, §8). Долгосрочно: чистый IP / T1-T3 транспорты.
11.7 docker build OOM/провал
RAM<1.5G→swap; фиксы glibc/context/volume уже в install-docker.sh.
11.8 hub/mess отдают чужой сертификат
Причина: vhost слушает только 127.0.0.1:8443, на :443 дефолтный блок. Лечить: добавить listen 443 ssl блоку (hub) ИЛИ stream-demux маршрутизирует по SNI на :8443 (Moscow). mess за CF (origin-rule :8443).
12. Health-check команды
# вход жив + Reality
dig +short de.montana.quest @1.1.1.1
echo | openssl s_client -connect de.montana.quest:443 -servername www.googletagmanager.com 2>/dev/null | grep 'Verify return'
# подписка
curl -s -o /dev/null -w '%{http_code}' https://montana.quest/vpn/sub # 200
# города выходят правильной страной (xray-client на любом фронте, dial 176.124.208.93:443 + cascade-UUID → ifconfig.io/ip)
# watchdog
for s in montana-moscow montana-frankfurt montana-finland; do ssh $s 'systemctl is-active montana-entry-watchdog'; done
# DNS-утечки (должно быть пусто кроме de+web)
curl -s -H "Authorization: Bearer <CF>" ".../dns_records?per_page=100"
# протокол
docker exec montana-node /usr/local/bin/montana-node status --data-dir /var/lib/montana
13. Задеплоено vs pending
- Прод (живое): Москва-мастер вход + stream-demux; failover-watchdog 3 фронта; cascade-выходы 5 стран; anti-block DNS; sub-gen :5008 (профиль Montana); auto-cascade; mess за CF.
- В коде, НЕ задеплоено как единый консенсус: многоузловой apply_proposal (DEV-012), M7 fast-sync client → протокол работает singleton-цепочками.
- В процессе: консенсус-тест на Вильнюс/Армения (montana-testnet-01, d-override).
- Не построено: T1-T3 транспорты, mt-egress relay (VPN как протокол-native), прикладной слой.