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.