容器镜像漏洞扫描的阻断策略,核心不是扫描本身,而是把“发现漏洞”变成“阻止上线”的硬性门槛在CI/CD流水线中设置自动化阻断点,让高危镜像根本无法进入生产环境。
镜像漏洞扫描的阻断策略应该设在哪一层
很多团队一开始把阻断策略放在镜像仓库,也就是Harbor或者Nexus里配置漏洞扫描规则,这个位置有作用,但有个先天短板扫到问题的时候镜像已经推上去了,阻断只能阻止后续的pull或deploy,没法阻止最初的push,行业共识认为,真正有效的阻断点必须前置到CI阶段,也就是构建镜像之后、推送镜像之前。
具体到流水线里的位置,大致有三个候选点:
- CI构建完成后:此时还没push,阻断代价最低,改代码重跑就好
- 镜像推送前:CI里需要先扫描再push,但这样每次构建都多一步,耗时明显
- 部署准入阶段:K8s的Admission Controller做拦截,但漏洞已经躺在仓库里了
实操中最合理的组合是CI阶段做严重漏洞阻断 + 部署阶段做策略兜底,CI负责挡第一道门,K8s的Kyverno或OPA Gatekeeper负责挡第二道门,防止有人绕过CI直接部署,这一步用搜索引擎查“镜像漏洞扫描失败阻断部署”,出来的一大半方案都是这个思路。
harbor镜像扫描阻断策略怎么设置
Harbor是目前使用率相当高的镜像仓库,它自带漏洞扫描能力,当你要在Harbor里配置阻断,其实是在配置两件事:扫描策略和镜像推送策略。
Harbor的阻止推送配置
在Harbor的Project配置里,有一个“阻止推送包含高危漏洞镜像”的开关(Prevent images with vulnerability severity from being pushed),开启之后,客户端执行docker push时,Harbor会实时检查该镜像的漏洞报告,如果严重级别到达你设定的阈值(Critical或High),就拒绝这次推送。
配置路径是:项目 → 配置 → 部署安全 → 勾选“阻止推送具有高危漏洞的镜像”,然后在下面的下拉框里选择从哪个漏洞级别开始阻止,默认是Critical,一般建议选High,因为相当一部分企业的内部标准是High以上不准进仓库。
扫描时机和触发方式
Harbor的扫描有手动触发和定时触发两种,如果只靠手动,开发推完镜像忘了点扫描,阻断策略就形同虚设,建议开启推送后自动触发扫描,在Configuration → Vulnerability Scanning里勾选“Scan images on push”。
但这里有个关键问题:Harbor只有在镜像成功推送之后才会触发扫描,而阻断策略是阻止“下一次”推送,也就是说,第一次推高危镜像的时候,Harbor是拦不住的,因为还没有漏洞报告,实际中这里容易有误解,很多人以为Harbor会拦截第一次推送,其实它只能拦截第二次。
要解决第一次推送就漏网的问题,需要结合CI侧扫描,在GitLab CI或Jenkins里先跑一轮扫描,高危直接让job失败,就不会触发docker push了。
gitlab ci镜像扫描失败怎么办
GitLab CI跑镜像扫描,最常见的方案是Trivy或者Clair作为扫描器,在.gitlab-ci.yml里加一个stage,如果扫描器报出了高危漏洞,你会看到job的exit code不是0,pipeline卡在镜像构建之后的那个stage。
三种处理方式
- 直接fail pipeline:最严格的做法,脚本里设定
--severity HIGH,CRITICAL --exit-code 1,scan job返回非零,pipeline自动红掉 - 只阻断master分支:开发分支只告警不阻断,防止阻塞日常迭代;合并到master时严格卡关,用
rules或only/except控制 - 生成报告人工确认:扫描结果作为artifacts归档,由安全负责人确认后手动触发后续stage,适合漏洞很多但业务急切的过渡期
实操示例是这样一段脚本:
image_scan:
stage: test
script:
- trivy image --severity HIGH,CRITICAL --exit-code 1 --no-progress $IMAGE_TAG
rules:
- if: '$CI_COMMIT_BRANCH == "master"'
when: always
- if: '$CI_COMMIT_BRANCH'
when: manual
这段配置的思路是master分支强制扫描,其他分支手动触发扫描,如果你在搜索引擎里搜“gitlab ci镜像扫描失败怎么办”,排在前面的结果基本都会推荐类似的写法,核心就是通过--exit-code控制失败状态。
失败后的修复链路
扫描失败之后,不能只让pipeline红着等开发自己看,要有明确的处理链条:
- 开发先在本地用
trivy image或docker scan复现漏洞详情 - 判断漏洞属于基础镜像自带还是应用层引入
- 基础镜像的漏洞,优先升级基础镜像版本
- 应用层漏洞,更新依赖版本或者加补丁层
- 无法立即修复的,在issue里记录豁免理由和时限,由安全负责人批准
这个流程里最重要的是不要默认所有阻断都走豁免,豁免一多,阻断策略就变成了摆设。
容器镜像漏洞扫描的阈值怎么定才不误伤
阈值设置是阻断策略里争议最大的部分,设成Critical以下全放行,等于没设;设成Medium也阻断,开发一天能骂八回。
分级阈值的实践经验
行业内的通行做法是两层阈值:
- Critical漏洞:无条件阻断,没有任何商量余地
- High漏洞:默认阻断,但提供白名单豁免通道
- Medium和Low:只记录不阻断,定期汇总review
有些团队比较激进,把所有High以上全部阻断,但如果你接手的是存量较大的老项目,建议先跑一轮基线扫描看看现状,一位长期做容器安全的专家指出,很多存量镜像第一次扫描能查出几十个High漏洞,一下全阻断会让业务完全停摆,比较务实的做法是先设Critical阻断,High只警告,运行三个月后再收紧到High阻断。
白名单的粒度控制
白名单不是一刀切,要按漏洞维度配置,比如某个特定CVE编号的漏洞,因为当前没有修复版本,业务方可以申请临时豁免。
这里容易踩坑的是通配符白名单,有人图省事,直接按镜像tag前缀加白名单,比如prod-全放行,这个操作相当于把生产环境的镜像全部开了绿灯,阻断策略等于没有,白名单应该细化到CVE ID + 镜像tag的组合维度,并且每条白名单都要有过期时间。
扫描器的误报率差异
不同扫描器的误报率差距相当大,Trivy的漏洞库来自GitHub Advisory,覆盖广但偶尔会把只影响特定发行版的漏洞报给所有镜像,Anchore的规则库更新稍微滞后,但误报率低很多。
实际选择原则是:以主扫描器为准,用辅助扫描器做交叉验证,如果Trivy和Grype同时报同一个CVE,基本可以确认是真实漏洞;如果只有其中一家报,先看漏洞详情里的影响版本范围是否匹配当前镜像。
镜像扫描阻断和K8s部署准入怎么联动
光在CI里阻断还不够,因为总有开发通过其他方式把镜像推进来,或者直接在集群里用kubectl run创建Pod,所以集群级的准入控制是阻断策略的最后一道防线,也是很多人容易忽略的环节。
Kyverno的镜像漏洞阻断写法
Kyverno是云原生计算基金会(CNCF)旗下的策略引擎,可以用一段很短的策略来做镜像校验,核心思路是:Pod创建时,Kyverno到镜像仓库查询该镜像的漏洞扫描结果,如果发现高危漏洞,拒绝Pod创建。
策略里最关键的是一个叫做imageRegistry的规则,它可以向Harbor或Docker Registry发起API查询,检查镜像的tag、digest和漏洞状态,实际配置中,只用校验严重级别和仓库地址,不要校验digest,否则每次镜像更新都要同步改策略。
阻断策略的回滚和逃生通道
准入控制拦住Pod之后,还必须预留逃生通道,比如线上出了紧急事故,需要立刻用某个旧镜像恢复服务,而该镜像恰好有High漏洞,完全拦住会让故障时间无限拉长。
推荐的做法是给策略加一条exclude规则,排除特定命名空间(比如emergency)或者特定标签(比如label: allow-vuln=true),这个逃生通道平时不用,但出事的时候能救命你在搜索引擎搜“k8s部署镜像扫描阻断配置”,你会发现不少团队的实际配置里都有这个细节,因为它防的是真实事故,不是理论场景。
与镜像签名体系的配合
准入控制还可以和镜像签名体系叠加,相当于给镜像加了一把“数字锁”,具体流程是:CI扫描通过后,用cosign给镜像签名;K8s集群只放行有有效签名的镜像,这样即使有人手动推了一个没有扫描记录的高危镜像,也会因为缺签名而被拒绝。
签名和漏洞扫描的组合,本质上让阻断策略从结果检查变成了源头信任,据行业数据,部署了这层组合策略的团队,因镜像漏洞导致的生产事故数量明显下降。
镜像漏洞扫描和依赖扫描的区别
阻断策略执行不彻底,还有一个隐藏原因:把镜像漏洞扫描和依赖扫描混为一谈。
镜像漏洞扫描查的是镜像里实际安装的软件包和操作系统库文件,比如OpenSSL版本、glibc版本、Nginx版本,扫描对象是文件系统层面凭空多出来的那一层,依赖扫描查的是源码或构建产物里的第三方库,比如npm的package.json、pip的requirements.txt、Go的go.mod,两者覆盖范围有重叠,但不完全一致一个漏洞可能只在你自己的代码依赖里,而镜像扫描根本扫不到它。
正确的配置方式是在CI里同时跑两类扫描,并行执行,任一失败都阻断,这样做的原因是,镜像扫描是静态的,构建产物里的依赖一旦被编译进去,镜像扫描看到的是编译后的文件,未必能还原出原始的依赖版本信息。
容器镜像漏洞扫描阻断策略设置常见问题
镜像扫描被阻断后,开发团队要怎么快速定位是哪个漏洞导致的
最简单的方法是直接看CI日志,以Trivy为例,日志里会列出漏洞的CVE编号、严重级别、受影响的包名和当前版本号,拿到CVE编号后,到NVD或GitHub Advisory页面搜索,就能看到修复版本号,如果CI配置了报告上传功能,直接在制品库里下载扫描报告,按严重级别排序即可。
高危漏洞阻断后,是否有办法临时放行一次
有,但必须走流程而不是改配置,在Harbor的项目设置里临时调低威胁级别阈值,或者在GitLab CI里把这个job改成allow_failure: true跑一次,都属于临时放行,关键是放行记录要留痕,修复期限要明确,到期后自动恢复阻断。
为什么镜像扫描已经提示有漏洞,但推送时没有被拦截
最可能的原因是Harbor的阻止推送策略是在“镜像已有扫描结果”的前提下生效的,如果你推送的是一个新tag,Harbor还没来得及扫描,阻断策略就无法生效,无论是用Harbor自带扫描还是外部扫描器,确保推送前已完成一次扫描并得到结果,是让阻断策略真正起作用的前提。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/640252.html




