From 838abafdfc7388045910f4e94452948c47cb8787 Mon Sep 17 00:00:00 2001 From: morazow Date: Mon, 14 Sep 2026 15:15:02 +0200 Subject: [PATCH 1/2] [helm] Document and validate Secret-backed readiness probe auth The tablet readiness probe authenticates as a Fluss client, so on a SASL-enforced cluster it needs a credential. The only documented way to supply one is tablet.readinessProbe.healthCheckAuth, which the chart inlines into the StatefulSet's exec command, leaving the credential in plain text in the rendered manifest. The chart already has a Secret-backed route: a secrets.env entry named READINESS_HEALTH_CHECK_AUTH renders a secretKeyRef on both the tablet and coordinator containers, and the exec probe inherits the container environment, so readiness-check.sh reads it unchanged. Nothing pointed at it, and setting both forms let the inline export silently override the Secret. Document the Secret-backed route in values.yaml and the Helm deployment guide, including the DESCRIBE-on-cluster requirement for the probe principal when the authorizer is enabled, and reject the combination at render time instead of silently preferring the inline value. --- helm/templates/_secrets.tpl | 7 ++++ helm/tests/secrets_test.yaml | 23 +++++++++++++ helm/tests/secrets_validate_test.yaml | 11 +++++++ helm/values.yaml | 17 ++++++++++ .../install-deploy/deploying-with-helm.md | 32 +++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/helm/templates/_secrets.tpl b/helm/templates/_secrets.tpl index 95a9637c91a..1bf3ea012e0 100644 --- a/helm/templates/_secrets.tpl +++ b/helm/templates/_secrets.tpl @@ -131,6 +131,13 @@ Usage: {{- $errMessages = append $errMessages "secrets.env entries require 'name', 'secretName' and 'key'." -}} {{- end -}} {{- end -}} +{{- if .Values.tablet.readinessProbe.healthCheckAuth -}} +{{- range .Values.secrets.env -}} +{{- if eq .name "READINESS_HEALTH_CHECK_AUTH" -}} +{{- $errMessages = append $errMessages "tablet.readinessProbe.healthCheckAuth conflicts with the secrets.env entry named READINESS_HEALTH_CHECK_AUTH; set only one." -}} +{{- end -}} +{{- end -}} +{{- end -}} {{- $errMessages = without $errMessages "" -}} {{- join "\n" $errMessages -}} {{- end -}} diff --git a/helm/tests/secrets_test.yaml b/helm/tests/secrets_test.yaml index 2eaa8f74617..fdc876e16fb 100644 --- a/helm/tests/secrets_test.yaml +++ b/helm/tests/secrets_test.yaml @@ -145,3 +145,26 @@ tests: path: data["server.yaml"] pattern: 'config\.providers: directory,env' template: templates/configmap.yaml + + - it: sources the readiness probe credential from a Secret + set: + secrets.env: + - name: READINESS_HEALTH_CHECK_AUTH + secretName: fluss-readiness-probe-auth + key: auth + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: READINESS_HEALTH_CHECK_AUTH + valueFrom: + secretKeyRef: + name: fluss-readiness-probe-auth + key: auth + template: templates/sts-tablet.yaml + # The probe command must not export the variable, otherwise it would + # override the Secret-sourced value inherited from the container. + - notMatchRegex: + path: spec.template.spec.containers[0].readinessProbe.exec.command[2] + pattern: 'export READINESS_HEALTH_CHECK_AUTH' + template: templates/sts-tablet.yaml diff --git a/helm/tests/secrets_validate_test.yaml b/helm/tests/secrets_validate_test.yaml index 4c4689aa1ac..4af370187e8 100644 --- a/helm/tests/secrets_validate_test.yaml +++ b/helm/tests/secrets_validate_test.yaml @@ -57,3 +57,14 @@ tests: asserts: - failedTemplate: errorMessage: "VALUES VALIDATION:\nsecrets.env entries require 'name', 'secretName' and 'key'." + + - it: fails when the probe credential is set both inline and via secrets.env + set: + tablet.readinessProbe.healthCheckAuth: "client.security.protocol:SASL" + secrets.env: + - name: READINESS_HEALTH_CHECK_AUTH + secretName: fluss-readiness-probe-auth + key: auth + asserts: + - failedTemplate: + errorMessage: "VALUES VALIDATION:\ntablet.readinessProbe.healthCheckAuth conflicts with the secrets.env entry named READINESS_HEALTH_CHECK_AUTH; set only one." diff --git a/helm/values.yaml b/helm/values.yaml index de9c8eb42e0..f4cf78ae9cb 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -79,6 +79,23 @@ tablet: # or escaping needed). Required only when the local client listener # enforces SASL. Example: # healthCheckAuth: "client.security.protocol:SASL;client.sasl.mechanism:PLAIN;client.security.sasl.username:admin;client.security.sasl.password:admin-pass" + # + # This value is inlined into the StatefulSet's probe command, so it is + # readable by anyone who can read the rendered manifest. To keep the + # credential in a Secret instead, leave this empty and declare the + # environment variable the probe reads under `secrets.env`: + # secrets: + # env: + # - name: READINESS_HEALTH_CHECK_AUTH + # secretName: fluss-readiness-probe-auth + # key: auth + # The exec probe inherits the container environment, so readiness-check.sh + # picks the value up unchanged. Setting both forms is rejected at render + # time, because the probe command would silently override the Secret. + # + # When the authorizer is enabled, the principal in this credential needs + # DESCRIBE on the cluster resource, otherwise the health call is denied and + # the pod never becomes Ready. healthCheckAuth: "" extraVolumes: [] extraVolumeMounts: [] diff --git a/website/docs/install-deploy/deploying-with-helm.md b/website/docs/install-deploy/deploying-with-helm.md index 4a483e14a74..496d5b3157c 100644 --- a/website/docs/install-deploy/deploying-with-helm.md +++ b/website/docs/install-deploy/deploying-with-helm.md @@ -873,6 +873,38 @@ tablet: | `failureThreshold` | `200` | Max consecutive probe failures before marking the pod as unready. With `periodSeconds=5`, this allows up to ~16 minutes for recovery. | | `periodSeconds` | `5` | How often the probe runs. | +##### Probe credentials on a SASL cluster + +The probe connects to the pod's own client listener as a regular Fluss client. When that +listener enforces SASL, the probe needs credentials or no TabletServer ever becomes Ready. +With the authorizer enabled, the principal also needs `DESCRIBE` on the cluster resource. + +The credential is a single-line string of semicolon-separated `key:value` pairs. Supply it in +one of two ways. + +Inline, which is simplest but puts the credential in plain text in the rendered StatefulSet: + +```yaml +tablet: + readinessProbe: + healthCheckAuth: "client.security.protocol:SASL;client.security.sasl.mechanism:PLAIN;client.security.sasl.username:probe;client.security.sasl.password:probe-pass" +``` + +Or from a Secret, which keeps it out of the manifest. Leave `healthCheckAuth` empty and +declare the environment variable the probe reads: + +```yaml +secrets: + env: + - name: READINESS_HEALTH_CHECK_AUTH + secretName: fluss-readiness-probe-auth + key: auth +``` + +The exec probe inherits the container environment, so the value reaches the check unchanged. +Setting both forms fails the render, because the inline form would silently override the +Secret. + :::note The CoordinatorServer does not need the Cluster Health API probe — it does not host data replicas, so a simple TCP check is sufficient. The Coordinator should be upgraded **after** all TabletServers are fully upgraded and recovered. From bff29009ff0f3d502f9a572b66a70b0c80061d0b Mon Sep 17 00:00:00 2001 From: morazow Date: Mon, 14 Sep 2026 16:29:36 +0200 Subject: [PATCH 2/2] refac --- helm/values.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/helm/values.yaml b/helm/values.yaml index f4cf78ae9cb..e4854077fc2 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -90,8 +90,7 @@ tablet: # secretName: fluss-readiness-probe-auth # key: auth # The exec probe inherits the container environment, so readiness-check.sh - # picks the value up unchanged. Setting both forms is rejected at render - # time, because the probe command would silently override the Secret. + # picks the value up unchanged. # # When the authorizer is enabled, the principal in this credential needs # DESCRIBE on the cluster resource, otherwise the health call is denied and