DEV Community

yutianle
yutianle

Posted on

Caddy forward_auth needs a backend the internet cannot bypass

Caddy forward_auth needs a backend the internet cannot bypass

Caddy's forward_auth directive can move login checks out of an application and into a separate authentication service. The proxy sends a pre-check request, examines the response, and can copy selected headers into the upstream request. That design is useful for shared login flows. It also creates two places where a missing control can turn a valid authentication decision into an authorization bypass.

The first risk is header trust. If the upstream application accepts an identity header such as Remote-User, every path that reaches the upstream must either remove incoming copies of that header or overwrite them after authentication. Caddy's copy_headers option is the intended mechanism for adding authenticated values. The authentication service should return a deliberate header only after validating the session. It should also reject or discard client-supplied values with the same name.

Test the complete request path, including the login page and every proxy hop. Send an unauthenticated request with a forged identity header to Caddy. Confirm that the authentication service denies it and that the upstream never receives the forged value. Then send a valid session and inspect what the upstream records. A reverse proxy that copies Remote-User: alice is safe only when it removes or replaces any attacker-provided Remote-User value.

Protect the upstream from direct access

An authentication check at the edge is ineffective when clients can reach the application server through another address. Bind the upstream to a private interface where the deployment permits it. Apply a firewall or network policy that accepts traffic only from the Caddy instances. Check for alternate listeners, container ports published by mistake, load-balancer targets, and management networks that can reach the same service.

This separation matters because the upstream may trust the header unconditionally. That trust is reasonable only when the network and proxy configuration enforce the boundary. A service that is reachable on a public node IP can skip Caddy and submit the header itself.

Review address trust separately

Caddy's trusted_proxies settings control which peers are allowed to supply forwarded client-address information. Configure this list with exact proxy ranges. If Caddy receives traffic from a CDN or another reverse proxy, those upstream addresses need to be explicit. Do not treat an arbitrary X-Forwarded-For value as a trustworthy client identity.

Keep the authentication service and upstream proxy configuration under the same change control. A routing change that exposes a new backend port can invalidate the assumptions behind forward_auth, even when the Caddyfile itself is unchanged.

Build a repeatable check

A useful verification covers four cases:

  1. No session and no identity header: deny.

  2. No session and a forged identity header: deny, with no forged header reaching the upstream.

  3. Valid session: allow, with an identity header generated by the authentication service.

  4. Direct request to the upstream: fail at the network boundary.

Log the authentication decision, the authenticated subject, the upstream selected, and whether any incoming identity header was removed. Avoid logging session cookies or full credentials. Review the logs after a configuration change and compare them with the expected proxy chain.

Caddy provides the routing primitive. The security property depends on a complete path in which only the trusted proxy can reach the application and only the authentication result can set the identity passed to it.

References

Top comments (0)