Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#171 - Allow setting up read only replicas #174

Merged
merged 24 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
329ec14
split LDAP_EXTRA_SCHEMAS from main configmap
davidfrickert Jun 20, 2024
e31a2a0
support creating a read only replica statefulset
davidfrickert Jun 20, 2024
a790f75
separate values for read only services from normal service
davidfrickert Jun 21, 2024
389e1cf
splitenable ldap port values
davidfrickert Jun 21, 2024
95f06a7
WIP: Write test that verifies readonly replica
davidfrickert Jun 24, 2024
d0dc984
Code review fixes
davidfrickert Jun 24, 2024
44c8ae9
ci - call readonly job
davidfrickert Jun 25, 2024
26156ba
need to load custom ldif for test to work as expected
davidfrickert Jun 25, 2024
2a410cd
use 0 read only replicas as the default
davidfrickert Jun 25, 2024
9740e50
add kind ports for read only ldap tests
davidfrickert Jun 25, 2024
4fe875e
changes in replication to support readonly replica
davidfrickert Jun 26, 2024
685cba1
CI readonly test fix
davidfrickert Jun 26, 2024
a8f0e11
save current readlonly strategy
jp-gouin Jul 18, 2024
b3c79d4
commit some changes that were uncommitted for readonly functionality
davidfrickert Sep 3, 2024
d1c380c
add read-only replica
jp-gouin Sep 27, 2024
f3fa7ea
add read-only replica
jp-gouin Sep 27, 2024
0ba2528
merge helper
jp-gouin Sep 27, 2024
baee6c9
merge helper
jp-gouin Sep 27, 2024
8567aaf
fix ci
jp-gouin Sep 27, 2024
7bb4a28
fix load of schema
jp-gouin Oct 1, 2024
cff4c66
fix mount custom schema
jp-gouin Oct 2, 2024
ae47cb4
allow read only to load custom schema during startup
jp-gouin Oct 3, 2024
02187ad
resolve conflicts
jp-gouin Oct 9, 2024
3f666e5
Merge branch 'master' into feat/add-readonly-replicas
jp-gouin Oct 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to update .bin/kind-conf.yml to include the 2 new ports :

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
  kubeadmConfigPatches:
  - |
    kind: JoinConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 8080
    protocol: TCP
  - containerPort: 443
    hostPort: 8444
    protocol: TCP
  - containerPort: 30636
    hostPort: 30636
  - containerPort: 30389
    hostPort: 30389
  - containerPort: 31389
    hostPort: 31389
  - containerPort: 31636
    hostPort: 31636
- role: worker
- role: worker

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, thanks for the tip

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