LXC TOR + Pihole + Unbound + DNSSEC - Anonymous Recursive Secure DNS

Version 2.0 is Released & Improved

Latest Version dated 2/20/25

Feeling Pretty?

Alpha Versions are potentially more powerful, and certainly less tested

Prefer Cloudflare?

We’ve got you covered.

Premise


Our cute mascot for this post isn’t performing so cutely with caching, so I recommend this tool instead.. You can also spin up ZimaOS and then spin up a Chrome container, if you grow tired of clearing cache on your own browser every five minutes… Please note: Your browser MUST support SNI to pass SNI test!

What is it?

This script is a robust and open source bash tool designed for Linux (Proxmox, Homelab) users interested in maximizing their online privacy and security. It leverages tools like Pi-hole, Unbound, and Tor to create a private, secure, and ad-free internet browsing environment.

How Does it Work?

  • System Preparation: The script begins by updating your system to ensure all software is current, reducing potential vulnerabilities.
  • Dependency Installation: It installs necessary software including Pi-hole for ad-blocking, Unbound for DNS resolution, Tor for traffic anonymity, and others to manage network services.
  • DNS Configuration with Unbound:
    • Sets up Unbound as your local DNS server with DNSSEC validation, enhancing DNS security by ensuring the integrity and authenticity of DNS responses.
    • Configures Unbound to forward DNS queries through Tor for added privacy.
  • Pi-hole Integration:
    • Configures Pi-hole to filter out ads and potentially harmful domains, reducing exposure to tracking and improving browsing experience.
    • Pi-hole is set to use Unbound for DNS resolution.
  • Tor for Anonymity:
    • Establishes a Tor hidden service for Pi-hole, allowing secure access to your ad-blocking service.
    • All outgoing traffic, including DNS queries, is routed through Tor, effectively hiding your IP address from many, but not all, external services.
  • Network Integration and Discovery:
    • Uses Avahi for local network discovery, which helps in setting up peer-to-peer DNS services.
    • Automatically registers with and discovers other peers running similar setups, enhancing the distributed nature of DNS resolution.
  • iptables for Traffic Management:
    • Configures iptables to ensure all internet traffic goes through Tor, thus preventing any accidental IP leaks.
    • Rules are set to block all non-Tor traffic, ensuring privacy.

What Does it Do?

  • Enhances Privacy: By routing all traffic through Tor, it anonymizes your internet presence, making it nearly impossible for anyone to trace your online activities back to your real IP address.
  • Secures DNS: Provides secure and encrypted DNS queries, protecting against DNS hijacking and man-in-the-middle attacks.
  • Blocks Unwanted Content: Through Pi-hole, it offers a clean browsing experience by removing ads and blocking known malicious sites.
  • Peer Network: Creates or joins a network of similar systems for a distributed, resilient DNS infrastructure.

Use Cases:

  • Privacy Enthusiasts: Anyone looking to maintain their privacy online, especially in environments where internet freedom is restricted or monitored.
  • Security Professionals: Those needing to secure their network communications, particularly when dealing with sensitive information or working in high-risk environments.
  • Home Network Administrators: To provide a private, ad-free browsing experience for all devices on a home network with an additional layer of security.
  • Journalists, Whistleblowers, or Activists: Individuals who require anonymity for their online activities to protect their identity and work.
  • Public Wi-Fi Users: To safely browse the internet by masking your IP and securing DNS lookups when using potentially insecure networks.

This script is ideal for anyone concerned about digital privacy, looking to reduce online tracking, or needing a secure, anonymous internet connection.

Prerequisites:

Proxmox Installed on your dedicated homelab host

Get Started:

Create a new LXC container using the template Debian.

First you need to have the debian template loaded into your filesystem. Click on your filesystem → CT Templates → Templates → Debian

Now that your prerequisites are out of the way, click Create CT
image

Fill out your desired credentials, click next

I recommend the following network config, to start. Click next

ONCE INSIDE THE LXC’s SHELL -

apt install sudo

VERSION 2.0

Version 2.0 fixes recursive DNS. Tor mode is optional, but try enabling Tor mode (Brave supports Tor mode) to see your IP address get masked without a VPN.

There’s a good chance this contains redundancies, so sorry in advance if there’s any boiler plate going on here. There’s an old saying I learned in this business - if it ain’t broke…

sudo nano deploy1.sh

APPLE + V (paste code into nano file)
deploy1.sh

#!/bin/bash
set -e

echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"

echo "[+] Updating system..."
apt update && apt upgrade -y

echo "[+] Installing dependencies..."
apt install curl

# Install Pi-hole with option to skip OS check for compatibility
sudo PIHOLE_SKIP_OS_CHECK=true curl -sSL https://install.pi-hole.net | bash && apt install -y iptables-persistent unbound tor avahi-daemon avahi-utils jq

echo "[+] Configuring Unbound..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    interface: ::1
    access-control: 127.0.0.1 allow
    access-control: ::1 allow
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    forward-zone:
        name: "."
        forward-addr: 127.0.0.1@9053
EOL

# Add DNSSEC validation to Unbound from Script 2
echo "[+] Adding DNSSEC validation to Unbound..."
cat > /etc/unbound/unbound.conf.d/pi-hole.conf <<EOL
server:
    verbosity: 1
    interface: 0.0.0.0
    port: 5353  # Changed from default to prevent conflicts
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    access-control: 0.0.0.0/0 allow
    access-control: ::0/0 allow
    cache-max-ttl: 86400
    cache-min-ttl: 3600
    harden-dnssec-stripped: yes
    use-caps-for-id: no
    prefetch: yes
    num-threads: 2
    so-reuseport: yes
    msg-cache-size: 128m
    rrset-cache-size: 256m
    infra-cache-numhosts: 100000
    infra-cache-lame-size: 10k
    neg-cache-size: 4m
    do-not-query-localhost: no
    hide-identity: yes
    hide-version: yes
    qname-minimisation: yes
    harden-glue: yes
    harden-below-nxdomain: yes
    target-fetch-policy: "2 1 0 0 0"
    val-log-level: 1
    tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt"
EOL

echo "[+] Configuring Pi-hole..."
cat > /etc/dnsmasq.d/02-custom.conf <<EOL
server=127.0.0.1#5335
server=::1#5335
no-resolv
EOL
systemctl restart pihole-FTL unbound

echo "[+] Configuring Tor Hidden Service..."
mkdir -p /var/lib/tor/pihole
chown -R debian-tor:debian-tor /var/lib/tor/pihole
chmod 700 /var/lib/tor/pihole
cat > /etc/tor/torrc <<EOL
HiddenServiceDir /var/lib/tor/pihole/
HiddenServicePort 53 127.0.0.1:53
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 9053 127.0.0.1:9053
AutomapHostsOnResolve 1
DNSPort 127.0.0.1:9053
TransPort 9040
SocksPort 127.0.0.1:9050

# Force all traffic through Tor
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit
TransListenAddress 0.0.0.0
DNSListenAddress 0.0.0.0
EOL

echo "[+] Restarting Tor..."
systemctl restart tor

echo "[+] Enabling Avahi for local discovery..."
systemctl enable avahi-daemon
systemctl restart avahi-daemon

echo "[+] Waiting for Tor Hidden Service to be available..."
sleep 20
ONION_ADDR=$(cat /var/lib/tor/pihole/hostname)

