Iptables rules for allowing specific DNS requests from external to internal network

domain-name-systemiptables

I have a network split into 4 parts: an external DMZ, an internal DMZ, a userspace and a cluster.

I also have 4 firewalls: a boundary router between the Internet and the external DMZ, a cluster front-end between the external DMZ and the cluster, a main firewall between the external and internal DMZs, and an internal firewall between the internal DMZ and the userspace.

There is an external DNS server in the external DMZ, and an internal DNS server in the internal DMZ.

My goal is to use iptables rules to allow or block specific queries. I want queries from the Internet to be dealt with by the external DNS server, but only allow resolving names for the external DMZ. In other words, I want to block these queries from reaching the internal DNS server. However, I want to allow all queries coming from the cluster (dealt with by the external DNS server), as well as all queries coming from the userspace (dealt with by the internal DNS server).

For instance, I have a web server in the internal DMZ. I want the cluster to be able to access it (which means the internal DNS server would have to answer a request originating from the cluster), but I want to block its access (or rather, hide it?) from the Internet. Since queries from the Internet are directed to the external DNS server, I want to block these queries from passing through the main firewall.

Is that possible with iptables rules only? I am having trouble coming up with something to allow only the cluster queries to reach to internal DNS server.

Here is what I am currently using (for each firewall, eth0 faces towards the Internet while eth1 faces the other side, "inwards").

On the boundary router:

iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to $extdns
iptables -A FORWARD -p udp -d $extdns --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -p udp -s $extdns --sport 53 -m state --state ESTABLISHED -j ACCEPT

iptables -A INPUT -p udp -s $extdns --sport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p udp -d $extdns --dport 53 -m state --state ESTABLISHED -j ACCEPT

On the cluster front-end:

iptables -t nat -A PREROUTING -i eth1 -p udp --dport 53 -j DNAT --to $extdns
iptables -A FORWARD -p udp -d $extdns --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -p udp -s $extdns --sport 53 -m state --state ESTABLISHED -j ACCEPT

On the main firewall:

iptables -A INPUT -p udp -s $intdns --sport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p udp -d $intdns --dport 53 -m state --state ESTABLISHED -j ACCEPT

On the internal firewall:

iptables -t nat -A PREROUTING -i eth1 -p udp --dport 53 -j DNAT --to $intdns
iptables -A FORWARD -p udp -d $intdns --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -p udp -s $intdns --sport 53 -m state --state ESTABLISHED -j ACCEPT

The default policy is set to drop packets (INPUT, FORWARD and OUTPUT).

Best Answer

I suspect you might need to use more than just iptables.

What you have now is something dealing with DNS flows, but not the actual queries.

In terms of DNS flows, your rules and setup look right to me.

However, this might be a case where you really want to have some defenses built into your DNS config - for example, restrict which ranges are allowed to query a given server, and for what (e.g. recursively or not).

An example of the kind of queries you specifically mean to restrict would be useful, but since much of your question seems to revolve around queries, I have to suspect you need to have some kind of application-level mechanism (i.e. configure each 'type' of DNS server according to what it is allowed to do).

Updated based on your example, iptables could be enough to achieve what you want, mainly by ensuring no queries from the external DMZ can reach any internal servers.

You may want to spend some time looking into types of DNS servers, as well as looking into the client/query restrictions you can put in place.

I can't really design your setup for you, but:

  • you have an internal DNS in your internal DMZ. The simplest thing to do would be to have that server be authoritative for your internal zone
  • if your internal hosts need to be able to resolve things outside your internal domain, then you will need to forward those queries to the external DMZ DNS server.
  • the external DNS DMZ server can then (if needed) have an authoritative zone for any required public records, and be configured to forward anything not in your domain to the outside world.

Do note these are two different roles/types of server, though, and that you may want to actually have separate processes for each (powerdns to a point 'encourages' this by disallowing authoritative and recursive from being done by the same process, if I recall correctly - and have a dedicated binary for each).

You would achieve the control you wanted by:

  • disallowing ANY queries from your external DMZ to internal servers
  • allow queries to your external DMZ servers only from the internal DNS server (and consider splitting the internal DNS into one forwarding server, and one authoritative).
  • disallowing ANY queries from outside your network to your external DNS server OR allowing queries from outside your networks to only look up the authoritative records you need to have there.

The key difference between iptables and spending some time on your DNS structure is that iptables blocks everything, or nothing (because the traffic is allowed or not). By configuring DNS to match your specific needs, you can have much more granular control, that relies on 'awareness' of the application (i.e. you can make decisions based on the request content).

I am basically saying it might be worth doing defense in depth, but that your firewall rules sound like they should handle the basic need. Setting DNS up to only work in the way you describe is definitely on top of firewall, not instead of firewall.