Enabling HTTPS for your Pi-hole Web Interface

I'll remember from now on :smiley:
Nice shield btw!

1 Like

Thanks for the tip.

I get the same error as Chrome.

So, what would be the next step? The certificate is working as if I go to the IP it works.........I inspect the certificate in Chrome and it is indeed that certificate with the CN and SAN....

I added the CA to CentOS 7 as well just in case but still nothing.

1 Like

Just a silly one but on the client, the domain/FQDN does resolve to the correct IP right ?

nslookup domain

Else I suspect your lighttpd config needs adjustment.
Post config here for us to have looksee ?

Yup

nslookup fqdn.of.the.pihole

Returns the IP of the Pi-Hole

What do I need to give you for the lighttpd config?

Thanks

This thread holds some config examples.
What did you alter in the lighttpd config to load the pem ?
Got below link from here as a nice reference:

https://ssl-config.mozilla.org/

I set it the way in the first post.

It doesnt make much sense if I go to the IP and it works...right?

BTW, should the owner be www-data or lighttpd?

Might want to fix that

Error log:

2019-10-13 16:25:51: (mod_openssl.c.1544) SSL: 1 error:1412E0E2:SSL routines:ssl_parse_clienthello_tlsext:clienthello tlsext
2019-10-13 16:25:51: (mod_openssl.c.1544) SSL: 1 error:1408A0E3:SSL routines:ssl3_get_client_hello:parse tlsext
2019-10-13 16:25:51: (mod_openssl.c.366) SSL: no certificate/private key for TLS server name phholeserver

external.conf Config

$HTTP["host"] == "phholeserver.domain.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/lighttpd/certs/phholeservercombined.pem"
    ssl.privkey = "/etc/lighttpd/certs/phholeserverkey.pem"
    ssl.ca-file = "/etc/lighttpd/certs/CA.pem"
    ssl.honor-cipher-order = "enable"
    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
    ssl.use-sslv2 = "disable"
    ssl.use-sslv3 = "disable"       
  }

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

$HTTP["host"] == "phholeserver" {
  # 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/phholeservercombined.pem"
    ssl.privkey = "/etc/lighttpd/certs/phholeserverkey.pem"
    ssl.ca-file = "/etc/lighttpd/certs/CA.pem"
    ssl.honor-cipher-order = "enable"
    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
    ssl.use-sslv2 = "disable"
    ssl.use-sslv3 = "disable"       
  }

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

lightttp.conf:

# Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# lighttpd config for Pi-hole
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.

###############################################################################
#     FILE AUTOMATICALLY OVERWRITTEN BY PI-HOLE INSTALL/UPDATE PROCEDURE.     #
# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
#                                                                             #
#              CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE:              #
#                         /etc/lighttpd/external.conf                         #
###############################################################################

server.modules = (
	"mod_access",
	"mod_auth",
	"mod_fastcgi",
	"mod_accesslog",
	"mod_expire",
	"mod_compress",
	"mod_redirect",
	"mod_setenv",
	"mod_rewrite",
	"mod_alias"
)

server.document-root        = "/var/www/html"
server.error-handler-404    = "/pihole/index.php"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "lighttpd"
server.groupname            = "lighttpd"
server.port                 = 80
accesslog.filename          = "/var/log/lighttpd/access.log"
accesslog.format            = "%{%s}t|%V|%r|%s|%b"


index-file.names            = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

compress.cache-dir          = "/var/cache/lighttpd/compress/"
compress.filetype           = ( "application/javascript", "text/css", "text/html", "text/plain" )

mimetype.assign   = ( ".png"  => "image/png",
                      ".jpg"  => "image/jpeg",
                      ".jpeg" => "image/jpeg",
                      ".html" => "text/html",
                      ".css" => "text/css; charset=utf-8",
                      ".js" => "application/javascript",
                      ".json" => "application/json",
                      ".txt"  => "text/plain",
                      ".svg"  => "image/svg+xml" )

# default listening port for IPv6 falls back to the IPv4 port
#include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
#include_shell "/usr/share/lighttpd/create-mime.assign.pl"
#include_shell "/usr/share/lighttpd/include-conf-enabled.pl"

