docs(agent): R1.11-R1.13 — podman ports, x-ui.db rollback, panel TLS

Also fixes R1 numbering: two items were both labelled '8', so reality443Forwarding counted as ninth while AGENTS.md, traps and f2e70bb already referenced it as R1.10. Verification commands were executed against the live host: '{{json .Ports}}' and 'exec xray version' both fail, corrected to .NetworkSettings.Ports and /app/bin/xray-linux-amd64.
This commit is contained in:
2026-10-10 21:43:00 +03:00
parent 31f13ae365
commit 6a5b075039
3 changed files with 82 additions and 21 deletions
+7 -7
View File
@@ -17,13 +17,13 @@
|---|---| |---|---|
| 16:25:52 | `0df9688` (задача T10) — удалён `reality443Forwarding` | | 16:25:52 | `0df9688` (задача T10) — удалён `reality443Forwarding` |
| 17:05:27 | первый `Connection refused` к `127.0.0.1:15380` в журнале nginx | | 17:05:27 | первый `Connection refused` к `127.0.0.1:15380` в журнале nginx |
| 17:07:33 | первое `worker_connections are not enough` (с 37.128.246.126) | | 17:07:33 | первое `worker_connections are not enough` (адрес обезличен) |
| ~17:15 | клиенты уходят в ретрай-петлю: ~1668 ошибок за ~40 мин | | ~17:15 | клиенты уходят в ретрай-петлю: ~1668 ошибок за ~40 мин |
| 17:30:58 | `37b4956` — revert nftables (T3) | | 17:30:58 | `37b4956` — revert nftables (T3) |
| 17:54:18 | `07a0437` — `reality443Forwarding` восстановлен | | 17:54:18 | `07a0437` — `reality443Forwarding` восстановлен |
| 17:55:38 | контейнер `3xui_app` пересоздан с `127.0.0.1:15380:443/tcp` | | 17:55:38 | контейнер `3xui_app` пересоздан с `127.0.0.1:15380:443/tcp` |
| ~18:05+ | флуд прекращается, ошибок 0 за 36 мин | | ~18:05+ | флуд прекращается, ошибок 0 за 36 мин |
| 19:16–19:19 | живые REALITY-сессии, `oqyude` и `snity` online | | 19:16–19:19 | живые REALITY-сессии, клиенты online |
Два вывода, которые неочевидны и потому требуют фиксации: Два вывода, которые неочевидны и потому требуют фиксации:
@@ -35,11 +35,11 @@
2. **`worker_connections are not enough` — вторичный симптом, а не причина.** 2. **`worker_connections are not enough` — вторичный симптом, а не причина.**
nginx имеет пустой `events {}` → `worker_connections` = 512 (дефолт). Пока nginx имеет пустой `events {}` → `worker_connections` = 512 (дефолт). Пока
маршрут на 443 был мёртв, реальные клиенты (37.128.246.126 — lanta-me, маршрут на 443 был мёртв, реальные клиенты слали 517-байтные ClientHello
Тамбов; 213.135.154.225 — Ростелеком, Тамбов) слали 517-байтные в цикле и выедали 512 соединений. Из-за этого «не работало всё» даже там,
ClientHello в цикле и выедали 512 соединений. Из-за этого «не работало где конкретная причина уже была устранена, и создавался флуд в логах,
всё» даже там, где конкретная причина уже была устранена, и создавался уводящий от настоящей причины. Источники флуда — два адреса /24 из
флуд в логах, уводящий от настоящей причины. разных сетей; адреса и провайдеры обезличены (репозиторий публичный).
Отдельно: `x-ui.db.bak.1787862834` (2026-08-27) при поверхностном взгляде Отдельно: `x-ui.db.bak.1787862834` (2026-08-27) при поверхностном взгляде
выглядит как «бэкап, который можно откатить». Это **другая конфигурационная выглядит как «бэкап, который можно откатить». Это **другая конфигурационная
+63 -7
View File
@@ -35,19 +35,18 @@
7. **sops-пути — через `config.sops.secrets.<name>.path`.** Любой 7. **sops-пути — через `config.sops.secrets.<name>.path`.** Любой
`path =` override на sops-блоке делает хардкод-потребителя молча `path =` override на sops-блоке делает хардкод-потребителя молча
сломанным: rebuild зелёный, сервис стартует, контент пустой. См. ADR-0001. сломанным: rebuild зелёный, сервис стартует, контент пустой. См. ADR-0001.
8. **Backups: external/unknown — accepted risk.** На 2026-10-10 open question 8. **Версия ядра Xray — состояние UI-панели 3x-ui, не Nix.** Ядро
ставится через UI панели (UI → xray version) и хранится в её
sqlite-БД. Перед любым деплоем/ребутом 3x-ui — проверить версию
ядра в панели. Nix декларирует панель (`:latest`), но не ядро.
9. **Backups: external/unknown — accepted risk.** На 2026-10-10 open question
5.6 («где бэкапы, как проверяются?») не отвечен владельцем. В этом 5.6 («где бэкапы, как проверяются?») не отвечен владельцем. В этом
репозитории **не декларируется** ни одна бэкап-стратегия. Failure modes репозитории **не декларируется** ни одна бэкап-стратегия. Failure modes
(потеря `/dev/sdc1` = потеря всех 9 сервисов на sapphira) перечислены (потеря `/dev/sdc1` = потеря всех 9 сервисов на sapphira) перечислены
в `.agent/decisions/0002-backups-external.md`. Если в будущем в `.agent/decisions/0002-backups-external.md`. Если в будущем
бэкап-система появится — отменить этот R1.9 и зафиксировать R1.x бэкап-система появится — отменить этот R1.9 и зафиксировать R1.x
про регулярную верификацию. про регулярную верификацию.
8. **Версия ядра Xray — состояние UI-панели 3x-ui, не Nix.** Ядро 10. **`reality443Forwarding = true` на VDS обязателен, НЕ удалять.**
ставится через UI панели (UI → xray version) и хранится в её
sqlite-БД. Перед любым деплоем/ребутом 3x-ui на sapphira —
проверить версию ядра в панели. Nix декларирует панель (`:latest`),
но не ядро.
9. **`reality443Forwarding = true` на VDS обязателен, НЕ удалять.**
`modules/vds/nginx.nix` маршрутизирует реальный `443` через stream: `modules/vds/nginx.nix` маршрутизирует реальный `443` через stream:
`pubray1.zeroq.su → 127.0.0.1:2049` (панель), всё остальное `pubray1.zeroq.su → 127.0.0.1:2049` (панель), всё остальное
(в т.ч. SNI `media.mediavitrina.ru` для REALITY-клиентов) → (в т.ч. SNI `media.mediavitrina.ru` для REALITY-клиентов) →
@@ -55,6 +54,38 @@
публикует опция `reality443Forwarding` (T10/C5 «погасил» её 2026-10-10 — публикует опция `reality443Forwarding` (T10/C5 «погасил» её 2026-10-10 —
Xray REALITY стал недоступен при живых SSH и pubray1.zeroq.su; Xray REALITY стал недоступен при живых SSH и pubray1.zeroq.su;
восстановлено `07a0437`, manifest T10 → status `pending`). восстановлено `07a0437`, manifest T10 → status `pending`).
**Сигнатура отказа НЕ выглядит как «Xray упал»** (2026-10-10, T10):
контейнер здоров, `restarts=0`, ядро 26.7.28 на месте, SSH жив, панель по
SNI `pubray1.zeroq.su` отвечает валидным сертификатом — но в журнале nginx
`connect() to 127.0.0.1:15380 failed (111: Connection refused)` и масса
`worker_connections are not enough`. Диагностировать «панель/Xray» надо
**пробой порта 15380 и `podman inspect .NetworkSettings.Ports`**, а не по симптому.
11. **Декларация портов ≠ runtime.** `ports` в
`virtualisation.oci-containers.containers."3xui_app"`
(`modules/containers/3x-ui.nix:26-35,68`) применяются **только при
пересоздании контейнера**. Правка Nix без `nixos-rebuild switch` не меняет
ничего; проверка после деплоя — обязательна:
```bash
sudo podman inspect 3xui_app --format '{{json .NetworkSettings.Ports}}' # ждём 15380
```
Любой ребилд, пересоздающий `3xui_app`, = окно недоступности REALITY
(2026-10-10: `07a0437` 17:54:18 → контейнер пересоздан 17:55:38).
Клиенты в это окно уходят в ретрай-флуд (см. R1.12 и ADR-0003).
12. **`x-ui.db` не откатывать на `x-ui.db.bak.1787862834`.** Живая БД —
`/mnt/services/nodes/otreca/3x-ui/db/x-ui.db` (в контейнере
`/etc/x-ui/x-ui.db`), схема **v3**, колонка `stream_settings`.
Бэкап от 2026-08-27 (311296 B) — **другая конфигурационная эпоха**:
инбаунды `id` 3/39/15/33 на портах **14380/14480/14910/14920**
(tcp/xhttp/xhttp/grpc), 7 клиентов, без `dest media.mediavitrina.ru`.
Текущая БД: `id` 42/45/46 на **443/8443/18443** (xhttp+reality), 11 клиентов.
Откат заменит рабочие инбаунды и обесценит все 11 клиентских конфигов.
13. **TLS терминирует панель 3x-ui, не nginx.** На otreca nginx в `stream`
делает только `ssl_preread` и проксирует сырой TCP; TLS живёт **внутри**
контейнера. Сертификат: хост `/var/lib/acme/pubray1.zeroq.su/{fullchain,key}.pem`
→ bind-mount `ro` в `/root/cert/*`; в настройках панели
`webCertFile=/root/cert/fullchain.pem`, `webKeyFile=/root/cert/key.pem`.
**Не добавлять** TLS-терминирующий `server {}` для `pubray1.zeroq.su` —
перекроет stream-SNI карту и убьёт и панель, и REALITY.
### R2. home-manager `Service` ≠ `serviceConfig` ### R2. home-manager `Service` ≠ `serviceConfig`
@@ -111,6 +142,8 @@ nix build .#nixosConfigurations.<хост>.config.system.build.toplevel --dry-ru
| `users.nix:66` | `uid = if hostname == "sapphira" then 1001 else …` | Костыль под 1000 = удалённый `yuyus`; удалять только после миграции ФС | | `users.nix:66` | `uid = if hostname == "sapphira" then 1001 else …` | Костыль под 1000 = удалённый `yuyus`; удалять только после миграции ФС |
| `3x-ui.nix:54` | `image = …:latest` | Панель намеренно latest; ядро Xray — состояние панели, см. R1.8 | | `3x-ui.nix:54` | `image = …:latest` | Панель намеренно latest; ядро Xray — состояние панели, см. R1.8 |
| `3x-ui.nix:33-35` | `reality443Forwarding = true` на VDS | **Обязательно, НЕ удалять** (R1.10). nginx stream (`vds/nginx.nix`) маршрутизирует `443 → 127.0.0.1:15380 → container:443`; без маппинга Xray REALITY мёртв. Удаление T10/C5 сломало — восстановлено `07a0437` | | `3x-ui.nix:33-35` | `reality443Forwarding = true` на VDS | **Обязательно, НЕ удалять** (R1.10). nginx stream (`vds/nginx.nix`) маршрутизирует `443 → 127.0.0.1:15380 → container:443`; без маппинга Xray REALITY мёртв. Удаление T10/C5 сломало — восстановлено `07a0437` |
| `vds/nginx.nix`, `events {}` | Пустой блок → `worker_connections` = **512** (дефолт) | Не баг, но и не запас: при окне недоступности REALITY клиенты дают ретрай-флуд и исчерпывают 512 → «не работает всё» даже после починки первопричины (2026-10-10: 1668 ошибок за ~40 мин). Известный пробел, задача не заведена |
| Настройки панели, `webBasePath = /pubray/` | `GET https://pubray1.zeroq.su/` → **404, 0 байт** | Панель жива, корень не корень. Реальный URL — `https://pubray1.zeroq.su/pubray/` (HTTP 200). `/subs/` без токена тоже 404 — это норма. Не «чинить» nginx под `/` |
| `server/default.nix:37-50` | 14 закомментированных модулей (13 архивировано, 1 stirling-pdf удалён в 5dd7a58) | Отключены осознанно, см. задачу T16 | | `server/default.nix:37-50` | 14 закомментированных модулей (13 архивировано, 1 stirling-pdf удалён в 5dd7a58) | Отключены осознанно, см. задачу T16 |
| `opencode.nix:339` | `systemd.user.services.opencode-web.Service` | `serviceConfig` рендерится в секцию `[serviceConfig]`, systemd молча игнорирует (`c73a698`); см. R2 | | `opencode.nix:339` | `systemd.user.services.opencode-web.Service` | `serviceConfig` рендерится в секцию `[serviceConfig]`, systemd молча игнорирует (`c73a698`); см. R2 |
| `vds.nix:73-91` | nftables без финального правила | Известный пробел, см. задачу A3 | | `vds.nix:73-91` | nftables без финального правила | Известный пробел, см. задачу A3 |
@@ -159,8 +192,31 @@ systemctl show postgresql -p Requires -p After | tr ' ' '\n' | grep -E 'mnt-|hom
# sops # sops
sops --version sops --version
# цепочка 3x-ui REALITY на otreca (после любого деплоя, трогающего 3x-ui/nginx)
sudo podman inspect 3xui_app --format '{{json .NetworkSettings.Ports}}' # есть 15380->443
sudo podman exec 3xui_app /app/bin/xray-linux-amd64 version # ожидаем 26.7.x
curl -ks --resolve pubray1.zeroq.su:443:127.0.0.1 -o /dev/null \
-w '%{http_code}\n' https://pubray1.zeroq.su/pubray/ # ожидаем 200
sudo journalctl -u nginx --since '-10 min' | grep -cE 'worker_connections|Connection refused' # ожидаем 0
``` ```
## Диагностика «REALITY на otreca не работает» (порядок, 2026-10-10)
1. `sudo podman inspect 3xui_app --format '{{json .NetworkSettings.Ports}}'` — есть ли `15380->443`.
Нет → R1.10/R1.11 (декларация разошлась с runtime), контейнер не пересоздан.
2. `sudo podman exec 3xui_app /app/bin/xray-linux-amd64 version` — должно быть
26.7.x (R1.8). Не 26.9.x. **В `$PATH` контейнера бинаря нет** — только
полный путь.
3. `sudo podman exec 3xui_app python3 -c "import sqlite3;…"` — `inbounds` в
`x-ui.db` (колонка `stream_settings`): порты 443/8443, `network=xhttp`,
`security=reality`, `enable=1`. Сверить с `/app/bin/config.json` внутри контейнера.
4. Проба 443 снаружи: TLS с SNI `media.mediavitrina.ru` должен вернуть
steal-сертификат `*.mediavitrina.ru`; SNI `pubray1.zeroq.su` — валидный
сертификат панели.
5. Только если 1–4 зелёные, а пользователь всё ещё видит отказ — проблема
клиентская (старый конфиг, клиент без поддержки `xhttp`).
## Конвенции проекта ## Конвенции проекта
- `xlib` (в `lib/xlib/`) — чистые данные: identity (`device`), capability flags, - `xlib` (в `lib/xlib/`) — чистые данные: identity (`device`), capability flags,
+5
View File
@@ -75,6 +75,9 @@ flake.nix
5. **3x-ui заморожен.** Панель на `:latest`, ядро Xray на 26.7.x. Миграция на 26.9.x провалена. → задачи T6–T10. 5. **3x-ui заморожен.** Панель на `:latest`, ядро Xray на 26.7.x. Миграция на 26.9.x провалена. → задачи T6–T10.
6. **nftables на VDS требует явной финальной политики.** Текущий ruleset — без финального правила → неявный accept. → задача T3. 6. **nftables на VDS требует явной финальной политики.** Текущий ruleset — без финального правила → неявный accept. → задача T3.
7. **sops-пути — через `config.sops.secrets.<name>.path`.** Любой `path =` override на sops-блоке делает хардкод-потребителя молча сломанным. → ADR-0001. 7. **sops-пути — через `config.sops.secrets.<name>.path`.** Любой `path =` override на sops-блоке делает хардкод-потребителя молча сломанным. → ADR-0001.
8. **Декларация портов ≠ runtime.** `ports` контейнера `3xui_app` применяются только при пересоздании; правка Nix без `nixos-rebuild` не меняет ничего. Проверка: `podman inspect 3xui_app --format '{{json .NetworkSettings.Ports}}'`. → R1.11, ADR-0003.
9. **`x-ui.db` не откатывать на `x-ui.db.bak.1787862834`.** Бэкап от 2026-08-27 — другая эпоха (порты 14380/14480/14910/14920, 7 клиентов). Откат убьёт рабочие 443/8443 и все 11 конфигов. → R1.12, ADR-0003.
10. **TLS терминирует панель 3x-ui, не nginx.** nginx делает только `ssl_preread` и проксирует сырой TCP; сертификат смонтирован в контейнер `ro`. TLS-терминирующий `server {}` для `pubray1.zeroq.su` перекроет stream-SNI и убьёт и панель, и REALITY. → R1.13, ADR-0003.
## Ловушки (выглядит сломанным, намеренно) ## Ловушки (выглядит сломанным, намеренно)
@@ -85,6 +88,8 @@ flake.nix
| `users.nix:66` | `uid = if hostname == "sapphira" then 1001 else …` | Костыль под 1000 = удалённый `yuyus`; удалять только после миграции ФС | | `users.nix:66` | `uid = if hostname == "sapphira" then 1001 else …` | Костыль под 1000 = удалённый `yuyus`; удалять только после миграции ФС |
| `3x-ui.nix:54` | `image = …:latest` | Панель намеренно latest; ядро Xray — на 26.7.x | | `3x-ui.nix:54` | `image = …:latest` | Панель намеренно latest; ядро Xray — на 26.7.x |
| `3x-ui.nix:33-35` | `reality443Forwarding = true` на VDS | **Обязательно, НЕ удалять.** nginx stream (`vds/nginx.nix`) маршрутизирует `443 → 127.0.0.1:15380 → container:443`; без маппинга Xray REALITY мёртв. Удаление T10/C5 сломало — восстановлено `07a0437` | | `3x-ui.nix:33-35` | `reality443Forwarding = true` на VDS | **Обязательно, НЕ удалять.** nginx stream (`vds/nginx.nix`) маршрутизирует `443 → 127.0.0.1:15380 → container:443`; без маппинга Xray REALITY мёртв. Удаление T10/C5 сломало — восстановлено `07a0437` |
| `vds/nginx.nix`, `events {}` | Пустой блок → `worker_connections` = **512** (дефолт) | Не баг, но и не запас: при окне недоступности REALITY клиенты дают ретрай-флуд и исчерпывают 512 → «не работает всё» даже после починки первопричины (1668 ошибок за ~40 мин). Известный пробел |
| Настройки панели | `GET https://pubray1.zeroq.su/` → **404** | Панель жива: `webBasePath=/pubray/`, реальный URL `https://pubray1.zeroq.su/pubray/`. Не «чинить» nginx под `/` |
| `server/default.nix:37-50` | 14 закомментированных модулей (13 архивировано, 1 stirling-pdf удалён в 5dd7a58) | Отключены осознанно, см. задачу T16 | | `server/default.nix:37-50` | 14 закомментированных модулей (13 архивировано, 1 stirling-pdf удалён в 5dd7a58) | Отключены осознанно, см. задачу T16 |
| `opencode.nix:339` | `systemd.user.services.opencode-web.Service` | `serviceConfig` рендерится в секцию `[serviceConfig]`, systemd молча игнорирует; см. R2 | | `opencode.nix:339` | `systemd.user.services.opencode-web.Service` | `serviceConfig` рендерится в секцию `[serviceConfig]`, systemd молча игнорирует; см. R2 |
| `vds.nix:73-91` | nftables без финального правила | Известный пробел, см. задачу T3 | | `vds.nix:73-91` | nftables без финального правила | Известный пробел, см. задачу T3 |