diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md index f48880eb..5dec2a10 100644 --- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md +++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md @@ -2,7 +2,7 @@ tags: [truenas, restore, incident, git, media, arr, permission] created: 2026-09-02 updated: 2026-09-02 -status: in-progress (Этап 0-3 + 2-й root-проход DONE — storage все 950, git-sync fetch ОК; ОСТАЛОСЬ основной блокер: NFS4 ACL DENY READ_DATA на ~1946 git-объектах → push на маке падает) +status: resolved (Git-sync на маке ПОЧИНЕН 2026-09-02: NFS4 ACL DENY сняты полным dacl-заменой через filesystem.setacl; push ИДЁТ; ahead=0 behind=0. Осталось: подъём arr-стека + фикс права на скрытые файлы корня storage + не связанный library-app restart-loop) --- # Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния @@ -174,7 +174,7 @@ ssh truenas_admin@mallexxx.duckdns.org 'bash /tmp/fix-storage-nfs4-acl.sh' - `storage/git/obsidian-vault.git` теперь **открывается** — `git log` показывает коммиты (HEAD `a2c72793` [2026-08-17]...). - `git/nas` уже не «matching вне-списка» — chown/chmod догнаны вторым проходом. -### ⬜ Найден БЛОКЕР PUSH: NFS4 ACL `DENY READ_DATA` на тировых объектах git-репозитория +### ✅ RESOLVED 2026-09-02: Блокер push снят — полной dacl-заменой, НЕ stripacl **Симптом (после починки прав):** на маке git-sync теперь: ``` @@ -193,25 +193,29 @@ git push nas main → FAIL: | `git cat-file -t acd1b45...` под root (gitea) | `tree` — объект читается, цел | | NFS4 ACL самого файла `objects/ac/d1b...` (через `filesystem.getacl`) | **`owner@ DENY READ_DATA=True`** поверх `ALLOW` → перекрывает | -**Суть:** у ~**1946 из 3373** loose git-объектов в `obsidian-vault.git/objects/` стоит битый NFS4 ACL `owner@ type=DENY READ_DATA=True`. DENY-запись в NFSv4 **перекрывает ALLOW**, поэтому владелец (uid 950 = truenas_admin) **не может mmap/прочитать эти объекты** при push. Git на приеме (`git-receive-pack`/`index-pack`) трактует нечитаемость как «loose object corrupt» → push rejected. Root игнорирует ACL → поэтому fsck под gitea чист, а push от 950 падает. **Данные целы.** +**Суть:** у **1946 из 3373** loose git-объектов в `obsidian-vault.git/objects/` стоит битый NFS4 ACL `owner@ type=DENY READ_DATA=True`. DENY-запись в NFSv4 **перекрывает ALLOW**, поэтому владелец (uid 950 = truenas_admin) **не может mmap/прочитать эти объекты** при push. Git на приеме (`git-receive-pack`/`index-pack`) трактует нечитаемость как «loose object corrupt» → push rejected. Root игнорирует ACL → поэтому fsck под gitea чист, а push от 950 падает. **Данные целы.** -POSIX-признак битых файлов: **mode `40` (`r--------`)**; нормальные объекты — mode `750`. (count: 3373 объектов, ~1946 с mode 4xx.) +POSIX-признак битых файлов: **mode `40` (`r--------`)**; нормальные объекты — mode `750`. -**Фикс (root/root-midclt) — ГОТОВ, Alex ещё не запускал (ждёт отдельного подтверждения, т.к. влияет на ACL объектов):** +**Фикс РАБОЧИЙ (RESOLVED 2026-09-02, Alex выполнил из root-шелла):** ⚠️ **`stripacl:true` с пустым `dacl:[]` НЕ работает** — джоба при `filesystem.setacl /path ` вываливалась `Too many arguments (expected 1, found 2)` (path передавался как отдельный аргумент — палево), а точечный `stripacl:true` на объекте дал `SUCCESS`, но DENY-ACL **остался**. Решило только **полная dacl-замена** (передача полного массива ACE без DENY, где owner@/group@ = ALLOW), т.е. setacl воспринимает полный dacl как *.replace* всего ACL, а не merge. + +Проверенная рабочая команда (root-шелл для truenas_admin, job асинхронный — вернёт id, потом `SUCCESS`): ```bash -# снять битые NFS4 ACL (DENY) на весь git-репозиторий, вернуть стандарт из uid/gid -midclt call filesystem.setacl /mnt/RED_2TB/storage/git/obsidian-vault.git \ - '{"uid":950,"gid":950,"dacl":[],"options":{"recursive":true,"traverse":true,"stripacl":true}}' +# точечно на один объект (проверка метода): +midclt call filesystem.setacl '{"path":"/mnt/RED_2TB/storage/git/obsidian-vault.git/objects/ac/d1b45542e82aa40f345da3b8ac9f721633b138","uid":950,"gid":950,"acltype":"NFS4","dacl":[, ],"options":{"recursive":false}}' -# проверить, что проблемный объект читается владельцем: -git --git-dir=/mnt/RED_2TB/storage/git/obsidian-vault.git cat-file -t acd1b45542e82aa40f345da3b8ac9f721633b138 - -# затем с мака: -cd ~/obsidian && git push nas main +# затем рекурсивно на весь репозиторий (dacl тот же, recursive:true): +midclt call filesystem.setacl '{"path":"/mnt/RED_2TB/storage/git/obsidian-vault.git","uid":950,"gid":950,"acltype":"NFS4","dacl":[{"tag":"owner@","id":-1,"type":"ALLOW","perms":{"READ_DATA":true,"WRITE_DATA":true,"EXECUTE":false,"APPEND_DATA":true,"DELETE_CHILD":false,"DELETE":false,"READ_ATTRIBUTES":true,"WRITE_ATTRIBUTES":true,"READ_NAMED_ATTRS":true,"WRITE_NAMED_ATTRS":true,"READ_ACL":true,"WRITE_ACL":true,"WRITE_OWNER":true,"SYNCHRONIZE":true},"flags":{"BASIC":"INHERIT"}},{"tag":"group@","id":-1,"type":"ALLOW","perms":{"READ_DATA":true,"WRITE_DATA":false,"EXECUTE":true,"APPEND_DATA":false,"DELETE_CHILD":false,"DELETE":false,"READ_ATTRIBUTES":true,"WRITE_ATTRIBUTES":false,"READ_NAMED_ATTRS":true,"WRITE_NAMED_ATTRS":false,"READ_ACL":true,"WRITE_ACL":false,"WRITE_OWNER":false,"SYNCHRONIZE":true},"flags":{"BASIC":"INHERIT"}}],"options":{"recursive":true,"traverse":true}}' ``` -> ⚠️ `chmod`/`chown` НЕ снимает NFS4 `DENY` ACE — нужен именно `stripacl:true` (или `setfacl -Rb` на хосте, если доступен). TrueNAS SCALE: `setacl` с `stripacl:true` + пустым `dacl` переведёт на POSIX-default из owner@/group@. -> ⚠️ Перед применением снять бэкап ACL `git/` (read-only): `midclt call filesystem.getacl /mnt/RED_2TB/storage/git > ~/variantA_bk/git_after.acl`. -> ⚠️ Стоп-правило Alex: nothing destructive без явного подтверждения — не делался, ждёт команды. +**Ключевое правило midclt:** `filesystem.setacl` принимает **ОДИН JSON-аргумент со всеми полями** (`path`, `uid`, `gid`, `acltype`, `dacl`, `options`) — НЕ path отдельно. Job вернёт id и выполнится асинхронно; проверять `midclt call core.get_jobs` (state `SUCCESS`/error). + +Проверка после (для владельца 950, НЕ root): +```bash +git --git-dir=/mnt/RED_2TB/storage/git/obsidian-vault.git cat-file -t acd1b45542e82aa40f345da3b8ac9f721633b138 # → tree +git --git-dir=/mnt/RED_2TB/storage/git/obsidian-vault.git fsck # → 0 corrupt/missing/permission denied +``` + +**Результат (проверено 2026-09-02):** объект читается (`tree`), `git fsck` под 950 → **0 ошибок**, и с мака `git push nas main` **прошёл** (`a2c7279..2b6c181 main -> main`, exit=0). Локально/remote **ahead=0 behind=0** — полностью синхронизировано. launchd-крона (коммиты каждые 5 мин) теперь реально пушит. Бэкап ACL репо снят до фикса: `~/variantA_bk/git_ACL_backup_2026-09-01_232328/` (root ACL + mode-map 3402 файлов). **Куда попадают DENY-ACL:** артефакт restore — часть loose-объектов при переносе данных получили инверсивный NFS4 ACL (`owner@ DENY read`). Это НЕ родние git-права (git-репо хранит объекты как read-only `r--r--r--` в обычном случае); на полке после поломки. @@ -239,9 +243,11 @@ git --git-dir="$S/git/obsidian-vault.git" log --oneline -2 2>&1 | head # пр > ⚠️ `rm -f` скрытых файлов корня — mac-мусор/остатки старого владельца 921, безопасно. Если `.profile`/`.bashrc` рабочие — убрать их из rm по решению. -### ⚠️ Текущий статус после второго прохода (актуально) -- **Push на маке БЛОКИРОВАН** NFS4 ACL `DENY READ_DATA` на ~1946 loose git-объектах — см. секцию «Найден БЛОКЕР PUSH» выше. Фикс `filesystem.setacl ... stripacl:true` ГОТОВ, Alex должен подтвердить. -- **Arr-стек**: после снятия DENY-блокера на git и починки прав медиа — `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps` + что jellyfin видит библиотеки (пути `/storage/*`). PUID/PGID=950 уже прописаны во всех 4 сервисах докер-стека. +### ⚠️ Текущий статус после вторго прохода + RESOLUTION (актуально на 2026-09-02) +- ✅ **Push на маке ПОЧИНЕН** — NFS4 ACL `DENY` на ~1946 loose git-объектах сняты через **полную dacl-замену** (не stripacl). См. секцию «✅ RESOLVED … dacl-заменой». Git-sync работает: fetch+push проходят, ahead=0 behind=0. +- 🔜 **Arr-стек** — НЕ поднят ещё. Все compose (`arr/docker-compose.yml` + transmission compose) уже с `PUID/PGID=950`, права медиа 950:950. Для запуска: `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps` + что jellyfin видит библиотеки (пути `/storage/*`). Обрати внимание: radarr/sonarr/jellyfin монтируют `/mnt/RED_2TB/storage` как `/storage`, а не `/media` (в отличие от Kraken-схемы) — root folder у radarr на TrueNAS другой. +- 🔜 **Скрытые файлы корня storage** (`.DS_Store`, `.bash_*`, `.profile`, `.com.apple*`) остаются `921:921 mode 0000` (не добиты — в root-шаге B rm для них предлагался, но решение не подтверждено). Сам корень `storage/` → `drwxrwx--- 921:921` (owner 921, хотя group 950 есть). Проверить, нужен ли корню chown 950. +- 🔜 **`books`** — владелец `950:921` (не 950:950) — оставлен как был (служебный, не критично для saga). ### Точное содержание transmission compose (оригинал, важно для пересоздания) ```yaml diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md index c13c0588..a1897e99 100644 --- a/family/how-to/arr-stack-taiga.md +++ b/family/how-to/arr-stack-taiga.md @@ -21,7 +21,7 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20. > **Transmission originally ran as ROOT** (not 911/950): its compose had no `PUID`/`PGID`, so linuxserver `/init` (s6-overlay) left it root. ✅ **Fixed 2026-09-02:** `PUID=950 PGID=950` added and container force-recreated → now daemon under uid 950. It mounts `storage` → `/mnt/storage`, download-dir = `/mnt/storage/Downloads`. > **Fix direction (approved Variant A):** set `PUID=950 PGID=950` on transmission + all arr/jellyfin compose services so every service shares uid `950` and mutually reads files; then recursively fix perms in depth. Plan + commands: `2026-09-02-restore-privilege-scope.md` §5. > -> **PROGRESS 2026-09-02 (updated end-of-session):** Both compose files edited for PUID/PGID=950 (transmission at `/mnt/RED_2TB/docker/transmission/docker-compose.yml`, arr at `/mnt/RED_2TB/docker/arr/docker-compose.yml` incl. all 4 services). **transmission container force-recreated** from its proper folder (`cd /mnt/RED_2TB/docker/transmission && docker compose up -d --force-recreate`) and now runs its daemon as **uid 950** (was root) — env `PUID=950 PGID=950` confirmed, process under `abc`/950. Media perms in depth recursively fixed to `950:950 drwxrwx---` on all top storage folders (incl. git/nas 2nd root pass). **Still to do before arr stack is useful / to unblock git-sync:** (1) strip the broken NFS4 `DENY READ_DATA` ACL on ~1946 loose git objects in `obsidian-vault.git` (this breaks `git push` from Mac with fake "corrupt" error) — fix command and details in `2026-09-02-restore-privilege-scope.md`; (2) then `cd /mnt/RED_2TB/docker/arr && docker compose up -d` and verify jellyfin sees libraries (`/storage/*`). +> **PROGRESS 2026-09-02 (updated end-of-session):** Both compose files edited for PUID/PGID=950 (transmission at `/mnt/RED_2TB/docker/transmission/docker-compose.yml`, arr at `/mnt/RED_2TB/docker/arr/docker-compose.yml` incl. all 4 services). **transmission container force-recreated** from its proper folder (`cd /mnt/RED_2TB/docker/transmission && docker compose up -d --force-recreate`) and now runs its daemon as **uid 950** (was root) — env `PUID=950 PGID=950` confirmed, process under `abc`/950. Media perms in depth recursively fixed to `950:950 drwxrwx---` on all top storage folders (incl. git/nas 2nd root pass). **Git-sync on Mac now RESOLVED** — the broken NFS4 `DENY READ_DATA` ACL on ~1946 loose git objects in `obsidian-vault.git` was stripped via full **dacl replacement** (not stripacl), so `git push` from Mac works again (push `a2c7279..2b6c181`, ahead=0 behind=0). Details + exact working command in `2026-09-02-restore-privilege-scope.md`. **Remaining:** `cd /mnt/RED_2TB/docker/arr && docker compose up -d` and verify jellyfin sees libraries (`/storage/*`). NOTE: radarr/sonarr/jellyfin mount `/mnt/RED_2TB/storage` as `/storage` (NOT `/media` like Kraken) — radarr root-folder on Taiga differs. ## Notes from Setup (2026-05-20)