I am running OpenWRT and attempting to force all DNS queries to OpenDNS while blocking any external DNS servers (e.g., Google 8.8.8.8, Cloudflare 1.1.1.1).
I have configured the firewall with:
DNAT rules to redirect all DNS traffic to OpenDNS (208.67.222.222, 208.67.220.220).
REJECT rules to block any DNS requests not directed to OpenDNS.
IPSet-based filtering to ensure only OpenDNS is allowed.
Problem:
Despite these settings, I am still able to resolve DNS queries using Google (8.8.8.8) and Cloudflare (1.1.1.1) when testing via nslookup from a client machine.
However, HTTP-based DoH services like https://dns.google/resolve are blocked, meaning some part of the setup is working.
Relevant Firewall Rules:
Here are the key parts of my /etc/config/firewall:
root@OpenWrt:~# cat /etc/config/firewall
config defaults
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option synflood_protect '1'
config zone
option name 'lan'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
list network 'lan'
config zone
option name 'wan'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option masq '1'
option mtu_fix '1'
list network 'wan'
config forwarding
option src 'lan'
option dest 'wan'
config rule
option name 'Allow-DHCP-Renew'
option src 'wan'
option proto 'udp'
option dest_port '68'
option target 'ACCEPT'
option family 'ipv4'
config rule
option name 'Allow-Ping'
option src 'wan'
option proto 'icmp'
option icmp_type 'echo-request'
option family 'ipv4'
option target 'ACCEPT'
config rule
option name 'Allow-IGMP'
option src 'wan'
option proto 'igmp'
option family 'ipv4'
option target 'ACCEPT'
config rule
option name 'Allow-DHCPv6'
option src 'wan'
option proto 'udp'
option dest_port '546'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-MLD'
option src 'wan'
option proto 'icmp'
option src_ip 'fe80::/10'
list icmp_type '130/0'
list icmp_type '131/0'
list icmp_type '132/0'
list icmp_type '143/0'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-ICMPv6-Input'
option src 'wan'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
list icmp_type 'router-solicitation'
list icmp_type 'neighbour-solicitation'
list icmp_type 'router-advertisement'
list icmp_type 'neighbour-advertisement'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-ICMPv6-Forward'
option src 'wan'
option dest '*'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-IPSec-ESP'
option src 'wan'
option dest 'lan'
option proto 'esp'
option target 'ACCEPT'
config rule
option name 'Allow-ISAKMP'
option src 'wan'
option dest 'lan'
option dest_port '500'
option proto 'udp'
option target 'ACCEPT'
config zone
option name 'DNS_Block'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
config forwarding
option src 'DNS_Block'
option dest 'wan'
config forwarding
option src 'lan'
option dest 'DNS_Block'
config ipset
option name 'opendns'
option match 'dst_ip'
option entry '208.67.222.222 208.67.220.220'
config rule
option name 'Bloquear DNS externo'
option src 'lan'
option dest 'wan'
option dest_port '53'
option proto 'tcp udp'
option target 'REJECT'
option ipset '!opendns'
config rule
option name 'Bloquear DNS Externo Totalmente'
option src 'lan'
option dest 'wan'
option proto 'tcp udp'
option dest_port '53'
option target 'REJECT'
config redirect
option name 'Forçar OpenDNS'
option src 'lan'
option proto 'tcp udp'
option src_dport '53'
option target 'DNAT'
option dest 'lan'
option dest_ip '192.168.1.1'
option dest_port '53'
root@OpenWrt:~#
Troubleshooting So Far:
Firewall rules appear active (nft list ruleset | grep -i "reject") confirms DNS blocking rules are in place.
HTTP-based DNS (DoH) is blocked: https://dns.google/resolve fails, and https://one.one.one.one/help/ is redirecting to OpenDNS block page (I have ruled it on blacklist on openDNS).
UDP/TCP DNS queries via nslookup still resolve with external DNS servers (Google & Cloudflare).
as long as you don't block the DNS name you're querying, a client will believe it's talking to 8.8.8.8, even if the traffic gets intercepted by the router.
block some FQDN, point it to 0.0.0.0 in your router's hosts file, if the nslookup FQDN 8.8.8.8 fails, you'll know the request was actually intercepted.
If you are concerned about DNS leaks keep the following in mind:
Firefox CLAIMS that this "protects users" but they DON'T tell you that this allows them to get a list of all sites that all firefox users access - which is of course, quite valuable marketing data which they undoubtedly are selling out the back end and making a mint on. That's why there's no easy way for users to turn this off and it's why Firefox hides these queries, to block admins like you who are attempting to force DNS to specific DNS servers by using firewall rules.
Good luck, this marketing data is quite valuable and we are going to see more applications attempting to use tricks like Firefox is using to gain access to it.
Your Intercept/ redirect rule is what is preventing you from seeing the time outs you are expecting, turn off the redirect rule temporarily and you'll see nslookup fail to resolve using e.g. 8.8.8.8. The behavior you are seeing shows that the redirect is working as intended.
Here is my current /etc/config/firewall configuration:
root@OpenWrt:~# cat /etc/config/firewall
config defaults
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option synflood_protect '1'
config zone
option name 'lan'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
list network 'lan'
config zone
option name 'wan'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option masq '1'
option mtu_fix '1'
list network 'wan'
config forwarding
option src 'lan'
option dest 'wan'
config rule
option name 'Allow-DHCP-Renew'
option src 'wan'
option proto 'udp'
option dest_port '68'
option target 'ACCEPT'
option family 'ipv4'
config rule
option name 'Allow-Ping'
option src 'wan'
option proto 'icmp'
option icmp_type 'echo-request'
option family 'ipv4'
option target 'ACCEPT'
config rule
option name 'Allow-IGMP'
option src 'wan'
option proto 'igmp'
option family 'ipv4'
option target 'ACCEPT'
config rule
option name 'Allow-DHCPv6'
option src 'wan'
option proto 'udp'
option dest_port '546'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-MLD'
option src 'wan'
option proto 'icmp'
option src_ip 'fe80::/10'
list icmp_type '130/0'
list icmp_type '131/0'
list icmp_type '132/0'
list icmp_type '143/0'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-ICMPv6-Input'
option src 'wan'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
list icmp_type 'router-solicitation'
list icmp_type 'neighbour-solicitation'
list icmp_type 'router-advertisement'
list icmp_type 'neighbour-advertisement'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-ICMPv6-Forward'
option src 'wan'
option dest '*'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-IPSec-ESP'
option src 'wan'
option dest 'lan'
option proto 'esp'
option target 'ACCEPT'
config rule
option name 'Allow-ISAKMP'
option src 'wan'
option dest 'lan'
option dest_port '500'
option proto 'udp'
option target 'ACCEPT'
config zone
option name 'DNS_Block'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
config forwarding
option src 'DNS_Block'
option dest 'wan'
config forwarding
option src 'lan'
option dest 'DNS_Block'
config ipset
option name 'opendns'
option match 'dst_ip'
option entry '208.67.222.222 208.67.220.220'
config redirect 'dns_int'
option name 'Intercept-DNS'
option family 'any'
option proto 'tcp udp'
option src 'lan'
option src_dport '53'
option target 'DNAT'
option dest_ip '208.67.222.222'
option dest_port '53'
config rule 'dot_fwd'
option name 'Deny-DoT'
option src 'lan'
option dest 'wan'
option dest_port '853'
option proto 'tcp udp'
option target 'REJECT'
# The rule below blocks all external DNS queries, but when enabled, it also blocks all internet traffic (HTTP/HTTPS, nslookup, etc.)
# config rule
# option name 'Bloquear DNS Externo Totalmente'
# option src 'lan'
# option dest 'wan'
# option proto 'tcp udp'
# option dest_port '53'
# option target 'REJECT'
root@OpenWrt:~#
Issue with the Bloquear DNS Externo Totalmente Rule
If I uncomment the rule at the end of the file, all traffic is blocked—meaning no internet access (HTTP(S) or even command-line tools like nslookup).
@brada4, I found the following file: /etc/nftables.d/10-custom-filter-chains.nft:
root@OpenWrt:~# cat /etc/nftables.d/10-custom-filter-chains.nft
## The firewall4 input, forward, and output chains are registered with
## priority `filter` (0).
## Uncomment the chains below if you want to stage rules *before* the
## default firewall input, forward, and output chains.
# chain user_pre_input {
# type filter hook input priority -1; policy accept;
# tcp dport ssh ct state new log prefix "SSH connection attempt: "
# }
#
# chain user_pre_forward {
# type filter hook forward priority -1; policy accept;
# }
#
# chain user_pre_output {
# type filter hook output priority -1; policy accept;
# }
## Uncomment the chains below if you want to stage rules *after* the
## default firewall input, forward, and output chains.
# chain user_post_input {
# type filter hook input priority 1; policy accept;
# ct state new log prefix "Firewall4 accepted ingress: "
# }
#
# chain user_post_forward {
# type filter hook forward priority 1; policy accept;
# ct state new log prefix "Firewall4 accepted forward: "
# }
#
# chain user_post_output {
# type filter hook output priority 1; policy accept;
# ct state new log prefix "Firewall4 accepted egress: "
# }
root@OpenWrt:~#
Questions:
@brada4, should I add a chain dstnat in this file as per your previous suggestions? Would that help to improve the firewall?
@tmittelstaedt, I have added a rule to block DoT (DNS over TLS), but I haven't blocked DoH (DNS over HTTPS) yet. Do you have any recommendations on effectively blocking DoH?
Final Thoughts
I am trying to prevent a teenager from bypassing content filtering during school hours.
The goal is to enforce OpenDNS for all DNS queries while blocking access to gaming and VPN/anonymizer services.
I appreciate any guidance on refining these firewall rules to ensure the best possible setup.
This depends on how smart the teen is. If he understands computers he will make a list of IP addresses of vpn/anonymizer sites and dns filtering will not work. If he's slightly less smart he will pull out his phone and turn on tethering on the phone then connect his laptop to the phone's wifi. If he's less smart than that he will "go over to a friends house to play after school" and surf the forbidden sites there.
This isn't for him. This is for YOU to make you comfortable.
As the father of 2 teens what worked the best was not letting them "work on their schoolwork" in their rooms - they had to do it in the living room, at workstations that I set up against the wall with the screens facing the center of the room. And we went over the schoolwork assignments with them after dinner and they stayed in the living room working on them until they were done, or bedtime, whichever came first.
Definitely it sort of uglified the living room but it was only until they graduated high school.
And for sure, my wife and I hated doing it. It was like going through school all over again. And it ruined our evening time especially since one of them really hated school and was enormously creative in dragging it out.
Interesting I don't recall seeing that in earlier versions. They must have taken a beating from EFF then. I wonder if it actually works. I also wonder if it's controllable via GPO.