Fleet 的 CVE 响应与漏洞管理实践:Trivy 扫描、OpenVEX 状态跟踪与修复流程
Fleet 的 CVE 响应与漏洞管理实践Trivy 扫描、OpenVEX 状态跟踪与修复流程【免费下载链接】fleetOpen device management项目地址: https://gitcode.com/GitHub_Trending/fl/fleet本文以 security/README.md 为主体完整解读 Fleet 开源仓库中「漏洞扫描 → CVE 评估 → OpenVEX 状态声明 → 自动报告生成 → 发布门禁」的安全运营闭环包括security/目录的组织方式、五个每日/发布期漏洞扫描 CI 工作流的职责划分、使用vexctl编写 affected / not_affected / fixed 状态声明的可复制命令以及 CRITICAL 级漏洞的处置 SLA。读完后你可以理解 Fleet 如何用 OpenVEXVEX标准化管理 Trivy 扫描结果并在自己的 Go 项目中复刻类似的漏洞跟踪流程。一、security/目录漏洞管理的三件套Fleet 将所有安全相关的扫描配置与漏洞跟踪数据集中在 security/ 目录下结构如下路径作用security/status.md当前 Fleet 软件组件被安全扫描器Trivy、Docker Scout报告漏洞的最新状态报告。该文件完全自动生成来源是vex/目录下的 VEX 文件security/code/用于对 Fleet 源码做漏洞/秘密扫描的配置文件security/vex/OpenVEX 文件用于声明 Trivy 在 Fleet Docker 镜像中检测到的漏洞的状态vex/下又按三个发布产物拆分为三个子目录security/vex/fleet/对应fleetdm/fleetDocker 镜像Fleet 服务端当前存放了 30 个CVE-*.vex.json文件security/vex/fleetctl/对应fleetdm/fleetctl镜像Fleet 的 CLI 工具security/vex/wix/对应fleetdm/wix镜像。该镜像被fleetctl可执行文件用来生成 Windows 平台的 MSI 版 fleetd 安装包。security/status.md 的文件头明确标注了DO NOT EDIT它是运行make vex-report时重新生成的详见第四节。从当前仓库快照看该报告中绝大多数条目160 余个的声明状态是not_affected说明 VEX 机制的主要价值之一是把扫描器的误报逐条转化为有依据的书面结论而不是一味升级依赖。二、漏洞扫描五个 CI 工作流构成的覆盖矩阵security/README.md列出了五个每日对 Fleet 软件组件做漏洞扫描的 GitHub Actions 工作流它们分别覆盖「源码」和「三个 Docker 镜像」且严重程度阈值各不相同工作流扫描对象严重程度阈值触发时机.github/workflows/trivy-scan.yml源码仓库fs 模式夜间任务覆盖 CRITICAL~LOWpush/PR仅秘密扫描 每日 04:00 UTC.github/workflows/build-fleetdm-fleetctl-check-vulnerabilities.ymlfleetdm/fleetctlDocker 镜像HIGH、CRITICAL手动 每日 06:00 UTC.github/workflows/check-wix-vulnerabilities.ymlfleetdm/wixDocker 镜像HIGH、CRITICAL手动 每日 06:00 UTC.github/workflows/goreleaser-snapshot-fleet.yamlfleetdm/fleetDocker 镜像HIGH、CRITICAL新 release 推送到 Docker registry之前.github/workflows/check-vulnerabilities-in-released-docker-images.yml最近 5 个 minor 版本的fleetdm/fleet 最新版fleetdm/fleetctlCRITICAL手动 每日 06:00 UTC源码扫描trivy-scan.yml 的双模式设计.github/workflows/trivy-scan.yml 是一个值得细看的设计它按事件类型区分了扫描深度PR / push**.tf路径变更时只开secret扫描器、exit-code: 1目的是在合并前阻止凭据泄漏每日调度 / 手动触发开vuln,secret双扫描器输出 SARIF 并上传到 Security 标签页供人工分诊exit-code: 0不阻断。工作流中的注释明确说明CVE 是夜间跟踪而非逐 PR 跟踪misconfig 扫描被有意排除。更特别的是它的「RC 分支发现」逻辑夜间任务除了扫main还会列出所有严格匹配rc-(minor|patch)-fleet-vX.Y.Z命名模式、且版本号大于最新已发布版本的分支即在途的 minor/patch 发布分支对每个分支都跑一遍扫描从而保证即将发布的代码在发布前也被覆盖。扫描使用了仓库内的两份压制配置TRIVY_SECRET_CONFIG: ./security/code/trivy-secret.yamltrivyignores: ./security/code/.trivyignore。.github/workflows/trivy-scan.yml 中 Trivy 以scan-type: fs运行DB 指向公开 ECR 仓库以加速拉取。已发布镜像扫描带 VEX 过滤的 CRITICAL 门禁.github/workflows/check-vulnerabilities-in-released-docker-images.yml 是「存量镜像」的守护者其关键步骤用仓库自带工具列出最近 5 个 minor 版本go run ./tools/github-releases --last-minor-releases 5然后docker pull这 5 个版本的fleetdm/fleet镜像把 security/vex/fleet/ 下每个文件展开成逐个的--vex./security/vex/fleet/file参数Trivy 要求每个 VEX 源单独传一个--vex标志逗号合并会被当成一个路径。工作流注释解释了为什么直接调用trivy命令而不是aquasecurity/trivy-actionaction 当时不支持加载 VEX 文件且难以对多个镜像分别执行对每个镜像执行./trivy image --exit-code1 --pkg-typesos,library --severityCRITICAL \ VEX_FLAGS --formatjson --outputtrivy-results-fleet-$version.json \ fleetdm/fleet:$version即只拦截 CRITICAL 级且已被 VEX 声明「not affected / fixed」的 CVE 会被过滤掉对fleetdm/fleetctl:latest做同样的 CRITICAL 扫描VEX 来源为 security/vex/fleetctl/失败时用jq从 JSON 结果中提取 CVE 列表含镜像、包名、已安装/修复版本通过 Slack Webhook 推送告警且仅在schedule事件失败时推送避免手动调试时打扰。fleetdm/fleetctl与fleetdm/wix的两个工作流结构类似HIGH/CRITICAL 阈值--ignore-unfixed区别在于它们先make fleetctl-docker/make wix-docker本地构建镜像再扫描也支持手动触发时改扫已发布的镜像。而 goreleaser-snapshot-fleet.yaml 中的扫描则嵌在发布流水线里作为发布前的门禁对即将推送的fleetdm/fleet:tag镜像跑 Trivy同样带--vex参数不干净就不发布。三、CVE 报告后的标准处置流程3.1 四种状态与 OpenVEX当 Trivy通过上述工作流在某个 Fleet Docker 镜像上报告HIGH或CRITICALCVE 时团队需要评估并把结论记录为四种状态之一not affected不受影响affected受影响fixed已修复under investigation调查中状态声明采用OpenVEX 格式存放于vex/目录并统一用 vexctl 中直接看到{ context: https://openvex.dev/ns/v0.2.0, author: lucasmrod, statements: [ { vulnerability: { name: CVE-2024-8260 }, products: [ { id: fleet }, { id: pkg:golang/github.com/open-policy-agent/opa } ], status: not_affected, status_notes: Fleet doesnt run on Windows, so its not affected by this vulnerability., justification: vulnerable_code_cannot_be_controlled_by_adversary } ] }要点products用id标识受影响对象可以是产品名fleet/fleetctl/wix、CPE 或 PURLjustification取 OpenVEX 标准枚举值如vulnerable_code_cannot_be_controlled_by_adversarystatus_notes写人类可读的评估依据。3.2 A 流程「affected」→「fixed」两段式声明CVE-2025-27509 实例security/README.md以 CVE-2025-27509 为例该 CVE 当时影响所有已发布 Fleet 版本。第 1 步创建 affected 状态。由于 OpenVEX 当时不支持版本区间必须把每个受影响的版本逐一写成 CPE 列出来。仓库为此提供了tools/github-releases工具all_fleet_releases$(go run ./tools/github-releases --all-cpes --separator,) vexctl create --product$all_fleet_releases \ --vulnCVE-2025-27509 \ --statusaffected \ --aliasesGHSA-52jx-g6m5-h735对应 GitHub security advisory \ --action-statementDisable SAML SSO authentication. \ --authorlucasmrod security/vex/fleet/CVE-2025-27509.vex.json在真实的 security/vex/fleet/CVE-2025-27509.vex.json 中可以看到这条 affected 声明的products列表从cpe:2.3:a:fleetdm:fleet:v3.3.0:...一直枚举到v4.64.1共 130 余个版本action_statement给出了临时缓解措施「禁用 SAML SSO 认证」。第 2 步修复发布后追加 fixed 声明。修复先后随v4.64.2、v4.63.2、v4.62.4、v4.58.1、v4.53.2这几个维护版本发布后对同一份VEX 文档原地追加一条fixedstatementvexctl add \ --document./security/vex/fleet/CVE-2025-27509.vex.json \ --vulnCVE-2025-27509 \ --statusfixed \ --productcpe:2.3:a:fleetdm:fleet:v4.64.2:*:*:*:*:*:*:*,cpe:2.3:a:fleetdm:fleet:v4.63.2:*:*:*:*:*:*:*,cpe:2.3:a:fleetdm:fleet:v4.62.4:*:*:*:*:*:*:*,cpe:2.3:a:fleetdm:fleet:v4.58.1:*:*:*:*:*:*:*,cpe:2.3:a:fleetdm:fleet:v4.53.2:*:*:*:*:*:*:* \ --aliasesGHSA-52jx-g6m5-h735对应 GitHub security advisory \ --in-place追加后文档的version字段从 1 递增到 2两条 statement 共存于同一 JSON 中。这一设计让「哪些版本受影响、哪些版本已修复」在时间线上完整可追溯——security/status.md 中该 CVE 的条目也正是按时间倒序渲染出fixed新与affected旧两个 Statement 块的。3.3 B 流程「not affected」声明两条真实案例当扫描报告的 CVE 经评估不影响产物时用not_affected状态加具体理由落盘。README 给出两个实例例 1github.com/goreleaser/nfpm/v2的 CVE-2023-32698已知不影响fleetdm/fleetctl对应文件 security/vex/fleetctl/CVE-2023-32698.vex.jsonvexctl create --productfleetctl,pkg:golang/github.com/goreleaser/nfpm/v2 \ --vulnCVE-2023-32698 \ --statusnot_affected \ --authorgetvictor \ --justificationvulnerable_code_cannot_be_controlled_by_adversary \ --status-noteWhen packaging linux files, fleetctl does not use global permissions. It was verified that packed fleetd package files do not have group/global write permissions. security/vex/fleetctl/CVE-2023-32698.vex.json例 2github.com/open-policy-agent/opa的 CVE-2024-8260已知不影响fleetdm/fleet对应文件 security/vex/fleet/CVE-2024-8260.vex.jsonvexctl create --productfleet,pkg:golang/github.com/open-policy-agent/opa \ --vulnCVE-2024-8260 \ --statusnot_affected \ --authorlucasmrod \ --justificationvulnerable_code_cannot_be_controlled_by_adversary \ --status-noteFleet doesnt run on Windows, so its not affected by this vulnerability. security/vex/fleet/CVE-2024-8260.vex.json值得注意的一个细节同一个 CVE 可能需要在多个产品下各写一条声明。CVE-2025-27509 影响fleet服务端但 Trivy 扫fleetctl镜像时也会因共享 Go 依赖而报出它因此 security/status.md 的 fleetctl 部分额外存在一条not_affectedjustification 为component_not_present声明用来消除跨镜像的误报。--product参数接受「产品名 PURL」的组合README 给出的 PURL 示例Debian 包liblzma5pkg:deb/debian/liblzma5Go 包github.com/goreleaser/nfpm/v2pkg:golang/github.com/goreleaser/nfpm/v2Java 包xerces/xercesImplpkg:maven/xerces/xercesImpl3.4 重新生成 status.mdmake vex-reportVEX 文件新增或更新后运行以下命令重新生成 security/status.mdmake vex-report从 Makefile 可以看到该目标的实现先写入文件头与# Vulnerability Report标题然后依次对security/vex/fleet、security/vex/fleetctl、security/vex/wix三个目录执行go run ./tools/vex-parser dir并追加到 status.md——这解释了为什么报告固定分为fleetdm/fleet、fleetdm/fleetctl、fleetdm/wix三个 Docker 镜像小节。3.5 两个配套工具vex-parser 与 github-releases这两个仓库内工具是上述流程的关键支撑源码很短值得直接读tools/vex-parser/vex-parser.go把目录下的*.vex.json翻译成 Markdown。它会校验同一文件内所有 statement 必须指向同一个 CVEL55-L60不一致直接报错把 statement 按时间戳倒序排列L62-L66并输出 Author、Status、Status notes、Products、Justification、Action statement、Timestamp 字段——与 security/status.md 中渲染出的条目格式一一对应。若目录为空则输出 No vulnerabilities tracked at the moment.。tools/github-releases/github-releases.go从 GitHub Releases API 分页拉取全部 Fleet 发布版本过滤orbit-前缀的 agent 发布、规范化fleet-/Fleet前缀与v前缀再按 semver 排序L46-L77。--all-cpes把每个版本渲染成cpe:2.3:a:fleetdm:fleet:version:*:*:*:*:*:*:*形式L86-L88--last-minor-releases N则取最新 patch 及最近 N 个 minor 的最高 patch 版本L94-L108二者互斥。已发布镜像扫描工作流中的「最近 5 个 minor 版本」正是--last-minor-releases 5的输出。四、更新软件与 CRITICAL 漏洞处置 SLA4.1 通过更新基础镜像/组件修复README 指出若漏洞可以通过升级 Docker 基础镜像或增删/替换镜像内组件修复就直接做修复自然进入下一个 release。保持软件常新本身也是良好实践。4.2 「affected」CRITICAL 漏洞的处置流程当CRITICALCVE 影响最近 5 个 release 的fleetdm/fleet镜像由已发布镜像扫描工作流报告时用扫描器报告的信息更新security/status.md持续向用户/客户通报若该 CRITICAL 漏洞且有可用修复位于latestrelease登记为 critical/P0 缺陷1 个工作日内发布补丁。其余被扫描到的 4 个历史版本不做回溯补丁只修latest。当CRITICALCVE 影响已发布的fleetdm/fleetctl:latest镜像时更新security/status.md后通知用户/客户该 CVE 及可能的缓解措施创建带P0/security标签的 issue 跟踪修复修复随fleetdm/fleetctl镜像的下一个 release 发布。五、误报压制trivy-secret.yaml 与 .trivyignore源码扫描能长期可用很大程度依赖两份「显式白名单」配置它们同样存放在security/code/下security/code/trivy-secret.yaml秘密扫描的allow-rules。设计原则在文件头注释中写得很清楚每个被压制的文件都显式列出测试/开发用私钥tools/osquery/fleet.key、tools/test-certs/**等仅按精确路径放行而生产文件必须同时给出内容正则保证同文件里新出现的真实凭据仍能触发告警。例如对cmd/fleet/serve.go中硬编码的--dev_license开发用 JWT、前端与 Sails 站点配置里的占位 Stripe/SendGrid 密钥都是「路径 内容正则」双重约束。security/code/.trivyignore仅压制 2 个 CVE且每条都附了人工判断理由——CVE-2020-7753被 trim 掉、实际未使用的漏洞代码与 CVE-2020-28469利用需已登录团队评估为低概率低影响不值得仅为它升级 glob-parent。六、Troubleshootingtrivy 的平台差异README 最后提醒了一个实操陷阱在 macOS 与 Linux 主机上分别执行trivy image报告的 CVE 可能存在差异差异主要来自镜像内fleet/fleetctl可执行文件由gobinary分析出的结果。遇到拿不准的情况请在 Ubuntu 主机上运行 trivy以对齐运行在ubuntu-*runner 上的 CI 结果——这也是为什么所有镜像扫描工作流的runs-on都固定为 Ubuntu如 check-vulnerabilities-in-released-docker-images.yml 中的ubuntu-22.04。七、小结这套机制的工程价值把security/README.md的流程与仓库内的真实产物对照起来可以看到一条完整的证据链扫描5 个工作流分别覆盖源码、fleet/fleetctl/wix镜像PR 门禁secret、夜间巡检vulnsecret、存量镜像 CRITICAL、发布门禁HIGH/CRITICAL三层防线评估每个 HIGH/CRITICAL CVE 必须落为 affected / not_affected / fixed / under investigation 之一的 OpenVEX 声明not_affected必须附 justification 与 status_notesaffected必须附缓解措施action_statementfixed用vexctl add --in-place在同一文档追加发布make vex-report tools/vex-parser 把 VEX 声明渲染为人类可读的 security/status.md 对外通报同时--vex参数把同一份声明喂回 Trivy让 CI 不再为已评估的 CVE 反复告警。对希望在自己的 Go/Docker 项目中建立同类机制的团队可以直接借鉴的三个「开箱即用」组件是tools/github-releases 的版本枚举与 CPE 生成、Makefile中vex-report目标的三段式报告拼接Makefile以及「路径 内容正则」双约束的 trivy-secret.yaml 白名单写法。【免费下载链接】fleetOpen device management项目地址: https://gitcode.com/GitHub_Trending/fl/fleet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考