Skip to content

Commit

Permalink
Merge pull request #174 from davidfrickert/feat/add-readonly-replicas
Browse files Browse the repository at this point in the history
#171 - Allow setting up read only replicas
  • Loading branch information
jp-gouin authored Oct 9, 2024
2 parents 8f1a567 + 3f666e5 commit 7b0d8ea
Show file tree
Hide file tree
Showing 14 changed files with 721 additions and 10 deletions.
4 changes: 4 additions & 0 deletions .bin/kind-conf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,9 @@ nodes:
hostPort: 30636
- containerPort: 30389
hostPort: 30389
- containerPort: 31389
hostPort: 31389
- containerPort: 31636
hostPort: 31636
- role: worker
- role: worker
113 changes: 113 additions & 0 deletions .bin/readonly.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
customSchemaFiles:
00-memberof.ldif: |-
# Load memberof module
dn: cn=module,cn=config
cn: module
objectClass: olcModuleList
olcModuleLoad: memberof
olcModulePath: /opt/bitnami/openldap/lib/openldap
dn: olcOverlay=memberof,olcDatabase={2}mdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcMemberOf
olcOverlay: memberof
olcMemberOfRefint: TRUE
10_owncloud_schema.ldif: |-
# This LDIF files describes the ownCloud schema and can be used to
# add two optional attributes: ownCloudQuota and ownCloudUUID
# The ownCloudUUID is used to store a unique, non-reassignable, persistent identifier for users and groups
dn: cn=owncloud,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: owncloud
olcObjectIdentifier: ownCloudOid 1.3.6.1.4.1.39430
olcAttributeTypes: ( ownCloudOid:1.1.1 NAME 'ownCloudQuota'
DESC 'User Quota (e.g. 2 GB)'
EQUALITY caseExactMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: ( ownCloudOid:1.1.2 NAME 'ownCloudUUID'
DESC 'A non-reassignable and persistent account ID)'
EQUALITY uuidMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.1.16.1 SINGLE-VALUE )
olcObjectClasses: ( ownCloudOid:1.2.1 NAME 'ownCloud'
DESC 'ownCloud LDAP Schema'
AUXILIARY
MAY ( ownCloudQuota $ ownCloudUUID ) )
customLdifFiles:
00-root.ldif: |-
# Root creation
dn: dc=example,dc=org
objectClass: dcObject
objectClass: organization
o: Example, Inc
01-default-group.ldif: |-
dn: cn=myGroup,dc=example,dc=org
cn: myGroup
gidnumber: 500
objectclass: posixGroup
objectclass: top
02-default-user.ldif: |-
dn: cn=Jean Dupond,dc=example,dc=org
cn: Jean Dupond
gidnumber: 500
givenname: Jean
homedirectory: /home/users/jdupond
objectclass: inetOrgPerson
objectclass: posixAccount
objectClass: ownCloud
objectclass: top
sn: Dupond
uid: jdupond
uidnumber: 1000
userpassword: {MD5}KOULhzfBhPTq9k7a9XfCGw==
03-test-memberof.ldif: |-
dn: ou=Group,dc=example,dc=org
objectclass: organizationalUnit
ou: Group
dn: ou=People,dc=example,dc=org
objectclass: organizationalUnit
ou: People
dn: uid=test1,ou=People,dc=example,dc=org
objectclass: account
uid: test1
dn: cn=testgroup,ou=Group,dc=example,dc=org
objectclass: groupOfNames
cn: testgroup
member: uid=test1,ou=People,dc=example,dc=org
persistence:
accessModes:
- ReadWriteOnce
enabled: true
size: 1Gi
ltb-passwd:
enabled : false
phpldapadmin:
enabled: false
replicaCount: 3
readOnlyReplicaCount: 1
replication:
clusterName: cluster.local
enabled: true
interval: "00:00:00:10"
retry: 60
starttls: critical
timeout: 1
tls_reqcert: never
initTLSSecret:
tls_enabled: true
secret: "custom-cert"
service:
ldapPortNodePort: 30389
sslLdapPortNodePort: 30636
type: NodePort
serviceReadOnly:
ldapPortNodePort: 31389
sslLdapPortNodePort: 31636
type: NodePort
17 changes: 17 additions & 0 deletions .bin/user2.ldif
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
dn: uid=ro,ou=users,dc=example,dc=org
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: posixAccount
objectClass: top
uid: ro
givenName: u
sn: u
cn: u
displayName: U
description: User to test that readonly cluster cannot be used to add more users.
mail: u@example.org
uidNumber: 21000
gidNumber: 31000
homeDirectory: /home/u
userPassword:: p1NTSEF9TXJEcXpFNGdKbXZxbVRVTGhvWEZ1VzJBbkV3NWFLK3J3WTIvbHc9PQ==
59 changes: 59 additions & 0 deletions .github/workflows/ci-readonly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# WIP!
name: Test readonly replica
on:
workflow_call:
jobs:
qualif:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v1
- name: Run custom action
# Use the location in the repository (without action.yml)
uses: ./.github/actions/setup
with:
install-chaos: false
- name: setup certs
shell: bash
run: |
openssl req -x509 -newkey rsa:4096 -nodes -subj '/CN=example.com' -keyout tls.key -out tls.crt -days 365
cp tls.crt ca.crt
kubectl create secret generic custom-cert --from-file=./tls.crt --from-file=./tls.key --from-file=./ca.crt
- name: deploy openldap-stack-ha
shell: bash
run: |
cd "$GITHUB_WORKSPACE"
helm install openldap-stack-ha -f .bin/readonly.yaml .
kubectl rollout status sts openldap-stack-ha
- name: verify deployment
shell: bash
run: |
echo "test access to openldap database"
sleep 10
LDAPTLS_REQCERT=never ldapsearch -x -D 'cn=admin,dc=example,dc=org' -w Not@SecurePassw0rd -H ldaps://localhost:30636 -b 'dc=example,dc=org'
- name: verify certs
shell: bash
run: |
echo "verify certificate"
echo | openssl s_client -showcerts -servername example.com -connect localhost:30636 2>/dev/null | openssl x509 -inform pem -noout -text > /tmp/test-cert.txt
if ! grep -q "CN = example.com" /tmp/test-cert.txt; then echo exit 1; fi
- name: test write on main cluster
shell: bash
run: |
echo "Write test to openldap database"
LDAPTLS_REQCERT=never ldapadd -x -D 'cn=admin,dc=example,dc=org' -w Not@SecurePassw0rd -H ldaps://localhost:30636 -f .bin/user.ldif
LDAPTLS_REQCERT=never ldapsearch -o nettimeout=20 -x -D 'cn=admin,dc=example,dc=org' -w Not@SecurePassw0rd -H ldaps://localhost:30636 -b 'dc=example,dc=org' > /tmp/test-write.txt
if ! grep "Einstein" /tmp/test-write.txt; then echo 'no Einstein entry found' ; fi
if ! grep "objectClass: ownCloud" /tmp/test-write.txt; then echo 'no ownCloud entry found'; fi
- name: test memberOf on main cluster
shell: bash
run: |
echo "MemberOf test to openldap database"
LDAPTLS_REQCERT=never ldapsearch -o nettimeout=20 -x -D 'cn=admin,dc=example,dc=org' -w Not@SecurePassw0rd -H ldaps://localhost:30636 -b 'dc=example,dc=org' "(memberOf=cn=testgroup,ou=Group,dc=example,dc=org)" > /tmp/test-write.txt
if [ $(grep "numResponses" /tmp/test-write.txt | cut -d ":" -f 2 | tr -d ' ') -ne 2 ]; then exit 1 ; fi
if ! grep -q "uid=test1,ou=People,dc=example,dc=org" /tmp/test-write.txt; then echo exit 1; fi
- name: test write on readonly replica
shell: bash
run: |
echo "Write test to openldap readonly replica"
if LDAPTLS_REQCERT=never ldapadd -x -D 'cn=admin,dc=example,dc=org' -w Not@SecurePassw0rd -H ldaps://localhost:31636 -f .bin/user2.ldif; then echo exit 1; fi
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ jobs:
call-ci-other:
uses: ./.github/workflows/ci-other.yml
call-ci-ha:
uses: ./.github/workflows/ci-ha.yml
uses: ./.github/workflows/ci-ha.yml
call-ci-readonly:
uses: ./.github/workflows/ci-readonly.yml
1 change: 1 addition & 0 deletions templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

** Please be patient while the chart is being deployed **

OpenLDAP-Stack-HA has been installed. You can access the server from within the k8s cluster using:
Expand Down
19 changes: 14 additions & 5 deletions templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,14 @@ Cannot return list => return string comma separated
*/}}
{{- define "openldap.builtinSchemaFiles" -}}
{{- $schemas := "" -}}
{{- if .Values.replication.enabled -}}
{{- $schemas = "syncprov,serverid,csyncprov,rep,bsyncprov,brep,acls" -}}
{{- $context := index . "context" -}}
{{- $mode := index . "mode" -}}
{{- if $context.Values.replication.enabled -}}
{{- if $mode -}}
{{- $schemas = "brep,readonly" -}}
{{- else -}}
{{- $schemas = "syncprov,serverid,csyncprov,rep,bsyncprov,brep,acls" -}}
{{- end -}}
{{- else -}}
{{- $schemas = "acls" -}}
{{- end -}}
Expand All @@ -187,8 +193,9 @@ Return the list of custom schema files to use
Cannot return list => return string comma separated
*/}}
{{- define "openldap.customSchemaFiles" -}}
{{- $context := index . "context" -}}
{{- $schemas := "" -}}
{{- $schemas := ((join "," (.Values.customSchemaFiles | keys | sortAlpha)) | replace ".ldif" "") -}}
{{- $schemas := ((join "," ($context.Values.customSchemaFiles | keys | sortAlpha)) | replace ".ldif" "") -}}
{{- print $schemas -}}
{{- end -}}

