Adding openldap + ldap user manager

This commit is contained in:
plm
2024-12-02 13:57:37 +01:00
parent 519fb80ee7
commit ba9a971964
38 changed files with 1984 additions and 29 deletions

View File

@@ -0,0 +1,20 @@
OpenLDAP has been installed. You can access the server from within the k8s cluster using:
{{ template "openldap.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.ldapPort }}
You can access the LDAP adminPassword and configPassword using:
kubectl get secret --namespace {{ .Release.Namespace }} {{ template "openldap.secretName" . }} -o jsonpath="{.data.LDAP_ADMIN_PASSWORD}" | base64 --decode; echo
kubectl get secret --namespace {{ .Release.Namespace }} {{ template "openldap.secretName" . }} -o jsonpath="{.data.LDAP_CONFIG_PASSWORD}" | base64 --decode; echo
You can access the LDAP service, from within the cluster (or with kubectl port-forward) with a command like (replace password and domain):
ldapsearch -x -H ldap://{{ template "openldap.fullname" . }}-service.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.ldapPort }} -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w $LDAP_ADMIN_PASSWORD
Test server health using Helm test:
helm test {{ .Release.Name }}
You can also consider installing the helm chart for phpldapadmin to manage this instance of OpenLDAP, or install Apache Directory Studio, and connect using kubectl port-forward.

View File

@@ -0,0 +1,74 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "openldap.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for statefulset.
*/}}
{{- define "statefulset.apiVersion" -}}
{{- if semverCompare "<1.14-0" .Capabilities.KubeVersion.GitVersion -}}
{{- print "apps/v1beta1" -}}
{{- else -}}
{{- print "apps/v1" -}}
{{- end -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "openldap.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "openldap.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Generate chart secret name
*/}}
{{- define "openldap.secretName" -}}
{{ default (include "openldap.fullname" .) .Values.existingSecret }}
{{- end -}}
{{/*
Generate replication services list
*/}}
{{- define "replicalist" -}}
{{- $name := (include "openldap.fullname" .) }}
{{- $namespace := .Release.Namespace }}
{{- $cluster := .Values.replication.clusterName }}
{{- $nodeCount := .Values.replicaCount | int }}
{{- range $index0 := until $nodeCount -}}
{{- $index1 := $index0 | add1 -}}
'ldap://{{ $name }}-{{ $index0 }}.{{ $name }}-headless.{{ $namespace }}.svc.{{ $cluster }}'{{ if ne $index1 $nodeCount }},{{ end }}
{{- end -}}
{{- end -}}
{{/*
Renders a value that contains template.
Usage:
{{ include "openldap.tplValue" ( dict "value" .Values.path.to.the.Value "context" $) }}
*/}}
{{- define "openldap.tplValue" -}}
{{- if typeIs "string" .value }}
{{- tpl .value .context }}
{{- else }}
{{- tpl (.value | toYaml) .context }}
{{- end }}
{{- end -}}

View File

