Pi-Hole & Let’s Encrypt

Update April 2024
I have a new article with instructions for installing on Ubuntu LTS 22.04 and all the things that have changed since writing this guide.
Original Instructions
The next server in my lab to sort out with a Let’s Encrypt (LE) trusted SSL is Pi-Hole. Pi-Hole does a good job of ad-blocking without needing to mess with anything on device endpoints (browser plugins etc). Version 5.0 which was released this week adds some new features, one of which I appreciate is the ability to add static DNS entries via GUI (it saves messing with text files).
Pi-Hole uses Lighttpd as the web server, so we need to provide the SSL files for that to use. Again I’m going to use the acme.sh utility with the DNS-01 challenge method for getting the certificate to avoid having to expose anything to the Internet.
For reference, my Pi-Hole server runs on a Ubuntu 16.04 LTS VM.
As we’ll need to move the certificate files around, I installed acme.sh as root so when the renewal script runs, it can access everything it needs. The authors of acme.sh don’t recommend running the script via sudo – see here.
Step One
Switch into sudo, install acme.sh and to create a sub-folder in the lighttpd folder to move the certificates to. acme.sh has also moved to using ZeroSSL by default for new installations, so we need the last command to use LE.
sudo -i curl https://get.acme.sh | sh mkdir -p /etc/lighttpd/certs/pihole.mylab.domain/ acme.sh --set-default-ca --server letsencrypt
Note: stay in sudo for now
Step Two
Generate a large DH file to improve security (see here for a full explanation!)
cd /etc/lighttpd/certs/pihole.mylab.domain openssl dhparam -out dhparam.pem -dsaparam 4096
Step Three
Obtain the certificate using acme.sh from LE with the DNS-01 challenge, so we need to provide the relevant Cloudflare IDs via the export command.
export CF_Token="sdfsdfsdfljlbjkljlkjsdfoiwje" export CF_Account_ID="xxxxxxxxxxxxx" export CF_Zone_ID="xxxxxxxxxxxxx" ./acme.sh --issue --dns dns_cf -d pihole.mylab.domain
If successful, the four SSL certificate files should be in /root/.acme.sh/pihole.mylab.domain/
Step Four
Enable SSL in lighttpd and configure it to use the new certificate
nano /etc/lighttpd/external.conf
Edit the configuration with the appropriate settings for your domain:
$HTTP["host"] == "pihole.mylab.domain" { # Ensure the Pi-hole Block Page knows that this is not a blocked domain setenv.add-environment = ("fqdn" => "true") # Enable the SSL engine with a LE cert, only for this specific host $SERVER["socket"] == ":443" { ssl.engine = "enable" ssl.pemfile = "/etc/lighttpd/certs/pihole.mylab.domain/ssl.pem" ssl.ca-file = "/etc/lighttpd/certs/pihole.mylab.domain/ca.cer" ssl.dh-file = "/etc/lighttpd/certs/pihole.mylab.domain/dhparam.pem" ssl.honor-cipher-order = "enable" ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH" ssl.use-compression = "disable" ssl.use-sslv2 = "disable" ssl.use-sslv3 = "disable" } # Redirect HTTP to HTTPS $HTTP["scheme"] == "http" { $HTTP["host"] =~ ".*" { url.redirect = (".*" => "https://%0$0") } } }
Step Five
Create the script which acme.sh will run as the post installation hook to move the files into the lighttpd folder. This script is based one on from Cyberciti.biz
nano /root/.acme.sh/pihole/hook.sh
#!/bin/bash dom="pihole.mylab.domain" #your Pihole FQDN dest="/etc/lighttpd/certs/pihole.mylab.domain" #lighttpd ssl folder created in step one croot="/root/.acme.sh/${dom}" #acme.sh root path for your domain ### NO edit below ### sslfile="${dest}/ssl.pem" #lighttpd .pem file path certfile="${croot}/${dom}.cer" #lighttpd certficate file path keyfile="${croot}/${dom}.key" #lighttpd key file path echo "Copying certificate" /bin/cat "${certfile}" "${keyfile}" > "${sslfile}" echo "Settings permissions" chown root:root /etc/lighttpd/certs/pihole.mylab.domain/ssl.pem chmod 400 /etc/lighttpd/certs/pihole.mylab.domain/ssl.pem echo "Restarting lighttpd service" /bin/systemctl restart lighttpd
Save / close the script, then make it executable:
chmod +x /root/.acme.sh/pihole/hook.sh
Step Six
Use acme.sh to install the certificate and run the hook.sh script we created to copy the file
acme.sh --installcert -d pihole.mylab.domain \ --capath /etc/lighttpd/certs/pihole.mylab.domain/ca.cer \ --reloadcmd '/root/.acme.sh/pihole/hook.sh'
Certificate Renewal
acme.sh will automatically a cron job to check & renew the certificate when required. You can check this is setup:
crontab -l
Hi,
I tried to use this for my pihole site but step 3 does not result in four SSL certificate files in /root/.acme.sh/pihole.mylab.domain/. Instead the certificates are located in /root/.acme.sh/pihole.mylab.domain_ecc/. Executing the command ./acme.sh –issue –dns dns_cf -d pihole.mylab.domain didnt work. Instead I used acme.sh –issue –dns dns_cf -d pihole.mylab.domain which did execute. I modified the rest of the install to cater for the certs being located in the pihole.mylab.domain_ecc and the final step resulted in certs being copied to pihole.mylab.domain folder but I still get vert errors in the browser. Can you help?
I’ve got an updated guide which uses new features in acme.sh (etc) to make things simpler – does that help?
https://cxi.org.uk/pi-hole-lets-encrypt-2024/
Thanks for you support. I’ll give it a shot and let you know.