Files
obsidian-vault/family/how-to/kraken-access.md
T

312 lines
32 KiB
Markdown
Executable File
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.
# Kraken — Внешний доступ
> # ✅ 2026-09-23 — КАНАЛ REVERSE ЖИВ + ТОПОЛОГИЯ УТОЧНЕНА
>
> **1. Reverse-туннель работает** (первая проверка после фикса гонки 2026-09-20): SOCKS `xray-reverse-portal:12345` → `92.62.77.233` ≠ TrueNAS `90.189.160.148`. Детали — [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
>
> 🔴 **2. IP Кра́кена ИЗМЕНИЛСЯ: `92.62.70.41` → `92.62.77.233`.** Прежний зафиксирован в этом документе и в `truenas-infrastructure.md` — при проверках брать актуальный из живого запроса.
>
> **3. Топология Кра́кена (проверено `ip -4 addr` + `ip route`, 2026-09-23):**
>
> ```
> eth0 192.168.1.14 metric 100 ← активный default route
> wlan0 192.168.1.15 metric 600
> default via 192.168.1.1 dev eth0 src 192.168.1.14
> wg0 10.99.1.2/24 (VPS qentra удалён — звено мёртво)
> ```
>
> 🔴 **Кра́кен и Mac (`192.168.1.57`) — СОСЕДИ в одной подсети `192.168.1.0/24`**, шлюз `192.168.1.1` (ASUS RT-AC68U). **`192.168.2.2` (OpenWrt) — это шлюз сети TrueNAS, к сети Mac/Кра́кена отношения не имеет.** Не приписывать OpenWrt сети `192.168.1.x`.
>
> ⚠️ Следствие: с Mac до `192.168.2.197` маршрут идёт `gateway: 192.168.1.1` (проверено `route -n get`), а не через OpenWrt. Схема «статический маршрут `192.168.2.0/24 → 192.168.1.15` на ASUS» **работоспособна**, потому что Кра́кен — сосед в той же подсети, и обратный NAT делает он сам. Нюанс интерфейса (`.14` eth0 vs `.15` wlan0) — в [[personal/plans/lan-via-reverse-tunnel]].
>
> ⚠️ **ASUS `192.168.1.1` по SSH недоступен** (`Permission denied` → `Connection closed`, порт 22 `closed`). Раньше в доке `home-network-config` вход был описан как `ssh -i ~/.ssh/id_rsa admin@192.168.1.1` — **сейчас не работает**. Правки маршрутов — только через веб-UI.
>
> **4. `xray-reverse-bridge` не имеет `inbounds`** — ключи конфига только `log`/`outbounds`/`routing`. Слушающих портов нет. Канал работает на отправку, принять трафик от роутера бриджу нечем. Разбор — [[personal/plans/lan-via-reverse-tunnel]].
> # ✅ 2026-09-20 — НАЙДЕНА ИСТИННАЯ ПРИЧИНА (2026-09-15 поломки): ГОНКА Docker ↔ монтирование USB-диска
>
> **Симптом 2026-09-15:** одновременно упали (1) reverse-туннель Kraken→TrueNAS (`kraken-user`) и (2) cloudflared SSH-туннель `ssh-kraken.qentra.top`. Обе поломки — **одна причина**, не две.
>
> ### Таймлайн последнего boot (Kraken)
> | Событие | Время |
> |---|---|
> | Boot | Sep 15 **17:29:17** |
> | Ядро видит `sda1` (USB HDD) | +130 с → **17:31:27** |
> | **`dockerd` стартовал** (PID 1324) | **17:31:01** ← на **26 с раньше** диска |
> | Диск смонтирован | **17:31:27** |
>
> **Механизм:** `/etc/docker/daemon.json` → `data-root` = `/srv/dev-disk-by-uuid-49e8f586-.../docker-data` (симлинк на `6194539b-...`). `docker.service` **не имеет `RequiresMountsFor=`** → не ждёт монтирования. dockerd на 17:31:01 открыл путь до появления диска → увидел пусто → загрузил **0 контейнеров**; через 26 с диск смонтировался поверх точки, но daemon уже держал пустое состояние в памяти. Перечитывания нет.
>
> ### Что НЕ потеряно (проверено живьём)
> - `docker-data/containers/` = **4.7G, 35 контейнеров целы** (cloudflared, xray-reverse-bridge, jellyfin, sonarr, radarr, prowlarr, transmission, homeassistant, hermes-kraken, portainer, rclone, watchtower, flaresolverr, timemachine).
> - Образы (21), 32 volume — на месте. Диск `sda1` 1.8T смонтирован, 242G свободно.
> - `docker info` при этом рапортует `Containers=0` — **не верь ему, metadata на диске цела**.
>
> ### Побочные дефекты, найденные при разборе
> 1. **`/etc/fstab` содержит ДУБЛЬ** строки для `6194539b`: OMV-блок + ручная строка `UUID=6194539b-...` → systemd: `Duplicate entry in '/etc/fstab'?`.
> 2. **`docker.service`: `RequiresMountsFor=` пусто** — корень гонки. Drop-in нужен, чтобы dockerd ждал диск.
> 3. ~~RestartPolicy=null → нужен ручной `docker start`~~ ❌ **ЭТО БЫЛО ОШИБОЧНОЕ ДОПУЩЕНИЕ.** Я прочитал `RestartPolicy.Name` из `hostconfig.json` (это reflection исходного `docker run`, там `null`), но в daemon state policy **есть**. Факт: после `systemctl restart docker` все 12 контейнеров с `unless-stopped` поднялись **сами**, ручной `docker start` не потребовался. Проверять политику надо `docker inspect <name> --format '{{.HostConfig.RestartPolicy.Name}}'` (живой daemon), а не `hostconfig.json` на диске.
>
> ### Команды диагностики (воспроизводимые)
> ```bash
> ssh kraken 'docker ps -a --format "{{.Names}}\t{{.Status}}"' # пусто → daemon state пуст
> ssh kraken 'ps -o pid,lstart,cmd -p $(pgrep -x dockerd)' # реальное время старта dockerd
> ssh kraken 'systemctl show docker -p RequiresMountsFor' # пусто = нет зависимости от mount
> ssh kraken 'uptime -s; findmnt /srv/dev-disk-by-uuid-6194539b-...' # когда boot / когда диск
> ssh kraken 'sudo ls /srv/dev-disk-by-uuid-6194539b-.../docker-data/containers | wc -l' # 35 = целы
> ```
> Питфолл: `systemctl show docker -p ExecMainStartTimestamp` дал stale `15:18:36`; **истинное время старта — `ps -o lstart`** = 17:31:01.
>
> ### План восстановления (согласован 2026-09-20)
> 1. Backup: `/etc/fstab`, `/etc/docker/daemon.json`, `/home/kraken/xray-reverse/` → `/home/kraken/backups/docker-recovery-<date>/`.
> 2. **`sudo systemctl restart docker`** — диск уже смонтирован → daemon прочитает все 35 контейнеров.
> 3. `docker start` по списку — ⚠️ **НЕ ПОТРЕБОВАЛСЯ**: оказалось, RestartPolicy в daemon state есть (`unless-stopped`), все 12 контейнеров поднялись автоматически после рестарта daemon. `hostconfig.json` на диске показывает `null` — это НЕ отражает реальную политику. Проверять через живой daemon: `docker inspect --format '{{.HostConfig.RestartPolicy.Name}}'`.
> 4. Правка боевого: убрать дубль fstab + drop-in `RequiresMountsFor=/srv/dev-disk-by-uuid-6194539b-...` — **только с апрувом Alex**. Порядок: коммит ДО → правка → заливка → проверка Alex → коммит ПОСЛЕ.
> 5. Проверка: `docker ps` все Up; reverse-канал `udp:reverse:0`; cloudflared tunnel.
> 6. Версии Xray на бридже: оставить `26.4.25` + `network_mode: host` (проверенный фикс 2026-09-02) — открытый вопрос к Alex.
>
> **Статус (обновлено 2026-09-20): ✅ ФИКС ПРИМЕНЁН, ПРОВЕРЕН РЕАЛЬНЫМ REBOOT'ом, REBOOT-БЕЗОПАСЕН.**
>
> **Подтверждение живым reboot'ом (2026-09-20):** Alex выключил Kraken (`sudo shutdown -h now`), включил — **все 12 контейнеров поднялись сами** (`docker ps` не пуст). Ручных `docker start` не потребовалось. Гонка закрыта.
>
> **Полный разбор механики и фикса:** [[personal/tech/docker-data-root-mount-race]].
>
> ⚠️ **После reboot `jellyfin` поднялся, но отдавал 503** — это **отдельный** дефект (образ 10.11.8 старше схемы БД 12.1.0.0, `no such column: p.Permission_Permissions_Guid`), НЕ связанный с гонкой. См. [[personal/tech/jellyfin-db-schema-mismatch]]. **Различать при диагностике:** контейнер не поднялся (гонка) vs поднялся, но падает внутри (своя ошибка).
>
> **Что сделано:**
> 1. ✅ Backup: `/home/kraken/backups/docker-recovery-20260920-105228/` (fstab, daemon.json, xray-reverse).
> 2. ✅ `sudo systemctl restart docker` → все 25 контейнеров загрузились, 12 поднялись автоматом (`cloudflared`, `xray-reverse-bridge` в т.ч.).
> 3. ✅ Drop-in `/etc/systemd/system/docker.service.d/mounts.conf`:
> ```ini
> [Unit]
> RequiresMountsFor=/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/docker-data
> After=srv-dev\x2ddisk\x2dby\x2duuid\x2d6194539b\x2d03e0\x2d4f33\x2d986b\x2d4da09834e82f.mount
> ```
> Проверено: `systemctl show docker -p Requires -p After` → маунт-юнит в **Requires И After**. dockerd теперь физически не стартует раньше диска → **гонка закрыта**.
> 4. ✅ `/etc/fstab`: убран дубль строки для `6194539b` + дубль bind-mount `/var/log/journal`. Осталось 1 вхождение (OMV-блок). Проверено грепом.
> 5. ✅ Sanity `systemctl restart docker` после правок → `containers=25`, 11-12 Up. Критичные поднялись сами.
>
> ✅ **`hermes-kraken` — crash-loop РАЗОБРАН И ПОЧИНЕН (2026-09-20, отдельная от гонки причина).**
> **Симптом:** `Restarting (1)` циклично, в логах `mkdir: cannot create directory '/opt/data': Permission denied`.
> **Причина:** контейнер работает под **UID 1000:1000** (из образа, `user:` в compose не задан), `HOME=/opt/data` ← bind-mount из `/srv/.../docker/hermes/config`. На хосте этот каталог имел владельца **`10000:10000`** mode `700` (аномалия — UID 10000 больше нигде в `docker/` не встречается; похоже след от перерегистрации диска 15.09). 1000 ≠ 10000 + mode 700 → `Permission denied`.
> **Фикс:** `sudo chown 1000:1000 …/docker/hermes/config` (сначала на верхнем уровне, затем `-R`).
> ⚠️ **Питфолл:** `chown -R` по большому дереву на медленном USB-диске **не влезает в SSH-таймаут**. Запускать независимо: `sudo systemd-run --unit=hermes-chown --collect --property=Type=oneshot chown -R 1000:1000 <path>` — юнит переживёт обрыв SSH. Проверка: `systemctl is-active hermes-chown`.
> **Результат:** контейнер `Up`, логи — чистый старт Gateway, API отвечает `404` на `/` (норма, рабочий эндпоинт `/v1/chat/completions`).
> 📌 Три `mkdir: Permission denied` в начале лога после фикса — **остаток буфера от прошлого падения**, не текущие ошибки.
>
> **Питфолл (опровергнут):** `RestartPolicy` в `hostconfig.json` = `null`, но в daemon state policy есть → при рестарте daemon контейнеры **поднимаются сами**. Не пугаться `null`, не нужен ручной `docker start`.
>
> **Урок на будущее:** USB-диск на Kraken поднимается ~130 с. Любой сервис, зависящий от `docker-data` на этом диске, ОБЯЗАН иметь `RequiresMountsFor`. Теперь это обеспечено для docker.service.
> # 🔴 2026-09-15 — KRAKEN НЕДОСТУПЕН ПО ВСЕМ ПУТЯМ (проверено с Mac)
>
> ⚠️ **ВНИМАНИЕ (обновлено 2026-09-20):** вывод этой секции был **ЧАСТИЧНО НЕВЕРЕН**. «Kraken недоступен» относилось к внешним путям (cloudflared `ssh-kraken.qentra.top`, WG `10.99.1.2`) — оба были down не из-за выключенной машины Kraken, а из-за **гонки docker↔mount** (см. секцию выше): `cloudflared`-контейнер просто не загрузился daemon'ом. По **LAN Kraken был жив всю дорогу** (`192.168.1.15`). Урок: «недоступен снаружи» ≠ «машина мертва» — проверять LAN и состояние docker daemon, прежде чем звонить провайдеру.
>
> **Симптом:** перестал работать клиент `kraken-user` в 3x-ui на TrueNAS. При разборе выяснилось — причина **не в БД и не в конфиге**, а в том, что **сам хост Kraken недоступен**.
>
> | Путь | Команда | Результат |
> |---|---|---|
> | Cloudflare SSH | `ssh ssh-kraken.qentra.top` | ❌ `Connection timed out during banner exchange` |
> | WireGuard Direct | `ssh kraken@10.99.1.2:22` | ❌ `Operation timed out` |
> | DNS | `host kraken` | ❌ `NXDOMAIN` (в `/etc/hosts` записи нет) |
>
> **Состояние TrueNAS-стороны (жива):** `xray-reverse-portal` Up 13 дней, порты `12345` (SOCKS) и `12346` (interconn) в LISTEN, конфиг `/mnt/RED_2TB/docker/reverse-portal/config.json` цел.
>
> **Диагностика, доказывающая «бридж молчит»:**
> ```bash
> docker exec xray-reverse-portal netstat -an | grep 12346 # пусто = от Kraken никто не звонит
> curl -s --max-time 20 --socks5-hostname 127.0.0.1:12345 https://api.ipify.org # пусто (ждём 92.62.70.41)
> ```
> Логи портала при этом содержат `app/dispatcher: non existing outTag: reverse-out` — **это не битый конфиг**, а ожидаемое следствие: `reverse-out` создаётся Xray'ем автоматически только пока бридж подключён (объявлен в `inbounds[interconn].clients[0].reverse.tag`, не в `.outbounds`).
>
> **Вывод:** нужно проверять Kraken со стороны панели хостинга/консоли провайдера — машина выключена либо сетевая недоступность. Решение Alex: **«подождём, может оживет»**.
>
> ⚠️ **Урок:** перед тем как искать причину в конфиге TrueNAS, проверять доступность самого Kraken. Полдня ушло на разбор 3x-ui, который к этой поломке отношения не имел.
>
> **Обновлено: 2026-09-04**
> Updated: 2026-09-01 23:00 — диск/докер кракена поднялись после перезагрузки (тогда причина ещё не была известна; истинная — гонка docker↔mount, найдена 2026-09-20).
## Как зайти
Везде `ssh kraken`. Резолвится через `~/.ssh/config` на Eagle:
- **Дома** — напрямую по LAN (`192.168.1.15`)
- **Снаружи** — ⚠️ через WireGuard (`10.99.1.2`) **БОЛЬШЕ НЕ РАБОТАЕТ**: VPS-узло (10.99.0.1/10.99.1.1) qentra.top **удалили 2026-09-01**. Альтернатива внешнего доступа обсуждается через Xray reverse (см. [[tech/xray-reverse-tunnel-kraken-truenas]]).
> 🔴 **2026-09-15 — ПРИЧИНА НАЙДЕНА 2026-09-20: гонка docker ↔ монтирование диска.** Диск не «нестабилен» — он **монтируется медленно (~130 с)**, dockerd обгоняет. См. секцию выше.
> ⚠️ **Kraken Docker на 2026-09-01:** Server 29.4.3, Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/docker-data`. **30 образов сохранены, но 0 контейнеров** (все прежние контейнеры — jellyfin/transmission/radarr/hermes-kraken и т.д. — не были видны daemon'ом после старта без диска). Причина — та же mount-race, НЕ дефект диска; после фикса 2026-09-20 такого не повторяется.
>
> 🔴 **Диск НЕ нестабилен.** Формулировка «USB-диск нестабилен» — ошибочное обобщение разового инцидента 2026-09-01 (циклы USB enumerate, `error -110/-62`, `Read Capacity(10) failed`). Истинная причина проблем с контейнерами — **mount race**: диск монтируется ~130 с, dockerd стартует раньше. Фикс (`RequiresMountsFor`) применён 2026-09-20, проверен живым reboot'ом — все контейнеры поднялись сами. **Не возвращать формулировку «диск нестабилен»** (Alex требовал её убрать, 2026-09-25).
> Реальные ограничения HDD: 5400rpm, ~95 МБ/с на чтение — лимит механики, не USB и не дефект.
## Portainer (локально)
`http://kraken:9000`
API:
```bash
BASE=http://localhost:9000
KEY="ptr_AJY+Ba9A7f6pcAHZfDD5koU4stkKgJCdbTEXLDLxn0g="
EP=3 # endpoint ID на Кракене = 3, не 1!
```
## Hermes API
`http://kraken:8642/v1/chat/completions` — OpenAI-compatible endpoint.
## Reverse Xray bridge (развёрнут 2026-09-01)
- Контейнер **`xray-reverse-bridge`** в `/home/kraken/xray-reverse/` (compose + `config.json`), образ **`teddysun/xray:26.4.25`** + `network_mode: host` (проверенный фикс 2026-09-02).
- Outbound VLESS+REALITY TCP → `mallexxx.duckdns.org:12346` (TrueNAS через OpenWrt DNAT), reverse tag `reverse-in`, egress freedom с `finalRules:[{action:allow}]`.
- ✅ **End-to-end payload работает** (верифицировано 2026-09-02: SOCKS `12345` → egress IP Kraken). Полная история диагностики (регрессии #6612/#6242, docker bridge/NAT) — в [[tech/xray-reverse-tunnel-kraken-truenas]].
- ⚠️ 2026-09-15…20 контейнер был **не запущен** (гонка docker↔mount, см. шапку) — это единственная причина «туннель down». После рестарта daemon — снова Up.
- ✅ **2026-09-23 — канал перепроверен end-to-end после починки гонки. Egress Кра́кена = `92.62.77.233`.**
> 🔴 **IP Кра́кена ИЗМЕНИЛСЯ:** `92.62.70.41` (все старые записи) → **`92.62.77.233`** (2026-09-23, проверено через SOCKS портала). **Не доверять `92.62.70.41`**, брать актуальный живым запросом.
- 🔧 **2026-09-23 — в конфиг бриджа добавлен inbound `lan-in`** (`dokodemo-door`, **`listen 0.0.0.0:12345`**, `followRedirect:true`, `sniffing:false`) + routing `lan-in → conn`. Прежде `inbounds` было **`null`** — бридж мог только отправлять в туннель, принимать не мог.
- ⚠️ **`listen` обязан быть `0.0.0.0`, НЕ `127.0.0.1`.** С `127.0.0.1` REDIRECT-трафик с внешнего адреса не принимается — соединение отбивается мгновенно (`HTTP 000` за ~0.005 с). Ошибка проверена и исправлена.
- Плюс на хосте Кра́кена: `iptables -t nat PREROUTING -s 192.168.1.0/24 -d 192.168.2.0/24 -p tcp -j REDIRECT --to-ports 12345`.
- 🏁 **Задача, ради которой это ставилось, — ТУПИК.** Xray reverse намеренно не пускает forward-proxy в приватные сети: портал отдаёт `not allowed to use forward proxy`. Это штатная защита, не баг. Теория: [[personal/tech/xray-reverse-limits-private-network]]. Полное состояние: [[personal/plans/lan-access-via-reverse-tunnel]].
- ⚠️ **`iptables-persistent` НЕ установлен** — правило REDIRECT слетит при reboot.
- Бэкапы: `config.json.bak-lan-in-20260923-130541`, `config.json.bak-listen0000-20260923-131655`, `config.json.bak-v3-<date>`, `/home/kraken/backups/iptables.bak-20260923-130541`.
- ⚠️ **`sudo` на Кра́кене через SSH требует `< /dev/null`** — иначе `Connection closed by remote host`. На Кра́кене **нет `ifconfig`** (использовать `ip addr`).
## Docker контейнеры (обновлено 2026-09-20, после фикса)
✅ **daemon видит все контейнеры. `containers=25`, 12 Up** (после восстановления гонки docker↔mount, см. шапку). Все 35 metadata-записей в `docker-data/containers/` целы.
| Имя | Статус 2026-09-20 | Заметки |
|-----|---------|---------|
| xray-reverse-bridge | ✅ Up | образ `teddysun/xray:26.4.25`, `network_mode: host` (фикс 2026-09-02) |
| cloudflared | ✅ Up | `cloudflare/cloudflared:latest`, `network_mode: host`, remote-managed по TUNNEL_TOKEN |
| jellyfin / sonarr / radarr / prowlarr | ✅ Up | |
| portainer / transmission / rclone / watchtower / flaresolverr | ✅ Up | |
| hermes-kraken | ✅ Up | crash-loop вылечен 2026-09-20 (см. ниже) |
| homeassistant | ⏹ `Exited (128)` 2 месяца назад | не поднимался, отдельная задача |
| timemachine + alpine-* (6 шт.) | ⏹ Exited (0) | одноразовые/диагностические, норма |
> **Питфолл про RestartPolicy (проверено):** `hostconfig.json` на диске показывает `RestartPolicy.Name = null`, но в daemon state политика `unless-stopped` есть → контейнеры поднимаются сами. Не пугаться `null`, не нужен ручной `docker start`. Проверять живой daemon: `docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' <name>`.
## ✅ hermes-kraken — crash-loop вылечен 2026-09-20 (права каталога)
> **Симптом:** `hermes-kraken` в вечном `Restarting (1)`. Логи циклично:
> `mkdir: cannot create directory '/opt/data': Permission denied`.
>
> **НЕ связано** с гонкой docker↔mount (контейнер был запущен, но падал сам).
| Что | Значение |
|---|---|
| Контейнер работает под | **UID 1000:1000** (`kraken`, задан в образе, не в compose) |
| `/opt/data` примонтирован из | `…/docker/hermes/config` |
| Владелец на хосте (было) | **10000:10000**, `drwx------` (700), mtime **Sep 15 15:37** |
| Итог | 1000 ≠ 10000 + mode 700 → `Permission denied` на `mkdir /opt/data` |
**Корень:** каталог `docker/hermes/config` после перерегистрации диска (15 сен) получил **чужой UID 10000** — след старого OMV-маппинга. `10000` больше нигде в `…/docker/` не встречается: все прочие каталоги либо `root:root`, либо `1000:1000`. Данные (`.env`, `auth.json`, `sessions/`, `cron/`, `response_store.db`) были целы — только владелец не тот.
**Фикс:**
```bash
# top-level — мгновенно
sudo chown 1000:1000 /srv/dev-disk-by-uuid-6194539b-.../docker/hermes/config
# рекурсивно — долго (медленный USB-HDD), запускать в фоне, НЕ блокируясь на SSH:
sudo systemd-run --unit=hermes-chown --collect --property=Type=oneshot \
chown -R 1000:1000 /srv/dev-disk-by-uuid-6194539b-.../docker/hermes/config
```
Проверка: `sudo find …/docker/hermes -uid 10000` → пусто; контейнер `Up`; API `http://localhost:8642/` отвечает (`404` на `/` — норма, эндпоинт `/v1/chat/completions`).
**Питфоллы:**
- `chown -R` по большому дереву на USB-HDD идёт **минуты** → **обычный ssh-вызов рвётся по таймауту и правка НЕ применяется**. Запускать через `systemd-run … --collect` в фоне, потом проверять `systemctl is-active hermes-chown` и `ls -ldn` каталога.
- Логи после старта содержат «хвост» прежних `mkdir` ошибок из буфера — не пугаться, если дальше идёт `Hermes Gateway Starting...` и скиллы синхронизированы.
> ### 🔁 2026-09-23 — ПОВТОРНЫЙ СИМПТОМ, НАЙДЕН ДРУГОЙ КОРЕНЬ
>
> Kraken-агент сообщил: **«нет SSH доступа к хосту»**. Проверка:
> ```
> sudo docker exec hermes-kraken ssh kraken-host "echo HELLO"
> → No user exists for uid 1000
> ```
> Это **не про ключ** и **не связано** с crash-loop 2026-09-20. `ssh` падает на `getpwuid(1000)` —
> **в `/etc/passwd` образа нет записи для uid 1000**, там только `hermes:x:10000:10000`.
> Контейнер запущен под uid 1000 (из `USER` в образе), т.е. анонимный uid.
>
> **Не путать три разных вещи** (все они про uid и все ломались отдельно):
> | Дефект | Что не так | Фикс | Дата |
> |---|---|---|---|
> | crash-loop `mkdir /opt/data: Permission denied` | владелец **файлов** на хосте = `10000:10000` | `chown 1000:1000` на хосте | 2026-09-20 ✅ |
> | гонка docker↔mount | data-root на USB раньше dockerd | `RequiresMountsFor` drop-in | 2026-09-20 ✅ |
> | `No user exists for uid 1000` | нет записи uid 1000 в **passwd образа** | завести `kraken` uid 1000 в Dockerfile | 2026-09-23 ⏳ план согласован |
>
> **Полный разбор + план фикса (вариант A):** [[family/how-to/hermes-docker-kraken]] → раздел
> «ПЛАН: фикс uid 1000». Статус: **согласован, НЕ применён**.
>
> Дополнительно найдено: 3-й bind-mount `config/.ssh → /root/.ssh` при `HOME=/opt/data` — SSH его не
> читает; в самом образе есть `entrypoint.sh` с логикой `gosu`/`usermod`, но вся она обёрнута в
> `if [ "$(id -u)" = "0" ]` и при старте под 1000 **пропускается целиком**.
## Cloudflared туннель — публичные маршруты (состояние на 2026-09-04)
Туннель `kraken` в Zero Trust, remote-managed по `TUNNEL_TOKEN`, `network_mode: host`, образ `cloudflare/cloudflared:latest`. Актуальный ingress конфиг (виден в логах cloudflared на Kraken, `INF Updated to new configuration`):
```
{"ingress":[
{"hostname":"kraken.qentra.top", "originRequest":{"httpHostHeader":"kraken.qentra.top"},"service":"http://localhost:8642"}, ← Hermes API
{"hostname":"ssh-kraken.qentra.top", "service":"ssh://localhost:22"}, ← SSH (published application route)
{"service":"http_status:404"} ← default catch-all
]}
```
⚠️ **Важно (путаница была!):** `kraken.qentra.top` — это **HTTP-маршрут на `localhost:8642` (Hermes API)**, НЕ SSH. Поэтому SSH к `kraken.qentra.top:22` и не шёл. Под SSH создан отдельный hostname **`ssh-kraken.qentra.top`**.
### ✅ SSH-доступ работает через `ssh-kraken.qentra.top` (РАБОЧИЙ, 2026-09-04)
**Вход (проверено, работает):**
```bash
ssh kraken@ssh-kraken.qentra.top
```
`~/.ssh/config` блок:
```
Host ssh-kraken.qentra.top
HostName ssh-kraken.qentra.top
User kraken
ProxyCommand cloudflared access ssh --hostname %h
```
Первый раз — авторизация cloudflared в Access-приложение:
```bash
cloudflared access login ssh-kraken.qentra.top
```
Маршрут на Kraken (UP, контейнер) подхвачен ingress `ssh-kraken.qentra.top → ssh://localhost:22`; sshd на Kraken — `0.0.0.0:22`. DNS проксировано CF (104.21.x / 172.67.x).
### ⚠️ Питфол: почему `ssh.kraken.qentra.top` (двухуровневый) НЕ сработал
Изначально под SSH добавлялся hostname `ssh.kraken.qentra.top` (`ssh.kraken` + `qentra.top`) → `ssh://localhost:22`, + Access Application (type SSH) + Service Token. Вход НЕ заработал. Решающий факт — на edge Cloudflare у этого hostname **НЕТ SSL/TLS-терминации**, в отличие от одиночных subdomain:
```
openssl s_client -connect ssh.kraken.qentra.top:443 → SSL alert 40 (handshake failure), no peer certificate
curl https://ssh.kraken.qentra.top → ssl=1 handshake failure
```
Любой `cloudflared access login/ssh` и Service Token к чистому двухуровневому SSH-hostname не помогают — клиент не доходит даже до авторизации из-за отсутствия edge-TLS.
**Решение, которое сработало:** использовать **одиночный subdomain через дефис** — `ssh-kraken.qentra.top` (покрывается Universal SSL `*.qentra.top`). На нём размещены тот же `published application route` SSH → `ssh://localhost:22` + Access Application. Вошло сразу.
> Почему так: Universal SSL зоны покрывает `*.qentra.top` (1 уровень) и `qentra.top`, но НЕ `*.kraken.qentra.top` (2 уровня). Для 2-уровневого нужен Advanced Certificate — проще взять одиночный subdomain.
### Диагноз IPv4/IPv6 «жив, но шумит» (актуально для обоих hostname)
- В логах cloudflared — часть `Registered tunnel connection ... protocol=quic`, но постоянные `ERR ... write udp [::]->198.41.x.x:7844: sendmsg: network is unreachable`.
- **Причина: на Kraken НЕТ глобального IPv6** (только `::1` и link-local `fe80::`; роутер `192.168.1.1` IPv6 не выдаёт → `ping6 2606:4700::1111` = `Network is unreachable`). cloudflared пробует QUIC и по IPv6 → каналы падают; туннель выживает по IPv4-QUIC.
- **Возможный фикс:** заставить cloudflared QUIC только по IPv4, чтобы убрать IPv6-шум.
### SSH через VPS reverse (независимо от cloudflared)
Остаётся рабочим внешним путём: reverse SSH туннель через VPS `91.207.28.205:2223` (см. ниже), НЕ cloudflared.
## Связанные заметки
- [[kraken-network]] — SSH, WG топология
- [[wireguard-vpn]] — полное описание WG (⚠️ VPS-звено мёртво на 2026-09-01)
- [[kraken-portainer-access]]
- [[openmediavault-rpi5]]
- [[tech/cloudflare-tunnel-ssh-published-route]] — генерализуемое руководство: SSH через Cloudflare Tunnel (1-уровневый subdomain + Universal SSL), питфолы