GitLab CI template for Dependency Track¶
This project implements a GitLab CI/CD template to collect and send your SBOM reports to a Dependency Track server.
Usage¶
This template can be used both as
a CI/CD component
or using the legacy include:project
syntax.
Use as a CI/CD component¶
Add the following to your .gitlab-ci.yml
:
include:
# 1: include the component
- component: $CI_SERVER_FQDN/to-be-continuous/dependency-track/gitlab-ci-dependency-track@1.5.1
# 2: set/override component inputs
inputs:
# ⚠ this is only an example
base-api-url: "https://dependency-track.my-company.org/api"
Use as a CI/CD template (legacy)¶
Add the following to your .gitlab-ci.yml
:
include:
# 1: include the template
- project: "to-be-continuous/dependency-track"
ref: "1.5.1"
file: "/templates/gitlab-ci-dependency-track.yml"
variables:
# 2: set/override template variables
# ⚠ this is only an example
DEPTRACK_BASE_API_URL: "https://dependency-track.my-company.org/api"
Understanding the Dependency Track template¶
The template will be triggered at each pipeline execution on your production branch (main
or master
by default) and each release pipeline (upon semver tag creation).
It will scan for all SBOM file in your project structure and will upload them to the configured Dependency Track server.\ SBOM files should have been generated in the upstream pipeline with appropriate tools, and propagated as build artifacts. Most to-be-continuous templates already support - whenever possible - a job to produce a SBOM report.
API Key permissions¶
In order to operate, this template needs a Dependency Track API Key. Here are some details about mandatory and optional permissions used by the template:
Permission | Required? | Explaination |
---|---|---|
BOM_UPLOAD |
mandatory | Required to publish SBOM files to the Dependency Track server |
PROJECT_CREATION_UPLOAD |
optional | This is required if you want to automatically create the project while uploading the SBOM files when the project does not exist (but the parent project must exist) |
VIEW_VULNERABILITY and VIEW_PORTFOLIO |
optional | Required if you want to display found vulnerabilities or compute a risk score after SBOM analysis. Granting those permissions without enabling Portfolio ACLs is not recommended in the general case as it gives a read access to all projects |
VIEW_PORTFOLIO and PORTFOLIO_MANAGEMENT |
optional | Required if you want to automatically create one or several project ancestors prior to uploading the SBOM files. Granting those permissions is not recommended in the general case as they virtually give administration rights to the API Key owner |
Multiple SBOM strategy¶
When collecting several SBOM files in a project, this template supports two strategies:
- publish each SBOM independently in separate projects (default),
- or merge all SBOM files and publish the result into a single project.
The merge strategy can be simply enabled by setting the merge
/ $DEPTRACK_MERGE
configuration to true
(see below).
Project Path¶
Whenever a SBOM file is published, the template uploads it to the Dependency Track server under a certain project.\
The target project is determined by evaluating the project-path
input / $DEPTRACK_PROJECT_PATH
variable (see configuration chapter).
The project path is a sequence of elements separated by double slashes //
(the separator is also configurable with the path-separator
input / $DEPTRACK_PATH_SEPARATOR
variable).\
Each element is expected to be one of the following:
#11111111-1111-1111-1111-111111111111
: a project Universally Unique Identifier (UUID) (starting with a hash#
)project-name@version
: a project name and a version (separated with a@
)project-name
: a project name only (empty version)
Lastly, the project path supports some expressions, that will be dynamically replaced when being evaluated:
Expression | Value for separate SBOM files | Value for merged SBOM |
---|---|---|
{file_prefix} |
SBOM filename prefix (before the first dot). Ex: when processing the file reports/docker-sbom.cyclonedx.json , {file_prefix} will be docker-sbom . |
merged |
{sbom_name} |
Metadata > Component > Name info extracted from the SBOM file (json or xml) |
unk |
{sbom_version} |
Metadata > Component > Version info extracted from the SBOM file (json or xml) |
empty string |
{sbom_type} |
Metadata > Component > Type info extracted from the SBOM file (json or xml) |
unk |
Default project path¶
By default, the Dependency Track project path is set to $CI_PROJECT_NAMESPACE//$CI_PROJECT_PATH-{file_prefix}@$CI_COMMIT_REF_NAME
(with path separator //
).
That means:
- The project structure in Dependency Track will always be two levels deep:
- a root project bearing the name of the GitLab project namespace,
- and leaf projects (hosting SBOM files) bearing the full path of the GitLab project as a name, suffixed with the SBOM file prefix and with project version matching either the Git branch name or the Git tag name (depending on the kind of pipeline that originated the SBOM file).
- If the API key has only the
BOM_UPLOAD
permission, then all the projects hierarchy must pre-exist down to the leaf sub-projects. - If the API key has the extra
PROJECT_CREATION_UPLOAD
permission, then only the root project must pre-exist, the template will automatically create the leaf projects if not found. - If the API key has the extra
VIEW_PORTFOLIO
andPORTFOLIO_MANAGEMENT
permissions (not recommanded), then the entire project hierarchy will be automatically created by the template if it doesn't exist.
Example: Let's imagine a GitLab project located in acme-program/acme-services/acme-user-api
with 2 SBOM files generated by the pipeline:
py-sbom.cyclonedx.json
: the SBOM of the Python application implementation, with version1.1.0
docker-sbom.cyclonedx.json
: the SBOM of the container image, with versionmain
Lastly, let's suppose the project production branch is main
, and 2 software versions have been released so far: 1.0.0
and 1.1.0
.
The corresponding Dependency Track project structure with separate SBOMs strategy will be:
| Project name | Version |
| --------------------------------------------------------- | ------- |
| 📂 acme-program/acme-services | |
| ├─📄 acme-program/acme-services/acme-user-api-py-sbom | main |
| ├─📄 acme-program/acme-services/acme-user-api-py-sbom | 1.0.0 |
| ├─📄 acme-program/acme-services/acme-user-api-py-sbom | 1.1.0 |
| ├─📄 acme-program/acme-services/acme-user-api-docker-sbom | main |
| ├─📄 acme-program/acme-services/acme-user-api-docker-sbom | 1.0.0 |
| ├─📄 acme-program/acme-services/acme-user-api-docker-sbom | 1.1.0 |
| ...
The corresponding Dependency Track project structure with merged SBOMs strategy will be:
| Project name | Version |
| ---------------------------------------------------- | ------- |
| 📂 acme-program/acme-services | |
| ├─📄 acme-program/acme-services/acme-user-api-merged | main |
| ├─📄 acme-program/acme-services/acme-user-api-merged | 1.0.0 |
| ├─📄 acme-program/acme-services/acme-user-api-merged | 1.1.0 |
| ...
this default configuration handles the Dependency Track constraint that each project name has to be globally unique in the server.
Other use cases¶
The default template behavior can be changed by overridding the project-path
input / $DEPTRACK_PROJECT_PATH
variable.
Examples:
#550e8400-e29b-41d4-a716-446655440000
: every SBOM found will be published to the project with UUID550e8400-e29b-41d4-a716-446655440000
as Dependency Track is only able to store one SBOM per project, this configuration is suitable only if exactly one SBOM file is found (otherwise each one will overwrite the previous one)my-project@v1.1.0
: every SBOM found will be published to the project with namemy-project
and versionv1.1.0
depending on your API key permissions,sbom-scanner
might try to automatically create the project if it doesn't exist
as in the previous example, this configuration is suitable only if exactly one SBOM file is found#550e8400-e29b-41d4-a716-446655440000/my-project-{file_prefix}@{sbom_version}
: every SBOM found will be published to a project namedmy-project-{file_prefix}
and version{sbom_version}
(extracted from the SBOM file), direct child of project with UUID550e8400-e29b-41d4-a716-446655440000
depending on your API key permissions,sbom-scanner
might try to automatically create the project if it doesn't existacme-program@v2/acme-services@v1.3/acme-user-api@v1.3/acme-user-api-{file_prefix}
: complete project path only defined by project names and versions
depending on your API key permissions,sbom-scanner
might try to automatically create the project and its ancestors if they don't exist
Configuration¶
The Dependency Track template uses the following configuration.
Input / Variable | Description | Default value |
---|---|---|
sbom-scanner-image / DEPTRACK_SBOM_SCANNER_IMAGE |
The container image with Dependency Track SBOM Scanner tool | registry.gitlab.com/to-be-continuous/tools/dt-sbom-scanner:latest |
base-api-url / DEPTRACK_BASE_API_URL |
Dependency Track server base API url (includes /api ) |
none (required) |
DEPTRACK_API_KEY |
Dependency Track API key | none (required) |
project-path / DEPTRACK_PROJECT_PATH |
Dependency Track target project path to publish SBOM files to | $CI_PROJECT_NAMESPACE//$CI_PROJECT_PATH-{file_prefix}@$CI_COMMIT_REF_NAME |
path-separator / DEPTRACK_PATH_SEPARATOR |
Separator to use in project path | // |
sbom-patterns / DEPTRACK_SBOM_PATTERNS |
SBOM file patterns to publish (supports glob patterns) | **/*.cyclonedx.json **/*.cyclonedx.xml |
merge / DEPTRACK_MERGE |
Merge all SBOM files into one (default false ) |
none (disabled) |
merge-output / DEPTRACK_MERGE_OUTPUT |
Output merged SBOM file (only used with merge enabled) - for debugging purpose | reports/deptrack-merged.cyclonedx.json |
purl-max-len / DEPTRACK_PURL_MAX_LEN |
PURLs max length (-1 : auto, 0 : no trim, >0 : trim to size - default: -1 ) |
-1 (auto) |
show-findings / DEPTRACK_SHOW_FINDINGS |
Wait for analysis and display found vulnerabilities in logs | none (disabled) |
risk-score-threshold / DEPTRACK_RISK_SCORE_THRESHOLD |
Fail the job if risk score threshold is exceeded (<0 : disabled - default: -1 ) |
-1 (disabled) |
Quality Gate¶
The job dependency-track
runs after all the jobs in the pipeline have completed. It will not block the pipeline execution.
The quality-gate-enabled
/ DEPTRACK_QUALITY_GATE_ENABLED
configuration can be set to true
to enable a quality gate job dependency-track-acceptance
that will block the pipeline if the risk score computed by Dependency Track exceeds a certain threshold defined by the risk-score-threshold
/ DEPTRACK_RISK_SCORE_THRESHOLD
configuration.
By default, this acceptance job will use the same target project path as the main job, erasing the previous release SBOM files. If you want to keep clean release SBOM files, you should define a different project path for the acceptance job.
You can override the following configuration to customize the quality gate job:
Input / Variable | Description | Default value |
---|---|---|
quality-gate-enabled / DEPTRACK_QUALITY_GATE_ENABLED |
Enable a job at acceptance stage which will block the pipeline when failing | false |
quality-gate-base-api-url / DEPTRACK_QUALITY_GATE_BASE_API_URL |
Override Dependency Track server base API url (includes /api ) to use for acceptance stage |
none (uses default) |
DEPTRACK_QUALITY_GATE_API_KEY |
Override Dependency Track API key to use for acceptance stage | none (uses default) |
quality-gate-project-path / DEPTRACK_QUALITY_GATE_PROJECT_PATH |
Override Dependency Track target project path to publish SBOM files to during acceptance stage | none (uses default) |
quality-gate-merge / DEPTRACK_QUALITY_GATE_MERGE |
Merge all SBOM files into one (default false ) during acceptance stage |
none (uses default) |
quality-gate-show-findings / DEPTRACK_QUALITY_GATE_SHOW_FINDINGS |
Wait for analysis and display found vulnerabilities in logs during acceptance stage | none (uses default) |
quality-gate-risk-score-threshold / DEPTRACK_QUALITY_GATE_RISK_SCORE_THRESHOLD |
Fail the job if risk score threshold is exceeded (<0 : disabled - default: -1 ) during acceptance stage |
none (uses default) |
Secrets management¶
Here are some advices about your secrets (variables marked with a ):
- Manage them as project or group CI/CD variables:
- masked to prevent them from being inadvertently displayed in your job logs,
- protected if you want to secure some secrets you don't want everyone in the project to have access to (for instance production secrets).
- Manage them using the Vault variant
- In case a secret
contains characters that prevent it from being masked,
simply define its value as the Base64 encoded value prefixed with
@b64@
: it will then be possible to mask it and the template will automatically decode it prior to using it. - Don't forget to escape special characters (ex:
$
->$$
).
Variants¶
Vault variant¶
This variant allows delegating your secrets management to a Vault server.
Configuration¶
In order to be able to communicate with the Vault server, the variant requires the additional configuration parameters:
Name | Description | Default value |
---|---|---|
TBC_VAULT_IMAGE |
The Vault Secrets Provider image to use (can be overridden) | registry.gitlab.com/to-be-continuous/tools/vault-secrets-provider:master |
VAULT_BASE_URL |
The Vault server base API url | none |
VAULT_OIDC_AUD |
The aud claim for the JWT |
$CI_SERVER_URL |
VAULT_ROLE_ID |
The AppRole RoleID | must be defined |
VAULT_SECRET_ID |
The AppRole SecretID | must be defined |
Usage¶
Then you may retrieve any of your secret(s) from Vault using the following syntax:
@url@http://vault-secrets-provider/api/secrets/{secret_path}?field={field}
With:
Name | Description |
---|---|
secret_path (path parameter) |
this is your secret location in the Vault server |
field (query parameter) |
parameter to access a single basic field from the secret JSON payload |
Example¶
include:
# main template
- project: 'to-be-continuous/dependency-track'
ref: '1.5.1'
file: '/templates/gitlab-ci-dependency-track.yml'
# Vault variant
- project: 'to-be-continuous/dependency-track'
ref: '1.5.1'
file: '/templates/gitlab-ci-dependency-track-vault.yml'
variables:
# audience claim for JWT
VAULT_OIDC_AUD: "https://vault.acme.host"
# Secret managed by Vault
DEPTRACK_API_KEY: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/runner/prod/deptrack?field=api-key"
VAULT_BASE_URL: "https://vault.acme.host/v1"
# $VAULT_ROLE_ID and $VAULT_SECRET_ID defined as a secret CI/CD variable