云原生容器安全之安全狗对云原生集群网络流量可观测性的思考

发布时间:2023-04-16 16:00

问题背景

在云原生技术的广泛普及和实施过程中,笔者接触到的很多用户需求里都涉及到对云原生集群的可观测性要求。实现集群的可观测性,是进行集群安全防护的前提条件。而在可观测性的需求中,集群中容器和容器之间网络流量的可观测性需求是其中一个比较重要的部分。对于容器网络的流量采集,其实施难度是大于传统主机网络的流量采集的。

那么容器网络的复杂度到底在哪里?如何更好的去适配容器网络?这里笔者结合在工作实践中的一些积累,在本文中给出一些关于云原生集群网络流量可观测性的一点思考,希望能起到抛砖引玉的效果。

目前在云隙(自适应微隔离)产品云原生场景适配的过程中,已经遇到过如下的一些 CNI 插件

Flannel —— 默认为 Vxlan 的分布式网关模式,可选择 host-gw 模式,也可以选择 udp 模式(早已被淘汰的一种模式)

Calico —— 默认的 BGP 模式,使用 IPIP 隧道进行跨节点容器间数据的转发,当然也可以不用 IPIP 隧道模式(类似于 Flannel 的 host-gw 模式)

Openshift-SDN —— 基于 Openvswitch 技术,是一套基于 SDN 技术的网络方案

OVN-Kubernetes —— 基于 Openvswitch 中的 OVN 方案,其实和 Openshift-SDN 是差不多类似的体系,只是在架构和细节上做了更多的云原生优化

01

现有问题

上述几类插件是大多数云原生环境中常见的 CNI 插件,并且它们本身也涵盖了容器间网络所使用的大部分技术。而在对以这几类 CNI 插件基础的容器网络进行流量信息采集的过程中,笔者发现并不能用一种模式完成所有的采集功能。例如在有的 CNI 插件中,我们需要把宿主机当作一个网络中转设备,在主机上利用旁路的方式检测容器网络流量的变化情况;而在有的 CNI 插件中,我们没法在宿主机上直接观察到任何容器网络的流量信息,需要进入到容器的网络命名空间中去完成采集工作;甚至在有的 CNI 插件中,我们无法抓到两个容器之间的直接访问关系,必须要配合这个 CNI 插件本身实现的技术原理,通过更多的分析来完成对访问关系的确定。

所以,在进行容器网络流量的可观测性方案实施之前,需要对相关 CNI 插件做进一步的分析,来帮助理解为什么不同的 CNI 插件兼容适配的要求会有不同。

插件分析

01

Flannel 插件的原理

Flannel 是 CoreOS 团队针对 Kubernetes 设计的一个覆盖网络(Overlay Network)CNI 插件。它可以配置为很多种工作模式,目前默认的工作模式是 Vxlan,是一个虚拟的二层网络。

组件概述

云原生容器安全之安全狗对云原生集群网络流量可观测性的思考_第1张图片

图1

Flannel 是以 DaemonSet 的方式部署在 Kubernetes 集群中。一个完整的 Flannel 架构中包含了如下的一些组件:

etcd —— 这是 Flannel 所依赖的配置中心,每个节点上的 flanneld 都会依赖这个 etcd 组件来实现协调和同步。在一般的部署方案中,这个 etcd 是直接复用的 Kubernetes 控制层平面的 etcd 组件,所以一般也没有单独部署。

flanneld —— 每一个宿主机节点之上都会启动一个 flanneld 服务,目前默认是基于 Vxlan 的配置工作模式,在这种模式和 host-gw 模式下,flanneld 都主要承担了一个旁路配置管理的工作。而在 UDP 模式下,flanneld 还要承担数据的转发工作,由于该方案本身有严重的性能问题。所以早已被放弃不用。

网络模式概述

总的来说,Flannel 是一个二层的网络解决方案。就其默认的 Vxlan 工作模式来说,可以看作是应用 Vxlan 的分布式网关架构组建了一个二层的通信网络。在这种架构下,宿主机中的虚拟网卡 flannel.1 可以看作是一个作为 Vxlan 二层网关和三层网关的 leaf 节点,而宿主机本身的网卡则可以看作是用于转发的 spine 节点。

云原生容器安全之安全狗对云原生集群网络流量可观测性的思考_第2张图片

图2

