Sigstore e cosign: assinatura de imagens Docker para supply chain segura
1. Fundamentos da Segurança na Supply Chain de Containers
A adoção de containers transformou a entrega de software, mas também introduziu novos vetores de ataque na supply chain. Uma imagem Docker pode ser alterada silenciosamente entre o momento da construção e o deploy em produção. Ataques como dependency confusion (onde pacotes maliciosos são inseridos em repositórios públicos), image poisoning (imagens legítimas contaminadas com malware) e man-in-the-middle (interceptação de tráfego durante o pull) são ameaças reais.
Hashes SHA256 e conexões TLS oferecem proteção parcial. Hashes garantem integridade do conteúdo, mas não autenticam a origem. TLS protege o canal de comunicação, mas não impede que um atacante com acesso ao registro substitua uma imagem por outra maliciosa. Para garantir que uma imagem foi gerada por uma identidade confiável, é necessário um mecanismo de assinatura criptográfica que vincule a imagem a um signatário verificável.
2. Introdução ao Sigstore: ecossistema de assinatura criptográfica
Sigstore é um ecossistema open source que simplifica a assinatura e verificação de artefatos de software. Seus componentes principais são:
- Fulcio: autoridade certificadora que emite certificados X.509 de curta duração baseados em identidade OIDC (OpenID Connect).
- Rekor: ledger transparente e imutável que registra todas as assinaturas, permitindo auditoria pública.
- Cosign: ferramenta de linha de comando para assinar e verificar containers e outros artefatos.
Diferente da assinatura tradicional com PGP (que exige gerenciamento complexo de chaves), o Sigstore adota o modelo keyless: a identidade do desenvolvedor (ex: email vinculado ao GitHub, Google ou Microsoft) é usada para obter um certificado temporário do Fulcio. Esse certificado expira em minutos, eliminando a necessidade de armazenar chaves privadas de longo prazo. A assinatura é registrada no Rekor, criando um histórico público de quem assinou o quê e quando.
3. Instalação e configuração do Cosign
A instalação do cosign pode ser feita de várias formas. No Linux e macOS, o método mais simples é via script oficial:
# Linux/macOS
curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64"
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
sudo chmod +x /usr/local/bin/cosign
No macOS com Homebrew:
brew install cosign
No Windows, via Chocolatey:
choco install cosign
Para verificar a instalação:
cosign version
A configuração de ambiente para autenticação OIDC depende do provedor. Para GitHub Actions, não é necessário configurar nada adicional — o token OIDC é injetado automaticamente no ambiente. Para uso local, o cosign abrirá o navegador para autenticação via provedor suportado (GitHub, Google, Microsoft).
4. Assinatura de imagens Docker com Cosign
Assinatura keyless (recomendada para CI/CD)
O comando básico para assinar uma imagem com identidade OIDC é:
cosign sign ghcr.io/seu-usuario/sua-imagem:tag
O cosign solicitará autenticação via navegador. Após autenticação, ele obtém um certificado do Fulcio, assina a imagem e registra a transação no Rekor. A assinatura é armazenada como uma tag ou anotação no registro de containers.
Assinatura com par de chaves (para ambientes offline)
Para cenários sem acesso à internet ou com políticas restritivas, é possível gerar um par de chaves:
cosign generate-key-pair
Isso cria cosign.key (privada) e cosign.pub (pública). A assinatura é feita com:
cosign sign --key cosign.key ghcr.io/seu-usuario/sua-imagem:tag
A chave privada deve ser armazenada com segurança (ex: cofre de senhas, secrets do CI/CD).
5. Verificação de assinaturas e políticas de segurança
Para verificar manualmente uma imagem assinada:
cosign verify ghcr.io/seu-usuario/sua-imagem:tag
A saída exibe o certificado do signatário, incluindo o email e o emissor OIDC:
Verification for ghcr.io/seu-usuario/sua-imagem:tag --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified identity
Certificate subject: usuario@example.com
Certificate issuer URL: https://github.com/login/oauth
Para verificar atestações (metadados adicionais como SBOM, resultados de scan):
cosign verify-attestation --type slsaprovenance ghcr.io/seu-usuario/sua-imagem:tag
Em clusters Kubernetes, a verificação pode ser automatizada com admission controllers. O Kyverno, por exemplo, pode ser configurado para bloquear pods que usem imagens não assinadas:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image
spec:
validationFailureAction: enforce
rules:
- name: verify-cosign-signature
match:
resources:
kinds:
- Pod
verifyImages:
- image: "ghcr.io/seu-usuario/*"
key: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
6. Automatizando assinatura em pipelines CI/CD
Um exemplo completo de GitHub Actions que assina automaticamente imagens após o build:
name: Build, Sign and Push
on:
push:
branches: [main]
jobs:
build-and-sign:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
run: |
docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Sign the image with Cosign (keyless)
run: |
cosign sign ghcr.io/${{ github.repository }}:${{ github.sha }}
O token OIDC é obtido automaticamente pelo GitHub Actions. A assinatura é registrada no Rekor público, garantindo transparência.
Boas práticas incluem:
- Usar secrets para tokens de registro.
- Rotacionar credenciais de registro regularmente.
- Segregar ambientes (dev/staging/prod) com diferentes políticas de assinatura.
7. Cenários avançados e troubleshooting
Assinatura de imagens multi-arquitetura
Imagens que suportam múltiplas arquiteturas (amd64, arm64) usam manifest lists. Para assiná-las:
cosign sign --key cosign.key ghcr.io/seu-usuario/sua-imagem:tag
O cosign detecta automaticamente o tipo de manifest e assina a lista inteira. Para verificar, use o mesmo comando cosign verify.
Erros comuns
Token OIDC expirado: ocorre quando o fluxo keyless demora demais. Solução: refazer a autenticação ou usar --yes para pular confirmações.
Conflitos de registro: alguns registros (como Docker Hub) exigem configuração extra de OIDC. Verifique se o registro suporta anotações OCI.
Ambientes corporativos: para empresas que não podem usar o Fulcio e Rekor públicos, é possível hospedar a própria stack Sigstore:
# Exemplo com docker-compose (simplificado)
services:
fulcio:
image: gcr.io/projectsigstore/fulcio:latest
ports:
- "5555:5555"
rekor:
image: gcr.io/projectsigstore/rekor-server:latest
ports:
- "3000:3000"
Configure o cosign para apontar para sua instância:
export SIGSTORE_FULCIO_URL=http://localhost:5555
export SIGSTORE_REKOR_URL=http://localhost:3000
Referências
- Documentação oficial do Sigstore — Guia completo sobre o ecossistema Sigstore, incluindo Fulcio, Rekor e Cosign.
- Cosign GitHub Repository — Código-fonte, issues e exemplos práticos de uso do Cosign.
- Keyless Signing with Sigstore — Artigo técnico explicando o modelo de assinatura sem chave e suas vantagens.
- Kyverno Policy for Image Verification — Documentação do Kyverno para políticas de verificação de imagens assinadas.
- Rekor Transparency Log — Visão geral do Rekor e como ele garante transparência na supply chain.
- GitHub Actions with Sigstore — Guia oficial do GitHub sobre uso de tokens OIDC em Actions, base para automação keyless.