Files
obsidian-vault/personal/tech/xray-outbound-subscription-3xui.md
T

11 KiB
Raw Blame History

title, created, updated, type, namespace, tags, confidence, status, related
title created updated type namespace tags confidence status related
Xray outbound — внешняя подписка в 3x-ui (vless-space) 2026-09-15 2026-09-15 tech personal
xray
3x-ui
subscription
outbound
truenas
reality
balancer
high artifacts-ready-not-applied
family/how-to/truenas-infrastructure
personal/tech/xray-reverse-tunnel-kraken-truenas
family/how-to/vps-qentra

Xray outbound из внешней подписки в 3x-ui (vless-space)

Статус 2026-09-15: артефакты собраны и проверены в песочнице, применение в живую БД НЕ выполнено (SSH к TrueNAS оборвался на docker stop xray-admin). Продолжать с §6. Разбор контейнера и клиентов — в family/how-to/truenas-infrastructure (разделы xray-admin).

Задача

Добавить в xray-admin (3x-ui на TrueNAS) третьего клиента vless-space, чей трафик идёт не напрямую (как user1) и не через reverse-Кра́кен (как kraken-user), а через серверы внешней Xray-подписки profilegrid.net, с автоматическим выбором живого сервера. user1 и kraken-user не трогаются.

[клиент vless-space] → vpn.mallexxx.duckdns.org:443 → xray-admin
                                                         └→ space-01…space-10 (leastPing) → интернет (DE/LV/NL/EE/PL/FR/US/SE/MD)

Ключевые решения и почему

Решение Почему
Править settings.xrayTemplateConfig в x-ui.db, а не панель 3x-ui генерирует /app/bin/config.json из БД при старте. Панель для этой задачи не нужна (/login с admin/admin → 403, креды не требовались). /app/bin/config.json править бесполезно — перезапишется.
Развернуть серверы подписки в обычные outbound'ы Не зависит от панели и её версии. Работает сразу после docker start.
НЕ использовать таблицу outbound_subscriptions Штатный путь 3x-ui (роуты /panel/api/xray/outbound-subs[/parse]), но требует панели и её сложных полей (link_identities, last_fetched_outbounds). Таблица осталась пустой (0 записей).
leastPing + observatory Alex: «авто брать» — все 10 серверов в пул, живой выбирается сам. observatory обязателен, иначе балансировщик не знает задержек.
Правило по user, а не по клиентскому id Образец взят с рабочего kraken-user-via-reverse: Xray матчит клиента inbound'а по email.

Питфоллы (все проверены фактом)

  1. 🔴 SSH к TrueNAS обрывается на docker stop xray-adminclosed by remote host. Контейнер сам поднялся (restart: unless-stopped), БД не пострадала. Останавливать и применять SQL одной короткой командой, не серией вызовов.
  2. WAL: docker stop у 3x-ui корректно закрывает БД и сливает WAL в основной файл. Удалять -wal/-shm можно только после остановки. Правка при живом WAL = риск порчи базы.
  3. sqlite3 внутри контейнера ОТСУТСТВУЕТ (which sqlite3 пусто). Использовать: docker cp БД на хост, либо хостовый sqlite3, либо alpine-контейнер.
  4. Дедуп подписки: 28 строк → 10 уникальных серверов. Одна подписка отдаёт несколько записей на один хост с разными pbk/sid (перебор ключей). Ключ дедупа — host + publicKey + shortId.
  5. REALITY в outbound: параметры кладутся в streamSettings.realitySettings (serverName, publicKey, shortId, spiderX, fingerprint), а flow: xtls-rprx-vision — в users[0].flow (в ссылке он приходит query-параметром).
  6. spx в ссылке URL-энкодирован (%2F…) — обязателен unquote, иначе Xray падает на spiderX.
  7. routing.balancers.selector — префиксный матч: ["space-"] подхватывает space-01space-10.
  8. Правки в routing.rules добавлять В КОНЕЦ — существующие правила (api, geoip:private→blocked, bittorrent→blocked, kraken-user→via-kraken) должны сохранить приоритет.

Шаблон конфига — что добавляется

