Anders Tall

Wildcard HTTPS the easy way: acme.sh + Let's Encrypt (DNS-01)

3 min read

One certificate for every subdomain you will ever add: blog., tube., status. — all of them, from a single issuance and a single renewal. That is the difference between one certificate to think about and one per service to forget.

This is the written depth behind the video: what DNS-01 is, why a wildcard needs it, and the exact flow with acme.sh and Let’s Encrypt.

What we end up with

A valid, auto-renewing certificate for tall.nu + *.tall.nu, proven with openssl:

openssl x509 -in /etc/ssl/tall.nu/full.pem -noout -dates -subject -ext subjectAltName
# ...
# X509v3 Subject Alternative Name:
#     DNS:*.tall.nu, DNS:tall.nu

Why DNS-01

Wildcard certificates require the DNS-01 challenge. HTTP-01 proves you control one specific host by putting a file on it — it can never prove you control * (every host at once). DNS-01 instead places a TXT record in the zone, and a record in the zone covers the whole domain.

Note that wildcards are only one level deep: *.tall.nu covers blog.tall.nu, but not x.blog.tall.nu.

The flow, end to end

Against the Let’s Encrypt staging issuer while you get it right (staging has its own, much higher rate limits):

export PATH="$HOME/.acme.sh:$PATH"
curl https://get.acme.sh | sh -s email=you@example.com
install -D -m 0644 dns_nmu.sh ~/.acme.sh/dnsapi/dns_nmu.sh
export NMU_API_TOKEN="$(cat /root/.nmu_token)"     # never in frame
acme.sh --issue --dns dns_nmu -d tall.nu -d '*.tall.nu' --staging
mkdir -p /etc/ssl/tall.nu
acme.sh --install-cert -d tall.nu \
  --key-file /etc/ssl/tall.nu/priv.key \
  --fullchain-file /etc/ssl/tall.nu/full.pem \
  --reloadcmd "systemctl reload apache2"

acme.sh reads the provider credential from the environment on first use and saves it to ~/.acme.sh/account.conf, so renewals run unattended. Don’t paste the token into your shell history and don’t hardcode it in the plugin — read it from a file (as above), and keep it out of anything you record or commit.

The wildcard trap: two TXT values, one name

This is the part that trips people up. Both tall.nu and *.tall.nu publish their challenge at the same name — _acme-challenge.tall.nu — so two TXT values must exist at the same time during validation.

If your provider’s API replaces the record set on every write (many do), a naive add-hook will overwrite the first value with the second and validation will hang forever, repeating Checking tall.nu for _acme-challenge.tall.nu. The fix is to accumulate: send the union of the values for the run. In our dns_nmu hook that means keeping the values in a small state file and sending the whole list each time.

Renewal is the point

Let’s Encrypt certificates last 90 days. acme.sh installs a cron job when it installs itself, and --install-cert --reloadcmd makes the web server pick up the new certificate after every renewal:

35 5,11,17,23 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

A certificate you have to renew by hand is a certificate that will one day expire.

Pitfalls

Watch the video: Wildcard HTTPS the easy way.

#wildcard certificate #letsencrypt #acme.sh #dns-01 #https #tls #self-hosting #linux #terminal