Hold Tight...

0 %
Driving cybersecurity excellence
Innovator behind Kodachi OS
  • Residence:
    ::0
  • Uptime Binary:
    110001
  • Mantra:
    Innovate, Secure, Repeat
ONS
EEDS
NSSG
Cybersecurity
Programming & R&D
Cryptocurrency
AI & vibe coding
  • Bash
  • Gambas
  • Delphi
  • PHP
  • Visual Basic
  • Rust

The art of port forwarding on Linux

23/03/2014
4 min readDeep Tech16 code examplesUpdated October 2026

Key points

  • Five ways to forward ports on Linux, scored on my own nodes: rinetd, socat, redir, HAProxy and iptables.
  • redir 3 changed its options and HAProxy 2.1 changed its timeout keywords: the corrected lines are under each tool.
  • Never run iptables -F on a remote server: it removes every firewall rule and can lock you out.
  • Two status scripts show CPU, RAM and forward counters for redir, socat and iptables.

Stealth Navigation with Port Forwarding on Linux

Updated October 2026: All five tools for port forwarding on Linux are still packaged in Debian 13 (HAProxy 3.0, socat 1.8, redir 3.3, rinetd 0.73), but several commands below are now wrong or risky. What changed:

  • HAProxy is in the normal Debian and Ubuntu repositories. Never add sid (unstable) to a stable system as the old steps below do, and note that HAProxy 2.1 and later refuse contimeout, clitimeout and srvtimeout. A corrected config, checked with HAProxy 3.0, follows the old one.
  • redir 3 replaced --laddr, --lport, --caddr and --cport with redir 212.72.6.1:808 62.41.90.2:443.
  • Do not run iptables -F on a remote server: it deletes every rule you have. Add only the forwarding rules and save them with netfilter-persistent.
  • Start forwarders with systemd units instead of /etc/init.d scripts and update-rc.d. Debian’s haproxy.service already restarts HAProxy by itself (Restart=always), so monit is optional.
  • ss -antp replaces netstat, conntrack -L lists forwarded connections, and venet0 in my scripts is an old OpenVZ interface name, so use your own (ip link).

The port forwarding tools I tested

You sometimes need to use port forwarding to be stealthy and move from one node to another to cover your tracks. Why choose Linux for this task? Because it is stable and offers many options that you don’t get on Windows.
There are many tools out there, but I picked the ones I have tried, as explained below:

  • rinetd score: 3/10, limited number of connections.
  • socat score: 6/10, high RAM consumption: more connections, more RAM.
  • redir score: 8/10, same as above but uses about 20% less RAM.
  • HAProxy score: 10/10, an HTTP, HTTPS and TCP load balancer that can also just forward TCP traffic, with light CPU and RAM usage.
  • iptables score: 10/10, perfect: 0 CPU and 12 MB of RAM, including the OS!

Rinetd

ShellScript
# To install rinetd, we simply run:

sudo apt-get update
sudo apt-get install rinetd

# rinetd's configuration file is /etc/rinetd.conf.

nano /etc/rinetd.conf

# To forward traffic from your internet node ip 212.72.6.1 port 808 to remote node 62.41.90.2 port 443 add this line:

212.72.6.1 808 62.41.90.2 443

# To forward from all local ips on port 808 to 62.41.90.2 on port 443 :

0.0.0.0 808 62.41.90.2 443

# Then we restart rinetd:

/etc/init.d/rinetd restart
#or
service rinetd restart
ShellScript

Socat

ShellScript
# To install Socat, we run:

sudo apt-get update
sudo apt-get install socat

# To run socat and forward traffic from your internet node ip 212.72.6.1 port 808 to remote node 62.41.90.2 port 443 run the following command:

socat TCP4-LISTEN:808,fork TCP4:62.41.90.2:443

# To forward traffic from your internet node from port 9090 to remote node 62.41.90.2 port 22 and forward traffic from port 81 to port 21 on ftp.example.com run the following command:

socat TCP4-LISTEN:9090,fork TCP4:62.41.90.2:22|socat TCP4-LISTEN:81,fork TCP4:ftp.example.com:21
ShellScript

Redir

ShellScript
# To install redir, we simply run:

