Skip to content

fix(ingress): retry a failed dns-01 issuance only after a new account - #159

Merged
kvinwang merged 1 commit into
mainfrom
fix/ingress-issue-retry
Oct 16, 2026
Merged

kvinwang merged 1 commit into
mainfrom
fix/ingress-issue-retry

Conversation

@kvinwang

Copy link
Copy Markdown
Collaborator

Why

process_domain retried every failed issuance once, after writing the CAA. The retry exists for one case: a fresh ACME account refused by a CAA record naming an earlier one, which set_caa_record has just replaced. For any other failure it doubled the failed validations, so a domain that keeps failing hit Let's Encrypt's limit of 5 per hostname per hour within three passes.

What

  • Retry only when the first attempt had to create the account and this container manages the CAA (DELEGATION_ZONE or SET_CAA=true).
  • Drop the status 2 → 0 mapping after the retry: run_pass already detects a saved certificate by checksum.

Tests

Pebble end-to-end results will follow in a comment.

Stacked on #158.

@kvinwang
kvinwang force-pushed the fix/ingress-serve-first branch from f2c5e0f to 9e8cf69 Compare October 16, 2026 01:09
@kvinwang
kvinwang force-pushed the fix/ingress-issue-retry branch 2 times, most recently from 140173f to 829d9fa Compare October 16, 2026 01:20
@kvinwang
kvinwang force-pushed the fix/ingress-serve-first branch from 9e8cf69 to 8f49c1a Compare October 16, 2026 01:20
@kvinwang
kvinwang force-pushed the fix/ingress-issue-retry branch from 829d9fa to ec5a04b Compare October 16, 2026 01:20
@kvinwang
kvinwang force-pushed the fix/ingress-serve-first branch from 8f49c1a to c5e01e2 Compare October 16, 2026 01:20
@kvinwang
kvinwang force-pushed the fix/ingress-issue-retry branch from ec5a04b to 4f30b0a Compare October 16, 2026 01:23
@kvinwang
kvinwang force-pushed the fix/ingress-serve-first branch from c5e01e2 to bd162d4 Compare October 16, 2026 01:23
@kvinwang
kvinwang force-pushed the fix/ingress-issue-retry branch from 4f30b0a to 88b936b Compare October 16, 2026 01:25
@kvinwang
kvinwang force-pushed the fix/ingress-serve-first branch from bd162d4 to 7eb3401 Compare October 16, 2026 01:25
process_domain retried every failed issuance once, after writing the
CAA. The retry exists for one case: a fresh account refused by a CAA
record naming an earlier one, which set_caa_record has just replaced.
For any other failure it doubled the failed validations, so a domain
that keeps failing reached Let's Encrypt's limit of five per hostname
per hour within three passes.

Retry only when the first attempt had to create the account and this
container manages the CAA. Also drop the status 2 -> 0 mapping after
the retry: run_pass already detects a saved certificate by checksum.

