diff --git a/devel/addon/bind/manifests/configmap.yaml b/devel/addon/bind/manifests/configmap.yaml index bef1279f9..fef833041 100644 --- a/devel/addon/bind/manifests/configmap.yaml +++ b/devel/addon/bind/manifests/configmap.yaml @@ -37,8 +37,8 @@ data: @ IN NS localhost. *.ingress-nginx IN A {SERVICE_IP_PREFIX}.15 ingress-nginx IN A {SERVICE_IP_PREFIX}.15 - *.haproxy IN A {SERVICE_IP_PREFIX}.14 - haproxy IN A {SERVICE_IP_PREFIX}.14 + *.gateway IN A {SERVICE_IP_PREFIX}.14 + gateway IN A {SERVICE_IP_PREFIX}.14 db.dns01.example.com: | ; diff --git a/devel/addon/haproxy/install.sh b/devel/addon/haproxy/install.sh index 28909333c..ba90717df 100755 --- a/devel/addon/haproxy/install.sh +++ b/devel/addon/haproxy/install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2020 The cert-manager Authors. +# Copyright 2021 The cert-manager Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Install HAProxy as a gateway-API e2e test +# Install HAProxy as a gateway-API e2e test. set -o nounset set -o errexit @@ -71,21 +71,3 @@ spec: namespaces: from: All EOYAML - -# Example of a cross namespace HTTPRoute -cat < to + // the IP of the Gateway's Service. + Domain string + + // Labels is a comma separated list of key=value labels set on the + // HTTPRoutes created by the Gateway API solver + Labels string +} + +func (g *Gateway) AddFlags(fs *flag.FlagSet) { + fs.StringVar( + &g.Domain, + "gateway-domain", + "gateway.http01.example.com", + "The domain name used during e2e tests to solve HTTP-01 "+ + "challenges. This must resolve to the IP of the Gateway's service.", + ) + fs.StringVar( + &g.Labels, + "gateway-httproute-labels", + "acme=solver", + "Labels is a comma separated list of key=value labels set on the "+ + "HTTPRoutes created by the Gateway API solver", + ) +} + +func (g *Gateway) Validate() []error { + return nil +} diff --git a/test/e2e/suite/conformance/certificates/acme/acme.go b/test/e2e/suite/conformance/certificates/acme/acme.go index ed9a6c328..388b27af4 100644 --- a/test/e2e/suite/conformance/certificates/acme/acme.go +++ b/test/e2e/suite/conformance/certificates/acme/acme.go @@ -58,6 +58,19 @@ func runACMEIssuerTests(eab *cmacme.ACMEExternalAccountBinding) { featureset.IssueCAFeature, ) + var unsupportedHTTP01GatewayFeatures = featureset.NewFeatureSet( + featureset.DurationFeature, + featureset.WildcardsFeature, + featureset.URISANsFeature, + featureset.CommonNameFeature, + featureset.KeyUsagesFeature, + featureset.EmailSANsFeature, + featureset.SaveCAToSecret, + featureset.IssueCAFeature, + // Gateway API does not allow raw IP addresses + featureset.IPAddressFeature, + ) + // unsupportedDNS01Features is a list of features that are not supported by the ACME // issuer type using DNS01 var unsupportedDNS01Features = featureset.NewFeatureSet( @@ -92,13 +105,21 @@ func runACMEIssuerTests(eab *cmacme.ACMEExternalAccountBinding) { } (&certificates.Suite{ - Name: "ACME HTTP01 Issuer", - UseIngressIPAddress: true, - CreateIssuerFunc: provisionerHTTP01.createHTTP01Issuer, + Name: "ACME HTTP01 Issuer (Ingress)", + HTTP01TestType: "Ingress", + CreateIssuerFunc: provisionerHTTP01.createHTTP01IngressIssuer, DeleteIssuerFunc: provisionerHTTP01.delete, UnsupportedFeatures: unsupportedHTTP01Features, }).Define() + (&certificates.Suite{ + Name: "ACME HTTP01 Issuer (Gateway)", + HTTP01TestType: "Gateway", + CreateIssuerFunc: provisionerHTTP01.createHTTP01GatewayIssuer, + DeleteIssuerFunc: provisionerHTTP01.delete, + UnsupportedFeatures: unsupportedHTTP01GatewayFeatures, + }).Define() + (&certificates.Suite{ Name: "ACME DNS01 Issuer", DomainSuffix: "dns01.example.com", @@ -108,13 +129,21 @@ func runACMEIssuerTests(eab *cmacme.ACMEExternalAccountBinding) { }).Define() (&certificates.Suite{ - Name: "ACME HTTP01 ClusterIssuer", - UseIngressIPAddress: true, - CreateIssuerFunc: provisionerHTTP01.createHTTP01ClusterIssuer, + Name: "ACME HTTP01 ClusterIssuer (Ingress)", + HTTP01TestType: "Ingress", + CreateIssuerFunc: provisionerHTTP01.createHTTP01IngressClusterIssuer, DeleteIssuerFunc: provisionerHTTP01.delete, UnsupportedFeatures: unsupportedHTTP01Features, }).Define() + (&certificates.Suite{ + Name: "ACME HTTP01 ClusterIssuer (Gateway)", + HTTP01TestType: "Gateway", + CreateIssuerFunc: provisionerHTTP01.createHTTP01GatewayClusterIssuer, + DeleteIssuerFunc: provisionerHTTP01.delete, + UnsupportedFeatures: unsupportedHTTP01GatewayFeatures, + }).Define() + (&certificates.Suite{ Name: "ACME DNS01 ClusterIssuer", DomainSuffix: "dns01.example.com", @@ -125,7 +154,7 @@ func runACMEIssuerTests(eab *cmacme.ACMEExternalAccountBinding) { (&certificates.Suite{ Name: "Public ACME Server HTTP01 Issuer", - UseIngressIPAddress: true, + HTTP01TestType: "Ingress", CreateIssuerFunc: provisionerPACMEHTTP01.createPublicACMEServerStagingHTTP01Issuer, DeleteIssuerFunc: provisionerPACMEHTTP01.delete, UnsupportedFeatures: unsupportedHTTP01Features.Copy().Add(unsupportedPublicACMEServerFeatures.List()...), @@ -155,15 +184,67 @@ func (a *acmeIssuerProvisioner) delete(f *framework.Framework, ref cmmeta.Object // - pebble // - a properly configured Issuer resource -func (a *acmeIssuerProvisioner) createHTTP01Issuer(f *framework.Framework) cmmeta.ObjectReference { +func (a *acmeIssuerProvisioner) createHTTP01IngressIssuer(f *framework.Framework) cmmeta.ObjectReference { a.ensureEABSecret(f, "") - By("Creating an ACME HTTP01 Issuer") + By("Creating an ACME HTTP01 Ingress Issuer") issuer := &cmapi.Issuer{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "acme-issuer-http01-", }, - Spec: a.createHTTP01IssuerSpec(f.Config.Addons.ACMEServer.URL), + Spec: a.createHTTP01IngressIssuerSpec(f.Config.Addons.ACMEServer.URL), + } + + issuer, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), issuer, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred(), "failed to create acme HTTP01 issuer") + + return cmmeta.ObjectReference{ + Group: cmapi.SchemeGroupVersion.Group, + Kind: cmapi.IssuerKind, + Name: issuer.Name, + } +} + +func (a *acmeIssuerProvisioner) createHTTP01IngressClusterIssuer(f *framework.Framework) cmmeta.ObjectReference { + a.ensureEABSecret(f, f.Config.Addons.CertManager.ClusterResourceNamespace) + + By("Creating an ACME HTTP01 Ingress ClusterIssuer") + issuer := &cmapi.ClusterIssuer{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "acme-cluster-issuer-http01-", + }, + Spec: a.createHTTP01IngressIssuerSpec(f.Config.Addons.ACMEServer.URL), + } + + issuer, err := f.CertManagerClientSet.CertmanagerV1().ClusterIssuers().Create(context.TODO(), issuer, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred(), "failed to create acme HTTP01 cluster issuer") + + return cmmeta.ObjectReference{ + Group: cmapi.SchemeGroupVersion.Group, + Kind: cmapi.ClusterIssuerKind, + Name: issuer.Name, + } +} + +func (a *acmeIssuerProvisioner) createHTTP01GatewayIssuer(f *framework.Framework) cmmeta.ObjectReference { + a.ensureEABSecret(f, "") + + labelFlag := strings.Split(f.Config.Addons.Gateway.Labels, ",") + labels := make(map[string]string) + for _, l := range labelFlag { + kv := strings.Split(l, "=") + if len(kv) != 2 { + continue + } + labels[kv[0]] = kv[1] + } + + By("Creating an ACME HTTP01 Gateway Issuer") + issuer := &cmapi.Issuer{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "acme-issuer-http01-", + }, + Spec: a.createHTTP01GatewayIssuerSpec(f.Config.Addons.ACMEServer.URL, labels), } issuer, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), issuer, metav1.CreateOptions{}) @@ -190,7 +271,7 @@ func (a *acmeIssuerProvisioner) createPublicACMEServerStagingHTTP01Issuer(f *fra ObjectMeta: metav1.ObjectMeta{ GenerateName: "pacme-issuer-http01-", }, - Spec: a.createHTTP01IssuerSpec(PublicACMEServerStagingURL), + Spec: a.createHTTP01IngressIssuerSpec(PublicACMEServerStagingURL), } issuer, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), issuer, metav1.CreateOptions{}) @@ -203,15 +284,25 @@ func (a *acmeIssuerProvisioner) createPublicACMEServerStagingHTTP01Issuer(f *fra } } -func (a *acmeIssuerProvisioner) createHTTP01ClusterIssuer(f *framework.Framework) cmmeta.ObjectReference { +func (a *acmeIssuerProvisioner) createHTTP01GatewayClusterIssuer(f *framework.Framework) cmmeta.ObjectReference { a.ensureEABSecret(f, f.Config.Addons.CertManager.ClusterResourceNamespace) - By("Creating an ACME HTTP01 ClusterIssuer") + labelFlag := strings.Split(f.Config.Addons.Gateway.Labels, ",") + labels := make(map[string]string) + for _, l := range labelFlag { + kv := strings.Split(l, "=") + if len(kv) != 2 { + continue + } + labels[kv[0]] = kv[1] + } + + By("Creating an ACME HTTP01 Gateway ClusterIssuer") issuer := &cmapi.ClusterIssuer{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "acme-cluster-issuer-http01-", }, - Spec: a.createHTTP01IssuerSpec(f.Config.Addons.ACMEServer.URL), + Spec: a.createHTTP01GatewayIssuerSpec(f.Config.Addons.ACMEServer.URL, labels), } issuer, err := f.CertManagerClientSet.CertmanagerV1().ClusterIssuers().Create(context.TODO(), issuer, metav1.CreateOptions{}) @@ -224,7 +315,7 @@ func (a *acmeIssuerProvisioner) createHTTP01ClusterIssuer(f *framework.Framework } } -func (a *acmeIssuerProvisioner) createHTTP01IssuerSpec(serverURL string) cmapi.IssuerSpec { +func (a *acmeIssuerProvisioner) createHTTP01IngressIssuerSpec(serverURL string) cmapi.IssuerSpec { return cmapi.IssuerSpec{ IssuerConfig: cmapi.IssuerConfig{ ACME: &cmacme.ACMEIssuer{ @@ -251,6 +342,32 @@ func (a *acmeIssuerProvisioner) createHTTP01IssuerSpec(serverURL string) cmapi.I } } +func (a *acmeIssuerProvisioner) createHTTP01GatewayIssuerSpec(serverURL string, labels map[string]string) cmapi.IssuerSpec { + return cmapi.IssuerSpec{ + IssuerConfig: cmapi.IssuerConfig{ + ACME: &cmacme.ACMEIssuer{ + Server: serverURL, + SkipTLSVerify: true, + PrivateKey: cmmeta.SecretKeySelector{ + LocalObjectReference: cmmeta.LocalObjectReference{ + Name: "acme-private-key-http01", + }, + }, + ExternalAccountBinding: a.eab, + Solvers: []cmacme.ACMEChallengeSolver{ + { + HTTP01: &cmacme.ACMEChallengeSolverHTTP01{ + Gateway: &cmacme.ACMEChallengeSolverHTTP01Gateway{ + Labels: labels, + }, + }, + }, + }, + }, + }, + } +} + func (a *acmeIssuerProvisioner) createDNS01Issuer(f *framework.Framework) cmmeta.ObjectReference { a.ensureEABSecret(f, f.Namespace.Name) diff --git a/test/e2e/suite/conformance/certificates/suite.go b/test/e2e/suite/conformance/certificates/suite.go index c4e51a745..653309bda 100644 --- a/test/e2e/suite/conformance/certificates/suite.go +++ b/test/e2e/suite/conformance/certificates/suite.go @@ -54,11 +54,9 @@ type Suite struct { // nginx-ingress addon. DomainSuffix string - // UseIngressIPAddress indicates that the IPAddress used - // for generating certificates should be the IngressIP. - // The ACME tests need this, so the challenges against the - // IPAddress will complete successfully. - UseIngressIPAddress bool + // HTTP01TestType is set to "Ingress" or "Gateway" to determine which IPs + // and Domains will be used to run the ACME HTTP-01 test suites. + HTTP01TestType string // UnsupportedFeatures is a list of features that are not supported by this // invocation of the test suite. @@ -81,7 +79,12 @@ func (s *Suite) complete(f *framework.Framework) { } if s.DomainSuffix == "" { - s.DomainSuffix = f.Config.Addons.IngressController.Domain + switch s.HTTP01TestType { + case "Ingress": + s.DomainSuffix = f.Config.Addons.IngressController.Domain + case "Gateway": + s.DomainSuffix = f.Config.Addons.Gateway.Domain + } } if s.UnsupportedFeatures == nil { diff --git a/test/e2e/suite/conformance/certificates/tests.go b/test/e2e/suite/conformance/certificates/tests.go index 1d16851dc..c77f8bf31 100644 --- a/test/e2e/suite/conformance/certificates/tests.go +++ b/test/e2e/suite/conformance/certificates/tests.go @@ -66,8 +66,11 @@ func (s *Suite) Define() { } s.complete(f) - if s.UseIngressIPAddress { + switch s.HTTP01TestType { + case "Ingress": sharedIPAddress = f.Config.Addons.ACMEServer.IngressIP + case "Gateway": + sharedIPAddress = f.Config.Addons.ACMEServer.GatewayIP } }) @@ -649,6 +652,10 @@ func (s *Suite) Define() { }, featureset.ReusePrivateKeyFeature, featureset.OnlySAN) s.it(f, "should issue a certificate for a single distinct DNS Name defined by an ingress with annotations", func(issuerRef cmmeta.ObjectReference) { + if s.HTTP01TestType != "Ingress" { + Skip("Skipping ingress-specific as non ingress HTTP-01 solver is in use") + return + } var certName string switch { case e2eutil.HasIngresses(f.KubeClientSet.Discovery(), networkingv1.SchemeGroupVersion.String()): @@ -697,6 +704,10 @@ func (s *Suite) Define() { }, featureset.OnlySAN) s.it(f, "should issue a certificate defined by an ingress with certificate field annotations", func(issuerRef cmmeta.ObjectReference) { + if s.HTTP01TestType != "Ingress" { + Skip("Skipping ingress-specific as non ingress HTTP-01 solver is in use") + return + } var certName string domain := e2eutil.RandomSubdomain(s.DomainSuffix) duration := time.Hour * 999