Todos os artigos
Eventos de push e merge request de repositório Git passando por um túnel HTTPS assinado para um serviço de desenvolvimento local.
GitLabDevOpswebhookslocalhost

Testar Webhooks do GitLab no Localhost com Segurança

Para testar um webhook do GitLab no localhost, exponha seu manipulador local através de um túnel HTTPS, adicione essa URL em Configurações → Webhooks, gere um token de assinatura e verifique a assinatura de Webhooks Padrão do GitLab antes de analisar o payload. Acione um push ou merge request, inspecione a entrega e itere localmente sem precisar implantar sua integração após cada alteração.

Use tokens de assinatura do GitLab, não um novo token secreto em texto plano.

O GitLab suporta dois mecanismos que são fáceis de confundir. O token secreto mais antigo é copiado no cabeçalho da X-Gitlab-Token requisição. Ele prova o conhecimento de um valor compartilhado, mas não protege a integridade do corpo. O GitLab agora recomenda um token de assinatura para novos webhooks. Ele produz uma assinatura HMAC-SHA256 e segue o formato de mensagem dos Webhooks Padrão.

O A documentação oficial de webhooks do GitLab diz que uma requisição assinada contém webhook-id, webhook-timestamp e webhook-signature. A assinatura cobre o ID da mensagem, o timestamp e o corpo JSON bruto exato. Isso protege tanto a origem quanto a integridade da carga útil.

Implemente a verificação padrão de Webhooks em Node.js

Os tokens de assinatura do GitLab são exibidos uma vez e usam um prefixo whsec_ . Remova esse prefixo e decodifique o restante em Base64 para obter a chave HMAC. Cada assinatura recebida tem a forma v1,<base64 signature>; o cabeçalho pode conter várias assinaturas separadas por espaço.

import crypto from 'node:crypto';

function safeEqual(a, b) {
  const left = Buffer.from(a);
  const right = Buffer.from(b);
  return left.length === right.length &&
    crypto.timingSafeEqual(left, right);
}

function verifyGitLabWebhook({ token, id, timestamp, body, signatures }) {
  if (!token?.startsWith('whsec_') || !id || !timestamp || !signatures) {
    return false;
  }

  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!Number.isFinite(age) || age > 300) return false;

  const key = Buffer.from(token.slice(6), 'base64');
  const message = `${id}.${timestamp}.${body}`;
  const digest = crypto.createHmac('sha256', key).update(message).digest('base64');
  const expected = `v1,${digest}`;
  return signatures.split(' ').some((value) => safeEqual(value, expected));
}

A janela de timestamp de cinco minutos mostrada aqui é uma política de aplicação, não um valor para copiar cegamente. Escolha uma tolerância que acomode o desvio do relógio, mas que bloqueie repetições úteis. Sincronize o relógio da máquina receptora. Armazene cada webhook-id sob uma restrição única, porque uma verificação de timestamp recente por si só não pode impedir duas entregas imediatas da mesma mensagem.

Construa a rota do webhook Express

Capture o corpo bruto nesta rota. Uma chamada global express.json() antes da verificação destrói a representação byte a byte assinada pelo GitLab.

import express from 'express';

const app = express();
app.post(
  '/webhooks/gitlab',
  express.raw({ type: 'application/json', limit: '2mb' }),
  async (req, res) => {
    const body = req.body.toString('utf8');
    const valid = verifyGitLabWebhook({
      token: process.env.GITLAB_WEBHOOK_SIGNING_TOKEN,
      id: req.get('webhook-id'),
      timestamp: req.get('webhook-timestamp'),
      signatures: req.get('webhook-signature'),
      body,
    });
    if (!valid) return res.sendStatus(401);

    await inbox.insertOnce(req.get('webhook-id'), JSON.parse(body));
    return res.sendStatus(202);
  },
);
app.use(express.json());
app.listen(3000);

Monte o parser JSON normal após a rota do webhook ou use seu verify callback para preservar um buffer bruto. Nunca desative as verificações de assinatura apenas porque o endpoint encaminha para localhost; a URL do túnel ainda é acessível a partir da internet pública.

Crie um endpoint HTTPS público

  1. Inicie a integração localmente e teste sua rota com uma solicitação deliberadamente não assinada. Deve retornar 401, provando que a autenticação está ativa.
  2. Execute npx portpreview 3000 em outro terminal.
  3. Copie a origem HTTPS e anexe /webhooks/gitlab.
  4. Mantenha o túnel aberto durante toda a configuração e os testes de eventos.

A verificação SSL do GitLab deve permanecer habilitada. Um túnel com TLS publicamente confiável evita erros de certificado autoassinado. Se o GitLab estiver em uma rede privada autogerenciada, ele também deve ter acesso de saída à URL pública do túnel.

Configure o webhook do projeto

  1. Abra o projeto do GitLab e escolha Configurações → Webhooks.
  2. Selecione Adicionar novo webhook e cole a URL completa de entrega do túnel.
  3. Selecione Gerar token de assinatura, copie o token imediatamente e salve-o em GITLAB_WEBHOOK_SIGNING_TOKEN.
  4. Selecione apenas os gatilhos necessários—por exemplo, eventos de push, eventos de merge request, eventos de push de tag ou eventos de pipeline.
  5. Mantenha a verificação SSL habilitada e salve o webhook.
  6. Use a ação de teste do GitLab ou produza um evento real, depois inspecione a solicitação local e o histórico de entregas do GitLab.

Reinicie o processo local após definir a variável de ambiente. Se você estiver migrando uma integração existente, o GitLab permite um token de assinatura e um token secreto legado juntos. Verifique webhook-signature quando presente, retorne temporariamente para X-Gitlab-Token, depois remova o segredo mais fraco após todos os receptores suportarem assinaturas.