sudo apt-get update
sudo apt-get install redir

# To run redir and forward traffic from your internet node ip 212.72.6.1 port 808 to remote node 62.41.90.2 port 443 run the following command:

redir --laddr=212.72.6.1 --lport=808  --caddr=62.41.90.2 --cport=443

# To forward traffic from your internet node 212.72.6.1 from port 9090 to remote node 62.41.90.2 port 22 and forward traffic from port 81 to port 21 on ftp.example.com run the following command:

redir --laddr=212.72.6.1 --lport=9090  --caddr=62.41.90.2 --cport=22|redir --laddr=212.72.6.1 --lport=81  --caddr=ftp.example.com --cport=21
ShellScript

Updated October 2026: redir 3 (Debian 13 ships 3.3) dropped the --laddr, --lport, --caddr and --cport options unless it was built with --enable-compat. The same two forwards with the current syntax (redir detaches by itself, add -n to keep it in the foreground):

redir 212.72.6.1:808 62.41.90.2:443
redir 212.72.6.1:9090 62.41.90.2:22

HAProxy

ShellScript
# At the moment haproxy can only be retrieved from sid unstable repository so you need to do the following changes:

nano /etc/apt/sources.list

# Add the following:

deb http://ftp.de.debian.org/debian sid main

# to the file you open with:

nano /etc/apt/sources.list

# To install haproxy, we simply run:

sudo apt-get update
sudo apt-get install haproxy

# We need to set ENABLED to 1 in order to get all the init scripts working:

nano /etc/default/haproxy

# Config it by adding the server details end of listen http-in section:

 nano /etc/haproxy/haproxy.cfg

# On the defaults section change HTTP to tcp:

mode    tcp
ShellScript

Updated October 2026: Do not add sid to your sources.list: mixing unstable packages into a stable system breaks later upgrades. HAProxy is in the normal repositories of every current Debian and Ubuntu release, and its systemd unit ignores the old ENABLED setting:

sudo apt update
sudo apt install haproxy
sudo systemctl enable --now haproxy

Then add the following to accept connections on ports 80 and 443 and forward them to your servers (two are shown; add one server line per back end):

ShellScript
global
        stats socket /var/run/haproxy/haproxy.sock mode 0600 level admin #Creates Unix-Like socket to fetch stats
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        user haproxy
        group haproxy
        daemon
        maxconn 99999

defaults
        log     global
        mode    tcp
       

        contimeout 500000
        clitimeout 500000
        srvtimeout 500000
       

listen http-in
        option tcplog
        option logasap
        contimeout 500000
        clitimeout 500000
        srvtimeout 500000
        maxconn 99999
        bind 0.0.0.0:80
        bind 0.0.0.0:443

		server server-1 ip:port maxconn 5000
		server server-2 ip:port maxconn 10000

listen  stats   0.0.0.0:1936
        mode            http
        #log             global

        maxconn 10

        clitimeout      100s
        srvtimeout      100s
        contimeout      100s
        timeout queue   100s

        stats enable
        stats hide-version
        stats refresh 30s
        stats show-node
        stats auth admin:your_password_here
        stats uri  /haproxy?stats

# Run this command to create the required directory:

mkdir /var/run/haproxy/

# To access the statistics report visit the following site and make sure you use your account as set on haproxy.cfg:

http://www.your-haproxy-server-ip.com:1936/haproxy?stats

# To run as daemon:

cd /etc/haproxy/
haproxy -f haproxy.cfg -D

# Or use the following:

/etc/init.d/haproxy start
/etc/init.d/haproxy stop
/etc/init.d/haproxy restart

# To check the ips of connected clients:

# You need to install netcat first:
apt-get install netcat-openbsd

# Then run the following command:
echo show sess | nc -U /var/run/haproxy/haproxy.sock | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' | sort | uniq -c
ShellScript

Updated October 2026: HAProxy 2.1 and later stop with an error on contimeout, clitimeout and srvtimeout, and they want bind instead of an address on the listen line, so the config above no longer loads. This version passes haproxy -c on HAProxy 3.0 (Debian 13). The Debian service creates /run/haproxy itself, and the statistics page is at http://your-server-ip:1936/haproxy?stats:

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    user haproxy
    group haproxy
    daemon
    maxconn 99999

