From e6b404f981cf09d4d016a2d14b8e4851ecbf8cec Mon Sep 17 00:00:00 2001 From: bryan Date: Sat, 9 Feb 2019 17:54:39 -0500 Subject: [PATCH] POST-Autorenewing wildcard LetsEncrypt certificates on Namecheap using certbot + acme-dns --- ...ic-certbot-namecheap-acme-dns-6b4a41dd.png | Bin 0 -> 8156 bytes ...09-automatic-certbot-namecheap-acme-dns.md | 178 ++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 blog.bryanroessler.com/_posts/2019_02_09-automatic-certbot-namecheap-acme-dns.assets/2019_02_09-automatic-certbot-namecheap-acme-dns-6b4a41dd.png create mode 100644 blog.bryanroessler.com/_posts/2019_02_09-automatic-certbot-namecheap-acme-dns.md diff --git a/blog.bryanroessler.com/_posts/2019_02_09-automatic-certbot-namecheap-acme-dns.assets/2019_02_09-automatic-certbot-namecheap-acme-dns-6b4a41dd.png b/blog.bryanroessler.com/_posts/2019_02_09-automatic-certbot-namecheap-acme-dns.assets/2019_02_09-automatic-certbot-namecheap-acme-dns-6b4a41dd.png new file mode 100644 index 0000000000000000000000000000000000000000..c00b283646c49b74376b325ee51f4b094e43b36b GIT binary patch literal 8156 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 and your server's IP address (yes the naming scheme is dumb, but leave "acme.", "ns1.acme.", etc): + +``` +#/etc/acme-dns/config.cfg +[general] +# DNS interface +listen = ":53" +protocol = "udp" +# domain name to serve the requests off of +domain = "acme..com" +# zone name server +nsname = "ns1.acme..com" +# admin email address, where @ is substituted with . +nsadmin = "admin..com" +# predefined records served in addition to the TXT +records = [ + "acme..com. A 176.223.132.72", + "ns1.acme..com. A 176.223.132.72", + "acme..com. NS ns1.acme..com.", +] +debug = false + +[database] +engine = "sqlite3" +connection = "/var/lib/acme-dns/acme-dns.db" + +[api] +api_domain = "" +ip = "127.0.0.1" +disable_registration = false +autocert_port = "80" +port = "8081" +tls = "none" +corsorigins = [ + "*" +] +use_header = false +header_name = "X-Forwarded-For" + +[logconfig] +loglevel = "debug" +logtype = "stdout" +logformat = "text" +``` +A short explanation: you are configuring acme-dns to listen to DNS requests (from certbot via Namecheap) globally on the standard DNS port 53 and configuring the HTTP port for certbot to talk to acme-dns on port 8081 (since you are probably running something way cooler on port 8080). +2. Since certbot has to traverse Namecheap to perform the challenge, we will need to open port 53 in the firewall (traffic on port 8081 is handled on the localhost, so we don't need to open that port): `sudo ufw allow 53/udp && sudo ufw reload` + + +3. Let's start the acme-dns program automatically on startup. Create: + +``` +# /etc/systemd/system/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 +``` + +Note (Ubuntu 18.04+): you can security-harden the above script on distros running newer kernels by executing as a non-privileged user in conjunction with the `AmbientCapabilities=CAP_NET_BIND_SERVICE` line. However, older kernels will cause systemd to choke on that line during service startup. If you choose to execute as a non-root user make sure to chown the `/etc/acme-dns/` and `/var/lib/acme-dns` directories! + + 4. Enable the service to run on startup and run it now: `sudo systemctl daemon-reload && sudo systemctl enable --now acme-dns.service` + +Great, now you've got a DNS authentication server that can respond to ACME challenges! You just saved yourself $50/month! + +### Initial Namecheap configuration + +You will need to add two DNS records: + +1. an NS record for acme..com pointing to ns1.auth.\.com +2. a record for ns1.auth..com pointing to the public IP address of your host + +Example: ![](2019_02_09-automatic-certbot-namecheap-acme-dns.assets/2019_02_09-automatic-certbot-namecheap-acme-dns-6b4a41dd.png) + + +### Configuring the Certbot auth hook + +Now you just need to get certbot and acme-dns to work together! + +1. Install python2 requests: `sudo apt-get install python-requests` +2. Acquire the acme-dns certbot hook file: `sudo wget -O /etc/letsencrypt/acme-dns-auth.py https://raw.githubusercontent.com/joohoi/acme-dns-certbot-joohoi/master/acme-dns-auth.py` +3. Configure the program you just downloaded: `sudo nano /etc/letsencrypt/acme-dns-auth.py` and change `ACMEDNS_URL = "http://localhost:8081"` + +### Run Certbot + +You will need to run certbot manually one time in order to be able to run `certbot renew` in the future to handle the certificate renewals manually. If you've followed the rest of this tutorial, go ahead and run certbot to acquire your certs: + +`sudo certbot certonly -d "*..com" -d ".com" --agree-tos --manual-public-ip-logging-ok --server https://acme-v02.api.letsencrypt.org/directory --preferred-challenges dns --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py --debug-challenges` + +When you run the command certbot will prompt you to add one more DNS CNAME record to your DNS host. + +Example: `_acme-challenge..com CNAME ch30791e-33f4-1af1-7db3-1ae95ecdde28.acme..com.` + +Create a new CNAME record named `\_acme-challenge` and give it a value of `ch30791e-33f4-1af1-7db3-1ae95ecdde28.acme..com.` + +Wait a few minutes and hit to complete the ACME challenge and receive your certificates! + +### Automation + +The step I'm sure you've been waiting for. + +1. Create the certbot-renew.service (if you are using Apache in lieu of nginx, substitute "nginx" with "httpd"): + +``` +#/etc/systemd/system/certbot-renew.service +[Unit] +Description=Certbot Renewal + +[Service] +ExecStart=/usr/bin/certbot renew --post-hook "systemctl restart nginx" +``` +2. Create the associated timer file to run the renewal weekly: + +``` +#/etc/systemd/system/certbot-renew.timer +[Unit] +Description=Timer for Certbot Renewal + +[Timer] +OnBootSec=300 +OnUnitActiveSec=1w + +[Install] +WantedBy=multi-user.target +``` +3. Enable the timer: `sudo systemctl enable certbot-renew.timer` + +### What the hell is going on here? + +A wild goose chase: + +1. LetsEncrypt first asks your .com domain for the TXT record at \_acme-challenge.example.com to complete the challenge +2. The Namecheap DNS server responds with a CNAME record that points to ch30791e-33f4-1af1-7db3-1ae95ecdde28.acme..com, so LetsEncrypt goes there instead +3. The authoritative DNS server for \*.acme..com is ns1.acme..com, which points at your server IP (running acme-dns) +4. LetsEncrypt can finally ask ns1.acme.example.com what is the TXT record for ch30791e-33f4-1af1-7db3-1ae95ecdde28.acme..com and acme-dns will answer that question + +### Additional Considerations +On a critical server it may be a good idea to start and stop acme-dns (and open and close port 53) alongside certbot execution. This can be handled fairly trivially with systemd `Requires=`, but I'll leave that up to you! + +### Conclusions +Congratulations! You have successfully enabled automatic LetsEncrypt site certificate renewal on a finicky DNS host provider!