Files
obsidian-vault/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
T

209 lines
19 KiB
Markdown
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.
---
tags: [truenas, restore, incident, git, media, arr, permission]
created: 2026-09-02
updated: 2026-09-02
status: in-progress (Этап 1 done — оба compose на PUID=950 правлены; Этап 2 заблокирован: transmission без compose-лейблов, нужен rm -f — ждёт ок Alex)
---
# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
**Сводный инцидент-документ.** Родитель: `2026-08-31-syncthing-truenas-incident.md` (Syncthing). Здесь документируем подтверждённый масштаб той же поломки прав и её воздействие на git-sync (мак/Eagle) и медиа/arr-стек на TrueNAS.
**Корень (общий для всех):** после восстановления из бэкапа `/mnt/RED_2TB/storage/*` стал принадлежать **uid 921 = `transmission`** с правами `----------`/`d---------` (0000), вместо `truenas_admin` (950). Контейнеры, пользователи и git, работающие под другими uid, теряют доступ.
---
## 1. Git-sync obsidian на маке (Eagle) — СТОИТ (ahead 55)
**Симптом на `~/obsidian`:**
```
git status → ## main...nas/main [ahead 55]
git fetch/push → fatal: '/mnt/RED_2TB/storage/git/obsidian-vault.git' does not appear to be a git repository
```
**Причина (read-only проверка TrueNAS):**
```
/mnt/RED_2TB/storage/git d--------- 4 921 921
/mnt/RED_2TB/storage/git/obsidian-vault.git (permission denied — вложено в storage/git)
```
Папка `storage/git/` (внутри — bare repo `obsidian-vault.git`) с правами 0000 / owner 921. Git на маке (SSH-ключ жив, `CONN_OK` ✅) не может открыть bare repo → fetch/push падают → локальные 55 коммитов не уходят.
**Важно про механизм cron — НЕ Hermes cron, а launchd:**
- Агент: `~/Library/LaunchAgents/com.sync-vault.plist`
- Программа: `/Users/admin/scripts/sync-vault.sh`, `StartInterval = 300` сек (5 мин)
- Состояние: **активен** (`launchctl list``com.sync-vault`), `runs = 3762`, `last exit code = 0`
- `sync-vault-lib.sh` **умышленно глушит** недоступность remote: если `git fetch` падает → логирует и `exit 0`. Поэтому launchd показывает exit code 0 при фактически сломанном sync (`ahead 55`). Это **не** сбой cron — это корректное поведение скрипта, скрывающее dead remote.
- Системный crontab на маке пуст (`crontab -l` нет), `/etc/crontab` нет. Запуск — только через launchd `${LABEL}`.
**Связанные доки:** `family/how-to/vault-git-sync.md`, `family/how-to/obsidian-sync.md`.
---
## 2. Медиа-папки TrueNAS — сломаны (uid 921, d---------/drwx------)
Read-only проверка `ls -ld /mnt/RED_2TB/storage/`:
```
d--------- transmission transmission storage/Cartoons
d--------- transmission transmission storage/Downloads
d--------- transmission transmission storage/Movies
d--------- transmission transmission storage/Music
drwx------ transmission transmission storage/series
d--------- transmission transmission storage/shared
```
(отображается как `transmission` = uid 921 — тот же, что и в инциденте; права 0000/700 сломаны для внешних сервисов.) `Movies-Radarr`, `Series-Sonarr` — не существуют (старая схема, на TrueNAS актуальны только `storage/`-медиа).
Медиа-папки починки не проходили (в отличие от `obsidian`/`obsidian-syncthing`).
---
## 3. Arr/Jellyfin-стек на TrueNAS — выключен + не сможет работать из-за прав
**Факты:**
- В `docker ps -a` НЕТ ни radarr/sonarr/prowlarr/jellyfin контейнеров (ни запущенных, ни остановленных). Стек не пересоздавался после проблемного периода.
- Composes живут в `/mnt/RED_2TB/docker/arr/`: `docker-compose.yml` (prowlarr/radarr/sonarr/jellyfin), подпапки `prowlarr/ radarr/ sonarr/ jellyfin/ media-pipeline/`.
- `docker/arr/docker-compose.yml` монтирует `/mnt/RED_2TB/storage` в radarr/sonarr (rw) и jellyfin (`:ro`). При сломанных правах медиа-папок контейнеры не смогут читать/импортировать → пустая библиотека.
- **Работает только `transmission`** (Up 8 days), его compose отдельный: `/mnt/RED_2TB/docker/transmission/`. config владелец `transdamon`/911. RPC `transmission-remote` вернул `403 Forbidden` — auth вкл., статус торрентов извне без токена не читается.
**Чтобы запустить стек:** восстановить права медиа-папок `storage/` (150 и выше 950/нужный сервисный uid, rwX) → `cd /mnt/RED_2TB/docker/arr && docker compose up -d`.
**Дексо стек** — разделение ролей (см. `family/how-to/arr-stack-taiga.md`):
- Taiga (TrueNAS) = acquisition (transmission качает, arr добавляет)
- Kraken = serving (jellyfin на план-сервире). Jellyfin на TrueNAS — acquisition-side media storage, не primary serving.
---
## 4. Несвязанные находки (не трогала диагностика, для контекста)
- Контейнер `library-app` (`library`) в restart-loop: `FileNotFoundError: '/library/flibusta_fb2_local.inpx'` — это приложение полки книг Flibusta, монтирует `/mnt/RED_2TB/storage/Downloads/fb2.Flibusta.Net → /library`; INPX-файла на пути нет. Не связано с поломкой прав; ручной фикс только по запросу.
---
## Общий план (ИСПРАВЛЕН — НЕ выполнен, требует подтверждения)
> ⚠️ **2026-09-02 УТОЧНЕНО: `/mnt/RED_2TB/storage` — единый ZFS dataset `RED_2TB/storage` с `acltype=nfsv4`. НЕ ПОСIX.** Ранее планировалось `chown -R`/`chmod -R` — это **НЕВЕРНО** для TrueNAS SCALE с NFSv4 ACL. Права управляются **только** через `midclt call filesystem.setacl` (эквивалент TrueNAS UI → Datasets → `RED_2TB/storage` → Edit ACL → Apply recursively). `truenas_admin` = FULL_ADMIN в midclt → **root/sudo для setacl НЕ нужен** (проверено: `midclt call auth.me` → roles `FULL_ADMIN`).
### Диагноз по NFS4 ACL (что именно сломано)
Реальные ACL через `midclt call filesystem.getacl` у `storage`, `git`, `Downloads`, `Movies`, `Cartoons` **структурно ИДЕНТИЧНЫ рабочему `obsidian`**, но:
1. владелец **uid/gid = 921** (`transmission`) вместо правильного **950** (`truenas_admin`);
2. у записи `owner@` **обнулены `READ_DATA / WRITE_DATA / EXECUTE / APPEND_DATA / DELETE_CHILD`** (все в `false`) → POSIX-маска `d---------`.
Рабочий эталон (`obsidian`): uid/gid **950:950**, `owner@` полный, `group@` rwX (без WRITE_ATTRS/ACL/OWNER), `everyone@` — только attrs.
### Проверенная сигнатура midclt
```bash
# Filesystem-методы существуют (40, подтверждены 2026-09-02):
# filesystem.getacl / filesystem.setacl / filesystem.chown / filesystem.setperm
# setacl(schema): {path, uid, gid, dacl, nfs41_flags, acltype, options}
# options: {stripacl:bool, recursive:bool, traverse:bool, canonicalize:bool(default true),
# validate_effective_acl:bool(default true)}
# dacl: массив ACE {tag: owner@|group@|everyone@|USER|GROUP, id, type: ALLOW|DENY, perms, flags}
```
### Применение (скрипт готов на маке: `~/fix-storage-nfs4-acl.sh`)
Скрипт делает 3 этапа; применяется через `filesystem.setacl` с uid/gid + dacl + options.recursive/traverse:
```bash
# скопировать на TrueNAS и запустить (sudo не нужен):
scp ~/fix-storage-nfs4-acl.sh truenas_admin@mallexxx.duckdns.org:/tmp/
ssh truenas_admin@mallexxx.duckdns.org 'bash /tmp/fix-storage-nfs4-acl.sh'
```
Внутри:
1. **Бэкап ACL** каждой папки → `/mnt/RED_2TB/backup/facl_fix_<дата>/<name>.acl.json` (откат — применить сохранённые через `filesystem.setacl`).
2. **Применение** к каждой папке списка (`Cartoons Downloads Edu Movies Music ada3s1 art books cartoons-series documentaries documentaries-series git nas photo_dedup_test seafile series shared singularity sonarr work`):
```bash
midclt call filesystem.setacl <path> \
'{"uid":950,"gid":950,"dacl":[<эталон-ACE obsidian>],"options":{"recursive":true,"traverse":true,"stripacl":false}}'
```
3. **Verify**: все каталоги → uid 950; тест `git --git-dir=<base>/git/obsidian-vault.git log`.
> `obsidian` / `obsidian-syncthing` в список **НЕ включены** — уже рабочие 950:950.
### После фикса прав
- **На маке**: `bash ~/scripts/sync-vault.sh` → 55 коммитов уедут в bare repo (launchd возобновит нормальную работу; см. раздел 1).
- **Arr-стек**: `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps`.
### Открытые вопросы перед запуском (подтверждение Alex)
- Transmission-контейнер работает **как root** (`docker exec transmission id` → uid 0), поэтому права медиа для него некритичны; но данные Transmission (downloadDir `/mnt/storage/Downloads`) док описывал под `transdamon`/911. Решено ставить единообразно **950** (нужно для git/arr/serving). Если нужен и доступ transdamon/911 — добавить явный ACE `GROUP` на 911.
- Альтернатива владельцу 950 — общая группа `nas_users` (3000, члены: truenas_admin, transmission, nas и др.). Эталон obsidian = 950.
---
## 5. ⭐ РЕШЕНИЕ от 2026-09-02: ВАРИАНТ А — единый PUID/PGID=950 + рекурсивный фикс прав
**Контекст:** вопрос «будут ли файлы, созданные transmission, читаться jellyfin?» вскрыл, что простой смены владельца верхних папок на 950 **НЕДОСТАТОЧНО** — нужно согласовать межсервисную читаемость контента + фикс вглубь файлов. Выбран **Вариант A** (одобрен Alex, стоп-правило: root-команды выполняет Alex, обязательный бэкап перед изменениями).
### Факты, на которых строится решение
- **Все linuxserver-образы загружены локально на TrueNAS** (проверено `docker images`): `radarr`, `sonarr`, `jellyfin`, `transmission`, `prowlarr`. Контейнеры (кроме transmission) **ещё не созданы** из них.
- **Проверен механизм PUID/PGID** в linuxserver: Entrypoint=`[/init]`, есть `/etc/s6-overlay` (s6-based). → `PUID`/`PGID` обрабатываются штатно s6-stage2-hook. Нативный механизм.
- **Дефолт без PUID:** у текущего transmission `PUID` не задан → контейнер работает **root** (подтверждено `docker exec transmission id` → root). Медиа-образы без явного PUID разъехались бы по разным uid → не видят файлы друг друга.
- **Масштаб поломки вглубь** (подсчёт через transmission-root, `find -printf '%u'`):
- `Movies`: **257 908** файлов, из них **257 751 → uid 921** (+157 → 950).
- `Downloads`: mix 518→911, 380→921, 7→1001.
- `series`: 94 → 921; `Music`: 8484; `git`(bare): 3430; `work`: 26180; `Cartoons`: 1009.
- Глубокие `.mkv`/`.avi`/`.iso` тоже `921:921 mode 0000` — поломка НЕ только каталоги, а каждый inode.
### Целевая модель Варианта А
Все медиа/arr/jellyfin контейнеры работают под **единым uid 950:950** (`truenas_admin`, как obsidian-эталон), тогда transmission→(radarr→jellyfin) взаимно читают:
- transmission (станет 950) качает → файлы 950
- radarr/sonarr (950) импортируют → 950
- jellyfin (950) читает → ✅
### Этапы (рабочий статус на 2026-09-02)
- ✅ **Этап 0 — БЭКАП выполнен** → `/home/truenas_admin/variantA_bk/`:
`transmission.docker-compose.yml` (оригинал), `arr.docker-compose.yml` (оригинал), `transmission_settings.json` (2982 б), ACL топ-папок (Cartoons Downloads Movies Music git work series books shared) + README.txt.
> ⚠️ **Питфолл бэкапа:** `/mnt/RED_2TB/backup` — root-owned (`drwxrwxr-x root root`), `truenas_admin` туда **писать не может** → бэкап сделан в домашку `~truenas_admin`. Файл `docker/transmission/docker-compose.yml` на хосте владеет uid 911 (`drwx------ 911 911`) → прочитан/скопирован **через root-контейнер** `docker exec transmission cat /config/docker-compose.yml`.
- ✅ **Этап 1 — правка compose на PUID/PGID=950 ВЫПОЛНЕНА (2026-09-02):**
- **transmission compose:** `/config/docker-compose.yml` перезаписан через `docker cp /tmp/transmission-new-compose.yml transmission:/config/docker-compose.yml`. Добавлено `PUID=950` + `PGID=950`. Оригинал в `variantA_bk`. (Скрипт-источник на маке: `~/transmission-new-compose.yml`.)
- **arr compose:** `/mnt/RED_2TB/docker/arr/docker-compose.yml` перезаписан (владелец truenas_admin → запись прошла без root): во всех 4 сервисах (prowlarr/radarr/sonarr/jellyfin) добавлено `PUID=950` + `PGID=950` (у всех уже был `TZ`). Проверено `docker compose config --quiet` → `COMPOSE_VALID`. Источник: `~/arr-new-compose.yml`. Оригинал в `variantA_bk`.
- **Arr compose НЕ имеет секции `networks` у сервисов** → при подъёме будет дефолт-сеть `<project>_default`. Consciously оставлено (оригинал не задавал networks у radarr/sonarr/jellyfin явно, только глобально `media_net`/`caddy_default`; НЕ переопределено чтобы не менять поведение без надобности).
- ⚠️ **Этап 2 — ПЕРЕСОЗДАНИЕ transmission — ЗАБЛОКИРОВАНО (ключевой технический питфолл, 2026-09-02):**
- `cd /mnt/RED_2TB/docker/transmission` → **`permission denied`** (каталог `drwx------ 911 911` — владелец transdamon/911, truenas_admin нет доступа).
- Попытка `docker compose -f /tmp/transmission-new-compose.yml up -d` → создал сеть `tmp_default` и упал: **name conflict** (`container "transmission" already in use`).
- Полный рабочий контейнер пересоздать из compose **невозможно** по двум причинам:
1. Контейнер создан **без compose-лейблов** (`com.docker.compose.project = <no value>`) → compose его **не распознаёт как свой** → не может мигрировать/пересоздать, только name-conflict.
2. Композ-файл в каталоге 911 недоступен для работы оттуда.
- **Найденное решение:** удалить старый контейнер и поднять заново с корректным проектом (сохранит сеть `transmission_default`):
```bash
docker rm -f transmission # данные в /config и /mnt/storage НЕ теряются (bind-mount на диск)
docker compose -f /tmp/transmission-new-compose.yml -p transmission up -d
docker exec transmission id # ожидаем uid=950
```
- **Почему PUID нельзя применить к работающему контейнеру?** env зашивается при `docker run` (создании). `docker update` меняет ТОЛЬКО ресурсы (CPU/RAM/restart), **НЕ переменные окружения** — ограничение Docker. Пересоздание — единственный способ.
- **Альтернатива без пересоздания (если не хотим трогать transmission root):** оставить root; НО тогда новые скачанные файлы будут `root:root`, и radarr/jellyfin (под 950) их не прочитают. → PUID=950 у transmission всё же нужен для цепочки. (Обход с setgid-битом на dir + group 950 возможен как fallback, не выбран.)
- **Статус:** ждёт ок Alex на `docker rm -f transmission`.
- ⬜ **Этап 3 — рекурсивный фикс прав вглубь** (root → выполняет Alex, масштаб ~257k+ файлов): рекурсивно вернуть владельца 950:950 + снять 0000 на файлах/папках (`find ... -exec chown -R 950:950 {} +`, `chmod u+rw,g+r` для файлов, `u+rwX,g+rwX` для каталогов). Или через UI `Apply recursively`.
### Точное содержание transmission compose (оригинал, важно для пересоздания)
```yaml
services:
transmission:
image: lscr.io/linuxserver/transmission:latest
container_name: transmission
restart: unless-stopped
environment:
- TZ=Asia/Novosibirsk
- USER=transmission_admin
- PASS=E$%p3En4a%R6
- WHITELIST=172.16.*.*
volumes:
- /mnt/RED_2TB/storage:/mnt/storage
- /mnt/RED_2TB/docker/transmission:/config
ports:
- 9091:9091
- 51413:51413
- 51413:51413/udp
```
(в compose НЕ было PUID/PGID → контейнер root; transmission монтирует `storage` → `/mnt/storage`, download-dir `/mnt/storage/Downloads`)
## Связанные заметки
- [[2026-08-31-syncthing-truenas-incident]] — вызвавший инцидент + фикс syncthing
- [[arr-stack-taiga]] — acquisition-стек на TrueNAS
- [[arr-stack-kraken]] — serving-стек (Kraken)
- [[vault-git-sync]] — git-sync алгоритм и скрипты
- [[obsidian-sync]] — общая схема Eagle↔Taiga↔Kraken
- [[syncthing-truenas-android]] — syncthing схема (требует обновления volumes → `obsidian-syncthing`)