Files
K3S/addons/splitgw/README.md
Sergey Antropoff 38aaadbfb1 docs: sync addon docs with explicit external/internal service modes
Обновлена документация под новые аддоны (gitlab, redis, mongodb, kafka, kafka-ui, rabbitmq) и новую модель явного выбора зависимостей. Добавлены и унифицированы описания переключателей *_database_mode и *_redis_mode, обновлена таблица зависимостей аддонов, примеры конфигурации и список vault-секретов.
2026-04-29 23:21:04 +03:00

18 KiB
Raw Permalink Blame History

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

Официальные ресурсы