Hi,
I got an nftables filtering of traffic based on sets of IP address filled via DNS queries, that is similar to the fw3 ipset implementation https://openwrt.org/docs/guide-user/firewall/fw3_configurations/dns_ipset but with fw4 in 22.03.
Here the configuration, I would like to get some comment and then add a page in the wiki.
Goal : Filter traffic in fw4 based on the destination IP address of the packets, getting the list of addresses from their domain names.
Prerequisites :
- You need a firewall zone without forwarding to wan, so that no traffic to the internet is allowed by default.
- Have dig and grep installed
Overall approach : We will add a rule to the forwarding chain of the firewall zone mentioned in the prerequisite, the rule will allow traffic to specified IP addresses associated to their domains.
In /etc/rc.local add the below code to create the nft set in which we will save the IP addresses, the proposed code is ipv4 only but can be extended to cover ipv6
# Filter wildlan by IP addresses
## Create a set for "inet fw4" table with name "blackhole" that can include "ipv4_addr"
nft add set inet fw4 blackhole { type ipv4_addr \;}
## Add element to "blackhole" from file urls.txt
for address in $(dig a -f /etc/sets-ipdns/wildlan-urls.list +short | grep -v '\.$'); do
nft add element inet fw4 blackhole {$address}
done
## Allow packes in "blackhole" (all others are denied by default)
nft insert rule inet fw4 forward_wildlan position 424 ip daddr @blackhole accept
The list of domains to which traffic will be allowed shall be included in a the custom file /etc/sets-ipdns/wildlan-urls.list, to create the file
cd /etc
mkdir sets-ipdns
cd sets-ipdns
vim wildlan-urls.list
List inside vim the domain names that shall be allowed.
At this point it has to be verified the handle to which insert the new firewall rule included in rc.local, in the above example is added before the handle 424.
To verify your handle, assuming you are using the standard table inet fw4
nft -n -a list table inet fw4
This will list the chains inside inet fw4, within those you need to identify the forward chain of the firewall zone mentioned in the prerequisites. In the below example the zone is called wildlan.
chain forward_wildlan { # handle 20
ct status 0x20 accept comment "!fw4: Accept port forwards" # handle 423
jump reject_to_wildlan # handle 424
}
The command nft insert rule inet fw4 forward_wildlan position 424 ip daddr @blackhole accept
will result in
chain forward_wildlan { # handle 20
ct status 0x20 accept comment "!fw4: Accept port forwards" # handle 423
ip daddr @blackhole accept # handle 450
jump reject_to_wildlan # handle 424
}
Based on this forward chain only the traffic with destination to the IP addresses included in @blackhole will be allowed. At this stage the @blackhole sets is still empty.
The rc.local is executed at boot time, so that @blackhole will be filled with IP addresses. That set shall be periodically updated for two reasons:
- The IP addresses may change
- In case of DNS Load Balancing, the same DNS query will result in different IP addresses (all valid) based on time of request.
In the Scheduled Task in Luci or in /etc/crontabs/root we execute every 15 minutes a script to update the sets
15 * * * * /etc/sets-ipdns/update-sets.sh
In the /etc/sets-ipdns/update-sets.sh include the update of sets code from /etc/rc.local
## Add element to "blackhole" from file urls.txt
for address in $(dig a -f /etc/sets-ipdns/wildlan-urls.list +short | grep -v '\.$'); do
nft add element inet fw4 blackhole {$address}
done
Enable the script and reboot
chmod +x /etc/sets-ipdns/update-sets.sh
reboot
After the reboot, verify the content of the @blackhole nft list set inet fw4 blackhole
and the result should be a list of ipv4 addresses.
The final crosscheck is to verify that addresses listed in /etc/sets-ipdns/wildlan-urls.list can be accessed, no other domains should be accessible unless the same IP address is shared between multiple domains (that happen with CDNs).