defaults
    log     global
    mode    tcp
    option  tcplog
    timeout connect 500000
    timeout client  500000
    timeout server  500000

listen http-in
    bind :80
    bind :443
    option logasap
    maxconn 99999
    # no port on a server line: HAProxy uses the port the client connected to (80 or 443)
    server server-1 192.0.2.10 maxconn 5000
    server server-2 192.0.2.11 maxconn 10000

listen stats
    bind :1936
    mode http
    timeout client 100s
    timeout server 100s
    timeout connect 100s
    timeout queue 100s
    maxconn 10
    stats enable
    stats hide-version
    stats refresh 30s
    stats show-node
    stats auth admin:your_password_here
    stats uri /haproxy?stats

# check the file, then reload:
# sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy

Troubleshooting

For some reason, if the load is too high, the HAProxy service stops running, so to make sure it stays alive I monitor it and restart it with the monit tool as follows (on current Debian, haproxy.service already restarts HAProxy by itself with Restart=always):

ShellScript
sudo apt-get install monit

# Now edit the config file

sudo nano /etc/monit/monitrc

# change  set daemon 120 to set daemon 5 
#uncomment the following lines:

set httpd port 2812 and
use address localhost  # only accept connection from localhost
allow localhost        # allow localhost to connect to the server and
allow admin:monit      # require user 'admin' with password 'monit'
allow @monit           # allow users of group 'monit' to connect (rw)
allow @users readonly  # allow users of group 'users' to connect readonly

# Now go to the end of the file and paste the following:
check process haproxy with pidfile /var/run/haproxy.pid
start program = "/etc/init.d/haproxy start"
stop program = "/etc/init.d/haproxy stop"

# Check if your syntax is correct if not you will get an error:

monit -t

# Now we are done with the configuration let us run it:

monit

# To check the status:

monit status

# To reload:

monit reload

# After resolving any possible syntax errors, you can start running all of the monitored programs by:

monit start all



# If you are getting the following error:

[....] Restarting haproxy: haproxy[ALERT] 221/134103 (1900) : cannot bind socket for UNIX listener (/var/run/haproxy/haproxy.sock). Aborting.
[ALERT] 221/134103 (1900) : [/usr/sbin/haproxy.main()] Some protocols failed to start their listeners! Exiting.
 failed!

# Open /etc/haproxy/haproxy.cfg and comment the following line:

 stats socket /var/run/haproxy/haproxy.sock mode 0600 level admin #Creates Unix-Like socket to fetch stats




# Extra commands for logging purpose only:

# create a HAProxy config file for rsyslog:

nano /etc/rsyslog.d/haproxy.conf

# Then add the following:

if ($programname == 'haproxy' and $syslogseverity-text == 'info') then -/var/log/haproxy/haproxy-info.log
& ~
if ($programname == 'haproxy' and $syslogseverity-text == 'notice') then -/var/log/haproxy/haproxy-notice.log
& ~

# This will rotate your HAProxy logs daily and keep them for 4 weeks.

nano /etc/logrotate.d/haproxy

# Add the following:

/var/log/haproxy/*.log {
    daily
    missingok
    rotate 28
    compress
    delaycompress
    notifempty
    create 644 root adm
    sharedscripts
    postrotate
    /etc/init.d/haproxy reload > /dev/null
    endscript
}
ShellScript

Iptables

To forward using iptables you don’t need to install any tool, so to forward traffic from your internet node ip 212.72.6.1 port 808 to remote node 62.41.90.2 port 443, run the following command:

ShellScript
iptables -F
iptables -F -t nat
echo 1 >| /proc/sys/net/ipv4/ip_forward
iptables -t nat -A  PREROUTING -p tcp -d  212.72.6.1 --dport 808 -j DNAT --to 62.41.90.2:443
iptables -t nat  -A POSTROUTING -j MASQUERADE

# List your changes:

iptables -L -t nat

# To forward traffic from your internet node 212.72.6.1 from port 9090 to remote node 62.41.90.2 port 22 and forward traffic from port 81 to port 21 on ftp.example.com run the following command:

iptables -F
iptables -F -t nat
echo 1 >| /proc/sys/net/ipv4/ip_forward
iptables -t nat -A  PREROUTING -p tcp -d  212.72.6.1 --dport 9090 -j DNAT --to 62.41.90.2:22
iptables -t nat -A  PREROUTING -p tcp -d  212.72.6.1 --dport 81 -j DNAT --to 67.215.65.132:21        # You can't use domain names use the ip I will show you later how to automate this
iptables -t nat  -A POSTROUTING -j MASQUERADE
iptables -L -t nat

# Display Iptables counters:

watch --interval 0 'iptables -nvL | grep -v "0     0"'

# or

while true; do iptables -nvL > /tmp/now; diff -U0 /tmp/prev /tmp/now > /tmp/diff; clear; cat /tmp/diff; mv /tmp/now /tmp/prev; sleep 1; done
ShellScript

Updated October 2026: The commands above start with iptables -F, which deletes all your firewall rules (on a remote server with a DROP policy it locks you out), and masquerade everything. This version adds only what the forward needs, turns forwarding on for good and keeps the rules after a reboot (Debian 13 runs these commands on the nftables backend):

echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-ip-forward.conf
sudo sysctl --system

sudo iptables -t nat -A PREROUTING -p tcp -d 212.72.6.1 --dport 808 -j DNAT --to-destination 62.41.90.2:443
sudo iptables -t nat -A POSTROUTING -p tcp -d 62.41.90.2 --dport 443 -j MASQUERADE
sudo iptables -A FORWARD -p tcp -d 62.41.90.2 --dport 443 -j ACCEPT
sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

sudo apt install iptables-persistent
sudo netfilter-persistent save

Startup script

ShellScript
# For socat, redir, and iptables you will need to put them on a start-up script so they start each time your node boots up.

# Create a boot script:

nano /etc/init.d/myfwd

# Add the following for socat:

#!/bin/bash
socat TCP4-LISTEN:9090,fork TCP4:62.41.90.2:22|socat TCP4-LISTEN:81,fork TCP4:ftp.example.com:21

# Add the following for redir:

#!/bin/bash
redir --laddr=212.72.6.1 --lport=9090  --caddr=62.41.90.2 --cport=22|redir --laddr=212.72.6.1 --lport=81  --caddr=ftp.example.com --cport=21

# Add the following for iptables:

#!/bin/bash
iptables -F
iptables -F -t nat
echo 1 >| /proc/sys/net/ipv4/ip_forward
iptables -t nat -A  PREROUTING -p tcp -d  212.72.6.1 --dport 9090 -j DNAT --to 62.41.90.2:22
iptables -t nat -A  PREROUTING -p tcp -d  212.72.6.1 --dport 81 -j DNAT --to 67.215.65.132:21        # You can't use domain names use the ip I will show you later how to automate this
iptables -t nat  -A POSTROUTING -j MASQUERADE

# If you need to get the ip automatically from a domain name you need to use dig command as the following:

sudo apt-get install dnsutils

# Then add the following for iptables:

#!/bin/bash
iptables -F
iptables -F -t nat
echo 1 >| /proc/sys/net/ipv4/ip_forward
IP_ADDR=$(dig +short ftp.example.com| awk 'NR==1')
iptables -t nat -A  PREROUTING -p tcp -d  212.72.6.1 --dport 9090 -j DNAT --to 62.41.90.2:22
iptables -t nat -A  PREROUTING -p tcp -d  212.72.6.1 --dport 81 -j DNAT --to $IP_ADDR:21         
iptables -t nat  -A POSTROUTING -j MASQUERADE

# Then run the following commands:

cd /etc/init.d
chmod a+x myfwd
update-rc.d myfwd defaults

# Reboot then check your results by the following commands:

socat and redir use:

netstat -antp

# To check ip tables you will need to install netstat-nat

sudo apt-get install netstat-nat

# Then run:

netstat-nat -D
ShellScript

Updated October 2026: On systemd systems (every current Debian and Ubuntu), an /etc/init.d script that runs socat in the foreground and has no LSB header does not start cleanly. Use one systemd unit per forward instead; for iptables, the netfilter-persistent step above already restores the rules at boot. Example for socat (this unit passes systemd-analyze verify):

sudo tee /etc/systemd/system/fwd-9090.service <<'EOF'
[Unit]
Description=Forward port 9090 to 62.41.90.2:22
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/socat TCP4-LISTEN:9090,fork,reuseaddr TCP4:62.41.90.2:22
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now fwd-9090.service

Custom script

Here are some scripts that I have written to monitor forward status for redir, socat, and iptables.

You will need to install vnstat:

ShellScript
sudo apt-get install vnstat
ShellScript

Shell script: redir and socat status

The following script will show you CPU, RAM and forward counters and the status of redir or socat:

ShellScript
#!/bin/sh
# Shell script written by Warith AL Maawali    
# (c) 2014 Warith AL Maawali
# https://www.digi77.com/
# https://om77.net/
# script starts here:

echo ""
echo ""
users_online=`uptime`
echo "System status: "$users_online
echo "======================================================================"
echo ""
echo ""
cat /proc/meminfo |grep Free
used_ram=`free | grep Mem | awk '{print $3/$2 * 100.0}'`
free_ram=`free | grep Mem | awk '{print $4/$2 * 100.0}'`
echo ""
echo "Used RAM  = %"$used_ram
echo "Free RAM  = %"$free_ram
echo ""
echo ""
echo "======================================================================"
echo "Bandwidth Status:"
vnstat -i venet0 -m
echo "======================================================================"
echo "Getting results......."
echo ""
echo ""
org1_count=`netstat -antp | grep -c '51.219.221.206\|215.165.18.97'`
org2_count=`netstat -antp | grep -c '132.71.46.3'`
sum=$(($org1_count+$org2_count))
sokatSum=`pgrep -c socat`
redirSum=`pgrep -c redir`
echo "Org_1      =" $org1_count
echo "Org_2      =" $org2_count
echo ""
echo "Sum      =" $sum
echo "Redir    =" $redirSum
echo "Socat    =" $sokatSum
echo ""
echo ""
echo "======================================================================"
echo "Server connection by ip numbers:"
echo ""
netstat -ntu | awk -F"[ :]+" 'NR>2{print $6}'|sort|uniq -c|sort -nr
echo ""
echo ""
echo "======================================================================"
ShellScript
Download

Shell script: iptables status

The following script will show you CPU, RAM and forward counters and the status of iptables:

ShellScript
#!/bin/sh
# Shell script written by Warith AL Maawali  
# (c) 2014 Warith AL Maawali
# https://www.digi77.com/
# https://om77.net/
# script starts here:

echo ""
echo ""
users_online=`uptime`
echo "System status: "$users_online
echo "======================================================================"
echo ""
echo ""
cat /proc/meminfo |grep Free
used_ram=`free | grep Mem | awk '{print $3/$2 * 100.0}'`
free_ram=`free | grep Mem | awk '{print $4/$2 * 100.0}'`
echo ""
echo "Used RAM  = %"$used_ram
echo "Free RAM  = %"$free_ram
echo ""
echo ""
echo "======================================================================"
echo "Bandwidth Status:"
vnstat -i venet0 -m
echo "======================================================================"
echo "Getting results......."
echo ""
echo ""
org1_count=`netstat-nat -s '51.219.221.206'|wc -l`
org1b_count=`netstat-nat -s '215.165.18.97'|wc -l`
org1_count=$(($org1_count+$org1b_count))
org2_count=`netstat-nat -s '132.71.46.3'|wc -l`
sum=$(($org1_count+$org2_count))
sokatSum=`pgrep -c socat`
redirSum=`pgrep -c redir`
echo "Org_1      =" $org1_count
echo "Org_2      =" $org2_count
echo ""
echo "Sum      =" $sum
echo "Redir    =" $redirSum
echo "Socat    =" $sokatSum
echo ""
echo ""
echo "======================================================================"
echo "Server connection by ip numbers:"
echo ""
netstat -ntu | awk -F"[ :]+" 'NR>2{print $6}'|sort|uniq -c|sort -nr
echo ""
echo ""
echo "======================================================================"
ShellScript
Download

The IPs mentioned above are just examples. For the commands to function properly, you should use your own IP addresses.


Posted in Deep TechTags: