|
@@ -280,6 +280,13 @@ DB_PATH=/var/www/data/app.sqlite
|
|
|
SESSION_PATH=/var/www/data/sessions
|
|
SESSION_PATH=/var/www/data/sessions
|
|
|
APP_ENV=production
|
|
APP_ENV=production
|
|
|
|
|
|
|
|
|
|
+# Reverse-proxy trust (R01-N05 / R01-N07). Comma-separated CIDRs of the
|
|
|
|
|
+# proxies in front of the app. When `REMOTE_ADDR` matches one of these the
|
|
|
|
|
+# app walks `X-Forwarded-For` for the originating client IP (audit log,
|
|
|
|
|
+# login throttle bucket) and honours `X-Forwarded-Proto: https` for
|
|
|
|
|
+# Secure-cookie / HSTS / HTTP→HTTPS-redirect decisions. Blank ⇒ no trust.
|
|
|
|
|
+TRUSTED_PROXIES=
|
|
|
|
|
+
|
|
|
# Optional explicit OIDC bootstrap (R01-N03). When the `users` table has no
|
|
# Optional explicit OIDC bootstrap (R01-N03). When the `users` table has no
|
|
|
# admin and the signing user matches one of these (case-insensitive,
|
|
# admin and the signing user matches one of these (case-insensitive,
|
|
|
# timing-safe), they are promoted on sign-in. Either or both may be set;
|
|
# timing-safe), they are promoted on sign-in. Either or both may be set;
|
|
@@ -1232,6 +1239,44 @@ is gone — see `src/Auth/BootstrapAdmin.php`.
|
|
|
§3.6. Tests: 184 / 502 (was 176 / 484). Seventh fix from
|
|
§3.6. Tests: 184 / 502 (was 176 / 484). Seventh fix from
|
|
|
`doc/REVIEW_01.md`.
|
|
`doc/REVIEW_01.md`.
|
|
|
|
|
|
|
|
|
|
+- [x] **R01-N05 + R01-N07 — Trusted-proxy aware HTTPS detection &
|
|
|
|
|
+ client IP** (`a2e77ea`). Behind a reverse proxy the app used
|
|
|
|
|
+ to record the proxy's address as the audit IP and to silently
|
|
|
|
|
+ drop the session cookie's `Secure` flag whenever `APP_BASE_URL`
|
|
|
|
|
+ was `http://` (and never to redirect mistyped HTTP traffic to
|
|
|
|
|
+ HTTPS). New `src/Http/TrustedProxies.php` owns the policy: a
|
|
|
|
|
+ comma-separated `TRUSTED_PROXIES=` env var lists the CIDRs of
|
|
|
|
|
+ hops that may speak `X-Forwarded-For` / `X-Forwarded-Proto` on
|
|
|
|
|
+ behalf of the user, and `clientIp()` walks the XFF chain
|
|
|
|
|
+ rightmost-to-leftmost to return the first hop that is not in any
|
|
|
|
|
+ configured CIDR. Bare addresses without `/n` are normalised to
|
|
|
|
|
+ host masks. With the env var blank — the default — both
|
|
|
|
|
+ forwarded headers are ignored, so a hostile direct client can't
|
|
|
|
|
+ lie its way into a different audit IP or HTTPS posture.
|
|
|
|
|
+ `Request::ip()` now goes through the helper (so the audit
|
|
|
|
|
+ pipeline and the R01-N06 throttle bucket key both fix
|
|
|
|
|
+ themselves), and a new `Request::isHttps()` exposes the same
|
|
|
|
|
+ decision to the rest of the app. `SessionGuard::start()` marks
|
|
|
|
|
+ the session cookie `Secure` when *either* `APP_BASE_URL` is
|
|
|
|
|
+ HTTPS or the live request is effectively HTTPS, so a TLS-
|
|
|
|
|
+ terminated proxy hop no longer downgrades the cookie.
|
|
|
|
|
+ `public/index.php` adds a one-shot HTTP→HTTPS redirect (308)
|
|
|
|
|
+ when `APP_BASE_URL` is HTTPS and the request is provably HTTP —
|
|
|
|
|
+ either there is no `TRUSTED_PROXIES` configured at all, or a
|
|
|
|
|
+ trusted proxy explicitly reported `X-Forwarded-Proto: http`. We
|
|
|
|
|
+ deliberately do NOT redirect when the proxy stays silent, to
|
|
|
|
|
+ avoid an infinite-loop with TLS-terminating proxies that forgot
|
|
|
|
|
+ to forward the scheme; `/healthz` is exempt outright. Operator
|
|
|
|
|
+ docs: new `.env.example` block, new admin-manual §3.5 "Reverse
|
|
|
|
|
+ proxy and HTTPS" (old §3.5 → §3.6, old §3.6 → §3.7) with an
|
|
|
|
|
+ Nginx snippet showing the required `proxy_set_header` lines.
|
|
|
|
|
+ New `tests/Http/TrustedProxiesTest.php` (14 cases) covers
|
|
|
|
|
+ direct-client / single-hop / multi-hop / IPv6 / typo / port-
|
|
|
|
|
+ stripping / `X-Forwarded-Proto` trust gating; new
|
|
|
|
|
+ `tests/Http/RequestTest.php` (4 cases) wires the env into
|
|
|
|
|
+ `Request::ip()` / `isHttps()`. Tests: 202 / 533 (was 184 / 502).
|
|
|
|
|
+ Eighth fix from `doc/REVIEW_01.md`.
|
|
|
|
|
+
|
|
|
- [x] **New sprint form: drop weeks input + task list row hover**
|
|
- [x] **New sprint form: drop weeks input + task list row hover**
|
|
|
(`3728106`). The `/sprints/new` form no longer collects an
|
|
(`3728106`). The `/sprints/new` form no longer collects an
|
|
|
`n_weeks` value — the week count is derived from `start_date` /
|
|
`n_weeks` value — the week count is derived from `start_date` /
|
|
@@ -1301,7 +1346,7 @@ for f in $(git ls-files '*.php'); do php -l "$f" | tail -1 | sed "s|^|$f: |"; do
|
|
|
Run the test suite:
|
|
Run the test suite:
|
|
|
```bash
|
|
```bash
|
|
|
vendor/bin/phpunit
|
|
vendor/bin/phpunit
|
|
|
-# → OK (184 tests, 502 assertions)
|
|
|
|
|
|
|
+# → OK (202 tests, 533 assertions)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
The Phase 20 parser tests need `ext-dom`, `ext-zip`, `ext-xmlreader`,
|
|
The Phase 20 parser tests need `ext-dom`, `ext-zip`, `ext-xmlreader`,
|
|
@@ -1348,6 +1393,7 @@ before acting — nothing here is load-bearing once it grows stale.
|
|
|
## 13. Git history (as of this writing)
|
|
## 13. Git history (as of this writing)
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
|
|
+a2e77ea Fix R01-N05 + R01-N07: trusted-proxy aware HTTPS + client IP
|
|
|
f565c86 Fix R01-N03: explicit env-bootstrap for the first OIDC admin
|
|
f565c86 Fix R01-N03: explicit env-bootstrap for the first OIDC admin
|
|
|
2b8f167 Docs: mark R01-N06 fixed, refresh SPEC §3 / §4 / §7 / §9 / §11 / §13
|
|
2b8f167 Docs: mark R01-N06 fixed, refresh SPEC §3 / §4 / §7 / §9 / §11 / §13
|
|
|
e295432 Fix R01-N06: throttle local-admin login by (ip, email)
|
|
e295432 Fix R01-N06: throttle local-admin login by (ip, email)
|