Skip to content

Instantly share code, notes, and snippets.

@kimus
Created March 2, 2014 22:46
Show Gist options
  • Save kimus/9315140 to your computer and use it in GitHub Desktop.
Save kimus/9315140 to your computer and use it in GitHub Desktop.
NAT and FORWARD with Ubuntu’s ufw firewall

UFW

I use Ubuntu’s Uncomplicated firewall because it is available on Ubuntu and it's very simple.

Install UFW

if ufw is not installed by default be sure to install it first.

$ sudo apt-get install ufw

NAT

If you needed ufw to NAT the connections from the external interface to the internal the solution is pretty straight forward. In the file /etc/default/ufw change the parameter DEFAULT_FORWARD_POLICY

DEFAULT_FORWARD_POLICY="ACCEPT"

Also configure /etc/ufw/sysctl.conf to allow ipv4 forwarding (the parameters is commented out by default). Uncomment for ipv6 if you want.

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

The final step is to add NAT to ufw’s configuration. Add the following to /etc/ufw/before.rules just before the filter rules.

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]

# Forward traffic through eth0 - Change to match you out-interface
-A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE

# don't delete the 'COMMIT' line or these nat table rules won't
# be processed
COMMIT

Now enable the changes by restarting ufw.

$ sudo ufw disable && sudo ufw enable

FORWARD

For port forwardind just do something like this.

# NAT table rules
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# Port Forwardings
-A PREROUTING -i eth0 -p tcp --dport 22 -j DNAT --to-destination 192.168.1.10

# Forward traffic through eth0 - Change to match you out-interface
-A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE

# don't delete the 'COMMIT' line or these nat table rules won't
# be processed
COMMIT
@eapereira
Copy link

Why not to make ufw do that on command line? Like:
ufw forward 25 to 10025

@pierre-auguste
Copy link

Hello,
I did not understood that a "COMMIT" must be added at the end of each sections. (I thought only one at the end the file was enough but that's wrong)
So you have to copy all the *nat rules with the "COMMIT" and paste it just before the *filters rules. And that's it !

@waznico
Copy link

waznico commented Jan 15, 2020

The chain POSTROUTING doesn't appear in my iptables --list
My before.rules looks like

#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw-before-input
#   ufw-before-output
#   ufw-before-forward
#

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]

# Forward traffic through eth0 - Change to match you out-interface
-A POSTROUTING -s 10.66.66.0/24 -o ens0 -j MASQUERADE

# don't delete the 'COMMIT' line or these nat table rules won't
# be processed
COMMIT

# Don't delete these required lines, otherwise there will be errors
*filter

[...(Lot's of default config)...]


# allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above
# is uncommented)
-A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j ACCEPT

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT

Does someone has an idea?

Edit: My fault. It was correctly configured but my interface wasn't correct. It's also necessary to use the following command:
iptables -t nat -L -v

@digitalsigi
Copy link

Thank you so much, you made my day.
After searching trying for a couple of days I came across this post.
Works like a charm.

@soletan
Copy link

soletan commented Dec 23, 2020

FWIW there are some additional information included with man ufw-framework covering IP masquerading and port redirections. In the latter case it states to have the patch in file /etc/ufw/before.rules regarding *nat table as well as adding route for allowing that NAT traffic.

@bodz1lla
Copy link

Quick and fast ufw manual, many thanks!

@radshop
Copy link

radshop commented May 2, 2021

Dude! There's never been a man so happy to get a Hello World. I've been trying to solve this with ufw allow route rules, but this is the ticket.

@SomwareHR
Copy link

Thank you! This save me quite a time :D
Just to add one tipical line from UFW log:

MMM DD HH:MM:SS $HOSTNAME kernel: [ $timestamp] [UFW BLOCK] IN=ethWAN OUT= MAC=01:02:03:04:05:06:07:08:09:01:02:03:04:05 SRC=$LANclient DST=$somethingsomething LEN=71 TOS=0x00 PREC=0x00 TTL=128 ID=40423 DF PROTO=TCP SPT=54306 DPT=3128 WINDOW=1021 RES=

BTW, ICMP was working fine during this blockage.

@Sturmkater
Copy link

Sturmkater commented Jul 18, 2021

Hi guys, somehow when I ping it says it's not reachable. What I try to accomplish: traffic coming in from ppp0 on UDP port 53 should be sent over ens3 to ip 192.168.1.2

are my settings correct for that?

# NAT table rules
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# Port Forwardings
-A PREROUTING -i ppp0 -p udp --dport 53 -j DNAT --to-destination 192.168.1.2

# Forward traffic through eth0 - Change to match you out-interface
-A POSTROUTING -s 192.168.1.2 -o ens3 -j MASQUERADE

# don't delete the 'COMMIT' line or these nat table rules won't
# be processed
COMMIT

@duffyduck00
Copy link

duffyduck00 commented Dec 3, 2021

almost everything is working fine - i am using a vpn connection .. i just changed the line
-A POSTROUTING -s 192.168.137.0/24 -o eth0 -j MASQUERADE to -A POSTROUTING -s 192.168.137.0/24 -o tun0 -j MASQUERADE
so if the vpn connection is lost, no traffic will reach internet ... thats how i would like it.

the only thing what is not working till now, is dns through 192.168.137.1 (which is the ip address to the local network). i have to enter dns address 1.1.1.1 at all my clients - but i want to use dns address 192.168.137.1 at all my clients.
i tried portforwarding for port 53

-A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to-destination 192.168.137.1

... but it doesnt work.

any ideas how to make this work ?

@duffyduck00
Copy link

duffyduck00 commented Jan 24, 2022

everything is workine fine now ... after i installed dhcp server on my router pc

@h6w
Copy link

h6w commented Jan 25, 2022

Instead of forwarding ports in before.rules, UFW now supports forwarding itself.

e.g. the command above

-A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to-destination 192.168.137.1

can instead be done along with other ufw commands on the command line

ufw route add in on eth0 to 192.168.137.1 port 53 proto udp

Or even, add an application file in /etc/ufw/applications.d/dns which contains:

[DNS]
title=DNS Server
description=Standard DNS Server
ports=53/udp

and then go:

ufw route add in on eth0 to 192.168.137.1 app DNS

The real advantage to this is that ufw status will show you (almost) everything that's going on, and not hide things away in before.rules!

Status: active

To                         Action      From
--                         ------      ----
192.168.137.1             ALLOW FWD   DNS on eth0

@nettoinfo
Copy link

nettoinfo commented Nov 11, 2022

Portuguese:
Pessoal, muito bom esse tutorial, parabéns!
Eu quero redirecionar a porta 443 para 8443, porque o Tomcat não trabalha com portas menores que 1024....
No FirewallD seria assim: /sbin/iptables -A PREROUTING -t nat -i ens160 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 8443
No UFW, como eu faria isso?

English (I'm sorry! - Thank you, Google Translator)
Guys, this tutorial is very good, congratulations!
I want to redirect port 443 to 8443, because Tomcat doesn't work with ports smaller than 1024....
In FirewallD it would look like this: /sbin/iptables -A PREROUTING -t nat -i ens160 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 8443
No UFW, how would I do that?

-------------------------------------------------- How resolve
Edit /etc/ufw/before.rules and add this:

*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
-A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
COMMIT

@aceqbaceq
Copy link

hello, thanks for the vauable info.
however i want to underline that if you enable NAT as desribed above you get a problem. when you restart uwf via

systemctl restart ufw

or

ufw reload

you notice that your iptables nat rules start to clone like that

iptables-save

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [66:3560]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
**-A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE**
COMMIT

ufw reload

iptables-save

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [66:3560]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
**-A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE
-A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE**

COMMIT

etc.

i have found the reason of such behavior = https://bugs.launchpad.net/ufw/+bug/364558
so i ve taken the solution

# cat /etc/default/ufw | grep MANAG
MANAGE_BUILTINS=yes

Hope it helps !

@yoyosbg
Copy link

yoyosbg commented Apr 13, 2023

Not straight forward at all!
I expect use simple "ufw deny 3306" for NAT, after some configuration.

@ktechmidas
Copy link

@yoyosbg some ancient code of mine still works I think :)

https://github.com/Monotoko/iptables-nat-helper

@yoyosbg
Copy link

yoyosbg commented May 5, 2023

@yoyosbg some ancient code of mine still works I think :)

https://github.com/Monotoko/iptables-nat-helper

thanks

@friki67
Copy link

friki67 commented May 22, 2023

Hello! I'm trying to set up a VPN gateway (using LXD containers). I'm using ufw for killswitch service. Almost all is working except for port forwarding.


             ┌─────────────────────────┐                  ┌──────────────────────────┐
             │   SECOND CONTAINER      │                  │   OVPN CLIENT CONTAINER  │
             │                         │                  │                          │                ┌─────────────┐
             │     192.168.1.3         │       LAN        │       192.168.1.2        │    VPN TUNNEL  │             │
             │  gw:192.168.1.2     eth0├──────────────────┤eth0                 tun0 ├────────────────┤  VPN NODE   │
             │                         │                  │                          │                │             │
             │ HTTP test server in     │                  │                          │                └──┬──────────┘
             │        8107 port        │                  │                          │                   │
             └─────────────────────────┘                  └──────────────────────────┘                   │
                                                                                                      xxxxxxxxxxxx
                                                                                                   xxxx          xxx
                                                                                                xxx                x
                                                                                                 xx    INTERNET    x
                                                                                                   xxx           xxx
                                                                                                     xxxx    xxxx
                                                                                                        xxxxx

In my VPN gateway I have:

ufw rules

192.168.1.0/24                ALLOW       Anywhere

192.168.1.0/24                ALLOW OUT   Anywhere
Anywhere                      ALLOW OUT   Anywhere on tun0
<myvpnremoteip> 1194/tcp      ALLOW OUT   Anywhere #vpn remote address

before.rules extract:

*nat
:POSTROUTING ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]
-A PREROUTING -i eth0 -p tcp --dport 8701 -j  DNAT --to-destination 192.168.1.3
-A POSTROUTING -s 192.168.1.0/24 -o tun0 -j MASQUERADE
COMMIT

I can access http://192.168.1.3:8701 directly, but I cannot access http://192.168.1.2:8107

So, what am I doing wrong?

@meirg
Copy link

meirg commented Oct 19, 2023

@bluebaroncanada

Is there a way to do this without changing the default policy?

Yes, this is certainly preferable. You can add this at the end of the filter rules in /etc/ufw/before.rules (before the COMMIT)
-A ufw-before-forward -i eth0 -p tcp --dport 22 -j ACCEPT

@phlplowe9
Copy link

Nice simple guide but I spent days struggling to make it work.

The problem is ufw will append all of the rules to the iptables chains every time ufw is reloaded or started/stopped. So you end up with lots of copies of the same thing. Or in my case lots of permutations of slightly different things to test, none of which were applying since they were further down.

you should have a -F before the first -A to flush all of the old rules out first.

@casesolved-co-uk
Copy link

casesolved-co-uk commented Aug 26, 2024

Does anyone know if:
net.ipv4.ip_forward=1
and

ufw route allow in on lxdbr0
ufw route allow out on lxdbr0

are interchangeable?
It seems one enables global routing while the other is more specific to the internal bridge interface

@casesolved-co-uk
Copy link

Can can confirm a -F first flushes the nat table before inserting the rules using the -A rules on a ufw reload.
Preferable to MANAGE_BUILTINS=yes

@casesolved-co-uk
Copy link

Does anyone know if: net.ipv4.ip_forward=1 and

ufw route allow in on lxdbr0
ufw route allow out on lxdbr0

are interchangeable? It seems one enables global routing while the other is more specific to the internal bridge interface

No, these are not interchangeable.
I think the former allows the kernel to action the routing table, while iptables controls/filters the packets that hit the routing table

@dsossna
Copy link

dsossna commented Sep 4, 2024

Hello,

I am trying to forward the traffic from a router on a stick at my pi ethernet to the wlan port at the pi. The problem is, that the packeges are not transfered to the wlan port or the translation to the external ip is not working. So for what is the parameter -s. If I type my vlan networks to it I see the ionternal IP external. I anabled ipv4 routing and the routing between the differen subnets is working fine.


| |
| Pi Ethernet with 5 VLAN
| Pi Wlan Dongle with DHCP
| The configuration above
| in ufw before.rules


@casesolved-co-uk
Copy link

casesolved-co-uk commented Sep 4, 2024

Hello,

I am trying to forward the traffic from a router on a stick at my pi ethernet to the wlan port at the pi. The problem is, that the packeges are not transfered to the wlan port or the translation to the external ip is not working. So for what is the parameter -s. If I type my vlan networks to ist I see the IP external. I anabled ipv4 routing and the routing between the differen subnets is working fine.

@dsossna
The -s in -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE means the source network for outbound NAT (making it look like your internal traffic comes from your public IP).
However these commands are only for NAT, either whole IP or individual ports. It sounds like you just want normal routing without any NAT, so net.ipv4.ip_forward=1 should be enough. A WLAN port won't have a public IP so NAT is not necessary. If routing between vlans is working, the WLAN interface should be the same, it is just another network.

@dsossna
Copy link

dsossna commented Sep 4, 2024

@casesolved-co-uk
No I need nat, because my Pi is behind a wlan access point that routes the traffik to the internet. I can not establish static routes back to the vlan networks. If I try to ping the wlan access points IP iget the answer time fault. This is a typical error if the reverse route is not available.
You are wright that normaly the icmp has no problem with a local connected subnet. But if I ping from a computer at a vlan subnet the router has to nat this because the router in front do not know the subnet

computer ----pi vlan port----Pi wlan port-----GLI Net router with nat to the Internet

@casesolved-co-uk
Copy link

casesolved-co-uk commented Sep 4, 2024

@dsossna
Then there are two ways to solve it:

  1. Add routes to your internet router so that it knows about all your other networks and the pi gateway and ignore internal NAT (recommended). I would try to put all your networks on the same larger subnet (e.g. 10.0.0.0/8) and subnet these onto your vlans (10.0.1.0/24) so your router only has to NAT a single network.
  2. NAT overload IPs on your pi (which is what you are attempting). However, having double nested NAT isn't necessary or recommended and is prone to issues like you're seeing

@dsossna
Copy link

dsossna commented Sep 4, 2024

@casesolved-co-uk
Yes I know that doble nat ios not nassasary, but this is an shool network where I have no internet, only with my GLI Spitz Wlan Hotspot I have Internet with gsm modem in the router. I installed the Pi as a router for the local practise networks for my students. I am using linux as a nat router for a long time and so I do not understand why this constallation is not working. When I am in other places there are subnets with firewall and routing, so I know how to translate the traffic. I normaly use this vommand for nat. iptables -t nat -A POSTROUTING -o enp0sx -j MASQUERADE and this is working fine. Now my wlan network card name is very long in bookworm, but this could not be the reason

@dsossna
Copy link

dsossna commented Sep 5, 2024

I solved the problem. It was a mistake in the Name of the wlan interface

@coding-linux
Copy link

my ufw router configuration:

#eth0=WAN (wired WAN)
#eth1=LAN (wired LAN)
#wlan0=LAN (wifi LAN)

sudo apt update
sudo apt dist-upgrade
sudo apt install nano iptables dnsmasq ufw


sudo nano /etc/network/interfaces:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
address 192.168.1.10
netmask 255.255.255.0
broadcast 192.168.1.255

auto eth1
iface eth1 inet static
address 192.168.10.1
netmask 255.255.255.0
broadcast 192.168.10.255


sudo nano /etc/dnsmasq.conf

interface=eth1
listen-address=192.168.10.1
bind-interfaces
domain-needed
bogus-priv
dhcp-range=192.168.10.50,192.168.10.80,24h


sudo nano /etc/sysctl.conf:

net.ipv4.ip_forward=1

or for ufw only:

sudo nano /etc/ufw/sysctl.conf:

net.ipv4.ip_forward=1


sudo nano /etc/default/ufw:

#Set the default input policy to ACCEPT, DROP, or REJECT. Please note that if
#you change this you will most likely want to adjust your rules.
DEFAULT_INPUT_POLICY="DROP"

#Set the default output policy to ACCEPT, DROP, or REJECT. Please note that if
#you change this you will most likely want to adjust your rules.
DEFAULT_OUTPUT_POLICY="ACCEPT"

#Set the default forward policy to ACCEPT, DROP or REJECT. Please note that
#if you change this you will most likely want to adjust your rules
DEFAULT_FORWARD_POLICY="ACCEPT"


sudo nano /etc/ufw/before.rules:

#rules.before
#Rules that should be run before the ufw command line added rules. Custom
#rules should be added to one of these chains:
#ufw-before-input
#ufw-before-output
#ufw-before-forward

#Don't delete these required lines, otherwise there will be errors
#NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]

#Forward traffic through eth0 - Change to match you out-interface
-A POSTROUTING -o eth0 -j MASQUERADE

#don't delete the 'COMMIT' line or these nat table rules won't
#be processed
COMMIT

*filter
:ufw-before-input - [0:0]
:ufw-before-output - [0:0]
:ufw-before-forward - [0:0]
:ufw-not-local - [0:0]
#End required lines

#allow all on loopback
-A ufw-before-input -i lo -j ACCEPT
-A ufw-before-output -o lo -j ACCEPT

#quickly process packets for which we already have a connection
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ufw-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

#drop INVALID packets (logs these in loglevel medium and higher)
-A ufw-before-input -m conntrack --ctstate INVALID -j ufw-logging-deny
-A ufw-before-input -m conntrack --ctstate INVALID -j DROP

#ok icmp codes for INPUT
-A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-input -p icmp --icmp-type echo-request -j DROP

#ok icmp code for FORWARD
-A ufw-before-forward -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type echo-request -j ACCEPT

#allow dhcp client to work

-A ufw-before-input -i eth1 -p udp --dport 67 -j ACCEPT
-A ufw-before-input -i eth1 -p udp --dport 68 -j ACCEPT

-A ufw-before-input -i eth1 -p udp --dport 53 -j ACCEPT
-A ufw-before-input -i eth1 -p tcp --dport 53 -j ACCEPT

-A ufw-before-input -i eth1 -p tcp --dport 22 -j ACCEPT
-A ufw-before-input -p tcp --dport 22 -j DROP

#ufw-not-local
-A ufw-before-input -j ufw-not-local

#if LOCAL, RETURN
-A ufw-not-local -m addrtype --dst-type LOCAL -j RETURN

#if MULTICAST, RETURN
-A ufw-not-local -m addrtype --dst-type MULTICAST -j RETURN

#if BROADCAST, RETURN
-A ufw-not-local -m addrtype --dst-type BROADCAST -j RETURN

#all other non-local packets are dropped
-A ufw-not-local -m limit --limit 3/min --limit-burst 10 -j ufw-logging-deny
-A ufw-not-local -j DROP

#allow MULTICAST mDNS for service discovery (be sure the MULTICAST line above
#is uncommented)
-A ufw-before-input -p udp -d 224.0.0.251 --dport 5353 -j DROP

#allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above
#is uncommented)
-A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j DROP

#don't delete the 'COMMIT' line or these rules won't be processed
COMMIT


sudo ufw disable
sudo ufw enable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment