Перейти к содержимому

Вебхуки ​

Вебхук сообщает вашей системе, что в репозитории что-то изменилось, и ей не нужно опрашивать сервер. Worker Arkvory отправляет HTTP POST на ваш URL для каждого события ленты изменений репозитория. С его помощью можно запустить развёртывание при публикации сборки или обновить кэш, когда у пути появилась новая версия.

Подписки задаёт администратор в файле. API и страницы консоли для них пока нет. Ленту можно читать и самому: GET /api/v1/repositories/<repository>/changes.

Как работает доставка ​

  • Одна подписка следит за одним репозиторием и отправляет на один URL.
  • События идут по порядку, по одному. Следующее ждёт, пока получатель не ответит статусом 2xx.
  • Доставка происходит минимум один раз. После сбоя или потерянного ответа то же событие может прийти снова. У каждого события постоянный id: дедуплицируйте по нему.
  • Новая подписка получает только новые события. События до её создания не отправляются.
  • Недоступный получатель задерживает только свою подписку. Событие ждёт. Arkvory повторяет попытку через 12 секунд, удваивает паузу до одного часа и отправляет события по порядку, когда получатель снова отвечает. Загрузки и скачивания вебхук не ждут.

Настройка подписки ​

Нужен доступ к файлам сервера и право перезапускать worker. См. Конфигурация.

  1. Создайте файл с секретом подписи: не короче 16 случайных печатных символов, например /root/ci.secret. Тот же секрет нужен получателю.
  2. Выполните configure с подпиской. Команда проверяет ввод, копирует секрет в config/webhooks, пишет файл подписок и настройку, перезапускает службы и возвращает прежнюю конфигурацию, если они не стали готовы. С получателем команда не связывается.
bash
sudo arkvory configure --root /opt/proanima-arkvory \
  --webhook ci --webhook-repository releases \
  --webhook-url https://ci.example.com/hooks/arkvory \
  --webhook-secret-file /root/ci.secret \
  --webhook-actions artifact.publish
  1. Найдите webhook.started в журнале worker, опубликуйте файл и посмотрите на получателя.

Команда пишет config/webhooks/webhooks.json; его можно написать и вручную, указав в ARKVORY_WEBHOOKS_FILE. Чтобы изменить подписку, повторите команду с тем же именем. --webhook-detach ci удаляет её: файлы удаляются, позиция забывается. Один вызов меняет одну подписку и не сочетается с другими параметрами configure.

В файле такие поля:

json
{
  "webhooks": [
    {
      "id": "ci",
      "repository": "releases",
      "url": "https://ci.example.com/hooks/arkvory",
      "secretFile": "/opt/proanima-arkvory/config/webhooks/ci.secret",
      "actions": ["artifact.publish"]
    }
  ]
}
ПолеЗначение
idИмя подписки: от 1 до 64 символов a-z, 0-9, _ и -. Под этим именем хранится её прогресс.
repositoryРепозиторий, лента которого отправляется.
urlПолучатель. HTTPS, без имени пользователя, query и fragment, до 2048 символов. Обычный HTTP допустим только для localhost, 127.0.0.1 и [::1].
secretFileАбсолютный путь к файлу с секретом подписи.
nextSecretFileНеобязательный второй секрет для смены секрета.
actionsНеобязательный список действий ленты для отправки, например artifact.publish, artifact.delete, asset.replace, stage.added и package.register. Без него отправляются все действия.

Неверный файл останавливает worker при запуске с worker.unavailable. Допускается до 16 подписок. В установке Compose команда также монтирует config/webhooks в контейнер worker.

Запрос ​

ЗаголовокЗначение
Content-Typeapplication/json
X-Arkvory-Deliveryid события. Одинаков при каждом повторе события.
X-Arkvory-EventДействие ленты, например artifact.publish.
X-Arkvory-TimestampВремя Unix в секундах, когда запрос подписан.
X-Arkvory-Signaturesha256=<hex>. При смене секрета значений два, через запятую.

Тело — JSON:

json
{
  "id": "releases:128",
  "repository": "releases",
  "sequence": "128",
  "action": "artifact.publish",
  "artifactId": "00000000-0000-4000-8000-000000000001",
  "detail": null
}

sequence — позиция в ленте, десятичная строка. detail — путь файла или стадия для действий, у которых они есть. В теле нет автора, содержимого файла и метаданных: по artifactId читайте текущее состояние через API.

Ответьте любым статусом 2xx в течение 10 секунд. Статус 3xx (редиректы не выполняются), 4xx, 5xx, ошибка соединения или таймаут считаются неудачей. Arkvory читает не более 4 КиБ ответа и игнорирует его.

Проверка подписи ​

Подпись — HMAC-SHA256 с вашим секретом по тексту <timestamp>.<body>, записанная как sha256= и hex-значение. Проверяйте её, прежде чем доверять запросу:

  1. Читайте тело как есть, до разбора JSON.
  2. Отклоняйте запрос, если метка времени отличается от ваших часов больше чем на 5 минут.
  3. Вычислите подпись и сравните функцией с постоянным временем. Если в заголовке два значения, принимайте любое.

Node.js:

