最近发现kolla
安装的openstack(Ocata版本)
环境中,某虚拟网络上的虚拟机对外访问异常。经过调查,发现虚拟机外发数据包经过安全组
的网桥后源地址被修改为宿主机的IP
。
简化的网络拓扑如图:
在虚拟机的网络接口上抓包观察,数据包正常:
1 | [root@control01 ~]# tcpdump -i tap812bd281-f5 -nn icmp |
而在安全组
的veth
端抓包观察,数据包源IP已被修改为宿主机IP:
1 | [root@control01 ~]# tcpdump -iqvb812bd281-f5 -nn icmp |
docker
的默认网段为172.17.0.1/16
, 而该虚拟网络网段恰好和docker
网段重叠, 因而怀疑是网络冲突造成。
1 | [root@control01 ~]# ip addr show docker0 |
查看系统上的iptables
规则,发现docker
会创建一条MASQUERADE
规则, 用于对docker
容器访问外部IP的数据包进行NAT
操作:
但实际上,该条规则写的过于宽泛,认为只要源IP网段是docker
接口所配置的网段,并且出口不是docker
接口的数据都认为是docker
容器内外出的数据包。
在kolla
部署的openstack
场景下,因为宿主机上net.bridge.bridge-nf-call-iptables
参数是开启的,从虚拟机通过安全组
网桥时也会进行iptables
规则匹配,由于网段重叠,同样会命中该条规则,因而被错误地执行MASQUERADE
操作。
该问题从根本上看,是由于docker
的这条规则写的过于宽泛,应该更精确一些,明确针对来自于docker
容器的数据包。因为在POSTROUTING
链上不允许使用-i
来指定来源设备,可以将条件拆成两个阶段。在PREROUTING
阶段对来自于docker
容器的数据包设置mark
值,而在POSTROUTING
阶段的MASQUERADE
规则额外添加上mark
值的匹配条件。
这需要修改docker
的源码,具体修改本文不详述,这里简单手动修改规则进行验证:
1 | iptables -t nat -I PREROUTING 1 -i docker0 -j MARK --set-mark 0x12345678 |
添加后的iptables
规则如图:
这时再次从安全组
网桥的veth
端抓包观察, 可以看到数据包恢复正常:
1 | [root@control01 ~]# tcpdump -iqvb812bd281-f5 -nn icmp |
还有另一种解决方法。因为openstack
所添加的安全组
网桥,名称是有特征性的, 都以qbr
为前缀,可以在MASQUERADE
规则前,添加iptables
规则跳过安全组
网桥的流量:
1 | iptables -t nat -I POSTROUTING 2 -o qbr+ -j RETURN |
修改后的iptables
规则如图:
此时再次在安全组
网桥的veth
端抓包,确认数据包也恢复正常:
1 | [root@control01 ~]# tcpdump -iqvb812bd281-f5 -nn icmp |
很久没有研究过openstack
,可能现在新版本的openstack
已经不存在这种网桥形式的安全组
实现了,因而也就不会存在这个问题。