mirror of
https://github.com/andrey271192/kaskad.git
synced 2026-09-20 13:49:56 +00:00
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/ — шаблоны конфигов
This commit is contained in:
106
docs/architecture.md
Normal file
106
docs/architecture.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# Архитектура
|
||||
|
||||
## Обзор
|
||||
|
||||
Каждый ам. сервер — это **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 часто недоступен, поэтому бот живёт на одном из ам. серверов.
|
||||
88
docs/bot.md
Normal file
88
docs/bot.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Telegram-бот
|
||||
|
||||
Бот живёт на одном из ам. серверов (там, где есть доступ к api.telegram.org). Управляет всеми остальными по SSH через свой ed25519 ключ.
|
||||
|
||||
## Доступ
|
||||
|
||||
Бот реагирует ТОЛЬКО на сообщения из чата с `chat_id == TG_CHAT_ID` (см. `/etc/wireguard/notify.env`). Остальные сообщения игнорирует.
|
||||
|
||||
## Команды
|
||||
|
||||
### Туннели
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `/status` | Endpoint и handshake age для каждого ам. сервера |
|
||||
| `/use <id>` | Принудительно переключить все ам. на RU-сервер с этим id |
|
||||
| `/primary` | Алиас `/use primary` |
|
||||
| `/backup` | Алиас `/use backup` |
|
||||
| `/failover` | Алиас `/use backup` |
|
||||
| `/failback` | Алиас `/use primary` |
|
||||
|
||||
### RU-серверы
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `/server-list` | Список RU-серверов с приоритетами |
|
||||
| `/server-add <host> <user> <ssh_port> <id> [prio] [password=PW] [label=...] [listen_port=1939] [probe_port=ssh_port]` | Бот SSHит на новый RU, ставит wireguard, генерит ключ, добавляет 4 ам. как peer'ы, открывает 1939/UDP, кладёт свой ключ для будущего управления |
|
||||
| `/server-remove <id\|host>` | Удалить RU из ротации (сам сервер не выключается, просто перестаёт использоваться) |
|
||||
| `/bot-key` | Показать публичный SSH-ключ бота — для добавления в `authorized_keys` на новом сервере (если не хочется передавать пароль через TG) |
|
||||
|
||||
**Безопасность**: при `password=PW` бот после обработки автоматически удаляет своё сообщение через TG API. Но в моменте оно всё равно попадает в логи TG, поэтому надёжнее — использовать ключевую авторизацию (см. `/bot-key`).
|
||||
|
||||
### Ам. серверы
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `/ams-list` | Список ам. серверов |
|
||||
| `/ams-add <host> <user> <ssh_port> <id> [tunnel_ip=auto] [xray_iface=amn0] [password=PW]` | Бот: ставит wireguard, кладёт свой ключ, генерит WG-ключ ам., выдаёт следующий свободный 10.0.0.X, копирует все скрипты и шаблон ru.conf со sga1, поднимает туннель на текущий primary, ставит cron, и добавляет [Peer] на ВСЕХ RU-серверах |
|
||||
| `/ams-remove <id\|host>` | Снять [Peer] со всех RU; обновить JSON. Сам сервер остаётся работать (X-ray, OS), просто перестаёт быть в туннеле |
|
||||
|
||||
X-ray на новый ам. сервер бот НЕ ставит — это вне его компетенции.
|
||||
|
||||
### Маршруты (IP/CIDR)
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `/ips` | Все маршруты сразу (база + доп.) |
|
||||
| `/list` | Только дополнительные (добавленные через бот) |
|
||||
| `/add <IP/CIDR ...>` | Добавить (через пробел или с новой строки; `/32` дописывается сам) |
|
||||
| `/remove <IP/CIDR ...>` | Удалить |
|
||||
| `/clear` | Удалить ВСЕ доп. маршруты |
|
||||
|
||||
Базовые подсети (10 крупных сервисов: Яндекс, ВК, Mail.ru, Госуслуги, Ozon) трогать через бот нельзя — они в `/etc/wireguard/ru-base.aips`. Если нужно изменить — править вручную и `/usr/local/bin/ru-routes.sh apply`.
|
||||
|
||||
### Домены
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `/list-domains` | Список доменов с количеством IP |
|
||||
| `/add-domain <domain ...>` | Резолвит каждый домен через `dig +short A`, добавляет полученные IP как /32 в маршруты на всех ам. серверах. Формат: пробел или с новой строки. |
|
||||
| `/remove-domain <domain ...>` | Убирает домен и его IP (если их не использует другой домен). |
|
||||
| `/show-domain <domain>` | Текущие IP конкретного домена |
|
||||
| `/refresh-domains` | Принудительно перерезолвить все домены |
|
||||
|
||||
Cron `17 */6 * * * /usr/local/bin/ru-domains.py refresh` — автоматический refresh каждые 6 часов. Лог: `/var/log/ru-domains.log`.
|
||||
|
||||
### Прочее
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `/bot-key` | Публичный SSH-ключ бота |
|
||||
| `/help` | Полная справка |
|
||||
|
||||
## Уведомления
|
||||
|
||||
При каждом автоматическом переключении (failover/failback успех/failback провал) бот шлёт сообщение в `TG_CHAT_ID` с тэгом `[ru-failover @ <hostname>]`. См. `journalctl -t ru-failover` для истории.
|
||||
|
||||
## Логи
|
||||
|
||||
```bash
|
||||
# логи бота
|
||||
journalctl -u ru-tg-bot.service -f
|
||||
|
||||
# логи failover
|
||||
journalctl -t ru-failover -f
|
||||
# или
|
||||
grep ru-failover /var/log/syslog | tail
|
||||
```
|
||||
216
docs/install.md
Normal file
216
docs/install.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# Установка
|
||||
|
||||
## Требования
|
||||
|
||||
- Минимум 1 RU-сервер (Ubuntu 20.04+) с публичным IP или пробросом UDP/1939 на роутере
|
||||
- Минимум 1 ам. сервер (Ubuntu 20.04+) с установленной X-ray панелью (3x-ui)
|
||||
- Telegram-бот (создать в @BotFather) и chat ID для уведомлений (узнать у @userinfobot)
|
||||
- Root доступ на оба сервера
|
||||
|
||||
## Шаг 1. Первый RU-сервер
|
||||
|
||||
На RU-сервере под root:
|
||||
|
||||
```bash
|
||||
apt update && apt install -y wireguard iptables-persistent
|
||||
|
||||
# 1. Генерация ключа
|
||||
cd /etc/wireguard
|
||||
umask 077
|
||||
wg genkey | tee ru_private.key | wg pubkey > ru_public.key
|
||||
PRIVKEY=$(cat ru_private.key)
|
||||
|
||||
# 2. ip_forward
|
||||
sysctl -w net.ipv4.ip_forward=1
|
||||
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
|
||||
|
||||
# 3. Конфиг — пока без peer'ов, добавятся когда подключим первый ам.
|
||||
IFACE=$(ip route | awk '/^default/{print $5; exit}')
|
||||
cat > /etc/wireguard/wg_ru.conf <<EOF
|
||||
[Interface]
|
||||
Address = 10.0.0.1/24
|
||||
PrivateKey = $PRIVKEY
|
||||
ListenPort = 1939
|
||||
PostUp = iptables -t nat -A POSTROUTING -o $IFACE -j MASQUERADE; iptables -I FORWARD 1 -i wg_ru -j ACCEPT; iptables -I FORWARD 1 -o wg_ru -j ACCEPT; iptables -I INPUT -i wg_ru -j ACCEPT
|
||||
PostDown = iptables -t nat -D POSTROUTING -o $IFACE -j MASQUERADE; iptables -D FORWARD -i wg_ru -j ACCEPT; iptables -D FORWARD -o wg_ru -j ACCEPT; iptables -D INPUT -i wg_ru -j ACCEPT
|
||||
EOF
|
||||
chmod 600 /etc/wireguard/wg_ru.conf
|
||||
|
||||
# 4. Открыть порт 1939/UDP
|
||||
iptables -A INPUT -p udp --dport 1939 -j ACCEPT
|
||||
netfilter-persistent save
|
||||
|
||||
# 5. Запуск + автостарт
|
||||
wg-quick up wg_ru
|
||||
systemctl enable wg-quick@wg_ru
|
||||
|
||||
# 6. Сохранить публичный ключ — пригодится
|
||||
cat ru_public.key
|
||||
```
|
||||
|
||||
Если RU за NAT (Keenetic и т.п.) — пробрось UDP/1939 на роутере на этот сервер.
|
||||
|
||||
## Шаг 2. Первый ам. сервер
|
||||
|
||||
На ам. сервере под root:
|
||||
|
||||
```bash
|
||||
apt update && apt install -y wireguard iptables-persistent dnsutils
|
||||
|
||||
# 1. Ключ
|
||||
cd /etc/wireguard
|
||||
umask 077
|
||||
wg genkey | tee ru_private.key | wg pubkey > ru_public.key
|
||||
cat ru_public.key # записать — добавим в peer'ы RU
|
||||
|
||||
# 2. Узнать имя интерфейса X-ray
|
||||
ip a | grep -E 'amn|tun' | grep -v '@'
|
||||
# обычно amn0 — используем дальше
|
||||
|
||||
# 3. Конфиг ru.conf — IP в туннеле = 10.0.0.2 (первый ам.)
|
||||
PRIVKEY=$(cat ru_private.key)
|
||||
cat > /etc/wireguard/ru.conf <<EOF
|
||||
[Interface]
|
||||
Address = 10.0.0.2/32
|
||||
PrivateKey = $PRIVKEY
|
||||
Table = off
|
||||
PostUp = ip route add 95.163.0.0/16 dev ru; ip route add 185.73.192.0/22 dev ru; ip route add 213.59.0.0/16 dev ru; ip route add 77.88.0.0/18 dev ru; ip route add 93.158.128.0/18 dev ru; ip route add 188.40.167.0/24 dev ru; ip route add 176.114.120.0/22 dev ru; ip route add 178.248.232.0/22 dev ru; ip route add 213.180.192.0/20 dev ru; ip route add 87.240.128.0/18 dev ru; ip rule add fwmark 100 table 200; ip route add default dev ru table 200; iptables -t nat -A POSTROUTING -o ru -j MASQUERADE
|
||||
PostDown = ip rule del fwmark 100 table 200; ip route flush table 200; iptables -t nat -D POSTROUTING -o ru -j MASQUERADE
|
||||
[Peer]
|
||||
PublicKey = ПУБЛИЧНЫЙ_КЛЮЧ_RU_СЕРВЕРА
|
||||
Endpoint = ВНЕШНИЙ_IP_RU:1939
|
||||
AllowedIPs = 10.0.0.0/24, 95.163.0.0/16, 185.73.192.0/22, 213.59.0.0/16, 77.88.0.0/18, 93.158.128.0/18, 188.40.167.0/24, 176.114.120.0/22, 178.248.232.0/22, 213.180.192.0/20, 87.240.128.0/18
|
||||
PersistentKeepalive = 25
|
||||
EOF
|
||||
chmod 600 /etc/wireguard/ru.conf
|
||||
|
||||
# 4. Mangle для X-ray трафика — за каждой подсетью
|
||||
for net in 95.163.0.0/16 185.73.192.0/22 213.59.0.0/16 77.88.0.0/18 93.158.128.0/18 188.40.167.0/24 176.114.120.0/22 178.248.232.0/22 213.180.192.0/20 87.240.128.0/18; do
|
||||
iptables -t mangle -A PREROUTING -i amn0 -d $net -j MARK --set-mark 100
|
||||
done
|
||||
netfilter-persistent save
|
||||
|
||||
# 5. Запуск
|
||||
wg-quick up ru
|
||||
systemctl enable wg-quick@ru
|
||||
wg show ru # должен быть handshake
|
||||
```
|
||||
|
||||
На RU-сервере добавить этот ам. как peer:
|
||||
```bash
|
||||
wg set wg_ru peer ПУБЛИЧНЫЙ_КЛЮЧ_АМ allowed-ips 10.0.0.2/32
|
||||
cat >> /etc/wireguard/wg_ru.conf <<EOF
|
||||
|
||||
[Peer]
|
||||
PublicKey = ПУБЛИЧНЫЙ_КЛЮЧ_АМ
|
||||
AllowedIPs = 10.0.0.2/32
|
||||
EOF
|
||||
```
|
||||
|
||||
Проверить что трафик идёт:
|
||||
```bash
|
||||
# на ам. сервере
|
||||
curl --interface ru https://gosuslugi.ru -I
|
||||
# должен ответить HTTP/... 200
|
||||
```
|
||||
|
||||
## Шаг 3. Установка скриптов и бота
|
||||
|
||||
На ам. сервере где будет жить бот (в Амстердаме, Telegram должен быть доступен!):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/andrey271192/kaskad.git /opt/kaskad
|
||||
cd /opt/kaskad
|
||||
|
||||
# 1. Скопировать скрипты
|
||||
install -m 755 bin/ru-failover.py /usr/local/bin/
|
||||
install -m 755 bin/ru-set.sh /usr/local/bin/
|
||||
install -m 755 bin/ru-routes.sh /usr/local/bin/
|
||||
install -m 755 bin/ru-domains.py /usr/local/bin/
|
||||
install -m 755 bin/add-ru-helper.sh /usr/local/bin/
|
||||
install -m 755 bin/add-ams-helper.sh /usr/local/bin/
|
||||
|
||||
# 2. Создать notify.env
|
||||
cp examples/notify.env.example /etc/wireguard/notify.env
|
||||
chmod 600 /etc/wireguard/notify.env
|
||||
# ВПИСАТЬ TG_BOT_TOKEN и TG_CHAT_ID
|
||||
|
||||
# 3. Создать ru-servers.json
|
||||
cp examples/ru-servers.example.json /etc/wireguard/ru-servers.json
|
||||
chmod 600 /etc/wireguard/ru-servers.json
|
||||
# ОТРЕДАКТИРОВАТЬ — вписать host, pubkey, ssh_user/port для каждого RU; для каждого ам. — host, pubkey, tunnel_ip
|
||||
|
||||
# 4. Базовый список allowed-ips (берётся из ru.conf при первом apply)
|
||||
# создаст ru-base.aips автоматически
|
||||
/usr/local/bin/ru-routes.sh apply
|
||||
|
||||
# 5. Cron на failover и refresh доменов
|
||||
( crontab -l 2>/dev/null; \
|
||||
echo '* * * * * /usr/local/bin/ru-failover.py'; \
|
||||
echo '17 */6 * * * /usr/local/bin/ru-domains.py refresh >> /var/log/ru-domains.log 2>&1' \
|
||||
) | crontab -
|
||||
|
||||
# 6. SSH-ключ бота (нужен для управления остальными серверами через ssh-key auth)
|
||||
[ -f /root/.ssh/id_ed25519 ] || ssh-keygen -t ed25519 -N '' -f /root/.ssh/id_ed25519
|
||||
cat /root/.ssh/id_ed25519.pub
|
||||
# скопировать в /root/.ssh/authorized_keys на всех остальных ам. серверах
|
||||
# и на всех RU-серверах
|
||||
|
||||
# 7. Установить бот
|
||||
install -m 755 bot/ru-tg-bot.py /usr/local/bin/
|
||||
install -m 644 bot/ru-tg-bot.service /etc/systemd/system/
|
||||
apt install -y python3 sshpass
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ru-tg-bot.service
|
||||
journalctl -u ru-tg-bot.service -f
|
||||
```
|
||||
|
||||
Послать боту в Telegram `/status` — должно прийти текущее состояние.
|
||||
|
||||
## Шаг 4. Веб-интерфейс (опционально)
|
||||
|
||||
На том же ам. сервере, где бот:
|
||||
|
||||
```bash
|
||||
cd /opt/kaskad
|
||||
apt install -y python3-flask
|
||||
|
||||
install -m 755 webui/app.py /usr/local/bin/ru-webui.py
|
||||
mkdir -p /usr/local/share/kaskad
|
||||
cp -r webui/templates webui/static /usr/local/share/kaskad/
|
||||
# в app.py указать template_folder и static_folder если нужно;
|
||||
# по умолчанию работает из текущей директории, поэтому проще:
|
||||
ln -s /usr/local/share/kaskad/templates /usr/local/bin/templates
|
||||
ln -s /usr/local/share/kaskad/static /usr/local/bin/static
|
||||
|
||||
mkdir -p /etc/kaskad
|
||||
cp webui/webui.env.example /etc/kaskad/webui.env
|
||||
chmod 600 /etc/kaskad/webui.env
|
||||
# ВПИСАТЬ KASKAD_WEB_USER и KASKAD_WEB_PASS
|
||||
|
||||
install -m 644 webui/ru-webui.service /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ru-webui.service
|
||||
|
||||
# Открыть порт 8088 (по желанию)
|
||||
iptables -I INPUT -p tcp --dport 8088 -j ACCEPT
|
||||
netfilter-persistent save
|
||||
|
||||
# Открыть в браузере: http://AMS-IP:8088
|
||||
```
|
||||
|
||||
Рекомендуется поставить за HTTPS reverse-proxy (nginx, caddy, traefik) с Let's Encrypt.
|
||||
|
||||
## Шаг 5. Дальше
|
||||
|
||||
Через бот или WebUI:
|
||||
- `/server-add` — добавить новый RU-сервер
|
||||
- `/ams-add` — добавить новый ам. сервер
|
||||
- `/add-domain vk.com ozon.ru` — добавить русские сайты по доменам
|
||||
- `/add 5.45.192.1/32` — добавить конкретные IP/CIDR
|
||||
|
||||
Failover-скрипт каждую минуту проверяет здоровье текущего peer'а и переключает при необходимости.
|
||||
|
||||
## Если что-то не работает
|
||||
|
||||
См. [docs/troubleshooting.md](troubleshooting.md).
|
||||
104
docs/troubleshooting.md
Normal file
104
docs/troubleshooting.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Туннель не устанавливается (нет handshake)
|
||||
|
||||
```bash
|
||||
# на ам. сервере
|
||||
wg show ru
|
||||
# если нет endpoint вообще — что-то с конфигом
|
||||
# если есть, но нет handshake:
|
||||
nc -uvz <RU_HOST> 1939 # проверить что 1939/UDP проброшен
|
||||
journalctl -u wg-quick@ru -n 30
|
||||
```
|
||||
|
||||
На RU:
|
||||
```bash
|
||||
iptables -L INPUT -n | grep 1939 # должно быть ACCEPT
|
||||
ss -ulnp | grep 1939 # WG слушает
|
||||
wg show wg_ru # должны быть peer'ы
|
||||
```
|
||||
|
||||
Если за NAT (Keenetic) — пробрось 1939/UDP на роутере.
|
||||
|
||||
## Handshake есть, curl через туннель не идёт (HTTP 000)
|
||||
|
||||
Скорее всего `FORWARD` policy = DROP на RU (часто из-за установленного Docker):
|
||||
```bash
|
||||
iptables -L FORWARD -n | head -1
|
||||
# если DROP — добавить:
|
||||
iptables -I FORWARD -i wg_ru -j ACCEPT
|
||||
iptables -I FORWARD -o wg_ru -j ACCEPT
|
||||
iptables -I INPUT -i wg_ru -j ACCEPT
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
И прописать в `PostUp` `wg_ru.conf` чтобы пережили рестарт.
|
||||
|
||||
Также проверить `ip_forward`:
|
||||
```bash
|
||||
sysctl net.ipv4.ip_forward # должно быть = 1
|
||||
```
|
||||
|
||||
## Failover не срабатывает / срабатывает зря
|
||||
|
||||
Логи:
|
||||
```bash
|
||||
journalctl -t ru-failover -n 50
|
||||
cat /var/lib/ru-failover/last_switch # timestamp последнего switch
|
||||
cat /var/lib/ru-failover/test_started # 0 если не в режиме failback test
|
||||
cat /var/lib/ru-failover/last_fail # timestamp последнего failed failback
|
||||
```
|
||||
|
||||
Health-check использует TCP probe (по `probe_port` из JSON). Если хост жив, но probe_port закрыт — будет ложное срабатывание. Проверь:
|
||||
```bash
|
||||
nc -zv <RU_HOST> <probe_port>
|
||||
```
|
||||
|
||||
Если ICMP блокируется на RU — это нормально, мы не используем ping.
|
||||
|
||||
## /add-domain ничего не добавляет
|
||||
|
||||
```bash
|
||||
# на ам. сервере
|
||||
which dig # если нет — apt install dnsutils
|
||||
dig +short A vk.com # должны быть IP
|
||||
/usr/local/bin/ru-domains.py add vk.com # ручной тест
|
||||
```
|
||||
|
||||
## Бот не отвечает
|
||||
|
||||
```bash
|
||||
systemctl status ru-tg-bot.service
|
||||
journalctl -u ru-tg-bot.service -n 30
|
||||
# проверка доступности TG:
|
||||
curl -s -m 5 -o /dev/null -w '%{http_code}\n' https://api.telegram.org
|
||||
# должно быть 302; 000 = заблокирован, бота нужно перенести на сервер вне РФ
|
||||
```
|
||||
|
||||
Проверь, что `TG_CHAT_ID` в `notify.env` совпадает с твоим chat_id (узнать у `@userinfobot` или просто ничего боту не пиши — он молчит для всех кроме разрешённого chat_id).
|
||||
|
||||
## WebUI не открывается
|
||||
|
||||
```bash
|
||||
systemctl status ru-webui.service
|
||||
journalctl -u ru-webui.service -n 30
|
||||
ss -tlnp | grep 8088
|
||||
iptables -L INPUT -n | grep 8088 # порт должен быть ACCEPT
|
||||
```
|
||||
|
||||
## Сменился pubkey ам. сервера, но JSON не обновился
|
||||
|
||||
Если ты руками регенерил ключи WG на ам. сервере — нужно:
|
||||
1. Обновить `pubkey` этого ам. в `ru-servers.json` через бот: `/ams-remove <id>` + `/ams-add ...`
|
||||
2. Или вручную в JSON и `wg syncconf wg_ru` на каждом RU
|
||||
|
||||
## Конфиги разъехались между серверами
|
||||
|
||||
Бот считает `ru-servers.json` на «локальном» ам. сервере источником правды и синхронит на остальные при каждом изменении. Если ты вручную менял JSON на не-локальном — изменения потеряются.
|
||||
|
||||
Принудительная синхронизация: на локальном ам.
|
||||
```bash
|
||||
/usr/local/bin/ru-tg-bot.py --resync # (если нет — через любую команду /add /remove /server-add т.п.)
|
||||
```
|
||||
|
||||
Или просто пересохрани JSON через WebUI (любая операция запишет и распространит).
|
||||
80
docs/webui.md
Normal file
80
docs/webui.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Web UI
|
||||
|
||||
Flask-приложение, бежит рядом с TG-ботом (на одном из ам. серверов). Использует **те же** скрипты и тот же `ru-servers.json`, что и бот.
|
||||
|
||||
## Возможности
|
||||
|
||||
- **Дашборд** — состояние туннелей всех ам. серверов: куда подключены, возраст handshake
|
||||
- **Force-переключение** на любой RU-сервер одной кнопкой
|
||||
- **CRUD RU-серверов** — добавление/удаление с веб-формы (бот сам пробрасывает SSH ключи и поднимает WG)
|
||||
- **CRUD ам. серверов** — то же
|
||||
- **CRUD доменов** — добавить/удалить с автоматическим резолвом
|
||||
- **CRUD IP/CIDR** — добавить/удалить любые подсети
|
||||
- **Просмотр базовых подсетей** — read-only
|
||||
- **Фильтры по доменам и IP** — для удобства поиска
|
||||
|
||||
## API
|
||||
|
||||
Все endpoint'ы под `/api/`, все требуют HTTP basic auth.
|
||||
|
||||
| Метод | Путь | Тело | Описание |
|
||||
|---|---|---|---|
|
||||
| GET | `/api/state` | — | Полное состояние JSON |
|
||||
| POST | `/api/use` | `{"id":"primary"}` | Force-переключить все ам. на этот RU |
|
||||
| POST | `/api/server` | `{host,id,user,ssh_port,priority,...}` | Добавить новый RU |
|
||||
| DELETE | `/api/server/<id>` | — | Удалить RU |
|
||||
| POST | `/api/ams` | `{host,id,user,ssh_port,xray_iface,...}` | Добавить новый ам. |
|
||||
| DELETE | `/api/ams/<id>` | — | Удалить ам. |
|
||||
| POST | `/api/domains` | `{"domains":["vk.com",...]}` | Добавить домены |
|
||||
| DELETE | `/api/domains` | `{"domains":[...]}` | Удалить домены |
|
||||
| POST | `/api/domains/refresh` | — | Перерезолвить |
|
||||
| POST | `/api/ips` | `{"ips":["1.2.3.4/32",...]}` или `{"ips":"text with IPs"}` | Добавить |
|
||||
| DELETE | `/api/ips` | то же | Удалить |
|
||||
| POST | `/api/ips/clear` | — | Очистить все доп. IP |
|
||||
|
||||
Пример:
|
||||
```bash
|
||||
curl -u admin:PASS https://your-host/api/state | jq
|
||||
curl -u admin:PASS -X POST https://your-host/api/use -d '{"id":"primary"}' -H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
## Безопасность
|
||||
|
||||
- Basic auth обязателен. `KASKAD_WEB_PASS` должен быть длинным и случайным (см. `webui.env.example`)
|
||||
- По умолчанию слушает на `0.0.0.0:8088`. **Рекомендуется** поставить за HTTPS reverse-proxy (nginx/caddy) с Let's Encrypt
|
||||
- Если хочется привязать только к localhost — `KASKAD_HOST=127.0.0.1` и пользоваться через SSH-туннель: `ssh -L 8088:localhost:8088 root@ams1`
|
||||
- Пароли SSH (`password=` при `/server-add`, `/ams-add`) передаются по HTTPS только если веб за reverse-proxy. Без HTTPS не передавай пароли через WebUI — используй ключи (см. `/bot-key`)
|
||||
|
||||
## Конфиг (env-переменные)
|
||||
|
||||
| Переменная | Дефолт | Описание |
|
||||
|---|---|---|
|
||||
| `KASKAD_WEB_USER` | `admin` | Логин для basic auth |
|
||||
| `KASKAD_WEB_PASS` | (нет) | Пароль; ОБЯЗАТЕЛЬНО задать |
|
||||
| `LOCAL_HOST` | `ams1` | Имя локального ам. сервера |
|
||||
| `LOCAL_IP` | `127.0.0.1` | Локальный IP — определяет, какой ам. читать локально без SSH |
|
||||
| `KASKAD_HOST` | `0.0.0.0` | Адрес для bind |
|
||||
| `KASKAD_PORT` | `8088` | Порт |
|
||||
| `KASKAD_SSH_KEY` | `/root/.ssh/id_ed25519` | SSH-ключ для управления другими серверами |
|
||||
| `KASKAD_SERVERS_JSON` | `/etc/wireguard/ru-servers.json` | Путь к конфигу |
|
||||
|
||||
## nginx reverse-proxy (пример)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name kaskad.example.com;
|
||||
ssl_certificate /etc/letsencrypt/live/kaskad.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/kaskad.example.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8088;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 600s; # для долгих /add-domain пачкой
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
И в WebUI поставить `KASKAD_HOST=127.0.0.1`, не открывать 8088 наружу.
|
||||
Reference in New Issue
Block a user