Need a generic iptables tcp proxy?

Posted by JacobAppelbaum on Tue 13 May 2008 at 10:02

Do you ever find yourself in need of a generic TCP proxy? Do you wish you could do it with netfilter? Do you want to proxy a connection to a given port on a given IP address to a completely different port on a totally different host or network?


It's as easy as the following three iptables calls:
YourIP=1.2.3.4
YourPort=80
TargetIP=2.3.4.5
TargetPort=22

iptables -t nat -A PREROUTING --dst $YourIP -p tcp --dport $YourPort -j DNAT \
--to-destination $TargetIP:$TargetPort
iptables -t nat -A POSTROUTING -p tcp --dst $TargetIP --dport $TargetPort -j SNAT \
--to-source $YourIP
iptables -t nat -A OUTPUT --dst $YourIP -p tcp --dport $YourPort -j DNAT \
--to-destination $TargetIP:$TargetPort


In the example above, a user may now ssh to $YourIP (1.2.3.4) on $YourPort (port 80) and they'll be transparently redirected to the $TargetIP (2.3.4.5) on the $TargetPort (22). The remote host ($TargetIP) will see the connection as coming from the server doing the forwarding ($YourIP).

Why bother with this at all? Why not just change the port that sshd listens on?

This is useful when a network filters outgoing connections based on destination ports and you don't control the host you want to connect to. If such a network only allowed outgoing connections to port 80, you'd be able to circumvent their filtering. However, if the firewall is doing stateful layer seven inspection, all bets are off. It's trivial to make this work for any other protocol, there is nothing special about ssh - it's just used as an example.

As a general note, this may invite abuse. It is basically a single hop protocol agnostic TCP proxy in kernel space. It's fast and useful though. You may want to restrict forwarding by source IP addresses if you're worried about letting anyone using you as a single hop bounce. I'll leave that as an exercise for the comments.

 

 


Posted by Xeeper (87.195.xx.xx) on Tue 13 May 2008 at 11:19
It's also wise to add the internet IP to your loopback device. That way if the internal machines tries to connect with itself NAT doesn't go crazy (in --> out --> out --> in doesn't work very good in most cases).

We use NAT forwarding for websites we expect that they will be loadbalanced in the future.

[ Parent | Reply to this comment ]

Posted by JacobAppelbaum (64.142.xx.xx) on Tue 13 May 2008 at 22:56
[ View Weblogs ]
Can you elaborate?

[ Parent | Reply to this comment ]

Posted by Anonymous (87.195.xx.xx) on Wed 14 May 2008 at 12:48
Say we loadbalance (ip:port) 1.2.3.4:80 on two load balance machines, requests to the 'public' IP will be forwarded by ultramonkey (in our case) to internal server A (10.0.0.1:80) or server B (10.0.0.2:80).

When new clients expects that they need loadbalancing within a year, we don't assign a public IP to the actual webserver, but to the firewall. The firewall will than forward all http trafic to the internal machine (as describes by this article, except that the port doesn't changes). After a year we can assign the public IP to the loadbalancer an add the second webserver to the pool.

About adding the public IP to the local loopback. We have several web applications that connect to their own webservices (SOA design). Say that www.example.org points to 1.2.3.4, than webserver with interface 10.0.0.1 routes to 1.2.3.4 (gateway does masquerading (POSTROUTING) part 1), but than it sees that that 1.2.3.4 points back to 10.0.0.1, that it double masquerades (adding the PREROUTING masquerade). But gateways don't like double forwarding packets. When rf_filter is enabled it will even drop the packet because the request originated from the wrong interface.

To prevent the extensive routing, you can add the 1.2.3.4 IP to your local loopback device and any attempts from the webserver to connect to itself are handled by the loopback device.


firewall gateway:
eth0: 1.2.3.4
eth1: 10.0.0.254
lo: 127.0.0.1

Server A:
eth0: IP 10.0.0.1
lo: 127.0.0.1, 1.2.3.4

--- IF LOADBALANCED

Server B:
eth0: 10.0.0.2
lo: 127.0.0.1, 1.2.3.4

