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

165 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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`)