fastcgi.server = ( ".php" =>
                   ( "localhost" =>
                     (
                       "socket" => "/tmp/php-fastcgi.socket",
                       "bin-path" => "/usr/bin/php-cgi"
                     )
                   )
                 )

# If the URL starts with /admin, it is the Web interface
$HTTP["url"] =~ "^/admin/" {
	  # Create a response header for debugging using curl -I
    setenv.add-response-header = (
        "X-Pi-hole" => "The Pi-hole Web interface is working!",
        "X-Frame-Options" => "DENY"
    )

    $HTTP["url"] =~ ".ttf$" {
        # Allow Block Page access to local fonts
        setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
    }
}

# Block . files from being served, such as .git, .github, .gitignore
$HTTP["url"] =~ "^/admin/\.(.*)" {
     url.access-deny = ("")
}

# Add user chosen options held in external file
# This uses include_shell instead of an include wildcard for compatibility
include_shell "cat external.conf 2>/dev/null"

Not entirely:

Am not sure if you need above one.
The pem file phholeservercombined.pem should already hold the private key plus the signed cert right ?

cat phholeservercombined.pem

EDIT: Just revisiting and saw above.
"phholeserver" is not a FQDN!

pi@noads:~ $ ps -O user -C lighttpd
  PID USER     S TTY          TIME COMMAND
  602 www-data S ?        00:17:01 /usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
 ps -O user -C lighttpd
   PID USER     S TTY          TIME COMMAND
 40108 lighttpd S ?        00:00:07 /usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf

Check shadow..... no www-data user.

I shouldnt need that according to OP but I tried it anyways....it gets ignored....

And yes, both the private key and certificate are there.

I added that to make sure that hostname access is allowed; I removed it and it still (obviously) does not work.

If I go to the IP, it presents the certificate correcty and it works. This means it actually loads the certificate and presents itself as safe

1 Like

Probably depends what distro your running.
I dont have a lighttpd user:

pi@noads:~ $ grep 'lighttpd\|www-data' /etc/passwd
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

But in general, the html root folder is owned by www-data so as to allow all sorts of web daemons to publish content from this folder and not only lighttpd:

pi@noads:~ $ stat /var/www/html/
[..]
Access: (0775/drwxrwxr-x)  Uid: (   33/www-data)   Gid: (   33/www-data)

Did you get below part:

You cant use a non FQDN with a cert ... I believe.
Why not use the config in the OP first to get things working without the ssl.privkey.
And use curl instead of a browser to check what cert is presented (CN & SAN + expire date etc):

curl -Iv https://192.168.1.90

curl -Iv https://phholeserver.domain.lan

EDIT: and live tail the lighttpd logs oc for errors.

lighttpd/1.4.45 (ssl) - a light and fast webserver
Build-Date: Jun 24 2019 22:58:56

using the default lighttpd.conf and the suggested settings for external.conf does not work for me. lighttpd doesnt start and will only start if i dont have those settings in external.conf
my lets encrypt cert is for a fqdn and i also changed pihole.example.com to my fqdn. error logs dont show anything from what I can see.

Just now I enabled https in both Apache and lighttpd and got both to work, using this howto for PiHole/lighttpd. There's one little issue (if you can even call it an issue) with PiHole/lighttpd.

Apache redirects both http://hostname and http://ip-address to https://hostname, using this howto only the first happens with PiHole/lighttpd.

Is there any way to modify this howto to have http://ip-address redirected to https://hostname as well?

1 Like

This works great. One things to note, if you have the site open in Chrome before you install the cert and restart, then change the URL to HTTPS, it will say the certificate is valid but also display "your connection is insecure."

To fix this, close Chrome and reopen it.

This works well, thanks for the write-up. However, I have a few concerns with the steps presented, one being very serious:

Insecure permissions on private keys.

Like Apache and nginx (and, I suspect, most other web servers), lighttpd reads cert and key files with root permissions, before dropping privileges after launch. Therefore, according to the lighttpd docs:

Be careful to keep your .pem file private! Lighttpd reads all pemfiles at startup, before dropping privileges. It is therefore best to make the pem file owned by root and readable by root only

Unnecessary use of tee

