用iptables将流量转发到另外一台机器

目的:

当客户端访问某一个服务器端口时,将流量转发到另外一台服务器的某一个端口。

客户端->转发服务器->目的服务器->转发服务器->客户端

 

理论:

首先看一下iptables处理流程。

dport-routing-02

这些操作使是在转发服务器上完成的。

数据包从网卡进入,首先会被PREROUTING这个内置的规则链处理。之后会进行路由判断。如果目的地址是本机,则会走INPUT规则链,然后提交给本机程序处理;如果目的地不是本机地址,且开启了转发功能,则会经过FORWARD规则链,然后准备将数据包发出。本机要发出的数据包和经过FORWARD的数据包,会经过路由判断,然后再经过POSTROUTING规则链,最后从网卡发出。这里的ethX和ethY 可以是同一个网卡。

为了能让数据包转发到目的地址,需要在第一个路由判断之前将目的地址用DNAT改成目的服务器的地址,这样数据包就不会提交给上层程序。然后在数据包出网卡之前,将远来源地址改成转发服务器的地址。这样目的服务器返回时会返回给转发服务器,转发服务器会根据之前建立的映射表再返回给客户端。需要注意的是我们应该打开ipv4时的转发功能。

具体实现:

  1. 假设转发服务器的地址192.168.1.20 。目的服务器的地址是 220.181.111.188 。在转发服务器上把8080端口转发到220.181.111.188的80端口。
  2.  打开ipv4时的转发功能
    1. 编辑"/etc/sysctl.conf",将net.ipv4.ip_forward改成1
    2. sysctl -p
  3. 在PREROUTING上修改目的地址
    1.  iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 220.181.111.188:80
  4. 在POSTROUTING上修改源地址
    1. iptables -t nat -A POSTROUTING -d 220.181.111.188 -p tcp --dport 80 -j SNAT --to 192.168.1.20
    2. 或者将用伪装的方式
    3. iptables -t nat -A POSTROUTING -d 220.181.111.188 -p tcp --dport 80 -j MASQUERADE
  5. 为了防止在FORWARD上面被丢弃,添加规则允许通过。
    1. iptables -I FORWARD -d 220.181.111.188 -p tcp --dport 80 -j ACCEPT
    2. iptables -I FORWARD -s 220.181.111.188 -p tcp --sport 80 -j ACCEPT
  6. 现在就可以在客户端上测试一下访问 192.168.1.20:8080 就会访问到 220.181.111.188:80

 

最后提供一段脚本:

#!/bin/bash -
#===============================================================================
#
# FILE: iptable_redirect_port.sh
#
# USAGE: ./iptable_redirect_port.sh PROTOCOL SERVER_PORT DHOST DPORT [-d]
#
# DESCRIPTION: Redirect traffic to another server.
#
# OPTIONS: ---
# REQUIREMENTS: ---
# BUGS: ---
# NOTES: ---
# AUTHOR: Zhang Guangtong <zhgt123@gmail.com>
# ORGANIZATION:
# CREATED: 2015年08月05日 10:07
# REVISION: ---
#===============================================================================

# example:
#iptables -t nat -A PREROUTING -p tcp --dport 1111 -j DNAT --to-destination 192.168.1.10:8022
#iptables -t nat -A POSTROUTING -d 192.168.1.10 -p tcp --dport 8022 -j MASQUERADE

redirect_port()
{
PROTOCOL=$1
SERVER_PORT=$2
DHOST=$3
DPORT=$4
iptables -t nat -A PREROUTING -p $PROTOCOL --dport $SERVER_PORT -j DNAT --to-destination $DHOST:$DPORT
iptables -I FORWARD -d $DHOST -p $PROTOCOL --dport $DPORT -j ACCEPT
iptables -I FORWARD -s $DHOST -p $PROTOCOL --sport $DPORT -j ACCEPT
iptables -t nat -A POSTROUTING -d $DHOST -p $PROTOCOL --dport $DPORT -j MASQUERADE
}
clean_redirect_port()
{
PROTOCOL=$1
SERVER_PORT=$2
DHOST=$3
DPORT=$4
iptables -t nat -D PREROUTING -p $PROTOCOL --dport $SERVER_PORT -j DNAT --to-destination $DHOST:$DPORT
iptables -D FORWARD -d $DHOST -p $PROTOCOL --dport $DPORT -j ACCEPT
iptables -D FORWARD -s $DHOST -p $PROTOCOL --sport $DPORT -j ACCEPT
iptables -t nat -D POSTROUTING -d $DHOST -p $PROTOCOL --dport $DPORT -j MASQUERADE
}
usage()
{
echo "Usage: $0 PROTOCOL SERVER_PORT DHOST DPORT [-d]"
echo ""
echo "example1:"
echo " $0 tcp 443 192.168.1.20 8043"
echo " Visit this host on port 443 equal vist 192.168.1.20:443"
echo "example2:"
echo " $0 tcp 443 192.168.1.20 8043 -d"
echo " Clean previous rules"
echo "Notes: please make sure net.ipv4.ip_forward=1 in /etc/sysctl.conf and run \"sysctl -p\" to apply changes"
}

if [ $# -lt 4 ]; then
usage
exit
fi
if [ "$5" == "-d" ]; then
clean_redirect_port "$@"
else
redirect_port "$@"
fi

用法:

./iptable_redirect_port.sh 协议(tcp或udp) 转发服务器端口 目的服务器IP 目的服务器端口

示例

将访问转发服务器上的443端口转到192.168.1.20:8043

./iptable_redirect_port.sh tcp 443 192.168.1.20 8043

删除刚才的规则:

./iptable_redirect_port.sh tcp 443 192.168.1.20 8043 -d

 

收工。