加入收藏 | 设为首页 | 会员中心 | 我要投稿 宁德站长网 (https://www.0593zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 云计算 > 正文

特洛伊木马图解VXLAN容器网络通信计划

发布时间:2022-04-15 15:05:24 所属栏目:云计算 来源:互联网
导读:一篇文章围绕一张图,讲述一个主题。不过这个主题偏大,我估计需要好几篇文章才能说得清楚。 云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。其中K8s是不可变基础设施的压舱石。典型的K8s集群由数十个Node, 成百个Pod,上千个Cont
  一篇文章围绕一张图,讲述一个主题。不过这个主题偏大,我估计需要好几篇文章才能说得清楚。
 
  云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。其中K8s是不可变基础设施的压舱石。典型的K8s集群由数十个Node, 成百个Pod,上千个Container组成。相互隔离的容器间需要协作才能完成更大规模的应用。而协作就需要网络通信。
 
  这篇文章我主要通过下面这张全景图来讲述K8s是如何利用VXLAN来实现K8s的容器通信方案的。网络通信不是量子纠缠,网络流量是实打实地通过了各个虚拟的、实体的网络设备,途径每个设备节点时自然也会受到设备上的路由、iptables等策略控制。
 
  环境说明
  这张图里面,Node 1 和Node X位于同一个局域网17.168.0.0/24。Node 1的IP地址是17.168.0.2,Node X的IP是17.168.0.3。
 
  K8s集群所使用的子网为10.244.0.0/16。对于网络17.168.0.0/24和它里面的交换机和路由器来说,K8s集群所使用的子网是无效的网络,交换机和路由器更是无从转发、路由任何源IP或目的IP为K8s子网的数据包。
 
  非常明显的矛盾出现了:K8s集群要通过子网为10.244.0.0/16通信,而宿主机环境却根本不认识这个子网。我们接下来将看到"特洛伊木马"的故事在这里再次上演。
 
  我们的目标是在这种矛盾的网络环境下,解释清楚pod a里面的container-1访问pod b里面的container-1时发生了哪些事情。图中蓝色的标线展示了数据流的方向。
 
  图中的绿色标线和绿色的框图表示了与VXLAN相关的数据流和网络封包示意图。
 
  出于简单,Node 1里面只画出了一个Pod, pod a,所有的Pod都连在了bridge cni0上,子网为10.244.0.1/24。Node X里面只画了两个Pod, pod b和pod c ,所有的Pod也一样都连在了bridge cni0上,子网为10.244.1.1/24。
 
  每个Node上面的bridge都分配有IP地址。Pod a的IP地址是10.244.0.2,Pod b的IP地址是10.244.1.3。
 
  同一个Pod内的容器间通信
  这是最简单的情形,内核自带技能,不需额外的组网技术加持。
 
  需要强调的一个知识点是Pod内部所有的容器是共享同一个网络栈、routes以及iptables的,因为它们属于同一个network namespace。
 
  在一个k8s cluster内部,每个Pod拥有独一无二的IP地址,Pod内部所有的container共享分配Pod的地址。Pod内部的容器共享pod的IP地址,但各个容器的端口不能冲突。
 
  由于Pod调度的原子性,一个Pod内部的所有container只会被调度到一台主机上运行。类似本地机器上两个应用程序通过localhost进行进程间通信一样,同一个Pod内部的容器间可以直接通过localhost来通信。此时的traffic直接通过loopback 网络设备在两个容器间流动。图中的bridge无法感知这样的traffic,主机上的网络栈和其它网络设备更不会感知到。
 
  同一个Node内的容器间通信
  图中Node X上画出了多个Pod。当Pod b里面的container-1想要访问Pod c里面的container-1时属于这个场景。
 
  Pod b里面的路由表决定了访问Pod c的traffic需要从自己的interface eth0出去。
 
  src IP:10.244.1.3,dest IP:10.244.1.8。
  src MAC为Pod b veth MAC,dest MAC为Pod c veth MAC。
  从图中可以看到Pod b和Pod c都是插在了bridge上面。作为一个虚拟的二层交换机,它按照二层交换机的行为交换、转发数据包。
 
  在这种场景下,这两个container之间的通信行为不会超出bridge的范围,包括Pod b的container-1通过ARP得知目的container的MAC地址也是在bridge内处理。也不会涉及NAT等地址转换操作。
 
  跨Node的容器间通信
  这是最常用的通信场景。容器访问api server即是典型的例子。
 
  下面开始最复杂的步骤,这些步骤发生在Node 1。Node X收到以太帧后的操作是一个逆过程,这里不做赘述。
 
  我们按照traffic的流向,以它途径的各个网络设备(虚拟的、实体的)为分割节点,分段讲述每段发生了什么。
 
  从container到cni0
  从Pod a的路由表可知,以太帧需要从它的NIC eth0离开。因为eth0是veth的其中一端,另外一端插在bridge cni0上面,于是以太帧进入cni0。此以太帧的目的MAC地址为bridge。
 
  src IP:10.244.0.2,dest IP:10.244.1.3。
  src MAC为Pod a veth MAC,dest MAC为cni0 MAC。
  从cni0到flannel.1
  前面提到该网桥配置有IP地址,现在它收到一个目的MAC地址为自己的数据包,于是触发了 Linux Bridge 的特殊转发规则:网桥不会将这个数据包转发给任何设备,而是直接转交给主机的三层协议栈处理。
 
  主机协议栈根据host的路由表,从而得知需要把IP包交给本机的flannel.1。
 
  从这步以后就是三层路由了,已经不在网桥的工作范围之内,而是由 Linux 主机依靠 Netfilter 进行 IP 转发(IP Forward)去实现的。注意这里是IP包转发,接收者收到的是3层的package,因而它不包含二层的数据。
 
  flannel.1组装内部数据帧
  至此,越过千山万水,本机的flannel.1终于收到了IP包。
 
  从这里开始,flannel.1需要想办法营造幻象:跨主机营造一个虚拟的网络10.244.0.0/16,好让Pod a看起来Pod b和它正处于一个完全合法的、信息交换自由无障碍的环境。天真的Pod们完全不知这个网络是一个虚拟的、私有的、宿主机网络里面的交换机和路由器根本不认识它这样一个事实。
 
  前面提到flannel.1收到的是 IP 包,既然是IP包,那它就没有MAC地址,但flannel.1同时又要想办法把“原始 IP 包”加上一个目的 MAC 地址(当然也需要包含源flannel.1的MAC地址),封装成一个完整的二层数据帧,然后发送给位于Node X上的flannel.1。
 
  而大家都知道要组装一个完整的二层数据帧,首先需要解决的问题是目标 flannel.1的MAC地址是什么呢?下面的提示给出了答案。

  通过ARP,我们知道了目的 flannel.1的MAC是 5e:f8:4f:00:e3:37。到此时,已经完整地产生了内部数据载荷(Inner payload), 内部IP头(Inner IP Header) 10.244.1.3和内部Ethernet头(Inner Ethernet Header)5e:f8:4f:00:e3:37了。
 
  但是,因为上面提到的这些 VTEP 设备的 MAC 地址,对于宿主机网络来说并没有什么实际意义,所以上面封装出来的这个数据帧,并不能在我们的宿主机二层网络里传输。为了方便叙述,我们把它称为“内部数据帧”(Inner Ethernet Frame),或者叫"原始二层数据帧"(Original Layer 2 Frame)。

  从flannel.1发起UDP连接
  好了,“希腊战士”有了,我们就差一个木马了。接下来要做的事情是,像把希腊战士藏到木马里一样,Linux 内核要把这个VXLAN数据帧塞进一个 UDP 包里发出去。上面的全景图中,我特意把VXLAN数据帧画得窄了一些,好让你感觉外围稍胖的UDP包确实像是个木马。
 
  Node 1上的flannel.1 设备要扮演一个“网桥”的角色,在二层网络进行 UDP 包的封包和转发。在Node 1看来,它会以为自己的 flannel.1 设备只是在向另外一台宿主机的 flannel.1 设备,发起了一次普通的 UDP 链接,却全然不知它发送的是一个木马(不要紧张,此木马非木马病毒)。
 
  但且慢,先回答一个问题:刚才在组装内部数据帧的时候,我们知道 flannel.1 设备已经知道了目的 flannel.1 设备的 MAC 地址,但这个 UDP 包该发给哪台宿主机呢?也就是说,木马有了,希腊战士也藏到木马肚子里了,但特洛伊城在哪里?
 
  是时候轮到一个叫作转发数据库(FDB, Forwarding Database)上场帮忙了。这个 flannel.1“网桥”对应的 FDB 信息,也是 flanneld 进程负责维护的。它的内容可以通过 bridge fdb 命令查看到,如下所示:
 
  复制
  # 在Node 1上,使用“目的VTEP设备”的MAC地址进行查询
  $ bridge fdb show flannel.1 | grep 5e:f8:4f:00:e3:37
  5e:f8:4f:00:e3:37 dev flannel.1 dst 17.168.0.3 self permanent
  1.
  2.
  3.
  在上面这条 FDB 记录里,指定了这样一条规则:发往我们前面提到的“目的 flannel.1”(MAC 地址是 5e:f8:4f:00:e3:37)的二层数据帧,应该通过本机的flannel.1 设备,发往 IP 地址为 17.168.0.3 的主机。显然,这台主机正是 Node X,UDP 包要发往的目的地就找到了。
 
  得到了目的IP地址,自然也会得知Node X的MAC地址。接下来的流程,就是一个正常的,宿主机网络上的封包工作,且最终从 Node 1 的 eth0 网卡发出去了。只不过这个过程发生在虚拟设备flannel.1上面罢了。

(编辑:宁德站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!