How to forward a local port to without SSH?

Posted on

Problem :

I am deploying a closed source service. However, due to some restrictions, its listening port can only be bind to on some port on 127.0.0.1. So, I cannot customize its code.
In the following, I am using Python’s HTTP server as an example.

python -m http.server --bind 127.0.0.1 8800

Now, only this computer can access this service.
However, I want to make everyone who can access this computer, can access this service. Just like:

python -m http.server --bind 0.0.0.0 9999

Many similar problems suggested that I use SSH forwarding to solve them.

ssh -g -L 9999:localhost:8800 -f -N user@127.0.0.1

But ssh will encrypt the data, and sshd decrypt. I’m forwarding between the same computer. Encryption and decryption are just a waste of time and resources.

Some other problems point out that I can use a reverse proxy, such as Nginx. But I don’t know what protocol this service uses (maybe UDP, or TCP but no HTTP on upper layer), I don’t know if Nginx work with this non-HTTP protocol. Besides, my supervisor didn’t allow me installing additional software.

So, is there anyway to solve this by using iptables, or any programs commonly installed in most linux distributions? Thank you.

Solution :

But I don’t know what protocol this service uses

Well, you will need to know the transport protocol no matter what method you use. Ports only exist inside a transport protocol. Sockets are created for a specific transport protocol; NAPT is done on a transport protocol level; iptables rules matching port numbers must know the protocol; etc.

Your program itself specifies which transport protocol it’s using whenever it creates the listening socket, and it’s shown as the leftmost column in netstat -ln. Most likely it’s TCP.

You don’t need to know the specific application layer protocol, though. Reverse proxies need to support HTTP because their job is to actually process the individual requests at HTTP level. But if you don’t need any such processing, then the proxy doesn’t need to support the application protocol at all – it merely needs to push the raw bytes back and forth.

So you can use various generic userspace tools:

  • Nginx indeed has a TCP proxy mode.
  • So does HAproxy, from what I remember.
  • Socat is a generic “network pipe” and can be configured to listen on one address and connect to another.
  • Netcat ‘nc’ is often preinstalled and can be launched from a systemd .socket unit.

If you want to purely stick with iptables, then you can achieve this using a DNAT rule:

-t nat -A PREROUTING -p tcp -m tcp --dport 8800 -j DNAT --dnat-to 127.0.0.1

This will however require you to enable the net.ipv4.conf.all.route_localnet sysctl, as it results in packets which have 127.0.0.1 as destination yet aren’t loopback. Such packets are dropped by default, and I think there might be security issues in allowing them (e.g. outside hosts might become able to connect to other potentially sensitive “127.0.0.1” services), so be sure you already have iptables accepting only specific port connections.

You could probably use iptables with something like:

iptables -t nat -A PREROUTING -i eth0 -p tcp 
  --dport 9999 -j DNAT --to-destination 127.0.0.1:8800

Replace eth0 with the interface you need to listen on.

You could also complain about getting tasks without the full details required to fulfill them. In order to pick a solution that is most suitable for the case, e.g. reverse proxying vs. port forwarding you’d have to know at least the protocol / API used for the communication. But you got it right that SSH tunneling is not a good choice for this!

Leave a Reply

Your email address will not be published. Required fields are marked *