容器安全拾遗 - Rootless Container初探

近期中发布了一个重要的特性“RootlessContainer支持”。趁着五一假期,快速验证一下。本文参考了ExperimentingwithRootlessDocker一文的内容,并且补充了更多的细节和上手内容。

Rootless容器背景与架构

DockerClient(TCP/UnixSocket)-DockerDaemon(Parent/ChildProcesses)-Container

由于Linux需要特权用户来创建namespace,挂载分层文件系统等,所以DockerDaemon一直以来是以root用户来运行的。这也导致了有Docker访问权限的用户可以通过连接DockerEngine获取root权限,而且可以绕开系统的审计能力对系统进行攻击。这阻碍了容器在某些场景的应用:比如在高性能计算领域,由于传统的资源管理和调度系统需要非特权用户来运行容器,社区实现了另外的容器运行时Singularity。

Moby社区的AkihiroSuda,为DockerEngine和Buildkit贡献了rootless容器支持,让DockerEngine以非特权用户方式运行,更好地复用Linux的安全体系。

注意:

目前rootless容器还在实验阶段,cgroups资源控制,apparmor安全配置,checkpoint/restore等能力还不支持。

目前只有Ubuntu提供了在rootless模式下对overlayfs的支持,由于安全顾虑,这个方案尚未得到upstream的支持。其他操作系统需要利用VFS存储驱动,有一定性能影响,并不适合I/O密集型应用。

Rootless容器有几个核心技术

首先是利用usernamespaces将容器中的root用户uid/gid映射到宿主机的非特权用户范围内。DockerEngine已经提供了--userns-remap标志支持了相关能力,提升了容器的安全隔离性。Rootless容器在此之上,让Dockerdaemon也运行在重映射的用户名空间中。

其次,虽然Linux中的非特权用户可以在用户名空间中创建网络名空间,并且执行iptables规则管理和tcpdump等操作,然而非特权用户无法在宿主机和容器之间创建vethpairs,这也意味着容器没有外网访问能力。为了解决这个问题,Akihiro利用用户态的网络“SLiRP”,通过一个TAP设备连接到非特权用户名空间,为容器提供外网连接能力。其架构如下

相关细节请参考,slirp4netns项目

环境准备

本文在一台的虚拟机上进行的验证

创建用户

$useraddmoby$passwdmoby

将新建用户添加到sudoers组

usermod-aGwheelmoby

切换到非特权用户

$su-moby$iduid=1000(moby)gid=1000(moby)groups=1000(moby),10(wheel)

进行uid/gid映射配置

echo"moby:100000:65536"|sudotee/etc/subuidecho"moby:100000:65536"|sudotee/etc/subgid

安装RootlessDocker

curl-sSL|sh

如果第一次安装,需要安装所需软件包

$curl-sSL|,dockerddaemonneedstobestartedmanually/home/moby/bin/entvariablesareset(oraddthemto~/.bashrc):\nexportXDG_RUNTIME_DIR=/tmp/docker-1000exportDOCKER_HOST=unix:///tmp/docker-1000/
验证Rootless容器

执行

$exportXDG_RUNTIME_DIR=/tmp/docker-1000$exportDOCKER_HOST=unix:///tmp/docker-1000/$/home/moby/bin/

然后在另外一个窗口执行

$exportXDG_RUNTIME_DIR=/tmp/docker-1000$exportDOCKER_HOST=unix:///tmp/docker-1000/$dockerversionClient:Version:master-dockerproject-2019-04-29APIversion:1.40Goversion::3273c2e2Built:MonApr2923:39:392019OS/Arch:linux/amd64Experimental:falseServer:Engine:Version:master-dockerproject-2019-04-29APIversion:1.40()Goversion::9a2c263Built:MonApr2923:46:232019OS/Arch:linux/amd64Experimental:truecontainerd:Version::894b81a4b802e4eb2a91d1ce216b8817763c29fbrunc:Version:1.0.0-rc7+devGitCommit:029124da7af7360afa781a0234d1b083550f797cdocker-init:Version:0.18.0GitCommit:fec3683$dockerrun-d-p8080:80nginx$curllocalhost:8080

利用iperf3进行网络性能测试,启动服务器端

$dockerrun-it--rm--name=iperf3-server-p5201:5201networkstatic/iperf3-s

测试容器之间的网络带宽

$SERVER_IP=$(dockerinspect--format"{{.}}"iperf3-server)$echo$SERVER_$dockerrun-it--rmnetworkstatic/iperf3-c$SERVER_IP-------------------------[ID]IntervalTransferBandwidthRetr[4]0.00-10.03/sec0ser[4]0.00-10.03/secreceiver

测试容器到宿主机之间的网络带宽(外网访问)

$HOST_IP=$(hostname--ip-address)$echo$HOST_$dockerrun-it--rmnetworkstatic/iperf3-c$HOST_IP-------------------------[ID]IntervalTransferBandwidthRetr[4]0.00-10.00sec1011MBytes848Mbits/sec0ser[4]0.00-10.00sec1008MBytes845Mbits/secreceiver

可以看到容器之间的通信带宽还比较不错,然而容器和宿主机不同网络名空间之间的通信性能有较大的损耗。

总结

Rootless容器在提升Docker/Runc容器的安全隔离性和可管理性方面前进了一大步,可以很好地复用Linux的安全体系,配合seccomp和SELinux等安全配置,可以减少攻击面。社区还提供了无需特权用户的Kubernetes实验版本,可以从如下项目获得

然而Rootless容器无法防范Linux内核的安全风险,目前其网络、存储的性能也有待优化,需要在特定场景中进行使用。也期待社区持续提升容器安全能力与效率,让容器有更加广阔的应用场景。

发布于 2025-05-17
32
目录

    推荐阅读