Существующее (outbound'ы direct/blocked/via-kraken, правила, inbound) не трогается. Добавляется:

// в outbounds[]
{ "tag": "space-01", "protocol": "vless",
  "settings": { "vnext": [{ "address": "edge-de.mirrorgrid.net", "port": 443,
    "users": [{ "id": "c67ce742-d94f-4e56-889e-9f894ae59940",
                "encryption": "none", "flow": "xtls-rprx-vision" }] }] },
  "streamSettings": { "network": "tcp", "security": "reality",
    "realitySettings": { "serverName": "edge-de.mirrorgrid.net",
      "fingerprint": "firefox", "publicKey": "<pbk>",
      "shortId": "<sid>", "spiderX": "/" } } }
// … space-02 … space-10

// в routing.rules[] (В КОНЕЦ)
{ "type": "field", "user": ["vless-space"],
  "outboundTag": "space-balancer", "ruleTag": "vless-space-via-subscription" }

// новый ключ routing.balancers
[ { "tag": "space-balancer", "selector": ["space-"],
    "strategy": { "type": "leastPing" } } ]

// новый ключ observatory (ОБЯЗАТЕЛЕН для leastPing)
{ "subjectSelector": ["space-"],
  "probeUrl": "https://www.google.com/generate_204",
  "probeInterval": "30s", "enableConcurrency": true }

Артефакты (Mac)

Рабочая папка: ~/tmp-xray-space/

Файл Назначение
build_template.py парсер vless:// → xray-outbound; сборка шаблона (outbounds + rule + balancer + observatory)
make_sql.py генератор apply_space.sql по живой БД
apply_space.sql готовый SQL для применения (3 операции)
template_config.new.json итоговый шаблон, 11 140 б, JSON VALID
space_outbounds.json 10 новых outbound'ов (отчёт)
sub_decoded.txt подписка, base64 развёрнут — 28 строк
xui_copy.db / test_apply.db копия живой БД / песочница с применённым SQL

Параметры клиента (сгенерированы):

email vless-space
UUID a792c483-07e2-4723-9c50-78054c0abc07
subId 24df9391356b48ff
sub-ссылка https://vpn-panel.mallexxx.duckdns.org/sub/24df9391356b48ff

§6 Применение — СЛЕДУЮЩИЙ ШАГ

# 1) залить SQL на TrueNAS
#    (apply_space.sql лежит в ~/tmp-xray-space/ на Mac)
scp ~/tmp-xray-space/apply_space.sql truenas_admin@mallexxx.duckdns.org:/tmp/

# 2) ОДНОЙ командой: стоп → чистка WAL → SQL → старт
ssh truenas_admin@mallexxx.duckdns.org '
  cd /mnt/RED_2TB/docker/xray-admin &&
  docker stop xray-admin &&
  rm -f x-ui.db-shm x-ui.db-wal &&
  sqlite3 x-ui.db < /tmp/apply_space.sql &&
  docker start xray-admin'

Проверка (шаг 6):

# клиент в конфиге
docker exec xray-admin sh -c "cat /app/bin/config.json" | jq -r '.inbounds[].settings.clients[]?.email'
# ожидаем: user1, kraken-user, vless-space

# outbound'ы и балансировщик
docker exec xray-admin sh -c "cat /app/bin/config.json" | jq -r '.outbounds|length'   # 13
docker exec xray-admin sh -c "cat /app/bin/config.json" | jq -c '.routing.balancers'
# end-to-end: временный xray-клиент с id vless-space → curl api.ipify.org
# ожидаем egress НЕ 90.189.160.148 (TrueNAS) и НЕ 92.62.70.41 (Kraken),
# а IP сервера из подписки (DE/LV/NL/…)

Откат: бэкап /mnt/RED_2TB/docker/backups/xray-admin-before-vless-space-20260915-012057/ → скопировать x-ui.db обратно (при остановленном контейнере) → docker start xray-admin.

Ограничение: «auto» здесь НЕ автоматическое

Выбранный путь разворачивает серверы подписки в статические outbound'ы. Если провайдер сменит список серверов, шаблон надо пересобрать вручную: build_template.pymake_sql.py → применить.

Для настоящего авто-обновления нужен cron-скрипт на TrueNAS:

curl подписки → base64 -d → build_template.py → пересобрать xrayTemplateConfig
→ стоп xray-admin → SQL → старт

Не реализован. Альтернатива без скрипта — использовать штатную таблицу outbound_subscriptions через панель (даёт update_interval из коробки, но требует доступа к UI).

Открытый вопрос безопасности (не решён)

/sub/<subId> отдаётся без авторизации (/sub/abc тоже 200). Новая ссылка vless-space будет так же открыта. Варианты: подписка по токену, ротация subId. Задано Alex 2026-09-15, решения нет.

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