Masquerading iptable NAT

Ubuntu Version:
Ubuntu Server 25.04

Desktop Environment (if applicable):
No Desktop

Problem Description:
After configuring NAT masquerading iptables etc everything connects right away. After reboot no devices or computers can access the internet.

Relevant System Information:
external card:
enp2s0 static ip
65.73.219.27
gateway
65.73.219.1

internal card:
enp3s0
192.168.1.2

Screenshots or Error Messages:
No errors

What I’ve Tried:
I have been following the most current instructions I could find on the web but its like the network resets or something after I reboot. Im missing something or doing something wrong or maybe something is conflicting. Not sure how netplan or networkmanager works

Im a complete newby coming from Fedora Server. With Fedora I configure my network cards within the installer. I set my second card to shared to other computers. After install I install dnsmasq, reboot and all works. Now with the newest version of Fedora Server out it wont work and I cant find anything current on it with Fedora either. I never really have to deal with it again unless I have to reinstall for some reason. I figured heck with it Ive been wanting to move to Ubuntu server for some time so now was the time as long as its down.

This is the site I referenced in configuring. It works briefly until reboot

https://tecadmin.net/configuring-nat-masquerading-with-iptables/

Can someone do a write up with a step by step or see what Im doing wrong here?

I didnt see anything on this site with regards to this… at least not current. I believe something with the network changed as of version 18
Ive searched and searched but cant come up with anything current.

Thanks in advance

1 Like

In step 4 of your link, which method are you using? After reboot is your iptables rule set empty? If not what are the rules? After reboot is /proc/sys/net/ipv4/ip_forward set?

I don’t use iptables-persistent, but lots do and like it. I use a service to execute a script which sets my iptables rules, and some other things, during boot.

iptables rules live only in RAM.
When you reboot the machine the kernel starts with an empty firewall table, so
your MASQUERADE rule and the “IP-forwarding = on” tweak vanish and the LAN
loses Internet. Two things make the setup stick:


Keep IP forwarding on at every boot

echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/30-ipforward.conf
sudo sysctl -p /etc/sysctl.d/30-ipforward.conf

Restore the NAT rule automatically

You can stay with iptables­-syntax or switch to native nftables.
25.04 already ships with nftables, so let’s use that—it’s simpler and the
service is built-in.

Create a tiny rules file:

sudo nano /etc/nftables.d/nat.nft
table ip nat {
    chain postrouting {
        type nat hook postrouting priority 100;
        oif "enp2s0" masquerade
    }
}

Enable and start the service:

sudo systemctl enable nftables
sudo nft -f /etc/nftables.d/nat.nft      # load it now

nft list ruleset should show the nat table.


Double-check netplan addresses

/etc/netplan/01-nics.yaml

network:
  version: 2
  ethernets:
    enp2s0:
      addresses: [65.73.219.27/24]
      gateway4: 65.73.219.1
      nameservers:
        addresses: [8.8.8.8, 1.1.1.1]

    enp3s0:
      addresses: [192.168.1.2/24]
sudo netplan apply

Now reboot.

  • cat /proc/sys/net/ipv4/ip_forward returns 1
  • nft list ruleset shows the masquerade rule
  • Clients on the 192.168.1.0/24 network reach the Internet right away.

If you ever prefer the old iptables-persistent method:

sudo apt install iptables-persistent
sudo iptables -t nat -A POSTROUTING -o enp2s0 -j MASQUERADE
sudo netfilter-persistent save

but nftables is the native, future-proof way on Ubuntu 24 / 25.

1 Like

Thanks for all of this.
Some of this worked. The nftables.d folder didnt exist so I had to create it and create the nat.nft file.
As soon as I did I had connectivity on the devices on the network. When I hit sudo netplan apply im getting all of these errors.
Im sure if I reboot it will probably wipe all of this.

jdkillian@server00:~$ sudo netplan apply
[sudo] password for jdkillian:

** (generate:2907): WARNING **: 22:09:57.318: Permissions for /etc/netplan/01-nics.yaml are too open. Netplan configuration should NOT be accessible by others.

** (generate:2907): WARNING **: 22:09:57.318: gateway4 has been deprecated, use default routes instead.
See the ‘Default routes’ section of the documentation for more details.

(generate:2907): GLib-WARNING **: 22:09:57.318: GError set over the top of a previous GError or uninitialized memory.
This indicates a bug in someone’s code. You must ensure an error is NULL before it’s set.
The overwriting error message was: Conflicting default route declarations for IPv4 (table: main, metric: default), first declared in enp2s0 but also in enp3s0

** (generate:2907): WARNING **: 22:09:57.318: Problem encountered while validating default route consistency.Please set up multiple routing tables and use routing-policy instead.
Error: Conflicting default route declarations for IPv4 (table: main, metric: default), first declared in enp2s0 but also in enp2s0

** (process:2905): WARNING **: 22:09:57.437: Permissions for /etc/netplan/01-nics.yaml are too open. Netplan configuration should NOT be accessible by others.

** (process:2905): WARNING **: 22:09:57.437: gateway4 has been deprecated, use default routes instead.
See the ‘Default routes’ section of the documentation for more details.

(process:2905): GLib-WARNING **: 22:09:57.437: GError set over the top of a previous GError or uninitialized memory.
This indicates a bug in someone’s code. You must ensure an error is NULL before it’s set.
The overwriting error message was: Conflicting default route declarations for IPv4 (table: main, metric: default), first declared in enp2s0 but also in enp3s0

** (process:2905): WARNING **: 22:09:57.437: Problem encountered while validating default route consistency.Please set up multiple routing tables and use routing-policy instead.
Error: Conflicting default route declarations for IPv4 (table: main, metric: default), first declared in enp2s0 but also in enp2s0

** (process:2905): WARNING **: 22:09:57.502: Permissions for /etc/netplan/01-nics.yaml are too open. Netplan configuration should NOT be accessible by others.

** (process:2905): WARNING **: 22:09:57.502: gateway4 has been deprecated, use default routes instead.
See the ‘Default routes’ section of the documentation for more details.

(process:2905): GLib-WARNING **: 22:09:57.502: GError set over the top of a previous GError or uninitialized memory.
This indicates a bug in someone’s code. You must ensure an error is NULL before it’s set.
The overwriting error message was: Conflicting default route declarations for IPv4 (table: main, metric: default), first declared in enp2s0 but also in enp3s0

** (process:2905): WARNING **: 22:09:57.502: Problem encountered while validating default route consistency.Please set up multiple routing tables and use routing-policy instead.
Error: Conflicting default route declarations for IPv4 (table: main, metric: default), first declared in enp2s0 but also in enp2s0
jdkillian@server00:~$

I fixed the first error by changing the permissions on the netplan folder to root/root 0600

As soon as I reboot all is gone and connections lost.

Outputs after reboot:

jdkillian@server00:~$ cat /proc/sys/net/ipv4/ip_forward
1

jdkillian@server00:~$ nft list ruleset
Operation not permitted (you must be root)
netlink: Error: cache initialization failed: Operation not permitted
jdkillian@server00:~$ 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 postrouting {
type nat hook postrouting priority srcnat; policy accept;
oif “enp2s0” masquerade
}
}

yes its set to 1
I can install iptables-persistent but I cant call it up from the command line

if I try to run
root@server00:~# service iptables-persistent save
iptables-persistent: unrecognized service

iptables-save > /etc/iptables.rules works.

Contents of that file (looks like too much):

Generated by iptables-save v1.8.11 (nf_tables) on Sat Jun 21 23:25:50 2025

*filter
:INPUT DROP [342:20133]
:FORWARD DROP [378:24144]
:OUTPUT ACCEPT [0:0]
:ufw-after-forward - [0:0]
:ufw-after-input - [0:0]
:ufw-after-logging-forward - [0:0]
:ufw-after-logging-input - [0:0]
:ufw-after-logging-output - [0:0]
:ufw-after-output - [0:0]
:ufw-before-forward - [0:0]
:ufw-before-input - [0:0]
:ufw-before-logging-forward - [0:0]
:ufw-before-logging-input - [0:0]
:ufw-before-logging-output - [0:0]
:ufw-before-output - [0:0]
:ufw-logging-allow - [0:0]
:ufw-logging-deny - [0:0]
:ufw-not-local - [0:0]
:ufw-reject-forward - [0:0]
:ufw-reject-input - [0:0]
:ufw-reject-output - [0:0]
:ufw-skip-to-policy-forward - [0:0]
:ufw-skip-to-policy-input - [0:0]
:ufw-skip-to-policy-output - [0:0]
:ufw-track-forward - [0:0]
:ufw-track-input - [0:0]
:ufw-track-output - [0:0]
:ufw-user-forward - [0:0]
:ufw-user-input - [0:0]
:ufw-user-limit - [0:0]
:ufw-user-limit-accept - [0:0]
:ufw-user-logging-forward - [0:0]
:ufw-user-logging-input - [0:0]
:ufw-user-logging-output - [0:0]
:ufw-user-output - [0:0]
-A INPUT -j ufw-before-logging-input
-A INPUT -j ufw-before-input
-A INPUT -j ufw-after-input
-A INPUT -j ufw-after-logging-input
-A INPUT -j ufw-reject-input
-A INPUT -j ufw-track-input
-A FORWARD -j ufw-before-logging-forward
-A FORWARD -j ufw-before-forward
-A FORWARD -j ufw-after-forward
-A FORWARD -j ufw-after-logging-forward
-A FORWARD -j ufw-reject-forward
-A FORWARD -j ufw-track-forward
-A FORWARD -i enp3s0 -o enp2s0 -j ACCEPT
-A FORWARD -i enp2s0 -o enp3s0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -j ufw-before-logging-output
-A OUTPUT -j ufw-before-output
-A OUTPUT -j ufw-after-output
-A OUTPUT -j ufw-after-logging-output
-A OUTPUT -j ufw-reject-output
-A OUTPUT -j ufw-track-output
-A ufw-after-input -p udp -m udp --dport 137 -j ufw-skip-to-policy-input
-A ufw-after-input -p udp -m udp --dport 138 -j ufw-skip-to-policy-input
-A ufw-after-input -p tcp -m tcp --dport 139 -j ufw-skip-to-policy-input
-A ufw-after-input -p tcp -m tcp --dport 445 -j ufw-skip-to-policy-input
-A ufw-after-input -p udp -m udp --dport 67 -j ufw-skip-to-policy-input
-A ufw-after-input -p udp -m udp --dport 68 -j ufw-skip-to-policy-input
-A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input
-A ufw-after-logging-forward -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW BLOCK] "
-A ufw-after-logging-input -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW BLOCK] "
-A ufw-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-forward -p icmp -m icmp --icmp-type 3 -j ACCEPT
-A ufw-before-forward -p icmp -m icmp --icmp-type 11 -j ACCEPT
-A ufw-before-forward -p icmp -m icmp --icmp-type 12 -j ACCEPT
-A ufw-before-forward -p icmp -m icmp --icmp-type 8 -j ACCEPT
-A ufw-before-forward -j ufw-user-forward
-A ufw-before-input -i lo -j ACCEPT
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-input -m conntrack --ctstate INVALID -j ufw-logging-deny
-A ufw-before-input -m conntrack --ctstate INVALID -j DROP
-A ufw-before-input -p icmp -m icmp --icmp-type 3 -j ACCEPT
-A ufw-before-input -p icmp -m icmp --icmp-type 11 -j ACCEPT
-A ufw-before-input -p icmp -m icmp --icmp-type 12 -j ACCEPT
-A ufw-before-input -p icmp -m icmp --icmp-type 8 -j ACCEPT
-A ufw-before-input -p udp -m udp --sport 67 --dport 68 -j ACCEPT
-A ufw-before-input -j ufw-not-local
-A ufw-before-input -d 224.0.0.251/32 -p udp -m udp --dport 5353 -j ACCEPT
-A ufw-before-input -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j ACCEPT
-A ufw-before-input -j ufw-user-input
-A ufw-before-output -o lo -j ACCEPT
-A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-output -j ufw-user-output
-A ufw-logging-allow -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW ALLOW] "
-A ufw-logging-deny -m conntrack --ctstate INVALID -m limit --limit 3/min --limit-burst 10 -j RETURN
-A ufw-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW BLOCK] "
-A ufw-not-local -m addrtype --dst-type LOCAL -j RETURN
-A ufw-not-local -m addrtype --dst-type MULTICAST -j RETURN
-A ufw-not-local -m addrtype --dst-type BROADCAST -j RETURN
-A ufw-not-local -m limit --limit 3/min --limit-burst 10 -j ufw-logging-deny
-A ufw-not-local -j DROP
-A ufw-skip-to-policy-forward -j DROP
-A ufw-skip-to-policy-input -j DROP
-A ufw-skip-to-policy-output -j ACCEPT
-A ufw-track-output -p tcp -m conntrack --ctstate NEW -j ACCEPT
-A ufw-track-output -p udp -m conntrack --ctstate NEW -j ACCEPT
-A ufw-user-input -p tcp -m tcp --dport 22 -j ACCEPT
-A ufw-user-input -p udp -m udp --dport 22 -j ACCEPT
-A ufw-user-limit -m limit --limit 3/min -j LOG --log-prefix "[UFW LIMIT BLOCK] "
-A ufw-user-limit -j REJECT --reject-with icmp-port-unreachable
-A ufw-user-limit-accept -j ACCEPT
COMMIT

Completed on Sat Jun 21 23:25:50 2025

Generated by iptables-save v1.8.11 (nf_tables) on Sat Jun 21 23:25:50 2025

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o eth0 -j MASQUERADE
-A POSTROUTING -o enp2s0 -j MASQUERADE
COMMIT

