Sigstore Cosign v roku 2026: Keyless podpisovanie kontajnerov a SLSA atestácie pre supply chain

Praktický návod ako keyless podpisovať kontajnery cez Sigstore Cosign 2.x. Zahŕňa GitHub Actions OIDC, Fulcio, Rekor, SLSA provenance, overenie podpisov a vynútenie v Kubernetes cez Kyverno admission controller, s príkladmi z pentestov.

Sigstore Cosign Keyless: SLSA Návod 2026

Aktualizované: 29. júna 2026

Sigstore Cosign je open-source nástroj pod hlavičkou Linux Foundation, ktorý kryptograficky podpisuje kontajnerové obrazy a iné OCI artefakty pomocou krátkodobých certifikátov viazaných na OIDC identitu z CI/CD systému, čím nahrádza klasické dlhodobé GPG/KMS kľúče. V tomto návode si ukážeme reálny útok na container supply chain, a potom krok za krokom Cosign keyless flow, SLSA atestácie, overenie podpisov a vynútenie politiky na úrovni Kubernetes admission controllera. Cieľ je jednoduchý: nikdy nespustiť obraz, ktorému neviete dokázať pôvod.

  • Cosign 2.x používa keyless podpisovanie cez Fulcio (krátkodobý X.509 certifikát) a Rekor (verejný transparency log), takže v CI nezostávajú dlhodobé súkromné kľúče.
  • Vždy podpisujte digest (@sha256:...), nie tag. Tagy sú prepisovateľné a otvárajú útok cez „tag mutability".
  • SLSA Build Level 3 vyžaduje, aby provenance generoval build služba a aby bol kryptograficky podpísaný. Cosign s GitHub Actions OIDC tieto požiadavky pokrýva.
  • Samotné podpisovanie nestačí: bez admission controllera (Sigstore Policy Controller alebo Kyverno) cluster naďalej akceptuje nepodpísané obrazy.
  • Overenie musí kontrolovať konkrétnu identitu workflow (--certificate-identity) aj OIDC vydavateľa. Len „obraz je podpísaný" nestačí, fork útočníka by tiež bol podpísaný.
  • Cosign integruje SBOM (CycloneDX/SPDX) ako in-toto atestácie, čo umožní pri ďalšom Log4Shell zistiť expozíciu naprieč registry v priebehu minút.

Anatómia útoku na container supply chain

Keď si predstavím reálny pentest, supply chain útok takmer nikdy nezačína u obete. Začína v build pipeline tretej strany, typicky cez kompromitovaný GitHub Actions runner alebo prebraný npm/PyPI balík, ktorý sa dostane do base obrazu, ktorý dotyčný tím konzumuje cez FROM. Druhý vektor je registry tampering: niekto s push právami prepíše tag :latest alebo :v1.2.3 tak, aby ukazoval na manifest s pridanou malware vrstvou. Z pohľadu Kubernetes clustera sa nič nezmenilo. Meno tagu sedí, image pull prejde, kontajner naštartuje a útočník má reverse shell skôr, než si stihne SOC vypiť kávu.

Tretí vektor, ktorý vidím v poslednom roku najčastejšie, je „dependency confusion" na úrovni base images: tím interne používa internal/python-base, ale niekto zaregistruje rovnaké meno vo verejnom Docker Hube s vyššou verziou a build pipeline si stiahne útočníkov obraz. Žiadny z týchto útokov nevyžaduje zero-day. Všetky tri stoja na jednom predpoklade: cluster verí, že obraz s daným menom je ten, ktorý sme my postavili. Cosign tento predpoklad ruší a nahrádza ho kryptografickým dôkazom o pôvode (provenance) a integrite. Ak chcete vidieť, ako sa skenovanie zraniteľností a SBOM mapuje na ten istý CI/CD reťazec, pozrite si môj predchádzajúci článok Trivy: skenovanie zraniteľností kontajnerov, IaC a SBOM v CI/CD.

