|
|
@@ -11,7 +11,7 @@
|
|
|
>
|
|
|
> Each finding is referenced as **F<N>** for later citation.
|
|
|
>
|
|
|
-> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (15 fixed, 12 open), 42 sev-1.
|
|
|
+> **Findings rolled up:** 5 sev-3 (5 fixed, 0 open), 27 sev-2 (16 fixed, 11 open), 42 sev-1.
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -886,6 +886,42 @@
|
|
|
`INTERNAL_JOB_TOKEN` is still required, but the network-layer
|
|
|
defense is bypassable on tenant-shared docker hosts.
|
|
|
- **Severity: 2**
|
|
|
+- **Status:** Fixed. Three layers tighten the gate to loopback-only by
|
|
|
+ default; everything else has to opt in explicitly:
|
|
|
+ 1. **Caddy `trusted_proxies` narrowed.** `api/docker/Caddyfile`
|
|
|
+ replaces `trusted_proxies static private_ranges` with
|
|
|
+ `trusted_proxies static {$TRUSTED_PROXIES:127.0.0.1/32 ::1/128}`.
|
|
|
+ With no env override, only loopback is treated as a "real proxy"
|
|
|
+ for XFF rewriting — so a non-loopback peer can no longer forge
|
|
|
+ `REMOTE_ADDR=127.0.0.1` via `X-Forwarded-For`. Operators behind a
|
|
|
+ genuine reverse proxy set `TRUSTED_PROXIES` to that proxy's CIDR.
|
|
|
+ 2. **Caddy `@internal` matcher narrowed.** The `remote_ip` allowlist
|
|
|
+ for `/internal/*` is now `127.0.0.1/32 ::1/128` only — the wide
|
|
|
+ RFC1918 entries (`172.16.0.0/12`, `10.0.0.0/8`, `192.168.0.0/16`)
|
|
|
+ are gone. Mirrored on the opposite `not remote_ip` deny rule.
|
|
|
+ 3. **PHP `InternalNetworkMiddleware` constructor-driven.** The
|
|
|
+ hardcoded RFC1918 list is gone; the constructor now takes an
|
|
|
+ optional CIDR list and falls back to
|
|
|
+ `DEFAULT_ALLOWED_CIDRS = ['127.0.0.1/32', '::1/128']`. The DI
|
|
|
+ container reads `INTERNAL_CIDR_ALLOWLIST` from env (parsed by a
|
|
|
+ new `parseCidrList()` helper that fails-closed on invalid input)
|
|
|
+ and passes the result to the middleware. Operators with a host-
|
|
|
+ cron VM on a private bridge add their CIDR via env and Caddyfile.
|
|
|
+ 4. **Sidecar scheduler joins the api's network namespace.**
|
|
|
+ `compose.scheduler.yml` switches to `network_mode: "service:api"`
|
|
|
+ and `scheduler.crontab` posts to `http://localhost:8081/...`
|
|
|
+ instead of `http://api:8081/...`. The scheduler's call now
|
|
|
+ arrives on `127.0.0.1` inside the shared netns, satisfying the
|
|
|
+ loopback-only gate without weakening it for actual neighbours.
|
|
|
+ SPEC §7 ("Scheduling") and §6 ("Internal endpoints") updated to
|
|
|
+ match.
|
|
|
+ Regression tests in `api/tests/Unit/Http/InternalNetworkMiddlewareTest.php`:
|
|
|
+ the data provider now expects RFC1918 sources to be **rejected** under
|
|
|
+ the default; new cases cover (a) `null` / `[]` falling back to the
|
|
|
+ loopback default, (b) a custom `172.20.0.5/32` allowlist admitting only
|
|
|
+ that exact source, (c) invalid CIDRs failing-closed at construction,
|
|
|
+ and (d) the `parseCidrList()` env-parser accepting comma- and
|
|
|
+ whitespace-separated input while throwing on garbage.
|
|
|
|
|
|
### F26 — JsonErrorHandler can leak raw exception messages in production
|
|
|
- **File:** `api/src/Infrastructure/Http/JsonErrorHandler.php:53-82`
|