From 0a8137fbc459e67b9254bd4ddea35d85828edc07 Mon Sep 17 00:00:00 2001 From: glecroc Date: Thu, 30 Jul 2026 12:02:12 +0200 Subject: [PATCH 1/7] add sanMode to configure certs dns names --- charts/pulsar/templates/_certs.tpl | 73 +++++++++++++++++++++++++++--- charts/pulsar/values.yaml | 6 +++ 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/charts/pulsar/templates/_certs.tpl b/charts/pulsar/templates/_certs.tpl index 8462d901..627f6e28 100644 --- a/charts/pulsar/templates/_certs.tpl +++ b/charts/pulsar/templates/_certs.tpl @@ -59,6 +59,45 @@ Define the pulsar certs ca issuer secret name {{- end -}} {{- end -}} +{{/* +Return the StatefulSet service name for a certificate component. +Usage: {{ include "pulsar.certs.statefulset.serviceName" (dict "root" . "component" "broker") }} +*/}} +{{- define "pulsar.certs.statefulset.serviceName" -}} +{{- if eq .component "broker" -}} +{{- template "pulsar.broker.service.headless" .root -}} +{{- else if eq .component "zookeeper" -}} +{{- template "pulsar.zookeeper.service.headless" .root -}} +{{- else if eq .component "function-worker" -}} +{{- template "pulsar.function_worker.service.headless" .root -}} +{{- else -}} +{{- printf "%s-%s" (include "pulsar.fullname" .root) .component -}} +{{- end -}} +{{- end -}} + +{{/* +Return the StatefulSet replica count for a certificate component. +*/}} +{{- define "pulsar.certs.statefulset.replicaCount" -}} +{{- if eq .component "broker" -}} +{{- default 1 .root.Values.broker.replicaCount -}} +{{- else if eq .component "zookeeper" -}} +{{- default 1 .root.Values.zookeeper.replicaCount -}} +{{- else if eq .component "bookie" -}} +{{- default 1 .root.Values.bookkeeper.replicaCount -}} +{{- else if eq .component "proxy" -}} +{{- default 1 .root.Values.proxy.replicaCount -}} +{{- else if eq .component "toolset" -}} +{{- default 1 .root.Values.toolset.replicaCount -}} +{{- else if eq .component "recovery" -}} +{{- default 1 .root.Values.autorecovery.replicaCount -}} +{{- else if eq .component "function-worker" -}} +{{- default 1 .root.Values.function_worker.replicaCount -}} +{{- else -}} +1 +{{- end -}} +{{- end -}} + {{/* Common certificate template Usage: {{- include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.proxy "tlsConfig" .Values.tls.proxy) -}} @@ -106,14 +145,34 @@ spec: {{- if .tlsConfig.dnsNames }} {{ toYaml .tlsConfig.dnsNames | indent 4 }} {{- end }} - {{- if or (eq .componentConfig.component "broker") (eq .componentConfig.component "zookeeper") }} - - {{ printf "*.%s-%s-headless.%s.svc.%s" (include "pulsar.fullname" .root) .componentConfig.component (include "pulsar.namespace" .root) .root.Values.clusterDomain | quote }} - - {{ printf "%s-%s-headless.%s.svc.%s" (include "pulsar.fullname" .root) .componentConfig.component (include "pulsar.namespace" .root) .root.Values.clusterDomain | quote }} - {{- else }} - - {{ printf "*.%s-%s.%s.svc.%s" (include "pulsar.fullname" .root) .componentConfig.component (include "pulsar.namespace" .root) .root.Values.clusterDomain | quote }} + {{- $root := .root -}} + {{- $component := .componentConfig.component -}} + {{- $sanMode := default "wildcard" .root.Values.tls.common.sanMode -}} + {{- if not (has $sanMode (list "wildcard" "fqdn" "none")) -}} + {{- fail (printf "tls.common.sanMode must be one of: wildcard, fqdn, none (got %q)" $sanMode) -}} + {{- end -}} + {{- $statefulSetComponents := list "zookeeper" "bookie" "broker" "proxy" "toolset" "recovery" "function-worker" -}} + {{- $isStatefulSetComponent := has $component $statefulSetComponents -}} + {{- if eq $sanMode "wildcard" }} + {{- if or (eq $component "broker") (eq $component "zookeeper") }} + - {{ printf "*.%s-%s-headless.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} + {{- else }} + - {{ printf "*.%s-%s.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} + {{- end }} + {{- else if and (eq $sanMode "fqdn") $isStatefulSetComponent }} + {{- $serviceName := include "pulsar.certs.statefulset.serviceName" (dict "root" $root "component" $component) -}} + {{- $replicaCount := (include "pulsar.certs.statefulset.replicaCount" (dict "root" $root "component" $component) | int) -}} + {{- if gt $replicaCount 0 }} + {{- range $i := until $replicaCount }} + - {{ printf "%s-%d.%s.%s.svc.%s" (printf "%s-%s" (include "pulsar.fullname" $root) $component) $i $serviceName (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- if or (eq $component "broker") (eq $component "zookeeper") }} + - {{ printf "%s-%s-headless.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} {{- end }} - - {{ printf "%s-%s.%s.svc.%s" (include "pulsar.fullname" .root) .componentConfig.component (include "pulsar.namespace" .root) .root.Values.clusterDomain | quote }} - - {{ printf "%s-%s" (include "pulsar.fullname" .root) .componentConfig.component | quote }} + - {{ printf "%s-%s.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} + - {{ printf "%s-%s" (include "pulsar.fullname" $root) $component | quote }} {{- if .tlsConfig.ipAddresses }} ipAddresses: {{ toYaml .tlsConfig.ipAddresses | indent 4 }} diff --git a/charts/pulsar/values.yaml b/charts/pulsar/values.yaml index 772f2826..8b06c967 100755 --- a/charts/pulsar/values.yaml +++ b/charts/pulsar/values.yaml @@ -273,6 +273,12 @@ tls: keySize: 4096 keyAlgorithm: RSA keyEncoding: PKCS8 + # SAN generation mode for generated certificates: + # - wildcard (default): use wildcard SANs (existing behavior) + # - fqdn: for StatefulSet components, generate explicit per-pod FQDN SANs + # from replica counts + # - none: do not add automatic SAN entries (use tls..dnsNames and/or ipAddresses) + sanMode: wildcard # settings for generating certs for proxy proxy: enabled: false From 07aaa2a67c7b143e0e59008918c2b49234bf40bf Mon Sep 17 00:00:00 2001 From: glecroc Date: Tue, 25 Aug 2026 10:29:25 +0200 Subject: [PATCH 2/7] add sanMode fqdn autoscaling --- charts/pulsar/templates/_certs.tpl | 44 +++++++++++++++++------------- charts/pulsar/values.yaml | 23 ++++++++++++++-- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/charts/pulsar/templates/_certs.tpl b/charts/pulsar/templates/_certs.tpl index 627f6e28..5e68a646 100644 --- a/charts/pulsar/templates/_certs.tpl +++ b/charts/pulsar/templates/_certs.tpl @@ -76,26 +76,32 @@ Usage: {{ include "pulsar.certs.statefulset.serviceName" (dict "root" . "compone {{- end -}} {{/* -Return the StatefulSet replica count for a certificate component. +Return the replica count for certificate SAN FQDN generation. +When autoscaling is enabled, uses maxReplicas; otherwise uses replicaCount. +Usage: {{ include "pulsar.certs.replicaCount" (dict "root" . "component" "broker") }} */}} -{{- define "pulsar.certs.statefulset.replicaCount" -}} -{{- if eq .component "broker" -}} -{{- default 1 .root.Values.broker.replicaCount -}} -{{- else if eq .component "zookeeper" -}} -{{- default 1 .root.Values.zookeeper.replicaCount -}} -{{- else if eq .component "bookie" -}} -{{- default 1 .root.Values.bookkeeper.replicaCount -}} -{{- else if eq .component "proxy" -}} -{{- default 1 .root.Values.proxy.replicaCount -}} -{{- else if eq .component "toolset" -}} -{{- default 1 .root.Values.toolset.replicaCount -}} -{{- else if eq .component "recovery" -}} -{{- default 1 .root.Values.autorecovery.replicaCount -}} -{{- else if eq .component "function-worker" -}} -{{- default 1 .root.Values.function_worker.replicaCount -}} +{{- define "pulsar.certs.replicaCount" -}} +{{- $component := .component -}} +{{- $componentConfigs := dict + "broker" .root.Values.broker + "proxy" .root.Values.proxy + "zookeeper" .root.Values.zookeeper + "bookie" .root.Values.bookkeeper + "toolset" .root.Values.toolset + "recovery" .root.Values.autorecovery + "function-worker" .root.Values.function_worker -}} +{{- if not (hasKey $componentConfigs $component) -}} +{{- fail (printf "Unknown component %q for pulsar.certs.replicaCount" $component) -}} +{{- end -}} +{{- $componentConfig := index $componentConfigs $component -}} +{{- $autoscaledComponents := list "broker" "proxy" "function-worker" -}} +{{- if and (has $component $autoscaledComponents) $componentConfig.autoscaling.enabled -}} +{{- if not $componentConfig.autoscaling.maxReplicas -}} +{{- fail (printf "%s.autoscaling.maxReplicas must be defined when %s.autoscaling.enabled is true" $component $component) -}} +{{- end -}} +{{- $componentConfig.autoscaling.maxReplicas -}} {{- else -}} -1 -{{- end -}} +{{- $componentConfig.replicaCount -}} {{- end -}} {{/* @@ -161,7 +167,7 @@ spec: {{- end }} {{- else if and (eq $sanMode "fqdn") $isStatefulSetComponent }} {{- $serviceName := include "pulsar.certs.statefulset.serviceName" (dict "root" $root "component" $component) -}} - {{- $replicaCount := (include "pulsar.certs.statefulset.replicaCount" (dict "root" $root "component" $component) | int) -}} + {{- $replicaCount := (include "pulsar.certs.replicaCount" (dict "root" $root "component" $component) | int) -}} {{- if gt $replicaCount 0 }} {{- range $i := until $replicaCount }} - {{ printf "%s-%d.%s.%s.svc.%s" (printf "%s-%s" (include "pulsar.fullname" $root) $component) $i $serviceName (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} diff --git a/charts/pulsar/values.yaml b/charts/pulsar/values.yaml index 8b06c967..5cd0a239 100755 --- a/charts/pulsar/values.yaml +++ b/charts/pulsar/values.yaml @@ -276,8 +276,8 @@ tls: # SAN generation mode for generated certificates: # - wildcard (default): use wildcard SANs (existing behavior) # - fqdn: for StatefulSet components, generate explicit per-pod FQDN SANs - # from replica counts - # - none: do not add automatic SAN entries (use tls..dnsNames and/or ipAddresses) + # from component replicaCount or autoscaling maxReplicas + # - none: do not add automatic SAN entries sanMode: wildcard # settings for generating certs for proxy proxy: @@ -286,6 +286,7 @@ tls: # set to false if you want to use an existing certificate createCert: true # The dnsNames field specifies a list of Subject Alternative Names to be associated with the certificate. + # Works in addition to the automatically generated SANs (if any) based on the `sanMode` setting. dnsNames: # - example.com # The ipAddresses fields specifies a list of IP addresses to include as SANs in the certificate. @@ -306,6 +307,7 @@ tls: enabled: false cert_name: tls-broker # The dnsNames field specifies a list of Subject Alternative Names to be associated with the certificate. + # Works in addition to the automatically generated SANs (if any) based on the `sanMode` setting. dnsNames: # - example.com # The ipAddresses fields specifies a list of IP addresses to include as SANs in the certificate. @@ -325,6 +327,10 @@ tls: bookie: enabled: false cert_name: tls-bookie + # The dnsNames field specifies a list of Subject Alternative Names to be associated with the certificate. + # Works in addition to the automatically generated SANs (if any) based on the `sanMode` setting. + dnsNames: + # - example.com cacerts: enabled: false certs: @@ -338,6 +344,10 @@ tls: zookeeper: enabled: false cert_name: tls-zookeeper + # The dnsNames field specifies a list of Subject Alternative Names to be associated with the certificate. + # Works in addition to the automatically generated SANs (if any) based on the `sanMode` setting. + dnsNames: + # - example.com cacerts: enabled: false certs: @@ -350,6 +360,10 @@ tls: # settings for generating certs for recovery autorecovery: cert_name: tls-recovery + # The dnsNames field specifies a list of Subject Alternative Names to be associated with the certificate. + # Works in addition to the automatically generated SANs (if any) based on the `sanMode` setting. + dnsNames: + # - example.com cacerts: enabled: false certs: @@ -362,6 +376,10 @@ tls: # settings for generating certs for toolset toolset: cert_name: tls-toolset + # The dnsNames field specifies a list of Subject Alternative Names to be associated with the certificate. + # Works in addition to the automatically generated SANs (if any) based on the `sanMode` setting. + dnsNames: + # - example.com cacerts: enabled: false certs: @@ -387,6 +405,7 @@ tls: # set to false if you want to use an existing certificate createCert: true # The dnsNames field specifies a list of Subject Alternative Names to be associated with the certificate. + # Works in addition to the automatically generated SANs (if any) based on the `sanMode` setting. dnsNames: # - example.com # The ipAddresses fields specifies a list of IP addresses to include as SANs in the certificate. From 77b7dff045ec88ceb6ef209f5e308f58a034140f Mon Sep 17 00:00:00 2001 From: glecroc Date: Tue, 25 Aug 2026 10:39:24 +0200 Subject: [PATCH 3/7] sanMode none wording --- charts/pulsar/values.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/charts/pulsar/values.yaml b/charts/pulsar/values.yaml index 5cd0a239..ede91058 100755 --- a/charts/pulsar/values.yaml +++ b/charts/pulsar/values.yaml @@ -277,7 +277,9 @@ tls: # - wildcard (default): use wildcard SANs (existing behavior) # - fqdn: for StatefulSet components, generate explicit per-pod FQDN SANs # from component replicaCount or autoscaling maxReplicas - # - none: do not add automatic SAN entries + # - none: no wildcard or per-pod SANs; only the service FQDN and short name are added + # (plus the headless name for broker/zookeeper). Use tls..dnsNames + # and/or ipAddresses to add your own. sanMode: wildcard # settings for generating certs for proxy proxy: From 383e83aefd845b4c0fa0a2b31eccb81a81e628ef Mon Sep 17 00:00:00 2001 From: glecroc Date: Tue, 25 Aug 2026 11:06:35 +0200 Subject: [PATCH 4/7] fix certs fqdn when component name is renamed --- charts/pulsar/templates/_certs.tpl | 45 ++++++++----------- .../pulsar/templates/tls-certs-internal.yaml | 16 +++---- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/charts/pulsar/templates/_certs.tpl b/charts/pulsar/templates/_certs.tpl index 5e68a646..c463c951 100644 --- a/charts/pulsar/templates/_certs.tpl +++ b/charts/pulsar/templates/_certs.tpl @@ -61,14 +61,14 @@ Define the pulsar certs ca issuer secret name {{/* Return the StatefulSet service name for a certificate component. -Usage: {{ include "pulsar.certs.statefulset.serviceName" (dict "root" . "component" "broker") }} +Usage: {{ include "pulsar.certs.statefulset.serviceName" (dict "root" . "componentKey" "broker") }} */}} {{- define "pulsar.certs.statefulset.serviceName" -}} -{{- if eq .component "broker" -}} +{{- if eq .componentKey "broker" -}} {{- template "pulsar.broker.service.headless" .root -}} -{{- else if eq .component "zookeeper" -}} +{{- else if eq .componentKey "zookeeper" -}} {{- template "pulsar.zookeeper.service.headless" .root -}} -{{- else if eq .component "function-worker" -}} +{{- else if eq .componentKey "function-worker" -}} {{- template "pulsar.function_worker.service.headless" .root -}} {{- else -}} {{- printf "%s-%s" (include "pulsar.fullname" .root) .component -}} @@ -78,31 +78,21 @@ Usage: {{ include "pulsar.certs.statefulset.serviceName" (dict "root" . "compone {{/* Return the replica count for certificate SAN FQDN generation. When autoscaling is enabled, uses maxReplicas; otherwise uses replicaCount. -Usage: {{ include "pulsar.certs.replicaCount" (dict "root" . "component" "broker") }} +Usage: {{ include "pulsar.certs.replicaCount" (dict "componentKey" "broker" "componentConfig" .Values.broker) }} */}} {{- define "pulsar.certs.replicaCount" -}} -{{- $component := .component -}} -{{- $componentConfigs := dict - "broker" .root.Values.broker - "proxy" .root.Values.proxy - "zookeeper" .root.Values.zookeeper - "bookie" .root.Values.bookkeeper - "toolset" .root.Values.toolset - "recovery" .root.Values.autorecovery - "function-worker" .root.Values.function_worker -}} -{{- if not (hasKey $componentConfigs $component) -}} -{{- fail (printf "Unknown component %q for pulsar.certs.replicaCount" $component) -}} -{{- end -}} -{{- $componentConfig := index $componentConfigs $component -}} -{{- $autoscaledComponents := list "broker" "proxy" "function-worker" -}} -{{- if and (has $component $autoscaledComponents) $componentConfig.autoscaling.enabled -}} +{{- $componentKey := .componentKey -}} +{{- $componentConfig := .componentConfig -}} +{{- $autoscaledComponentKeys := list "broker" "proxy" -}} +{{- if and (has $componentKey $autoscaledComponentKeys) $componentConfig.autoscaling.enabled -}} {{- if not $componentConfig.autoscaling.maxReplicas -}} -{{- fail (printf "%s.autoscaling.maxReplicas must be defined when %s.autoscaling.enabled is true" $component $component) -}} +{{- fail (printf "%s.autoscaling.maxReplicas must be defined when %s.autoscaling.enabled is true" $componentKey $componentKey) -}} {{- end -}} {{- $componentConfig.autoscaling.maxReplicas -}} {{- else -}} {{- $componentConfig.replicaCount -}} {{- end -}} +{{- end -}} {{/* Common certificate template @@ -152,29 +142,30 @@ spec: {{ toYaml .tlsConfig.dnsNames | indent 4 }} {{- end }} {{- $root := .root -}} + {{- $componentKey := .componentKey -}} {{- $component := .componentConfig.component -}} {{- $sanMode := default "wildcard" .root.Values.tls.common.sanMode -}} {{- if not (has $sanMode (list "wildcard" "fqdn" "none")) -}} {{- fail (printf "tls.common.sanMode must be one of: wildcard, fqdn, none (got %q)" $sanMode) -}} {{- end -}} - {{- $statefulSetComponents := list "zookeeper" "bookie" "broker" "proxy" "toolset" "recovery" "function-worker" -}} - {{- $isStatefulSetComponent := has $component $statefulSetComponents -}} + {{- $statefulSetComponentKeys := list "zookeeper" "bookie" "broker" "proxy" "toolset" "recovery" "function-worker" -}} + {{- $isStatefulSetComponent := has $componentKey $statefulSetComponentKeys -}} {{- if eq $sanMode "wildcard" }} - {{- if or (eq $component "broker") (eq $component "zookeeper") }} + {{- if or (eq $componentKey "broker") (eq $componentKey "zookeeper") }} - {{ printf "*.%s-%s-headless.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} {{- else }} - {{ printf "*.%s-%s.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} {{- end }} {{- else if and (eq $sanMode "fqdn") $isStatefulSetComponent }} - {{- $serviceName := include "pulsar.certs.statefulset.serviceName" (dict "root" $root "component" $component) -}} - {{- $replicaCount := (include "pulsar.certs.replicaCount" (dict "root" $root "component" $component) | int) -}} + {{- $serviceName := include "pulsar.certs.statefulset.serviceName" (dict "root" $root "componentKey" $componentKey "component" $component) -}} + {{- $replicaCount := (include "pulsar.certs.replicaCount" (dict "componentKey" $componentKey "componentConfig" .componentConfig) | int) -}} {{- if gt $replicaCount 0 }} {{- range $i := until $replicaCount }} - {{ printf "%s-%d.%s.%s.svc.%s" (printf "%s-%s" (include "pulsar.fullname" $root) $component) $i $serviceName (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} {{- end }} {{- end }} {{- end }} - {{- if or (eq $component "broker") (eq $component "zookeeper") }} + {{- if or (eq $componentKey "broker") (eq $componentKey "zookeeper") }} - {{ printf "%s-%s-headless.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} {{- end }} - {{ printf "%s-%s.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} diff --git a/charts/pulsar/templates/tls-certs-internal.yaml b/charts/pulsar/templates/tls-certs-internal.yaml index 8d0250c0..9e394386 100644 --- a/charts/pulsar/templates/tls-certs-internal.yaml +++ b/charts/pulsar/templates/tls-certs-internal.yaml @@ -21,41 +21,41 @@ {{- if .Values.tls.proxy.enabled }} {{- if .Values.tls.proxy.createCert }} -{{ include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.proxy "tlsConfig" .Values.tls.proxy) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "proxy" "componentConfig" .Values.proxy "tlsConfig" .Values.tls.proxy) }} --- {{- end }} {{- end }} {{- if or .Values.tls.broker.enabled (or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled) }} {{- if not .Values.standalone.enabled }} -{{ include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.broker "tlsConfig" .Values.tls.broker) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "broker" "componentConfig" .Values.broker "tlsConfig" .Values.tls.broker) }} --- {{- end }} {{- end }} {{- if .Values.tls.function_worker.enabled }} {{- if and .Values.components.function_worker .Values.tls.function_worker.createCert }} -{{ include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.function_worker "tlsConfig" .Values.tls.function_worker) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "function-worker" "componentConfig" .Values.function_worker "tlsConfig" .Values.tls.function_worker) }} --- {{- end }} {{- end }} {{- if .Values.standalone.enabled }} -{{ include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.standalone "tlsConfig" .Values.tls.standalone) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "standalone" "componentConfig" .Values.standalone "tlsConfig" .Values.tls.standalone) }} --- {{- end }} {{- if or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled }} -{{ include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.bookkeeper "tlsConfig" .Values.tls.bookie) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "bookie" "componentConfig" .Values.bookkeeper "tlsConfig" .Values.tls.bookie) }} --- {{- end }} {{- if .Values.tls.zookeeper.enabled }} -{{ include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.autorecovery "tlsConfig" .Values.tls.autorecovery) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "recovery" "componentConfig" .Values.autorecovery "tlsConfig" .Values.tls.autorecovery) }} --- -{{ include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.toolset "tlsConfig" .Values.tls.toolset) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "toolset" "componentConfig" .Values.toolset "tlsConfig" .Values.tls.toolset) }} --- -{{ include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.zookeeper "tlsConfig" .Values.tls.zookeeper) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "zookeeper" "componentConfig" .Values.zookeeper "tlsConfig" .Values.tls.zookeeper) }} {{- end }} {{- end }} From c3df1b3c61563aefd039f05065fb0bc1077766d5 Mon Sep 17 00:00:00 2001 From: glecroc Date: Tue, 25 Aug 2026 17:31:31 +0200 Subject: [PATCH 5/7] re-work + add tests --- .ci/test-certificate-san-modes.sh | 219 ++++++++++++++++++ .github/workflows/pulsar-helm-chart-ci.yaml | 4 + charts/pulsar/templates/_certs.tpl | 95 ++++---- charts/pulsar/templates/_proxy.tpl | 7 + charts/pulsar/templates/proxy-service.yaml | 2 +- .../pulsar/templates/proxy-statefulset.yaml | 2 +- .../pulsar/templates/tls-certs-internal.yaml | 6 +- 7 files changed, 285 insertions(+), 50 deletions(-) create mode 100644 .ci/test-certificate-san-modes.sh diff --git a/.ci/test-certificate-san-modes.sh b/.ci/test-certificate-san-modes.sh new file mode 100644 index 00000000..53f71218 --- /dev/null +++ b/.ci/test-certificate-san-modes.sh @@ -0,0 +1,219 @@ +#!/usr/bin/env bash +# +# 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. +# + +set -euo pipefail + +chart_dir=${1:-charts/pulsar} +helm_bin=${HELM_BIN:-helm} +release_name=myrelease +namespace=mynamespace +components=(proxy broker function-worker bookie recovery toolset zookeeper) + +render_certificates() { + local san_mode=$1 + + "$helm_bin" template "$release_name" "$chart_dir" \ + --namespace "$namespace" \ + --show-only templates/tls-certs-internal.yaml \ + --set certs.internal_issuer.enabled=true \ + --set tls.enabled=true \ + --set tls.common.sanMode="$san_mode" \ + --set tls.proxy.enabled=true \ + --set tls.broker.enabled=true \ + --set tls.bookie.enabled=true \ + --set tls.zookeeper.enabled=true \ + --set tls.function_worker.enabled=true \ + --set components.function_worker=true +} + +render_standalone_certificate() { + local san_mode=$1 + + "$helm_bin" template "$release_name" "$chart_dir" \ + --namespace "$namespace" \ + --show-only templates/tls-certs-internal.yaml \ + --set certs.internal_issuer.enabled=true \ + --set standalone.enabled=true \ + --set tls.enabled=true \ + --set tls.common.sanMode="$san_mode" +} + +render_renamed_certificates() { + "$helm_bin" template "$release_name" "$chart_dir" \ + --namespace "$namespace" \ + --show-only templates/tls-certs-internal.yaml \ + --set certs.internal_issuer.enabled=true \ + --set tls.enabled=true \ + --set tls.proxy.enabled=true \ + --set tls.broker.enabled=true \ + --set tls.bookie.enabled=true \ + --set tls.zookeeper.enabled=true \ + --set tls.function_worker.enabled=true \ + --set components.function_worker=true \ + --set tls.common.sanMode=fqdn \ + --set proxy.component=renamed-proxy \ + --set broker.component=renamed-broker \ + --set function_worker.component=renamed-function-worker \ + --set bookkeeper.component=renamed-bookie \ + --set autorecovery.component=renamed-recovery \ + --set toolset.component=renamed-toolset \ + --set zookeeper.component=renamed-zookeeper +} + +render_renamed_standalone_certificate() { + "$helm_bin" template "$release_name" "$chart_dir" \ + --namespace "$namespace" \ + --show-only templates/tls-certs-internal.yaml \ + --set certs.internal_issuer.enabled=true \ + --set standalone.enabled=true \ + --set tls.enabled=true \ + --set tls.common.sanMode=fqdn \ + --set standalone.component=renamed-standalone +} + +render_autoscaled_broker_certificate() { + "$helm_bin" template "$release_name" "$chart_dir" \ + --namespace "$namespace" \ + --show-only templates/tls-certs-internal.yaml \ + --set certs.internal_issuer.enabled=true \ + --set tls.enabled=true \ + --set tls.broker.enabled=true \ + --set tls.common.sanMode=fqdn \ + --set broker.autoscaling.enabled=true \ + --set broker.autoscaling.maxReplicas=5 +} + +certificate() { + local certificate_name=$1 + awk -v certificate_name="$certificate_name" ' + { sub(/\r$/, "") } + /^---$/ { in_certificate = 0 } + $0 == " name: \"" certificate_name "\"" { in_certificate = 1 } + in_certificate { print } + ' +} + +assert_contains() { + local content=$1 + local expected=$2 + local description=$3 + + if ! grep -Fq -- "$expected" <<<"$content"; then + echo "Expected $description to contain: $expected" >&2 + exit 1 + fi +} + +assert_not_contains() { + local content=$1 + local unexpected=$2 + local description=$3 + + if grep -Fq -- "$unexpected" <<<"$content"; then + echo "Expected $description not to contain: $unexpected" >&2 + exit 1 + fi +} + +service_name() { + local component_key=$1 + local component=${2:-$component_key} + + case "$component_key" in + broker|zookeeper) + printf '%s-pulsar-%s-headless' "$release_name" "$component" + ;; + *) + printf '%s-pulsar-%s' "$release_name" "$component" + ;; + esac +} + +for san_mode in wildcard fqdn none; do + rendered=$(render_certificates "$san_mode") + rendered+=$'\n' + rendered+=$(render_standalone_certificate "$san_mode") + + for component in "${components[@]}" standalone; do + certificate_name="$release_name-pulsar-tls-$component" + certificate_manifest=$(certificate "$certificate_name" <<<"$rendered") + service="$release_name-pulsar-$component" + service_fqdn="$service.$namespace.svc.cluster.local" + + assert_contains "$certificate_manifest" "name: \"$certificate_name\"" "$component certificate" + assert_contains "$certificate_manifest" "\"$service_fqdn\"" "$component $san_mode SANs" + assert_contains "$certificate_manifest" "\"$service\"" "$component $san_mode SANs" + + case "$san_mode:$component" in + wildcard:proxy|wildcard:standalone) + assert_contains "$certificate_manifest" "\"*.$service.$namespace.svc.cluster.local\"" "$component wildcard SANs" + ;; + wildcard:*) + assert_contains "$certificate_manifest" "\"*.$(service_name "$component").$namespace.svc.cluster.local\"" "$component wildcard SANs" + ;; + fqdn:proxy|fqdn:standalone) + assert_not_contains "$certificate_manifest" "-$component-0." "$component FQDN SANs" + ;; + fqdn:*) + assert_contains "$certificate_manifest" "\"$service-0.$(service_name "$component").$namespace.svc.cluster.local\"" "$component FQDN SANs" + ;; + none:*) + assert_not_contains "$certificate_manifest" "\"*." "$component none SANs" + assert_not_contains "$certificate_manifest" "-$component-0." "$component none SANs" + ;; + esac + done +done + +renamed_certificates=$(render_renamed_certificates) +renamed_certificates+=$'\n' +renamed_certificates+=$(render_renamed_standalone_certificate) + +for component in "${components[@]}" standalone; do + certificate_name="$release_name-pulsar-tls-$component" + certificate_manifest=$(certificate "$certificate_name" <<<"$renamed_certificates") + renamed_component="renamed-$component" + renamed_service="$release_name-pulsar-$renamed_component" + + assert_contains "$certificate_manifest" \ + "\"$renamed_service.$namespace.svc.cluster.local\"" \ + "renamed $component service SANs" + + case "$component" in + proxy|standalone) + assert_not_contains "$certificate_manifest" "-$renamed_component-0." "renamed $component FQDN SANs" + ;; + *) + assert_contains "$certificate_manifest" \ + "\"$renamed_service-0.$(service_name "$component" "$renamed_component").$namespace.svc.cluster.local\"" \ + "renamed $component FQDN SANs" + ;; + esac +done + +autoscaled_broker_certificate=$(render_autoscaled_broker_certificate | certificate "$release_name-pulsar-tls-broker") +assert_contains "$autoscaled_broker_certificate" \ + "\"$release_name-pulsar-broker-4.$release_name-pulsar-broker-headless.$namespace.svc.cluster.local\"" \ + "autoscaled broker FQDN SANs" +assert_not_contains "$autoscaled_broker_certificate" \ + "\"$release_name-pulsar-broker-5.$release_name-pulsar-broker-headless.$namespace.svc.cluster.local\"" \ + "autoscaled broker FQDN SANs" + +echo "Certificate SAN mode rendering checks passed" \ No newline at end of file diff --git a/.github/workflows/pulsar-helm-chart-ci.yaml b/.github/workflows/pulsar-helm-chart-ci.yaml index 154233ab..7f74b394 100644 --- a/.github/workflows/pulsar-helm-chart-ci.yaml +++ b/.github/workflows/pulsar-helm-chart-ci.yaml @@ -229,6 +229,10 @@ jobs: -strict -kubernetes-version $kube_version -summary "$render_dir_patch1" echo "::endgroup::" + - name: Validate certificate SAN modes + if: ${{ steps.check_changes.outputs.docs_only != 'true' }} + run: bash .ci/test-certificate-san-modes.sh + - name: Save kubeconform schema cache # save the cache even when validation failed so that already downloaded # schemas don't get re-downloaded on the next attempt diff --git a/charts/pulsar/templates/_certs.tpl b/charts/pulsar/templates/_certs.tpl index c463c951..8be65ed1 100644 --- a/charts/pulsar/templates/_certs.tpl +++ b/charts/pulsar/templates/_certs.tpl @@ -60,31 +60,32 @@ Define the pulsar certs ca issuer secret name {{- end -}} {{/* -Return the StatefulSet service name for a certificate component. -Usage: {{ include "pulsar.certs.statefulset.serviceName" (dict "root" . "componentKey" "broker") }} +Return the service name for a component. +Usage: {{ include "pulsar.certs.component.service" (dict "root" . "componentKey" "broker") }} */}} -{{- define "pulsar.certs.statefulset.serviceName" -}} -{{- if eq .componentKey "broker" -}} -{{- template "pulsar.broker.service.headless" .root -}} -{{- else if eq .componentKey "zookeeper" -}} -{{- template "pulsar.zookeeper.service.headless" .root -}} -{{- else if eq .componentKey "function-worker" -}} -{{- template "pulsar.function_worker.service.headless" .root -}} -{{- else -}} -{{- printf "%s-%s" (include "pulsar.fullname" .root) .component -}} +{{- define "pulsar.certs.component.service" -}} +{{- $templateName := printf "pulsar.%s.service" .componentKey -}} +{{- include $templateName .root -}} {{- end -}} + +{{/* +Return the headless service name for a component. +Usage: {{ include "pulsar.certs.component.service.headless" (dict "root" . "componentKey" "broker") }} +*/}} +{{- define "pulsar.certs.component.service.headless" -}} +{{- $templateName := printf "pulsar.%s.service.headless" .componentKey -}} +{{- include $templateName .root -}} {{- end -}} {{/* -Return the replica count for certificate SAN FQDN generation. +Return the component replica count. When autoscaling is enabled, uses maxReplicas; otherwise uses replicaCount. -Usage: {{ include "pulsar.certs.replicaCount" (dict "componentKey" "broker" "componentConfig" .Values.broker) }} +Usage: {{ include "pulsar.certs.component.replicaCount" (dict "componentKey" "broker" "componentConfig" .Values.broker) }} */}} -{{- define "pulsar.certs.replicaCount" -}} +{{- define "pulsar.certs.component.replicaCount" -}} {{- $componentKey := .componentKey -}} {{- $componentConfig := .componentConfig -}} -{{- $autoscaledComponentKeys := list "broker" "proxy" -}} -{{- if and (has $componentKey $autoscaledComponentKeys) $componentConfig.autoscaling.enabled -}} +{{- if and $componentConfig.autoscaling $componentConfig.autoscaling.enabled -}} {{- if not $componentConfig.autoscaling.maxReplicas -}} {{- fail (printf "%s.autoscaling.maxReplicas must be defined when %s.autoscaling.enabled is true" $componentKey $componentKey) -}} {{- end -}} @@ -96,12 +97,28 @@ Usage: {{ include "pulsar.certs.replicaCount" (dict "componentKey" "broker" "com {{/* Common certificate template -Usage: {{- include "pulsar.cert.template" (dict "root" . "componentConfig" .Values.proxy "tlsConfig" .Values.tls.proxy) -}} +Usage: {{- include "pulsar.cert.template" (dict "root" . "componentKey" "proxy" "componentConfig" .Values.proxy "tlsConfig" .Values.tls.proxy) -}} */}} {{- define "pulsar.cert.template" -}} {{- if eq .root.Values.certs.internal_issuer.apiVersion "cert-manager.io/v1beta1" -}} {{- fail "cert-manager.io/v1beta1 is no longer supported. Please set certs.internal_issuer.apiVersion to cert-manager.io/v1" -}} {{- end -}} +{{- $root := .root -}} +{{- $fullname := include "pulsar.fullname" .root -}} +{{- $component := .componentConfig.component -}} +{{- $namespace := include "pulsar.namespace" .root -}} +{{- $clusterDomain := .root.Values.clusterDomain -}} +{{- $service := include "pulsar.certs.component.service" (dict "root" .root "componentKey" .componentKey "component" $component) -}} +{{- $serviceHeadless := "" -}} +{{- $serviceDns := $service -}} +{{- if or (eq .componentKey "broker") (eq .componentKey "zookeeper") }} +{{- $serviceHeadless = include "pulsar.certs.component.service.headless" (dict "root" .root "componentKey" .componentKey "component" $component) -}} +{{- $serviceDns = $serviceHeadless -}} +{{- end -}} +{{- $sanMode := .root.Values.tls.common.sanMode -}} +{{- if not (has $sanMode (list "wildcard" "fqdn" "none")) -}} +{{- fail (printf "tls.common.sanMode must be one of: wildcard, fqdn, none (got %q)" $sanMode) -}} +{{- end -}} apiVersion: "{{ .root.Values.certs.internal_issuer.apiVersion }}" kind: Certificate metadata: @@ -127,7 +144,7 @@ spec: {{ toYaml .root.Values.tls.common.organization | indent 4 }} # The use of the common name field has been deprecated since 2000 and is # discouraged from being used. - commonName: "{{ template "pulsar.fullname" .root }}-{{ .componentConfig.component }}" + commonName: "{{ template "pulsar.fullname" .root }}-{{ $component }}" isCA: false privateKey: size: {{ .root.Values.tls.common.keySize }} @@ -138,38 +155,26 @@ spec: - client auth # At least one of a DNS Name, USI SAN, or IP address is required. dnsNames: -{{- if .tlsConfig.dnsNames }} +{{ if .tlsConfig.dnsNames }} {{ toYaml .tlsConfig.dnsNames | indent 4 }} -{{- end }} - {{- $root := .root -}} - {{- $componentKey := .componentKey -}} - {{- $component := .componentConfig.component -}} - {{- $sanMode := default "wildcard" .root.Values.tls.common.sanMode -}} - {{- if not (has $sanMode (list "wildcard" "fqdn" "none")) -}} - {{- fail (printf "tls.common.sanMode must be one of: wildcard, fqdn, none (got %q)" $sanMode) -}} - {{- end -}} - {{- $statefulSetComponentKeys := list "zookeeper" "bookie" "broker" "proxy" "toolset" "recovery" "function-worker" -}} - {{- $isStatefulSetComponent := has $componentKey $statefulSetComponentKeys -}} - {{- if eq $sanMode "wildcard" }} - {{- if or (eq $componentKey "broker") (eq $componentKey "zookeeper") }} - - {{ printf "*.%s-%s-headless.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} - {{- else }} - - {{ printf "*.%s-%s.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} - {{- end }} - {{- else if and (eq $sanMode "fqdn") $isStatefulSetComponent }} - {{- $serviceName := include "pulsar.certs.statefulset.serviceName" (dict "root" $root "componentKey" $componentKey "component" $component) -}} - {{- $replicaCount := (include "pulsar.certs.replicaCount" (dict "componentKey" $componentKey "componentConfig" .componentConfig) | int) -}} +{{ end }} +{{ if eq $sanMode "wildcard" }} + - {{ printf "*.%s.%s.svc.%s" $serviceDns $namespace $clusterDomain | quote }} +{{ end }} +{{/* The proxy uses a regular ClusterIP service, so it does not need per-pod FQDN SANs. */}} +{{ if and (eq $sanMode "fqdn") (not (eq .componentKey "proxy")) }} + {{- $replicaCount := (include "pulsar.certs.component.replicaCount" (dict "componentKey" .componentKey "componentConfig" .componentConfig) | int) -}} {{- if gt $replicaCount 0 }} {{- range $i := until $replicaCount }} - - {{ printf "%s-%d.%s.%s.svc.%s" (printf "%s-%s" (include "pulsar.fullname" $root) $component) $i $serviceName (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} + - {{ printf "%s-%s-%d.%s.%s.svc.%s" $fullname $component $i $serviceDns $namespace $clusterDomain | quote }} {{- end }} {{- end }} - {{- end }} - {{- if or (eq $componentKey "broker") (eq $componentKey "zookeeper") }} - - {{ printf "%s-%s-headless.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} - {{- end }} - - {{ printf "%s-%s.%s.svc.%s" (include "pulsar.fullname" $root) $component (include "pulsar.namespace" $root) $root.Values.clusterDomain | quote }} - - {{ printf "%s-%s" (include "pulsar.fullname" $root) $component | quote }} +{{ end }} +{{ if or (eq .componentKey "broker") (eq .componentKey "zookeeper") }} + - {{ printf "%s.%s.svc.%s" $serviceHeadless $namespace $clusterDomain | quote }} +{{ end }} + - {{ printf "%s.%s.svc.%s" $service $namespace $clusterDomain | quote }} + - {{ printf "%s" $service | quote }} {{- if .tlsConfig.ipAddresses }} ipAddresses: {{ toYaml .tlsConfig.ipAddresses | indent 4 }} diff --git a/charts/pulsar/templates/_proxy.tpl b/charts/pulsar/templates/_proxy.tpl index 958dc7c6..48905ca8 100644 --- a/charts/pulsar/templates/_proxy.tpl +++ b/charts/pulsar/templates/_proxy.tpl @@ -17,6 +17,13 @@ specific language governing permissions and limitations under the License. */}} +{{/* +Define the proxy service (ordinary ClusterIP, targeted by clients) +*/}} +{{- define "pulsar.proxy.service" -}} +{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }} +{{- end }} + {{/* Define proxy tls certs mounts */}} diff --git a/charts/pulsar/templates/proxy-service.yaml b/charts/pulsar/templates/proxy-service.yaml index c3d87e91..e3fb0281 100644 --- a/charts/pulsar/templates/proxy-service.yaml +++ b/charts/pulsar/templates/proxy-service.yaml @@ -21,7 +21,7 @@ apiVersion: v1 kind: Service metadata: - name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}" + name: "{{ template "pulsar.proxy.service" . }}" namespace: {{ template "pulsar.namespace" . }} labels: {{- include "pulsar.standardLabels" . | nindent 4 }} diff --git a/charts/pulsar/templates/proxy-statefulset.yaml b/charts/pulsar/templates/proxy-statefulset.yaml index 3460f882..20ef5f90 100644 --- a/charts/pulsar/templates/proxy-statefulset.yaml +++ b/charts/pulsar/templates/proxy-statefulset.yaml @@ -28,7 +28,7 @@ metadata: {{- include "pulsar.standardLabels" . | nindent 4 }} component: {{ .Values.proxy.component }} spec: - serviceName: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}" + serviceName: "{{ template "pulsar.proxy.service" . }}" {{- if not .Values.proxy.autoscaling.enabled }} replicas: {{ .Values.proxy.replicaCount }} {{- end }} diff --git a/charts/pulsar/templates/tls-certs-internal.yaml b/charts/pulsar/templates/tls-certs-internal.yaml index 9e394386..6d00df22 100644 --- a/charts/pulsar/templates/tls-certs-internal.yaml +++ b/charts/pulsar/templates/tls-certs-internal.yaml @@ -35,7 +35,7 @@ {{- if .Values.tls.function_worker.enabled }} {{- if and .Values.components.function_worker .Values.tls.function_worker.createCert }} -{{ include "pulsar.cert.template" (dict "root" . "componentKey" "function-worker" "componentConfig" .Values.function_worker "tlsConfig" .Values.tls.function_worker) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "function_worker" "componentConfig" .Values.function_worker "tlsConfig" .Values.tls.function_worker) }} --- {{- end }} {{- end }} @@ -46,12 +46,12 @@ {{- end }} {{- if or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled }} -{{ include "pulsar.cert.template" (dict "root" . "componentKey" "bookie" "componentConfig" .Values.bookkeeper "tlsConfig" .Values.tls.bookie) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "bookkeeper" "componentConfig" .Values.bookkeeper "tlsConfig" .Values.tls.bookie) }} --- {{- end }} {{- if .Values.tls.zookeeper.enabled }} -{{ include "pulsar.cert.template" (dict "root" . "componentKey" "recovery" "componentConfig" .Values.autorecovery "tlsConfig" .Values.tls.autorecovery) }} +{{ include "pulsar.cert.template" (dict "root" . "componentKey" "autorecovery" "componentConfig" .Values.autorecovery "tlsConfig" .Values.tls.autorecovery) }} --- {{ include "pulsar.cert.template" (dict "root" . "componentKey" "toolset" "componentConfig" .Values.toolset "tlsConfig" .Values.tls.toolset) }} --- From 072c6124abc89d5b409e839bfc5fed461b332a60 Mon Sep 17 00:00:00 2001 From: glecroc Date: Wed, 26 Aug 2026 13:53:44 +0200 Subject: [PATCH 6/7] doc sanMode modification need pod restart --- charts/pulsar/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/pulsar/values.yaml b/charts/pulsar/values.yaml index ede91058..7377b57d 100755 --- a/charts/pulsar/values.yaml +++ b/charts/pulsar/values.yaml @@ -275,11 +275,11 @@ tls: keyEncoding: PKCS8 # SAN generation mode for generated certificates: # - wildcard (default): use wildcard SANs (existing behavior) - # - fqdn: for StatefulSet components, generate explicit per-pod FQDN SANs - # from component replicaCount or autoscaling maxReplicas + # - fqdn: generate explicit per-pod FQDN SANs from component replicaCount or autoscaling maxReplicas # - none: no wildcard or per-pod SANs; only the service FQDN and short name are added # (plus the headless name for broker/zookeeper). Use tls..dnsNames # and/or ipAddresses to add your own. + # /!\ Any modification will require a restart of the component pods to pick up the new certs. sanMode: wildcard # settings for generating certs for proxy proxy: From c05790941a002cecf4ae3f0e7a6dde215095d4da Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Tue, 6 Oct 2026 11:27:13 -0700 Subject: [PATCH 7/7] Use headless service names for function worker and standalone FQDN SANs In fqdn mode, the function worker per-pod SANs used the ClusterIP service name, but the function worker StatefulSet publishes pod DNS records under its headless service. The standalone pod's advertised name (.) wasn't included at all. Build the per-pod names from the headless service for function_worker and standalone, and add the standalone pod's FQDN in fqdn mode. Wildcard and none mode output is unchanged. --- .ci/test-certificate-san-modes.sh | 10 +++++++--- charts/pulsar/templates/_certs.tpl | 13 ++++++++++++- charts/pulsar/values.yaml | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.ci/test-certificate-san-modes.sh b/.ci/test-certificate-san-modes.sh index 53f71218..9fea0451 100644 --- a/.ci/test-certificate-san-modes.sh +++ b/.ci/test-certificate-san-modes.sh @@ -137,7 +137,7 @@ service_name() { local component=${2:-$component_key} case "$component_key" in - broker|zookeeper) + broker|zookeeper|function-worker) printf '%s-pulsar-%s-headless' "$release_name" "$component" ;; *) @@ -162,15 +162,19 @@ for san_mode in wildcard fqdn none; do assert_contains "$certificate_manifest" "\"$service\"" "$component $san_mode SANs" case "$san_mode:$component" in - wildcard:proxy|wildcard:standalone) + wildcard:proxy|wildcard:standalone|wildcard:function-worker) assert_contains "$certificate_manifest" "\"*.$service.$namespace.svc.cluster.local\"" "$component wildcard SANs" ;; wildcard:*) assert_contains "$certificate_manifest" "\"*.$(service_name "$component").$namespace.svc.cluster.local\"" "$component wildcard SANs" ;; - fqdn:proxy|fqdn:standalone) + fqdn:proxy) assert_not_contains "$certificate_manifest" "-$component-0." "$component FQDN SANs" ;; + fqdn:standalone) + assert_not_contains "$certificate_manifest" "-$component-0." "$component FQDN SANs" + assert_contains "$certificate_manifest" "\"$component.$release_name-pulsar-$component-headless.$namespace.svc.cluster.local\"" "$component FQDN SANs" + ;; fqdn:*) assert_contains "$certificate_manifest" "\"$service-0.$(service_name "$component").$namespace.svc.cluster.local\"" "$component FQDN SANs" ;; diff --git a/charts/pulsar/templates/_certs.tpl b/charts/pulsar/templates/_certs.tpl index 8be65ed1..8fdf9cdd 100644 --- a/charts/pulsar/templates/_certs.tpl +++ b/charts/pulsar/templates/_certs.tpl @@ -115,6 +115,13 @@ Usage: {{- include "pulsar.cert.template" (dict "root" . "componentKey" "proxy" {{- $serviceHeadless = include "pulsar.certs.component.service.headless" (dict "root" .root "componentKey" .componentKey "component" $component) -}} {{- $serviceDns = $serviceHeadless -}} {{- end -}} +{{- /* Per-pod FQDNs are under the service the pod's DNS records are published in: the StatefulSet's +serviceName, or the standalone Deployment's subdomain. These are headless services for broker, zookeeper, +function_worker and standalone. */ -}} +{{- $podServiceDns := $serviceDns -}} +{{- if or (eq .componentKey "function_worker") (eq .componentKey "standalone") }} +{{- $podServiceDns = include "pulsar.certs.component.service.headless" (dict "root" .root "componentKey" .componentKey "component" $component) -}} +{{- end -}} {{- $sanMode := .root.Values.tls.common.sanMode -}} {{- if not (has $sanMode (list "wildcard" "fqdn" "none")) -}} {{- fail (printf "tls.common.sanMode must be one of: wildcard, fqdn, none (got %q)" $sanMode) -}} @@ -166,10 +173,14 @@ spec: {{- $replicaCount := (include "pulsar.certs.component.replicaCount" (dict "componentKey" .componentKey "componentConfig" .componentConfig) | int) -}} {{- if gt $replicaCount 0 }} {{- range $i := until $replicaCount }} - - {{ printf "%s-%s-%d.%s.%s.svc.%s" $fullname $component $i $serviceDns $namespace $clusterDomain | quote }} + - {{ printf "%s-%s-%d.%s.%s.svc.%s" $fullname $component $i $podServiceDns $namespace $clusterDomain | quote }} {{- end }} {{- end }} {{ end }} +{{- /* The standalone pod uses its component name as hostname in the headless service subdomain. */}} +{{- if and (eq $sanMode "fqdn") (eq .componentKey "standalone") }} + - {{ printf "%s.%s.%s.svc.%s" $component $podServiceDns $namespace $clusterDomain | quote }} +{{- end }} {{ if or (eq .componentKey "broker") (eq .componentKey "zookeeper") }} - {{ printf "%s.%s.svc.%s" $serviceHeadless $namespace $clusterDomain | quote }} {{ end }} diff --git a/charts/pulsar/values.yaml b/charts/pulsar/values.yaml index 96590f10..9551047e 100755 --- a/charts/pulsar/values.yaml +++ b/charts/pulsar/values.yaml @@ -268,6 +268,7 @@ tls: # SAN generation mode for generated certificates: # - wildcard (default): use wildcard SANs (existing behavior) # - fqdn: generate explicit per-pod FQDN SANs from component replicaCount or autoscaling maxReplicas + # (for standalone, the pod's FQDN in the standalone headless service) # - none: no wildcard or per-pod SANs; only the service FQDN and short name are added # (plus the headless name for broker/zookeeper). Use tls..dnsNames # and/or ipAddresses to add your own.