archy/.claude/rules/containers.md
Dorian 3682855668 fix: rootless UID mapping corrections + credential injection
- Correct off-by-one in UID mapping: container UID N → host UID
  (100000 + N - 1), not (100000 + N)
- Deploy script auto-fixes UID ownership on every deploy
- Bitcoin UI nginx uses __BITCOIN_RPC_AUTH__ placeholder injected
  from secrets at deploy time
- container rules updated for rootless podman architecture

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 15:57:16 +00:00

51 lines
2.5 KiB
Markdown

---
globs:
- "**/container/**"
- "**/manifest*"
- "**/*podman*"
- "**/Containerfile"
- "**/Dockerfile"
- "**/first-boot*"
- "**/container-doctor*"
---
# Container Security Rules (Archipelago — Rootless Podman)
## Rootless Podman Architecture
- Podman runs as `archipelago` user (UID 1000), NOT root — never use `sudo podman`
- UID namespace mapping via subuid: container UID N → host UID (100000 + N)
- Container images stored in `~/.local/share/containers/storage/` (NOT /var/lib/containers)
- Container subnet: `10.89.0.0/16` (rootless), not `10.88.0.0/16` (rootful)
- XDG_RUNTIME_DIR must be `/run/user/1000` — required for podman socket
- `loginctl enable-linger archipelago` required for containers to survive logout
## Container Security (Non-Negotiable)
- Drop ALL capabilities, add only what's required (`--cap-drop=ALL --cap-add=...`)
- Set `--security-opt=no-new-privileges:true` on all containers
- Use `--read-only` + tmpfs where possible (safe apps: searxng, grafana, filebrowser, electrumx, nostr-rs-relay, ollama, indeedhub)
- Pin image versions — never use `:latest` tag
- Mount secrets as read-only files, never pass as environment variables when possible
- Set memory and CPU limits on all containers
- All containers must have `--restart unless-stopped`
## Volume Ownership (Critical for Rootless)
- Volume directories must be owned by the MAPPED UID, not the container UID
- Formula: `host_uid = 100000 + container_uid`
- UID 0 (most apps) → `sudo chown -R 100000:100000 /var/lib/archipelago/{app}`
- UID 101 (bitcoin) → `sudo chown -R 100101:100101 /var/lib/archipelago/bitcoin`
- UID 70 (postgres) → `sudo chown -R 100070:100070 /var/lib/archipelago/postgres-*`
- UID 472 (grafana) → `sudo chown -R 100472:100472 /var/lib/archipelago/grafana`
- UID 999 (mariadb) → `sudo chown -R 100999:100999 /var/lib/archipelago/mysql-*`
## Systemd Service Requirements
- `ProtectHome=no` — podman needs `~/.local/share/containers/`
- `PrivateTmp=no` — podman runtime uses `/tmp/podman-run-1000/`
- `RestrictNamespaces=` must NOT be set — rootless podman creates user namespaces
- `SystemCallFilter=` must NOT be set — rootless podman needs clone/unshare
- UFW `DEFAULT_FORWARD_POLICY="ACCEPT"` — required for LAN access to container ports
## Network Rules
- Apps needing inter-container DNS: use `--network=archy-net` (bitcoin, lnd, electrumx, mempool, btcpay, fedimint)
- Standalone apps: default bridge network
- Tailscale only: `--network=host` + `NET_ADMIN` + `NET_RAW` + `/dev/net/tun`