14 KiB
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 |
|
high | artifacts-ready-not-applied |
|
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. |
Питфоллы (все проверены фактом)
- 🔴 SSH к TrueNAS обрывается на
docker stop xray-admin—closed by remote host. Контейнер сам поднялся (restart: unless-stopped), БД не пострадала. Останавливать и применять SQL одной короткой командой, не серией вызовов. - WAL:
docker stopу 3x-ui корректно закрывает БД и сливает WAL в основной файл. Удалять-wal/-shmможно только после остановки. Правка при живом WAL = риск порчи базы. sqlite3внутри контейнера ОТСУТСТВУЕТ (which sqlite3пусто). Использовать:docker cpБД на хост, либо хостовыйsqlite3, либоalpine-контейнер.- Дедуп подписки: 28 строк → 10 уникальных серверов. Одна подписка отдаёт несколько записей на один хост с разными
pbk/sid(перебор ключей). Ключ дедупа —host + publicKey + shortId. - REALITY в outbound: параметры кладутся в
streamSettings.realitySettings(serverName,publicKey,shortId,spiderX,fingerprint), аflow: xtls-rprx-vision— вusers[0].flow(в ссылке он приходит query-параметром). spxв ссылке URL-энкодирован (%2F…) — обязателенunquote, иначе Xray падает наspiderX.routing.balancers.selector— префиксный матч:["space-"]подхватываетspace-01…space-10.- Правки в
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 |
Параметры клиента (сгенерированы):
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.py → make_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, решения нет.
Связанные заметки
- family/how-to/truenas-infrastructure — контейнер
xray-admin, клиенты, sub-канал - personal/tech/xray-reverse-tunnel-kraken-truenas — reverse-туннель (отдельный механизм)
- family/how-to/vps-qentra — удалённый VPS (источник мёртвого
v.qentra.top)
🔴 2026-09-15: попутные находки этой сессии
1. hermes-taiga ходит НЕ через xray-admin, а через vless-proxy (мёртвый)
Разбор путаницы «таига настроена через user1?» — нет:
Env hermes-taiga |
TELEGRAM_PROXY=socks5://vless-proxy:1080, DISCORD_PROXY=socks5://vless-proxy:1080 |
Сети hermes-taiga |
hermes_taiga_net, ha_default |
vless-proxy |
образ teddysun/xray:latest, SOCKS :1080 + HTTP :1081, outbound → v.qentra.top:443 (мёртв, VPS удалён) |
Сеть xray-admin |
caddy_default — с hermes-taiga не пересекается |
user1 |
id ce320965-… — клиент inbound in-10095-tcp, hermes-taiga к нему не обращается |
Следствие: Telegram/Discord у hermes-taiga сейчас без сети (цепочка hermes-taiga → vless-proxy:1080 → v.qentra.top → timeout).
Как чинить (обсуждалось, НЕ выполнено): переписать outbound /mnt/RED_2TB/docker/vless-proxy/config.json с мёртвого v.qentra.top на живой сервер. 4 поля:
| Поле | Сейчас | Станет |
|---|---|---|
address |
v.qentra.top |
vpn.mallexxx.duckdns.org |
users[0].id |
2D9F24C4-21FE-4784-9843-F11C384DA67A |
ce320965-6956-4759-84bb-7cb71cfc6252 (user1) |
tlsSettings.serverName |
v.qentra.top |
vpn.mallexxx.duckdns.org |
wsSettings.headers.Host |
v.qentra.top |
vpn.mallexxx.duckdns.org |
wsSettings.path |
/qentra |
/vless |
Порт 443, security: tls, network: ws — без изменений. xray-admin не имеет SOCKS-входа (он сервер) — поэтому vless-proxy нужен как локальный клиент-мостик, его нельзя просто «выкинуть».
⚠️ Побочный эффект: трафик пойдёт
vless-proxy → vpn.mallexxx.duckdns.org:443 → Caddy → xray-adminна том же хосте → egress = IP TrueNAS90.189.160.148. Петля внутри TrueNAS.
2. Compose-файл xray-admin утрачен — подробный разбор
См. family/plans/reverse-xray-3xui-kraken (шапка). Кратко: rm -f /etc/x-ui/docker-compose.yml внутри контейнера 2026-09-01 05:40:08 UTC снёс файл на хосте через bind-mount. Восстанавливать из docker inspect.
3. «Auto» в Xray-контейнере = только скрипт
Ещё раз зафиксировано: Xray-бинарник подписки не умеет. Умеет один/несколько статических outbound'ов + routing.balancers. Авто-подписка (update_interval) — только GUI-клиенты (Happ/v2rayN/NekoBox) или штатная таблица outbound_subscriptions 3x-ui через панель. Для docker-контейнера (hermes-taiga, vless-proxy) «auto» = cron-скрипт.