iptables的一次修复日志
搭建配置wireguard后,使用内网连接设备十分方便,我采用的是星型连接,即每个节点都连接到中心节点,但是突然发生了重启wg后中心节点不转发流量的问题,即每个接入的节点只能与中心节点连接,而节点与节点间无法连接。然而reboot重启中心节点机器后,wg内网却能正常工作。最后发现是iptables配置的问题。借此机会熟悉了iptables的逻辑。
1. 发现和解决问题
1.1 现象
当时中心节点上挂了 ipsec,wireguard,和docker等,当时也不知道是谁干的好事,我甚至不知道ipsec也写了这个表,最后锁定了问题出在FORWARD 链。具体是这样的
重启wireguard,复现了问题后,使用
sudo iptables -nvL
查看这个表,最后观察到这个表是这样的。
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 195K 51M DOCKER-USER 0 -- * * 0.0.0.0/0 0.0.0.0/0
2 195K 51M DOCKER-ISOLATION-STAGE-1 0 -- * * 0.0.0.0/0 0.0.0.0/0
3 0 0 ACCEPT 0 -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
4 0 0 DOCKER 0 -- * docker0 0.0.0.0/0 0.0.0.0/0
5 0 0 ACCEPT 0 -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
6 0 0 ACCEPT 0 -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
7 20 800 DROP 0 -- * * 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
8 0 0 ACCEPT 0 -- eth0 ppp+ 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
9 0 0 ACCEPT 0 -- ppp+ eth0 0.0.0.0/0 0.0.0.0/0
10 0 0 ACCEPT 0 -- ppp+ ppp+ 0.0.0.0/0 0.0.0.0/0
11 0 0 ACCEPT 0 -- eth0 * 0.0.0.0/0 192.168.43.0/24 ctstate RELATED,ESTABLISHED
12 0 0 ACCEPT 0 -- * eth0 192.168.43.0/24 0.0.0.0/0
13 0 0 ACCEPT 0 -- * ppp+ 192.168.43.0/24 0.0.0.0/0
14 0 0 ACCEPT 0 -- wg2 * 0.0.0.0/0 0.0.0.0/0
15 0 0 ACCEPT 0 -- * wg2 0.0.0.0/0 0.0.0.0/0
16 393 26860 DROP 0 -- * * 0.0.0.0/0 0.0.0.0/0
17 0 0 ACCEPT 0 -- wg2 * 0.0.0.0/0 0.0.0.0/0
18 0 0 ACCEPT 0 -- * wg2 0.0.0.0/0 0.0.0.0/0
19 0 0 ACCEPT 0 -- wg0 * 0.0.0.0/0 0.0.0.0/0
20 0 0 ACCEPT 0 -- * wg0 0.0.0.0/0 0.0.0.0/0
21 0 0 ACCEPT 0 -- wg0 * 0.0.0.0/0 0.0.0.0/0
22 0 0 ACCEPT 0 -- * wg0 0.0.0.0/0 0.0.0.0/0
流量从上往下匹配,在匹配到wg0的规则前,匹配到了DROP,卒。
然后reboot重启了机器,FORWARD链变成了这样
1 491 106K DOCKER-USER 0 -- * * 0.0.0.0/0 0.0.0.0/0
2 491 106K DOCKER-ISOLATION-STAGE-1 0 -- * * 0.0.0.0/0 0.0.0.0/0
3 0 0 ACCEPT 0 -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
4 0 0 DOCKER 0 -- * docker0 0.0.0.0/0 0.0.0.0/0
5 0 0 ACCEPT 0 -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
6 0 0 ACCEPT 0 -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
7 0 0 DROP 0 -- * * 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
8 0 0 ACCEPT 0 -- eth0 ppp+ 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
9 0 0 ACCEPT 0 -- ppp+ eth0 0.0.0.0/0 0.0.0.0/0
10 0 0 ACCEPT 0 -- ppp+ ppp+ 0.0.0.0/0 0.0.0.0/0
11 0 0 ACCEPT 0 -- eth0 * 0.0.0.0/0 192.168.43.0/24 ctstate RELATED,ESTABLISHED
12 0 0 ACCEPT 0 -- * eth0 192.168.43.0/24 0.0.0.0/0
13 0 0 ACCEPT 0 -- * ppp+ 192.168.43.0/24 0.0.0.0/0
14 0 0 ACCEPT 0 -- wg2 * 0.0.0.0/0 0.0.0.0/0
15 0 0 ACCEPT 0 -- * wg2 0.0.0.0/0 0.0.0.0/0
16 491 106K ACCEPT 0 -- wg0 * 0.0.0.0/0 0.0.0.0/0
17 0 0 ACCEPT 0 -- * wg0 0.0.0.0/0 0.0.0.0/0
18 0 0 DROP 0 -- * * 0.0.0.0/0 0.0.0.0/0
19 0 0 ACCEPT 0 -- wg2 * 0.0.0.0/0 0.0.0.0/0
20 0 0 ACCEPT 0 -- * wg2 0.0.0.0/0 0.0.0.0/0
21 0 0 ACCEPT 0 -- wg0 * 0.0.0.0/0 0.0.0.0/0
22 0 0 ACCEPT 0 -- * wg0 0.0.0.0/0 0.0.0.0/0
因为有wg0的规则出现在了DROP之前,所以流量成功的走了出来。
1.2 补充知识点
iptables中的-A和-I,一个是Insert,一个是Append,Insert会直接插在链规则的最前边,Append则是会直接插在最后边。
1.3 解决问题
看了下wg0.conf是怎么写的
Address = **.**.**.1/24
PrivateKey = *******************************************=
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
publickey = *******************************************=
AllowedIPs = **.**.**.2/32
[Peer]
publickey = *******************************************=
AllowedIPs = **.**.**.3/32
[Peer]
publickey = *******************************************=
AllowedIPs = **.**.**.4/32
[Peer]
publickey = *******************************************=
AllowedIPs = **.**.**.5/32
... ...
其中的PostUp和PostDown被执行,-A 指定了规则append到该表该链的所有规则的后边。
所以reboot重启机器的时候,由于DROP最后才被append加进来,而wireguard的规则先append进来了,所以从上往下执行。就没事。
然而此后重启wireguard,由于重新append时append到了DROP后边,所以该规则没能先于DROP被执行
所以只要重启的时候使用Insert而不是Append就解决了,即把PostUP中的"-A"换成"-I",即
PostUp = iptables -I FORWARD -i %i -j ACCEPT; iptables -I FORWARD -o %i -j ACCEPT; iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
问题就解决了。
2. 问题的解释
关于wg0的规则并排的0有两条,一条是in * out wg0 ,一条是in wg0 out *,它们是冗余的,因为有路由表保证了只有来自wg0的流量才会到wg0。
3.排查docker和ipsec
缺德的DROP是谁添加的呢?为了观察这一点,首先引入了两条工具
3.1 工具
iptables的注释
语法是
iptables -m comment --comment "My comments here"
具体使用案例(直接写在wg0.conf里)
PostUp = iptables -I FORWARD -i %i -m comment --comment "Added in wg0.conf" -j ACCEPT; iptables -I FORWARD -o %i -m comment --comment "Added in wg0.conf" -j ACCEPT; iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
这样就可以在打印iptables表的时候看到注释。
sudo iptables -nvL
4270 203K ACCEPT 0 -- * wg0 0.0.0.0/0 0.0.0.0/0 /* Added in wg0.conf */
0 0 ACCEPT 0 -- wg0 * 0.0.0.0/0 0.0.0.0/0 /* Added in wg0.conf */
TRACE功能
执行以下命令
iptables -t raw -I PREROUTING -d **.**.**.70 -j TRACE
在raw表的PREROUTING链中添加这条规则,
这样在到**.**.**.70的流量如何通过的iptables就可以观察了。观察的方法是执行以下命令
sudo xtables-monitor -t
事后我看到的是这样的情况
PACKET: 2 31ae5dec IN=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=128 ID=59853
TRACE: 2 31ae5dec raw:PREROUTING:rule:0x2:CONTINUE -4 -t raw -A PREROUTING -d 11.11.11.70/32 -j TRACE
TRACE: 2 31ae5dec raw:PREROUTING:return:
TRACE: 2 31ae5dec raw:PREROUTING:policy:ACCEPT
TRACE: 2 31ae5dec nat:PREROUTING:return:
TRACE: 2 31ae5dec nat:PREROUTING:policy:ACCEPT
PACKET: 2 31ae5dec IN=wg0 OUT=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=127 ID=59853
TRACE: 2 31ae5dec filter:FORWARD:rule:0x25:ACCEPT -4 -t filter -A FORWARD -o wg0 -m comment --comment "Added in wg0.conf" -j ACCEPT
TRACE: 2 31ae5dec nat:POSTROUTING:return:
TRACE: 2 31ae5dec nat:POSTROUTING:policy:ACCEPT
PACKET: 2 510f38f9 IN=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=128 ID=59854
TRACE: 2 510f38f9 raw:PREROUTING:rule:0x2:CONTINUE -4 -t raw -A PREROUTING -d 11.11.11.70/32 -j TRACE
TRACE: 2 510f38f9 raw:PREROUTING:return:
TRACE: 2 510f38f9 raw:PREROUTING:policy:ACCEPT
PACKET: 2 510f38f9 IN=wg0 OUT=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=127 ID=59854
TRACE: 2 510f38f9 filter:FORWARD:rule:0x25:ACCEPT -4 -t filter -A FORWARD -o wg0 -m comment --comment "Added in wg0.conf" -j ACCEPT
PACKET: 2 47202071 IN=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=128 ID=59855
TRACE: 2 47202071 raw:PREROUTING:rule:0x2:CONTINUE -4 -t raw -A PREROUTING -d 11.11.11.70/32 -j TRACE
TRACE: 2 47202071 raw:PREROUTING:return:
TRACE: 2 47202071 raw:PREROUTING:policy:ACCEPT
PACKET: 2 47202071 IN=wg0 OUT=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=127 ID=59855
TRACE: 2 47202071 filter:FORWARD:rule:0x25:ACCEPT -4 -t filter -A FORWARD -o wg0 -m comment --comment "Added in wg0.conf" -j ACCEPT
PACKET: 2 47202071 IN=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=128 ID=59856
TRACE: 2 47202071 raw:PREROUTING:rule:0x2:CONTINUE -4 -t raw -A PREROUTING -d 11.11.11.70/32 -j TRACE
TRACE: 2 47202071 raw:PREROUTING:return:
TRACE: 2 47202071 raw:PREROUTING:policy:ACCEPT
PACKET: 2 47202071 IN=wg0 OUT=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=127 ID=59856
TRACE: 2 47202071 filter:FORWARD:rule:0x25:ACCEPT -4 -t filter -A FORWARD -o wg0 -m comment --comment "Added in wg0.conf" -j ACCEPT
当时的情况是
PACKET: 2 831e9d23 IN=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=128 ID=59207
TRACE: 2 831e9d23 raw:PREROUTING:rule:0x2:CONTINUE -4 -t raw -A PREROUTING -d 11.11.11.70/32 -j TRACE
TRACE: 2 831e9d23 raw:PREROUTING:return:
TRACE: 2 831e9d23 raw:PREROUTING:policy:ACCEPT
TRACE: 2 831e9d23 nat:PREROUTING:return:
TRACE: 2 831e9d23 nat:PREROUTING:policy:ACCEPT
PACKET: 2 831e9d23 IN=wg0 OUT=wg0 SRC=11.11.11.20 DST=11.11.11.70 LEN=60 TOS=0x0 TTL=127 ID=59207
TRACE: 2 831e9d23 filter:FORWARD:rule:0x36:JUMP:DOCKER-USER -4 -t filter -A FORWARD -j DOCKER-USER
TRACE: 2 831e9d23 filter:DOCKER-USER:return:
TRACE: 2 831e9d23 filter:FORWARD:rule:0x33:JUMP:DOCKER-ISOLATION-STAGE-1 -4 -t filter -A FORWARD -j DOCKER-ISOLATION-STAGE-1
TRACE: 2 831e9d23 filter:DOCKER-ISOLATION-STAGE-1:return:
TRACE: 2 831e9d23 filter:FORWARD:rule:0x1f:DROP -4 -t filter -A FORWARD -j DROP
就是通过这个DROP发现的问题。
3.2 排查结果
其实上边的工具都没用上,最后发现是ipsec写的DROP,它还同时添加了两条wg0的规则,导致wg0的规则出现了四次。
它想做的是用一个DROP兜底,结果不幸的是我的wg规则append了在它后边。其实这个DROP应该写进policy。
由于iptables大家一起用,所以我感觉在最后append一个DROP这种行为其实本身就很不负责。调整了ipsec,结束维护。
4.iptables的三表五链
三表: nat,mangle,filter,(raw)
五链: PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING