Files
obsidian-vault/personal/tech/xray-reverse-tunnel-kraken-truenas.md
T

26 KiB
Raw Blame History

title, created, updated, type, namespace, tags, confidence, related
title created updated type namespace tags confidence related
Xray Reverse Tunnel — Kraken ↔ TrueNAS 2026-09-01 2026-09-01 tech personal
xray
reverse
kraken
truenas
tunnel
networking
medium
family/how-to/vps-qentra
family/how-to/truenas-infrastructure
family/how-to/kraken-access
family/how-to/rasputin-router

Xray Reverse Tunnel — Kraken ↔ TrueNAS

Статус: КОРНЕВАЯ ПРИЧИНА НАЙДЕНА + ФИКС ПРИМЕНЁН, но блокируется недоступностью Kraken (2026-09-01). 3x-ui развёрнут и работает. reverse portal (TrueNAS) + bridge (Kraken) на прямом TCP+REALITY, reverse-канал установлен. 🔑 Найден корень проблемы payload через интернет-поиск (issue XTLS/Xray-core #6612): с v26.5+ у freedom/direct дефолтная политика блокирует VLESS Reverse payload, пока не задан явный finalRules: [{"action":"allow"}]. Фикс залит (новый bridge-конфиг с finalRules на Kraken, /home/kraken/xray-reverse/config.json). БЛОКЕР: Kraken недоступен по docker — USB-HDD sda показывает 0B (не поднялся), docker демон застрял в activating, bridge-контейнер пересоздать невозможно. Задание НЕ завершено — нужен фикс HDD/docker на Kraken. Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через TrueNAS → Kraken → интернет.

Контекст / Почему

  • VPS qentra.top (91.207.28.205) УДАЛЁН — вся прежняя Xray-инфраструктура на нём (VLESS+REALITY, OpenVPN, WebSocket v.qentra.top) больше не существует.
  • Нужен новый путь для выхода локальных клиентов сети TrueNAS в интернет через другую точку выхода (Kraken).
  • Белый IP есть только у TrueNAS (mallexxx.duckdns.org = 90.189.160.148).
  • Kraken за NAT, входящие не принимает — только исходящие соединения.

Принятая схема

Локальные клиенты (сеть TrueNAS, 192.168.2.x)
   │  шлют трафик на локальный прокси TrueNAS (SOCKS 1080 / HTTP 1081)
   ▼
TrueNAS  ←── контроллер / bridge (белый IP, mallexxx.duckdns.org), принимает
   │  TrueNAS заворачивает трафик в reverse-канал
   ▼  ▲
Kraken ──┘ держит ИСХОДЯЩИЙ reverse-канал к TrueNAS, отпускает трафик → интернет
   ▼
интернет

Тип решения: Xray reverse. Роли по Xray:

Узел Роль Держит канал? Выход в интернет?
TrueNAS bridge/контроллер (inbound reverse) слушает
Kraken outbound reverse-нода + exit исходящий к TrueNAS да
Локальные клиенты используют TrueNAS как локальный прокси-шлюз через Kraken

Ключевое физическое ограничение: Kraken за NAT не может принимать входящих. Поэтому канал держит Kraken исходящим к TrueNAS. Для передачи клиентского трафика TrueNAS → Kraken используются конструкции dokodemo-door + reverse + policy-маршрутизация (routing rules). Это чуть сложнее "поставить reverse и всё", конфиг нужен аккуратный.

⚠️ Факты, проверенные 2026-09-01 (важно, ломают прежние допущения)

  1. На 443 у TrueNAS НЕ Xray. Проверено openssl s_client: на mallexxx.duckdns.org:443 отвечает обычный TLS 1.3 с валидным Let's Encrypt сертификатом для mallexxx.duckdns.org (issuer=Let's Encrypt/CN=YE2). Это Caddy/nginx reverse-proxy, НЕ Xray REALITY (у REALITY сертификат был бы чужой/поддельный). → Твоя память «на 443 висел xray server» не подтверждается.
  2. vless-proxy на TrueNAS (контейнер teddysun/xray:latest, Up 7 дней):
    • inbound: SOCKS 0.0.0.0:1080, HTTP 0.0.0.0:1081 (локально в LAN TrueNAS)
    • outbound: VLESS+WS+TLS на v.qentra.top:443, path /qentra, id 2D9F24C4-21FE-4784-9843-F11C384DA67Aуказывает на УДАЛЁННЫЙ VPS → сейчас мёртвый.
    • ⚠️ Используется Hermes-Taiga как SOCKS5-прокси (vless-proxy:1080) → Hermes-Taiga сейчас без рабочего прокси.
  3. Kraken→TrueNAS по mallexxx.duckdns.org: открыты только 22 (SSH) и 443 (HTTPS/TLS). 8443/8964/8080/9000 — closed. Порт 443 — TLS (Caddy), не сырой.
  4. Kraken: Docker установлен (/usr/bin/docker), но демон отвечает медленно/рвёт SSH (проверка docker ps не завершилась за 40s). Xray на Kraken отсутствует (which xray пусто) — нужен Docker-контейнер.
  5. Связанность Kraken↔TrueNAS по 22/443 — подтверждена (оба OPEN с Kraken).

