Pi-Hole & Let’s Encrypt (2024)

Last modified date

Comments: 4

Since writing my original instructions for using a Let’s Encrypt (LE) SSL certificate to secure the Lighttpd web server used by Pi-Hole back in 2020, lots has changed. A double disk drop-out in my server (argh!) meant I needed to build a new Pi-Hole VM, so I took the chance to move to the current Ubuntu LTS (22.04).

I’m still using the acme.sh utility with the DNS-01 challenge method for getting the certificate to avoid having to expose anything to the Internet. I started with a fresh Ubuntu install which had been updated as usual with apt-get update && apt-get upgrade.

As appropriate, replace pihole.mylab.domain with the FQDN for your server. This guide also assumes you’ve already installed Pi-Hole – if not, see here.

Step One

The first tasks are to switch into sudo, install acme.sh and to create a sub-folder in the lighttpd folder to move the certificates to.

sudo -i
curl https://get.acme.sh | sh -s email=me@mydomain.com
mkdir -p /etc/lighttpd/certs/pihole.mylab.domain/

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. To find your CF information, see this post.

acme.sh has also moved to using ZeroSSL by default for new installations (see here), so we need to use the –server parameter to command to use LE. Once acme.sh issues a certificate it will remain with the issuing CA.

export CF_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export CF_Account_ID="xxxxxxxxxxxxx"
export CF_Zone_ID="xxxxxxxxxxxxx"
./acme.sh --issue --dns dns_cf -d pihole.mylab.domain --server letsencrypt 

If successful, the four SSL certificate files should be in /root/.acme.sh/pihole.mylab.domain_ecc/

  • The certificate will be pihole.mylab.domain.cer
  • The certificate private key will be pihole.mylab.domain.key
  • The intermediate certificate will be ca.cer
  • The full chain certificate will be fullchain.cer

Step Four

Previously we needed a custom script / hook to deploy the certificate but we can now use the standard acme.sh install method to copy the files. acme.sh will also create a crontab job to automate the renewal every 60 days. Prior to v1.4.53, lighttpd used to need a combined .pem file so the script had to concatenate two files but that is not needed anymore – we only need the private key and the full chain certificate. The last parameter is the command to reload lighttpd once the new certificate is deployed.

acme.sh --install-cert -d pihole.mylab.domain --key-file /etc/lighttpd/certs/pihole.mylab.domain/privkey.pem --fullchain-file /etc/lighttpd/certs/pihole.mylab.domain/fullchain.pem --reloadcmd "service lighttpd force-reload"

Step Five

We can now enable SSL in lighttpd. The old method of creating an external.conf file has changed, as lighttpd expects any configuration files to be in /etc/lighttpd/conf-enabled (either as a file or a symlink from another folder such as conf-availble). The file / symlink can be placed in conf-enabled manually, or using the lighty-enable-mod command (with lighty-disable-mod to go in the other direction).

Some of the parameters in the old configuration file are also deprecated and can be removed. Since lighttpd 1.4.68, it defaults to using strong ciphers and changing this is not recommended. SSL v2/v3 and TLS v1/1.1 are all disabled default.

nano /etc/lighttpd/conf-available/99-pihole-ssl.conf

This is my configuration file:

#Enable the SSL module
server.modules += ( "mod_openssl" )
#Update the value below to your correct domain
var.fqdn = "pihole.mylab.domain"
#Ensure the Pi-hole Block Page knows that this is not a blocked domain
setenv.add-environment = ("fqdn" => "true")

$SERVER["socket"] == ":443" {
    ssl.engine  = "enable" 
    ssl.pemfile = "/etc/lighttpd/certs/" + fqdn + "/fullchain.pem" 
    ssl.privkey = "/etc/lighttpd/certs/" + fqdn + "/privkey.pem"
    # Require TLS 1.3
    ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.3")
}

$HTTP["host"] == fqdn {
    # Set redirect code for any redirects
    url.redirect-code = 308
    # Redirect all http to https
    $HTTP["scheme"] == "http" {
        url.redirect = ("" => "https://" + fqdn + "${url.path}${qsa}")
    # Redirect root to admin
    } else $HTTP["url"] == "/" {
        url.redirect = ("" => "/admin/")
    }
}

Once the configuration file is saved, it should show up as available when lighty-enable-mod is run and thus can be enabled:

lighty-enable-mod

The last step should be to restart lighttpd now the certificate files are in the right folder and the configuration updated.

service lighttpd force-reload

All being well, your Pi-hole web UI should now be available at https://pihole.mylab.domain.

Troubleshooting

I have read posts on the Pi-Hole Discourse that the openssl module for lighttpd is not part of the standard installation. Testing on a fresh Ubuntu install and installing lighttpd manually via sudo apt install lighttpd installed the SSL module.

If I let the Pi-Hole installer install lighttpd, the SSL module wasn’t present.

To check if the SSL module is present, run:

lighty-enable-mod

Shows the ssl module is available to be enabled

If it is missing, the command to install is:

sudo apt install lighttpd-mod-openssl

Chris

4 Responses

  1. Hi Chris,
    an you clarify a couple of points?

    In your troubleshooting section you make mention of ssl module not being available. This was the case with my install so I added it. After installing it do I need to perform “lighty-enable-mod” and actually enable the ssl module before performing your revised procedure?

    Also, in step 5 performing “lighty-enable-mod” does show pihole-ssl as being disabled, as your pic. Should I then enable pihole ssl (and ssl?) modules at this point?

    thanks for your support.

  2. Hi,

    On my live pi-hole VM, lighty-enable-mod does show the ssl module as installed but disabled. However, I think that the first line in my custom pihole-ssl file (server.modules += ( “mod_openssl” ) enables it anyway.

    Sorry, I don’t think my note on enabling pihole-ssl is very clear, so I’ll update it when I get time. Once you’ve created the file in conf-available, it will need enabling using lighty-enable-mod. That should create the symlink in conf-enabled and lighty-enable-mod should then show it as enabled.

    Hope this helps!

  3. Fantastic article.

    One note:

    In the acme.sh install command, you have the certificate private key file specified as:
    key.pem

    and in 99-pihole-ssl.conf you have the certificate private key file specified as:
    privkey.pem

    This file name mismatch causes an error when the pihole-ssl module is loaded, which in turn causes lighttpd to fail to start.

    Reconciling the file names (in either place) fixes the issue. Thanks!

    • That’s a great spot, thank you. I’ve updated the acme.sh line to use privkey.pem like the .conf file.

      Apologies for not replying sooner, I e-mail notifications for activity were broken for a while.

      Thanks again!
      Chris.

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.