Route all TCP traffic from port to another host:port

I have a wireguard config, creating a VPN between a remote server (10.0.1.1) and my local machine (10.0.1.2), so that the server can reach the local machine and vice versa.

I’d like the server to route all incoming TCP connections on port 8000 to my local machine.

What I tried:

This answer:

iptables -t nat -I PREROUTING -p tcp --dport 8000 -j DNAT --to-destination 10.0.1.2:8000
iptables -t nat -I POSTROUTING -p tcp --dport 8000 -d 10.0.1.2 -j MASQUERADE

And this answer (quora):

iptables -t nat -A PREROUTING -p tcp --dport 8000 -j DNAT --to-destination 10.0.1.2:8000
iptables -t nat -D POSTROUTING -j MASQUERADE

Also tried this:

iptables -t nat -A POSTROUTING -p tcp -d 10.0.1.2 --dport 8000 -j MASQUERADE
iptables -t nat -A PREROUTING -p tcp --dport 8000 -j DNAT --to-destination 10.0.1.2:8000

None of them worked (cannot reach from the internet).

Of course, I can setup an nginx server to do just that, however, it wouldn’t be as reliable and robust, as I’d like it to be (e.g. websocket connections).

The VPN connection and the firewalls are working fine: I can both curl the server on my local machine from remote server’s shell, and I can reach an HTTP server started on remote server’s 8000 port. Also, sysctl net.ipv4.ip_forward is 1.

Asked By: winwin

||

You haven’t described if you have other tools running that may be controlling the tables; e.g. firewalld. These could block your rules from occuring.

I also don’t see you talking about FORWARD rules in the main table.

And I don’t see the full iptables listing; you may have other rules that jump to DROP or REJECT earlier in the chain that are taking effect before your NAT rule.

(I describe in more detail how I built my own firewall on RHEL 7 at https://www.sweharris.org/post/2017-05-07-home-grown-router/ – this is kinda a quick summary)

In general, if starting from a blank slate and before you even start NATting, the main table needs to be set up. The INPUT chain is for packets targeting your machine, the OUTPUT chain is for packets originating on your machine, and the FORWARD chain is for packets traversing your machine.

In my case I set all three to be the same

iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate INVALID -j DROP
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate INVALID -j DROP

You may also need to allow traffic to be initiated.

iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -i internal_interface -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A FORWARD -i internal_interface -j ACCEPT
iptables -A FORWARD -i external_interface -m conntrack --ctstate DNAT -j ACCEPT

I use "external_interface", which may be an interface such as eth0, or a tunnel like tun0 for a VPN, or even a bridge (which is what I use).

This allows for traffic on the "inside" to reach the outside and for the outside to respond.

Now we can start to NAT, allowing for "inside" machines to reach the "outside"

iptables -t nat -A POSTROUTING -o external_interface -j MASQUERADE

This causes all traffic leaving the local network and heading to the outside world to be NAT’d to the address of the external interface.

Now you can start to allow for traffic from the outside to reach an internal machine behind the firewall, with a rule similar to what you’ve already tried.

e.g.

iptables -t nat -A PREROUTING -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.0.0.2:443

Some of these rules aren’t strictly necessary, but between them we get filtering of malformed packets, the ability for the inside to reach the outside (and for the outside to respond) and for the outside to reach specific services inside by port forwarding.

Answered By: Stephen Harris