Щоб протестувати вебхук GitLab на локальному хості, зробіть свій локальний обробник доступним через HTTPS тунель, додайте цю URL-адресу в Налаштування → Вебхуки, згенеруйте підписуючий токен і перевірте підпис Стандартного вебхука GitLab перед розбором даних. Спровокуйте push або merge request, перевірте доставку та працюйте локально без розгортання інтеграції після кожної зміни.
Використовуйте підписуючі токени GitLab, а не новий простий токен.
GitLab підтримує два механізми, які легко сплутати. Старий секретний токен копіюється у заголовок X-Gitlab-Token запиту. Він підтверджує знання спільного значення, але не захищає цілісність тіла. GitLab тепер рекомендує підписуючий токен для нових вебхуків. Він створює HMAC-SHA256 підпис і відповідає формату повідомлень Стандартного вебхука.
The офіційна документація GitLab по вебхуках каже, що підписаний запит містить webhook-id, webhook-timestamp та webhook-signature. Підпис охоплює ID повідомлення, відмітку часу та точне сире тіло JSON. Це захищає як походження, так і цілісність даних.
Реалізуйте перевірку стандартних вебхуків у Node.js
Токени підпису GitLab відображаються один раз і використовують префікс whsec_ . Видаліть цей префікс і декодуйте залишок Base64, щоб отримати HMAC-ключ. Кожний отриманий підпис має форму v1,<base64 signature>; заголовок може містити кілька підписів, розділених пробілом.
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));
}
П’ятихвилинне вікно відмітки часу, показане тут, є політикою застосунку, а не значенням для сліпого копіювання. Оберіть толерантність, яка враховує розбіжність часу, але блокує корисне повторне використання. Синхронізуйте годинник машини, що отримує запити. Зберігайте кожен прийнятий webhook-id під унікальним обмеженням, оскільки перевірка свіжого штампу часу сама по собі не може запобігти двом негайним доставки того самого повідомлення.
Створіть маршрут webhook у Express
Захоплюйте необроблене тіло на цьому маршруті. Глобальний express.json() виклик перед перевіркою знищує побайтове представлення, підписане 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);
Підключіть звичайний JSON-парсер після маршруту webhook або використовуйте його verify callback для збереження необробленого буфера. Ніколи не вимикайте перевірку підпису тільки тому, що кінцева точка пересилає на localhost; URL тунелю все одно доступний з публічного інтернету.
Створіть публічну HTTPS-кінцеву точку
- Запустіть інтеграцію локально та протестуйте її маршрут навмисно непідписаним запитом. Він має повернути 401, що доводить, що аутентифікація активна.
- Запустіть
npx portpreview 3000в іншому терміналі. - Скопіюйте HTTPS-джерело та додайте
/webhooks/gitlab. - Тримайте тунель відкритим протягом усієї конфігурації та тестування подій.
Перевірка SSL у GitLab повинна залишатися увімкненою. Тунель із загальнодоступним TLS уникає помилок самопідписаного сертифіката. Якщо GitLab працює в приватній самостійно керованій мережі, він також має мати вихідний доступ до загальнодоступного URL тунелю.
Налаштуйте вебхук проекту
- Відкрийте проект у GitLab і виберіть Settings → Webhooks.
- Виберіть Add new webhook і вставте повний URL доставки тунелю.
- Виберіть Generate signing token, скопіюйте токен негайно та збережіть його в
GITLAB_WEBHOOK_SIGNING_TOKEN. - Вибирайте лише необхідні тригери — наприклад події Push, події Merge request, події Tag push або події Pipeline.
- Залиште перевірку SSL увімкненою та збережіть вебхук.
- Використовуйте тестову дію GitLab або створіть реальну подію, а потім перевірте локальний запит та історію доставки GitLab.
Перезапустіть локальний процес після встановлення змінної середовища. Якщо ви переносите існуючу інтеграцію, GitLab дозволяє використовувати токен підпису та старий секретний токен разом. Перевірте webhook-signature коли він є, тимчасово використовуйте X-Gitlab-Token, а потім видаліть слабший секрет після того, як усі отримувачі підтримуватимуть підписи.
Надсилайте події GitLab за заголовком і вмістом
X-Gitlab-Event , що дає зрозуміле ім'я події, таке як Push Hook або Merge Request Hook. Використовуйте його для маршрутизації, але також перевіряйте object_kind у вмісті. Це робить видимими несподівані комбінації.
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);
}
Події Push
Створення тестової гілки, звичайні коміти, примусові push-и та видалення гілки. Нульовий SHA може позначати відсутню сторону переходу ref. Великі push-и можуть відрізнятися від одного коміту, тож не припускайте, що кожен змінений коміт з’являється в необмеженому масиві. Використовуйте ідентифікатори проекту та ref замість аналізу рядка відображення.
Події запиту на злиття
Дії, такі як відкриття, оновлення, затвердження, злиття та закриття, можуть мати один і той же загальний тип події. Маршрутизуйте за задокументованими атрибутами об’єкта та робіть повторювані оновлення ідемпотентними. Ніколи не зливайте код і не затверджуйте розгортання лише тому, що змінна назва або ім’я користувача збігаються.
Події конвеєра та задачі
Вони можуть бути частими. Фільтруйте на GitLab і знову у вашому обробнику за проектом, гілкою, статусом та середовищем. Чергуйте повільні роботи з артефактами або розгортанням і спочатку підтверджуйте вебхук.
Проєктування для повторних спроб та рекурсивних тригерів
GitLab включає webhook-id, який залишається незмінним під час повторних спроб і дорівнює застарілому Idempotency-Key. Використовуйте його як ключ ідемпотентності доставки. X-Gitlab-Webhook-UUID ідентифікує виконання вебхука, тоді як X-Gitlab-Event-UUID може допомогти відстежувати події; рекурсивні вебхуки можуть мати спільний UUID події.
Якщо обробник змінює GitLab через API, він може створити ще один вебхук. Додайте явне запобігання зацикленням: позначайте дії своєю інтеграційною ідентичністю, ігноруйте зміни, які не змінюють потрібний стан, і обмежуйте переходи робочого процесу. Керівництво з повторних спроб та ідемпотентності охоплює патерни транзакційної скриньки.
Усунення неполадок невдалих тестів вебхуків GitLab
GitLab не може підключитися до URL
Підтвердьте, що процес тунелю активний, повний шлях правильний і ваш локальний сервер слухає на пересланому порті. Для самостійно керованого GitLab перевірте політику вихідної мережі та DNS. Не вимикайте перевірку SSL, щоб приховати несуміжну помилку маршрутизації.
Підпис ніколи не збігається
Використовуйте токен для підпису, а не старий секретний токен. Видаліть whsec_, Base64-декодуйте залишений токен і підпишіть {webhook-id}.{webhook-timestamp}.{raw body}. Закодуйте бінарний HMAC-дigest у Base64 і додайте префікс v1,. Порівняйте з кожним підписом, розділеним пробілами.
Часова мітка відхилена
Перевірте системний час та обробку часових зон; заголовок містить Unix-часову мітку в секундах. Не порівнюйте її з JavaScript-міллісекундами без поділу на 1000. Якщо налагоджуєте захоплений старий запит, відхилення часової мітки є нормальною захисною реакцією Replay.
GitLab вимикає вебхук або відступає
Перевірте останній стан доставки та відповідь вашого маршруту. Поверніть 2xx швидко після надійного прийняття. Повторювані 401 означають помилку конфігурації токена; повторювані 5xx означають збої обробника; тайм-аути вказують на занадто багато синхронної роботи.
Лише деякі події надходять
Перегляньте вибрані тригери та фільтри гілок. Групові та проєктні вебхуки мають різний обсяг. Підтвердіть, що подія відбулася саме у проєкті, де налаштований цей вебхук.
Зберігайте дані локального вебхука GitLab у безпеці
- Зберігайте токени підпису лише в ігнорованих файлах середовища та замініть будь-який витіклий токен.
- Перевіряйте підписи, часові позначки, ID проєкту та дозволені типи подій перед побічними ефектами.
- Редагуйте повідомлення комітів, URL приватних репозиторіїв, електронні адреси користувачів та змінні CI у захопленнях.
- Надавайте токен інтеграції API лише з тими дозволами, які потрібні для його подальших дій.
- Видаляйте історію локальних запитів після завершення тестування.
Для діагностики, що не залежить від провайдера, використовуйте посібник з налагодження локального вебхука. GitHub використовує інший формат підпису, тому зверніться до окремого посібника з вебхуків GitHub замість повторного використання його перевіряльника.
