Gate a host with SSO¶
Put an identity provider in front of one proxied host, so the proxy listeners refuse a request that carries no valid identity. This is separate from admin SSO, which governs the gpm panel itself.
Prerequisites¶
- An IdentityProvider object, or the details to create one: an OIDC issuer URL and client credentials, the CIDRs of a trusted forward-auth proxy, or an outpost URL.
- The host already serves HTTPS with
tls.forceSSL: true. - For
oidcmode, the ability to registerhttps://<host>/__gpm/oidc/callbackas a redirect URI at the identity provider.
Choosing a mode¶
| Mode | Identity comes from | Use it when |
|---|---|---|
oidc |
A browser redirect to the IdP; gpm is the relying party | gpm is the only thing in front of the app and you want a login page |
forward-auth |
Identity headers asserted by a trusted proxy | Something ahead of gpm already authenticated the user |
auth-request |
A subrequest to an identity outpost | You run an Authentik-style outpost that owns the sign-in flow |
client-cert |
The TLS handshake | Devices carry certificates: see mTLS |
basic |
The middleware's own bcrypt hashes | A shared password is enough: see Basic auth |
Full field rules for every mode are in Middleware.
Steps¶
- Create the identity provider:
yaml
# config/identity-providers/authentik.yaml
name: authentik
type: oidc
oidc:
issuerURL: https://auth.example.com/application/o/gpm/
clientID: gpm
clientSecret: ${FILE:/run/secrets/oidc_secret}
roleMapping:
adminGroups: [proxy-admins]
userGroups: [staff]
- Register the callback URI at the IdP:
https://<host>/__gpm/oidc/callback, once per gated host. - Gate the host. For a single host, write the block inline:
yaml
# config/proxy-hosts/grafana.yaml
auth:
identityProvider: authentik
mode: oidc
requiredRoles: [staff]
For a policy shared by several hosts, create a middleware instead and
reference it from each host's middlewares:
yaml
# config/middlewares/require-sso.yaml
name: require-sso
type: auth
auth:
identityProvider: authentik
mode: oidc
requiredRoles: [staff]
- Exempt a trusted network, if wanted.
allowFromis accepted inauth-request,client-certandbasicmodes only; it is refused inoidcandforward-authmode, where the gate has no bypass to honour. - Scope it to a path, if only part of the host needs the gate, by putting
the same block on a
locationsentry instead of the host.
Verify¶
| Check | Expected |
|---|---|
A fresh browser at https://<host>/ |
Redirected to the IdP, then back, then the app |
The same request with curl and no cookie |
302 to the IdP, not the app's response |
A user outside requiredRoles |
403, rendered through the host's error page |
GET /api/history |
One commit per object you created |
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
503 and "authentication not available" |
The middleware could not be compiled, usually a dangling identityProvider |
Fix the reference; the proxy listeners fail closed rather than serving the host open |
| Redirect loop at the IdP | The redirect URI registered at the IdP does not match https://<host>/__gpm/oidc/callback exactly |
Register the exact URI for that host |
Everyone is denied with 403 |
The groups claim is not in the ID token | gpm reads claims from the ID token; configure the IdP to include the groups claim there |
A write is refused: allowFrom not permitted |
allowFrom in oidc or forward-auth mode, including via an unset mode inheriting the provider type |
Set mode explicitly, or move the network rule into an access list |
| Sessions end after an hour | The per-host SSO cookie has a 1-hour absolute TTL, not a sliding window | Expected. Re-auth is silent while the IdP session is valid |