Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logic to handle ready vs valid ACME orders properly #800

Merged
merged 4 commits into from
Aug 8, 2018

Conversation

munnerz
Copy link
Member

@munnerz munnerz commented Aug 8, 2018

What this PR does / why we need it:

Recent changes to Let's Encrypt have meant that they now use the 'ready' ACME status condition to indicate an order is ready to be finalized.

Order's that have already been finalized will have a state 'valid'.

If an order is ready, we must finalize it (i.e. submit a CSR to the ACME server)
If an order is valid, all we can do is retrieve the existing Certificate (as signed by the server in response to the CSR initially submitted when the order was 'ready').

This PR adapts our Issue function to handle these cases properly.

It is slightly more complex due to case (2) (where the order is already valid). If we have lost the certificate's private key, we need to detect this and trigger a new order to be created so we can re-finalize with the new private key.

Which issue this PR fixes: fixes #778

Special notes for your reviewer:

Release note:

Fix issue that could cause Certificates to fail re-issuance if triggered before certificate expiry

/cc @kragniz

@jetstack-bot jetstack-bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Aug 8, 2018
@@ -175,16 +167,6 @@ func (c *Controller) getGenericIssuer(crt *v1alpha1.Certificate) (v1alpha1.Gener
}
}

func needsRenew(cert *x509.Certificate) bool {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function was not used

err := fmt.Errorf("expected certificate status to be %q, but it is %q", acmeapi.StatusReady, order.Status)
// print a more helpful message to users when an order is marked 'valid'.
// this happens when all challenges have been completed successfully, but
// the acme server has not finished processing the order.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment was incorrect. 'ready' indicates an order is ready to be finalized, whereas 'valid' indicates the order has already been finalized and can be retrieved with GetCertificate.

crt.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorIssueError, "Creating new order due to lost TLS private key", false)
crt.Status.ACMEStatus().Order.URL = ""
return nil, nil, fmt.Errorf("marking certificate as failed to trigger a new order to be created due to lost private key")
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handles the case where the private key for a 'valid' order has been lost. If this happens, we trigger a new order to be created.

// The only way to obtain a certificate from an already 'valid' order is to
// call the orders GetCertificate function.
// You can see we do this below *iff* certSlice is nil.
if order.Status == acmeapi.StatusReady {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the comment explains, if the order is 'ready' then we need to finalize it (i.e. submit a CSR)

if acmeErr.StatusCode >= 400 && acmeErr.StatusCode <= 499 {
crt.Status.ACMEStatus().Order.URL = ""
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might be able to remove this check now. Leaving it in for the time being however, because if the server responds 4xx then we probably should create a new order because we don't have permission/we are sending back requests to it.


// if the Certificate was marked 'valid', we need to retrieve the certificate
// from the URL specified on the Order resource.
if certSlice == nil {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

certSlice will be nil if the order status is 'valid'

return nil, nil, fmt.Errorf("failed to parse returned x509 certificate: %v", err.Error())
}

if a.Context.IssuerOptions.CertificateNeedsRenew(x509Cert) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we may have retrieved an existing certificate associated with an order, we must ensure that we have the correct private key for it and that the certificate is not nearing expiry (else we'd enter a loop of constantly requesting the same, expiring certificate).

Copy link
Contributor

@kragniz kragniz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm
/approve

@jetstack-bot jetstack-bot added the lgtm Indicates that a PR is ready to be merged. label Aug 8, 2018
@jetstack-bot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: kragniz

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@jetstack-bot jetstack-bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 8, 2018
@damienwebdev
Copy link
Contributor

@munnerz in your PR initial statement I think there's a slight error:

What this PR does / why we need it:

Recent changes to Let's Encrypt have meant that they now use the **'valid' -> 'ready'** ACME status condition to indicate an order is ready to be finalized.

@munnerz
Copy link
Member Author

munnerz commented Aug 8, 2018

Yes, thank you @damienwebdev!

@jetstack-bot jetstack-bot merged commit 0042cc4 into cert-manager:master Aug 8, 2018
jetstack-bot added a commit that referenced this pull request Aug 10, 2018
@munnerz munnerz deleted the fix-verify-ready branch January 29, 2019 23:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Verify release-0.4 HEAD (i.e. unreleased 0.4.1) resolves issues with 'valid' and 'ready' order state
4 participants