iptables学习笔记:端口转发之“外网访问内网”

考虑一种网络拓扑应用情景,一个内部局域网中有多台服务器提供不同的服务,如web服务、FTP服务、ssh、telnet等,通过服务器(或网关、防火墙)连接外部网络,如果外部网络上的主机需要访问这些服务器,则需要在网关上实现转发。

再转述成另一种应用场合,多台设备连接到一台服务器,服务器有2个网卡,分别连接内外网。外网无法直接访问设备上的数据、服务。在服务器上实现转发后,则可达到目的。

网络拓扑如下:

iptables学习笔记:端口转发之“外网访问内网”

比如,可以通过服务器的8081端口访问1号设备的web服务,8082端口访问2号设备web,这样可以在外部网络对内网设备进行参数配置、调整。类似地,通过2321访问1号设备的telnet服务,2322访问2号设备telnet,以方便登陆设备系统,进行设备状态监控,日志处理,等等。

本文将直接引用此网络拓扑图中的名称及IP地址。实际使用配置根据实际情况修改。另外说明一下,不必拘泥于本文给出的名称。像拓扑图中的“设备”,可以使用一台安装Linux的服务器替换。其它的类似。

一、原理

linux系统使用iptables实现防火墙、数据转发等功能。iptables有不同的表(tables),每个tables有不同的链(chain),每条chain有一个或多个规则(rule)。本文利用NAT(network address translation,网络地址转换)表来实现数据包的转发。iptables命令要用-t来指定表,如果没有指明,则使用系统缺省的表“filter”。所以使用NAT的时候,就要用“-t nat”选项了。
NAT表有三条缺省的链,它们分别是PREROUTING、POSTROUTING和OUTPUT。

先给出NAT结构,如下图:

iptables学习笔记:端口转发之“外网访问内网”


PREROUTING:在数据包传入时,就进到PREROUTIING链。该链执行的是修改数据包内的目的IP地址,即DNAT(变更目的IP地址)。PREROUTING只能进行DNAT。因为进行了DNAT,才能在路由表中做判断,决定送到本地或其它网口。

POSTROUTING:相对的,在POSTROUTING链后,就传出数据包,该链是整个NAT结构的最末端。执行的是修改数据包的源IP地址,即SNAT。POSTROUTING只能进行SNAT。
OUTPUT:定义对本地产生的数据包的目的NAT规则。

每个数据包都会依次经过三个不同的机制,首先是PREROUTING(DNAT),再到路由表,最后到POSTROUTING(SNAT)。下面给出数据包流方向:

iptables学习笔记:端口转发之“外网访问内网”

文中的网络拓扑图所示的数据包,是从eth0入,eth1出。但是,无论从eth0到eth1,还是从eth1到eth0,均遵守上述的原理。就是说,SNAT和DNAT并没有规定只能在某一个网口(某一侧)。

顺便给出netfilter的完整结构图:

iptables学习笔记:端口转发之“外网访问内网”

二、实现

首先要在服务器上使能转发,命令如下:

[cpp] view plain copy
  1. echo "1" > /proc/sys/net/ipv4/ip_forward  
根据拓扑图,一一实现不同IP、不同端口的映射,如下命令为一种示例形式:

[cpp] view plain copy
  1. # 第一台设备的telnet服务  
  2. iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 2321 -j DNAT --to 100.100.100.101:23  
  3. iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.101 -p tcp --dport 23 -j SNAT --to 100.100.100.44  
  4. # 第二台设备的telnet服务  
  5. iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 2322 -j DNAT --to 100.100.100.102:23  
  6. iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.102 -p tcp --dport 23 -j SNAT --to 100.100.100.44  
  7.   
  8. # 第一台设备的web服务  
  9. iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 8081 -j DNAT --to 100.100.100.101:80  
  10. iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.101 -p tcp --dport 80 -j SNAT --to 100.100.100.44  
  11. # 第二台设备的web服务  
  12. iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 8082 -j DNAT --to 100.100.100.102:80  
  13. iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.102 -p tcp --dport 80 -j SNAT --to 100.100.100.44  

以第一台设备转发命令为例,用白话解释一下。

第一条是PREROUTING链,只能进行DNAT,该命令对从eth0进入且目的IP为172.18.44.44(注:可以用-s指明数据包来源地址,但这时无法知道来源IP是多少,虽然可以用网段的做法,但用-d则指定必须一定唯一的是本机的eth0地址,相对好一点),端口号为2321的数据包进行目的地址更改,更改为100.100.100.101,端口为23,亦即此包的目的地为第一台设备的telnet服务。

第二条是POSTROUTING链,只能进行SNAT,即对先前已经DNAT过的数据包修改源IP地址。

这样,这个数据包达到第一台设备时,源IP地址、目的IP地址,均为100.100.100.0/24网段了。

上述命令的SNAT有些冗余,可以做简化,命令如下:

[cpp] view plain copy
  1. # 第一台设备的telnet、web服务  
  2. iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 2321 -j DNAT --to 100.100.100.101:23  
  3. iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 8081 -j DNAT --to 100.100.100.101:80  
  4. # 第二台设备的telnet、web服务  
  5. iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 2322 -j DNAT --to 100.100.100.102:23  
  6. iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 8082 -j DNAT --to 100.100.100.102:80  
  7. # 源IP地址SNAT  
  8. iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.0/24 -j SNAT --to 100.100.100.44  
实际中使用的命令可能还有变化(简化),本文不再展示。

三、测试

为了保证文中所述的正确性,本节列出操作结果,以及实验过程的信息。

服务器(网关)上的路由表如下:

[cpp] view plain copy
  1. [email protected]:test# route  
  2. Kernel IP routing table  
  3. Destination     Gateway         Genmask         Flags Metric Ref    Use Iface  
  4. 100.100.100.0   *               255.255.255.0   U     0      0        0 eth1  
  5. 172.18.0.0      *               255.255.0.0     U     0      0        0 eth0  

可以看到服务器上有2个网卡,网段都不相同。(注:此处没有设置默认网关,下一篇文章将需默认网关)

iptables的NAT表如下:

[cpp] view plain copy
  1. [email protected]:~# iptables -L -t nat  
  2. Chain PREROUTING (policy ACCEPT)  
  3. target     prot opt source               destination           
  4. DNAT       tcp  --  anywhere             172.18.44.44         tcp dpt:2324 to:100.100.100.101:23  
  5.   
  6. Chain INPUT (policy ACCEPT)  
  7. target     prot opt source               destination           
  8.   
  9. Chain OUTPUT (policy ACCEPT)  
  10. target     prot opt source               destination           
  11.   
  12. Chain POSTROUTING (policy ACCEPT)  
  13. target     prot opt source               destination           
  14. SNAT       all  --  anywhere             100.100.100.0/24     to:100.100.100.44  
可以看到,PREROUTING和POSTROUTING各有一条规则,这些规则由上文命令所产生。

对应的,在第一号设备上查看路由信息,如下:

[cpp] view plain copy
  1. [email protected]:~# route  
  2. Kernel IP routing table  
  3. Destination     Gateway         Genmask         Flags Metric Ref    Use Iface  
  4. 100.100.100.0   *               255.255.255.0   U     0      0        0 eth0  
  5. 172.18.0.0      *               255.255.0.0     U     0      0        0 eth1  
  6. default         100.100.100.44 0.0.0.0         UG    0      0        0 eth0  
可以看到这台设备有2个网卡,默认网关为服务器的IP地址。但是,其中一个网卡eth1竟然和PC所在网段相同!如果没有进行源IP地址修改(伪装),会匹配到eth1这个网口,无法匹配eth0。

在外网的PC上对设备进行telnet,设备抓包信息如下:

[cpp] view plain copy
  1. IP 100.100.100.44.32253 > 100.100.100.101.2323: Flags [P.], seq 1:4, ack 16, win 256, length 3  
  2. IP 100.100.100.101.2323 > 100.100.100.44.32253: Flags [P.], seq 16:19, ack 4, win 2190, length 3  
  3. IP 100.100.100.44.32253 > 100.100.100.101.2323: Flags [P.], seq 4:25, ack 19, win 256, length 21  
  4. IP 100.100.100.101.2323 > 100.100.100.44.32253: Flags [P.], seq 19:34, ack 25, win 2190, length 15  
可见,所有包的IP段都相同。
在服务器上对内网eth1进行抓包,由于进行了DNAT和SNAT,此网卡数据包IP地址还是100.100.100.0/24网段,如下:

[cpp] view plain copy
  1. IP 100.100.100.44.32253 > 100.100.100.101.telnet: Flags [.], ack 1, win 256, length 0  
  2. IP 100.100.100.101.telnet > 100.100.100.44.32253: Flags [P.], seq 1:16, ack 1, win 2190, length 15  
  3. IP 100.100.100.44.32253 > 100.100.100.101.telnet: Flags [P.], seq 1:4, ack 16, win 256, length 3  
  4. IP 100.100.100.101.telnet > 100.100.100.44.32253: Flags [P.], seq 16:19, ack 4, win 2190, length 3  
但是,在服务器eth0抓包,将会是172.18.0.0/16的网段数据包:

[cpp] view plain copy
  1. IP 172.18.44.142.32253 > 172.18.44.44.2324: Flags [P.], seq 18:20, ack 154, win 255, length 2  
  2. IP 172.18.44.44.2324 > 172.18.44.142.32253: Flags [P.], seq 154:156, ack 20, win 2190, length 2  
  3. IP 172.18.44.44.2324 > 172.18.44.142.32253: Flags [F.], seq 156, ack 20, win 2190, length 0  
  4. IP 172.18.44.142.32253 > 172.18.44.44.2324: Flags [.], ack 157, win 255, length 0  
  5. IP 172.18.44.142.32253 > 172.18.44.44.2324: Flags [F.], seq 20, ack 157, win 255, length 0  
  6. IP 172.18.44.44.2324 > 172.18.44.142.32253: Flags [.], ack 21, win 2190, length 0  

从抓包分析,本文所用命令已经能正确进行DNAT和SNAT了。

四、其它

本文使用的网络拓扑图中有多台设备,故只有使用特定端口转发,但如果转发的只有一台设备的话,在iptables命令中就无须指定端口,从而简化命令。

参考资料:
《Linux网络安全技术与实现》

李迟 2016.9.25 周日