Слушатель Webhook Microsoft Graph

Платформа gateway msgraph_webhook — это входящий слушатель событий. Так Hermes получает уведомления об изменениях от Microsoft Graph — «собрание Teams завершилось», «новое сообщение поступило в этот чат», «это событие календаря было обновлено». Отличается от платформы teams (которая является чат-ботом, которому пользователи пишут) — здесь M365 сообщает Hermes о произошедшем, а не человек.

Сейчас основным потребителем является конвейер саммари собраний: Graph уведомляет, когда собрание создаёт транскрипт, конвейер получает его, и Hermes публикует саммари обратно в Teams. Другие ресурсы Graph (/chats/.../messages, /users/.../events) используют тот же слушатель — потребители конвейера поступают с собственными PR.

Предварительные требования

Быстрый старт

Минимальный ~/.hermes/config.yaml:

platforms:
  msgraph_webhook:
    enabled: true
    extra:
      port: 8646
      client_state: "replace-with-a-strong-secret"
      accepted_resources:
        - "communications/onlineMeetings"

Или через переменные окружения в ~/.hermes/.env (автоматически объединяются при запуске):

MSGRAPH_WEBHOOK_ENABLED=true
MSGRAPH_WEBHOOK_PORT=8646
MSGRAPH_WEBHOOK_CLIENT_STATE=<generate-with-openssl-rand-hex-32>
MSGRAPH_WEBHOOK_ACCEPTED_RESOURCES=communications/onlineMeetings

Запустите gateway: hermes gateway run. Слушатель предоставляет:

Откройте слушатель публично (обратный прокси, dev-туннель, ingress). Ваш URL уведомлений для подписок Graph — это ваш публичный HTTPS-источник, за которым следует /msgraph/webhook:

https://ops.example.com/msgraph/webhook

Настройка

Все настройки размещаются в platforms.msgraph_webhook.extra:

Параметр По умолчанию Описание
host 0.0.0.0 Адрес привязки для HTTP-слушателя.
port 8646 Порт привязки.
webhook_path /msgraph/webhook URL-путь, на который Graph отправляет POST.
health_path /health Конечная точка готовности.
client_state Общий секрет, который Graph возвращает в каждом уведомлении. Сравнивается с помощью hmac.compare_digest — сгенерируйте с помощью openssl rand -hex 32.
accepted_resources [] (accept all) Белый список путей/шаблонов ресурсов Graph. Завершающий * действует как префиксное совпадение. Начальный / допускается. Пример: ["communications/onlineMeetings", "chats/*/messages"].
max_seen_receipts 5000 Размер кэша дедупликации для ID уведомлений. Самые старые записи вытесняются при достижении лимита.
allowed_source_cidrs [] (allow all) Необязательный белый список IP-источников. См. ниже.

Каждый параметр также имеет эквивалентную переменную окружения (MSGRAPH_WEBHOOK_*), которая объединяется с конфигурацией при запуске gateway — см. справочник переменных окружения.

Усиление безопасности

clientState — основная проверка аутентификации

Каждое уведомление Graph включает строку clientState, с которой была зарегистрирована ваша подписка. Слушатель отклоняет любое уведомление, чей clientState не совпадает, используя безопасное по времени сравнение. Это задокументированный механизм Microsoft — рассматривайте значение как надёжный общий секрет.

Если client_state не задан, слушатель принимает каждый правильно сформированный POST. Не запускайте без него в продакшене.

Белый список IP-источников (продакшн-развёртывания)

Для продакшена ограничьте слушатель опубликованными Microsoft диапазонами IP-источников вебхуков Graph. Microsoft документирует диапазоны исходящих адресов в веб-сервисе IP-адресов и URL Office 365. Настройте их как:

platforms:
  msgraph_webhook:
    enabled: true
    extra:
      client_state: "..."
      allowed_source_cidrs:
        - "52.96.0.0/14"
        - "52.104.0.0/14"
        # ...add the current Microsoft 365 "Common" + "Teams" category egress ranges

Или как переменную окружения:

MSGRAPH_WEBHOOK_ALLOWED_SOURCE_CIDRS="52.96.0.0/14,52.104.0.0/14"

Пустой белый список = принимать отовсюду (по умолчанию; сохраняет рабочие процессы dev-туннеля). Недопустимые строки CIDR логируют предупреждение и игнорируются. Проверяйте список IP Microsoft ежеквартально — он меняется.

Завершение HTTPS

Слушатель использует обычный HTTP. Завершайте TLS на вашем обратном прокси (Caddy, Nginx, Cloudflare Tunnel, AWS ALB) и проксируйте к слушателю через локальную сеть. Graph отказывается доставлять на не-HTTPS конечные точки, поэтому нет пути для незашифрованного трафика достичь вас от самого Graph.

Гигиена ответов

При успехе слушатель возвращает 202 Accepted с пустым телом — внутренние счётчики не попадают в ответ. Операторы могут наблюдать счётчики через /health.

Таблица кодов состояния:

Результат Статус
Уведомление(я) приняты или дублированы 202
Подтверждение связи (GET с validationToken) 200 (echoes the token)
Все элементы в пакете не прошли проверку clientState 403
Неправильный JSON / отсутствует массив value / неизвестный ресурс 400
IP-источник не в белом списке 403
Пустой GET без validationToken 400

Диагностика проблем

Проблема Что проверить
Ошибка подтверждения подписки Graph Публичный URL доступен, путь /msgraph/webhook совпадает, GET с validationToken возвращает токен дословно как text/plain в течение 10 секунд.
Уведомления POST, но ничего не принимается client_state совпадает с тем, что вы зарегистрировали при подписке. Запустите заново openssl rand -hex 32 и создайте новую подписку, если значение изменилось. Проверьте, что accepted_resources включает путь ресурса, который отправляет Graph.
Каждое уведомление получает 403 Несоответствие clientState (подделка или подписка зарегистрирована с другим значением). Пересоздайте подписку с помощью hermes teams-pipeline subscribe --client-state "$MSGRAPH_WEBHOOK_CLIENT_STATE" ... (поставляется с PR конвейера).
Слушатель запускается, но curl http://localhost:8646/health зависает Конфликт привязки порта. Проверьте ss -tlnp \| grep 8646 и измените port:, если необходимо.
Реальные запросы Graph от Microsoft получают 403 Белый список IP-источников слишком узкий. Временно удалите allowed_source_cidrs, подтвердите прохождение трафика, затем расширьте список, включив текущие диапазоны исходящих адресов Microsoft.