diff --git a/api/v1alpha1/temporalconnection_types.go b/api/v1alpha1/temporalconnection_types.go index f085b07f..b5cc2a80 100644 --- a/api/v1alpha1/temporalconnection_types.go +++ b/api/v1alpha1/temporalconnection_types.go @@ -27,8 +27,10 @@ type TemporalConnectionSpec struct { HostPort string `json:"hostPort"` // MutualTLSSecretRef is the name of the Secret that contains the TLS certificate and key - // for mutual TLS authentication. The secret must be `type: kubernetes.io/tls` and exist - // in the same Kubernetes namespace as the TemporalConnection resource. + // for mutual TLS authentication. The secret must be `type: kubernetes.io/tls` or + // `type: Opaque` and exist in the same Kubernetes namespace as the TemporalConnection + // resource. Opaque secrets are useful when bundling tls.crt, tls.key, and ca.crt into + // a single secret (e.g. multi-file cert-manager outputs). // // More information about creating a TLS secret: // https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets diff --git a/internal/controller/clientpool/clientpool.go b/internal/controller/clientpool/clientpool.go index ac8e114c..b5b9976b 100644 --- a/internal/controller/clientpool/clientpool.go +++ b/internal/controller/clientpool/clientpool.go @@ -249,8 +249,8 @@ func (cp *ClientPool) ParseClientSecret( // Check the secret type switch authMode { case AuthModeTLS: - if secret.Type != corev1.SecretTypeTLS { - err := fmt.Errorf("secret %s must be of type kubernetes.io/tls", secret.Name) + if secret.Type != corev1.SecretTypeTLS && secret.Type != corev1.SecretTypeOpaque { + err := fmt.Errorf("secret %s must be of type kubernetes.io/tls or Opaque", secret.Name) return nil, nil, nil, err } return cp.fetchClientUsingMTLSSecret(secret, opts) diff --git a/internal/controller/clientpool/clientpool_test.go b/internal/controller/clientpool/clientpool_test.go index 067bd586..b00cf014 100644 --- a/internal/controller/clientpool/clientpool_test.go +++ b/internal/controller/clientpool/clientpool_test.go @@ -270,26 +270,27 @@ func TestFetchAPIKey_CredentialsAndTLSSet(t *testing.T) { // ─── Tests: ParseClientSecret ───────────────────────────────────────────────── -// TestParseClientSecret_WrongSecretType verifies that presenting a secret of the wrong type -// (Opaque when TLS is expected) returns an error. This exercises the type-check switch in -// ParseClientSecret without a real k8s cluster by calling the internal dispatch directly. -func TestParseClientSecret_WrongSecretType(t *testing.T) { +// TestParseClientSecret_OpaqueSecretType verifies that an Opaque secret containing tls.crt +// and tls.key is accepted for mTLS auth. This is the regression test for the fix that +// relaxed the type check in ParseClientSecret to accept both kubernetes.io/tls and Opaque. +func TestParseClientSecret_OpaqueSecretType(t *testing.T) { now := time.Now() caCert, caKey, _ := generateSelfSignedCACert(t, now.Add(-time.Hour), now.Add(time.Hour)) _, certPEM, keyPEM := generateLeafCert(t, caCert, caKey, "test.example.com", now.Add(-time.Hour), now.Add(time.Hour)) - wrongTypeSecret := corev1.Secret{ + opaqueSecret := corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: "tls-secret", Namespace: "test-ns"}, - Type: corev1.SecretTypeOpaque, // wrong type for TLS auth + Type: corev1.SecretTypeOpaque, Data: map[string][]byte{"tls.crt": certPEM, "tls.key": keyPEM}, } - // Replicate the type-check logic from ParseClientSecret directly to verify the error path. - // (ParseClientSecret fetches from k8s first; we test the subsequent type check in isolation.) - if wrongTypeSecret.Type != corev1.SecretTypeTLS { - require.NotEqual(t, corev1.SecretTypeTLS, wrongTypeSecret.Type, - "secret with wrong type should be rejected before any auth parsing") - } + cp := newTestPool() + _, key, auth, err := cp.fetchClientUsingMTLSSecret(opaqueSecret, makeOpts("localhost:7233")) + + require.NoError(t, err, "Opaque secret with tls.crt and tls.key should be accepted for mTLS auth") + assert.Equal(t, AuthModeTLS, key.AuthMode) + assert.Equal(t, AuthModeTLS, auth.mode) + assert.NotNil(t, auth.mTLS) } // ─── Tests: DialAndUpsertClient ───────────────────────────────────────────────