639 lines
45 KiB
Plaintext
639 lines
45 KiB
Plaintext
---
|
||
title: vless-space — клиент 3x-ui с egress через external-подписку
|
||
created: 2026-09-15T00:00:00.000Z
|
||
updated: '2026-09-15T23:59:00.000Z'
|
||
type: tech
|
||
namespace: personal
|
||
tags:
|
||
- xray
|
||
- 3x-ui
|
||
- x-ui
|
||
- subscription
|
||
- outbound
|
||
- outbound_subscriptions
|
||
- profilegrid
|
||
- truenas
|
||
- balancer
|
||
- leastLoad
|
||
- balancerTag
|
||
- sampling
|
||
- burstObservatory
|
||
- vless-proxy
|
||
- hermes-taiga
|
||
- wal
|
||
confidence: high
|
||
status: done-egress-via-subscription-working
|
||
related:
|
||
- '[[family/plans/reverse-xray-3xui-kraken]]'
|
||
- '[[personal/tech/xray-reverse-tunnel-kraken-truenas]]'
|
||
- '[[personal/tech/xray-outbound-subscription-3xui]]'
|
||
- '[[family/how-to/truenas-infrastructure]]'
|
||
---
|
||
|
||
# `vless-space` — клиент 3x-ui, egress через внешнюю подписку
|
||
|
||
> # 🏁 2026-09-15 (ЗАКРЫТО) — РАБОТАЕТ на штатной подписке. `space-` вычищен.
|
||
>
|
||
> **Итог сессии:** статика `space-01…10` удалена, egress идёт через 28 серверов штатной подписки `sub1-*`.
|
||
>
|
||
> | Проверка (факт) | Результат |
|
||
> |---|---|
|
||
> | `vless-proxy:1080` egress | ✅ `104.28.219.140` / `188.239.191.18` — НЕ `90.189.160.148` |
|
||
> | `outbounds` | 31 = `direct`+`blocked`+`via-kraken` + **28 × `sub1-*`** |
|
||
> | `space-*` | ❌ 0 — вычищено |
|
||
> | `routing.balancers` | `space-balancer`, `selector: ["sub1-"]`, `leastLoad` |
|
||
> | `burstObservatory.subjectSelector` | `["sub1-"]` |
|
||
> | `routing.rules` | `{"user":["vless-space"], "balancerTag":"space-balancer"}` |
|
||
> | ошибки `non existing outTag` | 0 |
|
||
> | `in-10095-tcp` clients | `user1`, `kraken-user`, `vless-space` — целы |
|
||
>
|
||
> ---
|
||
>
|
||
> ## 🔴 ПИТФОЛЛ (стоил сессии): поле observatory в UI = `burstObservatory`, НЕ `observatory`
|
||
>
|
||
> Xray 26.x принимает **обе** секции, но ведут они себя по-разному, и панель путает:
|
||
>
|
||
> | Что | Где живёт | Примечание |
|
||
> |---|---|---|
|
||
> | `observatory` | `xrayTemplateConfig.observatory` | классическая секция, требует `sampling: 3` |
|
||
> | `burstObservatory` | `xrayTemplateConfig.burstObservatory` | `pingConfig{sampling, interval, destination}` — **рабочий вариант** |
|
||
>
|
||
> **Симптом ошибки:** правка секции в UI **удалила** `observatory` целиком (`jq 'has("observatory")'` → `false`), а `subjectSelector` остался `["space-"]` → балансировщик не пингует никого → **трафик молча падает в `direct`, ошибок в логе НЕТ**.
|
||
>
|
||
> **Как диагностировать правильно (3 команды, без остановки контейнера):**
|
||
> ```bash
|
||
> docker exec xray-admin cat /app/bin/config.json | jq -r 'keys[]' # ищем observatory ИЛИ burstObservatory
|
||
> docker exec xray-admin cat /app/bin/config.json | jq -c '.burstObservatory // .observatory'
|
||
> docker exec xray-admin cat /app/bin/config.json | jq -c '.routing.balancers' # selector должен матчить префикс подписки
|
||
> ```
|
||
> ⚠️ Я искал `observatory` и объявил секцию отсутствующей — она была, но под именем `burstObservatory`. **Проверять оба имени.**
|
||
>
|
||
> ## 🔴 ПИТФОЛЛ: селектор балансировщика И `subjectSelector` observatory надо менять ПАРОЙ
|
||
>
|
||
> При переходе `space-` → `sub1-` недостаточно поменять `routing.balancers[].selector`. `observatory/burstObservatory.subjectSelector` — **отдельное поле**, и если его забыть, балансировщик не находит живых кандидатов.
|
||
>
|
||
> ## 🔴 ПИТФОЛЛ: подмена `x-ui.db` при живом контейнере ТЕРЯЕТ панельные данные
|
||
>
|
||
> Скачал БД при работающем контейнере → UI-правки жили в `-wal` → `cp` перезаписал файл → **запись `outbound_subscriptions` потеряна**. Alex восстанавливал подписку в UI заново.
|
||
> **Правило:** `docker stop` ДО скачивания БД. Либо читать WAL через `strings x-ui.db-wal` (а не `sqlite3` на файле).
|
||
|
||
> # 🏁 ИСТОРИЯ: 2026-09-15 22:30 — на статике `space-01…10` egress РАБОТАЛ (`xui_v9.db`)
|
||
>
|
||
> | Проверка (факт, `xui_v9.db`) | Результат |
|
||
> |---|---|
|
||
> | IP через `vless-space` | ✅ `104.28.225.223` / `195.72.61.192` (**Стокгольм, SE**) |
|
||
> | IP TrueNAS напрямую | `90.189.160.148` — **отличается** → трафик идёт через подписку |
|
||
> | Стабильность | ✅ 3/3 запроса подряд через клиент в докере |
|
||
> | `in-10095-tcp` clients | ✅ `user1,kraken-user,vless-space` (один инбаунд, порта 10096 нет) |
|
||
> | outbounds | 13, balancer `space-balancer` (leastLoad) — **статика, позже удалена** |
|
||
> | подписка `/sub/24df9391356b48ff` | ✅ HTTP 200 |
|
||
> | `kraken-user`, `user1` | ✅ целы, не тронуты |
|
||
>
|
||
> ## 🔑 ТРИ ПИТФОЛЛА + ЧЕТВЁРТЫЙ (все обязательны)
|
||
>
|
||
> **⚠️ 4-е условие (2026-09-15, финал): `observatory.subjectSelector` обязан совпадать с `routing.balancers[].selector`.** Проверка: `jq -c '{bal:.routing.balancers[0].selector, obs:.observatory.subjectSelector}'` → значения равны. Рассинхрон = `direct` без единой ошибки в логе.
|
||
>
|
||
> **1. `balancerTag`, а НЕ `outboundTag`.** Правило маршрутизации обязано ссылаться на балансировщик через `balancerTag` — с `outboundTag` 3x-ui/Xray не распознаёт его как балансировщик, и на первом же соединении: `app/dispatcher: non existing outTag: space-balancer`.
|
||
|
||
>
|
||
> ```jsonc
|
||
> // ❌ НЕ РАБОТАЕТ
|
||
> { "type":"field", "user":["vless-space"], "outboundTag":"space-balancer", "ruleTag":"..." }
|
||
> // ✅ РАБОТАЕТ
|
||
> { "type":"field", "user":["vless-space"], "balancerTag":"space-balancer", "ruleTag":"..." }
|
||
> ```
|
||
>
|
||
> **2. `strategy: leastLoad`, а НЕ `leastPing`.** В Xray 26.x `leastPing` балансировщик **не создаёт** (проверено A/B на минимальном конфиге; `burstObservatory` — то же).
|
||
>
|
||
> **3. `observatory` ОБЯЗАТЕЛЬНО с `sampling`.** Без `sampling` Xray 26.7.28 **молча** не создаёт балансировщик — `-test` при этом печатает `Configuration OK`.
|
||
>
|
||
> ```jsonc
|
||
> "observatory": { "subjectSelector":["space-"], "probeUrl":"https://www.google.com/generate_204",
|
||
> "probeInterval":"30s", "enableConcurrency":true, "sampling":3 } // ← sampling обязателен
|
||
> ```
|
||
>
|
||
> **Итоговый рабочий шаблон (все три фикса):**
|
||
> ```jsonc
|
||
> "routing": {
|
||
> "rules": [ ..., { "type":"field","user":["vless-space"],
|
||
> "balancerTag":"space-balancer","ruleTag":"vless-space-via-subscription" } ],
|
||
> "balancers": [ { "tag":"space-balancer","selector":["space-"],
|
||
> "strategy":{"type":"leastLoad"} } ]
|
||
> },
|
||
> "observatory": { ..., "sampling": 3 }
|
||
> ```
|
||
>
|
||
> **Артефакт, который сработал:** `~/tmp-xray-space/xui_v9.db` (md5 `8113add5b1d43a54f54253324eed2f1c`), залита на NAS.
|
||
> **Бэкапы:** `/mnt/RED_2TB/docker/backups/xray-admin-v9-20260915-102126/` (и v8, v7load, v7, `…-before-vless-space-20260915-012057` — исходная).
|
||
>
|
||
> **Как проверялось (Xray-клиент в докере на NAS):** конфиг из sub-ссылки → `docker run --name vless-test --network host -v /tmp/client_test_vless_space.json:/etc/xray/config.json:ro ghcr.io/xtls/xray-core:latest` → `curl --socks5-hostname 127.0.0.1:10808 https://api.ipify.org`. Питфолл: `chmod 644` конфига перед `docker run`, иначе `permission denied`.
|
||
|
||
> ## ✅ ПРЕДЫДУЩИЙ ЭТАП: `xui_v7.db` ЗАЛИТА И РАБОТАЕТ ЧАСТИЧНО
|
||
>
|
||
> | Проверка после заливки `xui_v7.db` | Результат |
|
||
> |---|---|
|
||
> | `in-10095-tcp` clients | ✅ `user1,kraken-user,vless-space` — **один инбаунд, без дубля, без порта 10096** |
|
||
> | outbounds | ✅ 13 |
|
||
> | balancer | ✅ `space-balancer` в конфиге |
|
||
> | подписка `/sub/24df9391356b48ff` | ✅ **HTTP 200** (QR теперь есть — впервые за 7 попыток) |
|
||
> | `kraken-user` | ✅ **не отвалился** (исправление урока попытки 6 сработало) |
|
||
> | `user1` | ✅ на месте |
|
||
> | **egress `vless-space`** | ❌ **НЕ работает** — `non existing outTag: space-balancer`, туннель рвётся `websocket: close 1000` |
|
||
>
|
||
> **🔴 Корень найден и доказан:** стратегия **`leastPing` не работает в Xray 26.x** — балансировщик не создаётся. Лечится заменой на **`leastLoad`**. Подробности — §«ГЛАВНАЯ НАХОДКА СЕССИИ» ниже.
|
||
>
|
||
> **Собран `xui_v7_load.db`** (md5 `855ff3ac2d2319b17b20fe5c788ddc59`, integrity `ok`) — `xui_v7.db` с `leastPing` → `leastLoad`. **Залит не был** (апрувы истекли) — но это уже неважно: финальная сборка `xui_v9.db` залита и работает (см. ниже).
|
||
>
|
||
> **Ключевой вывод, который опроверг прежний пессимизм:** правка клиентов 3x-ui через SQLite **СРАБОТАЛА** в попытке 7 — клиент появился в конфиге и подписка отдала 200. Барьер был **только** в том, что инбаунд лежал внутри шаблона (урок 6). При исправленной сборке SQLite-путь рабочий.
|
||
|
||
## Что хотели
|
||
|
||
Добавить в 3x-ui (`xray-admin` на TrueNAS) **третьего клиента** `vless-space`, чей трафик уходит в интернет **не** через TrueNAS (`direct`) и **не** через Кра́кена (`via-kraken`), а через **внешнюю подписку** провайдера `profilegrid.net` (28 строк → 10 уникальных серверов).
|
||
|
||
Требование Alex (дословно): «не трогая `user1` и `kraken-user` (reverse)», «авто брать» — т.е. **балансировщик по всем серверам подписки**, а не один конкретный.
|
||
|
||
## Ключевое различие, которое я сначала понял неверно
|
||
|
||
**Подписка — функция КЛИЕНТА, не сервера.** 3x-ui — это сервер (принимает входящие), он **не может** «ходить через чужую подписку» в inbound-режиме.
|
||
|
||
**НО** у 3x-ui есть `outbound_subscriptions` — функция, которая **скачивает подписку и превращает её серверы в свои outbound'ы**. Тогда сервер может: принять клиента → отправить его трафик через сервер из подписки. Это и есть нужный механизм.
|
||
|
||
**Схема:**
|
||
|
||
```text
|
||
[клиент vless-space] → vpn.mallexxx.duckdns.org:443 → Caddy → xray-admin:10095
|
||
│
|
||
routing: user=[vless-space]
|
||
▼
|
||
space-balancer (leastPing)
|
||
▼
|
||
10 × VLESS+REALITY outbound (mirrorgrid.net)
|
||
▼
|
||
интернет
|
||
```
|
||
|
||
## Подписка (источник серверов)
|
||
|
||
- **URL:** `https://go.profilegrid.net/sub/djMsNDc3MjgsMTc4OTQ1OTg5NA.WjZEVaps9Xl3s5dkD6x7KRyNIGi7MlDUNAVPMKBUOdk`
|
||
- **Проверено 2026-09-15 с TrueNAS:** `HTTP 200`, 12 412 байт, `text/plain`, base64
|
||
- **28 строк** `vless://`, из них **10 уникальных серверов** (остальное — дубли одного UUID на тех же хостах)
|
||
- **Один UUID на весь список:** `c67ce742-d94f-4e56-889e-9f894ae59940` (на разных хостах встречаются варианты `…-4e56-…` и `…-0008-…`)
|
||
- **Формат:** VLESS + `security=reality` + `type=tcp` + `flow=xtls-rprx-vision` + `fp=firefox`
|
||
- **Уникальность** определяется парой `host` + `pbk` (publicKey) + `sid` (shortId) — у каждого сервера свои.
|
||
|
||
### 10 уникальных серверов
|
||
|
||
| tag | имя | host |
|
||
|---|---|---|
|
||
| `space-01` | 🇩🇪 Germany | `edge-de.mirrorgrid.net:443` |
|
||
| `space-02` | 🇱🇻 Latvia \| YT | `edge-lv.mirrorgrid.net:443` |
|
||
| `space-03` | 🇳🇱 Netherlands \| YT | `edge-nl.mirrorgrid.net:443` |
|
||
| `space-04` | 🇳🇱 Netherlands #2 | `packages-nl.repocache.com:443` |
|
||
| `space-05` | 🇪🇪 Estonia \| YT | `edge-ee.mirrorgrid.net:443` |
|
||
| `space-06` | 🇸🇪 Sweden \| YT | `packages-se.repocache.com:443` |
|
||
| `space-07` | 🇲🇩 Moldova \| Torrent | `md.repodelivery.com:443` |
|
||
| `space-08` | 🇵🇱 Poland \| YT | `edge-pl.mirrorgrid.net:443` |
|
||
| `space-09` | 🇫🇷 France \| YT | `edge-fr.mirrorgrid.net:443` |
|
||
| `space-10` | 🇺🇸 USA \| YT | `edge-us.mirrorgrid.net:443` |
|
||
|
||
## Артефакты (на Mac)
|
||
|
||
```
|
||
~/tmp-xray-space/
|
||
├── apply_space_v2.sql ← ИСПРАВЛЕННЫЙ SQL (применять этот)
|
||
├── apply_space.sql ← первая (битая) версия — НЕ использовать
|
||
├── make_sql.py ← генератор v1 (битый)
|
||
├── make_sql_v2.py ← генератор v2 (с PRAGMA journal_mode)
|
||
├── build_template.py ← сборка xrayTemplateConfig из подписки
|
||
├── sub_decoded.txt ← развёрнутая подписка (28 строк vless://)
|
||
├── space_outbounds.json ← 10 новых outbound (справочно)
|
||
├── template_config.new.json ← новый xrayTemplateConfig (13 outbound)
|
||
├── template_config.json ← исходный шаблон из БД
|
||
├── runtime_config.json ← снимок /app/bin/config.json
|
||
├── xui_copy.db ← копия БД до правок
|
||
└── docker-compose.yml ← восстановленный compose xray-admin
|
||
```
|
||
|
||
## Параметры клиента
|
||
|
||
| | |
|
||
|---|---|
|
||
| email | `vless-space` |
|
||
| UUID | `a792c483-07e2-4723-9c50-78054c0abc07` |
|
||
| subId | `24df9391356b48ff` |
|
||
| sub-ссылка | `https://vpn-panel.mallexxx.duckdns.org/sub/24df9391356b48ff` |
|
||
| inbound | `in-10095-tcp` (id=1), port 10095, VLESS-WS |
|
||
| egress | `space-balancer` → 10 серверов подписки |
|
||
|
||
## Изменения в `xrayTemplateConfig`
|
||
|
||
Шаблон хранится в БД: таблица `settings`, ключ **`xrayTemplateConfig`**. 3x-ui генерирует из него `/app/bin/config.json` при старте.
|
||
|
||
Добавлено (существующее не тронуто):
|
||
|
||
**1. Routing-правило** (после `kraken-user-via-reverse`):
|
||
|
||
```json
|
||
{
|
||
"type": "field",
|
||
"user": ["vless-space"],
|
||
"outboundTag": "space-balancer",
|
||
"ruleTag": "vless-space-via-subscription"
|
||
}
|
||
```
|
||
|
||
**2. Балансировщик:**
|
||
|
||
```json
|
||
"balancers": [
|
||
{
|
||
"tag": "space-balancer",
|
||
"selector": ["space-"],
|
||
"strategy": { "type": "leastPing" }
|
||
}
|
||
]
|
||
```
|
||
|
||
**3. Observatory** (нужен для `leastPing`):
|
||
|
||
```json
|
||
"observatory": {
|
||
"subjectSelector": ["space-"],
|
||
"probeUrl": "https://www.google.com/generate_204",
|
||
"probeInterval": "30s",
|
||
"enableConcurrency": true
|
||
}
|
||
```
|
||
|
||
**4. 10 outbound'ов** `space-01`…`space-10`, формат:
|
||
|
||
```json
|
||
{
|
||
"tag": "space-NN",
|
||
"protocol": "vless",
|
||
"settings": { "vnext": [ { "address": "<host>", "port": 443, "users": [
|
||
{ "id": "c67ce742-…", "encryption": "none", "flow": "xtls-rprx-vision" } ] } ] },
|
||
"streamSettings": {
|
||
"network": "tcp",
|
||
"security": "reality",
|
||
"realitySettings": {
|
||
"serverName": "<host>", "fingerprint": "firefox",
|
||
"publicKey": "<pbk>", "shortId": "<sid>", "spiderX": "<spx>"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Итого:** было 3 outbound (`direct`, `blocked`, `via-kraken`) → стало 13. Правил было 4 → стало 5.
|
||
|
||
## 🔴 ПРОВАЛ ПОПЫТКИ 1 (2026-09-15) — `database disk image is malformed`
|
||
|
||
**Симптом:** после применения `apply_space.sql` запрос к БД вернул `Parse error in 2nd command line argument: database disk image is malformed (11)`.
|
||
|
||
**Причина (моя ошибка в процедуре, не в SQL):**
|
||
|
||
1. SQL применялся к **копии** базы (`/tmp/x-ui.db.work`), созданной **без** её `-wal`/`-shm`.
|
||
2. Копия клалась обратно как `/data/x-ui.db`.
|
||
3. Рядом на хосте оставались **СТАРЫЕ** `x-ui.db-wal` и `x-ui.db-shm` от предыдущего процесса.
|
||
4. SQLite при открытии увидела WAL-файл, попыталась применить **чужой журнал** к новой базе → `malformed`.
|
||
|
||
**Дополнительно:** в SQL не было `PRAGMA journal_mode=DELETE` — WAL-режим не отключался на время записи.
|
||
|
||
> 🔴 **ПИТФОЛЛ (повторяемый):** при правке SQLite-базы, работающей в WAL-режиме, **нельзя** копировать только `.db` и класть рядом со старыми `-wal`/`-shm`. Либо работать **напрямую** с базой при остановленном процессе, либо удалять `-wal`/`-shm` до открытия.
|
||
|
||
## ✅ ИСПРАВЛЕННАЯ ПРОЦЕДУРА (`apply_space_v2.sql`)
|
||
|
||
Отличия от v1:
|
||
- `PRAGMA journal_mode=DELETE;` в начале → WAL отключён на время сессии
|
||
- `PRAGMA journal_mode=WAL;` в конце → возврат к режиму, которого ждёт 3x-ui
|
||
- `INSERT OR REPLACE` вместо `INSERT` (идемпотентность)
|
||
- **Работа идёт НАПРЯМУЮ с `/data/x-ui.db`**, не через копию
|
||
|
||
### Команды
|
||
|
||
```bash
|
||
# 1) залить SQL
|
||
scp ~/tmp-xray-space/apply_space_v2.sql truenas_admin@mallexxx.duckdns.org:/tmp/
|
||
|
||
# 2) стоп (3x-ui сам сбросит WAL)
|
||
ssh truenas_admin@mallexxx.duckdns.org 'docker stop xray-admin'
|
||
|
||
# 3) бэкап
|
||
ssh truenas_admin@mallexxx.duckdns.org '
|
||
TS=$(date +%Y%m%d-%H%M%S)
|
||
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/d -v /mnt/RED_2TB/docker/backups:/b alpine sh -c "
|
||
mkdir -p /b/xray-admin-v2-\$TS && cp -av /d/x-ui.db /b/xray-admin-v2-\$TS/
|
||
echo BACKUP_DIR=/b/xray-admin-v2-\$TS"'
|
||
|
||
# 4) применить — ВАЖНО: rm WAL до SQL, работа напрямую
|
||
ssh truenas_admin@mallexxx.duckdns.org '
|
||
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/data -v /tmp:/src alpine sh -c "
|
||
apk add --no-cache sqlite >/dev/null 2>&1
|
||
cd /data
|
||
rm -f x-ui.db-wal x-ui.db-shm
|
||
echo \"before=\$(sqlite3 x-ui.db \\\"PRAGMA integrity_check;\\\")\"
|
||
sqlite3 x-ui.db < /src/apply_space_v2.sql
|
||
echo \"sql_exit=\$?\"
|
||
echo \"after=\$(sqlite3 x-ui.db \\\"PRAGMA integrity_check;\\\")\"
|
||
sqlite3 x-ui.db \"select email, sub_id from clients order by id;\"
|
||
chown 950:root x-ui.db"'
|
||
|
||
# 5) старт
|
||
ssh truenas_admin@mallexxx.duckdns.org 'docker start xray-admin && sleep 6 && docker logs --tail 12 xray-admin'
|
||
|
||
# 6) проверка
|
||
ssh truenas_admin@mallexxx.duckdns.org '
|
||
docker exec xray-admin cat /app/bin/config.json | /usr/bin/jq -r ".inbounds[].settings.clients[]?.email"
|
||
docker exec xray-admin cat /app/bin/config.json | /usr/bin/jq -r ".outbounds | length"
|
||
docker exec xray-admin cat /app/bin/config.json | /usr/bin/jq -c ".routing.balancers"
|
||
curl -sk -o /dev/null -w "HTTP %{http_code}\n" "https://vpn-panel.mallexxx.duckdns.org/sub/24df9391356b48ff"'
|
||
```
|
||
|
||
**Ожидаемо:** клиенты `user1`, `kraken-user`, `vless-space` / outbounds `13` / balancer `space-balancer` / `HTTP 200`.
|
||
|
||
### Откат
|
||
|
||
```bash
|
||
# A — из бэкапа шага 3
|
||
ssh truenas_admin@mallexxx.duckdns.org '
|
||
docker stop xray-admin
|
||
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/d -v /mnt/RED_2TB/docker/backups:/b alpine sh -c "
|
||
cp -av /b/xray-admin-v2-<TS>/x-ui.db /d/x-ui.db
|
||
rm -f /d/x-ui.db-wal /d/x-ui.db-shm && chown 950:root /d/x-ui.db"
|
||
docker start xray-admin'
|
||
|
||
# B — из исходного бэкапа (до всех правок)
|
||
# /mnt/RED_2TB/docker/backups/xray-admin-before-vless-space-20260915-012057/
|
||
# (только x-ui.db; -wal/-shm удалить, не копировать)
|
||
```
|
||
|
||
`user1` и `kraken-user` **не изменяются ни в одной версии SQL** — откат нужен только если сломалась БД.
|
||
|
||
## 🔴 ИСТОРИЯ (промежуточная попытка) ФИНАЛ 2026-09-15: ОТКАЧЕНО, ЗАДАЧА НЕ ВЫПОЛНЕНА
|
||
|
||
> ⚠️ **Не текущий статус.** Это срез промежуточной попытки. Задача закрыта позже: залита `xui_v9.db`, egress работает, далее — штатная подписка `sub1-*`. Актуальный статус: `status: done-egress-via-subscription-working`.
|
||
|
||
Alex восстановил БД из бэкапа. **Проверено фактом после отката:**
|
||
|
||
| Проверка | Результат |
|
||
|---|---|
|
||
| `xray-admin` | Up |
|
||
| панель `vpn-panel.mallexxx.duckdns.org` | HTTP 200 |
|
||
| `vpn.mallexxx.duckdns.org` (Xray-сервер) | HTTP 200 |
|
||
| подписка `user1` (`/sub/68c5cy5n5ui138yh`) | HTTP 200 |
|
||
| подписка `kraken-user` (`/sub/f43074a029dc656e`) | HTTP 200 |
|
||
| клиенты в конфиге | `user1`, `kraken-user` |
|
||
| outbound'ов | 3 |
|
||
|
||
**Пережило откат (единственный полезный артефакт в системе):** `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml` — восстановленный compose, `docker compose config` валиден, `docker compose ps` распознаёт контейнер.
|
||
|
||
> 🔴 **Хард-вывод для будущих сессий: НЕ тратить попытки на правку клиентов 3x-ui через SQLite. Сразу просить доступ к панели (пароль или API-токен).** Шесть подходов в этой сессии (копия БД, прямой SQL, `auth`/`reverse`, `security`, `password`, инбаунд-в-шаблоне) — все отбились, при том что БД каждый раз была полностью корректна. Провал не в данных, а в архитектуре 3x-ui.
|
||
|
||
## 🔴 ПИТФОЛЛ 2026-09-15 (найден на 6-й попытке): инбаунд ВНУТРИ `xrayTemplateConfig` ломает своих клиентов
|
||
|
||
**Что делалось:** собрана БД `xui_v6.db`, где `vless-space` положен в **существующий** `in-10095-tcp` — но не в таблицу `inbounds`, а **в сам `settings.xrayTemplateConfig`** (инбаунд `in-10095-tcp` объявлен внутри шаблона рядом с `api`, с тремя клиентами).
|
||
|
||
**Симптом у Alex после заливки:** `kraken-user` отвалился. Вывод `jq` показывал `in-10095-tcp` **дважды** — с `user1,kraken-user,vless-space` (из шаблона) и с `user1,kraken-user` (из таблицы `inbounds`).
|
||
|
||
**Причина:** 3x-ui **мёржит** шаблон с таблицей `inbounds`. Инбаунд с тем же тегом (`in-10095-tcp`) из шаблона **перебивает** тот, которым управляет панель: панель рендерит клиентов из своего кэша (`client_traffics`/`client_inbounds`), а шаблонная версия инбаунда их **перетирает** — egress-правила привязываются к email, которых в активном инбаунде не остаётся.
|
||
|
||
**Как выглядела рабочая (живая) БД — эталон:**
|
||
|
||
```
|
||
шаблон (xrayTemplateConfig): api + 13 outbounds + 5 routing-rules + balancer + observatory
|
||
⛔ in-10095-tcp В ШАБЛОНЕ НЕТ ВООБЩЕ
|
||
таблица inbounds (id=1): in-10095-tcp port=10095 clients=user1,kraken-user
|
||
```
|
||
|
||
**Вывод — как правильно:**
|
||
|
||
| Что | Где держать |
|
||
|---|---|
|
||
| `space-01…10`, правило `vless-space-via-subscription`, `space-balancer`, `observatory` | ✅ **в `xrayTemplateConfig`** (работает) |
|
||
| `in-10095-tcp` **сам инбаунд** | ⛔ **НЕ в шаблоне** — только в таблице `inbounds`, которой владеет панель |
|
||
| клиент `vless-space` | ⛔ извне не добавляется (см. выше) — **только панель/API** |
|
||
|
||
> 🔴 **Сухой остаток сессии: не мешать два механизма.** Шаблон = только «дополнение» (outbound'ы/правила). Всё, что касается инбаундов и их клиентов, — эксклюзивно панель. Попытка протащить инбаунд через шаблон ломает чужих клиентов (`kraken-user`), а не решает задачу.
|
||
|
||
**Откат:** Alex восстановил БД из бэкапа; система вернулась к `user1` + `kraken-user`, 3 outbound'а.
|
||
|
||
## Открытые риски (проверено)
|
||
|
||
1. **`leastPing` + `observatory`** — ✅ **принимаются** (3x-ui 3.7.0 / Xray 26.7.28). Проверено фактом: после применения шаблона контейнер поднялся, `config.json` содержит `balancers` с `leastPing`, Xray-процесс запущен (`bin/xray-linux-amd64 -c bin/config.json`), в логах ошибок нет — только безобидные `WARNING common/protocol/http: received "X-Forwarded-For" … "sockopt.trustedXForwardedFor" is not configured` (Caddy шлёт XFF, на работу не влияет).
|
||
2. **~~`auth`/`reverse` = NULL~~** — ❌ **опровергнуто**, не было причиной. См. «Корень опровергнут».
|
||
3. **Ссылка подписки `vpn-panel…/sub/<subId>` открыта БЕЗ авторизации** — любой, кто знает `subId`, получит ключ. Кандидат на включение подписки-по-токену в панели (не сделано).
|
||
4. **🔴 Доступа к панели 3x-ui у агента нет** — единственный реальный блокер задачи. Пароль bcrypt, API-токен не выдан.
|
||
|
||
## Что НЕ сделано / как довести до конца
|
||
|
||
- [x] ~~Применить `apply_space_v2.sql`~~ → применён, БД была корректна
|
||
- [x] ~~Исправить `auth`/`reverse`/`flow` с `NULL`~~ → исправлено, **не помогло** (теория опровергнута)
|
||
- [x] ~~Дописать `security:"auto"` в `inbounds.settings`~~ → применено, **не помогло**
|
||
- [x] ~~Восстановить compose-файл~~ → **сделано** (см. [[family/plans/reverse-xray-3xui-kraken]])
|
||
- [x] ~~Откатить БД~~ → **сделано Alex'ом**, система в исходном состоянии
|
||
- [ ] **❌ ТЕКУЩИЙ БЛОКЕР: нет доступа к панели 3x-ui.** Логин `vpn-admin`, пароль — bcrypt-хэш (`$2a$10$JJscByJjUHJyqmp1a8LZ7egLY0g.XbIoY…`), нечитаем. `secret` из БД (`RuERf2DTzPTw3CoMcVjk7tGXKCQOk0Z4`) для логина не подошёл (403), API-путь вернул 404.
|
||
- [ ] **Довести задачу одним из двух:** (а) Alex добавляет клиента в панели — Inbounds → `vless-ws` (10095) → Edit → Add Client (`vless-space` / `a792c483-07e2-4723-9c50-78054c0abc07` / subId `24df9391356b48ff`) → Save → **Restart Xray**; (б) Alex создаёт API-токен (Settings → API Tokens) и агент делает всё сам через `/panel/api/inbounds/update/1`.
|
||
- [ ] После появления клиента — routing-правило на `space-balancer` **тоже через панель** (Xray → Routing)
|
||
- [ ] Проверить end-to-end: egress IP ≠ `90.189.160.148` (TrueNAS) и ≠ `92.62.70.41` (Kraken)
|
||
- [ ] Решить вопрос с авторизацией на sub-ссылках (открыты без авторизации)
|
||
- [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`
|
||
|
||
> **Это объясняет ошибку `non existing outTag: space-balancer`.** Балансировщик в конфиге есть, Xray конфиг принимает (`Configuration OK`), но **тег не регистрируется**, и правило `vless-space-via-subscription` уходит в никуда → клиент подключается, туннель строится, трафик обрывается (`websocket: close 1000`).
|
||
|
||
**Проверено на минимальном конфиге в докере на TrueNAS (тройной A/B):**
|
||
|
||
| Конфиг | Результат |
|
||
|---|---|
|
||
| `strategy: leastPing` + `observatory` | ❌ `app/dispatcher: non existing outTag: space-balancer` |
|
||
| `burstObservatory` + `pingConfig` + `leastPing` | ❌ то же самое |
|
||
| **`strategy: leastLoad` + `observatory`** | ✅ **работает:** `app/observatory: the outbound space-01 is alive:0.249334909`, `app/dispatcher: taking platform initialized detour [space-02]` |
|
||
|
||
**Вывод:** в Xray 26.x стратегия `leastPing` **устарела/несовместима** — балансировщик с ней не создаётся вообще. Рабочая стратегия — **`leastLoad`** (нативная для `observatory`).
|
||
|
||
**Команда проверки (минимальный конфиг, доказательство):**
|
||
```bash
|
||
# outbounds: [{"tag":"space-01","protocol":"freedom"},{"tag":"space-02","protocol":"freedom"}]
|
||
# routing.balancers: [{"tag":"space-balancer","selector":["space-"],"strategy":{"type":"leastLoad"}}]
|
||
# routing.rules: [{"type":"field","inboundTag":["socks-in"],"outboundTag":"space-balancer"}]
|
||
# observatory: {"subjectSelector":["space-"],"probeUrl":"https://www.google.com/generate_204","probeInterval":"30s","enableConcurrency":true}
|
||
docker run --name probe-load -d --network host -v /tmp/probe_load.json:/etc/xray/config.json:ro ghcr.io/xtls/xray-core:latest -c /etc/xray/config.json
|
||
docker logs probe-load 2>&1 | grep -iE 'observatory|detour|non existing'
|
||
```
|
||
|
||
> ⚠️ **Питфолл:** `xray -test -c config.json` → `Configuration OK` **НЕ гарантирует**, что балансировщик создастся. Ошибка `non existing outTag` видна **только в рантайм-логе** при первом реальном соединении. Проверять всегда живым запросом, не только `-test`.
|
||
|
||
> ⚠️ **Питфолл docker volume:** файл конфига, залитый через `scp` в `/tmp`, при монтировании в контейнер даёт `permission denied` — нужен `chmod 644` перед `docker run`.
|
||
|
||
## Артефакты на Mac — итоговые (`~/tmp-xray-space/`)
|
||
|
||
| Файл | Назначение |
|
||
|---|---|
|
||
| **`apply_final.sql`** | **ФИНАЛЬНЫЙ SQL** (13 447 б): клиент + инбаунд + шаблон с 10 серверами + правило + балансировщик + запись в `outbound_subscriptions`. Не применён. |
|
||
| **`xui_v7.db`** | **Сборка попытки 7** (историческая): оригинал + шаблон без инбаунда (13 outbound + balancer + observatory) + `vless-space` в таблице `inbounds`. `integrity ok`, 270 336 б. **Тогда не залита; позже заменена на `xui_v9.db` (рабочая).** |
|
||
| `xui_v3.db` / `xui_v4.db` / `xui_v5.db` / `xui_v6.db` | предыдущие сборки (в v3 — отдельный инбаунд `in-10096-space`; в v5/v6 — инбаунд внутри шаблона, сломало `kraken-user`) |
|
||
| `xui_live.db` / `xui_live2.db` | промежуточные копии (обе уже содержат правки — НЕ эталон) |
|
||
| `tpl_v5.json` / `tpl_v6.json` / `tpl_v7_final.json` | извлечённые шаблоны соответствующих сборок |
|
||
| `make_final.py` | генератор `apply_final.sql` |
|
||
| `inbound_settings.json` | снимок `inbounds.settings` из живой БД (2 клиента) |
|
||
| `tpl_live.json` / `template_config.json` | шаблон из живой БД (2129 б, 3 outbound, 4 правила) |
|
||
| `template_config.new.json` | шаблон с 13 outbound (v1, без правки инбаунда) |
|
||
| `apply_space_v2.sql` / `apply_space.sql` | SQL v2 (с `PRAGMA journal_mode`) / v1 (битый) |
|
||
| `make_sql_v2.py` / `make_sql.py` | генераторы v2 / v1 |
|
||
| `fix_security.sql` | правка `security` в `inbounds.settings` (применена, не помогла) |
|
||
| `make_fix_security.py` | генератор |
|
||
| `build_template.py` | парсер `vless://` → xray-outbound |
|
||
| `sub_decoded.txt` | подписка развёрнутая (28 строк) |
|
||
| `space_outbounds.json` | 10 outbound'ов (отчёт) |
|
||
| `runtime_config.json` | снимок `/app/bin/config.json` (3 клиента, 3 outbound) |
|
||
| `xui_copy.db` / `test_apply.db` | копия живой БД / песочница |
|
||
| `docker-compose.yml` | **восстановленный compose `xray-admin`** |
|
||
| `inbound_settings.json` | снимок инбаунда для финального SQL |
|
||
|
||
## 🏁 MILESTONE 2026-09-15 — точная процедура, которая сработала (повторяемо)
|
||
|
||
**Задача:** новый клиент 3x-ui с egress через внешнюю подписку, не трогая существующих.
|
||
|
||
### Шаг 1 — вытащить и разобрать подписку
|
||
```bash
|
||
curl -s "https://go.profilegrid.net/sub/<TOKEN>" | base64 -d > sub_decoded.txt
|
||
# дедуп по host+publicKey+shortId → 10 уникальных серверов
|
||
# парсер vless:// → xray-outbound: build_template.py
|
||
```
|
||
|
||
### Шаг 2 — собрать шаблон ЛОКАЛЬНО (на Mac)
|
||
```bash
|
||
cp xui_copy.db xui_vN.db # собирать ВСЕГДА от оригинала
|
||
sqlite3 xui_copy.db "SELECT value FROM settings WHERE key='xrayTemplateConfig';" > /tmp/base_orig.json
|
||
# ДОБАВИТЬ: 10 outbound space-01…10, правило, balancer, observatory
|
||
jq --slurpfile sp /tmp/space_outs.json '.outbounds += $sp[0]
|
||
| .routing.rules += [{"type":"field","user":["vless-space"],
|
||
"balancerTag":"space-balancer", # ← КЛЮЧ 1
|
||
"ruleTag":"vless-space-via-subscription"}]
|
||
| .routing.balancers = [{"tag":"space-balancer","selector":["space-"],
|
||
"strategy":{"type":"leastLoad"}}] # ← КЛЮЧ 2
|
||
| .observatory = {"subjectSelector":["space-"],
|
||
"probeUrl":"https://www.google.com/generate_204",
|
||
"probeInterval":"30s","enableConcurrency":true,
|
||
"sampling":3}' # ← КЛЮЧ 3
|
||
/tmp/base_orig.json > /tmp/tpl_final.json
|
||
```
|
||
⛔ **`.inbounds` в шаблоне НЕ трогать** — там остаётся только `api`.
|
||
|
||
### Шаг 3 — клиент в таблицы
|
||
```bash
|
||
# inbounds.settings JSON (id=1) + clients + client_inbounds, id=5
|
||
# client_traffics НЕ трогать — 3x-ui заполняет сам
|
||
sqlite3 xui_vN.db "PRAGMA journal_mode=DELETE;"; rm -f xui_vN.db-wal xui_vN.db-shm
|
||
sqlite3 xui_vN.db "PRAGMA integrity_check;" # → ok
|
||
```
|
||
|
||
### Шаг 4 — заливка
|
||
```bash
|
||
scp xui_vN.db truenas_admin@mallexxx.duckdns.org:/tmp/
|
||
docker stop xray-admin
|
||
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/data -v /mnt/RED_2TB/docker/backups:/b -v /tmp:/src alpine sh -c 'TS=$(date +%Y%m%d-%H%M%S); mkdir -p /b/xray-admin-vN-$TS; cp -av /data/x-ui.db /b/xray-admin-vN-$TS/; rm -f /data/x-ui.db-wal /data/x-ui.db-shm; cp -av /src/xui_vN.db /data/x-ui.db; chown 950:root /data/x-ui.db; echo BACKUP=/b/xray-admin-vN-$TS'
|
||
docker start xray-admin
|
||
```
|
||
|
||
### Шаг 5 — проверка (все три обязательны)
|
||
```bash
|
||
docker exec xray-admin cat /app/bin/config.json | /usr/bin/jq -r '.inbounds[].settings.clients[]?.email'
|
||
docker exec xray-admin cat /app/bin/config.json | /usr/bin/jq -c '{bal:.routing.balancers[0], ob:.observatory.sampling}'
|
||
docker logs --since 1m xray-admin 2>&1 | grep -c 'non existing' # → 0
|
||
```
|
||
**Плюс end-to-end через Xray-клиент в докере** — `api.ipify.org` должен вернуть НЕ `90.189.160.148`.
|
||
|
||
### Диагностика: как отличить три дефекта по симптому
|
||
|
||
| Симптом в рантайм-логе | Причина | Фикс |
|
||
|---|---|---|
|
||
| `non existing outTag: space-balancer` | правило ссылается через `outboundTag` | → `balancerTag` |
|
||
| то же, без `alive` в логе | нет `sampling` в observatory | → `"sampling": 3` |
|
||
| то же, `alive` нет, `sampling` есть | стратегия `leastPing` | → `leastLoad` |
|
||
| клиент в `config.json` отсутствует | инбаунд лежит **внутри** шаблона | убрать из шаблона |
|
||
| `in-10095-tcp` **дважды** в `jq` | то же (шаблонная копия + панельная) | то же |
|
||
| `database disk image is malformed` | копию БД положили рядом со старыми `-wal`/`-shm` | `rm -f *.db-wal *.db-shm` до старта |
|
||
|
||
### Откат (любой версии)
|
||
```bash
|
||
docker stop xray-admin
|
||
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/d -v /mnt/RED_2TB/docker/backups:/b alpine sh -c 'cp -av /b/xray-admin-v9-<TS>/x-ui.db /d/x-ui.db; rm -f /d/x-ui.db-wal /d/x-ui.db-shm; chown 950:root /d/x-ui.db'
|
||
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-инцидента
|
||
- [[personal/tech/xray-reverse-tunnel-kraken-truenas]] — архитектура reverse
|
||
- [[family/how-to/truenas-infrastructure]] — инфраструктура TrueNAS, контейнер `xray-admin`
|
||
- [[family/how-to/vps-qentra]] — удалённый VPS (объясняет, почему `vless-proxy` мёртв)
|