Renewing LetsEncrypt wildcard SSL certificate with ACME-DNS

Recently my widlcard SSL certificate from Let’s Encrypt expired and I renewed the certificates manually. This involved running certbot locally and completing the dns challenges which involves setting up TXT records in your DNS records. Though this isn’t a big task to be done every 3 months, I think it would be great to be able to automate it. Also Namecheap’s API access isn’t cheap 🙂

This is where I came across acme-dns. As the github page describes it, acme-dns is a simplified DNS server with a RESTful HTTP API to provide a simple way to automate ACME DNS challenges.

Here is the github repo: https://github.com/joohoi/acme-dns

This is how I got it set up and working on Ubuntu 1804.

Install Go
wget https://golang.org/dl/go1.14.6.linux-amd64.tar.gz
tar xzvf go1.14.6.linux-amd64.tar.gz
sudo mv go /usr/local/go
export PATH=$PATH:/usr/local/go/bin

Install acme-dns
git clone https://github.com/joohoi/acme-dns
cd acme-dns
export GOPATH=/tmp/acme-dns
go build
sudo mv acme-dns /usr/local/bin

Edit config.cfg

[general]
# DNS interface. Note that systemd-resolved may reserve port 53 on 127.0.0.53
# In this case acme-dns will error out and you will need to define the listening interface
# for example: listen = "127.0.0.1:53"
# As systemd-resolved had port 53 reserved, I used 127.0.0.1:53 initially
# which resulted in acme.myawesomesite.com not returning the correct A record
# finally had to use the public ip (10.10.10.10 for eg.)
listen = "10.10.10.10:53"
# protocol, "both", "both4", "both6", "udp", "udp4", "udp6" or "tcp", "tcp4", "tcp6"
protocol = "udp"
# domain name to serve the requests off of
domain = "acme.myawesomesite.com"
# zone name server
nsname = "ns1.acme.myawesomesite.com"
# admin email address, where @ is substituted with .
nsadmin = "admin.myawesomesite.com"
# predefined records served in addition to the TXT
records = [
    # domain pointing to the public IP of your acme-dns server
    "acme.myawesomesite.com. A 116.203.210.43",
    "ns1.acme.myawesomesite.com. A 116.203.210.43",
    # specify that auth.example.org will resolve any *.auth.example.org records
    "acme.myawesomesite.com. NS ns1.acme.myawesomesite.com.",
]
# debug messages from CORS etc
debug = false

[database]
# Database engine to use, sqlite3 or postgres
engine = "sqlite3"
# Connection string, filename for sqlite3 and postgres://$username:$password@$host/$db_name for postgres
# Please note that the default Docker image uses path /var/lib/acme-dns/acme-dns.db for sqlite3
connection = "/var/lib/acme-dns/acme-dns.db"
# connection = "postgres://user:password@localhost/acmedns_db"

[api]
# listen ip eg. 127.0.0.1
# 0.0.0.0 so it can be connect over the network
ip = "0.0.0.0"
# disable registration endpoint
disable_registration = false
# listen port, eg. 443 for default HTTPS
autocert_port = "80"
port = "8081"
# possible values: "letsencrypt", "letsencryptstaging", "cert", "none"
tls = "none"
# only used if tls = "cert"
#tls_cert_praivkey = "/etc/tls/example.org/privkey.pem"
#tls_cert_fullchain = "/etc/tls/example.org/fullchain.pem"
# only used if tls = "letsencrypt"
#aacme_cache_dir = "api-certs"
# CORS AllowOrigins, wildcards can be used
corsorigins = [
    "*"
]
# use HTTP header to get the client ip
use_header = false
# header name to pull the ip address / list of ip addresses from
header_name = "X-Forwarded-For"

[logconfig]
# logging level: "error", "warning", "info" or "debug"
loglevel = "debug"
# possible values: stdout, TODO file & integrations
logtype = "stdout"
# file path for logfile TODO
# logfile = "./acme-dns.log"
# format, either "json" or "text"
logformat = "text"

sudo mkdir /etc/acme-dns/ /var/lib/acme-dns
sudo cp config.cfg /etc/acme-dns/.
sudo ufw allow 53/udp && sudo ufw reload

Enable acme-dns service

[Unit]
Description=Limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely
After=network.target

[Service]
ExecStart=/usr/local/bin/acme-dns
Restart=on-failure

[Install]
WantedBy=multi-user.target

Enable and start acme-dns service

sudo systemctl daemon-reload

sudo systemctl enable --now acme-dns.service

Add DNS records in Namecheap

NS recordacmens1.acme.myawesomesite.com.Automatic
A recordns1.acme10.10.10.10Automatic

Test acme-dns service

curl -X POST http://localhost:8081/register

{"username":"3b68ce3f-133b-4f4d-94da-2bfec7457676","password":"u7Fo2aeEYKbceAVHQqcPb_v30d329CQHH-8FHmni","fulldomain":"fc31d598-4138-49b3-bb26-505dd17dab88.acme.myawesomesite.com","subdomain":"fc31d598-4138-49b3-bb26-505dd17dab88","allowfrom":[]}

curl -X POST \
  -H "X-Api-User: 3b68ce3f-133b-4f4d-94da-2bfec7457676" \
  -H "X-Api-Key: u7Fo2aeEYKbceAVHQqcPb_v30d329CQHH-8FHmni" \
  -d '{"subdomain": "fc31d598-4138-49b3-bb26-505dd17dab88", "txt": "_9_validation_token_received_from_the_ca_9_"}' \
  http://localhost:8081/update

dig -t txt @acme.myawesomesite.com fc31d598-4138-49b3-bb26-505dd17dab88.acme.myawesomesite.com
>> this will return _9_validation_token_received_from_the_ca_9_
dig acme.myawesomesite.com - This will return the ip address of myawesomesite.com

Install certbot auth hook

Python-requests - sudo apt-get install python-requests
Certbot client hook for acme-dns -
sudo wget -O /etc/letsencrypt/acme-dns-auth.py https://raw.githubusercontent.com/joohoi/acme-dns-certbot-joohoi/master/acme-dns-auth.py
sudo chmod 700 /etc/letsencrypt/acme-dns-auth.py

Change the ACMEDNS_URL setting in /etc/letsencrypt/acme-dns-auth.py

ACMEDNS_URL = "http://localhost:8081"

Automate certificate renewal

#/etc/systemd/system/certbot-renew.service
[Unit]
Description=Certbot Renewal

[Service]
ExecStart=/usr/bin/certbot renew --post-hook "systemctl restart apache2"
Create timer file in /etc/systemd/system
#/etc/systemd/system/certbot-renew.timer
[Unit]
Description=Timer for Certbot Renewal

[Timer]
OnBootSec=300
OnUnitActiveSec=1w

[Install]
WantedBy=multi-user.target

sudo systemctl enable certbot-renew.timer # Enable timer

Run certbot once so that it is set up for renewals.

sudo certbot certonly --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py \
--preferred-challenges dns --debug-challenges \
-d myawesomesite.com -d *.myawesomesite.com

This will ask you to create DNS CNAME records.

I hope this helps you set up your own DNS service for auto renewal of wildcard SSL certificates.

Resources I used to do configuration and set up:

https://golang.org/doc/install
https://github.com/joohoi/acme-dns
https://github.com/joohoi/acme-dns-certbot-joohoi

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s