在这种网络环境下,跨界点的容器通信在接收端其实是无法直接抓到来自于发送端的 IP 地址的,因为在三层转发的时候,发送端的 IP 就已经被替换为了网关的地址。如果从旁路的角度来看,宿主机上也无法直接观察到容器间流量。如果能观察到,一般都是节点自身两个容器之间的连接,因为它们本身处于一个二层网络中。同时,在这种环境中,如果使用 SideCar 模式去容器网络命名空间下进行观测,也会发现由于这个虚拟二层网络本身存在三层转发的网关,我们依然无法直接采集到两个容器之间的连接关系。

如果是 host-gw 模式,那么宿主机就会被纯粹配置一个三层转发的网关,虽然不走 Vxlan 这样的隧道了,但是由于三层转发的存在,我们依然无法直接观测到跨节点容器通信的网络关系。

综上所述,在观测基于 Flannel 的网络流量之时,一定要结合 Flannel 的架构本身,将三层网关的转发特性考虑到网络连接关系的梳理过程之中,才能真正地梳理出实际的连接关系。这样的网络架构,也是网络流量观察的一个难点

Calico 插件的原理

Calico 是一套开源的网络和网络安全方案,用于容器、虚拟机、宿主机之间的网络连接,可以用在kubernetes、OpenShift、DockerEE、OpenStrack 等 PaaS 或 IaaS 平台上

组件概述

云原生容器安全之安全狗对云原生集群网络流量可观测性的思考_第3张图片

图3

Felix —— Calico 的核心组件,运行在每个节点上。主要的功能有接口管理、路由规则、ACL 规则和状态报告

ETCD —— 保证数据一致性的数据库,存储集群中节点的所有路由信息。为保证数据的可靠和容错建议至少三个以上 ETCD 节点

Orchestrator Plugin —— 协调器插件负责允许 Kubernetes 或 OpenStack 等原生云平台方便管理 Calico,可以通过各自的 API 来配置 Calico 网络实现无缝集成。如 Kubernetes 的 CNI 网络插件

Bird —— BGP 客户端,Calico 在每个节点上的都会部署一个 BGP 客户端,它的作用是将 Felix 的路由信息读入内核,并通过 BGP 协议在集群中分发。当 Felix 将路由插入到 Linux 内核 FIB 中时,BGP 客户端将获取这些路由并将它们分发到部署中的其他节点。这可以确保在部署时有效地路由流量

BGP Router Reflector —— 大型网络仅仅使用 BGP Client 形成 Mesh 全网互联的方案就会导致规模限制,所有节点需要“N的平方”个连接,为了解决这个规模问题,可以采用 BGP 的 Router Reflector 的方法,使所有 BGP Client 仅与特定 RR 节点互联并做路由同步,从而大大减少连接数

Calicoctl —— Calico 命令行管理工具

网络模式概述

Calico 使用的是 BGP 协议来构建容器间网络,简单来理解就是将集群间的节点当作边界路由来实现整个网络的互通。BGP 协议之下,每一个节点上的所有容器被当作了一个 AS(自治系统),而不同的节点之间,则通过 BGP 服务来进行路由转发实现连通。实际上,Calico 项目提供的 BGP 网络解决方案,与 Flannel 的 Host-GW 模式几乎一样。不同之处在于 Calico 基于路由表实现容器数据包转发,而 Flannel 则使用 flanneld 进程维护路由信息。这里区别在于 flanneld 会介入到路由的转发过程中,导致实际的容器之间通信看起来如同中间会有一个 NAT 服务器的转换(这样的情况下,旁路模式无法直接获取两个容器之间的流量连接关系,需要做一定的推导)。Calico 则是基于内核自己的 BGP 路由转发,本质上还是容器之间原始数据包的投递,所以在宿主机节点上,可以旁路采集到整个容器之间的流量关系。

Calico 为了适配三层网络上运行容器间网络,还增加了对 IPIP 隧道模式的支持。这种模式下,BGP 协议转发的容器网络数据包,会通过内核的 IPIP 模块进行封装然后进行宿主机网络的传送。但是本质上,还是容器和容器 之间的原始通信数据包的传递,没有像 Flannel 那样在跨界点的时候会使用节点 IP 来替换数据包本身发送端的 IP 地址。

Calico 为了灵活适配不同的集群网络规模,提供了全互联模式(Node-to-Node Mesh)和路由反射模式(Router Reflection 简称 RR)。其中全互联模式适用于小规模集群节点,其本身结构简单,易于实现。但是缺点在于 BGP 的连接总数是 “N的平方” ,其中 N 是节点数,在节点增长的情况下,连接数增长会极快带来巨大的网络损耗。而路由反射模式则指定一个或多个 BGP Speaker 为 RouterReflection,这样就减少了连接总数。不过这种架构实现起来更加的复杂,同时对于作为 BGP Speaker 的节点也有更高的要求,适合大型集群的网络规划。

