本篇内容介绍了“Rootless容器背景与架构介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
Rootless容器背景与架构
Docker和Kubernetes已经成为企业IT架构的基础设施,其自身安全越来越被关注。Docker基于Linux操作系统提供了应用虚拟化能力,通过namespace, cgroup实现了资源的隔离和配额约束。Docker Engine是一个典型的 Client-Server 结构:
Docker Client (TCP/Unix Socket) -> Docker Daemon (Parent/Child Processes) -> Container
由于Linux需要特权用户来创建namespace,挂载分层文件系统等,所以 Docker Daemon 一直以来是以root用户来运行的。这也导致了有Docker访问权限的用户可以通过连接Docker Engine获取root权限,而且可以绕开系统的审计能力对系统进行攻击。这阻碍了容器在某些场景的应用:比如在高性能计算领域,由于传统的资源管理和调度系统需要非特权用户来运行容器,社区实现了另外的容器运行时Singularity 。
Moby社区的 Akihiro Suda,为Docker Engine和Buildkit贡献了rootless容器支持,让Docker Engine以非特权用户方式运行,更好地复用Linux的安全体系。
注意:
目前rootless容器还在实验阶段,cgroups 资源控制, apparmor安全配置, checkpoint/restore等能力还不支持。
目前只有Ubuntu提供了在rootless模式下对overlay fs的支持,由于安全顾虑,这个方案尚未得到upstream的支持。其他操作系统需要利用VFS存储驱动,有一定性能影响,并不适合I/O密集型应用。
Rootless容器有几个核心技术
首先是利用 user namespaces 将容器中的root用户uid/gid映射到宿主机的非特权用户范围内。Docker Engine已经提供了 --userns-remap
标志支持了相关能力,提升了容器的安全隔离性。Rootless容器在此之上,让Docker daemon也运行在重映射的用户名空间中。
其次,虽然Linux中的非特权用户可以在用户名空间中创建网络名空间,并且执行iptables规则管理和tcpdump等操作,然而非特权用户无法在宿主机和容器之间创建veth pairs, 这也意味着容器没有外网访问能力。为了解决这个问题,Akihiro 利用用户态的网络“SLiRP”,通过一个TAP设备连接到非特权用户名空间,为容器提供外网连接能力。其架构如下
相关细节请参考,slirp4netns项目
环境准备
本文在一台 CentOS 7.6的虚拟机上进行的验证
创建用户
$ useradd moby$ passwd moby
将新建用户添加到 sudoers 组
usermod -aG wheel moby
切换到非特权用户
$ su - moby$ iduid=1000(moby) gid=1000(moby) groups=1000(moby),10(wheel)
进行uid/gid映射配置
echo "moby:100000:65536" | sudo tee /etc/subuidecho "moby:100000:65536" | sudo tee /etc/subgid
安装Rootless Docker
curl -sSL https://get.docker.com/rootless | sh
如果第一次安装,需要安装所需软件包
$ curl -sSL https://get.docker.com/rootless | sh# Missing system requirements. Please run following commands to# install the requirements and run this installer again.# Alternatively iptables checks can be disabled with SKIP_IPTABLES=1cat <<EOF | sudo sh -xcurl -o /etc/yum.repos.d/vbatts-shadow-utils-newxidmap-epel-7.repo https://copr.fedorainfracloud.org/coprs/vbatts/shadow-utils-newxidmap/repo/epel-7/vbatts-shadow-utils-newxidmap-epel-7.repoyum install -y shadow-utils46-newxidmapcat <<EOT > /etc/sysctl.d/51-rootless.confuser.max_user_namespaces = 28633EOTsysctl --systemEOF
(可选)安装用户态网络协议栈实现 slirp4netns :由于yum 安装的slirp4netns版本比较老无法执行,需要从源码构建。
$ sudo yum install glib2-devel$ sudo yum group install "Development Tools"$ git clone https://github.com/rootless-containers/slirp4netns$ cd slirp4netns$ ./autogen.sh$ ./configure --prefix=/usr$ make$ sudo make install
安装 Rootless Docker成功之后,会出现如下提示
$ curl -sSL https://get.docker.com/rootless | sh# systemd not detected, dockerd daemon needs to be started manually/home/moby/bin/dockerd-rootless.sh --experimental --storage-driver vfs# Docker binaries are installed in /home/moby/bin# Make sure the following environment variables are set (or add them to ~/.bashrc):\nexport XDG_RUNTIME_DIR=/tmp/docker-1000export DOCKER_HOST=unix:///tmp/docker-1000/docker.sock
验证Rootless容器
执行
$ export XDG_RUNTIME_DIR=/tmp/docker-1000$ export DOCKER_HOST=unix:///tmp/docker-1000/docker.sock$ /home/moby/bin/dockerd-rootless.sh --experimental --storage-driver vfs
然后在另外一个窗口执行
$ export XDG_RUNTIME_DIR=/tmp/docker-1000$ export DOCKER_HOST=unix:///tmp/docker-1000/docker.sock$ docker versionClient: Version: master-dockerproject-2019-04-29 API version: 1.40 Go version: go1.12.4 Git commit: 3273c2e2 Built: Mon Apr 29 23:39:39 2019 OS/Arch: linux/amd64 Experimental: falseServer: Engine: Version: master-dockerproject-2019-04-29 API version: 1.40 (minimum version 1.12) Go version: go1.12.4 Git commit: 9a2c263 Built: Mon Apr 29 23:46:23 2019 OS/Arch: linux/amd64 Experimental: true containerd: Version: v1.2.6 GitCommit: 894b81a4b802e4eb2a91d1ce216b8817763c29fb runc: Version: 1.0.0-rc7+dev GitCommit: 029124da7af7360afa781a0234d1b083550f797c docker-init: Version: 0.18.0 GitCommit: fec3683$ docker run -d -p 8080:80 nginx$ curl localhost:8080
利用 iperf3 进行网络性能测试,启动服务器端
$ docker run -it --rm --name=iperf3-server -p 5201:5201 networkstatic/iperf3 -s
测试容器之间的网络带宽
$ SERVER_IP=$(docker inspect --format "{{ .NetworkSettings.IPAddress }}" iperf3-server)$ echo $SERVER_IP172.17.0.2$ docker run -it --rm networkstatic/iperf3 -c $SERVER_IP... - - - - - - - - - - - - - - - - - - - - - - - - -[ ID] Interval Transfer Bandwidth Retr[ 4] 0.00-10.03 sec 29.8 GBytes 25.5 Gbits/sec 0 sender[ 4] 0.00-10.03 sec 29.8 GBytes 25.5 Gbits/sec receiver
测试容器到宿主机之间的网络带宽(外网访问)
$ HOST_IP=$(hostname --ip-address)$ echo $HOST_IP192.168.1.162$ docker run -it --rm networkstatic/iperf3 -c $HOST_IP...- - - - - - - - - - - - - - - - - - - - - - - - -[ ID] Interval Transfer Bandwidth Retr[ 4] 0.00-10.00 sec 1011 MBytes 848 Mbits/sec 0 sender[ 4] 0.00-10.00 sec 1008 MBytes 845 Mbits/sec receiver
可以看到容器之间的通信带宽还比较不错,然而容器和宿主机不同网络名空间之间的通信性能有较大的损耗。
总结
Rootless容器在提升Docker/Runc容器的安全隔离性和可管理性方面前进了一大步,可以很好地复用Linux的安全体系,配合seccomp和SELinux等安全配置,可以减少攻击面。社区还提供了无需特权用户的Kubernetes实验版本,可以从如下项目获得 https://github.com/rootless-containers/usernetes
然而Rootless容器无法防范Linux内核的安全风险,目前其网络、存储的性能也有待优化,需要在特定场景中进行使用。也期待社区持续提升容器安全能力与效率,让容器有更加广阔的应用场景。
“Rootless容器背景与架构介绍”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!