最新消息:

iptables防火墙故障排除一例

Linux ipcpu 1825浏览

iptables防火墙故障排除一例.md

ipcpu,2014-08-26

前几天碰到这么一个故障。

故障现象

服务器A

私网IP 10.249.129.181
私网网关 10.249.129.254
私网路由段:10.0.0.0/8
公网IP 202.106.2.8

因为这个服务器有公网IP地址,所以被用来当做同网段其他服务器的网关,借助此机器上网。

iptables中的规则是这样的

  1. -A POSTROUTING -s 10.0.0.0/8 -j SNAT --to 202.106.2.8

在实际使用中发现,其他机器通过本机NAT上网时没问题的,但是本机ping 不通自己的内网网关!

经过实验发现此服务器有如下特性:

  1. 同网段和局域网内其他网段的机器访问本机的服务(HTTP)是ok的。
  2. 本机主动发起数据包到内网网关和其他内网机器不通。

故障定位

经过分析整个iptables数据流程图(备注1)发现,本地发起的数据包,需要经过

  1. nat-OUTPUT--->> filter-OUTPUT--->>nat-POSTROUTING(备注2

因此断定,POSTROUTING 中将本地发起的数据包SNAT导致内网网关不识别该数据包,无法通信。

将防火墙的NAT表的整个网段换成单个的IP地址就好了。

进阶分析

事情貌似是结束了,但其实并没有,才刚刚开始。

如果所有的数据包都要经过nat-OUTPUT,filter-OUTPUT,nat-POSTROUTING 这样一个过程,那么这个机器上的服务其实不能对内网正常提供服务的。这个问题才是值得思考的。

通过iptables的LOG来记录日志(同备注2),我发现,本地发起的包是按照nat-OUTPUT,filter-OUTPUT,nat-POSTROUTING 顺序的。但是别人访问该服务器的服务(例如apache),该服务器返回的数据包顺序只有 filter-OUTPUT 。(备注3)

经过查找相关资料,得到一个结论:

  1. 每个连接只有第一个数据包才会经过NAT这个表。
  2. Be aware that the nat table is traversed only by the first packet of each connection.

因此此处可以解释的通了,第一个连接对TCP来说就是SYN包,因此本地发起的SYN会经过nat-OUTPUT,nat-POSTROUTING表,于是被SNAT误操作。而对于别人访问该服务器,该服务器返回数据,此时返回的应该是ACK,SYN包,因此此包不经过nat表,是可以到达目的地的。

对于nat表的这个特性,我们也可以从NAT服务器上得到一些蛛丝马迹:

将iptables计数器全部清零以后,我们得到了如下数据:

  1. Chain FORWARD (policy ACCEPT 23948 packets, 1388K bytes)
  2. pkts bytes target prot opt in out source destination
  3. 489 22192 ACCEPT all -- * * 10.249.129 181 0.0.0.0/0
  4. Chain POSTROUTING (policy ACCEPT 59116 packets, 3331K bytes)
  5. pkts bytes target prot opt in out source destination
  6. 14 840 MASQUERADE all -- * * 10.249.129.181 0.0.0.0/0

从以上数据可以看出,nat表记录的包数量的确少了很多。

既然只有第一个包经过nat表,那后续的包怎么办?
后续的属于该连接的所有数据包将按照第一个数据包的处理动作做同样的操作,这种特性是由连接跟踪机制来实现的。

有兴趣的同学可以看下这两篇文章(难度较大,源码级别):
http://blog.chinaunix.net/uid-23069658-id-3169450.html
http://blog.chinaunix.net/uid-10167808-id-26001.html

OK。就到这里了。

备注1

NAT数据流图:

备注2

如何验证该流程?
这里我使用了iptables的LOG动作来记录数据包,在iptables中添加如下条目

  1. iptables -A OUTPUT -d 10.249.64.2/32 -j LOG --log-prefix "fl-OUTPUT"
  2. iptables -t nat -A POSTROUTING -d 10.249.64.2/32 -j LOG --log-prefix "nat-POSTROUTING"
  3. iptables -t nat -A OUTPUT -d 10.249.64.2/32 -j LOG --log-prefix "nat-OUTPUT"

然后使用curl http://10.249.64.2 产生数据包,查看message日志,得到

  1. nat-OUTPUTIN= OUT=eth0 SRC=10.249.129.181 DST=10.249.64.2 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=6247 DF PROTO=TCP SPT=55339 DPT=80 WINDOW=14600 RES=0x00 SYN URGP=0
  2. fl-OUTPUTIN= OUT=eth0 SRC=10.249.129.181 DST=10.249.64.2 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=6247 DF PROTO=TCP SPT=55339 DPT=80 WINDOW=14600 RES=0x00 SYN URGP=0
  3. nat-POSTROUTINGIN= OUT=eth0 SRC=10.249.129.181 DST=10.249.64.2 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=6247 DF PROTO=TCP SPT=55339 DPT=80 WINDOW=14600 RES=0x00 SYN URGP=0

备注3

保持iptables规则不变。
当我们反过来在10.249.64.2上执行curl http://10.249.129.181 得到的日志如下:

  1. fl-OUTPUTIN= OUT=eth0 SRC=10.249.129.181 DST=10.249.64.2 LEN=52 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=80 DPT=44952 WINDOW=14600 RES=0x00 ACK SYN URGP=0
  2. fl-OUTPUTIN= OUT=eth0 SRC=10.249.129.181 DST=10.249.64.2 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=28371 DF PROTO=TCP SPT=80 DPT=44952 WINDOW=123 RES=0x00 ACK URGP=0
  3. fl-OUTPUTIN= OUT=eth0 SRC=10.249.129.181 DST=10.249.64.2 LEN=562 TOS=0x00 PREC=0x00 TTL=64 ID=28372 DF PROTO=TCP SPT=80 DPT=44952 WINDOW=123 RES=0x00 ACK PSH URGP=0

参考资料:

http://www.microhowto.info/troubleshooting/troubleshooting_iptables.html
http://blog.chinaunix.net/uid-23069658-id-3163999.html
http://ebtables.sourceforge.net/br_fw_ia/br_fw_ia.html

转载请注明:IPCPU-网络之路 » iptables防火墙故障排除一例