iptables redirect local connections to remote system / port

I am trying to use SSH to port forward windows machines local port 3389 to a redhat server on some arbitrary port (in the unprivileged region) I have protected behind a firewall, then forward the unprivileged port on the redhat server onto the users remote desktop. Essentially create a highly specalized VPN which only handles one task: Get users to their systems behind a firewall using an encrypted tunnel I have control over.

I know years ago this was possible using the command:

iptables -A OUTPUT -t nat --dport ${LOCAL UNPRIV PORT} \
           -j DNAT ${ANOTHER SYSTEM}:${REMOTE PORT}

but from what I read, it looks like that ability was removed, and I get the message in /var/log/messages

      kernel: NAT: no longer support implicit source local NAT 

I found resources which suggest that from kernel 2.6.X - 2.6.10, there was a way to enable this in the kernel using IP_NF_NAT_LOCAL, but apparently in more recent kernels its been removed. I have tried forwarding all local traffic so it will enter the PREROUTING chain, and have had limited success, and that just feels like a bad idea, because then I have to open the unprivileged port on the server so I can feed it back into the eth0 interface. I'm sure given more time I could figure out some sort of bizarre kludge, or workaround, but I would rather not hack up my firewall scripts that much. It seems like there must be a much easier way of doing this I'm not seeing. Any help or guidance the community could provide would be very helpful! Thanks in advance


Solution 1:

I'm not completely sure if I understood, but I think you're just in the wrong chain. :-) I was confused, too, when I used iptables the first time. But the way to forward the local port ${LOCAL UNPRIV PORT} is the statement below:

$IPT -t nat -A PREROUTING -i eth0 -p tcp --dport ${LOCAL UNPRIV PORT} 
     -j DNAT --to ${ANOTHER SYSTEM}:${REMOTE PORT}

It is a mixture between a semantic problem and the way netfilter works: In the old days forwarding a local port involved a connection to the box that is forwarding, plus a second connection to the destination. Iptables does this in one step. So instead of two connections - you are forwarding the the traffic to that port directly to the destination. Netfilter does all the sanity checks and bookkeeping: only packets that belong to a valid connection are NATted and can therefore be forwarded.

Enabling DNAT doesn't allow any packets to be forwarded. You also have to add a rule:

$IPT -N INET-PRIV
$IPT -A FORWARD -i eth0 -o eth1 -j INET-PRIV
$IPT -A FORWARD -j DROP

$IPT -A INET-PRIV -p tcp -d ${ANOTHER SYSTEM} --dport ${REMOTE PORT} -j ACCEPT

$IPT -A INET-PRIV -j DROP

And you have to enable forwarding of course.

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

Pro iptables: more secure, more flexible, less memory and CPU used per connection

Contra iptables: forwarding a connection from a internal machine to a internal machine (feed it back into the eth0) doesn't make sense with iptables (of course you can always connect directly), forwarding traffic that is generated locally doesn't work (a port forwarding daemon might help - but usually doesn't make sense)

Exactly this might be the problem: you try to use NAT on a non-router, so you should use a forwarding daemon or skip this extra port forwarding completely and do:

ssh -L 1234:${ANOTHER SYSTEM}:${REMOTE PORT} special-vpn-box

On special-vpn-box you can only allow incoming connections from the router and outgoing connections to ${ANOTHER SYSTEM}:${REMOTE PORT} using iptables. That way users of the special-vpn-box can only access ${ANOTHER SYSTEM}:${REMOTE PORT} and won't be able to do anything else if they aren't trusted.

Solution 2:

Here is what I do specifically for localhost forwarding:

iptables -t nat -A OUTPUT -m addrtype --src-type LOCAL --dst-type LOCAL -p tcp --dport 3306 -j DNAT --to-destination ip.ip.ip.ip
iptables -t nat -A POSTROUTING -m addrtype --src-type LOCAL --dst-type UNICAST -j MASQUERADE

sysctl -w net.ipv4.conf.all.route_localnet=1

Make sure you substitute ip.ip.ip.ip for your real public IP and also the --dport 3306 for the port you want to forward.

Finally run the sysctl command and also update your /etc/sysctl.conf

You can update sysctl.ctl to allow the routing of localhost with the following command:

echo "net.ipv4.conf.all.route_localnet=1" >> /etc/sysctl.conf

Now this all seems simple and good but it did take some research and hunting down. Be warned and understand that forwarding localhost/127.0.0.1 requires this method and the typical other examples do not work. Some examples of solutions that do not work with localhost:

iptables -t nat -A PREROUTING -p tcp --dport 3306 -j DNAT --to ip.ip.ip.ip:3306
iptables -t nat -A POSTROUTING -d ip.ip.ip.ip -j MASQUERADE

http://realtechtalk.com/iptables_how_to_forward_localhost_port_to_remote_public_IP-1788-articles

Solution 3:

# Enable IP Forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

iptables -t nat -A PREROUTING  -p tcp \
--dport ${LOCAL UNPRIV PORT} -j DNAT --to-destination ${ANOTHER SYSTEM}:${REMOTE PORT}

iptables -t nat -A POSTROUTING -p tcp \
--dst ${ANOTHER SYSTEM} --dport ${REMOTE PORT} -j SNAT --to-source ${LOCAL SYSTEM}