#[CLOSED][OpenWrt] Translating `iptables` rules to `nftables` rules

1 messages · Page 1 of 1 (latest)

eager glade
#

OS: OpenWrt 22.03 branch (git-22.245.77528-487e58a) / OpenWrt 22.03.0 r19685-512e76967f

Hardware: Netgear WNDR3700v2 https://openwrt.org/toh/netgear/wndr3700

Error / Logs: Various, will provide more information in separate messages following this

Problem/What Is Happening: Need assistance with converting iptables rules to nftables. Will also require assistance with GeoIP IP blocks but not immediately. Lots of issues with using iptables-translate as well as iptables-restore-translate of sorts as is. Again more information will be provided in separate messages following this.

Versions:
kernel: 5.10.138
nftables: 1.0.2 (Lester Gooch)
iptables: 1.8.7 (nf_tables)

What you are doing: Trying to seamlessly migrate from old OpenWrt installation to a new one, and facing major obstacles with the firewall part. This is critical in attempting to building a somewhat more hardened network infrastructure on hobbyist-level.

#
iptables -A attack -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG SYN -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "SYN scan" -j DROP

This rule doesn't translate at all via iptables-translate, what I get is,

nft # -A ATTACK -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG SYN -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment SYN scan -j DROP

Which denotes this is a comment, not a valid nft rule.

I am suspecting -m recent isn't available in nftables.

#
#

Looks like this might be the answer,

nft add rule ip filter ATTACK tcp flags syn / fin,syn,rst,psh,ack,urg counter ct state new meter portscan { ip saddr and 255.255.255.255 } counter drop

Update:

nft add rule ip filter ATTACK tcp flags syn / fin,syn,rst,psh,ack,urg counter ct state new meter portscan { ip saddr and 255.255.255.255 } counter drop comment "SYN scan"

Fixed, hopefully,

nft add rule ip filter ATTACK tcp flags syn / fin,syn,rst,psh,ack,urg ct state new meter portscan { ip saddr and 255.255.255.255 limit rate over 8/minute burst 4 packets } counter drop comment "SYN scan"
eager glade
#

Locked myself out of the router. Reminds me of what happens when you mess with firewalls like the good old days, lol. Anyhow, the thing most likely won't work as is,

nft add table ip attack
nft add chain ip attack input { type filter hook input priority 0 \; }
nft add set ip attack portscan { type ipv4_addr \; flags dynamic, timeout \; timeout 5m \; }
nft add rule ip attack input ct state established,related accept
nft add rule ip attack input tcp flags syn / fin,syn,rst,psh,ack,urg ct state new ip saddr @portscan counter drop
nft list table ip attack

Seems like a possible workaround.

eager glade
eager glade
#

Upon executing nft list table ip attack, the following is produced,

table ip attack {
        set portscan {
                type ipv4_addr
                flags dynamic,timeout
                timeout 5m
        }

        chain input {
                type filter hook input priority filter; policy accept;
                ct state established,related accept
                tcp flags syn / fin,syn,rst,psh,ack,urg ct state new ip saddr @portscan counter packets 0 bytes 0 drop
        }
}
eager glade
#

