0 背景8 X8 F5 I: d3 X0 M
考虑一种网络拓扑应用情景,一个内部局域网中有多台服务器提供不同的服务,如web服务、FTP服务、ssh、telnet等,通过服务器(或网关、防火墙)连接外部网络,如果外部网络上的主机需要访问这些服务器,则需要在网关上实现转发。 再转述成另一种应用场合,多台设备连接到一台服务器,服务器有2个网卡,分别连接内外网。外网无法直接访问设备上的数据、服务。在服务器上实现转发后,则可达到目的。 网络拓扑如下: 比如,可以通过服务器的8081端口访问1号设备的web服务,8082端口访问2号设备web,这样可以在外部网络对内网设备进行参数配置、调整。类似地,通过2321访问1号设备的telnet服务,2322访问2号设备telnet,以方便登陆设备系统,进行设备状态监控,日志处理,等等。 本文将直接引用此网络拓扑图中的名称及IP地址。实际使用配置根据实际情况修改。另外说明一下,不必拘泥于本文给出的名称。像拓扑图中的“设备”,可以使用一台安装linux的服务器替换。其它的类似。
. T0 [1 s$ P3 i' M+ R+ b
一、原理 在Linux系统使用iptables实现防火墙、数据转发等功能。iptables有不同的表(tables),每个tables有不同的链(chain),每条chain有一个或多个规则(rule)。本文利用NAT(network address translation,网络地址转换)表来实现数据包的转发。iptables命令要用-t来指定表,如果没有指明,则使用系统缺省的表“filter”。所以使用NAT的时候,就要用“-t nat”选项了。
9 r8 i+ N- k w% ~( C7 iNAT表有三条缺省的链,它们分别是PREROUTING、POSTROUTING和OUTPUT。 先给出NAT结构,如下图: PREROUTING:在数据包传入时,就进到PREROUTIING链。该链执行的是修改数据包内的目的IP地址,即DNAT(变更目的IP地址)。PREROUTING只能进行DNAT。因为进行了DNAT,才能在路由表中做判断,决定送到本地或其它网口。 POSTROUTING:相对的,在POSTROUTING链后,就传出数据包,该链是整个NAT结构的最末端。执行的是修改数据包的源IP地址,即SNAT。POSTROUTING只能进行SNAT。 OUTPUT:定义对本地产生的数据包的目的NAT规则。
; T0 q0 i+ B) S8 b! b4 }$ X5 b0 J' H
每个数据包都会依次经过三个不同的机制,首先是PREROUTING(DNAT),再到路由表,最后到POSTROUTING(SNAT)。下面给出数据包流方向: 文中的网络拓扑图所示的数据包,是从eth0入,eth1出。但是,无论从eth0到eth1,还是从eth1到eth0,均遵守上述的原理。就是说,SNAT和DNAT并没有规定只能在某一个网口(某一侧)。 顺便给出netfilter的完整结构图: 二、实现出于安全考虑,Linux系统默认是禁止数据包转发的。所谓转发即当主机拥有多于一块的网卡时,其中一块收到数据包,根据数据包的目的ip地址将包发往本机另一网卡,该网卡根据路由表继续发送数据包。这通常就是路由器所要实现的功能。 配置Linux系统的ip转发功能,首先保证硬件连通,然后打开系统的转发功能 cat /proc/sys/net/ipv4/ip_forward,该文件内容为0,表示禁止数据包转发,1表示允许,将其修改为1。可使用命令echo "1" > /proc/sys/net/ipv4/ip_forward 修改文件内容,重启网络服务或主机后效果不再。若要其自动执行,可将命令echo "1" > /proc/sys/net/ipv4/ip_forward 写入脚本/etc/rc.d/rc.local 或者在/etc/sysconfig/network脚本中添加 FORWARD_IPV4="YES" 但在我的系统中没有这两个文件,因此可以修改/etc/sysctl.conf文件,将net.ipv4.ip_forward=1的注释取消即可 根据拓扑图,一一实现不同IP、不同端口的映射,如下命令为一种示例形式:
' ^0 O4 d4 l. i7 a! b8 i8 G- # 第一台设备的telnet服务. \: d6 x. ^- ~8 J# Y
- iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 2321 -j DNAT --to 100.100.100.101:23
5 E' F2 O) ?2 g4 S$ s: d - iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.101 -p tcp --dport 23 -j SNAT --to 100.100.100.44' p0 ~& L: m/ f) M: z. `0 d
- # 第二台设备的telnet服务5 ^2 Z- R* n$ S6 c$ z1 C R# b
- iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 2322 -j DNAT --to 100.100.100.102:23
. s" ^* @2 K1 H - iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.102 -p tcp --dport 23 -j SNAT --to 100.100.100.44+ n' x! K( T' B* D9 v
- , D2 b- O2 R4 Q6 h0 L9 g6 r
- # 第一台设备的web服务3 s# O. r/ |' a- \
- iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 8081 -j DNAT --to 100.100.100.101:80
) \; d9 c4 u9 n9 D: i+ P, E - iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.101 -p tcp --dport 80 -j SNAT --to 100.100.100.44
4 o1 g, F$ x6 O2 ] - # 第二台设备的web服务; J4 L" f A6 N
- iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 8082 -j DNAT --to 100.100.100.102:80
+ E% C. ?5 T/ U2 {. a - iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.102 -p tcp --dport 80 -j SNAT --to 100.100.100.44
复制代码
! O* l- \4 T9 x% ]4 g+ b+ _
以第一台设备转发命令为例,用白话解释一下。 第一条是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网段了。
2 a9 ^/ k% i+ R/ C( a& X4 ~' v
上述命令的SNAT有些冗余,可以做简化,命令如下:
0 L- ~- I2 u0 }- # 第一台设备的telnet、web服务$ ~7 C2 `; B" Y# G3 V. L' }
- iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 2321 -j DNAT --to 100.100.100.101:23
! g |& \) q7 ~, L; ] v3 U - iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 8081 -j DNAT --to 100.100.100.101:80
. b, X9 r j1 K. w# }+ m& R - # 第二台设备的telnet、web服务- u/ Y! I1 I7 s9 u
- iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 2322 -j DNAT --to 100.100.100.102:23
: ?$ P. F, g3 H - iptables -t nat -A PREROUTING -i eth0 -d 172.18.44.44 -p tcp --dport 8082 -j DNAT --to 100.100.100.102:809 g/ I( f0 G3 N! A; E( h3 ]7 |; B
- # 源IP地址SNAT9 Q7 H' r) r: K3 y# O* c4 O# T- ?" [
- iptables -t nat -A POSTROUTING -o eth1 -d 100.100.100.0/24 -j SNAT --to 100.100.100.44
复制代码
* r+ N6 [4 e1 x) g- T
实际中使用的命令可能还有变化(简化),本文不再展示。
/ R7 K0 n% p- \& C c h
三、测试 为了保证文中所述的正确性,本节列出操作结果,以及实验过程的信息。服务器(网关)上的路由表如下:
7 Y# e8 P) h7 q, I& r& w; o- root@latelee:test# route1 o" c. Y1 m% E/ l8 k2 w
- Kernel IP routing table
: }0 t. S6 a4 F# W - Destination Gateway Genmask Flags Metric Ref Use Iface
, Q- w9 p' T5 n1 ?; Z" ]* t7 o - 100.100.100.0 * 255.255.255.0 U 0 0 0 eth1
# o/ R, P+ X D0 u, g o" S$ | - 172.18.0.0 * 255.255.0.0 U 0 0 0 eth0
复制代码 ( t( a& s1 c0 C
可以看到服务器上有2个网卡,网段都不相同。iptables的NAT表如下:
2 d, j6 D: n: V7 e- root@latelee:~# iptables -L -t nat
1 W7 g+ z5 S* K. Z% { @ - Chain PREROUTING (policy ACCEPT)
# j6 N( B' U: _( d - target prot opt source destination 0 p- e; j& P4 }
- DNAT tcp -- anywhere 172.18.44.44 tcp dpt:2324 to:100.100.100.101:23
}- Z: u4 d: Q6 e; s7 A - Chain INPUT (policy ACCEPT)0 R! X& T7 x* P2 E8 K* D
- target prot opt source destination & f; w7 Y( @. `' M; D
- Chain OUTPUT (policy ACCEPT)6 T: ^; L& \7 ~" u
- target prot opt source destination $ I( V8 D6 Q) |
- Chain POSTROUTING (policy ACCEPT)
$ l% O+ ]' h [. P0 p0 V - target prot opt source destination 7 y* Q3 V6 S- d k
- SNAT all -- anywhere 100.100.100.0/24 to:100.100.100.44
复制代码
- q* X( F( }* ^2 K' K/ B可以看到,PREROUTING和POSTROUTING各有一条规则,这些规则由上文命令所产生。对应的,在第一号设备上查看路由信息,如下:
$ X' W( d% J$ s4 \& t- h- M- root@latelee:~# route2 a! h# i; \/ E
- Kernel IP routing table
- d' `" `7 G8 c - Destination Gateway Genmask Flags Metric Ref Use Iface
$ q8 R2 M+ T2 {' Y- B7 C# q d - 100.100.100.0 * 255.255.255.0 U 0 0 0 eth0
+ w9 {3 @3 G! F l/ | - 172.18.0.0 * 255.255.0.0 U 0 0 0 eth18 a$ H$ _0 A _. m/ |! z9 h* P
- default 100.100.100.44 0.0.0.0 UG 0 0 0 eth0
复制代码 2 s7 W5 X- K! x9 }% m' V
可以看到这台设备有2个网卡,默认网关为服务器的IP地址。但是,其中一个网卡eth1竟然和PC所在网段相同!如果没有进行源IP地址修改(伪装),会匹配到eth1这个网口,无法匹配eth0。
4 F9 K* B5 r# V
在外网的PC上对设备进行telnet,设备抓包信息如下: - ! j g' A8 V! s0 `. |# H) ?. y8 Q
- IP 100.100.100.44.32253 > 100.100.100.101.2323: Flags [P.], seq 1:4, ack 16, win 256, length 3
M! ]" ?- U: }, Y, w- N6 H - IP 100.100.100.101.2323 > 100.100.100.44.32253: Flags [P.], seq 16:19, ack 4, win 2190, length 3/ i0 b; D# F3 A
- IP 100.100.100.44.32253 > 100.100.100.101.2323: Flags [P.], seq 4:25, ack 19, win 256, length 21' p6 u- K: E$ w* h% _; ~0 }
- IP 100.100.100.101.2323 > 100.100.100.44.32253: Flags [P.], seq 19:34, ack 25, win 2190, length 15
复制代码 3 V& [% M9 E- B! q6 v
可见,所有包的IP段都相同。在服务器上对内网eth1进行抓包,由于进行了DNAT和SNAT,此网卡数据包IP地址还是100.100.100.0/24网段,如下:
- [& w$ s1 G4 p. H- IP 100.100.100.44.32253 > 100.100.100.101.telnet: Flags [.], ack 1, win 256, length 0
, @9 L& G, X! K9 n6 |8 E - IP 100.100.100.101.telnet > 100.100.100.44.32253: Flags [P.], seq 1:16, ack 1, win 2190, length 15 \# {: _6 ?/ F& p
- IP 100.100.100.44.32253 > 100.100.100.101.telnet: Flags [P.], seq 1:4, ack 16, win 256, length 3
p3 _% f5 d- q- P& T; T& `( o - IP 100.100.100.101.telnet > 100.100.100.44.32253: Flags [P.], seq 16:19, ack 4, win 2190, length 3
复制代码
/ e# O+ T: s; t+ A& Y7 C# C但是,在服务器eth0抓包,将会是172.18.0.0/16的网段数据包:
/ G' J( A- Z1 G9 a; y- IP 172.18.44.142.32253 > 172.18.44.44.2324: Flags [P.], seq 18:20, ack 154, win 255, length 28 M5 x. k! H( P' s# M- k+ u* k6 L
- IP 172.18.44.44.2324 > 172.18.44.142.32253: Flags [P.], seq 154:156, ack 20, win 2190, length 2
( K# J# T" z$ N$ o) A8 W - IP 172.18.44.44.2324 > 172.18.44.142.32253: Flags [F.], seq 156, ack 20, win 2190, length 0! `5 ]) u+ L! v$ w- E
- IP 172.18.44.142.32253 > 172.18.44.44.2324: Flags [.], ack 157, win 255, length 0
( B; k$ C" `* r u - IP 172.18.44.142.32253 > 172.18.44.44.2324: Flags [F.], seq 20, ack 157, win 255, length 0
* f! ?+ g( ~* u2 k# N - IP 172.18.44.44.2324 > 172.18.44.142.32253: Flags [.], ack 21, win 2190, length 0
复制代码从抓包分析,本文所用命令已经能正确进行DNAT和SNAT了。
* j3 W( }& Y' [
四、其它 建议在使用iptabls指令时,使用root用户进行操作,否则容易失败 保存iptables配置方法:
3 Y' s5 y* J# r: O
- iptables-save > /etc/iptables.up.rules$ L9 p D$ B- x: ^# p8 }
2 e( X4 B3 t6 V/ l7 c
配置iptables开机加载 - * A3 n$ P1 C& Q9 S9 {
- iptables-save > /etc/iptables.up.rules
6 u; ~4 A! J$ C5 U! ?4 }& V; c - echo -e '#!/bin/bash\n/sbin/iptables-restore < /etc/iptables.up.rules' > /etc/network/if-pre-up.d/iptables5 x6 p4 Y# Z/ Y0 H2 J$ v$ @2 y# q4 B
- chmod +x /etc/network/if-pre-up.d/iptables
复制代码
( n; E! C1 l' [ ], A
本地测试指令
; Z9 S+ W$ I& L8 D6 w* L, Y) t
" Z; x4 U( x" }1 \& ]* h- iptables -t nat -A PREROUTING -i wlan0 -d 192.168.11.100 -p tcp --dport 8081 -j DNAT --to 192.168.10.101:80
1 B1 u, u! p' b9 Q3 z8 F - iptables -t nat -A POSTROUTING -o eth0 -d 192.168.10.101 -p tcp --dport 80 -j SNAT --to 192.168.10.52
复制代码
' e- B/ V9 J. M- ^! v |