SBOM och Cosign 2026: Signera containerbilder och verifiera supply chain i CI/CD

Så bygger du en pipeline som genererar SBOM med Syft, signerar containerbilder keyless med Cosign och verifierar Sigstore-attesteringar i Kubernetes med Kyverno. Hela flödet från commit till admission control.

SBOM & Cosign CI/CD Guide 2026

Uppdaterad: 25 juli 2026

En SBOM (Software Bill of Materials) är en maskinläsbar inventering av allt som ingår i en artefakt: bibliotek, versioner, licenser och hashvärden. Cosign är Sigstore-verktyget som signerar och verifierar containerbilder och attesteringar utan att du behöver hantera egna nycklar. Kombinera dem i CI/CD och du får en supply chain som är både observerbar (SBOM) och verifierbar (signatur). Det är faktiskt precis det som EU:s Cyber Resilience Act börjar tillämpa fullt ut 2027, och redan under 2026 dyker kravet upp i svenska upphandlingar.

  • Syft (från Anchore) genererar SBOM i CycloneDX eller SPDX från containerbilder, filsystem eller källkodskatalogen. Kör det i CI direkt efter docker build.
  • Cosign v2.4+ stöder keyless signing via OIDC. GitHub Actions, GitLab CI och Buildkite kan signera utan att du hanterar privata nycklar; signaturen plus en post i Rekor transparency log räcker som bevis.
  • SLSA v1.0 definierar fyra byggnivåer; nivå 2 kräver signerad provenance och en betrodd byggmiljö. Det är vad du realistiskt hamnar på med GitHub Actions plus Cosign attest.
  • Grype läser samma SBOM som Syft producerade och matchar mot NVD, GHSA och OSV. Du slipper skanna om filsystemet varje gång och kan filtrera bort brus med en .grype.yaml.
  • På Kubernetes verifierar Kyverno eller Sigstore Policy Controller signaturer vid admission. En osignerad bild blockeras innan pod:en startar.
  • Räkna med falska positiva i första körningen: 30 till 50 procent av CVE:er i basbilder är antingen inte exponerade eller redan patchade upstream utan att NVD hunnit uppdatera.

Vad är en SBOM och varför behövs den 2026?

En SBOM är en formell, maskinläsbar lista över komponenter i en programvaruartefakt: direkta beroenden, transitiva beroenden, versioner, licens, ursprung och kryptografiska hashvärden. De två dominerande formaten 2026 är CycloneDX 1.6 från OWASP och SPDX 3.0 från Linux Foundation. Båda är JSON-baserade, båda accepteras av amerikanska CISA och EU:s regleringar, och båda genereras av samma verktyg. Du kan lika gärna emittera båda och låta konsumenten välja.

Så varför är SBOM plötsligt obligatoriskt? Tre pågående trender driver på. EU:s Cyber Resilience Act (CRA) som gäller från december 2027 kräver SBOM för alla produkter med digitala element som säljs i unionen. Amerikanska Executive Order 14028 kräver redan SBOM för federala inköp. Och Log4Shell 2021 visade att den som inte visste vilka bibliotek som fanns var i containern förlorade dygn på att jaga fram exponering. I praktiken betyder det att kunder, särskilt offentlig sektor i Sverige, börjat kräva SBOM som artefakt vid varje release. Inte som en engångsbilaga.

Utan SBOM kan du inte svara på frågan "är vi drabbade av CVE-2026-XXXX?" utan att bygga om och skanna varje bild. Med SBOM lagrad som artefakt kan du köra en Grype-fråga mot alla sparade SBOM:er och få svar på minuter. Jag har själv suttit med det motsatta scenariot en helg efter en zero-day, och kan lova att man aldrig vill uppleva det igen.

# CI-snutt: generera CycloneDX SBOM för en Go-modul
- name: Generate source SBOM
  run: syft dir:. -o cyclonedx-json=sbom-source.json

Sigstore-ekosystemet: Cosign, Rekor och Fulcio förklarat

Sigstore är ett OpenSSF-projekt som gör kodsignering till en självklarhet snarare än en ceremoni. Tre komponenter samverkar. Fulcio är en certifikatutfärdare som byter ett OIDC-token (från GitHub, GitLab, Google, Microsoft eller din egen IdP) mot ett kortlivat X.509-certifikat. Livslängden är tio minuter, precis nog för att signera. Cosign är CLI:t du kör i pipelinen; det pratar med Fulcio, skapar signaturen och laddar upp den vid sidan av bilden i registret. Rekor är en tamper-evident transparency log (samma teknik som Certificate Transparency) där varje signatur registreras publikt. En angripare som stjäl en signeringsnyckel kan alltså inte återsignera i det förflutna utan att lämna spår.

Det viktiga skiftet i Cosign v2 (från januari 2024, stabilt hela 2026) är att keyless signing är default. Du behöver inte generera cosign.key, inte lagra den i en KMS, inte rotera den. Din pipeline verifierar sig själv mot Fulcio via arbetsflödets OIDC-identitet. Signaturen har till exempel identiteten https://github.com/acme/repo/.github/workflows/release.yml@refs/tags/v1.2.3. Vid verifiering låser du policyn till just den identiteten, vilket är starkare än en delad nyckel som kan läcka.

Om du redan har HashiCorp Vault eller kör i en avskild miljö där du inte kan nå Sigstores publika instans, går det bra att köra en privat Sigstore-stack (Fulcio, Rekor och trust root) internt. Läs mer i vår jämförelse av Vault och OpenBao för hemlighetshantering på Linux om du väger på-plats-alternativ.

# Verifiera Cosigns signatur på en publik bild
cosign verify \
  --certificate-identity-regexp 'https://github.com/acme/.*' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  ghcr.io/acme/api:1.4.0

Generera SBOM med Syft i pipelinen

Syft från Anchore är förstahandsvalet 2026 eftersom det stöder både OCI-bilder, filsystem, arkiv och Rust, Go, Java, npm, PyPI, RubyGems, Alpine, Debian och RPM-paket i en och samma binär. Installera det via ett releaseskript istället för curl | bash. Pinna versionen så att en komprometterad releasekanal inte kan injicera oönskad kod i CI:

# Installera Syft med SHA256-verifiering
SYFT_VERSION="1.14.2"
curl -sSfL -o syft.tar.gz \
  "https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_linux_amd64.tar.gz"
echo "a5d9ee... syft.tar.gz" | sha256sum -c -
tar -xzf syft.tar.gz syft
sudo install -m 0755 syft /usr/local/bin/syft

Kör Syft mot den byggda containerbilden, inte källkoden, så att systembibliotek från basbilden kommer med. I en typisk multistage-build hämtar du gcr.io/distroless/base som runtime. Utan att skanna den slutliga bilden missar du glibc-CVE:er som finns i distroless.

# GitHub Actions: generera SBOM efter build och ladda upp som artefakt
- name: Build image
  run: docker build -t $IMAGE:$SHA .

- name: Generate SBOM (CycloneDX + SPDX)
  run: |
    syft "$IMAGE:$SHA" -o cyclonedx-json=sbom.cdx.json
    syft "$IMAGE:$SHA" -o spdx-json=sbom.spdx.json

- name: Upload SBOM artefacts
  uses: actions/upload-artifact@v4
  with:
    name: sbom
    path: sbom.*.json

Om du kör GitLab CI blir motsvarande sak ännu enklare eftersom Syft finns som container:

generate-sbom:
  stage: sbom
  image: anchore/syft:v1.14.2
  script:
    - syft "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" -o cyclonedx-json=sbom.cdx.json
  artifacts:
    paths: [sbom.cdx.json]
    expire_in: 90 days

Signera containerbilder keyless med Cosign

När bilden är pushad till registret signerar du den med Cosigns keyless-flöde. Det kräver att jobbet har id-token: write i GitHub Actions-permissions eller att GitLab CI:s id_tokens-block är konfigurerat. Cosign begär då ett OIDC-token, växlar det mot ett Fulcio-certifikat och signerar bildens digest. Inte taggen, alltså. På så vis avvisas en efteråt ompushad bild med samma tagg.

# .github/workflows/release.yml (utdrag)
permissions:
  contents: read
  packages: write
  id-token: write  # KRÄVS för keyless signing

steps:
  - uses: sigstore/cosign-installer@v3
    with: { cosign-release: 'v2.4.1' }

  - name: Push image
    run: |
      docker push ghcr.io/acme/api:${{ github.sha }}
      DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/acme/api:${{ github.sha }})
      echo "DIGEST=$DIGEST" >> "$GITHUB_ENV"

  - name: Sign image (keyless)
    env:
      COSIGN_EXPERIMENTAL: "1"
    run: cosign sign --yes "$DIGEST"

  - name: Attest SBOM
    run: cosign attest --yes --predicate sbom.cdx.json \
      --type cyclonedx "$DIGEST"

Signaturen och attesteringen hamnar som separata OCI-artefakter i registret (taggar med suffix .sig respektive .att), och en post skrivs till Rekor. Du kan slå upp den senare med:

rekor-cli search --sha $(echo -n "ghcr.io/acme/api@$DIGEST" | sha256sum | cut -d' ' -f1)

Skanna sårbarheter med Grype baserat på SBOM

Grype (även den från Anchore) matchar en SBOM mot flera sårbarhetsdatabaser: NVD, GitHub Security Advisories (GHSA), Alpine secdb, Debian security tracker, RHEL OVAL och OSV. Fördelen med att köra Grype mot en redan genererad SBOM istället för mot bilden direkt är dubbel. Du sparar CI-minuter (SBOM finns redan), och du kan revidera gamla releaser genom att skanna sparade SBOM:er utan att bygga om.

# Skanna befintlig SBOM och fail:a bygget vid High/Critical
grype sbom:./sbom.cdx.json \
  --fail-on high \
  --output table \
  --output sarif=grype.sarif

SARIF-utdata kan laddas upp till GitHub Advanced Security eller GitLab Vulnerability Report så att findings visas i PR-vyn snarare än begravda i loggen. För en pragmatisk pipeline: låt informations-fynd bara logga, medan high och critical failar bygget. Annars kommer utvecklarna att stänga av skannern efter en vecka. Det har jag sett hända.

# GitHub Actions: ladda upp SARIF till Code Scanning
- name: Upload Grype SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: grype.sarif
    category: grype

Ett vanligt fel är att köra både Trivy och Grype och undra varför resultaten skiljer sig. De använder olika matchningsheuristik för Alpine och Distroless. Välj en, dokumentera valet, och komplettera med eBPF-baserad runtime-säkerhet med Falco och Tetragon för det som ändå slinker igenom statisk analys.

SLSA v1.0 och attesteringar i praktiken

SLSA (Supply-chain Levels for Software Artifacts) v1.0 är OpenSSF:s ramverk för byggintegritet, uppdelat i fyra nivåer där L1 är "man vet vem som byggde" och L3 är "byggmiljön är härdad och kan inte manipuleras". För de flesta team är L2 realistiskt och kravställs oftast. Det innebär att bygget skedde i en betrodd miljö (till exempel GitHub-hosted runner) och att en signerad provenance-attestering genereras.

Slsa-github-generator producerar provenance-JSON automatiskt när du använder slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml som callable workflow. Alternativt kan du generera in-toto attesteringar manuellt med Cosign:

# Skapa och signera en provenance-attestering
cat > provenance.json <<EOF
{
  "buildType": "https://github.com/actions/runner",
  "builder": { "id": "https://github.com/actions/runner/v2.319.1" },
  "invocation": {
    "configSource": { "uri": "git+https://github.com/acme/api", "digest": {"sha1": "$GITHUB_SHA"} }
  }
}
EOF
cosign attest --yes --predicate provenance.json \
  --type slsaprovenance "$DIGEST"

Konsumentsidan verifierar med cosign verify-attestation och kan använda cue eller rego-policyer för att kräva specifika builder-identiteter. Om ditt team ännu inte hunnit dit, börja med att bara generera attesteringen. Konsumenten kan ratchet-a upp verifieringen när fler leverantörer stöder det.

Så håller du ner bruset från skannern

Det första du märker när en SBOM-skanner släpps loss på en produktionsbild är antalet CVE:er. Räkna med 100 till 300 stycken på en genomsnittlig Debian-baserad bild, och de flesta är faktiskt inte exploaterbara i din kontext. Här är den pragmatiska filtreringsordningen jag brukar följa i skarpa pipelines:

  1. Uteslut fastställda ej-exploaterbara med en .grype.yaml som listar CVE-ID med kommentar. Kräv att varje ignorering har ett Jira-ID eller en referens till en VEX-fil.
  2. Håll dig till fixade CVE:er initialt: kör grype --only-fixed. En ofixad CVE utan patch kan du ändå inte åtgärda idag. Flytta den till en separat rapport, blockera inte bygget.
  3. Använd VEX (Vulnerability Exploitability eXchange). OpenVEX är det snabbast växande formatet 2026 och stöds av Grype v0.79+. Publicera VEX-filer intill din SBOM så att konsumenter ser att en CVE finns, men att din kod inte anropar den sårbara vägen.
# .grype.yaml — undertryck kända icke-exploaterbara CVE:er
ignore:
  - vulnerability: CVE-2024-45491
    reason: "libexpat i basbilden, inte exponerat mot nätverk. JIRA SEC-482"
    package:
      name: libexpat1
  - vulnerability: CVE-2025-1234
    reason: "Fixad upstream, väntar på Debian backport"

Ett vanligt misstag är att auto-skapa dispenser för allt som failar första bygget. Då blir listan snart 500 rader lång och ingen läser den. Bättre är att lyfta tröskeln till critical under vecka ett, sedan sänka till high när de riktiga fynden är fixade, och slutligen till medium. Se det som schemaläggning, inte som en on/off-brytare.

Verifiera signaturer vid deployment med Kyverno

Att signera i CI är halva jobbet. Den andra halvan är att blockera osignerade bilder vid deployment. På Kubernetes har du två mogna alternativ 2026: Kyverno (bredare policyfunktion) och Sigstore Policy Controller (fokuserad på signaturer). Kyverno är den mer generella lösningen om du också vill enforce:a labels, resource limits och nätverkspolicyer via samma admissionscontroller.

# Kyverno ClusterPolicy — kräv Cosign keyless-signatur
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-cosign-signatures
spec:
  validationFailureAction: Enforce
  webhookTimeoutSeconds: 30
  rules:
  - name: verify-signature
    match:
      any:
      - resources: { kinds: [Pod] }
    verifyImages:
    - imageReferences: ["ghcr.io/acme/*"]
      attestors:
      - entries:
        - keyless:
            subject: "https://github.com/acme/*/.github/workflows/release.yml@refs/tags/v*"
            issuer: "https://token.actions.githubusercontent.com"
            rekor:
              url: https://rekor.sigstore.dev

Policyn matchar identiteten så att bara bilder byggda från en tagg via release-workflow accepteras. En utvecklare som pushar en handbyggd bild till registret kommer inte förbi admissionscontrollern, och felet blir tydligt vid kubectl apply istället för att felsöka i produktion. Kombinera detta med seccomp- och AppArmor-profiler för att begränsa körtidsblastradien så att en signerad bild med sårbarhet ändå inte får lov att göra syscalls den inte behöver.

Vanliga frågor

Vad är skillnaden mellan SBOM och SLSA?

SBOM svarar på vad som finns i en artefakt (komponentlista), SLSA svarar på hur den byggdes (byggintegritet och provenance). De kompletterar varandra. SBOM utan SLSA säger inget om huruvida listan är att lita på; SLSA utan SBOM säger inget om innehållet.

CycloneDX eller SPDX, vilket SBOM-format ska jag välja?

CycloneDX 1.6 har starkare stöd för säkerhetsanvändningsfall (VEX, exploitability) och är snabbare på att lägga till fält. SPDX 3.0 är bredare för licenskonformans och krävs ofta i statliga upphandlingar. Syft kan generera båda på en gång, så gör det och låt konsumenten välja.

Kan Cosign signera bilder utan att röra Sigstores publika tjänster?

Ja. Du kan köra Fulcio, Rekor och en egen trust root internt och peka Cosign mot dem med --fulcio-url, --rekor-url och SIGSTORE_ROOT_FILE. Alternativt går klassisk key-based signing kvar, men då förlorar du transparency-log-fördelen och måste hantera nyckelrotation.

Måste jag failsa bygget på alla CVE:er skannern hittar?

Nej. Det leder till att teamet stänger av skannern. Failsa på high eller critical med fix tillgänglig, rapportera övrigt separat, och använd OpenVEX för att formellt märka fynd som ej exploaterbara i din kontext. Kravet är att veta, inte att åtgärda allt samma dag.

Fungerar SBOM och Cosign för icke-container-artefakter?

Ja. Syft genererar SBOM för Python-wheel, JAR, tarball, Debian-paket och ren källkod. Cosign kan signera godtyckliga blobbar med cosign sign-blob. Använd det för Helm charts, Terraform-moduler och release-binärer. Samma pipeline, samma verifieringsflöde.

Raj Patel
Om Författaren Raj Patel

DevSecOps engineer who's gradually turning every CI pipeline he sees into a security-checking machine.