Cloud

APIM interne Azure : diagnostiquer une API privée avant de modifier les policies

Qualifier une panne sur un flux Application Gateway, WAF, APIM interne et backend privé en séparant DNS, routage, policy, identité et logs avant toute correction.

08 juin 2026 azureapi-managementapplication-gatewaywafprivate-endpointdnskqllogsmonitoringidentityrunbookrollback

Quand une API publiée via Azure API Management en mode interne commence à répondre en 404, 500, 502 ou 403, la tentation est forte de corriger directement la policy APIM, de désactiver une règle WAF ou de rouvrir temporairement le backend. Sur un chemin privé, cette réaction mélange trop vite plusieurs couches : résolution DNS côté client, listener Application Gateway, WAF, route vers APIM, policy entrante, résolution du backend, identité présentée au service cible et journaux applicatifs.

Le cas d’usage est un flux d’API interne : un client d’entreprise appelle Application Gateway avec WAF, la gateway transmet vers APIM interne, APIM applique les policies puis appelle une Azure Function, une API privée ou un service derrière Private Endpoint. L’objectif du runbook est de trouver la couche qui refuse ou casse la requête sans affaiblir durablement la sécurité du chemin.

Cartographier le flux réel avant de toucher aux policies

La première étape consiste à écrire le flux réellement attendu, pas le diagramme simplifié. Chaque nom DNS, chaque hostname TLS et chaque identité doivent être explicités. Une policy APIM peut être correcte alors que le backend résout encore en public depuis le réseau APIM. Une règle WAF peut sembler responsable alors que la requête n’atteint jamais APIM. Un 403 backend peut venir de l’identité APIM ou d’une clé applicative absente.

text private-api-flow.txt
Client interne
Résout api.internal.example.com
Appelle Application Gateway avec le hostname public interne attendu

Application Gateway + WAF
Sélectionne le listener HTTPS
Applique la policy WAF
Transmet vers le pool APIM avec le bon host header

APIM interne
Reçoit la requête sur son endpoint privé
Applique les policies inbound/backend/outbound/on-error
Résout le backend privé par son nom réel
Présente l'identité, le certificat, la clé ou le token attendu

Backend privé
Reçoit la requête via Private Endpoint ou chemin privé
Journalise correlationId, identité et résultat applicatif

Cette cartographie donne une règle simple : ne pas modifier une policy APIM tant que l’entrée APIM n’est pas prouvée, ne pas modifier le WAF tant que le blocage WAF n’est pas visible, ne pas ouvrir le backend tant que la résolution privée depuis APIM n’est pas qualifiée.

Séparer les symptômes par couche

Les codes HTTP seuls ne suffisent pas. Un 502 peut venir d’Application Gateway, d’APIM ou du backend. Un 403 peut être un blocage WAF, une authorization APIM, un refus Key Vault, une Function key manquante ou une identité managée sans rôle. Le runbook doit donc imposer une lecture par couche.

text layered-reading.txt
Symptôme
WAF Blocked
  Lire ruleId, matchVariable, URI et client IP avant toute exclusion

Application Gateway 502
  Vérifier backend health, probe, SNI, host header et certificat vers APIM

APIM 404 ou 500
  Vérifier API path, operationId, policy error, backend URL et correlationId

Backend 401 ou 403
  Vérifier identité, secret, certificat, Entra ID token, RBAC et firewall

Backend introuvable depuis APIM
  Vérifier DNS privé, Private Endpoint, route, NSG et resolver utilisé

Le point important est de garder la même fenêtre temporelle et le même identifiant de corrélation quand c’est possible. Sinon, l’équipe compare des événements qui ne concernent pas la même requête.

Vérifier DNS et TLS depuis le chemin APIM

APIM doit appeler le backend avec son hostname, pas avec l’adresse IP privée. L’adresse IP peut aider à un test réseau ponctuel, mais elle casse le modèle TLS, rend le SNI ambigu et masque les erreurs de DNS privé. La validation doit partir d’un point qui utilise le même resolver et le même chemin que l’instance APIM.

bash 01-apim-backend-dns-tls-check.sh
BACKEND_HOST=func-orders-prod.azurewebsites.net

nslookup "$BACKEND_HOST"

resolved_ip="$(dig +short "$BACKEND_HOST" | tail -n 1)"
case "$resolved_ip" in
10.*|172.16.*|172.17.*|172.18.*|172.19.*|172.2*|172.30.*|172.31.*|192.168.*)
  echo "private_resolution_ok=$resolved_ip"
  ;;
*)
  echo "unexpected_public_or_empty_resolution=$resolved_ip"
  exit 2
  ;;
esac

