Skip to content

Commit

Permalink
Add test to check InvalidRequest handling for certificates
Browse files Browse the repository at this point in the history
Signed-off-by: James Munnelly <jmunnelly@apple.com>
  • Loading branch information
munnerz committed Aug 4, 2022
1 parent 51014e5 commit e62bfaf
Showing 1 changed file with 130 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,136 @@ import (
"github.com/cert-manager/cert-manager/test/integration/framework"
)

func TestGeneratesNewPrivateKeyIfMarkedInvalidRequest(t *testing.T) {
namespace := "default"
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()

config, stopFn := framework.RunControlPlane(t, ctx)
defer stopFn()

// Build, instantiate and run all required controllers
stopControllers := runAllControllers(t, ctx, config)
defer stopControllers()

_, _, cmCl, _ := framework.NewClients(t, config)
crt, err := cmCl.CertmanagerV1().Certificates(namespace).Create(ctx, &cmapi.Certificate{
ObjectMeta: metav1.ObjectMeta{Name: "testcrt"},
Spec: cmapi.CertificateSpec{
SecretName: "testsecret",
DNSNames: []string{"something"},
IssuerRef: cmmeta.ObjectReference{
Name: "issuer",
},
PrivateKey: &cmapi.CertificatePrivateKey{
// This doesn't actually make any difference in this test case because there is no existing private
// key, meaning there's no private key to re-use.
RotationPolicy: cmapi.RotationPolicyAlways,
},
},
}, metav1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create certificate: %v", err)
}

var req *cmapi.CertificateRequest
if err := wait.Poll(time.Millisecond*500, time.Second*10, func() (done bool, err error) {
reqs, err := cmCl.CertmanagerV1().CertificateRequests(namespace).List(ctx, metav1.ListOptions{})
if err != nil {
return false, err
}

if len(reqs.Items) > 1 {
return false, fmt.Errorf("invalid state, expected only one CR but got %d", len(reqs.Items))
}

if len(reqs.Items) == 0 {
return false, nil
}

req = &reqs.Items[0]
return true, nil
}); err != nil {
t.Fatal(err)
}

t.Logf("Found CertificateRequest")
// Remember the CSR data used for the first request so we can compare it later
originalCSR := req.Spec.Request

// Mark the CSR as 'Failed'
apiutil.SetCertificateRequestCondition(req, cmapi.CertificateRequestConditionInvalidRequest, cmmeta.ConditionTrue, cmapi.CertificateRequestReasonFailed, "manually failed")
_, err = cmCl.CertmanagerV1().CertificateRequests(req.Namespace).UpdateStatus(ctx, req, metav1.UpdateOptions{})
if err != nil {
t.Fatalf("failed to mark CertificateRequest as Failed: %v", err)
}
t.Log("Marked CertificateRequest as InvalidRequest")

// Wait for Certificate to be marked as Failed
if err := wait.Poll(time.Millisecond*500, time.Second*50, func() (done bool, err error) {
crt, err := cmCl.CertmanagerV1().Certificates(crt.Namespace).Get(ctx, crt.Name, metav1.GetOptions{})
if err != nil {
return false, err
}

return apiutil.GetCertificateCondition(crt, cmapi.CertificateConditionReady).Status == cmmeta.ConditionFalse &&
apiutil.GetCertificateCondition(crt, cmapi.CertificateConditionIssuing).Status == cmmeta.ConditionFalse, nil
}); err != nil {
t.Fatal(err)
}
t.Logf("Issuance acknowledged as failed as expected")
t.Logf("Triggering new issuance")

crt, err = cmCl.CertmanagerV1().Certificates(crt.Namespace).Get(ctx, crt.Name, metav1.GetOptions{})
if err != nil {
t.Fatalf("failed to get certificate: %v", err)
}

apiutil.SetCertificateCondition(crt, crt.Generation, cmapi.CertificateConditionIssuing, cmmeta.ConditionTrue, "ManualTrigger", "triggered by test case manually")
crt, err = cmCl.CertmanagerV1().Certificates(crt.Namespace).UpdateStatus(ctx, crt, metav1.UpdateOptions{})
if err != nil {
t.Fatalf("failed to update certificate: %v", err)
}

if err := wait.Poll(time.Millisecond*500, time.Second*10, func() (done bool, err error) {
reqs, err := cmCl.CertmanagerV1().CertificateRequests(namespace).List(ctx, metav1.ListOptions{})
if err != nil {
return false, err
}

if len(reqs.Items) > 1 {
return false, fmt.Errorf("invalid state, expected only one CR but got %d", len(reqs.Items))
}

if len(reqs.Items) == 0 {
return false, nil
}

req = &reqs.Items[0]
return true, nil
}); err != nil {
t.Fatal(err)
}
t.Logf("Second request created successfully")
t.Logf("Comparing public keys of first and second request...")

csr1, err := pki.DecodeX509CertificateRequestBytes(originalCSR)
if err != nil {
t.Fatalf("failed to parse first CSR: %v", err)
}
csr2, err := pki.DecodeX509CertificateRequestBytes(req.Spec.Request)
if err != nil {
t.Fatalf("failed to parse first CSR: %v", err)
}

pk1 := csr1.PublicKey.(crypto.PublicKey)
pk2 := csr2.PublicKey.(crypto.PublicKey)

if pk1.(comparablePublicKey).Equal(pk2) {
t.Errorf("expected the two requests to have been signed by distinct private keys, but the private key has been reused")
}
}

// Runs all Certificate controllers to exercise the full flow of attempting issuance.
// Checks to make sure that when an issuance fails and is re-attempted, a new private key is used
// to sign the second request.
Expand Down

0 comments on commit e62bfaf

Please sign in to comment.