1. 为什么是Dependency-Track:从一次性扫描到持续治理
先说个真实场景。上个月有个研发团队找到我,说他们上线前被安全部门卡住了,原因是第三方依赖库里的漏洞报告一直无法闭环——OWASP Dependency-Check扫出来的问题没人跟进,下次扫描又全部冒出来,开发说“我改了这版依赖”,安全说“报告里没体现”,最后只能靠截图和Excel表格来回拉扯。
我给他们部署了一套Dependency-Track,两周后这个问题基本消停了。Dependency-Track是一个开源的软件组件分析平台,核心思路非常直接:它不站在“扫描一次”的角度工作,而是以软件物料清单(SBOM)为中心,帮你做持续性的安全治理。开发把当前版本的依赖清单交上去,平台负责入库、关联漏洞库、持续监控新披露的漏洞、跟踪审计状态,并把结果通知给相关人员。
和常见依赖扫描工具做个对比,差异就很明显:
| 对比项 | Dependency-Check | Dependency-Track |
|---|---|---|
| 工作方式 | 构建时扫描,出报告即结束 | 平台化持续分析,记录每次快照 |
| 漏洞跟踪 | 无状态,下次扫描重新开始 | 有状态,标记审计结论、修复进度 |
| 数据输入 | 自动探测项目依赖 | 接收SBOM、自动检测,也可手工管理 |
| 团队协作 | 单机报告 | 多用户、权限、通知、策略管理 |
| 生态扩展 | 插件形式 | REST API + 平台级集成 |
一句话概括:Dependency-Check是“安全体检”,Dependency-Track是“健康档案”。如果你的团队只是偶尔看一下依赖有没有问题,前者够用;如果你需要应对合规审计、漏洞闭环、供应链安全治理,那Dependency-Track这个层级才是真正需要的。
这篇文章就基于我实际部署和使用的经历,把从环境搭建、CI/CD接入、SBOM分析遇到的核心问题到团队落地经验一次性讲透。适合正在选型或已经部署但用不起来的同学。
2. 从零搭建:Docker Compose部署与初始配置
2.1 部署方式选型
Dependency-Track官方提供了多种部署方式,包括Docker Compose、Kubernetes Helm Chart、以及传统的WAR包部署。个人建议直接上Docker Compose,理由有三点:
- 官方维护了一套配置好的多容器编排,前端(前端服务)、API服务、数据库服务的关系已经理清,不需要自己去折腾环境变量对接。
- 升级方便,拉新镜像重启即可,备份也简单。
- 对大多数团队来说,单节点部署完全够用——它不需要ES那套复杂组件,默认用的数据库是内置的H2,也可以切换成PostgreSQL或SQL Server。
下面是生产环境推荐的结构图(文字版):浏览器请求通过Nginx进入前端容器,前端容器通过反向代理访问API服务,API服务连接数据库和各类漏洞数据源。所有组件通过docker-compose统一管理。
2.2 实际部署步骤
部署前先看看机器配置。官方建议的最低配置是4核8GB内存,但我强烈建议给8核16GB以上。很多人忽视了内存问题,后面同步NVD漏洞库时直接OutOfMemory,这是最常踩的坑,后面专门说。
部署过程分四步:
第一步,准备docker-compose.yml。官方仓库里有现成的(docker-compose.yml),也可以自己写一个精简版本。我用的配置类似这样:
version: '3' services: dtrack-frontend: image: dependencytrack/frontend:4.11.6 restart: unless-stopped depends_on: - dtrack-apiserver ports: - "8080:8080" dtrack-apiserver: image: dependencytrack/apiserver:4.11.6 restart: unless-stopped environment: - ALPINE_DATABASE_MODE=external - ALPINE_DATABASE_URL=jdbc:postgresql://dtrack-db:5432/dtrack - ALPINE_DATABASE_DRIVER=org.postgresql.Driver - ALPINE_DATABASE_USERNAME=dtrack - ALPINE_DATABASE_PASSWORD=change_me_to_a_strong_password - ALPINE_HTTP_PORT=8081 depends_on: - dtrack-db volumes: - dtrack-data:/data dtrack-db: image: postgres:16-alpine restart: unless-stopped environment: - POSTGRES_DB=dtrack - POSTGRES_USER=dtrack - POSTGRES_PASSWORD=change_me_to_a_strong_password volumes: - dtrack-db-data:/var/lib/postgresql/data volumes: dtrack-data: dtrack-db-data:第二步,启动容器:
docker-compose up -d第三步,打开前端页面,地址是http://你的服务器IP:8080,初始账号是admin,密码是admin。登录后系统会强制要求修改密码,这是官方的安全策略,不要跳过。
第四步,强烈建议开启TOTP双因素认证。Dependency-Track支持基于时间的一次性验证码,在个人账号设置里扫描二维码绑定即可。安全团队的对账审计会要求账号必须有双因素,提前做好能省不少事。
2.3 初始配置的关键细节
有几个配置项在部署的时候就要想清楚,不然后面改起来麻烦:
一是数据库密码。不要在默认compose文件上直接跑,改成强密码是底线。如果是在云服务器上部署,建议把数据库端口完全关闭,只允许内部网络访问API容器。
二是API服务的端口映射。前端默认8080,API服务默认8081。前端页面访问的不是API端口,它自己会做代理,所以正常使用只需暴露8080。但后面做CI/CD集成时,API请求直接走8081会更清晰,建议在安全组里只允许公司内部IP访问8081。
三是数据目录的持久化。API容器里的/data目录存了漏洞数据库索引、上传的SBOM文件、任务日志,数据库容器里的数据文件存了所有业务数据。这两个volume一定要保留好,容器没了可以重建,数据没了就很被动。生产环境建议用云盘或NFS,避免宿主机磁盘损坏导致数据丢失。
四是升级策略。Dependency-Track的版本迭代不算慢,但其间涉及数据库schema迁移,升级前务必做一次完整备份。它没有自动备份功能,我是写了一个cron脚本,每天凌晨用docker exec导出PostgreSQL的数据,保留最近14天的备份文件,实测恢复过两次,都很顺利。
3. 接入研发流程:CI/CD集成与API调用
3.1 设计思路:生成SBOM,而不是直接扫描
很多人第一次用Dependency-Track,第一反应是“它怎么像Dependency-Check那样扫我项目?”这里有个关键的设计理念需要转变:Dependency-Track的核心输入是SBOM,而不是项目源码。它不直接扫描代码,它消费的是描述“我这版本用了哪些组件、哪些版本”的结构化清单。
把SBOM生成和漏洞分析解耦的最大好处是什么?生成SBOM是编译期的事情,速度快、反馈及时;漏洞分析是平台侧的事情,可以后台慢慢跑,不会拖慢构建流水线,也不会因为NVD同步慢而让开发等结果。
所以在CI/CD里的正确姿势是:
- 构建阶段生成SBOM(格式选CycloneDX JSON)。
- 调用Dependency-Track API上传SBOM,绑定项目名称和版本。
- 平台异步处理,分析结果通过Webhook或后续查询反馈,也可以在发布前通过策略检查决定是否阻断。
3.2 生成SBOM的工具选型
不同技术栈生成SBOM的工具不一样,我这里列几个亲测好用的:
| 技术栈 | 推荐工具 | 说明 |
|---|---|---|
| Java/Maven | CycloneDX Maven Plugin | 执行mvn cyclonedx:makeAggregateBom生成整个多模块项目的完整清单 |
| Java/Gradle | CycloneDX Gradle Plugin | 用法类似,能处理依赖约束和BOM引入 |
| Node.js | @cyclonedx/cyclonedx-npm | 支持读取package-lock.json生成SBOM |
| Python | CycloneDX Python库或pip-audit的SBOM输出 | 虚拟环境里直接生成 |
| 通用容器镜像 | Syft(Anchore出品) | 一条命令扫描镜像,输出CycloneDX JSON,支持各种镜像格式 |
Syft几乎成了我团队的标准配置,因为现在服务大多是容器化部署,直接扫镜像最省事,不用管内部是什么语言:
syft packages your-registry/app:1.2.3 -o cyclonedx-json > sbom.json3.3 在GitLab CI里集成
以GitLab CI为例,一个完整的阶段可以这样设计。dependency-track阶段负责生成和上报SBOM,security-check阶段在发布前做策略判定:
generate-sbom: stage: dependency-track image: anchore/syft:latest script: - syft packages ${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG:-latest} -o cyclonedx-json > sbom.json artifacts: paths: - sbom.json expire_in: 1 week only: - tags upload-sbom: stage: dependency-track image: alpine:latest variables: DEPENDENCY_TRACK_URL: "http://dtrack.internal.example.com/api" DEPENDENCY_TRACK_API_KEY: "$DTRACK_API_KEY" script: - apk add --no-cache curl jq - | curl -sS -X "POST" "${DEPENDENCY_TRACK_URL}/v1/bom" -H "X-Api-Key: ${DEPENDENCY_TRACK_API_KEY}" -H "Content-Type: application/json" -d "{ \"projectName\": \"${CI_PROJECT_NAME}\", \"projectVersion\": \"${CI_COMMIT_TAG}\", \"bom\": \"$(cat sbom.json | base64 -w0)\", \"autoCreate\": true }" only: - tags这里有两个容易忽略的细节:
bom字段需要base64编码,不是直接传JSON。很多人第一次调API报400就是死在这个地方。autoCreate设置为true后,平台发现项目不存在会自动创建,省去手工建项目的步骤。但如果项目已经有固定归属团队,建议还是预先创建好,避免一人一个命名风格搞出很多重复项目。
上传之后,平台返回一串token,可以用来查询分析状态:
curl -sS -X "GET" "${DEPENDENCY_TRACK_URL}/v1/bom/token/你的token" -H "X-Api-Key: ${DEPENDENCY_TRACK_API_KEY}" | jq .status返回PROCESSING表示还没解析完,COMPLETED表示可以去看结果了。
3.4 API Key的管理权限
Dependency-Track的API Key分三种层级:管理员、发布经理、观察者。CI/CD里用的Key建议单独创建一个观察者或发布经理角色的Key,不要直接拿管理员Key到处用。在“管理-访问管理-团队”里新建团队,赋权后生成Key,Key本身很长,建议放到CI/CD的变量里统一管理,不要写死在代码仓库。
关于projectName的设计,我的建议是:应用名留CI_PROJECT_NAME即可,版本号用CI_COMMIT_TAG或构建号,这样每次发版都会形成一条独立的快照记录。以后安全团队说“v1.2.3这个版本有高危漏洞”,你在平台上点一下就能看到当时的完整依赖情况,这就是审计追溯的价值。
4. SBOM上传与漏洞分析的正确姿势
4.1 项目、版本与SBOM的对应关系
先理解Dependency-Track的数据模型:项目(Project)是顶层对象,一个项目下有多个版本(Project Version),每个版本关联一份或多份SBOM快照。漏洞分析的结果是挂在项目版本下面的。
所以正确用法不是每次上传都建新项目,而是按“应用名+版本号”上报。这样同一个应用的历史漏洞记录都在一个项目下,可以按版本切换查看。
我见过不少团队把每个构建版本都建一个独立项目,最后平台上一堆同名项目,看不到时间线,审计状态也串了。这就是数据模型没理解到位。
4.2 漏洞数据源与同步管理
平台能分析出漏洞,核心在于它聚合了多个漏洞数据源。管理界面(Administration-System Configuration)里可以配置数据源,包括:
- NVD(美国国家漏洞数据库):最基础的数据源,官方接口有速率限制,第一次同步特别慢,要耐心等。
- GitHub Advisories:开源生态的漏洞公告,覆盖很及时,但需要配置GitHub Token才能拉取。
- OSV:谷歌维护的开源漏洞数据库,覆盖面广,不少新披露的漏洞第一时间在这里出现。
- CISA KEV:已知被利用漏洞目录,用于优先级排序。
这些数据源不是一次性拉完就结束了,平台会按固定周期增量更新。默认的同步周期可以保留,生产环境建议NVD每天同步一次,GitHub Advisories每6小时一次,这样新漏洞披露到平台可查的窗口期不会太长。
同步状态可以在后台任务面板里看,如果哪个数据源一直失败,通常是网络代理问题或者Token失效,排查思路很直接:先看API服务日志里对应任务的报错,再确认出网是否有白名单限制。
4.3 分析页面怎么用
上传SBOM并完成分析后,主要看这几个页面:
- 项目视图:选中项目版本,能看到本次SBOM包含的组件总数、漏洞数、策略违例数。按风险等级(严重/高危/中危/低危)分布也在这里展示。
- 组件视图:点进某个组件,能看到它涉及的漏洞列表、CVE详情、CVSS评分、EPSS分数(利用概率评分),以及该组件在哪些项目里被使用。
- 漏洞视图:从漏洞维度看影响面,知道“这个CVE影响了我们哪几个项目”,用于评估紧急程度。
- 策略违例视图:如果你配置了策略(Policy),这里会显示哪些组件触发了限制性规则。
审计操作就在漏洞视图里做。打开一个漏洞,右侧面板里可以设置审计状态:
| 审计状态 | 含义 | 使用场景 |
|---|---|---|
| 存在(Vulnerable) | 确认受影响,需要修复 | 默认状态 |
| 不受影响(Not Affected) | 分析后确认实际不受影响 | 例如组件虽然匹配CVE但代码路径未用到 |
| 误报(False Positive) | 确认不是真实漏洞 | 例如CVE实际影响的是另一个语言生态的同名包 |
| 已修复(Resolved) | 该版本已验证修复 | 升级依赖后确认不再存在 |
加上注释后,审计状态会被记录,下次再有人看这个漏洞时就能看到结论。这就是闭环:开发说“这个组件实际没用到危险函数”,安全同学同意后标记为误报并附上分析说明,以后重复扫描不会再次报警。
实际操作下来,比较费时间的是刚接入平台第一周,旧项目的存量漏洞需要批量审计。好在平台支持多选组件后批量设置审计状态并添加统一注释,不需要逐条点。
4.4 保险起见:利用策略做发布防线
Dependency-Track的策略(Policy)能力值得单独说明。策略可以基于漏洞严重程度、组件名称、组件版本、CISA KEV标记等条件来定义“违规”。例如:
- 策略A:存在CISA KEV标记漏洞的组件,不允许发布。
- 策略B:存在CVSS评分9.0以上的严重漏洞,不允许发布。
- 策略C:禁止使用已EOL的组件版本。
配置策略后,CI里调用“策略违例查询”接口,就能拿到当前项目是否有阻断级的违规项,再决定流水线是否放行:
curl -sS -X "GET" "${DEPENDENCY_TRACK_URL}/v1/project/lookup?name=项目名&version=版本号" -H "X-Api-Key: ${DEPENDENCY_TRACK_API_KEY}" | jq -r '.uuid'拿到UUID后查询违例:
curl -sS -X "GET" "${DEPENDENCY_TRACK_URL}/v1/violation/project/项目UUID" -H "X-Api-Key: ${DEPENDENCY_TRACK_API_KEY}" | jq '[.[] | select(.type == "VULNERABILITY" and .severity == "CRITICAL")] | length'返回数量大于0就阻断。有的团队担心“阻断发布会拖慢交付”,我的建议是:刚开始策略先做成“审计视线”不加阻断,跑两个迭代看误报率,稳定后再从最关键的项目开启阻断。安全建设不是一次性到位,而是逐步收紧。
5. 日常运维中最容易翻车的几个点
5.1 内存不足导致NVD同步失败
这是Dependency-Track部署后最常见的故障。NVD全量数据同步时,API服务需要把大量CVE数据写入本地索引,如果JVM堆内存分配不够,直接抛出OutOfMemoryError,后台任务显示失败,前端页面可能出现组件漏洞信息缺失。
排查思路是:先看容器日志里有没有java.lang.OutOfMemoryError: Java heap space,如果有,基本可以锁定。解决办法有两个方向:一是给Docker分配更大内存,把宿主机内存从8GB升到16GB,同时确认API容器的JVM参数允许大堆内存;二是在/data目录下合理配置索引存储,不要让它和系统其他服务抢磁盘。
我给一个小团队的部署建议是:如果公司只是做几十个项目的依赖治理,单机16GB内存绰绰有余;如果上百个项目、SBOM上传频率高,考虑把所有组件迁到PostgreSQL、API服务独立部署、数据库交给云厂商托管那一套。
5.2 同步NVD走了代理,Token失效
很多公司内网服务器不能直连外网,NVD同步必须走代理。Dependency-Track本身没有在UI里直接配置代理的地方,需要在API服务的启动参数里设:
export JAVA_OPTIONS="-Dhttps.proxyHost=代理IP -Dhttps.proxyPort=代理端口"更隐蔽的问题是GitHub Token失效。平台每次同步GitHub Advisories,需要有效的Token。Token过期后后台任务报401,但UI上的“上次同步时间”看起来还正常,容易忽略。运维上我建议用公司统一的机器账号Token,加上告警:每天检查同步任务日志里有没有401/403报错,有就通知安全负责人。
5.3 上传SBOM后漏洞数量异常
可能出现“上传的SBOM明明很小,但平台分析出的组件数量翻了几倍”的情况。这种一般是SBOM里的components字段嵌套了元组件和子组件,比如Java的BOM引入传递性依赖,平台在解析CycloneDX时会把所有传递依赖全部展开。
这不一定是问题,但会导致审计工作量变大。如果业务上只关心直接依赖,可以在生成SBOM时关闭传递依赖解析(以CycloneDX Maven插件为例,设置includeTransitive为false),或者在上传前用jq过滤掉非直接依赖。权衡下来,我是建议保留传递依赖,因为不少高危漏洞恰恰藏在二级依赖里。
5.4 前端页面加载慢、卡顿
前端大量数据的表格渲染,在项目组件数量过万时确实会卡。除了清理浏览器的缓存,更有效的是开启API服务的压缩功能,在Nginx层配置:
gzip on; gzip_min_length 1k; gzip_types application/json application/javascript text/css image/svg+xml;另一个常见原因是平台自动刷新漏洞视图,每次切换项目版本都会重新拉全量数据。数据量很大的项目,建议通过左侧的过滤器缩小范围,不要一上来就全量加载“所有项目”。
5.5 备份和恢复
备份这事儿嘴上说再多不如实际演练一次。我是用cron脚本每天备份PostgreSQL数据库,具体流程是:
docker exec dtrack-db pg_dump -U dtrack dtrack > dtrack_$(date +%Y%m%d).sql注意/data目录下的索引文件不备份问题也不大,但数据库必须备份。恢复的时候,先把API和前端容器停掉,启动一个临时PostgreSQL容器导入SQL,再启动业务容器。整个过程我实测过,10分钟内能完成。
有一种情况比较麻烦:数据库备份是在业务还在运行的时候导出的,可能要恢复的瞬间有未提交事务,不过Dependency-Track不会有那么高写入频率,所以只要不是正在大量上传SBOM的窗口,正常备份恢复是可靠的。
6. 团队落地推广:从部署到真正被用起来
工具部署好只是第一步,真正难的是让团队成员接受并养成习惯。我在这几个项目里总结了一些适用经验。
6.1 从两个项目试点切入
不要试图一次性把所有项目都接入Dependency-Track。我的习惯是选两个代表性项目试点:一个是对外提供服务的核心应用,一个是内部工具类应用。试点时间定两个迭代周期,目标不是看漏洞数量,而是验证:
- CI里集成SBOM上传是否稳定。
- 开发人员能否看懂漏洞审计页面。
- 安全团队能否接收通知并处理告警。
试点通过后再批量接入其他项目,推广阻力会小很多。
6.2 通知规则要分层配置
Dependency-Track的通知机制支持通过Webhook、邮件等方式推送漏洞信息。这里最忌讳的是“所有漏洞都通知所有人”,那样很快就被当做垃圾消息忽略了。
我的配置经验是:
| 通知对象 | 漏洞级别 | 频率 |
|---|---|---|
| 应用负责人 | 高危/严重 | 实时Webhook到企业微信/钉钉群 |
| 开发团队 | 中危 | 日报汇总 |
| 安全团队 | 全部 | 实时或日报,视团队人力而定 |
| 管理层 | 严重漏洞+无法修复的漏洞 | 周报 |
细分下来,通知数量可控,也能保证漏洞出现后有人响应。
6.3 权限要分角色
默认的admin账号不建议给所有人用。Dependency-Track基于角色的访问控制支持自定义角色和映射。落地时我是这样分的:
- 安全团队:管理员权限,负责配置数据源、策略、分析审计。
- 研发负责人:项目的查看权限+审计权限。
- 普通开发人员:查看权限,可以标记误报,但不能修改策略配置。
这样既保证开发能参与漏洞闭环,又避免误操作把全局策略改坏。
6.4 与Jira等工单系统联动
漏洞闭环最怕“看了就没了”。Dependency-Track支持Webhook触发,配合自动化脚本把高危漏洞自动创建Jira工单,分配给对应应用负责人,工单关联漏洞详情,修复后在平台上反查确认。这个联动逻辑不复杂,本质是一个接收Webhook的小服务,但对流程闭环的意义非常大。没有这类自动化之前,漏洞记录和安全运营割裂,有了联动,KPI就变成“工单关闭率”了。
6.5 定期做数据治理和报告
运行一两个月后,平台里会有大量项目、组件和审计记录。项目如果废弃了,可以归档;组件如果长时间没有版本更新,要关注一下是不是没人维护。建议安全团队每月拉一次全量报告,看三个指标:
- 高危漏洞数量环比变化。
- 平均修复时间(从漏洞发现到标记已修复的天数)。
- 审计率(已审计漏洞占全部漏洞的比例)。
审计率的价值容易被忽略。如果审计率低,说明开发和安全没有真正在用这个平台,漏洞都可能没被点开过;审计率高,说明流程是活的,有安全运营在里面。
Dependency-Track不是装了就能“自动解决安全合规”的银弹,但它确实把供应链依赖治理这件事从“手工扫码发邮件”推进到了“平台化持续运营”的层面。按这套方法部署、接入、运维、推广下来,团队能真正把这套系统用起来,而不是让它沦为摆设。