Tất cả bài viết
Các sự kiện push và merge request của Git vượt qua tunnel HTTPS đã ký vào dịch vụ phát triển cục bộ.
GitLabDevOpswebhookslocalhost

Kiểm tra GitLab Webhooks trên Localhost một cách An toàn

Để kiểm tra một webhook GitLab trên localhost, bạn hãy mở handler cục bộ của mình thông qua một đường hầm HTTPS, thêm URL đó vào mục Cài đặt → Webhooks, tạo một mã ký, và xác minh chữ ký Webhooks Chuẩn của GitLab trước khi phân tích payload. Kích hoạt một lần push hoặc merge request, kiểm tra việc gửi, và lập trình thử nghiệm cục bộ mà không cần triển khai tích hợp sau mỗi thay đổi.

Sử dụng mã ký GitLab, không phải một mã bí mật dạng văn bản thuần mới

GitLab hỗ trợ hai cơ chế mà dễ nhầm lẫn. Mã bí mật cũ được sao chép vào X-Gitlab-Token header của request. Nó chứng minh việc biết giá trị chung nhưng không bảo vệ tính toàn vẹn của nội dung. GitLab hiện khuyến nghị một mã ký cho các webhook mới. Nó tạo ra một chữ ký HMAC-SHA256 và tuân theo định dạng thông điệp Webhooks Chuẩn.

The tài liệu chính thức về webhook của GitLab nói rằng một yêu cầu được ký chứa webhook-id, webhook-timestampwebhook-signature. Chữ ký bao phủ ID thông điệp, dấu thời gian và phần thân JSON thô chính xác. Điều này bảo vệ cả nguồn gốc và tính toàn vẹn của tải.

Triển khai xác minh Webhooks Tiêu chuẩn trong Node.js

Token ký của GitLab chỉ hiển thị một lần và sử dụng tiền tố whsec_ . Xóa tiền tố đó và giải mã Base64 phần còn lại để lấy khóa HMAC. Mỗi chữ ký nhận được có dạng v1,<base64 signature>; header có thể chứa nhiều chữ ký cách nhau bởi dấu cách.

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));
}

Khoảng thời gian năm phút cho dấu thời gian được hiển thị ở đây là chính sách ứng dụng, không phải giá trị để sao chép một cách mù quáng. Chọn mức dung sai phù hợp với lệch đồng hồ nhưng ngăn chặn việc phát lại có ích. Đồng bộ hóa đồng hồ của máy nhận. Lưu trữ mỗi yêu cầu được chấp nhận webhook-id dưới một ràng buộc duy nhất vì chỉ kiểm tra dấu thời gian mới không thể ngăn hai lần gửi ngay lập tức cùng một tin nhắn.

Xây dựng tuyến webhook Express

Ghi lại nội dung thô trên tuyến này. Một lệnh express.json() toàn cục trước khi xác minh sẽ phá hủy biểu diễn byte-for-byte mà GitLab đã ký.

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);

Gắn bộ phân tích JSON thông thường sau tuyến webhook hoặc sử dụng verify callback của nó để giữ nguyên bộ đệm thô. Không bao giờ tắt kiểm tra chữ ký chỉ vì điểm cuối chuyển tiếp đến localhost; URL đường hầm vẫn có thể truy cập từ Internet công cộng.

Tạo một điểm cuối HTTPS công khai

  1. Khởi động tích hợp cục bộ và kiểm tra tuyến của nó với một yêu cầu cố tình không ký. Nó nên trả về 401, chứng minh xác thực đang hoạt động.
  2. Chạy npx portpreview 3000 trong một terminal khác.
  3. Sao chép nguồn HTTPS và thêm /webhooks/gitlab.
  4. Giữ cho đường hầm luôn mở trong suốt quá trình cấu hình và kiểm tra sự kiện.

Việc xác thực SSL của GitLab nên được bật. Một đường hầm với TLS được tin cậy công khai sẽ tránh lỗi chứng chỉ tự ký. Nếu GitLab chạy trong một mạng tự quản lý riêng tư, nó cũng phải có quyền truy cập ra ngoài tới URL của đường hầm công khai.

