Cosign e Sigstore: Assinatura de Imagens Docker e Verificação em Kubernetes (2026)
Guia prático para assinar imagens Docker com Cosign 2.4 (keyless), anexar SBOM e attestations SLSA, e bloquear deploys não assinados no Kubernetes com Policy Controller.
Cosign, parte do projeto Sigstore, é a ferramenta padrão de fato para assinar imagens de contêiner OCI e gerar attestations verificáveis sem precisar gerenciar chaves privadas. No fluxo keyless, o Cosign emite um certificado efêmero via Fulcio a partir do seu token OIDC (GitHub Actions, GitLab, Google, etc.), assina o digest da imagem e publica o registro imutável da assinatura no Rekor. Em 2026, com a versão Cosign 2.4 e o requisito SLSA v1.0 para nível 3 de proveniência, assinar imagens deixou de ser opcional em pipelines DevSecOps minimamente sérios.
Cosign 2.4 (abril de 2026) já assume keyless signing como padrão e exige Rekor v1.3+ para anexação de attestations.
Uma única linha (cosign sign --yes registry.example.com/app@sha256:<digest>) assina por digest usando OIDC do CI, sem armazenar nenhum segredo.
Attestations SLSA v1.0 (provenance) e SBOM em formato CycloneDX/SPDX são anexadas com cosign attest e ficam consultáveis via Rekor.
No Kubernetes, o Sigstore Policy Controller (admission webhook) bloqueia imagens sem assinatura confiável, evitando deploys de artefatos não verificados.
Trate o Rekor como append-only log: assinaturas não podem ser revogadas, apenas marcadas como expiradas por política. Planeje rotação de identidades OIDC.
Em pipelines GitHub Actions e GitLab CI, todo o fluxo cabe em ~30 linhas de YAML. Cada seção deste guia termina com um snippet pronto para colar.
O que é o Sigstore e por que assinar imagens de contêiner?
O Sigstore é uma fundação de código aberto (sob o guarda-chuva da OpenSSF) que entrega três peças complementares: Cosign (CLI de assinatura), Fulcio (CA que emite certificados X.509 efêmeros vinculados a uma identidade OIDC) e Rekor (log de transparência append-only, baseado em árvore de Merkle, que registra cada assinatura emitida). A motivação prática é simples: sem assinatura, um atacante que comprometa o registro consegue empurrar uma imagem com a mesma tag, e nenhum cluster vai notar.
Assinar resolve dois problemas que scanners como Trivy e Grype não resolvem: autenticidade (essa imagem realmente foi produzida pelo meu pipeline?) e integridade pós-build (o digest aqui é exatamente o que saiu do build?). A combinação assinatura mais scan é o que move uma pipeline do SLSA L1 para o L3. Honestamente, venho aplicando isso desde 2024 e a parte mais importante é esta: sempre assine por digest, nunca por tag. Tags são mutáveis e a assinatura presa a uma tag dá uma falsa sensação de segurança.
Em 2026 a maioria dos registros (GHCR, Harbor 2.11+, ECR, GAR, ACR, Quay) entende o esquema de referência OCI 1.1 que o Cosign usa para anexar artefatos ao manifesto da imagem, então você não precisa mais do hack de tags sha256-...sig.
Cosign vs Notary v2 vs Docker Content Trust: qual escolher em 2026?
Os três projetos resolvem o mesmo problema-base, mas com posturas muito diferentes em relação a chaves, governança e ecossistema. Notary v2 (agora chamado de notation) é o esforço de padronização da CNCF baseado em OCI artifacts. O legacy Docker Content Trust (DCT, baseado no TUF + Notary v1) está em modo manutenção e deve ser considerado depreciado para novos projetos.
Critério
Cosign 2.4 (Sigstore)
Notation v1.2 (Notary v2)
Docker Content Trust
Modelo de chave
Keyless (OIDC) ou par de chaves
Par de chaves obrigatório (x509)
Chaves locais TUF
Log de transparência
Rekor (público, padrão)
Opcional, via plugin
Não
Attestations (SLSA, SBOM)
Nativas (cosign attest)
Via referrers OCI 1.1
Não
Admission no Kubernetes
Policy Controller / Kyverno / Connaisseur
Ratify, Kyverno
Sem suporte
Curva de adoção
Baixa em CI (1 binário)
Média (gerência de truststore)
Alta, mal documentada
Registros suportados
Todos OCI compliant
Todos OCI 1.1
Apenas Docker Hub + registry v2
Status do projeto
Ativo, OpenSSF graduated
Ativo, CNCF incubating
Em manutenção, evite
Recomendação pragmática: comece com Cosign keyless. A barreira de entrada é a menor, porque você não precisa decidir agora como vai rodar uma KMS própria, e migrar de Cosign para Notation depois é mais fácil do que o contrário, já que ambos publicam como referrers OCI 1.1.
Como instalar o Cosign 2.4 no Linux
Em 2026 a forma canônica de instalar Cosign é baixar o binário estático dos releases oficiais no GitHub e verificar a própria assinatura do Cosign. Esse bootstrap de confiança é feito usando uma versão anterior ou a chave pública do projeto. Distros baseadas em Debian/Ubuntu também disponibilizam pacotes .deb, e no Fedora 41+ o pacote está em dnf direto.
# Instalação em Ubuntu 24.04 / Debian 12 / RHEL 10 (Cosign 2.4.1)
COSIGN_VERSION="2.4.1"
curl -fsSLo cosign \
"https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64"
curl -fsSLo cosign.sig \
"https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64.sig"
curl -fsSLo cosign.pub \
"https://raw.githubusercontent.com/sigstore/cosign/main/release/release-cosign.pub"
# Verifica o binário ANTES de mover para o PATH
openssl dgst -sha256 -verify cosign.pub -signature cosign.sig cosign
sudo install -m 0755 cosign /usr/local/bin/cosign
cosign version # confirma GitVersion: v2.4.1
Para builds reprodutíveis em pipelines, prefira fixar pelo digest da imagem oficial gcr.io/projectsigstore/cosign em vez de baixar a cada execução. O snippet abaixo é a forma mais confiável que eu uso em runners auto-hospedados:
# CI-ready: instala Cosign 2.4.1 a partir do container oficial, pinned por digest
# (substitua o digest pelo da release que você auditou)
docker run --rm --entrypoint sh \
gcr.io/projectsigstore/cosign@sha256:<digest-pinned> \
-c 'cat /ko-app/cosign' > /usr/local/bin/cosign
chmod +x /usr/local/bin/cosign
Como assinar uma imagem Docker com Cosign usando keyless signing
O fluxo keyless é, na prática, três passos. Primeiro, o Cosign pede um token OIDC ao seu provedor (GitHub Actions, GitLab, Google, etc.). Depois, troca esse token por um certificado X.509 efêmero (vida de 10 minutos) emitido pelo Fulcio. Por fim, assina o digest da imagem com a chave privada associada, que é descartada logo em seguida. A assinatura e o certificado vão para o Rekor e são anexados ao registro OCI.
Antes de assinar, faça push da imagem e capture o digest. Eu nunca assino por tag, sempre por digest:
Agora a assinatura propriamente dita. Em um shell interativo o Cosign abre um navegador para o OIDC. Em CI, ele detecta automaticamente o ambiente (variável ACTIONS_ID_TOKEN_REQUEST_URL no GitHub, CI_JOB_JWT_V2 no GitLab, etc.) e usa o token do runner:
A flag --yes aceita o aviso de upload para o Rekor público (em ambientes regulados, considere rodar um Rekor privado). A regex de identidade é o que evita ataques de impersonation. Se você só checasse "alguma assinatura válida", qualquer pessoa com um token OIDC do GitHub poderia assinar uma imagem com seu nome.
Como anexar SBOM e attestations SLSA com Cosign
Assinar a imagem prova autenticidade, mas não diz como a imagem foi construída ou o que está dentro. É aí que entram as attestations: documentos in-toto, assinados, que descrevem fatos sobre o artefato. As duas mais importantes em 2026 são a SLSA v1.0 provenance (cumprida pelo SLSA build L3) e o SBOM (CycloneDX ou SPDX). Veja o SLSA v1.0 specification para o esquema completo.
Para gerar SBOM, eu uso syft (mesma família do Grype) e anexo com cosign attest:
# Gera SBOM CycloneDX e anexa como attestation
syft "$REF" -o cyclonedx-json > sbom.cdx.json
cosign attest --yes \
--predicate sbom.cdx.json \
--type cyclonedx \
"$REF"
# Para SLSA provenance gerada por slsa-github-generator:
cosign attest --yes \
--predicate provenance.intoto.jsonl \
--type slsaprovenance1 \
"$REF"
Cada attestation vira um artefato OCI separado referenciado pelo manifesto da imagem (subject digest). Para verificar do lado consumidor, você usa cosign verify-attestation com a mesma identidade keyless e, adicionalmente, uma política CUE ou Rego:
# Política CUE: rejeita SBOM com dependências GPL e exige build SLSA L3
cosign verify-attestation \
--type cyclonedx \
--policy policies/no-gpl.cue \
--certificate-identity-regexp '^https://github\.com/acme/.*$' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
"$REF"
Na prática, mantenho uma pasta policies/ com 4 ou 5 arquivos: slsa-level3.cue, no-critical-vulns.cue (consome a attestation do Trivy), no-root-user.cue e sbom-license.cue. Esse é o ponto onde a coleta de evidências feita pelo Lynis e os CIS Benchmarks se conecta com a cadeia de suprimentos: as attestations viram artefatos auditáveis.
Como verificar assinaturas no Kubernetes com Policy Controller
Verificar localmente é bom. Verificar no momento do kubectl apply é o que efetivamente bloqueia deploys ruins. O Sigstore Policy Controller é um admission webhook que intercepta pods e recusa imagens cujas assinaturas não batam com as ClusterImagePolicy configuradas. Ele convive bem com Falco e Tetragon (que monitoram runtime), enquanto o Policy Controller fica na admissão. Eu costumo usá-los em camadas, veja o comparativo Falco vs Tetragon para runtime security.
Aplique com kubectl apply -f clusterimagepolicy.yaml e teste o bloqueio fazendo deploy de uma imagem não assinada. Deve retornar admission webhook "policy.sigstore.dev" denied the request: no signatures found. Se você preferir Kyverno (já roda em muitos clusters), o equivalente é uma ClusterPolicy com regra verifyImages. Sintaxe diferente, mesmo resultado.
Como integrar Cosign no pipeline GitHub Actions e GitLab CI
Aqui é onde o trabalho compensa: depois de configurado, cada release passa a gerar imagem, assinatura, SBOM e provenance sem intervenção. O snippet GitHub Actions abaixo é o que eu uso em praticamente todos os meus repositórios em 2026:
Depois de bastante quilometragem com Cosign em CI, esses são os achados que aparecem repetidamente, junto com o meu critério para ignorar ou não findings de scanner em cima deles:
Error: signing [...]: getting key from OIDC: ...: quase sempre falta permissão id-token: write no job do GitHub Actions, ou o aud está errado no GitLab. Não é coisa do Cosign.
no matching signatures: invalid signature when validating ASN.1 encoded signature: você assinou a imagem antes de o registro suportar OCI 1.1 referrers, ou o registro está usando proxy/cache que descarta os artefatos anexados. Faça push e assinatura no mesmo registro.
Falsos positivos do Trivy reclamando da própria imagem do Cosign: o binário Go estático faz scanners listarem CVEs do toolchain. Ignore se a vulnerabilidade não é alcançável pelo cosign CLI (é a regra que sigo: ruído de scanner em ferramentas de build, sem PoC, não bloqueia merge).
error fetching attestations: not found: você está consultando a tag em vez do digest. Sempre por digest.
Policy Controller bloqueia tudo de repente: quase sempre o Fulcio rotacionou o root e seu TUF mirror está desatualizado. cosign initialize resolve.
Para depurar verificação localmente sem rodar o cluster, exporte COSIGN_EXPERIMENTAL=1 e use cosign tree <REF>. Isso lista todos os referrers (assinatura, SBOM, provenance) que o registro tem para aquele digest. Em 90% dos casos a depuração para aí: ou o artefato esperado não existe, ou existe mas com tipo diferente do que a política exige.
Perguntas frequentes
É seguro usar o Rekor público para assinaturas de imagens privadas?
Sim. O Rekor só armazena o hash da assinatura e o certificado X.509 emitido pelo Fulcio, nunca o conteúdo da imagem. Mesmo assim, ambientes regulados (PCI-DSS, FedRAMP) costumam exigir Rekor privado para evitar vazamento de metadados como nomes de repositório.
Posso usar Cosign sem internet (ambiente air-gapped)?
Sim, mas você precisa rodar uma instância privada de Fulcio + Rekor (Helm charts oficiais) ou usar o modo key-pair tradicional com cosign generate-key-pair. Mantenha as chaves em uma KMS (HashiCorp Vault, AWS KMS, GCP KMS) e nunca em disco do runner.
Qual a diferença entre cosign sign e cosign attest?
cosign sign gera uma assinatura simples sobre o digest da imagem e prova autenticidade. cosign attest assina um documento in-toto que descreve fatos sobre a imagem (SBOM, provenance, resultado de testes) e prova proveniência e propriedades. Use os dois em conjunto.
Cosign substitui o scan de vulnerabilidades?
Não. Assinatura prova quem produziu a imagem, e o scan diz se há vulnerabilidades conhecidas no conteúdo. São camadas complementares: scan no build, attestation de "imagem foi scaneada e passou" anexada via Cosign, e verificação dessa attestation na admissão do Kubernetes.
Como faço para revogar uma assinatura Cosign?
O Rekor é append-only, então você não revoga. Em vez disso, publica uma nova assinatura com claim explícito de depreciação ou ajusta sua política de admissão para rejeitar a identidade comprometida via certificate-identity-regexp. Em casos críticos, atualize a ClusterImagePolicy para listar digests bloqueados.
Como escanear vulnerabilidades em contêineres com Trivy e Grype, gerar SBOM em CycloneDX/SPDX, integrar a GitHub Actions/GitLab CI e tratar falsos positivos com VEX em 2026.