Authentik SSO¶
This document covers the Authentik deployment in the homelab k3s cluster - what it provides, how it's set up, and how to protect services with ForwardAuth.
Overview¶
Authentik is the identity provider (IdP) for the homelab cluster. It provides:
- SSO - single sign-on across all protected services
- OIDC / OAuth2 - for apps that support standards-based authentication
- LDAP - for apps that only speak LDAP (via built-in LDAP outpost)
- ForwardAuth - proxy-level authentication via Traefik, so services without any auth support can be gated behind a login page
Architecture¶
| Component | Detail |
|---|---|
| PostgreSQL | CNPG cluster authentik-db in the authentik namespace |
| Redis | Authentik's built-in Redis (bundled in the Helm chart) |
| TLS | cert-manager with ClusterIssuer letsencrypt-production |
| Ingress | Tailscale Funnel at authentik.daggertooth-scala.ts.net |
| Credentials secret | authentik-credentials (must be patched after deploy - see below) |
| ForwardAuth middleware | authentik-forward-auth in namespace authentik |
Flux manages the deployment via a HelmRelease in k3s/flux/apps/authentik.yaml. Once the HelmRelease reconciles, a few manual post-deploy steps are required before Authentik is usable.
Flows as Code (OpenTofu)¶
All Authentik flows, stages, providers, applications and outpost membership in this homelab
are managed declaratively using the goauthentik/authentik OpenTofu provider. The Terraform
state lives alongside the rest of the homelab IaC in S3 (opentofu/ directory).
Adding a new ForwardAuth-protected app¶
- Add an
authentik_provider_proxy+authentik_application, append the provider's id toauthentik_outpost.embedded.protocol_providers - Add the middleware chain
kube-system-cloudflare-https-scheme@kubernetescrd,authentik-authentik-forward-auth@kubernetescrdto the KubernetesIngress.
Protecting a Service with ForwardAuth¶
The authentik-forward-auth Traefik Middleware is already deployed in the authentik namespace. Reference it in any Ingress or IngressRoute to require authentication.
Standard Kubernetes Ingress¶
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myservice
namespace: myservice
annotations:
traefik.ingress.kubernetes.io/router.middlewares: >-
kube-system-cloudflare-https-scheme@kubernetescrd,authentik-authentik-forward-auth@kubernetescrd
cert-manager.io/cluster-issuer: letsencrypt-production
spec:
rules:
- host: myservice.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myservice
port:
number: 80
tls:
- hosts:
- myservice.example.com
secretName: myservice-tls
Middleware reference format
Traefik middleware references follow <namespace>-<name>@kubernetescrd. Because the middleware lives in the authentik namespace and is named authentik-forward-auth, the full reference is:
```
authentik-authentik-forward-auth@kubernetescrd
```
Cloudflare Tunnel requires an additional middleware
When traffic arrives via Cloudflare Tunnel, chain kube-system-cloudflare-https-scheme@kubernetescrd before the ForwardAuth middleware (as shown above). This rewrites X-Forwarded-Proto to https, which Authentik requires to build a valid OIDC callback URL. Without it, auth will fail with a 400 error on the callback. See cloudflare-tunnels.md for details.
Traefik IngressRoute CRD¶
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: myservice
namespace: myservice
spec:
entryPoints:
- websecure
routes:
- match: Host(`myservice.example.com`)
kind: Rule
middlewares:
- name: authentik-forward-auth
namespace: authentik
services:
- name: myservice
port: 80
tls:
certResolver: letsencrypt-production
With IngressRoute, the middleware namespace is specified explicitly so there is no namespace-prefix ambiguity.
ForwardAuth with Services That Have Built-in Auth¶
Some services (e.g. Uptime Kuma, Grafana, Gitea) ship with their own login page. When you add Authentik ForwardAuth in front of them, users hit two logins in sequence - Authentik first, then the service's own login. This is confusing and unnecessary.
The fix: disable the service's built-in auth¶
Preferred: environment variable (DR-resilient)
Set an env var in the Deployment to tell the service to skip its own login:
containers:
- name: uptime-kuma
image: louislam/uptime-kuma:1
env:
- name: UPTIME_KUMA_DISABLE_AUTH
value: "1"
Common env vars for other services:
| Service | Env var | Value |
|---|---|---|
| Uptime Kuma | UPTIME_KUMA_DISABLE_AUTH |
"1" |
| Grafana | GF_AUTH_DISABLE_LOGIN_FORM |
"true" |
| Grafana (anonymous access) | GF_AUTH_ANONYMOUS_ENABLED |
"true" |
Check each service's documentation for the exact variable name.
Why env var and not the UI setting? Many services allow disabling auth via a UI toggle that is then persisted in the service's data volume. That works fine day-to-day, but after a disaster recovery restore with a fresh PVC the volume is empty - the UI setting is gone and the service's login page reappears. The env var approach is baked into the Deployment manifest (committed to git) and survives any PVC loss.
Alternative: UI setting
Some services let you disable auth from within their admin panel (e.g. Grafana → Administration → Authentication). This is faster to set up but only persists in the PVC. Use the env var approach for any service you want to be fully DR-resilient.