一次 k3s 容器无法访问外网分析

2023年 9月 26日 83.5k 0

问题现象

k3s 边缘集群容器无法与 [cc-rancher-xxx.xeewo.com] 建连,导致 rancher 无法启动。现象是发出去的 SYN 包没有收到回复 SYN+ACK 的包,但在物理机上一切正常,ping 和 curl 都可以成功。

经确认,实际上不止这个域名无法建连,其实是容器压根无法访问外网,ping 和 tcp 建连都是不行的,ping 显示 100% lost。

分析过程

首先先解决 ping 都不通的问题,经确认我们用的是 flannel 网络模型,在 pod 内发起目标地址为外网的 icmp 包会依次经经过 pod/eth0 -> cni0 -> node/eth0

于是分别在这三个网卡上抓包,发现 icmp 包正常通过宿主机的 eth0 发了出去,且 eth0 也收到了 ICMP 的响应,但是,eth0 没有将 ICMP 响应包进一步发送给 cni0,pod 中自然是收不到的 ICMP 的响应包,因此 ping 一直都是 100% lost。

正常网络包丢了,本能怀疑是 iptables 规则导致,经过一番查找和对比,没有发现什么异常的规则会丢弃,于是去查看 icmp 的包,之前 tcpdump 没有加 -v,没发现宿主机收到的 icmp 包的 ttl 变为了 1。同时还发现,宿主机回复了一个「time exceed in-transit」的 icmp 消息。

TTL 的设计初衷是为了防止数据包在互联网上无限制地循环下去。每当一个数据包经过一个路由器,该路由器都会将数据包的 TTL 值减少 1。当 TTL 达到 0 时,路由器会丢弃该数据包,并通常发送一个 ICMP "Time Exceeded" 消息给原始发送者。

当 ICMP 或 IP 头中的 TTL 变为 1,意味着数据包要么到达目标,要么经过下一个路由器后被丢弃。

当数据包在宿主机和容器之间移动时,数据包可能需要经过一个或多个网络设备(网桥等),这可能会导致 TTL 的减少到 0,被网络设备丢弃。

比如 flannel 代码为例,在处理包时就有类似的逻辑

image.png

image.png

那是不是确实经过的网络设备太多,导致 ttl 不够用了呢?在容器内,把 ttl 先改大为 255,再试下,发现回复的 ttl 依然是 1,看来是有什么路由器在把包发给边缘节点的时候,强制把 ttl 改为了 1。

如果是这个问题,那可以通过 iptables 来把宿主机收到的 ttl 改大一点,比如改为 64。

iptables -t mangle -A PREROUTING -p icmp --icmp-type echo-reply -m ttl --ttl-eq 1 -j TTL --ttl-set 64

改完 ping 命令就都可以成功了。

那 ping 能成功,是不是 tcp 请求就能成功呢?

会发现 telnet 和 curl 都会超时,此时再来抓包看看。

发现 tcp 包里的 IP 头里的 TTL 也被设置为了 1。IP 头的 TTL 变为 1,不会把包继续传给容器内下一跳,容器就收不到服务器回复的 SYN+ACK 了。

于是我们再通过 iptables 将 IP 头的 ttl 也设置为 64。

iptables -t mangle -A PREROUTING -m ttl --ttl-eq 1 -j TTL --ttl-set 64

再次请求就成功返回了。

16956965999435

小结

目前为什么 ttl 被设置为了 1,还需要跟边缘节点部署方确认。暂时可以通过此方法 hack
绕过。

iptables -t mangle -A PREROUTING -p icmp --icmp-type echo-reply -m ttl --ttl-eq 1 -j TTL --ttl-set 64
iptables -t mangle -A PREROUTING -m ttl --ttl-eq 1 -j TTL --ttl-set 64

另外,上面 iptables 规则的改动,重启会丢失,需要做持久化。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论