openssl s_client -connect "$BACKEND_HOST:443" -servername "$BACKEND_HOST" </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer

Si ce test échoue, la correction attendue concerne la zone DNS privée, le lien VNet, le resolver hybride, la route ou l’emplacement du runner de diagnostic. Modifier une policy APIM ou une règle WAF ne corrigera pas cette couche.

Lire APIM et Application Gateway ensemble

Pour une panne d’API privée, les logs doivent montrer si la requête est bloquée avant APIM, rejetée par APIM ou refusée par le backend. Une requête KQL de tri permet de garder une lecture exploitable pendant incident.

kusto 02-apim-private-api-triage.kql
let Window = 2h;
let Hostname = "api.internal.example.com";
let ApiPath = "/orders";
let Gateway =
AzureDiagnostics
| where TimeGenerated > ago(Window)
| where ResourceProvider == "MICROSOFT.NETWORK"
| where Category in ("ApplicationGatewayAccessLog", "ApplicationGatewayFirewallLog")
| where tostring(host_s) == Hostname or tostring(requestUri_s) has ApiPath
| project TimeGenerated,
        Layer="application-gateway",
        Action=tostring(action_s),
        Status=tostring(httpStatus_d),
        RuleId=tostring(ruleId_s),
        Uri=tostring(requestUri_s),
        ClientIp=tostring(clientIP_s),
        CorrelationId=tostring(transactionId_g);
let Apim =
AzureDiagnostics
| where TimeGenerated > ago(Window)
| where ResourceProvider == "MICROSOFT.APIMANAGEMENT"
| where tostring(Url) has ApiPath or tostring(RequestUri) has ApiPath
| project TimeGenerated,
        Layer="apim",
        Action=tostring(OperationName),
        Status=tostring(ResponseCode),
        RuleId="",
        Uri=tostring(Url),
        ClientIp=tostring(CallerIPAddress),
        CorrelationId=tostring(CorrelationId);
Gateway
| union Apim
| order by TimeGenerated desc

La requête n’est pas là pour remplacer les logs applicatifs. Elle sert à répondre vite à une question de routage opérationnel : la requête est-elle bloquée par WAF, reçue par APIM, rejetée par APIM ou absente du chemin attendu ?

Rejouer une requête contrôlée

Une fois la couche suspecte identifiée, rejoue une requête minimale avec un x-correlation-id explicite. Le test doit passer par le même hostname que les clients, pas par un endpoint de contournement.

bash 03-controlled-request.sh
CORRELATION_ID="ops-$(date +%Y%m%d%H%M%S)"

curl -vk "https://api.internal.example.com/orders/health" -H "x-correlation-id: $CORRELATION_ID" -H "Host: api.internal.example.com"

echo "correlation_id=$CORRELATION_ID"

Si la requête apparaît dans Application Gateway mais pas dans APIM, regarde le pool backend, le host header, la probe et TLS vers APIM. Si elle apparaît dans APIM mais pas dans le backend, regarde la policy backend, DNS privé, route et identité. Si elle apparaît partout avec un refus applicatif, la correction n’est probablement pas réseau.

Encadrer la correction et le rollback

La correction doit rester minimale. Une exclusion WAF doit viser la variable et la règle concernées. Une policy APIM doit être versionnée et testée avec un identifiant de corrélation. Une ouverture réseau temporaire doit avoir un propriétaire, une durée et une preuve de retrait. Un changement d’identité doit être validé avec le principal réel utilisé par APIM ou le backend.

text bounded-fix-checklist.txt
Avant changement
Couche fautive identifiée avec logs
Requête de test reproductible
Impact sécurité compris
Rollback documenté

Pendant changement
Modifier une seule couche
Garder l'identifiant de corrélation
Surveiller WAF, APIM et backend dans la même fenêtre

Après changement
Rejouer la requête contrôlée
Vérifier l'absence d'ouverture publique inattendue
Supprimer l'exception temporaire
Documenter preuve, propriétaire et date de retrait

Cette discipline évite les réparations qui fonctionnent seulement parce qu’elles ont contourné plusieurs contrôles à la fois.

Conclusion

Un incident sur APIM interne n’est pas seulement un sujet de policy. C’est un chemin complet qui combine DNS, TLS, WAF, Application Gateway, APIM, identité et backend privé. Le bon runbook commence par prouver où la requête disparaît ou change de statut.

En gardant les couches séparées, l’équipe peut corriger vite sans transformer l’urgence en exception durable : pas d’exclusion WAF globale, pas de backend rouvert par réflexe, pas de policy modifiée sans preuve. L’API privée reste opérable parce que chaque décision s’appuie sur un signal vérifiable.