net-kingdom/sso-mfa/k8s/keycape/deployment.yaml
tegwick c0e2c1f7ba
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
NK-WP-0056-T06: standing paths live (custody, audit sender, KeyCape client); manifest carries the new env binding
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 523928@bnt-lap001
Assistant-Session: 6c737afb-9c14-4152-9f02-477214b9c350
2026-11-04 23:40:07 +01:00

196 lines
6.9 KiB
YAML

# Deployment + Service — KeyCape (namespace: sso)
#
# KeyCape orchestrates OIDC. Pending logins and authorization codes are process-local;
# use one replica with Recreate during replacement. Persistent identity state remains
# in Authelia, LLDAP and privacyIDEA. Scoped authentication policy and receipts
# persist separately in the keycape-authentication-policy PVC.
#
# 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-factor-read Secret — ESO delivery owned by railiance-platform RPF-WP-0040
# 3. policy-pvc.yaml
# 4. This file
# 5. middleware.yaml + ingress.yaml
#
# Container image:
# KeyCape publishes immutable main-<short-sha> tags through Forgejo Actions.
# K3s pulls through the HTTPS Forgejo registry origin.
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: Recreate # one issuer instance; process-local login/code state
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 the self-hosted Forgejo OCI registry (KEY-WP-0002).
# key-cape@e2b5fa1 (main-e2b5fa1, KEY-WP-0046): reads the directory bind
# password from KEYCAPE_LLDAP_BIND_PW; also carries the opt-in token status
# (67a0869..e2c5a94, inactive while tokenStatus is unset) and identity-facts.
# Rollback: sha256:cdec9dd7c515f7e0a6169fc3fa560ee2cc47bf40d899755dec808c362f5ce010
# (af3b1b0, sign-in diagnosis page) together with removing KEYCAPE_LLDAP_BIND_PW.
image: forgejo.coulomb.social/coulomb/key-cape@sha256:9e082dca78f65a4af3e92d3de3705b99e49ca9b298d9562ad0d37511c4306f8d
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
# secrets-engine-openbao service client (NK-WP-0046, live go caafe4be, 2026-10-07).
- name: KEYCAPE_SECRETS_ENGINE_OPENBAO_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: keycape-secrets-engine-openbao-client
key: client-secret
- name: KEYCAPE_POLICY_PATH
value: /var/lib/keycape-policy/policy.json
- name: KEYCAPE_ACCOUNT_PORTAL_URL
value: https://users.coulomb.social
- name: KEYCAPE_BROWSER_LOGOUT_URL
value: https://auth.coulomb.social/logout
- name: KEYCAPE_CONFIG
value: /etc/keycape/config.yaml
# svc-keycape directory bind from OpenBao via ESO (NK-WP-0056-T04, CCR-2026-0054).
# KeyCape refuses to start if this differs from lldap.bindPW in keycape-config.
- name: KEYCAPE_LLDAP_BIND_PW
valueFrom:
secretKeyRef:
name: keycape-lldap-bind
key: password
- name: KEYCAPE_RAPP_QONTO_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: keycape-rapp-qonto-client
key: client-secret
- name: KEYCAPE_SECRETS_ENGINE_APPROVAL_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: keycape-secrets-engine-approval-client
key: client-secret
- name: KEYCAPE_APPROVAL_ENGINE_OPERATOR_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: keycape-approval-engine-operator-client
key: client-secret
# NK-WP-0056-T06 (2026-11-04, founder-run): SecurityCase nk-bind-executor-approval
# (oidc-service-client 0.2); delivered by ESO, CCR-2026-0060. Bound live with the
# client's registration in one rollout (sso-mfa/k8s/bounded-executor/T06-PILOT-RUNBOOK.md section 4).
- name: KEYCAPE_NK_BIND_EXECUTOR_APPROVAL_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: keycape-nk-bind-executor-approval-client
key: client-secret
volumeMounts:
- name: authentication-policy
mountPath: /var/lib/keycape-policy
- name: factor-token
mountPath: /etc/keycape-factor
readOnly: true
# 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: /readyz
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3
resources:
requests:
cpu: "5m"
memory: "32Mi"
limits:
cpu: "200m"
memory: "128Mi"
volumes:
- name: authentication-policy
persistentVolumeClaim:
claimName: keycape-authentication-policy
- name: factor-token
secret:
secretName: keycape-factor-read
defaultMode: 0440
items:
- key: admin-token
path: admin-token
- 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