mirror of
https://gitlab.kube.huskypup.net/Scooby/Homelabv4.git
synced 2026-08-21 05:26:49 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: authentik
|
||||
namespace: authentik
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: edge
|
||||
namespace: gateway
|
||||
sectionName: https
|
||||
hostnames:
|
||||
- auth.kube.huskypup.net
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: authentik-server
|
||||
port: 80
|
||||
@@ -0,0 +1,179 @@
|
||||
# Nginx proxy that rewrites Host header for Authentik ext_authz
|
||||
#
|
||||
# Problem: Envoy's ext_authz HTTP filter forwards the ORIGINAL request's Host
|
||||
# header (e.g., prometheus.kube.huskypup.net) to Authentik. But Authentik's
|
||||
# embedded outpost only serves /outpost.goauthentik.io/auth/nginx on the
|
||||
# brand domain (auth.kube.huskypup.net), returning 404 for other hosts.
|
||||
#
|
||||
# Solution: This lightweight nginx proxy sits between Istio's ext_authz filter
|
||||
# and Authentik. It rewrites the Host header to auth.kube.huskypup.net while
|
||||
# preserving all other headers (X-Forwarded-Host, cookies, etc.) so Authentik
|
||||
# knows the original service being accessed.
|
||||
#
|
||||
# Flow:
|
||||
# 1. Client → Istio ingress gateway (Host: prometheus.kube.huskypup.net)
|
||||
# 2. ext_authz filter → this proxy (Host: prometheus.kube.huskypup.net)
|
||||
# 3. This proxy → Authentik (Host: auth.kube.huskypup.net, X-Forwarded-Host preserved)
|
||||
# 4. Authentik checks auth, returns 200 (allow) or 302 (redirect to login)
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ext-authz-proxy-config
|
||||
namespace: authentik
|
||||
data:
|
||||
nginx.conf: |
|
||||
load_module modules/ngx_http_js_module.so;
|
||||
|
||||
worker_processes 1;
|
||||
error_log /dev/stderr warn;
|
||||
pid /tmp/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 256;
|
||||
}
|
||||
|
||||
http {
|
||||
access_log /dev/stdout;
|
||||
proxy_temp_path /tmp/proxy_temp;
|
||||
client_body_temp_path /tmp/client_temp;
|
||||
|
||||
js_path /etc/nginx/njs/;
|
||||
js_import cookie from cookie_domain.js;
|
||||
|
||||
server {
|
||||
listen 4180;
|
||||
|
||||
location / {
|
||||
# Rewrite all paths to the exact Authentik traefik handler path.
|
||||
# Envoy ext_authz appends the original request path to pathPrefix,
|
||||
# creating paths like /auth/traefik/ which Authentik's Go router
|
||||
# doesn't match (exact path only, no trailing slash).
|
||||
rewrite ^ /outpost.goauthentik.io/auth/traefik break;
|
||||
proxy_pass http://authentik-server.authentik.svc.cluster.local;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host auth.kube.huskypup.net;
|
||||
proxy_set_header Connection "";
|
||||
proxy_pass_request_headers on;
|
||||
|
||||
# Add Domain=kube.huskypup.net to Set-Cookie headers from Authentik.
|
||||
# The outpost creates session cookies without Domain attribute, causing
|
||||
# browser to scope them to the original request domain (e.g., frigate.kube.huskypup.net).
|
||||
# The callback goes to auth.kube.huskypup.net, so the cookie must be domain-scoped.
|
||||
js_header_filter cookie.addDomain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cookie_domain.js: |
|
||||
function addDomain(r) {
|
||||
var cookies = r.headersOut['Set-Cookie'];
|
||||
if (cookies) {
|
||||
if (!Array.isArray(cookies)) {
|
||||
cookies = [cookies];
|
||||
}
|
||||
var modified = cookies.map(function(c) {
|
||||
if (c.indexOf('Domain=') === -1) {
|
||||
return c + '; Domain=kube.huskypup.net';
|
||||
}
|
||||
return c;
|
||||
});
|
||||
r.headersOut['Set-Cookie'] = modified;
|
||||
}
|
||||
}
|
||||
|
||||
export default { addDomain };
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ext-authz-proxy
|
||||
namespace: authentik
|
||||
labels:
|
||||
app: ext-authz-proxy
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ext-authz-proxy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ext-authz-proxy
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.27-alpine
|
||||
ports:
|
||||
- containerPort: 4180
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/nginx/nginx.conf
|
||||
subPath: nginx.conf
|
||||
readOnly: true
|
||||
- name: njs
|
||||
mountPath: /etc/nginx/njs/cookie_domain.js
|
||||
subPath: cookie_domain.js
|
||||
readOnly: true
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
- name: cache
|
||||
mountPath: /var/cache/nginx
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10101
|
||||
runAsGroup: 10101
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 4180
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 4180
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: ext-authz-proxy-config
|
||||
- name: njs
|
||||
configMap:
|
||||
name: ext-authz-proxy-config
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
- name: cache
|
||||
emptyDir: {}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ext-authz-proxy
|
||||
namespace: authentik
|
||||
labels:
|
||||
app: ext-authz-proxy
|
||||
spec:
|
||||
selector:
|
||||
app: ext-authz-proxy
|
||||
ports:
|
||||
- port: 4180
|
||||
targetPort: 4180
|
||||
protocol: TCP
|
||||
@@ -0,0 +1,63 @@
|
||||
apiVersion: networking.istio.io/v1beta1
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
name: authentik
|
||||
namespace: authentik
|
||||
spec:
|
||||
hosts:
|
||||
- auth.kube.huskypup.net
|
||||
gateways:
|
||||
- istio-system/edge
|
||||
http:
|
||||
# Allow NetBird silent auth in an iframe (prompt=none)
|
||||
- match:
|
||||
- uri:
|
||||
prefix: /application/o/authorize/
|
||||
headers:
|
||||
response:
|
||||
remove:
|
||||
- x-frame-options
|
||||
set:
|
||||
content-security-policy: "frame-ancestors https://netbird.kube.huskypup.net"
|
||||
route:
|
||||
- destination:
|
||||
host: authentik-server.authentik.svc.cluster.local
|
||||
port:
|
||||
number: 80
|
||||
|
||||
# Ensure CORS headers are present even on 401 responses
|
||||
- match:
|
||||
- uri:
|
||||
prefix: /application/o/userinfo/
|
||||
- uri:
|
||||
prefix: /application/o/token/
|
||||
headers:
|
||||
response:
|
||||
set:
|
||||
access-control-allow-origin: "https://netbird.kube.huskypup.net"
|
||||
access-control-allow-credentials: "true"
|
||||
access-control-allow-methods: "GET, POST, OPTIONS"
|
||||
access-control-allow-headers: "authorization, content-type"
|
||||
vary: "Origin"
|
||||
route:
|
||||
- destination:
|
||||
host: authentik-server.authentik.svc.cluster.local
|
||||
port:
|
||||
number: 80
|
||||
|
||||
# Forward auth endpoint for Istio ext_authz
|
||||
- match:
|
||||
- uri:
|
||||
prefix: /outpost.goauthentik.io
|
||||
route:
|
||||
- destination:
|
||||
host: authentik-server.authentik.svc.cluster.local
|
||||
port:
|
||||
number: 80
|
||||
|
||||
# All other traffic goes to Authentik
|
||||
- route:
|
||||
- destination:
|
||||
host: authentik-server.authentik.svc.cluster.local
|
||||
port:
|
||||
number: 80
|
||||
@@ -0,0 +1,36 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: authentik-oauth-sync
|
||||
namespace: authentik
|
||||
annotations:
|
||||
argocd.argoproj.io/hook: PostSync
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||
argocd.argoproj.io/sync-wave: "1"
|
||||
spec:
|
||||
backoffLimit: 3
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: argocd-hook-sa
|
||||
containers:
|
||||
- name: oauth-sync
|
||||
image: bitnami/kubectl:1.31
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
set -euo pipefail
|
||||
echo "=== Authentik PostSync - OAuth Sync ==="
|
||||
|
||||
echo "Waiting for Authentik server..."
|
||||
kubectl -n authentik rollout status deploy/authentik-server --timeout=600s
|
||||
|
||||
echo "Waiting for Authentik worker..."
|
||||
kubectl -n authentik rollout status deploy/authentik-worker --timeout=300s
|
||||
|
||||
echo "OAuth sync scripts should be run manually or via a separate automation."
|
||||
echo "Scripts are in the scripts/ directory of the Homelabv4 repo."
|
||||
echo "Run: bash scripts/sync-*-oauth.sh"
|
||||
|
||||
echo "=== Authentik PostSync Complete ==="
|
||||
restartPolicy: Never
|
||||
@@ -0,0 +1,48 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: authentik-presync
|
||||
namespace: authentik
|
||||
annotations:
|
||||
argocd.argoproj.io/hook: PreSync
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||
spec:
|
||||
backoffLimit: 3
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: argocd-hook-sa
|
||||
containers:
|
||||
- name: presync
|
||||
image: bitnami/kubectl:1.31
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
set -euo pipefail
|
||||
echo "=== Authentik PreSync ==="
|
||||
|
||||
# Wait for CNPG cluster to be ready (applied by ArgoCD as sync-wave resource)
|
||||
echo "Waiting for Authentik PostgreSQL cluster..."
|
||||
for i in $(seq 1 60); do
|
||||
PHASE=$(kubectl -n authentik get clusters.postgresql.cnpg.io pg-authentik -o jsonpath='{.status.phase}' 2>/dev/null || echo "")
|
||||
if [ "$PHASE" = "Cluster in healthy state" ] || [ "$PHASE" = "Healthy" ]; then
|
||||
echo " PostgreSQL cluster ready"
|
||||
break
|
||||
fi
|
||||
echo " waiting for pg-authentik... (attempt $i/60, phase=$PHASE)"
|
||||
sleep 5
|
||||
done
|
||||
|
||||
# Generate Authentik secret key if it doesn't exist
|
||||
if ! kubectl -n authentik get secret authentik >/dev/null 2>&1; then
|
||||
echo "Generating Authentik secret key..."
|
||||
SECRET_KEY=$(openssl rand -hex 50)
|
||||
kubectl -n authentik create secret generic authentik \
|
||||
--from-literal=AUTHENTIK_SECRET_KEY="$SECRET_KEY"
|
||||
echo " Authentik secret key generated"
|
||||
else
|
||||
echo " Authentik secret already exists"
|
||||
fi
|
||||
|
||||
echo "=== Authentik PreSync Complete ==="
|
||||
restartPolicy: Never
|
||||
Reference in New Issue
Block a user