diff --git a/apps/base/kanidm/README.md b/apps/base/kanidm/README.md new file mode 100644 index 0000000..8861d79 --- /dev/null +++ b/apps/base/kanidm/README.md @@ -0,0 +1,32 @@ +# kanidm + +Single-replica kanidm identity server deployment. + +## Initial setup + +After the pod starts for the first time, generate the admin and idm_admin credentials: + +```bash +kubectl exec -n kanidm kanidm-0 -- /sbin/kanidmd recover-account admin +kubectl exec -n kanidm kanidm-0 -- /sbin/kanidmd recover-account idm_admin +``` + +## Adding replication + +If replication is needed in the future: + +1. Scale the StatefulSet to 3 replicas and add `podAntiAffinity` to spread across nodes. +2. Add a `[replication]` section to `configmap.yaml` per pod (origin is pod-specific: + `repl://kanidm-N.kanidm-headless.kanidm.svc.cluster.local:8444`). +3. Add the replication port (8444) back to the StatefulSet container ports and headless service. +4. Restore `rbac.yaml` for the cert-publisher sidecar, or exchange certificates manually: + +```bash +# On each pod, get its replication certificate +kubectl exec -n kanidm kanidm-0 -- /sbin/kanidmd renew-replication-certificate + +# Add each peer's certificate to the other pods' configs under: +# [replication."repl://:8444"] +# type = "mutual-pull" +# partner_cert = "" +``` diff --git a/apps/base/kanidm/configmap.yaml b/apps/base/kanidm/configmap.yaml deleted file mode 100644 index d74ed18..0000000 --- a/apps/base/kanidm/configmap.yaml +++ /dev/null @@ -1,40 +0,0 @@ ---- -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/kustomization.yaml b/apps/base/kanidm/kustomization.yaml index 26430b9..05456f2 100644 --- a/apps/base/kanidm/kustomization.yaml +++ b/apps/base/kanidm/kustomization.yaml @@ -5,12 +5,23 @@ 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 + +configMapGenerator: + - name: kanidm-config + namespace: kanidm + options: + disableNameSuffixHash: true + labels: + app.kubernetes.io/name: kanidm + app.kubernetes.io/instance: kanidm + files: + - server-0.toml=resources/server-0.toml + - server-1.toml=resources/server-1.toml + - server-2.toml=resources/server-2.toml diff --git a/apps/base/kanidm/resources/server-0.toml b/apps/base/kanidm/resources/server-0.toml new file mode 100644 index 0000000..8aba04e --- /dev/null +++ b/apps/base/kanidm/resources/server-0.toml @@ -0,0 +1,19 @@ +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://kanidm-0.kanidm-headless.kanidm.svc.cluster.local:8444" +bindaddress = "[::]:8444" diff --git a/apps/base/kanidm/resources/server-1.toml b/apps/base/kanidm/resources/server-1.toml new file mode 100644 index 0000000..44aa47d --- /dev/null +++ b/apps/base/kanidm/resources/server-1.toml @@ -0,0 +1,19 @@ +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://kanidm-1.kanidm-headless.kanidm.svc.cluster.local:8444" +bindaddress = "[::]:8444" diff --git a/apps/base/kanidm/resources/server-2.toml b/apps/base/kanidm/resources/server-2.toml new file mode 100644 index 0000000..808f436 --- /dev/null +++ b/apps/base/kanidm/resources/server-2.toml @@ -0,0 +1,19 @@ +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://kanidm-2.kanidm-headless.kanidm.svc.cluster.local:8444" +bindaddress = "[::]:8444" diff --git a/apps/base/kanidm/statefulset.yaml b/apps/base/kanidm/statefulset.yaml index 1dd852c..397e1e4 100644 --- a/apps/base/kanidm/statefulset.yaml +++ b/apps/base/kanidm/statefulset.yaml @@ -36,24 +36,10 @@ spec: fsGroup: 1000 initContainers: - name: config-init - image: kanidm/server:1.10.3 + image: busybox:1.36 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 + - cp "/config-template/server-${POD_NAME##*-}.toml" /config/server.toml env: - name: POD_NAME valueFrom: @@ -62,41 +48,12 @@ spec: volumeMounts: - name: config-template mountPath: /config-template + readOnly: true - 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: kanidm/server:1.10.3 @@ -144,9 +101,6 @@ spec: name: kanidm-config - name: config emptyDir: {} - - name: repl-certs - configMap: - name: kanidm-repl-certs - name: tls secret: secretName: kanidm-tls