fix(certbot): tighten the ACME challenge-domain rule and the paths that report failures - #1133
Merged
Conversation
kvinwang
force-pushed
the
fix/certbot-acme-followups
branch
from
August 25, 2026 10:50
ff85f14 to
50f3e6a
Compare
This was referenced Aug 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Four small things left over from the
instant-acme0.8 port in #1129. None of them breakissuance today; each one is a place where the next change is more likely to break it, or
where the log will not say why it broke.
1. The wildcard rule lives inline and is untested.
authorize()picks the bareidentifierfield out of theAuthorizedIdentifierbecause that type'sDisplayrendersa wildcard authorization as
*.example.com, and formatting that into_acme-challenge.{}publishes the TXT record at_acme-challenge.*.example.com— wrongfor every wildcard certificate, which is every gateway certificate. #1129 got this right
and said so in a comment, but a comment is what a future refactor deletes. The rule was
also the one piece of the port that is dstack's own logic rather than a mechanical API
rename, and it was the only piece with no test.
2.
set_challenges_readyno longer says which challenge. 0.7 loggedsetting challenge ready for {url}; the port replaced it with a baresetting challenge ready. An order forexample.comand*.example.comnow emits thesame contextless line twice, which is exactly the log you reach for when one authorization
of several fails.
3.
authorize()logs the same domain twice in a row.creating dns record for {name}followed immediately by
removing existing TXT record for {acme_domain}— after the portboth render the same string.
4.
find_error()swallows the reason it gave up. It matchesSome(Ok(authz)), so afailed authorization fetch ends the loop indistinguishably from a clean end of stream. The
caller then reports
order is invalid:built from a default emptyProblem, and theactual transport error is gone. This is the path that runs when issuance has already
failed, so it is precisely when the missing detail costs the most.
Fix
Five commits, one concern each:
refactor(certbot): move the bare-vs-wildcard decision intochallenge_domain(), whichreturns the full record name. The non-DNS identifier case now names the offending
identifier in its error. Drops the duplicate debug line from (3).
test(certbot): pin the rule —wildcard: falseandwildcard: trueon the sameidentifier must both yield
_acme-challenge.example.com, and a non-DNS identifier musterror.
fix(certbot): restore the challenge URL in the readiness log.ChallengeHandlederefsto
Challenge, sochallenge.urlis still available in 0.8 — the thing 0.8 actuallyremoved is
Order::set_challenge_ready(url), not access to the URL.fix(certbot): propagate authorization fetch failures through the returned error chain instead of only logging them.No behavior change to issuance itself: same records, same order of operations, same
requests.
Verification
The new test fails on the bug it pins. Reverting
challenge_domain()to format theAuthorizedIdentifierdirectly — i.e. the mistake the helper exists to prevent:That left-hand value is the record name that would fail every wildcard issuance, so the
test is anchored to the real failure rather than to the current implementation.
Build/lint/test:
cargo test -p certbot(6 passed),cargo fmt --check, andcargo clippy -p certbot -p dstack-gateway -- -D warningsall pass.Not covered: no live ACME run. These changes do not touch the wire behavior — the
diff is one extracted helper, two log lines, and a test — so the offline evidence above is
the whole story.
Not in this PR
certbot/src/acme_client/tests.rsis dead: it opens with#![cfg(not(test))], so it iscompiled out of every test run, and it has drifted — it calls
AcmeClient::load()withthree arguments where the function now takes five. It is a live-network test against
Cloudflare and Let's Encrypt, so deciding between fixing it, gating it on an env var, or
deleting it is its own conversation. Flagging it here so it is not mistaken for coverage.