Environment
- Ubuntu 26.04, certbot 5.8.0 (snap)
- Ploi's Hetzner DNS integration, using the Hetzner Cloud DNS API (api.hetzner.cloud/v1/zones)
- Hook scripts written by Ploi: /usr/local/bin/certbot-hetzner-auth.sh and /usr/local/bin/certbot-hetzner-cleanup.sh
- Subdomains don't work
The auth hook looks for a zone named exactly like the certificate's domain:
GET /v1/zones?name=${CERTBOT_DOMAIN} → "zones": [] ERROR: Could not find zone for api.example.com
For api.example.com in the zone example.com, no zone named api.example.com exists, so verification always fails. Only certificates for a bare domain (example.com) can work. Most API and app hostnames are subdomains.
Expected: find the zone the name belongs to by trying api.example.com, then example.com, and so on, and use the first one that exists.
- The challenge record is written at the wrong name
Even when a zone is found, both hooks use the record name _acme-challenge at the top of the zone. For a subdomain it must be relative to the zone: _acme-challenge.api in example.com.
- Cleanup deletes the whole TXT record
The cleanup hook deletes the whole _acme-challenge TXT record (DELETE /zones/{id}/rrsets/_acme-challenge/TXT) instead of only its own value. Two certificate requests in the same zone at the same time remove each other's values: two servers serving the same domain, or several certificates. The auth hook already adds values correctly instead of replacing them.
Expected: use POST /zones/{zone}/rrsets/{name}/TXT/actions/remove_records with only this challenge's value. The add side can use actions/add_records.
- Failed writes of the hook scripts are ignored
Ploi rewrites both hook scripts on every certificate request. When the files can't be written (we made them immutable with chattr +i to keep a custom version with our fixes), the request continues and runs certbot with whatever scripts are on disk. No error is reported. Any other write failure would probably behave the same. Ploi doesn't verify which scripts it will run.
Expected: stop the request with a visible error when a hook can't be written.
- The failure can stay hidden until the first renewal
Our first request seemed to succeed. Let's Encrypt reused a still-valid HTTP authorization from an earlier attempt and never asked for the DNS check. The bug only showed in certbot renew --dry-run. Without that dry run it would have surfaced as a failed renewal about 60 days later.
Suggested fix
The auth and cleanup hooks should:
- look up the zone by walking up the domain name
- use the record name relative to the zone
- add and remove only their own value
- fail with a clear error on any API error
Why we need this
We run an API service on two servers behind a Hetzner Floating IP: a main server and a hot-standby fallback. DNS points only at the Floating IP. If the main server fails, or during maintenance, the Floating IP moves to the standby, which has to take over immediately with a valid certificate. The standby gets no traffic until then, so Let's Encrypt's HTTP check can't reach it, neither for the first certificate nor for renewals. Putting a separate server in front to handle HTTPS for both isn't an option: it would introduce a new single point of failure, the very thing the standby exists to remove. Each server therefore has to hold its own certificate, and the DNS check is the only way for the standby to keep it valid. Since our API runs on a subdomain, Ploi's Hetzner DNS verification would need to support subdomains for this setup to work.
-
Jesser Horitani moved item to project Sites
8 hours ago -
Jesser Horitani created the item
8 hours ago