张入文,罗俊,胡晓勤,龚勋
(四川大学网络空间安全学院,成都 610065)
云计算是一种将云端物理资源以虚拟资源池的方式供租户使用的模式,在云计算模式下可以通过网络实现租户的资源按需分配与获取[1]。其具有的高可用性、弹性伸缩、便宜性和部署灵活等特点,能在提高人们工作效率的同时,在各个行业带创新[2]。云计算模式下,用户只需要通过网络向云服务提供商申请并支付相应的费用,即可方便快捷地获取所需要的计算、存储等资源,相比于购买并部署昂贵的硬件设备,资源的按需购买更为经济便捷。经过多年的发展,云计算技术已经越来越被人们广泛使用。
尽管有云计算有众多优势,但仍存在数据丢失、数据破坏和流量劫持等方面的安全问题[3]。因此,需要在云环境下有效的VPN解决方案。可以通过虚拟专用网络即服务(VPNaaS)提供与租户网络的连接。通过加密可以确保云环境中敏感信息的数据保护和机密性,这可以通过在云中部署VPN服务来实现。传统意义的VPN主要以硬件为主,将VPN功能集成到特定的专用硬件中,实现软硬一体的VPN系统[4],但这样也存在许多问题,例如:可用性差、不易于扩展、部署困难等。将VPN服务迁移到云环境中,可以有效解决这些问题,在云环境下,VPN系统应满足:组件快速部署;VPN资源的动态分配与快速扩展,支持多租户、多VPN实例;租户VPN系统的相互隔离等诸多要求。
目前国内外的研究主要集中在传统VPN,对云环境下VPN服务的研究仍然较少,Goethals等人对云上VPN软件的可伸缩性进行了评估,对VPN软件是否以及如何应用于包含边缘节点的大规模集群中进行了研究[5],本文主要研究Kubernetes环境下的VPN服务的应用;选择合适的开源VPN,制作VPN镜像;利用Multus-CNI插件实现Kubernetes的多网络平面。
传统VPN通常以设备的形式部署在网络边界处。VPN的关键技术包括隧道技术、密钥管理技术、加密技术和身份认证技术[6]。其中,隧道技术是VPN最为关键的一项技术,其实质是在传输两点之间通过相同的隧道协议建立安全的传输信道;加密技术的主要用于传输过程中的信息保护,以防止非授权用户获取真实数据;密钥管理技术主要用于实现在公用数据网上安全地传递密钥而不被窃取;通常在隧道连接开始前还要进行用户身份认证,保证只有拥有合法身份的用户能够通过VPN访问网络内部资源。
容器是一个轻量级的操作系统级别的虚拟化技术,它允许应用程序在资源隔离的环境下,运行应用程序及其各种依赖项,通过将应用程序所需的所有必要组件都打包为镜像,运行在独立环境中,相比于虚拟机而言,容器不需要整个操作系统,从而节省出大量资源,同时容器运行也更加快速,性能更好。此外不需要在虚拟机上安装配套的软件环境,更便于业务的迁移。
Kubernetes是Google开源的容器集群管理系统,它基于docker技术,提供资源调度、部署运行、服务发现、缩扩容等一整套功能,本质上可看做是基于容器技术的Micro-PaaS平台,即第三代PaaS的代表性项目[8]。其整体架构如图1所示。
图1 Kubernetes总体架构图
如图1所示,Kubernetes的核心组件,主要有以下几个:
etcd:保存整个集群的各种状态信息;
apiserver:提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
controller manager:负责维护集群的状态,例如故障检测、自动扩展、滚动更新等;
scheduler:负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;
kubelet:负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理;
kube-proxy:负责为Service提供cluster内部的服务发现和负载均衡。
为便于理解,下面将对Kubernetes的重要概念进行简单介绍。
Pod:Pod是Kubernetes中一个抽象化概念,是一组共享资源(包括存储、网络等)的容器,Pod内的容器在同一个namespace命名空间。master节点通过controler组件把Pod调度到合适的node工作节点上,并与容器运行时协调以启动容器在Kubernetes中,Pod是调度的最小元素。
Master:集群控制节点,负责整个集群的管理控制,在master节点上运行这集群的关键组件,如apiserver、controller manager、scheduler等。Master节点通常占据整个服务器,由于master的重要性,高可用方案建议部署3台服务器以保证整个集群的安全稳定。
Node:除了master,集群其他的机器均为node节点,node是集群的工作负载节点,由master分配工作负载,其上运行着kubelet、kube-proxy、Docker Engine等关键进程。当某个node宕机时,其上的工作负载会自动转移到其他节点。
Kubernetes网络被设计为一个扁平化的网络中,集群外部访问只能通过以下几种方式:
(1)通过port-forward转发,这种方式最为方便快捷,但只适合调试时使用,不适用于生产环境。
(2)通过NodePort,NodePort方式为集群中每一个节点(Node)指定对应的监听端口,通过任意节点的端口即可访问到指定服务。
(3)通过LoadBalance来暴露服务,LoadBalance(负载均衡LB)通常由云服务商提供,如果云环境中不提供LB服务,通常直接使用Ingress,或使用MetalLB来自行配置LB。
(4)通过Ingress公开多个服务,Ingress Controller基于Ingress规则将客户端请求直接转发到Service对应的后端pod上。
这些服务暴露方式使集群内部服务可以对外访问,但这些方式没有任何安全验证机制,且外部网络中的各类客户端皆可访问,对于需要建立可信的安全连接,而又不希望外部网络随意访问的服务,使用VPN服务是非常必要的。
以传统VPN与常见的开源VPN Strongswan作为参考,将传统VPN与K8S环境相结合,将Strongswan制作为容器镜像,部署到Kubernetes容器集群,其整体架构如图2所示。
图2 容器化VPN总体架构图
以三个节点搭建的Kubernetes集群为例,其中节点1为master节点,为集群控制节点,负责整个集群的管理与控制,是整个集群的核心,在其上通常不运行工作负载,也可以通过特殊命令使其作为工作节点使用,master节点上运行着API server、Controller Manager、Scheduler等重要进程。节点2与节点3为Node工作节点,每个Node节点可运行多个pod,pod是Kubernetes中最基本的单元,其中包含多个与业务相关的容器,为Kubernetes调度的最小单元,VPN服务运行在node节点,负责容器内服务与外部通信。
其中,VPN pod有两个网络接口,外部网络访问的数据由flannel网络负责转发,内部访问的数据由calico网络转发,Kubernetes可以是公有云上由租户搭建的集群或是异地的数据中心,本地数据中心采用传统的方式,在网络边界处部署VPN设备,集群中的VPN封装在容器中,通过kubelet作为pod启动,IPSec隧道建立在本地网关与Kubernetes中VPN pod之间,本地中心与Kubernetes集群建立连接时首先通过互联网将数据发送到云上弹性公网ip或真实物理ip,master节点上运行的ingress controller组件负责将数据转发到Node工作节点上的VPN服务pod上,再通过VPN pod将数据转发到相应的pod,通过在本地数据中心的VPN网关与VPN pod间建立安全隧道,实现对集群内部的安全访问。
由于VPN服务同时需要内网与外网网卡,而Kubernetes设计之初,一直遵循One Pod One IP的策略,即一个Pod分配一个网卡,一个IP地址,在这种情况下显然不满足要求,为了支持多网络平面,本文以Multus-CNI为例进行了多网络平面实践。目前Kubernetes中最常用的跨主机容器之间的通信组件为flannel与calico,flannel在主机网络之上创建了覆盖网络(overlay网络),通过这个覆盖网络,将数据包原封不动地传递到目标容器内,calico使用BGP路由协议配置一个3层网络在主机之间路由数据包。为实现多网络平面,首先需要在Kubernetes集群中安装flannel与calico网络插件,并安装Multus-CNI插件,通过创建与calico和flannel组件同名的NetworkAttachmentDefinition类型的CRD自定义资源,并注册到kubernetes的apiserver,创建方式如下:
可以通过kubectl get命令查看自定义资源是否创建成功,在multus的cni读取目录下,新建并编写calico和flannel的cni配置文件,以便multus能获取到calico和flannel的组件信息,动态进行网络的配置。创建pod时可以通过添加annotations参数选择默认网络或创建双网卡pod。
本文中,以calico网络作为内网网络,负责Kubernetes内部pod与pod,service与pod或各节点之间的通信,flannel作为外网网络,负责集群外部数据接收后通过VPN Service内网网口转发到集群内部通信。多网络平面下的数据流图如图3所示。
图3 集群内pod与本地数据中心通信的数据流图
通过本地数据中心VPN网关和VPN pod之间建立安全隧道,本地数据中心内网的服务器与集群内pod的通信过程大致如下:
(1)本地数据中心内网的服务器发出源IP为本机(内网网段),目的IP为集群内pod IP的数据包,根据本机路由策略发送到本地数据中心的VPN网关。
(2)VPN网关根据其目的地址查找相应的VPN安全策略,将数据包重新封装后,发送到互联网。
(3)数据包通过公共网络传输到集群的公网ip后,由master节点的ingress组件将数据转发到flannel0设备。
(4)flannel网络的flannel0根据存储在etcd上的网络配置信息,将数据包转发给VPN pod。
(5)数据包到达VPN pod,根据数据包源地址,查找安全策略,对其进行相应的解包工作,并根据安全策略,由内网接接口发送到calico网络的canal0设备。
(6)calico网络根据其路由规则,将数据包转发到对应的node节点的canal0设备。
(7)数据包到达pod所在的node节点,canal0根据路由规则将数据包发送到对应的pod,至此,整个通信流程结束。
创建两个网络接口的pod的大致流程如下:
(1)当用户在Kubernetes的master节点执行pod创建命令后,kublet观察到新pod的创建,于是首先调用CRI(容器运行时接口),创建pod内的若干容器,包括pause系统容器和其他用户容器
(2)首先会创建pause系统容器,它是由Kubernetes系统默认创建的第一个容器,里面运行一个功能简单的C程序,pause容器一经创建即把自己永远阻塞。这个逻辑简单的容器主要用于在pod创建过程中完成一系列容器初始化过程,并占用一个Linux的network namespace,不同的pod通过Linux的namespace机制实现隔离功能。
(3)pod内的其他容器与系统pause容器共享network namespace,在创建阶段加入这个namespace,因此,在CRI调用容器运行时时,其默认网络模式都为none模式,不会初始化网络协议栈。
(4)在pause创建阶段,会通过CNI插件完成容器网络初始化的过程,包括容器网络接口创建,IP分配等,CNI有多种实现,官方自带的插件有p2p、bridge等,一般常用的CNI网络插件有flannel、calico等,用以解决容器之间跨机通信问题。
(5)Kubelet调用Multus-CNI插件通过cmdAdd创建容器网络并根据pod创建时的annotations参数,再调用相关的第三方网络插件如calico、weave等完成网络初始化,从而完成双网络接口pod的创建。
本实验通过一台本地物理机与两台公有云租用的ESC搭建测试如图4所示的测试环境,物理机1通过路由器连接至互联网,在其上运行三个虚拟机VM1、VM2和VM3,将VM1作为集群的master节点,VM2和VM3作为工作节点,搭建了Kubernetes集群,VPN pod通过annotation参数为flannel和calico的yaml文件,以命令方式创建,具有两个网络接口,分别连接至flannel和calico网络,公有云上租用的ECS1上运行strongswan,创建了两个网络接口,ESC2的默认网关为ECS1,通过云管理平台对VPC网络进行配置,物理机1可以通过EIP(弹性公网ip)访问到ECS1,ECS1的eth1 ip与ECS2 ip处于同一子网,可以直接访问,容器镜像选择strongswan-5.9.0。
图4 测试环境网络拓扑图
使用ping命令对IPSec VPN功能有效性进行测试,测试结果为:在创建IPSec隧道前,ECS2与pod1无法通信,创建IPSec隧道后,ECS2与pod1能进行正常通信,能实现VPN功能。
通过ping命令测试VPN的通信延迟,作为对比,分别在IPSec VPN连接建立前,测试物理机1与ECS1的通信延迟与IPSec VPN连接建立成功后,测试pod1与ECS2通信时延,测试结果如图5和图6所示,pod1与ECS2通信时延和物理机1与ECS1通信时延均在40ms左右波动,其中pod1与ECS2的平均时延为:42.3ms,物理机1与ECS1的平均时延为40.1ms,结果表明,VPN隧道建立后的通信时延与物理机到云端经过互联网访问的通信时延相差不大,在可接受的范围内。
图5 VPN时延测试
图6 互联网连接时延测试
本文针对容器VPN在Kubernetes环境下的应用进行研究,下一步的研究可以扩展到VPN服务的网络性能与提升上。