From ee95685f4efea6cdcc4a4144e7089184d538365a Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Tue, 15 Sep 2026 17:14:52 +0600 Subject: [PATCH] [2026-09-15] eagle: family/how-to/truenas-infrastructure.md personal/tech/vless-space-subscription-egress.md personal/tech/xray-outbound-subscription-3xui.md --- family/how-to/truenas-infrastructure.md | 74 ++++++++++++++----- .../tech/vless-space-subscription-egress.md | 49 +++++++++--- .../tech/xray-outbound-subscription-3xui.md | 38 +++++----- 3 files changed, 112 insertions(+), 49 deletions(-) diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md index b745e1d1..b735efd4 100644 --- a/family/how-to/truenas-infrastructure.md +++ b/family/how-to/truenas-infrastructure.md @@ -13,16 +13,18 @@ > - **⚠️ «Камера на TrueNAS» — ИСПРАВЛЕНО (2026-09-14, вечер-10) + ✅ РЕШЕНО ОКОНЧАТЕЛЬНО (вечер-13):** прежняя запись «камера = USB-вебка на t610, upstream `cam.*:8090` к камере отношения не имеет» — **❌ НЕВЕРНА**. Поиск в `Caddyfile.bak` доказал: **`cam.mallexxx.duckdns.org → 192.168.2.197:8090` — ЭТО И БЫЛА камера** на TrueNAS: отдельный HTTP-MJPEG-сервис (`ustreamer`/`mjpg-streamer`, порт 8090 — канон для «USB-вебка → MJPEG»). **Контейнер УТРАЧЕН** при пересоздании пула (локальный образ не пережил `.ix-apps`; из живого Caddyfile строка удалена — `cam.*` больше нет). **✅ ФИНАЛ (вечер-13):** вебка `046d:0825` физически в **t610**, работает через **аддон `a889bffc_go2rtc-hardware`** → RTSP H.264 `rtsp://192.168.2.176:8554/usb_camera_h264` → **Generic Camera** `camera.192_168_2_176` (зона `kotelnaia`, `unique_id`, **WebRTC работает**, поворот `#rotate=90`). Схема `camera: platform: ffmpeg` в Core — **❌ ОТВЕРГНУТА** (`Resource busy` + нет `unique_id`). Подробно — [[family/how-to/home-automation]] §5-кватер-И-3/И-6. > - **Не перенесено с TrueNAS:** ~~погашение TrueNAS-стека (Этап 4 п.6 — заблокировано: Caddy на TrueNAS держит точку входа)~~ → **✅ ЗАКРЫТО 2026-09-14 (ночь): стек автоматизации ПОГАШЕН, Caddy остался на TrueNAS (так и задумано).** См. §5-кватер-Л в [[family/how-to/home-automation]]. -> ## 🏁 2026-09-15 22:30 — `vless-space` ПОЛНОСТЬЮ РАБОТАЕТ. EGRESS ИДЁТ ЧЕРЕЗ ПОДПИСКУ. +> ## 🏁 2026-09-15 (ФИНАЛ) — ШТАТНАЯ ПОДПИСКА РАБОТАЕТ, СТАТИКА `space-` ВЫЧИЩЕНА. `vless-proxy` ПОДКЛЮЧЁН. > -> **Проверено фактом (Xray-клиент в докере на NAS, `xui_v9.db`):** IP через `vless-space` = `195.72.61.192` (**Стокгольм, SE**) / `104.28.225.223` ≠ `90.189.160.148` (IP TrueNAS) → трафик идёт через серверы подписки `profilegrid`. 3/3 запроса подряд стабильно. +> **Итог:** egress через **штатный механизм `outbound_subscriptions`** (28 серверов `sub1-*`, авто-обновление 600 с). Статические `space-01…10` **удалены** как костыль. Живой тест: `vless-proxy:1080` → `104.28.219.140` / `188.239.191.18` ≠ `90.189.160.148`. > -> | Что | Состояние | +> | Что | Значение | > |---|---| -> | `in-10095-tcp` clients | ✅ `user1,kraken-user,vless-space` — один инбаунд, порта 10096 нет | -> | outbounds | ✅ 13 + balancer `space-balancer` (`leastLoad`) | -> | подписка `/sub/24df9391356b48ff` | ✅ HTTP 200 (QR есть) | -> | `user1`, `kraken-user` | ✅ целы, не тронуты | +> | `outbound_subscriptions` | `remark=vless-space`, `tag_prefix=sub1-`, `update_interval=600` | +> | outbounds | 31 = `direct`+`blocked`+`via-kraken` + **28 × `sub1-*`** | +> | балансировщик | `space-balancer`, `selector: ["sub1-"]`, `leastLoad` | +> | observatory | **`burstObservatory`**, `subjectSelector: ["sub1-"]`, `pingConfig{sampling:2, interval:1m}` | +> | `routing.rules` | `{"user":["vless-space"],"balancerTag":"space-balancer"}` | +> | клиенты | `user1`, `kraken-user`, `vless-space` — целы | > > ### 🔑 ТРИ ПИТФОЛЛА БАЛАНСИРОВЩИКА — все три обязательны > @@ -32,21 +34,55 @@ > |---|---|---| > | 1 | правило ссылалось через `outboundTag` | → **`balancerTag`** | > | 2 | `strategy: leastPing` (в Xray 26.x балансировщик не создаётся) | → **`leastLoad`** | -> | 3 | в `observatory` отсутствовал `sampling` | → **`"sampling": 3`** | +> | 3 | в observatory отсутствовал `sampling` | → **`"sampling": 3`** | > -> ```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": { "subjectSelector":["space-"], "probeUrl":"https://www.google.com/generate_204", -> "probeInterval":"30s", "enableConcurrency":true, "sampling":3 } +> ### 🔴 ЧЕТВЁРТЫЙ ПИТФОЛЛ (стоил этой сессии) — два поля селектора + два имени секции +> +> **а) `selector` и `subjectSelector` — РАЗНЫЕ поля, менять ПАРОЙ.** Сменил префикс подписки → проверь оба: +> ```bash +> docker exec xray-admin cat /app/bin/config.json | jq -c '{bal:.routing.balancers[0].selector, obs:(.burstObservatory // .observatory).subjectSelector}' +> # оба должны быть ["sub1-"] +> ``` +> **б) Секция в UI называется `burstObservatory`, не `observatory`.** Xray 26.x принимает обе; панель при сохранении может удалить `observatory` и создать `burstObservatory`. **Проверять `keys[]`, оба имени:** +> ```bash +> docker exec xray-admin cat /app/bin/config.json | jq -r 'keys[]' +> docker exec xray-admin cat /app/bin/config.json | jq -c '.burstObservatory // .observatory' +> ``` +> **в) Симптом рассинхрона — молчаливый `direct`:** `subjectSelector` не матчит ни один outbound → `leastLoad` без живых кандидатов → трафик в **первый outbound списка**. **Ошибок в логе НЕТ.** +> +> ⚠️ **`xray -test` печатает `Configuration OK` при всех этих ошибках** — семантику маршрутизации ловит только рантайм-лог (`non existing outTag`) + живой запрос через клиент. +> +> **Артефакты:** `~/tmp-xray-space/` — `xui_v9.db` (статика, рабочая история), `xui_v10.db`, `xui_v10_src.db`, `strip_space_outbounds.sh`, `write_v10.sh`, `vless-proxy-config-new.json`. Бэкапы `/mnt/RED_2TB/docker/backups/xray-admin-before-cleanup-20260915-035749/` + `xray-admin-before-vless-space-20260915-012057` (точка отката). +> +> --- +> +> ## 🔗 `vless-proxy` → `xray-admin` (2026-09-15) +> +> **Что было:** `vless-proxy` (SOCKS `:1080` + HTTP `:1081`) смотрел на **мёртвый** `v.qentra.top` → `hermes-taiga` (Telegram/Discord) без сети. +> +> **Что стало** — `/mnt/RED_2TB/docker/vless-proxy/config.json`: +> +> | Поле | Было | Стало | +> |---|---|---| +> | `vnext[0].address` | `v.qentra.top` | `vpn.mallexxx.duckdns.org` | +> | `vnext[0].users[0].id` | `2D9F24C4-…` | `a792c483-07e2-4723-9c50-78054c0abc07` (`vless-space`) | +> | `tlsSettings.serverName` | `v.qentra.top` | `vpn.mallexxx.duckdns.org` | +> | `wsSettings.path` | `/qentra` | `/vless` | +> | `wsSettings.headers.Host` | `v.qentra.top` | `vpn.mallexxx.duckdns.org` | +> +> Порт 443, `security: tls`, `network: ws` — без изменений. +> +> **Цепочка:** `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-соединения устанавливаются на каждый запрос. +> +> 🔴 **Правильная проверка egress (питфолл):** `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" +> # ожидается НЕ 90.189.160.148 > ``` > -> ⚠️ **`xray -test` печатает `Configuration OK` при всех трёх ошибках** — семантику маршрутизации ловит только рантайм-лог + живой запрос через клиент. -> -> **Артефакты:** `~/tmp-xray-space/xui_v9.db` (md5 `8113add5b1d43a54f54253324eed2f1c`) — залита. Бэкапы `/mnt/RED_2TB/docker/backups/xray-admin-v{9,8,7load,7}-*` + исходный `xray-admin-before-vless-space-20260915-012057`. +> 🔴 **ДОСТУП:** `/mnt/RED_2TB/docker/vless-proxy/` — `root:root 755`, `config.json` примонтирован `:ro`. **`truenas_admin` писать НЕ может** (`touch` → Permission denied; `sudo` требует пароль; `root@` — publickey denied). Файл правит **Alex от root**. Агент готовит конфиг локально и отдаёт целиком. > > 🔴 **Прежний хард-вывод «3x-ui нельзя править снаружи» — ОКОНЧАТЕЛЬНО ОПРОВЕРГНУТ.** SQLite-путь работает при двух условиях: (1) в `xrayTemplateConfig` нет инбаунда (только `api`), (2) БД правится локально на Mac и кладётся файлом с `rm -f *.db-wal *.db-shm` + `chown 950:root`. > diff --git a/personal/tech/vless-space-subscription-egress.md b/personal/tech/vless-space-subscription-egress.md index 2c6780f4..986f5055 100644 --- a/personal/tech/vless-space-subscription-egress.md +++ b/personal/tech/vless-space-subscription-egress.md @@ -18,7 +18,7 @@ tags: - balancerTag - sampling confidence: high -status: blocked-egress-direct-observatory-mismatch +status: done-egress-via-subscription-working related: - '[[family/plans/reverse-xray-3xui-kraken]]' - '[[personal/tech/xray-reverse-tunnel-kraken-truenas]]' @@ -28,23 +28,50 @@ related: # `vless-space` — клиент 3x-ui, egress через внешнюю подписку -> # 🔴 2026-09-15 (финал сессии) — EGRESS `direct`. НЕ ЗАКРЫТО. +> # 🏁 2026-09-15 (ЗАКРЫТО) — РАБОТАЕТ на штатной подписке. `space-` вычищен. > -> **Живая проверка Alex: IP = `90.189.160.148` (TrueNAS)** → трафик `vless-space` НЕ идёт через подписку. +> **Итог сессии:** статика `space-01…10` удалена, egress идёт через 28 серверов штатной подписки `sub1-*`. > -> **Причина найдена:** `observatory.subjectSelector` = `["space-"]`, а outbound'ы теперь `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` — целы | > -> ```jsonc -> "balancers": [ { "tag":"space-balancer", "selector":["sub1-"], "strategy":{"type":"leastLoad"} } ] // ✅ -> "observatory": { "subjectSelector":["space-"], ... "sampling":3 } // ❌ ПУСТО -> "outbounds": 31 (3 + 28 × sub1-*) // ✅ +> --- +> +> ## 🔴 ПИТФОЛЛ (стоил сессии): поле 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` матчит `space-*` → после удаления статики **не пингуется ни один сервер** → `leastLoad` без живых кандидатов → трафик в первый outbound = `direct`. **Ошибок в логе НЕТ.** +> ## 🔴 ПИТФОЛЛ: селектор балансировщика И `subjectSelector` observatory надо менять ПАРОЙ > -> **ЧТО ОСТАЛОСЬ:** заменить `subjectSelector` → `["sub1-"]` (UI `Xray → Observatory` либо `xrayTemplateConfig` при остановленном контейнере), затем живой тест. Полный порядок — в [[personal/tech/xray-outbound-subscription-3xui]] §«Статус: ЧТО ОСТАЛОСЬ». +> При переходе `space-` → `sub1-` недостаточно поменять `routing.balancers[].selector`. `observatory/burstObservatory.subjectSelector` — **отдельное поле**, и если его забыть, балансировщик не находит живых кандидатов. > -> > ⚠️ Блок «ЗАДАЧА ВЫПОЛНЕНА» ниже относится к `xui_v9.db` со **статическими** `space-01…10` — та конфигурация работала. Она заменена штатной подпиской (`sub1-*`), где egress пока нет. +> ## 🔴 ПИТФОЛЛ: подмена `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`) > diff --git a/personal/tech/xray-outbound-subscription-3xui.md b/personal/tech/xray-outbound-subscription-3xui.md index a9b2c3b2..d6ced758 100644 --- a/personal/tech/xray-outbound-subscription-3xui.md +++ b/personal/tech/xray-outbound-subscription-3xui.md @@ -19,7 +19,7 @@ tags: - subjectSelector - wal confidence: high -status: blocked-egress-direct-observatory-mismatch +status: done-egress-via-subscription-working related: - '[[family/how-to/truenas-infrastructure]]' - '[[personal/tech/xray-reverse-tunnel-kraken-truenas]]' @@ -29,29 +29,29 @@ related: # Xray outbound из внешней подписки в 3x-ui (`vless-space`) -> ## 🔴 2026-09-15 (финал сессии) — EGRESS ВСЁ ЕЩЁ `direct`. НЕ ЗАКРЫТО. +> ## 🏁 2026-09-15 (ЗАКРЫТО) — EGRESS РАБОТАЕТ через штатную подписку > -> **Статус на конец сессии:** архитектура собрана, но трафик `vless-space` уходит в `direct`, а не через подписку. Подтверждено Alex живым запросом — **IP = `90.189.160.148` (TrueNAS)**, т.е. egress НЕ работает. +> **Статус:** всё собрано и проверено живым запросом. `space-` вычищен, 28 серверов подписки `sub1-*` работают. > -> **Что осталось (одна правка, не выполнена):** `observatory.subjectSelector` = `["space-"]`, а должен быть `["sub1-"]`. +> | Проверка (факт) | Результат | +> |---|---| +> | `vless-proxy:1080` egress | ✅ `104.28.219.140` / `188.239.191.18` — НЕ `90.189.160.148` | +> | `outbounds` | 31 = 3 базовых + **28 × `sub1-*`** | +> | `balancers` | `space-balancer`, `selector: ["sub1-"]`, `leastLoad` | +> | `burstObservatory.subjectSelector` | `["sub1-"]` | +> | `non existing outTag` в логе | 0 | +> | клиенты 10095 | `user1`, `kraken-user`, `vless-space` — целы | > -> ```jsonc -> // ФАКТИЧЕСКОЕ состояние config.json на момент завершения сессии: -> "balancers": [ { "tag": "space-balancer", "selector": ["sub1-"], "strategy": {"type":"leastLoad"} } ] // ✅ верно -> "observatory": { "subjectSelector": ["space-"], ... "sampling": 3 } // ❌ ПУСТО — space-* удалены -> "routing.rules": [ { "user":["vless-space"], "balancerTag":"space-balancer" } ] // ✅ верно -> "outbounds": 31 (3 базовых + 28 × sub1-*) // ✅ верно -> ``` -> -> **Механизм отказа:** `observatory.subjectSelector` матчит `space-*` → после удаления статики **ни один сервер не пингуется** → `leastLoad` не находит живых кандидатов → трафик падает в **первый outbound списка** (`direct`). **Молча: ошибок в логе нет, `non existing outTag` не появляется.** -> -> **Правка:** в панели `Xray → Observatory` (или Settings → Xray Config) заменить `subjectSelector` `space-` → `sub1-`. Либо через `xrayTemplateConfig` в БД — **обязательно** по безопасному порядку (см. §WAL ниже). -> -> 🔑 **ПРАВИЛО:** selector балансировщика и `observatory.subjectSelector` — **ДВА РАЗНЫХ ПОЛЯ, менять ВМЕСТЕ.** Сменил префикс подписки — проверь ОБА. Проверка: +> 🔴 **ГЛАВНЫЙ ПИТФОЛЛ СЕССИИ — имя поля:** в UI правится **`burstObservatory`**, а не `observatory`. Я искал `observatory`, получил `has("observatory")` → `false` и **ошибочно объявил секцию отсутствующей** — она была под другим именем. Диагностика: > ```bash -> docker exec xray-admin cat /app/bin/config.json | jq -c '{bal:.routing.balancers[0].selector, obs:.observatory.subjectSelector}' -> # оба должны быть одинаковы, напр. ["sub1-"] +> docker exec xray-admin cat /app/bin/config.json | jq -r 'keys[]' +> docker exec xray-admin cat /app/bin/config.json | jq -c '.burstObservatory // .observatory' > ``` +> ⚠️ **Обе секции валидны для Xray 26.x.** `observatory` требует `sampling: 3`; `burstObservatory` использует `pingConfig{sampling, interval, destination}`. Панель при сохранении может удалить `observatory` и создать `burstObservatory` — проверять ОБА. +> +> 🔴 **Правило:** `routing.balancers[].selector` и `observatory|burstObservatory.subjectSelector` — **ДВА разных поля, менять ПАРОЙ.** +> +> **Историческая формулировка (до фикса) — оставлена ниже для контекста.** > ## 🏁 Архитектура (собрана и подтверждена; egress — см. блок выше) >