System Administration & Network Administration
linux redhat iptables forwarding loopback
Updated Fri, 29 Jul 2022 11:01:31 GMT

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

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.





Comments (5)

  • +0 – I certainly have tried the ssh the way you described, with the automatically forwarding it to the next machine. So basically if the system behind the firewall has an IP of 192.168.0.2, then do ssh -L 3389:192.168.0.2:3389 to special vpn box. But what I'm left with is when you try to connect to 192.168.0.2 3389 it just says "no path to host" when you try telnet. I tried doing the configuration you suggested (which is pretty close to what I already have) but when i check /var/log/messages the packet never hit the PREROUTING rule (when i switch to log vs acept) — Mar 14, 2011 at 20:19  
  • +0 – Can you please list your hosts, what you do on what host and how they are connected. I suspect we still don't talk about the same thing. — Mar 15, 2011 at 11:37  
  • +0 – Firstly Ganwell, thank you very much for your time. I really appreciate your help. Ok, here is my layout. I have a remote user, I will call ${RU}. I have a redhat server with an open ssh port in the firewall directed at it I will call ${RHS}, and a local system behind the firewall I will call ${LS}. I want to have ${RU}-->SSH-->${RHS}-->RDP-->${LS}. To do this I have been using ssh port forwarding to get to the ${RHS}, then trying to DNAT that apparent (to the ${RHS} "local" traffic onto the ${LS} on port 3389 (RDP). — Mar 15, 2011 at 15:39  
  • +0 – I have tried to use output to DNAT the destination back to the address of eth0, then using postrouting to SNAT and also set that to eth0. Then I was going to use the prerouting chain to DNAT them to the correct source. Seems WAY too complex. However the output chain, if I DNAT them directly to the address of the ${LS}, it never hits the postrouting chain (I have tried various logging options, and they definitely never get there). And from my understanding, if they are not making it to the postrouting chain, they are not leaving the system. — Mar 15, 2011 at 15:49  
  • +0 – Ok. I think I got the problem, you are doing DNAT on the ${RHS} System right? You can't DNAT traffic that is generated on the local system. You can DNAT on the firewall, but once you passed the firewall you don't need any port forwarding (DNAT) anymore. You just have to change: ssh -L 3389:192.168.0.2:3389 to ssh -L 3389:${LS}:3389 — Mar 15, 2011 at 17:12