Redirect DNS Via iptables

Background

I've been stuggling to get a redirection working via iptables - a combination of restrictive capabilities of the s/w versions on DD-WRT and (more likely) my shoddy script.

We have a very limited bandwidth out to the internet on the network in question, and so I've deployed a number of measures to make utilisation more efficient e.g. ad blocking & replacement technology, traffic filtering, QoS prioritisation

I have the ISP-supplied router as the outer router, and an inner router running WiFi, LANs & VPN out to the internet.

Specific Problem

Among the list of enhancements is DNS caching on a dedicated server node, and redirection of all DNS requests to this server. Most of this is done via network settings in the dhcp servers, but some mobile & desktop apps are overriding this.

I use DNS upstream servers as targets from the LAN DNS server which I know are trustworthy. The inner router has a firewall script which is designed to apply a vpn killswitch and allow sticky IP's access outside of the VPN / straight out via our ISP (thanks a bunch Netflix).

However I'm struggling to get the DNS traffic redirection working on DD-WRT - I tested it on a combination of servers and desktops using dig and tcpdump, simulating a request being made to server A, redirected to server B, and the client getting a DNS response accordingly. That appeared to work as expected, but when I applied the same configuration to the inner router it has no effect. I can't see any traffic arriving at the DNS server when I fire a request out to a public resolver e.g. Cloudflare.

In fact the request is still blocked.

lanDns="dns server IP"
iptables -I FORWARD -s ! $lanDns -p tcp --dport 53 -j ACCEPT
iptables -I FORWARD -s ! $lanDns -p udp --dport 53 -j ACCEPT
iptables -t nat -A PREROUTING -s ! $lanDns -p tcp --dport 53 -j DNAT --to $lanDns:53
iptables -t nat -A PREROUTING -s ! $lanDns -p udp --dport 53 -j DNAT --to $lanDns:53
iptables -I FORWARD -s ! $lanDns -p tcp --dport 5353 -j ACCEPT
iptables -I FORWARD -s ! $lanDns -p udp --dport 5353 -j ACCEPT
iptables -t nat -A PREROUTING -s ! $lanDns -p tcp --dport 5353 -j DNAT --to $lanDns:53
iptables -t nat -A PREROUTING -s ! $lanDns -p udp --dport 5353 -j DNAT --to $lanDns:53
# secure DNS - TODO letsencrypt certificate for <servername>
iptables -t nat -A PREROUTING -s ! $lanDns -p tcp --dport 853 -j DNAT --to $lanDns:853
# allow direct DNS traffic from the LAN DNS server, don't need to drop as well as this will only interfere
iptables -A FORWARD -p tcp --dport 53 -s $lanDns -j ACCEPT
iptables -A FORWARD -p udp --dport 53 -s $lanDns -j ACCEPT

iptables output looks ok, but what am I missing? Is it as simple as ordering or just my poor iptables usage? :)

Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DNAT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:53 to:[DNS server ip]
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:53 to:[DNS server ip]
DNAT tcp -- ![DNS server ip] 0.0.0.0/0 tcp dpt:53 to:[DNS server ip]:53
DNAT udp -- ![DNS server ip] 0.0.0.0/0 udp dpt:53 to:[DNS server ip]:53
DNAT tcp -- ![DNS server ip] 0.0.0.0/0 tcp dpt:5353 to:[DNS server ip]:53
DNAT udp -- ![DNS server ip] 0.0.0.0/0 udp dpt:5353 to:[DNS server ip]:53 

iptables version on DD-WRT (Kong Ac): 1.3.7

Update

Thanks to a comment by davidgo it appears that the requests are being redirected correctly, but the responses are getting as far as the router (but not being send back to the requestor). Will experiment with rule ordering a little more and update if progress - support welcome if that makes the problem easier to identify

Alternative Solutions Not Appropriate Here

I acknowledge that the following are good solutions, but are not appropriate in the context of this specific scenario:

  • PiHole - other nodes are delivering similar capabilities
  • Replacing router
  • Replacing DD-WRT firmware with a.n. other firmware distro
  • Non-Linux solutions
  • Closed-source solutions
7

1 Answer

Credit to davidgo for the hints in the right direction to solve this. What I hadn't spotted previously was that the requests were being redirected to the right end point, but the responses were not being passed back to the requester.

To solve that I used a postrouting command, and I'd also changed the order and alterered from append to inserts. The code excerpt below was also then used successfully to redirect tcp requests over 853 (sDNS), it's also part of a loop to reduce code duplication.

# declarations to make the excerpt make more sense
lanDns="LAN server IP"
dnsPorts="53 5353"
for dnsPort in $dnsPorts; do iptables -t nat -I POSTROUTING -s ! $lanDns -p udp --dport $dnsPort -d $lanDns -j MASQUERADE
done

You don't necassarily want to try and redirect everything that could be DNS as often unusual failed DNS requests can be used to trace malware on your network. Having your own local DNS can really help spot unwelcome activity.

Additional

I found it useful to find my shoddy coding using || logger -p daemon.error "Error code $? for 4" at the end of each iptables command - replace 4 with some unique line indicator. DD-WRT makes it even harder in that these logged items don't get shunted through remote logging, so you can't watch them on your log server.

You still have to SSH onto the router and grep for the messages.

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like