Čo je Sigstore Cosign a ako funguje keyless signing

Sigstore je projekt pod Linux Foundation, ktorý ekosystému open-source dáva tri komponenty: Cosign (klient na podpisovanie a overovanie), Fulcio (certifikačná autorita pre krátkodobé X.509 certifikáty viazané na OIDC identitu) a Rekor (append-only transparency log). Najväčší skok oproti klasickému GPG/KMS modelu je keyless signing: namiesto otázky „vlastníš tento súkromný kľúč?" sa pýtame „si naozaj tá identita (workflow, repository, ref), ktorú deklaruješ?". Krátkodobý kľúč existuje len niekoľko minút počas behu jednej build úlohy a potom je zahodený.

Tok pri keyless signe je presne týchto päť krokov. GitHub Actions vystaví OIDC token, ktorý kryptograficky deklaruje workflow identity (repo, workflow súbor, ref, SHA commitu). Cosign tento token pošle do Fulcio CA, ktorá vystaví X.509 certifikát na ~10 minút. Cosign vygeneruje efemérny kľúčový pár, podpíše digest obrazu, certifikát aj podpis zapíše do Rekoru a kľúč zahodí. Pri overovaní si Cosign stiahne Rekor záznam a Fulcio certifikát a skontroluje, že identita v certifikáte zodpovedá tomu, koho čakáme. Útočník by potreboval súčasne kompromitovať GitHubov OIDC issuer, Fulcio CA aj Rekor log, čo je nesporne ťažšie ako exfiltrovať jeden GPG kľúč zo CI secrets. Tento model je dôvod, prečo CNCF projekty ako Kubernetes a Helm migrovali z PGP na Cosign.

Inštalácia Cosignu na Linuxe

V čase písania (jún 2026) je aktuálna stabilná verzia Cosign 2.4.x. Najjednoduchšia inštalácia na Debian/Ubuntu/RHEL hostiteľa je cez prebuilt binárku z GitHub Releases. Vyhnete sa tak starým balíkom v distribučných repozitároch.

# Stiahnite konkrétnu verziu (skontrolujte https://github.com/sigstore/cosign/releases)
COSIGN_VERSION="2.4.1"
curl -fsSL -o /tmp/cosign \
  "https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64"

# Overte SHA256 voči release stránke
sha256sum /tmp/cosign

sudo install -m 755 /tmp/cosign /usr/local/bin/cosign
cosign version

Na vývojárskej stanici môžete použiť go install github.com/sigstore/cosign/v2/cmd/[email protected] alebo Homebrew (brew install cosign). Vyhnite sa apt install cosign v starších distribúciách. V Debian 12 je verzia 1.x, ktorá nevie SLSA v1.0 atestácie a má iný flag syntax. Po inštalácii odporúčam vytvoriť si testovací registry token pre GHCR alebo Docker Hub a vyskúšať cosign login ghcr.io ešte pred prvým build behom (ušetríte si pol hodiny ladenia v pipeline).

Podpisovanie obrazu v GitHub Actions cez OIDC

Keyless flow vyžaduje, aby workflow mal povolenie id-token: write. Bez toho GitHub Actions nemôže vyžiadať OIDC token od svojho issueru. Toto je najčastejšia príčina, prečo prvý pokus o cosign sign --yes v pipeline zlyhá s chybou „failed to get OIDC token". Druhá podmienka: image musíme adresovať digestom, nie tagom, pretože medzi vybuilďovaním a podpísaním by sa nám mohol tag pod rukami zmeniť (a v praxi sa to pri paralelných pipeline behoch naozaj stáva, sám som to raz odchytával hodinu).

name: build-sign-attest
on:
  push:
    branches: [main]
    tags: ['v*']

permissions:
  contents: read
  packages: write
  id-token: write   # POVINNÉ pre Sigstore OIDC

jobs:
  build:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Install Cosign
        uses: sigstore/cosign-installer@v3
        with:
          cosign-release: 'v2.4.1'

      - name: Build & push (capture digest)
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

      - name: Cosign sign by digest (keyless)
        env:
          DIGEST: ${{ steps.build.outputs.digest }}
          IMAGE: ghcr.io/${{ github.repository }}
        run: |
          cosign sign --yes "${IMAGE}@${DIGEST}"

Po úspešnom behu uvidíte v GHCR popri obraze nový artefakt s príponou .sig. To je signature OCI objekt, ktorý Cosign uložil do registry vedľa samotného obrazu. Záznam v Rekore je verejne dohľadateľný na search.sigstore.dev podľa SHA digestu obrazu, čo je užitočné pri forenznej analýze. Zachytenie digestu z build-push-action a jeho použitie pri signe je kritické. Keď to spravíte cez tag, dostanete sa do race condition.

SLSA provenance a in-toto atestácie

Podpis dokazuje kto obraz vyrobil. Atestácia dokazuje ako ho vyrobil a čo obsahuje. Cosign atestácie sú JSON dokumenty v in-toto formáte s predikátom typu slsaprovenance, spdxjson, cyclonedx alebo vuln, a sú uložené v registry ako samostatný OCI objekt s príponou .att. SLSA v1.0 framework definuje štyri úrovne (L0 až L3), kde L3 vyžaduje, aby provenance generoval build služba (nie vývojár), aby bol neclonable, a aby bol kryptograficky podpísaný. Keyless Cosign s GitHub Actions OIDC tieto požiadavky priamo spĺňa.

# Pridajte do existujúceho workflow po Cosign sign kroku

- name: Generate SBOM (Syft)
  run: |
    syft "ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}" \
      -o spdx-json > sbom.spdx.json

- name: Attest SBOM
  env:
    DIGEST: ${{ steps.build.outputs.digest }}
    IMAGE: ghcr.io/${{ github.repository }}
  run: |
    cosign attest --yes \
      --predicate sbom.spdx.json \
      --type spdxjson \
      "${IMAGE}@${DIGEST}"

- name: Attest SLSA provenance
  env:
    DIGEST: ${{ steps.build.outputs.digest }}
    IMAGE: ghcr.io/${{ github.repository }}
  run: |
    # Provenance.json typicky generuje slsa-github-generator alebo
    # actions/attest-build-provenance — tu predpokladáme, že je
    # k dispozícii ako artefakt z predchádzajúceho kroku.
    cosign attest --yes \
      --predicate provenance.json \
      --type slsaprovenance \
      "${IMAGE}@${DIGEST}"

Pridanou hodnotou SBOM ako podpísanej atestácie je, že pri ďalšom Log4Shell-type incidente sa nemusíte spoliehať na to, čo si pamätáte. cosign download attestation vám stiahne SPDX manifest pre každý obraz v registry a jednoduchý jq dotaz vám povie, kde máte zraniteľnú knižnicu. Mnoho tímov toto v praxi nasaduje súbežne s runtime monitoringom popísaným v článku eBPF bezpečnostný monitoring s Tetragonom. Provenance vám povie čo bežalo, Tetragon vám povie čo to v runtime robilo.

Ako overiť podpis a atestáciu Cosignom

Najčastejšia chyba pri verifikácii je overenie len toho, že obraz nejaký podpis má. To útočníka nezastaví. Keď si fork-ne váš repo, GitHub Actions mu radostne vystaví OIDC token a Cosign mu radostne podpíše obraz. Rozdielom je hodnota claim-u subject v certifikáte: musí presne odkazovať na váš workflow file v hlavnom repozitári a referenciu (tag alebo branch).

# Overenie podpisu — vyžaduje konkrétnu identitu workflow
cosign verify \
  --certificate-identity "https://github.com/your-org/your-repo/.github/workflows/release.yml@refs/tags/v1.2.3" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/your-org/your-repo@sha256:abc123...

# Pre dynamický tag/branch použite regexp variant
cosign verify \
  --certificate-identity-regexp "^https://github.com/your-org/your-repo/\.github/workflows/release\.yml@refs/tags/v.*$" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/your-org/your-repo@sha256:abc123...

