How to port forward IPv4 traffic using ufw on Ubuntu CLI

This is a detailed guide on how to port forward IP traffic on Ubuntu CLI.

Warning: Please make sure that you have access to the device you are working on as making changes to the ufw could potentially lock you out of your machine if working remotely.

Please note this guide also covers Masquerading private IP traffic outbound.

1. nano /etc/default/ufw

Enable packet forwarding by editing DEFAULT_FORWARD_POLICY=”ACCEPT”

root@test:~# nano /etc/default/ufw
# 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"

2. nano /etc/ufw/sysctl.conf

Uncomment net/ipv4/ip_forward=1 (remove the # symbol)

# 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

3. nano /etc/ufw/before.rules 

Add the following to /etc/ufw/before.rules 

This will need to be added to the top of the file – please see example below.

Make sure you specify the source subnet you are wanting to NAT and the destination interface where your Public IP address is configured. The example below is 10.0.125.0/24 (source) and destination interface is eth1.

The port forward part of this guide is the following addition to the file:

-A PREROUTING -i eth1 -d 91.203.x.x -p tcp –dport 2200 -j DNAT –to-destination 10.0.125.10:22

This line forwards traffic connecting to public IP address and port 91.203.x.x:2200 to private IP address and port 10.0.125.10:22

# nat Table rules
*nat
:POSTROUTING ACCEPT [0:0]

# Forward traffic from eth1 through eth0.
-A POSTROUTING -s 10.0.125.0/24 -o eth1 -j MASQUERADE
-A PREROUTING -i eth1 -d 91.203.x.x  -p tcp --dport 2200 -j  DNAT --to-destination 10.0.125.10:22
# don't delete the 'COMMIT' line or these nat table rules won't be processed
COMMIT

IMPORTANT: I have added the whole file as a reference below , so you can see the positioning of the lines.

Example:

root@test:~# cat /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
#

# nat Table rules
*nat
:POSTROUTING ACCEPT [0:0]

# Forward traffic from eth1 through eth0.
-A POSTROUTING -s 10.0.125.0/24 -o eth1 -j MASQUERADE
-A PREROUTING -i eth1 -d 91.203.x.x  -p tcp --dport 2200 -j  DNAT --to-destination 10.0.125.10:22
# 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
: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 source-quench -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 ACCEPT

# 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 source-quench -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 -p udp --sport 67 --dport 68 -j ACCEPT

#
# 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 ACCEPT

# 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

4. ufw disable && ufw enable

root@test:~# ufw disable && ufw enable
Firewall stopped and disabled on system startup
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
root@test:~#

5. Check that the configuration is correct

You can check the ufw policy using ” iptables -t nat -L -v”

Check for DNAT (destination NAT)

root@test:~# iptables -t nat -L -v
Chain PREROUTING (policy ACCEPT 196 packets, 10492 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    1    52 DNAT       tcp  --  eth1   any     anywhere             91-203-x-x

7. Troubleshoot using tcpdump

Make sure traffic is traversing the Ubuntu device where you have configured the Port Forward.

root@test:~# tcpdump -i eth1 -c 200 port 2200
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes

If you are new to the world of Linux, an avid Linux enthusiast or a student why not try our 0.99p per month Linux VPS.

Simply click on the screen shot below to find out more or navigate to https://piggybank.cloud

Thank you for reading and please feel free to leave any feedback.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s