# Deployment + Service — KeyCape (namespace: sso) # # KeyCape is the OIDC orchestration layer. It is stateless: all persistent # state lives in Authelia (session), LLDAP (users), and privacyIDEA (MFA tokens). # No PVC is required. # # Configuration is stored entirely in the keycape-config Secret, which holds # a complete config.yaml and the RSA private key used to sign OIDC tokens # issued to downstream applications. # # Prerequisites (apply in order): # 1. keycape-config Secret — run keycape/create-secrets.sh # 2. keycape-pi-token Secret — run keycape/create-pi-token.sh (after T04 bootstrap) # 3. This file # 4. middleware.yaml + ingress.yaml # # Container image: # KeyCape has no published image. Build from ~/key-cape/ and push to a registry, # or import directly into K3s (see README.md "Building the image"). # Image tag below is a placeholder — update before applying. apiVersion: apps/v1 kind: Deployment metadata: name: keycape namespace: sso labels: app.kubernetes.io/name: keycape app.kubernetes.io/part-of: net-kingdom-sso-mfa net-kingdom/component: sso spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: keycape strategy: type: RollingUpdate # stateless — safe to roll template: metadata: labels: app.kubernetes.io/name: keycape app.kubernetes.io/part-of: net-kingdom-sso-mfa net-kingdom/component: sso spec: securityContext: runAsNonRoot: true runAsUser: 65534 # nobody — matches distroless static image fsGroup: 65534 containers: - name: keycape # Image published to self-hosted Gitea OCI registry on CoulombCore (KEY-WP-0002). # k3s insecure registry configured for 92.205.130.254:32166 — no pull secret needed. image: 92.205.130.254:32166/coulomb/key-cape:latest imagePullPolicy: Always ports: - name: http containerPort: 8080 protocol: TCP env: - name: KEYCAPE_CONFIG value: /etc/keycape/config.yaml volumeMounts: # keycape-config Secret provides config.yaml and key.pem - name: config-secret mountPath: /etc/keycape readOnly: true startupProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 3 periodSeconds: 3 failureThreshold: 10 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 0 periodSeconds: 15 failureThreshold: 3 readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 0 periodSeconds: 10 failureThreshold: 3 resources: requests: cpu: "25m" memory: "32Mi" limits: cpu: "200m" memory: "128Mi" volumes: - name: config-secret secret: secretName: keycape-config # Secret must contain two keys: config.yaml and key.pem items: - key: config.yaml path: config.yaml - key: key.pem path: key.pem mode: 0400 # key.pem is sensitive; restrict to owner read only --- # Service — ClusterIP; Traefik reaches KeyCape via port 8080. apiVersion: v1 kind: Service metadata: name: keycape namespace: sso labels: app.kubernetes.io/name: keycape app.kubernetes.io/part-of: net-kingdom-sso-mfa net-kingdom/component: sso spec: type: ClusterIP selector: app.kubernetes.io/name: keycape ports: - name: http port: 8080 targetPort: 8080 protocol: TCP