Cấu hình webhook cho dự án

  1. Mở dự án GitLab và chọn Settings → Webhooks.
  2. Chọn Add new webhook và dán URL giao hàng đầy đủ của đường hầm.
  3. Chọn Generate signing token, sao chép token ngay lập tức và lưu vào GITLAB_WEBHOOK_SIGNING_TOKEN.
  4. Chỉ chọn những sự kiện kích hoạt cần thiết — ví dụ: Push events, Merge request events, Tag push events, hoặc Pipeline events.
  5. Giữ xác thực SSL được bật và lưu webhook.
  6. Sử dụng hành động kiểm tra của GitLab hoặc tạo ra một sự kiện thực, sau đó kiểm tra yêu cầu cục bộ và lịch sử phân phối của GitLab.

Khởi động lại quy trình cục bộ sau khi thiết lập biến môi trường. Nếu bạn đang di chuyển một tích hợp hiện có, GitLab cho phép sử dụng cùng lúc mã thông báo ký và mã thông báo bí mật cũ. Xác minh webhook-signature khi có mặt, tạm thời quay lại với X-Gitlab-Token, sau đó loại bỏ mã bí mật yếu khi tất cả các bộ nhận đã hỗ trợ chữ ký.

Phân phối các sự kiện của GitLab theo tiêu đề và nội dung tải

X-Gitlab-Event sẽ cung cấp một tên sự kiện dễ đọc như Push Hook hoặc Merge Request Hook. Sử dụng nó để định tuyến, nhưng cũng xác thực object_kind của nội dung tải. Điều này giúp thấy được các kết hợp không mong muốn.

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);
}

Sự kiện Push

Kiểm tra việc tạo nhánh, các commit thông thường, đẩy ép buộc và xoá nhánh. Một SHA bằng không có thể đại diện cho một bên bị thiếu của một chuyển đổi ref. Các lần đẩy lớn có thể khác với một fixture chỉ một commit, vì vậy đừng giả định rằng mọi commit đã thay đổi đều xuất hiện trong một mảng không giới hạn. Sử dụng các định danh dự án và ref thay vì phân tích một chuỗi hiển thị.

Sự kiện yêu cầu hợp nhất

Các hành động như mở, cập nhật, phê duyệt, hợp nhất và đóng có thể chia sẻ cùng một loại sự kiện rộng. Chuyển hướng theo các thuộc tính đối tượng đã được tài liệu hóa và làm cho các cập nhật lặp lại là trạng thái không đổi. Không bao giờ hợp nhất code hoặc phê duyệt triển khai chỉ vì tiêu đề hoặc tên người dùng có thể thay đổi trùng khớp.

Sự kiện pipeline và job

Những sự kiện này có thể xảy ra thường xuyên. Lọc tại GitLab và một lần nữa trong trình xử lý của bạn theo dự án, nhánh, trạng thái và môi trường. Xếp hàng công việc artifact hoặc triển khai chậm và xác nhận webhook trước.

Thiết kế cho các lần thử lại và kích hoạt đệ quy

GitLab bao gồm webhook-id, điều này vẫn nhất quán qua các lần thử lại và bằng với Idempotency-Key cũ. Sử dụng nó như khóa tính bất biến khi gửi. X-Gitlab-Webhook-UUID xác định một lần thực thi webhook, trong khi X-Gitlab-Event-UUID có thể giúp theo dõi sự kiện; webhook đệ quy có thể chia sẻ UUID sự kiện.

Nếu bộ xử lý thay đổi GitLab thông qua API, nó có thể tạo một webhook khác. Thêm biện pháp ngăn chặn vòng lặp rõ ràng: gắn nhãn hành động với danh tính tích hợp của bạn, bỏ qua các thay đổi không làm thay đổi trạng thái mong muốn, và giới hạn các chuyển đổi trong quy trình làm việc. Hướng dẫn thử lại và tính bất biến bao gồm các mẫu hộp thư giao dịch.

Khắc phục sự cố kiểm tra webhook GitLab thất bại

GitLab không thể kết nối với URL

