Skip to content

feat(edge): opt-in Hetzner Cloud DNS-01 challenge method for certbot#20

Merged
baditaflorin merged 1 commit into
mainfrom
claude/edge-dns01-hetzner-cloud
May 31, 2026
Merged

feat(edge): opt-in Hetzner Cloud DNS-01 challenge method for certbot#20
baditaflorin merged 1 commit into
mainfrom
claude/edge-dns01-hetzner-cloud

Conversation

@baditaflorin
Copy link
Copy Markdown
Owner

Summary

Fixes the wildcard-cert issuance failure for *.apps.0mcp.com (and any future wildcard SAN). The existing dns-hetzner challenge method uses the certbot-dns-hetzner plugin, which talks to the deprecated standalone dns.hetzner.com API and now fails with the token you have provided is invalid (unauthorized) — the account uses the Hetzner Cloud API (api.hetzner.cloud/v1) for DNS.

This adds a new opt-in public_edge_acme_challenge_method: dns-hetzner-cloud that drives DNS-01 through certbot --manual hooks against the Cloud API, mirroring the rrset shape already used by the hetzner_dns_records role.

What's included

  • files/hetzner_cloud_acme_hook.py — self-contained, stdlib-only certbot auth/cleanup hook. Resolves the longest-suffix zone, computes the zone-relative _acme-challenge name, and appends (not overwrites) TXT values so apex + wildcard challenges at the same name coexist. Cleanup is idempotent.
  • tasks/main.yml — three new guarded tasks (hook dir, hook copy, credential file + assert) and a three-way method branch in both certbot certonly argv/environment blocks.
  • defaults/main.yml + meta/argument_specs.yml — three new vars and the dns-hetzner-cloud choice.
  • README.md — short usage note.

Safety

  • Purely additive / inert until enabled. webroot and dns-hetzner render byte-identically (verified by Jinja render of all branches). Nothing changes unless public_edge_acme_challenge_method is explicitly set to dns-hetzner-cloud.
  • Reuses the existing HETZNER_DNS_API_TOKEN (Cloud API token) the platform already uses for hetzner_dns_records.

Test plan

  • YAML parse + py_compile pass; hook unit-tested against a mocked Cloud API (append / dedupe / partial-cleanup / delete-on-empty / idempotent-cleanup / longest-suffix-zone / relative-name).
  • Not yet live-validated — requires an edge converge with the method enabled (GitHub Actions is currently billing-locked; the local pre-push gate validated the snapshot).

Known follow-up

  • The hook addresses an rrset by id if the Cloud API returns one, else falls back to <name>/<type>. The sibling role uses an opaque provider_ref loaded from its own inventory; if the live rrset reference differs, only the update/delete addressing needs a one-line tweak. Create/append (the wildcard path) follows the sibling role's confirmed shape exactly.

The existing dns-hetzner method uses the certbot-dns-hetzner plugin, which
talks to the deprecated standalone dns.hetzner.com API and now fails with
"token invalid (unauthorized)" because the account uses the Hetzner Cloud
API (api.hetzner.cloud/v1) for DNS. This blocks wildcard cert issuance
(e.g. *.apps.0mcp.com).

Add a new opt-in public_edge_acme_challenge_method=dns-hetzner-cloud that
drives DNS-01 via certbot --manual hooks against the Cloud API, mirroring
the rrset shape already used by the hetzner_dns_records role. The hook is
stdlib-only Python; it appends (not overwrites) TXT values so SAN certs with
both apex and wildcard challenges at the same _acme-challenge name coexist,
and cleans up idempotently.

Existing webroot and dns-hetzner behavior is unchanged (additive, inert
until explicitly enabled).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@baditaflorin baditaflorin merged commit 1d79f6c into main May 31, 2026
0 of 2 checks passed
@baditaflorin baditaflorin deleted the claude/edge-dns01-hetzner-cloud branch May 31, 2026 21:10
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