echo "[+] Registering local and remote peers..."
PEER_FILE="/etc/pihole/nodes.conf"
avahi-browse -rt _pihole._tcp | grep "=" | awk '{print $6}' > $PEER_FILE
echo "tor://$ONION_ADDR" >> $PEER_FILE

while read -r NODE; do
    echo "Discovered node: $NODE"
    curl -s "http://$NODE/peers" >> $PEER_FILE || true
done < $PEER_FILE

echo "[+] Updating Unbound configuration..."
UNBOUND_CONF="/etc/unbound/unbound.conf"

# Ensure localhost queries are allowed
sed -i 's/do-not-query-localhost: yes/do-not-query-localhost: no/g' $UNBOUND_CONF

# Remove any existing forward zone configuration to prevent duplication
sed -i '/forward-zone:/,/^$/d' $UNBOUND_CONF

# Add new forward rules
cat >> $UNBOUND_CONF <<EOL
forward-zone:
    name: "."
    forward-addr: 127.0.0.1@9053
EOL

echo "[+] Restarting Unbound..."
systemctl restart unbound

echo "[+] Installing iptables-persistent..."
apt install -y iptables-persistent

echo "[+] Configuring iptables to force all traffic through Tor..."
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X

# Allow local traffic
iptables -A OUTPUT -o lo -j ACCEPT

# Allow Tor process to connect to the internet
iptables -A OUTPUT -m owner --uid-owner debian-tor -j ACCEPT

# Redirect DNS requests to Tor
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 9053
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 9053

# Redirect all traffic to Tor except local (127.0.0.1) and LAN (192.168.0.0/16)
iptables -t nat -A OUTPUT -d 127.0.0.1 -j RETURN
iptables -t nat -A OUTPUT -d 192.168.0.0/16 -j RETURN
iptables -t nat -A OUTPUT -p tcp --syn -j REDIRECT --to-ports 9040

# Drop all non-Tor traffic to prevent leaks
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -j REJECT

# Save iptables rules
iptables-save > /etc/iptables/rules.v4
echo "[+] iptables rules set!"

iptables -F
iptables -t nat -F
iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 9040
iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-port 9040
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-port 9053

iptables-save > /etc/iptables.rules

netfilter-persistent save
netfilter-persistent reload
systemctl enable netfilter-persistent

echo "[+] Script completed."

sleep 5

reboot
chmod +x deploy1.sh
sudo ./deploy1.sh

Now for PiHole default everything, except for upstream DNS you will select custom, then fill it out like so. YOU MUST DO THIS if you want recursive DNS!

DON’T FORGET TO WRITE DOWN YOUR PASSWORD!


… but if you do forget your password, you can later use command

pihole setpassword [pwd]

Yes to both IPV4 and IPV6, which again is simply your default:

Once complete, open the IPV4 in the URL bar of browser on your other computer. PLEASE NOTE: YOU MUST BE IN PRIVATE (INCOGNITO) MODE to guarantee operation. That’s easy, just right click Brave (Chrome) → New Private Window. In my case the URL (from the screenshot above) was 192.168.0.50/admin, but your might have chosen a different static IP address (again, from above)

You should now have DNSSEC, tor, and no DNS leaks, especially if your IPV4 address (Pihole Container) is the sole DNS provider for your client.
deploy2.sh

#!/bin/bash
set -e

echo "[+] Updating system..."
apt update && apt upgrade -y

echo "[+] Installing dependencies..."
apt install -y curl unbound tor

echo "[+] Configuring Unbound for DNS over Tor..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    do-not-query-localhost: no
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-large-queries: yes
    harden-referral-path: yes
    harden-short-bufsize: yes
    harden-algo-downgrade: yes
    harden-below-nxdomain: yes
    use-caps-for-id: no
    aggressive-nsec: yes
    qname-minimisation: yes
    val-clean-additional: yes
    prefetch: yes
    prefetch-key: yes
    so-rcvbuf: 8m            
    so-sndbuf: 8m
    num-threads: 4
    target-fetch-policy: "5 5 0 0 0"
    val-permissive-mode: no
    cache-min-ttl: 5
    cache-max-ttl: 86401
    serve-expired: no
    rrset-roundrobin: yes
    val-clean-additional: yes
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
#    tls-use-sni: yes

    # Route ALL Uncached Queries via Tor (Cloudflare through Tor)
    forward-zone:
        name: "."
#        forward-tls-upstream: yes
        forward-addr: 127.0.0.1@9053  # Force all queries through Tor's DNSPort
        forward-addr: ::1@9053 # Force all IPV6 queries through Tor's DNSPort

remote-control:
        control-enable: no
EOL

echo "[+] Configuring Tor for DNS resolution..."
cat > /etc/tor/torrc <<EOL
SocksPort 9050
DNSPort 9053
AutomapHostsOnResolve 1
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit

# Route Cloudflare's DoH and DoT requests via Tor
MapAddress 1.1.1.1 10.192.0.1
MapAddress 1.0.0.1 10.192.0.2
MapAddress 2606:4700:4700::1111 10.192.0.3
MapAddress 2606:4700:4700::1001 10.192.0.4
EOL

echo "[+] Restarting Tor and Unbound..."
systemctl restart tor unbound

echo "[+] Setting up permanent iptables rules for DNS..."
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5335
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 5335

echo "[+] Saving iptables rules..."
iptables-save > /etc/iptables/rules.v4
netfilter-persistent save
netfilter-persistent reload

echo "[+] Your DNS queries are now being routed through Tor for privacy."

sleep 5

reboot
chmod +x deploy2.sh
sudo ./deploy2.sh

UPDATE: As of the latest update of Pihole on 2/19/25, Local DNS is not always remembered from installation. You will need to manually copy and past the following after installation assuming you are an unfortunate victim of chance.

127.0.0.1#5335

Point Your Router’s DNS to Your Pihole

Now go to your router’s URL. This is usually 192.168.0.1 OR 192.168.1.1. Find your DNS settings and change your primary DNS to 192.168.0.50 as in our example (or whatever static IP you had chosen for your Pihole). You can create a second Pihole if you wish to have redundancy.

IPV6 (Advanced, optional)

Scroll down to IPV6 Support for more information)

 ::1#5335

Normal looks like this:



Test Results


Hey Megaman, you did it. Don’t forget to lock your gate.

image

It’s Not Perfect…

Now you should be shielded much from your spying ISP, and maybe even from Cloudflare, but you are still not completely anonymous. In addition to various leaks which are difficult to track down unless you are an expert, you will need a VPN to cover prying eyes from seeing what this insightful tool sees:

I utilize VeePN for proof of concept. Greetings from India!

OPTIONAL: Go easy on your Hard Drive (Turn off logging):

Did I mention it’s faster than snot?

Since you can stack LXC’s to your heart’s delight, TOR is no longer associated with slow ping times. The screenshot was captured on my laptop on an old 802.11n network!

forestgump

IPV6 Support

For IPV6 support, don’t forget to add the following to your list of custom DNS hosts…

::1#5335


A common approach is establishing your IPV6 address as static. I sort of abhor IPV6. It’s supposed to be native DHCP but you need to have a static IPV6 to guarantee things will play nice? Backwards… NEwhoosies…

It really is optional to set up IPV6. Proceed only if you are advanced…

If you followed my guide correctly, you will have assigned DHCP to your IPV6 during installation. Now it’s time to change it to a static.

You can sniff out your DHCP IPV6 using the Pihole user interface (settings → system) and use it as your inspiration for your static IPV6…


… and yes, you can also use the “ip address” command, all you old schoolies…

ip address

Now you can paste your inspired IPV6 into your router’s upstream DNS settings for IPV6… Note: all routers will hide this somewhere different. For my router, it was an afterthought, they added it after the thought, so it was hidden quite well…

But before restarting your router, you will need to make sure the DHCP IPV6 of the pihole is converted to a Static IP address in Proxmox:


You must ADD a /64 or something like this after your Static IP address. You must REMOVE the /64 from the LOCAL IPV6 that we copied from our router’s information page in order to assign the router as your IPV6 Gateway.

Don’t forget to enable IPV6 in your Pihole’s DHCP settings…


I know this isn’t proper, but I like to leave a few slots open on my router’s DHCP then assign the rest to my pihole. You can see that mutiple piholes can split DHCP so that there are in fact three DHCP servers not fighting each other. I know, I know you’re supposed to disable DHCP on your router so they don’t fight, but are they really just suggesting that so that you don’t confuse yourself? Any case, if you don’t want to go through a DNS down event destroying your whole network, consider something closer to what I’ve done here?

(for example):
Router → 192.168.0.2 - 192.168.0.7
Pihole1 → 192.168.0.8 - 192.168.0.99
Pihole 2 → 192.168.0.100 - 192.168.0.254

Something even more exciting?
Router → 192.168.0.2 - 192.168.0.254
Pihole1 → 192.168.1.1 - 192.168.1.254
Pihole 2 → 192.168.2.1 - 192.168.2.254

Look at Google and Cloudflare creepin’ in the TOR networks? Would you just look at that? How does one kick big corporate roundly and fully out of distributed? To do so, your trustless model must become even more granular, and even more ethereal. We map and wrap at the data level. At that point, it doesn’t matter if our obfuscated, shattered, dns text string is intercepted. Yes I said text. Thanks for the free bus ride, meccas. Stay tuned…

Why is Bitcoin not truly anonymous either? Because it has been mapped in full on the other side. And because the miners are captured. That’s how.

…God bless.

EDIT: I simply needed to restart between the two scrips. UPDATE: I’m zeroing in on the recursive aspect of this. Our recursive aspect needs a little loving at this time. You can see this in the screenshots, our current work-around (non-recursive). A small edit somewhere in the code fixes or makes this more reliable even after reboot. It may be a matter of cron firewall settings at boot. It might be a UI bug. Maybe there’s a circular dependency in there, maybe it’s a port conflict, maybe it’s something to do with it only letting me in when incognito mode, maybe I just need to look better at letting the recursive call out.

mah tool

The entirety of the below script can be copy/pasted into one nano file to display a nice snapshot of how our configuration is doing.

sudo nano tool.sh

CTRL + x
y (yes) to save

chmod +x tool.sh
sudo ./tool.sh

tool.sh

#!/bin/bash
set -e

echo "[+] Checking network connectivity..."
ping -c 4 google.com || { echo "[!] No network connection. Exiting..."; exit 1; }

echo "[+] Checking if Pi-hole service is running..."
if systemctl is-active --quiet pihole-FTL; then
    echo "[+] Pi-hole is running."
else
    echo "[!] Pi-hole is not running. Exiting..."; exit 1;
fi

echo "[+] Checking if Unbound DNS service is running..."
if systemctl is-active --quiet unbound; then
    echo "[+] Unbound is running."
else
    echo "[!] Unbound is not running. Exiting..."; exit 1;
fi

echo "[+] Checking if Tor service is running..."
if systemctl is-active --quiet tor; then
    echo "[+] Tor is running."
else
    echo "[!] Tor is not running. Exiting..."; exit 1;
fi

echo "[+] Verifying iptables rules for DNS redirection..."
iptables -t nat -L OUTPUT -n -v | grep 'REDIRECT' || { echo "[!] DNS redirection via Tor is not set correctly. Exiting..."; exit 1; }

echo "[+] Testing DNS resolution through Tor..."
dig @127.0.0.1 -p 9053 google.com || { echo "[!] DNS resolution through Tor failed. Exiting..."; exit 1; }

echo "[+] Checking if Tor Hidden Service is running..."
if [ -f /var/lib/tor/pihole/hostname ]; then
    ONION_ADDR=$(cat /var/lib/tor/pihole/hostname)
    echo "[+] Tor Hidden Service is running with address: $ONION_ADDR"
else
    echo "[!] Tor Hidden Service is not available. Exiting..."; exit 1;
fi

echo "[+] Verifying Avahi for local peer discovery..."
avahi-browse -rt _pihole._tcp || { echo "[!] Avahi service is not properly configured or not running. Exiting..."; exit 1; }

echo "[+] Checking Pi-hole peers configuration..."
if [ -f "/etc/pihole/nodes.conf" ]; then
    echo "[+] Pi-hole peer configuration file exists."
else
    echo "[!] Pi-hole peer configuration file does not exist. Exiting..."; exit 1;
fi

echo "[+] Network diagnostics completed successfully."

Version Zero

Here’s the older version I was talking about with no DNSSEC support. This version works ONLY in tor mode. Brave browser has tor mode.

deploy0.sh

#!/bin/bash
set -e

echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"

echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"

echo "[+] Updating system..."
apt update && apt upgrade -y

echo "[+] Installing dependencies..."

apt install curl

sudo curl -sSL https://install.pi-hole.net | bash && apt install -y iptables-persistent unbound tor avahi-daemon avahi-utils jq

echo "[+] Configuring Unbound..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    interface: ::1
    access-control: 127.0.0.1 allow
    access-control: ::1 allow
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    forward-zone:
        name: "."
        forward-addr: 127.0.0.1@9053
EOL

echo "[+] Configuring Pi-hole..."
cat > /etc/dnsmasq.d/02-custom.conf <<EOL
server=127.0.0.1#5335
server=::1#5335
EOL
systemctl restart pihole-FTL unbound

echo "[+] Configuring Tor Hidden Service..."
mkdir -p /var/lib/tor/pihole
chown -R debian-tor:debian-tor /var/lib/tor/pihole
chmod 700 /var/lib/tor/pihole
cat > /etc/tor/torrc <<EOL
HiddenServiceDir /var/lib/tor/pihole/
HiddenServicePort 53 127.0.0.1:53
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 9053 127.0.0.1:9053
AutomapHostsOnResolve 1
DNSPort 127.0.0.1:9053
TransPort 9040
SocksPort 127.0.0.1:9050
EOL

echo "[+] Restarting Tor..."
systemctl restart tor

echo "[+] Enabling Avahi for local discovery..."
systemctl enable avahi-daemon
systemctl restart avahi-daemon

echo "[+] Waiting for Tor Hidden Service to be available..."
sleep 20
ONION_ADDR=$(cat /var/lib/tor/pihole/hostname)

echo "[+] Registering local and remote peers..."
PEER_FILE="/etc/pihole/nodes.conf"
avahi-browse -rt _pihole._tcp | grep "=" | awk '{print $6}' > $PEER_FILE
echo "tor://$ONION_ADDR" >> $PEER_FILE

while read -r NODE; do
    echo "Discovered node: $NODE"
    curl -s "http://$NODE/peers" >> $PEER_FILE || true
