// DSKR.DEV: Настройка github webhooks

В предыдущей статье я рассказал как перенёс статьи в отдельный репозиторий. Многие заметили что в этом решении есть проблема. Чтоб понять что добавилась новая статья, я раз в минуту делаю запросы и сверяю время модификации репозитория. Для таких случаев правильнее использовать webhooks.

Настроить webhook в гитхабе достаточно просто. Нужно написать немного кода для обработки и верификации webhook`а.

import { config } from 'dotenv';
import Koa from 'koa';
import Router from '@koa/router';
import parser from 'koa-bodyparser';
import { createHmac } from 'crypto';

config();

const app = new Koa();

const router = new Router();

app.use(parser());

router.post('/hooks/github', (ctx) => {
  // Подписываем body запроса нашим секретом.
  const key = createHmac('sha256', process.env.WEBHOOK_SECRET as string)
    .update(JSON.stringify(ctx.request.body))
    .digest('hex');
  
  // Сверяем получившуюся подпись, с подписью указанной в заголовке запроса.
  if (ctx.request.headers['x-hub-signature-256'] !== 'sha256=' + key) {
    return 404;
  }

  // Тут будет наш код который должен вызываться по webhook`у.

  // Отвечаем что-нибудь гитхабу. Главное чтоб код ответа был 200.
  ctx.body = { status: 'ok' };
});

app.use(router.routes());

app.listen(8000);

Секретный ключ не должен хранится в репозитории, а значит и в коде приложения. Правильным вариантом будет хранить его в .env файле. А для загрузки переменных окружения можно использовать dotenv.

Чтобы убедится, что запрос приходит от гитхаба и по пути его никто не мог модифицировать, используется криптографическая подпись HMAC. Гитхаб при отправке сообщения подписывает body секретным ключом, который мы указываем при настройке webhook`а. А в коде приложения мы подписываем пришедший в запросе body и сверяем получившуюся подпись. Если подпись совпадает, то мы можем быть уверенны, что тот кто отправил этот запрос знает наш секрет, соответственно ему можно доверять.

В моём случае проверка подписи на самом деле не очень нужна, так как я использую webhook только для того, чтобы понять что в репозитории что-то изменилось и нужно пойти и обновить посты. Единственная угроза которая может быть, это то что злоумышленник заставит моё приложение очень часто обновлять посты, и у меня кончится лимит запросов к гитхабу.

В самом интерфейсе гитхаба в настройках репозитория со статьями нужно добавить webhook.

Скриншот

Payload URL - урл по которому мы будем обрабатывать webhook, желательно чтоб это был https.

Content type - в большинстве случаев лучше выбрать json.

Secret - тот самый секрет который мы записали в .env. Это может быть любая строка, для генерации лучше воспользоваться генератором паролей, я использую Bitwarden. Строка из 40 символов, включая большие и маленькие буквы и цифры достаточно безопасна.

Единственная сложность которая может возникнуть при разработке приложения с webhook, это необходимость белого ip адреса для тестирования. Но если его нет, то можно воспользоваться сервисом ngrok. Он позволяет задать публичный url для локальной разработки и тестирования.