Let's Encrypt Wildcards Are Here, and They Run Through Your DNS
On Tuesday, March 13, Let’s Encrypt announced that ACME v2 and wildcard certificate support are live. Let’s Encrypt called wildcards a commonly requested feature when it first promised them last July, and now you can get a certificate for *.example.com for free. The same announcement has a line that’s easy to skim past, though: Let’s Encrypt still recommends non-wildcard certificates for most use cases. Both halves of that are worth understanding before you rip out your per-host certificates.
A new endpoint, not an upgrade in place
ACME v2 lives at its own directory URL, next to the v1 endpoint rather than replacing it:
https://acme-v02.api.letsencrypt.org/directory
The announcement says the plan is to move all clients and subscribers to v2, but no end-of-life date for the v1 API has been set yet. So nothing breaks this week, and nothing changes for you unless your client starts talking to the new URL.
The reason for a second API is standardization. Let’s Encrypt’s v1 API grew up alongside early ACME drafts, which then changed in backwards incompatible ways at the IETF. Its January post announcing the v2 staging endpoint said that with the draft in last call, it could finally ship an API much closer to the eventual RFC. It isn’t a final RFC yet, though. The technical post for client developers compares the implementation to draft-ietf-acme-acme-10, dated March 5, and lists what isn’t implemented. Pre-authorization is optional in the draft and Let’s Encrypt has no plans for it, and the orders field on account objects is still to come.
Orders instead of authorizations
The biggest change in how a client talks to the CA is the issuance flow. In v1, a client asked for an authorization for each name through the new-authz endpoint, completed a challenge for each one, and then sent a CSR to new-cert. In v2 the client starts with a newOrder request that lists every identifier it wants. The order comes back with the authorizations it needs, the client satisfies their challenges, and then it finalizes the order by sending the CSR to the order’s finalize URL.
A few other things moved too. Creating an account and agreeing to the terms of service now happen in one newAccount request instead of two steps. Most signed requests identify the account with a kid header holding the account URL rather than embedding the public key, and the URL being posted to goes into the protected JWS header, replacing the old resource field in the body. Field names are consistently camelCase, so a v1 directory entry like revoke-cert is now revokeCert.
For you as a user, the practical upshot is that v1 clients won’t work against v2 without code changes. Your existing production account does carry over, but authorizations held by a v1 account don’t, so expect to revalidate your domains the first time you issue through v2.
Why wildcards need DNS-01
Under Let’s Encrypt’s policy, a wildcard identifier has to be validated with the DNS-01 challenge, so its authorization offers no other challenge.
The clearest explanation came from Let’s Encrypt engineer Jacob Hoffman-Andrews when wildcards were announced last July. His point was that passing an HTTP check shows you control one host, while passing a DNS check shows, roughly, that you control the domain’s DNS. Someone can legitimately run the web server at example.com without controlling mail.example.com, and that person could pass an HTTP check but not a DNS one. A certificate for *.example.com would cover mail.example.com too, so control of one web server isn’t enough evidence.
The mechanics are in section 8.4 of the draft. The client builds a key authorization from the challenge token and its account key, takes the SHA-256 digest, base64url encodes it, and publishes that as a TXT record at _acme-challenge under the name being validated. For a wildcard, the draft says the authorization’s identifier drops the *. prefix and gets a wildcard flag instead, so validating *.example.com means a TXT record at _acme-challenge.example.com. The CA then queries for TXT records at that name and checks whether one of them matches.
A name can have only one wildcard, and it has to be the whole leftmost label, but one certificate can mix wildcards for several base domains with ordinary names. Asking for both example.com and *.example.com is fine, and the order will contain two separate authorizations for example.com. Asking for *.example.com alongside www.example.com is an error, because the wildcard already covers www. If you validate both example.com authorizations through DNS, you’ll have two TXT values at the same name, which works because the draft only requires one record to match.
What DNS-01 means in practice
With 90-day certificates, a DNS challenge you complete by hand in your registrar’s web panel is a chore you’ll repeat forever. Realistically, wildcards mean giving your ACME client API access to your DNS provider, and that deserves most of your thinking.
Joona Hoikkala laid out the risk in an EFF post last month, A Technical Deep Dive: Securing the Automation of ACME DNS Challenge Validation. The machine doing the automation has to store DNS credentials, so if it’s compromised, those credentials are too. With your DNS, an attacker can get valid certificates for your domain and intercept your web and mail traffic. The client only needs to update _acme-challenge TXT records, but the post notes most providers don’t offer credentials that narrow, and it walks through mitigations, including credentials limited to TXT records and a CNAME from _acme-challenge to a separate validation zone with its own credentials, which works because Let’s Encrypt follows CNAME chains.
Certbot’s own plugin docs are blunt about this. The Cloudflare plugin tells you to protect its credentials file like your Cloudflare account password, and Certbot warns about unsafe permissions every time it uses a credentials file that other users on the system can access. The Route 53 plugin documents an IAM policy that limits record changes to a single hosted zone. My advice is to use the narrowest credential your provider allows, keep it on as few machines as possible, and chmod 600 the file.
Then there’s timing. The TXT record has to be visible when Let’s Encrypt checks, and Certbot’s DNS plugins in 0.22.0 simply wait a configurable number of seconds before asking for validation. The Cloudflare plugin defaults to 10, and several others default to 30 or 60. If your provider is slow to update its nameservers, raise that value rather than burning through retries, since the failed validation limit is 5 failures per account, per hostname, per hour.
Getting one with Certbot 0.22.0
Let’s Encrypt lists several ACME v2 clients, including Certbot from 0.22.0. The Certbot changelog for 0.22.0, released March 7, adds wildcard support and the newer protocol, and says Certbot picks the protocol version based on what the CA implements. Depending on how you installed Certbot, Let’s Encrypt notes, 0.22.0 may not have reached you yet.
In 0.22.0 the default server is still the v1 endpoint, so you point Certbot at v2 yourself. With a DNS plugin, such as the Cloudflare plugin as of 0.22.0, that looks like this:
certbot certonly \
--server https://acme-v02.api.letsencrypt.org/directory \
--dns-cloudflare \
--dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
--dns-cloudflare-propagation-seconds 60 \
-d example.com -d '*.example.com'
Quote the wildcard so your shell doesn’t expand it. If there’s no DNS plugin for your provider, the manual plugin with --preferred-challenges dns can call your own scripts through --manual-auth-hook and --manual-cleanup-hook. Without an auth hook, the manual plugin refuses to run non-interactively, so unattended renewal needs one.
Rate limits
The existing limits still apply to v2, plus one new one: an account can create at most 300 new orders per 3 hours. The limit on pending authorizations is still there too, and since one order can create several, the technical post says you may hit that one first. The rate limits doc also lists 20 certificates per registered domain per week and 100 names per certificate. Test against the staging v2 endpoint at https://acme-staging-v02.api.letsencrypt.org/directory before you point automation at production.
When a wildcard is worth it
A wildcard earns its keep when you can’t list the names ahead of time: a subdomain per customer, preview environments that come and go, or anything close to the 100 names per certificate limit.
The cost is scope. A leaked wildcard key works for every name under that label, including ones you haven’t created yet, while a leaked key for an explicit list only covers the names on it. A wildcard also tends to get copied onto every server that needs it, and the renewal box now holds a DNS credential, so risk piles up in a couple of very valuable secrets. Also keep in mind, as Hoffman-Andrews noted in that same thread, that a wildcard only matches one level deep, so *.example.com won’t match a.b.example.com.
If you know your hostnames and HTTP-01 works for them, I’d keep issuing explicit certificates per service. Reach for a wildcard when names are genuinely dynamic, run the DNS side on one well protected machine with the tightest credential you can get, and treat that machine like part of your DNS infrastructure, because it is.