# Forward auth

Sign in through a reverse proxy that already signs people in.

## Trust the proxy

1. Restrict kogut's listening port so only the proxy can reach it. Make the proxy remove
   client-supplied identity headers and set its own.
2. List the proxy's TCP source address or network and choose a preset:

   ```toml title="kogut.toml"
   [auth.proxy]
   trusted_proxies = ["172.18.0.0/16"]
   preset = "authelia"
   admin_groups = ["kogut-admins"]
   ```

   `X-Forwarded-For` does not establish trust. Loopback is trusted only when listed. Do
   not trust a network where other clients can reach kogut directly.
3. For a proxy without a preset, replace `preset` with header names:

   ```toml title="kogut.toml"
   [auth.proxy]
   trusted_proxies = ["10.0.0.2"]
   headers = { email = "X-Auth-Email", groups = "X-Auth-Groups" }
   ```

   Custom groups are optional and comma-separated. Use either `headers` or `preset`.

## Presets

| Preset | Email header | Groups header |
|---|---|---|
| `authelia` | `Remote-Email` | `Remote-Groups`, comma-separated |
| `authentik` | `X-authentik-email` | `X-authentik-groups`, separated by `\|` |
| `oauth2_proxy` | `X-Forwarded-Email` | `X-Forwarded-Groups`, comma-separated |
| `pomerium` | `X-Pomerium-Claim-Email` | `X-Pomerium-Claim-Groups`, comma-separated |
| `cloudflare_access` | `Cf-Access-Authenticated-User-Email` | None |
| `tailscale` | `Tailscale-User-Login` | None |

All keys and defaults are in the [configuration reference](https://docs.kogut.dev/reference/configuration/#authproxy).

## Set up accounts

Before any account exists, sign in as an `admin_groups` member to become the first admin.
Without groups, create the first admin with the
[setup token](https://docs.kogut.dev/sign-in/passwords/#the-first-account), using your proxy email.

Without groups, set roles by adding people in **Settings → People** with an empty
password; existing accounts keep their role. With groups, roles update on every request.
See [Roles](https://docs.kogut.dev/sign-in/roles/). Cloudflare Access and Tailscale send no groups and cannot use
`admin_groups`.

A valid proxy identity takes precedence over a session cookie. Send exactly one email
header. Changes still need an `Origin` matching the panel. Sign out at the proxy: signing
out of kogut alone signs you in again on the next request.

## Paths outside the gate

Exempt `/b.js`, `/script.js`, `/e`, `/ping/*`, `/api/push/*`, `/api/deploys`, `/api/changes`,
`/hooks/*`, `/agent/*`, `/mcp`, `/healthz`, and `/status` from proxy sign-in. Allow requests
with `Authorization: Bearer` through. With an `Authorization` header, kogut ignores the
proxy identity and requires a valid bearer token. If you use
[OpenID Connect](https://docs.kogut.dev/sign-in/openid-connect/), exempt `/auth/oidc/*` too.

## Configure Caddy with Authelia

With the `authelia` preset and your trusted proxy address:

```caddy title="Caddyfile"
kogut.acme.com {
	@open path /b.js /script.js /e /ping/* /api/push/* /api/deploys /api/changes /hooks/* /agent/* /mcp /healthz /status
	@token header Authorization "Bearer *"
	handle @open {
		reverse_proxy kogut:8484
	}
	handle @token {
		reverse_proxy kogut:8484
	}
	handle {
		forward_auth authelia:9091 {
			uri /api/authz/forward-auth
			copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
		}
		reverse_proxy kogut:8484
	}
}
```

If you also use OpenID Connect, add `/auth/oidc/*` to `@open`. Keep the status domain as a
plain `reverse_proxy` block. See [Caddy](https://docs.kogut.dev/installation/caddy/) for the two-domain setup.

## Cloudflare Access

Trust the address `cloudflared` connects from, such as its container network, not
Cloudflare's public ranges: a public origin can receive requests through Cloudflare
without Access.

## Tailscale

With the [Tailscale setup](https://docs.kogut.dev/installation/tailscale/), kogut and Tailscale share a network
namespace. Set `preset = "tailscale"` and trust `127.0.0.1` and `::1` only if other local
processes cannot reach kogut's port. Tagged devices and Funnel requests carry no login.
