Pular para o conteúdo

Assinar imagens e exigir assinatura no cluster

Avançado16 min de leituraseguranca

Assinar imagem responde a uma pergunta específica: este artefato foi produzido pelo nosso pipeline, a partir do nosso repositório? Sem verificação no cluster, a assinatura é decorativa — este guia faz as duas pontas.

O modo keyless do Sigstore usa a identidade do workflow (via OIDC) para emitir um certificado de curta duração e registrar a assinatura em log público de transparência. Não há chave privada para guardar, rotacionar ou vazar.

name: publicar
on:
push: { branches: [main] }
permissions:
id-token: write # necessário para o keyless
packages: write
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: build
uses: docker/build-push-action@v6
with:
push: true
tags: registry.exemplo.com/loja:${{ github.sha }}
- uses: sigstore/cosign-installer@v3
- name: Assinar pelo digest
run: |
cosign sign --yes \
registry.exemplo.com/loja@${{ steps.build.outputs.digest }}

Assine sempre o digest, nunca a tag. Tag é ponteiro mutável: assinar :1.8.3 garante pouco se alguém puder reapontá-la depois.

Assinatura diz quem; atestação diz a partir de quê.

Janela do terminal
syft registry.exemplo.com/loja@sha256:SUBSTITUA -o spdx-json > sbom.json
cosign attest --yes --predicate sbom.json --type spdxjson \
registry.exemplo.com/loja@sha256:SUBSTITUA

Assinatura, SBOM e proveniência ficam no próprio registry, ao lado da imagem — é isso que permite responder a uma CVE nova em minutos.

Janela do terminal
cosign verify registry.exemplo.com/loja@sha256:SUBSTITUA \
--certificate-identity-regexp '^https://github.com/empresa/loja/\.github/workflows/.+' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com

As duas restrições de identidade não são opcionais: cosign verify sem --certificate-identity aceita assinatura de qualquer pessoa do mundo — é o erro mais comum na adoção, e ele anula todo o resto.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata: { name: exigir-imagem-assinada }
spec:
validationFailureAction: Audit # comece aqui; Enforce vem no passo 5
background: false
webhookTimeoutSeconds: 30
rules:
- name: verificar-assinatura
match:
any: [{ resources: { kinds: [Pod], namespaces: [prod, hom] } }]
verifyImages:
- imageReferences: ["registry.exemplo.com/*"]
mutateDigest: true # troca tag por digest ao admitir
verifyDigest: true
required: true
attestors:
- entries:
- keyless:
issuer: https://token.actions.githubusercontent.com
subject: "https://github.com/empresa/*"
rekor: { url: https://rekor.sigstore.dev }

mutateDigest: true fecha a janela entre verificar e executar: o Pod passa a referenciar o digest verificado, e não a tag.

A alternativa ao Kyverno é o policy-controller do Sigstore; a mecânica é a mesma.

Janela do terminal
kubectl get policyreport -A | grep -i exigir-imagem-assinada
kubectl get clusterpolicyreport -o wide

Rode em Audit por uma a duas semanas e resolva o que aparecer. Você vai encontrar imagens legítimas não assinadas: agentes de observabilidade, controladores instalados por Helm, jobs antigos. Para cada uma, decida entre assinar (se for sua), confiar no fornecedor com regra específica, ou registrar exceção com prazo.

# exceção com dono e prazo — nunca uma exceção permanente e silenciosa
apiVersion: kyverno.io/v2
kind: PolicyException
metadata: { name: agentes-terceiros, namespace: observabilidade }
spec:
exceptions:
- policyName: exigir-imagem-assinada
ruleNames: [verificar-assinatura]
match:
any: [{ resources: { namespaces: [observabilidade] } }]
# Dono @ana — revisão em 2026-12-01, quando o fornecedor passar a assinar

Só depois disso troque para validationFailureAction: Enforce, e comece por um namespace.

Sintoma Causa provável
no matching signatures Assinou a tag e verifica o digest (ou o contrário)
Verificação passa para qualquer imagem Faltou --certificate-identity-regexp
Pods param de subir ao ligar Enforce Imagens de terceiros não assinadas — volte para Audit
Timeout no webhook Verificação lenta contra o Rekor; ajuste webhookTimeoutSeconds e cache
Falha só em ambiente sem internet Keyless precisa alcançar Fulcio/Rekor — considere chave própria ou espelho

Em ambiente isolado, o caminho é chave gerenciada em KMS (cosign sign --key awskms://...) com verificação pela chave pública.

  • Pipeline assinando pelo digest, sem chave estática.
  • SBOM e proveniência publicados como atestações.
  • Verificação no pipeline com identidade e emissor restritos.
  • Política de admissão instalada em modo auditoria.
  • Relatórios revisados e acervo tratado.
  • Enforce ligado por namespace, com exceções datadas.
  • Alerta quando uma imagem sem assinatura é recusada.