how to filter dns requests with iptables

I am trying to filter the dns requests from my local network. Only authorize requests to specific dns and deny the rest, but it has not worked for me. This is my rule (with dns google example):

internal=enp2s1
external=enp2s0

iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT

echo 1 > /proc/sys/net/ipv4/ip_forward # default 0
echo 1 > /proc/sys/net/ipv6/conf/all/disable_ipv6 # default 0
echo 1 > /proc/sys/net/ipv6/conf/default/disable_ipv6 # default 0
echo 1 > /proc/sys/net/ipv6/conf/lo/disable_ipv6 # default 0

iptables -A INPUT -p all -i lo -j ACCEPT
iptables -A INPUT -s 192.168.0.10 -j ACCEPT
iptables -A OUTPUT -p all -o lo -j ACCEPT
iptables -A OUTPUT -p all -s 127.0.0.1 -j ACCEPT
iptables -t mangle -A PREROUTING -p all -i lo -j ACCEPT
iptables -t mangle -A PREROUTING -p all -s 127.0.0.1 -j ACCEPT
iptables -t nat -A PREROUTING -p all -i lo -j ACCEPT
iptables -t mangle -A PREROUTING -i lo -s 127.0.0.0/8 -j ACCEPT
iptables -t mangle -A PREROUTING -i lo -d 127.0.0.0/8 -j ACCEPT

iptables -t mangle -A PREROUTING -i $internal -s 255.255.255.0/32 -j ACCEPT
iptables -t mangle -A PREROUTING -i $internal -d 255.255.255.0/32 -j ACCEPT

iptables -t mangle -A PREROUTING -i $internal -s 192.168.0.0/24 -j ACCEPT
iptables -t mangle -A PREROUTING -i $internal -d 192.168.0.0/24 -j ACCEPT

iptables -t mangle -A PREROUTING -p udp --dport 853 -j DROP
iptables -t mangle -A PREROUTING -p tcp --dport 853 -j DROP

dns="8.8.8.8 8.8.4.4"
for ip in $dns; do
   iptables -A INPUT -s $ip -p udp --sport 53 -m state --state RELATED,ESTABLISHED -j ACCEPT
   iptables -A OUTPUT -d $ip -p udp --dport 53 -m state --state RELATED,ESTABLISHED -j ACCEPT
   iptables -A FORWARD -d $ip -p udp --dport 53 -m state --state RELATED,ESTABLISHED -j ACCEPT
done
iptables -A FORWARD -p udp --dport 53 -j REJECT

For example, if I put manually cloudflare dns on a PC on my local network (1.1.1.1 1.0.0.1) the PC has internet access

PD:

  1. The rule “-m state –state RELATED,ESTABLISHED” is in this question selected as correct
  2. The blocking rule is in this question selected as correct
  3. I have tried the same blocking rule on all chains (INPUT, Mangle, OUTPUT, FORWARD) and change REJECT with DROP and it does not block
  4. I added additional blocking rules for src and it also does not block. Example:
iptables -A FORWARD -s $ip -p udp --sport 53 -m state --state RELATED,ESTABLISHED -j ACCEPT
# and block
iptables -A FORWARD -p udp --sport 53 -j DROP

Note: These rules are also for TCP (but not to repeat them I do not put them)

Update:

I changed dns rule to:

dns="8.8.8.8 8.8.4.4"
for ip in $dns; do
   iptables -A INPUT -s $ip -p udp --sport 53 -j ACCEPT
   iptables -A OUTPUT -d $ip -p udp --dport 53 -j ACCEPT
   iptables -A FORWARD -d $ip -p udp --dport 53 -j ACCEPT
done
iptables -A INPUT -p udp --sport 53 -j DROP
iptables -A OUTPUT -p udp --dport 53 -j DROP
iptables -A FORWARD -p udp --dport 53 -j DROP

But it doesn’t do the blocking correctly either

Note: INPUT rule with or without “-m state –state RELATED,ESTABLISHED -j ACCEPT” it is irrelevant because what interests me is to block the connection and it is what does not happen

thanks

Answer

Short summary

iptables -A OUTPUT -d $ip -p udp --dport 53 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d $ip -p udp --dport 53 -m state --state RELATED,ESTABLISHED -j ACCEPT

rules should be:

iptables -A OUTPUT -d $ip -p udp --dport 53 -j ACCEPT
iptables -A FORWARD -d $ip -p udp --dport 53 -j ACCEPT

Explanation

I’ll use client IP 192.168.100.100 and port 12345 as example.

When a client (192.168.100.100) in your network sends a DNS request, it sends a UDP packet from port 12345 to DNS server’s port 53.

When the packet goes via your Linux router box, the router creates a connection tracking entry for the DNS query. This connection tracking entry has multiple states, one of them ESTABLISHED.

Now, in your original rules, you require ESTABLISHED state for the first outgoing DNS query packets. In the example case, client 192.168.100.100 sending UDP packet from port 12345 to 8.8.8.8 port 53, there is no ESTABLISHED entry for that quintuple. Therefore the firewall drops the packet.

When the match state is removed from outgoing rules, then all outgoing packets to DNS server port 53 are approved, and connection tracking entry is created properly.

Then you can match the return direction using the -m state --state ESTABLISHED, because the connection tracking entry exists.

Attribution
Source : Link , Question Author : acgbox , Answer Author : Tero Kilkanen

Leave a Comment