docs(06-04): add partial plan summary — Tasks 1 & 2 complete, Task 3 human checkpoint pending
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
f98cab30e9
commit
f9dfa3dd9b
@@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
plan: 06-04
|
||||||
|
phase: 06-performance-production-hardening
|
||||||
|
status: partial
|
||||||
|
completed_at: 2026-06-04
|
||||||
|
commits:
|
||||||
|
- 3788226
|
||||||
|
checkpoint_pending: task-3-human-verify
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plan 06-04: Container Hardening
|
||||||
|
|
||||||
|
## What was built (Tasks 1 & 2)
|
||||||
|
|
||||||
|
### backend/Dockerfile — multi-stage with appuser
|
||||||
|
|
||||||
|
Replaced the single-stage root-running Dockerfile with a two-stage build:
|
||||||
|
|
||||||
|
**Stage 1 `AS builder`:**
|
||||||
|
- `FROM python:3.12-slim AS builder`
|
||||||
|
- Installs `gcc` (build-only dep, discarded after this stage) with `--no-install-recommends`
|
||||||
|
- Runs `pip install --no-cache-dir --prefix=/install -r requirements.txt`
|
||||||
|
|
||||||
|
**Stage 2 `AS runtime`:**
|
||||||
|
- `FROM python:3.12-slim AS runtime`
|
||||||
|
- Installs runtime apt deps (tesseract-ocr, libgl1, libglib2.0-0) with `--no-install-recommends`
|
||||||
|
- `COPY --from=builder /install /usr/local` (copies compiled packages, discards builder)
|
||||||
|
- Creates `appgroup` gid=1000 and `appuser` uid=1000 (no login shell, no home dir)
|
||||||
|
- `COPY --chown=appuser:appgroup . .`
|
||||||
|
- `USER appuser`
|
||||||
|
- `CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]` (no `--reload`)
|
||||||
|
|
||||||
|
### docker-compose.yml — runtime hardening
|
||||||
|
|
||||||
|
Added to **backend** and **celery-worker** services (not celery-beat):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
read_only: true
|
||||||
|
tmpfs:
|
||||||
|
- "/tmp:mode=1777"
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
security_opt:
|
||||||
|
- "no-new-privileges:true"
|
||||||
|
```
|
||||||
|
|
||||||
|
Added comment before celery-beat:
|
||||||
|
```yaml
|
||||||
|
# celery-beat: NOT hardened — writes celerybeat-schedule to working directory (Phase 6 Pitfall 7).
|
||||||
|
```
|
||||||
|
|
||||||
|
Existing bind mounts (`./backend:/app`), env vars, healthchecks, and Loki logging labels preserved.
|
||||||
|
|
||||||
|
## Static verification (Tasks 1 & 2 acceptance criteria)
|
||||||
|
|
||||||
|
| Check | Result |
|
||||||
|
|-------|--------|
|
||||||
|
| `FROM python:3.12-slim AS builder` | 1 ✓ |
|
||||||
|
| `FROM python:3.12-slim AS runtime` | 1 ✓ |
|
||||||
|
| `USER appuser` | 1 ✓ |
|
||||||
|
| `useradd --uid 1000` | 1 ✓ |
|
||||||
|
| `COPY --from=builder` | 1 ✓ |
|
||||||
|
| `--no-install-recommends` occurrences | 2 ✓ |
|
||||||
|
| `tesseract-ocr` in runtime only | 1 ✓ |
|
||||||
|
| `--reload` in Dockerfile | 0 ✓ |
|
||||||
|
| `docker compose config --quiet` | exit 0 ✓ |
|
||||||
|
| `read_only: true` in backend block | 1 ✓ |
|
||||||
|
| `/tmp:mode=1777` in backend block | 1 ✓ |
|
||||||
|
| `no-new-privileges:true` in backend block | 1 ✓ |
|
||||||
|
| `read_only: true` in celery-worker block | 1 ✓ |
|
||||||
|
| hardening keys in celery-beat block | 0 ✓ |
|
||||||
|
| `./backend:/app` bind mount preserved | 1 ✓ |
|
||||||
|
|
||||||
|
## Task 3: Human smoke test (PENDING)
|
||||||
|
|
||||||
|
Docker daemon was not running at execution time. Task 3 requires:
|
||||||
|
1. `docker compose up -d --build` (rebuilds with new Dockerfile)
|
||||||
|
2. `docker compose exec -T backend id` → must show uid=1000(appuser)
|
||||||
|
3. `docker compose exec -T backend sh -c 'touch /readonly_probe 2>&1 || echo "rootfs is read-only"'`
|
||||||
|
4. `docker compose exec -T backend sh -c 'touch /tmp/ok && ls -la /tmp/ok'`
|
||||||
|
5. Same checks on celery-worker
|
||||||
|
6. Confirm celery-beat is writable (intentionally)
|
||||||
|
7. End-to-end upload test (proves tempfile.NamedTemporaryFile works under hardened runtime)
|
||||||
|
|
||||||
|
Human responds "approved" to advance to Wave 3 (06-06).
|
||||||
|
|
||||||
|
## Satisfaction of requirements
|
||||||
|
- D-07 (multi-stage non-root image): ✓ Tasks 1 & 2 complete
|
||||||
|
- D-08 (read-only rootfs + tmpfs): ✓ Tasks 1 & 2 complete
|
||||||
|
- D-09 (cap_drop ALL + no-new-privileges): ✓ Tasks 1 & 2 complete
|
||||||
|
- Runtime verification (Task 3): PENDING human checkpoint
|
||||||
Reference in New Issue
Block a user