{"id":15148319,"url":"https://github.com/brumka/pihole-dot","last_synced_at":"2025-07-02T04:31:45.310Z","repository":{"id":54913306,"uuid":"355597514","full_name":"brumka/PiHole-DOT","owner":"brumka","description":"Building Pi-Hole with DNS over TLS (DOT) for home network","archived":false,"fork":false,"pushed_at":"2024-08-15T19:04:26.000Z","size":101,"stargazers_count":36,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-06T21:42:50.554Z","etag":null,"topics":["dns","dns-over-tls","dns-resolver","dns-server","dot","nginx","pihole","rdns","resolver","unbound","unbound-dns"],"latest_commit_sha":null,"homepage":"https://github.com/brumka","language":null,"has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brumka.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-07T15:36:54.000Z","updated_at":"2025-03-14T04:52:52.000Z","dependencies_parsed_at":"2025-02-11T23:32:21.052Z","dependency_job_id":null,"html_url":"https://github.com/brumka/PiHole-DOT","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/brumka/PiHole-DOT","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brumka%2FPiHole-DOT","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brumka%2FPiHole-DOT/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brumka%2FPiHole-DOT/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brumka%2FPiHole-DOT/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brumka","download_url":"https://codeload.github.com/brumka/PiHole-DOT/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brumka%2FPiHole-DOT/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263074670,"owners_count":23409802,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dns","dns-over-tls","dns-resolver","dns-server","dot","nginx","pihole","rdns","resolver","unbound","unbound-dns"],"created_at":"2024-09-26T13:02:50.439Z","updated_at":"2025-07-02T04:31:45.289Z","avatar_url":"https://github.com/brumka.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tutorial\n## Configuring PiHole as DNS-over-TLS recursive DNS resolver\n\nUsing [PiHole](https://pi-hole.net/) is a  popular way to filter out ads, malware, and trackers.  It is easy to install and has excellent UI.  PiHole comes with the built in [dnsmasq](https://en.wikipedia.org/wiki/Dnsmasq) DNS resolver as well as the [lighttpd](https://en.wikipedia.org/wiki/Lighttpd) web server.\n\nFor quite a few years, PiHole been doing great job for me.  I first installed it, just like many, on RasberryPi 3+ SBC, but later switched to [AtomicPI](https://ameridroid.com/products/atomic-pi) to get a full gigabit NIC and to avoid the imminent failure or RPi's SD card.  AtomicPI comes with the on-board eMMC storage, which is way faster and, despite its small size, still is sufficient for my purpose.  I could now turn on the logging without worrying about file I/O killing the SD card.\n\nOver the past few years we start seeing [DNS-over-TLS](https://en.wikipedia.org/wiki/DNS_over_TLS) (aka \"DOT\") gaining more popularity.  Suffice to say that  Android phones now come with so-called `\"secure DNS\"` enabled by default, which is a mere DOT.\n\nWith the advent of DOT and having mostly Android devices in my home network, I started noticing that PiHole became not up to its task  due to the fact that my devices were using the default Google's DOT (8.8.8.8).  Thus, the task was to make PiHole relevant again.  To my surprise, PiHole developers refuse to add DOT and DOH functionality.\n\nOf course, my knee-jerk reaction was to block Google's DNS on my Ubiquiti firewall, but that would be a lame workaround.\n\nLuckily, there are few hints out there on how to configure DOT.  Most notes are generic and require tinkering.  Some PiHole users are opting out for using Cloudflare's DOT/DOH gateway.  However, if I do not trust my ISP with collecting browsing history of my family (and guests), why would I trust Cloudflare, which is a for-profit company?\n\nAfter some research I decided to switch from the default lighttpd webserver to nginx and, why not to install a proper DNS server as well?  Switching to UNBOUND is easy.  As a result, final design idea could be illustrated as\n\n![image](https://raw.githubusercontent.com/brumka/PiHole-DOT/main/Arch_Diagram.png)\n\nThroughout this guide I am going to use `YourDNSServer` and, respectively, `YourDNSServerIPaddress` for the FQDN and the IP address of my DNS server\n\n\n\u003c/br\u003e\n\n##Installing UNBOUND\n====================\n\nThere is an easy-to-follow [guide on pihole site](https://docs.pi-hole.net/guides/dns/unbound/).  Once UNBOUND is installed you can validate it is correctly resolving by running\n\n```\n$ dig @YourDNSServer -p 5335 www.github.com +nocomment\n\n; \u003c\u003c\u003e\u003e DiG 9.11.3-1Ubuntu1.14-Ubuntu \u003c\u003c\u003e\u003e @YourDNSServer -p 5335 www.github.com +nocomment\n; (1 server found)\n;; global options: +cmd\n;www.github.com.                        IN      A\nwww.github.com.         2101    IN      CNAME   github.com.\ngithub.com.             60      IN      A       140.82.114.3\n;; Query time: 15 msec\n;; SERVER: YourDNSServer#5335(YourDNSServerIP)\n;; WHEN: Wed Apr 07 12:44:59 DST 2021\n;; MSG SIZE  rcvd: 73\n```\n\nNote the port number 5335, it is defined in `/etc/unbound/unbound.conf`.   Note that your new shiny DNS server also supports DNSSEC!  \nIt is still worth noting that your upstream resolution to upstream root DNS servers is still unencrypted as of the moment of this writing none of the root servers supports DOT.\n\n\u003c/br\u003e\n\n##Installing NGINX\n==================\n\nThe PiHole's [guide on installing NGINX](https://docs.pi-hole.net/guides/webserver/nginx/) is pretty straightforward as well.  Note, that for DOT we are going to install a  TLS certificate.  The obvious side benefit of having a cert would be ability to acces PiHole's UI via secured addess.  I used Les's Encrypt, which is quite simple to get installed using [these instructions](https://letsencrypt.org/getting-started/).\n\nNext we are going to follow the example of configuring the DNS-over-TLS gateway using the excellent [Mark Boddington's example](https://github.com/TuxInvader/nginx-dns/blob/master/examples/nginx-dot-to-dns-simple.conf).\n\nIn order for it to work we have to install the NGINX NJS module.  First in order to install it on my Ubuntu 18.04 I had to add NGINX's repository to the list of sources by adding the following lines to  `/etc/apt/sources.list`\n\n```\ndeb http://nginx.org/packages/mainline/ubuntu/ bionic nginx\ndeb-src http://nginx.org/packages/mainline/ubuntu/ bionic nginx\n```\n\n\u003cbr\u003eand only then running the install \n\n```\n$ sudo apt-get install nginx-module-njs\n```\n\n\nAs a result, my `/etc/nginx/dns.d/dot.conf` for NGINX DOT gateway now looks as following \n\n```\nstream {\n    # DNS logging.  This log file will show the DNS requests geting forwarded to UNBOUND\n    log_format  dns   '$remote_addr [$time_local] $protocol \"$dns_qname\"';\n    access_log /var/log/nginx/dns-access.log dns; \n\n    # Include the NJS module.  Get the file from  https://github.com/TuxInvader/nginx-dns/tree/master/njs.d\n    js_import /etc/nginx/njs.d/dns/dns.js;\n\n    # The $dns_qname variable can be populated by preread calls, and can be used for DNS routing\n    js_set $dns_qname dns.get_qname;\n\n    # DNS upstream pool.  We use the local PIHOLE instance at 127.0.0.1:53.  \n    # For bypassing PIHOLE and going directly to UNBOUD change the port to 5335\n    upstream dns {\n        zone dns 64k;\n        server 127.0.0.1:53;  \n    }\n\n    # DNS over TLS (DoT) gateway.  Proxy the traffic onto standard DNS\n    # Note that we're re-using the Let's Encrypt certificates\n    server {\n        listen 853 ssl;\n        ssl_certificate /etc/letsencrypt/live/YourDNSServer/fullchain.pem;\n        ssl_certificate_key /etc/letsencrypt/live/YourDNSServer/privkey.pem;\n        js_preread dns.preread_dns_request;\n        proxy_pass dns;\n    }\n}\n```\n\n\nAdded the call to load NJS modules and referenced the `/etc/nginx/dns.d/dot.conf` at the bottom of my current NGINX configuration file `/etc/nginx/nginx.conf`\n\n```\t\n\tuser  nginx;\n\tworker_processes  1;\n\t\n\terror_log  /var/log/nginx/error.log warn;\n\tpid        /var/run/nginx.pid;\n\t\n\t# This is where we load NJS modules \n\tload_module modules/ngx_http_js_module.so;\n\tload_module modules/ngx_stream_js_module.so;\n\t\n\tevents {\n\t    worker_connections  1024;\n\t}\n\t\n\thttp {\n\t    include       /etc/nginx/mime.types;\n\t    default_type  application/octet-stream;\n\t\n\t    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n\t                      '$status $body_bytes_sent \"$http_referer\" '\n\t                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\t\n\t    access_log  /var/log/nginx/access.log  main;\n\t\n\t    sendfile        on;\n\t    keepalive_timeout  65;\n\t\n\t    include /etc/nginx/conf.d/*.conf;\n\t}\n\t\n  \t# now let's add the dot.conf file\n\tinclude /etc/nginx/dns.d/dot.conf;  \n```\n\n\nAnd the resulting NGINX  `/etc/nginx/conf.d/default.conf` file is below.  Note the comments explaining few minor tweaks such as 302 redirects, TLS1.2, and H2 settings\n\n```\n        server {\n            listen 80 default_server;\n            listen [::]:80 default_server;\n\n            server_name YourDNSServer;\n\n            root /var/www/html;\n\n            autoindex off;\n\n            index pihole/index.php index.php index.html index.htm;\n\n            location / {\n                expires max;\n                try_files $uri $uri/ =404;\n            }\n\n            location ~ \\.php$ {\n                include fastcgi_params;\n                fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;\n                fastcgi_pass unix:/run/php/php7.2-fpm.sock;  # Ensure you use the correct PHP-FPM version here\n                fastcgi_param FQDN true;\n            }\n\n            location /*.js {\n                index pihole/index.js;\n            }\n\n            location /admin {\n                root /var/www/html;\n                index index.php index.html index.htm;\n            }\n\n            location ~ /\\.ht {\n                deny all;\n            }\n\n            # turn on the HTTP-to-HTTPS 302 redirect\n            if ($host = YourDNSServerIPaddress) {\n                return 302 http://YourDNSServer$request_uri;\n            }\n            return 302 https://YourDNSServer$request_uri;\n        }\n\n        #########################################\n        # now let's do the same same for port 443  \n        server {\n            # note the http2 verb added here\n            listen [::]:443 ssl ipv6only=on;  # Note that we enable H2 support, because why not \n            listen 443 ssl default_server;\n\t    http2 on;\n\n            server_name YourDNSServer;\n            \n            # I always like using the FQDN instead of an IP address\n            if ($host = YourDNSServerIPaddress) {\n                return 302 https://YourDNSServer$request_uri;\n            }\n            # Here comes our Let's Encrypt TLS certificate.  These lines were automagically added by Let's Encrypt CERTBOT\n            ssl_certificate /etc/letsencrypt/live/YourDNSServer/fullchain.pem; \n            ssl_certificate_key /etc/letsencrypt/live/YourDNSServer/privkey.pem;\n            include /etc/letsencrypt/options-ssl-nginx.conf;\n            ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; \n\n            # enable TLSv1.2 and TLSv1.3\n            ssl_protocols TLSv1.2 TLSv1.3;\n\n            root /var/www/html;\n\n            autoindex off;\n\n            index pihole/index.php index.php index.html index.htm;\n\n            location / {\n                expires max;\n                try_files $uri $uri/ =404;\n            }\n\n            location ~ \\.php$ {\n                include fastcgi_params;\n                fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;\n                fastcgi_pass unix:/run/php/php7.2-fpm.sock;  # again, ensure you have the correct PHP-FPM version\n                fastcgi_param FQDN true;\n            }\n\n            location /*.js {\n                index pihole/index.js;\n            }\n\n            location /admin {\n                root /var/www/html;\n                index index.php index.html index.htm;\n            }\n\n            location ~ /\\.ht {\n                deny all;\n            }\n        }\n```\n\u003cbr\u003e\u003cbr\u003e\n##Testing DNS-over-TLS\n=======================\n\nLet us start with installing `kdig` (enhanced DIG) \n\n```$ apt-get install knot-dnsutils```\n\u003cbr\u003e\u003cbr\u003e\nNow we can test our DOT server by executing the following command (P.S. note the ```+dnssec``` flag!)\n\u003cbr\u003e\n```\n$ kdig -d @YourDNSServerIPaddress  +tls-ca +tls-host=YourDNSServer +dnssec www.whitehouse.gov\n;; DEBUG: Querying for owner(www.whitehouse.gov.), class(1), type(1), server(YourDNSServer), port(853), protocol(TCP)\n;; DEBUG: TLS, imported 129 system certificates\n;; DEBUG: TLS, received certificate hierarchy:\n;; DEBUG:  #1, CN=YourDNSServer\n;; DEBUG:      SHA-256 PIN: p3oBivTcbQFriXHOAZC3isTsBc3adON/gKYEywQcJqA=\n;; DEBUG:  #2, C=US,O=Let's Encrypt,CN=R3\n;; DEBUG:      SHA-256 PIN: jQJTbIh0grw0/1TkHSumWb+Fs0Ggogr621gT3PvPKG0=\n;; DEBUG:  #3, C=US,O=Internet Security Research Group,CN=ISRG Root X1\n;; DEBUG:      SHA-256 PIN: C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=\n;; DEBUG: TLS, skipping certificate PIN check\n;; DEBUG: TLS, The certificate is trusted.\n;; TLS session (TLS1.2)-(ECDHE-SECP256R1)-(RSA-SHA256)-(AES-256-GCM)\n;; -\u003e\u003eHEADER\u003c\u003c- opcode: QUERY; status: NOERROR; id: 13706\n;; Flags: qr rd ra; QUERY: 1; ANSWER: 4; AUTHORITY: 0; ADDITIONAL: 1\n\n;; EDNS PSEUDOSECTION:\n;; Version: 0; flags: do; UDP size: 1472 B; ext-rcode: NOERROR\n\n;; QUESTION SECTION:\n;; www.whitehouse.gov.          IN      A\n\n;; ANSWER SECTION:\nwww.whitehouse.gov.     191     IN      CNAME   wildcard.whitehouse.gov.edgekey.net.\nwww.whitehouse.gov.     191     IN      RRSIG   CNAME 7 3 300 20210818033924 20210815023924 29448 whitehouse.gov. GenVnmmY/2EnUU1sSRJEl2yj4inwcAstHBw8PH7dzeGHaS9ZhmjkNiZTERc088X8O0Z50c1JDr3LVEr/10rk1cbJCgaQUx2cXb19HOQlrOP06zP7uTDLSAgDQEwQxuuXVNDDDwPauMnUr+OGGL7a/Btyakb1eNGjZUTQQnM1BhM=\nwildcard.whitehouse.gov.edgekey.net. 791        IN      CNAME   e4036.dscb.akamaiedge.net.\ne4036.dscb.akamaiedge.net.      0       IN      A       23.59.103.244\n\n;; Received 322 B\n;; Time 2021-08-15 20:24:14 EDT\n;; From @YourDNSServerIPaddress@853(TCP) in 39.1 ms\n```\n\nNote how it uses port `853` and `-d` switch provides us with the details of TLS handshake.\n\nAnd, finally, you can also see the list of DOT resolutions by accessing your ```/var/log/nginx/dns-access.log``` (that is the log file defined in ```dot.conf``` above)\n\n```\nuser@YourDNSServer:~$ sudo tail -f /var/log/nginx/dns-access.log\n192.168.1.XX [15/Aug/2021:12:36:08 -0400] TCP \"mobile-collector.newrelic.com\"\n192.168.1.XX [15/Aug/2021:12:38:01 -0400] TCP \"whoami.akamai.net\"\n192.168.1.XX [15/Aug/2021:12:40:27 -0400] TCP \"api16-normal-c-useast1a.tiktokv.com\"\n192.168.1.XX [15/Aug/2021:12:41:16 -0400] TCP \"v21-us-a.tiktokcdn.com\"\n...\n```\n\n\nNote that PI-Hole's UI is not going to list all the servers resolving through your DOT.  Instead, PIHOLE's dashboard and reports will show you all those DOT-resolved requests as performed by your DNS server itself.\n\nPLEASE, use [my_github_username AT gmail.com] for any corrections and/or comments\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrumka%2Fpihole-dot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrumka%2Fpihole-dot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrumka%2Fpihole-dot/lists"}