security: Sprint 00 — critical security fixes (OPC WS auth, Transmission creds, SECRET_KEY, passkey rate limit)

- Add JWT token auth to OPC WebSocket (unauthenticated → 403, per-user tracking)
- Externalize Transmission RPC credentials to TRANSMISSION_USER/PASS env vars
- Remove hardcoded SECRET_KEY fallback from dev compose
- Rate-limit passkey register options endpoint at 5/minute
- Add PDD (docs/improvement-plan.md) and sprint specs (specs/)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Gan, Jimmy
2026-05-03 13:31:25 +08:00
parent 5c7d185953
commit ddaf3c6cee
16 changed files with 987 additions and 21 deletions
+96
View File
@@ -0,0 +1,96 @@
# Sprint 05 — Operations & Resilience
**Depends on:** S04 (CI must be reliable before automating ops)
**Duration:** ~12h
**Goal:** Add health checks, resource limits, log rotation, fix Immich uploads, back up dashboard data, add monitoring endpoint.
---
## S05.1 — Add HEALTHCHECK to claude-dev image
- **Files:** `claude-dev/Dockerfile`, `claude-dev/docker-compose.yml`
- **Estimate:** 1h
- **Done means:** Dockerfile has `HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD claude --version || exit 1`. `docker ps` shows healthy.
- **Verify by:**
1. Deploy claude-dev → `docker ps` shows "(healthy)"
2. Break `claude` binary → container shows "(unhealthy)" after 3 failures
- **Risk:** `claude --version` may require network access. Test on NAS first. Alternative: `pgrep -f claude` or simple `curl localhost:<port>`.
- [ ] S05.1 — Add HEALTHCHECK to claude-dev image
## S05.2 — Add resource limits to production containers
- **Files:** `claude-dev/docker-compose.yml`, `dashboard/docker-compose.yml`
- **Estimate:** 1.5h
- **Done means:** Production compose files have `mem_limit`, `cpus`, and `restart_policy`. Limits are generous: 2GB dashboard, 1GB claude-dev, 0.5GB sidecars.
- **Verify by:**
1. `docker stats` shows containers respecting limits
2. Normal operation unaffected
3. `docker compose up -d` applies limits without restarting
- **Risk:** Limits too tight → OOM kills. Start generous, adjust after a week of monitoring.
- [ ] S05.2 — Add resource limits to production containers
## S05.3 — Implement audit log rotation
- **Files:** `dashboard/backend/main.py:161,171-188`
- **Estimate:** 2h
- **Done means:** Audit logging uses `RotatingFileHandler` with max 10MB and 5 backups.
- **Verify by:**
1. Write 11MB of audit entries → file rotates, `audit.log.1` created
2. Original `audit.log` starts fresh
3. Old backups capped at 5 files
- **Risk:** Log format change may affect parsing in `system.py:82-116`. Test the log reader with rotated files.
- [ ] S05.3 — Implement audit log rotation
## S05.4 — Fix Immich ML model downloads
- **Files:** `immich/docker-compose.yml`, `immich/.env`
- **Estimate:** 3h (investigation + fix)
- **Done means:** Immich mobile upload works. ML models download successfully or ML is gracefully disabled.
- **Verify by:**
1. Upload photo from phone → succeeds (no timeout)
2. `ssh nas "docker logs immich_machine_learning"` → no "Failed to load detection model" errors
3. If ML disabled: photo upload works, smart search unavailable (acceptable)
- **Risk:** Most complex item. May require pre-downloading models, configuring mirror, increasing timeouts, or disabling ML temporarily. Environment-specific (China network to modelscope.cn).
- [ ] S05.4 — Fix Immich ML model downloads
## S05.5 — Add dashboard data to backup script
- **Files:** `backup.sh`
- **Estimate:** 1.5h
- **Done means:** `backup.sh` copies `opc.db`, `auth.json`, `rbac.json`, and audit log to backup tarball. Restore procedure documented.
- **Verify by:**
1. Run `backup.sh` → tarball contains dashboard data files
2. Extract tarball → files are valid (SQLite DB opens, JSON parses)
- **Risk:** `auth.json` contains passkey data — ensure backup stored securely.
- [ ] S05.5 — Add dashboard data to backup script
## S05.6 — Add minimal health check endpoint
- **Files:** `dashboard/backend/main.py` or `routers/health.py`
- **Estimate:** 1h
- **Done means:** `GET /api/health` returns `{"status":"ok","db":true,"docker":true,"disk":{"free_gb":123}}`. Non-200 triggers optional Telegram notification.
- **Verify by:**
1. All services healthy → `/api/health` returns 200
2. Docker socket unreachable → returns 503 with `docker: false`
3. Cron job calls `/api/health` every 5 minutes
- **Risk:** Health endpoint must be lightweight. Cache results for 30 seconds.
- [ ] S05.6 — Add minimal health check endpoint
---
## Exit criteria
- [ ] `docker ps` shows claude-dev as "(healthy)"
- [ ] Production containers have CPU/memory limits in compose
- [ ] Audit log rotates at 10MB with 5 backups
- [ ] Photo upload from phone succeeds
- [ ] `backup.sh` includes dashboard data files
- [ ] `GET /api/health` returns component statuses
- [ ] All backend tests pass
- [ ] All frontend tests pass