There's absolutely no reason to use tee in the cat command that creates combined.pem (which itself isn't necessary since lighttpd 1.4.53). A simple cat file1 file2 > file3 does the job just fine. This doesn't hurt anything, but just unnecessarily complicates the command.

Incorrect ca-file

The external.conf file has the ssl.ca-file pointing to fullchain.pem, which contains both the "leaf" certificate and the intermediate signing certificate. Again, this isn't likely to break anything, but it'll probably result in an unnecessary cert being sent for every TLS negotiation. The correct file would be chain.pem.

So, for anyone who might care, here's how I modified/updated these instructions to suit my system:


Like another poster up-thread, I use acme.sh for my ACME client for most purposes. It generally separates the action of issuing the cert and installing it, like this:
acme.sh --issue --dns dns_acmedns --dnssleep 5 -d pihole.yourdomain
acme.sh --install-cert -d pihole.yourdomain --cert-file /etc/ssl/certs/cert.pem --ca-file /etc/ssl/certs/ca.pem --key-file /etc/ssl/private/privkey.pem --reloadcmd "cat /etc/ssl/certs/cert.pem /etc/ssl/private/privkey.pem > /etc/ssl/private/combined.pem && systemctl restart lighttpd"

I'm placing the certs and key in /etc/ssl, which is the "default" location for them under Ubuntu. CentOS, IIRC, places them in /etc/pki/tls, other OSs may do otherwise. But it's lately being seen as a "best practice" to put your cert files where the OS would generally expect them to be, rather than directly using your ACME client's cert store.

Now, make combined.pem readable by root only:
chmod 600 /etc/ssl/private/combined.pem

Download DH parameters recommended by the Mozilla config generator:
curl https://ssl-config.mozilla.org/ffdhe2048.txt > /etc/ssl/certs/dhparam

Then, given these changes, the external.conf file looks like this:

$HTTP["host"] == "pihole.yourdomain" {
  # 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/ssl/private/combined.pem"
    ssl.ca-file =  "/etc/ssl/certs/ca.pem"

    # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
    ssl.dh-file               = "/etc/ssl/certs/dhparam"

    # intermediate configuration
    ssl.openssl.ssl-conf-cmd = ("Protocol" => "ALL, -SSLv2, -SSLv3, -TLSv1, -TLSv1.1")
    ssl.cipher-list           = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
    ssl.honor-cipher-order    = "disable"
}

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

Guys, I have a problem here. I followed the manual to install SSL on my pihole's lighttpd webserver. It was a struggle at first, but it worked after trying for a while. I have an end user ssl experience. Later after 1 week, I experience a weird problem when browsing with my pihole.
Some sites, not many, respond with a " Your connection is not private" message, saying there is no SSL connection to the site.....Now going back in the instructions on this site, there is an issue mentioned in case of a faulty SSL configuration at pihole. I thought ssl configuration is only for MY webserver, but it seems in the site here, I was mistaken.

........." Blindly enabling HTTPS for your Pi-hole Web Interface via Let’s Encrypt or a Self-Signed certificate causes issues such as:

** Browsing slowdowns on any site visited, as blocked content needed to time out (or load infinitely)*
** Web Browser errors, such as mismatched certificates*
** Operating system popups on macOS/iOS devices on every site containing blocked content*
......."
Well, I have the line :

$HTTP["host"] == "server.me" {

setenv.add-environment = ("fqdn" => "true")

"
in my external.conf

I confirmed my hostname is the same in this config for 'server.me'
And I don't understand why then most sites work perfect and only a few do not.
It is painful, but when I bypass the pihole and surf directly to the same server on the net, it works again. An example: https://go.skimresources.com/
Gives a certificate error. Is there any other explanation for this? Note, I have tried to add this domain to the whitelist of pihole. Doesn't work either which makes me feel desperated....

Can somebody help me here?
Thanks.

UPDATE:

I made a mistake in whitelisting. Weird, the site https://go.skimresources.com/
works now. I must have a problem with all go.(something) content , cause in my whitelist there ar emore starting like that. Maybe a single blocklist link ...let's see...

UPDATE:
found it, it was in this blocklist:

I the problem still occurs I will ask later. Sorry for any wasted time