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 Gowget 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-dnsgit 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 record | acme | ns1.acme.myawesomesite.com. | Automatic |
A record | ns1.acme | 10.10.10.10 | Automatic |
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
One thought on “Renewing LetsEncrypt wildcard SSL certificate with ACME-DNS”