This is an automated email from the ASF dual-hosted git repository.
ipolyzos pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss.git
The following commit(s) were added to refs/heads/main by this push:
new bdbbbce94 [helm] Enable SASL authenticated connection to Zookeeper
nodes (#2700)
bdbbbce94 is described below
commit bdbbbce945ae4884ac72f45b602dfe9e4a681876
Author: Muhammet Orazov <[email protected]>
AuthorDate: Sun Mar 29 17:14:43 2026 +0200
[helm] Enable SASL authenticated connection to Zookeeper nodes (#2700)
---
Co-authored-by: Lorenzo Affetti <[email protected]>
---
helm/templates/_security.tpl | 91 +++++++-
helm/templates/configmap.yaml | 6 +
helm/templates/secret-jaas-config.yaml | 17 +-
helm/templates/sts-coordinator.yaml | 8 +-
helm/templates/sts-tablet.yaml | 8 +-
helm/tests/security_test.yaml | 231 +++++++++++++++++++++
helm/values.yaml | 9 +
website/docs/install-deploy/deploying-with-helm.md | 27 +++
8 files changed, 385 insertions(+), 12 deletions(-)
diff --git a/helm/templates/_security.tpl b/helm/templates/_security.tpl
index 210a35746..f3684fcd4 100644
--- a/helm/templates/_security.tpl
+++ b/helm/templates/_security.tpl
@@ -29,6 +29,28 @@ Usage:
{{- $mechanism -}}
{{- end -}}
+{{/*
+Returns the ZooKeeper SASL authentication mechanism value.
+Allowed mechanism values: '', 'plain'
+Usage:
+ include "fluss.security.zookeeper.sasl.mechanism" .
+*/}}
+{{- define "fluss.security.zookeeper.sasl.mechanism" -}}
+{{- $sasl := .Values.security.zookeeper.sasl | default (dict) -}}
+{{- $mechanism := lower (default "" $sasl.mechanism) -}}
+{{- $mechanism -}}
+{{- end -}}
+
+{{/*
+Returns true if ZooKeeper SASL authentication is enabled (mechanism is
non-empty).
+Usage:
+ include "fluss.security.zookeeper.sasl.enabled" .
+*/}}
+{{- define "fluss.security.zookeeper.sasl.enabled" -}}
+{{- $mechanism := include "fluss.security.zookeeper.sasl.mechanism" . -}}
+{{- if ne $mechanism "" -}}true{{- end -}}
+{{- end -}}
+
{{/*
Returns true if any of the listeners uses SASL based authentication mechanism
('plain' for now).
Usage:
@@ -117,6 +139,56 @@ Usage:
{{- end -}}
{{- end -}}
+{{/*
+Validates that ZooKeeper SASL mechanism is valid.
+Returns an error message if invalid, empty string otherwise.
+Usage:
+ include "fluss.security.zookeeper.sasl.validateMechanism" .
+*/}}
+{{- define "fluss.security.zookeeper.sasl.validateMechanism" -}}
+{{- $allowedMechanisms := list "" "plain" -}}
+{{- $mechanism := include "fluss.security.zookeeper.sasl.mechanism" . -}}
+{{- if not (has $mechanism $allowedMechanisms) -}}
+ {{- print "security.zookeeper.sasl.mechanism must be empty or: plain" -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Validates that ZooKeeper SASL loginModuleClass is not empty when ZK SASL is
enabled.
+Returns an error message if invalid, empty string otherwise.
+Usage:
+ include "fluss.security.zookeeper.sasl.validateLoginModuleClass" .
+*/}}
+{{- define "fluss.security.zookeeper.sasl.validateLoginModuleClass" -}}
+{{- if and (include "fluss.security.zookeeper.sasl.enabled" .) (not
.Values.security.zookeeper.sasl.plain.loginModuleClass) -}}
+ {{- print "security.zookeeper.sasl.plain.loginModuleClass must not be empty
when security.zookeeper.sasl.mechanism is plain" -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Validates that ZooKeeper SASL username is not empty when ZK SASL is enabled.
+Returns an error message if invalid, empty string otherwise.
+Usage:
+ include "fluss.security.zookeeper.sasl.validateUsername" .
+*/}}
+{{- define "fluss.security.zookeeper.sasl.validateUsername" -}}
+{{- if and (include "fluss.security.zookeeper.sasl.enabled" .) (not
.Values.security.zookeeper.sasl.plain.username) -}}
+ {{- print "security.zookeeper.sasl.plain.username must not be empty when
security.zookeeper.sasl.mechanism is plain" -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Validates that ZooKeeper SASL password is not empty when ZK SASL is enabled.
+Returns an error message if invalid, empty string otherwise.
+Usage:
+ include "fluss.security.zookeeper.sasl.validatePassword" .
+*/}}
+{{- define "fluss.security.zookeeper.sasl.validatePassword" -}}
+{{- if and (include "fluss.security.zookeeper.sasl.enabled" .) (not
.Values.security.zookeeper.sasl.plain.password) -}}
+ {{- print "security.zookeeper.sasl.plain.password must not be empty when
security.zookeeper.sasl.mechanism is plain" -}}
+{{- end -}}
+{{- end -}}
+
{{/*
Returns the default internal SASL username based on the release name.
Usage:
@@ -153,6 +225,17 @@ Usage:
{{- .Values.security.internal.sasl.plain.password | default (include
"fluss.security.sasl.plain.internal.defaultPassword" .) -}}
{{- end -}}
+{{/*
+Returns true if JAAS configuration is required, either by listeners using SASL
protocol or ZooKeeper SASL enablement.
+Usage:
+ include "fluss.security.jaas.required" .
+*/}}
+{{- define "fluss.security.jaas.required" -}}
+{{- if or (include "fluss.security.sasl.enabled" .) (include
"fluss.security.zookeeper.sasl.enabled" .) -}}
+{{- true -}}
+{{- end -}}
+{{- end -}}
+
{{/*
Returns a warning if the internal SASL user is using auto-generated
credentials.
Usage:
@@ -179,6 +262,10 @@ Usage:
{{- $errMessages := list -}}
{{- $errMessages = append $errMessages (include
"fluss.security.sasl.validateMechanisms" .) -}}
{{- $errMessages = append $errMessages (include
"fluss.security.sasl.validateClientPlainUsers" .) -}}
+{{- $errMessages = append $errMessages (include
"fluss.security.zookeeper.sasl.validateMechanism" .) -}}
+{{- $errMessages = append $errMessages (include
"fluss.security.zookeeper.sasl.validateLoginModuleClass" .) -}}
+{{- $errMessages = append $errMessages (include
"fluss.security.zookeeper.sasl.validateUsername" .) -}}
+{{- $errMessages = append $errMessages (include
"fluss.security.zookeeper.sasl.validatePassword" .) -}}
{{- $errMessages = without $errMessages "" -}}
{{- $errMessage := join "\n" $errMessages -}}
@@ -202,8 +289,8 @@ Usage:
{{/*
Returns the SASL JAAS config name.
Usage:
- include "fluss.security.sasl.configName" .
+ include "fluss.security.jaas.configName" .
*/}}
-{{- define "fluss.security.sasl.configName" -}}
+{{- define "fluss.security.jaas.configName" -}}
{{ include "fluss.fullname" . }}-sasl-jaas-config
{{- end -}}
diff --git a/helm/templates/configmap.yaml b/helm/templates/configmap.yaml
index 66f3eb095..c4574b6c1 100644
--- a/helm/templates/configmap.yaml
+++ b/helm/templates/configmap.yaml
@@ -53,3 +53,9 @@ data:
{{- end }}
{{- end }}
+
+ ### Zookeeper
+
+ {{- if (include "fluss.security.zookeeper.sasl.enabled" .) }}
+ zookeeper.client.config.path: /etc/fluss/conf/zookeeper-client.properties
+ {{- end }}
diff --git a/helm/templates/secret-jaas-config.yaml
b/helm/templates/secret-jaas-config.yaml
index 6ed411acb..436a2fbb2 100644
--- a/helm/templates/secret-jaas-config.yaml
+++ b/helm/templates/secret-jaas-config.yaml
@@ -16,7 +16,7 @@
# limitations under the License.
#
-{{ if (include "fluss.security.sasl.plain.enabled" .) }}
+{{ if (include "fluss.security.jaas.required" .) }}
{{- $internalMechanism := include "fluss.security.listener.mechanism" (dict
"context" .Values "listener" "internal") -}}
{{- $clientMechanism := include "fluss.security.listener.mechanism" (dict
"context" .Values "listener" "client") -}}
{{- $internalUsername := include "fluss.security.sasl.plain.internal.username"
. -}}
@@ -24,12 +24,13 @@
apiVersion: v1
kind: Secret
metadata:
- name: {{ include "fluss.security.sasl.configName" . }}
+ name: {{ include "fluss.security.jaas.configName" . }}
labels:
{{- include "fluss.labels" . | nindent 4 }}
type: Opaque
stringData:
jaas.conf: |
+{{- if (include "fluss.security.sasl.plain.enabled" .) }}
{{- if eq $internalMechanism "plain" }}
internal.FlussServer {
org.apache.fluss.security.auth.sasl.plain.PlainLoginModule required
@@ -49,4 +50,16 @@ stringData:
{{- end }};
};
{{- end }}
+{{- end }}
+{{- if (include "fluss.security.zookeeper.sasl.enabled" .) }}
+ ZookeeperClient {
+ {{ .Values.security.zookeeper.sasl.plain.loginModuleClass }} required
+ username="{{ .Values.security.zookeeper.sasl.plain.username }}"
+ password="{{ .Values.security.zookeeper.sasl.plain.password }}";
+ };
+{{- end }}
+{{- if (include "fluss.security.zookeeper.sasl.enabled" .) }}
+ zookeeper-client.properties: |
+ zookeeper.sasl.clientconfig=ZookeeperClient
+{{- end }}
{{- end -}}
diff --git a/helm/templates/sts-coordinator.yaml
b/helm/templates/sts-coordinator.yaml
index c443e230f..7a6774b28 100644
--- a/helm/templates/sts-coordinator.yaml
+++ b/helm/templates/sts-coordinator.yaml
@@ -60,7 +60,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: status.hostIP
- {{- if (include "fluss.security.sasl.plain.enabled" .) }}
+ {{- if (include "fluss.security.jaas.required" .) }}
- name: FLUSS_ENV_JAVA_OPTS
value:
"-Djava.security.auth.login.config=/etc/fluss/conf/jaas.conf"
{{- end }}
@@ -105,7 +105,7 @@ spec:
mountPath: /opt/conf
- name: data
mountPath: /tmp/fluss/data
- {{- if (include "fluss.security.sasl.plain.enabled" .) }}
+ {{- if (include "fluss.security.jaas.required" .) }}
- name: sasl-config
mountPath: /etc/fluss/conf
readOnly: true
@@ -118,10 +118,10 @@ spec:
- name: data
emptyDir: {}
{{- end }}
- {{- if (include "fluss.security.sasl.plain.enabled" .) }}
+ {{- if (include "fluss.security.jaas.required" .) }}
- name: sasl-config
secret:
- secretName: {{ include "fluss.security.sasl.configName" . }}
+ secretName: {{ include "fluss.security.jaas.configName" . }}
{{- end }}
{{- if .Values.coordinator.storage.enabled }}
volumeClaimTemplates:
diff --git a/helm/templates/sts-tablet.yaml b/helm/templates/sts-tablet.yaml
index 1ffe1af31..b58fc49f8 100644
--- a/helm/templates/sts-tablet.yaml
+++ b/helm/templates/sts-tablet.yaml
@@ -56,7 +56,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- {{- if (include "fluss.security.sasl.plain.enabled" .) }}
+ {{- if (include "fluss.security.jaas.required" .) }}
- name: FLUSS_ENV_JAVA_OPTS
value:
"-Djava.security.auth.login.config=/etc/fluss/conf/jaas.conf"
{{- end }}
@@ -102,7 +102,7 @@ spec:
mountPath: /opt/conf
- name: data
mountPath: /tmp/fluss/data
- {{- if (include "fluss.security.sasl.plain.enabled" .) }}
+ {{- if (include "fluss.security.jaas.required" .) }}
- name: sasl-config
mountPath: /etc/fluss/conf
readOnly: true
@@ -115,10 +115,10 @@ spec:
- name: data
emptyDir: {}
{{- end }}
- {{- if (include "fluss.security.sasl.plain.enabled" .) }}
+ {{- if (include "fluss.security.jaas.required" .) }}
- name: sasl-config
secret:
- secretName: {{ include "fluss.security.sasl.configName" . }}
+ secretName: {{ include "fluss.security.jaas.configName" . }}
{{- end }}
{{- if .Values.tablet.storage.enabled }}
volumeClaimTemplates:
diff --git a/helm/tests/security_test.yaml b/helm/tests/security_test.yaml
index f6d67f508..d73e2604e 100644
--- a/helm/tests/security_test.yaml
+++ b/helm/tests/security_test.yaml
@@ -282,3 +282,234 @@ tests:
- matchRegex:
path: spec.template.spec.containers[0].command[2]
pattern: '>> \$FLUSS_HOME/conf/server\.yaml'
+
+---
+
+suite: zookeeper-sasl-enabled-secret
+templates:
+ - templates/secret-jaas-config.yaml
+tests:
+ - it: renders ZookeeperClient JAAS block when ZK SASL is enabled
+ set:
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.username: zk-user
+ security.zookeeper.sasl.plain.password: zk-pass
+ asserts:
+ - hasDocuments:
+ count: 1
+ - matchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'ZookeeperClient\s*\{'
+ - matchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'username="zk-user"'
+ - matchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'password="zk-pass"'
+ - matchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'DigestLoginModule required'
+ - matchRegex:
+ path: stringData["zookeeper-client.properties"]
+ pattern: 'zookeeper\.sasl\.clientconfig=ZookeeperClient'
+ - it: renders both SASL and ZK JAAS blocks when both are enabled
+ set:
+ security.internal.sasl.mechanism: plain
+ security.internal.sasl.plain.username: internal-user
+ security.internal.sasl.plain.password: internal-pass
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.username: zk-user
+ security.zookeeper.sasl.plain.password: zk-pass
+ asserts:
+ - matchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'internal\.FlussServer\s*\{'
+ - matchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'ZookeeperClient\s*\{'
+ - it: does not render SASL listener blocks when only ZK SASL is enabled
+ set:
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.username: zk-user
+ security.zookeeper.sasl.plain.password: zk-pass
+ asserts:
+ - notMatchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'internal\.FlussServer\s*\{'
+ - notMatchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'FlussClient\s*\{'
+
+---
+
+suite: zookeeper-sasl-enabled-statefulset
+templates:
+ - templates/sts-coordinator.yaml
+ - templates/sts-tablet.yaml
+ - templates/configmap.yaml
+tests:
+ - it: renders JAAS env var and mounts secret volume for coordinator when ZK
SASL is enabled
+ template: templates/sts-coordinator.yaml
+ set:
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.username: zk-user
+ security.zookeeper.sasl.plain.password: zk-pass
+ asserts:
+ - contains:
+ path: spec.template.spec.containers[0].env
+ content:
+ name: FLUSS_ENV_JAVA_OPTS
+ value:
"-Djava.security.auth.login.config=/etc/fluss/conf/jaas.conf"
+ - contains:
+ path: spec.template.spec.volumes
+ content:
+ name: sasl-config
+ secret:
+ secretName: RELEASE-NAME-fluss-sasl-jaas-config
+ - it: renders JAAS env var and mounts secret volume for tablet when ZK SASL
is enabled
+ template: templates/sts-tablet.yaml
+ set:
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.username: zk-user
+ security.zookeeper.sasl.plain.password: zk-pass
+ asserts:
+ - contains:
+ path: spec.template.spec.containers[0].env
+ content:
+ name: FLUSS_ENV_JAVA_OPTS
+ value:
"-Djava.security.auth.login.config=/etc/fluss/conf/jaas.conf"
+ - contains:
+ path: spec.template.spec.volumes
+ content:
+ name: sasl-config
+ secret:
+ secretName: RELEASE-NAME-fluss-sasl-jaas-config
+ - it: renders zookeeper client config path in configmap when ZK SASL is
enabled
+ template: templates/configmap.yaml
+ set:
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.username: zk-user
+ security.zookeeper.sasl.plain.password: zk-pass
+ asserts:
+ - matchRegex:
+ path: data["server.yaml"]
+ pattern: 'zookeeper\.client\.config\.path:
/etc/fluss/conf/zookeeper-client\.properties'
+
+---
+
+suite: zookeeper-sasl-custom-login-module
+templates:
+ - templates/secret-jaas-config.yaml
+tests:
+ - it: renders custom login module class in ZookeeperClient JAAS block
+ set:
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.username: zk-user
+ security.zookeeper.sasl.plain.password: zk-pass
+ security.zookeeper.sasl.plain.loginModuleClass:
"com.example.CustomLoginModule"
+ asserts:
+ - matchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'com\.example\.CustomLoginModule required'
+ - notMatchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'DigestLoginModule'
+
+---
+
+suite: zookeeper-sasl-disabled
+templates:
+ - templates/secret-jaas-config.yaml
+ - templates/sts-coordinator.yaml
+ - templates/sts-tablet.yaml
+ - templates/configmap.yaml
+tests:
+ - it: does not render ZookeeperClient JAAS block when ZK SASL is disabled
+ template: templates/secret-jaas-config.yaml
+ set:
+ security.internal.sasl.mechanism: plain
+ security.internal.sasl.plain.username: internal-user
+ security.internal.sasl.plain.password: internal-pass
+ asserts:
+ - hasDocuments:
+ count: 1
+ - notMatchRegex:
+ path: stringData["jaas.conf"]
+ pattern: 'ZookeeperClient\s*\{'
+ - isNull:
+ path: stringData["zookeeper-client.properties"]
+ - it: does not render JAAS secret when neither SASL nor ZK SASL is enabled
+ template: templates/secret-jaas-config.yaml
+ asserts:
+ - hasDocuments:
+ count: 0
+ - it: does not render JAAS env var or volume mount for coordinator when ZK
SASL is disabled
+ template: templates/sts-coordinator.yaml
+ asserts:
+ - notContains:
+ path: spec.template.spec.containers[0].env
+ content:
+ name: FLUSS_ENV_JAVA_OPTS
+ value:
"-Djava.security.auth.login.config=/etc/fluss/conf/jaas.conf"
+ - notContains:
+ path: spec.template.spec.volumes
+ content:
+ name: sasl-config
+ any: true
+ - it: does not render JAAS env var or volume mount for tablet when ZK SASL
is disabled
+ template: templates/sts-tablet.yaml
+ asserts:
+ - notContains:
+ path: spec.template.spec.containers[0].env
+ content:
+ name: FLUSS_ENV_JAVA_OPTS
+ value:
"-Djava.security.auth.login.config=/etc/fluss/conf/jaas.conf"
+ - notContains:
+ path: spec.template.spec.volumes
+ content:
+ name: sasl-config
+ any: true
+ - it: does not render zookeeper client config path in configmap when ZK SASL
is disabled
+ template: templates/configmap.yaml
+ asserts:
+ - notMatchRegex:
+ path: data["server.yaml"]
+ pattern: 'zookeeper\.client\.config\.path'
+
+---
+
+suite: zookeeper-sasl-validation
+templates:
+ - templates/NOTES.txt
+tests:
+ - it: fails when loginModuleClass is empty and ZK SASL is enabled
+ set:
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.username: zk-user
+ security.zookeeper.sasl.plain.password: zk-pass
+ security.zookeeper.sasl.plain.loginModuleClass: ""
+ asserts:
+ - failedTemplate:
+ errorMessage: "VALUES
VALIDATION:\nsecurity.zookeeper.sasl.plain.loginModuleClass must not be empty
when security.zookeeper.sasl.mechanism is plain"
+ - it: fails when ZK SASL is enabled but username is empty
+ set:
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.password: zk-pass
+ asserts:
+ - failedTemplate:
+ errorMessage: "VALUES
VALIDATION:\nsecurity.zookeeper.sasl.plain.username must not be empty when
security.zookeeper.sasl.mechanism is plain"
+ - it: fails when ZK SASL is enabled but password is empty
+ set:
+ security.zookeeper.sasl.mechanism: plain
+ security.zookeeper.sasl.plain.username: zk-user
+ asserts:
+ - failedTemplate:
+ errorMessage: "VALUES
VALIDATION:\nsecurity.zookeeper.sasl.plain.password must not be empty when
security.zookeeper.sasl.mechanism is plain"
+ - it: fails for invalid zookeeper mechanism value
+ set:
+ security.zookeeper.sasl.mechanism: bogus
+ security.zookeeper.sasl.plain.username: zk-user
+ security.zookeeper.sasl.plain.password: zk-pass
+ asserts:
+ - failedTemplate:
+ errorMessage: "VALUES VALIDATION:\nsecurity.zookeeper.sasl.mechanism
must be empty or: plain"
diff --git a/helm/values.yaml b/helm/values.yaml
index 63c11e724..219fbc75c 100644
--- a/helm/values.yaml
+++ b/helm/values.yaml
@@ -75,6 +75,15 @@ security:
username: ""
password: ""
+ zookeeper:
+ sasl:
+ # "" | plain
+ mechanism: ""
+ plain:
+ username: ""
+ password: ""
+ loginModuleClass:
"org.apache.fluss.shaded.zookeeper3.org.apache.zookeeper.server.auth.DigestLoginModule"
+
metrics:
reporters: ""
prometheus:
diff --git a/website/docs/install-deploy/deploying-with-helm.md
b/website/docs/install-deploy/deploying-with-helm.md
index ba9a8932b..f7e543934 100644
--- a/website/docs/install-deploy/deploying-with-helm.md
+++ b/website/docs/install-deploy/deploying-with-helm.md
@@ -202,6 +202,15 @@ If the internal SASL username or password is left empty,
the chart automatically
It is recommended to set these explicitly in production.
+#### ZooKeeper SASL Parameters
+
+| Parameter | Description | Default |
+|-----------|-------------|---------|
+| `security.zookeeper.sasl.mechanism` | ZooKeeper SASL mechanism (`""`,
`plain`) | `""` |
+| `security.zookeeper.sasl.plain.username` | ZooKeeper SASL username | `""` |
+| `security.zookeeper.sasl.plain.password` | ZooKeeper SASL password | `""` |
+| `security.zookeeper.sasl.plain.loginModuleClass` | JAAS login module class
for ZooKeeper |
`org.apache.fluss.shaded.zookeeper3.org.apache.zookeeper.server.auth.DigestLoginModule`
|
+
### Metrics Parameters
| Parameter | Description | Default |
@@ -325,6 +334,24 @@ security:
password: internal-password
```
+### Enabling ZooKeeper SASL Authentication
+
+You can enable ZooKeeper ensemble SASL authentication, with the following
values in the Fluss Helm chart:
+
+```yaml
+security:
+ zookeeper:
+ sasl:
+ mechanism: plain
+ plain:
+ username: fluss-zk-user
+ password: fluss-zk-password
+```
+
+The `security.zookeeper.sasl.plain.username` and
`security.zookeeper.sasl.plain.password` fields are required when
`security.zookeeper.sasl.mechanism` is set to `plain`.
+
+ZooKeeper SASL can be enabled independently or together with the listeners
SASL authentication.
+
### Metrics and Monitoring
When `metrics.reporters` is set, the chart adds the following `server.yaml`
configuration entries: