Files
kaskad/docs/architecture.md
andrey271192 209caa81a8 Initial release of Kaskad
Каскадная маршрутизация русских сайтов через свой набор RU-серверов
с failover, Telegram-ботом и веб-интерфейсом.

- bin/        — failover-script, route/domain helpers, ams/ru bootstrap
- bot/        — Telegram-бот (Python, long-poll)
- webui/      — Flask single-page dashboard
- docs/       — установка, архитектура, API бота и WebUI
- examples/   — шаблоны конфигов
2026-05-02 09:13:23 +03:00

107 lines
7.3 KiB
Markdown
Raw Permalink 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.

# Архитектура
## Обзор
Каждый ам. сервер — это **WG-клиент**, держит ОДИН активный туннель к одному из RU-серверов. RU-серверы — это **WG-серверы**, у каждого N пиров (по числу ам.).
```
┌─────────────┐
│ RU primary│
│ (priority 1)│
└──┬──┬──┬──┬──┘
│ │ │ │ WG туннели
┌─────┘ │ │ └─────┐
▼ ▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐
│ ams1 │ │ ams2 │ │ ams3 │
│X-ray │ │X-ray │ │X-ray │
└──────┘ └──────┘ └──────┘
▲ ▲ ▲
│ │ │ failover при падении primary
│ ┌────┘ │
│ │ │
┌─▼─────────────▼──┐
│ RU backup │
│ (priority 2) │
└──────────────────┘
```
## Компоненты
### На RU-серверах
- `wg_ru` интерфейс, listen UDP/1939, peers = все ам. серверы (`tunnel_ip``pubkey`)
- `iptables -t nat -A POSTROUTING -o <wan_iface> -j MASQUERADE` — выходящий трафик от ам. серверов наружу
- `iptables -I FORWARD -i wg_ru -j ACCEPT`, `-o wg_ru -j ACCEPT` — пропуск форварда
### На ам. серверах
- `ru` интерфейс, peer = один из RU-серверов (тот что активен сейчас)
- `/etc/wireguard/ru.conf` — текущая конфигурация туннеля
- `/etc/wireguard/ru-servers.json` — общий список всех RU и ам. серверов (синхронизируется ботом/WebUI)
- `/etc/wireguard/ru-base.aips` — базовый набор подсетей в `AllowedIPs` (создаётся при первой установке)
- `/etc/wireguard/ru-extra.list` — дополнительные IP/CIDR, добавленные через бот
- `/etc/wireguard/ru-domains.json` — карта домен → список IP (резолвится через `dig`)
### Скрипты на ам. серверах (`/usr/local/bin/`)
| Скрипт | Что делает |
|---|---|
| `ru-failover.py` | Cron каждую минуту. Проверяет handshake/probe текущего peer'а, переключает на другой при падении. Trial-failback при возврате primary. |
| `ru-set.sh <id>` | Принудительная установка peer'а из JSON по `id` |
| `ru-routes.sh add\|remove\|clear\|list\|apply` | Управление `ru-extra.list` + live `ip route` + iptables mangle + sync allowed-ips через `wg syncconf` |
| `ru-domains.py add\|remove\|list\|show\|refresh` | Резолв доменов и проксирование результатов в `ru-routes.sh` |
## Маршрутизация трафика
```
Запрос с телефона на gosuslugi.ru:
1. X-ray на ам. сервере получает пакет на amn0 интерфейсе
2. iptables -t mangle -A PREROUTING -i amn0 -d 95.163.0.0/16 -j MARK --set-mark 100
3. ip rule fwmark 100 lookup 200
4. table 200: default dev ru
5. WG проверяет AllowedIPs пира (95.163.0.0/16 ∈ allowed) → шифрует
6. Пакет идёт в туннель к RU-серверу
7. На RU: pакет приходит на wg_ru, MASQUERADE на wan, идёт в интернет с RU IP
8. Ответ возвращается обратно через NAT → wg_ru → ам. сервер → телефон
```
Для НЕ-российских сайтов: пакет в mangle не получает MARK 100, идёт по обычному маршруту через провайдера ам. сервера.
## Failover
`ru-failover.py` запускается из cron каждую минуту:
```
1. Прочитать /etc/wireguard/ru-servers.json (отсортированный по priority)
2. Определить current = peer чей endpoint в /etc/wireguard/ru.conf
3. age = now - last_handshake (от wg show)
4. Если current dead (age > 180 && TCP probe не отвечает && cooldown 5min прошёл):
→ переключиться на ближайший живой
5. Если current не highest-priority И есть более приоритетный живой И cooldown && fail_backoff прошли:
→ переключиться на него (mode = failback test)
6. Если в режиме failback test:
- handshake появился за <60s → success
- не появился → откат на запасной + 30min backoff на повторный failback
```
Переключение через `wg syncconf` — без рестарта интерфейса. Маршруты, mangle, расширенные allowed-ips восстанавливаются через `ru-routes.sh apply` (вызывается из PostUp и сразу после switch).
## Синхронизация конфига
`ru-servers.json` — единый источник правды. Хранится на каждом ам. сервере. Изменения вносятся ТОЛЬКО через бот/WebUI на «локальном» ам. сервере (где бот живёт), затем `save_and_distribute()` SCP-ит файл на остальные через root SSH-ключ.
Конфиг WG-серверов (`/etc/wireguard/wg_ru.conf` на RU) бот редактирует напрямую при `/ams-add` / `/ams-remove` — добавляет/удаляет [Peer] секции и делает `wg syncconf wg_ru`.
## SSH между серверами
Бот (на одном из ам. серверов) генерирует ed25519 ключ при первом запуске. Этот ключ:
- автоматически прописывается в `/root/.ssh/authorized_keys` на всех остальных ам. серверах при `/ams-add` (или вручную при онбординге)
- автоматически прописывается на новых RU при `/server-add` (через add-ru-helper.sh)
- для существующих RU — добавляется один раз вручную при первичной настройке
После этого бот работает только по ключу, никаких сохранённых паролей.
## Уведомления
`ru-failover.py` каждый switch/success/fail логирует через `logger -t ru-failover` И шлёт в Telegram если есть `/etc/wireguard/notify.env` с `TG_BOT_TOKEN` и `TG_CHAT_ID`.
С RU-серверов в России TG API часто недоступен, поэтому бот живёт на одном из ам. серверов.