Server C:
eth0: 10.0.0.3
lo: 127.0.0.1, 1.2.3.4

Server D:
eth0: 10.0.0.4
lo: 127.0.0.1, 1.2.3.4

--- END IF

Hope that this make my previous comment a bit clearer.

[ Parent | Reply to this comment ]

Posted by Anonymous (68.198.xx.xx) on Mon 25 Apr 2011 at 19:00
This does not seem to work when you have to do two hops...
Setup for Clarity:
Machine A
IP address: i.i.i.1

Machine B has 2 IP
Internal IP: i.i.i.2 (on another NIC)
External IP: x.x.x.1 (on one NIC)

Machine C
IP address: x.x.x.2

Machine B and Machine C are in the same network (intranet) where Machine B can connect to Machine C only via its external IP (x.x.x.x)
Machine B and Machine A are in internal privatenetwork where Machine B can connect to Machine A only via its internal IP (i.i.i.i)

Machine A <i.i.i.1> <--> Machine B (i.i.i.2>:60 <---> Machine B <x.x.x.1> <---> Machine C <x.x.x.2>:60

Basically forward auditd log from Machine A in internal network to auditd (port 60) on Machine C on external network via Machine A.

We tried the rinetd equivalent rules given below,but in vain.

YourIP= i.i.i.1
YourPort=60
TargetIP=x.x.x.2
TargetPort=60

iptables -t nat -A PREROUTING --dst $YourIP -p tcp --dport $YourPort -j DNAT \
--to-destination $TargetIP:$TargetPort
iptables -t nat -A POSTROUTING -p tcp --dst $TargetIP --dport $TargetPort -j SNAT \
--to-source $YourIP
iptables -t nat -A OUTPUT --dst $YourIP -p tcp --dport $YourPort -j DNAT \
--to-destination $TargetIP:$TargetPort

Any Advice ?

[ Parent | Reply to this comment ]

Posted by Anonymous (195.24.xx.xx) on Fri 25 Jan 2013 at 12:02
I need redirect full IP.
I have 2 linux machine in internet.

First - 82.*.*.*
Second - 76.*.*.*

I need :

All inquiries should be forwarded to another address

For example if i from my home computer (33.x.x.x) ping First, in fact I must ping Second.

I run in First:
# iptables -t nat -A PREROUTING -d $First -j DNAT --to-destination $Second
# iptables -t nat -A POSTROUTING -d $Second -j SNAT --to-source $First

Everythink is works.
(Via $First IP I get access to $Second).


Problem 1:

But when i run (from my home computer) 'tracert -d $First' - I get loop:
1. ...
2. ...
....
7. 82.*.*.*
8. 82.*.*.*
9. 82.*.*.*
...
30. 82.*.*.*

Problem 2:

And on $Second I run 'tcpdump -i eth0 -n icmp' and see requests (pings from my home computer) from $First, but I want to see request from my home ip (33.x.x.x)

Thanks for help.









[ Parent | Reply to this comment ]

Posted by Anonymous (87.230.xx.xx) on Tue 13 May 2008 at 12:44
Maybe have a look at the tool "stone", too, the Debian package description is:

"TCP/IP packet repeater in the application layer. It repeats TCP and UDP packets from inside to outside of a firewall, or from outside to inside."

You can basically proxy or forward any tcp or udp connection with one single command. However, learning iptables is always a good idea ;-) !

kind regards, mistersixt.

[ Parent | Reply to this comment ]

Posted by JacobAppelbaum (64.142.xx.xx) on Tue 13 May 2008 at 22:51
[ View Weblogs ]
I generally prefer the kernel to take of care of this kind of issue. For privileged ports, you'd want to redirect the ports using a similar method or you may have giant security issues.

[ Parent | Reply to this comment ]

Posted by Anonymous (204.31.xx.xx) on Sat 17 May 2008 at 01:16
Nice

[ Parent | Reply to this comment ]

Posted by Anonymous (220.255.xx.xx) on Thu 29 May 2008 at 08:17
Problem with this kind of setup is that you'll always see connections to be coming from the machine making the translation. not good if you intend to get statistics of your web server.

[ Parent | Reply to this comment ]

Posted by Anonymous (190.3.xx.xx) on Wed 25 Jun 2008 at 18:38
Ive made a simple bash script for TCP Proxying based on this article, so if you need to set up multiple proxies it keeps readable... i had to add the ip_forward line (without that the proxy didn't work). The multiple local IPs are "made" using virtual interfaces, so you can use the same port multiple times...

===============================================

#!/bin/sh
# TCP Proxy using IPTables

IPTABLES=/sbin/iptables

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

# Flush nat table
$IPTABLES -t nat -F

# tcpproxy LOCAL_IP LOCAL_PORT REMOTE_IP REMOTE_PORT
function tcpproxy {
$IPTABLES -t nat -A PREROUTING --dst $1 -p tcp --dport $2 -j DNAT --to-destination $3:$4
$IPTABLES -t nat -A POSTROUTING --dst $3 -p tcp --dport $4 -j SNAT --to-source $1
$IPTABLES -t nat -A OUTPUT --dst $1 -p tcp --dport $2 -j DNAT --to-destination $3:$4
}

# Example 1
tcpproxy 192.168.40.244 3200 10.10.10.2 3200

# Example 2
tcpproxy 192.168.40.245 3200 192.168.1.30 3200

===============================================

sorry for any spelling errors (english isnt my native language) :)


[ Parent | Reply to this comment ]

Posted by Anonymous (204.14.xx.xx) on Tue 29 Jan 2013 at 19:06
thank you so much for this... im trying to teach my self a variety of things to be more viable .. and this little script is perfect. and i cant possibly express my gratitude. Thank you.

[ Parent | Reply to this comment ]

Posted by Anonymous (66.92.xx.xx) on Thu 3 Jul 2008 at 03:45
does the
-j
belong in that last rule? (sorry if this is a stupid
question. i'm an iptables ignoramus)

[ Parent | Reply to this comment ]

Posted by Anonymous (66.92.xx.xx) on Thu 3 Jul 2008 at 03:47
scratch that

--dummy

[ Parent | Reply to this comment ]

Posted by Anonymous (68.198.xx.xx) on Mon 25 Apr 2011 at 19:02
Setup for Clarity:
Machine A
IP address: i.i.i.1

Machine B has 2 IP
Internal IP: i.i.i.2 (on another NIC)
External IP: x.x.x.1 (on one NIC)

Machine C
IP address: x.x.x.2

Machine B and Machine C are in the same network (intranet) where Machine B can connect to Machine C only via its external IP (x.x.x.x)
Machine B and Machine A are in internal privatenetwork where Machine B can connect to Machine A only via its internal IP (i.i.i.i)

Machine A <i.i.i.1> <--> Machine B (i.i.i.2>:60 <---> Machine B <x.x.x.1> <---> Machine C <x.x.x.2>:60

Basically forward auditd log from Machine A in internal network to auditd (port 60) on Machine C on external network via Machine A.

We tried the rinetd equivalent rules given below,but in vain.

YourIP= i.i.i.1
YourPort=60
TargetIP=x.x.x.2
TargetPort=60

iptables -t nat -A PREROUTING --dst $YourIP -p tcp --dport $YourPort -j DNAT \
--to-destination $TargetIP:$TargetPort
iptables -t nat -A POSTROUTING -p tcp --dst $TargetIP --dport $TargetPort -j SNAT \
--to-source $YourIP
iptables -t nat -A OUTPUT --dst $YourIP -p tcp --dport $YourPort -j DNAT \
--to-destination $TargetIP:$TargetPort

Any Advice ?

[ Parent | Reply to this comment ]

Posted by Anonymous (131.207.xx.xx) on Fri 4 May 2012 at 07:34
do not forget to set sysctl "net.ipv4.ip_forward = 1" parameter
then it should look like
[bart@simps ~]# cat /proc/sys/net/ipv4/ip_forward
1

[ Parent | Reply to this comment ]

Sign In

Username:

Password:

[Register|Advanced]

 

Flattr

 

Current Poll

What do you use for configuration management?








( 269 votes ~ 1 comments )