在前文源地址审计:追踪 kubernetes 服务的SNAT ,我介绍了kubernetes的svc的SNAT,以及如何避免的方法,但实际应用时,还会遇到另一种SNAT的情况:flannel。

有关flannel请参考我的另外两篇文章:kubernets的网络插件:flannel kubernetes flannel代码解析

先来介绍下背景。

我们某个产品由于某些不可告人的原因,server需要多实例,并且需要server对外提供服务的端口号是不变的(可以参考云上的RDS,假设是3306)。我们选择的方案是,将server置于flannel中,通过kube-keepalived来管理virtual ip和DNAT规则的下发,它会向宿主机网卡下发一个虚IP,并监控server对应的svc,下发DNAT规则,将目的地址是虚IP+3306的流量,转到前述svc的endpoint。

最近要做源地址审计,发现server容器中看到的所有来源IP都是node上的ip,也就是说client过来的报文被做了SNAT,查了一下iptables规则,发现是flannel下发的。

-A POSTROUTING -s 10.244.0.0/16 -d 10.244.0.0/16 -j RETURN
-A POSTROUTING -s 10.244.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE
-A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.0.0/16 -j MASQUERADE

起作用的是最后一条:所有来源IP不是flannel网络、目的IP是flannel网络的流量,都要做MASQUERADE,即SNAT。因此在容器里看到的就是SNAT后的地址了。

那么,这条规则是不是必须的呢?

对于到容器里的流量,其实做SNAT是没有必要的:容器内已经有默认路由指向cni0了,即使不做SNAT,报文也可以正常从容器出来,到了node上,跟做不做SNAT就更没什么关系了。

我试着把这条规则在某个kubernets测试集群上删除了以后,client仍然能正常连接到容器内,且确实可以避免SNAT,容器能看到真实的源地址;也不影响nodePort的功能。

给flannel提了个issue,咨询了下删除是否是安全的;早上看到tomdee 答复这个问题已经在0.8以后的版本都修复了(我们用的是0.7),修复的PR是#745。看代码是加了一条针对本node的flannel网络的iptables规则,对于这种从外部来、目的地址是本node上的容器的ip,则直接return,跳过下面的SNAT。

刚好手头有个1.8.1的kubernetes,看了下新的规则:

-A POSTROUTING -s 10.244.0.0/16 -d 10.244.0.0/16 -j RETURN
-A POSTROUTING -s 10.244.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE
-A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.0.0/24 -j RETURN
-A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.0.0/16 -j MASQUERADE

新加的是第三条。

那么最后这条SNAT规则是否还有用呢?

注意这两条规则的区别,一个是16网段,一个是24网段,即一个是整个flannel网段,一个是本node的网段。

如果将最后这条规则删除,那么如果virutal ip和server容器不在同一个node上,流量需要从virtual ip的node转发到server容器的node上,此时若不走最后一条规则做SNAT,报文到了server容器以后,直接就从server容器的node返回到client了,也是一个三角流量,连接建立不起来。所以这条规则是需要的。

flannel的升级还是很有必要的,我们之前遇到一个误重启iptables-service,造成之前flannel下发的iptables规则被删除不能恢复的问题,社区在0.8版本加了一个5s的定期检查,如果规则被删掉了则重新下发(类似kube-proxy),解决了这个问题。