removing auth ns
This commit is contained in:
@@ -1,99 +0,0 @@
|
|||||||
# Auth (Authentik + Kyverno SSO annotation policy)
|
|
||||||
|
|
||||||
Draft manifests only — nothing applied yet. Convention: any Ingress annotated
|
|
||||||
with `use-sso-auth: "true"` gets the Authentik forward-auth annotations injected
|
|
||||||
by the Kyverno ClusterPolicy (`auth/sso-auth-policy.yaml`).
|
|
||||||
|
|
||||||
## Deploy order
|
|
||||||
|
|
||||||
1. **Authentik** (`auth/authentik.yaml`) — IdP + embedded outpost. Needs a
|
|
||||||
Postgres user/DB on postgresql.haven first (see below), plus secrets from
|
|
||||||
Vaultwarden/ESO.
|
|
||||||
2. **Proxy provider** — create in Authentik UI (or via API) once it's up:
|
|
||||||
one provider per app you want header-auth'd, mode "forward_auth (single
|
|
||||||
app)", or a domain-level provider with the embedded outpost.
|
|
||||||
3. **Kyverno** (`auth/kyverno-helm.yaml`) — policy engine.
|
|
||||||
4. **SSO policy** (`auth/sso-auth-policy.yaml`) — expands `use-sso-auth: "true"`
|
|
||||||
into `auth-url` / `auth-signin` / `auth-response-headers` annotations.
|
|
||||||
5. Annotate ingresses: `kubectl annotate ingress <name> -n <ns> use-sso-auth=true`
|
|
||||||
and mirror the change in this repo.
|
|
||||||
|
|
||||||
## Postgres bootstrap (one-time, on postgresql.haven)
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE USER authentik WITH PASSWORD '<from-vaultwarden>';
|
|
||||||
CREATE DATABASE authentik OWNER authentik;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Outpost URL used everywhere is `https://auth.haven` — add DNS + ingress for
|
|
||||||
the outpost before annotating anything.
|
|
||||||
- Apps that should *consume* the injected `X-Authentik-*` headers (Grafana auth
|
|
||||||
proxy, Paperless remote-user, OpenWebUI trusted headers) need their own env
|
|
||||||
changes; those are app-side, not covered by the policy.
|
|
||||||
- API/WebSocket-heavy apps (arr stack, qBittorrent) should NOT get the
|
|
||||||
annotation on API paths — either skip the annotation or exclude paths.
|
|
||||||
|
|
||||||
## Which apps get the annotation (scoping)
|
|
||||||
|
|
||||||
Three tiers, applied per ingress — never blanket:
|
|
||||||
|
|
||||||
1. **OIDC tier** (no annotation, wire native OIDC in the app instead):
|
|
||||||
grafana, paperless, affine, openwebui, vaultwarden, beszel, slink.
|
|
||||||
Proper logout, group mapping, zero API breakage.
|
|
||||||
2. **Forward-auth tier** (`use-sso-auth: "true"`): browser-only utilities with
|
|
||||||
no real auth — it-tools, notepad, searxng, homepage, archivebox,
|
|
||||||
stirlingpdf, file-nginx, code-config, havenllo, own apps (chacal,
|
|
||||||
mindforge) if wanted.
|
|
||||||
3. **Hands off** (built-in auth is fine, forward-auth breaks clients):
|
|
||||||
sonarr, radarr, prowlarr, qbittorrent, adguard, changedetection,
|
|
||||||
uptimekuma, cloudreve (WebDAV), jellyfin (clients can't do redirects —
|
|
||||||
only the community SSO plugin route exists).
|
|
||||||
|
|
||||||
## Pre-annotation checklist (per new app)
|
|
||||||
|
|
||||||
Forward-auth breakage is loud and immediate if you look in the right places.
|
|
||||||
Before annotating any real hostname:
|
|
||||||
|
|
||||||
1. **Ask: what talks to this app that isn't a human in a browser?**
|
|
||||||
Mobile/desktop apps with own login, API-key consumers, push/webhook
|
|
||||||
receivers, cronjobs curling through the ingress, WebDAV/RSS → any of
|
|
||||||
those = hands off (tier 3) or exclude paths. "Just me in a browser" =
|
|
||||||
safe.
|
|
||||||
2. **Canary first:** create a scratch ingress for the same service
|
|
||||||
(`<app>-test.haven`) with the annotation; leave the original untouched
|
|
||||||
for ~a week. Forgotten integrations keep hitting the original and only
|
|
||||||
break the canary — instant rollback
|
|
||||||
(`kubectl annotate ingress <n> -n <ns> use-sso-auth-`).
|
|
||||||
3. **Grep the docs** (30s): "reverse proxy", "trusted header", "API key",
|
|
||||||
"webhook", "basic auth". A "running behind a reverse proxy" doc section
|
|
||||||
is where landmines are documented.
|
|
||||||
4. **Header-trust hygiene:** apps consuming `X-Authentik-*` must be
|
|
||||||
reachable ONLY through the ingress (no NodePort/exposed port, otherwise
|
|
||||||
LAN clients can forge headers straight to the pod). Keep built-in local
|
|
||||||
login enabled; never set SSO-only mode.
|
|
||||||
|
|
||||||
## Files
|
|
||||||
|
|
||||||
- `kyverno-helm.yaml` — Kyverno install values
|
|
||||||
- `sso-auth-policy.yaml` — ClusterPolicy: `use-sso-auth` → nginx auth annotations
|
|
||||||
- `authentik.yaml` — Authentik server/worker + outpost, Postgres PVC, ingress
|
|
||||||
|
|
||||||
## Status / when to actually deploy this (decision from 2026-08-28)
|
|
||||||
|
|
||||||
NOT deployed — deliberate. Sole user, low login frequency, everything
|
|
||||||
reachable via wg-easy, so SSO solves a problem that doesn't exist. The
|
|
||||||
`*.ivanch.me` public ingresses were reviewed instead: exposure without usage
|
|
||||||
is pure risk, and the right fix is removing exposure (VPN-only), not adding
|
|
||||||
an IdP.
|
|
||||||
|
|
||||||
Pull the trigger only if one of these becomes true:
|
|
||||||
|
|
||||||
1. A second person uses the homelab regularly (SSO value scales with users).
|
|
||||||
2. Something must be exposed that can't sit behind VPN (third-party
|
|
||||||
webhooks/callbacks) — then public tier gets `use-sso-auth` + 2FA.
|
|
||||||
3. Login friction on the OIDC apps (grafana/paperless/affine/openwebui/
|
|
||||||
vaultwarden/beszel/slink) actually annoys on a weekly basis.
|
|
||||||
|
|
||||||
Until then this folder is a shelf-ready draft; cost of keeping it is zero.
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
# Authentik — IdP + embedded outpost (proxy provider forward-auth).
|
|
||||||
# Draft — do NOT apply as-is:
|
|
||||||
# - secret values are placeholders; create them from Vaultwarden/ESO first
|
|
||||||
# - requires the authentik user/DB on postgresql.haven (see README.md)
|
|
||||||
# - requires DNS record for auth.haven -> ingress IP (user manages DNS)
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: authentik-secrets
|
|
||||||
namespace: auth
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
postgres_password: "CHANGE_ME" # Vaultwarden -> ESO later
|
|
||||||
secret_key: "CHANGE_ME" # `openssl rand -base64 60`
|
|
||||||
bootstrap_password: "CHANGE_ME" # initial akadmin password
|
|
||||||
bootstrap_token: "CHANGE_ME" # outpost token
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: authentik-media
|
|
||||||
namespace: auth
|
|
||||||
spec:
|
|
||||||
accessModes: [ReadWriteOnce]
|
|
||||||
storageClassName: nfs-fast-client # verified: exists in cluster (nfs-client is also default)
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 2Gi
|
|
||||||
---
|
|
||||||
# Server (web + API)
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: authentik-server
|
|
||||||
namespace: auth
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels: { app: authentik, component: server }
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels: { app: authentik, component: server }
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: server
|
|
||||||
image: ghcr.io/goauthentik/server:2026.8
|
|
||||||
args: [server]
|
|
||||||
env:
|
|
||||||
- name: AUTHENTIK_SECRET_KEY
|
|
||||||
valueFrom:
|
|
||||||
{ secretKeyRef: { name: authentik-secrets, key: secret_key } }
|
|
||||||
- name: AUTHENTIK_POSTGRESQL__HOST
|
|
||||||
value: postgresql.haven
|
|
||||||
- name: AUTHENTIK_POSTGRESQL__NAME
|
|
||||||
value: authentik
|
|
||||||
- name: AUTHENTIK_POSTGRESQL__USER
|
|
||||||
value: authentik
|
|
||||||
- name: AUTHENTIK_POSTGRESQL__PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
{
|
|
||||||
secretKeyRef:
|
|
||||||
{ name: authentik-secrets, key: postgres_password },
|
|
||||||
}
|
|
||||||
- name: AUTHENTIK_BOOTSTRAP_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
{
|
|
||||||
secretKeyRef:
|
|
||||||
{ name: authentik-secrets, key: bootstrap_password },
|
|
||||||
}
|
|
||||||
- name: AUTHENTIK_BOOTSTRAP_TOKEN
|
|
||||||
valueFrom:
|
|
||||||
{
|
|
||||||
secretKeyRef:
|
|
||||||
{ name: authentik-secrets, key: bootstrap_token },
|
|
||||||
}
|
|
||||||
- name: AUTHENTIK_ERROR_REPORTING__ENABLED
|
|
||||||
value: "false"
|
|
||||||
ports:
|
|
||||||
- { containerPort: 9000, name: http }
|
|
||||||
- { containerPort: 9443, name: https }
|
|
||||||
readinessProbe:
|
|
||||||
httpGet: { path: /-/health/ready/, port: 9000 }
|
|
||||||
initialDelaySeconds: 20
|
|
||||||
periodSeconds: 10
|
|
||||||
livenessProbe:
|
|
||||||
httpGet: { path: /-/health/live/, port: 9000 }
|
|
||||||
initialDelaySeconds: 40
|
|
||||||
periodSeconds: 20
|
|
||||||
resources:
|
|
||||||
requests: { cpu: 250m, memory: 512Mi }
|
|
||||||
limits: { memory: 1Gi, cpu: 1000m }
|
|
||||||
volumeMounts:
|
|
||||||
- { name: media, mountPath: /media }
|
|
||||||
volumes:
|
|
||||||
- name: media
|
|
||||||
persistentVolumeClaim: { claimName: authentik-media }
|
|
||||||
---
|
|
||||||
# Worker (policies, outpost management, scheduled tasks)
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: authentik-worker
|
|
||||||
namespace: auth
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels: { app: authentik, component: worker }
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels: { app: authentik, component: worker }
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: worker
|
|
||||||
image: ghcr.io/goauthentik/server:2026.8
|
|
||||||
args: [worker]
|
|
||||||
env: # same env as server; kept duplicated for a flat draft manifest
|
|
||||||
- name: AUTHENTIK_SECRET_KEY
|
|
||||||
valueFrom:
|
|
||||||
{ secretKeyRef: { name: authentik-secrets, key: secret_key } }
|
|
||||||
- name: AUTHENTIK_POSTGRESQL__HOST
|
|
||||||
value: postgresql.haven
|
|
||||||
- name: AUTHENTIK_POSTGRESQL__NAME
|
|
||||||
value: authentik
|
|
||||||
- name: AUTHENTIK_POSTGRESQL__USER
|
|
||||||
value: authentik
|
|
||||||
- name: AUTHENTIK_POSTGRESQL__PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
{
|
|
||||||
secretKeyRef:
|
|
||||||
{ name: authentik-secrets, key: postgres_password },
|
|
||||||
}
|
|
||||||
- name: AUTHENTIK_BOOTSTRAP_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
{
|
|
||||||
secretKeyRef:
|
|
||||||
{ name: authentik-secrets, key: bootstrap_password },
|
|
||||||
}
|
|
||||||
- name: AUTHENTIK_BOOTSTRAP_TOKEN
|
|
||||||
valueFrom:
|
|
||||||
{
|
|
||||||
secretKeyRef:
|
|
||||||
{ name: authentik-secrets, key: bootstrap_token },
|
|
||||||
}
|
|
||||||
- name: AUTHENTIK_ERROR_REPORTING__ENABLED
|
|
||||||
value: "false"
|
|
||||||
resources:
|
|
||||||
requests: { cpu: 200m, memory: 512Mi }
|
|
||||||
limits: { memory: 1Gi, cpu: 1000m }
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: authentik
|
|
||||||
namespace: auth
|
|
||||||
spec:
|
|
||||||
selector: { app: authentik, component: server }
|
|
||||||
ports:
|
|
||||||
- { name: http, port: 80, targetPort: 9000 }
|
|
||||||
---
|
|
||||||
# Embedded outpost — runs the proxy providers; this is what ingress-nginx
|
|
||||||
# calls for /auth/nginx on every use-sso-auth ingress.
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: authentik-outpost
|
|
||||||
namespace: auth
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels: { app: authentik, component: outpost }
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels: { app: authentik, component: outpost }
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: outpost
|
|
||||||
image: ghcr.io/goauthentik/proxy:2026.8
|
|
||||||
env:
|
|
||||||
- name: AUTHENTIK_HOST
|
|
||||||
value: http://authentik.auth.svc.cluster.local
|
|
||||||
- name: AUTHENTIK_INSECURE
|
|
||||||
value: "true"
|
|
||||||
- name: AUTHENTIK_TOKEN
|
|
||||||
valueFrom:
|
|
||||||
{
|
|
||||||
secretKeyRef:
|
|
||||||
{ name: authentik-secrets, key: bootstrap_token },
|
|
||||||
}
|
|
||||||
ports:
|
|
||||||
- { containerPort: 9000, name: http }
|
|
||||||
readinessProbe:
|
|
||||||
httpGet: { path: /outpost.goauthentik.io/ping, port: 9000 }
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 10
|
|
||||||
resources:
|
|
||||||
requests: { cpu: 200m, memory: 512Mi }
|
|
||||||
limits: { memory: 1Gi, cpu: 1000m }
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: authentik-outpost
|
|
||||||
namespace: auth
|
|
||||||
spec:
|
|
||||||
selector: { app: authentik, component: outpost }
|
|
||||||
ports:
|
|
||||||
- { name: http, port: 80, targetPort: 9000 }
|
|
||||||
---
|
|
||||||
# SSO portal (user-facing login) — auth.haven
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: authentik
|
|
||||||
namespace: auth
|
|
||||||
spec:
|
|
||||||
ingressClassName: traefik
|
|
||||||
rules:
|
|
||||||
- host: auth.haven
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend: { service: { name: authentik, port: { number: 80 } } }
|
|
||||||
---
|
|
||||||
# Outpost route — the /outpost.goauthentik.io path must resolve on the same
|
|
||||||
# host the protected apps redirect to. Kept as a separate ingress so it can
|
|
||||||
# also be attached to other hosts later if needed (do NOT give it
|
|
||||||
# use-sso-auth — that would create an auth loop).
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: authentik-outpost
|
|
||||||
namespace: auth
|
|
||||||
spec:
|
|
||||||
ingressClassName: traefik
|
|
||||||
rules:
|
|
||||||
- host: auth.haven
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /outpost.goauthentik.io
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
{ service: { name: authentik-outpost, port: { number: 80 } } }
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Kyverno — policy engine. Install with:
|
|
||||||
# helm repo add kyverno https://kyverno.github.io/kyverno
|
|
||||||
# helm upgrade --install kyverno kyverno/kyverno -n kyverno --create-namespace -f auth/kyverno-helm.yaml
|
|
||||||
# The only consumer of Kyverno here is the use-sso-auth mutation policy;
|
|
||||||
# if you later drop it, Kyverno can be removed with no other impact.
|
|
||||||
admissionController:
|
|
||||||
replicas: 1
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
memory: 256Mi
|
|
||||||
backgroundController:
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 50m
|
|
||||||
memory: 64Mi
|
|
||||||
limits:
|
|
||||||
memory: 128Mi
|
|
||||||
cleanupController:
|
|
||||||
enabled: false
|
|
||||||
reportsController:
|
|
||||||
enabled: false
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# ClusterPolicy: expands the single `use-sso-auth: "true"` Ingress annotation
|
|
||||||
# into the ingress-nginx forward-auth annotations pointed at the Authentik
|
|
||||||
# embedded outpost.
|
|
||||||
#
|
|
||||||
# Test before applying to real ingresses:
|
|
||||||
# kubectl apply -f auth/sso-auth-policy.yaml
|
|
||||||
# kubectl annotate ingress -n default homepage use-sso-auth=true --dry-run=server -o yaml # check annotations get injected
|
|
||||||
# # remove the test annotation afterwards
|
|
||||||
---
|
|
||||||
apiVersion: kyverno.io/v1
|
|
||||||
kind: ClusterPolicy
|
|
||||||
metadata:
|
|
||||||
name: inject-sso-auth
|
|
||||||
spec:
|
|
||||||
validationFailureAction: Audit
|
|
||||||
background: false
|
|
||||||
rules:
|
|
||||||
- name: authentik-forward-auth
|
|
||||||
match:
|
|
||||||
any:
|
|
||||||
- resources:
|
|
||||||
kinds: [Ingress]
|
|
||||||
preconditions:
|
|
||||||
all:
|
|
||||||
- key: "{{ request.object.metadata.annotations.\"use-sso-auth\" || '' }}"
|
|
||||||
operator: Equals
|
|
||||||
value: "true"
|
|
||||||
mutate:
|
|
||||||
patchStrategicMerge:
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
nginx.ingress.kubernetes.io/auth-url: https://auth.haven/outpost.goauthentik.io/auth/nginx
|
|
||||||
nginx.ingress.kubernetes.io/auth-signin: https://auth.haven/outpost.goauthentik.io/start?rd=$scheme://$http_host$escaped_request_uri
|
|
||||||
# NOTE: no auth-snippet used — ingress-nginx 1.15 rejects snippet
|
|
||||||
# annotations by default (allow-snippet-annotations=false, your
|
|
||||||
# controller ConfigMap is empty so it uses the default).
|
|
||||||
# ingress-nginx already forwards X-Original-URL to the auth
|
|
||||||
# backend automatically, which is all Authentik needs.
|
|
||||||
nginx.ingress.kubernetes.io/auth-response-headers: X-Authentik-Username,X-Authentik-Email,X-Authentik-Groups,X-Authentik-Name,X-Authentik-Metadata
|
|
||||||
Reference in New Issue
Block a user