diff --git a/.ci/test-certificate-san-modes.sh b/.ci/test-certificate-san-modes.sh new file mode 100644 index 00000000..9fea0451 --- /dev/null +++ b/.ci/test-certificate-san-modes.sh @@ -0,0 +1,223 @@ +#!/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|function-worker) + 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|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) + 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" + ;; + 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 be3f8fad..e549838f 100644 --- a/.github/workflows/pulsar-helm-chart-ci.yaml +++ b/.github/workflows/pulsar-helm-chart-ci.yaml @@ -233,6 +233,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 8462d901..8fdf9cdd 100644 --- a/charts/pulsar/templates/_certs.tpl +++ b/charts/pulsar/templates/_certs.tpl @@ -59,14 +59,73 @@ Define the pulsar certs ca issuer secret name {{- end -}} {{- end -}} +{{/* +Return the service name for a component. +Usage: {{ include "pulsar.certs.component.service" (dict "root" . "componentKey" "broker") }} +*/}} +{{- 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 component replica count. +When autoscaling is enabled, uses maxReplicas; otherwise uses replicaCount. +Usage: {{ include "pulsar.certs.component.replicaCount" (dict "componentKey" "broker" "componentConfig" .Values.broker) }} +*/}} +{{- define "pulsar.certs.component.replicaCount" -}} +{{- $componentKey := .componentKey -}} +{{- $componentConfig := .componentConfig -}} +{{- 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 -}} +{{- $componentConfig.autoscaling.maxReplicas -}} +{{- else -}} +{{- $componentConfig.replicaCount -}} +{{- end -}} +{{- end -}} + {{/* 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 -}} +{{- /* 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) -}} +{{- end -}} apiVersion: "{{ .root.Values.certs.internal_issuer.apiVersion }}" kind: Certificate metadata: @@ -92,7 +151,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 }} @@ -103,17 +162,30 @@ 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 }} +{{ 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-%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 .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 }} - {{- 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 }} +{{ 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 8d0250c0..6d00df22 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" "bookkeeper" "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" "autorecovery" "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 }} diff --git a/charts/pulsar/values.yaml b/charts/pulsar/values.yaml index e269551c..9551047e 100755 --- a/charts/pulsar/values.yaml +++ b/charts/pulsar/values.yaml @@ -265,6 +265,15 @@ tls: keySize: 4096 keyAlgorithm: RSA keyEncoding: PKCS8 + # 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. + # /!\ 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 @@ -272,6 +281,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. @@ -292,6 +302,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. @@ -311,6 +322,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: @@ -324,6 +339,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: @@ -336,6 +355,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: @@ -348,6 +371,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: @@ -373,6 +400,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.