From b1baabc1f4d48109297ff7fe15b686adc297e627 Mon Sep 17 00:00:00 2001 From: morazow Date: Fri, 16 Jan 2026 23:19:44 +0100 Subject: [PATCH 1/2] [helm] Enable SASL authentication configurations Co-authored-by: Lorenzo Affetti --- .github/workflows/helm-chart.yaml | 54 +++++ helm/README.md | 4 +- helm/templates/_helpers.tpl | 2 +- helm/templates/_sasl.tpl | 32 +++ helm/templates/secret-jaas-config.yaml | 48 +++++ helm/templates/sts-coordinator.yaml | 20 ++ helm/templates/sts-tablet.yaml | 20 ++ helm/tests/sasl_test.yaml | 203 ++++++++++++++++++ helm/values.yaml | 11 + .../install-deploy/deploying-with-helm.md | 42 +++- 10 files changed, 430 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/helm-chart.yaml create mode 100644 helm/templates/_sasl.tpl create mode 100644 helm/templates/secret-jaas-config.yaml create mode 100644 helm/tests/sasl_test.yaml diff --git a/.github/workflows/helm-chart.yaml b/.github/workflows/helm-chart.yaml new file mode 100644 index 0000000000..7ae546ee9e --- /dev/null +++ b/.github/workflows/helm-chart.yaml @@ -0,0 +1,54 @@ +################################################################################ +# 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. +################################################################################ + +name: "Helm Chart" + +permissions: + contents: read + +on: + pull_request: + paths: + - 'helm/**' + - '.github/workflows/helm-chart.yaml' + push: + branches: [main, release-*, ci-*] + paths: + - 'helm/**' + - '.github/workflows/helm-chart.yaml' + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.number || github.run_id }} + cancel-in-progress: true + +jobs: + test-helm-chart: + name: "Helm Lint and Unittest" + runs-on: ubuntu-latest + steps: + - name: "Checkout code" + uses: actions/checkout@v6 + + - name: "Run helm-unittest" + uses: d3adb5/helm-unittest-action@v2 + with: + helm-version: latest + charts: helm + + - name: "Lint Helm chart" + run: helm lint ./helm diff --git a/helm/README.md b/helm/README.md index f09750c489..837294e5d8 100644 --- a/helm/README.md +++ b/helm/README.md @@ -22,7 +22,7 @@ This chart deploys an Apache Fluss cluster on Kubernetes, following Helm best pr It requires a Zookeeper ensemble to be running in the same Kubernetes cluster. In future releases, we may add support for an embedded Zookeeper cluster. -## Development environment +## Development environment | component | version | | ------------------------------------------------------------------------------ | ------- | @@ -33,7 +33,7 @@ It requires a Zookeeper ensemble to be running in the same Kubernetes cluster. I | [Apache Fluss](https://fluss.apache.org/docs/) | v0.10.0-incubating | -## Image requirements +## Image requirements A container image for Fluss is available on DockerHub as `fluss/fluss`. You can use it directly or build your own from this repo. To use your own image you need to build the project with [Maven](https://fluss.apache.org/community/dev/building/) and build it with Docker. diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index 79ae9d3106..53d0097ae8 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -63,4 +63,4 @@ Selector labels {{- define "fluss.selectorLabels" -}} app.kubernetes.io/name: {{ include "fluss.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/helm/templates/_sasl.tpl b/helm/templates/_sasl.tpl new file mode 100644 index 0000000000..1082ade903 --- /dev/null +++ b/helm/templates/_sasl.tpl @@ -0,0 +1,32 @@ +# +# 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. +# + +{{/* +Return true if SASL is configured in any of the listener protocols +*/}} +{{- define "fluss.sasl.enabled" -}} +{{- $enabled := false -}} +{{- range $id, $l := .Values.listeners -}} + {{- if and (not $enabled) (regexFind "SASL" (upper $l.protocol)) -}} + {{- $enabled = true -}} + {{- end -}} +{{- end -}} +{{- if $enabled -}} +{{- true -}} +{{- end -}} +{{- end -}} diff --git a/helm/templates/secret-jaas-config.yaml b/helm/templates/secret-jaas-config.yaml new file mode 100644 index 0000000000..6f53dd3017 --- /dev/null +++ b/helm/templates/secret-jaas-config.yaml @@ -0,0 +1,48 @@ +# +# 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. +# + +{{- if (include "fluss.sasl.enabled" .) }} +{{- $saslUsers := default (list) .Values.security.sasl_plain.users -}} +{{- if eq (len $saslUsers) 0 -}} +{{- fail "security.sasl_plain.users must contain at least one user when SASL is enabled in listeners" -}} +{{- end }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "fluss.fullname" . }}-sasl-jaas-config + labels: + {{- include "fluss.labels" . | nindent 4 }} +type: Opaque +stringData: + jaas.conf: | + FlussServer { + org.apache.fluss.security.auth.sasl.plain.PlainLoginModule required + {{- range $saslUsers }} + user_{{ .username }}="{{ .password }}" + {{- end }}; + }; +{{- if regexFind "SASL" (upper .Values.listeners.internal.protocol) }} + {{- $clientUser := first $saslUsers }} + + FlussClient { + org.apache.fluss.security.auth.sasl.plain.PlainLoginModule required + username="{{ $clientUser.username }}" + password="{{ $clientUser.password }}"; + }; +{{- end }} +{{- end -}} diff --git a/helm/templates/sts-coordinator.yaml b/helm/templates/sts-coordinator.yaml index 6f2ec40ffc..a39ccf01e8 100644 --- a/helm/templates/sts-coordinator.yaml +++ b/helm/templates/sts-coordinator.yaml @@ -77,6 +77,16 @@ spec: echo "" >> $FLUSS_HOME/conf/server.yaml && \ echo "bind.listeners: ${BIND_LISTENERS}" >> $FLUSS_HOME/conf/server.yaml && \ echo "advertised.listeners: ${ADVERTISED_LISTENERS}" >> $FLUSS_HOME/conf/server.yaml && \ + echo "security.protocol.map: INTERNAL:{{ upper .Values.listeners.internal.protocol }},CLIENT:{{ upper .Values.listeners.client.protocol }}" >> $FLUSS_HOME/conf/server.yaml && \ + + {{- if (include "fluss.sasl.enabled" .) }} + echo "security.sasl.enabled.mechanisms: PLAIN" >> $FLUSS_HOME/conf/server.yaml && \ + {{ if regexFind "SASL" (upper .Values.listeners.internal.protocol) }} + echo "client.security.protocol: SASL" >> $FLUSS_HOME/conf/server.yaml && \ + echo "client.security.sasl.mechanism: PLAIN" >> $FLUSS_HOME/conf/server.yaml && \ + {{ end }} + export FLUSS_ENV_JAVA_OPTS="-Djava.security.auth.login.config=/etc/fluss/conf/jaas.conf ${FLUSS_ENV_JAVA_OPTS}" && \ + {{- end }} bin/coordinator-server.sh start-foreground livenessProbe: @@ -100,6 +110,11 @@ spec: mountPath: /opt/conf - name: data mountPath: /tmp/fluss/data + {{- if (include "fluss.sasl.enabled" .) }} + - name: sasl-config + mountPath: /etc/fluss/conf + readOnly: true + {{- end }} volumes: - name: fluss-conf configMap: @@ -108,6 +123,11 @@ spec: - name: data emptyDir: {} {{- end }} + {{- if (include "fluss.sasl.enabled" .) }} + - name: sasl-config + secret: + secretName: {{ include "fluss.fullname" . }}-sasl-jaas-config + {{- end }} {{- if .Values.coordinator.storage.enabled }} volumeClaimTemplates: - metadata: diff --git a/helm/templates/sts-tablet.yaml b/helm/templates/sts-tablet.yaml index f329eb1f29..1f7c1bd6ee 100644 --- a/helm/templates/sts-tablet.yaml +++ b/helm/templates/sts-tablet.yaml @@ -74,6 +74,16 @@ spec: echo "tablet-server.id: ${FLUSS_SERVER_ID}" >> $FLUSS_HOME/conf/server.yaml && \ echo "bind.listeners: ${BIND_LISTENERS}" >> $FLUSS_HOME/conf/server.yaml && \ echo "advertised.listeners: ${ADVERTISED_LISTENERS}" >> $FLUSS_HOME/conf/server.yaml && \ + echo "security.protocol.map: INTERNAL:{{ upper .Values.listeners.internal.protocol }},CLIENT:{{ upper .Values.listeners.client.protocol }}" >> $FLUSS_HOME/conf/server.yaml && \ + + {{- if (include "fluss.sasl.enabled" .) }} + echo "security.sasl.enabled.mechanisms: PLAIN" >> $FLUSS_HOME/conf/server.yaml && \ + {{ if regexFind "SASL" (upper .Values.listeners.internal.protocol) }} + echo "client.security.protocol: SASL" >> $FLUSS_HOME/conf/server.yaml && \ + echo "client.security.sasl.mechanism: PLAIN" >> $FLUSS_HOME/conf/server.yaml && \ + {{ end }} + export FLUSS_ENV_JAVA_OPTS="-Djava.security.auth.login.config=/etc/fluss/conf/jaas.conf ${FLUSS_ENV_JAVA_OPTS}" && \ + {{- end }} bin/tablet-server.sh start-foreground livenessProbe: @@ -97,6 +107,11 @@ spec: mountPath: /opt/conf - name: data mountPath: /tmp/fluss/data + {{- if (include "fluss.sasl.enabled" .) }} + - name: sasl-config + mountPath: /etc/fluss/conf + readOnly: true + {{- end }} volumes: - name: fluss-conf configMap: @@ -105,6 +120,11 @@ spec: - name: data emptyDir: {} {{- end }} + {{- if (include "fluss.sasl.enabled" .) }} + - name: sasl-config + secret: + secretName: {{ include "fluss.fullname" . }}-sasl-jaas-config + {{- end }} {{- if .Values.tablet.storage.enabled }} volumeClaimTemplates: - metadata: diff --git a/helm/tests/sasl_test.yaml b/helm/tests/sasl_test.yaml new file mode 100644 index 0000000000..93f427ea42 --- /dev/null +++ b/helm/tests/sasl_test.yaml @@ -0,0 +1,203 @@ +# +# 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: sasl-disabled-secret +templates: + - templates/secret-jaas-config.yaml +tests: + - it: does not render sasl secret when sasl is disabled + set: + listeners.internal.protocol: PLAINTEXT + listeners.client.protocol: PLAINTEXT + asserts: + - hasDocuments: + count: 0 +--- + +suite: sasl-disabled-statefulset +templates: + - templates/sts-coordinator.yaml +tests: + - it: does not render sasl settings in pod spec when sasl is disabled + set: + listeners.internal.protocol: PLAINTEXT + listeners.client.protocol: PLAINTEXT + asserts: + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'security\.sasl\.enabled\.mechanisms:' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'security\.sasl\.plain\.jaas\.config:' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.sasl\.username:' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.sasl\.password:' + - notContains: + path: spec.template.spec.volumes + content: + name: sasl-config +--- + +suite: sasl-enabled-generated-secret +templates: + - templates/secret-jaas-config.yaml +tests: + - it: renders generated sasl secret when sasl is enabled + set: + listeners.internal.protocol: SASL_PLAINTEXT + listeners.client.protocol: SASL_PLAINTEXT + asserts: + - hasDocuments: + count: 1 + - matchRegex: + path: metadata.name + pattern: ^RELEASE-NAME-fluss-sasl-jaas-config$ + - matchRegex: + path: stringData["jaas.conf"] + pattern: 'FlussServer\s*\{' + - matchRegex: + path: stringData["jaas.conf"] + pattern: 'FlussClient\s*\{' +--- + +suite: sasl-enabled-secret-with-plaintext-internal +templates: + - templates/secret-jaas-config.yaml +tests: + - it: does not include FlussClient section when internal listener is not SASL + set: + listeners.internal.protocol: PLAINTEXT + listeners.client.protocol: SASL_PLAINTEXT + asserts: + - hasDocuments: + count: 1 + - matchRegex: + path: stringData["jaas.conf"] + pattern: 'FlussServer\s*\{' + - notMatchRegex: + path: stringData["jaas.conf"] + pattern: 'FlussClient\s*\{' +--- + +suite: sasl-enabled-statefulset-uses-generated-secret +templates: + - templates/sts-coordinator.yaml +tests: + - it: references generated secret and wires JVM JAAS config + set: + listeners.internal.protocol: SASL_PLAINTEXT + listeners.client.protocol: SASL_PLAINTEXT + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: sasl-config + secret: + secretName: RELEASE-NAME-fluss-sasl-jaas-config + - matchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'FLUSS_ENV_JAVA_OPTS="-Djava\.security\.auth\.login\.config=/etc/fluss/conf/jaas\.conf \$\{FLUSS_ENV_JAVA_OPTS\}"' + - matchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.protocol: SASL' +--- + +suite: sasl-enabled-statefulset-internal-plaintext +templates: + - templates/sts-coordinator.yaml + - templates/sts-tablet.yaml +tests: + - it: does not render internal client sasl config for coordinator when internal listener is plaintext + set: + listeners.internal.protocol: PLAINTEXT + listeners.client.protocol: SASL_PLAINTEXT + template: templates/sts-coordinator.yaml + asserts: + - matchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'security\.sasl\.enabled\.mechanisms: PLAIN' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.protocol: SASL' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.sasl\.mechanism: PLAIN' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.sasl\.username:' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.sasl\.password:' + - matchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'FLUSS_ENV_JAVA_OPTS="-Djava\.security\.auth\.login\.config=/etc/fluss/conf/jaas\.conf \$\{FLUSS_ENV_JAVA_OPTS\}"' + - it: does not render internal client sasl config for tablet when internal listener is plaintext + set: + listeners.internal.protocol: PLAINTEXT + listeners.client.protocol: SASL_PLAINTEXT + template: templates/sts-tablet.yaml + asserts: + - matchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'security\.sasl\.enabled\.mechanisms: PLAIN' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.protocol: SASL' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.sasl\.mechanism: PLAIN' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.sasl\.username:' + - notMatchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'client\.security\.sasl\.password:' + - matchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: 'FLUSS_ENV_JAVA_OPTS="-Djava\.security\.auth\.login\.config=/etc/fluss/conf/jaas\.conf \$\{FLUSS_ENV_JAVA_OPTS\}"' +--- + +suite: sasl-empty-users-fails +templates: + - templates/secret-jaas-config.yaml +tests: + - it: fails with clear message when sasl users are empty + set: + listeners.internal.protocol: SASL_PLAINTEXT + listeners.client.protocol: SASL_PLAINTEXT + security.sasl_plain.users: [] + asserts: + - failedTemplate: + errorMessage: security.sasl_plain.users must contain at least one user when SASL is enabled in listeners +--- + +suite: server-yaml-write-path +templates: + - templates/sts-tablet.yaml +tests: + - it: writes server yaml using expected destination path + asserts: + - matchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: cp /opt/conf/server\.yaml \$FLUSS_HOME/conf + - matchRegex: + path: spec.template.spec.containers[0].command[2] + pattern: '>> \$FLUSS_HOME/conf/server\.yaml' diff --git a/helm/values.yaml b/helm/values.yaml index db5d3fc2a9..086107a32a 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -54,8 +54,10 @@ coordinator: # Fluss listener configurations listeners: internal: + protocol: PLAINTEXT port: 9123 client: + protocol: PLAINTEXT port: 9124 resources: {} @@ -85,3 +87,12 @@ serviceAccount: # Additional annotations to apply to the ServiceAccount. # These can be useful, for example, to support integrations like workload identity. annotations: {} + +## Fluss security configurations for authentication. +## These are required if SASL listeners are configured. +security: + sasl_plain: + # List of client users for authentication + users: + - username: admin + password: password diff --git a/website/docs/install-deploy/deploying-with-helm.md b/website/docs/install-deploy/deploying-with-helm.md index 9bdbf414e5..c0016da075 100644 --- a/website/docs/install-deploy/deploying-with-helm.md +++ b/website/docs/install-deploy/deploying-with-helm.md @@ -36,7 +36,7 @@ the installation documentation provides instructions for deploying one using Bit ### Running Fluss locally with Minikube -For local testing and development, you can deploy Fluss on Minikube. This is ideal for development, testing, and learning purposes. +For local testing and development, you can deploy Fluss on Minikube. This is ideal for development, testing and learning purposes. #### Prerequisites @@ -157,7 +157,7 @@ kubectl logs -l app.kubernetes.io/component=tablet ## Configuration Parameters -The following table lists the configurable parameters of the Fluss chart and their default values. +The following table lists the configurable parameters of the Fluss chart, and their default values. ### Global Parameters @@ -225,6 +225,11 @@ The following table lists the configurable parameters of the Fluss chart and the | `resources.tabletServer.limits.cpu` | CPU limits for tablet servers | Not set | | `resources.tabletServer.limits.memory` | Memory limits for tablet servers | Not set | +### SASL Parameters + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `security.sasl_plain.users` | User list for SASL/PLAIN authentication | `[{username: admin, password: password}]` | ## Advanced Configuration @@ -245,16 +250,47 @@ The chart automatically configures listeners for internal cluster communication - **Internal Port (9123)**: Used for internal communication within the cluster - **Client Port (9124)**: Used for client connections -Custom listener configuration: +Default listeners configuration: ```yaml listeners: internal: + protocol: PLAINTEXT port: 9123 client: + protocol: PLAINTEXT port: 9124 ``` +To enable SASL based authentication, set any of the protocols to `SASL`. + +### Enabling Secure Connection + +With the helm deployment, you can specify authentication protocols when connecting to the Fluss cluster. + +The following table shows the supported protocols and security they provide: + +| Method | Authentication | TLS Encryption | +|-------------|:--------------:|:------------------:| +| `PLAINTEXT` | No | No | +| `SASL` | Yes | No | + +By default, the `PLAINTEXT` protocol is used. + +The SASL authentication will be enabled if any of the listener protocols is using `SASL`. + +Set these values for additional configurations: + +```yaml +security: + sasl_plain: + users: + - username: admin + password: password +``` + +The `security.sasl_plain.users` field defines the list of usernames and passwords for SASL/PLAIN authentication. When the internal listener protocol uses SASL, the first user in the list is used for internal client authentication. The authentication mechanism is fixed to `PLAIN`. + ### Storage Configuration Configure different storage volumes for coordinator or tablet pods: From 8ad271778fb1a0d5d35f3fe88970fb825a593951 Mon Sep 17 00:00:00 2001 From: morazow Date: Fri, 13 Feb 2026 16:47:54 +0100 Subject: [PATCH 2/2] [helm] Enable metrics reporting in helm charts --- helm/README.md | 43 ++++ helm/templates/_metrics.tpl | 53 +++++ helm/templates/configmap.yaml | 5 +- helm/templates/svc-coordinator.yaml | 4 + helm/templates/svc-tablet.yaml | 4 + helm/tests/metrics_test.yaml | 195 ++++++++++++++++++ helm/values.yaml | 7 + .../install-deploy/deploying-with-helm.md | 28 +++ 8 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 helm/templates/_metrics.tpl create mode 100644 helm/tests/metrics_test.yaml diff --git a/helm/README.md b/helm/README.md index 837294e5d8..b16563fec6 100644 --- a/helm/README.md +++ b/helm/README.md @@ -93,6 +93,49 @@ Important Fluss options surfaced by the chart: - internal.listener.name: Which listener is used for internal communication (defaults to INTERNAL). - tablet-server.id: Required to be unique per TabletServer. The chart auto‑derives this from the StatefulSet pod ordinal at runtime. +### Metrics and Prometheus scraping + +The chart can enable Fluss metrics reporters and add scrape annotations to Services. + +Example: + +```bash +helm install fluss ./fluss-helm \ + --set metrics.enabled=true +``` + +When enabled, the chart will: +- configure `metrics.reporters` from reporter names in values +- configure `metrics.reporter..