Skip to content
Merged
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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,11 @@ These changes don't require changes to your `values.yaml`, but they change what
explicit FQDN for each pod instead of the wildcard. `none` adds only the service names, so you can add your own
with `tls.<component>.dnsNames` and `tls.<component>.ipAddresses`. Restart the component pods after
changing it so that they pick up the reissued certificates.
- **Externally managed certificates:** every component now supports `tls.<component>.createCert: false`, not
only the proxy and the function worker, so you can provide all the certificate Secrets yourself. The proxy
Secret no longer needs the `tls-combined.pem` key, so a publicly trusted certificate can be used for the
proxy while the other components use certificates from a private CA. The `## TLS` comments in `values.yaml`
describe the supported setups and the required Secret keys.

## Upgrading to Helm chart version 4.6.0

Expand Down
14 changes: 13 additions & 1 deletion charts/pulsar/templates/_certs.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,23 @@ Define the pulsar certs ca issuer secret name
{{- if .Values.certs.issuers.ca.secretName -}}
{{- .Values.certs.issuers.ca.secretName -}}
{{- else -}}
{{- fail "certs.issuers.ca.secretName is required when TLS is enabled and certs.internal_issuer.enabled is false" -}}
{{- fail "certs.issuers.ca.secretName is required when TLS is enabled and certs.internal_issuer.enabled is false. Set it to the name of a Secret with a ca.crt key that contains the CA certificates the components use to verify each other's TLS certificates." -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Return "true" when the chart should create the cert-manager Certificate for a component.
Only an explicit `createCert: false` disables it, so that values without the key
(for example from `helm upgrade --reuse-values`) keep creating the certificate.
Usage: {{ include "pulsar.tls.createCert" .Values.tls.broker }}
*/}}
{{- define "pulsar.tls.createCert" -}}
{{- if not (and (hasKey . "createCert") (eq (toString .createCert) "false")) -}}
true
{{- end -}}
{{- end -}}

{{/*
Return the service name for a component.
Usage: {{ include "pulsar.certs.component.service" (dict "root" . "componentKey" "broker") }}
Expand Down
4 changes: 0 additions & 4 deletions charts/pulsar/templates/_proxy.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,6 @@ Define proxy tls certs volumes
path: tls.crt
- key: tls.key
path: tls.key
{{- if .Values.tls.zookeeper.enabled }}
- key: tls-combined.pem
path: tls-combined.pem
{{- end }}
{{- end }}
- name: ca
secret:
Expand Down
16 changes: 11 additions & 5 deletions charts/pulsar/templates/tls-certs-internal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,48 @@
{{- if .Values.tls.enabled }}

{{- if .Values.tls.proxy.enabled }}
{{- if .Values.tls.proxy.createCert }}
{{- if include "pulsar.tls.createCert" .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 }}
{{- if and (not .Values.standalone.enabled) (include "pulsar.tls.createCert" .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 }}
{{- if and .Values.components.function_worker (include "pulsar.tls.createCert" .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 }}
{{- if and .Values.standalone.enabled (include "pulsar.tls.createCert" .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 }}
{{- if and (or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled) (include "pulsar.tls.createCert" .Values.tls.bookie) }}
{{ include "pulsar.cert.template" (dict "root" . "componentKey" "bookkeeper" "componentConfig" .Values.bookkeeper "tlsConfig" .Values.tls.bookie) }}
---
{{- end }}

{{- if .Values.tls.zookeeper.enabled }}
{{- if include "pulsar.tls.createCert" .Values.tls.autorecovery }}
{{ include "pulsar.cert.template" (dict "root" . "componentKey" "autorecovery" "componentConfig" .Values.autorecovery "tlsConfig" .Values.tls.autorecovery) }}
---
{{- end }}
{{- if include "pulsar.tls.createCert" .Values.tls.toolset }}
{{ include "pulsar.cert.template" (dict "root" . "componentKey" "toolset" "componentConfig" .Values.toolset "tlsConfig" .Values.tls.toolset) }}
---
{{- end }}
{{- if include "pulsar.tls.createCert" .Values.tls.zookeeper }}
{{ include "pulsar.cert.template" (dict "root" . "componentKey" "zookeeper" "componentConfig" .Values.zookeeper "tlsConfig" .Values.tls.zookeeper) }}
{{- end }}
{{- end }}

{{- end }}
113 changes: 104 additions & 9 deletions charts/pulsar/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,77 @@ images:
pullPolicy:

## TLS
## templates/tls-certs.yaml
## templates/tls-certs-internal.yaml
##
## The chart is using cert-manager for provisioning TLS certs for
## brokers and proxies.
## The chart uses cert-manager to create the TLS certificates of the components. Each component reads its
## certificate and private key from the Secret "<release name>-<tls.<component>.cert_name>" (for example
## "pulsar-tls-broker"), and verifies the certificates of the other components with the CA certificates in the
## "ca.crt" key of the CA Secret (see `certs.internal_issuer` and `certs.issuers.ca` below).
##
## Certificates issued by a public CA (for example with an ACME issuer such as Let's Encrypt):
## Publicly trusted certificates can't be used for the connections between the components, for two reasons:
## - A public CA can't issue certificates for cluster-internal names such as
## "<release>-pulsar-broker.<namespace>.svc.cluster.local". The components connect to each other using these
## Kubernetes service and pod names and verify that the server certificate matches the name, so the
## certificates used between the components must include them. The chart therefore always adds the
## Kubernetes service names to the certificates it creates (see `tls.common.sanMode`).
## - The components also present their certificate as a TLS client certificate: the brokers to the bookies,
## and all ZooKeeper clients to ZooKeeper when `tls.zookeeper.enabled` is true. Public CAs are removing the
## clientAuth extended key usage from the TLS certificates they issue (Let's Encrypt removed it from its
## default profile in February 2026), so a renewed public certificate can only be used as a server
## certificate. See https://news.apache.org/foundation/entry/the-public-ca-clientauth-eku-sunset-what-apache-software-deployers-need-to-know
## Use a private CA for the certificates of the components: `certs.internal_issuer`, or for production, a
## cert-manager issuer backed by a private CA such as OpenBao, HashiCorp Vault or step-ca, configured with
## `certs.internal_issuer.enabled: false` and `certs.issuers.ca` (name, kind, group and secretName).
## The recommended setup is to use a publicly trusted certificate only where clients connect from outside the
## cluster, which is the proxy. It only needs the server auth usage:
## tls:
## enabled: true
## proxy:
## enabled: true
## # the chart doesn't create the proxy certificate; create the Secret "<release>-tls-proxy" yourself,
## # for example with a cert-manager Certificate that uses your public ClusterIssuer and has
## # `secretName: <release>-tls-proxy` and the public DNS names of the proxy in `dnsNames`
## createCert: false
## broker:
## enabled: true
## certs:
## internal_issuer:
## enabled: true
## The proxy uses only the "tls.crt" and "tls.key" keys of its Secret, and it verifies the broker certificates
## with the internal CA. Clients can verify the publicly trusted proxy certificate with their default trust
## store. Clients that authenticate with TLS client certificates (mTLS) need certificates from a private CA
## for the same reason, since a public certificate without the clientAuth usage can't be used as a client
## certificate.
## When clients connect to the brokers directly instead of through the proxy, a topic lookup returns the
## address advertised by the broker that owns the topic, and the client verifies the broker certificate
## against that address. By default, the brokers advertise their pod FQDN in the broker headless service,
## which external clients can't reach and a public CA can't issue certificates for. Direct access from
## outside the cluster requires an external listener in `advertisedListeners` with a reachable DNS name for
## each broker, and a broker certificate that matches each of these names, either with a wildcard or with an
## individual name for each broker. The chart doesn't support this yet. See
## https://pulsar.apache.org/docs/next/concepts-multiple-advertised-listeners/#kubernetes-deployment-suggestions
##
## Certificates that you manage yourself (`createCert: false`):
## Set `tls.<component>.createCert: false` for each component whose certificate you provide, and create the
## Secret "<release>-<tls.<component>.cert_name>" before installing the chart. The Secret must contain:
## - "tls.crt": the certificate chain, starting with the component's certificate.
## - "tls.key": the private key in PKCS#8 PEM format ("-----BEGIN PRIVATE KEY-----").
## - "tls-combined.pem" (only when `tls.zookeeper.enabled` is true, for the broker, bookie, autorecovery,
## toolset and zookeeper certificates): the private key followed by the certificate chain in a single PEM
## file. ZooKeeper reads this file as its PEM key store. It can only read PKCS#8 keys, so a PKCS#1 key
## ("-----BEGIN RSA PRIVATE KEY-----") fails with
## "InvalidKeySpecException: Inappropriate key specification: Unable to decode key".
## With cert-manager, these keys are created by a Certificate that has `privateKey.encoding: PKCS8` and
## `additionalOutputFormats: [{type: CombinedPEM}]`. CombinedPEM requires cert-manager 1.15 or later, or the
## `AdditionalCertificateOutputFormats=true` feature gate in earlier versions.
## The certificates must include the Kubernetes names that the other components use to connect to the
## component, as listed for `tls.common.sanMode`. Except for the proxy certificate, they must allow both the
## server auth and client auth usages, so they must be issued by a private CA (see above).
## When `certs.internal_issuer.enabled` is false, set `certs.issuers.ca.secretName` to a Secret with a
## "ca.crt" key that contains the CA certificates that issued the component certificates. The components
## use it as their trust store, so it's required even when the chart doesn't create any certificates.
## `certs.issuers.ca.name` is only needed when the chart creates certificates.
tls:
enabled: false
ca_suffix: ca-tls
Expand Down Expand Up @@ -348,13 +415,20 @@ tls:
# - 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.<component>.dnsNames
# and/or ipAddresses to add your own.
# In every mode, the certificates include the Kubernetes names that the other components use to connect
# to the component, because the connections verify that the certificate matches the name:
# - "<release>-pulsar-<component>" and "<release>-pulsar-<component>.<namespace>.svc.<clusterDomain>"
# - for broker and zookeeper, also "<release>-pulsar-<component>-headless.<namespace>.svc.<clusterDomain>"
# These names can't be removed. Certificates from a public CA can't include them, so use a publicly
# trusted certificate only for the proxy, as described in the TLS comments above.
# /!\ 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:
enabled: false
cert_name: tls-proxy
# set to false if you want to use an existing certificate
# Set to false to provide the certificate Secret "<release>-<cert_name>" yourself instead of having the
# chart create it with cert-manager. See the TLS comments above for the required Secret keys.
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.
Expand All @@ -377,6 +451,9 @@ tls:
broker:
enabled: false
cert_name: tls-broker
# Set to false to provide the certificate Secret "<release>-<cert_name>" yourself instead of having the
# chart create it with cert-manager. See the TLS comments above for the required Secret keys.
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:
Expand All @@ -398,6 +475,9 @@ tls:
bookie:
enabled: false
cert_name: tls-bookie
# Set to false to provide the certificate Secret "<release>-<cert_name>" yourself instead of having the
# chart create it with cert-manager. See the TLS comments above for the required Secret keys.
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:
Expand All @@ -415,6 +495,9 @@ tls:
zookeeper:
enabled: false
cert_name: tls-zookeeper
# Set to false to provide the certificate Secret "<release>-<cert_name>" yourself instead of having the
# chart create it with cert-manager. See the TLS comments above for the required Secret keys.
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:
Expand All @@ -431,6 +514,9 @@ tls:
# settings for generating certs for recovery
autorecovery:
cert_name: tls-recovery
# Set to false to provide the certificate Secret "<release>-<cert_name>" yourself instead of having the
# chart create it with cert-manager. See the TLS comments above for the required Secret keys.
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:
Expand All @@ -447,6 +533,9 @@ tls:
# settings for generating certs for toolset
toolset:
cert_name: tls-toolset
# Set to false to provide the certificate Secret "<release>-<cert_name>" yourself instead of having the
# chart create it with cert-manager. See the TLS comments above for the required Secret keys.
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:
Expand All @@ -463,6 +552,9 @@ tls:
# settings for standalone TLS cert (used when tls.enabled is true and standalone.enabled is true)
standalone:
cert_name: tls-standalone
# Set to false to provide the certificate Secret "<release>-<cert_name>" yourself instead of having the
# chart create it with cert-manager. See the TLS comments above for the required Secret keys.
createCert: true
# Annotations to apply to the secrets generated by the cert-manager certificate
# secretAnnotations: {}
# TLS setting for function runtime instance
Expand All @@ -473,7 +565,8 @@ tls:
function_worker:
enabled: false
cert_name: tls-function-worker
# set to false if you want to use an existing certificate
# Set to false to provide the certificate Secret "<release>-<cert_name>" yourself instead of having the
# chart create it with cert-manager. See the TLS comments above for the required Secret keys.
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.
Expand Down Expand Up @@ -619,11 +712,13 @@ certs:
secretName:
# used for certs.internal_issuer.type as ca or when internal_issuer is disabled
ca:
# The name of the issuer, it is mandatory to specify this value if TLS is enabled
# and selfsigning is not used
# The name of the cert-manager issuer that issues the component certificates. Required when TLS is enabled,
# selfsigning is not used and the chart creates at least one certificate (createCert is not false).
name:
# The secret name of the CA certificate, it is mandatory to specify this value if TLS is enabled
# and selfsigning is not used
# The name of the Secret with the CA certificate in its "ca.crt" key. Required when TLS is enabled and
# selfsigning is not used. The components mount it as the trust store that they use to verify each other's
# certificates, so it's required even when you provide all the certificates yourself (createCert: false).
# With certs.internal_issuer.type ca, the Secret must also contain the CA private key ("tls.key").
secretName:
# The k8s kind of the ca issuer: Issuer or ClusterIssuer
kind: Issuer
Expand Down