Completed on Sat Jun 21 23:25:50 2025

You appear to have ufw enabled. I don’t use ufw and do not like it (but many do like it). ufw is just a front end for iptables.
I have not yet migrated to nftables and still use iptables.

2 Likes

How is the firewall configured?

https://help.ubuntu.com/community/UFW

check the documentation.

Welcome jkillian

Which services do you have enabled?

systemctl status ufw
systemctl status nftables

Best to use one service that you are comfortable with. There is a way with any of them to do what you want. In this thread you appear to be dabbling into multiple services, likely leading to some unexpected behavior.

Also, if you start a service but don’t also enable it, then the service won’t run after you reboot.

Is your netplan configuration correct; interface names, addresses, etc. It also appears you may be using the deprecated gateway4 entry, doesn’t mean it won’t work but probably should switch soon to routes to default.

cd /etc/netplan
ls -al
sudo cat <yourfilename>.yaml

I am still using 22.04 on my servers, nftables on my nextcloud & wireguard servers, and ufw on a few desktops with some basic allows. I have not done anything with 25.04 yet so I may not be much help showing my setup. Plus my use-case is a bit different. I do masquerading w/nftables on the VPN server to allow tunnel interface traffic from multiple external peers through to a lan host. Opposite what you are doing.

2 Likes

I’ll also add that nftables service looks for /etc/nftables.conf when it starts up. You have a config file in /etc/nftables.d, which won’t get loaded unless you have a line

include "/etc/nftables.d/*.conf"

(or you can use the actual filename instead of *.conf)

in /etc/nftables.conf

(I’m assuming here that 25.04 does things the same way as 24.04.)

OK These are my notes I keep for myself if I ever have to redo it. They are crude but this is what ive put together and it works without fail.

ip forwarding nat masquerading in Ubuntu

check:
sysctl net.ipv4.ip_forward

&

sysctl net.ipv6.conf.all.forwarding

or
cat /proc/sys/net/ipv4/ip_forward

make sure both are

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

edit
/etc/ufw/sysctl.conf

Uncomment this to allow this host to route packets between interfaces

net/ipv4/ip_forward=1
net/ipv6/conf/default/forwarding=1
net/ipv6/conf/all/forwarding=1


sudo nft list ruleset

sudo nft flush ruleset
sudo nft add table inet filter
sudo nft add chain inet filter input ‘{type filter hook input priority 0; policy accept; }’
sudo nft add chain inet filter forward ‘{type filter hook forward priority 0; policy accept; }’
sudo nft add chain inet filter output ‘{type filter hook output priority 0; policy accept; }’

sudo nft add chain inet filter forward ‘{type filter hook forward priority 0; policy accept; }’

sudo nft add table inet nat
sudo nft add chain inet nat prerouting ‘{ type nat hook prerouting priority -100; }’
sudo nft add chain inet nat postrouting ‘{ type nat hook postrouting priority 100; }’
sudo nft add rule inet nat postrouting masquerade

  1. Configure UFW for NAT Masquerading:
    Edit /etc/default/ufw:
    Set DEFAULT_FORWARD_POLICY=“ACCEPT” to allow forwarding.
    Edit /etc/ufw/before.rules:
    Add the following rules before the COMMIT line, typically after the header comments:
    Code
    MAKE SURE THIS IS AT THE TOP AND THE “COMMIT” IS AFTER IT!!!

     *nat
     :POSTROUTING ACCEPT [0:0]
     # Masquerade traffic from your internal network
     -A POSTROUTING -s 192.168.1.0/24 ! -d 192.168.1.0/24 -j MASQUERADE
     COMMIT
    

A good read here on firewall options.
https://documentation.ubuntu.com/security/docs/security-features/network/firewall/

And on nftables specifically here.
https://wiki.nftables.org/wiki-nftables/index.php/Main_Page

If you do some reading on nftables, you will find there are warnings about using nftables alongside the legacy iptables.

It appears you may be using both still. If it’s working the way you want then good.

If you do virtual machines, might I suggest you spin up a VM and play around with nftables to start learning it? In your new VM, start by disabling iptables. UFW should be disabled by default, but you can check and disable it if running. Then create/edit an nftables.conf file under /etc/ like @irihapeti mentioned above. Start with a basic configuration. In most firewall applications, it is best to deny all incoming and then accept only what is required. Once your .conf file is set, start and enable nftables, then reboot the VM. Check your statuses, the only firewall service that should be active and running is nftables.

There are some good examples for basic nftables configurations on their site. You can either create your own nftables.conf file directly, or use nft commands to build it that way.

Happy firewalling :slight_smile:

1 Like