done < $PEER_FILE

echo "[+] Updating Unbound configuration..."
UNBOUND_CONF="/etc/unbound/unbound.conf"

# Ensure localhost queries are allowed
sed -i 's/do-not-query-localhost: yes/do-not-query-localhost: no/g' $UNBOUND_CONF

# Remove any existing forward zone configuration to prevent duplication
sed -i '/forward-zone:/,/^$/d' $UNBOUND_CONF

# Add new forward rules
cat >> $UNBOUND_CONF <<EOL
forward-zone:
    name: "."
    forward-addr: 127.0.0.1@9053
EOL

echo "[+] Restarting Unbound..."
systemctl restart unbound

cat > /etc/tor/torrc <<EOL
HiddenServiceDir /var/lib/tor/pihole/
HiddenServicePort 53 127.0.0.1:53
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 9053 127.0.0.1:9053

AutomapHostsOnResolve 1
DNSPort 127.0.0.1:9053
TransPort 9040
SocksPort 127.0.0.1:9050

# Force all traffic through Tor
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit
TransListenAddress 0.0.0.0
DNSListenAddress 0.0.0.0
EOL

systemctl restart tor

echo "[+] Installing iptables-persistent..."
apt install -y iptables-persistent

echo "[+] Configuring iptables to force all traffic through Tor..."

iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X

# Allow local traffic
iptables -A OUTPUT -o lo -j ACCEPT

# Allow Tor process to connect to the internet
iptables -A OUTPUT -m owner --uid-owner debian-tor -j ACCEPT

# Redirect DNS requests to Tor
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 9053
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 9053

# Redirect all traffic to Tor except local (127.0.0.1) and LAN (192.168.0.0/16)
iptables -t nat -A OUTPUT -d 127.0.0.1 -j RETURN
iptables -t nat -A OUTPUT -d 192.168.0.0/16 -j RETURN
iptables -t nat -A OUTPUT -p tcp --syn -j REDIRECT --to-ports 9040

# Drop all non-Tor traffic to prevent leaks
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -j REJECT

# Save iptables rules
iptables-save > /etc/iptables/rules.v4
echo "[+] iptables rules set!"

cat > /etc/dnsmasq.d/02-custom.conf <<EOL
server=127.0.0.1#5335
server=::1#5335
no-resolv
EOL

systemctl restart pihole-FTL

iptables -F
iptables -t nat -F
iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 9040
iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-port 9040
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-port 9053

iptables-save > /etc/iptables.rules

netfilter-persistent save
netfilter-persistent reload
systemctl enable netfilter-persistent

In the past I’ve had to bash the following if things weren’t working, for example after a reboot. I don’t know at this time if this is still necessary without more tests. Yes, this is included in the deploy script. But it doesn’t seem to be sticky enough in some cases, such as after a reboot.

iptables -F
iptables -t nat -F
iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 9040
iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-port 9040
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-port 9053

iptables-save > /etc/iptables.rules

This bash may also be helpful:

iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 9053
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 9053

And these:

echo "@reboot root iptables-restore < /etc/iptables/rules.v4" >> /etc/crontab
apt install -y iptables-persistent
iptables-save > /etc/iptables.rules
netfilter-persistent save
netfilter-persistent reload
systemctl enable netfilter-persistent

VERSION 1.0

APPLE + V (paste code into nano file) NOTE: Version 1 does not support recursive DNS. You will instead use the TWO scripts from version 2.0 in this place if you wish to have recursive DNS.

Tor mode is optional, but try enabling Tor mode (Brave supports Tor mode) to see your IP address get masked without a VPN. Interestingly, TOR mode renders impotent DNSSEC, at least for the test. Do you think this vulnerability was built into TOR on purpose?

apt install sudo
sudo nano deploy.sh

deploy.sh

#!/bin/bash
set -e

echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"

echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo "zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"
echo " zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org zchg.org"

echo "[+] Updating system..."
apt update && apt upgrade -y

echo "[+] Installing dependencies..."

apt install curl

curl -sSL https://install.pi-hole.net | bash && apt install -y iptables-persistent unbound tor avahi-daemon avahi-utils jq

echo "[+] Configuring Unbound..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    interface: ::1
    access-control: 127.0.0.1 allow
    access-control: ::1 allow
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    forward-zone:
        name: "."
        forward-addr: 127.0.0.1@9053
EOL

# Add DNSSEC validation to Unbound
echo "[+] Adding DNSSEC validation to Unbound..."
cat > /etc/unbound/unbound.conf.d/pi-hole.conf <<EOL
server:
    verbosity: 1
    interface: 0.0.0.0
    port: 5353  # Changed from default to prevent conflicts
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    access-control: 0.0.0.0/0 allow
    access-control: ::0/0 allow
    cache-max-ttl: 86400
    cache-min-ttl: 3600
    harden-dnssec-stripped: yes
    use-caps-for-id: no
    prefetch: yes
    num-threads: 2
    so-reuseport: yes
    msg-cache-size: 128m
    rrset-cache-size: 256m
    infra-cache-numhosts: 100000
    infra-cache-lame-size: 10k
    neg-cache-size: 4m
    do-not-query-localhost: no
    hide-identity: yes
    hide-version: yes
    qname-minimisation: yes
    harden-glue: yes
    harden-below-nxdomain: yes
    target-fetch-policy: "2 1 0 0 0"
    val-log-level: 1
    tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt"
EOL

echo "[+] Configuring Pi-hole..."
cat > /etc/dnsmasq.d/02-custom.conf <<EOL
server=127.0.0.1#5335
server=::1#5335
EOL
systemctl restart pihole-FTL unbound

echo "[+] Configuring Tor Hidden Service..."
mkdir -p /var/lib/tor/pihole
chown -R debian-tor:debian-tor /var/lib/tor/pihole
chmod 700 /var/lib/tor/pihole
cat > /etc/tor/torrc <<EOL
HiddenServiceDir /var/lib/tor/pihole/
HiddenServicePort 53 127.0.0.1:53
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 9053 127.0.0.1:9053
AutomapHostsOnResolve 1
DNSPort 127.0.0.1:9053
TransPort 9040
SocksPort 127.0.0.1:9050
EOL

echo "[+] Restarting Tor..."
systemctl restart tor

echo "[+] Enabling Avahi for local discovery..."
systemctl enable avahi-daemon
systemctl restart avahi-daemon

echo "[+] Waiting for Tor Hidden Service to be available..."
sleep 20
ONION_ADDR=$(cat /var/lib/tor/pihole/hostname)

echo "[+] Registering local and remote peers..."
PEER_FILE="/etc/pihole/nodes.conf"
avahi-browse -rt _pihole._tcp | grep "=" | awk '{print $6}' > $PEER_FILE
echo "tor://$ONION_ADDR" >> $PEER_FILE

while read -r NODE; do
    echo "Discovered node: $NODE"
    curl -s "http://$NODE/peers" >> $PEER_FILE || true
done < $PEER_FILE

echo "[+] Updating Unbound configuration..."
UNBOUND_CONF="/etc/unbound/unbound.conf"

# Ensure localhost queries are allowed
sed -i 's/do-not-query-localhost: yes/do-not-query-localhost: no/g' $UNBOUND_CONF

