บทความทั้งหมด
เหตุการณ์การ push และ merge request ของ Git repository ที่ผ่านอุโมงค์ HTTPS ที่ลงชื่อแล้วไปยังบริการพัฒนาท้องถิ่น
GitLabDevOpswebhookslocalhost

ทดสอบ GitLab Webhooks บน Localhost อย่างปลอดภัย

ในการทดสอบเว็บฮุก GitLab บนเครื่อง localhost ให้เปิดเผยตัวจัดการท้องถิ่นของคุณผ่านอุโมงค์ HTTPS เพิ่ม URL นั้นภายใต้ การตั้งค่า → เว็บฮุก สร้างโทเค็นสำหรับการลงชื่อ และตรวจสอบลายเซ็นมาตรฐานของเว็บฮุก GitLab ก่อนวิเคราะห์ payload กระตุ้นการ push หรือ merge request ตรวจสอบการส่ง และทำซ้ำในเครื่องของคุณโดยไม่ต้องปรับใช้การรวมหลังจากทุกการเปลี่ยนแปลง

ใช้โทเค็นการลงชื่อของ GitLab แทนการใช้โทเค็นความลับแบบข้อความธรรมดาใหม่

GitLab รองรับกลไกสองแบบที่ง่ายต่อการสับสน โทเค็นความลับแบบเก่าจะถูกคัดลอกลงใน X-Gitlab-Token header ของคำขอ มันยืนยันความรู้ของค่าที่ใช้ร่วมกันแต่ไม่ปกป้องความสมบูรณ์ของตัวเนื้อหา GitLab แนะนำตอนนี้ให้ใช้ โทเค็นการลงชื่อ สำหรับเว็บฮุกใหม่ มันสร้างลายเซ็น HMAC-SHA256 และเป็นไปตามรูปแบบข้อความของเว็บฮุกมาตรฐาน

เอกสารเว็บฮุค GitLab อย่างเป็นทางการ กล่าวว่าคำขอที่ลงนามมี webhook-id, webhook-timestamp, และ webhook-signatureลายเซ็นครอบคลุมทั้งรหัสข้อความ เวลา และเนื้อหา JSON ดิบที่แน่นอน ซึ่งช่วยปกป้องความสมบูรณ์ทั้งต้นทางและข้อมูลที่ส่ง

ใช้งานการตรวจสอบ Webhooks มาตรฐานใน 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 ปกติหลังเส้นทางเว็บฮุคหรือใช้ verify callback ของมันเพื่อรักษาบัฟเฟอร์ดิบ อย่าปิดการตรวจสอบลายเซ็นเพียงเพราะจุดสิ้นสุดส่งต่อไปที่ localhost URL ของอุโมงค์ยังสามารถเข้าถึงได้จากอินเทอร์เน็ตสาธารณะ

สร้างจุดสิ้นสุด HTTPS สาธารณะ

  1. เริ่มการรวมในเครื่องและทดสอบเส้นทางด้วยคำขอที่ตั้งใจไม่เซ็น มันควรคืนค่า 401 ซึ่งพิสูจน์ว่าการรับรองความถูกต้องทำงานอยู่
  2. รัน npx portpreview 3000 ในเทอร์มินัลอื่น
  3. คัดลอกต้นทาง HTTPS และต่อท้าย /webhooks/gitlab.
  4. เก็บอุโมงค์ให้เปิดอยู่ตลอดทั้งการตั้งค่าและการทดสอบเหตุการณ์

การตรวจสอบ SSL ของ GitLab ควรเปิดใช้งานอยู่เสมอ อุโมงค์ที่มี TLS ที่ได้รับการยอมรับสาธารณะช่วยหลีกเลี่ยงข้อผิดพลาดของใบรับรองที่เซ็นด้วยตนเอง หาก GitLab ทำงานในเครือข่ายที่จัดการเองแบบส่วนตัว จะต้องสามารถเข้าถึง URL อุโมงค์สาธารณะได้เช่นกัน

