Skip to content

Forward auth

atreoAGENT runs a forward-auth endpoint on port 9091 that external reverse proxies can use to authenticate requests. This lets you use your preferred proxy (Caddy, Traefik, nginx) while still getting per-member ACL enforcement.

  1. A client makes a request to your reverse proxy.
  2. Your proxy sends a subrequest to http://<agent-ip>:9091/auth.
  3. The agent checks the source IP and requested hostname against the ACL.
  4. On success (200), the proxy forwards the original request to the app.
  5. On failure (403), the proxy returns an access denied error.
GET http://<agent-ip>:9091/auth

The agent reads these headers from the proxy’s subrequest:

HeaderPurpose
X-Forwarded-For or X-Real-IPClient’s tunnel IP — honoured only when the caller is in trusted_proxies (see below). Falls back to the TCP source address otherwise.
X-Forwarded-Host or HostThe requested hostname (e.g. jellyfin.alice.atreo.link). X-Forwarded-Host is honoured only when the caller is in trusted_proxies.

The forward-auth endpoint listens on the WireGuard tunnel IP, so every tunnel peer can reach it. If the agent blindly trusted X-Forwarded-For and X-Forwarded-Host, any peer could claim another member’s IP and inherit their permissions. By default these headers are ignored.

To trust them, list the source IP of your external proxy in proxy.trusted_proxies:

proxy:
enabled: false
trusted_proxies:
- "100.64.0.1/32" # external proxy bound to the WG tunnel IP
- "192.168.1.10/32" # external proxy on a LAN host

Or via environment variable (comma-separated CIDRs):

Terminal window
PROXY_TRUSTED_PROXIES=100.64.0.1/32,192.168.1.10/32

Without trusted_proxies, every forward-auth request is evaluated against the calling proxy’s own TCP source address — which means the ACL check sees the proxy’s IP, not the client’s, and returns 403 for everything that isn’t itself a member.

When access is granted, the agent returns these headers for your proxy to pass downstream:

HeaderExampleDescription
X-Auth-Userjohn or localMember name (or “local” for trusted networks)
X-Auth-Member-IDuuidMember’s unique identifier
X-Auth-Roleadmin or memberMember’s role

Returned when the client’s IP is not authorised to access the requested app.

If the source IP matches a trusted network, the request is always allowed and the response headers will show:

X-Auth-User: local
X-Auth-Role: admin

See the specific integration guides for your proxy:

WireGuard is a registered trademark of Jason A. Donenfeld.