Safeguard
Software Supply Chain•

How do you sign artifacts and record provenance in Bitbucket Pipelines?

In Bitbucket Pipelines, sign artifacts with a protected key and use the step OIDC token for identity. Self-written provenance reaches SLSA level 1 only.

Safeguard Security Team
5 min read

To sign artifacts in Bitbucket Pipelines, generate a signature in the build step with a key kept in a secured variable or an external key service, publish the signature next to the artifact, and verify it before deploy. Provenance you write yourself is useful, but by SLSA's definitions it reaches only level 1.

What are signing and provenance, in plain terms?

A signature answers "did the holder of this key produce these exact bytes?" Provenance answers "how was this artifact built, from which source, on which system?" You want both, and you want the consumer to check them, because an unchecked signature protects nobody.

SLSA's build track describes the ladder. Build L1 means provenance exists and shows how the package was built, which helps with mistakes and documentation. Build L2 requires signed provenance generated by a hosted build platform, which addresses tampering after the build. Build L3 requires a hardened build platform, addressing tampering during the build.

What does Bitbucket Pipelines give you to start with?

Atlassian documents OpenID Connect for Pipelines. Setting oidc: true on a step makes a token available in the variable BITBUCKET_STEP_OIDC_TOKEN, and the documented example exchanges that token with a cloud provider for short-lived credentials instead of storing long-lived keys. The identity provider URL and audience come from the repository's OpenID Connect settings, and the token carries the workspace, repository and deployment environment identifiers.

That is a strong base for one thing: not storing cloud keys in variables. It is not, by itself, an artifact signature. Whether a given signing service accepts Bitbucket as an identity issuer is something to confirm in that service's documentation before you design around it.

What does signing look like with a key?

Sigstore's documentation describes keyless signing, where a short lived certificate binds an ephemeral key to an OpenID Connect identity and signing events go to a transparency log. Keyless depends on the identity provider being accepted by the certificate authority. If yours is not, key based signing still works.

A constructed example for a container image in a pipeline:

pipelines:
  tags:
    'v*':
      - step:
          name: Build, sign, push
          script:
            - docker build -t $REGISTRY/app:$BITBUCKET_TAG .
            - docker push $REGISTRY/app:$BITBUCKET_TAG
            - cosign sign --key env://COSIGN_PRIVATE_KEY $REGISTRY/app:$BITBUCKET_TAG

Notes on that sketch. The private key lives in a secured, deployment-scoped variable that only the tag pipeline can read. Sign the digest, not only the tag, because tags move. The signature is stored in the registry next to the image. And the pipeline now holds a signing key, so anyone who can change the pipeline file on that branch can sign whatever they like. Protect the branch and tag rules as strictly as the key.

How do you describe provenance honestly?

Write a small provenance record at the end of the build: source repository, commit, pipeline run identifier, builder image digest, and the artifact digest. Sign it too. Because your own script generated it inside the same job that builds, it shows how you say it was built, not that a separate trusted system observed it. Under SLSA's definitions that is level 1 territory: provenance exists, with limited protection against tampering. Say so in your documentation instead of claiming a higher level.

To move up you need provenance produced by the platform or a separate trusted step the build job cannot alter, and a hardened build service. If you require L2 or L3 evidence for customers, check whether your build platform can supply it today.

What does the consumer do?

Verification is where the value arrives. Before deploy, a job or admission policy should fetch the signature, check it against the expected public key or identity, and compare the digest to what is about to run. If no one verifies, the signatures are decoration.

What to do

  1. Use OIDC for cloud access in pipelines so build jobs stop holding long-lived cloud keys.
  2. Create a dedicated signing key per product, stored in a secured, restricted variable or an external key service.
  3. Sign image digests and release archives in a tag-only pipeline.
  4. Generate a provenance record and sign it. Label it level 1 in your own documentation.
  5. Restrict who can edit the pipeline file and push release tags.
  6. Verify signatures in the deploy step and fail closed.
  7. Rotate the key on a schedule and when anyone with access leaves.

Safeguard policies and gates can stop a pipeline on findings, which sits alongside signing. Safeguard does not sign your builds for you.

Quick check

Does enabling OIDC in a Bitbucket step sign my artifact?

No. It gives the step an identity token for exchanging with other services. Signing is a separate action with a key or a signing service.

Which SLSA level is a self-written provenance file?

Level 1: provenance exists. Level 2 needs signed provenance generated by a hosted build platform.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.