Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do local port forwarding with iptables

Tags:

iptables

I have an application (server) listening on port 8080. I want to be able to forward port 80 to it, such that hitting http://localhost resolves my application (on localhost:8080).

This should be generalized for any port mapping (e.g. 80:8080 => P_src:P_target), and use best practices for modern *nix machines (e.g. Ubuntu).

N.B. This is all done locally, so there is no need to accept connections from anyone but localhost.

like image 652
jonathan3692bf Avatar asked Jan 27 '15 11:01

jonathan3692bf


People also ask

How can I port forward with iptables?

Port forwarding on iptables is done with something called a Destination NAT. This will tell the incoming packs, depending on the conditions implied, to route through a different port or address. For this, we will need to do this through iptables' NAT PREROUTING chain.

What is forwarding in iptables?

Port forwarding also referred to as port mapping, is a method for allowing remote devices to connect to a specific service within your private local-area network (LAN).

How do I setup port forwarding in Linux?

Remote port forwarding allows you to connect from your remote machine to the local computer. By default, SSH does not permit remote port forwarding. You can enable this using the GatewayPorts directive in your SSHD main configuration file /etc/ssh/sshd_config on the remote host.


Video Answer


1 Answers

So after much searching around, I found the answer uses iptables, setting up a NAT, and using the built-ins PREROUTING and OUTPUT.

First, you must have port forwarding enabled:

echo "1" > /proc/sys/net/ipv4/ip_forward

Then you have to add the following rules to your iptables NAT table, using your own values for ${P_src} and ${P_target}:

iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport ${P_src} -j REDIRECT --to ${P_target}`
iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport ${P_src} -j REDIRECT --to ${P_target}`

If you want to remove the rules, you simply need to use the -D switch instead of -A for each rule.

I build a nice little script for this that does adding and removing of mappings.

#!/bin/bash
#
#   API: ./forwardPorts.sh add|rm p1:p1' p2:p2' ...
#
#   Results in the appending (-A) or deleting (-D) of iptable rule pairs that
#   would otherwise facilitate port forwarding.
#
#   E.g
#   sudo iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 80 -j REDIRECT --to 8080
#   sudo iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 80 -j REDIRECT --to 8080
#

if [[ $# -lt 2 ]]; then
    echo "forwardPorts takes a state (i.e. add or rm) and any number port mappings (e.g. 80:8080)";
    exit 1;
fi

case $1 in
    add )
        append_or_delete=A;;
    rm )
        append_or_delete=D;;
    * )
        echo "forwardPorts requires a state (i.e. add or rm) as it's first argument";
        exit 1; ;;
esac

shift 1;

# Do a quick check to make sure all mappings are integers
# Many thanks to S.O. for clever string splitting:
# http://stackoverflow.com/questions/918886/how-do-i-split-a-string-on-a-delimiter-in-bash
for map in "$@"
do
    IFS=: read -a from_to_array <<< "$map"
    if  [[ ! ${from_to_array[0]} =~ ^-?[0-9]+$ ]] || [[ ! ${from_to_array[1]} =~ ^-?[0-9]+$ ]]; then
        echo "forwardPorts port maps must go from an integer, to an integer (e.g. 443:4443)";
        exit 1;
    fi
    mappings[${#mappings[@]}]=${map}
done

# We're shooting for transactional consistency. Only manipulate iptables if all 
# the rules have a chance to succeed.
for map in "${mappings[@]}"
do
    IFS=: read -a from_to_array <<< "$map" 
    from=${from_to_array[0]}
    to=${from_to_array[1]}

    sudo iptables -t nat -$append_or_delete PREROUTING -s 127.0.0.1 -p tcp --dport $from -j REDIRECT --to $to
    sudo iptables -t nat -$append_or_delete OUTPUT -s 127.0.0.1 -p tcp --dport $from -j REDIRECT --to $to
done

exit 0;
like image 187
jonathan3692bf Avatar answered Oct 24 '22 07:10

jonathan3692bf