一、前言
工作中有聊到容器安全相关的话题,专门研究了一下容器安全见上一篇博文Docker容器安全性分析,文章详尽的分析了容器相关的安全如图所示:
针对其中的容器逃逸问题,之前关注过Openstack的一个项目Kata container项目:
Kata container是一个开源社区,致力于使用轻量级虚拟机构建安全的容器运行时,这些虚拟机感觉和执行起来都像容器,但是使用硬件虚拟化技术作为第二层防御,提供更强的工作负载隔离。
上一次部署k8s还是去年使用的是v1.17版本,这次打算重新安装一次k8s v1.18版本并搭配kata container试验一下。
二、Docker
这里简要介绍一下docker的组件,为后续的kata containe做一个铺垫。
docker在 1.11 之 后,被拆分成了多个组件以适应 OCI 标准。拆分之后,其包括 docker daemon, containerd,containerd-shim 以及 runC。组件 containerd 负责集群节点上容器 的生命周期管理,并向上为 docker daemon 提供 gRPC 接口。
- dockerd 是docker-containerd 的父进程, docker-containerd 是n个docker-containerd-shim 的父进程。
- Containerd 是一个 gRPC 的服务器。它会在接到 docker daemon 的远程请 求之后,新建一个线程去处理这次请求。依靠 runC 去创建容器进程。而在容器启动之后, runC 进程会退出。
- runC 命令,是 libcontainer 的一个简单的封装。这个工具可以 用来管理单个容器,比如容器创建,或者容器删除。
container-shim,shim的翻译是垫片,就是修自行车的时候,用来夹在螺丝和螺母之间的小铁片。关于shim本身,网上介绍的文章很少,但是作者在 Google Groups 里有解释到shim的作用[2]:
- 允许runc在创建&运行容器之后退出
- 用shim作为容器的父进程,而不是直接用containerd作为容器的父进程,是为了防止这种情况:当containerd挂掉的时候,shim还在,因此可以保证容器打开的文件描述符不会被关掉
- 依靠shim来收集&报告容器的退出状态,这样就不需要containerd来wait子进程
关于docker组件的详细介绍这里不再赘述了,想深入了解可以查看[2]。主要关注的是runc
,后续的kata containe也主要是替换runc
为Hyper的runv
。
三、kata container
Kata container使用的是Rust
语言编写,特性如下:
安全:运行在专用的内核中,提供网络、I/O和内存隔离,可以利用虚拟化VT扩展中的硬件强制隔离。
兼容:支持行业标准,包括OCI容器格式、Kubernetes CRI接口以及传统虚拟化技术。
性能:提供与标准Linux容器一致的性能;增强了隔离性,没有标准虚拟机的性能负担。
简单:消除了在完整的虚拟机内部嵌套容器的要求; 标准接口使插入和入门变得容易。
kata container基于轻量级虚拟机的容器,不同容器跑在一个个不同的虚拟机(kernel)上,比起传统容器提供了更好的隔离性和安全性,同时继承了容器快速启动和快速部署等优点。这也是本次想使用kata 的原因,能够提供比原生docker更好的隔离性,Kata 容器每个容器都使用专有内核,避免容器逃逸后影响宿主机的内核。
废话不多说,开始体验一下kata
3.1 安装kata
这里使用ubuntu 18.04安装kata最新的版本Kata Container 1.12.0
参考kata官方安装手册[3]
1 | admin@k8smaster:~$ ARCH=$(arch) |
安装完成后测试一下
1 | admin@k8smaster:~$ kata-runtime --version |
3.2 配置docker的runtime
为了保持安装的docker能够使用,这里默认仍然使用runc
,但是增加kata-runtime
做为可选的docker runtime。相关配置可参考官网kata install。
1 | admin@k8smaster:~$ sudo mkdir -p /etc/systemd/system/docker.service.d/ |
下面创建两个容器,一个使用 kata runtime,另一个使用默认的 runc。创建完容器之后,查看一下 kata container 的一些信息。
1 | admin@k8smaster:~$ docker run -d --name kata-test -p 8080:80 --runtime kata-runtime httpd:alpine |
查看一下两个容器的内核版本
1 | admin@k8smaster:~$ docker exec -it kata-test sh |
查看一下kata容器的虚拟机,可以看到kata专门给容器启动了一个虚拟机沙盒
1 | admin@k8smaster:~$ ps -ef | grep qemu | grep -v 'grep' |
3.3 配置k8s与kata的集成
kata 不直接与 k8s 通信,因为对于 k8s 来说,它只跟实现了 CRI 接口的容器管理进程打交道,比如 docker-engine,rkt, containerd(使用 cri plugin) 或 CRI-O,而 kata 跟 runc 是同一个级别的进程。所以如果要与 k8s 集成,则需要安装 CRI-O 或 CRI-containerd 来支持 CRI 接口,本文使用 CRI-O。CRI-O 的 O 的意思是 OCI-compatible,即 CRI-O 是实现 CRI 接口来跑 OCI 容器的[4]。
由于ubuntu 18.04 的suse源中含有相关软件的包,这里展示如何直接用apt快速安装体验,相关软件的版本如表3.1所示
表3.1 相关软件版本
软件名 | 版本 |
---|---|
kubelet、kubeadm、kubectl | 1.18.6 |
cri-o | 1.17 |
kata | 1.12-alpha0 |
3.3.1 安装 cri-o
CRI-O - OCI-based implementation of Kubernetes Container Runtime Interface
这是cri-o的github标题,符合OCI基准实现的Kubernetes容器运行时接口,从这个标题很容易看出,这是一个专门服务k8s的容器实现。
- CRI Container Runtime Interface这是k8s提出的一个概念,在容器界除了最出名的docker和本文介绍的cri-o,还有很多不同的容器实现,例如contrainerd, frakti,rkt等,这些容器实现各有特色,只要支持CRI就可以被k8s支持
- OCI Open Container Initiative这是开放容器标准,也叫容器runtime,简单来说只要符合这个标准运行的就是容器,例如runC,Kata,gVisor这些runtime创建出来的都是OCI标准容器容器标准,也叫容器runtime,简单来说只要符合这个标准运行的,就是容器,例如runC,Kata,gVisor这些runtime创建出来的都是OCI标准容器
1)安装cri-o
1 | admin@k8smaster:~$ CRIO_VERSION=1.17 # 当前ubuntu只有1.17 |
2)修改crio配置文件/etc/crio/crio.conf
,增加kata runtime
1 | admin@k8smaster:~$ sudo vim /etc/crio/crio.conf |
3)修改crio systemd配置,设置自启动
1 | admin@k8smaster:~$ sudo vim /usr/lib/systemd/system/crio.service |
4)测试crio
1 | admin@k8smaster:~$ cat /etc/crictl.yaml |
3.3.2 安装k8s
1)添加k8s源并安装k8s
1 | # 使得 apt 支持 ssl 传输 |
2)配置k8s使用crio
根据官方手册,通过cri-o运行k8s需要修改相应文件
1 | admin@k8smaster:~$ sudo cat /etc/default/kubelet |
修改kubeadm配置,修改cgroup驱动为systemd
1 | admin@k8smaster:~$ sudo vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf |
重启kubelet
1 | admin@k8smaster:~$ sudo systemctl daemon-reload; systemctl restart kubelet |
3)初始化k8s集群
这里通过制定k8s容器仓库为阿里云镜像仓库避免被墙而无法下载镜像,由于阿里云的镜像与官方不同步,因此这里镜像的版本指定为v1.18.0
,稍微与kubeadm的镜像版本v1.18.6
不一致。
1 | admin@k8smaster:~$ sudo kubeadm init --image-repository=registry.aliyuncs.com/google_containers --pod-network-cidr=192.168.0.0/16 --kubernetes-version=v1.18.0 --apiserver-advertise-address=10.37.129.3 --cri-socket=/var/run/crio/crio.sock --v=5 |
--pod-network-cidr=192.168.0.0/16
,若于主机所在网络冲突可修改--apiserver-advertise-address=10.37.129.3
,可不指定API地址,本次安装为了避免网络影响,给机器新增了一块网口
【NOTE】
1)想查看k8s对应镜像版本可以通过如下命令查看
1 | admin@k8smaster:~$ kubeadm config images list |
对于离线情况下,亦可手动下载镜像再导入,这里推荐一个博客Docker/Kubernetes镜像,详细介绍了如何获取gcr.io
镜像
2)手动安装相关软件最新版本可参考Katacontainers 与 Docker 和 Kubernetes 的集成,文中相关软件版本较老,这里列出相关软件的最新realease版本
3.3.3 配置k8s集群网络
这里选用calico网络,参考calico官网
1)安装Tigera Calico操作符和自定义资源定义
1 | admin@k8smaster:~$ kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml |
2)通过创建必要的自定义资源来安装Calico
1 | admin@k8smaster:~$ kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml |
这里需要注意,若k8s集群初始化时CIDR
指定的不是192.168.0.0/16
需要修改custom-resources.yaml
文件中的CIDR
地址
3)确认所有pod都在运行
1 | admin@k8smaster:~$ watch kubectl get pods -n calico-system |
等待所有pod的状态为running
4)本次安装只使用了一个节点,需要配置master节点“无污点”
1 | admin@k8smaster:~$ kubectl taint nodes --all node-role.kubernetes.io/master- |
5)查看所有master节点状态
1 | admin@k8smaster:~$ kubectl get nodes -o wide |
6)查看所有pod状态
1 | admin@k8smaster:~$ kubectl get pods -A -o wide |
3.3.4 测试k8s使用kata runtime
k8s从1.14版本开始设置了runt imeclass ,同时crio从1.14版本开始增加RuntimeHandler以取代trusted/untrusted runtime,因此k8s使用kata不能再使用如下配置了。
1 | annotations: |
1)首先创建kata runtime class
编写kata runtime class配置文件
1 | # k8s-runtimecleass.yaml |
创建kata runtime class
1 | admin@k8smaster:~$ kubectl apply -f k8s-runtimecleass.yaml |
2)创建kata runtime pod
1 | admin@k8smaster:~$ cat kata-test.yaml |
等待kata pod 启动,查看pod状态
1 | admin@k8smaster:~$ kubectl get pod -o wide |
【参考链接】
1)Docker组件介绍(二):shim, docker-init和docker-proxy
2)Docker组件介绍(一):runc和containerd