Pi-hole interaction with iptables/BanIP

I am thinking about the feature which could improve security in the network.
It would be nice, if pi-hole could interact with iptables firewall and create allow rule for destination (dns resolved IP) and source (client IP who requested it).
Any client in the network could then access the site only when it was resolved by the pi-hole. If any site would be accessed directly by IP, without resolving the name, it would be blocked.

Use cases:

  • any software with hard coded IPs would not be able to access the remote server via IP only. This prevents malicious code to contact Command and Control servers.
    It is unlikely that bogus/malicious services on the internet are having dns registered domain.
  • In case the malicious site does have registered dns domain, than it will be very likely filtered by pi-hole adlists or for example openwrt BanIP, which blocks a lot of IP subnets based IP sets which are updated daily.
    packages/README.md at ae7f62d637d86b8fe6ae03cdce948ab2d25ff19b · openwrt/packages · GitHub

By this feature, there will be another layer of the security.
Or maybe this concept isn't workable?
I will be glad for any inputs, comments, idea extensions...

How would Pi-hole know that this request is made? If a client goes directly to an IP address, no DNS resolution is required and Pi-hole would see none of the traffic, and thus would not be in a position to act.

All outgoing traffic would be blocked and allowed only when pi-hole create firewall exception

This is already possible, you may want to check it the following options:

conntrack
Read the Linux connection track mark associated with incoming DNS queries and set the same mark value on upstream traffic used to answer those queries. This allows traffic generated by dnsmasq to be associated with the queries which cause it, useful for bandwidth accounting and firewalling. Dnsmasq must have conntrack support compiled in and the kernel must have conntrack support included and configured.

As this feature requires kernel support, we do not include it in the pre-compiled FTL binaries. This means you will have to compile FTL from source which is, however, an easy task as you can just follow our step-by-step guide.

ipset=/<domain>[/<domain>...]/<ipset>[,<ipset>...]
Places the resolved IP addresses of queries for one or more domains in the specified Netfilter IP set. If multiple setnames are given, then the addresses are placed in each of them, subject to the limitations of an IP set (IPv4 addresses cannot be stored in an IPv6 IP set and vice versa). Domains and subdomains are matched in the same way as --address. These IP sets must already exist. See ipset(8) for more details.

(ipset is a companion application for the iptables Linux firewall)


Furthermore, FTL also contains support for the newer nftables:

nftset=/<domain>[/<domain>...]/[(6|4)#[<family>#]<table>#<set>[,[(6|4)#[<family>#]<table>#<set>]...]
Similar to the --ipset option, but accepts one or more nftables
sets to add IP addresses into. These sets must already exist. See nft (8) for more details. The family, table and set are passed directly to the nft. If the spec starts with 4# or 6# then only A or AAAA records respectively are added to the set. Since an nftset can hold only IPv4 or IPv6 addresses, this avoids errors being logged for addresses of the wrong type.

For nftset, the same restrictions as for conntrack apply (you have to compile FTL from source).

1 Like

Would you please explain this a litle more, possibly with an example on how to do this. It looks interesting enough, but I'm not sure I fully undrestand this. Thanks

Note that those conntrack, ipset and nftset options interact with components on the same machine, i.e. your firewall has to be located on the same machine, and that machine has to handle all traffic in your network (not just DNS) for the firewall to be effective for your network.

This would mean that Pi-hole either has to run on your router, or your Pi-hole host machine has to be configured as a router and be used as a gateway in your network. For the latter, you want to make sure the hardware your Pi-hole runs on is up to the job.

1 Like

This is very relevant note, and even I took this interaction requirement to run on the same system as natural thing, other may not be aware of it. Thanks for emphasizing it.

I do have no example available myself, however, a quick web search immediately finds a few things like this example for `iptset (following the Introduction links is helpful):

and this for conntrack:
https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=blob;f=contrib/conntrack/README;h=e88344751a840be9ce22f62b5f3a368b6d29200f;hb=HEAD

nftset works similar. If you not sure how ipset/nftset work in general, it is better to first look at them before trying to get the sets automagically configured through Pi-hole. It will prevent a lot of frustration because things happen differently than you may be expecting. There are a few more forum posts and guides easily found using your favorite search engine. If everything fails, we can have a look together or, probably a lot better, you can ask the dnsmasq-discuss mailing list where it is a lot more likely to find someone who has a similar configuration already running on their system.


Furthermore, let me add some emphasis:

I added a comment about this in my post above.

I am using ipset with Pi-hole for a long time. It is very performant and a lot better than what other popular script like fail2ban are doing with adding thousands of individual rules to the firewall.

The dnsmasq man page wrongly suggests that you need a domain for this to work but maybe this is only a Pi-hole enhancement. You can simply use

ipset=pihole

to add the IPs of all queries to the pihole ipset. You can see what happens as lines like ipset add ... are added to /var/log/pihole.log if you do it properly.

The ipset needs to exist, created by something like

ipset create pihole hash:ip

or you will see errors.

First, add a rule for the Pi-hole itself that allows the Pi-hole to connect to ports 53 worldwide to exclude the Pi-hole from being blocked to reach the DNS servers:

iptables -A INPUT -d 127.0.0.1 --sport 53 -m state --state ESTABLISHED -j ACCEPT
iptables -A OUTPUT -s 127.0.0.1 --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT

Finally, configure iptables to permit only addresses in that set:

iptables -I INPUT -m set --match-set pihole src -j ACCEPT
iptables -I FORWARD -m set --match-set pihole dst -j ACCEPT
iptables -I OUTPUT -m set --match-set pihole dst -j ACCEPT

and set the default policies to DROP:

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP

This configures you firewall to only permit outgoing and forwarding to and incoming from traffic to IP addresses derived via DNS.

Pro tips:

  • You can check the IP addresses with ipset list pihole
  • Do the same for IPv6 using ip6tables
  • Add Pi-hole port 53 rules for IPv6, too
  • The ipset you have created is stored in memory for speed. It will be gone after reboot. Use ipset save > /some/backup.file to backup your ipset when rebooting or your clients may experience issues when rebooting the device.
4 Likes

Wow, that is called step-by-step guide :grinning:
Much appreciated, thanks a lot.

A word of caution: I wrote this mostly from memory as I'm away from the place where I have this installation running until next year (CoVid travel ban). It is working flawlessly and I have no remote SSH access so I could not verify what I wrote is 100% accurate.

I'm going to close this feature request as the feature is already available in Pi-hole. This will release the votes submitted here so you can spend them on other requests. Please post questions to the features and @Coro's guide in the Help category.

1 Like