Aby przetestować webhook GitLab na localhost, udostępnij swój lokalny handler przez tunel HTTPS, dodaj ten URL w Ustawienia → Webhooki, wygeneruj token podpisu i zweryfikuj standardowy podpis Webhooków GitLab przed analizą danych. Wyzwól push lub merge request, zbadaj dostawę i iteruj lokalnie bez wdrażania integracji po każdej zmianie.
Używaj tokenów podpisu GitLab, a nie nowego tokenu w postaci zwykłego tekstu
GitLab obsługuje dwa mechanizmy, które łatwo pomylić. Starszy token tajny jest kopiowany do nagłówka żądania X-Gitlab-Token . Udowadnia znajomość wspólnej wartości, ale nie chroni integralności danych. GitLab obecnie zaleca token podpisu dla nowych webhooków. Generuje on podpis HMAC-SHA256 i stosuje się do standardowego formatu wiadomości Webhooks.
oficjalna dokumentacja webhooków GitLab mówi, że podpisane żądanie zawiera webhook-id, webhook-timestamp i webhook-signature. Podpis obejmuje identyfikator wiadomości, znacznik czasu oraz dokładne surowe ciało JSON. Chroni to zarówno pochodzenie, jak i integralność danych.
Zaimplementuj weryfikację Standardowych Webhooków w Node.js
Tokeny podpisujące GitLab są wyświetlane tylko raz i używają prefiksu whsec_ . Usuń ten prefiks i zdekoduj resztę Base64, aby uzyskać klucz HMAC. Każdy otrzymany podpis ma postać v1,<base64 signature>; nagłówek może zawierać kilka podpisów oddzielonych spacją.
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));
}
Okno pięciu minut dla znacznika czasu pokazane tutaj jest polityką aplikacji, a nie wartością do ślepego kopiowania. Wybierz tolerancję, która uwzględnia różnice czasowe, ale blokuje użyteczne powtórzenia. Synchronizuj zegar maszyny odbierającej. Przechowuj każdy zaakceptowany webhook-id pod unikalnym ograniczeniem, ponieważ sama kontrola świeżego znacznika czasu nie może zapobiec dwóm natychmiastowym dostawom tej samej wiadomości.
Zbuduj trasę webhook Express
Przechwyć surowe ciało na tej trasie. Globalne wywołanie express.json() przed weryfikacją niszczy reprezentację bit po bicie podpisaną przez 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);
Zamontuj normalny parser JSON po trasie webhook lub użyj jego verify callback, aby zachować surowy bufor. Nigdy nie wyłączaj sprawdzania podpisu tylko dlatego, że endpoint przekazuje do localhost; adres URL tunelu jest nadal dostępny z publicznego internetu.
Utwórz publiczny punkt końcowy HTTPS
- Uruchom integrację lokalnie i przetestuj jej trasę za pomocą celowo niepodpisanego żądania. Powinno zwrócić 401, co dowodzi, że uwierzytelnianie jest aktywne.
- Uruchom
npx portpreview 3000w innym terminalu. - Skopiuj pochodzenie HTTPS i dodaj
/webhooks/gitlab. - Utrzymuj tunel otwarty podczas konfiguracji i testowania zdarzeń.
Weryfikacja SSL w GitLab powinna pozostać włączona. Tunel z publicznie zaufanym TLS zapobiega błędom certyfikatów samopodpisanych. Jeśli GitLab działa w prywatnej, samodzielnie zarządzanej sieci, musi również mieć dostęp wychodzący do publicznego adresu URL tunelu.
Skonfiguruj webhook projektu
- Otwórz projekt GitLab i wybierz Settings → Webhooks.
- Wybierz Add new webhook i wklej pełny adres URL dostarczania tunelu.
- Wybierz Generate signing token, natychmiast skopiuj token i zapisz go w
GITLAB_WEBHOOK_SIGNING_TOKEN. - Wybierz tylko wymagane wyzwalacze — na przykład Push events, Merge request events, Tag push events lub Pipeline events.
- Pozostaw włączoną weryfikację SSL i zapisz webhook.
- Użyj akcji testowej GitLab lub wygeneruj prawdziwe zdarzenie, a następnie sprawdź lokalne żądanie i historię dostarczania GitLab.
Uruchom ponownie lokalny proces po ustawieniu zmiennej środowiskowej. Jeśli migrujesz istniejącą integrację, GitLab pozwala na użycie tokena podpisu i starszego tokena sekretnego razem. Zweryfikuj webhook-signature w przypadku obecności, tymczasowo wróć do X-Gitlab-Token, a następnie usuń słabszy sekret po tym, jak wszyscy odbiorcy obsłużą podpisy.
Dystrybuuj zdarzenia GitLab według nagłówka i ładunku
X-Gitlab-Event daje czytelną nazwę zdarzenia, taką jak Push Hook lub Merge Request Hook. Używaj jej do trasowania, ale również weryfikuj object_kind ładunku. To sprawia, że nieoczekiwane kombinacje stają się widoczne.
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);
}
Zdarzenia push
Testowanie tworzenia gałęzi, zwykłych commitów, wymuszonych pushy oraz usuwania gałęzi. Zerowe SHA może reprezentować brakującą stronę w przejściu referencji. Duże pushy mogą różnić się od pojedynczego commita, więc nie zakładaj, że każdy zmieniony commit pojawia się w nieograniczonej tablicy. Używaj identyfikatorów projektu i referencji zamiast parsowania ciągu wyświetlanego.
Zdarzenia żądania scalania
Działania takie jak otwarcie, aktualizacja, zatwierdzenie, scalanie i zamknięcie mogą mieć ten sam ogólny typ zdarzenia. Kieruj na podstawie udokumentowanych atrybutów obiektu i spraw, aby powtarzające się aktualizacje były idempotentne. Nigdy nie łącz kodu ani nie zatwierdzaj wdrożenia tylko dlatego, że zmienny tytuł lub nazwa użytkownika się zgadza.
Zdarzenia potoków i zadań
Te mogą być częste. Filtruj w GitLab, a następnie ponownie w swoim handlerze według projektu, gałęzi, statusu i środowiska. Kolejkuj powolne zadania związane z artefaktami lub wdrożeniami i najpierw potwierdź webhook.
Projektowanie pod kątem ponownych prób i wywołań rekurencyjnych
GitLab zawiera webhook-id, który pozostaje spójny podczas ponownych prób i jest równy starszemu Idempotency-Key. Użyj go jako klucza idempotencji dostawy. X-Gitlab-Webhook-UUID identyfikuje wykonanie webhooka, podczas gdy X-Gitlab-Event-UUID może pomóc w śledzeniu zdarzeń; rekurencyjne webhooki mogą dzielić UUID zdarzenia.
Jeśli handler modyfikuje GitLab poprzez API, może utworzyć kolejny webhook. Dodaj wyraźną ochronę przed pętlami: oznacz działania swoją tożsamością integracji, ignoruj zmiany, które nie zmieniają pożądanego stanu, i ogranicz przejścia w przepływie pracy. Przewodnik retry i idempotency obejmuje wzorce transakcyjnej skrzynki odbiorczej.
Rozwiązywanie problemów z nieudanymi testami webhooków GitLab
GitLab nie może połączyć się z URL
Potwierdź, że proces tunelu działa, pełna ścieżka jest poprawna, a Twój lokalny serwer nasłuchuje na przekierowanym porcie. W przypadku samodzielnie zarządzanego GitLaba sprawdź politykę sieci wychodzącej i DNS. Nie wyłączaj weryfikacji SSL, aby ukryć niezwiązane błędy routingu.
Podpis nigdy się nie zgadza
Użyj tokena podpisywania, a nie starego tokena sekretu. Usuń whsec_, zdekoduj pozostały token w Base64 i podpisz {webhook-id}.{webhook-timestamp}.{raw body}. Zakoduj binarny skrót HMAC w Base64 i poprzedź go v1,. Porównaj z każdą podpisaną wartości oddzieloną spacją.
Znacznik czasu jest odrzucany
Sprawdź czas systemowy i obsługę strefy czasowej; nagłówek to znacznik czasu Unix w sekundach. Nie porównuj go z milisekundami w JavaScript bez podzielenia przez 1000. Jeśli debugujesz przechwycone stare żądanie, odrzucenie znacznika czasu jest poprawną ochroną przed powtórnym użyciem.
GitLab wyłącza lub ogranicza webhook
Sprawdź ostatni status dostawy i odpowiedź twojej trasy. Zwróć 2xx szybko po trwałym zaakceptowaniu. Powtarzające się 401 oznacza błędną konfigurację tokena; powtarzające się 5xx oznacza awarie obsługi; limity czasu wskazują na zbyt dużą ilość pracy synchronicznej.
Tylko niektóre zdarzenia docierają
Przejrzyj wybrane wyzwalacze i filtry gałęzi. Webhooki grupy i projektu mają różny zakres. Potwierdź, że zdarzenie wystąpiło dokładnie w projekcie, w którym ten webhook jest skonfigurowany.
Trzymaj lokalne dane webhooków GitLab w bezpiecznym miejscu
- Przechowuj tokeny podpisujące tylko w ignorowanych plikach środowiskowych i rotuj każdy wyciekły token.
- Weryfikuj podpisy, znaczniki czasu, identyfikatory projektów i dozwolone typy zdarzeń przed efektami ubocznymi.
- Cenzuruj wiadomości commitów, prywatne URL repozytorium, adresy e-mail użytkowników i zmienne CI w przechwyceniach.
- Przyznaj tokenowi API integracji tylko uprawnienia potrzebne do jego działań docelowych.
- Usuń lokalną historię danych testowych po zakończeniu testów.
Do diagnostyki niezależnej od dostawcy użyj lokalnego przewodnika po debugowaniu webhooków. GitHub używa innego formatu podpisu, więc należy skonsultować się z osobnym przewodnikiem po webhookach GitHub , zamiast ponownie używać jego weryfikatora.
