diff --git a/.claude/agents/security-auditor.md b/.claude/agents/security-auditor.md index d1f7ef4..026c23f 100644 --- a/.claude/agents/security-auditor.md +++ b/.claude/agents/security-auditor.md @@ -83,3 +83,5 @@ Key management: private key (`JWT_PRIVATE_KEY`) signs tokens and must never be e - Never use `text()` with string interpolation in SQLAlchemy queries - Never expose `hashed_password`, `is_superuser`, or internal IDs in API responses unless explicitly required - After any code change, verify the pre-commit hook still passes +- **Never mount `/var/run/docker.sock` directly into the backend container** — Docker socket access must always go through `tecnativa/docker-socket-proxy` on an internal-only network with a minimal API whitelist. Raw socket access inside any app container is equivalent to root on the host. +- **Never spawn `--privileged` containers** or containers with added capabilities for app workloads diff --git a/TODO.md b/TODO.md index a834f02..7872cdf 100644 --- a/TODO.md +++ b/TODO.md @@ -25,6 +25,16 @@ - [x] **Profile page** (`/profile`) — shows personal information for the logged-in user - [x] **Edit & save profile** — form to update personal details, stored in a dedicated `profiles` table (separate from `users`, same PostgreSQL container) +## App container architecture (future) + +Design decision: each installable app (billing, PDF, email, etc.) runs in its own isolated Docker/Podman container, spawned and managed by the backend via the Docker API. Key rules to implement: + +- [ ] **Docker socket proxy** — backend must never mount `/var/run/docker.sock` directly; use `tecnativa/docker-socket-proxy` on an internal-only network, with only the required API endpoints whitelisted (CONTAINERS, IMAGES, NETWORKS, POST). Raw socket access = root on the host. +- [ ] **Network isolation per app** — each spawned app container gets its own Docker bridge network; app containers never talk to each other directly; only the backend can reach them +- [ ] **No privileged app containers** — all spawned containers run without `--privileged`, without extra capabilities, with resource limits (CPU, memory) +- [ ] **Image allowlist** — backend may only spawn containers from a pre-approved image list; never pull or build arbitrary images at runtime +- [ ] **Consider Podman** — evaluate rootless Podman as replacement for Docker daemon; daemonless model eliminates the socket entirely; Docker SDK compatible + ## Infrastructure - [ ] **Docker port hardening** — expose only port 80 externally; backend (8000) and db (5432) must not be reachable from outside the Docker network. Prepare for deployment behind Traefik or nginx proxy manager (SSL termination, reverse proxy, no direct container exposure).