feat(kanidm): automate replication cert exchange via native sidecar
Add a native sidecar (bitnami/kubectl, restartPolicy: Always) that runs kanidmd renew-replication-certificate on each pod and patches the result into the kanidm-repl-certs ConfigMap (certs are public keys, not secrets). The config-init init container reads peer certs from the ConfigMap at startup, building the replication stanza automatically — no manual cert exchange required after first deploy. Add RBAC (Role + RoleBinding) granting the kanidm service account pods/exec and configmap patch permissions scoped to the kanidm namespace.
This commit is contained in:
@@ -29,34 +29,15 @@ data:
|
|||||||
origin = "__REPL_ORIGIN__"
|
origin = "__REPL_ORIGIN__"
|
||||||
bindaddress = "[::]:8444"
|
bindaddress = "[::]:8444"
|
||||||
---
|
---
|
||||||
# kanidm-repl-peers is initially empty.
|
|
||||||
#
|
|
||||||
# After first deployment, exchange replication certificates:
|
|
||||||
# kubectl exec -n kanidm kanidm-0 -- kanidmd show-replication-certificate
|
|
||||||
# kubectl exec -n kanidm kanidm-1 -- kanidmd show-replication-certificate
|
|
||||||
# kubectl exec -n kanidm kanidm-2 -- kanidmd show-replication-certificate
|
|
||||||
#
|
|
||||||
# Then populate peers.toml with all nodes' certs and restart pods.
|
|
||||||
# Example peers.toml content:
|
|
||||||
#
|
|
||||||
# [replication."repl://kanidm-0.kanidm-headless.kanidm.svc.cluster.local:8444"]
|
|
||||||
# type = "mutual-pull"
|
|
||||||
# partner_cert = "<base64-cert-from-kanidm-0>"
|
|
||||||
#
|
|
||||||
# [replication."repl://kanidm-1.kanidm-headless.kanidm.svc.cluster.local:8444"]
|
|
||||||
# type = "mutual-pull"
|
|
||||||
# partner_cert = "<base64-cert-from-kanidm-1>"
|
|
||||||
#
|
|
||||||
# [replication."repl://kanidm-2.kanidm-headless.kanidm.svc.cluster.local:8444"]
|
|
||||||
# type = "mutual-pull"
|
|
||||||
# partner_cert = "<base64-cert-from-kanidm-2>"
|
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: kanidm-repl-peers
|
name: kanidm-repl-certs
|
||||||
namespace: kanidm
|
namespace: kanidm
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kanidm
|
app.kubernetes.io/name: kanidm
|
||||||
app.kubernetes.io/instance: kanidm
|
app.kubernetes.io/instance: kanidm
|
||||||
data:
|
data:
|
||||||
peers.toml: ""
|
kanidm-0: ""
|
||||||
|
kanidm-1: ""
|
||||||
|
kanidm-2: ""
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ kind: Kustomization
|
|||||||
resources:
|
resources:
|
||||||
- namespace.yaml
|
- namespace.yaml
|
||||||
- serviceaccount.yaml
|
- serviceaccount.yaml
|
||||||
|
- rbac.yaml
|
||||||
- certificate.yaml
|
- certificate.yaml
|
||||||
- configmap.yaml
|
- configmap.yaml
|
||||||
- service.yaml
|
- service.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
|
||||||
@@ -43,9 +43,17 @@ spec:
|
|||||||
set -e
|
set -e
|
||||||
REPL_ORIGIN="repl://${POD_NAME}.kanidm-headless.kanidm.svc.cluster.local:8444"
|
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
|
sed "s|__REPL_ORIGIN__|${REPL_ORIGIN}|g" /config-template/server.toml > /config/server.toml
|
||||||
if [ -s /repl-peers/peers.toml ]; then
|
for peer in kanidm-0 kanidm-1 kanidm-2; do
|
||||||
cat /repl-peers/peers.toml >> /config/server.toml
|
if [ "${peer}" = "${POD_NAME}" ]; then
|
||||||
fi
|
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:
|
env:
|
||||||
- name: POD_NAME
|
- name: POD_NAME
|
||||||
valueFrom:
|
valueFrom:
|
||||||
@@ -56,11 +64,39 @@ spec:
|
|||||||
mountPath: /config-template
|
mountPath: /config-template
|
||||||
- name: config
|
- name: config
|
||||||
mountPath: /config
|
mountPath: /config
|
||||||
- name: repl-peers
|
- name: repl-certs
|
||||||
mountPath: /repl-peers
|
mountPath: /repl-certs
|
||||||
|
readOnly: true
|
||||||
securityContext:
|
securityContext:
|
||||||
allowPrivilegeEscalation: false
|
allowPrivilegeEscalation: false
|
||||||
readOnlyRootFilesystem: true
|
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:
|
containers:
|
||||||
- name: kanidm
|
- name: kanidm
|
||||||
image: ghcr.io/kanidm/server:1.10.3
|
image: ghcr.io/kanidm/server:1.10.3
|
||||||
@@ -108,9 +144,9 @@ spec:
|
|||||||
name: kanidm-config
|
name: kanidm-config
|
||||||
- name: config
|
- name: config
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
- name: repl-peers
|
- name: repl-certs
|
||||||
configMap:
|
configMap:
|
||||||
name: kanidm-repl-peers
|
name: kanidm-repl-certs
|
||||||
- name: tls
|
- name: tls
|
||||||
secret:
|
secret:
|
||||||
secretName: kanidm-tls
|
secretName: kanidm-tls
|
||||||
|
|||||||
Reference in New Issue
Block a user