Construindo Imagens de Container Imutáveis e Seguras com Wolfi e Chainguard

A superfície de ataque da cadeia de suprimentos de software é uma preocupação primordial em ambientes de desenvolvimento e operações modernos. Vulnerabilidades em imagens de container, muitas vezes decorrentes de pacotes desnecessários ou bases de sistemas operacionais obsoletas, representam um risco significativo. Para mitigar esses riscos, a adoção de imagens de container minimalistas e verificáveis tornou-se uma estratégia mandatoria. Este artigo explora a construção de imagens de container inerentemente mais seguras utilizando Wolfi, a distribuição linux base da Chainguard, e o ecossistema de ferramentas que a suporta.

A Base da Segurança: Entendendo Wolfi

Wolfi é uma distribuição Linux focada em segurança, projetada especificamente para containers e ambientes de nuvem. Diferente de distribuições tradicionais como Debian ou Alpine, Wolfi adota uma abordagem de segurança por design, fornecendo apenas os pacotes essenciais, compilados a partir do código fonte com opções de segurança rigorosas.

Características Essenciais de Wolfi:

  • Musl libc como Padrão: Utiliza musl libc em vez de glibc, resultando em imagens de container menores e com menos dependências, simplificando a análise de segurança.
  • Construções Reproduzíveis: Todos os pacotes em Wolfi são construídos de forma reproduzível, o que permite a verificação independente da origem binária e garante que o binário corresponde ao código fonte.
  • SBOMs Nativos (Software Bill of Materials): Cada imagem Wolfi gera um SBOM detalhado, fornecendo uma lista exaustiva de todos os componentes, suas versões e licenças. Este é um pilar fundamental para a conformidade e análise de vulnerabilidades.
  • Foco em Segurança: Projetada para ter uma superfície de ataque mínima. Pacotes são mantidos atualizados e vulnerabilidades (CVEs) são corrigidas proativamente.
  • Ferramentas de Construção Próprias: Wolfi utiliza o apko, uma ferramenta de construção de imagens de container que gera imagens OCI (Open Container Initiative) diretamente de arquivos de configuração declarativos.

A filosofia por trás de Wolfi é de fornecer uma base limpa e transparente, onde cada byte na imagem é justificado e auditável. Este enfoque contrasta drasticamente com imagens base monolíticas que incluem inúmeros utilitários e bibliotecas desnecessárias, aumentando exponencialmente o vetor de ataque.

Chainguard Images: Imagens Otimizadas por Padrão

As Chainguard Images são coleções de imagens de container pré-construídas e otimizadas para segurança, mantidas pela Chainguard e baseadas em Wolfi. Elas representam um passo adiante na segurança da cadeia de suprimentos, oferecendo imagens com:

  • Zero CVEs Conhecidas: No momento da construção, estas imagens visam ter zero vulnerabilidades conhecidas. Isso é alcançado pela sua base Wolfi e pelo monitoramento contínuo de pacotes.
  • Tamanho Mínimo: Redução drástica no tamanho das imagens, o que acelera o download, melhora o tempo de inicialização e, criticamente, reduz a superfície de ataque.
  • Assinatura com Sigstore: Todas as Chainguard Images são assinadas com Sigstore e Cosign, permitindo a verificação criptográfica da sua integridade e proveniência.
  • Ciclo de Vida Gerenciado: A Chainguard se responsabiliza pela atualização e correção de vulnerabilidades, aliviando o fardo de manutenção para os usuários.

Para utilizar uma Chainguard Image, o processo é semelhante ao de qualquer outra imagem OCI. Basta substituir sua imagem base atual por uma equivalente da Chainguard. Por exemplo, para um aplicativo Go, em vez de usar golang:1.21-alpine, você usaria cgr.dev/chainguard/go:latest (ou uma tag de versão específica):


# Dockerfile de exemplo com Chainguard Go Image
FROM cgr.dev/chainguard/go:1.21-static as builder

WORKDIR /app
COPY go.mod go.sum ./ 
RUN go mod download
COPY . . 
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o myapp .

FROM cgr.dev/chainguard/static:latest
COPY --from=builder /app/myapp /usr/local/bin/myapp
EXPOSE 8080
CMD ["myapp"]

Observe o uso da imagem static da Chainguard para a fase final. Esta imagem é a mais mínima possível, contendo apenas o essencial para executar binários estaticamente compilados, garantindo a menor superfície de ataque.

Construindo Imagens Wolfi Customizadas com apko

Embora as Chainguard Images cubram muitos casos de uso comuns, pode ser necessário construir imagens Wolfi personalizadas para aplicações específicas. É aqui que o apko, a ferramenta de construção de imagens da Chainguard, entra em cena. O apko permite definir o conteúdo de uma imagem OCI de forma declarativa via um arquivo YAML.

Estrutura de um Arquivo apko.yaml:


# apko.yaml para uma imagem customizada com Nginx

