infrastructure/auth/keycloak.yaml
Infrastructure Admin 0dee133377 Add Argo Workflows, mTLS container registry, and fix infrastructure
- Move Keycloak off Helm to plain Crossplane Object manifests (PostgreSQL + Keycloak deployment)
- Add Vaultwarden SSO/OIDC config with Keycloak, fix Recreate deployment strategy for RWO volumes
- Switch routing from Helm-based Pomerium to pomerium-allinone with all service routes
- Deploy Argo Workflows (controller, server, CRDs, RBAC) with KEDA queue-depth autoscaling
- Add Civo cluster autoscaler with pool-scaler for zero-to-one scale-up via Civo API
- Add node-labeler to auto-tag nodes by pool membership for nodeSelector scheduling
- Set up mTLS container registry at registry.nge6.com (Forgejo built-in, client cert required)
- Add internal registry route (registry-internal.nge6.com) for in-cluster image pulls
- Fix DNS records for new Emissary LB IP (212.2.241.28)
- Fix CoreDNS crash from invalid custom config
- Fix Emissary apiext expired webhook CA certificate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 21:23:12 -04:00

410 lines
10 KiB
YAML

# auth-system namespace
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: auth-system-namespace
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: v1
kind: Namespace
metadata:
name: auth-system
---
# Keycloak service account
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-service-account
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: v1
kind: ServiceAccount
metadata:
name: keycloak
namespace: auth-system
---
# Keycloak role
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-role
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: keycloak
namespace: auth-system
rules:
- apiGroups: [""]
resources: ["secrets", "configmaps", "pods"]
verbs: ["get", "list", "watch"]
---
# Keycloak role binding
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-role-binding
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: keycloak
namespace: auth-system
subjects:
- kind: ServiceAccount
name: keycloak
namespace: auth-system
roleRef:
kind: Role
name: keycloak
apiGroup: rbac.authorization.k8s.io
---
# Keycloak admin credentials
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-admin-secret
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: v1
kind: Secret
metadata:
name: keycloak-admin-creds
namespace: auth-system
type: Opaque
stringData:
password: "thefi9paechooh"
---
# PostgreSQL credentials
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-postgresql-secret
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: v1
kind: Secret
metadata:
name: keycloak-postgresql
namespace: auth-system
type: Opaque
stringData:
postgresql-password: "keycloak-db-password"
POSTGRES_PASSWORD: "keycloak-db-password"
POSTGRES_USER: "keycloak"
POSTGRES_DB: "keycloak"
---
# PostgreSQL StatefulSet
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-postgresql-statefulset
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: keycloak-postgresql
namespace: auth-system
spec:
serviceName: keycloak-postgresql
replicas: 1
selector:
matchLabels:
app: keycloak-postgresql
template:
metadata:
labels:
app: keycloak-postgresql
spec:
containers:
- name: postgresql
image: postgres:17-bookworm
ports:
- containerPort: 5432
name: postgresql
envFrom:
- secretRef:
name: keycloak-postgresql
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
subPath: pgdata
readinessProbe:
exec:
command:
- pg_isready
- -U
- keycloak
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
exec:
command:
- pg_isready
- -U
- keycloak
initialDelaySeconds: 30
periodSeconds: 30
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
storageClassName: civo-volume
resources:
requests:
storage: 5Gi
---
# PostgreSQL Service
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-postgresql-service
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: v1
kind: Service
metadata:
name: keycloak-postgresql
namespace: auth-system
spec:
selector:
app: keycloak-postgresql
ports:
- name: postgresql
port: 5432
targetPort: 5432
type: ClusterIP
---
# Keycloak Deployment
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-deployment
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
namespace: auth-system
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
serviceAccountName: keycloak
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:24.0.4
args:
- start
- --db=postgres
- --hostname=auth.nge6.com
- --hostname-strict=false
- --hostname-strict-https=false
- --proxy=edge
- --http-enabled=true
ports:
- containerPort: 8080
name: http
env:
- name: KEYCLOAK_ADMIN
value: admin
- name: KEYCLOAK_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-admin-creds
key: password
- name: KC_DB
value: postgres
- name: KC_DB_URL
value: jdbc:postgresql://keycloak-postgresql:5432/keycloak
- name: KC_DB_USERNAME
value: keycloak
- name: KC_DB_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-postgresql
key: postgresql-password
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
readinessProbe:
httpGet:
path: /realms/master
port: 8080
initialDelaySeconds: 90
timeoutSeconds: 3
periodSeconds: 10
failureThreshold: 10
livenessProbe:
httpGet:
path: /realms/master
port: 8080
initialDelaySeconds: 120
timeoutSeconds: 5
periodSeconds: 30
failureThreshold: 10
startupProbe:
httpGet:
path: /realms/master
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 3
periodSeconds: 5
failureThreshold: 30
---
# Keycloak Service
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-service
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: v1
kind: Service
metadata:
name: keycloak-http
namespace: auth-system
spec:
selector:
app: keycloak
ports:
- name: http
port: 80
targetPort: 8080
type: ClusterIP
---
# Keycloak SSL Certificate
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-certificate
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: keycloak-tls
namespace: emissary
spec:
secretName: keycloak-tls
issuerRef:
name: letsencrypt-dns
kind: ClusterIssuer
dnsNames:
- auth.nge6.com
---
# Keycloak Ambassador Host
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-host
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: getambassador.io/v3alpha1
kind: Host
metadata:
name: keycloak-host
namespace: emissary
annotations:
external-dns.ambassador-service: emissary-ingress.emissary.svc.cluster.local
external-dns.alpha.kubernetes.io/target: 212.2.241.28
spec:
hostname: auth.nge6.com
tlsSecret:
name: keycloak-tls
---
# Keycloak Ambassador Mapping
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
name: keycloak-mapping
namespace: crossplane-system
spec:
providerConfigRef:
name: kubernetes-provider
forProvider:
manifest:
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
name: keycloak-mapping
namespace: emissary
spec:
hostname: auth.nge6.com
prefix: /
service: keycloak-http.auth-system:80
timeout_ms: 30000
connect_timeout_ms: 10000