Make TRUSTED_PROXIES' insecure-by-default risk loudly visible (#94)
TRUSTED_PROXIES ships empty in .env.example, which disables AuthMiddleware's reverse-proxy allowlist entirely — a fresh deployment that doesn't explicitly set it has zero verification that Remote-User/Remote-Groups headers actually came from the trusted Authelia proxy. Anything that can reach the app directly (a misconfigured firewall rule, an exposed container port, SSRF from another internal service) can set Remote-User: admin and fully impersonate any user with zero authentication. The enforcement logic itself was already correct; this was purely a dangerous, easy-to-miss default. Added a boxed, unmissable warning around TRUSTED_PROXIES in .env.example (previously just an inline comment easy to skim past), added the same warning to README's setup instructions (which didn't mention this variable at all), and added a Check 8 to api/health.php that reports a 'warning' status when TRUSTED_PROXIES is empty, so a deployment that forgets it doesn't go unnoticed after the fact. Verified against real MariaDB via a running server: the health endpoint correctly reports 'warning' when empty and 'ok' once set. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lhz7pGMaoTfL5sdYS5XiKv
This commit is contained in:
+11
-4
@@ -49,19 +49,26 @@ APP_DOMAIN=
|
||||
; Include all domains that can access this application
|
||||
ALLOWED_HOSTS=localhost,127.0.0.1
|
||||
|
||||
; ============================================================================
|
||||
; REQUIRED FOR PRODUCTION -- READ BEFORE DEPLOYING -- TRUSTED_PROXIES
|
||||
; ============================================================================
|
||||
; Trusted reverse proxy IPs, comma-separated -- e.g. the Authelia/nginx proxy.
|
||||
; Set this to the IP address(es) of your reverse proxy. Authelia forward-auth
|
||||
; headers (Remote-User / Remote-Groups) and forwarded client IPs are only
|
||||
; trusted when REMOTE_ADDR is in this list.
|
||||
;
|
||||
; Leaving this EMPTY disables reverse-proxy verification entirely: the app then
|
||||
; trusts Remote-User / Remote-Groups headers from ANY source. That is unsafe if
|
||||
; the PHP backend is reachable directly (bypassing the proxy), because a client
|
||||
; can then spoof those headers and log in as an admin. Only leave it empty when
|
||||
; network topology guarantees PHP is reachable solely via the trusted proxy.
|
||||
; trusts Remote-User / Remote-Groups headers from ANY source. If the PHP
|
||||
; backend is reachable directly -- a misconfigured firewall rule, a container
|
||||
; network accidentally exposing the port, SSRF from another internal service
|
||||
; -- ANYONE can set Remote-User: admin themselves and fully impersonate any
|
||||
; user, including an admin, with ZERO authentication. Only leave it empty when
|
||||
; network topology guarantees PHP is reachable solely via the trusted proxy
|
||||
; (e.g. local development), never in a real deployment.
|
||||
;
|
||||
; Exact IP match only (no CIDR). Example (single proxy): TRUSTED_PROXIES=10.10.10.27
|
||||
; Example (multiple): TRUSTED_PROXIES=10.10.10.27,10.10.10.28
|
||||
; ============================================================================
|
||||
TRUSTED_PROXIES=
|
||||
|
||||
; Timezone (default: America/New_York)
|
||||
|
||||
@@ -447,6 +447,21 @@ APP_DOMAIN=your.domain.example
|
||||
TIMEZONE=America/New_York
|
||||
```
|
||||
|
||||
**⚠️ REQUIRED FOR PRODUCTION — `TRUSTED_PROXIES`:** This app trusts Authelia
|
||||
forward-auth headers (`Remote-User`, `Remote-Groups`, etc.) to identify who's
|
||||
logged in. `TRUSTED_PROXIES` restricts that trust to requests that actually
|
||||
came through your reverse proxy — **leaving it empty disables that check
|
||||
entirely**, and anyone who can reach the PHP backend directly (a
|
||||
misconfigured firewall rule, an exposed container port, SSRF from another
|
||||
internal service) can set `Remote-User: admin` themselves and fully
|
||||
impersonate any user with zero authentication. Set it to your reverse proxy's
|
||||
IP address(es) before deploying anywhere reachable beyond your own machine:
|
||||
```env
|
||||
TRUSTED_PROXIES=10.10.10.27
|
||||
```
|
||||
`GET /api/health.php` reports a `warning` on the `trusted_proxies` check if
|
||||
this is left empty, so it doesn't go unnoticed after deployment.
|
||||
|
||||
Matrix notification variables (all optional):
|
||||
```env
|
||||
# hookshot generic webhook URL — send events to Matrix room
|
||||
|
||||
@@ -162,6 +162,21 @@ if ($maxExecTime === 0 || $maxExecTime >= $requirements['min_max_execution_time'
|
||||
];
|
||||
}
|
||||
|
||||
// Check 8: TRUSTED_PROXIES configured. Empty disables enforceTrustedProxy()'s
|
||||
// allowlist entirely, meaning anything that can reach this app directly can
|
||||
// spoof the Authelia forward-auth Remote-* headers and impersonate any user,
|
||||
// including an admin. Not fatal (a fresh/dev install may not sit behind a
|
||||
// proxy yet), but should never go unnoticed on a real deployment.
|
||||
if (!empty($GLOBALS['config']['TRUSTED_PROXIES'] ?? [])) {
|
||||
$checks['trusted_proxies'] = ['status' => 'ok', 'message' => 'configured'];
|
||||
} else {
|
||||
$checks['trusted_proxies'] = [
|
||||
'status' => 'warning',
|
||||
'message' => 'TRUSTED_PROXIES is empty — forward-auth headers are NOT verified; '
|
||||
. 'anything that can reach this app directly can impersonate any user'
|
||||
];
|
||||
}
|
||||
|
||||
// Calculate response time
|
||||
$responseTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user