# Versão do formato de arquivo apko
apiVersion: "apko.dev/v1beta1"
kind: "Configuration"

# Informações da imagem
metadata:
  name: "meu-nginx"
  version: "1.0.0"
  architecture: "amd64"

# Pacotes a serem incluídos na imagem
packages:
  - ca-certificates
  - nginx

# Comandos a serem executados na inicialização do container
cmd: "nginx -g 'daemon off;'"

# Usuário padrão para o container
user: "nginx"

# Porta exposta
ports:
  - 80/tcp

# Adiciona um novo diretório com permissões específicas
# (exemplo: para arquivos de configuração)
accounts:
  groups:
    - groupname: "nginx"
      gid: 1000
  users:
    - username: "nginx"
      uid: 1000
      gid: 1000

env:
  PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Adicionando arquivos extras (ex: configurações customizadas)
# Se você tem um diretório 'configs/' local, por exemplo
# paths:
#  - path: /etc/nginx/conf.d/
#    source: ./configs/

Processo de Construção:

Após definir o apko.yaml, a construção da imagem é um processo simples:

  1. Instale o apko CLI.
  2. Execute o comando de construção, especificando o arquivo YAML e o nome da imagem de saída.

# Instalação do apko (exemplo via curl)
curl -sfL https://raw.githubusercontent.com/chainguard-dev/apko/main/install.sh | sh

# Construção da imagem
apko build meu-nginx.apko.yaml meu-nginx:latest

Este comando gera um arquivo .tar.gz contendo a imagem OCI. Para empurrá-la para um registry, pode-se usar apko push ou extrair o tar e usar ferramentas como docker load e docker push. O uso de apko elimina a necessidade de um Dockerfile complexo para a imagem base, concentrando-se apenas nos pacotes e configurações essenciais.

Integrando Assinatura de Imagens com Sigstore e Cosign

A segurança da cadeia de suprimentos não termina na construção de imagens mínimas. É crucial verificar a proveniência e a integridade das imagens. O Sigstore, em conjunto com cosign, oferece uma maneira padrão e robusta de assinar e verificar artefatos de software, incluindo imagens de container.

Assinando uma Imagem com Cosign:

Após construir sua imagem com apko (ou Docker), você pode assiná-la. Primeiro, certifique-se de que a imagem está disponível em um registry (ex: Docker Hub, GCR, ECR).


# Faça login no registry, se necessário
# docker login your.registry.com

# Tag e push da imagem para o registry
docker tag meu-nginx:latest your.registry.com/meu-nginx:latest
docker push your.registry.com/meu-nginx:latest

# Geração de uma chave de assinatura (se ainda não tiver)
# cosign generate-key-pair

# Assinatura da imagem (usando uma chave previamente gerada)
# cosign sign --key cosign.key your.registry.com/meu-nginx:latest

# Alternativamente, para assinatura sem chave com o Fulcio (mais simples)
cosign sign your.registry.com/meu-nginx:latest

O cosign sign sem a flag --key interage com o Sigstore Fulcio para emitir certificados efêmeros baseados em sua identidade OIDC (ex: GitHub, Google), e o Rekor para armazenar a assinatura publicamente em um log de transparência. Isso permite um modelo de assinatura de imagem sem a complexidade do gerenciamento de chaves estáticas.

Verificando a Assinatura:

Em ambientes de CI/CD ou em clusters Kubernetes, a verificação da assinatura pode ser automatizada para garantir que apenas imagens confiáveis sejam implantadas.


# Verificação da assinatura (para assinatura sem chave)
cosign verify your.registry.com/meu-nginx:latest

# Para verificação de assinatura baseada em chave, você precisaria da chave pública
# cosign verify --key cosign.pub your.registry.com/meu-nginx:latest

Um resultado bem-sucedido indica que a imagem não foi adulterada e que sua proveniência pode ser rastreada até o signatário. Integrações com ferramentas como Gatekeeper ou Kyverno no Kubernetes podem impor políticas para só permitir a implantação de imagens verificadas pelo Cosign.

Gerenciamento de Dependências e SBOMs Nativos

Um dos maiores benefícios de Wolfi e Chainguard Images é a geração nativa de SBOMs (Software Bill of Materials). Cada imagem contém um registro completo de todos os pacotes, suas versões, licenças e hashes, permitindo uma visibilidade sem precedentes sobre o conteúdo da imagem.

Extraindo e Analisando SBOMs:

O apko e as ferramentas Chainguard facilitam a extração e o consumo de SBOMs. Eles são geralmente armazenados em formatos padronizados como SPDX ou CycloneDX, que são interoperáveis com scanners de vulnerabilidades e outras ferramentas de segurança.


# Exemplo: Extraindo o SBOM de uma imagem Chainguard
# Assumindo que a imagem já está puxada localmente ou em um registry acessível

# Usando a ferramenta 'melange' (que é a base do apko) ou outras ferramentas de imagem
# para imagens Chainguard publicadas, os SBOMs geralmente estão disponíveis diretamente no registry

