# Incident response — VPN-сеть Montana Версия: **2026-05-18** ## Severity матрица | Sev | Симптом | Reaction time | |---|---|---| | **S1** | ВСЕ узлы в pool недоступны (нет VPN ни у кого) | < 15 минут | | **S2** | Один из узлов недоступен > 5 минут после автоматического prune не отрабатывает | < 30 минут | | **S3** | Утечка любого секрета подтверждена | < 1 час, по ротации | | **S4** | DPI начал блокировать наш Reality в одной стране | < 4 часа, переключение SNI | | **S5** | Падение orchestrator | < 1 час (multi-A продолжает работать) | ## Playbook ### S1: вся сеть лежит ``` # 1. Проверить multi-A: dig @1.1.1.1 cdn.montana.quest A # если пусто или мало → CF API упал, либо watchdog ошибся и удалил всё # 2. Прямой probe каждого узла: for n in 91.132.142.42 89.19.208.158 86.104.72.12; do echo "$n: $(timeout 4 openssl s_client -connect $n:443 -servername www.googletagmanager.com -tls1_3 &1 | grep -c 'Verification: OK')" done # 3. Если узлы живы, а pool пуст — force re-register: TOKEN=$(security find-generic-password -s montana-orchestrator-admin -a token -w) for spec in "91.132.142.42|helsinki|FI" "89.19.208.158|frankfurt|DE" "86.104.72.12|newyork|US"; do ip=$(echo $spec|cut -d'|' -f1); alias=$(echo $spec|cut -d'|' -f2); c=$(echo $spec|cut -d'|' -f3) curl -sX POST -H 'Content-Type: application/json' \ https://montana.quest/vpn/node/register \ --data "{\"ip\":\"$ip\",\"alias\":\"$alias\",\"country\":\"$c\",\"hosting\":\"x\",\"label\":\"$alias\",\"coords\":[0,0],\"secret\":\"$TOKEN\"}" done # 4. Если узлы тоже мертвы — каскад инфраструктурного фейла: # a) Проверить статус хостинга (Timeweb dashboard / THE.Hosting) # b) Если хостинг ОК — SSH к узлу, проверить xray ssh montana-finland 'systemctl status xray && tail -50 /var/log/xray/error.log' ``` ### S2: один узел мёртв > 5 мин, не выпал ``` # 1. Проверить watchdog работает: ssh montana-moscow 'cat /var/lib/montana-orchestrator/pool-health.json | jq' # 2. Проверить probe от Moscow к узлу: ssh montana-moscow 'python3 -c "import ssl,socket; ctx=ssl.create_default_context() s=socket.create_connection((\"\",443),timeout=5) ws=ctx.wrap_socket(s,server_hostname=\"www.googletagmanager.com\") print(ws.getpeercert())"' # Если timeout — но узел живой → проверить ufw/CrowdSec на узле ssh 'cscli decisions list 2>/dev/null | grep 176.124.208.93' # 3. Если watchdog не работает — restart orchestrator: ssh montana-moscow 'systemctl restart montana-orchestrator' # 4. Ручное удаление IP: TOKEN=$(security find-generic-password -s montana-orchestrator-admin -a token -w) curl -X POST -H 'Content-Type: application/json' \ https://montana.quest/vpn/node/deregister \ --data "{\"ip\":\"\",\"secret\":\"$TOKEN\"}" ``` ### S3: утечка секрета #### S3a: утечка Reality privateKey 1. Ввести **ускоренную ротацию** (см. OPERATIONS.md → "Ротация ключей Reality"). 2. Уведомить пользователей через канал коммуникации (Telegram, sitemontana.quest баннер). 3. Заблокировать старый pbk (xray на узле не примет старый клиент после смены privateKey). #### S3b: утечка admin TOKEN ``` # 1. Сгенерировать новый: NEW=$(openssl rand -hex 32) # 2. Положить на Moscow: ssh montana-moscow "install -m 600 /dev/stdin /etc/montana/orchestrator-admin-token <<<'$NEW'" ssh montana-moscow 'systemctl restart montana-orchestrator' # 3. Обновить Keychain: security add-generic-password -s montana-orchestrator-admin -a token -w "$NEW" -U ``` #### S3c: утечка CF API token 1. Сразу revoke в Cloudflare dashboard. 2. Создать новый scoped token (Zone montana.quest, Permissions: Zone.DNS.Edit). 3. Обновить в Keychain `cloudflare-api-token` и в `/etc/montana/cf-api-token` на Moscow. 4. Restart orchestrator. ### S4: DPI блокировка в стране ``` # Временное решение: сменить SNI в realitySettings на другой популярный домен. # Все узлы должны иметь тот же SNI (привязан к pbk). for n in montana-finland montana-frankfurt montana-us; do ssh $n "python3 -c \" import json p='/usr/local/etc/xray/config.json' c=json.load(open(p)) ib=c['inbounds'][0] ib['streamSettings']['realitySettings']['serverNames']=['www.gstatic.com'] ib['streamSettings']['realitySettings']['dest']='www.gstatic.com:443' json.dump(c,open(p,'w'),indent=2)\" && systemctl restart xray" done # Обновить /vpn/sub: в URL sni=www.gstatic.com ssh montana-moscow 'sed -i "s/www.googletagmanager.com/www.gstatic.com/g" ' ``` ### S5: orchestrator упал, не поднимается ``` ssh montana-moscow 'journalctl -u montana-orchestrator -n 200 --no-pager' # Типичные причины: # - синтакс ошибка в server.py → откатить из *.bak ssh montana-moscow 'ls /opt/montana-orchestrator/server.py.bak-*' # - /etc/montana/cf-api-token нечитаем → chmod 600 root:root # - Flask недоступен → apt install python3-flask # Существующие подключения клиентов НЕ затронуты до DNS TTL expire (120s). # Если orch будет down > 30 минут — manual prune через CF API напрямую. ``` ## Эскалация | Уровень | Кто | Канал | |---|---|---| | L1 | Архитектор сети | efir369999@gmail.com | | L2 | Cloudflare support (DNS issues) | https://dash.cloudflare.com → support | | L3 | Хостинг провайдер | Timeweb / THE.Hosting dashboard | ## После инцидента 1. Записать в `OPEN-RISKS.md` если выявлен новый риск. 2. Если фикс уровня кода — соответствующее изменение в `Montana/Node/join.sh` или orchestrator. 3. Записать дату и причину в `AUDIT-READINESS.md → История проверки`.