Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 44 additions & 0 deletions helm/templates/_security.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,49 @@ Usage:
{{- include "fluss.security.sasl.warnInternalUser" . -}}
{{- end -}}

{{/*
secretKeyRef env entry for the readiness probe credential, or empty when no
Secret is referenced. Rendered on every server container, so the exec probe
inherits it whichever component runs the cluster-health check.
Usage:
include "fluss.security.readinessProbe.env" .
*/}}
{{- define "fluss.security.readinessProbe.env" -}}
{{- $ref := .Values.security.readinessProbe.existingSecret | default (dict) -}}
{{- if $ref.name }}
- name: READINESS_HEALTH_CHECK_AUTH
valueFrom:
secretKeyRef:
name: {{ $ref.name }}
key: {{ $ref.key | default "auth" }}
{{- end }}
{{- end -}}

{{/*
Validates the readiness probe credential. It has three possible sources, and
setting more than one is an error.
Returns an error message if invalid, empty string otherwise.
Usage:
include "fluss.security.readinessProbe.validateAuth" .
*/}}
{{- define "fluss.security.readinessProbe.validateAuth" -}}
{{- $sources := list -}}
{{- if .Values.tablet.readinessProbe.healthCheckAuth -}}
{{- $sources = append $sources "tablet.readinessProbe.healthCheckAuth" -}}
{{- end -}}
{{- if (.Values.security.readinessProbe.existingSecret | default (dict)).name -}}
{{- $sources = append $sources "security.readinessProbe.existingSecret" -}}
{{- end -}}
{{- range .Values.secrets.env -}}
{{- if eq .name "READINESS_HEALTH_CHECK_AUTH" -}}
{{- $sources = append $sources "the secrets.env entry named READINESS_HEALTH_CHECK_AUTH" -}}
{{- end -}}
{{- end -}}
{{- if gt (len $sources) 1 -}}
{{- printf "the readiness probe credential is set more than once (%s); set only one" (join ", " $sources) -}}
{{- end -}}
{{- end -}}

