Set up CI/CD pipelines

Connect GitHub Actions, GitLab CI/CD, Jenkins, Azure Pipelines, Bitbucket Pipelines, CircleCI, and Buildkite to the firewall, and trace every package request back to the run that made it.

Route your pipelines through the firewall and every dependency a build installs is checked against the same rules as a developer's install. Each pipeline gets its own token, and each request carries the run's ID. When a build fails on a blocked package, filter the logs on that run and you see which rule blocked what.

Pipeline run
Firewall
You
Every run
token from a CI secret or OIDC exchange, plus ::<run-id>
Install with token::<run-id>
split off the run ID, check token and rules
Package, or a block that fails the build
Investigate
Filter logs by the run ID
Log entries from that run
Pipeline run
Firewall
You
request response

Before you start

  • The repository has the firewall configured as its registry, reading the token from BYTESAFE_TOKEN. Commit that configuration and the pipeline uses the firewall the same way a developer's machine does. Setup per package manager, including what differs in CI, is on the ecosystem pages.
  • You hold ns:create:service-token, which admins have by default. Both a Service Access Token and a Trusted Builder need it.

Choose a credential

  • GitHub Actions: a Trusted Builder. Each run proves its identity with OpenID Connect (OIDC) and exchanges it for a Service Access Token valid for 15 minutes. There is no secret to store in GitHub or to rotate.
  • Every other CI system: a Service Access Token, stored as a secret in the CI system. Trusted Builders currently accept GitHub Actions only.

Either way the pipeline has a token of its own. Log entries name it, and revoking it stops that one pipeline.

GitHub Actions

  1. In the dashboard, open the user menu in the lower-left corner, select Access tokens, switch to Trusted builders, and select Add builder. Enter the GitHub owner and repository, give it fw:access, and restrict it to the firewalls the workflow installs from. Copy the audience value.
  2. Give the job id-token: write permission and add a step that exchanges the run's identity for a token:
      - name: Get firewall token
        env:
          BYTESAFE_ENDPOINT: https://eu-sov-1.bytesafecloud.eu
          BYTESAFE_NAMESPACE: <namespace-id>
          BYTESAFE_TRUSTED_BUILDER_AUDIENCE: bytesafe:trusted-builder:6f1e9c2a-...
        run: |
          TOKEN="$(bsfw oidc exchange)::${GITHUB_RUN_ID}"
          echo "::add-mask::$TOKEN"
          echo "BYTESAFE_TOKEN=$TOKEN" >> "$GITHUB_ENV"

Later steps in the job read BYTESAFE_TOKEN. The ::${GITHUB_RUN_ID} suffix tags each request with the run ID, so you can find the run in the logs.

The GitHub Actions tutorial covers the rest: installing bsfw on the runner, the exchange without the CLI, and what to check when the exchange fails.

Other CI systems

GitLab CI/CD, Jenkins, Azure Pipelines, Bitbucket Pipelines, CircleCI, and Buildkite all work the same way: a Service Access Token kept as a CI secret, with the run ID appended in the install step.

Create a Service Access Token

Create the token as described in Manage tokens, with these values for a pipeline:

  • Name: the pipeline or build server it is for, such as gitlab-webshop or jenkins-eu. Log entries show this name.
  • Validity: how long the pipeline will use it. Every token expires, so plan the replacement before it does.
  • Permissions: fw:access is enough to install. Add publish permissions only for a pipeline that publishes.
  • Restrict to firewalls: the firewalls this pipeline installs from.

Copy the value right away; it is shown only once. To see which system made each request, create one token per pipeline or build server, add an interaction ID as described in Identify each run, or both. Separate tokens also let you revoke one system without affecting the others.

Store it as a CI secret

Store the token as a secret named BYTESAFE_TOKEN, and note the variable that identifies a run:

CI systemStore the token asRun ID variable
GitLab CI/CDA masked variable under Settings > CI/CD > VariablesCI_PIPELINE_ID
JenkinsA Secret text credential, bound with credentials() in the pipeline's environment blockBUILD_TAG
Azure PipelinesA secret pipeline variable or variable group, mapped into each step with env:BUILD_BUILDID
Bitbucket PipelinesA Secured variable under Repository settings > Pipelines > Repository variablesBITBUCKET_BUILD_NUMBER
CircleCIA project environment variable or a contextCIRCLE_WORKFLOW_ID
BuildkiteA Buildkite secret, exposed to the step with the secrets keyBUILDKITE_BUILD_ID

Check that the secret reaches the install step. Azure Pipelines only passes a secret variable to a step that maps it with env:. A GitLab variable marked Protect variable is only available in pipelines on protected branches and tags.

Identify each run

In the install step, before the package manager runs, append the run ID to the token:

export BYTESAFE_TOKEN="${BYTESAFE_TOKEN}::${CI_PIPELINE_ID}"

Swap in the run ID variable for your CI system. Keep the line in the same step as the install, because most CI systems start each step in a new shell.

The firewall splits off the suffix before it checks the token and records it as the request's interaction ID. This works whether the package manager sends the token in a header or inside a URL, such as Go's GOPROXY. In a URL, keep the ID to letters, digits, dots, and dashes. Jenkins' BUILD_TAG includes the job name, so use BUILD_NUMBER if job names contain spaces.

Find a run in the logs

Every request from the run carries its interaction ID:

  • Firewall logs record it on each entry. To see one run, filter on its ID:

    bsfw logs --all-users --interaction-id "<run-id>"

    The CLI matches exactly, so build-12 does not return build-123. In the dashboard, search for the ID on Logs, or open an entry from the run and select its interaction value; the dashboard search also matches part of an ID.

  • Audit log entries made with the token carry it as interactionId.

  • Webhook events carry it, as interaction on firewall events and interactionId on audit events. A block notification therefore names the run it came from.

When a build is blocked

The install step fails with the package manager's own error. For a blocked download, the message includes a request ID that leads to the log entry and the rule. Investigate a blocked install covers each kind of block.

If the package should be allowed, the developer whose build failed requests an exception, from the dashboard or with their own Personal Access Token. Service Access Tokens cannot request exceptions, because a request needs a person to notify of the decision. See Set up an exception workflow.

Troubleshooting

The firewall did not get a valid token. Check that the secret reaches the step that installs: Azure Pipelines secrets need an env: mapping, and protected GitLab variables are missing on unprotected branches. Then check on the Access tokens page that the token has not expired or been deleted. On GitHub Actions, a failing exchange is covered in When the exchange fails.

Requests that match no logging rule are not logged, so a run that installed only allowed packages can leave nothing behind. Add a log-only rule to see all traffic, as described in View logs. If the run should have matched a rule, check that its requests reached the firewall at all: a package manager without the firewall configured goes straight to the public registry.