ตั้งค่า webhook ของโปรเจ็ค

  1. เปิดโปรเจ็ค GitLab และเลือก การตั้งค่า → Webhooks.
  2. เลือก เพิ่ม webhook ใหม่ และวาง URL การส่งของอุโมงค์ทั้งหมด
  3. เลือก สร้างโทเค็นเซ็นชื่อ, คัดลอกโทเค็นทันที และบันทึกไว้ใน GITLAB_WEBHOOK_SIGNING_TOKEN.
  4. เลือกเฉพาะทริกเกอร์ที่จำเป็น เช่น เหตุการณ์ Push, เหตุการณ์ Merge request, เหตุการณ์ Tag push หรือเหตุการณ์ Pipeline
  5. ปล่อยการตรวจสอบ SSL ไว้และบันทึก webhook.
  6. ใช้การกระทำทดสอบของ 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 ขนาดใหญ่สามารถแตกต่างจาก fixture ที่มีเพียงคอมมิตเดียว ดังนั้นอย่าสมมติว่าคอมมิตที่เปลี่ยนแปลงทุกอันจะปรากฏในอาร์เรย์ที่ไม่มีขีดจำกัด ใช้ตัวระบุโปรเจกต์และ ref แทนการแยกวิเคราะห์สตริงที่แสดงผล

เหตุการณ์คำขอการรวม

การกระทำเช่น เปิด, อัปเดต, อนุมัติ, รวม, และปิด อาจแชร์ประเภทเหตุการณ์กว้างเดียวกัน ส่งเส้นทางตามคุณสมบัติของวัตถุที่มีเอกสารและทำให้อัปเดตซ้ำ ๆ เป็น idempotent อย่ารวมโค้ดหรืออนุมัติการปรับใช้เพียงเพราะชื่อเรื่องหรือชื่อผู้ใช้ที่เปลี่ยนแปลงได้ตรงกัน

เหตุการณ์ pipeline และงาน

สิ่งเหล่านี้สามารถเกิดบ่อย กรองที่ GitLab และอีกครั้งในตัวจัดการของคุณตามโปรเจกต์, สาขา, สถานะ, และสภาพแวดล้อม จัดลำดับคิวงานชิ้นงาน artifact หรือ deployment ที่ช้าและยืนยัน webhook ก่อน

ออกแบบสำหรับการลองใหม่และทริกเกอร์แบบเรียกซ้ำ

GitLab รวมถึง webhook-id, ซึ่งยังคงความสอดคล้องกันในทุกครั้งที่ลองใหม่และเท่ากับแบบเดิม Idempotency-Key. ใช้มันเป็นคีย์ความไม่เปลี่ยนแปลงของการส่ง X-Gitlab-Webhook-UUID ระบุการทำงานของเว็บฮุค ในขณะที่ X-Gitlab-Event-UUID สามารถช่วยติดตามเหตุการณ์ได้; เว็บฮุกแบบวนซ้ำอาจแชร์ UUID ของเหตุการณ์

หากผู้จัดการแก้ไข GitLab ผ่าน API อาจสร้าง webhook อีกตัว เพิ่มการป้องกันลูปอย่างชัดเจน: ทำเครื่องหมายการกระทำด้วยตัวตนของการรวมของคุณ มองข้ามการเปลี่ยนแปลงที่ไม่ได้เปลี่ยนแปลงสถานะที่ต้องการ และจำกัดการเปลี่ยนแปลงของเวิร์กโฟลว์ คู่มือการลองใหม่และความสามารถในการดำเนินการซ้ำโดยไม่เปลี่ยนผลลัพธ์ ครอบคลุมรูปแบบกล่องจดหมายธุรกรรม

แก้ไขปัญหาการทดสอบ webhook ของ GitLab ที่ล้มเหลว

GitLab ไม่สามารถเชื่อมต่อกับ URL ได้

ยืนยันว่ากระบวนการท่อยังทำงานอยู่, เส้นทางเต็มถูกต้อง, และเซิร์ฟเวอร์ภายในเครื่องของคุณรอฟังที่พอร์ตที่ส่งต่อ สำหรับ GitLab ที่จัดการเอง, ตรวจสอบนโยบายเครือข่ายขาออกและ DNS อย่าลบการตรวจสอบ SSL เพื่อปกปิดความล้มเหลวของการส่งต่อที่ไม่เกี่ยวข้อง

ลายเซ็นไม่ตรงกันเลย

ใช้โทเค็นการเซ็น, ไม่ใช่โทเค็นความลับเก่า ตัด whsec_, ถอดรหัส Base64 ของโทเค็นที่เหลือ, และเซ็น {webhook-id}.{webhook-timestamp}.{raw body} เข้ารหัส Base64 ของ HMAC digest แบบไบนารี่และเติมหน้า v1, เปรียบเทียบกับลายเซ็นที่คั่นด้วยช่องว่างทั้งหมด

เวลาประทับถูกปฏิเสธ

