Microsoft Graph Webhook 监听器
msgraph_webhook 网关平台是一个入站事件监听器。这是 Hermes 接收 Microsoft Graph 变更通知的方式——例如"一个 Teams 会议结束了"、"这个聊天中收到了一条新消息"、"这个日历事件被更新了"。它与 teams 平台(用户用来聊天的聊天机器人)不同——这个是 M365 告诉 Hermes 发生了什么事,而不是某个人。
目前主要消费者是 Teams 会议摘要流水线:Graph 在会议生成转录时发出通知,流水线获取转录内容,Hermes 将摘要发回 Teams。其他 Graph 资源(/chats/.../messages、/users/.../events)使用同一个监听器——流水线消费者会通过各自的 PR 陆续加入。
前提条件
- Microsoft Graph 应用程序凭据——注册 Microsoft Graph 应用程序
- 一个 Microsoft Graph 可以访问的公开 HTTPS URL(Graph 不会调用私有端点)。开发隧道可用于测试;生产环境需要带有有效证书的真实域名。
- 一个强共享密钥,用作
clientState值。使用openssl rand -hex 32生成,并将其放入~/.hermes/.env中作为MSGRAPH_WEBHOOK_CLIENT_STATE。
快速开始
最小化 ~/.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
启动网关:hermes gateway run。监听器暴露以下端点:
POST /msgraph/webhook——来自 Graph 的变更通知GET /msgraph/webhook?validationToken=...——Graph 订阅验证握手GET /health——带 accepted/duplicate 计数器的就绪探针
公开暴露监听器(反向代理、开发隧道、ingress)。你在 Graph 订阅中使用的通知 URL 是公开 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 | Graph POST 到的 URL 路径。 |
health_path | /health | 就绪端点。 |
client_state | — | 共享密钥,Graph 在每条通知中回显。使用 hmac.compare_digest 进行比较——使用 openssl rand -hex 32 生成。 |
accepted_resources | [](接受所有) | Graph 资源路径/模式白名单。尾部 * 作为前缀匹配。允许开头带 /。示例:["communications/onlineMeetings", "chats/*/messages"]。 |
max_seen_receipts | 5000 | 通知 ID 的去重缓存大小。达到上限时淘汰最旧的条目。 |
allowed_source_cidrs | [](允许所有) | 可选源 IP 白名单。见下文。 |
每个设置也有对应的环境变量(MSGRAPH_WEBHOOK_*),在网关启动时合并到配置中——参见环境变量参考。
安全加固
clientState 是主要的身份验证检查
每条 Graph 通知都包含你的订阅注册时使用的 clientState 字符串。监听器会拒绝任何 clientState 不匹配的通知,使用计时安全的比较方式。这是 Microsoft 文档化的机制——请将该值视为强共享密钥。
如果未设置 client_state,监听器接受所有格式正确的 POST 请求。不要在生产环境中不使用它运行。
源 IP 白名单(生产部署)
对于生产环境,请将监听器限制为 Microsoft 发布的 Graph webhook 源 IP 范围。Microsoft 在 Office 365 IP 地址和 URL Web 服务 中记录了出口范围。按如下方式配置:
platforms:
msgraph_webhook:
enabled: true
extra:
client_state: "..."
allowed_source_cidrs:
- "52.96.0.0/14"
- "52.104.0.0/14"
# ...添加当前 Microsoft 365"Common"+ "Teams"类别的出口范围
或者作为环境变量:
MSGRAPH_WEBHOOK_ALLOWED_SOURCE_CIDRS="52.96.0.0/14,52.104.0.0/14"
空白名单 = 接受来自任何地方的请求(默认;保留开发隧道工作流)。无效的 CIDR 字符串会记录警告并被忽略。每季度审查 Microsoft IP 列表——它会变化。
HTTPS 终止
监听器使用纯 HTTP。在你的反向代理(Caddy、Nginx、Cloudflare Tunnel、AWS ALB)处终止 TLS,并通过本地网络代理到监听器。Graph 拒绝向非 HTTPS 端点投递,因此从 Graph 本身到达你的流量没有未加密的路径。
响应规范
成功时,监听器返回 202 Accepted,响应体为空——内部计数器不会出现在网络响应中。运维人员可以通过 /health 观察计数。
状态码表:
| 结果 | 状态码 |
|---|---|
| 通知已被接受或去重 | 202 |
验证握手(带 validationToken 的 GET) | 200(原样回显 token) |
| 批处理中每个项目的 clientState 验证均失败 | 403 |
JSON 格式错误 / 缺少 value 数组 / 未知资源 | 400 |
| 源 IP 不在白名单中 | 403 |
不带 validationToken 的裸 GET | 400 |
故障排除
| 问题 | 检查方法 |
|---|---|
| Graph 订阅验证失败 | 公开 URL 可访问,/msgraph/webhook 路径匹配,带 validationToken 的 GET 在 10 秒内将原样回显 token 为 text/plain。 |
| 通知已 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:。 |
| 来自 Microsoft 的真实 Graph 请求被 403 | 源 IP 白名单过于狭窄。临时移除 allowed_source_cidrs,确认流量正常,然后扩大列表以包含当前 Microsoft 出口范围。 |
相关文档
- 注册 Microsoft Graph 应用程序——Azure 应用注册前置条件
- 环境变量 → Microsoft Graph——完整环境变量列表
- Microsoft Teams 机器人设置——允许用户在 Teams 中与 Hermes 聊天的不同平台