# Remove any existing forward zone configuration to prevent duplication
sed -i '/forward-zone:/,/^$/d' $UNBOUND_CONF

# Add new forward rules
cat >> $UNBOUND_CONF <<EOL
forward-zone:
    name: "."
    forward-addr: 127.0.0.1@9053
EOL

echo "[+] Restarting Unbound..."
systemctl restart unbound

cat > /etc/tor/torrc <<EOL
HiddenServiceDir /var/lib/tor/pihole/
HiddenServicePort 53 127.0.0.1:53
HiddenServicePort 80 127.0.0.1:80
HiddenServicePort 9053 127.0.0.1:9053

AutomapHostsOnResolve 1
DNSPort 127.0.0.1:9053
TransPort 9040
SocksPort 127.0.0.1:9050

# Force all traffic through Tor
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit
TransListenAddress 0.0.0.0
DNSListenAddress 0.0.0.0
EOL

systemctl restart tor

echo "[+] Installing iptables-persistent..."
apt install -y iptables-persistent

echo "[+] Configuring iptables to force all traffic through Tor..."

iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X

# Allow local traffic
iptables -A OUTPUT -o lo -j ACCEPT

# Allow Tor process to connect to the internet
iptables -A OUTPUT -m owner --uid-owner debian-tor -j ACCEPT

# Redirect DNS requests to Tor
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 9053
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 9053

# Redirect all traffic to Tor except local (127.0.0.1) and LAN (192.168.0.0/16)
iptables -t nat -A OUTPUT -d 127.0.0.1 -j RETURN
iptables -t nat -A OUTPUT -d 192.168.0.0/16 -j RETURN
iptables -t nat -A OUTPUT -p tcp --syn -j REDIRECT --to-ports 9040

# Drop all non-Tor traffic to prevent leaks
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -j REJECT

# Save iptables rules
iptables-save > /etc/iptables/rules.v4
echo "[+] iptables rules set!"

cat > /etc/dnsmasq.d/02-custom.conf <<EOL
server=127.0.0.1#5335
server=::1#5335
no-resolv
EOL

systemctl restart pihole-FTL

iptables -F
iptables -t nat -F
iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 9040
iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-port 9040
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-port 9053

iptables-save > /etc/iptables.rules

netfilter-persistent save
netfilter-persistent reload
systemctl enable netfilter-persistent

CTRL + X (exit)

y
chmod +x deploy.sh
sudo ./deploy.sh

Normal looks like this.





To Do:

Resolve or clarify intermittent non-DNSSEC.
I think this is now fixed in version 2.0, but I need to do more testing.
[

]
image
(DNSSEC Resolver Test)

Harden DNSSEC and Improve Performance of Tor mode in browser (work in progress):

This is useful: Unbound config with hardened security to support DNS over TLS 1.3 via Cloudflare & CleanBrowsing, DNS-SEC, and multi-threading. · GitHub

I’m tweaking the following config file to resolve DNSSEC fails and increase speed even when in Tor. The performance increases come at the expense of some anonymity.

sudo nano /etc/unbound/unbound.conf 

APPLE + V to paste the following,
CTRL + x to save
(this is a note to myself and you can probably ignore it: Find out if this is also utilized by our scheme: sudo nano /etc/pihole/setupVars.conf, sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf)

An improved Unbound Config which doesn’t fail DNSSEC and has improved performance?

cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    do-not-query-localhost: no
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-large-queries: no
    harden-referral-path: yes
    harden-short-bufsize: yes
    harden-algo-downgrade: yes
    harden-below-nxdomain: yes
    use-caps-for-id: no
    aggressive-nsec: yes
    qname-minimisation: yes
    val-clean-additional: yes
    prefetch: yes
    prefetch-key: yes
    so-rcvbuf: 8m
    so-sndbuf: 8m
    num-threads: 4
    target-fetch-policy: "5 5 0 0 0"
    cache-min-ttl: 0
    cache-max-ttl: 86401
    serve-expired: no
    rrset-roundrobin: yes
    val-clean-additional: yes
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    val-log-level: 2
    val-permissive-mode: no
    serve-expired-ttl: 0
    infra-cache-numhosts: 100000
    infra-cache-min-rtt: 10

    # Drop SERVFAIL responses
    module-config: "validator iterator"
    serve-expired-client-timeout: 0
    serve-expired-ttl: 0

    # Route ALL Uncached Queries via Tor (Cloudflare through Tor)
    forward-zone:
        name: "."
        forward-addr: 127.0.0.1@9053  # Force all queries through Tor's DNSPort

remote-control:
        control-enable: no
EOL

SUPER FREAK:

server:
    # 🏎️ Performance Optimizations
    num-threads: 8                           # Use multiple CPU cores
    so-rcvbuf: 16m                           # Increase socket receive buffer
    so-sndbuf: 16m                           # Increase socket send buffer
    msg-cache-size: 256m                     # Increase message cache for faster responses
    rrset-cache-size: 512m                   # Increase RRset cache for more stored DNS records
    infra-cache-numhosts: 100000              # Cache more infrastructure queries
    cache-max-ttl: 86400                      # Cache valid responses for 24 hours
    cache-min-ttl: 300                        # Prevent unnecessary refreshes for 5 min
    serve-expired: yes                        # Serve expired responses while refreshing in the background

    # ⚡ Fastest Cache-First Querying
    target-fetch-policy: "15 15 2 2 2"        # Prioritize local cache; parallel external queries
    prefetch: yes                             # Prefetch frequently requested records
    prefetch-key: yes                         # Prefetch DNSSEC records for fast validation
    aggressive-nsec: yes                      # Cache negative responses to reduce queries
    val-clean-additional: yes                 # Ensure only valid responses are stored
    harden-dnssec-stripped: yes               # Prevent downgrade attacks
    harden-glue: yes                          # Prevent malicious glue records
    harden-referral-path: yes                 # Ensure queries follow the proper path

    # 🔐 Security and Privacy
    do-tcp: yes                               # Allow TCP (reduces UDP spoofing attacks)
    hide-identity: yes                        # Don't reveal Unbound version
    hide-version: yes                         # Don't reveal software version
    use-caps-for-id: no                       # Disables capitalization in queries for privacy
    minimal-responses: yes                    # Reduce unnecessary response data
    qname-minimisation: yes                   # Minimize information leakage

    # 🌍 Secure External Queries (DoT + DoH)
    forward-zone:
        name: "."
        forward-tls-upstream: yes             # Enable DNS over TLS
        forward-addr: 1.1.1.1@853#cloudflare-dns.com  # Cloudflare DoT
        forward-addr: 9.9.9.9@853#dns.quad9.net      # Quad9 DoT
        forward-addr: 8.8.8.8@853#dns.google        # Google DoT
        forward-addr: 2620:fe::fe@853#dns.opennic.glue  # OpenNIC DoT

    # 🎯 TCP Fast Open (Reduce Latency)
    so-reuseport: yes                         # Allow multiple sockets for parallelism
    tcp-fastopen: yes                         # Reduce latency on TCP connections

    # 🔄 Logging (Optional)
    logfile: "/var/log/unbound.log"
    verbosity: 1                               # Reduce noise; set to 2+ for debugging

remote-control:
    control-enable: no                        # Disable remote control for security
systemctl restart unbound
systemctl restart tor

