Docker 容器安全扫描怎么做?工具怎么选?
容器安全扫描的核心,是在镜像进入生产环境前尽早发现风险:基础镜像有没有 CVE,应用依赖有没有漏洞,构建文件里有没有把密钥写进去,许可证会不会带来合规问题。它只适用于自己有权构建、维护或部署的镜像和仓库,属于防御性安全检查,不是拿工具去扫描别人的系统。
容器安全扫描到底要扫什么?
只扫“镜像漏洞”远远不够。一次比较完整的容器安全扫描,通常会覆盖这些内容:
| 扫描对象 | 重点看什么 | 常见处理方式 |
|---|---|---|
| 操作系统包 | glibc、openssl、curl、apt/apk/yum 包里的 CVE | 更新基础镜像、升级系统包、换更小的基础镜像 |
| 应用依赖 | npm、pip、Maven、Go module、Ruby gem 等依赖漏洞 | 升级依赖、替换废弃库、锁定安全版本 |
| 敏感信息 | Token、私钥、数据库密码、云厂商 AK/SK | 立即撤销泄露凭据,改用 Secret Manager 或 CI 密钥注入 |
| Dockerfile 和 IaC | root 用户运行、特权模式、暴露多余端口、危险挂载 | 调整 Dockerfile、Kubernetes YAML、Helm Chart、Terraform 配置 |
| SBOM | 镜像里到底包含哪些包、版本和来源 | 生成 CycloneDX 或 SPDX 清单,便于审计和追踪 |
| License | GPL、AGPL、商业限制许可证等 | 按公司策略拦截或人工确认 |
| 签名和来源 | 镜像是否来自可信构建流程,是否被篡改 | 使用 cosign 等工具签名和验签 |
如果团队只在发布前跑一次漏洞扫描,很容易漏掉两类问题:一类是密钥、配置和许可证风险,另一类是镜像发布后新披露的 CVE。因此扫描不应该只发生在构建阶段,还要覆盖仓库中的存量镜像和生产准入环节。
常用工具怎么选?
| 工具 | 特点 | 更适合的场景 | 注意点 |
|---|---|---|---|
| Docker Scout | Docker 官方工具,和 Docker CLI、Docker Hub、Docker Desktop 集成较好,能给出基础镜像更新建议 | 已经使用 Docker 官方生态,希望快速看到镜像风险和修复建议 | 企业级策略、跨平台治理能力要看套餐和使用方式 |
| Trivy | 开源,能扫镜像、文件系统、Git 仓库、Kubernetes 配置、IaC、Secret、SBOM、License | 中小团队、CI/CD 集成、想用一个工具覆盖多类风险 | 默认规则很多,建议按项目配置忽略规则和严重级别 |
| Grype | Anchore 开源漏洞扫描器,常和 Syft 搭配生成 SBOM | 想先生成 SBOM,再基于 SBOM 做漏洞分析的团队 | Secret、IaC 不是它的核心能力,需要配合其他工具 |
| Clair | 开源镜像漏洞扫描服务,适合接入镜像仓库 | 自建 Registry、需要服务化扫描能力 | 主要聚焦镜像包漏洞,落地和维护成本比 CLI 工具高 |
| Anchore | 企业级供应链安全平台,包含策略、SBOM、合规和治理能力 | 大型团队、多业务线、需要统一策略管理和审计 | 成本和平台建设复杂度更高 |
| Snyk | 商业服务,依赖漏洞、容器、IaC、许可证和开发者工作流体验较成熟 | 已经重度使用 GitHub/GitLab/Jira,希望把修复流转起来 | 扫描效果和可用功能与套餐相关 |
简单选型可以这样看:个人项目或小团队先用 Trivy;偏 SBOM 工作流可用 Syft + Grype;已经在 Docker Hub 和 Docker Desktop 里协作,可以先接 Docker Scout;需要企业级策略、审计和工单流转,再评估 Anchore 或 Snyk;自建镜像仓库并愿意维护扫描服务,可以考虑 Clair。
CI/CD 里怎么设置拦截规则?
安全扫描最怕两个极端:要么只出报告不拦截,大家慢慢无视;要么所有漏洞都拦,最后流水线天天红。比较稳的做法是分层处理。
可以先设置这些阈值:
- Critical 必须阻断发布,尤其是有可用修复版本、可远程利用、出现在运行路径上的漏洞。
- High 默认阻断,但允许短期例外;例外必须写明原因、负责人和过期时间。
- Medium 进入缺陷池,结合是否暴露在公网、是否运行在生产环境决定修复优先级。
- Secret 命中直接阻断,并且不能只删除代码,还要轮换已经泄露的密钥。
- License 命中按公司策略处理,例如 AGPL、未知许可证进入人工审批。
- 基础镜像过旧要提醒或阻断,例如超过 30-60 天没有重建,哪怕应用代码没变也要重新构建。
一个常见的 Trivy CI 命令大概是这样:
bashtrivy image --severity HIGH,CRITICAL --exit-code 1 your-image:tag
但生产里不要只靠一条命令。更合理的是把策略写进 CI 模板:哪些严重级别失败、哪些目录跳过、哪些漏洞暂时忽略、忽略到什么时候失效。这样规则才不会散落在每个仓库里。
如何处理误报和“看起来没法修”的漏洞?
容器扫描会遇到误报,尤其是发行版 backport 补丁。比如 Debian 或 Ubuntu 可能已经把修复补丁回合到旧版本包里,但版本号没有升到上游公告里的新版本,工具如果只看版本号,就可能误判。
处理方式不是简单把漏洞加入 ignore,而是先确认三件事:
- 漏洞对应的包是否真的存在于最终镜像层里;
- 漏洞代码路径是否会被应用调用;
- 发行版安全公告是否已经说明该版本已修复。
如果确实是误报,可以加入忽略文件,但要写清原因和过期时间。没有过期时间的忽略规则,最后往往会变成“永久免死金牌”。
还有一种情况是漏洞暂时没有修复版本。这时可以做风险缓解:移除不需要的包,关闭相关功能,限制容器权限,缩小网络访问范围,或者换一个维护更及时的基础镜像。
基础镜像更新和重建为什么重要?
很多团队以为应用代码没变,就不用重新构建镜像。实际不是这样。基础镜像里的 openssl、glibc、ca-certificates 等包会持续更新,旧镜像即使昨天还是安全的,今天也可能因为新披露的 CVE 变成高危。
建议把基础镜像治理纳入日常流程:
- 优先使用官方、可信、仍在维护的基础镜像;
- 尽量固定 digest,避免同一个 tag 在不同时间指向不同内容;
- 定期刷新基础镜像并重新构建业务镜像;
- 使用多阶段构建,最终镜像只保留运行所需文件;
- 能用 slim、distroless、alpine 时再用,不要为了小而牺牲兼容性和可维护性;
- 删除构建工具、包管理缓存和临时文件,减少攻击面。
修复漏洞不一定是“在容器里 apt upgrade 一下”。更推荐修改 Dockerfile 或基础镜像版本,然后重新构建、扫描、签名和发布,保证过程可追踪。
签名、准入控制和运行时扫描怎么配合?
镜像扫描解决的是“镜像里有什么风险”,签名解决的是“这个镜像是不是可信流程产物”,准入控制解决的是“能不能进入集群”。三者最好连起来。
常见链路是:
- CI 构建镜像;
- 生成 SBOM;
- 执行漏洞、Secret、IaC、License 扫描;
- 达到策略阈值后用 cosign 签名;
- 推送到镜像仓库;
- Kubernetes 准入控制检查签名、镜像来源、扫描结果和基础安全配置;
- 不满足条件的镜像禁止部署。
准入控制可以用 Kyverno、OPA Gatekeeper、Connaisseur 或云厂商自带策略能力。规则不必一开始写得很重,可以先从“禁止 latest 标签”“必须来自可信 Registry”“必须非 root 运行”“必须有签名”这些低争议规则开始。
运行时扫描也有边界。镜像扫描看不到容器启动后的异常行为,例如进程被注入、容器逃逸尝试、异常网络连接、运行时挂载了危险目录。这部分要靠 Falco、eBPF Runtime Security、Kubernetes Audit、云安全产品或主机入侵检测来补齐。
所以,容器安全不是某一个工具的事。镜像扫描负责提前发现已知风险,CI 策略负责把风险挡在发布前,签名和准入控制负责防止不可信镜像进入环境,运行时监控负责发现上线后的异常行为。把这几步串起来,才算真正把 Docker 容器安全扫描落到了工程流程里。