Capabilities
目录
- 概述:从 root 全能到精细权限
- 历史背景:为什么需要 Capabilities
- 完整能力列表与分类
- 五大能力集详解
- 文件能力(File Capabilities)
- 能力继承规则:execve() 时的完整变换
- Securebits:纯能力环境
- 用户命名空间与 Capabilities
- 实战命令大全
- Docker 容器与 Capabilities
- Capabilities 与 SUID 的对比
- 安全加固最佳实践
- Capabilities 提权攻击与防范
- 总结
1. 概述:从 root 全能到精细权限
在传统的 Unix/Linux 系统中,权限模型是二元的:要么你是 root(UID 0),拥有所有权限;要么你是普通用户,几乎没有特权。这种"全有或全无"的模式存在严重的安全隐患——一个只需要绑定 80 端口的 Web 服务器,却不得不以 root 身份运行,获得了修改系统时间、加载内核模块等它根本不需要的权限。
Linux Capabilities 解决了这个问题。它将 root 的全部特权拆分为 40+ 个独立的能力单元,每个能力代表一组相关的特权操作。进程可以被授予恰好完成工作所需的能力,而不需要完整的 root 权限。
核心概念
传统模型:
root (UID=0) ─── 拥有所有特权 ─── 可执行任何操作
普通用户 ─── 无特权 ─── 只能操作自己的资源
Capabilities 模型:
进程 A ─── CAP_NET_BIND_SERVICE ─── 可绑定 80 端口
进程 B ─── CAP_SYS_TIME ─── 可修改系统时钟
进程 C ─── CAP_DAC_OVERRIDE ─── 可绕过文件权限
进程 D ─── (无能力) ─── 普通进程
Capabilities 在整个安全体系中的位置
应用请求操作
│
▼
┌─────────────────┐
│ 1. DAC 检查 │ ← 传统文件权限 (rwx)
│ (UID/GID) │
└────────┬────────┘
│ 通过
▼
┌─────────────────┐
│ 2. SELinux/ │ ← MAC 强制访问控制
│ AppArmor │
└────────┬────────┘
│ 通过
▼
┌─────────────────┐
│ 3. Capabilities │ ← 能力检查
│ (特权操作) │ 这是本文的主题
└────────┬────────┘
│ 通过
▼
执行操作
2. 历史背景:为什么需要 Capabilities
| 时间 | 里程碑 |
|---|---|
| 1997 | POSIX 1003.1e 草案定义 Capabilities 标准 |
| 1999 | Linux 2.2:Capabilities 首次引入内核 |
| 2003 | Linux 2.6.0:Capabilities 机制成熟 |
| 2008 | Linux 2.6.24:文件能力(File Capabilities)支持,替代 SUID |
| 2008 | Linux 2.6.25:Bounding Set 从系统级变为每线程属性 |
| 2015 | Linux 4.3:Ambient Set 引入,解决非 root 继承问题 |
| 2019 | Linux 5.0+:用户命名空间 + Capabilities 深度整合 |
| 2020 | Linux 5.8:CAP_BPF、CAP_PERFMON 从 CAP_SYS_ADMIN 拆分 |
| 2021 | Linux 5.12:CAP_CHECKPOINT_RESTORE 独立 |
| 2024+ | 容器生态全面依赖 Capabilities 实现无 root 运行 |
SUID 的缺陷 → Capabilities 的优势
SUID 问题示例:
$ ls -l /usr/bin/ping
-rwsr-xr-x 1 root root /usr/bin/ping
↑
SUID 位:ping 以 root 身份运行
ping 只需要 CAP_NET_RAW(创建原始套接字),
但 SUID 却赋予了完整的 root 权限!
如果 ping 存在缓冲区溢出漏洞,攻击者获得 root shell。
Capabilities 方案:
$ sudo setcap cap_net_raw=ep /usr/bin/ping
$ ls -l /usr/bin/ping
-rwxr-xr-x 1 root root /usr/bin/ping ← 不需要 SUID 位!
ping 现在只拥有 CAP_NET_RAW,无法执行其他特权操作。
即使被攻破,攻击者也拿不到完整 root 权限。
3. 完整能力列表与分类
截至 Linux 6.x,系统支持 40+ 个能力。以下是完整分类列表。
3.1 POSIX 基础能力
这些是最基础的权限控制能力,遵循 POSIX 1003.1e 草案:
| 能力 | 引入版本 | 描述 |
|---|---|---|
CAP_CHOWN |
2.2 | 任意修改文件的 UID 和 GID |
CAP_DAC_OVERRIDE |
2.2 | 绕过文件的读、写、执行权限检查(DAC) |
CAP_DAC_READ_SEARCH |
2.2 | 绕过文件的读权限检查,绕过目录的读和执行检查 |
CAP_FOWNER |
2.2 | 绕过需要进程 UID 与文件 UID 匹配的操作检查 |
CAP_FSETID |
2.2 | 修改文件时不自动清除 SUID/SGID 位 |
CAP_KILL |
2.2 | 向任意进程发送信号(绕过权限检查) |
CAP_SETGID |
2.2 | 任意操作进程的 GID 和补充组列表 |
CAP_SETUID |
2.2 | 任意操作进程的 UID(包括 setuid(0)) |
CAP_SETPCAP |
2.2 | 向其他进程授予/移除能力;修改 securebits |
CAP_LINUX_IMMUTABLE |
2.2 | 设置文件的 immutable 和 append-only 属性 |
CAP_NET_BIND_SERVICE |
2.2 | 绑定低于 1024 的特权端口 |
CAP_NET_BROADCAST |
2.2 | 套接字广播和监听多播 |
CAP_NET_ADMIN |
2.2 | 网络管理:接口配置、防火墙、路由表等 |
CAP_NET_RAW |
2.2 | 使用 RAW 和 PACKET 套接字 |
CAP_IPC_LOCK |
2.2 | 锁定内存(mlock);使用大页内存 |
CAP_IPC_OWNER |
2.2 | 绕过 System V IPC 对象的权限检查 |
CAP_SYS_MODULE |
2.2 | 加载和卸载内核模块 |
CAP_SYS_RAWIO |
2.2 | 原始 I/O 端口操作;访问 /dev/mem、/dev/kmem |
CAP_SYS_CHROOT |
2.2 | 使用 chroot() |
CAP_SYS_PTRACE |
2.2 | 使用 ptrace() 追踪任意进程 |
CAP_SYS_PACCT |
2.2 | 进程记账(acct()) |
CAP_SYS_ADMIN |
2.2 | ⚠️ 严重过载:大量系统管理操作 |
CAP_SYS_BOOT |
2.2 | 重启系统(reboot);加载新内核(kexec) |
CAP_SYS_NICE |
2.2 | 修改进程优先级、调度策略、CPU 亲和性 |
CAP_SYS_RESOURCE |
2.2 | 超越资源限制(rlimit);磁盘配额 |
CAP_SYS_TIME |
2.2 | 设置系统时钟和硬件时钟 |
CAP_SYS_TTY_CONFIG |
2.2 | 虚拟终端特权操作(vhangup 等) |
3.2 Linux 扩展能力
这些是 Linux 特有的能力,提供了更细粒度的控制:
| 能力 | 引入版本 | 描述 |
|---|---|---|
CAP_MKNOD |
2.4 | 创建特殊设备文件(mknod) |
CAP_LEASE |
2.4 | 在文件上建立租约(file lease) |
CAP_AUDIT_WRITE |
2.6.11 | 向内核审计日志写入记录 |
CAP_AUDIT_CONTROL |
2.6.11 | 启用/禁用内核审计;修改审计规则 |
CAP_AUDIT_READ |
3.16 | 通过 netlink 多播读取审计日志 |
CAP_SETFCAP |
2.6.24 | 设置文件的 capabilities 扩展属性 |
CAP_MAC_OVERRIDE |
2.6.25 | 覆盖 MAC(Smack LSM 实现) |
CAP_MAC_ADMIN |
2.6.25 | MAC 配置更改(Smack LSM 实现) |
CAP_SYSLOG |
2.6.37 | 特权 syslog 操作;读取内核指针地址 |
CAP_WAKE_ALARM |
3.0 | 设置能唤醒系统的定时器 |
CAP_BLOCK_SUSPEND |
3.5 | 阻止系统进入挂起/休眠状态 |
3.3 从 CAP_SYS_ADMIN 拆分出的新能力
CAP_SYS_ADMIN 长期被称为"新的 root",因为它的覆盖范围太广。近年来内核社区持续将功能从中拆分:
| 能力 | 引入版本 | 从 CAP_SYS_ADMIN 拆分出的功能 |
|---|---|---|
CAP_BPF |
5.8 | 特权 BPF 操作(加载程序、创建 maps) |
CAP_PERFMON |
5.8 | 性能监控(perf_event_open 等) |
CAP_CHECKPOINT_RESTORE |
5.9 | 进程检查点/恢复(CRIU)相关操作 |
趋势:内核社区正持续将
CAP_SYS_ADMIN拆分为更细粒度的能力,以减少"全能型"能力的安全风险。
4. 五大能力集详解
每个线程拥有五个能力集,每个集合是一个位图,包含零个或多个能力:
┌──────────────────┐
│ Bounding Set │ ← 上限限制(不可恢复地缩小)
│ (边界集) │
└────────┬─────────┘
│ 限制
┌────────▼─────────┐
│ Permitted Set │ ← 线程可用的最大能力集合
│ (许可集) │
└──┬──────┬──────┬─┘
│ │ │
┌────────▼──┐ ┌─▼──────▼─┐ ┌──────────────┐
│ Effective │ │Inheritable│ │ Ambient Set │
│ Set │ │ Set │ │ (环境集) │
│ (生效集) │ │(可继承集) │ │ │
└───────────┘ └──────────┘ └──────────────┘
↑
内核权限检查
使用这个集合
4.1 Permitted(许可集)
定义:线程可以拥有的最大能力集合。它是一个"能力上限"。
特性:
- 从 Permitted 中移除的能力永久丢失(除非执行具有该能力的特权程序)
- 新的能力只能通过
execve()执行具有文件能力的程序来获得 - Effective 和 Inheritable 集合必须是 Permitted 的子集
# 查看当前进程的 Permitted 集
cat /proc/self/status | grep CapPrm
# CapPrm: 0000000000000000
# 解码为可读名称
capsh --decode=0000000000000000
4.2 Inheritable(可继承集)
定义:在 execve() 执行新程序时可以跨 exec 保留的能力集合。
特性:
- 历史上这个集合的实际作用有限——非 root 进程执行程序时,Inheritable 集通常被清空
- 与文件的 Inheritable 集进行交集运算后,决定新进程的 Permitted 集
- 这正是 Ambient 集被引入的原因——弥补 Inheritable 在非 root 场景下的不足
4.3 Effective(生效集)
定义:内核用来实际进行权限检查的能力集合。
特性:
- 这是唯一"起作用"的集合——内核检查
capable()时查询的是 Effective 集 - 通常是 Permitted 的子集
- 可以通过
libcapAPI 或prctl()动态开关
// 内核中检查能力的典型代码
if (!capable(CAP_NET_BIND_SERVICE))
return -EACCES;
// 等价于检查:CAP_NET_BIND_SERVICE 是否在 Effective 集中
4.4 Bounding(边界集)
定义:一个不可恢复的上限限制,限定在 execve() 期间能获得的能力。
特性:
- 只能缩小(通过
prctl(PR_CAPBSET_DROP, ...)),无法扩大 - 一旦某个能力从 Bounding 集中移除,所有后代进程都无法再获得它
- 这是实现"纵深防御"的关键机制
# 查看当前进程的 Bounding 集
cat /proc/self/status | grep CapBnd
# CapBnd: 000001ffffffffff
# 通过 capsh 查看
capsh --print | grep Bounding
4.5 Ambient(环境集)
定义(Linux 4.3+):解决非 root 进程执行程序时无法保留能力的历史性缺陷。
特性:
- 在执行非特权程序时自动保留并添加到新进程的 Permitted 和 Effective 集中
- 一个能力要进入 Ambient 集,必须同时存在于 Permitted 和 Inheritable 集中
- 解决了"非 root 进程无法将能力传递给子进程"的问题
Ambient 集的价值:
问题场景:
$ ping 8.8.8.8
ping 需要 CAP_NET_RAW 能力
旧方案:SUID root → ping 以 root 运行 → 安全风险
新方案:Ambient Set → ping 只拥有 CAP_NET_RAW → 最小权限
父进程(bash, 无特殊权限)
│ execve("/usr/bin/ping")
│ ping 有文件能力 cap_net_raw=pie
▼
子进程(ping)
Permitted: CAP_NET_RAW ← 从文件能力获得
Ambient: CAP_NET_RAW ← 自动添加到生效集
Effective: CAP_NET_RAW ← 内核权限检查通过!
5. 文件能力(File Capabilities)
从 Linux 2.6.24 起,能力可以附加到可执行文件上,存储在文件的 security.capability 扩展属性中。
文件能力的三个组成部分
| 组件 | 含义 |
|---|---|
| Permitted (p) | 自动授予执行该文件进程的能力 |
| Inheritable (i) | 与进程的 Inheritable 集做"与"运算 |
| Effective (e) | 单个位:如果设置,新获得的许可能力自动变为生效 |
版本
| 版本 | 内核版本 | 说明 |
|---|---|---|
| VFS_CAP_REVISION_1 | 2.6.24 | 最初版本,32 位掩码 |
| VFS_CAP_REVISION_2 | 2.6.25 | 64 位掩码,支持更多能力 |
| VFS_CAP_REVISION_3 | 4.14 | 命名空间化文件能力 |
文件能力 vs SUID
# 旧方式:SUID(赋予完整 root 权限)
chmod u+s /usr/bin/myapp
# 新方式:文件能力(仅赋予所需能力)
setcap cap_net_bind_service=ep /usr/bin/myapp
# ↑↑
# e: effective bit 设置
# p: permitted set
6. 能力继承规则:execve() 时的完整变换
6.1 标准变换算法
当进程执行 execve() 时,内核按以下公式计算新进程的能力集:
设:
P(X) = execve() 前的旧进程能力集 X
P'(X) = execve() 后的新进程能力集 X
F(X) = 文件的文件能力集 X
变换公式:
P'(ambient) = (文件是特权文件) ? 空集 : P(ambient)
P'(permitted) = (P(inheritable) ∩ F(inheritable))
∪ (F(permitted) ∩ P(bounding))
∪ P'(ambient)
P'(effective) = F(effective_bit) ? P'(permitted) : P'(ambient)
P'(inheritable) = P(inheritable) ← 不变
P'(bounding) = P(bounding) ← 不变
通俗解释:
- Ambient:如果不是执行特权文件(SUID 或文件能力标记),保留 Ambient 集
- Permitted:新许可集 = (可继承交集) + (文件许可 ∩ 边界限制) + 环境能力
- Effective:如果文件 effective bit 被设置,所有新许可能力自动生效;否则只生效环境能力
- Inheritable 和 Bounding:保持不变
6.2 Root 用户的特殊行为
当进程的 real UID 或 effective UID 为 0(root)时执行程序:
文件的 Permitted 和 Inheritable 集被忽略!
被视为全 1(所有能力)。
因此 root 执行任何程序都会获得其 Bounding 集内的所有能力。
可通过 SECBIT_NOROOT 禁用此行为。
6.3 UID 变换的影响
进程 UID 从 0 变为非 0 时:
├─ Permitted 集被清空
└─ Effective 集被清空
除非设置了 SECBIT_KEEP_CAPS,才能保留能力。
# 示例:从 root 切换到普通用户,保留 CAP_NET_RAW
capsh --secbits=0x10 -- -c '
ping -c 1 8.8.8.8
'
# 0x10 = SECBIT_KEEP_CAPS 设置
7. Securebits:纯能力环境
Securebits 允许创建一个完全基于能力的执行环境,彻底禁用对 UID=0 的特殊处理:
| Securebit | 位值 | 含义 |
|---|---|---|
SECBIT_KEEP_CAPS |
0x10 | UID 变为非 0 时保留许可能力 |
SECBIT_NO_SETUID_FIXUP |
0x20 | UID 切换时不自动调整能力集 |
SECBIT_NOROOT |
0x40 | Root 执行程序时不自动获得所有能力 |
SECBIT_NO_CAP_AMBIENT_RAISE |
0x80 | 禁止通过 prctl 添加 Ambient 能力 |
每个标志都有对应的 _LOCKED 版本,设置后该标志不可逆:
# 创建一个纯能力环境(root 也不享有特权)
capsh --secbits=0x2f -- -c 'id; cat /proc/self/status | grep Cap'
# 0x2f = KEEP_CAPS|NO_SETUID_FIXUP|NOROOT 及其锁定位
8. 用户命名空间与 Capabilities
用户命名空间(User Namespace)与 Capabilities 深度结合,实现了"容器中的 root":
宿主机:
进程 A (UID=1000) ─── 无特殊能力 ─── 普通用户
创建用户命名空间后:
进程 A 在新命名空间中映射为 UID=0
进程 A 获得该命名空间内的完整能力集
但这些能力只在该命名空间内有效!
┌─────────────────────────────────┐
│ User Namespace (容器) │
│ UID=0 (root in container) │
│ Full capabilities │
│ ├─ 可以挂载文件系统 │
│ ├─ 可以绑定端口 │
│ └─ 可以修改网络配置 │
└───────────────┬─────────────────┘
│
宿主机视角: │ UID=1000, 无特权
│ 容器内的操作不影响宿主机
# 在用户命名空间中体验"root"
unshare --user --map-root-user bash
# 现在你是"容器内的 root"
whoami # root
id # uid=0(root)
ping 8.8.8.8 # 可能仍然失败(CAP_NET_RAW 受宿主机限制)
9. 实战命令大全
9.1 进程能力查看
# 查看当前 shell 的能力
capsh --print
# 查看特定进程的能力
cat /proc/$(pidof nginx)/status | grep -i cap
# CapInh: 0000000000000000 (Inheritable)
# CapPrm: 0000000000000000 (Permitted)
# CapEff: 0000000000000000 (Effective)
# CapBnd: 000001ffffffffff (Bounding)
# CapAmb: 0000000000000000 (Ambient)
# 解码能力位图
capsh --decode=000001ffffffffff
# 查看进程的可读能力列表
getpcaps $(pidof nginx)
# 查看所有进程的能力
ps -eo pid,comm,cap_eff,cap_prm --sort=-cap_eff | head -20
9.2 文件能力管理(getcap/setcap)
# ========== 查看文件能力 ==========
# 查看单个文件
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep
# 递归查看目录
getcap -r /usr/bin/
getcap -r / 2>/dev/null
# ========== 设置文件能力 ==========
# 格式:setcap <能力名>=<集合标志> <文件>
# 设置 Permitted + Effective(最常用)
sudo setcap cap_net_raw=ep /usr/bin/ping
# ↑↑
# e: effective bit 设置
# p: permitted 设置
# 仅设置 Permitted(不自动生效)
sudo setcap cap_net_bind_service=p /usr/bin/myapp
# 设置 Permitted + Inheritable
sudo setcap cap_dac_override=ip /usr/bin/myapp
# 设置多个能力
sudo setcap 'cap_net_raw,cap_net_bind_service=ep' /usr/bin/myapp
# ========== 移除文件能力 ==========
# 移除所有能力
sudo setcap -r /usr/bin/myapp
# ========== 验证文件能力 ==========
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep
9.3 能力测试与调试
# ========== capsh 工具 ==========
# 以特定能力运行命令
capsh --caps="cap_net_raw=ep" -- -c "ping -c 1 8.8.8.8"
# 以特定能力启动 shell
capsh --caps="cap_net_bind_service=ep cap_chown=ep" --
# 删除特定能力
capsh --drop=cap_sys_admin -- -c "mount /dev/sda1 /mnt"
# mount 会失败:权限不足
# 完全无能力运行
capsh --caps="=" -- -c "cat /etc/shadow"
# 即使 root 也读不了(如果 DAC 也拒绝了)
# ========== 解码能力掩码 ==========
capsh --decode=0000000000003000
# 0x3000 = CAP_NET_ADMIN | CAP_NET_RAW
# ========== 查看系统支持的能力列表 ==========
capsh --supported
9.4 libcap 编程接口
#include <sys/capability.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
cap_t caps;
cap_value_t cap_list[2];
// 获取当前进程的能力
caps = cap_get_proc();
if (caps == NULL) {
perror("cap_get_proc");
exit(1);
}
// 以文本形式打印
char *cap_text = cap_to_text(caps, NULL);
printf("Current capabilities: %s\n", cap_text);
cap_free(cap_text);
// 添加 CAP_NET_RAW 到 Permitted 和 Effective
cap_list[0] = CAP_NET_RAW;
cap_set_flag(caps, CAP_PERMITTED, 1, cap_list, CAP_SET);
cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET);
// 应用新的能力集
if (cap_set_proc(caps) == -1) {
perror("cap_set_proc");
cap_free(caps);
exit(1);
}
printf("Successfully set CAP_NET_RAW\n");
// 释放
cap_free(caps);
return 0;
}
# 编译
gcc -o cap_demo cap_demo.c -lcap
sudo ./cap_demo
libcap 核心 API 速查:
| API | 功能 |
|---|---|
cap_get_proc() |
获取当前进程的能力集 |
cap_set_proc() |
设置当前进程的能力集 |
cap_get_file() |
获取文件的能力 |
cap_set_file() |
设置文件的能力 |
cap_get_flag() |
获取特定能力的标志状态 |
cap_set_flag() |
设置特定能力的标志状态 |
cap_clear() |
清空能力集 |
cap_to_text() |
将能力集转换为文本 |
cap_from_text() |
从文本解析能力集 |
cap_free() |
释放能力集内存 |
10. Docker 容器与 Capabilities
10.1 Docker 默认授予的能力
Docker 默认授予容器 14 种能力,远少于 root 的全部能力:
| 默认授予的能力 | 用途 |
|---|---|
CAP_CHOWN |
修改文件所有者 |
CAP_DAC_OVERRIDE |
绕过文件权限检查 |
CAP_FSETID |
修改文件时不清理 SUID/SGID |
CAP_FOWNER |
绕过文件所有者检查 |
CAP_MKNOD |
创建设备文件 |
CAP_NET_RAW |
使用原始套接字(ping) |
CAP_SETGID |
修改 GID |
CAP_SETUID |
修改 UID |
CAP_SETFCAP |
设置文件能力 |
CAP_SETPCAP |
修改能力集 |
CAP_NET_BIND_SERVICE |
绑定特权端口 |
CAP_SYS_CHROOT |
使用 chroot |
CAP_KILL |
发送信号 |
CAP_AUDIT_WRITE |
写入审计日志 |
Docker 默认移除的能力(安全原因):
CAP_SYS_MODULE ← 防止加载内核模块
CAP_SYS_RAWIO ← 防止直接访问 I/O 端口
CAP_SYS_PACCT ← 防止进程记账
CAP_SYS_ADMIN ← 防止大量系统管理操作
CAP_SYS_NICE ← 防止修改调度优先级
CAP_SYS_RESOURCE ← 防止超越资源限制
CAP_SYS_TIME ← 防止修改系统时钟
CAP_SYS_TTY_CONFIG ← 防止虚拟终端操作
CAP_AUDIT_CONTROL ← 防止修改审计规则
CAP_MAC_ADMIN ← 防止修改 MAC 策略
CAP_MAC_OVERRIDE ← 防止绕过 MAC
CAP_SYSLOG ← 防止特权 syslog
... 以及更多
10.2 最小权限容器配置
# 极致安全:移除所有能力,仅加回必需的
docker run -d \
--name secure-app \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--security-opt no-new-privileges:true \
--read-only \
--tmpfs /tmp:noexec,nosuid \
nginx:alpine
# 参数说明:
# --cap-drop=ALL → 移除所有能力
# --cap-add=NET_BIND_SERVICE → 仅加回绑定端口能力
# no-new-privileges:true → 禁止进程获取新能力
# --read-only → 根文件系统只读
# --tmpfs /tmp:noexec,nosuid → tmpfs 禁止执行
10.3 Kubernetes 中的 Capabilities
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
containers:
- name: app
image: nginx:alpine
securityContext:
capabilities:
drop:
- ALL # 移除所有能力
add:
- NET_BIND_SERVICE # 仅加回必需的能力
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 1000
11. Capabilities 与 SUID 的对比
| 维度 | SUID | Capabilities |
|---|---|---|
| 权限粒度 | 全部 root 权限 | 精确到单个能力 |
| 安全风险 | 高(漏洞→root shell) | 低(漏洞→有限能力) |
| 配置方式 | chmod u+s |
setcap |
| 存储位置 | 文件权限位(inode mode) | 文件扩展属性(xattr) |
| 继承性 | 仅对可执行文件有效 | 可跨 exec 继承(Ambient) |
| 可审计性 | 难追踪(都是 root) | 可追踪(精确到能力) |
| 兼容性 | 所有 UNIX 系统 | Linux 2.6.24+ |
迁移 SUID 到 Capabilities
# 传统 SUID 方式
ls -l /usr/bin/ping
# -rwsr-xr-x 1 root root /usr/bin/ping
# 迁移步骤:
# 1. 设置文件能力
sudo setcap cap_net_raw=ep /usr/bin/ping
# 2. 移除 SUID 位
sudo chmod u-s /usr/bin/ping
# 3. 验证
ls -l /usr/bin/ping
# -rwxr-xr-x 1 root root /usr/bin/ping ← SUID 已移除
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep ← 能力已设置
ping -c 1 8.8.8.8 # 仍然可以正常工作
12. 安全加固最佳实践
12.1 最小权限原则
# ✅ 好的实践:移除所有默认能力,只加必需的
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx
# ✅ 检查当前能力,关闭不需要的
capsh --print
capsh --drop=CAP_SYS_ADMIN,CAP_SYS_MODULE --
# ❌ 坏的做法:赋予过多能力
docker run --cap-add=ALL nginx
# ❌ 更坏的做法:特权模式
docker run --privileged nginx
12.2 能力审计 Checklist
# 1. 检查所有 SUID 文件(潜在的替换目标)
find / -perm -4000 -type f 2>/dev/null
# 2. 检查所有文件能力
getcap -r / 2>/dev/null
# 3. 检查运行的容器能力
for c in $(docker ps -q); do
echo "Container: $c"
docker inspect $c | jq '.[0].HostConfig.CapAdd, .[0].HostConfig.CapDrop'
done
# 4. 审查进程的能力
ps -eo pid,comm,user,cap_eff --sort=-cap_eff | grep -v "0000000000000000"
12.3 纵深防御组合
# 完整的容器安全配置:多层次防御
docker run -d \
--name defense-in-depth \
--cap-drop=ALL \ # Capabilities:移除所有
--cap-add=NET_BIND_SERVICE \ # 仅加回必需的
--security-opt no-new-privileges \ # 禁止获取新能力
--security-opt seccomp=strict.json \ # Seccomp:系统调用过滤
--security-opt apparmor=custom-prof \ # AppArmor:文件访问控制
--read-only \ # 只读根文件系统
--tmpfs /tmp:noexec,nosuid \ # 临时文件系统限制
--user 1000:1000 \ # 非 root 用户运行
nginx:alpine
13. Capabilities 提权攻击与防范
13.1 已知的危险能力
以下能力如果配置不当,可能被用于提权:
| 能力 | 攻击向量 | 风险等级 |
|---|---|---|
CAP_SYS_ADMIN |
mount、pivot_root、unshare 等大量操作 | 🔴 极高 |
CAP_SYS_MODULE |
加载恶意内核模块 | 🔴 极高 |
CAP_SYS_RAWIO |
直接内存访问、修改内核 | 🔴 极高 |
CAP_SYS_PTRACE |
注入任意进程 | 🔴 高 |
CAP_SETUID |
setuid(0) 直接获得 root | 🔴 高 |
CAP_SETFCAP |
给任意文件设置能力 | 🔴 高 |
CAP_DAC_OVERRIDE |
修改 /etc/shadow 添加 root 用户 | 🔴 高 |
CAP_CHOWN |
修改关键文件所有者 | 🟡 中 |
CAP_FOWNER |
修改关键文件权限 | 🟡 中 |
CAP_DAC_READ_SEARCH |
读取 /etc/shadow | 🟡 中 |
13.2 防护措施
# 1. 审计文件能力
getcap -r / 2>/dev/null | grep -E "cap_sys_admin|cap_setuid|cap_dac_override"
# 确保没有意外的高权限能力
# 2. 使用 Bounding Set 限制整个进程树
# 在系统启动早期设置
capsh --drop=cap_sys_admin,cap_sys_module,cap_sys_rawio --
# 3. 结合 Seccomp 防止能力绕过
# Seccomp 可以阻止可能导致提权的系统调用
# 4. 启用 Securebits 锁定
# SECBIT_NOROOT_LOCKED 确保即使是 SUID-root 程序也不获得全部能力
14. 总结
核心要点
| 要点 | 说明 |
|---|---|
| 拆分 root 权限 | 将传统 root 全部特权拆分为 40+ 个独立能力 |
| 五大能力集 | Permitted / Inheritable / Effective / Bounding / Ambient |
| 文件能力替代 SUID | 比 SUID 更安全、更精细的权限赋予方式 |
| Bounding Set 不可逆 | 实现纵深防御的关键机制 |
| Ambient Set 解决继承 | 让非 root 进程的子进程也能保留能力 |
| 容器默认受限 | Docker 默认移除 20+ 种危险能力 |
| 最小权限原则 | 永远使用 --cap-drop=ALL --cap-add=<必需的> |
推荐资源
- capabilities(7) - Linux 手册页
- libcap 官方仓库
- Docker Runtime Privilege and Linux Capabilities
- Linux Capabilities in Practice
- A Brief Introduction to Linux Capabilities
免责声明:本文内容基于 Linux 内核文档和手册页编写,适用于 Linux 2.2+ 内核系列。具体能力列表和 API 行为可能因内核版本和发行版配置而异,请以实际运行环境为准。