02

Openshift-SDN 插件的原理

Openshift-SDN 是红帽推出的一款容器集群网络方案,是部署 Openshift 时默认使用的网络方案。可以通过 oc get Network.config.openshift.io cluster -o yaml 命令来查看当前环境使用 CNI 插件。

组件概述

云原生容器安全之安全狗对云原生集群网络流量可观测性的思考_第4张图片

图4

一套 Openshift-SDN 包含了管控面和数据面:

CTRL —— 管控面,是一套 Deployment,用于自动化地给每个节点分配网段,并记录到 CRD 中

Node —— 数据面,是一套 DaemonSet,用于根据 CRD 变化,构建节点网络数据面。包括路由、网卡、流表、IPTABLES 规则

就具体组件来说,主要由 Controller、Agent 以及 CNI 的几个部分构成,它们各自负责的主要内容包括:

① Controller

负责配置集群级别的容器 CIDR,对应 Openshift-SDN 的 CRD :clusterNetwork

给新加入的 Node 分配子段,对应 Openshift-SDN 的 CRD :hostSubnet

观察k8s集群中 namespace、networkpolicy 等对象的变更,同步地更新 Openshift-SDN 的 CRD :netnamespaces、egressnetworkpolicies(专门针对出站的networkpolicy)

② Agent

每次启动时获取集群 clusterNetwork,与本地流表做对比,当发现有出入,就会重新配置本地的集群网络流表、节点上的路由、以及 IPTABLES 规则

观察集群中 Openshift-SDN 的 CRD :hostSubnet 的变化,配置到达其他 Node 的流表

观察集群中 Openshift-SDN 的 CRD :netnamespaces、egressnetworkpolicies 的变化,配置相应的租户隔离和出站限制的流表

生成节点上的 CNI 二进制文件,并提供 IP 分配功能

针对本节点的每个容器,配置对应的流表

③ CNI

负责被 kubelet 调用,以进行容器网络的配置和解除

会向 Agent 申请和释放 IP

会配置容器内部的IP和路由

网络模式概述

一言蔽之,Openshift-SDN 就是构建了一套容器间的大二层网络(虚拟二层中没有三层转发)。所有容器的 IP 都属于一个虚拟的 L2 中,他们彼此可以互相通过 ARP 请求确认对方物理地址,并进行正常的网络发包。不管是 ARP 包还是普通的 IP 包,都会被 ovs 流处理并进行必要的封装。

就实际的链路来看,在使用 Openshift-SDN 的时候,主要会有如下几种情况:

  • 同节点的容器与容器访问 :包从客户端容器的 Veth,到宿主机的 ovs 网桥,直接到达对端容器的 Veth

  • 跨节点的容器与容器访问 :包从客户端容器的 Veth,到宿主机的 ovs 网桥,走 vxlan0 端口封装后,经过宿主机的协议栈,从宿主机的物理网卡发出,到对端容器所在宿主机的物理网卡,被识别为 Vxlan,进入对端机器的 ovs 网桥,然后到对端容器的 Veth

  • 容器访问 Node :包从客户端容器的 Veth,到宿主机 ovs 网桥,因为 Node 的物理网卡 IP 与容器的网络不在一个平面,所以直接走内部流表 Table100,然后从 tun0 口发出,经过宿主机的协议栈,进行路由转发,最后走宿主机所在的网络到达某个 Node 的物理网卡

  • Node 访问本节点的容器 :根据宿主机的路由,包从 tun0 发出,进入宿主机的 ovs 网桥,送达对端容器的Veth

  • 容器访问 Service :包从客户端容器的 Veth,到宿主机 ovs 网桥,从 tun0 发出,经过宿主机协议栈,受 IPTABLES 规则做了 DNAT 和 MASQUERADE,至此变成了 Node 访问其他节点的容器

  • Service 的后端回包给容器:因为上一步,容器访问 Service 时,做了 MASQUERADE,所以 Service 后端会认为是某个 Node 访问了自己,回包给客户端容器所在的 Node,Node 上收到后对照 Conntrack 表,确认是之前连接的响应包,于是对包的源地址和目的地址做了修改(对应之前做的 DNAT 和 MASQUERADE),变成了 ServiceIP 访问客户端容器的包。根据 Node 上的路由,走 tun0,进入 ovs 网桥后,直接送到容器的 Veth

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号