# Para uma imagem construída com apko, o SBOM é gerado junto
# apko build meu-nginx.apko.yaml meu-nginx:latest --sbom "./sbom.spdx.json"

# Para consultar SBOMs de imagens já existentes em um registry
# cosign sbom your.registry.com/meu-nginx:latest

A posse de um SBOM permite que equipes de segurança identifiquem rapidamente se uma vulnerabilidade recém-descoberta (CVE) afeta suas imagens em produção, sem a necessidade de escanear toda a imagem. Isso acelera a resposta a incidentes e a aplicação de patches.

Automatizando a Construção e o Deploy Seguro em CI/CD

A verdadeira força das imagens seguras reside na sua integração perfeita em pipelines de CI/CD, garantindo que a segurança seja um aspecto inerente a cada build e deploy. Um pipeline típico envolveria as seguintes etapas:

  1. Construção da Imagem: Utilizando apko para imagens Wolfi customizadas ou um Dockerfile com base Chainguard.
  2. Análise de Segurança (Opcional, mas Recomendado): Embora Wolfi/Chainguard minimizem CVEs, uma varredura adicional com ferramentas como Grype, Trivy ou Syft pode validar o conteúdo do SBOM e identificar configurações de segurança impróprias.
  3. Assinatura da Imagem: Com cosign, garantindo que a imagem seja auditável e verificável.
  4. Push para o Registry: Envio da imagem e suas assinaturas (e SBOMs) para um registry OCI seguro.
  5. Deploy Automatizado: Ferramentas como Argo CD ou Flux CD, integradas com Gatekeeper/Kyverno, verificam as assinaturas do Cosign antes de permitir o deploy no cluster Kubernetes.

Exemplo de GitHub Actions para Construção e Assinatura:


# .github/workflows/build-and-sign.yaml
name: Build and Sign Container Image
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

env:
  REGISTRY: your.registry.com
  IMAGE_NAME: meu-nginx

jobs:
  build-and-sign:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      id-token: write # Necessário para autenticação OIDC do cosign

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup apko (para builds Wolfi customizados)
        uses: chainguard-dev/apko-action@v1 # Ou instale apko manualmente

      - name: Build Wolfi Image (se apko.yaml estiver presente)
        if: runner.os == 'Linux'
        run: |
          apko build meu-nginx.apko.yaml "${{ env.IMAGE_NAME }}:latest"
          docker tag "${{ env.IMAGE_NAME }}:latest" "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"

      - name: Build Docker Image (se Dockerfile estiver presente, usando Chainguard base)
        # Construa sua imagem via Dockerfile se não estiver usando apko diretamente
        run: |
          docker build -t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" .

      - name: Log in to the Container registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GH_TOKEN }} # Ou use token de um cloud provider

      - name: Push Image to Registry
        run: docker push "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"

      - name: Setup Cosign
        uses: sigstore/[email protected]

      - name: Sign the Image with Cosign
        # Assinatura usando OIDC do GitHub Actions com Fulcio e Rekor
        env:
          COSIGN_EXPERIMENTAL: "true" # Necessário para algumas funcionalidades mais novas
        run: |
          cosign sign --yes "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"

      - name: Verify the Image Signature
        run: |
          cosign verify "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"

Este pipeline ilustra como as práticas de segurança, desde a escolha da imagem base até a assinatura e verificação, podem ser automatizadas, criando um guardrail eficaz contra artefatos não autorizados ou comprometidos. A utilização de id-token: write e COSIGN_EXPERIMENTAL: "true" permite que o Cosign utilize o provedor OIDC do GitHub Actions para autenticação, eliminando a necessidade de gerenciar chaves de assinatura manualmente.

Considerações Operacionais e o Futuro da Imagem Segura

A adoção de Wolfi e Chainguard Images representa um investimento significativo na resiliência da cadeia de suprimentos de software. No entanto, a segurança é um processo contínuo. É fundamental manter um monitoramento ativo sobre as vulnerabilidades, mesmo para imagens consideradas seguras. Scanners de vulnerabilidade em tempo de execução, como Falco, podem complementar a segurança baseada em imagens, detectando comportamentos anômalos em containers já implantados.

A evolução para padrões como SLSA (Supply Chain Levels for Software Artifacts) visa formalizar a segurança da cadeia de suprimentos, estabelecendo níveis de garantia para o processo de construção. Ferramentas como Wolfi e apko, com sua ênfase em builds reproduzíveis, SBOMs e assinaturas, estão intrinsecamente alinhadas com os requisitos SLSA, posicionando as organizações para alcançar níveis mais altos de conformidade e segurança. A transição para essas bases mais seguras não é apenas uma melhoria técnica; é uma mudança cultural em direção a uma mentalidade de segurança proativa e transparente, onde a proveniência e a integridade de cada componente são verificáveis em cada etapa do ciclo de vida do software.