From ad066730c510c918f69b2692aa7152b22d2d4e71 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Fri, 13 Mar 2026 19:30:11 +0530 Subject: [PATCH 01/11] updated tls config to include acme --- ...utorecoveries.kaap.oss.datastax.com-v1.yml | 193 +++++++++++++++--- .../bastions.kaap.oss.datastax.com-v1.yml | 193 +++++++++++++++--- .../bookkeepers.kaap.oss.datastax.com-v1.yml | 193 +++++++++++++++--- .../crds/brokers.kaap.oss.datastax.com-v1.yml | 193 +++++++++++++++--- ...ctionsworkers.kaap.oss.datastax.com-v1.yml | 193 +++++++++++++++--- .../crds/proxies.kaap.oss.datastax.com-v1.yml | 193 +++++++++++++++--- ...ulsarclusters.kaap.oss.datastax.com-v1.yml | 193 +++++++++++++++--- .../zookeepers.kaap.oss.datastax.com-v1.yml | 193 +++++++++++++++--- .../CertManagerCertificatesProvisioner.java | 28 +-- .../oss/kaap/crds/configs/tls/TlsConfig.java | 78 +++++-- .../datastax/oss/kaap/tests/helm/TlsTest.java | 12 +- 11 files changed, 1398 insertions(+), 264 deletions(-) diff --git a/helm/kaap/crds/autorecoveries.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/autorecoveries.kaap.oss.datastax.com-v1.yml index 332ac1e8..4c559ab7 100644 --- a/helm/kaap/crds/autorecoveries.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/autorecoveries.kaap.oss.datastax.com-v1.yml @@ -1682,6 +1682,157 @@ spec: certProvisioner: description: "Certificate provisioner configuration." properties: + acme: + description: "ACME certificate provisioner configuration." + properties: + broker: + description: "Broker self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + enabled: + description: "Generate self signed certificates for\ + \ broker, proxy and functions worker." + type: "boolean" + external: + additionalProperties: + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject\ + \ Alternative Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key\ + \ will be stored whe perComponent is enabled.\ + \ Required for external services. Internal\ + \ services pick up the secret name from the\ + \ tls config if not specified" + type: "string" + type: "object" + description: "External services self signed certificate\ + \ config (e.g., admin console, grafana). The key\ + \ is the service name, and the value contains generation\ + \ config" + type: "object" + issuer: + description: "ACME issuer configuration." + properties: + email: + description: "Email used for ACME registration." + type: "string" + ingressClass: + description: "Ingress class used for HTTP01 challenge." + type: "string" + name: + description: "Name of the Issuer resource." + type: "string" + privateKeySecretName: + description: "Secret storing the ACME account\ + \ private key." + type: "string" + server: + description: "ACME server URL." + type: "string" + type: "object" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + proxy: + description: "Proxy self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + type: "object" selfSigned: description: "Self signed certificate provisioner configuration." properties: @@ -1692,14 +1843,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1729,14 +1878,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1766,14 +1913,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1810,14 +1955,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject\ - \ Alternative Names (SANs) extension along\ - \ with the default K8s service DNS." + \ Alternative Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1853,14 +1996,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1911,14 +2052,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1948,14 +2087,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/bastions.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/bastions.kaap.oss.datastax.com-v1.yml index 98790279..3f6a6b71 100644 --- a/helm/kaap/crds/bastions.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/bastions.kaap.oss.datastax.com-v1.yml @@ -1686,6 +1686,157 @@ spec: certProvisioner: description: "Certificate provisioner configuration." properties: + acme: + description: "ACME certificate provisioner configuration." + properties: + broker: + description: "Broker self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + enabled: + description: "Generate self signed certificates for\ + \ broker, proxy and functions worker." + type: "boolean" + external: + additionalProperties: + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject\ + \ Alternative Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key\ + \ will be stored whe perComponent is enabled.\ + \ Required for external services. Internal\ + \ services pick up the secret name from the\ + \ tls config if not specified" + type: "string" + type: "object" + description: "External services self signed certificate\ + \ config (e.g., admin console, grafana). The key\ + \ is the service name, and the value contains generation\ + \ config" + type: "object" + issuer: + description: "ACME issuer configuration." + properties: + email: + description: "Email used for ACME registration." + type: "string" + ingressClass: + description: "Ingress class used for HTTP01 challenge." + type: "string" + name: + description: "Name of the Issuer resource." + type: "string" + privateKeySecretName: + description: "Secret storing the ACME account\ + \ private key." + type: "string" + server: + description: "ACME server URL." + type: "string" + type: "object" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + proxy: + description: "Proxy self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + type: "object" selfSigned: description: "Self signed certificate provisioner configuration." properties: @@ -1696,14 +1847,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1733,14 +1882,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1770,14 +1917,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1814,14 +1959,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject\ - \ Alternative Names (SANs) extension along\ - \ with the default K8s service DNS." + \ Alternative Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1857,14 +2000,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1915,14 +2056,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -1952,14 +2091,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/bookkeepers.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/bookkeepers.kaap.oss.datastax.com-v1.yml index 9efe0ae1..beac153b 100644 --- a/helm/kaap/crds/bookkeepers.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/bookkeepers.kaap.oss.datastax.com-v1.yml @@ -5029,6 +5029,157 @@ spec: certProvisioner: description: "Certificate provisioner configuration." properties: + acme: + description: "ACME certificate provisioner configuration." + properties: + broker: + description: "Broker self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + enabled: + description: "Generate self signed certificates for\ + \ broker, proxy and functions worker." + type: "boolean" + external: + additionalProperties: + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject\ + \ Alternative Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key\ + \ will be stored whe perComponent is enabled.\ + \ Required for external services. Internal\ + \ services pick up the secret name from the\ + \ tls config if not specified" + type: "string" + type: "object" + description: "External services self signed certificate\ + \ config (e.g., admin console, grafana). The key\ + \ is the service name, and the value contains generation\ + \ config" + type: "object" + issuer: + description: "ACME issuer configuration." + properties: + email: + description: "Email used for ACME registration." + type: "string" + ingressClass: + description: "Ingress class used for HTTP01 challenge." + type: "string" + name: + description: "Name of the Issuer resource." + type: "string" + privateKeySecretName: + description: "Secret storing the ACME account\ + \ private key." + type: "string" + server: + description: "ACME server URL." + type: "string" + type: "object" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + proxy: + description: "Proxy self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + type: "object" selfSigned: description: "Self signed certificate provisioner configuration." properties: @@ -5039,14 +5190,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5076,14 +5225,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5113,14 +5260,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5157,14 +5302,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject\ - \ Alternative Names (SANs) extension along\ - \ with the default K8s service DNS." + \ Alternative Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5200,14 +5343,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5258,14 +5399,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5295,14 +5434,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/brokers.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/brokers.kaap.oss.datastax.com-v1.yml index b654aa1c..39b186c3 100644 --- a/helm/kaap/crds/brokers.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/brokers.kaap.oss.datastax.com-v1.yml @@ -4961,6 +4961,157 @@ spec: certProvisioner: description: "Certificate provisioner configuration." properties: + acme: + description: "ACME certificate provisioner configuration." + properties: + broker: + description: "Broker self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + enabled: + description: "Generate self signed certificates for\ + \ broker, proxy and functions worker." + type: "boolean" + external: + additionalProperties: + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject\ + \ Alternative Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key\ + \ will be stored whe perComponent is enabled.\ + \ Required for external services. Internal\ + \ services pick up the secret name from the\ + \ tls config if not specified" + type: "string" + type: "object" + description: "External services self signed certificate\ + \ config (e.g., admin console, grafana). The key\ + \ is the service name, and the value contains generation\ + \ config" + type: "object" + issuer: + description: "ACME issuer configuration." + properties: + email: + description: "Email used for ACME registration." + type: "string" + ingressClass: + description: "Ingress class used for HTTP01 challenge." + type: "string" + name: + description: "Name of the Issuer resource." + type: "string" + privateKeySecretName: + description: "Secret storing the ACME account\ + \ private key." + type: "string" + server: + description: "ACME server URL." + type: "string" + type: "object" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + proxy: + description: "Proxy self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + type: "object" selfSigned: description: "Self signed certificate provisioner configuration." properties: @@ -4971,14 +5122,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5008,14 +5157,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5045,14 +5192,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5089,14 +5234,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject\ - \ Alternative Names (SANs) extension along\ - \ with the default K8s service DNS." + \ Alternative Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5132,14 +5275,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5190,14 +5331,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -5227,14 +5366,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/functionsworkers.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/functionsworkers.kaap.oss.datastax.com-v1.yml index e75c68c0..65d74fd8 100644 --- a/helm/kaap/crds/functionsworkers.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/functionsworkers.kaap.oss.datastax.com-v1.yml @@ -2572,6 +2572,157 @@ spec: certProvisioner: description: "Certificate provisioner configuration." properties: + acme: + description: "ACME certificate provisioner configuration." + properties: + broker: + description: "Broker self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + enabled: + description: "Generate self signed certificates for\ + \ broker, proxy and functions worker." + type: "boolean" + external: + additionalProperties: + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject\ + \ Alternative Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key\ + \ will be stored whe perComponent is enabled.\ + \ Required for external services. Internal\ + \ services pick up the secret name from the\ + \ tls config if not specified" + type: "string" + type: "object" + description: "External services self signed certificate\ + \ config (e.g., admin console, grafana). The key\ + \ is the service name, and the value contains generation\ + \ config" + type: "object" + issuer: + description: "ACME issuer configuration." + properties: + email: + description: "Email used for ACME registration." + type: "string" + ingressClass: + description: "Ingress class used for HTTP01 challenge." + type: "string" + name: + description: "Name of the Issuer resource." + type: "string" + privateKeySecretName: + description: "Secret storing the ACME account\ + \ private key." + type: "string" + server: + description: "ACME server URL." + type: "string" + type: "object" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + proxy: + description: "Proxy self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + type: "object" selfSigned: description: "Self signed certificate provisioner configuration." properties: @@ -2582,14 +2733,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -2619,14 +2768,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -2656,14 +2803,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -2700,14 +2845,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject\ - \ Alternative Names (SANs) extension along\ - \ with the default K8s service DNS." + \ Alternative Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -2743,14 +2886,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -2801,14 +2942,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -2838,14 +2977,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/proxies.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/proxies.kaap.oss.datastax.com-v1.yml index b245542f..e1e66e1a 100644 --- a/helm/kaap/crds/proxies.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/proxies.kaap.oss.datastax.com-v1.yml @@ -339,6 +339,157 @@ spec: certProvisioner: description: "Certificate provisioner configuration." properties: + acme: + description: "ACME certificate provisioner configuration." + properties: + broker: + description: "Broker self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + enabled: + description: "Generate self signed certificates for\ + \ broker, proxy and functions worker." + type: "boolean" + external: + additionalProperties: + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject\ + \ Alternative Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key\ + \ will be stored whe perComponent is enabled.\ + \ Required for external services. Internal\ + \ services pick up the secret name from the\ + \ tls config if not specified" + type: "string" + type: "object" + description: "External services self signed certificate\ + \ config (e.g., admin console, grafana). The key\ + \ is the service name, and the value contains generation\ + \ config" + type: "object" + issuer: + description: "ACME issuer configuration." + properties: + email: + description: "Email used for ACME registration." + type: "string" + ingressClass: + description: "Ingress class used for HTTP01 challenge." + type: "string" + name: + description: "Name of the Issuer resource." + type: "string" + privateKeySecretName: + description: "Secret storing the ACME account\ + \ private key." + type: "string" + server: + description: "ACME server URL." + type: "string" + type: "object" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + proxy: + description: "Proxy self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + type: "object" selfSigned: description: "Self signed certificate provisioner configuration." properties: @@ -349,14 +500,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -386,14 +535,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -423,14 +570,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -467,14 +612,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject\ - \ Alternative Names (SANs) extension along\ - \ with the default K8s service DNS." + \ Alternative Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -510,14 +653,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -568,14 +709,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -605,14 +744,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/pulsarclusters.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/pulsarclusters.kaap.oss.datastax.com-v1.yml index 073ada27..240b80e6 100644 --- a/helm/kaap/crds/pulsarclusters.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/pulsarclusters.kaap.oss.datastax.com-v1.yml @@ -14570,6 +14570,157 @@ spec: certProvisioner: description: "Certificate provisioner configuration." properties: + acme: + description: "ACME certificate provisioner configuration." + properties: + broker: + description: "Broker self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + enabled: + description: "Generate self signed certificates for\ + \ broker, proxy and functions worker." + type: "boolean" + external: + additionalProperties: + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject\ + \ Alternative Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key\ + \ will be stored whe perComponent is enabled.\ + \ Required for external services. Internal\ + \ services pick up the secret name from the\ + \ tls config if not specified" + type: "string" + type: "object" + description: "External services self signed certificate\ + \ config (e.g., admin console, grafana). The key\ + \ is the service name, and the value contains generation\ + \ config" + type: "object" + issuer: + description: "ACME issuer configuration." + properties: + email: + description: "Email used for ACME registration." + type: "string" + ingressClass: + description: "Ingress class used for HTTP01 challenge." + type: "string" + name: + description: "Name of the Issuer resource." + type: "string" + privateKeySecretName: + description: "Secret storing the ACME account\ + \ private key." + type: "string" + server: + description: "ACME server URL." + type: "string" + type: "object" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + proxy: + description: "Proxy self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + type: "object" selfSigned: description: "Self signed certificate provisioner configuration." properties: @@ -14580,14 +14731,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -14617,14 +14766,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -14654,14 +14801,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -14698,14 +14843,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject\ - \ Alternative Names (SANs) extension along\ - \ with the default K8s service DNS." + \ Alternative Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -14741,14 +14884,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -14799,14 +14940,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -14836,14 +14975,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/zookeepers.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/zookeepers.kaap.oss.datastax.com-v1.yml index a3d7b677..7eaf465a 100644 --- a/helm/kaap/crds/zookeepers.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/zookeepers.kaap.oss.datastax.com-v1.yml @@ -339,6 +339,157 @@ spec: certProvisioner: description: "Certificate provisioner configuration." properties: + acme: + description: "ACME certificate provisioner configuration." + properties: + broker: + description: "Broker self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + enabled: + description: "Generate self signed certificates for\ + \ broker, proxy and functions worker." + type: "boolean" + external: + additionalProperties: + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject\ + \ Alternative Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key\ + \ will be stored whe perComponent is enabled.\ + \ Required for external services. Internal\ + \ services pick up the secret name from the\ + \ tls config if not specified" + type: "string" + type: "object" + description: "External services self signed certificate\ + \ config (e.g., admin console, grafana). The key\ + \ is the service name, and the value contains generation\ + \ config" + type: "object" + issuer: + description: "ACME issuer configuration." + properties: + email: + description: "Email used for ACME registration." + type: "string" + ingressClass: + description: "Ingress class used for HTTP01 challenge." + type: "string" + name: + description: "Name of the Issuer resource." + type: "string" + privateKeySecretName: + description: "Secret storing the ACME account\ + \ private key." + type: "string" + server: + description: "ACME server URL." + type: "string" + type: "object" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + proxy: + description: "Proxy self signed certificate config." + properties: + dnsNames: + description: "A list of DNS names (and IP addresses)\ + \ to include in the certificate's Subject Alternative\ + \ Names (SANs) extension." + items: + type: "string" + type: "array" + generate: + description: "Generate certificate for the component." + type: "boolean" + privateKey: + description: "Cert-manager options for generating\ + \ the private key." + properties: + algorithm: + type: "string" + encoding: + type: "string" + rotationPolicy: + type: "string" + size: + type: "integer" + type: "object" + secretName: + description: "The name of the Kubernetes Secret\ + \ where the generated certificate and key will\ + \ be stored whe perComponent is enabled. Required\ + \ for external services. Internal services pick\ + \ up the secret name from the tls config if\ + \ not specified" + type: "string" + type: "object" + type: "object" selfSigned: description: "Self signed certificate provisioner configuration." properties: @@ -349,14 +500,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -386,14 +535,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -423,14 +570,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -467,14 +612,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject\ - \ Alternative Names (SANs) extension along\ - \ with the default K8s service DNS." + \ Alternative Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -510,14 +653,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -568,14 +709,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ @@ -605,14 +744,12 @@ spec: dnsNames: description: "A list of DNS names (and IP addresses)\ \ to include in the certificate's Subject Alternative\ - \ Names (SANs) extension along with the default\ - \ K8s service DNS." + \ Names (SANs) extension." items: type: "string" type: "array" generate: - description: "Generate self signed certificates\ - \ for the component." + description: "Generate certificate for the component." type: "boolean" privateKey: description: "Cert-manager options for generating\ diff --git a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java index 24350688..092b97e2 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java +++ b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java @@ -27,7 +27,7 @@ import com.datastax.oss.kaap.crds.GlobalSpec; import com.datastax.oss.kaap.crds.cluster.PulsarClusterSpec; import com.datastax.oss.kaap.crds.configs.tls.TlsConfig; -import com.datastax.oss.kaap.crds.configs.tls.TlsConfig.SelfSignedCertificatePerComponentConfig; +import com.datastax.oss.kaap.crds.configs.tls.TlsConfig.ComponentCertificateConfig; import io.fabric8.certmanager.api.model.v1.Certificate; import io.fabric8.certmanager.api.model.v1.CertificateBuilder; import io.fabric8.certmanager.api.model.v1.CertificatePrivateKey; @@ -155,10 +155,10 @@ public void generateCertificates() { getFunctionsWorkerDNSNames(selfSigned.getFunctionsWorker())); } if (selfSigned.getExternal() != null) { - for (Map.Entry entry + for (Map.Entry entry : selfSigned.getExternal().entrySet()) { final String serviceName = entry.getKey(); - final SelfSignedCertificatePerComponentConfig config = entry.getValue(); + final ComponentCertificateConfig config = entry.getValue(); if (config.getGenerate() != null && config.getGenerate()) { createCertificatePerComponent( @@ -175,7 +175,7 @@ public void generateCertificates() { private void createAutorecoveryCertificate() { // autorecovery only need to be accessed via the client tls auth, no dns names needed - final TlsConfig.SelfSignedCertificatePerComponentConfig autorecoveryConfig = selfSigned.getAutorecovery(); + final ComponentCertificateConfig autorecoveryConfig = selfSigned.getAutorecovery(); final CertificatePrivateKey privateKey = ObjectUtils .firstNonNull(autorecoveryConfig == null ? null : autorecoveryConfig.getPrivateKey(), @@ -203,7 +203,7 @@ private void createAutorecoveryCertificate() { .createOrReplace(); } - private void createCertificatePerComponent(final TlsConfig.SelfSignedCertificatePerComponentConfig componentConfig, + private void createCertificatePerComponent(final ComponentCertificateConfig componentConfig, final String baseName, final String secretName, List dnsNames) { @@ -219,7 +219,7 @@ private void createCertificatePerComponent(final TlsConfig.SelfSignedCertificate ); } - private List mergeDnsNames(List k8sDnsNames, SelfSignedCertificatePerComponentConfig config) { + private List mergeDnsNames(List k8sDnsNames, ComponentCertificateConfig config) { final List finalDnsNames = new ArrayList<>(ObjectUtils.firstNonNull(k8sDnsNames, new ArrayList<>())); if (config != null && config.getDnsNames() != null) { finalDnsNames.addAll(config.getDnsNames()); @@ -230,21 +230,21 @@ private List mergeDnsNames(List k8sDnsNames, SelfSignedCertifica private List getDnsNamesForExternalServicesForGlobalCert() { final List dnsNames = new ArrayList<>(); if (selfSigned.getExternal() != null) { - for (Map.Entry entry + for (Map.Entry entry : selfSigned.getExternal().entrySet()) { - final SelfSignedCertificatePerComponentConfig config = entry.getValue(); + final ComponentCertificateConfig config = entry.getValue(); dnsNames.addAll(getBaseK8sDnsNames(config, entry.getKey())); } } return dnsNames; } - private List getBaseK8sDnsNames(SelfSignedCertificatePerComponentConfig config, String serviceName) { + private List getBaseK8sDnsNames(ComponentCertificateConfig config, String serviceName) { List k8sDnsNames = enumerateDnsNames(serviceName, true); return mergeDnsNames(k8sDnsNames, config); } - private List getBookKeeperDNSNames(SelfSignedCertificatePerComponentConfig config) { + private List getBookKeeperDNSNames(ComponentCertificateConfig config) { final String componentBaseName = BookKeeperResourcesFactory.getComponentBaseName(globalSpec); List k8sDnsNames = BookKeeperController .enumerateBookKeeperSets(clusterSpecName, componentBaseName, pulsarClusterSpec.getBookkeeper()).stream() @@ -253,7 +253,7 @@ private List getBookKeeperDNSNames(SelfSignedCertificatePerComponentConf return mergeDnsNames(k8sDnsNames, config); } - private List getFunctionsWorkerDNSNames(SelfSignedCertificatePerComponentConfig config) { + private List getFunctionsWorkerDNSNames(ComponentCertificateConfig config) { final String functionsWorkerBase = FunctionsWorkerResourcesFactory.getResourceName(clusterSpecName, FunctionsWorkerResourcesFactory.getComponentBaseName(globalSpec)); @@ -265,7 +265,7 @@ private List getFunctionsWorkerDNSNames(SelfSignedCertificatePerComponen return mergeDnsNames(k8sDnsNames, config); } - private List getZookeeperDNSNames(SelfSignedCertificatePerComponentConfig config) { + private List getZookeeperDNSNames(ComponentCertificateConfig config) { final String zookeeperDNSNames = ZooKeeperResourcesFactory.getResourceName(clusterSpecName, ZooKeeperResourcesFactory.getComponentBaseName(globalSpec)); @@ -277,7 +277,7 @@ private List getZookeeperDNSNames(SelfSignedCertificatePerComponentConfi return mergeDnsNames(k8sDnsNames, config); } - private List getProxyDNSNames(SelfSignedCertificatePerComponentConfig config) { + private List getProxyDNSNames(ComponentCertificateConfig config) { final String componentBaseName = ProxyResourcesFactory.getComponentBaseName(globalSpec); List k8sDnsNames = ProxyController .enumerateProxySets(clusterSpecName, componentBaseName, pulsarClusterSpec.getProxy()).stream() @@ -286,7 +286,7 @@ private List getProxyDNSNames(SelfSignedCertificatePerComponentConfig co return mergeDnsNames(k8sDnsNames, config); } - private List getBrokerDNSNames(SelfSignedCertificatePerComponentConfig config) { + private List getBrokerDNSNames(ComponentCertificateConfig config) { final String componentBaseName = BrokerResourcesFactory.getComponentBaseName(globalSpec); List k8sDnsNames = BrokerController .enumerateBrokerSets(clusterSpecName, componentBaseName, pulsarClusterSpec.getBroker()).stream() diff --git a/operator/src/main/java/com/datastax/oss/kaap/crds/configs/tls/TlsConfig.java b/operator/src/main/java/com/datastax/oss/kaap/crds/configs/tls/TlsConfig.java index 4e76751e..5983af17 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/crds/configs/tls/TlsConfig.java +++ b/operator/src/main/java/com/datastax/oss/kaap/crds/configs/tls/TlsConfig.java @@ -25,6 +25,7 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; +import lombok.experimental.SuperBuilder; @Data @NoArgsConstructor @@ -119,53 +120,90 @@ public FunctionsWorkerTlsEntryConfig(Boolean enabled, String secretName, Boolean public static class CertProvisionerConfig { @JsonPropertyDescription("Self signed certificate provisioner configuration.") SelfSignedCertProvisionerConfig selfSigned; - + @JsonPropertyDescription("ACME certificate provisioner configuration.") + AcmeCertProvisionerConfig acme; } @Data @NoArgsConstructor @AllArgsConstructor - @Builder - public static class SelfSignedCertProvisionerConfig { + @SuperBuilder + public static class BaseCertProvisionerConfig { @JsonPropertyDescription("Generate self signed certificates for broker, proxy and functions worker.") Boolean enabled; - @JsonPropertyDescription("Include dns name in the DNS names covered by the certificate.") - Boolean includeDns; @JsonPropertyDescription("Cert-manager options for generating the private key.") CertificatePrivateKey privateKey; + @JsonPropertyDescription("Broker self signed certificate config.") + ComponentCertificateConfig broker; + @JsonPropertyDescription("Proxy self signed certificate config.") + ComponentCertificateConfig proxy; + @JsonPropertyDescription("External services self signed certificate config (e.g., admin console, grafana). " + + "The key is the service name, and the value contains generation config") + Map external; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class SelfSignedCertProvisionerConfig extends BaseCertProvisionerConfig { + @JsonPropertyDescription("Include dns name in the DNS names covered by the certificate.") + Boolean includeDns; @JsonPropertyDescription("Generate a different certificate for each component.") Boolean perComponent; @JsonPropertyDescription("Secret where to store the root CA certificate.") String caSecretName; - @JsonPropertyDescription("Zookeeper self signed certificate config.") - SelfSignedCertificatePerComponentConfig zookeeper; + ComponentCertificateConfig zookeeper; @JsonPropertyDescription("Bookkeeper self signed certificate config.") - SelfSignedCertificatePerComponentConfig bookkeeper; - @JsonPropertyDescription("Broker self signed certificate config.") - SelfSignedCertificatePerComponentConfig broker; - @JsonPropertyDescription("Proxy self signed certificate config.") - SelfSignedCertificatePerComponentConfig proxy; + ComponentCertificateConfig bookkeeper; @JsonPropertyDescription("Functions worker self signed certificate config.") - SelfSignedCertificatePerComponentConfig functionsWorker; + ComponentCertificateConfig functionsWorker; @JsonPropertyDescription("Autorecovery self signed certificate config.") - SelfSignedCertificatePerComponentConfig autorecovery; - @JsonPropertyDescription("External services self signed certificate config (e.g., admin console, grafana). " - + "The key is the service name, and the value contains generation config") - Map external; + ComponentCertificateConfig autorecovery; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class AcmeCertProvisionerConfig extends BaseCertProvisionerConfig { + @JsonPropertyDescription("ACME issuer configuration.") + AcmeIssuerConfig issuer; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class AcmeIssuerConfig { + @JsonPropertyDescription("Name of the Issuer resource.") + String name; + @JsonPropertyDescription("ACME server URL.") + String server; + @JsonPropertyDescription("Email used for ACME registration.") + String email; + @JsonPropertyDescription("Secret storing the ACME account private key.") + String privateKeySecretName; + @JsonPropertyDescription("Ingress class used for HTTP01 challenge.") + String ingressClass; } @Data @NoArgsConstructor @AllArgsConstructor @Builder - public static class SelfSignedCertificatePerComponentConfig { - @JsonPropertyDescription("Generate self signed certificates for the component.") + public static class ComponentCertificateConfig { + @JsonPropertyDescription("Generate certificate for the component.") Boolean generate; @JsonPropertyDescription("Cert-manager options for generating the private key.") CertificatePrivateKey privateKey; @JsonPropertyDescription("A list of DNS names (and IP addresses) to include in the certificate's Subject " - + "Alternative Names (SANs) extension along with the default K8s service DNS.") + + "Alternative Names (SANs) extension.") List dnsNames; @JsonPropertyDescription("The name of the Kubernetes Secret where the generated certificate " + "and key will be stored whe perComponent is enabled. Required for external services. " diff --git a/tests/src/test/java/com/datastax/oss/kaap/tests/helm/TlsTest.java b/tests/src/test/java/com/datastax/oss/kaap/tests/helm/TlsTest.java index 4198b205..af8e6d9e 100644 --- a/tests/src/test/java/com/datastax/oss/kaap/tests/helm/TlsTest.java +++ b/tests/src/test/java/com/datastax/oss/kaap/tests/helm/TlsTest.java @@ -48,27 +48,27 @@ protected void test(boolean perComponentCerts, boolean pulsar3) throws Exception .selfSigned(TlsConfig.SelfSignedCertProvisionerConfig.builder() .enabled(true) .perComponent(true) - .zookeeper(TlsConfig.SelfSignedCertificatePerComponentConfig + .zookeeper(TlsConfig.ComponentCertificateConfig .builder() .generate(true) .build()) - .functionsWorker(TlsConfig.SelfSignedCertificatePerComponentConfig + .functionsWorker(TlsConfig.ComponentCertificateConfig .builder() .generate(true) .build()) - .proxy(TlsConfig.SelfSignedCertificatePerComponentConfig + .proxy(TlsConfig.ComponentCertificateConfig .builder() .generate(true) .build()) - .broker(TlsConfig.SelfSignedCertificatePerComponentConfig + .broker(TlsConfig.ComponentCertificateConfig .builder() .generate(true) .build()) - .bookkeeper(TlsConfig.SelfSignedCertificatePerComponentConfig + .bookkeeper(TlsConfig.ComponentCertificateConfig .builder() .generate(true) .build()) - .autorecovery(TlsConfig.SelfSignedCertificatePerComponentConfig + .autorecovery(TlsConfig.ComponentCertificateConfig .builder() .generate(true) .build()) From eb4ac436f0ca7dd39c8f8b25053b6bbd2112da02 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Mon, 16 Mar 2026 19:04:00 +0530 Subject: [PATCH 02/11] Updated CertManagerCertificatesProvisioner.java to process the acme config and generate certificates --- .../CertManagerCertificatesProvisioner.java | 173 ++++++++++++++---- 1 file changed, 142 insertions(+), 31 deletions(-) diff --git a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java index 092b97e2..0c803167 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java +++ b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java @@ -48,6 +48,7 @@ public class CertManagerCertificatesProvisioner { private final PulsarClusterSpec pulsarClusterSpec; private final GlobalSpec globalSpec; private final TlsConfig.SelfSignedCertProvisionerConfig selfSigned; + private final TlsConfig.AcmeCertProvisionerConfig acme; private final String clusterSpecName; private final String ssCaSecretName; private final String caIssuerName; @@ -59,30 +60,36 @@ public CertManagerCertificatesProvisioner(KubernetesClient client, String namesp this.namespace = namespace; this.pulsarClusterSpec = pulsarClusterSpec; this.globalSpec = pulsarClusterSpec.getGlobalSpec(); - if (globalSpec.getTls() == null - || !globalSpec.getTls().getEnabled() - || globalSpec.getTls().getCertProvisioner() == null - || globalSpec.getTls().getCertProvisioner().getSelfSigned() == null) { + TlsConfig tls = globalSpec != null ? globalSpec.getTls() : null; + TlsConfig.CertProvisionerConfig provisioner = tls != null ? tls.getCertProvisioner() : null; + if (tls == null || !Boolean.TRUE.equals(tls.getEnabled()) || provisioner == null) { this.selfSigned = null; + this.acme = null; this.clusterSpecName = null; this.caIssuerName = null; this.ssCaSecretName = null; this.serviceDnsSuffix = null; - } else { - this.selfSigned = globalSpec.getTls().getCertProvisioner().getSelfSigned(); - this.clusterSpecName = globalSpec.getName(); - this.caIssuerName = "%s-ca-issuer".formatted(clusterSpecName); - this.ssCaSecretName = BaseResourcesFactory.getTlsSsCaSecretName(globalSpec); - this.serviceDnsSuffix = "%s.svc.%s".formatted(namespace, globalSpec.getKubernetesClusterDomain()); + return; } - + this.selfSigned = provisioner.getSelfSigned(); + this.acme = provisioner.getAcme(); + this.clusterSpecName = globalSpec.getName(); + this.caIssuerName = "%s-ca-issuer".formatted(clusterSpecName); + this.ssCaSecretName = BaseResourcesFactory.getTlsSsCaSecretName(globalSpec); + this.serviceDnsSuffix = "%s.svc.%s".formatted(namespace, globalSpec.getKubernetesClusterDomain()); } public void generateCertificates() { - if (selfSigned == null) { - return; + validateProvisionersConfig(); + if (selfSigned != null && Boolean.TRUE.equals(selfSigned.getEnabled())) { + generateSelfSignedCertificates(); + } + if (acme != null && Boolean.TRUE.equals(acme.getEnabled())) { + generateAcmeCertificates(); } + } + private void generateSelfSignedCertificates() { createRootCACertificate(); if (selfSigned.getPerComponent() == null || !selfSigned.getPerComponent()) { List dnsNames = new ArrayList<>(); @@ -112,7 +119,8 @@ public void generateCertificates() { certName, globalSpec.getTls().getDefaultSecretName(), selfSigned.getPrivateKey(), - dnsNames + dnsNames, + caIssuerName ); } else { if (TlsConfig.TlsEntryConfig.isEnabled(globalSpec.getTls().getBroker())) { @@ -120,14 +128,18 @@ public void generateCertificates() { globalSpec.getComponents().getBrokerBaseName(), ObjectUtils.firstNonNull(selfSigned.getBroker().getSecretName(), globalSpec.getTls().getBroker().getSecretName()), - getBrokerDNSNames(selfSigned.getBroker())); + getBrokerDNSNames(selfSigned.getBroker()), + caIssuerName, + selfSigned.getPrivateKey()); } if (TlsConfig.TlsEntryConfig.isEnabled(globalSpec.getTls().getBookkeeper())) { createCertificatePerComponent(selfSigned.getBookkeeper(), globalSpec.getComponents().getBookkeeperBaseName(), ObjectUtils.firstNonNull(selfSigned.getBookkeeper().getSecretName(), globalSpec.getTls().getBookkeeper().getSecretName()), - getBookKeeperDNSNames(selfSigned.getBookkeeper())); + getBookKeeperDNSNames(selfSigned.getBookkeeper()), + caIssuerName, + selfSigned.getPrivateKey()); } if (TlsConfig.TlsEntryConfig.isEnabled(globalSpec.getTls().getAutorecovery())) { createAutorecoveryCertificate(); @@ -137,14 +149,18 @@ public void generateCertificates() { globalSpec.getComponents().getProxyBaseName(), ObjectUtils.firstNonNull(selfSigned.getProxy().getSecretName(), globalSpec.getTls().getProxy().getSecretName()), - getProxyDNSNames(selfSigned.getProxy())); + getProxyDNSNames(selfSigned.getProxy()), + caIssuerName, + selfSigned.getPrivateKey()); } if (TlsConfig.TlsEntryConfig.isEnabled(globalSpec.getTls().getZookeeper())) { createCertificatePerComponent(selfSigned.getZookeeper(), globalSpec.getComponents().getZookeeperBaseName(), ObjectUtils.firstNonNull(selfSigned.getZookeeper().getSecretName(), globalSpec.getTls().getZookeeper().getSecretName()), - getZookeeperDNSNames(selfSigned.getZookeeper())); + getZookeeperDNSNames(selfSigned.getZookeeper()), + caIssuerName, + selfSigned.getPrivateKey()); } if (TlsConfig.FunctionsWorkerTlsEntryConfig.isEnabled(globalSpec.getTls().getFunctionsWorker())) { @@ -152,7 +168,9 @@ public void generateCertificates() { globalSpec.getComponents().getFunctionsWorkerBaseName(), ObjectUtils.firstNonNull(selfSigned.getFunctionsWorker().getSecretName(), globalSpec.getTls().getFunctionsWorker().getSecretName()), - getFunctionsWorkerDNSNames(selfSigned.getFunctionsWorker())); + getFunctionsWorkerDNSNames(selfSigned.getFunctionsWorker()), + caIssuerName, + selfSigned.getPrivateKey()); } if (selfSigned.getExternal() != null) { for (Map.Entry entry @@ -165,7 +183,9 @@ public void generateCertificates() { config, serviceName, config.getSecretName(), - getBaseK8sDnsNames(config, serviceName) + getBaseK8sDnsNames(config, serviceName), + caIssuerName, + selfSigned.getPrivateKey() ); } } @@ -173,6 +193,60 @@ public void generateCertificates() { } } + private void generateAcmeCertificates() { + createAcmeIssuer(); + String issuerName = acme.getIssuer().getName(); + if (TlsConfig.TlsEntryConfig.isEnabled(globalSpec.getTls().getBroker())) { + createCertificatePerComponent( + acme.getBroker(), + globalSpec.getComponents().getBrokerBaseName(), + ObjectUtils.firstNonNull(acme.getBroker().getSecretName(), + globalSpec.getTls().getBroker().getSecretName()), + mergeDnsNames(null, acme.getBroker()), + issuerName, + acme.getPrivateKey() + ); + } + if (TlsConfig.ProxyTlsEntryConfig.isEnabled(globalSpec.getTls().getProxy())) { + createCertificatePerComponent( + acme.getProxy(), + globalSpec.getComponents().getProxyBaseName(), + ObjectUtils.firstNonNull(acme.getProxy().getSecretName(), + globalSpec.getTls().getProxy().getSecretName()), + mergeDnsNames(null, acme.getProxy()), + issuerName, + acme.getPrivateKey() + ); + } + } + + private void createAcmeIssuer() { + TlsConfig.AcmeIssuerConfig issuerConfig = acme.getIssuer(); + final Issuer issuer = new IssuerBuilder() + .withNewMetadata() + .withName(issuerConfig.getName()) + .withNamespace(namespace) + .endMetadata() + .withNewSpec() + .withNewAcme() + .withServer(issuerConfig.getServer()) + .withEmail(issuerConfig.getEmail()) + .withNewPrivateKeySecretRef() + .withName(issuerConfig.getPrivateKeySecretName()) + .endPrivateKeySecretRef() + .addNewSolver() + .withNewHttp01() + .withNewIngress() + .withIngressClassName(issuerConfig.getIngressClass()) + .endIngress() + .endHttp01() + .endSolver() + .endAcme() + .endSpec() + .build(); + client.resource(issuer).inNamespace(namespace).createOrReplace(); + } + private void createAutorecoveryCertificate() { // autorecovery only need to be accessed via the client tls auth, no dns names needed final ComponentCertificateConfig autorecoveryConfig = selfSigned.getAutorecovery(); @@ -206,16 +280,19 @@ private void createAutorecoveryCertificate() { private void createCertificatePerComponent(final ComponentCertificateConfig componentConfig, final String baseName, final String secretName, - List dnsNames) { - if (componentConfig == null || !componentConfig.getGenerate()) { + List dnsNames, + String issuerName, + CertificatePrivateKey provisionerPrivateKey) { + if (componentConfig == null || !Boolean.TRUE.equals(componentConfig.getGenerate())) { return; } createCertificate( "%s-%s-tls".formatted(clusterSpecName, baseName), secretName, - ObjectUtils.firstNonNull(componentConfig.getPrivateKey(), selfSigned.getPrivateKey()), - dnsNames + ObjectUtils.firstNonNull(componentConfig.getPrivateKey(), provisionerPrivateKey), + dnsNames, + issuerName ); } @@ -311,8 +388,9 @@ private List enumerateDnsNames(final String serviceName, boolean wildcar private void createCertificate(String name, String secretName, CertificatePrivateKey privateKey, - List dnsNames) { - final Certificate ssCertificate = new CertificateBuilder() + List dnsNames, + String issuerName) { + final Certificate certificate = new CertificateBuilder() .withNewMetadata() .withName(name) .withNamespace(namespace) @@ -321,16 +399,15 @@ private void createCertificate(String name, .withPrivateKey(privateKey) .withSecretName(secretName) .withNewIssuerRef() - .withName(caIssuerName) + .withName(issuerName) .endIssuerRef() .withDnsNames(dnsNames) .endSpec() .build(); - - client.resource(ssCertificate) + client.resource(certificate) .inNamespace(namespace) .createOrReplace(); - log.debugf("Created self-signed certificate %s mapped to secret %s", name, secretName); + log.debugf("Created certificate %s mapped to secret %s", name, secretName); } private void createRootCACertificate() { @@ -390,5 +467,39 @@ private void createRootCACertificate() { log.debug("Created self-signed root CA certificate"); } - + private void validateProvisionersConfig() { + if (selfSigned == null || acme == null) { + return; + } + if (selfSigned.getBroker() != null && acme.getBroker() != null + && Boolean.TRUE.equals(selfSigned.getBroker().getGenerate()) + && Boolean.TRUE.equals(acme.getBroker().getGenerate())) { + String ssSecret = ObjectUtils.firstNonNull(selfSigned.getBroker().getSecretName(), + globalSpec.getTls().getBroker().getSecretName()); + String acmeSecret = ObjectUtils.firstNonNull(acme.getBroker().getSecretName(), + globalSpec.getTls().getBroker().getSecretName()); + if (ObjectUtils.equals(ssSecret, acmeSecret)) { + throw new IllegalArgumentException( + String.format("Invalid TLS configuration: both selfSigned and ACME provisioners generate a " + + "broker certificate using the same secret '%s'. This would cause certificates" + + " to override each other.", ssSecret) + ); + } + } + if (selfSigned.getProxy() != null && acme.getProxy() != null + && Boolean.TRUE.equals(selfSigned.getProxy().getGenerate()) + && Boolean.TRUE.equals(acme.getProxy().getGenerate())) { + String ssSecret = ObjectUtils.firstNonNull(selfSigned.getProxy().getSecretName(), + globalSpec.getTls().getProxy().getSecretName()); + String acmeSecret = ObjectUtils.firstNonNull(acme.getProxy().getSecretName(), + globalSpec.getTls().getProxy().getSecretName()); + if (ObjectUtils.equals(ssSecret, acmeSecret)) { + throw new IllegalArgumentException( + String.format("Invalid TLS configuration: both selfSigned and ACME provisioners generate a " + + "proxy certificate using the same secret '%s'. This would cause certificates" + + " to override each other.", ssSecret) + ); + } + } + } } \ No newline at end of file From 4c76f02ec909b7392d965049edcddc1d0f95b73f Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Tue, 17 Mar 2026 11:53:35 +0530 Subject: [PATCH 03/11] Added support for multiple challenge solvers --- .../CertManagerCertificatesProvisioner.java | 108 +++++++++++++++--- .../oss/kaap/crds/configs/tls/TlsConfig.java | 59 ++++++++++ 2 files changed, 152 insertions(+), 15 deletions(-) diff --git a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java index 0c803167..36cc9922 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java +++ b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java @@ -28,6 +28,12 @@ import com.datastax.oss.kaap.crds.cluster.PulsarClusterSpec; import com.datastax.oss.kaap.crds.configs.tls.TlsConfig; import com.datastax.oss.kaap.crds.configs.tls.TlsConfig.ComponentCertificateConfig; +import io.fabric8.certmanager.api.model.acme.v1.ACMEChallengeSolver; +import io.fabric8.certmanager.api.model.acme.v1.ACMEChallengeSolverBuilder; +import io.fabric8.certmanager.api.model.acme.v1.ACMEChallengeSolverDNS01Builder; +import io.fabric8.certmanager.api.model.acme.v1.ACMEChallengeSolverHTTP01Builder; +import io.fabric8.certmanager.api.model.acme.v1.ACMEChallengeSolverHTTP01IngressBuilder; +import io.fabric8.certmanager.api.model.acme.v1.ACMEIssuerBuilder; import io.fabric8.certmanager.api.model.v1.Certificate; import io.fabric8.certmanager.api.model.v1.CertificateBuilder; import io.fabric8.certmanager.api.model.v1.CertificatePrivateKey; @@ -220,28 +226,99 @@ private void generateAcmeCertificates() { } } + public static void validateAcmeIssuerConfig(TlsConfig.AcmeIssuerConfig config) { + if (config.getSolvers() == null || config.getSolvers().isEmpty()) { + throw new IllegalArgumentException("At least one ACME solver must be configured"); + } + for (TlsConfig.SolverConfig solver : config.getSolvers()) { + int count = 0; + if (solver.getHttp01() != null) count++; + if (solver.getDns01() != null) count++; + if (count != 1) { + throw new IllegalArgumentException( + "Exactly one of http01 or dns01 must be configured per solver"); + } + if (solver.getDns01() != null) { + TlsConfig.Dns01Config dns = solver.getDns01(); + int providers = 0; + if (dns.getRoute53() != null) providers++; + if (dns.getCloudflare() != null) providers++; + if (dns.getCloudDNS() != null) providers++; + if (providers != 1) { + throw new IllegalArgumentException( + "Exactly one DNS provider must be configured per solver"); + } + } + } + } + private void createAcmeIssuer() { TlsConfig.AcmeIssuerConfig issuerConfig = acme.getIssuer(); - final Issuer issuer = new IssuerBuilder() + validateAcmeIssuerConfig(issuerConfig); + List solvers = new ArrayList<>(); + + for (TlsConfig.SolverConfig solver : issuerConfig.getSolvers()) { + ACMEChallengeSolverBuilder solverBuilder = new ACMEChallengeSolverBuilder(); + if (solver.getHttp01() != null) { + TlsConfig.Http01Config http = solver.getHttp01(); + solverBuilder.withHttp01( + new ACMEChallengeSolverHTTP01Builder() + .withIngress( + new ACMEChallengeSolverHTTP01IngressBuilder() + .withIngressClassName(http.getIngressClass()) + .build() + ) + .build() + ); + } + + if (solver.getDns01() != null) { + TlsConfig.Dns01Config dns = solver.getDns01(); + ACMEChallengeSolverDNS01Builder dnsBuilder = new ACMEChallengeSolverDNS01Builder(); + if (dns.getRoute53() != null) { + TlsConfig.Route53Config r = dns.getRoute53(); + dnsBuilder + .withNewRoute53() + .withRegion(r.getRegion()) + .withHostedZoneID(r.getHostedZoneId()) + .endRoute53(); + } else if (dns.getCloudflare() != null) { + TlsConfig.CloudflareConfig cf = dns.getCloudflare(); + dnsBuilder + .withNewCloudflare() + .withEmail(cf.getEmail()) + .withNewApiTokenSecretRef(cf.getApiTokenSecretKey(), cf.getApiTokenSecretName()) + .endCloudflare(); + } else if (dns.getCloudDNS() != null) { + TlsConfig.GoogleCloudDnsConfig g = dns.getCloudDNS(); + dnsBuilder + .withNewCloudDNS() + .withProject(g.getProject()) + .withNewServiceAccountSecretRef(g.getServiceAccountSecretKey(), + g.getServiceAccountSecretName()) + .endCloudDNS(); + } + solverBuilder.withDns01(dnsBuilder.build()); + } + solvers.add(solverBuilder.build()); + } + + Issuer issuer = new IssuerBuilder() .withNewMetadata() .withName(issuerConfig.getName()) .withNamespace(namespace) .endMetadata() .withNewSpec() - .withNewAcme() - .withServer(issuerConfig.getServer()) - .withEmail(issuerConfig.getEmail()) - .withNewPrivateKeySecretRef() - .withName(issuerConfig.getPrivateKeySecretName()) - .endPrivateKeySecretRef() - .addNewSolver() - .withNewHttp01() - .withNewIngress() - .withIngressClassName(issuerConfig.getIngressClass()) - .endIngress() - .endHttp01() - .endSolver() - .endAcme() + .withAcme( + new ACMEIssuerBuilder() + .withServer(issuerConfig.getServer()) + .withEmail(issuerConfig.getEmail()) + .withNewPrivateKeySecretRef() + .withName(issuerConfig.getPrivateKeySecretName()) + .endPrivateKeySecretRef() + .withSolvers(solvers) + .build() + ) .endSpec() .build(); client.resource(issuer).inNamespace(namespace).createOrReplace(); @@ -467,6 +544,7 @@ private void createRootCACertificate() { log.debug("Created self-signed root CA certificate"); } + //TODO remove duplicate code private void validateProvisionersConfig() { if (selfSigned == null || acme == null) { return; diff --git a/operator/src/main/java/com/datastax/oss/kaap/crds/configs/tls/TlsConfig.java b/operator/src/main/java/com/datastax/oss/kaap/crds/configs/tls/TlsConfig.java index 5983af17..0fec85d7 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/crds/configs/tls/TlsConfig.java +++ b/operator/src/main/java/com/datastax/oss/kaap/crds/configs/tls/TlsConfig.java @@ -189,10 +189,69 @@ public static class AcmeIssuerConfig { String email; @JsonPropertyDescription("Secret storing the ACME account private key.") String privateKeySecretName; + @JsonPropertyDescription("ACME challenge solvers. Solvers are evaluated in order; no domain-based routing") + List solvers; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class SolverConfig { + @JsonPropertyDescription("HTTP01 solver configuration.") + Http01Config http01; + @JsonPropertyDescription("DNS01 solver configuration.") + Dns01Config dns01; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class Http01Config { @JsonPropertyDescription("Ingress class used for HTTP01 challenge.") String ingressClass; } + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class Dns01Config { + Route53Config route53; + CloudflareConfig cloudflare; + GoogleCloudDnsConfig cloudDNS; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class Route53Config { + String region; + String hostedZoneId; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class CloudflareConfig { + String email; + String apiTokenSecretName; + String apiTokenSecretKey; + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class GoogleCloudDnsConfig { + String project; + String serviceAccountSecretName; + String serviceAccountSecretKey; + } + @Data @NoArgsConstructor @AllArgsConstructor From a6c866cf893bbe313b7d28a97a360cddb81eafb5 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Tue, 17 Mar 2026 12:11:50 +0530 Subject: [PATCH 04/11] Refactored duplicate code for validation --- .../CertManagerCertificatesProvisioner.java | 65 ++++++++++++------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java index 36cc9922..8a60db02 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java +++ b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java @@ -224,6 +224,7 @@ private void generateAcmeCertificates() { acme.getPrivateKey() ); } + // TODO external certificate support } public static void validateAcmeIssuerConfig(TlsConfig.AcmeIssuerConfig config) { @@ -544,38 +545,52 @@ private void createRootCACertificate() { log.debug("Created self-signed root CA certificate"); } - //TODO remove duplicate code private void validateProvisionersConfig() { if (selfSigned == null || acme == null) { return; } - if (selfSigned.getBroker() != null && acme.getBroker() != null - && Boolean.TRUE.equals(selfSigned.getBroker().getGenerate()) - && Boolean.TRUE.equals(acme.getBroker().getGenerate())) { - String ssSecret = ObjectUtils.firstNonNull(selfSigned.getBroker().getSecretName(), - globalSpec.getTls().getBroker().getSecretName()); - String acmeSecret = ObjectUtils.firstNonNull(acme.getBroker().getSecretName(), - globalSpec.getTls().getBroker().getSecretName()); - if (ObjectUtils.equals(ssSecret, acmeSecret)) { - throw new IllegalArgumentException( - String.format("Invalid TLS configuration: both selfSigned and ACME provisioners generate a " - + "broker certificate using the same secret '%s'. This would cause certificates" - + " to override each other.", ssSecret) - ); - } + validateComponent( + "broker", + selfSigned.getBroker(), + acme.getBroker(), + globalSpec.getTls().getBroker() + ); + validateComponent( + "proxy", + selfSigned.getProxy(), + acme.getProxy(), + globalSpec.getTls().getProxy() + ); + } + + private void validateComponent( + String componentName, + ComponentCertificateConfig selfSignedConfig, + ComponentCertificateConfig acmeConfig, + TlsConfig.TlsEntryConfig tlsEntryConfig) { + if (selfSignedConfig == null || acmeConfig == null) { + return; } - if (selfSigned.getProxy() != null && acme.getProxy() != null - && Boolean.TRUE.equals(selfSigned.getProxy().getGenerate()) - && Boolean.TRUE.equals(acme.getProxy().getGenerate())) { - String ssSecret = ObjectUtils.firstNonNull(selfSigned.getProxy().getSecretName(), - globalSpec.getTls().getProxy().getSecretName()); - String acmeSecret = ObjectUtils.firstNonNull(acme.getProxy().getSecretName(), - globalSpec.getTls().getProxy().getSecretName()); + + if (Boolean.TRUE.equals(selfSignedConfig.getGenerate()) + && Boolean.TRUE.equals(acmeConfig.getGenerate())) { + + String ssSecret = ObjectUtils.firstNonNull( + selfSignedConfig.getSecretName(), + tlsEntryConfig.getSecretName() + ); + String acmeSecret = ObjectUtils.firstNonNull( + acmeConfig.getSecretName(), + tlsEntryConfig.getSecretName() + ); + if (ObjectUtils.equals(ssSecret, acmeSecret)) { throw new IllegalArgumentException( - String.format("Invalid TLS configuration: both selfSigned and ACME provisioners generate a " - + "proxy certificate using the same secret '%s'. This would cause certificates" - + " to override each other.", ssSecret) + String.format( + "Invalid TLS configuration: both selfSigned and ACME provisioners generate a %s " + + "certificate using the same secret '%s'. This would cause certificates to " + + "override each other.", componentName, ssSecret + ) ); } } From a5bcbd88711409963880deac42786455c12df3e8 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Tue, 17 Mar 2026 12:32:09 +0530 Subject: [PATCH 05/11] Added external certificate support for ACME --- .../CertManagerCertificatesProvisioner.java | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java index 8a60db02..aebbec4c 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java +++ b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import lombok.extern.jbosslog.JBossLog; import org.apache.commons.lang3.ObjectUtils; @@ -224,7 +225,23 @@ private void generateAcmeCertificates() { acme.getPrivateKey() ); } - // TODO external certificate support + if (acme.getExternal() != null) { + for (Map.Entry entry : acme.getExternal().entrySet()) { + final String serviceName = entry.getKey(); + final ComponentCertificateConfig config = entry.getValue(); + + if (Boolean.TRUE.equals(config.getGenerate())) { + createCertificatePerComponent( + config, + serviceName, + config.getSecretName(), + mergeDnsNames(null, config), + issuerName, + acme.getPrivateKey() + ); + } + } + } } public static void validateAcmeIssuerConfig(TlsConfig.AcmeIssuerConfig config) { @@ -561,6 +578,7 @@ private void validateProvisionersConfig() { acme.getProxy(), globalSpec.getTls().getProxy() ); + validateExternal(selfSigned.getExternal(), acme.getExternal()); } private void validateComponent( @@ -584,7 +602,7 @@ private void validateComponent( tlsEntryConfig.getSecretName() ); - if (ObjectUtils.equals(ssSecret, acmeSecret)) { + if (Objects.equals(ssSecret, acmeSecret)) { throw new IllegalArgumentException( String.format( "Invalid TLS configuration: both selfSigned and ACME provisioners generate a %s " @@ -595,4 +613,36 @@ private void validateComponent( } } } + + private void validateExternal(Map selfSignedExternal, + Map acmeExternal) { + + if (selfSignedExternal == null || acmeExternal == null) { + return; + } + for (Map.Entry entry : selfSignedExternal.entrySet()) { + String serviceName = entry.getKey(); + ComponentCertificateConfig ssConfig = entry.getValue(); + ComponentCertificateConfig acmeConfig = acmeExternal.get(serviceName); + if (acmeConfig == null) { + continue; + } + if (Boolean.TRUE.equals(ssConfig.getGenerate()) + && Boolean.TRUE.equals(acmeConfig.getGenerate())) { + + String ssSecret = ssConfig.getSecretName(); + String acmeSecret = acmeConfig.getSecretName(); + + if (Objects.equals(ssSecret, acmeSecret)) { + throw new IllegalArgumentException( + String.format("Invalid TLS configuration: both selfSigned and ACME provisioners generate an" + + " external certificate for service '%s' using the same secret '%s'. " + + "This would cause certificates to override each other.", serviceName, + ssSecret + ) + ); + } + } + } + } } \ No newline at end of file From a9e01c896e7de2a4f1611ca3007d6a47c3f7e820 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Tue, 17 Mar 2026 12:41:53 +0530 Subject: [PATCH 06/11] minor fix when generate is false on external services. --- .../controllers/utils/CertManagerCertificatesProvisioner.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java index aebbec4c..23652632 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java +++ b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java @@ -405,7 +405,9 @@ private List getDnsNamesForExternalServicesForGlobalCert() { for (Map.Entry entry : selfSigned.getExternal().entrySet()) { final ComponentCertificateConfig config = entry.getValue(); - dnsNames.addAll(getBaseK8sDnsNames(config, entry.getKey())); + if (Boolean.TRUE.equals(config.getGenerate())) { + dnsNames.addAll(getBaseK8sDnsNames(config, entry.getKey())); + } } } return dnsNames; From 344979d116b4de57614b1e93d5c2d43e4a4e191a Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Tue, 17 Mar 2026 16:49:26 +0530 Subject: [PATCH 07/11] pre-commit and checkstyle --- docs/api-reference.md | 691 +++++++++++++++++- ...utorecoveries.kaap.oss.datastax.com-v1.yml | 47 +- .../bastions.kaap.oss.datastax.com-v1.yml | 47 +- .../bookkeepers.kaap.oss.datastax.com-v1.yml | 47 +- .../crds/brokers.kaap.oss.datastax.com-v1.yml | 47 +- ...ctionsworkers.kaap.oss.datastax.com-v1.yml | 47 +- .../crds/proxies.kaap.oss.datastax.com-v1.yml | 47 +- ...ulsarclusters.kaap.oss.datastax.com-v1.yml | 47 +- .../zookeepers.kaap.oss.datastax.com-v1.yml | 47 +- .../CertManagerCertificatesProvisioner.java | 20 +- 10 files changed, 1044 insertions(+), 43 deletions(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index 0c1dd435..52b4dbb8 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -61645,6 +61645,13 @@ Certificate provisioner configuration. + acme + object + + ACME certificate provisioner configuration.
+ + false + selfSigned object @@ -61655,6 +61662,662 @@ Certificate provisioner configuration. +### PulsarCluster.spec.global.tls.certProvisioner.acme + + + +ACME certificate provisioner configuration. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
brokerobject + Broker self signed certificate config.
+
false
enabledboolean + Generate self signed certificates for broker, proxy and functions worker.
+
false
externalmap[string]object + External services self signed certificate config (e.g., admin console, grafana). The key is the service name, and the value contains generation config
+
false
issuerobject + ACME issuer configuration.
+
false
privateKeyobject + Cert-manager options for generating the private key.
+
false
proxyobject + Proxy self signed certificate config.
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.broker + + + +Broker self signed certificate config. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
dnsNames[]string + A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
+
false
generateboolean + Generate certificate for the component.
+
false
privateKeyobject + Cert-manager options for generating the private key.
+
false
secretNamestring + The name of the Kubernetes Secret where the generated certificate and key will be stored whe perComponent is enabled. Required for external services. Internal services pick up the secret name from the tls config if not specified
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.broker.privateKey + + + +Cert-manager options for generating the private key. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
algorithmstring +
+
false
encodingstring +
+
false
rotationPolicystring +
+
false
sizeinteger +
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.external[key] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
dnsNames[]string + A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
+
false
generateboolean + Generate certificate for the component.
+
false
privateKeyobject + Cert-manager options for generating the private key.
+
false
secretNamestring + The name of the Kubernetes Secret where the generated certificate and key will be stored whe perComponent is enabled. Required for external services. Internal services pick up the secret name from the tls config if not specified
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.external[key].privateKey + + + +Cert-manager options for generating the private key. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
algorithmstring +
+
false
encodingstring +
+
false
rotationPolicystring +
+
false
sizeinteger +
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.issuer + + + +ACME issuer configuration. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
emailstring + Email used for ACME registration.
+
false
namestring + Name of the Issuer resource.
+
false
privateKeySecretNamestring + Secret storing the ACME account private key.
+
false
serverstring + ACME server URL.
+
false
solvers[]object + ACME challenge solvers. Solvers are evaluated in order; no domain-based routing
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.issuer.solvers[index] + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
dns01object + DNS01 solver configuration.
+
false
http01object + HTTP01 solver configuration.
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.issuer.solvers[index].dns01 + + + +DNS01 solver configuration. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
cloudDNSobject +
+
false
cloudflareobject +
+
false
route53object +
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.issuer.solvers[index].dns01.cloudDNS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
projectstring +
+
false
serviceAccountSecretKeystring +
+
false
serviceAccountSecretNamestring +
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.issuer.solvers[index].dns01.cloudflare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
apiTokenSecretKeystring +
+
false
apiTokenSecretNamestring +
+
false
emailstring +
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.issuer.solvers[index].dns01.route53 + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
hostedZoneIdstring +
+
false
regionstring +
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.issuer.solvers[index].http01 + + + +HTTP01 solver configuration. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
ingressClassstring + Ingress class used for HTTP01 challenge.
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.privateKey + + + +Cert-manager options for generating the private key. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
algorithmstring +
+
false
encodingstring +
+
false
rotationPolicystring +
+
false
sizeinteger +
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.proxy + + + +Proxy self signed certificate config. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
dnsNames[]string + A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
+
false
generateboolean + Generate certificate for the component.
+
false
privateKeyobject + Cert-manager options for generating the private key.
+
false
secretNamestring + The name of the Kubernetes Secret where the generated certificate and key will be stored whe perComponent is enabled. Required for external services. Internal services pick up the secret name from the tls config if not specified
+
false
+ + +### PulsarCluster.spec.global.tls.certProvisioner.acme.proxy.privateKey + + + +Cert-manager options for generating the private key. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
algorithmstring +
+
false
encodingstring +
+
false
rotationPolicystring +
+
false
sizeinteger +
+
false
+ + ### PulsarCluster.spec.global.tls.certProvisioner.selfSigned @@ -61777,14 +62440,14 @@ Autorecovery self signed certificate config. dnsNames []string - A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension along with the default K8s service DNS.
+ A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
false generate boolean - Generate self signed certificates for the component.
+ Generate certificate for the component.
false @@ -61871,14 +62534,14 @@ Bookkeeper self signed certificate config. dnsNames []string - A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension along with the default K8s service DNS.
+ A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
false generate boolean - Generate self signed certificates for the component.
+ Generate certificate for the component.
false @@ -61965,14 +62628,14 @@ Broker self signed certificate config. dnsNames []string - A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension along with the default K8s service DNS.
+ A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
false generate boolean - Generate self signed certificates for the component.
+ Generate certificate for the component.
false @@ -62059,14 +62722,14 @@ Cert-manager options for generating the private key. dnsNames []string - A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension along with the default K8s service DNS.
+ A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
false generate boolean - Generate self signed certificates for the component.
+ Generate certificate for the component.
false @@ -62153,14 +62816,14 @@ Functions worker self signed certificate config. dnsNames []string - A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension along with the default K8s service DNS.
+ A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
false generate boolean - Generate self signed certificates for the component.
+ Generate certificate for the component.
false @@ -62294,14 +62957,14 @@ Proxy self signed certificate config. dnsNames []string - A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension along with the default K8s service DNS.
+ A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
false generate boolean - Generate self signed certificates for the component.
+ Generate certificate for the component.
false @@ -62388,14 +63051,14 @@ Zookeeper self signed certificate config. dnsNames []string - A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension along with the default K8s service DNS.
+ A list of DNS names (and IP addresses) to include in the certificate's Subject Alternative Names (SANs) extension.
false generate boolean - Generate self signed certificates for the component.
+ Generate certificate for the component.
false diff --git a/helm/kaap/crds/autorecoveries.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/autorecoveries.kaap.oss.datastax.com-v1.yml index 4c559ab7..1fc6fd9f 100644 --- a/helm/kaap/crds/autorecoveries.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/autorecoveries.kaap.oss.datastax.com-v1.yml @@ -1770,9 +1770,6 @@ spec: email: description: "Email used for ACME registration." type: "string" - ingressClass: - description: "Ingress class used for HTTP01 challenge." - type: "string" name: description: "Name of the Issuer resource." type: "string" @@ -1783,6 +1780,50 @@ spec: server: description: "ACME server URL." type: "string" + solvers: + description: "ACME challenge solvers. Solvers\ + \ are evaluated in order; no domain-based routing" + items: + properties: + dns01: + description: "DNS01 solver configuration." + properties: + cloudDNS: + properties: + project: + type: "string" + serviceAccountSecretKey: + type: "string" + serviceAccountSecretName: + type: "string" + type: "object" + cloudflare: + properties: + apiTokenSecretKey: + type: "string" + apiTokenSecretName: + type: "string" + email: + type: "string" + type: "object" + route53: + properties: + hostedZoneId: + type: "string" + region: + type: "string" + type: "object" + type: "object" + http01: + description: "HTTP01 solver configuration." + properties: + ingressClass: + description: "Ingress class used for\ + \ HTTP01 challenge." + type: "string" + type: "object" + type: "object" + type: "array" type: "object" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/bastions.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/bastions.kaap.oss.datastax.com-v1.yml index 3f6a6b71..24d98245 100644 --- a/helm/kaap/crds/bastions.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/bastions.kaap.oss.datastax.com-v1.yml @@ -1774,9 +1774,6 @@ spec: email: description: "Email used for ACME registration." type: "string" - ingressClass: - description: "Ingress class used for HTTP01 challenge." - type: "string" name: description: "Name of the Issuer resource." type: "string" @@ -1787,6 +1784,50 @@ spec: server: description: "ACME server URL." type: "string" + solvers: + description: "ACME challenge solvers. Solvers\ + \ are evaluated in order; no domain-based routing" + items: + properties: + dns01: + description: "DNS01 solver configuration." + properties: + cloudDNS: + properties: + project: + type: "string" + serviceAccountSecretKey: + type: "string" + serviceAccountSecretName: + type: "string" + type: "object" + cloudflare: + properties: + apiTokenSecretKey: + type: "string" + apiTokenSecretName: + type: "string" + email: + type: "string" + type: "object" + route53: + properties: + hostedZoneId: + type: "string" + region: + type: "string" + type: "object" + type: "object" + http01: + description: "HTTP01 solver configuration." + properties: + ingressClass: + description: "Ingress class used for\ + \ HTTP01 challenge." + type: "string" + type: "object" + type: "object" + type: "array" type: "object" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/bookkeepers.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/bookkeepers.kaap.oss.datastax.com-v1.yml index beac153b..b733eab6 100644 --- a/helm/kaap/crds/bookkeepers.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/bookkeepers.kaap.oss.datastax.com-v1.yml @@ -5117,9 +5117,6 @@ spec: email: description: "Email used for ACME registration." type: "string" - ingressClass: - description: "Ingress class used for HTTP01 challenge." - type: "string" name: description: "Name of the Issuer resource." type: "string" @@ -5130,6 +5127,50 @@ spec: server: description: "ACME server URL." type: "string" + solvers: + description: "ACME challenge solvers. Solvers\ + \ are evaluated in order; no domain-based routing" + items: + properties: + dns01: + description: "DNS01 solver configuration." + properties: + cloudDNS: + properties: + project: + type: "string" + serviceAccountSecretKey: + type: "string" + serviceAccountSecretName: + type: "string" + type: "object" + cloudflare: + properties: + apiTokenSecretKey: + type: "string" + apiTokenSecretName: + type: "string" + email: + type: "string" + type: "object" + route53: + properties: + hostedZoneId: + type: "string" + region: + type: "string" + type: "object" + type: "object" + http01: + description: "HTTP01 solver configuration." + properties: + ingressClass: + description: "Ingress class used for\ + \ HTTP01 challenge." + type: "string" + type: "object" + type: "object" + type: "array" type: "object" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/brokers.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/brokers.kaap.oss.datastax.com-v1.yml index 39b186c3..86e1eeb1 100644 --- a/helm/kaap/crds/brokers.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/brokers.kaap.oss.datastax.com-v1.yml @@ -5049,9 +5049,6 @@ spec: email: description: "Email used for ACME registration." type: "string" - ingressClass: - description: "Ingress class used for HTTP01 challenge." - type: "string" name: description: "Name of the Issuer resource." type: "string" @@ -5062,6 +5059,50 @@ spec: server: description: "ACME server URL." type: "string" + solvers: + description: "ACME challenge solvers. Solvers\ + \ are evaluated in order; no domain-based routing" + items: + properties: + dns01: + description: "DNS01 solver configuration." + properties: + cloudDNS: + properties: + project: + type: "string" + serviceAccountSecretKey: + type: "string" + serviceAccountSecretName: + type: "string" + type: "object" + cloudflare: + properties: + apiTokenSecretKey: + type: "string" + apiTokenSecretName: + type: "string" + email: + type: "string" + type: "object" + route53: + properties: + hostedZoneId: + type: "string" + region: + type: "string" + type: "object" + type: "object" + http01: + description: "HTTP01 solver configuration." + properties: + ingressClass: + description: "Ingress class used for\ + \ HTTP01 challenge." + type: "string" + type: "object" + type: "object" + type: "array" type: "object" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/functionsworkers.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/functionsworkers.kaap.oss.datastax.com-v1.yml index 65d74fd8..425977f3 100644 --- a/helm/kaap/crds/functionsworkers.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/functionsworkers.kaap.oss.datastax.com-v1.yml @@ -2660,9 +2660,6 @@ spec: email: description: "Email used for ACME registration." type: "string" - ingressClass: - description: "Ingress class used for HTTP01 challenge." - type: "string" name: description: "Name of the Issuer resource." type: "string" @@ -2673,6 +2670,50 @@ spec: server: description: "ACME server URL." type: "string" + solvers: + description: "ACME challenge solvers. Solvers\ + \ are evaluated in order; no domain-based routing" + items: + properties: + dns01: + description: "DNS01 solver configuration." + properties: + cloudDNS: + properties: + project: + type: "string" + serviceAccountSecretKey: + type: "string" + serviceAccountSecretName: + type: "string" + type: "object" + cloudflare: + properties: + apiTokenSecretKey: + type: "string" + apiTokenSecretName: + type: "string" + email: + type: "string" + type: "object" + route53: + properties: + hostedZoneId: + type: "string" + region: + type: "string" + type: "object" + type: "object" + http01: + description: "HTTP01 solver configuration." + properties: + ingressClass: + description: "Ingress class used for\ + \ HTTP01 challenge." + type: "string" + type: "object" + type: "object" + type: "array" type: "object" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/proxies.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/proxies.kaap.oss.datastax.com-v1.yml index e1e66e1a..74c2a820 100644 --- a/helm/kaap/crds/proxies.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/proxies.kaap.oss.datastax.com-v1.yml @@ -427,9 +427,6 @@ spec: email: description: "Email used for ACME registration." type: "string" - ingressClass: - description: "Ingress class used for HTTP01 challenge." - type: "string" name: description: "Name of the Issuer resource." type: "string" @@ -440,6 +437,50 @@ spec: server: description: "ACME server URL." type: "string" + solvers: + description: "ACME challenge solvers. Solvers\ + \ are evaluated in order; no domain-based routing" + items: + properties: + dns01: + description: "DNS01 solver configuration." + properties: + cloudDNS: + properties: + project: + type: "string" + serviceAccountSecretKey: + type: "string" + serviceAccountSecretName: + type: "string" + type: "object" + cloudflare: + properties: + apiTokenSecretKey: + type: "string" + apiTokenSecretName: + type: "string" + email: + type: "string" + type: "object" + route53: + properties: + hostedZoneId: + type: "string" + region: + type: "string" + type: "object" + type: "object" + http01: + description: "HTTP01 solver configuration." + properties: + ingressClass: + description: "Ingress class used for\ + \ HTTP01 challenge." + type: "string" + type: "object" + type: "object" + type: "array" type: "object" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/pulsarclusters.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/pulsarclusters.kaap.oss.datastax.com-v1.yml index 240b80e6..958a2896 100644 --- a/helm/kaap/crds/pulsarclusters.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/pulsarclusters.kaap.oss.datastax.com-v1.yml @@ -14658,9 +14658,6 @@ spec: email: description: "Email used for ACME registration." type: "string" - ingressClass: - description: "Ingress class used for HTTP01 challenge." - type: "string" name: description: "Name of the Issuer resource." type: "string" @@ -14671,6 +14668,50 @@ spec: server: description: "ACME server URL." type: "string" + solvers: + description: "ACME challenge solvers. Solvers\ + \ are evaluated in order; no domain-based routing" + items: + properties: + dns01: + description: "DNS01 solver configuration." + properties: + cloudDNS: + properties: + project: + type: "string" + serviceAccountSecretKey: + type: "string" + serviceAccountSecretName: + type: "string" + type: "object" + cloudflare: + properties: + apiTokenSecretKey: + type: "string" + apiTokenSecretName: + type: "string" + email: + type: "string" + type: "object" + route53: + properties: + hostedZoneId: + type: "string" + region: + type: "string" + type: "object" + type: "object" + http01: + description: "HTTP01 solver configuration." + properties: + ingressClass: + description: "Ingress class used for\ + \ HTTP01 challenge." + type: "string" + type: "object" + type: "object" + type: "array" type: "object" privateKey: description: "Cert-manager options for generating\ diff --git a/helm/kaap/crds/zookeepers.kaap.oss.datastax.com-v1.yml b/helm/kaap/crds/zookeepers.kaap.oss.datastax.com-v1.yml index 7eaf465a..fa3396e9 100644 --- a/helm/kaap/crds/zookeepers.kaap.oss.datastax.com-v1.yml +++ b/helm/kaap/crds/zookeepers.kaap.oss.datastax.com-v1.yml @@ -427,9 +427,6 @@ spec: email: description: "Email used for ACME registration." type: "string" - ingressClass: - description: "Ingress class used for HTTP01 challenge." - type: "string" name: description: "Name of the Issuer resource." type: "string" @@ -440,6 +437,50 @@ spec: server: description: "ACME server URL." type: "string" + solvers: + description: "ACME challenge solvers. Solvers\ + \ are evaluated in order; no domain-based routing" + items: + properties: + dns01: + description: "DNS01 solver configuration." + properties: + cloudDNS: + properties: + project: + type: "string" + serviceAccountSecretKey: + type: "string" + serviceAccountSecretName: + type: "string" + type: "object" + cloudflare: + properties: + apiTokenSecretKey: + type: "string" + apiTokenSecretName: + type: "string" + email: + type: "string" + type: "object" + route53: + properties: + hostedZoneId: + type: "string" + region: + type: "string" + type: "object" + type: "object" + http01: + description: "HTTP01 solver configuration." + properties: + ingressClass: + description: "Ingress class used for\ + \ HTTP01 challenge." + type: "string" + type: "object" + type: "object" + type: "array" type: "object" privateKey: description: "Cert-manager options for generating\ diff --git a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java index 23652632..c90f4145 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java +++ b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java @@ -250,8 +250,12 @@ public static void validateAcmeIssuerConfig(TlsConfig.AcmeIssuerConfig config) { } for (TlsConfig.SolverConfig solver : config.getSolvers()) { int count = 0; - if (solver.getHttp01() != null) count++; - if (solver.getDns01() != null) count++; + if (solver.getHttp01() != null) { + count++; + } + if (solver.getDns01() != null) { + count++; + } if (count != 1) { throw new IllegalArgumentException( "Exactly one of http01 or dns01 must be configured per solver"); @@ -259,9 +263,15 @@ public static void validateAcmeIssuerConfig(TlsConfig.AcmeIssuerConfig config) { if (solver.getDns01() != null) { TlsConfig.Dns01Config dns = solver.getDns01(); int providers = 0; - if (dns.getRoute53() != null) providers++; - if (dns.getCloudflare() != null) providers++; - if (dns.getCloudDNS() != null) providers++; + if (dns.getRoute53() != null) { + providers++; + } + if (dns.getCloudflare() != null) { + providers++; + } + if (dns.getCloudDNS() != null) { + providers++; + } if (providers != 1) { throw new IllegalArgumentException( "Exactly one DNS provider must be configured per solver"); From a409203c1ea7467cbe1b666c0060899320bdbfb5 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Thu, 19 Mar 2026 16:58:46 +0530 Subject: [PATCH 08/11] moved from deprecated createOrReplace to serverSideApply with forceConflicts --- .../CertManagerCertificatesProvisioner.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java index c90f4145..c66af227 100644 --- a/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java +++ b/operator/src/main/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisioner.java @@ -349,7 +349,7 @@ private void createAcmeIssuer() { ) .endSpec() .build(); - client.resource(issuer).inNamespace(namespace).createOrReplace(); + client.resource(issuer).inNamespace(namespace).forceConflicts().serverSideApply(); } private void createAutorecoveryCertificate() { @@ -379,7 +379,8 @@ private void createAutorecoveryCertificate() { client.resource(caCertificate) .inNamespace(namespace) - .createOrReplace(); + .forceConflicts() + .serverSideApply(); } private void createCertificatePerComponent(final ComponentCertificateConfig componentConfig, @@ -513,7 +514,8 @@ private void createCertificate(String name, .build(); client.resource(certificate) .inNamespace(namespace) - .createOrReplace(); + .forceConflicts() + .serverSideApply(); log.debugf("Created certificate %s mapped to secret %s", name, secretName); } @@ -533,7 +535,8 @@ private void createRootCACertificate() { client.resource(ssIssuer) .inNamespace(namespace) - .createOrReplace(); + .forceConflicts() + .serverSideApply(); final Certificate caCertificate = new CertificateBuilder() .withNewMetadata() @@ -554,7 +557,8 @@ private void createRootCACertificate() { client.resource(caCertificate) .inNamespace(namespace) - .createOrReplace(); + .forceConflicts() + .serverSideApply(); final Issuer caIssuer = new IssuerBuilder() .withNewMetadata() @@ -570,7 +574,8 @@ private void createRootCACertificate() { client.resource(caIssuer) .inNamespace(namespace) - .createOrReplace(); + .forceConflicts() + .serverSideApply(); log.debug("Created self-signed root CA certificate"); } From c6826db457f24d7982ab9893f2d3b336852a56f1 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Thu, 19 Mar 2026 17:17:38 +0530 Subject: [PATCH 09/11] test fix for forceConflicts() Update the annotation to jupiter to allow it to run in CI --- .../java/com/datastax/oss/kaap/mocks/MockKubernetesClient.java | 2 ++ .../utils/CertManagerCertificatesProvisionerTest.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/operator-common/src/test/java/com/datastax/oss/kaap/mocks/MockKubernetesClient.java b/operator-common/src/test/java/com/datastax/oss/kaap/mocks/MockKubernetesClient.java index 79e789dc..6c413a1e 100644 --- a/operator-common/src/test/java/com/datastax/oss/kaap/mocks/MockKubernetesClient.java +++ b/operator-common/src/test/java/com/datastax/oss/kaap/mocks/MockKubernetesClient.java @@ -195,6 +195,7 @@ private void mockAndInterceptResourceCreation(String namespace) { final NamespaceableResource interaction = Mockito.mock(NamespaceableResource.class); when(interaction.inNamespace(eq(namespace))).thenReturn(interaction); + when(interaction.forceConflicts()).thenReturn(interaction); when(interaction.create()).thenAnswer(ic1 -> { addCreatedResource(ic); return null; @@ -217,6 +218,7 @@ private void mockAndInterceptResourceCreation(String namespace) { final Resource resourceMock = Mockito.mock(Resource.class); when(interaction.resource(any(HasMetadata.class))).thenAnswer(ic2 -> { final Resource mockedResource = resourceMock; + when(mockedResource.forceConflicts()).thenReturn(mockedResource); when(mockedResource.create()).thenAnswer(ic3 -> { addCreatedResource(ic2); return null; diff --git a/operator/src/test/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisionerTest.java b/operator/src/test/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisionerTest.java index 50bb6da1..2479f1a8 100644 --- a/operator/src/test/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisionerTest.java +++ b/operator/src/test/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisionerTest.java @@ -20,8 +20,8 @@ import com.datastax.oss.kaap.mocks.MockKubernetesClient; import io.fabric8.certmanager.api.model.v1.Certificate; import io.fabric8.certmanager.api.model.v1.Issuer; +import org.junit.jupiter.api.Test; import org.testng.Assert; -import org.testng.annotations.Test; public class CertManagerCertificatesProvisionerTest { From 5538be645a7b37acb3eb351de0dd9af9e03b1e31 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Fri, 20 Mar 2026 11:23:54 +0530 Subject: [PATCH 10/11] updated rbac rules for cert-manager issuers and certificates to include patch --- helm/kaap/templates/rbac/cluster-scoped.yaml | 1 + helm/kaap/templates/rbac/namespace-scoped.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/helm/kaap/templates/rbac/cluster-scoped.yaml b/helm/kaap/templates/rbac/cluster-scoped.yaml index 7d4a677f..f9ef9f3b 100644 --- a/helm/kaap/templates/rbac/cluster-scoped.yaml +++ b/helm/kaap/templates/rbac/cluster-scoped.yaml @@ -140,6 +140,7 @@ rules: - get - update - list + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/helm/kaap/templates/rbac/namespace-scoped.yaml b/helm/kaap/templates/rbac/namespace-scoped.yaml index cbe4fc6f..033bb26c 100644 --- a/helm/kaap/templates/rbac/namespace-scoped.yaml +++ b/helm/kaap/templates/rbac/namespace-scoped.yaml @@ -160,6 +160,7 @@ rules: - get - update - list + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding From 727881aee977141afcc27494f16f74d49fa7ac92 Mon Sep 17 00:00:00 2001 From: sandeep-ctds Date: Sat, 21 Mar 2026 19:03:42 +0530 Subject: [PATCH 11/11] added unit test for the acme certificate generation flow --- ...ertManagerCertificatesProvisionerTest.java | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) diff --git a/operator/src/test/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisionerTest.java b/operator/src/test/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisionerTest.java index 2479f1a8..ce28f14d 100644 --- a/operator/src/test/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisionerTest.java +++ b/operator/src/test/java/com/datastax/oss/kaap/controllers/utils/CertManagerCertificatesProvisionerTest.java @@ -18,6 +18,7 @@ import com.datastax.oss.kaap.common.SerializationUtil; import com.datastax.oss.kaap.crds.cluster.PulsarClusterSpec; import com.datastax.oss.kaap.mocks.MockKubernetesClient; +import io.fabric8.certmanager.api.model.acme.v1.ACMEChallengeSolver; import io.fabric8.certmanager.api.model.v1.Certificate; import io.fabric8.certmanager.api.model.v1.Issuer; import org.junit.jupiter.api.Test; @@ -451,6 +452,243 @@ public void testGeneratePerComponent() throws Exception { } + @Test + public void testAcmeIssuerHttp01() { + final MockKubernetesClient mockKubernetesClient = generateCertificates(""" + global: + name: pul + tls: + enabled: true + broker: + enabled: true + secretName: pul-broker-tls + certProvisioner: + acme: + enabled: true + broker: + generate: true + secretName: pul-broker-tls + issuer: + name: pul-acme-issuer + server: https://acme-v02.api.letsencrypt.org/directory + email: admin@example.com + privateKeySecretName: pul-acme-account-key + solvers: + - http01: + ingressClass: nginx + """); + + final Issuer issuer = mockKubernetesClient + .getCreatedResource(Issuer.class, "pul-acme-issuer") + .getResource(); + assertAcmeIssuerBase(issuer); + Assert.assertEquals(issuer.getSpec().getAcme().getSolvers().size(), 1); + + final ACMEChallengeSolver solver = issuer.getSpec().getAcme().getSolvers().getFirst(); + Assert.assertNotNull(solver.getHttp01()); + Assert.assertNotNull(solver.getHttp01().getIngress()); + Assert.assertEquals(solver.getHttp01().getIngress().getIngressClassName(), "nginx"); + Assert.assertNull(solver.getDns01()); + } + + @Test + public void testAcmeIssuerRoute53() { + final MockKubernetesClient mockKubernetesClient = generateCertificates(""" + global: + name: pul + tls: + enabled: true + broker: + enabled: true + secretName: pul-broker-tls + certProvisioner: + acme: + enabled: true + broker: + generate: true + secretName: pul-broker-tls + issuer: + name: pul-acme-issuer + server: https://acme-v02.api.letsencrypt.org/directory + email: admin@example.com + privateKeySecretName: pul-acme-account-key + solvers: + - dns01: + route53: + region: us-east-1 + hostedZoneId: Z123456789 + """); + + final Issuer issuer = mockKubernetesClient + .getCreatedResource(Issuer.class, "pul-acme-issuer") + .getResource(); + assertAcmeIssuerBase(issuer); + Assert.assertEquals(issuer.getSpec().getAcme().getSolvers().size(), 1); + + final ACMEChallengeSolver solver = issuer.getSpec().getAcme().getSolvers().getFirst(); + Assert.assertNull(solver.getHttp01()); + Assert.assertNotNull(solver.getDns01()); + Assert.assertNotNull(solver.getDns01().getRoute53()); + Assert.assertEquals(solver.getDns01().getRoute53().getRegion(), "us-east-1"); + Assert.assertEquals(solver.getDns01().getRoute53().getHostedZoneID(), "Z123456789"); + Assert.assertNull(solver.getDns01().getCloudflare()); + Assert.assertNull(solver.getDns01().getCloudDNS()); + } + + @Test + public void testAcmeIssuerCloudflare() { + final MockKubernetesClient mockKubernetesClient = generateCertificates(""" + global: + name: pul + tls: + enabled: true + broker: + enabled: true + secretName: pul-broker-tls + certProvisioner: + acme: + enabled: true + broker: + generate: true + secretName: pul-broker-tls + issuer: + name: pul-acme-issuer + server: https://acme-v02.api.letsencrypt.org/directory + email: admin@example.com + privateKeySecretName: pul-acme-account-key + solvers: + - dns01: + cloudflare: + email: cf-admin@example.com + apiTokenSecretName: cf-api-token + apiTokenSecretKey: token + """); + + final Issuer issuer = mockKubernetesClient + .getCreatedResource(Issuer.class, "pul-acme-issuer") + .getResource(); + assertAcmeIssuerBase(issuer); + Assert.assertEquals(issuer.getSpec().getAcme().getSolvers().size(), 1); + + final ACMEChallengeSolver solver = issuer.getSpec().getAcme().getSolvers().getFirst(); + Assert.assertNull(solver.getHttp01()); + Assert.assertNotNull(solver.getDns01()); + Assert.assertNotNull(solver.getDns01().getCloudflare()); + Assert.assertEquals(solver.getDns01().getCloudflare().getEmail(), "cf-admin@example.com"); + Assert.assertEquals(solver.getDns01().getCloudflare().getApiTokenSecretRef().getName(), + "cf-api-token"); + Assert.assertEquals(solver.getDns01().getCloudflare().getApiTokenSecretRef().getKey(), "token"); + Assert.assertNull(solver.getDns01().getRoute53()); + Assert.assertNull(solver.getDns01().getCloudDNS()); + } + + @Test + public void testAcmeIssuerCloudDns() { + final MockKubernetesClient mockKubernetesClient = generateCertificates(""" + global: + name: pul + tls: + enabled: true + broker: + enabled: true + secretName: pul-broker-tls + certProvisioner: + acme: + enabled: true + broker: + generate: true + secretName: pul-broker-tls + issuer: + name: pul-acme-issuer + server: https://acme-v02.api.letsencrypt.org/directory + email: admin@example.com + privateKeySecretName: pul-acme-account-key + solvers: + - dns01: + cloudDNS: + project: my-gcp-project + serviceAccountSecretName: gcp-sa-secret + serviceAccountSecretKey: service-account.json + """); + + final Issuer issuer = mockKubernetesClient + .getCreatedResource(Issuer.class, "pul-acme-issuer") + .getResource(); + assertAcmeIssuerBase(issuer); + Assert.assertEquals(issuer.getSpec().getAcme().getSolvers().size(), 1); + + final ACMEChallengeSolver solver = issuer.getSpec().getAcme().getSolvers().getFirst(); + Assert.assertNull(solver.getHttp01()); + Assert.assertNotNull(solver.getDns01()); + Assert.assertNotNull(solver.getDns01().getCloudDNS()); + Assert.assertEquals(solver.getDns01().getCloudDNS().getProject(), "my-gcp-project"); + Assert.assertEquals( + solver.getDns01().getCloudDNS().getServiceAccountSecretRef().getName(), + "gcp-sa-secret" + ); + Assert.assertEquals( + solver.getDns01().getCloudDNS().getServiceAccountSecretRef().getKey(), + "service-account.json" + ); + Assert.assertNull(solver.getDns01().getRoute53()); + Assert.assertNull(solver.getDns01().getCloudflare()); + } + + @Test + public void testAcmeCertificateGeneration() { + final MockKubernetesClient mockKubernetesClient = generateCertificates(""" + global: + name: pul + tls: + enabled: true + broker: + enabled: true + secretName: pul-broker-tls + certProvisioner: + acme: + enabled: true + broker: + generate: true + secretName: pul-broker-tls + issuer: + name: pul-acme-issuer + server: https://acme-v02.api.letsencrypt.org/directory + email: admin@example.com + privateKeySecretName: pul-acme-account-key + solvers: + - http01: + ingressClass: nginx + """); + + final Certificate brokerCertificate = + mockKubernetesClient.getCreatedResource(Certificate.class, "pul-broker-tls").getResource(); + Assert.assertNotNull(brokerCertificate); + Assert.assertEquals(brokerCertificate.getSpec().getIssuerRef().getName(), "pul-acme-issuer"); + Assert.assertEquals(brokerCertificate.getSpec().getSecretName(), "pul-broker-tls"); + + final Issuer issuer = mockKubernetesClient + .getCreatedResource(Issuer.class, "pul-acme-issuer") + .getResource(); + Assert.assertNotNull(issuer); + Assert.assertEquals(issuer.getSpec().getAcme().getSolvers().size(), 1); + } + + private static void assertAcmeIssuerBase(Issuer issuer) { + Assert.assertNotNull(issuer); + Assert.assertEquals(issuer.getMetadata().getName(), "pul-acme-issuer"); + Assert.assertEquals(issuer.getMetadata().getNamespace(), NAMESPACE); + + Assert.assertNotNull(issuer.getSpec()); + Assert.assertNotNull(issuer.getSpec().getAcme()); + Assert.assertEquals( + issuer.getSpec().getAcme().getServer(), + "https://acme-v02.api.letsencrypt.org/directory" + ); + Assert.assertEquals(issuer.getSpec().getAcme().getEmail(), "admin@example.com"); + Assert.assertEquals(issuer.getSpec().getAcme().getPrivateKeySecretRef().getName(), + "pul-acme-account-key"); + } + private MockKubernetesClient generateCertificates(String spec) { final PulsarClusterSpec pulsarClusterSpec = SerializationUtil.readYaml(spec, PulsarClusterSpec.class);