[2026-09-15] eagle: family/how-to/truenas-infrastructure.md personal/tech/vless-space-subscription-egress.md

This commit is contained in:
Alexey Martemyanov
2026-09-15 17:35:14 +06:00
parent ee95685f4e
commit 4bd66fb143
2 changed files with 112 additions and 12 deletions
+39 -8
View File
@@ -26,6 +26,8 @@
> | `routing.rules` | `{"user":["vless-space"],"balancerTag":"space-balancer"}` |
> | клиенты | `user1`, `kraken-user`, `vless-space` — целы |
>
> ✅ **Доки актуализированы 2026-09-15** — сняты устаревшие статусы «`vless-space` НЕ СОЗДАН / БД откачена» и «авто-обновление не начато».
>
> ### 🔑 ТРИ ПИТФОЛЛА БАЛАНСИРОВЩИКА — все три обязательны
>
> Ошибка `app/dispatcher: non existing outTag: space-balancer` вызывалась **тремя независимыми причинами** (каждая воспроизведена и снята отдельно):
@@ -82,13 +84,15 @@
> # ожидается НЕ 90.189.160.148
> ```
>
> 🔴 **ДОСТУП:** `/mnt/RED_2TB/docker/vless-proxy/` — `root:root 755`, `config.json` примонтирован `:ro`. **`truenas_admin` писать НЕ может** (`touch` → Permission denied; `sudo` требует пароль; `root@` publickey denied). Файл правит **Alex от root**. Агент готовит конфиг локально и отдаёт целиком.
> 🔴 **ДОСТУП (проверено 2026-09-15):** `/mnt/RED_2TB/docker/vless-proxy/` — `root:root 755`, `config.json` примонтирован `:ro`, внутри контейнера `/etc/xray/config.json` тоже `Read-only file system`. **`truenas_admin` править НЕ может** (`touch` → Permission denied; `sudo` требует пароль; `root@192.168.2.197` publickey denied). Файл правит **только Alex от root**; агент готовит конфиг локально (`~/tmp-xray-space/vless-proxy-config-new.json`) и отдаёт целиком.
>
> ⚠️ **`docker compose up -d hermes-taiga` → `no such service`.** Сервис в `/mnt/RED_2TB/docker/hermes/docker-compose.yml` называется **`taiga`** (`hermes-taiga` — это `container_name`). Правильно: `docker compose up -d taiga`.
>
> 🔴 **Прежний хард-вывод «3x-ui нельзя править снаружи» — ОКОНЧАТЕЛЬНО ОПРОВЕРГНУТ.** SQLite-путь работает при двух условиях: (1) в `xrayTemplateConfig` нет инбаунда (только `api`), (2) БД правится локально на Mac и кладётся файлом с `rm -f *.db-wal *.db-shm` + `chown 950:root`.
>
> **⚠️ ОТДЕЛЬНАЯ НЕЗАКРЫТАЯ ПРОБЛЕМА — `kraken-user`:** не работает, потому что **сервер Kraken недоступен** (не связано с БД). Проверено 2026-09-15: `ssh-kraken.qentra.top` → `Connection timed out during banner exchange`; `kraken@10.99.1.2:22` → `Operation timed out`; DNS `kraken` → NXDOMAIN. Портал на TrueNAS жив (`xray-reverse-portal` Up, `12345`/`12346` LISTEN), но соединений от бриджа **ноль** → ждём восстановления Kraken (решение Alex «подождём, может оживет»).
>
> **Остаточная задача (не начата):** авто-обновление списка серверов подписки. Outbound'ы `space-01…10` **статически вбиты в шаблон** — при смене списка у `profilegrid` всё сломается молча. Варианты: (A) cron-скрипт на NAS (sub → пересборка шаблона → рестарт), (B) штатная таблица `outbound_subscriptions` 3x-ui через панель.
> **✅ РЕШЕНО 2026-09-15 (штатная подписка).** Было: `space-01…10` статически вбиты в шаблон — при смене списка у `profilegrid` всё ломалось молча. Стало: штатная таблица **`outbound_subscriptions`** 3x-ui (`remark=vless-space`, `tag_prefix=sub1-`, `update_interval=600`) → **28 серверов `sub1-*`, авто-обновление**. Статика `space-01…10` **удалена**. Балансировщик `space-balancer`: `selector: ["sub1-"]`, `leastLoad`; наблюдение — **`burstObservatory`** с `subjectSelector: ["sub1-"]`. Ручная пересборка шаблона больше НЕ нужна.
>
> **Правила взаимодействия (Alex, 2026-09-15):** команды — плоские однострочники, **без `ssh … '…'`-обёртки**, **без `sleep N`**, **без кириллицы в bash**, без `execute_code`/python. Правку SQLite делать **локально на Mac**, потом заливать готовый файл.
>
@@ -367,11 +371,23 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
> - ❌ **НЕТ «subscription channel» как отдельной сущности с ветками/каналами** (типа mihomo-«channel»/Nekoray-«channel»): в бинарнике `/app/x-ui` **нет ни одной строки `channel` в этом смысле** и нет API-роутов каналов. Есть категории **`subJsonURI` / `subJsonPath`** — это JSON/Mihomo-формат той же подписки, `XUI`/`subClashEnableRouting` — mihomo-специфика. То есть «channel» = **ссылка подписки + клиентский auto-update**, отдельного механизма в панели нет.
> - Роуты панели по темам подписок (из `strings /app/x-ui`): `/panel/api/clients/sub`, `/panel/api/sub-balancers*`, `/panel/api/xray/outbound-subs*`. (`outbound-subs` = панель сама тянет ВНЕШНИЕ подписки в свои outbound — противоположное направление, не клиентская подписка.)
>
> **🔴 ОТКРЫТЫЙ ВОПРОС БЕЗОПАСНОСТИ (задан Alex 2026-09-15, решения НЕТ):** путь `/sub/<subId>` отдаётся **без авторизации** — `/sub/abc` тоже даёт 200. Кто знает `subId` — получает рабочий ключ. Возможный фикс: подписка по токену/`subUpdates`-проверке в настройках панели либо короткий `subId` + ротация. **Ничего не менялось, ждём решения.**
> **ОТКРЫТЫЙ ВОПРОС БЕЗОПАСНОСТИ (задан Alex 2026-09-15, решения НЕТ):** путь `/sub/<subId>` отдаётся **без авторизации** — `/sub/abc` тоже даёт 200. Кто знает `subId` — получает рабочий ключ. Возможный фикс: подписка по токену/`subUpdates`-проверке в настройках панели либо короткий `subId` + ротация. **Ничего не менялось, ждём решения.**
>
> ### 🔴 ПИТФОЛЛ: подмена `x-ui.db` при ЖИВОМ контейнере ТЕРЯЕТ панельные данные (2026-09-15)
>
> **Как было потеряно:** снят `x-ui.db` при работающем контейнере → `docker stop` (панель сбросила WAL в файл) → поверх положен снятый ранее файл → **запись `outbound_subscriptions` уничтожена**.
>
> Панель пишет в `-wal`; `sqlite3 x-ui.db "SELECT …"` снаружи показывает **устаревшие** данные (0 записей при живой подписке).
>
> **Правильный порядок:** `docker stop` **ДО** скачивания БД. Если остановить нельзя — читать WAL через `strings /mnt/RED_2TB/docker/xray-admin/x-ui.db-wal` (не через `sqlite3` на файле). Признак: `SELECT COUNT(*) FROM outbound_subscriptions` → `0`, а панель запись показывает.
>
> **Восстановление:** заново добавить подписку в UI (`Xray → Outbound Subscriptions`). Бэкапы БД старше правки пусты — URL подписки в доке не хранится.
---
> ## ✅ РЕАЛИЗАЦИЯ 2026-09-15: клиент `vless-space` на ВНЕШНЕЙ outbound-подписке — АРТЕФАКТЫ ГОТОВЫ, ПРИМЕНЕНИЕ НЕ ВЫПОЛНЕНО
> ## ⚠️ ИСТОРИЯ (2026-09-15, ранние попытки) — АРТЕФАКТЫ ГОТОВЫ, ПРИМЕНЕНИЕ НЕ ВЫПОЛНЕНО
>
> **⚡ ВНИМАНИЕ: это ЛЕТОПИСЬ, а не текущее состояние.** Финальный статус — в шапке документа: клиент `vless-space` **СОЗДАН И РАБОТАЕТ**, egress через штатную подписку `sub1-*` (28 серверов, авто-обновление 600 с). Ниже — разбор промежуточных попыток (все провалившиеся), оставлен для контекста и как источник грабель.
>
> **Задача Alex:** добавить в `xray-admin` третьего клиента `vless-space`, чей трафик идёт через **внешнюю** Xray-подписку `profilegrid.net` с авто-обновлением серверов. `user1` и `kraken-user` **НЕ трогать**. Серверы — **«авто брать»** (все, с балансировкой).
>
@@ -495,8 +511,23 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
>
> ---
>
> ## ПОДЗАДАЧА 2026-09-15: `hermes-taiga` переключить с мёртвого `vless-proxy` (НЕ ВЫПОЛНЕНА)
> ## ПОДЗАДАЧА 2026-09-15: `hermes-taiga` → `vless-proxy` → `xray-admin` — **ВЫПОЛНЕНА**
>
> **Цепочка:** `hermes-taiga → vless-proxy:1080 → vpn.mallexxx.duckdns.org:443 → xray-admin → 28 × sub1-* → интернет`
>
> `vless-proxy` переключён с мёртвого `v.qentra.top` на `vpn.mallexxx.duckdns.org` с клиентом **`vless-space`** (`a792c483-07e2-4723-9c50-78054c0abc07`), path `/vless`. Проверено фактом: egress `104.28.219.140` / `188.239.191.18` ≠ `90.189.160.148`.
>
> **Env `hermes-taiga` (`TELEGRAM_PROXY`, `DISCORD_PROXY` = `socks5://vless-proxy:1080`) не менялся** — рестарт не нужен, SOCKS-соединения устанавливаются на каждый запрос.
>
> ⚠️ **ПИТФОЛЛ проверки:** `wget` **не умеет SOCKS5** — `docker exec vless-proxy wget -qO- https://api.ipify.org` вернёт **локальный** IP контейнера `90.189.160.148` и даст ложный вывод «direct». Проверять ТОЛЬКО через SOCKS:
> ```bash
> docker run --rm --network hermes_taiga_net alpine sh -c \
> "apk add -q curl; curl -s --max-time 20 --socks5-hostname vless-proxy:1080 https://api.ipify.org"
> ```
>
> ⚠️ **ПИТФОЛЛ доступа:** `/mnt/RED_2TB/docker/vless-proxy/` — `root:root 755`, `config.json` примонтирован `:ro`, внутри контейнера `/etc/xray/config.json` тоже `Read-only file system`. **`truenas_admin` править НЕ может** (`touch` → Permission denied; `sudo` требует пароль; `root@` → publickey denied). Правку делает **Alex от root**; агент готовит конфиг локально (`~/tmp-xray-space/vless-proxy-config-new.json`).
>
> ⏳ **Осталось (не закрыто):** Telegram-адаптер `hermes-taiga` при `TELEGRAM_PROXY` уходил в ветку `TelegramFallbackTransport` (прямое подключение по fallback-IP) вместо честного SOCKS. Симптом — `Connecting (attempt 1/8)` и **тишина**, без `Proxy detected` в логе. Попытка фикса через `HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=1` **не помогла**, переменная убрана. **Telegram РАБОТАЕТ** (проверено Alex), т.е. проблема саморазрешилась либо была в тайминге рестартов.
>
> **Факт:** `hermes-taiga` ходит **НЕ** через `xray-admin`/`user1`, а через **отдельный** контейнер `vless-proxy`.
> - env: `TELEGRAM_PROXY=socks5://vless-proxy:1080`, `DISCORD_PROXY=socks5://vless-proxy:1080`
@@ -504,7 +535,7 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
> - `vless-proxy`: `teddysun/xray:latest`, Up, SOCKS `:1080` + HTTP `:1081`, **единственный outbound → мёртвый `v.qentra.top:443`** (VPS удалён)
> - **⇒ Telegram/Discord бота сейчас без сети.**
>
> **Готовый план (4 поля, `user1` и `kraken-user` не трогаются):**
> **Файлы `/mnt/RED_2TB/docker/vless-proxy/`:** `config.json` (1091 б), `config.json.bak-20260707-102922`, `config.json.bak-switch-ws-client-20260707-103233`, `docker-compose.yml` (336 б).
> | Поле в `/mnt/RED_2TB/docker/vless-proxy/config.json` | Сейчас | Станет |
> |---|---|---|
> | `outbounds[0].settings.vnext[0].address` | `v.qentra.top` | `vpn.mallexxx.duckdns.org` |
@@ -576,7 +607,7 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
|---|---|---|---|
| `user1` | `68c5cy5n5ui138yh` | `https://vpn-panel.mallexxx.duckdns.org/sub/68c5cy5n5ui138yh` | TrueNAS `90.189.160.148` |
| `kraken-user` | `f43074a029dc656e` | `https://vpn-panel.mallexxx.duckdns.org/sub/f43074a029dc656e` | Kraken `92.62.70.41` |
| `vless-space` | `24df9391356b48ff` | `https://vpn-panel.mallexxx.duckdns.org/sub/24df9391356b48ff` | подписка `profilegrid.net` (10 серверов, `space-balancer` leastPing) — ❌ **НЕ СОЗДАН**, БД откачена 2026-09-15 |
| `vless-space` | `24df9391356b48ff` | `https://vpn-panel.mallexxx.duckdns.org/sub/24df9391356b48ff` |**СОЗДАН 2026-09-15**, egress через подписку `profilegrid.net` (**28 серверов `sub1-*`**, `space-balancer` **leastLoad** + `burstObservatory`) |
- Sub-сервер отдаёт **base64-список** `vless://…` (проверено: HTTP 200, `text/plain`). Пример тела: `vless://ce320965-…@vpn.mallexxx.duckdns.org:443?encryption=none&host=vpn.mallexxx.duckdns.org&path=%2Fvless&security=tls&type=ws#vless-ws-user1`.
- **`/sub/<subId>` отдаётся БЕЗ авторизации** — любой, кто знает `subId`, получает рабочий ключ. Открытый вопрос безопасности.
@@ -1,7 +1,7 @@
---
title: vless-space — клиент 3x-ui с egress через external-подписку
created: 2026-09-15T00:00:00.000Z
updated: '2026-09-15T22:30:00.000Z'
updated: '2026-09-15T23:59:00.000Z'
type: tech
namespace: personal
tags:
@@ -10,13 +10,17 @@ tags:
- x-ui
- subscription
- outbound
- outbound_subscriptions
- profilegrid
- truenas
- space
- balancer
- leastLoad
- balancerTag
- sampling
- burstObservatory
- vless-proxy
- hermes-taiga
- wal
confidence: high
status: done-egress-via-subscription-working
related:
@@ -644,8 +648,9 @@ curl -sk -o /dev/null -w "sub: %{http_code}\n" https://vpn-panel.mallexxx.duckdn
- [ ] После появления клиента — routing-правило на `space-balancer` **тоже через панель** (Xray → Routing)
- [ ] Проверить end-to-end: egress IP ≠ `90.189.160.148` (TrueNAS) и ≠ `92.62.70.41` (Kraken)
- [ ] Решить вопрос с авторизацией на sub-ссылках (открыты без авторизации)
- [ ] Обновить [[family/how-to/truenas-infrastructure]] (новый клиент + балансировщик)
- [ ] **Отдельная задача, НЕ начата:** `hermes-taiga` ходит через `vless-proxy`, чей outbound указывает на **мёртвый** `v.qentra.top` (VPS удалён). Переключение на живой сервер — правка 5 полей в `/mnt/RED_2TB/docker/vless-proxy/config.json` (таблица в [[personal/tech/xray-outbound-subscription-3xui]] §попутные находки). Alex спрашивал — **ответ: да, можно**.
- [x] ~~Обновить [[family/how-to/truenas-infrastructure]] (новый клиент + балансировщик)~~ → **сделано 2026-09-15**
- [x] ~~**Отдельная задача:** `hermes-taiga` ходит через `vless-proxy`, чей outbound указывает на **мёртвый** `v.qentra.top`~~ → **✅ ВЫПОЛНЕНО 2026-09-15.** `vless-proxy` переключён на `vpn.mallexxx.duckdns.org:443` + `/vless`, клиент **`vless-space`** (`a792c483-07e2-4723-9c50-78054c0abc07`). Проверено фактом: egress `104.28.219.140` / `188.239.191.18` ≠ `90.189.160.148`. См. §«`vless-proxy` → `xray-admin`» ниже.
- [ ] ⏳ **Осталось (не закрыто):** Telegram-адаптер `hermes-taiga` уходил в ветку `TelegramFallbackTransport` (прямой коннект по fallback-IP) вместо SOCKS, несмотря на `TELEGRAM_PROXY`. Симптом — `Connecting (attempt 1/8)` и тишина, без `Proxy detected` в логе. Попытка фикса `HERMES_TELEGRAM_DISABLE_FALLBACK_IPS=1` **не помогла**, переменная убрана. **Telegram РАБОТАЕТ** (подтверждено Alex) — саморазрешалось, вероятно тайминг рестартов.
## 🔴🔴 ГЛАВНАЯ НАХОДКА СЕССИИ (2026-09-15, позже в тот же день): `leastPing` В XRAY 26.x НЕ РАБОТАЕТ — НУЖЕН `leastLoad`
@@ -773,6 +778,70 @@ docker start xray-admin
```
**Исходное состояние (до всей работы):** `/mnt/RED_2TB/docker/backups/xray-admin-before-vless-space-20260915-012057/`
## 🔗 `vless-proxy` → `xray-admin` (2026-09-15) — ВЫПОЛНЕНО
**Задача:** `vless-proxy` (SOCKS `:1080` + HTTP `:1081`) смотрел на **мёртвый** `v.qentra.top` (VPS удалён) → `hermes-taiga` (Telegram/Discord) без сети. Переключить на живой `xray-admin`.
**Что изменено** — `/mnt/RED_2TB/docker/vless-proxy/config.json`:
| Поле | Было | Стало |
|---|---|---|
| `outbounds[0].settings.vnext[0].address` | `v.qentra.top` | `vpn.mallexxx.duckdns.org` |
| `…vnext[0].users[0].id` | `2D9F24C4-21FE-4784-9843-F11C384DA67A` | `a792c483-07e2-4723-9c50-78054c0abc07` (`vless-space`) |
| `streamSettings.tlsSettings.serverName` | `v.qentra.top` | `vpn.mallexxx.duckdns.org` |
| `streamSettings.wsSettings.path` | `/qentra` | `/vless` |
| `streamSettings.wsSettings.headers.Host` | `v.qentra.top` | `vpn.mallexxx.duckdns.org` |
`port: 443`, `security: tls`, `network: ws` — без изменений.
**Выбор клиента — `vless-space`, НЕ `user1`.** Через `user1` egress = IP TrueNAS (`90.189.160.148`); через `vless-space` egress = IP подписки (нероссийский). Для Telegram/Discord нужен второй.
**Итоговая цепочка:**
```
hermes-taiga → vless-proxy:1080 → vpn.mallexxx.duckdns.org:443 → xray-admin → 28 × sub1-* → интернет
```
Env `hermes-taiga` (`TELEGRAM_PROXY`, `DISCORD_PROXY` = `socks5://vless-proxy:1080`) **менять не нужно** — SOCKS-соединения устанавливаются на каждый запрос, рестарт не требуется.
**Проверено фактом:** `104.28.219.140` / `188.239.191.18` ≠ `90.189.160.148` ✅
### 🔴 ПИТФОЛЛ ПРОВЕРКИ: `wget` не умеет SOCKS5
```bash
# ❌ ЛОЖНЫЙ РЕЗУЛЬТАТ — wget идёт напрямую, минуя SOCKS
docker exec vless-proxy wget -qO- https://api.ipify.org # → 90.189.160.148 (обман!)
# ✅ ПРАВИЛЬНО — только через SOCKS-прокси
docker run --rm --network hermes_taiga_net alpine sh -c \
"apk add -q curl; curl -s --max-time 20 --socks5-hostname vless-proxy:1080 https://api.ipify.org"
```
Также проверять `httpx` из venv taiga (доказательство, что SOCKS работает из python-стека):
```bash
docker exec hermes-taiga /opt/hermes/.venv/bin/python3 -c "
import asyncio, httpx
async def m():
async with httpx.AsyncClient(proxy='socks5://vless-proxy:1080', timeout=15) as c:
r = await c.get('https://api.telegram.org'); print('OK', r.status_code)
asyncio.run(m())"
# → OK 302
```
### 🔴 ПИТФОЛЛ ДОСТУПА: правку делает только Alex от root
`/mnt/RED_2TB/docker/vless-proxy/` — `root:root 755`, `config.json` примонтирован `:ro`, внутри контейнера `/etc/xray/config.json` тоже `Read-only file system`. **`truenas_admin` править НЕ может:** `touch` → Permission denied; `sudo` требует пароль; `root@192.168.2.197` → publickey denied.
Проверка прав (факт):
```bash
ssh truenas_admin@192.168.2.197 'ls -ld /mnt/RED_2TB/docker/vless-proxy; touch /mnt/RED_2TB/docker/vless-proxy/.wtest 2>&1 || echo CANNOT_WRITE'
ssh truenas_admin@192.168.2.197 'docker exec vless-proxy sh -c "touch /etc/xray/config.json 2>&1 || echo READONLY"'
```
**Рабочий приём:** агент готовит конфиг **локально на Mac** (`~/tmp-xray-space/vless-proxy-config-new.json`), отдаёт целиком; Alex копирует и делает `docker restart vless-proxy`.
### ⚠️ ПИТФОЛЛ: `docker compose up -d hermes-taiga` → `no such service`
Сервис в `/mnt/RED_2TB/docker/hermes/docker-compose.yml` называется **`taiga`**, не `hermes-taiga` (это `container_name`). Правильно: `docker compose up -d taiga`.
## Связанные заметки
- [[family/plans/reverse-xray-3xui-kraken]] — reverse-туннель к Кра́кену, история compose-инцидента