Apparently while I was busy working on some other things, the world switched to nftables and no one told me.

I logged into my Linux web server the other day and realized the firewall rule set that had been hand crafted with iptables probably a decade or more ago is now completely gone. It probably got booted on the last distribution upgrade, which I remember doing and taking notes on, but don’t remember seeing anything about firewall rules.

I tried to run iptables on the system to see what was up, and the command wasn’t even there. So now, instead of working on whatever project I had planned, I’m learning nftables!

So far I was able to achieve what I wanted by converting the iptables to nft syntax and saving things into my /etc/nftables.conf. The syntax DOES seem better to me now that I understand it. As much as I hate switching things around, I always love learning something new!

Why did I need nftables?

I had to put this rule in place on my ec2 instance so I can accept SMTP incoming for my Custom Email Domain. This bypasses the outbound block from my home router and ISP on 25 (don’t tell them), by letting the mail server listen on 25537 and redirecting that to 25 locally on the same server.

There were so many blog posts and stack overflow questions about how to do this in postfix itself, and everyone pretty much settled on.. “Don’t change where postfix listens.” I tend to agree – and with this little hack, I didn’t have to.

I knew the IPTables rule I wanted was the following, so I converted it with iptables-translate

Here is the rule I’d expect to use in iptables. I could adjust this EVEN FURTHER to lock it down from only a specific IP, but my AWS security group does that already for me.

iptables -t nat -A PREROUTING -p tcp --dport 25537 -j REDIRECT --to-port 25

Since my machine didn’t HAVE iptables I couldn’t use that command directly.

To get that into a usable format I found out there was a translate utility I could use!

iptables-translate -t nat -A PREROUTING -p tcp --dport 25537 -j REDIRECT --to-port 25

And it told me:

nft add rule ip nat PREROUTING tcp dport 25537 counter redirect to :25

But that command FAILED, because with nftables no tables or chains exist at first. It’s a totally blank slate, meaning there is no nat table and no prerouting chain setup.

I did some searching and found out commands for creating the right tables and chains, then my command worked! Note the subtle difference here that removes the shouted PREROUTING you would have expected with iptables with a more sensible prerouting lowercase version.

sudo systemctl enable nftables
sudo nft add table nat
sudo nft 'add chain nat prerouting { type nat hook prerouting priority -100; }'
sudo nft add rule ip nat prerouting tcp dport 25537 counter redirect to :25

Then I’m able to print out the resulting ruleset from the config. Looks like I lied about there not being ANY tables or chains. The Ubuntu default install seems to have created the table inet filter, with the input, forward, and output chains you’d expect, but in lowercase again.

user@host:~$ sudo nft list ruleset
table inet filter {
        chain input {
                type filter hook input priority filter; policy accept;

        chain forward {
                type filter hook forward priority filter; policy accept;

        chain output {
                type filter hook output priority filter; policy accept;
table ip nat {
        chain prerouting {
                type nat hook prerouting priority dstnat; policy accept;
                tcp dport 25537 counter packets 3 bytes 180 redirect to :25

Then I rebooted my host, and everything was gone. Yeah, that’s about what I expected. Turns out they changed how persistent worked as well.

So I took the contents of the above nft ruleset and pasted it into /etc/nftables.conf

Now I can reboot my host and the rules stay in place!

That’s certainly different behavior than I was used to after learning iptables-persistent and netfilter-persistent decades ago, but It’s honestly a little easier for me to get my head around. Of course your config goes in an /etc/ file somewhere.

With that, I now have a way to relay email from a few home servers up to a mail server listening in ec2 without being blocked by my ISP’s firewall. Postfix relay might be a topic for another blog.

I suppose I could have also done this with some sort of tunnel solution as well, but what I have here works!