Ok, I think I figured out why it was done this (https://stackoverflow.com/a/69178498) way,

  • It needed two lists, one for temporary tracking, and the other for literal blocking via rejecting, this requires three names instead of what I had via iptables in that I only needed two, attack and portscan. With this it might need to be something like attack, portscan, bad_ipv4 instead,
  • It needs an amount before the suspects in temporary tracking list gets moved into bad_ipv4. This amount is to be specified in however many packets over a set amount of time, and
  • timeout 5m is generous for timeout, in my case it is 86400 which is in seconds. Doing some maths would reveal that I had "bad ipv4" be blocked (via drop not reject) for 24 hours.
eager glade
#

So the somewhat revised setup would be this,

nft add table ip attack
nft add chain ip attack input { type filter hook input priority 0 \; }
nft add set ip attack portscan { type ipv4_addr \; flags dynamic, timeout \; timeout 5m \; }
nft add set ip attack bad_ipv4 { type ipv4_addr \; flags dynamic, timeout \; timeout 5m \; }
nft add rule ip attack input ct state established,related accept
nft add rule ip attack input tcp flags syn / fin,syn,rst,psh,ack,urg ct state new ip saddr @bad_ipv4 counter drop
nft add rule ip attack input tcp flags syn / fin,syn,rst,psh,ack,urg ct state new ip saddr @portscan add @bad_ipv4 { ip saddr } accept
nft add rule ip attack input tcp flags syn / fin,syn,rst,psh,ack,urg ct state new limit rate over 2/minute burst 3 packets add @portscan { ip saddr } counter accept
nft list table ip attack

Which produces an output like this,

table ip attack {
        set portscan {
                type ipv4_addr
                size 65535
                flags dynamic,timeout
                timeout 5m
                elements = { 172.16.12.211 timeout 5m expires 4m49s270ms }
        }

        set bad_ipv4 {
                type ipv4_addr
                size 65535
                flags dynamic,timeout
                timeout 5m
                elements = { 172.16.12.211 timeout 5m expires 4m49s660ms }
        }

        chain input {
                type filter hook input priority filter; policy accept;
                ct state established,related accept
                tcp flags syn / fin,syn,rst,psh,ack,urg ct state new ip saddr @bad_ipv4 counter packets 14 bytes 868 drop
                tcp flags syn / fin,syn,rst,psh,ack,urg ct state new ip saddr @portscan add @bad_ipv4 { ip saddr } accept
                tcp flags syn / fin,syn,rst,psh,ack,urg ct state new limit rate over 2/minute burst 3 packets add @portscan { ip saddr } counter packets 1 bytes 60 accept
        }
}
#

That IP is my internal LAN IP, the same IP that is used to SSH into the machine. thonk the commands needs further adjusting to not like target me lol

eager glade
#

I just glossed over the rest of my old iptables rules that needs to be ported. Apart from -m recent, the rest doesn't seem to be terribly too bad to deal with,

$ iptables-translate -A attack -p icmp -m icmp --icmp-type 10 -m comment --comment "ICMP router solicitation packet" -j DROP
nft add rule ip filter attack icmp type router-solicitation counter drop comment \"ICMP router solicitation packet\"
$ iptables-translate -A forwarding_wan_rule -i eth1 -p tcp -m tcp --tcp-flags RST RST -m limit --limit 2/sec --limit-burst 2 -m comment --comment "Excessive RST" -j ACCEPT
nft add rule ip filter forwarding_wan_rule iifname "eth1" tcp flags rst / rst limit rate 2/second burst 2 packets counter accept comment \"Excessive RST\"

Which with very little modifications needed to be done to it, can be easily translated into nftables format, the GeoIP stuff…

$ iptables-translate -A pet_peeves -m set --match-set exempt src -j ACCEPT
iptables-translate v1.8.8 (nf_tables): Can't open socket to ipset.

That's kind of expected when I am running iptables-translate on my PC, rather than going through the router. There's also the fact that I also have partly https://github.com/pvxe/nftables-geoip done, in making it load discrete "country IP block ranges", as opposed to loading the entire world, needing a whopping 300MB of RAM which obviously this poor old router does not have. So it shouldn't be too hard to deal with I hope.

The most pressing issue is -m recent rules, a large chunk of the file is dedicated to filtering bad TCP packets, one for INPUT and the other for FORWARD. That, and it is only in ipv4. With ipv6 being set up, this nftables thing is about to get a whole lot messier.

#

In case anyone's wondering,

iptables -A attack -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG SYN -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "SYN scan" -j DROP
iptables -A attack -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "FIN scan" -j DROP
iptables -A attack -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG ACK -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "ACK scan" -j DROP
iptables -A attack -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,ACK,URG -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "XMAS-PSH scan" -j DROP
iptables -A attack -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "Null scan" -j DROP
iptables -A attack -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,PSH,URG -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "XMAS scan" -j DROP
iptables -A attack -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,PSH,ACK,URG -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "XMAS-ALL scan" -j DROP
iptables -A attack -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,PSH,URG -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "NMAP-ID" -j DROP
iptables -A attack -p tcp -m tcp --tcp-flags FIN,RST FIN,RST -m recent --update --seconds 86400 --name portscan --mask 255.255.255.255 --rsource -m comment --comment "FIN-RST" -j DROP
…
#

Basically years of accumulated tinhattery attitudes. Sending bad TCP packets is rare nowadays, and dealing with UDP which is stateless is a whole another can of worms to deal with.

Other than that, it's originally (now loosely) based off MonMotha's IPtables firewall script, https://web.archive.org/web/20051126134117/http://monmotha.mplug.org/firewall/index.php a bunch of rules that I have seen in other routers or by other users who have had that similar mentality. Then a whole bunch of tracking down figuring out what sort of TCP combination flags meant the type of attack it used to be.

As for GeoIP thing, http://ix.io/4cC4

… If in case you are still reading after all this rambling of mine, the issue still hasn't been addressed, but here's the link to it, #1028595131684757534 message

#

[OpenWrt] Translating iptables rules to nftables rules

eager glade
#
The Cloudflare Blog

We have been dealing with conntrack, the connection tracking layer in the Linux kernel, for years. And yet, despite the collected know-how, questions about its inner workings occasionally come up. When they do, it is hard to resist the temptation to go digging for answers.

Samuel Forestier

A not-so-complete nftables hardening guide

eager glade
#

Turns out that a typo on the iifname caused it to match packets coming from elsewhere. Here's the fix,

nft add table ip attack
nft add chain ip attack input { type filter hook input priority 0 \; }
nft add set ip attack portscan { type ipv4_addr \; flags dynamic, timeout \; timeout 5m \; }
nft add set ip attack bad_ipv4 { type ipv4_addr \; flags dynamic, timeout \; timeout 5m \; }
nft add rule ip attack input ct state established,related accept
nft add rule ip attack input tcp flags syn / fin,syn,rst,psh,ack,urg ct state new iifname != "br-lan" ip saddr @bad_ipv4 drop
nft add rule ip attack input tcp flags syn / fin,syn,rst,psh,ack,urg ct state new iifname != "br-lan" ip saddr @portscan add @bad_ipv4 { ip saddr } accept
nft add rule ip attack input tcp flags syn / fin,syn,rst,psh,ack,urg ct state new iifname != "br-lan" limit rate over 2/minute burst 3 packets add @portscan { ip saddr } accept
nft list table ip attack

Which looks something like this,

table ip attack {
        set portscan {
                type ipv4_addr
                size 65535
                flags dynamic,timeout
                timeout 5m
        }

        set bad_ipv4 {
                type ipv4_addr
                size 65535
                flags dynamic,timeout
                timeout 5m
        }

        chain input {
                type filter hook input priority filter; policy accept;
                ct state established,related accept
                tcp flags syn / fin,syn,rst,psh,ack,urg ct state new iifname != "br-lan" ip saddr @bad_ipv4 drop
                tcp flags syn / fin,syn,rst,psh,ack,urg ct state new iifname != "br-lan" ip saddr @portscan add @bad_ipv4 { ip saddr } accept
                tcp flags syn / fin,syn,rst,psh,ack,urg ct state new iifname != "br-lan" limit rate over 2/minute burst 3 packets add @portscan { ip saddr } accept
        }
}
#

The counter stuff doesn't seem to work. There's also a lot of things I have to say about how this is going to pan out, with one line per invalid TCP flag(s) in iptables translates to (currently) three lines for nftables equivalent. Not exactly sure how efficient this will be on a memory constrained machines, which has to also deal with not only IPv6, but also GeoIP stuff.

At any rate, I am going to close this post once again. If you're interested, mention me and do state that you're interested.

#

[CLOSED][OpenWrt] Translating iptables rules to nftables rules