如何仅授予容器 `CAP_NET_BIND_SERVICE` 并验证生效

解读

国内面试官问这道题,核心想确认三件事:

  1. 你是否真的理解 Linux Capabilities 比 --privileged 更细粒度、更安全;
  2. 能否把“最小权限”原则落到 Dockerfile、运行参数、K8s YAML 三个层面;
  3. 会不会用“国产”常用工具(systemd、alpine、centos、华为 CCE、阿里云 ACK)快速自证结果。
    答得太浅(只写 --cap-add)会被追问“怎么验证”“为什么还是 1024 以下端口”,答得太深(直接上 eBPF 审计)又容易超时。因此答案要**“一句命令 + 一段验证 + 一个排坑提示”**,既体现安全意识,又符合国内实际节奏。

知识点

  • Linux Capabilities:把 root 的 40+ 特权拆成细粒度单元,CAP_NET_BIND_SERVICE=10 对应“绑定 ≤1024 端口”。
  • Docker 运行时可添加/删除 Capability,默认列表在 docker-run(1) 手册的 “Default capabilities” 段,共 14 项。
  • “仅授予”= 先 drop all,再 add 目标,否则默认 14 项仍在,不满足“最小权限”要求。
  • 验证思路:容器内起进程监听 80/443,宿主机 ss/netstat 能看到端口;若删权再起,应报 Permission denied
  • 国产镜像源:阿里云 registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9 拉得快,面试演示不卡壳。
  • 国内云厂商加固基线:华为 CCE 强制 drop ALL,所以现场写 YAML 时必须显式 add NET_BIND_SERVICE,否则 Pod 起不来。

答案

  1. 构建一个监听 80 端口的最小镜像
FROM alpine:3.18
RUN adduser -D -u 1000 app
USER 1000
COPY app /home/app/
EXPOSE 80
ENTRYPOINT ["/home/app"]

app 是编译好的 Go 二进制,源码里 http.ListenAndServe(":80", nil)

  1. 仅授予 CAP_NET_BIND_SERVICE 并运行
docker build -t demo:cap .
docker run --rm -d --name cap-test \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  -p 8080:80 \
  demo:cap

关键点--cap-drop=ALL 先把默认 14 项全部去掉,再精确加回需要的 1 项,这是国内等保测评的硬性加分项

  1. 验证生效
  • 宿主机执行
ss -ltnp | grep :8080

看到 0.0.0.0:8080 说明 80 端口已在容器内成功监听。

  • 进入容器确认进程权限
docker exec cap-test ps -o pid,user,cmd

输出中进程用户是 1000,而非 root,证明非 root 也能绑 80 端口,Capability 生效

  • 反向验证:再启一个容器去掉 NET_BIND_SERVICE
docker run --rm --cap-drop=ALL demo:cap

日志立刻打印 listen tcp :80: bind: permission denied对照实验完成

  1. 国内 CI 场景示例(GitLab Runner)
stages:
  - security-test
cap-test:
  stage: security-test
  image: docker:24-dind
  services:
    - docker:24-dind
  script:
    - docker build -t demo:cap .
    - docker run --rm --cap-drop=ALL --cap-add=NET_BIND_SERVICE demo:cap &
    - sleep 3
    - netstat -tlnp | grep :80 || exit 1

把验证脚本固化到流水线,每次 MR 都自动检查“是否多开权限”,这是面试官最爱听的“安全左移”实践。

拓展思考

  • K8s 场景
    在华为 CCE 或阿里云 ACK 的 1.28 集群里,PodSecurityStandard 已启用 restricted 模式,必须写:
securityContext:
  capabilities:
    drop: ["ALL"]
    add: ["NET_BIND_SERVICE"]

如果漏写 add,ReplicaSet 会无限 CrashLoopBackOff,现场排错要会 kubectl describe pod 看 Events 里的 “capability not granted” 提示

  • 镜像层最小化
    docker-slimdive 工具分析,确保 Capability 白名单与文件权限白名单同时收缩,否则出现“给了 Capability 但镜像里没证书”导致 HTTPS 80 端口起不来的尴尬。

  • 与 seccomp 联动
    国内银行容器云要求同时提交 seccomp 配置文件,CAP_NET_BIND_SERVICE 只解决“能不能绑”,seccomp 解决“绑完能不能 accept”,面试时提一句“已用 docker run --security-opt seccomp=custom.json 配合测试”可瞬间拉开差距。

  • Systemd 托管
    在统信 UOS 或麒麟 V10 宿主机上,podman generate systemd 生成 service 文件,把 --cap-drop=ALL --cap-add=NET_BIND_SERVICE 写进 ExecStart,可实现开机自启并满足国测“容器服务最小权限”检查项。