Expand All @@ -197,6 +204,8 @@ Return the list of all schema files to use
Cannot return list => return string comma separated
*/}}
{{- define "openldap.schemaFiles" -}}
{{- $context := index . "context" -}}
{{- $mode := index . "mode" -}}
{{- $schemas := (include "openldap.builtinSchemaFiles" .) -}}
{{- $custom_schemas := (include "openldap.customSchemaFiles" .) -}}
{{- if gt (len $custom_schemas) 0 -}}
Expand Down Expand Up @@ -236,7 +245,7 @@ Return the server name
{{- end -}}

{{/*
Return the bdmin indDN
Return the admin bindDN
*/}}
{{- define "global.bindDN" -}}
{{- printf "cn=%s,%s" .Values.global.adminUser (include "global.baseDomain" .) -}}
Expand All @@ -254,4 +263,4 @@ Return the ldap port
*/}}
{{- define "global.ldapPort" -}}
{{- printf "%d" .Values.global.ldapPort -}}
{{- end -}}
{{- end -}}
1 change: 0 additions & 1 deletion templates/configmap-env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ metadata:
{{- end }}
data:
LDAP_ROOT: {{ include "global.baseDomain" . }}
LDAP_EXTRA_SCHEMAS: {{ print "cosine,inetorgperson,nis," (include "openldap.schemaFiles" .) }}
{{- if .Values.users }}
LDAP_USERS: {{ .Values.users }}
{{- end }}
Expand Down
20 changes: 20 additions & 0 deletions templates/configmap-readonly.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{- if (gt (.Values.readOnlyReplicaCount | int) 0) }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "openldap.fullname" . }}-readonly
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:
readonly.ldif: |
dn: olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcReadOnly
olcReadOnly: TRUE
{{- end }}
58 changes: 58 additions & 0 deletions templates/service-readonly.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{{- if (gt (.Values.readOnlyReplicaCount | int) 0) }}
apiVersion: v1
kind: Service
metadata:
{{- if .Values.serviceReadOnly.annotations }}
annotations:
{{ toYaml .Values.serviceReadOnly.annotations | indent 4 }}
{{- end }}
name: {{ template "openldap.fullname" . }}-readonly
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/component: {{ 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.serviceReadOnly.type }}
ipFamilyPolicy: {{ .Values.serviceReadOnly.ipFamilyPolicy }}
{{- if and (eq .Values.serviceReadOnly.type "LoadBalancer") .Values.serviceReadOnly.loadBalancerIP }}
loadBalancerIP: {{ .Values.serviceReadOnly.loadBalancerIP }}
{{- end }}
{{- if and (eq .Values.serviceReadOnly.type "LoadBalancer") .Values.serviceReadOnly.loadBalancerSourceRanges }}
loadBalancerSourceRanges: {{ toYaml .Values.serviceReadOnly.loadBalancerSourceRanges | nindent 4 }}
{{- end }}
{{- if and (eq .Values.serviceReadOnly.type "ClusterIP") .Values.serviceReadOnly.clusterIP }}
clusterIP: {{ .Values.serviceReadOnly.clusterIP }}
{{- end }}
ports:
{{- if .Values.serviceReadOnly.enableLdapPort }}
- name: ldap-port
protocol: TCP
port: {{ .Values.global.ldapPort }}
targetPort: ldap-port
{{- if and (or (eq .Values.serviceReadOnly.type "NodePort") (eq .Values.serviceReadOnly.type "LoadBalancer")) (not (empty .Values.serviceReadOnly.ldapPortNodePort)) }}
nodePort: {{ .Values.serviceReadOnly.ldapPortNodePort }}
{{- else if eq .Values.serviceReadOnly.type "ClusterIP" }}
nodePort: null
{{- end }}
{{- end }}
{{- if .Values.serviceReadOnly.enableSslLdapPort }}
- name: ssl-ldap-port
protocol: TCP
port: {{ .Values.global.sslLdapPort }}
targetPort: ssl-ldap-port
{{- if and (or (eq .Values.serviceReadOnly.type "NodePort") (eq .Values.serviceReadOnly.type "LoadBalancer")) (not (empty .Values.serviceReadOnly.sslLdapPortNodePort)) }}
nodePort: {{ .Values.serviceReadOnly.sslLdapPortNodePort }}
{{- else if eq .Values.serviceReadOnly.type "ClusterIP" }}
nodePort: null
{{- end }}
{{- end }}
sessionAffinity: {{ .Values.service.sessionAffinity }}
selector:
app.kubernetes.io/component: {{ template "openldap.fullname" . }}-readonly
release: {{ .Release.Name }}
{{- end }}
Loading

0 comments on commit 7b0d8ea

Please sign in to comment.