js
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(secret, headers, rawBody) {
  const timestamp = headers['x-arkvory-timestamp'];
  if (!/^\d{1,12}$/.test(timestamp ?? '')) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = Buffer.from(
    'sha256=' + createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex'),
  );
  return String(headers['x-arkvory-signature'] ?? '')
    .split(',')
    .some((given) => {
      const actual = Buffer.from(given.trim());
      return actual.length === expected.length && timingSafeEqual(actual, expected);
    });
}

Python:

python
import hashlib, hmac, time

def verify(secret: bytes, headers, raw_body: bytes) -> bool:
    timestamp = headers.get("X-Arkvory-Timestamp", "")
    if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
        return False
    digest = hmac.new(secret, timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    expected = "sha256=" + digest
    given = headers.get("X-Arkvory-Signature", "").split(",")
    return any(hmac.compare_digest(part.strip(), expected) for part in given)

Повторные доставки ​

  • Храните id каждого обработанного события и игнорируйте повтор.
  • Отвечайте быстро: поставьте работу в очередь и верните 204. Медленный получатель задерживает все следующие события своей подписки.
  • Повтор не значит, что операция произошла дважды. Считайте событие подсказкой и читайте текущее состояние через API.

Смена секрета ​

  1. Выполните ту же команду с --webhook-secret-file для текущего секрета и --webhook-next-secret-file для нового. Теперь каждый запрос несёт две подписи.
  2. Переведите получателя на новый секрет. Получатель, принимающий любую подпись, работает всё это время.
  3. Выполните команду снова: новый файл секрета в --webhook-secret-file и без --webhook-next-secret-file.

Частные получатели и сертификаты ​

  • Arkvory отказывает получателям на loopback, частных, link-local и metadata-адресах и именам, которые на них разрешаются. Так сервер нельзя использовать для доступа к внутренним службам.
  • Чтобы отправлять получателю в вашей сети, добавьте сеть параметром --webhook-allow-private, например --webhook-allow-private 10.20.0.0/16 (ARKVORY_WEBHOOKS_ALLOW_PRIVATE). Она действует, пока не удалена последняя подписка.
  • Если сертификат получателя выдан вашим собственным УЦ, добавьте PEM-файл с этим УЦ параметром --webhook-ca-file (ARKVORY_WEBHOOKS_CA_FILE). Сертификат проверяется всегда.

Наблюдение за вебхуками ​

Worker пишет webhook.step_failed с errorCode при неудачной доставке и webhook.recovered, когда она снова работает. Метрики arkvory_webhook_failing и arkvory_webhook_last_success_timestamp_seconds и оповещение ArkvoryWebhookFailing описаны в разделе Мониторинг. webhook.stopped отмечает конец цикла подписки: при остановке или когда worker теряет владение хранилищем. webhook.prune_failed значит, что при запуске worker не смог забыть удалённые подписки; он повторит это при следующем запуске. Коды unavailable и internal есть только в журнале, не в arkvory_webhook_failing.

Подписка не двигается ​

Arkvory никогда не пропускает событие: на этом держится надёжность доставки. Поэтому получатель, который раз за разом отклоняет одно событие, например отвечает 400, потому что не может его разобрать, останавливает всю свою подписку. Следующие события этой подписки не отправляются; другие подписки работают. Worker пишет webhook.step_failed с http_4xx и повторяет попытки с паузой до часа, arkvory_webhook_failing равна 1, оповещение ArkvoryWebhookFailing срабатывает через 15 минут.

  1. Исправьте получателя, чтобы он отвечал на событие 2xx. Arkvory отправит его при следующей попытке, не позже чем примерно через час, а затем все ожидающие события по порядку. Повторный configure с тем же именем и репозиторием сохраняет позицию.
  2. Если событие никогда не будет принято, удалите подписку через --webhook-detach и добавьте её снова. Она начнёт с конца ленты: ожидающие события не отправляются. Прочитайте их из ленты изменений после последнего sequence, который обработал получатель.

Устранение неполадок ​

errorCodeПричина и что делать
blockedАдрес получателя не разрешён (loopback, частный, link-local или metadata). Используйте публичный адрес или добавьте сеть в ARKVORY_WEBHOOKS_ALLOW_PRIVATE.
timeoutНет ответа за 10 секунд. Отвечайте быстрее и ставьте работу в очередь.
networkСоединение отклонено, имя не найдено или соединение сброшено. Проверьте URL, DNS и файрвол с сервера.
tlsСертификат не доверенный, просрочен или с неверным именем. Исправьте его или задайте ARKVORY_WEBHOOKS_CA_FILE.
redirectПолучатель ответил 3xx. Редиректы не выполняются: укажите конечный URL.
http_4xxПолучатель отклонил запрос. Проверьте его проверку подписи, путь и ключ.
http_5xxПолучатель отказал. Arkvory повторяет попытки с паузой до часа.
secretФайл секрета отсутствует, нечитаем или короче 16 байт.
unavailableWorker не смог прочитать ленту или состояние подписки из базы данных. Ничего не отправлено; повтор через 5 секунд. Проверьте базу данных.
internalНепредвиденная ошибка в worker. Ничего не отправлено; повтор через 5 секунд. Сообщите о ней вместе с журналом worker.

Ничего не приходит? Проверьте, что в журнале worker есть webhook.started, что задан ARKVORY_WEBHOOKS_FILE, имя репозитория и фильтр actions. Новая подписка отправляет только события после своего первого шага.

Arkvory · ProAnimaStudio