Skip to content

Commit

Permalink
Merge pull request #370 from wtzhang23/e2e-client-auth
Browse files Browse the repository at this point in the history
Add end to end test for client certificate authenticator
  • Loading branch information
cert-manager-prow[bot] authored Aug 13, 2024
2 parents 53184ae + 17335bc commit 4782eb7
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 10 deletions.
1 change: 1 addition & 0 deletions make/test-e2e.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 20 additions & 5 deletions test/e2e/suite/internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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{
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
34 changes: 31 additions & 3 deletions test/e2e/suite/request/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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)}),
)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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())
})
})
10 changes: 8 additions & 2 deletions test/gen/csr.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package gen

import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
Expand All @@ -31,7 +30,7 @@ import (

var (
// shared signer to reduce testing time
sk crypto.Signer
sk *rsa.PrivateKey
)

func init() {
Expand Down Expand Up @@ -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),
})
}

0 comments on commit 4782eb7

Please sign in to comment.