Provisioning wildcard, on_demand_tls records

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
        }
    }
}

I’m not sure I follow. Are you trying to use the DNS challenge for domains you don’t own/control? Are your customers pointing wildcard domains to your server instead of just a single domain?

In general, On-Demand TLS should be used for domains you DON’T control, i.e. customers pointing their domains to yours. You should prevent the ask endpoint from giving a “yes” for more than one (or a small handful) of domains per customer, probably, to avoid abuse.

In general, wildcard certificates should be used for domains you DO control, where you are able to perform DNS updates on that domain. So it doesn’t make sense for that reason to try to use the DNS challenge for customer’s domains.

It’s generally our opinion that there’s no overlap on those two cases. So we’ll need more details on what you’re trying to do here.

1 Like

I guess what I’m also trying to understand is, why?

This can almost certainly be done, but will be a lot easier with a JSON config. The Caddyfile is quite opinionated and conforms to common use cases.

One other thing I’ll highly recommend is a sponsorship – depending on the tier, we’ll be able to help you in private and really hone your infrastructure to optimize for scale.

1 Like

Thanks for the quick reply!

Our use case might be a little unconventional, so it’s totally understandable that out-of-the-box features might not cover it.

We park abusive domains on a nameserver to halt abuse, and point requests to our own web server to collect logs. On Demand TLS stood out to us as a good fit initially, because from the web server side it’s not too dissimilar to a web hosting company that needs to accommodate HTTPS traffic from an unknown, non-fixed set of domains. The ask endpoint merely needs to query the database of currently parked domains to determine which domains to grant certificates for.

However, many such bad domains also serve content on subdomains, so setting wildcard records is very important to capture a large portion of the traffic. This means that our ask endpoint likewise needs to respond true if we have a parent domain of those requested (i.e. test.example.com would search our database for example.com so we don’t discard subdomain traffic).

Consequently, Caddy will dutifully attempt to provision certificates for all unique hostnames for domains we control, at the expense of over-provisioning thousands of certificates that could have been covered by wildcards. That’s where we’re coming from with this – we need some means of managing TLS certificates for a constantly-changing list of domains, including HTTPS traffic on subdomains.

I figured this would be an appropriate forum post because it does seem like there are other use cases where service providers might want their customers to be able to use the entire recursive zone under their domain on their product. Thanks for any direction you can point us in!

Hi there Francis, thanks for your quick reply!

See my reply to Matt for more context, but wanted to address this point as well. It’s also possible that if there’s a Caddy API route to append new domains to our server without stopping it, we can use that to handle this in our business logic at “registration” time (i.e. when we process a new domain, we attempt to provision a wildcard record programmatically) rather than “lazily” with On Demand TLS. That might make more sense if these features aren’t intended to be mixed together like this, though I see other forum posts of the same nature linking back to On Demand TLS.