Authentik Forward Auth Pattern¶
This guide documents how to protect any web service with Authentik forward authentication via K8s Ingress.
When to Use This Pattern¶
Use forward auth when: - App doesn't support native SSO (LDAP/OIDC/SAML) - You want to control WHO can access the app - App runs outside K8s (VMs, Docker, external services)
Note: Forward auth controls access but doesn't provide single sign-on INTO the app. Users authenticate with Authentik, then may still need to log into the app separately.
Architecture¶
User → app.bogocat.com → K8s Ingress (10.89.97.220)
│
▼
┌─────────────────────┐
│ nginx auth_request │
│ → Authentik server │
└─────────────────────┘
│
┌─────────────┴─────────────┐
│ │
(not authenticated) (authenticated)
│ │
▼ ▼
Redirect to Authentik Proxy to backend
login via /outpost... (VM, container, etc.)
Prerequisites¶
- Authentik deployed in K8s (
authentiknamespace) - Embedded outpost configured (type: proxy)
- nginx-ingress controller
- DNS entries pointing to K8s ingress IP
Step-by-Step Setup¶
Step 1: Create Proxy Provider in Authentik¶
- Go to https://auth.bogocat.com → Providers → Create → Proxy Provider
- Configure:
| Field | Value |
|---|---|
| Name | {app-name}-proxy |
| Authorization flow | default-provider-authorization-implicit-consent |
| Forward auth mode | Forward auth (single application) |
| External host | http://{app-name}.bogocat.com |
Step 2: Create Application¶
- Applications → Create
- Configure:
| Field | Value |
|---|---|
| Name | {App Display Name} |
| Slug | {app-name} |
| Provider | {app-name}-proxy |
- Add Policy Binding → Group → Select allowed group
Step 3: Assign to Embedded Outpost¶
- Applications → Outposts → authentik Embedded Outpost → Edit
- Add the new application to Applications
- Click Update
Step 4: Create K8s Manifests¶
Save as /root/tower-fleet/manifests/{namespace}/{app-name}.yaml:
# {App Name} Forward Auth Configuration
# Replace: {app-name}, {APP_NAME}, {NAMESPACE}, {BACKEND_IP}, {PORT}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {app-name}-auth-headers
namespace: {NAMESPACE}
data:
X-Forwarded-Host: "{app-name}.bogocat.com"
X-Forwarded-Proto: "https"
---
# Backend service (headless + endpoints for external IP)
apiVersion: v1
kind: Service
metadata:
name: {app-name}-backend
namespace: {NAMESPACE}
spec:
type: ClusterIP
clusterIP: None
ports:
- name: http
port: {PORT}
targetPort: {PORT}
---
apiVersion: v1
kind: Endpoints
metadata:
name: {app-name}-backend
namespace: {NAMESPACE}
subsets:
- addresses:
- ip: {BACKEND_IP}
ports:
- name: http
port: {PORT}
---
# ExternalName service to reach Authentik
apiVersion: v1
kind: Service
metadata:
name: {app-name}-authentik
namespace: {NAMESPACE}
spec:
type: ExternalName
externalName: authentik-server.authentik.svc.cluster.local
---
# Ingress for outpost paths (handles login flow)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {app-name}-outpost
namespace: {NAMESPACE}
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/upstream-vhost: "authentik-server.authentik.svc.cluster.local"
spec:
ingressClassName: nginx
rules:
- host: {app-name}.bogocat.com
http:
paths:
- path: /outpost.goauthentik.io/
pathType: Prefix
backend:
service:
name: {app-name}-authentik
port:
number: 80
---
# Main ingress with forward auth
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {app-name}
namespace: {NAMESPACE}
annotations:
nginx.ingress.kubernetes.io/auth-url: "http://authentik-server.authentik.svc.cluster.local/outpost.goauthentik.io/auth/nginx"
nginx.ingress.kubernetes.io/auth-signin: "http://{app-name}.bogocat.com/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri"
nginx.ingress.kubernetes.io/auth-response-headers: "Set-Cookie,X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-uid"
nginx.ingress.kubernetes.io/auth-proxy-set-headers: "{NAMESPACE}/{app-name}-auth-headers"
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
ingressClassName: nginx
rules:
- host: {app-name}.bogocat.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {app-name}-backend
port:
number: {PORT}
Step 5: Apply and Add DNS¶
Add DNS in OPNsense: {app-name}.bogocat.com → 10.89.97.220
Step 6: Test¶
- Open incognito browser
- Go to
http://{app-name}.bogocat.com - Should redirect to Authentik login
- After login → access granted to app
Key Implementation Details¶
Why Headless Service + Endpoints?¶
ExternalName services with IP addresses don't work with nginx-ingress (DNS lookup fails). Use headless service pattern instead:
Why ConfigMap for Headers?¶
The embedded outpost matches requests by X-Forwarded-Host. nginx-ingress doesn't pass this by default, so we use:
Why Separate Outpost Ingress?¶
The login flow uses paths like /outpost.goauthentik.io/start and /outpost.goauthentik.io/callback. These must be served under the same hostname as the app (not auth.bogocat.com) so the X-Forwarded-Host header is correct.
Why No configuration-snippet?¶
The nginx-ingress controller has snippets disabled by admin. Use auth-proxy-set-headers with ConfigMap instead.
Troubleshooting¶
| Issue | Cause | Fix |
|---|---|---|
| 404 on auth endpoint | App not assigned to outpost | Add app to embedded outpost in Authentik |
| 503 Service Unavailable | Backend unreachable | Check Endpoints exist, verify backend IP reachable |
| 500 Internal Server Error | Ingress config error | Check ingress controller logs |
| Redirect loop | Cookie issues | Clear cookies, check auth-signin URL |
| "Not Found" on /start | Wrong X-Forwarded-Host | Verify ConfigMap and auth-proxy-set-headers |
Examples¶
- arr-stack apps: See arr-stack SSO for Sonarr, Radarr, Jellyseerr, etc.
Related Documentation¶
- Authentik SSO Infrastructure - LDAP setup, user management
- Authentik Docs - Forward Auth