在 Docker 容器中,应用启动失败、功能异常,日志却只出现 Operation not permitted、Permission denied 或 EPERM,往往很难第一时间定位。除了文件权限、Linux Capabilities、AppArmor 或 SELinux,Seccomp 拒绝系统调用也是常见原因。本文从工作原理、定位步骤和安全配置三个方面,整理一套可直接执行的排查方法。🔍
一、Seccomp 为什么会拒绝系统调用
Seccomp 是 Linux 内核提供的系统调用过滤机制,可以限制进程能够调用的内核接口。Docker 默认会为容器加载 Seccomp 配置,以“默认拒绝、明确允许”的方式缩小容器可访问的内核攻击面。未被策略允许的调用通常返回 EPERM,因此应用看到的只是普通权限错误。相关机制可参考 Docker Seccomp 官方文档 和 Linux seccomp 手册。
需要注意:出现 EPERM 不等于一定是 Seccomp。Capabilities 不足、只读文件系统、安全模块策略以及普通用户权限问题,也可能产生相似现象。
二、先确认容器是否启用了 Seccomp
查看正在运行容器的安全选项:
docker inspect --format '{{json .HostConfig.SecurityOpt}}' 容器名
如果返回自定义 profile 路径或相关安全配置,说明启动时进行了显式设置;即使结果为空,Docker 通常仍会使用默认 Seccomp 策略。还可以进入容器查看进程状态:
grep -E 'Seccomp|NoNewPrivs' /proc/1/status
- Seccomp: 0:当前进程未启用 Seccomp。
- Seccomp: 1:严格模式。
- Seccomp: 2:过滤模式,也是容器中常见的状态。
若要确认宿主机内核是否支持,可执行:
grep CONFIG_SECCOMP= /boot/config-$(uname -r)
grep CONFIG_SECCOMP_FILTER= /boot/config-$(uname -r)
三、通过对照实验快速缩小范围
测试环境中,可以使用不加载 Seccomp 策略的方式启动相同镜像:
docker run --rm --security-opt seccomp=unconfined 镜像名
若原问题立即消失,Seccomp 很可能是主要原因;若仍然失败,应继续检查容器用户、挂载权限、Capabilities、AppArmor 或 SELinux。⚠️ seccomp=unconfined 只能用于短时间诊断,不应直接作为生产环境的长期解决方案。
排查时必须保持镜像版本、环境变量、挂载目录、网络模式和启动参数一致,否则对照结果可能失真。对于现有容器,可从 docker inspect 中提取配置,再构造等价测试命令。
四、找出具体被拒绝的系统调用
最直接的方法是使用 strace 跟踪目标程序:
strace -f -o /tmp/app.strace 应用启动命令
复现问题后搜索失败记录:
grep -E 'EPERM|EACCES' /tmp/app.strace
-f 会跟踪子进程,适用于存在工作进程、线程或脚本包装器的应用。若镜像中没有 strace,可制作临时调试镜像安装工具,或在具备权限的宿主机上使用调试容器。不要为了方便而把调试工具永久加入精简生产镜像。
还应检查宿主机内核和审计日志:
dmesg | grep -i seccomp
journalctl -k | grep -i seccomp
ausearch -m SECCOMP -ts recent
日志可能包含进程名、系统调用编号、架构和返回动作。系统调用编号在不同 CPU 架构上可能对应不同名称,因此应结合 uname -m、审计工具或对应架构的 syscall 表进行解析,不能仅凭编号猜测。
五、区分 Seccomp 与其他权限机制
- 若关闭 Seccomp 后问题仍存在,执行 docker inspect 检查 AppArmor、SELinux 标签和只读根文件系统。
- 执行 docker exec 容器名 grep Cap /proc/1/status,检查进程实际拥有的 Capabilities。
- 核对容器内运行用户,并检查目标文件、设备节点和挂载目录的属主及权限。
- 如果调用涉及挂载、调试、修改时间、加载内核模块或访问设备,应重点判断是否同时受到 Capability 限制。
Seccomp 控制“能否发起某类系统调用”,Capabilities 控制“进程是否具备执行特权操作的权限”,两者可能同时生效。仅增加 --cap-add 不一定能放行被 Seccomp 拦截的调用,反之亦然。
六、安全编写自定义 Seccomp 配置
确认具体调用后,应以 Docker 默认 profile 为基线复制一份自定义配置,只增加应用确实需要的系统调用,而不是改成全放行。默认配置可从 来源链接 默认 Seccomp Profile 获取。
应用自定义策略的命令如下:
docker run --rm --security-opt seccomp=/path/profile.json 镜像名
在 Compose 中可通过服务的 security_opt 指定 seccomp:/path/profile.json。配置前应确认路径、Docker Engine 版本、目标架构以及 profile 中的系统调用名称均有效。
- 优先按单个系统调用放行,不要使用宽泛策略。
- 记录放行原因、应用版本、内核版本和验证结果。
- 覆盖启动、健康检查、定时任务、升级及异常恢复场景。
- 在 CI/CD 或预发布环境执行回归测试,防止应用升级后静默失败。
- 定期比较自定义策略与新版 Docker 默认策略,避免长期保留多余权限。
总结
排查 Docker Seccomp 问题,可以遵循“确认过滤状态 → 使用 unconfined 做短时对照 → 通过 strace 和审计日志定位系统调用 → 排除 Capabilities 与安全模块 → 最小化修改 profile”的顺序。✅ 最重要的原则不是让容器先跑起来,而是在明确调用用途和风险后,只开放业务真正需要的内核接口,从而兼顾可用性与最小权限安全。