|
@@ -11,7 +11,7 @@
|
|
|
>
|
|
>
|
|
|
> Each finding is referenced as **F<N>** for later citation.
|
|
> Each finding is referenced as **F<N>** for later citation.
|
|
|
>
|
|
>
|
|
|
-> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (10 fixed, 17 open), 42 sev-1.
|
|
|
|
|
|
|
+> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (11 fixed, 16 open), 42 sev-1.
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
@@ -587,6 +587,50 @@
|
|
|
CVE gets root inside the container. No `--chown` on `COPY`. The
|
|
CVE gets root inside the container. No `--chown` on `COPY`. The
|
|
|
`irdb-data:/data` volume is owned by root.
|
|
`irdb-data:/data` volume is owned by root.
|
|
|
- **Severity: 2**
|
|
- **Severity: 2**
|
|
|
|
|
+- **Status:** Fixed in `33179d8`. Both `api/Dockerfile` and
|
|
|
|
|
+ `ui/Dockerfile` now create an `app` system user (UID/GID 1000) and
|
|
|
|
|
+ switch to it via `USER app` after the last root-required step
|
|
|
|
|
+ (`apk`, `install-php-extensions`, `chmod +x`, `mkdir`/`chown`).
|
|
|
|
|
+ FrankenPHP/Caddy bind to unprivileged ports (8080/8081) — no
|
|
|
|
|
+ `setcap` is needed.
|
|
|
|
|
+
|
|
|
|
|
+ Layout choices:
|
|
|
|
|
+ - `/app` stays **root-owned and world-readable**. The runtime needs
|
|
|
|
|
+ only read access; leaving source root-owned is a partial mitigation
|
|
|
|
|
+ for F20 (an attacker landing RCE under `app` cannot overwrite
|
|
|
|
|
+ `/app/vendor/**` or `/app/public/index.php` to persist). Tightening
|
|
|
|
|
+ the read-only stance further (e.g. mounting `/app` read-only at
|
|
|
|
|
+ runtime) is tracked separately by F20.
|
|
|
|
|
+ - `/data` is **app-owned** so phinx migrations, the
|
|
|
|
|
+ `auth:bootstrap-service-token` console call, and runtime SQLite
|
|
|
|
|
+ writes succeed without root. Newly-created Docker named volumes
|
|
|
|
|
+ (`irdb-data`) inherit this ownership on first creation.
|
|
|
|
|
+ - `/home/app/.config` and `/home/app/.local/share` are pre-created
|
|
|
|
|
+ and app-owned; `XDG_CONFIG_HOME` / `XDG_DATA_HOME` point at them
|
|
|
|
|
+ so Caddy's autosaved-config and TLS-cache state has somewhere to
|
|
|
|
|
+ land without falling back to `/root`.
|
|
|
|
|
+ - PHP sessions in the UI continue to use the default `/tmp` save
|
|
|
|
|
+ path (world-writable), so no extra mount is required.
|
|
|
|
|
+
|
|
|
|
|
+ **Upgrade note for operators.** Existing volumes from pre-F18
|
|
|
|
|
+ deployments were created when `/data` was root-owned; after pulling
|
|
|
|
|
+ this image the new uid=1000 process cannot write to them and phinx
|
|
|
|
|
+ fails with `attempt to write a readonly database`. Recover with
|
|
|
|
|
+ either of:
|
|
|
|
|
+ ```
|
|
|
|
|
+ # one-shot chown (preserves data)
|
|
|
|
|
+ docker run --rm -u 0 -v irdb_irdb-data:/data alpine chown -R 1000:1000 /data
|
|
|
|
|
+ # or, if the SQLite data is disposable
|
|
|
|
|
+ docker compose down -v && docker compose up -d
|
|
|
|
|
+ ```
|
|
|
|
|
+ Documented in the upgrade section of the deployment notes.
|
|
|
|
|
+
|
|
|
|
|
+ Verification: rebuilt both images and confirmed
|
|
|
|
|
+ `docker compose exec api id` / `exec ui id` report `uid=1000(app)`,
|
|
|
|
|
+ the api healthz returns 200, all 429 integration tests still pass
|
|
|
|
|
+ under the new user, and the UI Caddy log shows
|
|
|
|
|
+ `serving initial configuration` with TLS storage at
|
|
|
|
|
+ `/home/app/.local/share/caddy`.
|
|
|
|
|
|
|
|
### F19 — No `.dockerignore` — host artifacts baked into images
|
|
### F19 — No `.dockerignore` — host artifacts baked into images
|
|
|
- **Files:** build context roots `api/`, `ui/`; `COPY . ./` lines
|
|
- **Files:** build context roots `api/`, `ui/`; `COPY . ./` lines
|