前言
在之前的文章中,我们从 SPEC.md 中了解了 Namespace(视图隔离)、Filesystem(文件系统)以及 Cgroups / Intel RDT(资源限制)。经过这三层包装,我们似乎已经得到了一个完整的容器。
但是,还面临一个致命的问题:如果容器内的应用是用 root 用户运行的,当应用被攻破时,黑客能直接控制宿主机吗?
答案是:通常不能。这就是容器安全机制的作用。在 runc 的 SPEC.md 中,专门有一个 ### Security 章节,定义了容器的**纵深防御(Defense in Depth)**体系。本文将带你逐一拆解这些安全防线。
防线一:Linux Capabilities(能力细分)
在传统的 Linux 系统中,权限是二元的:要么是普通用户(权限受限),要么是 root(UID=0,拥有所有权限)。为了实现更细粒度的控制,Linux 引入了 Capabilities(能力) 机制,把 root 的权限“切碎”成了几十个独立的特权操作。
SPEC.md 中给出了一个容器默认 Capabilities 的清单:
The standard set of Linux capabilities that are set in a container provide a good default for security and flexibility for the applications.
规范中明确规定了哪些能力默认开启(1),哪些默认关闭(0):
| Capability | Enabled | 作用简述 |
|---|---|---|
CAP_CHOWN | 1 | 允许修改文件所有者 |
CAP_NET_BIND_SERVICE | 1 | 允许绑定 1024 以下的特权端口 |
CAP_SETUID / CAP_SETGID | 1 | 允许切换用户身份(如 su/sudo) |
CAP_KILL | 1 | 允许发送信号给进程 |
CAP_SYS_ADMIN | 0 | Linux 的终极权限,允许执行大量高危系统管理操作 |
CAP_NET_ADMIN | 0 | 允许配置网络接口、路由表等 |
CAP_SYS_MODULE | 0 | 允许加载/卸载内核模块 |
CAP_SYS_TIME | 0 | 允许修改系统时间 |
解读: 你会发现,runc 默认开启的,都是维持一个普通应用(如 Nginx、数据库)正常运行所需的最小权限。 而那些被禁用的,往往是可以直接影响宿主机的操作。比如没有 CAP_SYS_ADMIN,你就无法在容器内挂载文件系统;没有 CAP_SYS_TIME,你就无法修改宿主机的时间(因为时间是全局的,直到较新的内核才支持 Time Namespace);没有 CAP_SYS_MODULE,黑客就无法植入恶意的内核 Rootkit。
这就是为什么即便你在容器里是 root,你依然会觉得这个 root 是个“阉割版”。
防线二:AppArmor 与 SELinux(强制访问控制)
即便是去掉了危险的 Capabilities,容器内的 root 依然有能力去读取或修改系统内的大量文件。为了进一步收缩攻击面,SPEC.md 提到了 MAC(Mandatory Access Control,强制访问控制):
Additional security layers like apparmor and selinux can be used with the containers. A container should support setting an apparmor profile or selinux process and mount labels if provided in the configuration.
文档中甚至直接给出了一个标准的 AppArmor Profile 配置示例。这段配置虽然短,但字字诛心:
profile <profile_name> flags=(attach_disconnected,mediate_deleted) {
...
deny @{PROC}/sys/fs/** wklx,
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/mem rwklx,
...
deny mount,
...
deny /sys/kernel/security/** rwklx,
}这段规则的核心目的只有一个:保护宿主机的内核状态不被篡改。
deny mount:从 MAC 层面再次死死封住挂载文件系统的口子。deny @{PROC}/sys/fs/** wklx和deny /sys/...:禁止对/proc和/sys下敏感内核参数的写入操作。即使由于某种原因容器内的进程获得了较高的 Capabilities,AppArmor 依然会在最后关头将恶意写操作拦截(Permission denied)。
防线三:Seccomp(系统调用过滤)
如果说 Capabilities 是对“业务动作”的过滤,AppArmor 是对“文件路径”的过滤,那么 Seccomp (Secure Computing Mode) 就是对 Linux 内核**“系统调用 (Syscall)”**的硬核过滤。
seccomp can be used to apply filters to the system calls used in a container… The primary use-case is to provide an explicit allow-list of syscalls for a container, to reduce the kernel API attack surface exposed to the container.
容器本质上与宿主机共享同一个内核。如果内核某个系统调用存在 0-day 漏洞,容器内的程序就可以通过触发这个系统调用来提权逃逸。
Seccomp 通过 BPF (Berkeley Packet Filter) 技术拦截系统调用。runc 在这里做了一个很聪明的决定:
libcontainer does not provide a default filter, but higher-level runtimes tend to define their own filters for use with runc…
runc/libcontainer 本身不提供默认的 Seccomp 过滤规则,而是将定义权交给了上层运行时(如 Docker、containerd)。 实际上,Docker 默认配置的 Seccomp profile 禁用了 Linux 300 多个系统调用中的 40 多个不安全调用(如 kexec_load、open_by_handle_at 等)。历史上,Seccomp 已经无数次成功防御了内核提权 0-day 漏洞。
实验与动手 Demo:探测容器的“阉割版” root
我们可以通过一个简单的实验,直观地感受这层安全防御体系。我们启动一个普通的容器,并尝试在里面做一些“越权”的事情。
1. 尝试修改系统时间
$ docker run --rm -it ubuntu bash
root@container_id:/# id
uid=0(root) gid=0(root) groups=0(root)
root@container_id:/# date -s "2030-01-01"
date: 2026-03-19T10:00:00+08:00失败原因:我们虽然是 root,但是缺少 CAP_SYS_TIME 能力。
2. 尝试挂载文件系统
root@container_id:/# mount -t tmpfs tmpfs /mnt
mount: /mnt: permission denied.失败原因:缺少 CAP_SYS_ADMIN 能力,并且同时被 AppArmor 的 deny mount 规则拦截。
3. 查看容器当前拥有的 Capabilities
我们可以使用 capsh 工具来清晰地看到我们到底被赋予了哪些能力。
root@container_id:/# apt-get update && apt-get install -y libcap2-bin
root@container_id:/# capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+eip对照我们打印出的 Current 列表和前面 SPEC.md 中的表格,你会发现它们惊人地一致!高危的特权能力已经被彻底剥离。
结语
从 Namespaces 的“骗”(让你以为你有一台完整的机器),到 Cgroups 的“抠”(限制你能用的资源),再到 Security(Capabilities + AppArmor + Seccomp)的“防”(没收所有危险的武器),runc 通过对 Linux 内核特性的极致压榨,组合成了一套轻量却强大的沙箱体系。
至此,我们对 runc SPEC.md 的源码与规范解读也告一段落。深入理解这些底层规范,对于我们在日常工作中排查容器权限报错(Permission denied)、编写高安全的 Dockerfile,甚至是进行云原生安全防御,都有着极其重要的指导意义。
| Capability | Enabled | 作用简述 |
|---|---|---|
CAP_NET_RAW | 1 | 允许使用原始套接字(如 ping 命令) |
CAP_AUDIT_READ | 1 | 允许读取审计日志 |
CAP_AUDIT_WRITE | 1 | 允许写入审计日志 |
CAP_DAC_OVERRIDE | 1 | 忽略文件的 DAC 访问权限(读、写、执行) |
CAP_SETFCAP | 1 | 允许设置文件上的 Capabilities |
CAP_SETPCAP | 1 | 允许修改进程的 Capabilities |
CAP_MKNOD | 1 | 允许创建特殊文件(使用 mknod) |
CAP_FOWNER | 1 | 忽略通常要求进程 UID 与文件 UID 匹配的限制 |
CAP_FSETID | 1 | 当文件被修改时,不清除 setuid 和 setgid 位 |
CAP_SYS_CHROOT | 1 | 允许调用 chroot() |
CAP_NET_BROADCAST | 0 | 允许网络广播和多播 |
CAP_SYS_RAWIO | 0 | 允许原始 I/O 端口操作 |
CAP_SYS_PACCT | 0 | 允许配置进程记账 |
CAP_SYS_NICE | 0 | 允许提高进程优先级和设置特定的调度策略 |
CAP_SYS_RESOURCE | 0 | 允许重写资源限制并配置 ext2 预留空间 |
CAP_SYS_TTY_CONFIG | 0 | 允许配置 tty 设备 |
CAP_AUDIT_CONTROL | 0 | 允许配置内核审计系统 |
CAP_MAC_OVERRIDE | 0 | 允许忽略 MAC 策略 |
CAP_MAC_ADMIN | 0 | 允许配置 MAC 策略 |
CAP_SYSLOG | 0 | 允许执行特权 syslog 操作 |
CAP_DAC_READ_SEARCH | 0 | 绕过文件读取和目录搜索的权限检查 |
CAP_LINUX_IMMUTABLE | 0 | 允许修改文件的不可变和仅追加属性 |
CAP_IPC_LOCK | 0 | 允许锁定内存(mlock等) |
CAP_IPC_OWNER | 0 | 绕过 IPC 对象的权限检查 |
CAP_SYS_PTRACE | 0 | 允许跟踪任何进程(ptrace) |
CAP_SYS_BOOT | 0 | 允许重启系统或加载新内核 |
CAP_LEASE | 0 | 允许在任意文件上建立租约 |
CAP_WAKE_ALARM | 0 | 允许触发能唤醒系统的定时器 |
CAP_BLOCK_SUSPEND | 0 | 允许阻止系统挂起 |