ВНЕДРЕНО 2026-09-01: 3x-ui на TrueNAS (фронтенд + Docker Compose)

Решение Alex: использовать 3x-ui frontend + Docker Compose (управление клиентами через панель), а не ручной Caddy-pass-through с сырым Xray. Это изменило исходный план реализации (Шаг 1 ниже устарел по способу).

Развёрнут контейнер xray-admin (TrueNAS)

  • Имя контейнера: xray-admin (строго, т.к. Caddyfile резолвит xray-admin по имени в docker-сети).
  • Образ: ghcr.io/mhsanaei/3x-ui:latest (3.7.0, Xray 26.7.28).
  • Сеть: caddy_default (external) — там же Caddy, чтобы резолвить имя.
  • Volume: /mnt/RED_2TB/docker/xray-admin:/etc/x-ui (туда лёг существующий x-ui.db).
  • Порт панели: хостовый 54321 → 54321 (внутри панель реально слушает 2053 — Caddy проксирует vpn-panel.mallexxx.duckdns.orgxray-admin:2053).
  • Compose-файл: /mnt/RED_2TB/docker/xray-admin/docker-compose.yml (deployed версия с TZ=Asia/Novosibirsk, PUID/PGID=950; см. актуальный файл на TrueNAS — отличается от черновика в плане).
  • Панель доступна: https://vpn-panel.mallexxx.duckdns.org/HTTP 200.
  • Sub-сервер: [::]:443 (path /sub), панель [::]:2053, inbound vless-ws:10095 (path /vless, host vpn.mallexxx.duckdns.org).

Почему это заработало «из коробки» (важно)