@@ -0,0 +1,23 @@
#
# A ConfigMap spec for openldap slapd that map directly to files under
# /container/service/slapd/assets/config/bootstrap/ldif/custom
#
{{- if .Values.customLdifFiles }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "openldap.fullname" . }}-customldif
labels:
app: {{ template "openldap.name" . }}
chart: {{ template "openldap.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
data:
{{- range $key, $val := .Values.customLdifFiles }}
{{ $key }}: |-
{{ $val | indent 4}}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,26 @@
#
# A ConfigMap spec for openldap slapd that map directly to env variables in the Pod.
# List of environment variables supported is from the docker image:
# https://github.com/osixia/docker-openldap#beginner-guide
# Note that passwords are defined as secrets
#
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "openldap.fullname" . }}-env
labels:
app: {{ template "openldap.name" . }}
chart: {{ template "openldap.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
data:
{{ toYaml .Values.env | indent 2 }}
{{- if .Values.replication.enabled }}
LDAP_REPLICATION: "true"
LDAP_REPLICATION_CONFIG_SYNCPROV: "binddn=\"cn=admin,cn=config\" bindmethod=simple credentials=$LDAP_CONFIG_PASSWORD searchbase=\"cn=config\" type=refreshAndPersist retry=\"{{.Values.replication.retry }} +\" timeout={{.Values.replication.timeout }} starttls={{.Values.replication.starttls }} tls_reqcert={{.Values.replication.tls_reqcert }}"
LDAP_REPLICATION_DB_SYNCPROV: "binddn=\"cn=admin,$LDAP_BASE_DN\" bindmethod=simple credentials=$LDAP_ADMIN_PASSWORD searchbase=\"$LDAP_BASE_DN\" type=refreshAndPersist interval={{.Values.replication.interval }} retry=\"{{.Values.replication.retry }} +\" timeout={{.Values.replication.timeout }} starttls={{.Values.replication.starttls }} tls_reqcert={{.Values.replication.tls_reqcert }}"
LDAP_REPLICATION_HOSTS: "#PYTHON2BASH:[{{ template "replicalist" . }}]"
{{- end }}

View File

@@ -0,0 +1,17 @@
{{ if not .Values.existingSecret }}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "openldap.fullname" . }}-ltb-passwd
labels:
app: {{ template "openldap.name" . }}
chart: {{ template "openldap.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
type: Opaque
data:
LDAP_ADMIN_PASSWORD: {{ .Values.adminPassword | b64enc | quote }}
{{ end }}

View File

@@ -0,0 +1,18 @@
{{ if not .Values.existingSecret }}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "openldap.fullname" . }}
labels:
app: {{ template "openldap.name" . }}
chart: {{ template "openldap.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
type: Opaque
data:
LDAP_ADMIN_PASSWORD: {{ .Values.adminPassword | b64enc | quote }}
LDAP_CONFIG_PASSWORD: {{ .Values.configPassword | b64enc | quote }}
{{ end }}

View File

@@ -0,0 +1,47 @@
apiVersion: v1
kind: Service
metadata:
{{- if .Values.service.annotations }}
annotations:
{{ toYaml .Values.service.annotations | indent 4 }}
{{- end }}
name: {{ template "openldap.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
app: {{ template "openldap.fullname" . }}
chart: {{ template "openldap.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
{{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
{{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerSourceRanges }}
loadBalancerSourceRanges: {{ toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }}
{{- end }}
ports:
- name: ldap-port
protocol: TCP
port: {{ .Values.service.ldapPort }}
targetPort: ldap-port
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort)) }}
nodePort: {{ .Values.service.ldapPortNodePort }}
{{- else if eq .Values.service.type "ClusterIP" }}
nodePort: null
{{- end }}
- name: ssl-ldap-port
protocol: TCP
port: {{ .Values.service.sslLdapPort }}
targetPort: ssl-ldap-port
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort)) }}
nodePort: {{ .Values.service.sslLdapPortNodePort }}
{{- else if eq .Values.service.type "ClusterIP" }}
nodePort: null
{{- end }}
selector:
app: {{ template "openldap.fullname" . }}
release: {{ .Release.Name }}

View File

