# 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 ```ini # 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 — Настроить переменные ```yaml # 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. ```bash make vault-edit ``` ```yaml # 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 — Деплой ```bash # С SSH-ключом: make addon-splitgw # С SSH-паролем: make addon-splitgw ARGS="-k" # С SSH + sudo паролем: make addon-splitgw ARGS="-k -K" ``` ### Шаг 5 — Настроить Keenetic Смотри раздел [Настройка 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: ```bash # Добавить статический маршрут для 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) Генерируется автоматически шаблоном. Полная структура: ```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 правила ```bash # Таблица маршрутизации: помеченные пакеты → локальные 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 кластера. ```bash # 1. Пометить ноду kubectl label node splitgw=true # 2. Деплой make addon-splitgw ARGS="-e splitgw_deploy_mode=k8s" ``` DaemonSet запускает sing-box с `hostNetwork: true`. Init-контейнер (privileged) применяет iptables правила на хосте. Конфиг — в ConfigMap. --- ## Параметры ```yaml # 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. Статус сервисов ```bash # На 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 правила ```bash # Посмотреть цепочку SPLITGW: iptables -t mangle -L SPLITGW -v -n # Посмотреть rule для fwmark: ip rule list | grep 0x1 # Посмотреть таблицу 100: ip route show table 100 ``` ### 3. Трафик с TV ```bash # На 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. Проверка маршрутов ```bash # Проверить что 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 проверка ```bash # Проверить что 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 не работает через прокси ```bash # 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 сервисы идут через прокси (не должны) ```bash # Проверить что 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 не видит интернет ```bash # 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 не маршрутизирует трафик через шлюз ```bash # С самого 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) ```bash # Текущие логи: journalctl -u singbox -n 100 # Режим слежения: journalctl -u singbox -f # Фильтр ошибок: journalctl -u singbox | grep -E "(ERR|WARN)" ``` ### Статистика трафика ```bash # Счётчики 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 не экспортирует метрики нативно. Для мониторинга используй: ```yaml # Prometheus node-exporter метрики iptables (через iptables-exporter или вручную): # В ServiceMonitor добавить textfile collector с iptables счётчиками ``` --- ## Обновление ```bash # Обновить 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 ``` --- ## Деинсталляция ```bash # На 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 ``` ## Официальные ресурсы - Официальный сайт: [https://sing-box.sagernet.org/](https://sing-box.sagernet.org/) - Официальная документация: [https://sing-box.sagernet.org/configuration/](https://sing-box.sagernet.org/configuration/) - Версии Helm chart / ПО: [https://github.com/SagerNet/sing-box/releases](https://github.com/SagerNet/sing-box/releases)