База /mnt/RED_2TB/docker/xray-admin/x-ui.db уже была настроена под эту Caddy-интеграцию (раньше 3x-ui уже задумывался на TrueNAS): inbound vless-ws port 10095 tag in-10095-tcp, subDomain vpn-panel.mallexxx.duckdns.org, subPort 443, subScheme https. Caddyfile уже содержал (мёртвые до подъёма) блоки:

  • vpn.mallexxx.duckdns.org@ws path /vlessxray-admin:10095 (WS)
  • vpn-panel.mallexxx.duckdns.org@sub path /sub/*xray-admin:443 + fallback xray-admin:2053
  • LE-сертификаты для этих поддоменов уже выданы.

3x-ui поддерживает reverse

В бинарнике /app/x-ui присутствует clientReverseTags → reverse-настройка реализуема через панель 3x-ui (3.7.0).

Бэкап (сделан до изменений)

/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/ — содержит: caddy/Caddyfile (копия из контейнера, 90 строк), xray-admin/x-ui.db, docker-inventory.txt.

Мелочь/питфолл

Скопированный в volume docker-compose.yml оказался внутри контейнера /etc/x-ui/ (т.к. volume смонтирован туда) — удалить лишний файл из контейнера: docker exec xray-admin rm -f /etc/x-ui/docker-compose.yml.

ВНЕДРЕНО 2026-09-01: Reverse portal (TrueNAS) + bridge (Kraken) — VLESS-WS

После 3x-ui развёрнут полноценный reverse. Ключевое открытие: Xray 26.x заменил legacy reverse на "VLESS Reverse Proxy" — старая схема reverse.bridges/portals (Xray-examples/ReverseProxy) не работает в этой версии (ошибка The feature "legacy reverse" has been removed and migrated to "VLESS Reverse Proxy"). Новая схема: reverse.tag в VLESS-клиентах/аутбаундах (примеры — xtls.github.io/en/document/level-2/vless_reverse.html, use case "Home Broadband Egress").

Роли (новая терминология VLESS Reverse)

  • Internal device (Kraken) = BRIDGE: VLESS-outbound с "reverse":{"tag":"reverse-in"} → активно устанавливает канал к TrueNAS; на своей стороне создаётся виртуальный inbound reverse-in, чей трафик направляется в freedom (интернет).
  • Public server (TrueNAS) = PORTAL: VLESS-inbound с "reverse":{"tag":"reverse-out"} у клиента → создаёт routable-маутбаунд reverse-out; локальные клиенты (SOCKS) маршрутизируются в reverse-out.
  • reverse.tag на двух сторонах не обязаны совпадать — соответствие через общий UUID соединения.

TrueNAS — xray-reverse-portal (папка /mnt/RED_2TB/docker/reverse-portal/)

compose: image teddysun/xray:latest (26.7.28), сеть caddy_default, volume config.json:/etc/xray/config.json:ro, проброс host 12345:12345. config.json:

  • inbound interconn (:12346, VLESS, ws path /rvs, host vpn.mallexxx.duckdns.org, client id a81c3179-312b-4c5e-8a46-6bf3c30c2941, "reverse":{"tag":"reverse-out"})
  • inbound local (:12345, SOCKS noauth udp:true) — точка входа для клиентов сети TrueNAS
  • routing: inboundTag:["local"] → outboundTag:"reverse-out"
  • outbounds: freedom (placeholder — обязателен, иначе reverse-out станет default и весь трафик уйдёт в reverse)
  • Порт 12346 НЕ проброшен наружу — к нему обращается Caddy по имени xray-reverse-portal:12346 в docker-сети.

TrueNAS — Caddyfile (bind mount /mnt/RED_2TB/docker/caddy/Caddyfile)

Добавлен маршрут в блок vpn.mallexxx.duckdns.org:

@rvs path /rvs
reverse_proxy @rvs xray-reverse-portal:12346

Питфоллы: Caddyfile нельзя править docker cp в контейнер (volume → device or resource busy) — править на хосте через docker run --rm -v /:/host alpine cp /host/tmp/.... Валидация docker exec caddy caddy validate --config /tmp/Caddyfile.new → "Valid configuration". Перезапуск docker restart caddy безопасен. Бэкап: Caddyfile.pre-reverse.

Kraken — xray-reverse-bridge (папка /home/kraken/xray-reverse/)

compose: image teddysun/xray:latest, volume config.json:/etc/xray/config.json:ro. config.json:

  • outbound direct = freedom + "finalRules":[{"action":"allow","network":"tcp,udp","ip":["0.0.0.0/0"]}] (интернет-egress Kraken)
  • outbound conn = VLESS упрощённого стиля (settings.address/port/id/encryption/reverse — НЕ vnext[]!) → vpn.mallexxx.duckdns.org:443 ws /rvs "reverse":{"tag":"reverse-in"}, security tls, tlsSettings.serverName vpn.mallexxx.duckdns.org
  • routing: inboundTag:["reverse-in"] → outboundTag:"direct"
  • Питфолл: VLESS-outbound с reverse должен быть упрощённого стиля. Если через vnext[]/users[] → Xray 26.x: VLESS users: please use simplified outbound's config style to use "reverse".
  • loglevel debug (для диагностики).

Reverse-канал: установлен

  • Kraken netstat: 172.24.0.2:... → 90.189.160.148:443 ESTABLISHED
  • Portal: ::ffff:172.16.1.7:12346 ::ffff:172.16.1.3:52616 ESTABLISHED (172.16.1.3 = Caddy)
  • Bridge logs: common/mux: received request for udp:reverse:0

End-to-end payload НЕ работает

  • curl --socks5 127.0.0.1:12345 http://api.ipify.org на TrueNAS → code=000 (прямой curl → 200).
  • Portal логи: accepted tcp:8.47.69.0:80 [local -> reverse-out] — запрос ушёл в reverse-out.
  • Bridge НЕ логирует received для этого TCP-payload — данные теряются между reverse-out (portal) и reverse-in (bridge).
  • Kraken имеет прямой интернет (api.ipify92.62.70.41), проблема не в egress.

ВНЕДРЕНО (2026-09-01) — TCP+REALITY переход, все шаги выполнены

WS-транспорт не пропускал reverse-payload; переведён на прямой TCP + REALITY (Alex согласовал проброс). Все 4 шага ВЫПОЛНЕНЫ:

  1. Пересоздан xray-reverse-portal на TrueNAS (compose 12345:12345 + 12346:12346, config REALITY TCP). Проверено ss -tlnp0.0.0.0:12346 LISTEN.
  2. OpenWrt DNAT добавлен: redirect xray-reverse-12346, src_dport=12346dest_ip=192.168.2.197:12346, target=DNAT, /etc/init.d/firewall reloadDNAT_ADDED. Проверено: mallexxx.duckdns.org:12346OPEN извне.
  3. Пересоздан xray-reverse-bridge на Kraken (config REALITY TCP). Reverse-канал ESTABLISHED: Kraken 172.24.0.2 → 90.189.160.148:12346 + common/mux: received request for udp:reverse:0.
  4. Тест payload НЕ прошёл даже на TCP+REALITY: curl --socks5 127.0.0.1:12345 http://api.ipify.orgcode=000. Portal: accepted tcp:8.6.112.0:80 [local -> reverse-out], но bridge НЕ логирует приход TCP-payload.

Дополнительно пробовано: снятие flow: xtls-rprx-vision

Убрал flow: xtls-rprx-vision с обеих сторон (portal interconn client + bridge conn outbound), пересоздал контейнеры (оба Configuration OK). Результат: по-прежнему code=000. Reverse-канал устанавливается, TCP-payload до bridge не доходит.

Вывод: проблема НЕ в flow и НЕ в WS. Даже на «каноничном» TCP+REALITY payload от portal-reverse-out до bridge-reverse-in не передаётся (bridge не получает inbound-TCP). Гипотеза про WS опровергнута.

🔑 КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН (2026-09-01, интернет-поиск) — issue XTLS/Xray-core #6612

Симптомы точно совпадают с #6612: reverse-канал устанавливается (udp:reverse:0 на месте), но TCP-payload не проходит; portal логирует accepted tcp:... -> reverse-out, а bridge НЕ получает входящих reverse-in-соединений; curl → 000.

Причина: начиная с Xray v26.5+ (PR #6027) у outbound freedom/direct появилась дефолтная политика безопасности, которая блокирует ВСЕ цели для трафика VLESS Reverse, если не задан явный finalRules. Контрольный reverse-канал при этом продолжает устанавливаться — поэтому «всё выглядит подключённым», но реальный payload молча отбрасывается. Это ровно наш случай на версии 26.7.28.

Точный фикс — добавить в freedom direct на BRIDGE (место реального egress из reverse-in в интернет):

{
  "protocol": "freedom",
  "tag": "direct",
  "settings": {
    "domainStrategy": "AsIs",
    "finalRules": [ { "action": "allow" } ]
  }
}

Issue говорит прямо: проблема воспроизводится с обычным Freedom без finalRules вообще, и добавление безусловного allow чинит на 26.7.28.

Релевантные ссылки:

  • #6612 — главный фикс: https://github.com/XTLS/Xray-core/issues/6612
  • #6027 — корень (finalRules у Direct/Freedom): https://github.com/XTLS/Xray-core/pull/6027
  • Документация freedom: https://xtls.github.io/en/config/outbounds/freedom.html
  • 3x-ui разбор под reverse: https://github.com/MHSanaei/3x-ui/issues/4782
  • #2664 — если finalRules не поможет: flow: xtls-rprx-vision на BRIDGE-outbound + REALITY может ломать доставку (держите flow:"" на reverse-клиенте): https://github.com/XTLS/Xray-core/issues/2664
  • Регрессия роутинга reverse v26.5+ (если фикс не поможет): #6242, #6195, #6248 — временный кварк: пинить v26.4.25.

Важные детали из поиска (проверить при продолжении):

  • Портал ДОЛЖЕН иметь placeholder-freedom (у нас есть) — иначе reverse-out станет default. Плейсхолдеру не нужен finalRules:allow (он обслуживает только несопоставленный трафик).
  • Роутинг inboundTag:["reverse-in"] -> direct на bridge — нужен (у нас есть).
  • У VLESS outbound поле encryption обязательно ("none" или парный PQ-ключ к серверному decryption) — у нас "none", ок.

ФИКС ПРИМЕНЁН, но заблокирован (2026-09-01)

Новый bridge-конфиг с finalRules:[{action:"allow"}] в direct залит на Kraken (/home/kraken/xray-reverse/config.json, 1098 байт, проверено cat | head -5). НО:

  • Kraken по docker НЕДОСТУПЕН: systemctl is-active dockeractivating (не active), демон застрял, накоплено множество зависших docker-процессов (ps/run/compose).
  • Причина: USB-HDD снова не поднялсяlsblk /dev/sda0B (без раздела). Docker data-root = /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker-data (на этом HDD) → daemon ждёт диск и застревает.
  • ⚠️ Замечено расхождение UUID: сейчас data-root указывает на /srv/dev-disk-by-uuid-49e8f586-..., а в контексте ранее фигурировал смонтированный HDD /srv/dev-disk-by-uuid-6194539b-... (sda1 1.8T). Текущий sda = 0B без раздела.
  • docker compose down/up для bridge не выполнен — контейнер всё ещё со старым конфигом (больше не деплоится, daemon не отвечает).

Шаг для продолжения (требует подтверждения Alex): починить HDD/docker на Kraken (повторить утренний сценарий: sudo reboot или физическое переподключение USB-диска), затем пересоздать bridge-контейнер (cd /home/kraken/xray-reverse && docker compose down && docker compose up -d), затем тест curl --socks5 127.0.0.1:12345 http://api.ipify.org на TrueNAS → ожидаем выход с IP Kraken.

Статус: задание НЕ завершено — остаётся применить bridge-фикс после восстановления docker на Kraken.

Открытые вопросы по reverse (требуют ответа/действия Alex)

План реализации (история; Шаг 1 устарел — см. «ВНЕДРЕНО» выше)

Шаг 1 — TrueNAS: Xray reverse-server поверх Caddy

  • Способ (рекомендован): Caddy TLS-pass-through. Добавить в Caddy TCP-правило: поддомен xray.mallexxx.duckdns.org:443 → внутренний порт Xray-контейнера (напр. localhost:8443). Caddy передаёт сырой TLS-стрим дальше. Xray на TrueNAS принимает VLESS+Reality/TLS. Kraken ходит на xray.mallexxx.duckdns.org:443.
    • Плюс: переиспользуем уже открытый/проброшенный 443 на роутере, не трогаем роутер, туннель маскируется под HTTPS.
    • Альтернатива: пробросить отдельный порт на роутере (напр. 8443) → требует доступа к роутеру. Менее предпочтительно.
  • Новый/доразвернутый Xray-контейнер:
    • inbound reverse/dokodemo-door на 8443 (извне по pass-through)
    • local inbound SOCKS/HTTP (уже есть 1080/1081)
    • routing: трафик клиентов → в reverse-канал к Kraken

Шаг 2 — TrueNAS: бэкап перед изменением

  • Снять копию текущего конфига vless-proxy (config.json), в /mnt/RED_2TB/docker/<app>/backup/ (паттерн как с cups-splix).

Шаг 3 — Kraken: Xray outbound reverse в Docker

  • Контейнер Xray на Kraken (docker run --restart unless-stopped, как остальные).
  • outbound vless → xray.mallexxx.duckdns.org:443 (через Caddy pass-through).
  • inbound SOCKS/HTTP на Kraken — точка выхода.
  • reverse-конфиг: Kraken регистрирует «сервис» (весь трафик через dokodemo-door) наружу через канал к TrueNAS.

Шаг 4 — Проверка связности

  • Kraken → xray.mallexxx.duckdns.org:443 (TCP).
  • Локальный клиент → TrueNAS:1080 → curl через прокси → должен выйти с IP Kraken.

Шаг 5 — Обновить Obsidian после внедрения

  • Дополнить family/how-to/truenas-infrastructure.md (Xray reverse-настройка), family/how-to/kraken-access.md; обновить personal/tech/vps-qentra.md (VPS удалён, ссылки на него).

Открытые вопросы (требуют ответа Alex)

  1. (решено) Проброс порта для reverse — переведён на TCP+REALITY, ВСЁ ВЫПОЛНЕНО (portal пересоздан с REALITY, OpenWrt DNAT внешний:12346 → 192.168.2.197:12346 добавлен, bridge на Kraken пересоздан). См. секции «ВНЕДРЕНО — TCP+REALITY» выше. Компанент работает, но payload НЕ проходит (см. пункт 4).
  2. Какой трафик TrueNAS пустить через Kraken — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
  3. Kraken docker — РАБОТАЕТ (после перезагрузки 2026-09-01): Server 29.4.3, Docker Root Dir /srv/dev-disk-by-uuid-6194539b-.../docker-data (актуальный UUID, HDD смонтирован), 30 образов сохранены, но 0 контейнеров (all контейнеры не восстановлены после сбоя). Reverse-bridge-контейнер развёрнут поверх. НО: известные контейнеры (jellyfin, ha, transmission и т.д.) требуется пересоздать — отдельная задача, не входила в reverse-сферу.
  4. 🔑 End-to-end payload не проходилКОРЕНЬ НАЙДЕН (issue #6612): с v26.5+ у freedom/direct дефолт блокирует VLESS Reverse payload без явного finalRules:[{action:"allow"}]. Фикс залит на bridge-конфиг, но не применён — Kraken по docker недоступен (USB-HDD sda=0B, docker в activating). См. секции «КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН» и «ФИКС ПРИМЕНЁН, но заблокирован» выше. Задание не завершено — остаётся восстановить docker на Kraken и пересоздать bridge.

Связанные заметки