docs(wiki): registry.kzntsv.site auth-model concept + bind-mount config-mode postmortem

- concepts/registry-kzntsv-auth-model.md (new): standalone registry:2 + htpasswd
  Basic, binary access, NOT Gitea-packages; how to add htpasswd users (hot-reload),
  GC via v2 DELETE + host garbage-collect; users vitya + books-ci.
- concepts/bindmount-config-edit-preserve-mode.md (new): mktemp+mv drops file mode
  644->600 -> non-root container (uid 1000) EACCES crash-loop; chmod --reference.
  Worked example: books-job-scheduler prod-down incident 2026-06-18.
- vds-kzntsv entity registry row links the auth-model concept.
- index.md + log.md updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-18 10:39:33 +03:00
parent 53c2756265
commit 28de8e60e0
5 changed files with 105 additions and 1 deletions

View File

@@ -0,0 +1,44 @@
---
title: Правка bind-mounted config'а — сохранять file mode (иначе EACCES crash-loop у non-root контейнера)
type: concept
tags: [docker, bind-mount, permissions, gotcha, postmortem, books-vds, eacces]
updated: 2026-06-18
---
# Bind-mounted config: сохраняй mode при правке
Когда правишь config-файл, **bind-mounted в контейнер, который бежит под non-root юзером** (например books `node`, uid 1000), атомарная запись через `mktemp`+`mv` **теряет исходный режим файла** и роняет контейнер в crash-loop с `EACCES`.
## Механизм
```bash
jq ... > "$(mktemp)" && mv tmp default.json # ← баг
```
`mktemp` создаёт файл `0600 root`. `mv` (rename) **заменяет inode**, перенося права temp-файла на цель — **режим назначения НЕ сохраняется** (в отличие от `cp`/`install`). Исходный был `0644`; после правки `others`-read бит пропал → контейнерный uid 1000 не может открыть файл → падение ещё в загрузчике config'а (node lib `config`, `util.parseFile`), до любого прикладного кода:
```
Error: Config file .../default.json cannot be read. Error code is: EACCES
```
## Правильно
Восстановить mode/owner после mv — проще всего по до-правочному бэкапу (`cp -a` его сохраняет):
```bash
chmod --reference="$backup" "$f" && chown --reference="$backup" "$f"
```
Альтернативы: `install -m 644`, либо запись **в тот же inode** (сохраняет mode):
```bash
jq ... > "$f.new" && cat "$f.new" > "$f" && rm "$f.new"
```
Всегда `ls -l` после — убедиться, что `others`-read бит на месте для контейнерного uid.
## Инцидент 2026-06-18 (worked example)
Добавлял `docker.registryAuth` (кред [`books-ci`](registry-kzntsv-auth-model.md)) в `/opt/books/job-scheduler/config/default.json` на [`books-vds`](../entities/books-vds.md) через `jq>mktemp && mv`. Режим слетел `644 → 600 root`. На ближайшем деплое books (`all`-tenant пересоздаёт контейнеры) `books-job-scheduler` ушёл в `Restarting (1)` каждые ~60с**прод-даун, slovo-cron'ы стояли**. Корень — не код books и не деплой, а мой способ записи.
Фикс: `chmod/chown --reference=<backup>``-rw-r--r-- root root`. Контейнер сам поднялся на следующей попытке restart-policy (отдельный рестарт не нужен — policy уже крутила). `registryAuth` остался цел.
## Связанные uid-гочи
- [`ocis-on-vds-deploy-recipe`](ocis-on-vds-deploy-recipe.md) — UID 1001 vs 1000 на oCIS (тот же класс: контейнерный uid ≠ root, права host-файлов должны это учитывать).
- [`registry-kzntsv-auth-model`](registry-kzntsv-auth-model.md) — что за кред клали, когда словили инцидент.