Since StartSSL had issues and are being delisted, I needed an alternative.

The one thing that put me off Lets Encrypt for so long is that I could no longer administer all my certs from a central location. This meant running software on several systems to keep the certs updated - or manual intervention every 90 days.

This wasn't acceptable to me - so I hunted for another solution. Enter the DNS-01 validation challenge. The Lets Encrypt recommended solution (certbot) doesn't support the DNS-01 method yet - so I needed something else.

This solution is aimed at those who have your own domain, and have many hosts that you use certs on (could be anything from SQL servers, to mail, to web servers) and want to manage them all from the same place - and not run additional software on them. In this guide, I've used 'example.com' as the domain name. Adjust to suit.

Set up your VM to use for the cert management. As always, I used Scientific Linux 7. You can use whatever you like, just adjust accordingly.

Install bind, then create your base zone file in /var/named/data/ for 'le.example.com' with something like:

$ORIGIN . $TTL 21600 ; 6 hours le.example.com IN SOA certvm.example.com. webmaster.example.com. ( 2016112648 ; serial 43200 ; refresh (12 hours) 3600 ; retry (1 hour) 1209600 ; expire (2 weeks) 3600 ; minimum (1 hour) ) NS certvm.example.com.

Set up bind and dynamic DNS updates with 'nsupdate'. There is lots written on this topic that is covered in much more detail that I could cram into here. I like the guide nsupdate: Painless Dynamic DNS.

Now we set up the LE software. I used 'dehydrated' (previously letsencrypt.sh). You'll want to check this out as a non-root user.

# git clone https://github.com/lukas2511/dehydrated.git

I wrote a hook file that gets called from dehydrated while it goes through its process. Save it to the directory you cloned dehydrated into under the filename 'dns-hook.sh'

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #!/usr/bin/env bash # # Example how to deploy a DNS challenge using nsupdate # set -e set -u set -o pipefail NSUPDATE = "/usr/bin/nsupdate -k /home/user/Knsupdate-key.+157+17276.key" TTL = 300 case " $1 " in "deploy_challenge" ) $NSUPDATE <<-EOF server 127.0.0.1 zone le.example.com. update add ${2}.le.example.com. ${TTL} in TXT "${4}" send EOF ;; "clean_challenge" ) $NSUPDATE <<-EOF server 127.0.0.1 zone le.example.com. update delete ${2}.le.example.com. ${TTL} in TXT "${4}" send EOF ;; "deploy_cert" ) /home/user/bin/deploy_cert " ${ 1 } " " ${ 2 } " " ${ 3 } " " ${ 4 } " ;; * ) echo "Nothing to do for hook ${ 1 } " exit 0 ;; esac exit 0

Configure dehydrated by coping the example config file in docs/examples, and set:

CA="https://acme-staging.api.letsencrypt.org/directory" CHALLENGETYPE="dns-01" HOOK=${BASEDIR}/dns-hook.sh

Once you have verified that all these steps work, you can comment out the CA line to obtain live certs.

Now we create the deployment script called from dns-hook.sh as ~/bin/deploy_cert:

#!/bin/bash set -e HOST = $2 SSHOPTIONS = "-o ControlMaster=auto -o ControlPersist=60 -o ControlPath=~/.ssh/%r@%h-%p" ## Do we have a config file for this host? if [ -f $HOME /deployment/ $2 ] ; then . $HOME /deployment/ $2 else echo "No config file for host $host " echo "Aborting..." exit 0 ; fi echo "Starting deployment to $HOST " ## Connect to the remote server and leave session open for max 60 seconds. ssh $HOST $SSHOPTIONS "/bin/true" ## Do we have a cert to deploy? if [ -f " $HOME /dehydrated/certs/ $2 /cert.pem" ] && [ ! -z " $CERT " ] ; then echo " $HOST - Copying CERT to $CERT " CERT_FILE = ` cat " $HOME /dehydrated/certs/ $2 /cert.pem" ` ssh $HOST $SSHOPTIONS \ "cat << 'EOF' > $CERT $CERT_FILE EOF" fi ## Do we have a key to deploy? if [ -f " $HOME /dehydrated/certs/ $2 /privkey.pem" ] && [ ! -z " $KEY " ] ; then echo " $HOST - Copying KEY to $KEY " KEY_FILE = ` cat " $HOME /dehydrated/certs/ $2 /privkey.pem" ` ssh $HOST $SSHOPTIONS \ "cat << 'EOF' > $KEY $KEY_FILE EOF" fi ## Do we have a chain to deploy? if [ -f " $HOME /dehydrated/certs/ $2 /chain.pem" ] && [ ! -z " $CHAIN " ] ; then echo " $HOST - Copying CHAIN to $CHAIN " CHAIN_FILE = ` cat " $HOME /dehydrated/certs/ $2 /chain.pem" ` ssh $HOST $SSHOPTIONS \ "cat << 'EOF' > $CHAIN $CHAIN_FILE EOF" fi ## Do we have a fullchain to deploy? if [ -f " $HOME /dehydrated/certs/ $2 /fullchain.pem" ] && [ ! -z " $FULLCHAIN " ] ; then echo " $HOST - Copying FULLCHAIN to $FULLCHAIN " FULLCHAIN_FILE = ` cat " $HOME /dehydrated/certs/ $2 /fullchain.pem" ` ssh $HOST $SSHOPTIONS \ "cat << 'EOF' > $FULLCHAIN $FULLCHAIN_FILE EOF" fi ## Do we have a command to run afterwards? if [ ! -z " $COMMAND " ] ; then echo " $HOST - Executing remote command: $COMMAND " ssh -t $HOST $SSHOPTIONS " $COMMAND " fi

Create the directory $HOME/deployment and create a text file (for example) www.example.com:

HOST=webhost.example.com CERT=/path/to/www.example.com.crt KEY=/path/to/www.example.com.key CHAIN=/path/to/intermediate.crt COMMAND="sudo service httpd reload"

Within this file CERT, KEY and CHAIN are the files that hold your SSL cert on the server. HOST is the SSH server that hosts your web site. COMMAND is the command run after the cert is copied across. It is expected that you be able to set up SSH keys to do this without entering a password to allow automatic updates of your certs.

Now we turn our attention to the main DNS server that serves your domain to the public.

In your main DNS server for example.com, we now want to delegate the entire 'le.example.com' namespace to the DNS server we just created. Don't forget to also set an IP for the certvm system. This will look something like:

certvm IN A 1.2.3.4 le IN NS certvm.example.com.

Next, for each fqdn we want to create a certificate for, we want to create a CNAME back to the le.example.com namespace - for example:

_acme-challenge.www IN CNAME www.example.com.le.example.com. _acme-challenge.sslserver IN CNAME sslserver.example.com.le.example.com.

You should now be ready to run dehydrated for the first time. After verifying that your setup works, remember to remove the CA line from the dehydrated config file. Feel free to leave feedback on this guide - I wrote it up mostly from memory - so I may well have missed a step.

Changes: