Using UFW for Port Forwarding Understanding the PREROUTING and POSTROUTING Chains

I don’t use iptables for port forwarding because iptables is too complicated. Many servers these days don’t even have iptables-save, and even when the server configurations are correct, sometimes they don’t work. I looked up ufw port forwarding online and found that although many commands work fine, none of them mention that you need to modify DEFAULT_FORWARD_POLICY. This is because the default forward policy is DROP, which caused me a lot of trouble. I also wanted to test the effectiveness of “-A POSTROUTING -j MASQUERADE”.(English version Translated by GPT-3.5, 返回中文)

Quick Conclusion

If you are familiar with basic vim usage, I will provide the commands directly. For those who want a quick summary, I won’t go into too much detail.

  1. First, edit /etc/default/ufw and find the DEFAULT_FORWARD_POLICY around line 18. Change its value to true.

  2. Set sysctl.conf to uncomment net.ipv4.ip_forward=1 and ensure that the value is set to 1. Then run sysctl -p to apply the changes.

  3. Edit /etc/ufw/before.rules and add the following command above *filter, usually at the top. 33810 is the listening port and 200.120.130.140:33800 is the forwarding port. If you want to support UDP, copy the -A PREROUTING xxxx command and change tcp to udp.

  4. Restart ufw.

Step-by-step Conclusion

Let’s assume I have two servers: Server A with IP 200.130.140.160 and Server B with IP 200.120.130.140. I want to access Server B’s port 33800 through Server A’s port 33810. Here are the steps to achieve this:

  1. First, edit /etc/default/ufw.

  2. Set forwarding.

  3. Set sysctl.conf.

  4. Uncomment net.ipv4.ip_forward=1 and ensure that the value is set to 1.

  5. Run sysctl -p to apply the changes.

Make sure the output contains net.ipv4.ip_forward = 1.

  1. Edit /etc/ufw/before.rules.

  2. Add the following command above *filter.

The modified file should look like this.

  1. Restart ufw.

Attention: Restarting UFW!

Restarting UFW may cause duplicate iptables rules. When using ufw reload, the existing iptables rules are not deleted, resulting in duplicate NAT rules. This can cause the rules to not take effect. You can use the following command to delete the duplicated rules. Delete them multiple times depending on the number of duplicates.

It is better not to use iptables -F, as it may cause other programs, such as Docker, to immediately lose connection.

Understanding NAT PREROUTING POSTROUTING

Before we begin

Before understanding the prerouting chain, we need to know what the IP packet looks like. Although this table does not fully represent the complete structure of a packet, we can get a rough idea.

We can see that packets are layered. The bottom layer, which is the green layer, contains the application data such as the HTTP headers. Going up, it is encapsulated with IP information. In theory, as long as we don’t encounter Network Address Translation (NAT), the source and destination IP will not change.

Of course, in reality, packets are not layered like this. They are concatenated from left to right, as shown in the example.

What are PREROUTING and POSTROUTING?

Here, we can split them into pre-routing and post-routing. Routing represents routing, and each computer has its own routing table (route print). So, prerouting means before the routing table, and postrouting means after the routing table. Each computer has its own routing table. Therefore, prerouting means that the packet comes into the network card before reaching the routing table, and postrouting means that the packet is ready to leave the computer through the network card after being judged by the routing table. Port forwarding is done in these two places.

Detailed Explanation

Let’s use port forwarding as an example. We want to request 200.130.140.160:33810 and forward it to another server 200.120.130.140:33810 using ufw. This is a simple requirement. Here are the steps:

  1. First, the packet originates from the local machine. The local machine will definitely access Server A’s port. Here is the packet (let’s assume the local machine is connected to the internet and has a public IP of 1.0.0.1. We won’t worry about whether it passes through a router or if it is an internal IP).

  2. The packet enters Server A’s ETH0 network card (at this point, the packet has not reached Server A’s routing table). The PREROUTING rule takes effect. The rule states that when TCP data comes in on port 33800, the destination IP should be changed to 200.120.130.140, and the destination port should be changed to 33810. After this, the packet looks like this.

  3. The packet enters Server A’s routing table. The routing table determines that the data is destined for 200.120.130.140. Therefore, the packet is about to leave Server A’s network card. Before that, the POSTROUTING rule takes effect. The POSTROUTING rule detects that it needs to disguise the IP. It modifies the packet to look like this: the original IP is changed to Server A’s IP, and the NAT table records that this packet will be returned to the real IP 1.0.0.1 later. Server A also opens a random port 51241.

  4. The packet officially departs from Server A and heads to Server B. After processing on Server B, the packet is ready to be sent back. At this point, the IP and port are as follows: the source port and destination port are swapped, and the packet is on its return journey.

  5. The packet returns to Server A, enters the PREROUTING rule, and Server A checks the NAT rules. It finds that this packet has been disguised by POSTROUTING and needs to restore its original IP, 1.0.0.1. At this point, port 51241 has served its purpose and is closed.

  6. Before the packet leaves Server A, during POSTROUTING (just before leaving the network card), it detects that the IP address was previously disguised and needs to be undisguised. Now, all the IPs are restored to their original values.

  7. The packet returns to the user and successfully gets the desired data. Now, it should be easier to understand.