código

15 set, 2025

Código, Fumaça e Assinaturas: Vault Transit na Linha de Fogo

Publicidade

Introdução: Selando confiança em tempos hostis

A cidade nunca dorme. Nem seus pipelines.

Em um mundo onde ataques de supply chain se tornam tão comuns quanto commits em um repositório monorepo, não basta mais verificar o conteúdo do código. É preciso comprovar a origem, assinar digitalmente a confiança e rastrear cada mudança até a última linha.

Já discutimos como acessos efêmeros com OTP via Vault revolucionam a segurança SSH. Agora, trago uma evolução desse mesmo princípio: assinatura de artefatos na linha de produção, com Zero Trust como base e o HashiCorp Vault como caneta criptográfica.

O Problema da Proveniência

Assumir que todo artefato gerado no CI/CD é confiável é um erro clássico.

  • Quem garante que o build foi mesmo daquele commit?
  • Quem impede que alguém injete algo no runner entre etapas?
  • Como confiar no deploy se o artefato não prova quem o gerou?

É aqui que entra o conceito de validação de proveniência com assinatura digital.

Assinatura Digital com Vault Transit

O Vault Transit Engine permite assinar qualquer conteúdo sem expor a chave privada. Ele atua como um “cofre com caneta criptográfica”:

  • Você envia o hash do conteúdo.
  • O Vault assina usando a chave assimétrica protegida.
  • Você recebe uma assinatura verificável com validade garantida.

Essa abordagem encaixa perfeitamente em ambientes com pipelines GitLab CI/CD, onde o CI apenas invoca o Vault, sem nunca ter acesso direto à chave.

Arquitetura na prática

No exemplo real a seguir, usamos:

  • GitLab CI/CD
  • Chave ed25519 gerenciada no Vault
  • Assinatura do hash de todos os arquivos do diretório
  • Armazenamento da assinatura + metadados em um path do Vault (kv-v2)

Pipeline Real com GitLab

Etapa 1: Hash de todos os arquivos do projeto

package_artifact:
  stage: package
  image: alpine:latest
  before_script:
    - apk add --no-cache tar
  script:
    - mkdir -p artifact/
    - find $CODE_DIR -type f ! -name ".filehashes" -exec sha256sum {} \; > artifact/.filehashes
  artifacts:
    paths:
      - artifact/.filehashes

Gera o arquivo .filehashes com o hash de todos os arquivos presentes na pasta do código.

Etapa 2: Assinar o .filehashes via Vault

sign_artifact:
  stage: sign
  image: alpine:latest
  needs:
    - job: package_artifact
  before_script:
    - apk add --no-cache curl bash coreutils openssl jq xxd
  script: |
    ARTIFACT_PATH="artifact/.filehashes"
    HASH_HEX=$(sha256sum "$ARTIFACT_PATH" | awk '{print $1}')
    HASH_B64=$(echo -n "$HASH_HEX" | xxd -r -p | base64)
    SIGNATURE=$(curl -s --header "X-Vault-Token: $VAULT_SIGN_TOKEN" \
        --request POST \
        --data "{\"input\": \"$HASH_B64\"}" \
        "$VAULT_ADDR/v1/transit/sign/$VAULT_KEY" | jq -r .data.signature)
    echo "$SIGNATURE" > "$ARTIFACT_PATH.sig"
    echo "$HASH_HEX" > "$ARTIFACT_PATH.sha256"
    VAULT_PAYLOAD=$(jq -n \
      --arg sig "$(cat "$ARTIFACT_PATH.sig")" \
      --arg hash "$(cat "$ARTIFACT_PATH.sha256")" \
      --arg datetime "$(TZ='America/Sao_Paulo' date -d '-3 hours' '+%d/%m/%Y - %H:%M:%S')" \
      --arg size "$(stat -c%s "$ARTIFACT_PATH" | numfmt --to=iec)" \
      --arg executed_by "$(whoami)" \
      '{sign: $sig, hash_sha256: $hash, datetime: $datetime, artefact_size: $size, executed_by: $executed_by}')
 

  curl -s --header "X-Vault-Token: $VAULT_ARTEFATO_TOKEN" \
        --request POST \
        --data "$VAULT_PAYLOAD" \
        "$VAULT_ADDR/v1/artefatos/monitoramento_acesso"
  artifacts:
    paths:
      - artifact/.filehashes
      - artifact/.filehashes.sig
      - artifact/.filehashes.sha256

Verificação de assinatura (manual ou automática)

# Validar integridade
sha256sum artifact/.filehashes | grep $(cat artifact/.filehashes.sha256)

# Validar autenticidade com Vault
HASH_B64=$(cat artifact/.filehashes.sha256 | xxd -r -p | base64)

curl -s --header "X-Vault-Token: $VAULT_SIGN_TOKEN" \
    --request POST \
    --data "{\"input\": \"$HASH_B64\", \"signature\": \"$(cat artifact/.filehashes.sig)\"}" \
    "$VAULT_ADDR/v1/transit/verify/my-signing-key"

Se válido:

{"data":{"valid":true}}

Benefícios reais

Expansões possíveis

  • Uso com containers (cosign + Vault)
  • Integração com OIDC/JWT do GitLab (sem token fixo)
  • Auditoria automática com Lambda ou webhook
  • Verificação em runtime antes do deploy via webhook de validação

Conclusão

Se você já pensa em segurança como efemeridade, sua próxima etapa é a assinatura granular de tudo que o CI/CD gera. Vault deixa de ser só um cofre de senhas e vira o cartório digital do seu DevSecOps.

Em tempos de código, fumaça e ameaças, só sobrevive quem assina o que entrega.
A confiança agora tem um hash. E uma assinatura.