The Power and Simplicity of UFW
When securing an Ubuntu server, the underlying kernel technology responsible for packet filtering is iptables (or more recently, nftables). While incredibly powerful, the syntax for these raw tools is notoriously complex and difficult for junior administrators to read. To solve this, Ubuntu introduced UFW (Uncomplicated Firewall).
UFW is an interface designed to simplify the process of configuring a firewall. While it is widely known for basic tasks—like running ufw allow ssh—it is also capable of handling advanced routing scenarios, such as Port Forwarding and Network Address Translation (NAT), which are essential when using an Ubuntu server as a gateway or a Docker host.
Step 1: Enabling IP Forwarding in the Kernel
By default, the Linux kernel will drop any packets that are not explicitly destined for its own IP address. If you want Ubuntu to act as a router (forwarding packets from one network interface to another, or from an external port to an internal IP), you must enable IP Forwarding.
Open the sysctl configuration file:
sudo nano /etc/sysctl.conf
Uncomment or add the following line:
net.ipv4.ip_forward=1
Apply the change immediately without rebooting:
sudo sysctl -p
Step 2: Configuring UFW Default Forwarding Policy
By default, UFW is configured to drop all forwarded packets. We must change this default policy in the UFW configuration file to allow traffic to pass through the routing engine.
Open the UFW configuration file:
sudo nano /etc/default/ufw
Find the DEFAULT_FORWARD_POLICY directive and change it from "DROP" to "ACCEPT":
DEFAULT_FORWARD_POLICY="ACCEPT"
Step 3: Implementing Port Forwarding (Pre-Routing)
Port forwarding requires modifying the iptables NAT table. UFW allows you to inject raw iptables rules by editing its before.rules file. This file contains rules that are evaluated before any standard UFW rules you create via the command line.
Assume your Ubuntu server has a public IP on the eth0 interface. You want to forward all incoming traffic on port 8080 to an internal web server at 10.0.0.5 on port 80.
Open the before rules file:
sudo nano /etc/ufw/before.rules
At the very top of the file (before the *filter section), add the NAT configuration:
*nat
:PREROUTING ACCEPT [0:0]
# Forward port 8080 to internal IP 10.0.0.5:80
-A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.0.0.5:80
COMMIT
Step 4: Implementing Outbound NAT / IP Masquerading
If your internal server (10.0.0.5) needs to respond to the request or access the internet, its private IP address cannot be routed over the public internet. You must configure Masquerading (Source NAT). This tells the Ubuntu gateway to replace the internal server’s source IP with its own public IP before sending the packet out.
In the same /etc/ufw/before.rules file, modify the *nat block you just created to include the POSTROUTING rule:
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# Forward port 8080 to internal IP 10.0.0.5:80
-A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.0.0.5:80
# Masquerade traffic leaving the eth0 interface
-A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
COMMIT
Step 5: Allowing the Traffic Through the Firewall
Even though you have configured the NAT translation, the actual packets must still pass through the standard UFW filter. You must explicitly tell UFW to allow traffic to reach the internal server.
Run the following command to allow the forwarded port:
sudo ufw route allow in on eth0 out on eth1 to 10.0.0.5 port 80
(Assuming eth1 is the interface connected to the internal 10.0.0.0/24 network).
Finally, reload UFW to apply all changes:
sudo ufw reload
Conclusion
While UFW is celebrated for its simplicity in handling basic port blocking, its ability to cleanly integrate raw iptables NAT rules makes it a surprisingly powerful tool. By modifying the before.rules file and enabling kernel IP forwarding, administrators can transform a standard Ubuntu server into a fully functional, highly secure NAT router and port-forwarding gateway.