[2026-09-15] eagle: family/how-to/truenas-infrastructure.md family/how-to/vps-qentra.md personal/tech/xray-outbound-subscription-3xui.md personal/tech/xray-reverse-tunnel-kraken-truenas.md work/projects/cpm-web-extension-breakage-validation.md

This commit is contained in:
Alexey Martemyanov
2026-09-15 14:39:02 +06:00
parent 16b4120a4a
commit 5b433b77bf
5 changed files with 277 additions and 38 deletions
@@ -0,0 +1,164 @@
---
title: Xray outbound — внешняя подписка в 3x-ui (vless-space)
created: '2026-09-15'
updated: '2026-09-15'
type: tech
namespace: personal
tags:
- xray
- 3x-ui
- subscription
- outbound
- truenas
- reality
- balancer
confidence: high
status: artifacts-ready-not-applied
related:
- '[[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-admin`** — `closed 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-01``space-10`.
8. **Правки в `routing.rules` добавлять В КОНЕЦ** — существующие правила (`api`, `geoip:private→blocked`, `bittorrent→blocked`, `kraken-user→via-kraken`) должны сохранить приоритет.
## Шаблон конфига — что добавляется
Существующее (outbound'ы `direct`/`blocked`/`via-kraken`, правила, inbound) **не трогается**. Добавляется:
```jsonc
// в 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 Применение — СЛЕДУЮЩИЙ ШАГ
```bash
# 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):**
```bash
# клиент в конфиге
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'
```
```bash
# 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`)
@@ -1,7 +1,7 @@
---
title: Xray Reverse Tunnel — Kraken ↔ TrueNAS
created: '2026-09-01'
updated: '2026-09-02'
updated: '2026-09-15'
type: tech
namespace: personal
tags:
@@ -17,6 +17,7 @@ related:
- '[[family/how-to/truenas-infrastructure]]'
- '[[family/how-to/kraken-access]]'
- '[[family/how-to/rasputin-router]]'
- '[[personal/tech/xray-outbound-subscription-3xui]]'
downgrade_to_26.4.25: >-
portal+bridge BOTH pinned to 26.4.25; required but insufficient by itself.
verified_fix_2026_09_02: >-
@@ -26,6 +27,16 @@ verified_fix_2026_09_02: >-
# Xray Reverse Tunnel — Kraken ↔ TrueNAS
> ## 🔀 НЕ ПУТАТЬ с egress-подпиской (2026-09-15)
> У `xray-admin` **три независимых механизма выхода**, их легко перепутать:
> | Механизм | Кто выходит | Док |
> |---|---|---|
> | `user1` → outbound `direct` | клиент выходит через **TrueNAS** `90.189.160.148` | ниже |
> | `kraken-user` → `via-kraken` → **этот** reverse-туннель | клиент выходит через **Кра́кен** `92.62.70.41` | **этот док** |
> | `vless-space` → `space-balancer` (`space-01…10`) | клиент выходит через **внешнюю подписку** `profilegrid.net` | [[personal/tech/xray-outbound-subscription-3xui]] |
>
> Reverse-туннель (portal на TrueNAS + bridge на Кра́кене) — отдельные контейнеры, `xray-admin` его лишь **использует** как outbound `via-kraken` (SOCKS `xray-reverse-portal:12345`). Подписочный клиент `vless-space` reverse-туннель **не задействует**.
> **Статус: ✅ ВНЕДРЕНО И ПРОВЕРЕНО END-TO-END (2026-09-02).** TrueNAS SOCKS `:12345` → VLESS Reverse TCP+REALITY+Vision → Kraken → internet работает. HTTP и HTTPS тесты оба вернули выходной IP Kraken `92.62.70.41`. **Истинная причина:** Docker bridge/NAT на Kraken сбрасывал reverse mux; bridge должен работать с `network_mode: host`. Более ранние секции со статусом «не работает» и гипотезами #6612/#6242 сохранены ниже как история диагностики и суперсидированы секцией «Верифицированный фикс».
> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.