https://discourse.pi-hole.net/t/change-the-ttl/6903/13
https://www.reddit.com/r/TOR/comments/sryr2c/comment/hwv1nx9/

To Do

And hey, maybe a premium version could do a better job in utilizing local DNSSEC? I’m versioning one with NGINX. And one without DNSSEC. Sometimes one step backwards is two steps forward, in hindsight, is it?

Should I name the collection after MegaMan?
Could that be another proper noun which could pass as popular culture?

I’ve got several approaches to consider, need to pick my cherries.
Cheers.

Premise: Running an exit? Please secure your DNS with DNSCrypt+Unbound - tor-relays - lists.torproject.org

This doesn’t need to exist yet… (coming attraction + NGINX + Bind)

The full of the below can be copy/pasted into command for a snapshot of diagnostics when an NGINX-containing packaged is installed.

#!/bin/bash

# Function to check service status and logs
check_service() {
    service_name=$1
    echo "[+] Checking $service_name status..."
    if systemctl is-active --quiet $service_name; then
        echo "  - $service_name is running."
    else
        echo "  - $service_name is NOT running."
    fi
    echo "[+] Checking $service_name logs for errors..."
    journalctl -u $service_name -n 100 | grep -i "error\|warn\|fail" | tail -n 5 || echo "  - No recent errors found."
}

# Function to check for redirects
check_redirects() {
    url=$1
    echo "[+] Checking for redirects for URL: $url"
    curl -s -I -L -w "%{url_effective}\n" -o /dev/null "$url" | awk 'END {print "  - Final redirect URL: " $0}'
    echo "  - Number of redirects: $(curl -s -I -L -w "%{num_redirects}\n" -o /dev/null "$url")"
}

# Check SSL certificate
check_ssl_cert() {
    echo "[+] Checking SSL certificate..."
    if [ -f /etc/nginx/ssl/nginx-selfsigned.crt ]; then
        echo "  - SSL certificate exists."
        echo "  - Certificate validity:"
        openssl x509 -in /etc/nginx/ssl/nginx-selfsigned.crt -noout -dates | sed 's/^/    /'
    else
        echo "  - SSL certificate does NOT exist."
    fi
}

