feat(auth): support reverse-proxy driven proxy auth

Closes HP-353.
This commit is contained in:
Aarnav Tale
2026-06-20 11:40:46 -04:00
parent c7822e3ec2
commit 96f2721272
24 changed files with 837 additions and 75 deletions
+5 -1
View File
@@ -50,7 +50,11 @@ export default defineConfig({
{
text: "Features",
items: [
{ text: "Single Sign-On (SSO)", link: "/features/sso" },
{
text: "Single Sign-On (SSO)",
link: "/features/sso",
items: [{ text: "Proxy Authentication", link: "/features/proxy-auth" }],
},
{ text: "Headplane Agent", link: "/features/agent" },
{ text: "Browser SSH", link: "/features/ssh" },
],
+79
View File
@@ -350,3 +350,82 @@ _Description:_ The port to listen on.
_Type:_ 16 bit unsigned integer; between 0 and 65535 (both inclusive)
_Default:_ `3000`
## settings.server.proxy_auth
_Description:_ Proxy authentication configuration.
_Type:_ submodule
_Default:_ `{ }`
## settings.server.proxy_auth.allowed_cidrs
_Description:_ Direct client CIDR ranges allowed to bypass Headplane's login flow.
These should be the addresses your trusted reverse proxy uses to connect
to Headplane. Requires headscale.api_key_path.
_Type:_ list of string
_Default:_ `[ "127.0.0.1/32" "::1/128" ]`
_Example:_ `[ "10.0.0.0/24" ]`
## settings.server.proxy_auth.email_header
_Description:_ Optional header containing the authenticated user's email address.
_Type:_ null or string
_Default:_ `null`
## settings.server.proxy_auth.enabled
_Description:_ Whether to trust reverse proxy authentication for allowed client CIDRs.
_Type:_ boolean
_Default:_ `false`
## settings.server.proxy_auth.ip_header
_Description:_ Optional header containing the original client IP, such as X-Forwarded-For or X-Real-IP.
_Type:_ null or string
_Default:_ `null`
## settings.server.proxy_auth.name_header
_Description:_ Optional header containing the authenticated user's display name.
_Type:_ null or string
_Default:_ `null`
## settings.server.proxy_auth.picture_header
_Description:_ Optional header containing the authenticated user's profile picture URL.
_Type:_ null or string
_Default:_ `null`
## settings.server.proxy_auth.user_header
_Description:_ Header containing the stable authenticated proxy user identity.
_Type:_ string
_Default:_ `"Remote-User"`
## settings.server.proxy_auth.trusted_proxy_cidrs
_Description:_ Direct proxy CIDR ranges trusted to supply ip_header.
Only used when ip_header is set.
_Type:_ list of string
_Default:_ `[ "127.0.0.1/32" "::1/128" ]`
_Example:_ `[ "127.0.0.1/32" ]`
+2 -2
View File
@@ -58,8 +58,8 @@ The following configuration options in Headplane are treated as secret paths:
- _Note:_ Either `cookie_secret` or `cookie_secret_path` must be provided for web session security.
- **Headscale Connection Settings (`headscale.*`):**
- `api_key_path` (Headscale API key for server-side operations like OIDC and agent sync)
- _Note:_ Either `api_key` or `api_key_path` must be provided when using OIDC or the agent.
- `api_key_path` (Headscale API key for server-side operations like OIDC, proxy authentication, and agent sync)
- _Note:_ Either `api_key` or `api_key_path` must be provided when using OIDC, proxy authentication, or the agent.
- `tls_cert_path` (custom TLS certificate for connecting to Headscale)
- _Note:_ This is treated as a regular path, not a secret path, so it will not have its content loaded.
+99
View File
@@ -0,0 +1,99 @@
---
title: Proxy Authentication
description: Delegate Headplane authentication to a trusted reverse proxy.
outline: [2, 3]
---
:::warning
Proxy authentication is **dangerously powerful**. If misconfigured, it can allow
anyone to impersonate users and gain access to Headplane.
It is recommended to use Headplane's built-in SSO integrations over proxy
authentication if possible. No guarantees are made about the security of proxy
authentication.
:::
# Proxy Authentication
Proxy authentication lets Headplane delegate user authentication to a trusted
reverse proxy. This is useful when Headplane is already protected by middleware
such as nginx basic auth, Authelia, Authentik, or another SSO-aware proxy and
you do not want users to log in to Headplane separately.
Proxy authentication is intentionally opt-in and requires `headscale.api_key`.
When enabled, Headplane trusts identity headers only on requests whose client IP
matches `server.proxy_auth.allowed_cidrs`; all Headscale API calls then use the
configured `headscale.api_key`.
## Basic Configuration
```yaml
headscale:
api_key: "<your-headscale-api-key>"
server:
proxy_auth:
enabled: true
user_header: "Remote-User"
email_header: "Remote-Email"
name_header: "Remote-Name"
allowed_cidrs:
- "127.0.0.1/32"
- "::1/128"
```
`user_header` is required for a request to authenticate and defaults to
`Remote-User`. The value becomes the stable proxy identity in Headplane as
`proxy:<value>`. `email_header`, `name_header`, and `picture_header` are
optional profile metadata headers.
The first proxy-authenticated user is created as the Headplane owner, matching
the normal SSO first-user behavior. Subsequent users are created as members and
can be reassigned from the Users page.
## Client IP Checks
If `allowed_cidrs` is omitted, Headplane trusts only localhost. By default, this
CIDR check uses the socket address connected to Headplane, not
`X-Forwarded-For`, `X-Real-IP`, or other forwarded headers. Configure
`allowed_cidrs` for the direct address range your proxy uses to connect to
Headplane.
## Forwarded Client IP Headers
If you need to check the original client IP from a proxy header, set `ip_header`
to `X-Forwarded-For`, `X-Real-IP`, or another header your proxy controls. When
`ip_header` is set, Headplane only reads that header if the direct socket peer
matches `trusted_proxy_cidrs` (default localhost). The first IP in the header is
then checked against `allowed_cidrs`:
```yaml
server:
proxy_auth:
enabled: true
ip_header: "X-Forwarded-For"
trusted_proxy_cidrs:
- "127.0.0.1/32"
allowed_cidrs:
- "10.0.0.0/8"
```
::: warning
Only enable proxy authentication when Headplane is not directly reachable by
untrusted clients. Anyone who can connect to Headplane from an allowed CIDR will
be able to spoof the configured identity headers. Only configure `ip_header`
for headers set or overwritten by your trusted reverse proxy.
:::
## Header Reference
| Field | Description |
| --------------------------------------- | ------------------------------------------------------------------------------------ |
| `server.proxy_auth.enabled` | Enables proxy authentication. |
| `server.proxy_auth.user_header` | Header containing the stable authenticated user identity. Defaults to `Remote-User`. |
| `server.proxy_auth.email_header` | Optional header containing the authenticated user's email address. |
| `server.proxy_auth.name_header` | Optional header containing the authenticated user's display name. |
| `server.proxy_auth.picture_header` | Optional header containing the authenticated user's profile picture URL. |
| `server.proxy_auth.allowed_cidrs` | Client CIDRs allowed to authenticate. Defaults to localhost. |
| `server.proxy_auth.ip_header` | Optional original-client-IP header such as `X-Forwarded-For` or `X-Real-IP`. |
| `server.proxy_auth.trusted_proxy_cidrs` | Direct proxy CIDRs trusted to supply `ip_header`. Defaults to localhost. |
+3
View File
@@ -17,6 +17,9 @@ Identity Provider (IdP) using the OpenID Connect (OIDC) protocol. When enabled,
users sign in through your IdP and Headplane automatically links them to their
Headscale identity, assigns a role, and manages their session.
If your reverse proxy already performs authentication and can pass trusted user
headers to Headplane, see [Proxy Authentication](./proxy-auth.md) instead.
## Getting Started
### Requirements