# Overenie SLSA atestácie
cosign verify-attestation \
  --type slsaprovenance \
  --certificate-identity-regexp "^https://github.com/your-org/your-repo/.+" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/your-org/your-repo@sha256:abc123...

Vynútenie podpisov v Kubernetes cez Kyverno

Podpisovanie obrazov nemá zmysel, pokiaľ cluster akceptuje aj nepodpísané. Existujú dve hlavné možnosti, ako vynútiť overenie pri admission čase: Sigstore Policy Controller (priamy projekt Sigstore, lightweight) a Kyverno s verifyImages politikou (širší policy engine, ktorý už mnohé tímy používajú na iné kontroly). Pre väčšinu produkčných clustrov v 2026 odporúčam Kyverno, pretože ho zvyčajne máte nasadený aj kvôli network policy či pod security štandardom. Súvisí to aj s témou v článku zabezpečenie kontajnerov: rootless Podman a Seccomp.

apiVersion: kyverno.io/v2beta1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: Enforce   # alebo Audit pre rollout
  background: false
  rules:
  - name: verify-signature
    match:
      any:
      - resources:
          kinds: [Pod]
          namespaces: [production, staging]
    verifyImages:
    - imageReferences:
      - "ghcr.io/your-org/*"
      mutateDigest: true       # prepíše tag na sha256 digest
      required: true
      attestors:
      - entries:
        - keyless:
            subject: "https://github.com/your-org/*/.github/workflows/release.yml@refs/tags/v*"
            issuer: "https://token.actions.githubusercontent.com"
            rekor:
              url: https://rekor.sigstore.dev

Kľúčové detaily: mutateDigest: true prepíše každý :tag na @sha256:... hneď pri admission a eliminuje tag mutability ako útočný vektor priamo na úrovni clustra. subject používa glob (nie regex) a kontroluje, že obraz musel byť podpísaný workflow súborom v rámci vašej GitHub org. Pri rollout do existujúceho prostredia vždy začnite s validationFailureAction: Audit, nazbierajte PolicyReport objekty a riešte unsigned obrazy postupne. Prepnutie na Enforce v deň 1 zhasne všetko, vrátane vendor obrazov, ktoré ešte nikto nepodpisuje (tento bug som už raz spravil, neopakujte ho). Pre vendor obrazy, ktoré nemajú vlastné Sigstore podpisy, je typický pattern „re-sign on import": pri pull-e do internej registry ich podpíše vaša build pipeline a downstream politika kontroluje vašu identitu.

Pasce, na ktoré útočníci stavajú

Štyri konkrétne diery, ktoré pri pentestoch zneužívam najčastejšie aj v 2026, a ako ich zatvoriť:

1. Podpisovanie tagov namiesto digestov

Tagy sú mutable. Keď podpíšete :v1.2.3 a niekto neskôr ten tag prepíše, podpis sa „odpojí" od pôvodného manifestu a nový manifest beží unsigned. Riešenie: vždy cosign sign IMAGE@sha256:... a v Kyverno mutateDigest: true.

2. Verifikácia bez identity check

Fork útočníka má rovnakú GitHub Actions OIDC infraštruktúru. Ak vaša pipeline overuje len, že podpis existuje, fork útočníka prejde. Riešenie: vždy --certificate-identity s plnou cestou k workflow alebo regexp obmedzený na vašu org.

3. Dlhodobý kľúč v CI secrets

Niektoré tímy ešte stále používajú cosign sign --key cosign.key s privátnym kľúčom v GitHub Secrets. CI secrets sú typicky logované, kešované a replikované, takže k privátnemu kľúču má prístup každý workflow vrátane PR z forks. Riešenie: keyless flow, alebo ak musíte mať kľúče, držte ich v KMS (AWS KMS, GCP KMS, HashiCorp Vault) a Cosign volajte cez --key kms://....

