Add trusted-proxy auth hardening + PHP requirements checks
Lint / PHP (phpcs PSR-12) (push) Successful in 20s
Lint / JS (eslint) (push) Successful in 11s
Lint / PHP requirements (version + extensions) (push) Successful in 52s
Security / PHP Security (semgrep) (push) Successful in 2m6s
Lint / Deploy (push) Successful in 13s
Lint / Notify on failure (push) Has been skipped

Trusted-proxy hardening (defense-in-depth for Authelia forward-auth):
- AuthMiddleware now only honors Remote-* identity headers when REMOTE_ADDR
  is in a configured TRUSTED_PROXIES allowlist; otherwise it refuses with 403
  and logs an 'untrusted_proxy' security event. Previously anything that could
  reach PHP directly could spoof Remote-User/Remote-Groups and log in as admin.
- New config TRUSTED_PROXIES (comma-separated, from .env). Empty = enforcement
  off, so this is backward compatible until the allowlist is set on a host.

Requirements checks (so a PHP upgrade dropping an extension can't silently
break features like avatars again):
- config/requirements.php: single source of truth for min PHP version and
  required extensions (ldap, mysqli, curl, mbstring, fileinfo, json).
- scripts/check_requirements.php: CI script that fails the build if the
  environment doesn't satisfy them.
- New 'requirements' CI job installs those extensions and runs the check;
  deploy now depends on it.
- api/health.php: adds php_extensions + php_version checks so production
  monitoring surfaces the drift (returns 503 if a required extension is gone).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 10:22:53 -04:00
co-authored by Claude Opus 4.8
parent b3bc3ab159
commit b2c19745eb
6 changed files with 168 additions and 2 deletions
+33
View File
@@ -96,6 +96,12 @@ class AuthMiddleware
}
}
// Only honor Authelia forward-auth headers from a trusted reverse proxy.
// Without this, anything that can reach PHP directly could spoof
// Remote-User / Remote-Groups and log in (as admin). No valid session
// exists at this point, so we are about to trust request headers.
$this->enforceTrustedProxy();
// Read Authelia forward auth headers
$username = $this->getHeader('HTTP_REMOTE_USER');
$displayName = $this->getHeader('HTTP_REMOTE_NAME');
@@ -136,6 +142,33 @@ class AuthMiddleware
return $user;
}
/**
* Reject forward-auth headers that did not arrive via a trusted proxy.
*
* If TRUSTED_PROXIES is configured and the connecting REMOTE_ADDR is not in
* the allowlist, the Remote-* headers cannot be trusted, so we refuse rather
* than honor a potentially spoofed identity. Empty allowlist = disabled.
*/
private function enforceTrustedProxy(): void
{
$trusted = $GLOBALS['config']['TRUSTED_PROXIES'] ?? [];
if (empty($trusted)) {
return; // Enforcement disabled (no allowlist configured)
}
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '';
if (!in_array($remoteAddr, $trusted, true)) {
$this->logSecurityEvent('untrusted_proxy', [
'reason' => 'Remote-* auth headers from non-allowlisted source',
'remote_addr' => $remoteAddr ?: 'unknown'
]);
header('HTTP/1.1 403 Forbidden');
header('Content-Type: text/plain; charset=utf-8');
echo 'Forbidden: authentication headers must arrive via a trusted proxy.';
exit;
}
}
/**
* Get header value from server variables
*