Signed-off-by: Kevin Wang <wy721@qq.com>
@kvinwang
kvinwang force-pushed the fix/ingress-serve-first branch from 7eb3401 to f01040e Compare October 16, 2026 01:27
@kvinwang
kvinwang force-pushed the fix/ingress-issue-retry branch from 88b936b to d651b61 Compare October 16, 2026 01:27
@kvinwang
kvinwang changed the base branch from fix/ingress-serve-first to main October 16, 2026 01:29
@kvinwang kvinwang closed this Oct 16, 2026
@kvinwang kvinwang reopened this Oct 16, 2026
@kvinwang
kvinwang merged commit 9c40797 into main Oct 16, 2026
6 checks passed
@kvinwang
kvinwang deleted the fix/ingress-issue-retry branch October 16, 2026 01:31
kvinwang added a commit that referenced this pull request Oct 16, 2026
CAA handling had grown a branch per setup. Plain dns-01 issued, wrote the
CAA if SET_CAA, then retried under conditions (#159, #163); delegation
ignored SET_CAA and always wrote it; neither checked the CAA in force
before asking the CA, except delegation through dnsguide. The retry
existed only because the account was created as a side effect of the
first order, so the CAA could not name it in time.

Now every dns-01 pass runs the same steps:
1. register the ACME account on its own (new `certman.py register`);
2. publish the records, including the CAA pinned to that account when
   SET_CAA is on -- at the name, or in the delegation zone;
3. check that the CAA in force lets this account issue (new
   `dnsguide.py --caa-only`; exit 3 only on a definite refusal), so a
   refusal the CA would give does not cost a failed validation;
4. issue once.

SET_CAA means the same thing everywhere. It defaults to true under
DELEGATION_ZONE, where anyone else who can write the delegation zone
could otherwise pass the delegated challenge, and setting it false
there now works, with a warning. A failed CAA write fails the pass in
both setups rather than issuing without the pin that was asked for.

Since an account now always exists before renewal, certman replaces a
lineage when the account it records is gone, instead of when no
account exists at all.

Signed-off-by: Kevin Wang <wy721@qq.com>
kvinwang added a commit that referenced this pull request Oct 16, 2026
CAA handling had grown a branch per setup. Plain dns-01 issued, wrote the
CAA if SET_CAA, then retried under conditions (#159, #163); delegation
ignored SET_CAA and always wrote it; neither checked the CAA in force
before asking the CA, except delegation through dnsguide. The retry
existed only because the account was created as a side effect of the
first order, so the CAA could not name it in time.

Now every dns-01 pass runs the same steps:
1. register the ACME account on its own (new `certman.py register`);
2. publish the records, including the CAA pinned to that account when
   SET_CAA is on -- at the name, or in the delegation zone;
3. check that the CAA in force lets this account issue (new
   `dnsguide.py --caa-only`; exit 3 only on a definite refusal), so a
   refusal the CA would give does not cost a failed validation. With
   SET_CAA the record was just written and resolvers may still cache the
   one it replaced, so the check waits up to DNS_SETUP_TIMEOUT for it;
   otherwise it checks once. This is the only CAA gate in dns-01:
   dnsguide's record check no longer also checks CAA unless asked, as
   tls-alpn-01 still does;
4. issue once.

SET_CAA means the same thing everywhere. It defaults to true under
DELEGATION_ZONE, where anyone else who can write the delegation zone
could otherwise pass the delegated challenge, and setting it false
there now works, with a warning. A failed CAA write fails the pass in
both setups rather than issuing without the pin that was asked for.

Since an account now always exists before renewal, certman replaces a
lineage when the account it records is gone, instead of when no
account exists at all.

Signed-off-by: Kevin Wang <wy721@qq.com>
kvinwang added a commit that referenced this pull request Oct 16, 2026
CAA handling had grown a branch per setup. Plain dns-01 issued, wrote the
CAA if SET_CAA, then retried under conditions (#159, #163); delegation
ignored SET_CAA and always wrote it; neither checked the CAA in force
before asking the CA, except delegation through dnsguide. The retry
existed only because the account was created as a side effect of the
first order, so the CAA could not name it in time.

Now every dns-01 pass runs the same steps:
1. register the ACME account on its own (new `certman.py register`);
2. publish the records, including the CAA pinned to that account when
   SET_CAA is on -- at the name, or in the delegation zone;
3. check that the CAA in force lets this account issue (new
   `dnsguide.py --caa-only`; exit 3 only on a definite refusal), so a
   refusal the CA would give does not cost a failed validation. With
   SET_CAA the record was just written and resolvers may still cache the
   one it replaced, so the check waits up to DNS_SETUP_TIMEOUT for it;
   otherwise it checks once. This is the only CAA gate in dns-01:
   dnsguide's record check no longer also checks CAA unless asked, as
   tls-alpn-01 still does;
4. issue once.

SET_CAA means the same thing everywhere. It defaults to true under
DELEGATION_ZONE, where anyone else who can write the delegation zone
could otherwise pass the delegated challenge, and setting it false
there now works, with a warning. A failed CAA write fails the pass in
both setups rather than issuing without the pin that was asked for.

Since an account now always exists before renewal, certman replaces a
lineage when the account it records is gone, instead of when no
account exists at all.

Signed-off-by: Kevin Wang <wy721@qq.com>
kvinwang added a commit that referenced this pull request Oct 16, 2026
CAA handling had grown a branch per setup. Plain dns-01 issued, wrote the
CAA if SET_CAA, then retried under conditions (#159, #163); delegation
ignored SET_CAA and always wrote it; neither checked the CAA in force
before asking the CA, except delegation through dnsguide. The retry
existed only because the account was created as a side effect of the
first order, so the CAA could not name it in time.

Now every dns-01 pass runs the same steps:
1. register the ACME account on its own (new `certman.py register`);
2. publish the records, including the CAA pinned to that account when
   SET_CAA is on -- at the name, or in the delegation zone;
3. check that the CAA in force lets this account issue (new
   `dnsguide.py --caa-only`; exit 3 only on a definite refusal), so a
   refusal the CA would give does not cost a failed validation. With
   SET_CAA the record was just written and resolvers may still cache the
   one it replaced, so the check waits up to DNS_SETUP_TIMEOUT for it;
   otherwise it checks once. This is the only CAA gate in dns-01:
   dnsguide's record check no longer also checks CAA unless asked, as
   tls-alpn-01 still does;
4. issue once.

SET_CAA means the same thing everywhere. It defaults to true under
DELEGATION_ZONE, where anyone else who can write the delegation zone
could otherwise pass the delegated challenge, and setting it false
there now works, with a warning. A failed CAA write fails the pass in
both setups rather than issuing without the pin that was asked for.

Since an account now always exists before renewal, certman replaces a
lineage when the account it records is gone, instead of when no
account exists at all.

Signed-off-by: Kevin Wang <wy721@qq.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant