1. The problem I’m having:
Similar to a couple of other forum questions that have been asked around this topic but I haven’t found a full answer: we’re attempting to mix the on_demand_tls feature with wildcards.
Essentially, the desired outcome would be that any domain in our system would be provisioned a TLS certificate for itself, and all subdomains underneath it. Currently, our Caddy server is bumping up against rate limits from Let’s Encrypt and ZeroSSL, because our nameserver is rather naively routing traffic from all subdomains to our Caddy server, which has an ask
endpoint set up to provision TLS certificates for all hostnames in the zone of the domains in our system. So for example, if our system has example.com
registered, both example.com
and a.example.com
would receive distinct certificates upon being requested for the first time. This has caused thousands of certificates to be over-provisioned.
This is not ideal both for us and the CA’s, and as much as we want to fix this issue for us we would like to be respectful of our usage on their servers.
We have created a custom libssl plugin to provision DNS challenges with our nameserver, so the DNS challenge portion of this should be solved. However, there’s no mechanism that I know of in Caddyfiles to provision wildcard certificates without specifying a domain in the block (see the caddyfile we’re using below). When a request comes in and matches the https://
block, it will attempt to provision a TLS certificate for the exact domain included in the request, leading to duplicate certificates that could have been rolled into one.
This gets complicated when working with non-top-level domains like, for example, any .co.uk domain or any public suffix list domain for which we would not control the top-level zone, only the zone below the registered domain. Perhaps the easiest way to resolve this would be for us to intercept the provisioner request, and translate the hostname to the domain registered in our system. Is there some mechanism to intercept the request from the on_demand provisioner and modify the domain being requested so we could turn the above a.example.com
certificate into a *.exampe.com
certificate?
Thanks for any guidance or suggestions!
2. Error messages and/or full log output:
2024/05/07 20:46:04.539 ERROR tls.obtain will retry {"error": "[test.badactortest.com] Obtain: [test.badactortest.com] solving challenges: presenting for challenge: expected one record, got 0: [] (order=https://acme.zerossl.com/v2/DV90/order/m--41EWYRPQU0HX2fp_aAQ) (ca=https://acme.zerossl.com/v2/DV90)", "attempt": 2, "retrying_in": 120, "elapsed": 62.182385708, "max_duration": 2592000}
The error here is not so important as this was a test domain run locally so it did not have any public acme challenge records. The important part is that when visiting test.badactortest.com
, Caddy attempts to provision a cert for that name. We would like all domains enabled through on_demand_tls to provision a wildcard certificate for the topmost level domain that we control, in this case badactortest.com
.
3. Caddy version:
v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=
4. How I installed and ran Caddy:
a. System environment:
MacOS Sonoma 14.1 (also Docker with standard Caddy)
b. Command:
./caddy run --config Caddyfile
(run with custom xcaddy build to include custom libdns provisioner)
d. My complete Caddy config:
{
on_demand_tls {
ask http://localhost:3001/v1/caddy/check
}
}
https:// {
tls {
on_demand
dns custom_dns {
api_endpoint http://127.0.0.1:3001
}
}
}