4. Žiadne monitorovanie Rekoru

Rekor je verejný log. Keď vidíte záznam s vašou GitHub identity, ktorý neprislúcha k známemu buildu, niekto získal token vášho workflow. Riešenie: automatizovaná kontrola Rekor entries pre vašu org cez rekor-cli search --email '*@your-org.com' alebo prostredníctvom Cosign monitor služby.

Privátna Sigstore infraštruktúra a air-gapped prostredia

Verejná Sigstore inštancia rieši 90 % prípadov, ale regulované prostredia (banky, vláda, healthcare) potrebujú držať celý trust chain interne. Sigstore poskytuje referenčnú inštaláciu privátneho stacku (Fulcio, Rekor, Trillian a dex pre OIDC) cez scaffolding Helm charty, ktoré sa nasadia do dedikovaného clustra. Privátne Fulcio CA viažete na firemný IdP (Okta, Azure AD, Keycloak) namiesto Sigstore OIDC, takže certifikát subject claim odráža reálne firemné identity (napr. [email protected] alebo ci-build-runner-prod).

Pre air-gapped prostredia bez prístupu k internetu existujú dve cesty. Prvá je cosign sign --tlog-upload=false a externé udržiavanie audit logu. Strácate tým transparency guarantee, ale podpisovanie funguje offline. Druhá je „bundle" formát (cosign sign --bundle artifact.sigstore.json), ktorý do JSON súboru zabalí podpis, certifikát aj Rekor inclusion proof. Bundle si môžete preniesť cez air-gap a overiť ho na druhej strane cez cosign verify --bundle --offline. Toto je v praxi to, čo robia tímy pri klasifikovaných workloadoch: build v pripojenom prostredí, verify offline na cieli.

Často kladené otázky

Je Cosign zadarmo aj pre komerčné použitie?

Áno. Cosign je open-source pod Apache 2.0 licenciou a verejná Sigstore inštancia (Fulcio + Rekor) je prevádzkovaná Linux Foundation bez poplatkov. Komerčné použitie nie je nijako obmedzené, ale verejná inštancia poskytuje len „best effort" SLA. Pre kritické produkčné pipelines zvážte privátnu inštanciu.

Aký je rozdiel medzi Cosign signom a SLSA provenance?

Cosign sign dokazuje, kto obraz podpísal (identity proof). SLSA provenance je atestácia popisujúca, ako bol obraz zostavený: aký zdroj, aký builder, aké kroky. Cosign attest oboje spája, pretože provenance JSON je predikát, ktorý sa kryptograficky podpíše cez keyless flow.

Funguje Cosign aj s Docker Hubom a self-hosted registry?

Áno. Cosign ukladá podpisy ako štandardné OCI objekty, takže funguje s každým OCI-kompatibilným registry vrátane Docker Hubu, GHCR, Harbor, Quay, AWS ECR, GCP Artifact Registry a self-hosted Distribution alebo Zot registry. Registry musí podporovať OCI artifacts (drvivá väčšina dnešných áno).

Ako rotujem kľúče pri keyless signovaní?

Žiadnym spôsobom, pretože keyless flow nepoužíva dlhodobé kľúče. Každý sign vygeneruje nový efemérny kľúč, ktorý existuje len niekoľko minút a potom je zahodený. „Rotácia" sa deje pri každom podpise automaticky. Toto je hlavná operačná výhoda oproti GPG/KMS modelu.

Čo sa stane, ak Sigstore verejná inštancia spadne?

Nový sign nepôjde (Fulcio CA nedostupné), ale existujúce podpisy zostávajú overiteľné, pokiaľ máte uložené Rekor inclusion proofs. Pre kritické pipelines: cachovať Rekor entries lokálne, používať bundle formát s offline verification a/alebo nasadiť privátnu Sigstore inštanciu ako fallback.

Felix Lindqvist
O Autorovi Felix Lindqvist

Penetration tester and OSCP holder. Reverse engineers misconfigured servers for a living and writes about what he finds.