feat(albok-service): unit 6 sealed wing personal/sealed — LUKS2 volume, gid 1513, rslave bind of /srv/albok/sealed

albok-dev's host ask under Prime's goal "land all of u6". A 20 GiB LUKS2 file
with ext4, keyed only from the vault (piped over ssh, never on the host's
disk). The mountpoint is immutable while unmounted, and the mounted root is
1500:1513 0750 with .albok-wing. The parent is bound into the container with
rslave propagation: a lock then unlock with the service running showed through
without a restart. The wing is not in albok.yaml yet (albok-dev's release).
Runbook and backup posture are in the README.
This commit is contained in:
vh
2026-10-16 08:06:06 -07:00
parent 16e0dac572
commit 767b3af409
4 changed files with 40 additions and 0 deletions
+29
View File
@@ -184,3 +184,32 @@ docker push gitea.phasefinal.com/pfi/albok-service:<ver> # then pin the new
Mount `/srv/albok/store` **`:ro`**, `group_add` the read group's **numeric** gid (1510 / 1511), set
`GIT_OPTIONAL_LOCKS=0`, and add `safe.directory` for the path as the viewer sees it. Nothing else
may mount the store read-write.
## Sealed (encrypted) wings: unit 6 (2026-10-16)
The first is `personal/sealed`: restricted, default-deny, gid **1513 `albok-sealed`**. It is empty, has no Nemi source,
and what goes in it is Prime's call. albok-dev provisioned it on Prime's goal "land all of u6". **It is not yet in
`albok.yaml`**: albok-dev sends that stanza with the release that carries the encrypted-wing replay fix. Until then the
service ignores the mount.
| | |
|---|---|
| container | `/srv/albok/luks/personal.sealed.img`: a 20 GiB file (root 0600, dir 0700), LUKS2 aes-xts, ext4 inside (label `personal.sealed`). The file grows later with `truncate` + `cryptsetup resize` + `resize2fs` |
| key | vault **`albok/wing-key-personal-sealed`** (64 random base64 chars). It is never written to nh3-albok's disk: it is piped in over ssh, and infra-ops' sudo there logs output, not input |
| mapping | `/dev/mapper/albok-personal-sealed` |
| mountpoint | **`/srv/albok/sealed/personal.sealed`**: EMPTY and `chattr +i` while unmounted, so nothing can write plaintext into a locked wing (root's `touch` there fails, tested). The mounted root is `1500:1513 0750`, holding `.albok-wing` (`1500:1513 0640`) and ext4's `lost+found`. The service creates `store/` and `private/` there on its first start with the wing configured |
| container view | the parent `/srv/albok/sealed` is bound with `propagation: rslave`, so a lock or unlock on the host shows inside the RUNNING container (tested both ways) |
```bash
# UNLOCK (after a VM reboot the wing comes up LOCKED, by design: no crypttab). No service restart is needed.
secret get albok/wing-key-personal-sealed | tr -d '\n' | ssh infra-ops@10.100.50.44 \
'sudo -n cryptsetup open --type luks2 --key-file=- /srv/albok/luks/personal.sealed.img albok-personal-sealed \
&& sudo -n mount /dev/mapper/albok-personal-sealed /srv/albok/sealed/personal.sealed'
# LOCK: the service holds the wing's SQLite/Chroma open, so umount refuses while it runs.
ssh infra-ops@10.100.50.44 'cd /opt/docker/compose/albok-service && sudo -n docker compose stop \
&& sudo -n umount /srv/albok/sealed/personal.sealed && sudo -n cryptsetup close albok-personal-sealed \
&& sudo -n docker compose start'
```
**Backups:** nh3-albok has NO file-level backup, so nothing ever copies the plaintext. Its whole-VM vzdump (nh3-pve-2,
21:30) holds the LUKS file, which is ciphertext; the key lives only in the vault. Never add a file-level job that walks
`/srv/albok/sealed`. ⚠ Losing the vault entry loses the wing.