- 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>
51 lines
2.5 KiB
Markdown
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`
|