Cover Image

Wildcard Certificates with Letsencrypt and DNS Challenge

March, 25. 2019f 2019 - Reading time: 6 minutes

If you run several web applications on a web server, it is relatively annoying to keep separate processes running for each individual subdomain, which fetches the required SSL certificates in good time before the expiration date and carry out the verification process for each website.

For this reason, there are wildcard certificates, i.e. certificates that cover your own domain including all subdomains in one certificate. This certificate can then be stored centrally and used for all subdomains without further actions. I'll explain how to do that now.

Prerequisites

Configure your DNS server so that all of your sub-domains point to the same IP address of your central web server, so that requests from users are all sent to hits one. The DNS of your provider must also offer the possibility to get text entries set via an API, which will be used in the verification process.

First we have to get the script acme.sh, more about this below. Since we want to install our script centrally, we do this as user root:

sudo -s
wget -O acme.sh https://get.acme.sh

Alternatively, we can also clone this with Git, if installed:

git clone https://github.com/Neilpang/acme.sh.git
cd acme.sh

Installation

We use the script acme.sh by Neil Pang, which is explained on Github: https://github.com/Neilpang/acme.sh . This script has the advantage over Certbot that it does not require a complete Python installation but is written in shell instead. It runs on any Linux system and even on Windows (if you like it). Storing certificates can be configured easily and individually.

We would like to install this script and the certificates created later centrally under /etc/acme.sh, the own account key belong to the system admin (user root):

sudo ./acme.sh --install \
--home /etc/acme.sh \
--config-home /etc/acme.sh/config \
--cert-home  /etc/acme.sh/certs \
--accountemail  "admin@email.de" \
--accountkey  ~/.acme/myaccount.key \
--accountconf ~/.acme/myaccount.conf

The script also creates a cron job so that the certificates later get renewed regularly and automatically. You can view this as user root with crontab -l.

Configure DNS access (if possible)

In order to request certificates, the DNS used must be able to be queried by an API and a password. To communicate these settings to acme.sh, some environment variables are now set before the certificates are actually retrieved. This only has to be done once, acme.sh remembers this in its configuration:

Example for the DNS of Netcup:

export NC_Apikey=""
export NC_Apipw=""
export NC_CID=""

Example for the Cloudfare DNS:

export CF_Key="Apikey"
export CF_Email="xxxx@sss.com"

Further DNS settings can be viewed here, this is excellently documented:

https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api

Of course, the DNS query on provider side must be enabled in advance via the API.

Fetch Certificates (automatically)

After all the preparatory work has been done, the certificates can now be fetched, again using our two examples. We get the certificates for the domain itself, here mydomain.de, as well as the respective subdomains we might want to use later. We are increasing the DNS response time a bit, as some DNS servers take longer to actually make the new text entry available. I've had good results with 480 seconds of waiting time.

For the DNS from Netcup:

./acme.sh --issue --dnssleep 480 --dns dns_netcup -d 'mydomain.de' -d '*.mydomain.de'

Für den von Cloudflare:

./acme.sh --issue --dnssleep 480 --dns dns_cf -d 'mydomain.de' -d '*.mydomain.de'

Get Certificates (manually)

If a DNS cannot be queried automatically because you do not have access to the API, it is possible to make the text entries manually. But this has to happen every time the certificates are renewed. Therefore this solution is basically not practicable. We want to automate as much as possible. If you want it anyway, you can do it that way:

acme.sh --issue -d 'mydomain.de' -d '*.mydomain.de' --dns --dnssleep 960 --yes-I-know-dns-manual-mode-enough-go-ahead-please

Problems?

If problems arise, you can further analyze the process with the options --log or --debug. The biggest problem is the timeout. Here you should increase the value of --dnssleep.

Result and Conclusion

We now have our new certificates in the /etc/acme.sh/certs directory. We can now include them in the respective server configurations of Apache or Nginx. I myself use Nginx (as reverse proxy):

# Let's Encrypt SSL
ssl_certificate     /etc/acme.sh/certs/mydomain.de/fullchain.cer;
ssl_certificate_key /etc/acme.sh/certs/mydomain.de/mydomain.de.key;

Since the certificates are renewed without the web server being required (we use the DNS API), the configuration of the web server is also much easier. There is no need to set up a mechanism to intercept letsencrypt challenges and port 80 can be left unused if desired.