ตรวจสอบเวลาระบบและการจัดการเขตเวลา; เฮดเดอร์เป็นเวลาสำหรับ Unix เป็นวินาที อย่าเปรียบเทียบกับมิลลิวินาทีของ JavaScript โดยไม่หารด้วย 1000 หากกำลังดีบักคำขอเก่าที่จับไว้, การปฏิเสธเวลาประทับถือเป็นการป้องกันการเล่นซ้ำที่ถูกต้อง

GitLab ปิดใช้งานหรือถอยกลับ webhook

ตรวจสอบสถานะการส่งล่าสุดและการตอบสนองของเส้นทางของคุณ ส่งกลับ 2xx อย่างรวดเร็วหลังจากการยอมรับที่ทนทาน 401 ซ้ำ ๆ หมายถึงการตั้งค่าโทเค็นผิดพลาด; 5xx ซ้ำ ๆ หมายถึงข้อผิดพลาดของตัวจัดการ; เวลา timeout บ่งชี้ว่ามีงาน synchronous มากเกินไป

มีเพียงบางเหตุการณ์ที่มาถึง

ตรวจสอบทริกเกอร์ที่เลือกและตัวกรองสาขา Webhook ของกลุ่มและโปรเจ็กต์มีขอบเขตต่างกัน ยืนยันว่าเหตุการณ์เกิดขึ้นในโปรเจ็กต์ที่ตั้งค่า webhook นี้อย่างถูกต้อง

เก็บข้อมูล webhook ของ GitLab ในเครื่องให้ปลอดภัย

  • เก็บโทเค็นการลงชื่อในไฟล์สภาพแวดล้อมที่ถูกละเว้นเท่านั้นและหมุนเวียนโทเค็นที่รั่วไหลใด ๆ
  • ตรวจสอบลายเซ็น เวลาโปรแกรมโปรเจ็กต์ และประเภทเหตุการณ์ที่อนุญาตก่อนผลกระทบทางด้านข้าง
  • ลบข้อความ commit, URL ของ repository ส่วนตัว, อีเมลผู้ใช้ และตัวแปร CI จากการจับข้อมูล
  • ให้โทเค็น API การรวมระบบมีเพียงขอบเขตที่จำเป็นสำหรับการกระทำด้านล่างของมันเท่านั้น
  • ลบประวัติข้อความ payload ในเครื่องเมื่อการทดสอบสิ้นสุดลง

สำหรับการวินิจฉัยที่ไม่ขึ้นกับผู้ให้บริการ ให้ใช้ คู่มือการดีบัก webhook ในเครื่อง GitHub ใช้รูปแบบลายเซ็นที่แตกต่างกัน ดังนั้นให้ปรึกษา คู่มือ webhook ของ GitHub แยกต่างหาก แทนที่จะนำตัวตรวจสอบของมันมาใช้ซ้ำ

คำถามที่พบบ่อย

ฉันทดสอบ GitLab webhook บน localhost ได้อย่างไร?
เปิดเผยเส้นทางโลคัลของคุณด้วย HTTPS tunnel เพิ่ม URL สาธารณะของมันภายใต้ Webhooks ของโปรเจกต์ GitLab ตั้งค่ารหัสเซ็นและตัวทริกเกอร์ จากนั้นสร้างอีเวนต์ทดสอบหรืออีเวนต์จริง
GitLab webhooks ใหม่ควรใช้ X-Gitlab-Token หรือไม่?
GitLab แนะนำให้ใช้รหัสเซ็นสำหรับ webhooks ใหม่ X-Gitlab-Token ใช้รหัสลับแบบข้อความธรรมดา ขณะที่รหัสเซ็นยืนยัน HMAC-SHA256 digest ของคำขอ
การคำนวณ webhook-signature ของ GitLab เป็นอย่างไร?
ถอดรหัสโทเค็นการเซ็นหลังจากลบ whsec_ ออก, ทำ HMAC-SHA256 กับสตริง webhook-id.webhook-timestamp.raw-body, เข้ารหัส Digest ด้วย Base64, และใส่คำนำหน้า v1,.
ฉันจะป้องกันการเรียกใช้งาน GitLab webhook ซ้ำได้อย่างไร?
เก็บ webhook-id ภายใต้ข้อจำกัดที่ไม่ซ้ำกันและประยุกต์ผลกระทบข้างเคียงแบบ Transactional GitLab จะเก็บ ID นั้นคงที่ข้ามการลองใหม่ ทำให้เหมาะสำหรับการทำให้ทำซ้ำได้ (idempotency)