跳到主要内容

生产安全与资源限制

1. 概念

容器默认配置不安全。容器共享宿主内核,默认 root + 无资源限制 = 被入侵后直接提权 + 耗尽宿主资源。生产必须加固。

2. 非 root 运行(最重要)

FROM node:20-alpine
WORKDIR /app
COPY --chown=node:node . .
USER node
CMD ["node", "server.js"]

K8s 强制:

securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000

为什么:容器内 root = 宿主内核 root uid 0。容器逃逸漏洞(如 runc CVE-2024-21626)利用容器 root 提权到宿主 root。

3. 只读文件系统

docker run --read-only --tmpfs /tmp --tmpfs /var/cache myimage
# K8s
securityContext:
readOnlyRootFilesystem: true

应用需要写的目录用 tmpfs / volume。大多数前端应用只需 /tmp

4. capabilities 最小化

Linux capabilities 把 root 权限切成小块。容器默认保留一些 cap(如 NET_BIND_SERVICE、SETUID)。

# 看容器默认 cap
docker run --rm alpine grep Cap /proc/1/status

# 全部去掉
docker run --cap-drop ALL myimage

# 只加需要的
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myimage

K8s:

securityContext:
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]

80% 的应用 cap-drop ALL 就够。

5. seccomp profile

系统调用白名单。Docker 默认的 seccomp profile 已禁了约 50 个危险 syscall(如 ptracemountkeyctl)。

生产不要 --security-opt seccomp=unconfined

6. AppArmor / SELinux

强制访问控制。限制容器能读写什么文件、连什么网络。Docker 默认自带 AppArmor profile。

加固不需要自己写,确认没关掉就行:

docker info | grep -i apparmor
# Security Options: apparmor

7. 资源限制

7.1 内存

docker run -m 2g --memory-swap 2g myimage
# -m:最大内存
# --memory-swap:含 swap(设成和 -m 一样 = 禁 swap)

超 memory limit → OOM kill 容器。Node 应用对应设 --max-old-space-size < 容器 limit 的 75%:

docker run -m 2g -e NODE_OPTIONS="--max-old-space-size=1536" myimage

7.2 CPU

docker run --cpus 2.0 myimage # 最多用 2 核
docker run --cpu-shares 512 myimage # 相对权重(默认 1024)
docker run --cpuset-cpus 0,1 myimage # 绑定 CPU 0 和 1

--cpus 是硬限制,--cpu-shares 是竞争时的分配比例。

7.3 PID 限制

docker run --pids-limit 200 myimage

防 fork 炸弹。

7.4 磁盘

docker run --storage-opt size=5G myimage # 需要 overlay2 + xfs + pquota

更常用:不设容器磁盘限制,但监控 volume 使用率。

8. 网络隔离

docker run --network none myimage # 完全无网络

多容器场景:用不同 network 隔离。

services:
web:
networks: [frontend, backend]
db:
networks: [backend] # 对外不可见
nginx:
networks: [frontend]
networks:
frontend:
backend:
internal: true # 无外网

9. 镜像安全

9.1 锁定版本

FROM node:20.11.1-alpine3.19@sha256:abc123

9.2 不装不必要的包

# ✗
RUN apk add bash curl wget vim git openssh

# ✓ 生产镜像什么都不装
FROM gcr.io/distroless/nodejs20

9.3 CI 扫描

每次构建都跑 Trivy / Scout,高危漏洞阻断部署。

9.4 签名验证

Cosign 签名 + K8s admission policy 强制验签。

10. Docker daemon 安全

// /etc/docker/daemon.json
{
"userns-remap": "default", // 开 user namespace(容器 root → 宿主非 root)
"live-restore": true, // daemon 重启不杀容器
"no-new-privileges": true, // 禁止进程获取额外权限
"icc": false, // 禁止容器间通信(除非显式 link)
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}

Docker socket(/var/run/docker.sock)不要挂进容器,等于给容器宿主 root 权限。

11. Rootless Docker

完全不以 root 跑 Docker daemon:

dockerd-rootless-setuptool.sh install

更安全(daemon 没 root),但兼容性差(不能绑 1024 以下端口等)。

12. K8s 安全策略

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
spec:
privileged: false
runAsUser:
rule: MustRunAsNonRoot
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
allowedCapabilities: []
requiredDropCapabilities:
- ALL
readOnlyRootFilesystem: true

PodSecurityPolicy 已被 Pod Security Admission 替代。

13. 安全检查清单

生产容器上线前:

  • 非 root 运行
  • 镜像扫描无高危漏洞
  • 资源限制(memory + CPU)
  • 只读文件系统 + tmpfs
  • capabilities drop ALL
  • 不挂 Docker socket
  • 镜像签名
  • 日志限制
  • 网络隔离
  • 无硬编码 secret

14. 常见反模式

  • 容器跑 root:一条容器逃逸 = 宿主 root
  • --privileged:等于关掉所有安全机制
  • /var/run/docker.sock:给容器完全宿主控制权
  • 不限内存:内存泄漏拖垮宿主所有容器
  • latest 基础镜像不扫描:含 CVE 上线
  • 关 seccomp / AppArmor "解决兼容问题":等于关安全门
  • 容器内装 SSH:应用 docker exec 或 kubectl exec 调试
  • 没设日志上限:stdout 日志写满磁盘

15. 延伸阅读