Networking and DNSguide
Glory-Hole DoT DNS service interop with Magic WAN and VyOS
Expose a Glory-Hole DoT listener (gloryhole, now a private repo) on VyOS 1.4 via DNAT (853 -> 192.168.23.10:853) directly to the internet. LAN hosts hairpin through Magic WAN IPsec. The reply rides rule 1 rather than the hairpin rules 5-8: Magic WAN source-NATs hairpin traffic to an address inside the Cloudflare group (the 172.71.x.x in the captures below, and 172.71.0.0/16 sits inside the group’s 172.64.0.0/13), which rule 1 already sends to table 20. All Cloudflare traffic routes via Magic WAN since we can’t differentiate site-local Cloudflare services from internet Cloudflare.
Network topology
Section titled “Network topology”1) Podman container (concise)
Section titled “1) Podman container (concise)”set container name glory-hole cap-add 'net-bind-service'set container name glory-hole environment TZ value 'Europe/Amsterdam'set container name glory-hole host-name 'glory-hole'set container name glory-hole image 'erfianugrah/glory-hole:v0.9.11'set container name glory-hole memory '1024'set container name glory-hole network podman-2 address '192.168.23.10'set container name glory-hole port dot_tcp destination '853'set container name glory-hole port dot_tcp protocol 'tcp'set container name glory-hole port dot_tcp source '8853'set container name glory-hole port dot_udp destination '853'set container name glory-hole port dot_udp protocol 'udp'set container name glory-hole port dot_udp source '8853' # DoT is TCP-only; UDP kept for parityset container name glory-hole restart 'always'set container name glory-hole shared-memory '512'set container name glory-hole volume data destination '/var/lib/glory-hole'set container name glory-hole volume data mode 'rw'set container name glory-hole volume data source '/config/glory-hole/data'set container name glory-hole volume etc destination '/etc/glory-hole'set container name glory-hole volume etc mode 'rw'set container name glory-hole volume etc source '/config/glory-hole/etc'set container name glory-hole volume localtime destination '/etc/localtime'set container name glory-hole volume localtime mode 'ro'set container name glory-hole volume localtime source '/etc/localtime'set container name glory-hole volume logs destination '/var/log/glory-hole'set container name glory-hole volume logs mode 'rw'set container name glory-hole volume logs source '/config/glory-hole/logs'Ensure config.yml inside /config/glory-hole/etc has:
server.dot_enabled: trueserver.dot_address: ":853"- TLS configured (manual PEM or ACME) for your DoT hostname.
2) DNAT (853 -> 192.168.23.10:853)
Section titled “2) DNAT (853 -> 192.168.23.10:853)”Direct port 853 exposure (no intermediate port like 8853):
set nat destination rule 29 description 'gloryhole-dot'set nat destination rule 29 destination port '853'set nat destination rule 29 inbound-interface name 'pppoe0'set nat destination rule 29 logset nat destination rule 29 protocol 'tcp_udp'set nat destination rule 29 translation address '192.168.23.10'set nat destination rule 29 translation port '853'Other services (optional, for reference):
# CoreDNS on alternative portset nat destination rule 30 description 'coredns'set nat destination rule 30 destination port '5055'set nat destination rule 30 inbound-interface name 'pppoe0'set nat destination rule 30 logset nat destination rule 30 protocol 'tcp_udp'set nat destination rule 30 translation address '192.168.23.6'set nat destination rule 30 translation port '53'
# PiKVM web interfaceset nat destination rule 31 description 'kvm'set nat destination rule 31 destination port '2054'set nat destination rule 31 inbound-interface name 'pppoe0'set nat destination rule 31 logset nat destination rule 31 protocol 'tcp_udp'set nat destination rule 31 translation address '192.168.21.5'set nat destination rule 31 translation port '80'
# PiKVM HTTPSset nat destination rule 32 description 'kvm'set nat destination rule 32 destination port '2054'set nat destination rule 32 inbound-interface name 'pppoe0'set nat destination rule 32 logset nat destination rule 32 protocol 'tcp_udp'set nat destination rule 32 translation address '192.168.21.5'set nat destination rule 32 translation port '443'
# RIPE Atlas probeset nat destination rule 33 description 'atlas-probe'set nat destination rule 33 destination port '2023'set nat destination rule 33 inbound-interface name 'pppoe0'set nat destination rule 33 logset nat destination rule 33 protocol 'tcp_udp'set nat destination rule 33 translation address '192.168.23.7'set nat destination rule 33 translation port '2023'
# RIPE Atlas webset nat destination rule 34 description 'atlas-probe'set nat destination rule 34 destination port '8080'set nat destination rule 34 inbound-interface name 'pppoe0'set nat destination rule 34 logset nat destination rule 34 protocol 'tcp_udp'set nat destination rule 34 translation address '192.168.23.7'set nat destination rule 34 translation port '8080'Source NAT (WAN masquerade)
Section titled “Source NAT (WAN masquerade)”Outbound internet traffic is masqueraded on pppoe0:
set nat source rule 20 description 'pppoe'set nat source rule 20 logset nat source rule 20 outbound-interface name 'pppoe0'set nat source rule 20 source address '10.0.0.0/8'set nat source rule 20 translation address 'masquerade'3) Firewall allow
Section titled “3) Firewall allow”EXTERNAL-IN already allows it; ensure:
set firewall ipv4 name EXTERNAL-IN rule 50 action 'accept'set firewall ipv4 name EXTERNAL-IN rule 50 description 'Allow DoT to glory-hole'set firewall ipv4 name EXTERNAL-IN rule 50 protocol 'tcp'set firewall ipv4 name EXTERNAL-IN rule 50 destination address '192.168.23.10'set firewall ipv4 name EXTERNAL-IN rule 50 destination port '853'4) Policy routing
Section titled “4) Policy routing”Overview
Section titled “Overview”Policy routing controls how traffic from Glory-Hole reaches different destinations:
- Rule 1: All Cloudflare traffic via Magic WAN (cf-ipv4 -> table 20) - the rule a hairpin reply actually matches, because Magic WAN source-NATs hairpin traffic into the Cloudflare group
- Rule 2: Catch-all to main table/pppoe0 (current order puts this before hairpin rules)
- Rules 5-8: Hairpin specific LAN hosts through IPsec (shadowed by rule 2 unless rule 2 is moved lower; they only matter for a reply addressed to a raw LAN host address)
- Rule 100: Final catch-all to main
Current order: 1, 2, 5, 6, 7, 8, 100. Move rules 5-8 above rule 2 if you need a reply to a raw LAN host address to stay on IPsec.
Glory-Hole policy routing
Section titled “Glory-Hole policy routing”set policy route magic-wan-ipsec-glory-hole default-logset policy route magic-wan-ipsec-glory-hole interface 'pod-podman-2'
# Rule 1: All Cloudflare traffic via Magic WAN (REQUIRED)set policy route magic-wan-ipsec-glory-hole rule 1 description 'gh -> cloudflare magic wan'set policy route magic-wan-ipsec-glory-hole rule 1 destination group network-group 'cf-ipv4'set policy route magic-wan-ipsec-glory-hole rule 1 set table '20'set policy route magic-wan-ipsec-glory-hole rule 1 source address '192.168.23.10'
# Rule 2: Current catch-all for internet traffic via pppoe0set policy route magic-wan-ipsec-glory-hole rule 2 description 'All other traffic from gh -> main (WAN)'set policy route magic-wan-ipsec-glory-hole rule 2 set table 'main'set policy route magic-wan-ipsec-glory-hole rule 2 source address '192.168.23.10'
# Rules 5-8: Hairpin specific LAN hosts through IPsec# (These are shadowed by rule 2 unless you move them above it)set policy route magic-wan-ipsec-glory-hole rule 5 description 'gh -> homehub'set policy route magic-wan-ipsec-glory-hole rule 5 destination address '192.168.21.7'set policy route magic-wan-ipsec-glory-hole rule 5 set table '20'set policy route magic-wan-ipsec-glory-hole rule 5 source address '192.168.23.10'
set policy route magic-wan-ipsec-glory-hole rule 6 description 'gh -> traefik'set policy route magic-wan-ipsec-glory-hole rule 6 destination address '192.168.22.100'set policy route magic-wan-ipsec-glory-hole rule 6 set table '20'set policy route magic-wan-ipsec-glory-hole rule 6 source address '192.168.23.10'
set policy route magic-wan-ipsec-glory-hole rule 7 description 'gh -> workstation'set policy route magic-wan-ipsec-glory-hole rule 7 destination address '192.168.21.3'set policy route magic-wan-ipsec-glory-hole rule 7 set table '20'set policy route magic-wan-ipsec-glory-hole rule 7 source address '192.168.23.10'
set policy route magic-wan-ipsec-glory-hole rule 8 description 'gh -> pikvm'set policy route magic-wan-ipsec-glory-hole rule 8 destination address '192.168.21.5'set policy route magic-wan-ipsec-glory-hole rule 8 set table '20'set policy route magic-wan-ipsec-glory-hole rule 8 source address '192.168.23.10'
# Rule 100: Final catch-all to mainset policy route magic-wan-ipsec-glory-hole rule 100 description 'gh -> main'set policy route magic-wan-ipsec-glory-hole rule 100 set table 'main'set policy route magic-wan-ipsec-glory-hole rule 100 source address '192.168.23.10'
commitsaveNetwork group for Cloudflare IPs
Section titled “Network group for Cloudflare IPs”Define a network group containing Cloudflare’s IP ranges (since we can’t differentiate site-local from internet Cloudflare):
set firewall group network-group cf-ipv4 network '173.245.48.0/20'set firewall group network-group cf-ipv4 network '103.21.244.0/22'set firewall group network-group cf-ipv4 network '103.22.200.0/22'set firewall group network-group cf-ipv4 network '103.31.4.0/22'set firewall group network-group cf-ipv4 network '141.101.64.0/18'set firewall group network-group cf-ipv4 network '108.162.192.0/18'set firewall group network-group cf-ipv4 network '190.93.240.0/20'set firewall group network-group cf-ipv4 network '188.114.96.0/20'set firewall group network-group cf-ipv4 network '197.234.240.0/22'set firewall group network-group cf-ipv4 network '198.41.128.0/17'set firewall group network-group cf-ipv4 network '162.158.0.0/15'set firewall group network-group cf-ipv4 network '104.16.0.0/13'set firewall group network-group cf-ipv4 network '104.24.0.0/14'set firewall group network-group cf-ipv4 network '172.64.0.0/13'set firewall group network-group cf-ipv4 network '131.0.72.0/22'Rule order implications
Section titled “Rule order implications”With the current order (1, 2, 5-8, 100), the broad catch-all rule 2 matches before the hairpin rules 5-8, so those rules never fire. That does not break the hairpin as shipped: Magic WAN source-NATs hairpin traffic into the Cloudflare group (172.71.0.0/16 inside 172.64.0.0/13), so rule 1 carries the reply back to table 20. Rules 5-8 matter only when a reply is addressed to a raw LAN host address - then move them above rule 2, or narrow rule 2’s match.
LAN host policy routes (as provided)
Section titled “LAN host policy routes (as provided)”The current config defines per-host policy routes:
# workstation (no interface set)set policy route magic-wan-ipsec-workstation default-logset policy route magic-wan-ipsec-workstation rule 5 description 'workstation -> gh'set policy route magic-wan-ipsec-workstation rule 5 destination address '192.168.23.10'set policy route magic-wan-ipsec-workstation rule 5 set table '20'set policy route magic-wan-ipsec-workstation rule 5 source address '192.168.21.3'set policy route magic-wan-ipsec-workstation rule 100 set table 'main'set policy route magic-wan-ipsec-workstation rule 100 source address '192.168.21.3'
# homehub on eth1set policy route magic-wan-ipsec-homehub default-logset policy route magic-wan-ipsec-homehub interface 'eth1'set policy route magic-wan-ipsec-homehub rule 5 description 'homehub -> gh'set policy route magic-wan-ipsec-homehub rule 5 destination address '192.168.23.10'set policy route magic-wan-ipsec-homehub rule 5 set table '20'set policy route magic-wan-ipsec-homehub rule 5 source address '192.168.21.7'set policy route magic-wan-ipsec-homehub rule 100 set table 'main'set policy route magic-wan-ipsec-homehub rule 100 source address '192.168.21.7'
# pikvmset policy route magic-wan-ipsec-pikvm default-logset policy route magic-wan-ipsec-pikvm rule 5 description 'pikvm -> gh'set policy route magic-wan-ipsec-pikvm rule 5 destination address '192.168.23.10'set policy route magic-wan-ipsec-pikvm rule 5 set table '20'set policy route magic-wan-ipsec-pikvm rule 5 source address '192.168.21.5'set policy route magic-wan-ipsec-pikvm rule 100 set table 'main'set policy route magic-wan-ipsec-pikvm rule 100 source address '192.168.21.5'
# traefik on vlan 60set policy route magic-wan-ipsec-traefik default-logset policy route magic-wan-ipsec-traefik interface 'eth1.60'set policy route magic-wan-ipsec-traefik rule 5 description 'traefik -> gh'set policy route magic-wan-ipsec-traefik rule 5 destination address '192.168.23.10'set policy route magic-wan-ipsec-traefik rule 5 set table '20'set policy route magic-wan-ipsec-traefik rule 5 source address '192.168.22.100'set policy route magic-wan-ipsec-traefik rule 100 set table 'main'set policy route magic-wan-ipsec-traefik rule 100 source address '192.168.22.100'magic-wan-ipsec-glory-holeis bound topod-podman-2; the per-host routes above include the provided interfaces (two are unset).- VyOS processes rules top-down; if you want the hairpin routes (rules 5-8) to match a reply to a raw LAN host address, place them above the catch-alls.
- Add interfaces to the workstation/pikvm policy routes if they should be interface-bound like the others.
Static routes (as provided)
Section titled “Static routes (as provided)”set protocols static table 10 route 0.0.0.0/0 interface tun0set protocols static table 20 route 0.0.0.0/0 interface vti0set protocols static table 30 route 0.0.0.0/0 interface wg05) Validation
Section titled “5) Validation”From WAN (public):
kdig @your-dot-host -p 853 +tls-host=your-dot-host +tls-ca A example.comDirect to IP (bypassing any proxy/CDN):
kdig @203.0.113.10 -p 853 +tls-host=your-dot-host +tls-ca A example.comLocal sanity:
kdig @192.168.23.10 -p 853 +tls-host=your-dot-host +tls-ca A example.comRouting sanity:
# Check IP rules (run from VyOS shell)ip rule show
# Test routing decision for glory-hole trafficsudo ip route get 8.8.8.8 from 192.168.23.10Expect lookup main via pppoe0.
If you see Network is unreachable, PBR isn’t selecting main; ensure rule 100 exists as the catch-all.
Packet capture (if issues):
# Capture on podman interfacesudo tcpdump -i pod-podman-2 -n host 192.168.23.10 and port 853
# Capture on WAN interfacesudo tcpdump -i pppoe0 -n host 203.0.113.10 and port 8536) Common pitfalls
Section titled “6) Common pitfalls”Policy routing issues
Section titled “Policy routing issues”- Catch-all ordering matters: with rule 2 as a broad catch-all, hairpin rules (5-8) never match. Move them above rule 2 or narrow rule 2’s match if a reply to a raw LAN host address has to stay on IPsec.
- Optional return-path rule: Magic WAN source-NATs hairpin traffic into 172.71.0.0/16, which the
172.64.0.0/13entry of cf-ipv4 already covers, so rule 1 sends it to table 20 and nothing extra is needed. Add a rule above the catch-all only if the translated address falls outside the Cloudflare group; otherwise those replies exit pppoe0. - Symptoms of wrong order/return path:
[UNREPLIED]in conntrack, DNS queries timeout, hairpin hosts unreachable.
Protocol notes
Section titled “Protocol notes”- DoT is TCP only; UDP 853 mapping is not required but harmless if configured
7) Troubleshooting hairpin issues
Section titled “7) Troubleshooting hairpin issues”If LAN hosts cannot reach Glory-Hole through the hairpin:
Check policy route order
Section titled “Check policy route order”# From VyOS operational modeshow policy route magic-wan-ipsec-glory-holeEnsure rules 5-8 (hairpins) sit above the catch-all(s) so they are evaluated.
Verify packet marking
Section titled “Verify packet marking”# Check nftables rules and counters (run from VyOS shell)sudo nft list ruleset | grep -B2 -A10 "magic-wan-ipsec"Check the counters for both LAN host and Glory-Hole PBR chains. The packets and bytes counters should increment when testing hairpin traffic.
Check routing tables
Section titled “Check routing tables”# From VyOS operational modeshow ip route table 20show ip route table main
# From VyOS shell - test routing decisionsudo ip route get 192.168.23.10 mark 0x7fffffebThe marked packet should use table 20 and route via vti0.
Check conntrack for unreplied connections
Section titled “Check conntrack for unreplied connections”# From VyOS shellsudo conntrack -L | grep 192.168.23.10Look for [UNREPLIED] entries, that indicates return traffic is failing. Successful hairpins show [ASSURED].
Capture traffic on VTI tunnel
Section titled “Capture traffic on VTI tunnel”# From VyOS shell - monitor IPsec tunnel trafficsudo tcpdump -i vti0 -n host 192.168.23.10From LAN host, query Glory-Hole (e.g., dig @192.168.23.10 example.com). You should see:
- Outbound:
192.168.30.20.xxxxx > 192.168.23.10.853(VTI IP -> glory-hole) - Inbound:
172.71.x.x.xxxxx > 192.168.23.10.853(Magic WAN edge -> glory-hole)
If you only see outbound, Magic WAN isn’t routing return traffic back through the tunnel.
Capture traffic on Glory-Hole interface
Section titled “Capture traffic on Glory-Hole interface”# From VyOS shell - monitor podman bridge trafficsudo tcpdump -i pod-podman-2 -n host 192.168.23.10 and port 853From LAN host, query Glory-Hole. You should see:
- Inbound: Packets with
src=172.71.x.x(Magic WAN edge IP, not LAN host IP) - Outbound: Replies to
dst=172.71.x.x
If packets arrive with src=<LAN-host-IP>, hairpin isn’t working. Traffic is routing directly instead of through Magic WAN.
Check LAN host policy routes
Section titled “Check LAN host policy routes”# From VyOS operational mode - check LAN host PBRshow policy route magic-wan-lan-host
# From VyOS shell - test LAN host routing decisionsudo ip route get 192.168.23.10 from 192.168.21.7 iif eth1The routing decision should show table 20 and vti0. If it shows direct routing via pod-podman-2, the LAN host PBR isn’t working.
Verify IPsec tunnel status
Section titled “Verify IPsec tunnel status”# From VyOS operational modeshow vpn ipsec sashow interfaces vtiEnsure IPsec tunnel is UP and passing traffic (check Bytes In/Out).
Verify Magic WAN static routes
Section titled “Verify Magic WAN static routes”In your Magic WAN dashboard, confirm:
- Both glory-hole subnet (192.168.23.0/24) and LAN subnets (192.168.21.0/24, etc.) are configured
- Routes point to the correct IPsec tunnel (check tunnel name matches)
- No conflicting routes with higher priority
- All prefixes show “Healthy” status
8) Quick reference
Section titled “8) Quick reference”Key VyOS commands
Section titled “Key VyOS commands”# Policy routingshow policy route magic-wan-ipsec-glory-holeshow policy route magic-wan-lan-host
# Routing tablesshow ip route table 20show ip route table main
# IPsec statusshow vpn ipsec sashow interfaces vti
# Network groupsshow firewall group network-group cf-ipv4Key diagnostic commands (from VyOS shell)
Section titled “Key diagnostic commands (from VyOS shell)”# Check routing decisionsudo ip route get 192.168.23.10 from 192.168.21.7 iif eth1sudo ip route get 192.168.23.10 mark 0x7fffffeb
# Check packet markingsudo nft list ruleset | grep -B2 -A10 "magic-wan-ipsec"
# Check connection trackingsudo conntrack -L | grep 192.168.23.10
# Capture trafficsudo tcpdump -i vti0 -n host 192.168.23.10sudo tcpdump -i pod-podman-2 -n host 192.168.23.10 and port 853