{{/*
Collects security error messages.
Usage:
Expand All @@ -299,6 +342,7 @@ Usage:
{{- $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 = append $errMessages (include "fluss.security.readinessProbe.validateAuth" .) -}}
{{- $errMessages = without $errMessages "" -}}
{{- join "\n" $errMessages -}}
{{- end -}}
Expand Down
3 changes: 3 additions & 0 deletions helm/templates/sts-coordinator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ spec:
{{- if (include "fluss.secrets.enabled" .) }}
{{- include "fluss.secrets.env" . | trim | nindent 12 }}
{{- end }}
{{- with (include "fluss.security.readinessProbe.env" .) }}
{{- . | trim | nindent 12 }}
{{- end }}
{{- with .Values.coordinator.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
Expand Down
3 changes: 3 additions & 0 deletions helm/templates/sts-tablet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ spec:
{{- if (include "fluss.secrets.enabled" .) }}
{{- include "fluss.secrets.env" . | trim | nindent 12 }}
{{- end }}
{{- with (include "fluss.security.readinessProbe.env" .) }}
{{- . | trim | nindent 12 }}
{{- end }}
{{- with .Values.tablet.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
Expand Down
114 changes: 114 additions & 0 deletions helm/tests/readiness_probe_auth_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

suite: readiness-probe-auth
templates:
- templates/sts-coordinator.yaml
- templates/sts-tablet.yaml
- templates/NOTES.txt
tests:
- it: renders no probe credential by default
asserts:
- notContains:
path: spec.template.spec.containers[0].env
content:
name: READINESS_HEALTH_CHECK_AUTH
any: true
template: templates/sts-tablet.yaml
- notMatchRegex:
path: spec.template.spec.containers[0].readinessProbe.exec.command[2]
pattern: 'READINESS_HEALTH_CHECK_AUTH'
template: templates/sts-tablet.yaml

- it: sources the probe credential from a Secret on every server container
set:
security.readinessProbe.existingSecret:
name: 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
- 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-coordinator.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

- it: defaults the Secret key to auth
set:
security.readinessProbe.existingSecret.name: fluss-readiness-probe-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

- it: fails when the credential is set both inline and via existingSecret
set:
tablet.readinessProbe.healthCheckAuth: "client.security.protocol:SASL"
security.readinessProbe.existingSecret.name: fluss-readiness-probe-auth
asserts:
- failedTemplate:
errorMessage: "VALUES VALIDATION:\nthe readiness probe credential is set more than once (tablet.readinessProbe.healthCheckAuth, security.readinessProbe.existingSecret); set only one"
template: templates/NOTES.txt

- it: fails when the credential is set both via existingSecret and secrets.env
set:
security.readinessProbe.existingSecret.name: fluss-readiness-probe-auth
secrets.env:
- name: READINESS_HEALTH_CHECK_AUTH
secretName: fluss-readiness-probe-auth
key: auth
asserts:
- failedTemplate:
errorMessage: "VALUES VALIDATION:\nthe readiness probe credential is set more than once (security.readinessProbe.existingSecret, the secrets.env entry named READINESS_HEALTH_CHECK_AUTH); set only one"
template: templates/NOTES.txt

- it: fails when the 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:\nthe readiness probe credential is set more than once (tablet.readinessProbe.healthCheckAuth, the secrets.env entry named READINESS_HEALTH_CHECK_AUTH); set only one"
template: templates/NOTES.txt
20 changes: 20 additions & 0 deletions helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ 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 set
# `security.readinessProbe.existingSecret`.
# Setting both is rejected at render time, because the probe command
# would silently override the Secret.
healthCheckAuth: ""
extraVolumes: []
extraVolumeMounts: []
Expand Down Expand Up @@ -197,6 +204,19 @@ security:
# usernameKey: username
# passwordKey: password

# Credential for the cluster-health readiness probes, sourced from a Secret.
# The referenced key holds the same single-line string that
# `<component>.readinessProbe.healthCheckAuth` takes as a literal; the chart
# renders it as the READINESS_HEALTH_CHECK_AUTH environment variable on every
# server container, so the exec probe picks it up unchanged and the credential
# never appears in the rendered manifest. It lives here rather than under a
# component because every component's probe authenticates with it.
readinessProbe:
existingSecret: {}
# existingSecret:
# name: fluss-readiness-probe-auth
# key: auth

zookeeper:
sasl:
# "" | plain
Expand Down
42 changes: 42 additions & 0 deletions website/docs/install-deploy/deploying-with-helm.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ The following table lists the configurable parameters of the Fluss chart, and th
| `security.internal.sasl.plain.username` | Internal listener PLAIN username | `""` |
| `security.internal.sasl.plain.password` | Internal listener PLAIN password | `""` |
| `security.internal.sasl.plain.existingSecret` | Reference to a pre-existing Secret for internal SASL credentials | `{}` |
| `security.readinessProbe.existingSecret` | Reference to a pre-existing Secret holding the [readiness probe credential](#probe-credentials-on-a-sasl-cluster), `{name, key}` | `{}` |

Only `plain` mechanism is supported for now. An empty string disables the SASL authentication, and maps to the `PLAINTEXT` protocol.

Expand Down Expand Up @@ -873,6 +874,47 @@ 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.

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
reference the Secret instead:

```bash
kubectl create secret generic fluss-readiness-probe-auth \
--from-literal=auth="client.security.protocol:SASL;client.security.sasl.mechanism:PLAIN;client.security.sasl.username:probe;client.security.sasl.password:probe-pass"
```

```yaml
security:
readinessProbe:
existingSecret:
name: fluss-readiness-probe-auth
key: auth # optional, defaults to `auth`
```

The chart renders it as the `READINESS_HEALTH_CHECK_AUTH` environment variable on every server
container, and the exec probe inherits the container environment, so the value reaches the
check unchanged. The setting sits under `security` rather than under a component because every
component's probe authenticates with the same credential.

Setting the credential more than once fails the render. The inline form would silently
override the Secret, and a second `READINESS_HEALTH_CHECK_AUTH` entry under
[`secrets.env`](#secrets-in-configuration-overrides) would make the StatefulSet invalid.

:::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.
Expand Down
Loading