From 3756208ccd67f782e4a47dff2eda4eac05214f6a Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 24 May 2026 19:55:22 +1000 Subject: [PATCH] benvin/kanidm (#159) Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/159 --- apps/base/kanidm/certificate.yaml | 26 +++ apps/base/kanidm/configmap.yaml | 40 +++++ apps/base/kanidm/gateway.yaml | 30 ++++ apps/base/kanidm/httproute.yaml | 27 +++ apps/base/kanidm/kustomization.yaml | 16 ++ apps/base/kanidm/namespace.yaml | 5 + apps/base/kanidm/poddisruptionbudget.yaml | 15 ++ apps/base/kanidm/rbac.yaml | 37 ++++ apps/base/kanidm/service.yaml | 43 +++++ apps/base/kanidm/serviceaccount.yaml | 9 + apps/base/kanidm/statefulset.yaml | 161 ++++++++++++++++++ apps/base/kanidm/tlsroute.yaml | 21 +++ .../au-syd1/kanidm/kustomization.yaml | 6 + argocd/applicationsets/platform.yaml | 7 + argocd/projects/platform.yaml | 2 + 15 files changed, 445 insertions(+) create mode 100644 apps/base/kanidm/certificate.yaml create mode 100644 apps/base/kanidm/configmap.yaml create mode 100644 apps/base/kanidm/gateway.yaml create mode 100644 apps/base/kanidm/httproute.yaml create mode 100644 apps/base/kanidm/kustomization.yaml create mode 100644 apps/base/kanidm/namespace.yaml create mode 100644 apps/base/kanidm/poddisruptionbudget.yaml create mode 100644 apps/base/kanidm/rbac.yaml create mode 100644 apps/base/kanidm/service.yaml create mode 100644 apps/base/kanidm/serviceaccount.yaml create mode 100644 apps/base/kanidm/statefulset.yaml create mode 100644 apps/base/kanidm/tlsroute.yaml create mode 100644 apps/overlays/au-syd1/kanidm/kustomization.yaml diff --git a/apps/base/kanidm/certificate.yaml b/apps/base/kanidm/certificate.yaml new file mode 100644 index 0000000..01a3000 --- /dev/null +++ b/apps/base/kanidm/certificate.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: kanidm-tls + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +spec: + secretName: kanidm-tls + issuerRef: + kind: ClusterIssuer + name: vault-issuer + commonName: auth.unkin.net + dnsNames: + - auth.unkin.net + - au.auth.unkin.net + - kanidm.k8s.syd1.au.unkin.net + - kanidm.kanidm.svc.cluster.local + - kanidm-0.kanidm-headless.kanidm.svc.cluster.local + - kanidm-1.kanidm-headless.kanidm.svc.cluster.local + - kanidm-2.kanidm-headless.kanidm.svc.cluster.local + privateKey: + algorithm: RSA + size: 4096 diff --git a/apps/base/kanidm/configmap.yaml b/apps/base/kanidm/configmap.yaml new file mode 100644 index 0000000..d74ed18 --- /dev/null +++ b/apps/base/kanidm/configmap.yaml @@ -0,0 +1,40 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: kanidm-config + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +data: + server.toml: | + version = "2" + + domain = "auth.unkin.net" + origin = "https://auth.unkin.net" + bindaddress = "[::]:8443" + db_path = "/data/kanidm.db" + db_arc_size = 2048 + tls_chain = "/data/tls/tls.crt" + tls_key = "/data/tls/tls.key" + log_level = "info" + + [online_backup] + path = "/data/backups/" + schedule = "0 22 * * *" + versions = 7 + + [replication] + origin = "__REPL_ORIGIN__" + bindaddress = "[::]:8444" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: kanidm-repl-certs + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +data: {} diff --git a/apps/base/kanidm/gateway.yaml b/apps/base/kanidm/gateway.yaml new file mode 100644 index 0000000..cf3479a --- /dev/null +++ b/apps/base/kanidm/gateway.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: kanidm + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm + traefik.io/instance: internal + annotations: + external-dns.alpha.kubernetes.io/hostname: kanidm.k8s.syd1.au.unkin.net + external-dns.alpha.kubernetes.io/target: 198.18.200.4 +spec: + gatewayClassName: traefik-internal + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - name: https-passthrough + port: 443 + protocol: TLS + tls: + mode: Passthrough + allowedRoutes: + namespaces: + from: Same diff --git a/apps/base/kanidm/httproute.yaml b/apps/base/kanidm/httproute.yaml new file mode 100644 index 0000000..5c25e08 --- /dev/null +++ b/apps/base/kanidm/httproute.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: kanidm-http-redirect + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +spec: + hostnames: + - kanidm.k8s.syd1.au.unkin.net + - auth.unkin.net + - au.auth.unkin.net + parentRefs: + - name: kanidm + sectionName: http + rules: + - filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 + matches: + - path: + type: PathPrefix + value: / diff --git a/apps/base/kanidm/kustomization.yaml b/apps/base/kanidm/kustomization.yaml new file mode 100644 index 0000000..26430b9 --- /dev/null +++ b/apps/base/kanidm/kustomization.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - namespace.yaml + - serviceaccount.yaml + - rbac.yaml + - certificate.yaml + - configmap.yaml + - service.yaml + - statefulset.yaml + - poddisruptionbudget.yaml + - gateway.yaml + - httproute.yaml + - tlsroute.yaml diff --git a/apps/base/kanidm/namespace.yaml b/apps/base/kanidm/namespace.yaml new file mode 100644 index 0000000..bde2072 --- /dev/null +++ b/apps/base/kanidm/namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: kanidm diff --git a/apps/base/kanidm/poddisruptionbudget.yaml b/apps/base/kanidm/poddisruptionbudget.yaml new file mode 100644 index 0000000..9db7ad1 --- /dev/null +++ b/apps/base/kanidm/poddisruptionbudget.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: kanidm + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +spec: + maxUnavailable: 1 + selector: + matchLabels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm diff --git a/apps/base/kanidm/rbac.yaml b/apps/base/kanidm/rbac.yaml new file mode 100644 index 0000000..73d27a0 --- /dev/null +++ b/apps/base/kanidm/rbac.yaml @@ -0,0 +1,37 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: kanidm-repl + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list"] + - apiGroups: [""] + resources: ["pods/exec"] + verbs: ["create"] + - apiGroups: [""] + resources: ["configmaps"] + resourceNames: ["kanidm-repl-certs"] + verbs: ["get", "patch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: kanidm-repl + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +subjects: + - kind: ServiceAccount + name: kanidm + namespace: kanidm +roleRef: + kind: Role + name: kanidm-repl + apiGroup: rbac.authorization.k8s.io diff --git a/apps/base/kanidm/service.yaml b/apps/base/kanidm/service.yaml new file mode 100644 index 0000000..8de5884 --- /dev/null +++ b/apps/base/kanidm/service.yaml @@ -0,0 +1,43 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: kanidm + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +spec: + type: ClusterIP + ports: + - name: https + port: 8443 + targetPort: https + protocol: TCP + selector: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +--- +apiVersion: v1 +kind: Service +metadata: + name: kanidm-headless + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +spec: + type: ClusterIP + clusterIP: None + ports: + - name: https + port: 8443 + targetPort: https + protocol: TCP + - name: replication + port: 8444 + targetPort: replication + protocol: TCP + selector: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm diff --git a/apps/base/kanidm/serviceaccount.yaml b/apps/base/kanidm/serviceaccount.yaml new file mode 100644 index 0000000..66e0834 --- /dev/null +++ b/apps/base/kanidm/serviceaccount.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kanidm + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm diff --git a/apps/base/kanidm/statefulset.yaml b/apps/base/kanidm/statefulset.yaml new file mode 100644 index 0000000..d68ce44 --- /dev/null +++ b/apps/base/kanidm/statefulset.yaml @@ -0,0 +1,161 @@ +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: kanidm + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +spec: + serviceName: kanidm-headless + replicas: 3 + selector: + matchLabels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm + template: + metadata: + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm + spec: + serviceAccountName: kanidm + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm + topologyKey: kubernetes.io/hostname + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + runAsNonRoot: true + fsGroup: 1000 + initContainers: + - name: config-init + image: ghcr.io/kanidm/server:1.10.3 + command: ["/bin/sh", "-c"] + args: + - | + set -e + REPL_ORIGIN="repl://${POD_NAME}.kanidm-headless.kanidm.svc.cluster.local:8444" + sed "s|__REPL_ORIGIN__|${REPL_ORIGIN}|g" /config-template/server.toml > /config/server.toml + for peer in kanidm-0 kanidm-1 kanidm-2; do + if [ "${peer}" = "${POD_NAME}" ]; then + continue + fi + cert_file="/repl-certs/${peer}" + if [ -s "${cert_file}" ]; then + fqdn="${peer}.kanidm-headless.kanidm.svc.cluster.local" + printf '\n[replication."repl://%s:8444"]\ntype = "mutual-pull"\npartner_cert = "%s"\n' \ + "${fqdn}" "$(cat ${cert_file})" >> /config/server.toml + fi + done + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + volumeMounts: + - name: config-template + mountPath: /config-template + - name: config + mountPath: /config + - name: repl-certs + mountPath: /repl-certs + readOnly: true + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + - name: repl-cert-publisher + image: bitnami/kubectl:1.33 + restartPolicy: Always + command: ["/bin/sh", "-c"] + args: + - | + until kubectl exec "${POD_NAME}" -c kanidm -- /sbin/kanidmd renew-replication-certificate 2>/dev/null | grep -q '^# certificate:'; do + sleep 30 + done + while true; do + cert=$(kubectl exec "${POD_NAME}" -c kanidm -- /sbin/kanidmd renew-replication-certificate 2>/dev/null \ + | grep '^# certificate:' | sed 's/^# certificate: "\(.*\)"$/\1/') + if [ -n "${cert}" ]; then + kubectl patch configmap kanidm-repl-certs \ + --type=merge \ + -p "{\"data\":{\"${POD_NAME}\":\"${cert}\"}}" + fi + sleep 3600 + done + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + containers: + - name: kanidm + image: ghcr.io/kanidm/server:1.10.3 + command: ["/sbin/kanidmd"] + args: ["server", "-c", "/config/server.toml"] + ports: + - name: https + containerPort: 8443 + protocol: TCP + - name: replication + containerPort: 8444 + protocol: TCP + volumeMounts: + - name: data + mountPath: /data + - name: config + mountPath: /config + readOnly: true + - name: tls + mountPath: /data/tls + readOnly: true + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 1Gi + cpu: 500m + readinessProbe: + tcpSocket: + port: 8443 + initialDelaySeconds: 15 + periodSeconds: 10 + livenessProbe: + tcpSocket: + port: 8443 + initialDelaySeconds: 30 + periodSeconds: 30 + volumes: + - name: config-template + configMap: + name: kanidm-config + - name: config + emptyDir: {} + - name: repl-certs + configMap: + name: kanidm-repl-certs + - name: tls + secret: + secretName: kanidm-tls + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: [ReadWriteOnce] + storageClassName: cephrbd-fast-delete + resources: + requests: + storage: 10Gi diff --git a/apps/base/kanidm/tlsroute.yaml b/apps/base/kanidm/tlsroute.yaml new file mode 100644 index 0000000..fb14f65 --- /dev/null +++ b/apps/base/kanidm/tlsroute.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: TLSRoute +metadata: + name: kanidm + namespace: kanidm + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm +spec: + hostnames: + - kanidm.k8s.syd1.au.unkin.net + - auth.unkin.net + - au.auth.unkin.net + parentRefs: + - name: kanidm + sectionName: https-passthrough + rules: + - backendRefs: + - name: kanidm + port: 8443 diff --git a/apps/overlays/au-syd1/kanidm/kustomization.yaml b/apps/overlays/au-syd1/kanidm/kustomization.yaml new file mode 100644 index 0000000..05ef408 --- /dev/null +++ b/apps/overlays/au-syd1/kanidm/kustomization.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../../base/kanidm diff --git a/argocd/applicationsets/platform.yaml b/argocd/applicationsets/platform.yaml index ac5afac..9b538c9 100644 --- a/argocd/applicationsets/platform.yaml +++ b/argocd/applicationsets/platform.yaml @@ -20,6 +20,7 @@ spec: - path: apps/overlays/*/externaldns - path: apps/overlays/*/inteldeviceplugins-system - path: apps/overlays/*/jfrog + - path: apps/overlays/*/kanidm - path: apps/overlays/*/node-feature-discovery - path: apps/overlays/*/puppet - path: apps/overlays/*/purelb @@ -43,6 +44,12 @@ spec: destination: server: https://kubernetes.default.svc namespace: '{{path[3]}}' # Use directory name as namespace + ignoreDifferences: + - group: "" + kind: ConfigMap + name: kanidm-repl-certs + jsonPointers: + - /data syncPolicy: automated: prune: true diff --git a/argocd/projects/platform.yaml b/argocd/projects/platform.yaml index 53e4e28..67d125c 100644 --- a/argocd/projects/platform.yaml +++ b/argocd/projects/platform.yaml @@ -27,6 +27,8 @@ spec: server: https://kubernetes.default.svc - namespace: 'jfrog' server: https://kubernetes.default.svc + - namespace: 'kanidm' + server: https://kubernetes.default.svc - namespace: 'node-feature-discovery' server: https://kubernetes.default.svc - namespace: 'purelb'