acme.sh with lighttpd on pihole


# 20220718 – updated with some things I’ve learned since I wrote the original post.

I’ve got step-ca setup in my homelab for a private ACME server for certificate renewal automation. It’s been working fantastically well with Traefik in front of all my Docker containers. I’ve been meaning to automate the certificate renewal on my pihole and finally got around to doing it this week.

I’m going to assume you’ve already setup step-ca or some other acme server. This procedure could also be used for LetsEncrypt. You just wouldn’t use the –server switch when issuing the certificate.

username: latte
acme ca: acme.guammie.lan
pihole: pihole.guammie.lan

# Get the root CA certificates and trust them
scp [email protected]:.step/certs/*.crt ./
sudo mv *.crt /usr/local/share/ca-certificates
sudo update-ca-certificates

# Add user to www-data in preparation for webroot mode
sudo usermod -a -G www-data latte

# logoff for the group modification to take effect and validate
logoff
id latte

# Allow the user to restart lighttpd without having to reauthenticate
# this is necessary for the reloadcmd to work in the cron job
echo "latte ALL=(ALL) NOPASSWD: /usr/sbin/service lighttpd restart" | sudo tee -a /etc/sudoers.d/latte-nopasswd

# My pihole isn't setup for internal name resolution at the OS level
# adding an entry for the CA
sudo sh -c 'echo "192.168.1.10 acme.guammie.lan" >> /etc/hosts'

# Setup the directory structure for where acme.sh will download certificates to
sudo mkdir -p /etc/pki/certs /etc/pki/keys /etc/pki/fullchain
sudo chown -R latte:www-data /etc/pki

# Clone acme.sh
git clone https://github.com/acmesh-official/acme.sh.git

# I initially did this in standalone mode and needed socat... 
# don't know if it's necessary for webroot mode.  Never bothered to check.
sudo apt install socat
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/socat

# Getting ready to install acme.sh in /opt
sudo mkdir /opt/acme.sh
sudo chown -R latte:latte /opt/acme.sh

# Install acme.sh to /opt
cd acme.sh
./acme.sh --install --home /opt/acme.sh --config-home /opt/acme.sh --cert-home /opt/pki

# If you skip the logging out and back then everything still goes to ~/.acme.sh
# the installer even says to do so: 
# "OK, Close and reopen your terminal to start using acme.sh"
logoff
log back in (duh)

# Get the certificates
cd /opt/acme.sh 
./acme.sh --issue --webroot /var/www/html -d $HOSTNAME.guammie.lan --server https://acme.guammie.lan/acme/guammie/directory --days 7

# Install the certificates to the previously setup directories
./acme.sh --install-cert --domain $HOSTNAME.guammie.lan --cert-file /etc/pki/certs/$HOSTNAME.guammie.lan.cer --key-file /etc/pki/keys/$HOSTNAME.guammie.lan.key --fullchain-file /etc/pki/fullchain/$HOSTNAME.guammie.lan.crt --reloadcmd "sudo service lighttpd restart"

# Even though we haven't setup lighttpd to use the certificates yet, it's
# important to specify the reloadcmd because it becomes part of the renewal cron
# job that gets setup during install

# Validate everything looks correct
./acme.sh --info -d $HOSTNAME.guammie.lan

# I'm putting this here just to remember to do it periodically
# I should setup a cron job for it
./acme.sh --upgrade --auto-upgrade

# Now modify your /etc/lighttpd/external.conf file
sudo nano /etc/lighttpd/external.conf                                                                                                          

# File contents of external.conf
$HTTP["host"] == "pihole.guammie.lan" { 
  # 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/pki/fullchain/pihole.guammie.lan.crt"
    ssl.privkey = "/etc/pki/keys/pihole.guammie.lan.key"
    ssl.honor-cipher-order = "enable"
    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
    ssl.openssl.ssl-conf-cmd = ("Protocol" => "-TLSv1.1, -TLSv1, -SSLv3")
  }

  # Redirect HTTP to HTTPS
  $HTTP["scheme"] == "http" {
    $HTTP["host"] =~ ".*" {
      url.redirect = (".*" => "https://%0$0")
    }
  }
}

That’s it.


Leave a Reply

Your email address will not be published.