Want to HTTPS an unknown set of subdomains without doing the DNS challenge required for wildcard certs?
Use on-demand TLS instead. It still has a bit of a challenge involved to prevent abuse though: it’ll call out to an HTTP endpoint to ask, “should I get a cert for this domain?”
You tell it where to send that query with the ask directive of the global on_demand_tls configuration block at the top of your Caddyfile:
{
on_demand_tls {
ask http://example.com
}
}This won’t change how any existing sites get their certs. It only comes into play when a site handler explicitly enables on_demand within its tls block:
example.com, *.example.com {
tls {
on_demand
issuer acme {
email ops@example.com
}
}
}When Caddy receives a request to some random subdomain defined within that site block (subdomain.example.com), it will send an HTTP GET to the configured ask endpoint, like http://example.com/?domain=subdomain.example.com. If it gets a 200 success code back, it will go ahead and obtain the certificate; otherwise, it will not.
I think the intent is to probably have some sort of API or database or other such logic to keep track of which subdomains are permitted, but I don’t feel like standing up additional infrastructure. I also don’t want to point it at a URL that will just automatically return a 200 for any domain.
So I tweaked my site block to handle that for me.
The @tls_allow matcher return a 200 for requests for subdomains below the configured domain so that Caddy will go ahead and fetch those certs for me. @tls_deny returns a 403 for any other domain validation requests. And httpredir (without a domain query) will redirect to HTTPS so that we don’t get stuck in plaintext hell.
example.com, *.example.com, http://example.com {
@tls_allow `protocol('http') && method('GET') && {query.domain}.endsWith("example.com")`
@tls_deny `protocol('http') && method('GET') && ({query.domain}.size() > 0 && !{query.domain}.endsWith("example.com"))`
@httpredir `protocol('http') && {query.domain}.size() == 0`
handle @tls_allow {
respond 200
}
handle @tls_deny {
respond 403
}
handle @httpredir {
redir https://{host}{uri}
}
handle {
reverse_proxy http://localhost:8080
}
tls {
on_demand
issuer acme {
email ops@example.com
}
}
}