|
|
@@ -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`,
|