43.8 Supply Chain Security: SLSA and Sigstore
Right, let’s talk about supply chain security. You’ve probably heard the term “software supply chain” and thought, “That sounds… corporate. And boring.” I get it. But think of it this way: you’re not just running apt-get install or pulling a random container image anymore. You’re becoming a curator, a verifier, a detective. You’re building a chain of evidence from the original developer’s keyboard all the way to your production cluster. And the goal is to stop some chucklehead from slipping a backdoor into your application because you blindly trusted a base image from the internet. We’re going to use two tools to build this chain: SLSA (the blueprint) and Sigstore (the notary public).
The Problem: “But It Worked On My Machine!”
The classic supply chain attack is depressingly simple. You docker pull node:18, add your code, build it, and ship it. The problem? That node:18 image could have been tampered with. Or the dependency you npm installed could be hijacked. We need a way to verify two things: provenance (where did this thing really come from and how was it built?) and integrity (is this the exact artifact that was built, and has it been signed?).
SLSA: The Aspirational Blueprint
SLSA (pronounced “salsa”) is a set of standards, a checklist of sorts, created by Google based on their internal “Google Binary Authorization” practices. It’s not a tool you run; it’s a framework you build towards. Think of it as a security maturity model. It has levels (0 through 3), and each level adds more rigorous requirements for making your software supply chain hermetic and reproducible.
- SLSA 1: Basically, “Hey, at least you’re using automated builds and version control.” It’s a start.
- SLSA 2: Now we’re talking. You need a cryptographically verifiable provenance statement (a signed document saying how something was built).
- SLSA 3: The big leagues. Everything is hermetic (the build is isolated from the internet, so no sneaky downloads) and the entire chain is managed by a separate, trusted service.
You don’t have to be SLSA 3 on day one. The goal is to use it as a guide to make your process less terrible over time.
Sigstore: The Free & Easy Notary Public
This is where the rubber meets the road. SLSA tells us what to do, and Sigstore gives us the how without requiring a massive corporate PKI infrastructure. It’s a suite of tools that does the heavy lifting for us mere mortals:
- Cosign: For signing and verifying container images and other blobs.
- Fulcio: A free root Certificate Authority (CA) that issues short-lived certificates based on your identity (e.g., your GitHub OIDC token).
- Rekor: A transparency log that records signatures and provenance data. It’s a public, tamper-resistant ledger that lets you verify a signature wasn’t just made up.
The magic is in the workflow. A developer doesn’t need a pre-shared key. They prove their identity (e.g., via GitHub Actions), Fulcio gives them a short-lived certificate to sign with, and the signature is sent to Rekor. Anyone in the world can then use Cosign to check the signature against the public Rekor log.
Signing Your Images with Cosign
Let’s say you’ve built your masterpiece: my-app:v1.0.0. Signing it is a one-liner. Cosign will handle the authentication with Fulcio (via your browser or environment variables in CI) behind the scenes.
# Assuming you're already logged into your container registry
cosign sign --key env://COSIGN_PRIVATE_KEY my-registry.com/my-app:v1.0.0
But managing private keys is a pain. The better, more modern way is to let Fulcio manage the keys for you using its built-in keyless signing. This command will open a browser to authenticate your identity (e.g., via Google or GitHub):
cosign sign my-registry.com/my-app:v1.0.0
For CI/CD (like GitHub Actions), you set environment variables (COSIGN_EXPERIMENTAL=1) and it uses the OIDC token from the workflow to authenticate. No key management. It’s brilliant.
Verifying the Signature
Now, the consumer (which is probably also you, in your deployment pipeline) can verify the image. They don’t need any special access or keys—just Cosign and the public Rekor log.
cosign verify my-registry.com/my-app:v1.0.0 \
--certificate-identity=https://github.com/MyOrg/my-repo/.github/workflows/ci.yml@refs/heads/main \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
The --certificate-identity flag is crucial. It’s not enough to know the image was signed; you need to know it was signed by the specific identity you trust. In this case, you’re saying, “I only trust images signed by the CI workflow from the main branch of my specific repo.” This stops someone from forking your repo, building a malicious image, and signing it with their own GitHub identity—your verifier would reject it.
Generating and Attaching Provenance
Signing is half the battle. Provenance is the other half. This is the document that says, “This image was built from this commit, by this workflow, using these materials.” With Sigstore, you generate this using the SLSA provenance generator. In a GitHub Action, it looks something like this:
- name: Generate SLSA provenance
uses: slsa-framework/slsa-github-generator@v1.4.0
id: build
with:
base64-subjects: true
# The container image we just built
image-name: my-registry.com/my-app:${{ github.sha }}
- name: Attach provenance to image
run: |
cosign attest \
--predicate ${{ steps.build.outputs.provenance-name }} \
--type slsaprovenance \
my-registry.com/my-app:${{ github.sha }}
Now, you can verify the attestation just like the signature, giving you a complete picture of where your artifacts came from and how they were constructed. It turns your deployment from a act of faith into a verified, auditable transaction. And that, my friend, is how you stop being the low-hanging fruit.