Envie eventos do GitLab por cabeçalho e payload

X-Gitlab-Event fornece um nome de evento legível, como Push Hook ou Merge Request Hook. Use-o para roteamento, mas valide o object_kind do payload também. Isso torna combinações inesperadas visíveis.

switch (req.get('x-gitlab-event')) {
  case 'Push Hook':
    await handlePush(payload);
    break;
  case 'Merge Request Hook':
    await handleMergeRequest(payload);
    break;
  case 'Pipeline Hook':
    await handlePipeline(payload);
    break;
  default:
    await recordUnsupportedGitLabEvent(payload.object_kind);
}

Eventos de push

Criação de branch de teste, commits comuns, force pushes e exclusão de branch. Um SHA zero pode representar um lado ausente de uma transição de ref. Pushes grandes podem diferir de um fixture de um commit, então não assuma que todo commit alterado apareça em uma matriz ilimitada. Use identificadores de projeto e de ref em vez de analisar uma string de exibição.

Eventos de merge request

Ações como abrir, atualizar, aprovar, mesclar e fechar podem compartilhar o mesmo tipo de evento amplo. Roteie pelos atributos de objeto documentados e torne atualizações repetidas idempotentes. Nunca mescle código ou aprove uma implantação apenas porque um título ou nome de usuário mutável corresponda.

Eventos de pipeline e job

Estes podem ser frequentes. Filtre no GitLab e novamente em seu handler por projeto, branch, status e ambiente. Enfileire trabalhos lentos de artefato ou implantação e reconheça o webhook primeiro.

Design para tentativas e gatilhos recursivos

O GitLab inclui webhook-id, que permanece consistente entre as tentativas e é igual ao legado Idempotency-Key. Use-o como a chave de idempotência de entrega. X-Gitlab-Webhook-UUID Identifica a execução de um webhook, enquanto X-Gitlab-Event-UUID pode ajudar a rastrear eventos; webhooks recursivos podem compartilhar o UUID do evento.

Se o manipulador modificar o GitLab através da API, ele pode criar outro webhook. Adicione prevenção explícita de loops: marque ações com a identidade da sua integração, ignore alterações que não modifiquem o estado desejado e limite as transições de fluxo de trabalho. O guia de tentativas e idempotência cobre padrões de inbox transacional.

Solucionar problemas de testes de webhook do GitLab falhados

O GitLab não consegue conectar à URL

Confirme que o processo do túnel está ativo, o caminho completo está correto e seu servidor local está ouvindo na porta encaminhada. Para GitLab autogerenciado, verifique a política de rede de saída e o DNS. Não desative a verificação SSL para ocultar uma falha de roteamento não relacionada.

A assinatura nunca corresponde

Use o token de assinatura, não o token secreto antigo. Remova whsec_, decodifique em Base64 o token restante e assine {webhook-id}.{webhook-timestamp}.{raw body}. Codifique em Base64 o resumo HMAC binário e prefixe com v1,. Compare com todas as assinaturas separadas por espaços.

O carimbo de data/hora é rejeitado

Verifique a hora do sistema e o manuseio do fuso horário; o cabeçalho é um carimbo de data/hora Unix em segundos. Não o compare com milissegundos do JavaScript sem dividir por 1000. Se estiver depurando uma solicitação antiga capturada, a rejeição do carimbo de data/hora é a proteção de replay correta.

O GitLab desativa ou diminui o webhook

Verifique o status de entrega recente e a resposta da sua rota. Retorne 2xx rapidamente após aceitação durável. 401 repetidos significam que a configuração do token está errada; 5xx repetidos significam falhas no manipulador; timeouts indicam trabalho síncrono excessivo.

Apenas alguns eventos chegam

Revise os gatilhos selecionados e filtros de branch. Webhooks de grupo e projeto têm escopos diferentes. Confirme se o evento ocorreu exatamente no projeto onde este webhook está configurado.

Mantenha os dados do webhook local do GitLab seguros

  • Armazene tokens de assinatura apenas em arquivos de ambiente ignorados e substitua qualquer token vazado.
  • Valide assinaturas, timestamps, IDs de projeto e tipos de evento permitidos antes de efeitos colaterais.
  • Oculte mensagens de commit, URLs de repositórios privados, e-mails de usuários e variáveis de CI das capturas.
  • Dê ao token de API de integração apenas os escopos necessários para sua ação subsequente.
  • Exclua o histórico de payload local quando os testes terminarem.

Para diagnósticos independentes do provedor, use o guia de depuração de webhook local. O GitHub usa um formato de assinatura diferente, portanto, consulte o guia de webhook do GitHub separado em vez de reutilizar seu verificador.

Perguntas frequentes

Como testar um webhook do GitLab no localhost?
Exponha sua rota local com um túnel HTTPS, adicione sua URL pública em Webhooks do projeto GitLab, configure um token de assinatura e gatilhos, e então gere um evento de teste ou real.
Novos webhooks do GitLab devem usar X-Gitlab-Token?
O GitLab recomenda tokens de assinatura para novos webhooks. X-Gitlab-Token carrega um segredo em texto puro, enquanto tokens de assinatura autenticam um digest HMAC-SHA256 da requisição.
Como é calculada a assinatura do webhook do GitLab?
Decodifique o token de assinatura após remover whsec_, faça HMAC-SHA256 da string webhook-id.webhook-timestamp.raw-body, codifique em Base64 o digest e adicione o prefixo v1,.
Como posso evitar ações duplicadas de webhook no GitLab?
Armazene webhook-id sob uma restrição única e aplique efeitos colaterais de forma transacional. O GitLab mantém esse ID estável em reinícios, tornando-o adequado para idempotência.