diff --git a/make/test-e2e.mk b/make/test-e2e.mk index e0203d79..d5144955 100644 --- a/make/test-e2e.mk +++ b/make/test-e2e.mk @@ -83,6 +83,7 @@ test-e2e-deps: INSTALL_OPTIONS += --set image.repository=$(oci_manager_image_nam test-e2e-deps: INSTALL_OPTIONS += --set app.runtimeIssuanceConfigMap=$(E2E_RUNTIME_CONFIG_MAP_NAME) test-e2e-deps: INSTALL_OPTIONS += --set app.logFormat=json test-e2e-deps: INSTALL_OPTIONS += --set app.controller.disableKubernetesClientRateLimiter=true +test-e2e-deps: INSTALL_OPTIONS += --set app.server.authenticators.enableClientCert=true test-e2e-deps: INSTALL_OPTIONS += -f ./make/config/istio-csr-values.yaml test-e2e-deps: e2e-setup-cert-manager test-e2e-deps: e2e-create-cert-manager-istio-resources diff --git a/test/e2e/suite/internal/client/client.go b/test/e2e/suite/internal/client/client.go index 75a15427..ad267654 100644 --- a/test/e2e/suite/internal/client/client.go +++ b/test/e2e/suite/internal/client/client.go @@ -41,19 +41,22 @@ var ( certmanagerClientLog = log.RegisterScope("certmanagerclient", "cert-manager client debugging") ) +var _ security.Client = &certmanagerClient{} + type certmanagerClient struct { caEndpoint string enableTLS bool caTLSRootCert []byte clusterID string token string + certificate tls.Certificate client securityapi.IstioCertificateServiceClient conn *grpc.ClientConn } // NewCertManagerClient create a CA client for cert-manager istio agent. -func NewCertManagerClient(endpoint, token string, tls bool, rootCert []byte, clusterID string) (security.Client, error) { +func NewCertManagerClient(endpoint, token string, tls bool, rootCert []byte, clusterID string) (*certmanagerClient, error) { c := &certmanagerClient{ caEndpoint: endpoint, token: token, @@ -72,6 +75,15 @@ func NewCertManagerClient(endpoint, token string, tls bool, rootCert []byte, clu return c, nil } +// UpdateCertificates updates the client certificate chain used to connect with istio-csr +func (c *certmanagerClient) UpdateCertificates(certificate tls.Certificate) { + c.certificate = certificate +} + +func (c *certmanagerClient) RemoveToken() { + c.token = "" +} + // CSR Sign calls cert-manager istio-csr to sign a CSR. func (c *certmanagerClient) CSRSign(csrPEM []byte, certValidTTLInSec int64) ([]string, error) { req := &securityapi.IstioCertificateRequest{ @@ -81,7 +93,10 @@ func (c *certmanagerClient) CSRSign(csrPEM []byte, certValidTTLInSec int64) ([]s if err := c.reconnect(); err != nil { return nil, err } - ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("Authorization", bearerTokenPrefix+c.token, "ClusterID", c.clusterID)) + ctx := context.Background() + if c.token != "" { + ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs("Authorization", bearerTokenPrefix+c.token, "ClusterID", c.clusterID)) + } resp, err := c.client.CreateCertificate(ctx, req) if err != nil { return nil, fmt.Errorf("create certificate: %v", err) @@ -114,12 +129,12 @@ func (c *certmanagerClient) getTLSDialOption() (grpc.DialOption, error) { } certmanagerClientLog.Infof("cert-manager client using custom root: %s %s", c.caEndpoint, string(c.caTLSRootCert)) } - var certificate tls.Certificate config := tls.Config{ MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{certificate}, + Certificates: []tls.Certificate{c.certificate}, GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { - return &certificate, nil + certmanagerClientLog.Info("cert-manager client received request for client certificate") + return &c.certificate, nil }, } config.RootCAs = certPool diff --git a/test/e2e/suite/request/request.go b/test/e2e/suite/request/request.go index 57bac1c0..d6d16f59 100644 --- a/test/e2e/suite/request/request.go +++ b/test/e2e/suite/request/request.go @@ -19,10 +19,12 @@ package api import ( "bytes" "context" + "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "reflect" + "strings" "time" cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" @@ -58,7 +60,7 @@ var _ = framework.CasesDescribe("Request Authentication", func() { var ok bool rootCA, ok = cm.Data["root-cert.pem"] if !ok { - Expect(cm, "epected CA root cert not present").NotTo(HaveOccurred()) + Expect(cm, "expected CA root cert not present").NotTo(HaveOccurred()) } ns, err := f.KubeClientSet.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ @@ -96,7 +98,7 @@ var _ = framework.CasesDescribe("Request Authentication", func() { Expect(err).NotTo(HaveOccurred()) }) - It("should reject a request with a bad service account token", func() { + It("should reject a request with no valid authentication secret", func() { csr, err := gen.CSR( gen.SetCSRIdentities([]string{fmt.Sprintf("spiffe://foo.bar/ns/%s/sa/%s", namespace, saName)}), ) @@ -214,7 +216,7 @@ var _ = framework.CasesDescribe("Request Authentication", func() { roots := x509.NewCertPool() ok := roots.AppendCertsFromPEM([]byte(rootCA)) if !ok { - Expect("failed to appent root certificate").NotTo(HaveOccurred()) + Expect("failed to append root certificate").NotTo(HaveOccurred()) } for i, certPEM := range certs { @@ -285,4 +287,30 @@ var _ = framework.CasesDescribe("Request Authentication", func() { )) } }) + + It("should return a new certificate on a request authenticated with an existing certificate", func() { + By("correctly request a valid certificate") + + id := fmt.Sprintf("spiffe://foo.bar/ns/%s/sa/%s", namespace, saName) + csr, err := gen.CSR( + gen.SetCSRIdentities([]string{id}), + ) + Expect(err).NotTo(HaveOccurred()) + + client, err := cmclient.NewCertManagerClient("localhost:30443", saToken, true, []byte(rootCA), "") + Expect(err).NotTo(HaveOccurred()) + certs, err := client.CSRSign(csr, 100) + Expect(err).NotTo(HaveOccurred()) + + By("correctly creating TLS client certificate from response") + combinedCerts := strings.Join(certs, "\n") + tlsCert, err := tls.X509KeyPair([]byte(combinedCerts), gen.Key()) + Expect(err).NotTo(HaveOccurred()) + + By("returning a new certificate without the token but over mTLS") + client.UpdateCertificates(tlsCert) + client.RemoveToken() + _, err = client.CSRSign(csr, 100) + Expect(err).NotTo(HaveOccurred()) + }) }) diff --git a/test/gen/csr.go b/test/gen/csr.go index fdaac977..f4f907c9 100644 --- a/test/gen/csr.go +++ b/test/gen/csr.go @@ -17,7 +17,6 @@ limitations under the License. package gen import ( - "crypto" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -31,7 +30,7 @@ import ( var ( // shared signer to reduce testing time - sk crypto.Signer + sk *rsa.PrivateKey ) func init() { @@ -129,3 +128,10 @@ func SetCSRCommonName(cn string) CSRModifier { csr.cn = cn } } + +func Key() []byte { + return pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(sk), + }) +}