Using a wildcard domain specification with a Let's Encrypt SSL certificate
March 18, 2018


On March 13, 2018, Let’s Encrypt began supporting wildcard certificates. This post goes over how to create a Let’s Encrypt certificate with a wildcard domain specification, and how to transition a Let’s Encrypt certificate from one that doesn’t have a wildcard domain specification to one that does.

It’s worth noting that this post is largely a note to myself. As such, the instructions are tailored to my situation (which involves the DNS for my domains being hosted by Gandi and the servers for my domains being hosted by Linode (referral link)).

Anyway, if you’re transitioning a Let’s Encrypt certificate from one without a wildcard specification to one with a wildcard specification, the first thing you will want to do is get some information about your certificate; you can do this by running the following command:

sudo certbot certificates

This should give you some informative output about the certificates that you’ve installed with certbot. The output should look something like this:

-------------------------------------------------------------------------------
Found the following certs:
  Certificate Name: example.org
    Domains: example.org,git.example.org,shiny.example.org
    Expiry Date: 2018-06-05 22:10:00+00:00 (VALID: 79 days)
    Certificate Path: /etc/letsencrypt/live/example.org/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/example.org/privkey.pem
-------------------------------------------------------------------------------

Next, you’ll want to use the certonly command to modify the certificate. This command has several options that you can use. First, you’ll want to specify the certificate name with --cert-name. If you’re transitioning an old certificate, you’ll want to use this flag to specify the same name as the certificate name that you just saw after running the command sudo certbot certificates. If you’re creating a new certificate, you can optionally use this flag to specify a name for the certificate; if you omit it, it will just use the first domain in the certificate as the name for the certificate.

After the certificate name, you’ll need to specify the domains that the certificate should be used for, which you can do with the --domains flag (or -d for short).

So, for the above certificate, where I previously had example.org, git.example.org, and shiny.example.org as domains, I now would only have example.org and *.example.org domains. The second one covers both of the previous subdomains: git.example.org and shiny.example.org. Note, however, that the wildcard *.example.org does not cover domains like adam.users.example.org; it only covers subdomains up to one level. If you wanted a subsubdomain for an arbitrary user, you’d need to add the domain *.users.example.org.

You’ll also need to ensure that you’re using a version of certbot ≥ 0.22.0. It is only since version 0.22.0 that certbot is compatible with version 2 of Let’s Encrypt’s ACME API, and it is only version 2 of this API that supports wildcard certificates. Moreover, the default API is still the first version of the API, so you’ll need to explicitly specify --server https://acme-v02.api.letsencrypt.org/directory, when running the command.

Relatedly, the only acceptable challenge for proving you own the domain when requesting a wildcard certificate is the DNS challenge, so you’ll also need to specify --preferred-challenges dns.1

There are several DNS authenticator plugins for certbot; however, there is no authenticator plugin for Gandi, so I furthermore need to specify the --manual flag in order to do the authentication myself.

Putting this all together, you should run the following command:

sudo certbot certonly --cert-name example.org -d example.org,*.example.org --server https://acme-v02.api.letsencrypt.org/directory --preferred-challenges dns --manual

You may then be asked to enter your email address (I think you’ll be asked if this is the first time using version 2 of the ACME API):

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel):

Go ahead and enter your email address.

Next, you’ll be asked to agree to the terms of service for version 2 of the API:

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel:

And then you’ll be asked whether you want to share your email address with EFF:

-------------------------------------------------------------------------------
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
-------------------------------------------------------------------------------
(Y)es/(N)o:

If you’re updating an old certificate that had subdomains that you’ve now dropped in favor of the wildcard specification, you’ll see a warning about the fact that you’re adding new domains and removing previously included domains. Review this information, and, if it looks correct, accept it by typing U:

-------------------------------------------------------------------------------
You are updating certificate example.org to include new domain(s):
+ *.example.org

You are also removing previously included domain(s):
- git.example.org
- shiny.example.org

Did you intend to make this change?
-------------------------------------------------------------------------------
(U)pdate cert/(C)ancel:

Next, you’ll see a warning about the fact that the IP address of your server will be logged as having publicly requested the certificate for your domain.

Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for example.org

-------------------------------------------------------------------------------
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
-------------------------------------------------------------------------------
(Y)es/(N)o:

Finally, you will see instructions for deploying a DNS TXT record to verify that you own the domain(s) for the certificate that you’ve just requested. If the certificate is for multiple domains, you’ll need to deploy this TXT record for each domain. Minimally, you’ll be asked to create two TXT records for _acme-challenge.example.org: one is for the certification of example.org, and one is for the certification of *.example.org. Furthermore, if you’ve requested a wildcard certificate for subsubdomains, you’ll also need to deploy the TXT record for, e.g., _acme-challenge.users.example.org.

-------------------------------------------------------------------------------
Please deploy a DNS TXT record under the name
_acme-challenge.example.org with the following value:

okzpb6F0pE8klBLVE4456jkwlUnNCC1SObMmhAu4qwe

Before continuing, verify the record is deployed.
-------------------------------------------------------------------------------
Press Enter to Continue

After setting up the TXT record with your DNS provider (Gandi, in my case), you’ll need to wait for the DNS record to propogate before hitting Enter.2 You can verify that the DNS record has propogated by running the nslookup command:

nslookup -type=TXT _acme-challenge.example.org

If the DNS record has not yet propogated, you should see this in the returned value:

[...]
Non-authoritative answer:
*** Can't find _acme-challenge.example.org: No answer
[...]

However, if the DNS record has propogated, you should see this in the returned value:

[...]
Non-authoritative answer:
_acme-challenge.example.org   text = "okzpb6F0pE8klBLVE4456jkwlUnNCC1SObMmhAu4qwe"
[...]

Again, if you’re creating a certificate that covers multiple domains, you’ll need to do this for each domain, and the value that you need to specify for the TXT record will be different for each domain.

If everything succeeds, you’ll see the following output:

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/example.org/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/example.org/privkey.pem
   Your cert will expire on 2018-05-18. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

The last thing to do, at least in my case, is to restart my web server. In my case, that’s nginx, so I just run:

sudo /etc/init.d/nginx restart

Feel free to comment with any questions! I’m happy to try to help out as best I can. 🤓 However, like I said, this particular post is largely intended as a note to self, and some aspects of this process are dependent on your particular setup; nonetheless, I’m happy to help if I can!


Notes

  1. Specifically, you need to use the dns-01 challenge; at the time of writing this post, the dns-01 challenge is the most recent dns challenge, and dns is just an alias for the most recent dns challenge. 

  2. Because it can take a while for DNS records to propogate, it might be worth doing all of this from inside of a multiplexer, such as tmux or GNU Screen in order to keep the process alive, should you be disconnected from your server while you’re waiting for the DNS records to propogate. See, for example, “How to keep processes running after ending ssh session?”.