Skip to content

Fix stale renewal check and ACME DNS validation race - #1

Merged
TomTyack merged 1 commit into
TomTyack:masterfrom
thomas-norg-ai:fix-cert-renewal-checks
Aug 25, 2026
Merged

Fix stale renewal check and ACME DNS validation race#1
TomTyack merged 1 commit into
TomTyack:masterfrom
thomas-norg-ai:fix-cert-renewal-checks

Conversation

@thomas-norg-ai

Copy link
Copy Markdown

What this fixes

Two bugs were causing the Sev1 availability alerts to flap: the cert
was about to expire, and the auto-renewal wasn't renewing it.

1. NeedsNewCertificateAsync() never found the existing cert.
It filtered by Issuer.Contains("Let's Encrypt Authority"), which was
Let's Encrypt's pre-2021 intermediate CA name. Current certs are
issued by R11/R12/R13/YR1 etc., so the filter never matched,
existingCert was always null, and the tool requested a brand-new
certificate on every scheduled run instead of honoring
TimeBeforeExpiryToRenew. Upstream's own fix for this (n3wt0n#30) just
hardcoded "R3" instead, which is equally stale today since LE keeps
rotating intermediate names. This PR drops the issuer filter
entirely — ExistingCertificates is already scoped to the target
hostname a few lines earlier in GetResourceConfigurationAsync, so
no name-matching is needed at all.

2. GetCertificateAsync() raced Azure DNS propagation.
dnsChallenge.Validate() was called immediately after creating the
_acme-challenge TXT record. If Azure DNS hadn't propagated the
record yet, Let's Encrypt's first check fails and flips the
authorization out of "pending" — so the existing 90-second retry loop
then errors on every subsequent attempt with
urn:ietf:params:acme:error:malformed: Unable to update challenge :: authorization must be pending, instead of actually retrying. This
matches n3wt0n#48 and n3wt0n#50 exactly (same error message reported there). This
PR adds a configurable delay (WaitTimeBeforeValidateInSeconds,
default 15s) before the first validation attempt, along the same
lines as n3wt0n#50.

Together these mean the tool only attempts renewal when actually
needed, and that attempt no longer races DNS propagation.

- NeedsNewCertificateAsync() filtered existing certs by issuer
  containing "Let's Encrypt Authority" (the pre-2021 intermediate
  name). Current certs are issued by R11/R12/R13/YR1 etc., so the
  filter never matched, existingCert was always null, and the tool
  requested a brand-new certificate on every scheduled run instead
  of honoring TimeBeforeExpiryToRenew. Upstream's own fix for this
  (n3wt0n#30) just hardcoded "R3" instead, which is equally stale
  today. Dropped the issuer filter entirely - ExistingCertificates
  is already scoped to the target hostname.

- GetCertificateAsync() called dnsChallenge.Validate() immediately
  after creating the DNS TXT record, before Azure DNS had time to
  propagate it. A premature check fails the challenge and flips the
  authorization out of "pending", so the retry loop's subsequent
  Validate() calls error with "authorization must be pending"
  instead of actually retrying. Matches upstream issue n3wt0n#48/n3wt0n#50.
  Added a configurable delay (default 15s, WaitTimeBeforeValidateInSeconds)
  before the first validation attempt.
@TomTyack
TomTyack merged commit 00a9585 into TomTyack:master Aug 25, 2026
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.

2 participants