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