From e58e8bcd48750743b2576a5d940ed4d0e4860001 Mon Sep 17 00:00:00 2001 From: candita Date: Fri, 1 Nov 2024 19:39:34 -0400 Subject: [PATCH] Fix yaml for httpRoute and backendTLSPolicy. Fix CA generation. Fix certificate unit test. --- conformance/base/manifests.yaml | 63 -------------- conformance/tests/backendtlspolicy.yaml | 84 ++++++++++++++++++- conformance/utils/kubernetes/certificate.go | 62 +++++++------- .../utils/kubernetes/certificate_test.go | 18 ++-- .../utils/roundtripper/roundtripper.go | 3 +- conformance/utils/suite/suite.go | 4 +- 6 files changed, 124 insertions(+), 110 deletions(-) diff --git a/conformance/base/manifests.yaml b/conformance/base/manifests.yaml index 081112144b..ac545dea2a 100644 --- a/conformance/base/manifests.yaml +++ b/conformance/base/manifests.yaml @@ -730,66 +730,3 @@ data: foo.bar.com:53 { whoami } ---- -apiVersion: v1 -kind: Service -metadata: - name: backendtlspolicy-test - namespace: gateway-conformance-infra -spec: - selector: - app: backendtlspolicy-test - ports: - - protocol: TCP - port: 443 - targetPort: 8443 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: backendtlspolicy-test - namespace: gateway-conformance-infra - labels: - app: backendtlspolicy-test -spec: - replicas: 1 - selector: - matchLabels: - app: backendtlspolicy-test - template: - metadata: - labels: - app: backendtlspolicy-test - spec: - containers: - - name: backendtlspolicy-test - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20241007-v1.2.0-6-g9f820af9 - volumeMounts: - - name: secret-volume - mountPath: /etc/secret-volume - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: CA_CERT - value: /etc/secret-volume/crt - - name: CA_CERT_KEY - value: /etc/secret-volume/key - resources: - requests: - cpu: 10m - volumes: - - name: secret-volume - secret: - # This secret is generated dynamically by the test suite. - secretName: backend-tls-checks-certificate - items: - - key: tls.crt - path: crt - - key: tls.key - path: key diff --git a/conformance/tests/backendtlspolicy.yaml b/conformance/tests/backendtlspolicy.yaml index 645ef4787e..e650b433ff 100644 --- a/conformance/tests/backendtlspolicy.yaml +++ b/conformance/tests/backendtlspolicy.yaml @@ -35,7 +35,7 @@ spec: validation: caCertificateRefs: - group: "" - kind: Secret + kind: ConfigMap # This secret is generated dynamically by the test suite. name: "backend-tls-checks-certificate" hostname: "abc.example.com" @@ -52,7 +52,87 @@ spec: hostnames: - abc.example.com rules: - - matches: + - backendRefs: + - group: "" + kind: Service + name: backendtlspolicy-test + port: 443 + matches: - path: type: Exact value: /backendTLS +--- +apiVersion: v1 +kind: Service +metadata: + name: backendtlspolicy-test + namespace: gateway-conformance-infra +spec: + selector: + app: backendtlspolicy-test + ports: + - protocol: TCP + port: 443 + targetPort: 8443 +--- +# Deployment must not be applied until after the secret is generated. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backendtlspolicy-test + namespace: gateway-conformance-infra + labels: + app: backendtlspolicy-test +spec: + replicas: 1 + selector: + matchLabels: + app: backendtlspolicy-test + template: + metadata: + labels: + app: backendtlspolicy-test + spec: + containers: + - name: backendtlspolicy-test + image: echo-basic:2.2 + volumeMounts: + - name: ca-volume + mountPath: /etc/ca-volume + - name: secret-volume + mountPath: /etc/secret-volume + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CA_CERT + value: /etc/ca-volume/crt + - name: TLS_SERVER_CERT + value: /etc/secret-volume/crt + - name: TLS_SERVER_PRIVKEY + value: /etc/secret-volume/key + resources: + requests: + cpu: 10m + volumes: + - name: ca-volume + configMap: + # This configMap is generated dynamically by the test suite. + name: backend-tls-checks-certificate + items: + - key: ca.crt + path: crt + - name: secret-volume + secret: + # This secret is generated dynamically by the test suite. + secretName: tls-checks-certificate + items: + - key: tls.crt + path: crt + - key: tls.key + path: key diff --git a/conformance/utils/kubernetes/certificate.go b/conformance/utils/kubernetes/certificate.go index 4324e454d8..5549e1b1ea 100644 --- a/conformance/utils/kubernetes/certificate.go +++ b/conformance/utils/kubernetes/certificate.go @@ -120,34 +120,41 @@ func generateRSACert(hosts []string, keyOut, certOut io.Writer) error { return nil } -// MustCreateCASignedCertSecret will create a secret using a CA Certificate, and public and private key for that certificate. -func MustCreateCASignedCertSecret(t *testing.T, namespace, secretName string, hosts []string) *corev1.Secret { +// MustCreateCASignedCertConfigMap will create a ConfigMap containing a CA Certificate, given a TLS Secret +// for that CA certificate. +func MustCreateCASignedCertConfigMap(t *testing.T, namespace, configMapName string, hosts []string) *corev1.ConfigMap { require.NotEmpty(t, hosts, "require a non-empty hosts for Subject Alternate Name values") - var serverKey, serverCert bytes.Buffer - - require.NoError(t, generateCACert(hosts, &serverKey, &serverCert), "failed to generate CA certificate") + caBytes, err := generateCACert(hosts) + if err != nil { + t.Errorf("failed to generate CA certificate: %v", err) + return nil + } - data := map[string][]byte{ - corev1.TLSCertKey: serverCert.Bytes(), - corev1.TLSPrivateKeyKey: serverKey.Bytes(), + var certData bytes.Buffer + if err := pem.Encode(&certData, &pem.Block{Type: "CERTIFICATE", Bytes: caBytes}); err != nil { + t.Errorf("failed creating cert: %v", err) + return nil } - newSecret := &corev1.Secret{ + // Store the certificate in a ConfigMap. + caConfigMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, - Name: secretName, + Name: configMapName, + }, + Data: map[string]string{ + "ca.crt": certData.String(), }, - Type: corev1.SecretTypeTLS, - Data: data, } - - return newSecret + return caConfigMap } -// generateCACert generates a CA Certificate signed certificate valid for a year. -func generateCACert(hosts []string, keyOut, certOut io.Writer) error { - // Create the CA certificate. +// generateCACert generates a ConfigMap containing a CA Certificate signed certificate valid for a year. +func generateCACert(hosts []string) ([]byte, error) { + var caBytes []byte + + // Create the CA certificate template. ca := &x509.Certificate{ SerialNumber: big.NewInt(2024), Subject: pkix.Name{ @@ -162,7 +169,7 @@ func generateCACert(hosts []string, keyOut, certOut io.Writer) error { NotAfter: time.Now().AddDate(1, 0, 0), IsCA: true, // Indicates this is a CA Certificate. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment, BasicConstraintsValid: true, } @@ -175,26 +182,19 @@ func generateCACert(hosts []string, keyOut, certOut io.Writer) error { } } - // Generate the private key. + // Generate the private key to sign certificates. caPrivKey, err := rsa.GenerateKey(rand.Reader, rsaBits) if err != nil { - return err + return caBytes, fmt.Errorf("error generating key for CA: %v", err) } - // Generate the certificate using the CA certificate. - caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey) + // Create the self-signed certificate using the CA certificate. + caBytes, err = x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey) if err != nil { - return err - } - - if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: caBytes}); err != nil { - return fmt.Errorf("failed creating cert: %w", err) + return caBytes, fmt.Errorf("error creating CA: %v", err) } - if err := pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey)}); err != nil { - return fmt.Errorf("failed creating key: %w", err) - } - return nil + return caBytes, nil } // validateHost ensures that the host name length is no more than 253 characters. diff --git a/conformance/utils/kubernetes/certificate_test.go b/conformance/utils/kubernetes/certificate_test.go index 6ab308f597..b0c2758918 100644 --- a/conformance/utils/kubernetes/certificate_test.go +++ b/conformance/utils/kubernetes/certificate_test.go @@ -67,11 +67,16 @@ func Test_generateCACert(t *testing.T) { serverKey.Reset() // Test the function generateCACert. We can only test normative function // and hostnames, everything else is hardcoded. - err := generateCACert(tc.hosts, &serverKey, &serverCert) + caBytes, err := generateCACert(tc.hosts) require.NoError(t, err, "unexpected error generating RSA certificate") + var certData bytes.Buffer + if err := pem.Encode(&certData, &pem.Block{Type: "CERTIFICATE", Bytes: caBytes}); err != nil { + require.NoError(t, err, "failed to create certificater") + } + // Test that the CA certificate is decodable, parseable, and has the configured hostname/s. - block, _ := pem.Decode(serverCert.Bytes()) + block, _ := pem.Decode(certData.Bytes()) if block == nil { require.FailNow(t, "failed to decode PEM block containing cert") } else if block.Type == "CERTIFICATE" { @@ -86,15 +91,6 @@ func Test_generateCACert(t *testing.T) { } } } - - // Test that the server key is decodable and parseable. - block, _ = pem.Decode(serverKey.Bytes()) - if block == nil { - require.FailNow(t, "failed to decode PEM block containing public key") - } else if block.Type == "RSA PRIVATE KEY" { - _, err := x509.ParsePKCS1PrivateKey(block.Bytes) - require.NoError(t, err, "failed to parse key") - } }) } } diff --git a/conformance/utils/roundtripper/roundtripper.go b/conformance/utils/roundtripper/roundtripper.go index 45553c618e..d5314bb7b7 100644 --- a/conformance/utils/roundtripper/roundtripper.go +++ b/conformance/utils/roundtripper/roundtripper.go @@ -229,8 +229,9 @@ func (d *DefaultRoundTripper) defaultRoundTrip(request Request, transport http.R return nil, nil, err } tlog.Logf(request.T, "Error sending request:\n%s\n\n", formatDump(dump, "< ")) + } else { + tlog.Logf(request.T, "Error sending request: %v (no response)\n", err) } - tlog.Log(request.T, "Error sending request: no response\n") } return nil, nil, err } diff --git a/conformance/utils/suite/suite.go b/conformance/utils/suite/suite.go index 502c861fba..e8b7c1fded 100644 --- a/conformance/utils/suite/suite.go +++ b/conformance/utils/suite/suite.go @@ -361,8 +361,8 @@ func (suite *ConformanceTestSuite) Setup(t *testing.T, tests []ConformanceTest) suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup) secret = kubernetes.MustCreateSelfSignedCertSecret(t, "gateway-conformance-app-backend", "tls-passthrough-checks-certificate", []string{"abc.example.com"}) suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{secret}, suite.Cleanup) - caSecret := kubernetes.MustCreateCASignedCertSecret(t, "gateway-conformance-infra", "backend-tls-checks-certificate", []string{"abc.example.com"}) - suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{caSecret}, suite.Cleanup) + caConfigMap := kubernetes.MustCreateCASignedCertConfigMap(t, "gateway-conformance-infra", "backend-tls-checks-certificate", []string{"abc.example.com"}) + suite.Applier.MustApplyObjectsWithCleanup(t, suite.Client, suite.TimeoutConfig, []client.Object{caConfigMap}, suite.Cleanup) tlog.Logf(t, "Test Setup: Ensuring Gateways and Pods from base manifests are ready") namespaces := []string{