Jenkins Supply Chain Security: SBOM, Cosign & SLSA Gotchas
Production guide to SBOM generation via CycloneDX plugin, Cosign signing, SLSA levels in Jenkins.
20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.
- ✓Deep devops experience in a production environment
- ✓Familiarity with debugging, profiling, and performance analysis
- ✓Understanding of distributed systems and failure modes
- Generate SBOMs with CycloneDX plugin for Maven/Gradle builds; attach as build artifacts.
- Sign artifacts with
cosign sign-blobusing keyless OIDC (Fulcio) and store signatures in OCI registry. - Achieve SLSA Level 2+ by generating provenance attestations (in-toto) and verifying signatures in deploy pipelines.
- Use
supply-chain-levelsplugin to score your pipeline and track SLSA compliance. - Block unsigned artifacts in deployment with
cosign verify; integrate Docker Content Trust for image signing. - Integrate with GitHub/GitLab OIDC for attestation; store all metadata in Rekor transparency log.
- Common failures: missing SBOM after rebuild, expired ephemeral keys, Rekor search failures.
Imagine you're ordering a custom-built PC. You want a list of every component (SBOM), a tamper-proof seal on the box (Cosign signature), and a certificate that the factory followed strict assembly standards (SLSA). Without these, someone could swap a malicious part. In Jenkins, we generate the component list automatically, sign the build outputs, and record every step so that when the software reaches production, we can verify nothing was tampered with.
I once spent a weekend debugging a production incident where a compromised Jenkins node injected malicious code into a Java artifact. We had no SBOM to trace dependencies, no signature to verify integrity, and no provenance to prove the build process. The attacker had modified a transitive dependency and the artifact passed all tests. That incident cost us 48 hours of downtime and a painful post-mortem. Since then, I've implemented supply chain security in every Jenkins pipeline I touch. This guide covers the exact tools and practices: CycloneDX for SBOM, Cosign for signing, and SLSA framework for attestation. These are not just buzzwords—they are the difference between a secure pipeline and a breach waiting to happen.
1. Generating SBOM with CycloneDX Plugin in Jenkins
The CycloneDX plugin for Jenkins integrates with Maven and Gradle builds to generate Software Bill of Materials (SBOM) in CycloneDX or SPDX format. Install the plugin, then configure a post-build action to generate SBOM. For Maven, add the CycloneDX Maven plugin in pom.xml or use the Jenkins plugin's built-in Maven integration. Example pipeline step: cyclonedxBom outputFormat: 'json', outputName: 'bom'. The SBOM lists all direct and transitive dependencies, including versions and licenses. Store the SBOM as a build artifact for later verification. In production, we attach the SBOM to the release and upload it to a repository like Artifactory. Common gotcha: The plugin must run after dependency resolution, otherwise the SBOM will be empty. Use mvn dependency:tree to verify dependencies are resolved before SBOM generation.
2. Signing Artifacts with Cosign (Keyless Mode)
Cosign supports keyless signing using OIDC (OpenID Connect) identity from Jenkins. The identity flows through Fulcio (certificate authority) to issue an ephemeral signing certificate. The signature and certificate are stored in an OCI registry (e.g., Docker Hub, GCR). Jenkins pipeline: cosign sign-blob --oidc-issuer https://token.actions.githubusercontent.com --output-certificate cert.pem --output-signature sig.pem <artifact>. Then upload the signature and certificate to the registry: cosign attach signature --signature sig.pem --cert cert.pem <oci-image>. Production tip: Use a dedicated OCI registry for signatures, separate from the artifact registry. Keyless mode eliminates the need to manage long-lived keys. However, ensure the OIDC token from Jenkins is valid; tokens expire quickly. Use the withEnv step to set ACTIONS_ID_TOKEN_REQUEST_TOKEN and ACTIONS_ID_TOKEN_REQUEST_URL for GitHub Actions integration.
3. SLSA Framework Levels in Jenkins
SLSA (Supply-chain Levels for Software Artifacts) defines four levels of build integrity. Level 1: Build process must be scripted and produce provenance. Level 2: Provenance must be signed and generated by a build service (e.g., Jenkins). Level 3: Provenance must be non-forgeable (e.g., signed with ephemeral keys) and hardened build platform. Level 4: Two-person review of changes and hermetic builds. In Jenkins, achieve Level 2 by generating signed provenance using in-toto attestations. Use the supply-chain-levels plugin to score your pipeline. For Level 3, ensure the build runs in isolated containers (e.g., Kubernetes pods) with no manual steps. Level 4 requires source control with branch protection and mandatory code review. Example: supply-chain-levels score --level 2 to check compliance.
4. Cosign sign-blob and verify Commands
cosign sign-blob signs a file (artifact) and outputs a signature and certificate. cosign verify-blob verifies the signature against the artifact. Example: cosign verify-blob --cert cert.pem --signature sig.pem app.jar. For container images, use cosign sign and cosign verify. Production usage: In the deployment pipeline, verify the artifact before deployment. Use cosign verify-blob --key <key> app.jar if using key-based signing. For keyless, the verification automatically checks the certificate chain against Fulcio and Rekor. Common gotcha: The --cert and --signature flags must point to the correct files. Use cosign tree to see all signatures on an image. Example: cosign tree docker.io/myorg/myapp:latest lists all signatures and attestations.
5. Generating SPDX/CycloneDX During Maven/Gradle Builds
For Maven, add the CycloneDX Maven plugin: mvn org.cyclonedx:cyclonedx-maven-plugin:makeBom. For Gradle, apply the org.cyclonedx.bom plugin. The plugin generates a bom.json or bom.xml file containing all dependencies. In Jenkins, run this step after mvn clean install to ensure all dependencies are resolved. Production tip: Store the SBOM in a central repository (e.g., Artifactory) with a unique version tag. Use the SBOM for vulnerability scanning with tools like Grype or Trivy. Example pipeline: sh 'mvn org.cyclonedx:cyclonedx-maven-plugin:makeBom -DoutputFormat=json -DoutputName=bom'. For SPDX, use the SPDX SBOM plugin or convert CycloneDX to SPDX using a tool.
6. Storing Signatures in OCI Registries
Cosign stores signatures in OCI registries as separate tags or in the same repository as the artifact. Use cosign attach signature to attach a signature to an existing image. Alternatively, use cosign sign which automatically pushes the signature to the registry. The signature is stored as an OCI artifact with a predictable tag (e.g., sha256-<digest>.sig). Production tip: Use a dedicated registry for signatures (e.g., sigregistry.example.com) to separate from artifacts. This avoids clutter and allows different access controls. Ensure the registry supports OCI artifacts (most do). Example: cosign sign --key key.pem --tlog-upload=false docker.io/myorg/myapp:latest pushes signature to docker.io/myorg/myapp:sha256-...sig. Use cosign copy to copy signatures between registries.
7. Cosign Keyless Mode with OIDC, Fulcio and Rekor Transparency Log
Keyless mode uses OIDC to authenticate the signer. Jenkins obtains an OIDC token from its identity provider (e.g., GitHub, GitLab, or Jenkins itself). The token is presented to Fulcio, which issues a short-lived certificate. The signature and certificate are uploaded to Rekor, a transparency log, providing an immutable record. To use keyless mode, set environment variables: COSIGN_EXPERIMENTAL=1 (in older versions) or configure OIDC provider. The command cosign sign-blob --oidc-issuer <issuer> app.jar triggers the flow. Verification automatically checks Rekor for the signing certificate. Production gotcha: Rekor may be slow or unavailable; consider using a private Rekor instance for reliability. Also, OIDC tokens expire, so ensure the pipeline step is not too long.
8. Attesting Pipeline Artifacts with In-Toto Attestations
In-toto attestations provide a signed statement about the build process (e.g., source repository, build command, builder identity). Cosign supports creating in-toto attestations with cosign attest. The attestation is stored as an OCI artifact. Example: cosign attest --predicate predicate.json --key key.pem docker.io/myorg/myapp:latest. The predicate file contains SLSA provenance. Use the slsa-generator tool to generate the predicate. In Jenkins, generate the predicate in a stage and then attest. Verification: cosign verify-attestation --key key.pem docker.io/myorg/myapp:latest. Production tip: Attestations should include the pipeline ID, build timestamp, and source commit. This enables auditability and meets SLSA Level 2+ requirements.
9. Verifying Signatures in Deployment Pipelines
Deployment pipelines must verify signatures and attestations before deploying. Use cosign verify-blob for files or cosign verify for container images. Example: cosign verify --key public-key.pem docker.io/myorg/myapp:latest. For keyless verification, the command automatically checks Fulcio and Rekor. If verification fails, the pipeline should fail. Production scenario: A deployment pipeline that pulls an artifact from a registry, verifies it, then deploys. If verification fails, the pipeline stops and alerts the team. This prevents deployment of tampered or unsigned artifacts. Use a dedicated verification step that runs before any deployment action. Integrate with Docker Content Trust for additional image signing.
10. Blocking Unsigned Artifacts with Pipeline Gates
To block unsigned artifacts, add a gate in the deployment pipeline that checks for a valid signature. Use cosign verify and if it fails, the pipeline fails. Additionally, use the supply-chain-levels plugin to enforce a minimum SLSA score. Example: supply-chain-levels check --min-level 2. If the score is below 2, fail the pipeline. Another approach: Use a webhook or policy agent (e.g., OPA) to evaluate attestations before deployment. Production tip: Implement a 'break glass' mechanism to bypass the gate in emergencies, but log all overrides. Common gotcha: Ensure the verification key is securely stored (e.g., Jenkins credential store) and rotated regularly.
11. Docker Content Trust Integration
Docker Content Trust (DCT) uses Notary to sign and verify images. In Jenkins, enable DCT by setting environment variable DOCKER_CONTENT_TRUST=1. This forces docker push and docker pull to sign and verify images. To sign an image, use docker trust sign. DCT uses a local delegation key; store the key in Jenkins credential store. Example: withEnv(['DOCKER_CONTENT_TRUST=1']) { sh 'docker push myorg/myapp:latest' }. DCT is an alternative to Cosign for container images; however, Cosign is more flexible and supports keyless mode. Production tip: Use both DCT and Cosign for defense in depth. DCT ensures image integrity at the Docker client level, while Cosign provides attestation and Rekor transparency.
12. Integrating with GitHub/GitLab for Attestation
GitHub and GitLab can act as OIDC providers for Cosign keyless signing. In Jenkins, use the GitHub OIDC token from the GitHub context (e.g., ACTIONS_ID_TOKEN_REQUEST_TOKEN). For GitLab, use the GitLab OIDC token. Example: cosign sign-blob --oidc-issuer https://gitlab.com --oidc-client-id <client-id> app.jar. The attestation ties the artifact to the source repository and pipeline run. For SLSA, the provenance should include the repository URL and commit SHA. Use the slsa-github-generator or slsa-gitlab-generator to generate provenance. Production tip: Ensure the OIDC token has the correct audience; misconfiguration leads to verification failures.
The Case of the Missing SBOM and the Silent Backdoor
- Always generate SBOM and sign artifacts.
- Without them, you are blind to supply chain attacks.
Print-friendly master reference covering all topics in this track.
Key takeaways
Common mistakes to avoid
6 patternsGenerating SBOM before dependency resolution
mvn dependency:resolve or mvn install before SBOM generationUsing long-lived keys for Cosign and storing them in Jenkins credentials without rotation
Not verifying signatures in deployment pipeline
cosign verify step before deploymentIgnoring Rekor transparency log entries
Setting SLSA level target too high without infrastructure readiness
Using Docker Content Trust without proper key management
Interview Questions on This Topic
How would you achieve SLSA Level 2 in a Jenkins pipeline?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.
That's Jenkins. Mark it forged?
5 min read · try the examples if you haven't