# Check NGINX configuration for common redirect issues
check_nginx_config() {
    echo "[+] Checking NGINX configuration..."
    nginx -t
    echo "[+] Looking for problematic redirect rules..."
    grep -E "return|rewrite" /etc/nginx/sites-enabled/*
}

# Main execution
echo "Starting diagnostic for redirect and SSL issues..."

# Check services
check_service pihole-FTL
check_service nginx

# Check SSL
check_ssl_cert

# Check redirects
check_redirects "https://localhost"
check_redirects "https://localhost/admin"

# NGINX configuration check
check_nginx_config

# Check if Pi-hole's admin page is accessible via localhost
echo "[+] Attempting to access Pi-hole admin page..."
if curl -k -I -s -o /dev/null -w "%{http_code}\n" https://localhost/admin | grep -q "200"; then
    echo "  - Pi-hole admin page is accessible and returns 200 OK."
else
    echo "  - Pi-hole admin page is NOT accessible or does not return 200 OK."
fi

# Check if Pi-hole itself is redirecting
echo "[+] Checking if Pi-hole is configured to redirect..."
grep -E "url\.rewrite" /etc/lighttpd/external.conf || echo "  - No specific redirect found in Pi-hole's Lighttpd config."

echo "[+] Diagnostic completed. Review the output for clues on redirect issues."

For all of the following, you can clear cache and restore internet fairly simply by running deploy1.sh (see latest version)

I recommend a naming scheme e.g. ‘deploy41.sh’ for version 4.1 so you can hotswap between this and other deploys.

sudo ./deploy1.sh

Please note: Your browser MUST support SNI to pass SNI test!

Version 4.0 is EXPERIMENTAL

Working towards no more Cloudflare :smiley:

#!/bin/bash
set -e

echo "[+] Updating system..."
apt update && apt upgrade -y

echo "[+] Installing dependencies..."
apt install -y curl unbound tor iptables-persistent

echo "[+] Configuring Unbound for DNS over Tor..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    do-not-query-localhost: no
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-large-queries: yes
    harden-referral-path: yes
    harden-short-bufsize: yes
    harden-algo-downgrade: yes
    harden-below-nxdomain: yes
    use-caps-for-id: no
    aggressive-nsec: yes
    qname-minimisation: yes
    val-clean-additional: yes
    prefetch: yes
    prefetch-key: yes
    so-rcvbuf: 8m            
    so-sndbuf: 8m
    num-threads: 4
    target-fetch-policy: "5 5 0 0 0"
    val-permissive-mode: no
    cache-min-ttl: 5
    cache-max-ttl: 86401
    serve-expired: no
    rrset-roundrobin: yes
    val-clean-additional: yes
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    tls-use-sni: yes  

    # Cached Queries: Encrypt local queries over DoT
    forward-zone:
        name: "."
        forward-tls-upstream: yes
        forward-addr: 127.0.0.1@853  # Local encryption for cached queries

    # Uncached Queries: Use an anonymous resolver inside Tor
    forward-zone:
        name: "."
        forward-addr: 127.0.0.1@9053  # Tor Hidden Service DNS resolver
        forward-addr: ::1@9053
EOL

echo "[+] Configuring Tor for Anonymous DNS Resolution..."
cat > /etc/tor/torrc <<EOL
SocksPort 9050
DNSPort 9053
AutomapHostsOnResolve 1
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit

# Use a trusted DNS resolver inside Tor instead of Cloudflare
ClientDNSRejectInternalAddresses 1
ClientUseIPv6 1
ClientPreferIPv6ORPort 1
EOL

echo "[+] Restarting Tor and Unbound..."
systemctl restart tor unbound

echo "[+] Setting up permanent iptables rules for DNS..."
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5335
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 5335

echo "[+] Saving iptables rules..."
iptables-save > /etc/iptables/rules.v4
netfilter-persistent save
netfilter-persistent reload

echo "[+] Your DNS queries are now fully anonymous:"
echo "    - Uncached queries go through a Tor hidden service DNS."
echo "    - Cached queries are encrypted locally to prevent snooping."

sleep 5

reboot


Version 4.1 is EXPERIMENTAL

#!/bin/bash
set -e

echo "[+] Updating system..."
apt update && apt upgrade -y

echo "[+] Installing dependencies..."
apt install -y curl unbound tor iptables-persistent

echo "[+] Configuring Unbound for DNS over Tor with .onion Resolvers..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    do-not-query-localhost: no
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-large-queries: yes
    harden-referral-path: yes
    harden-short-bufsize: yes
    harden-algo-downgrade: yes
    harden-below-nxdomain: yes
    use-caps-for-id: no
    aggressive-nsec: yes
    qname-minimisation: yes
    val-clean-additional: yes
    prefetch: yes
    prefetch-key: yes
    so-rcvbuf: 8m            
    so-sndbuf: 8m
    num-threads: 4
    target-fetch-policy: "5 5 0 0 0"
    val-permissive-mode: no
    cache-min-ttl: 5
    cache-max-ttl: 86401
    serve-expired: no
    rrset-roundrobin: yes
    val-clean-additional: yes
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    tls-use-sni: yes  

    # Cached Queries: Encrypt using local DNS-over-TLS
    forward-zone:
        name: "."
        forward-tls-upstream: yes
        forward-addr: 127.0.0.1@853  # Local encryption for cached queries

    # Uncached Queries: Route through Tor Hidden Service resolvers
    forward-zone:
        name: "."
        forward-addr: 127.0.0.1@9053
        forward-addr: ::1@9053
        # Privacy-respecting .onion resolvers (replace with valid ones)
        forward-addr: 1234567890abcdef.onion@53
        forward-addr: abcdef1234567890.onion@53
EOL

echo "[+] Configuring Tor for Anonymous DNS Resolution with .onion Resolvers..."
cat > /etc/tor/torrc <<EOL
SocksPort 9050
DNSPort 9053
AutomapHostsOnResolve 1
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit

# Use a trusted DNS resolver inside Tor instead of Cloudflare
ClientDNSRejectInternalAddresses 1
ClientUseIPv6 1
ClientPreferIPv6ORPort 1

# Map some commonly used public DNS providers to .onion resolvers
MapAddress 9.9.9.9 10.192.0.1  # Quad9
MapAddress 94.140.14.14 10.192.0.2  # AdGuard
MapAddress 77.88.8.8 10.192.0.3  # Yandex
EOL

echo "[+] Restarting Tor and Unbound..."
systemctl restart tor unbound

echo "[+] Setting up permanent iptables rules for DNS..."
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5335
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 5335

echo "[+] Saving iptables rules..."
iptables-save > /etc/iptables/rules.v4
netfilter-persistent save
netfilter-persistent reload

echo "[+] Your DNS queries are now fully anonymous:"
echo "    - Uncached queries go through privacy-focused .onion resolvers inside Tor."
echo "    - Cached queries are encrypted locally to prevent network snooping."

sleep 5

reboot


Version 3.0 is EXPERIMENTAL:

#!/bin/bash
set -e

echo "[+] Updating system and installing dependencies..."
apt update && apt upgrade -y
apt install -y curl unbound tor iptables-persistent dnsutils

echo "[+] Configuring Unbound for DNS over TLS with ECH support..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    verbosity: 1                    # Increase logging for debugging
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    tls-use-sni: yes               # Enable SNI handling for ECH
    hide-identity: yes
    hide-version: yes
    do-not-query-localhost: no
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-large-queries: yes
    harden-referral-path: yes
    harden-short-bufsize: yes
    harden-algo-downgrade: yes
    harden-below-nxdomain: yes
    use-caps-for-id: no
    aggressive-nsec: yes
    qname-minimisation: yes
    prefetch: yes
    prefetch-key: yes
    so-rcvbuf: 4m            
    so-sndbuf: 4m
    num-threads: $(nproc)
    cache-min-ttl: 3600
    cache-max-ttl: 86400
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt

forward-zone:
    name: "."
    forward-tls-upstream: yes      # Enable DNS over TLS for upstream
    forward-addr: 1.1.1.1@853      # Cloudflare DoT with ECH
    forward-addr: 1.0.0.1@853
    forward-addr: 2606:4700:4700::1111@853
    forward-addr: 2606:4700:4700::1001@853

remote-control:
    control-enable: no
EOL

echo "[+] Configuring Tor for SOCKS proxy and DNS..."
cat > /etc/tor/torrc <<EOL
SocksPort 9050
DNSPort 9053
AutomapHostsOnResolve 1
VirtualAddrNetworkIPv4 10.192.0.0/10
VirtualAddrNetworkIPv6 [fc00::/8]
AutomapHostsSuffixes .onion

ExitNodes {us},{de},{nl} StrictNodes 0
AvoidDiskWrites 1
HardwareAccel 1
TransPort 9040
TransListenAddress 127.0.0.1
EOL

echo "[+] Setting Tor as a systemd service..."
systemctl enable tor
systemctl restart tor

echo "[+] Configuring Unbound to route through Tor..."
cat > /etc/unbound/unbound.conf.d/tor.conf <<EOL
server:
    do-not-query-localhost: no

forward-zone:
    name: "."
    forward-tls-upstream: yes
    forward-addr: 127.0.0.1@9050   # Route via Tor SOCKS5
EOL

echo "[+] Validating Unbound configuration..."
unbound-checkconf || { echo "[-] Unbound config error detected!"; exit 1; }

echo "[+] Restarting Unbound..."
systemctl enable unbound
systemctl restart unbound

echo "[+] Setting up iptables rules..."
iptables -t nat -F  # Flush existing NAT rules to avoid conflicts
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5335
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 5335
iptables -t nat -A OUTPUT -p tcp --dport 853 -j REDIRECT --to-ports 9040
iptables -t nat -A OUTPUT -p udp --dport 853 -j REDIRECT --to-ports 9040
iptables -t nat -A OUTPUT -d 127.0.0.1 -p udp --dport 53 -j ACCEPT
iptables -t nat -A OUTPUT -d 127.0.0.1 -p tcp --dport 53 -j ACCEPT

echo "[+] Saving iptables rules..."
iptables-save > /etc/iptables/rules.v4
systemctl enable netfilter-persistent
systemctl restart netfilter-persistent

echo "[+] Verifying services..."
sleep 5
if systemctl is-active tor >/dev/null; then
    echo "[+] Tor is running"
else
    echo "[-] Tor failed to start. Check: journalctl -u tor"
    exit 1
fi
if systemctl is-active unbound >/dev/null; then
    echo "[+] Unbound is running"
else
    echo "[-] Unbound failed to start. Check: journalctl -u unbound"
    exit 1
fi

echo "[+] Testing DNS resolution..."
dig_result=$(dig @127.0.0.1 -p 5335 example.com +short)
if [ -n "$dig_result" ]; then
    echo "[+] DNS resolution successful: $dig_result"
else
    echo "[-] DNS resolution failed. Check logs and steps below."
fi

echo "[+] Configuration applied. Debugging steps if needed:"
echo "1. Check Unbound logs: journalctl -u unbound -b"
echo "2. Check Tor logs: journalctl -u tor -b"
echo "3. Test direct resolution: dig @1.1.1.1 -p 853 +tls example.com"
echo "4. Test Tor routing: torsocks dig @1.1.1.1 example.com"
echo "[!] If using Pi-hole, disable DNSSEC and set upstream to 127.0.0.1#5335"
echo "[!] Verify ECH: https://www.cloudflare.com/ssl/encrypted-sni/"

sleep 5

reboot

image
DNS Leak - Cloudflare (non-regional) Prevails over TOR

Version 3.1 Beta

#!/bin/bash
set -e

echo "[+] Updating system..."
apt update && apt upgrade -y

echo "[+] Installing dependencies..."
apt install -y curl unbound tor iptables-persistent

echo "[+] Configuring Unbound for DNS over Tor and Cloudflare..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    do-not-query-localhost: no
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-large-queries: yes
    harden-referral-path: yes
    harden-short-bufsize: yes
    harden-algo-downgrade: yes
    harden-below-nxdomain: yes
    use-caps-for-id: no
    aggressive-nsec: yes
    qname-minimisation: yes
    val-clean-additional: yes
    prefetch: yes
    prefetch-key: yes
    so-rcvbuf: 8m            
    so-sndbuf: 8m
    num-threads: 4
    target-fetch-policy: "5 5 0 0 0"
    val-permissive-mode: no
    cache-min-ttl: 5
    cache-max-ttl: 86401
    serve-expired: no
    rrset-roundrobin: yes
    val-clean-additional: yes
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    tls-use-sni: yes  # Enable SNI support for DoT requests

    # Route uncached DNS queries through Cloudflare's DoT, using Tor for security
    forward-zone:
        name: "."
        forward-tls-upstream: yes
        forward-addr: 1.1.1.1@853  # Cloudflare DoT
        forward-addr: 1.0.0.1@853  # Cloudflare DoT
        forward-addr: 2606:4700:4700::1111@853
        forward-addr: 2606:4700:4700::1001@853

    # Route cached DNS queries through Tor for privacy and anonymity
    forward-zone:
        name: "."
        forward-addr: 127.0.0.1@9053  # Tor DNS for cached queries
        forward-addr: ::1@9053  # IPv6 Tor DNS

remote-control:
    control-enable: no
EOL

echo "[+] Configuring Tor for DNS resolution..."
cat > /etc/tor/torrc <<EOL
SocksPort 9050
DNSPort 9053
AutomapHostsOnResolve 1
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit

# Route Cloudflare's DNS-over-TLS requests via Tor for additional anonymity
MapAddress 1.1.1.1 10.192.0.1
MapAddress 1.0.0.1 10.192.0.2
MapAddress 2606:4700:4700::1111 10.192.0.3
MapAddress 2606:4700:4700::1001 10.192.0.4
EOL

echo "[+] Restarting Tor and Unbound..."
systemctl restart tor unbound

echo "[+] Setting up permanent iptables rules for DNS..."
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5335
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 5335

echo "[+] Saving iptables rules..."
iptables-save > /etc/iptables/rules.v4
netfilter-persistent save
netfilter-persistent reload

echo "[+] Your DNS queries are now routed through Tor for privacy (cached queries) and Cloudflare with SNI for secure DNS-over-TLS (non-cached queries)."

sleep 5

reboot


DNS LEAK- TOR PREVAILS

Version 2.1 BETA

#!/bin/bash
set -e

echo "[+] Updating system..."
apt update && apt upgrade -y

echo "[+] Installing dependencies..."
apt install -y curl unbound tor

echo "[+] Configuring Unbound for DNS over Tor..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    do-not-query-localhost: no
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-large-queries: yes
    harden-referral-path: yes
    harden-short-bufsize: yes
    harden-algo-downgrade: yes
    harden-below-nxdomain: yes
    use-caps-for-id: no
    aggressive-nsec: yes
    qname-minimisation: yes
    val-clean-additional: yes
    prefetch: yes
    prefetch-key: yes
    so-rcvbuf: 8m
    so-sndbuf: 8m
    num-threads: 4
    target-fetch-policy: "5 5 0 0 0"
    val-permissive-mode: no
    cache-min-ttl: 5
    cache-max-ttl: 86401
    serve-expired: no
    rrset-roundrobin: yes
    val-clean-additional: yes
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    tls-use-sni: yes

    # Route ALL Uncached Queries via Tor (Cloudflare through Tor)
    forward-zone:
        name: "."
        forward-tls-upstream: yes
        forward-addr: 127.0.0.1@853  # Force all queries through Tor's DNSPort
        forward-addr: ::1@853 # Force all IPV6 queries through Tor's DNSPort

remote-control:
        control-enable: no
EOL

echo "[+] Configuring Tor for DNS resolution..."
cat > /etc/tor/torrc <<EOL
SocksPort 9050
DNSPort 853
AutomapHostsOnResolve 1
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit

# Route Cloudflare's DoH and DoT requests via Tor
MapAddress 1.1.1.1@853 10.192.0.1@853
MapAddress 1.0.0.1@853 10.192.0.2@853
#MapAddress 2606:4700:4700::1111@853 10.192.0.3@853
#MapAddress 2606:4700:4700::1001@853 10.192.0.4@853
EOL

echo "[+] Restarting Tor and Unbound..."
systemctl restart tor unbound

echo "[+] Setting up permanent iptables rules for DNS..."
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5335
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 5335

echo "[+] Saving iptables rules..."
iptables-save > /etc/iptables/rules.v4
netfilter-persistent save
netfilter-persistent reload

echo "[+] Your DNS queries are now being routed through Tor for privacy."

sleep 5

reboot

Version 2.2 BETA

#!/bin/bash
set -e

echo "[+] Updating system..."
apt update && apt upgrade -y

echo "[+] Installing dependencies..."
apt install -y curl unbound tor iptables-persistent

echo "[+] Configuring Unbound for DNS over Tor..."
cat > /etc/unbound/unbound.conf <<EOL
server:
    interface: 127.0.0.1
    port: 5335
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    hide-identity: yes
    hide-version: yes
    do-not-query-localhost: no
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-large-queries: yes
    harden-referral-path: yes
    harden-short-bufsize: yes
    harden-algo-downgrade: yes
    harden-below-nxdomain: yes
    use-caps-for-id: no
    aggressive-nsec: yes
    qname-minimisation: yes
    val-clean-additional: yes
    prefetch: yes
    prefetch-key: yes
    so-rcvbuf: 8m
    so-sndbuf: 8m
    num-threads: 4
    target-fetch-policy: "5 5 0 0 0"
    val-permissive-mode: no
    cache-min-ttl: 5
    cache-max-ttl: 86401
    serve-expired: no
    rrset-roundrobin: yes
    val-clean-additional: yes
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    tls-use-sni: yes

    # Route ALL Uncached Queries via Tor (Cloudflare through Tor)
    forward-zone:
        name: "."
        forward-tls-upstream: yes
        forward-addr: 127.0.0.1@853  # Force all queries through Tor's DNSPort
        forward-addr: ::1@853         # Force all IPV6 queries through Tor's DNSPort

remote-control:
        control-enable: no
EOL

echo "[+] Configuring Tor for DNS resolution..."
cat > /etc/tor/torrc <<EOL
SocksPort 9050
DNSPort 853
AutomapHostsOnResolve 1
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit

# Route Cloudflare's DoH and DoT requests via Tor
MapAddress 1.1.1.1@853 10.192.0.1@853
MapAddress 1.0.0.1@853 10.192.0.2@853
MapAddress 2606:4700:4700::1111@853 10.192.0.3@853
MapAddress 2606:4700:4700::1001@853 10.192.0.4@853
EOL

echo "[+] Restarting Tor and Unbound..."
systemctl restart tor unbound

echo "[+] Setting up permanent iptables rules for DNS..."
iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5335
iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 5335

echo "[+] Saving iptables rules..."
iptables-save > /etc/iptables/rules.v4
netfilter-persistent save
netfilter-persistent reload

echo "[+] Your DNS queries are now being routed through Tor for privacy."

echo "[+] Testing DNS resolution..."
dig_result=$(dig @127.0.0.1 -p 5335 example.com +short)
if [ -n "$dig_result" ]; then
    echo "[+] DNS resolution successful: $dig_result"
else
    echo "[-] DNS resolution failed. Check logs and steps below."
fi

echo "[+] Configuration applied. Debugging steps if needed:"
echo "1. Check Unbound logs: journalctl -u unbound -b"
echo "2. Check Tor logs: journalctl -u tor -b"
echo "3. Test direct resolution: dig @1.1.1.1 -p 853 +tls example.com"
echo "4. Test Tor routing: torsocks dig @1.1.1.1 example.com"

sleep 5

reboot