- Роль: defaults, tasks (main/deploy-host/deploy-k8s), handlers, templates (sing-box config, iptables setup/teardown, systemd, K8s DaemonSet/ConfigMap/Secret) - Режимы: systemd (host) и K8s DaemonSet с hostNetwork + privileged init-container - Маршрутизация: YouTube/Google → Hysteria2, RU (geoip/geosite) → прямой, остальное → прямой - DNS без утечек: protocol=dns перехватывается TPROXY, per-domain DNS серверы - Интеграция: inventory [splitgw], addons.yml flag, Makefile target, playbooks/addons.yml - Документация: README.md, docs/addons.md, README.md (счётчик 36)
17 KiB
Split Gateway
Прозрачный split-tunnel шлюз для домашней сети. Трафик Android TV (или любого другого устройства) автоматически разделяется на два потока — без какой-либо настройки на самом устройстве.
Архитектура
Android TV
│
│ (любой трафик — TV ничего не знает о прокси)
▼
Keenetic (policy route: from TV_IP → gateway node)
│
▼
K3S нода / Linux хост (gateway node)
│
│ iptables TPROXY → sing-box :7893
▼
┌──────────────────────────────────────────────────┐
│ sing-box │
│ │
│ DNS: YouTube домены → 8.8.8.8 via Hysteria2 │
│ RU домены → 192.168.1.1 (роутер) │
│ │
│ Routing: │
│ geosite:youtube ──► Hysteria2 ──► VPS ──► 🌍 │
│ geoip:ru + .ru ──► direct ──► 🇷🇺 │
│ всё остальное ──► direct │
└──────────────────────────────────────────────────┘
Ключевые свойства:
- Прозрачный TPROXY — TV ничего не настраивается
- YouTube (TCP + UDP/QUIC) — через Hysteria2
- RU сервисы (Plex, Кинопоиск, Яндекс, ВК) — прямой маршрут
- DNS без утечек — раздельные DNS серверы по типу домена
- geoip + geosite БД — автообновление каждые 7 дней
Компоненты
| Компонент | Роль |
|---|---|
| sing-box | Routing engine: TPROXY inbound + split routing |
| Hysteria2 outbound | Встроен в sing-box — отдельный процесс не нужен |
| iptables TPROXY | Перехват трафика от TV на порт sing-box |
| splitgw-rules.service | systemd: применяет iptables при старте |
| singbox.service | systemd: запускает sing-box |
Установка
Шаг 1 — Добавить узел-шлюз в inventory
# inventory/hosts.ini
[splitgw]
# Вариант А: отдельный Linux хост как шлюз
gateway ansible_host=192.168.1.10 ansible_user=root
# Вариант Б: K3S нода как шлюз (та же нода что и master01)
# gateway ansible_host=192.168.1.10 ansible_user=devops
Шаг 2 — Настроить переменные
# group_vars/all/main.yml или host_vars/gateway/main.yml
splitgw_tv_sources:
- "192.168.1.100/32" # IP Android TV (узнать: Keenetic → Устройства)
splitgw_router_ip: "192.168.1.1" # IP роутера Keenetic
Шаг 3 — Добавить Hysteria2 в vault
Если уже используешь mediaserver аддон — vault уже настроен, перейди к шагу 4.
make vault-edit
# group_vars/all/vault.yml
# Способ 1 — URL из Shadowrocket/NekoBox:
vault_hysteria2_url: "hysteria2://mypassword@vps.example.com:443?insecure=1"
# Способ 2 — по отдельности:
vault_hysteria2_server: "vps.example.com:443"
vault_hysteria2_auth: "mypassword"
Шаг 4 — Деплой
# С SSH-ключом:
make addon-splitgw
# С SSH-паролем:
make addon-splitgw ARGS="-k"
# С SSH + sudo паролем:
make addon-splitgw ARGS="-k -K"
Шаг 5 — Настроить Keenetic
Смотри раздел Настройка Keenetic.
Настройка Keenetic
Keenetic выполняет policy-based routing: трафик от TV направляется на узел-шлюз вместо стандартного маршрута.
В веб-интерфейсе Keenetic
1. Узнать IP Android TV
Мои сети и Wi-Fi → список подключённых устройств → найти TV → скопировать IP.
Зафиксировать IP (чтобы не менялся):
Мои сети и Wi-Fi → устройство → Постоянный IP-адрес → задать статический IP.
2. Создать политику маршрутизации
Интернет → Другие подключения → Маршруты → Добавить маршрут.
Или через Управление → Политика маршрутизации (зависит от версии прошивки).
В KeeneticOS 4.x: Интернет → Доступ в интернет → Политика маршрутизации → +.
3. Параметры правила
Тип: Маршрут источника (source-based routing)
Источник: 192.168.1.100 ← IP Android TV
Через: 192.168.1.10 ← IP узла-шлюза (gateway node)
Метрика: 10 (ниже = приоритетнее)
4. Через CLI Keenetic (точнее)
Подключись к Keenetic по SSH:
# Добавить статический маршрут для TV через шлюз:
ip route 192.168.1.100/32 192.168.1.10 auto
# Или через policy routing (если поддерживается):
ip policy-route source 192.168.1.100 nexthop 192.168.1.10
Сохранить: system configuration save
5. Альтернатива — DHCP опция Gateway
Если TV получает IP по DHCP — настроить на роутере выдачу специфического шлюза для TV:
Мои сети и Wi-Fi → Домашняя сеть → Настройки DHCP → найти устройство → Шлюз: 192.168.1.10
Это заставит TV слать трафик через 192.168.1.10 (gateway node) вместо роутера.
Полные конфигурационные файлы
sing-box config (config.json)
Генерируется автоматически шаблоном. Полная структура:
{
"log": { "level": "info", "timestamp": true },
"dns": {
"servers": [
{ "tag": "dns-proxy", "address": "tls://8.8.8.8", "detour": "proxy" },
{ "tag": "dns-direct", "address": "udp://8.8.4.4", "detour": "direct" },
{ "tag": "dns-ru", "address": "udp://192.168.1.1", "detour": "direct" }
],
"rules": [
{ "outbound": "any", "server": "dns-direct" },
{ "rule_set": ["geosite-youtube", "geosite-google"], "server": "dns-proxy" },
{ "rule_set": ["geosite-category-ru"], "server": "dns-ru" },
{ "domain_suffix": [".ru", ".рф", ".su"], "server": "dns-ru" }
],
"final": "dns-direct",
"independent_cache": true,
"reverse_mapping": true
},
"inbounds": [
{
"type": "tproxy",
"tag": "tproxy-in",
"listen": "::",
"listen_port": 7893,
"tcp_fast_open": true,
"udp_fragment": true,
"sniff": true,
"sniff_override_destination": true,
"domain_strategy": "prefer_ipv4",
"udp_timeout": "5m"
}
],
"outbounds": [
{
"type": "hysteria2",
"tag": "proxy",
"server": "vps.example.com",
"server_port": 443,
"password": "YOUR_PASSWORD",
"obfs": { "type": "salamander", "salamander": { "password": "OBFS" } },
"tls": { "enabled": true, "insecure": false }
},
{ "type": "direct", "tag": "direct" },
{ "type": "block", "tag": "block" },
{ "type": "dns", "tag": "dns-out" }
],
"route": {
"rule_set": [
{ "tag": "geosite-youtube", "type": "remote", "format": "binary",
"url": "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite-youtube.srs" },
{ "tag": "geosite-google", "type": "remote", "format": "binary",
"url": "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite-google.srs" },
{ "tag": "geosite-category-ru", "type": "remote", "format": "binary",
"url": "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite-category-ru.srs" },
{ "tag": "geoip-ru", "type": "remote", "format": "binary",
"url": "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip-ru.srs" }
],
"rules": [
{ "protocol": "dns", "outbound": "dns-out" },
{ "ip_is_private": true, "outbound": "direct" },
{
"type": "logical", "mode": "or",
"rules": [
{ "rule_set": ["geosite-youtube"] },
{ "domain_keyword": ["youtube", "googlevideo", "ytimg", "ggpht"] }
],
"outbound": "proxy"
},
{
"type": "logical", "mode": "or",
"rules": [
{ "rule_set": ["geosite-category-ru", "geoip-ru"] },
{ "domain_suffix": [".ru", ".рф", ".su"] },
{ "domain_keyword": ["yandex", "kinopoisk", "vk.com"] }
],
"outbound": "direct"
}
],
"final": "direct",
"auto_detect_interface": true
}
}
iptables TPROXY правила
# Таблица маршрутизации: помеченные пакеты → локальные
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
# Цепочка SPLITGW
iptables -t mangle -N SPLITGW
iptables -t mangle -A SPLITGW -i lo -j RETURN
iptables -t mangle -A SPLITGW -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
iptables -t mangle -A SPLITGW -d 127.0.0.0/8 -j RETURN
iptables -t mangle -A SPLITGW -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A SPLITGW -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A SPLITGW -d 192.168.0.0/16 -j RETURN
iptables -t mangle -A SPLITGW -p tcp -j TPROXY --on-port 7893 --tproxy-mark 0x1/0x1
iptables -t mangle -A SPLITGW -p udp -j TPROXY --on-port 7893 --tproxy-mark 0x1/0x1
# Применить к трафику от TV
iptables -t mangle -A PREROUTING -s 192.168.1.100/32 -j SPLITGW
Kubernetes DaemonSet (альтернатива systemd)
Используй когда gateway node — нода K3S кластера.
# 1. Пометить ноду
kubectl label node <gateway-node> splitgw=true
# 2. Деплой
make addon-splitgw ARGS="-e splitgw_deploy_mode=k8s"
DaemonSet запускает sing-box с hostNetwork: true. Init-контейнер (privileged) применяет iptables правила на хосте. Конфиг — в ConfigMap.
Параметры
# group_vars/all/main.yml
# TV устройства (один или несколько IP/подсетей)
splitgw_tv_sources:
- "192.168.1.100/32" # Android TV
- "192.168.1.101/32" # SmartTV на кухне
splitgw_router_ip: "192.168.1.1" # Keenetic (для RU DNS)
splitgw_tproxy_port: 7893 # порт sing-box
splitgw_singbox_log_level: "info" # trace|debug|info|warn|error
# Hysteria2 (из vault — те же что используются в mediaserver)
# vault_hysteria2_url: "hysteria2://..." ← уже в vault
# Режим деплоя
splitgw_deploy_mode: "host" # host | k8s
Проверка работы
1. Статус сервисов
# На gateway node:
systemctl status singbox
systemctl status splitgw-rules
# Логи sing-box в реальном времени:
journalctl -u singbox -f
# Проверить конфиг:
sing-box check --config /etc/sing-box/config.json
2. iptables правила
# Посмотреть цепочку SPLITGW:
iptables -t mangle -L SPLITGW -v -n
# Посмотреть rule для fwmark:
ip rule list | grep 0x1
# Посмотреть таблицу 100:
ip route show table 100
3. Трафик с TV
# На gateway node — смотреть трафик с TV:
tcpdump -i any -nn "src host 192.168.1.100" -c 20
# Проверить что TPROXY работает (пакеты приходят в sing-box):
ss -tnlp | grep 7893
# Реальный тест: с Android TV открыть YouTube, затем:
conntrack -L -s 192.168.1.100 | grep ESTABLISHED | head -20
4. Проверка маршрутов
# Проверить что YouTube идёт через прокси:
# Подключиться к TV по ADB (Android Debug Bridge):
adb connect 192.168.1.100
adb shell curl -s https://ifconfig.me # должен вернуть IP VPS
# Проверить что ВКонтакте идёт напрямую:
adb shell curl -s https://api.vk.com # должен работать с RU IP
5. DNS проверка
# Проверить что DNS для YouTube резолвится через прокси:
# На gateway node:
dig youtube.com @8.8.8.8 +short
# Должен вернуть зарубежный IP (не российский CDN)
# Лог DNS запросов в sing-box (включить debug):
sing-box run --config /etc/sing-box/config.json 2>&1 | grep -i dns
Диагностика проблем
YouTube не работает через прокси
# 1. Проверить что sing-box запущен:
systemctl status singbox
# 2. Проверить iptables:
iptables -t mangle -L PREROUTING -v -n | grep SPLITGW
# 3. Проверить подключение к Hysteria2 серверу:
sing-box run --config /etc/sing-box/config.json 2>&1 | grep -E "(hysteria|proxy|connect)"
# 4. Включить debug логи временно:
sed -i 's/"level": "info"/"level": "debug"/' /etc/sing-box/config.json
systemctl restart singbox
journalctl -u singbox -f | grep -i youtube
RU сервисы идут через прокси (не должны)
# Проверить что geoip-ru БД загружена:
ls -la /var/lib/sing-box/*.srs
# Перезапустить sing-box (загружает БД при старте):
systemctl restart singbox
# Проверить routing rule для yandex.ru:
sing-box run --config /etc/sing-box/config.json 2>&1 | grep yandex
TV не видит интернет
# 1. Включить IP forwarding на gateway:
sysctl net.ipv4.ip_forward # должно быть 1
# 2. Проверить что TV трафик доходит:
tcpdump -i any -nn "src host TV_IP"
# 3. Проверить маршрут на шлюзе:
ip route get 8.8.8.8 from TV_IP # должен показать интерфейс
# 4. Проверить что sing-box отвечает:
curl -x socks5h://127.0.0.1:1080 https://ifconfig.me # если включён SOCKS5
Keenetic не маршрутизирует трафик через шлюз
# С самого TV (если есть ADB):
adb shell ip route # проверить default gateway
# На роутере через CLI:
show ip route
# Должен быть маршрут: 192.168.1.100/32 via 192.168.1.10
# Попробовать ping от роутера до шлюза:
ping 192.168.1.10
Мониторинг
Логи (по умолчанию info)
# Текущие логи:
journalctl -u singbox -n 100
# Режим слежения:
journalctl -u singbox -f
# Фильтр ошибок:
journalctl -u singbox | grep -E "(ERR|WARN)"
Статистика трафика
# Счётчики iptables цепочки SPLITGW:
iptables -t mangle -L SPLITGW -v -n
# Активные соединения через sing-box:
cat /proc/net/nf_conntrack | grep "src=192.168.1.100" | wc -l
Prometheus (если установлен prometheus-stack)
sing-box не экспортирует метрики нативно. Для мониторинга используй:
# Prometheus node-exporter метрики iptables (через iptables-exporter или вручную):
# В ServiceMonitor добавить textfile collector с iptables счётчиками
Обновление
# Обновить sing-box до последней версии:
make addon-splitgw
# Обновить только конфиг (изменил переменные):
make addon-splitgw ARGS="--tags config"
# Добавить новый TV:
# group_vars/all/main.yml:
# splitgw_tv_sources:
# - "192.168.1.100/32"
# - "192.168.1.105/32" ← новый TV
make addon-splitgw
Деинсталляция
# На gateway node:
systemctl disable --now singbox splitgw-rules
rm -f /etc/systemd/system/{singbox,splitgw-rules}.service
rm -rf /etc/sing-box /var/lib/sing-box /var/log/sing-box
rm -f /usr/local/bin/sing-box
systemctl daemon-reload
# Удалить iptables правила:
iptables -t mangle -D PREROUTING -s 192.168.1.100/32 -j SPLITGW
iptables -t mangle -F SPLITGW
iptables -t mangle -X SPLITGW
ip rule del fwmark 0x1 lookup 100
ip route flush table 100