容器安全基础:镜像、权限与资源限制
从可信镜像、最小权限到资源限额,用几条可落地的默认设置显著收紧容器攻击面。
容器让部署变得简单,但默认配置往往偏向「能跑起来」而非「跑得安全」。无论你把容器放在单台 VPS 上,还是运行在集群里,下面这几层加固都值得在上线前逐项过一遍。
从可信镜像开始
镜像是容器的根基,一个被投毒的基础镜像会让后续所有防护形同虚设。
- 优先用官方或可信来源的镜像,并倾向于精简变体(如 -slim、distroless、alpine),体积越小、可被攻击的组件越少。
- 锁定明确的版本 tag,不要用 latest。latest 会随时间漂移,导致「昨天能复现的构建今天变了」。生产环境最好进一步用镜像摘要(digest)固定:
# 用 digest 固定,内容不可变
docker pull nginx@sha256:<digest>
- 在流水线里扫描漏洞,把已知高危 CVE 挡在部署之前:
trivy image --severity HIGH,CRITICAL your-registry/app:1.4.2
在容器内以最小权限运行
即使镜像干净,容器内部也应假设「进程可能被攻破」,并尽量削弱被攻破后的破坏力。
- 不要以 root 运行。在 Dockerfile 里用 USER 切换到非特权用户:
RUN adduser --system --uid 10001 app
USER 10001
- 只读根文件系统,避免攻击者写入或篡改文件;确需写入的目录单独挂可写卷。
- 丢弃不需要的 Linux capabilities,只保留必要能力,并禁止提权。
在集群中,这些可以集中写进 Pod 的 securityContext:
securityContext:
runAsNonRoot: true
runAsUser: 10001
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
设置资源 requests 与 limits
一个失控的容器(内存泄漏、死循环)可能吃光宿主资源,把同机的其它服务一起拖垮。给每个容器设定资源上下限,可以隔离「爆炸半径」:
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
requests 用于调度和资源预留,limits 是硬上限——超过内存上限的容器会被终止,而不是拖垮整台宿主。
不要把宿主的钥匙交给容器
许多严重逃逸事故都源于「随手挂载」。
- 绝不挂载 Docker socket(/var/run/docker.sock)进普通容器——拿到它几乎等于拿到宿主的 root。
- 不要挂载宿主敏感目录,如 /、/etc、/root、/var/run;只挂容器真正需要的最小路径,并尽量以只读方式挂载。
- 避免 --privileged 和 host 网络/PID 命名空间,除非你非常清楚代价。
持续更新基础镜像
安全是持续状态而非一次性动作。基础镜像里的库会不断爆出新漏洞,因此要定期重建并重新部署:重新拉取打了补丁的基础镜像、跑扫描、滚动更新。把「镜像扫描 + 重建」接进定时任务或 CI,能让你在漏洞公开后几小时内而不是几个月后完成修补。
小结
容器安全的多数收益来自少数几个默认设置:用可信、锁定版本并经过扫描的镜像;以非 root、只读根、丢弃 capabilities 的最小权限运行;用 requests/limits 隔离资源;绝不把 Docker socket 或宿主敏感目录挂进容器;并持续重建更新基础镜像。把这几条写进你的 Dockerfile 和部署清单模板,新服务就能默认安全,而不必事后补救。