Xác nhận rằng tiến trình tunnel đang hoạt động, đường dẫn đầy đủ là chính xác, và máy chủ cục bộ của bạn đang lắng nghe trên cổng được chuyển tiếp. Đối với GitLab tự quản lý, kiểm tra chính sách mạng ra ngoài và DNS. Không được tắt xác thực SSL để che giấu lỗi định tuyến không liên quan.

Chữ ký không bao giờ khớp

Sử dụng token ký, không dùng token bí mật cũ. Loại bỏ whsec_, giải mã Base64 token còn lại và ký {webhook-id}.{webhook-timestamp}.{raw body}. Mã hóa Base64 giá trị băm HMAC nhị phân và thêm tiền tố v1,. So sánh với mọi chữ ký cách nhau bởi khoảng trắng.

Dấu thời gian bị từ chối

Kiểm tra thời gian hệ thống và cách xử lý múi giờ; header là dấu thời gian Unix tính bằng giây. Không so sánh với mili giây của JavaScript mà không chia cho 1000. Nếu đang gỡ lỗi một yêu cầu cũ đã bị chụp, việc từ chối dấu thời gian là cơ chế bảo vệ replay chính xác.

GitLab vô hiệu hóa hoặc giảm tần suất webhook

Kiểm tra trạng thái giao hàng gần đây và phản hồi của tuyến của bạn. Trả về 2xx nhanh chóng sau khi chấp nhận bền vững. Lặp lại 401 có nghĩa là cấu hình token sai; lặp lại 5xx có nghĩa là lỗi xử lý; hết thời gian chờ chỉ ra quá nhiều công việc đồng bộ.

Chỉ một số sự kiện được nhận

Xem xét các trigger đã chọn và bộ lọc nhánh. Webhook nhóm và dự án có phạm vi khác nhau. Xác nhận sự kiện đã xảy ra trong chính dự án mà webhook này được cấu hình.

Giữ dữ liệu webhook GitLab cục bộ an toàn

  • Chỉ lưu trữ token ký trong các file môi trường bị bỏ qua và thay thế bất kỳ token bị rò rỉ nào.
  • Xác thực chữ ký, dấu thời gian, ID dự án và loại sự kiện được phép trước khi thực hiện các tác động phụ.
  • Ẩn các thông điệp commit, URL kho riêng tư, email người dùng và biến CI khỏi các bản ghi.
  • Chỉ cấp cho token API tích hợp những phạm vi cần thiết cho hành động hạ nguồn của nó.
  • Xóa lịch sử payload cục bộ khi việc kiểm tra kết thúc.

Đối với chẩn đoán độc lập với nhà cung cấp, sử dụng hướng dẫn gỡ lỗi webhook cục bộ. GitHub sử dụng định dạng chữ ký khác, vì vậy hãy tham khảo hướng dẫn webhook GitHub riêng thay vì tái sử dụng trình xác thực của nó.

Câu hỏi thường gặp

Làm thế nào để tôi kiểm tra một webhook của GitLab trên localhost?
Mở đường dẫn cục bộ của bạn bằng một kênh HTTPS, thêm URL công khai của nó vào mục Webhooks trong dự án GitLab, cấu hình token ký và các triggers, sau đó tạo một sự kiện thử nghiệm hoặc thực tế.
Các webhook mới của GitLab có nên sử dụng X-Gitlab-Token không?
GitLab khuyến nghị sử dụng token ký cho các webhook mới. X-Gitlab-Token chứa một bí mật dạng văn bản thuần, trong khi token ký xác thực một digest HMAC-SHA256 của yêu cầu.
Webhook-signature của GitLab được tính như thế nào?
Giải mã token ký sau khi loại bỏ whsec_, sử dụng HMAC-SHA256 cho chuỗi webhook-id.webhook-timestamp.raw-body, mã hóa Base64 cho kết quả băm, và thêm tiền tố v1,.
Làm thế nào để ngăn các hành động webhook GitLab bị trùng lặp?
Lưu webhook-id dưới ràng buộc duy nhất và thực hiện các tác động phụ một cách giao dịch. GitLab giữ ID đó ổn định trong các lần thử lại, làm cho nó phù hợp để đảm bảo tính idempotent.