Documentação oficial desta página · v0.20.6
Webhook do Microsoft Graph
A plataforma de gateway msgraph_webhook é um ouvinte de eventos de entrada. É por ela que o Hermes recebe notificações de mudança do Microsoft Graph — "uma reunião do Teams terminou", "chegou uma mensagem nova neste chat", "este evento de calendário foi atualizado". É diferente da plataforma teams (que é um bot de chat para as pessoas conversarem): aqui é o M365 avisando o Hermes que algo aconteceu, não uma pessoa.
Hoje o consumidor principal é o pipeline de resumo de reuniões do Teams: o Graph avisa quando uma reunião gera uma transcrição, o pipeline busca essa transcrição e o Hermes publica um resumo de volta no Teams. Outros recursos do Graph (/chats/.../messages, /users/.../events) usam o mesmo ouvinte — os consumidores do pipeline chegam em PRs próprios.
Pré-requisitos
- Credenciais de aplicativo do Microsoft Graph — Registrar um aplicativo do Microsoft Graph
- Uma URL HTTPS pública que o Microsoft Graph consiga alcançar (o Graph não chama endpoints privados). Um túnel de desenvolvimento serve para testes; em produção você precisa de um domínio real com certificado válido.
- Um segredo compartilhado forte para usar como valor de
clientState. Gere comopenssl rand -hex 32e coloque em~/.hermes/.envcomoMSGRAPH_WEBHOOK_CLIENT_STATE.
Início rápido
~/.hermes/config.yaml mínimo:
platforms:
msgraph_webhook:
enabled: true
extra:
host: 127.0.0.1
port: 8646
client_state: "troque-por-um-segredo-forte"
accepted_resources:
- "communications/onlineMeetings"
Ou por variáveis de ambiente em ~/.hermes/.env (mescladas automaticamente na inicialização):
MSGRAPH_WEBHOOK_ENABLED=true
MSGRAPH_WEBHOOK_PORT=8646
MSGRAPH_WEBHOOK_CLIENT_STATE=<gere-com-openssl-rand-hex-32>
MSGRAPH_WEBHOOK_ACCEPTED_RESOURCES=communications/onlineMeetings
Atenção: o host de bind é lido de extra.host no config.yaml (veja o exemplo acima); não existe variável de ambiente MSGRAPH_WEBHOOK_HOST para sobrescrever.
Inicie o gateway: hermes gateway run. O ouvinte expõe:
POST /msgraph/webhook— notificações de mudança vindas do GraphGET /msgraph/webhook?validationToken=...— handshake de validação da assinatura do GraphGET /health— sonda de prontidão com contadores de aceitos/duplicados
Exponha o ouvinte publicamente (proxy reverso, túnel de desenvolvimento, ingress). A URL de notificação para as assinaturas do Graph é a sua origem HTTPS pública seguida de /msgraph/webhook:
https://ops.exemplo.com/msgraph/webhook
Configuração
Todos os ajustes ficam sob platforms.msgraph_webhook.extra:
| Ajuste | Padrão | Descrição |
|---|---|---|
host | não definido (pilha dupla: todas as interfaces, IPv4+IPv6) | Endereço de bind do ouvinte HTTP. Binds fora de loopback exigem allowed_source_cidrs; loopback (127.0.0.1 / ::1) é a configuração mais fácil com túnel de desenvolvimento / proxy reverso. |
port | 8646 | Porta de bind. |
webhook_path | /msgraph/webhook | Caminho de URL para onde o Graph faz POST. |
health_path | /health | Endpoint de prontidão. |
client_state | — | Segredo compartilhado que o Graph devolve em toda notificação. Comparado com hmac.compare_digest — gere com openssl rand -hex 32. |
accepted_resources | [] (aceita tudo) | Lista de permissão de caminhos/padrões de recurso do Graph. Um * no fim funciona como casamento de prefixo. Uma / no início é tolerada. Exemplo: ["communications/onlineMeetings", "chats/*/messages"]. |
max_seen_receipts | 5000 | Tamanho do cache de deduplicação dos IDs de notificação. As entradas mais antigas são descartadas quando o limite é atingido. |
allowed_source_cidrs | [] | Obrigatório para binds fora de loopback. Deixe vazio só quando o ouvinte estiver ligado ao loopback e atrás de um túnel local / proxy reverso. |
Quase todos os ajustes também têm uma variável de ambiente equivalente (MSGRAPH_WEBHOOK_*) que é mesclada na configuração na inicialização do gateway (a exceção é host, que só existe na configuração — veja a observação acima) — consulte a referência de variáveis de ambiente.
Reforço de segurança
O clientState é a verificação de autenticação principal
Toda notificação do Graph inclui a string clientState registrada com a sua assinatura. O ouvinte rejeita qualquer notificação cujo clientState não bata, usando comparação de tempo constante. Esse é o mecanismo documentado pela Microsoft — trate o valor como um segredo compartilhado forte.
Se client_state não estiver definido, o ouvinte se recusa a iniciar.
Lista de permissão de IP de origem (implantações em produção)
Em produção, restrinja o ouvinte às faixas de IP de origem dos webhooks do Graph publicadas pela Microsoft. A Microsoft documenta as faixas de saída no serviço web de endereços IP e URLs do Office 365. Configure assim:
platforms:
msgraph_webhook:
enabled: true
extra:
host: 0.0.0.0
client_state: "..."
allowed_source_cidrs:
- "52.96.0.0/14"
- "52.104.0.0/14"
# ...acrescente as faixas de saída atuais das categorias "Common" + "Teams" do Microsoft 365
Ou como variável de ambiente:
MSGRAPH_WEBHOOK_ALLOWED_SOURCE_CIDRS="52.96.0.0/14,52.104.0.0/14"
Ligar a um host fora de loopback, como 0.0.0.0, :: ou um IP de rede local, sem allowed_source_cidrs é recusado na inicialização. Se você usa um túnel de desenvolvimento ou proxy reverso na mesma máquina, ligue o Hermes a 127.0.0.1 ou ::1 e deixe a lista de permissão vazia. Strings de CIDR inválidas geram um aviso no log e são ignoradas. Revise a lista de IPs da Microsoft a cada trimestre — ela muda.
Terminação HTTPS
O ouvinte fala HTTP puro. Faça a terminação TLS no seu proxy reverso (Caddy, Nginx, Cloudflare Tunnel, AWS ALB) e encaminhe para o ouvinte pela rede local. O Graph se recusa a entregar em endpoints que não sejam HTTPS, então não há caminho para tráfego sem criptografia chegar até você vindo do próprio Graph.
Higiene das respostas
Em caso de sucesso, o ouvinte retorna 202 Accepted com corpo vazio — os contadores internos ficam fora da resposta. Os operadores acompanham as contagens pelo /health, protegido pelas mesmas regras de IP de origem do caminho do webhook.
Tabela de códigos de status:
| Resultado | Status |
|---|---|
| Notificação(ões) aceita(s) ou deduplicada(s) | 202 |
Handshake de validação (GET com validationToken) | 200 (devolve o token) |
| Todos os itens do lote falharam no clientState | 403 |
JSON malformado / array value ausente / recurso desconhecido | 400 |
| IP de origem fora da lista de permissão | 403 |
GET simples, sem validationToken | 400 |
Solução de problemas
| Problema | O que verificar |
|---|---|
| A validação da assinatura do Graph falha | A URL pública está acessível, o caminho /msgraph/webhook bate e o GET com validationToken devolve o token literalmente como text/plain em até 10 segundos. |
| As notificações chegam via POST, mas nada é ingerido | O client_state bate com o que você registrou na assinatura. Rode openssl rand -hex 32 de novo e crie uma assinatura nova se o valor divergiu. Confira se accepted_resources inclui o caminho de recurso que o Graph está enviando. |
| Toda notificação retorna 403 | clientState divergente (forjado, ou assinatura registrada com outro valor). Recrie a assinatura com hermes teams-pipeline subscribe --client-state "$MSGRAPH_WEBHOOK_CLIENT_STATE" ... (vem com o PR do runtime do pipeline). |
O ouvinte se recusa a iniciar em 0.0.0.0 | Defina allowed_source_cidrs com as faixas de saída de webhook atuais da Microsoft, ou ligue o Hermes a 127.0.0.1 / ::1 atrás do seu túnel ou proxy reverso. |
O ouvinte inicia, mas curl http://localhost:8646/health trava | Conflito de porta. Verifique com ss -tlnp | grep 8646 e mude o port: se precisar. |
| Requisições reais do Graph vindas da Microsoft levam 403 | A lista de permissão de IP de origem está estreita demais. Amplie-a para incluir as faixas de saída atuais da Microsoft. Se você ainda está validando o caminho do túnel, ligue o Hermes ao loopback e deixe o túnel cuidar da exposição pública. |
Documentos relacionados
- Registrar um aplicativo do Microsoft Graph — pré-requisito de registro do app no Azure
- Variáveis de ambiente → Microsoft Graph — lista completa de variáveis de ambiente
- Configuração do bot do Microsoft Teams — a outra plataforma, que deixa as pessoas conversarem com o Hermes no Teams