Jelajahi Sumber

docs: mark SEC_REVIEW F20 as fixed in 1ec9d04

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chiappa 5 hari lalu
induk
melakukan
240ca37e1a
1 mengubah file dengan 43 tambahan dan 0 penghapusan
  1. 43 0
      doc/SEC_REVIEW.md

+ 43 - 0
doc/SEC_REVIEW.md

@@ -710,6 +710,49 @@
   directive plus stricter perms (read-only `/app`, writable only
   `/data`) blocks this persistence path.
 - **Severity: 2**
+- **Status:** Fixed in `1ec9d04`. F18 already made `/app`
+  root-owned so the unprivileged `app` user cannot write to it
+  through standard ownership/perm checks. F20 layers on a
+  kernel-level read-only rootfs at runtime so the same protection
+  holds even against a primitive that bypasses uid checks (e.g.
+  capability escalation, or a future regression that flips
+  ownership). All three services in `docker-compose.yml` now carry:
+
+  ```
+  read_only: true
+  tmpfs:
+    - /tmp:uid=1000,gid=1000,mode=1777
+    - /home/app/.config:uid=1000,gid=1000,mode=0700
+    - /home/app/.local/share:uid=1000,gid=1000,mode=0700
+  ```
+
+  Writable paths are restricted to:
+  - `/data` (api + migrate) — the existing `irdb-data` named
+    volume; holds the SQLite database, phinxlog table, geoip mmdb
+    files, the bootstrapped service token row, and any other future
+    persistent state.
+  - `/tmp` — PHP scratch and session files. World-writable
+    (`mode=1777`) to match Linux convention; PHP sessions in the ui
+    rely on this default save_path.
+  - `/home/app/.config` and `/home/app/.local/share` — XDG dirs
+    where Caddy/FrankenPHP write their `autosave.json` and
+    (unused-here) TLS storage. Owned by uid=1000 with `mode=0700`
+    so only the runtime user can read them.
+
+  Verification: brought the stack up clean (`docker compose up -d`)
+  with a synthetic `.env`. Phinx ran all 22 migrations; both api
+  and ui reached `healthy`; both `/healthz` endpoints returned 200.
+  Inside each container, `touch` against `/app`, `/app/src`,
+  `/app/vendor`, `/app/public/index.php` returned
+  `Read-only file system`, while `/tmp`, `/home/app/.config`,
+  `/home/app/.local/share`, and `/data` (api only) accepted writes
+  as uid=1000.
+
+  Operator note: when adding a new writable path to the runtime
+  (e.g. a cache dir, a queue spool, an upload staging area),
+  declare it as either a named volume or a tmpfs in compose —
+  writes anywhere else now fail with EROFS. F22 (scheduler) is
+  scoped separately; this commit does not change `compose.scheduler.yml`.
 
 ### F21 — `getTraceAsString` logged in production may leak plaintext credentials
 - **Files:** `api/src/Infrastructure/Http/JsonErrorHandler.php:48`,