Wildcard Certificates with Letsencrypt and DNS Challenge

If you run several web applications on a separate server, it is relatively annoying to keep a separate process running for each individual subdomain, which always fetches the required SSL certificate in good time before the expiration date and carries out the verification process via its own 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 IP address of your central web server, so that requests from users are first sent to you. The DNS of your provider must also offer the possibility to generate text entries via an API, which can be queried 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 get 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 Python installation and 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 our script or the certificates created later centrally under /etc/acme.sh, the own account key remains with the user root:

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

The script also creates a cron job so that the certificates are renewed regularly and automatically later. 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 requested via 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="<Apikey>"
export NC_Apipw="<Apipassword>"
export NC_CID="<Customernumber>"

Example for the DNS of Cloudflare:

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

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

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

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

Get the certificates (automatically)

After all the preparatory work has been done, the certificates can now be collected, again using our two examples. We get the certificates for the domain itself, here meinedomain.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 'meinedomain.de' -d '*.meinedomain.de'

For the one from Cloudflare:

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

Pick up certificates (manual)

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 'meinedomain.de' -d '*.meinedomain.de' --dns --dnssleep 960 --yes-I-know-dns-manual-mode-enough-go-ahead-please

Problems?

If there are problems, 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

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/meinedomain.de/fullchain.cer;
ssl_certificate_key /etc/acme.sh/certs/meinedomain.de/meinedomain.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 if desired.