I think I've figured it out. I've gotten DoH requests to work using the Pi-hole's mDNS name and IP address, and my iPhone no longer has the security warning. I'll make a tutorial here on what I did so others can do the same if they want.
-
Install this DoH proxy. I put it in
/usr/local/bin
and made sure it was owned by root (sudo chown root:root
). The DNScrypt people also have a proxy for the DNScrypt protocol, but I didn't bother because as far as I know none of my devices support it. -
Make a new user to run the proxy as so it doesn't have to run as root.
sudo useradd -s /usr/sbin/nologin -r -M doh-proxy
-
Make a
systemd
service to keep it running. As root, put the following in/etc/systemd/system/doh-proxy.service
[Unit]
Description=DNS over HTTPS server proxy
After=syslog.target network-online.target
[Service]
Type=simple
User=doh-proxy
ExecStart=/usr/local/bin/doh-proxy -u 127.0.0.1:53
Restart=on-failure
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target
- Start the
systemd
service
sudo systemctl enable doh-proxy
sudo systemctl start doh-proxy
sudo systemctl status doh-proxy
This will start doh-proxy
listening on localhost port 3000. It will convert these requests to standard DNS queries which it will then go to Pi-hole to resolve using the usual port 53.
- Issue a certificate to be used with DoH. This tutorial is good. Make sure to add your Pi-hole's static IP address to the
.ext
file when creating the certificate (the stuff underalt_names
). Transfer the certificates to your Pi-hole machine somehow (scp
is good). Set your devices to trust this certificate (and keep the certificate key safe!). Any devices that don't trust it shouldn't be able to use DoH (that's the whole point).
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = pihole.local
IP.1 = 192.168.xxx.xxx
-
Prepare the certificate files for
lighttpd
. Concatenate the key and certificates into one file usingcat pihole.crt pihole.key > pihole.pem
. It's a good idea to make all these files owned and only readable by root:sudo chown root:root pihole.*
,sudo chmod 600 pihole.*
-
We now need to modify Pi-hole's web serve (
lighttpd
) to use HTTPS and to forward requests to the standard/dns-request
URL todoh-proxy
. Pi-hole allows you to do this by editing/etc/lighttpd/external.conf
. Make sure to enter the location you decided to put your.pem
file.
# Load necessary lighttpd modules
server.modules += ("mod_openssl", "mod_proxy")
# Forward DoH DNS queries to doh-proxy
proxy.server = ("/dns-query" => ( (
"host" => "127.0.0.1",
"port" => 3000
) ) )
$HTTP["remoteip"] != "127.0.0.1" {
# Ensure the Pi-hole Block Page knows that this is not a blocked domain
setenv.add-environment = ("fqdn" => "true")
# Enable the SSL engine when using port 443
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/path/to/your/pihole.pem"
ssl.honor-cipher-order = "enable"
ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
}
# Redirect HTTP to HTTPS
$HTTP["scheme"] == "http" {
$HTTP["host"] =~ ".*" {
url.redirect = (".*" => "https://%0$0")
}
}
}
-
Restart
lighttpd
.
sudo systemctl restart lighttpd
-
Check that it's working (use Pi-hole's IP or hostname). This tool is very useful for testing all the different DNS protocols.
dnslookup example.com https://pihole.local/dns-query
-
(optional)
lighttpd
will sometimes log the URLs of the queries, which includes the base64 representation of the cleartext DNS query in the URL's parameters. If you don't want this, the best way seems to be to symlink the log file to/dev/null
so that the logs are never saved. Of course, you then won't have any logs of access to the admin panel, if you care.
sudo ln -sf /dev/null /var/log/lighttpd/access.log
Now, DoH queries will take the following path.
[/dns-query, HTTPS port 443] -> lighttpd -> [HTTP port 3000] -> doh-proxy -> [DNS port 53] -> Pi-hole -> [cache or DNS port 53]
You can follow this tutorial to add another proxy to Pi-hole's outgoing requests so that they are either over HTTPS as well, or to do recursive lookups. Using unbound
for this would get quite the chain of stuff going on.
[/dns-query, HTTPS port 443] -> lighttpd -> [HTTP port 3000] -> doh-proxy -> [DNS port 53] -> Pi-hole -> [cache or DNS port 5335] -> unbound -> [DNS port 53]