@@ -0,0 +1,153 @@
apiVersion: {{ template "statefulset.apiVersion" . }}
kind: StatefulSet
metadata:
name: {{ template "openldap.fullname" . }}
labels:
app: {{ template "openldap.fullname" . }}
chart: {{ template "openldap.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
spec:
replicas: {{ .Values.replicaCount }}
{{- if .Values.strategy }}
strategy:
{{ toYaml .Values.strategy | indent 4 }}
{{- end }}
selector:
matchLabels:
app: {{ template "openldap.fullname" . }}
release: {{ .Release.Name }}
serviceName: {{ template "openldap.fullname" . }}-headless
template:
metadata:
annotations:
checksum/configmap-env: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }}
{{- if .Values.customLdifFiles}}
checksum/configmap-customldif: {{ include (print $.Template.BasePath "/configmap-customldif.yaml") . | sha256sum }}
{{- end }}
{{- if .Values.podAnnotations}}
{{ toYaml .Values.podAnnotations | indent 8}}
{{- end }}
labels:
app: {{ template "openldap.fullname" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args:
- -l
- {{ .Values.logLevel }}
{{- if .Values.customLdifFiles }}
- --copy-service
{{- end }}
ports:
- name: ldap-port
containerPort: 389
- name: ssl-ldap-port
containerPort: 636
envFrom:
- configMapRef:
name: {{ template "openldap.fullname" . }}-env
- secretRef:
name: {{ template "openldap.secretName" . }}
volumeMounts:
- name: data
mountPath: /var/lib/ldap
subPath: data
- name: data
mountPath: /etc/ldap/slapd.d
subPath: config-data
- name: data
mountPath: /container/service/slapd/assets/certs
{{- if .Values.customLdifFiles }}
- name: custom-ldif-files
mountPath: /container/service/slapd/assets/config/bootstrap/ldif/custom
{{- end }}
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
#- name: HOSTNAME
# value: $(POD_NAME).{{ template "openldap.fullname" . }}-headless
{{- if .Values.tls.enabled }}
- name: LDAP_TLS_CRT_FILENAME
value: tls.crt
- name: LDAP_TLS_KEY_FILENAME
value: tls.key
{{- if .Values.tls.CA.enabled }}
- name: LDAP_TLS_CA_CRT_FILENAME
value: ca.crt
{{- end }}
{{- end }}
livenessProbe:
tcpSocket:
port: ldap-port
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 10
readinessProbe:
tcpSocket:
port: ldap-port
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 10
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app.kubernetes.io/component: {{ .Release.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
imagePullSecrets:
- name: {{ .Values.image.pullSecret }}
{{- if .Values.customLdifFiles }}
volumes:
- name: custom-ldif-files
configMap:
name: {{ template "openldap.fullname" . }}-customldif
{{- end }}
{{- if .Values.persistence.enabled }}
volumeClaimTemplates:
- metadata:
name: data
annotations:
{{- range $key, $value := .Values.persistence.annotations }}
{{ $key }}: {{ $value }}
{{- end }}
spec:
accessModes:
{{- range .Values.persistence.accessModes }}
- {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.size | quote }}
{{- if .Values.persistence.storageClass }}
{{- if (eq "-" .Values.persistence.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: "{{ .Values.persistence.storageClass }}"
{{- end }}
{{- end }}
{{- else }}
- name: data
emptyDir: {}
{{- end }}

View File

@@ -0,0 +1,20 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "openldap.fullname" . }}-headless
labels:
app: {{ template "openldap.fullname" . }}
chart: {{ template "openldap.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
ports:
- port: {{ .Values.service.ldapPort }}
name: ldap-port
targetPort: ldap-port
clusterIP: None
selector:
app: {{ template "openldap.fullname" . }}
release: {{ .Release.Name }}
type: ClusterIP
sessionAffinity: None

View File

@@ -0,0 +1,50 @@
{{- if .Values.test.enabled -}}
apiVersion: v1
kind: Pod
metadata:
name: "{{ template "openldap.fullname" . }}-test-{{ randAlphaNum 5 | lower }}"
labels:
app: {{ template "openldap.name" . }}
chart: {{ template "openldap.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
annotations:
"helm.sh/hook": test-success
spec:
initContainers:
- name: test-framework
image: {{ .Values.test.image.repository }}:{{ .Values.test.image.tag }}
command:
- "bash"
- "-c"
- |
set -ex
# copy bats to tools dir
cp -R /usr/local/libexec/ /tools/bats/
volumeMounts:
- mountPath: /tools
name: tools
containers:
- name: {{ .Release.Name }}-test
image: {{ .Values.test.image.repository }}:{{ .Values.test.image.tag }}
envFrom:
- secretRef:
name: {{ template "openldap.secretName" . }}
command: ["/tools/bats/bats", "-t", "/tests/run.sh"]
volumeMounts:
- mountPath: /tests
name: tests
readOnly: true
- mountPath: /tools
name: tools
volumes:
- name: tests
configMap:
name: {{ template "openldap.fullname" . }}-tests
- name: tools
emptyDir: {}
restartPolicy: Never
{{- end -}}

View File

@@ -0,0 +1,22 @@
{{- if .Values.test.enabled -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "openldap.fullname" . }}-tests
labels:
app: {{ template "openldap.name" . }}
chart: {{ template "openldap.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- if .Values.extraLabels }}
{{ toYaml .Values.extraLabels | indent 4 }}
{{- end }}
data:
run.sh: |-
@test "Testing connecting to slapd server" {
# Ideally, this should be in the docker image, but there is not a generic image we can use
# with bats and ldap-utils installed. It is not worth for now to push an image for this.
apt-get update && apt-get install -y ldap-utils
ldapsearch -x -H ldap://{{ template "openldap.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.ldapPort }} -b "dc=example,dc=org" -D "cn=admin,dc=example,dc=org" -w $LDAP_ADMIN_PASSWORD
}
{{- end -}}