From 6fbc0c7d1392dfee619919634dc16586d9a4f2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Mouton?= Date: Wed, 17 Dec 2025 15:12:29 +0100 Subject: [PATCH 1/3] Add configurable TLS secret key names for etcd client certificates --- pkg/solver/solver.go | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/pkg/solver/solver.go b/pkg/solver/solver.go index 9801a9b..1d63479 100644 --- a/pkg/solver/solver.go +++ b/pkg/solver/solver.go @@ -40,6 +40,12 @@ type EtcdConfig struct { TLSSecretRef string `json:"tlsSecretRef,omitempty"` // TLSSecretNamespace is the namespace of the TLS secret (defaults to challenge namespace) TLSSecretNamespace string `json:"tlsSecretNamespace,omitempty"` + // TLSCAKey is the key name for CA certificate in the secret (default: ca.crt) + TLSCAKey string `json:"tlsCAKey,omitempty"` + // TLSCertKey is the key name for client certificate in the secret (default: tls.crt) + TLSCertKey string `json:"tlsCertKey,omitempty"` + // TLSKeyKey is the key name for client private key in the secret (default: tls.key) + TLSKeyKey string `json:"tlsKeyKey,omitempty"` // TLSInsecureSkipVerify skips TLS certificate verification (not recommended for production) TLSInsecureSkipVerify bool `json:"tlsInsecureSkipVerify,omitempty"` // TLSCA is the CA certificate in PEM format (alternative to using a secret) @@ -300,19 +306,35 @@ func (e *EtcdDNSSolver) loadTLSConfigFromSecret(cfg *EtcdConfig, ch *v1alpha1.Ch InsecureSkipVerify: cfg.TLSInsecureSkipVerify, } + // Determine key names (use defaults if not specified) + caKey := cfg.TLSCAKey + if caKey == "" { + caKey = "ca.crt" + } + certKey := cfg.TLSCertKey + if certKey == "" { + certKey = "tls.crt" + } + keyKey := cfg.TLSKeyKey + if keyKey == "" { + keyKey = "tls.key" + } + + klog.V(2).Infof("Using TLS secret keys: CA=%s, Cert=%s, Key=%s", caKey, certKey, keyKey) + // Load CA certificate if present - if caCert, ok := secret.Data["ca.crt"]; ok { + if caCert, ok := secret.Data[caKey]; ok { caCertPool := x509.NewCertPool() if !caCertPool.AppendCertsFromPEM(caCert) { - return nil, fmt.Errorf("failed to parse CA certificate") + return nil, fmt.Errorf("failed to parse CA certificate from key '%s'", caKey) } tlsConfig.RootCAs = caCertPool - klog.V(2).Info("CA certificate loaded from secret") + klog.V(2).Infof("CA certificate loaded from secret key '%s'", caKey) } // Load client certificate and key if present (for mTLS) - clientCert, certOk := secret.Data["tls.crt"] - clientKey, keyOk := secret.Data["tls.key"] + clientCert, certOk := secret.Data[certKey] + clientKey, keyOk := secret.Data[keyKey] if certOk && keyOk { cert, err := tls.X509KeyPair(clientCert, clientKey) @@ -320,9 +342,9 @@ func (e *EtcdDNSSolver) loadTLSConfigFromSecret(cfg *EtcdConfig, ch *v1alpha1.Ch return nil, fmt.Errorf("failed to load client certificate and key: %v", err) } tlsConfig.Certificates = []tls.Certificate{cert} - klog.V(2).Info("Client certificate and key loaded from secret") + klog.V(2).Infof("Client certificate and key loaded from secret keys '%s' and '%s'", certKey, keyKey) } else if certOk || keyOk { - return nil, fmt.Errorf("both tls.crt and tls.key must be present in the secret for client authentication") + return nil, fmt.Errorf("both '%s' and '%s' must be present in the secret for client authentication", certKey, keyKey) } return tlsConfig, nil From 74cb528519c29fe47f83de91c44a7fccd535c94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Mouton?= Date: Wed, 17 Dec 2025 15:13:04 +0100 Subject: [PATCH 2/3] Update README.md to clarify groupName matching requirements and add TLS secret key configuration options --- README.md | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b589a55..0f034f0 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ kubectl apply -f deploy/apiservice.yaml ## ⚙️ Configuration +> **⚠️ Important:** Make sure the `groupName` in your Helm values/deployment matches **exactly** the `groupName` in your ClusterIssuer. This is a common source of configuration errors. + ### 1. Create a ClusterIssuer ```yaml @@ -117,6 +119,9 @@ spec: | `dialTimeout` | Connection timeout (seconds) | no | `10` | | `tlsSecretRef` | Name of Kubernetes secret containing TLS certs | no | - | | `tlsSecretNamespace` | Namespace of the TLS secret | no | challenge namespace | +| `tlsCAKey` | Key name for CA certificate in the secret | no | `ca.crt` | +| `tlsCertKey` | Key name for client certificate in the secret | no | `tls.crt` | +| `tlsKeyKey` | Key name for client private key in the secret | no | `tls.key` | | `tlsInsecureSkipVerify` | Skip TLS verification (not recommended) | no | `false` | ## 🔐 TLS Configuration @@ -261,23 +266,28 @@ REGISTRY=ghcr.io/your-org make docker-push ## 🐛 Troubleshooting -### DNS challenge not resolving +**⚠️ Important:** The `groupName` must match exactly in: +1. Helm values (or deployment ENV) +2. ClusterIssuer `webhook.groupName` field -1. Verify that etcd is accessible from the webhook -2. Check the prefix in the configuration -3. Verify that CoreDNS uses the same prefix +Common error: `"failed to load config: config is required"` - See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for detailed debugging steps. -### Authentication error +### Quick Checks -Check the etcd credentials in the Issuer configuration. +```bash +# Verify groupName matches everywhere +kubectl get deployment cert-manager-webhook-etcd -n cert-manager -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="GROUP_NAME")].value}' +kubectl get clusterissuer letsencrypt-prod -o yaml | grep groupName -### Webhook not starting +# Check APIService status +kubectl get apiservice | grep acme -```bash -kubectl describe pod -n cert-manager -l app.kubernetes.io/name=cert-manager-webhook-etcd +# Check webhook logs kubectl logs -n cert-manager -l app.kubernetes.io/name=cert-manager-webhook-etcd ``` +For detailed troubleshooting, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md). + ## 📄 License Apache License 2.0 From 3156ead17f7acf7d0551bb8b3599a094f5d8129e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Mouton?= Date: Wed, 17 Dec 2025 15:13:54 +0100 Subject: [PATCH 3/3] Add optional custom key names for TLS secret in issuer example --- deploy/examples/issuer.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/deploy/examples/issuer.yaml b/deploy/examples/issuer.yaml index db11290..dcb690e 100644 --- a/deploy/examples/issuer.yaml +++ b/deploy/examples/issuer.yaml @@ -55,9 +55,17 @@ spec: # TLS configuration - reference to a Kubernetes secret tlsSecretRef: "etcd-tls-certs" tlsSecretNamespace: "etcd" + # Optional: Custom key names in the TLS secret (defaults shown) + # tlsCAKey: "ca.crt" # Key name for CA certificate + # tlsCertKey: "tls.crt" # Key name for client certificate + # tlsKeyKey: "tls.key" # Key name for client private key + # Example with custom key names: + # tlsCAKey: "etcd-ca.crt" + # tlsCertKey: "etcd-server.crt" + # tlsKeyKey: "etcd-server.key" --- # Example TLS Secret for etcd connection -# The secret should contain: +# The secret should contain (key names are configurable via tlsCAKey, tlsCertKey, tlsKeyKey): # - ca.crt: CA certificate to verify etcd server # - tls.crt: Client certificate (for mTLS) # - tls.key: Client private key (for mTLS)