diff --git a/.spelling b/.spelling index 36a4c3a32d0..0332a7263d4 100644 --- a/.spelling +++ b/.spelling @@ -426,6 +426,7 @@ goroutine gosec hardcodes hardcoded +hairpinning healthz honour hostname diff --git a/content/docs/configuration/acme/README.md b/content/docs/configuration/acme/README.md index 386887a6ac8..89e81d305f6 100644 --- a/content/docs/configuration/acme/README.md +++ b/content/docs/configuration/acme/README.md @@ -89,6 +89,69 @@ Solvers come in the form of [`dns01`](./dns01/README.md) and these solver types, visit their respective documentation - [DNS01](./dns01/README.md), [HTTP01](./http01/README.md). +### Skip the self-check with `waitInsteadOfSelfCheck` + +> ℹ️ This feature is available in cert-manager `>= v1.21.0`. + +The `waitInsteadOfSelfCheck` solver option skips cert-manager's own +[self-check](../../concepts/acme-orders-challenges.md#challenge-lifecycle) +and instead waits a configured duration after presentation before asking the +ACME server to validate the challenge. + +You can set this field on either an `http01` or `dns01` solver entry. + +This is aimed at environments such as: + +- NAT loopback limitations, where cert-manager cannot reach the public address that the + ACME server can reach; +- split-horizon DNS, where cert-manager resolves a different address than the + ACME server does; or +- public/private ingress topologies where cert-manager only sees the internal + path but the ACME server validates against the external path. + +The behavior when `waitInsteadOfSelfCheck` is set is: + +- cert-manager presents the challenge as usual; +- cert-manager records the time of first presentation in `status.presentedAt`; +- cert-manager skips its own self-check; and +- once the configured duration has elapsed since `status.presentedAt`, + cert-manager asks the ACME server to validate the challenge. + +For example: + +```yaml +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-staging +spec: + acme: + email: user@example.com + server: https://acme-staging-v02.api.letsencrypt.org/directory + privateKeySecretRef: + name: example-issuer-account-key + solvers: + - http01: + ingress: + ingressClassName: nginx-public + # or use dns01: instead of http01: for split-horizon DNS scenarios + waitInsteadOfSelfCheck: 30s +``` + +This is an advanced escape hatch for cases where cert-manager cannot directly +observe the same validation path as the ACME server. + +Choose the delay conservatively. If it is too short, the ACME server may still +start validation before your solver resources are reachable. If it is too long, +certificate issuance will wait longer than necessary. + +A value of `0` is permitted: cert-manager skips the self-check and asks the ACME +server to validate immediately after presentation. In this case cert-manager +relies on the ACME server's own validation retries +([RFC 8555 section 8.2](https://www.rfc-editor.org/rfc/rfc8555#section-8.2)) to +succeed once the challenge has propagated. Negative durations are rejected by the +validating webhook. + ### ACME Certificate Profiles > ℹ️ This feature is available in cert-manager `>= v1.18.0`. diff --git a/content/docs/releases/release-notes/release-notes-1.21.md b/content/docs/releases/release-notes/release-notes-1.21.md index cc0e2bf7d93..832f57c25f9 100644 --- a/content/docs/releases/release-notes/release-notes-1.21.md +++ b/content/docs/releases/release-notes/release-notes-1.21.md @@ -40,6 +40,19 @@ privately reported security advisories) that this default RBAC widened the trust boundary beyond what cert-manager's published [threat model](../../devops-tips/threat-modelling.md) documents. +### Skip the self-check with `waitInsteadOfSelfCheck` + +cert-manager 1.21 adds the `waitInsteadOfSelfCheck` solver option for ACME +HTTP01 and DNS01 challenges. When set, cert-manager skips its own self-check +and instead waits the configured duration after presentation before asking the +ACME server to validate. This is an escape hatch for environments where +cert-manager cannot reliably observe the same validation path as the ACME +server, such as split-horizon DNS or NAT loopback (or hairpinning). + +See [Skip the self-check with +`waitInsteadOfSelfCheck`](../../configuration/acme/README.md#skip-the-self-check-with-waitinsteadofselfcheck) +for configuration details. + ## Community As always, we'd like to thank all of the community members who helped in this release cycle, including all below who merged a PR and anyone that helped by commenting on issues, testing, or getting involved in cert-manager meetings. We're lucky to have you involved. @@ -70,7 +83,7 @@ And finally, thanks to the cert-manager steering committee for their feedback in ### Feature -TODO +- Add the `waitInsteadOfSelfCheck` solver option for ACME HTTP01 and DNS01 challenges, allowing cert-manager to skip its own self-check and ask the ACME server to validate after a configured wait. See the [ACME issuer documentation](../../configuration/acme/README.md#skip-the-self-check-with-waitinsteadofselfcheck) for configuration details. ### Documentation diff --git a/content/docs/troubleshooting/acme.md b/content/docs/troubleshooting/acme.md index c4668d3bc5d..b82c1300ce2 100644 --- a/content/docs/troubleshooting/acme.md +++ b/content/docs/troubleshooting/acme.md @@ -199,6 +199,18 @@ $ kubectl get challenge -ojsonpath='{.spec.authorizationURL}' ### HTTP01 troubleshooting First of all check if you can see the challenge URL from the public internet, if this does not work check your Ingress and firewall configuration as well as the service and pod cert-manager created to solve the ACME challenge. + +If the ACME server can reach the challenge URL from the public internet but +cert-manager's [self-check](../concepts/acme-orders-challenges.md#challenge-lifecycle) cannot, you may be in one of the environments +mentioned on the [ACME configuration](../configuration/acme/README.md) page: +NAT loopback limitations, split-horizon DNS, or a public/private ingress split. +The [`waitInsteadOfSelfCheck`](../configuration/acme/README.md#skip-the-self-check-with-waitinsteadofselfcheck) +solver option skips the self-check entirely and instead waits a configured +duration after presentation before asking the ACME server to validate. + +A similar issue can also affect DNS01 if cert-manager can only query internal +DNS views while the ACME server validates against the public TXT record. + If this does work check if your cluster can see it too. It is important to test this from inside a Pod. If you get a connection error it is suggested to check the cluster's network configuration. If you receive a `tls: handshake failure`, try setting the annotation `cert-manager.io/issue-temporary-certificate: "true"` on the Ingress or Certificate resource. This will issue a temporary self signed certificate for the ingress controller to use before the actual certificate is issued. If you still are having issues, there may be an issue with your ingress controller handling multiple resources for the same hostname, in this case, the annotation `acme.cert-manager.io/http01-edit-in-place: "true"` is likely required.