Introductie: Waarom Software Supply Chain Security in 2026 Onmisbaar Is
Als je de afgelopen jaren de cybersecurity-wereld een beetje hebt gevolgd, dan weet je dat supply chain-aanvallen serieus uit de hand zijn gelopen. De SolarWinds-aanval in 2020 compromitteerde duizenden organisaties via één geïnfecteerde software-update. Log4Shell (CVE-2021-44228) bewees dat één kwetsbare dependency in een populaire Java-bibliotheek letterlijk het hele internet kon bedreigen. En toen kwam in 2024 de XZ Utils backdoor (CVE-2024-3094) — eerlijk gezegd nog steeds een van de meest angstaanjagende incidenten die ik ken. Een social engineering-aanval die bijna een backdoor in vrijwel elke Linux-distributie had geïntroduceerd, via iemand die zich jarenlang als vertrouwde open-source maintainer had voorgedaan.
Dat was toen. Dit is nu.
In 2026 is software supply chain security geen optioneel onderwerp meer voor een select groepje beveiligingsspecialisten — het is een wettelijke verplichting. De EU Cyber Resilience Act (CRA) schrijft voor dat fabrikanten van producten met digitale elementen vanaf september 2026 actief kwetsbaarheden moeten melden. Vanaf december 2027 moet je een volledige Software Bill of Materials (SBOM) kunnen overleggen. Wie niet voldoet, riskeert boetes tot €15 miljoen of 2,5% van de wereldwijde jaaromzet. Dat zijn geen bedragen waar je mee wilt experimenteren.
In deze gids neem ik je mee langs drie kernpijlers waarmee je als Linux-systeembeheerder of DevOps-engineer je software supply chain praktisch kunt beveiligen: SBOM (Software Bill of Materials), Sigstore (keyless signing) en SLSA (Supply-chain Levels for Software Artifacts). Met concrete codevoorbeelden die je direct kunt toepassen. Dus laten we erin duiken.
Wat is Software Supply Chain Security?
Software supply chain security omvat alle maatregelen om de integriteit, authenticiteit en veiligheid van software te waarborgen — van het moment dat code wordt geschreven tot het moment dat deze in productie draait. Je kunt het vergelijken met een fysieke supply chain: op elk punt in de keten kan er worden gemanipuleerd.
Aanvalsvectoren
De belangrijkste aanvalsvectoren die je moet kennen:
- Gecompromitteerde dependencies: Aanvallers injecteren kwaadaardige code in veelgebruikte open-source packages. Dit kan via typosquatting (packages met bijna identieke namen), het overnemen van verlaten projecten, of het compromitteren van maintainer-accounts.
- Manipulatie van het build-systeem: Als je CI/CD-systeem wordt gecompromitteerd, kan een aanvaller kwaadaardige code injecteren tijdens het bouwproces — zelfs als de broncode zelf schoon is. SolarWinds is hiervan het bekendste voorbeeld.
- Kwaadaardige packages in registries: Package registries zoals npm, PyPI en crates.io worden regelmatig misbruikt om malware te verspreiden via ogenschijnlijk legitieme packages.
- Compromittering van de broncode: Via gestolen credentials, insider threats of social engineering (de XZ Utils-casus) kan de broncode zelf worden gemanipuleerd.
- Distributiekanaal-aanvallen: Het manipuleren van download-servers, mirrors of update-mechanismen om geïnfecteerde binaries te verspreiden.
Waarom Linux-systemen bijzonder kwetsbaar zijn
Linux-systemen zijn om meerdere redenen een bijzonder aantrekkelijk doelwit. Het Linux-ecosysteem vormt het fundament van vrijwel alle cloudinfrastructuur, containerplatformen en serveromgevingen. Een succesvolle aanval op een veelgebruikt Linux-pakket heeft daardoor een enorm bereik.
Daar komt bij dat de Linux-distributieketen behoorlijk complex is. Packages doorlopen meerdere stappen — van upstream-ontwikkelaar via distributiemaintainers naar eindgebruikers — en elke schakel is een potentieel aanvalspunt. En het open-source-model maakt het mogelijk voor iedereen om bij te dragen. Dat is tegelijkertijd de grootste kracht én de grootste kwetsbaarheid, zoals de XZ Utils-affaire pijnlijk duidelijk maakte.
SBOM: De Software Bill of Materials
Een Software Bill of Materials (SBOM) is eigenlijk niets meer dan een gestructureerde, machineleesbare inventaris van alle componenten waaruit een softwareproduct is opgebouwd. Zie het als een ingrediëntenlijst voor software: het beschrijft welke bibliotheken, frameworks en modules (inclusief hun versies) in een applicatie zitten, met hun licenties en onderlinge afhankelijkheden.
SPDX vs CycloneDX
Er zijn twee dominante standaarden voor SBOM's, en het is handig om het verschil te kennen:
- SPDX (Software Package Data Exchange): Een ISO-standaard (ISO/IEC 5962:2021) ontwikkeld door de Linux Foundation. SPDX is erg sterk in het vastleggen van licentie-informatie en wordt breed ondersteund door overheidsinstellingen. Vanaf versie 3.0 biedt het ook uitgebreide ondersteuning voor security-metadata.
- CycloneDX: Ontwikkeld door OWASP, specifiek ontworpen voor beveiligingsdoeleinden. Het formaat is lichter en eenvoudiger te implementeren, met uitstekende ondersteuning voor kwetsbaarheidsgegevens, service-informatie en afhankelijkheidsgrafieken.
Beide formaten worden ondersteund door de EU Cyber Resilience Act. Welke je kiest hangt af van je behoeften: SPDX voor compliance en licentie-tracking, CycloneDX voor security-gerichte workflows. In de praktijk genereer ik vaak beide — kost vrijwel geen extra moeite.
Praktisch aan de slag met Syft
Syft van Anchore is inmiddels dé industriestandaard voor het genereren van SBOM's op Linux-systemen. Het ondersteunt containers, filesystems, archieven en meer.
Installatie van Syft
# Installeer Syft via het officiële installatiescript
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Verifieer de installatie
syft version
# Alternatief: installeer via Homebrew (op systemen met Linuxbrew)
brew install syft
SBOM genereren voor een projectdirectory
Het genereren van een SBOM voor een lokale codebase is verrassend eenvoudig:
# Genereer een SBOM in SPDX JSON-formaat voor een projectdirectory
syft dir:/pad/naar/project -o spdx-json=sbom.spdx.json
# Bekijk een samenvatting van de gevonden packages
cat sbom.spdx.json | python3 -m json.tool | head -50
# Genereer een SBOM voor het volledige bestandssysteem
syft dir:/ -o spdx-json=systeem-sbom.spdx.json
SBOM genereren voor containerimages
# Genereer een SBOM in CycloneDX-formaat voor een Alpine-containerimage
syft alpine:latest -o cyclonedx-json=sbom.cdx.json
# Genereer een SBOM voor een lokaal gebouwde image
syft docker:mijn-applicatie:v1.0 -o spdx-json=app-sbom.spdx.json
# Genereer meerdere uitvoerformaten tegelijk
syft nginx:latest \
-o spdx-json=nginx-sbom.spdx.json \
-o cyclonedx-json=nginx-sbom.cdx.json \
-o table=nginx-sbom.txt
De uitvoer analyseren
Een SBOM in SPDX JSON-formaat bevat best veel informatie over elk gedetecteerd component. Hier een fragment om je een idee te geven:
// Voorbeeld van een SPDX JSON-fragment
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "alpine-latest",
"packages": [
{
"SPDXID": "SPDXRef-Package-apk-alpine-baselayout-3.4.3-r2",
"name": "alpine-baselayout",
"versionInfo": "3.4.3-r2",
"supplier": "Organization: Alpine Linux",
"downloadLocation": "https://git.alpinelinux.org/aports",
"licenseConcluded": "GPL-2.0-only",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:apk/alpine/[email protected]"
}
]
}
]
}
Let vooral op het purl-referentieveld (Package URL). Dit is de universele identifier waarmee packages worden gekoppeld aan bekende kwetsbaarheden in databases zoals de National Vulnerability Database (NVD) en de Open Source Vulnerabilities (OSV) database. Zonder deze identifier is correlatie een stuk lastiger.
Vulnerability Scanning met Grype
Een SBOM op zichzelf is slechts een inventaris — een lijst. De echte waarde ontstaat wanneer je die inventaris koppelt aan kwetsbaarheidsdatabases om te zien welke componenten bekende beveiligingsproblemen bevatten. Grype, ook van Anchore, is hiervoor de perfecte aanvulling op Syft.
Installatie van Grype
# Installeer Grype via het officiële installatiescript
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# Verifieer de installatie
grype version
# Update de kwetsbaarheidsdatabase
grype db update
SBOM scannen op kwetsbaarheden
# Scan een eerder gegenereerde SBOM
grype sbom:sbom.spdx.json
# Voorbeeld uitvoer:
# NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
# libcrypto3 3.1.4-r1 3.1.4-r3 apk CVE-2024-0727 Medium
# libssl3 3.1.4-r1 3.1.4-r3 apk CVE-2024-0727 Medium
# zlib 1.3-r2 1.3.1-r0 apk CVE-2023-6992 High
Directe scanning van containerimages
# Scan een containerimage rechtstreeks (zonder eerst een SBOM te genereren)
grype nginx:latest
# Scan een specifieke versie met gedetailleerde uitvoer
grype nginx:1.25 -o json > nginx-vulnerabilities.json
# Scan met tabel-uitvoer voor betere leesbaarheid
grype nginx:latest -o table
Syft en Grype combineren in een pipeline
# Pipe Syft-uitvoer rechtstreeks naar Grype voor een gestroomlijnde workflow
syft alpine:latest -o syft-json | grype
# Dit combineert SBOM-generatie en vulnerability scanning in één stap
# Ideaal voor gebruik in CI/CD-pipelines
Drempelwaarden instellen voor CI/CD
# Laat de pipeline falen bij kwetsbaarheden van severity 'high' of hoger
grype sbom:sbom.spdx.json --fail-on high
# Exit code 0 = geen kwetsbaarheden boven drempel
# Exit code 1 = kwetsbaarheden gevonden boven drempel
# Laat de pipeline alleen falen bij kritieke kwetsbaarheden
grype sbom:sbom.spdx.json --fail-on critical
# Negeer specifieke CVE's die als vals-positief zijn beoordeeld
grype sbom:sbom.spdx.json --fail-on high \
--exclude ./grype-ignore.yaml
Het bestand grype-ignore.yaml voor het uitsluiten van bekende vals-positieven of geaccepteerde risico's:
# .grype.yaml - Grype configuratiebestand
ignore:
# Negeer een specifieke CVE die niet van toepassing is op onze configuratie
- vulnerability: CVE-2023-44487
reason: "HTTP/2 rapid reset - niet van toepassing, nginx staat achter WAF"
# Negeer alle kwetsbaarheden in een specifiek package tot een fixdatum
- package:
name: openssl
version: "3.1.4-r1"
reason: "Update gepland voor volgende release-cyclus"
Resultaten interpreteren en prioriteren
Een veelgemaakte fout: alle kwetsbaarheden als even urgent behandelen. Dat leidt tot alert fatigue en uiteindelijk tot het negeren van echte problemen. Focus in plaats daarvan op deze criteria:
- Severity: Begin met Critical en High. Medium en Low kunnen in de volgende sprint worden opgepakt.
- Fix beschikbaar: Kwetsbaarheden waarvoor een patch beschikbaar is, hebben prioriteit — je kunt direct actie ondernemen.
- Exploiteerbaarheid: Controleer via de EPSS-score (Exploit Prediction Scoring System) hoe waarschijnlijk het is dat een kwetsbaarheid actief wordt misbruikt.
- Context: Een kwetsbaarheid in een bibliotheek die alleen in dev-tools wordt gebruikt, is een heel ander verhaal dan eentje in een productie-webserver.
Sigstore: Keyless Software Signing
Sigstore heeft de manier waarop we software-artefacten ondertekenen en verifiëren behoorlijk veranderd. Geen traditioneel sleutelbeheer meer nodig (hallelujah). Het bestaat uit drie kerncomponenten:
- Cosign: Het command-line tool voor het ondertekenen en verifiëren van containers, binaries en andere artefacten.
- Fulcio: Een certificate authority die kortstondige certificaten uitgeeft op basis van OpenID Connect-identiteiten (zoals GitHub-, Google- of GitLab-accounts).
- Rekor: Een onveranderlijk, transparant logboek (transparency log) waarin alle ondertekeningen worden vastgelegd. Vergelijkbaar met Certificate Transparency voor TLS-certificaten.
Even ter verduidelijking: het keyless aspect betekent niet dat er geen cryptografische sleutels worden gebruikt. Het betekent dat jij als gebruiker geen langlevende sleutels hoeft te beheren. Je bewijst je identiteit via een OIDC-provider, krijgt een kortstondig certificaat van Fulcio, ondertekent je artefact en de handtekening wordt vastgelegd in Rekor. Dit elimineert het risico van gestolen of gelekte signing keys — en dat is een enorme verbetering.
Installatie van Cosign
# Installeer Cosign via het officiële installatiescript
# Download de laatste release van GitHub
COSIGN_VERSION="v2.4.1"
curl -sSfL https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64 \
-o /usr/local/bin/cosign
chmod +x /usr/local/bin/cosign
# Verifieer de installatie
cosign version
# Alternatief: installeer via Go
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
Een containerimage ondertekenen
# Onderteken een containerimage met keyless signing
# Dit opent een browser voor OIDC-authenticatie
cosign sign --yes ghcr.io/gebruiker/applicatie:latest
# Bij gebruik in CI/CD (bijv. GitHub Actions) wordt de OIDC-token
# automatisch gebruikt via workload identity federation
COSIGN_EXPERIMENTAL=1 cosign sign --yes ghcr.io/gebruiker/applicatie:latest
# Onderteken met aanvullende annotaties
cosign sign --yes \
-a "team=platform-engineering" \
-a "environment=productie" \
-a "build-url=${CI_JOB_URL}" \
ghcr.io/gebruiker/applicatie:latest
Een ondertekend image verifiëren
# Verifieer een ondertekend image met specifieke identiteitscontrole
cosign verify ghcr.io/gebruiker/applicatie:latest \
[email protected] \
--certificate-oidc-issuer=https://accounts.google.com
# Verifieer images die zijn ondertekend via GitHub Actions
cosign verify ghcr.io/gebruiker/applicatie:latest \
--certificate-identity="https://github.com/organisatie/repo/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com"
# Verifieer met uitgebreide uitvoer voor debugging
cosign verify ghcr.io/gebruiker/applicatie:latest \
--certificate-identity-regexp=".*@organisatie.nl" \
--certificate-oidc-issuer=https://accounts.google.com \
-o json | python3 -m json.tool
SBOM als attestatie ondertekenen
Dit is naar mijn mening een van de krachtigste functies van Cosign: je kunt een SBOM als attestatie aan een containerimage koppelen. Dat betekent dat de SBOM onlosmakelijk verbonden is met het image, en elke consument kan verifiëren dat de SBOM authentiek is en niet is gemanipuleerd.
# Onderteken een SBOM als attestatie en koppel deze aan het containerimage
cosign attest --predicate sbom.spdx.json \
--type spdxjson \
--yes \
ghcr.io/gebruiker/applicatie:latest
# Voor CycloneDX SBOM's
cosign attest --predicate sbom.cdx.json \
--type cyclonedx \
--yes \
ghcr.io/gebruiker/applicatie:latest
Attestaties verifiëren
# Verifieer de SBOM-attestatie van een containerimage
cosign verify-attestation \
--type spdxjson \
[email protected] \
--certificate-oidc-issuer=https://accounts.google.com \
ghcr.io/gebruiker/applicatie:latest
# Extraheer de SBOM uit de attestatie voor verdere analyse
cosign verify-attestation \
--type spdxjson \
--certificate-identity-regexp=".*@organisatie.nl" \
--certificate-oidc-issuer=https://accounts.google.com \
ghcr.io/gebruiker/applicatie:latest \
| jq -r '.payload' | base64 -d | jq '.predicate'
SLSA Framework: Supply-chain Levels for Software Artifacts
Het SLSA-framework (uitgesproken als "salsa" — ja, echt) biedt een gestructureerd model om de integriteit van je software supply chain stapsgewijs te verbeteren. SLSA definieert vier niveaus van toenemende beveiliging, elk met specifieke eisen aan het bouwproces en de herkomstdocumentatie (provenance).
De vier SLSA-niveaus
SLSA Level 1: Provenance bestaat
Op het eerste niveau wordt er simpelweg documentatie gegenereerd over hoe een artefact is gebouwd. Welke broncode, welk bouwcommando, welke build-omgeving. De provenance hoeft nog niet ondertekend te zijn — het feit dat het überhaupt bestaat is al een flinke stap vooruit.
- Het bouwproces is gedocumenteerd
- Provenance wordt automatisch gegenereerd
- Geen eisen aan de build-omgeving
SLSA Level 2: Gehoste build, ondertekende provenance
Level 2 vereist dat de build plaatsvindt op een gehoste build-service (GitHub Actions, GitLab CI, Jenkins — dat soort platforms) en dat de provenance cryptografisch is ondertekend. Dit maakt manipulatie van de provenance aanzienlijk moeilijker.
- Build wordt uitgevoerd op een gehoste service
- Provenance is ondertekend door de build-service
- De consument kan de authenticiteit van de provenance verifiëren
SLSA Level 3: Geharde builds, niet-vervalsbare provenance
Level 3 voegt hardening toe aan de build-omgeving. De build-service moet garanderen dat de provenance niet kan worden vervalst, zelfs niet door een kwaadwillende projectbeheerder. Builds zijn geïsoleerd en de build-service controleert de integriteit van het proces.
- Builds vinden plaats in geïsoleerde, efemere omgevingen
- Provenance is niet-vervalsbaar (de ontwikkelaar kan het niet manipuleren)
- Broncode-integriteit wordt gecontroleerd
SLSA Level 4: Twee-persoons-review, hermetische builds
Het hoogste niveau. Dit vereist een twee-persoons-review van alle wijzigingen en hermetische builds — builds die volledig geïsoleerd zijn van externe invloeden, waarbij alle inputs vooraf zijn gedefinieerd. Niet makkelijk te bereiken, maar het biedt de sterkste garantie tegen zowel interne als externe aanvallen.
- Alle wijzigingen worden door ten minste twee personen beoordeeld
- Hermetische builds: geen netwerktoegang tijdens het bouwproces
- Reproduceerbare builds worden sterk aanbevolen
- Volledige dependency-vastlegging
SLSA Provenance genereren met GitHub Actions
Het slsa-framework/slsa-github-generator project biedt kant-en-klare GitHub Actions workflows voor het genereren van SLSA Level 3 provenance. Hier is hoe dat eruitziet:
# .github/workflows/slsa-build.yml
name: SLSA Build en Provenance
on:
push:
tags:
- 'v*'
permissions:
contents: read
id-token: write # Vereist voor keyless signing
jobs:
# Stap 1: Bouw het artefact
build:
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.hash.outputs.digest }}
steps:
- name: Checkout broncode
uses: actions/checkout@v4
- name: Bouw de applicatie
run: |
# Bouw uw applicatie
go build -o mijn-applicatie ./cmd/server
- name: Bereken SHA256-hash
id: hash
run: |
DIGEST=$(sha256sum mijn-applicatie | cut -d ' ' -f 1)
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
- name: Upload artefact
uses: actions/upload-artifact@v4
with:
name: mijn-applicatie
path: mijn-applicatie
# Stap 2: Genereer SLSA provenance
provenance:
needs: build
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
with:
base64-subjects: |
${{ needs.build.outputs.digest }} mijn-applicatie
upload-assets: true
Dit genereert een SLSA Level 3 provenance-document in het in-toto attestatieformaat. Het bevat informatie over de broncode-repository, de commit-hash, de build-instructies en de build-omgeving, ondertekend door de GitHub Actions OIDC-provider via Sigstore.
SLSA Provenance verifiëren
# Installeer de SLSA-verifier
go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@latest
# Verifieer het artefact tegen de provenance
slsa-verifier verify-artifact mijn-applicatie \
--provenance-path mijn-applicatie.intoto.jsonl \
--source-uri github.com/organisatie/repo \
--source-tag v1.0.0
# Bij succesvolle verificatie:
# Verified signature against tlog entry index ... at URL: https://rekor.sigstore.dev
# Verified build using builder "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v2.1.0"
# PASSED: Verified SLSA provenance
in-toto: End-to-End Supply Chain Verificatie
in-toto is een framework voor end-to-end verificatie van de software supply chain dat in 2025 de CNCF Graduation-status bereikte. Dat zegt wat over de volwassenheid ervan. Het maakt het mogelijk om het volledige pad van broncode naar uiteindelijk artefact te documenteren en te verifiëren.
in-toto werkt met twee kernconcepten:
- Layouts: Een blauwdruk die beschrijft welke stappen in de supply chain moeten plaatsvinden, in welke volgorde, en door wie. De projecteigenaar definieert dit.
- Links: Bewijsstukken dat een specifieke stap daadwerkelijk is uitgevoerd, inclusief de inputs, outputs en wie de stap heeft uitgevoerd.
Een mooie eigenschap: in-toto complementeert SLSA omdat SLSA het in-toto attestatieformaat gebruikt als basis voor zijn provenance-documenten. Waar SLSA zich richt op het build-proces, biedt in-toto een breder raamwerk dat ook code-review, testing, vulnerability scanning en andere stappen kan omvatten.
# Installeer in-toto (Python-implementatie)
pip install in-toto
# Genereer een sleutelpaar voor een functionary (bijv. een developer of build-systeem)
in-toto-keygen --type ed25519 --name developer
# Registreer een stap in de supply chain
in-toto-run \
--step-name build \
--key developer \
--materials src/main.go go.mod go.sum \
--products mijn-applicatie \
-- go build -o mijn-applicatie ./cmd/server
# Verifieer de volledige supply chain
in-toto-verify \
--layout layout.json \
--layout-key project-eigenaar.pub
Voor organisaties die al SLSA implementeren, biedt in-toto een extra verificatielaag die het hele pad van commit tot deployment kan afdekken — inclusief stappen die buiten het build-proces vallen.
Alles Samen: Een DevSecOps Pipeline
Goed, nu we alle puzzelstukjes hebben bekeken, is het tijd om ze samen te voegen in een complete CI/CD-pipeline. Het doel: SBOM-generatie, vulnerability scanning, image signing en attestatie automatisch uitvoeren bij elke build. Geen handmatige stappen meer.
GitHub Actions Pipeline
# .github/workflows/secure-supply-chain.yml
name: Beveiligde Supply Chain Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
permissions:
contents: read
packages: write
id-token: write # Vereist voor Sigstore keyless signing
security-events: write
jobs:
build-en-beveilig:
runs-on: ubuntu-latest
steps:
# 1. Checkout broncode
- name: Checkout
uses: actions/checkout@v4
# 2. Installeer beveiligingstools
- name: Installeer Syft
uses: anchore/sbom-action/download-syft@v0
- name: Installeer Grype
uses: anchore/scan-action/download-grype@v4
- name: Installeer Cosign
uses: sigstore/cosign-installer@v3
# 3. Bouw en push het containerimage
- name: Log in bij container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Bouw en push containerimage
id: build
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
# 4. Genereer SBOM met Syft
- name: Genereer SBOM
run: |
syft ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-o spdx-json=sbom.spdx.json \
-o cyclonedx-json=sbom.cdx.json
echo "SBOM succesvol gegenereerd"
# 5. Scan op kwetsbaarheden met Grype
- name: Vulnerability scan
run: |
# Scan de SBOM en faal bij kritieke kwetsbaarheden
grype sbom:sbom.spdx.json \
--fail-on critical \
-o table
echo "Geen kritieke kwetsbaarheden gevonden"
# 6. Onderteken het containerimage met Cosign
- name: Onderteken containerimage
run: |
cosign sign --yes \
-a "git-sha=${{ github.sha }}" \
-a "build-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}@${{ steps.build.outputs.digest }}
echo "Image succesvol ondertekend"
# 7. Koppel SBOM als attestatie aan het image
- name: SBOM attestatie
run: |
cosign attest --predicate sbom.spdx.json \
--type spdxjson \
--yes \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}@${{ steps.build.outputs.digest }}
echo "SBOM attestatie succesvol gekoppeld"
# 8. Upload SBOM als build-artefact voor archivering
- name: Archiveer SBOM
uses: actions/upload-artifact@v4
with:
name: sbom-artefacten
path: |
sbom.spdx.json
sbom.cdx.json
retention-days: 365 # Bewaar voor compliance-doeleinden
GitLab CI Pipeline
Gebruikt je organisatie GitLab? Hier is een equivalente pipeline:
# .gitlab-ci.yml
stages:
- build
- beveilig
- onderteken
variables:
IMAGE_TAG: "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}"
# Bouw het containerimage
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
# Genereer SBOM en scan op kwetsbaarheden
beveiligingsscan:
stage: beveilig
image: ubuntu:latest
before_script:
# Installeer Syft en Grype
- curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
- curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
script:
# Genereer SBOM
- syft $IMAGE_TAG -o spdx-json=sbom.spdx.json -o cyclonedx-json=sbom.cdx.json
# Scan op kwetsbaarheden - faal bij 'high' of hoger
- grype sbom:sbom.spdx.json --fail-on high -o table
artifacts:
paths:
- sbom.spdx.json
- sbom.cdx.json
expire_in: 1 year
# Onderteken het image en koppel SBOM-attestatie
ondertekening:
stage: onderteken
image: ubuntu:latest
needs:
- build
- beveiligingsscan
id_tokens:
SIGSTORE_ID_TOKEN:
aud: sigstore
before_script:
- curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 -o /usr/local/bin/cosign
- chmod +x /usr/local/bin/cosign
script:
# Onderteken het containerimage
- cosign sign --yes $IMAGE_TAG
# Koppel SBOM als attestatie
- cosign attest --predicate sbom.spdx.json --type spdxjson --yes $IMAGE_TAG
EU Cyber Resilience Act Compliance
De EU Cyber Resilience Act (CRA) is zonder twijfel de meest impactvolle regelgeving voor software supply chain security in Europa. Als Linux-beheerder of DevOps-engineer moet je weten wat de CRA van je verwacht en — minstens zo belangrijk — wanneer.
Tijdlijn
September 2026: Verplichte melding van actief misbruikte kwetsbaarheden binnen 24 uur aan ENISA en het nationale CSIRT. Binnen 72 uur moet een gedetailleerde melding volgen.
December 2027: Volledige compliance vereist, inclusief SBOM, conformiteitsbeoordeling en doorlopende kwetsbaarheidsbeheer gedurende de gehele levenscyclus van het product.
Praktische compliance-checklist
-
SBOM-generatie en -bewaring
- Genereer een SBOM voor elk softwareproduct en elke release.
- Gebruik een gestandaardiseerd formaat: SPDX of CycloneDX.
- Bewaar SBOM's gedurende minimaal 10 jaar of de levensduur van het product.
- De SBOM moet beschikbaar zijn voor markttoezichtautoriteiten op verzoek.
- Documenteer ten minste de top-level dependencies van het product.
-
Kwetsbaarheidsmelding (vanaf september 2026)
- Implementeer een proces voor het melden van actief misbruikte kwetsbaarheden.
- Eerste melding aan ENISA en nationaal CSIRT binnen 24 uur na ontdekking.
- Gedetailleerde melding binnen 72 uur inclusief CVE-ID, getroffen versies en mitigatiemaatregelen.
- Eindmelding inclusief root cause analysis binnen 14 dagen.
- Stel een contactpunt aan voor kwetsbaarheidscoördinatie.
-
Machineleesbare formaatvereisten
- SBOM's moeten machineleesbaar zijn (JSON of XML).
- Gebruik Package URLs (purl) als standaard identifiers voor componenten.
- Integreer VEX-documenten (Vulnerability Exploitability eXchange) om de toepasbaarheid van kwetsbaarheden te communiceren.
-
Boetes en sancties
- Tot €15 miljoen of 2,5% van de wereldwijde jaaromzet voor niet-naleving van de essentiële beveiligingseisen.
- Tot €10 miljoen of 2% van de wereldwijde jaaromzet voor andere overtredingen.
- Markttoezichtautoriteiten kunnen producten van de markt laten halen.
Automatisering van CRA-compliance
#!/bin/bash
# cra-compliance-check.sh
# Script voor het controleren van CRA-compliance van een softwareproduct
set -euo pipefail
PRODUCT_IMAGE="$1"
OUTPUT_DIR="./cra-compliance-$(date +%Y%m%d)"
mkdir -p "${OUTPUT_DIR}"
echo "=== CRA Compliance Check voor: ${PRODUCT_IMAGE} ==="
# 1. Genereer SBOM in beide standaardformaten
echo "[1/5] SBOM genereren..."
syft "${PRODUCT_IMAGE}" \
-o spdx-json="${OUTPUT_DIR}/sbom.spdx.json" \
-o cyclonedx-json="${OUTPUT_DIR}/sbom.cdx.json"
# 2. Valideer SBOM-volledigheid
echo "[2/5] SBOM valideren..."
PACKAGE_COUNT=$(cat "${OUTPUT_DIR}/sbom.spdx.json" | python3 -c "
import json, sys
data = json.load(sys.stdin)
packages = data.get('packages', [])
print(len(packages))
")
echo " Aantal gedetecteerde packages: ${PACKAGE_COUNT}"
# 3. Vulnerability scan uitvoeren
echo "[3/5] Kwetsbaarheidsscan uitvoeren..."
grype sbom:"${OUTPUT_DIR}/sbom.spdx.json" \
-o json > "${OUTPUT_DIR}/vulnerabilities.json" 2>/dev/null || true
# Tel kwetsbaarheden per severity
echo " Kwetsbaarheden per severity:"
cat "${OUTPUT_DIR}/vulnerabilities.json" | python3 -c "
import json, sys
from collections import Counter
data = json.load(sys.stdin)
matches = data.get('matches', [])
severities = Counter(m['vulnerability']['severity'] for m in matches)
for sev in ['Critical', 'High', 'Medium', 'Low', 'Negligible']:
print(f' {sev}: {severities.get(sev, 0)}')
"
# 4. Controleer of image is ondertekend
echo "[4/5] Handtekening controleren..."
if cosign verify "${PRODUCT_IMAGE}" \
--certificate-identity-regexp=".*" \
--certificate-oidc-issuer-regexp=".*" > /dev/null 2>&1; then
echo " Image is ondertekend"
else
echo " WAARSCHUWING: Image is NIET ondertekend"
fi
# 5. Genereer compliance-rapport
echo "[5/5] Compliance-rapport genereren..."
cat > "${OUTPUT_DIR}/cra-rapport.json" << EOF
{
"product": "${PRODUCT_IMAGE}",
"datum": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"sbom_spdx": "sbom.spdx.json",
"sbom_cyclonedx": "sbom.cdx.json",
"vulnerability_rapport": "vulnerabilities.json",
"aantal_componenten": ${PACKAGE_COUNT},
"formaat_conformiteit": "SPDX-2.3 + CycloneDX-1.5"
}
EOF
echo ""
echo "=== Compliance-rapport opgeslagen in: ${OUTPUT_DIR} ==="
Best Practices en Aanbevelingen
Supply chain security implementeren is geen eenmalig project dat je afvinkt en vergeet. Het is een doorlopend proces. Hieronder de best practices die ik iedere organisatie zou aanraden.
1. Automatiseer SBOM-generatie in CI/CD
Een SBOM heeft alleen waarde als deze consistent en automatisch wordt gegenereerd bij elke build. Handmatige SBOM-generatie is foutgevoelig en schaalt simpelweg niet. Integreer Syft (of een vergelijkbare tool) als vaste stap in je pipeline — niet als optionele stap die je "later nog wel toevoegt."
# Voorbeeld: SBOM genereren als onderdeel van de Docker-build
# Voeg dit toe aan uw Dockerfile als multi-stage build
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o applicatie ./cmd/server
# Genereer SBOM tijdens de build
FROM anchore/syft:latest AS sbom-generator
COPY --from=builder /app /scan-target
RUN syft dir:/scan-target -o spdx-json=/sbom.spdx.json
# Definitief productie-image
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/applicatie /applicatie
COPY --from=sbom-generator /sbom.spdx.json /sbom.spdx.json
ENTRYPOINT ["/applicatie"]
2. Pin dependencies en verifieer checksums
Gebruik altijd exacte versienummers in plaats van bereiken of latest-tags. Klinkt als een open deur, maar je zou versteld staan hoe vaak dit nog misgaat.
# Go: gebruik go.sum voor automatische hash-verificatie
go mod verify
# Python: genereer hashes voor pip-dependencies
pip install --require-hashes -r requirements.txt
# Voorbeeld requirements.txt met hashes:
# requests==2.31.0 \
# --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003eb
# Node.js: gebruik npm ci in plaats van npm install
# npm ci gebruikt package-lock.json voor exacte versies
npm ci --ignore-scripts
# Container: pin images op digest in plaats van tag
# In plaats van: FROM nginx:latest
FROM nginx@sha256:abc123def456...
3. Gebruik reproduceerbare builds
Reproduceerbare builds garanderen dat dezelfde broncode altijd hetzelfde binaire resultaat oplevert, ongeacht wanneer of waar de build wordt uitgevoerd. Dit maakt het mogelijk om te verifiëren dat een binary werkelijk is gebouwd uit de gepubliceerde broncode.
- Elimineer niet-determinisme: sorteer bestanden, gebruik vaste tijdstempels, vermijd random seeds.
- Gebruik hermetische build-omgevingen die geen netwerktoegang hebben tijdens het bouwproces.
- Documenteer de exacte build-omgeving inclusief compiler-versie, OS-versie en alle tools.
- Overweeg tools zoals
reprotest(Debian) om de reproduceerbaarheid van je builds te testen.
4. Monitor continu op nieuwe CVE's
Kwetsbaarheden worden voortdurend ontdekt. Een SBOM die vandaag schoon is, kan morgen kritieke kwetsbaarheden bevatten. Dat klinkt misschien alarmerend, maar het is gewoon de realiteit. Implementeer continue monitoring:
# Crontab-voorbeeld voor dagelijkse kwetsbaarheidsscan
# Scan alle productie-images dagelijks en stuur alerts bij nieuwe kwetsbaarheden
# /etc/cron.d/vulnerability-scan
0 6 * * * root /opt/scripts/dagelijkse-scan.sh
# /opt/scripts/dagelijkse-scan.sh
#!/bin/bash
set -euo pipefail
SBOM_DIR="/var/lib/sbom"
ALERT_EMAIL="[email protected]"
# Update de Grype-database
grype db update
# Scan alle opgeslagen SBOM's
for sbom in "${SBOM_DIR}"/*.spdx.json; do
PRODUCT=$(basename "${sbom}" .spdx.json)
RESULT=$(grype "sbom:${sbom}" --fail-on high 2>&1) || {
echo "ALERT: Nieuwe kwetsbaarheden gevonden in ${PRODUCT}" | \
mail -s "[SECURITY] Kwetsbaarheden in ${PRODUCT}" "${ALERT_EMAIL}"
echo "${RESULT}" | \
mail -s "[SECURITY] Details: ${PRODUCT}" "${ALERT_EMAIL}"
}
done
5. Implementeer least-privilege in build-systemen
Build-systemen zijn een aantrekkelijk doelwit voor aanvallers. Logisch ook — als je het build-systeem controleert, controleer je alles wat eruit komt. Minimaliseer de impact van een compromittering door het principe van minimale rechten toe te passen:
- Gebruik ephemeral build-agents die na elke build worden vernietigd.
- Beperk de netwerktoegang van build-agents tot alleen noodzakelijke registries en repositories.
- Gebruik workload identity federation in plaats van langlevende service-account-sleutels.
- Implementeer separate rollen voor build, sign en deploy — één compromittering mag niet de hele keten treffen.
- Audit alle toegang tot het build-systeem en review regelmatig de permissies.
# Voorbeeld: GitHub Actions met minimale permissies per job
jobs:
build:
permissions:
contents: read # Alleen lezen van broncode
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make build
scan:
needs: build
permissions:
contents: read
security-events: write # Alleen schrijven van scan-resultaten
runs-on: ubuntu-latest
steps:
- run: grype sbom:sbom.json
sign:
needs: scan
permissions:
packages: write # Alleen schrijven naar package registry
id-token: write # Alleen voor keyless signing
runs-on: ubuntu-latest
steps:
- run: cosign sign --yes $IMAGE
6. Voer regelmatige beveiligingsaudits uit
Naast geautomatiseerde scanning is het echt belangrijk om regelmatig handmatige beveiligingsaudits uit te voeren. Automatisering vangt veel op, maar niet alles.
- Dependency review: Controleer regelmatig of al je dependencies nog actief worden onderhouden. Verlaten projecten vormen een risico — de XZ Utils-casus bewees dat overduidelijk.
- Toegangscontrole-audit: Review wie toegang heeft tot je build-systemen, package registries en signing-sleutels.
- Supply chain mapping: Breng je volledige supply chain in kaart, inclusief transitive dependencies, build-tools en infrastructuurcomponenten.
- Incident response planning: Zorg dat je een plan hebt voor het geval een dependency wordt gecompromitteerd. Hoe snel kun je identificeren welke systemen zijn getroffen?
7. Implementeer Admission Control voor Kubernetes
Als je Kubernetes gebruikt (en de kans is groot dat je dat doet), implementeer dan admission controllers die alleen ondertekende en gescande images toelaten:
# Voorbeeld: Kyverno-beleid dat alleen ondertekende images toestaat
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verifieer-image-handtekening
spec:
validationFailureAction: Enforce
background: false
rules:
- name: controleer-cosign-handtekening
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/organisatie/*"
attestors:
- entries:
- keyless:
subject: "*@organisatie.nl"
issuer: "https://accounts.google.com"
rekor:
url: https://rekor.sigstore.dev
attestations:
- type: spdxjson
conditions:
- all:
- key: "{{ creationTimestamp }}"
operator: GreaterThan
value: "2026-01-01T00:00:00Z"
message: "SBOM-attestatie is vereist en moet recent zijn"
Veelgestelde Vragen
Is open-source software vrijgesteld van de CRA?
Gedeeltelijk, en dit is een punt dat veel verwarring veroorzaakt. De CRA maakt een uitzondering voor open-source software die buiten commerciële activiteiten wordt ontwikkeld. Maar zodra open-source software deel uitmaakt van een commercieel product of dienst, valt het wél onder de CRA-vereisten. Als je organisatie open-source software distribueert als onderdeel van je product, ben jij verantwoordelijk voor de compliance ervan.
Hoe verhouden SPDX en CycloneDX zich tot de CRA?
De CRA schrijft geen specifiek SBOM-formaat voor, maar vereist wel dat SBOM's machineleesbaar zijn. Zowel SPDX als CycloneDX voldoen aan deze eis. Mijn advies: genereer voorlopig beide formaten totdat er meer duidelijkheid komt vanuit de Europese Commissie over voorkeursformaten. Het kost je nauwelijks extra moeite.
Kan ik Sigstore gebruiken in een air-gapped omgeving?
Ja, dat kan. Sigstore kan worden gedeployed als private instantie binnen je eigen infrastructuur. Het Sigstore Scaffolding-project biedt Helm-charts voor het deployen van Fulcio, Rekor en CT-log binnen je eigen Kubernetes-cluster. Daarnaast ondersteunt Cosign ook traditionele sleutelparen als alternatief voor keyless signing.
# Cosign met een eigen sleutelpaar (voor air-gapped omgevingen)
# Genereer een sleutelpaar
cosign generate-key-pair
# Onderteken met de privésleutel
cosign sign --key cosign.key ghcr.io/organisatie/app:latest
# Verifieer met de publieke sleutel
cosign verify --key cosign.pub ghcr.io/organisatie/app:latest
Conclusie
Software supply chain security is in 2026 geen luxe meer — het is een fundamentele vereiste. De combinatie van steeds geavanceerdere supply chain-aanvallen en stricter wordende regelgeving zoals de EU Cyber Resilience Act maakt het onvermijdelijk om nu te handelen.
Het goede nieuws? De tools en frameworks zijn volwassen, open-source en gratis beschikbaar. Met Syft en Grype genereer en scan je SBOM's in seconden. Sigstore met Cosign maakt sleutelbeheer bij het ondertekenen overbodig. En het SLSA-framework biedt een helder groeipad van basis-provenance tot volledig geharde builds.
De kernboodschap is simpel: begin vandaag. Start met het genereren van SBOM's voor je belangrijkste applicaties. Integreer vulnerability scanning in je CI/CD-pipeline. Implementeer image signing voor je containerworkloads. En werk stapsgewijs toe naar hogere SLSA-niveaus.
Elke stap die je zet, verkleint je aanvalsoppervlak en brengt je dichter bij compliance met de aankomende Europese regelgeving. De software supply chain is net zo sterk als de zwakste schakel — zorg ervoor dat jouw schakel niet de zwakste is.