Cloud

Publier une application métier avec Azure Application Gateway, mTLS et WAF dédié

Un retour opérationnel sur la publication contrôlée d’une application métier derrière Azure Application Gateway, avec listeners HTTPS dédiés, authentification mutuelle, WAF spécifique, backend HTTPS et points de validation réseau.

13 mai 2026 azureapplication-gatewaywafmtlstlsnginxnetworking

Publier une application métier vers un système externe ne devrait jamais se limiter à ajouter un nom DNS sur une IP publique. Dès qu’un flux applicatif arrive depuis une plateforme SaaS, un partenaire ou un système B2B, il faut séparer plusieurs sujets qui sont souvent mélangés dans les premières discussions : qui a le droit d’appeler l’application, comment le client est authentifié, quel chemin TLS est réellement utilisé, quelle politique WAF s’applique, comment le backend est validé, et comment l’équipe d’exploitation diagnostique un échec sans tout confondre.

Azure Application Gateway répond assez bien à ce besoin quand l’architecture attendue reste une publication HTTPS contrôlée, avec routage vers des machines virtuelles ou des backends privés existants. Il n’est pas toujours nécessaire d’ajouter API Management si le besoin ne porte pas sur le cycle de vie complet d’une API, les abonnements, les produits, le throttling applicatif avancé ou la transformation des payloads. Dans un cas plus direct, Application Gateway peut servir de point d’entrée unique, terminer le TLS côté frontend, imposer une authentification mutuelle par certificat client, appliquer une politique WAF dédiée, puis joindre le backend en HTTPS.

Le schéma cible reste volontairement simple.

text
Client SaaS ou partenaire externe
↓
IP publique Azure Application Gateway
↓
Listener HTTPS dédié par nom applicatif
↓
Authentification mTLS stricte
↓
Politique WAF dédiée à l’application
↓
Backend pool vers VM applicative
↓
Backend setting HTTPS avec hostname explicite
↓
Nginx ou reverse proxy local
↓
Application métier

Cette simplicité est justement ce qui rend le modèle exploitable. Chaque objet Application Gateway porte une intention claire. Le listener représente un nom public. La règle de routage représente le lien entre ce nom et un backend. Le backend setting décrit le comportement HTTP ou HTTPS vers le serveur applicatif. Le probe valide la santé réelle du backend. La politique WAF est isolée pour éviter de réutiliser des exceptions conçues pour une autre application. Le profil SSL matérialise le contrôle mTLS.

Le périmètre utilisé dans l’exemple

L’exemple ci dessous expose trois environnements d’une même application à travers une Application Gateway déjà existante. Les noms et adresses sont anonymisés.

text
DEV
FQDN        : app-dev.example.com
Backend VM  : vm-app-dev-01
Backend IP  : 10.10.54.137

QA
FQDN        : app-qa.example.com
Backend VM  : vm-app-qa-01
Backend IP  : 10.10.54.69

PRD
FQDN        : app.example.com
Backend VM  : vm-app-prd-01
Backend IP  : 10.10.54.8

La passerelle d’application et son frontend public existent déjà.

bash 00-existing-components.sh
export RG="rg-network-hub-prod"
export AGW="agw-internet-prod-001"

export PUBLIC_IP="203.0.113.10"

export FRONTEND_IP="appGwPublicFrontendIpIPv4"
export FRONTEND_PORT_443="port_443"

Le DNS public doit pointer les trois noms vers la même IP publique. Dans un environnement d’entreprise, cette zone peut être gérée par une équipe DNS séparée. Il faut donc traiter cette étape comme un prérequis de changement, pas comme un détail à valider à la fin.

text
app-dev.example.com -> 203.0.113.10
app-qa.example.com  -> 203.0.113.10
app.example.com     -> 203.0.113.10

La validation se fait depuis un résolveur qui voit le DNS public, pas uniquement depuis une machine interne dont le comportement peut être modifié par des forwarders ou des zones privées.

bash 01-dns-checks.sh
nslookup app-dev.example.com
nslookup app-qa.example.com
nslookup app.example.com

Clarifier les deux chaînes TLS avant de créer les objets

Ce type de design contient deux connexions TLS différentes.

La première connexion va du client externe vers Application Gateway. Le client valide le certificat serveur présenté par le listener. Dans l’exemple, un certificat wildcard couvre les noms publiés.

text
Certificat serveur côté listener : *.example.com

Noms couverts :
app-dev.example.com
app-qa.example.com
app.example.com

La deuxième connexion va d’Application Gateway vers la VM applicative. Si le backend setting est en HTTPS, la passerelle doit pouvoir établir une connexion TLS correcte vers le serveur backend. Le hostname utilisé dans le backend setting doit donc correspondre au certificat présenté par Nginx ou par le reverse proxy local. C’est un point important quand le backend pool contient des adresses IP. Il ne faut pas compter sur pickHostNameFromBackendAddress avec des IP privées. Le nom doit être explicitement défini dans le backend setting.

L’authentification mTLS ajoute un troisième sujet, mais côté frontend uniquement. Le client externe présente un certificat client. Application Gateway vérifie ce certificat contre une chaîne d’autorité de certification cliente approuvée attachée au profil SSL. En mode strict, si le certificat client est absent ou invalide, la négociation échoue avant même que la requête soit routée vers le backend.

La chaîne de confiance du certificat client doit être préparée proprement. Le certificat client lui même reste du côté client. La clé privée ne doit pas être envoyée à Azure. Ce qui est chargé dans Application Gateway, c’est la chaîne d’autorité de certification approuvée qui permet de valider le certificat client.

text
Côté client externe
- CSR générée par le client ou la plateforme SaaS
- Certificat client signé par une CA de confiance
- Clé privée conservée côté client
- Certificat client configuré dans la plateforme appelante

Côté Application Gateway
- Chaîne CA publique ou privée qui a signé le certificat client
- Profil SSL avec authentification client activée
- Profil SSL attaché uniquement aux listeners de cette application

Préparer les variables de déploiement

Le jeu de variables suivant permet de garder les commandes lisibles. Les noms sont volontairement génériques, mais ils restent suffisamment proches d’un standard d’exploitation pour être réutilisables.

bash 02-variables.sh
export RG="rg-network-hub-prod"
export AGW="agw-internet-prod-001"

export FRONTEND_IP="appGwPublicFrontendIpIPv4"
export FRONTEND_PORT_443="port_443"

export SERVER_CERT_NAME="wildcard-example-com"

export WAF_POLICY="waf-agw-app-publication"
export SSL_PROFILE="sslprof-app-mtls-001"
export TRUSTED_CLIENT_CA_NAME="ca-client-app-chain"

export DEV_HOST="app-dev.example.com"
export QA_HOST="app-qa.example.com"
export PRD_HOST="app.example.com"

export DEV_IP="10.10.54.137"
export QA_IP="10.10.54.69"
export PRD_IP="10.10.54.8"

Avant de créer les listeners, il faut identifier le nom exact du certificat serveur déjà présent sur l’Application Gateway.

bash 03-check-listener-certificates.sh
az network application-gateway ssl-cert list -g "$RG" --gateway-name "$AGW" -o table

Cette vérification évite de créer des listeners avec un mauvais objet certificat ou de réimporter inutilement un certificat déjà géré par l’équipe réseau.

Créer les backend pools

Chaque environnement reçoit son propre backend pool. Même si les trois backends servent la même application, les isoler simplifie le diagnostic et évite qu’une modification QA touche accidentellement la production.

bash 04-backend-pools.sh
az network application-gateway address-pool create -g "$RG" --gateway-name "$AGW" -n "bp-app-dev" --servers "$DEV_IP"

az network application-gateway address-pool create -g "$RG" --gateway-name "$AGW" -n "bp-app-qa" --servers "$QA_IP"

az network application-gateway address-pool create -g "$RG" --gateway-name "$AGW" -n "bp-app-prd" --servers "$PRD_IP"

Le choix d’utiliser des IP privées dans les backend pools est acceptable pour des VM, mais il impose d’être explicite sur le hostname TLS envoyé au backend. Sans cela, les erreurs de certificat deviennent vite trompeuses.

Créer les probes HTTPS

Les probes doivent tester le même comportement que celui attendu par la passerelle. Si le backend répond en HTTPS et présente un certificat pour app-qa.example.com, le probe QA doit utiliser ce hostname.

bash 05-probes.sh
az network application-gateway probe create -g "$RG" --gateway-name "$AGW" -n "probe-app-dev" --protocol Https --host "$DEV_HOST" --path "/" --interval 60 --timeout 60 --threshold 3 --match-status-codes "200-399"

az network application-gateway probe create -g "$RG" --gateway-name "$AGW" -n "probe-app-qa" --protocol Https --host "$QA_HOST" --path "/" --interval 60 --timeout 60 --threshold 3 --match-status-codes "200-399"

az network application-gateway probe create -g "$RG" --gateway-name "$AGW" -n "probe-app-prd" --protocol Https --host "$PRD_HOST" --path "/" --interval 60 --timeout 60 --threshold 3 --match-status-codes "200-399"

Le chemin / n’est qu’un exemple. En production, un endpoint de santé applicatif explicite est préférable si l’application le fournit. Il doit toutefois rester stable, rapide et représentatif. Un probe qui dépend d’un service tiers ou d’une logique métier lourde crée de faux incidents.

Créer les backend settings HTTPS

Le backend setting est l’objet où beaucoup d’erreurs se cachent. Le port et le protocole ne suffisent pas. Le hostname est déterminant pour le TLS backend.

bash 06-backend-settings.sh
az network application-gateway http-settings create -g "$RG" --gateway-name "$AGW" -n "bhs-app-dev" --protocol Https --port 443 --host-name "$DEV_HOST" --probe "probe-app-dev" --timeout 60

az network application-gateway http-settings create -g "$RG" --gateway-name "$AGW" -n "bhs-app-qa" --protocol Https --port 443 --host-name "$QA_HOST" --probe "probe-app-qa" --timeout 60

az network application-gateway http-settings create -g "$RG" --gateway-name "$AGW" -n "bhs-app-prd" --protocol Https --port 443 --host-name "$PRD_HOST" --probe "probe-app-prd" --timeout 60

Une vérification ciblée permet de détecter immédiatement un mauvais setting.

bash 07-check-backend-setting.sh
az network application-gateway http-settings show -g "$RG" --gateway-name "$AGW" -n "bhs-app-qa" --query "{hostName:hostName,pickHostNameFromBackendAddress:pickHostNameFromBackendAddress,protocol:protocol,port:port,probe:probe.id}" -o json

Le résultat attendu doit ressembler à ceci.

json
{
"hostName": "app-qa.example.com",
"pickHostNameFromBackendAddress": false,
"port": 443,
"protocol": "Https"
}

Si hostName est vide ou si la passerelle tente de déduire le nom depuis une IP privée, il faut corriger avant d’aller plus loin. La suite du déploiement ne réparera pas un backend TLS mal nommé.

Créer une politique WAF dédiée

Réutiliser une politique WAF existante parce qu’elle fonctionne déjà pour une autre application est une mauvaise économie. Une politique WAF finit souvent par accumuler des exclusions, des règles custom et des réglages liés au comportement d’une application précise. La réutiliser pour une intégration partenaire revient à transporter des décisions de sécurité qui n’ont peut être rien à voir avec le nouveau flux.

Créer une politique dédiée garde le périmètre clair.

bash 08-waf-policy.sh
az network application-gateway waf-policy create -g "$RG" -n "$WAF_POLICY" --type OWASP --version 3.2

az network application-gateway waf-policy policy-setting update -g "$RG" --policy-name "$WAF_POLICY" --mode Prevention --state Enabled --request-body-check true

Le mode Prevention est cohérent pour un flux restreint et attendu. En revanche, il doit être validé avec de vrais appels applicatifs. Certaines applications anciennes ou certains payloads métier peuvent déclencher des règles OWASP de manière inattendue. Le bon compromis consiste souvent à commencer avec la politique dédiée, observer les logs pendant les tests QA, puis n’ajouter que les exclusions justifiées et documentées.

Restreindre l’accès aux IP du client externe

Le mTLS valide l’identité cryptographique du client, mais il ne remplace pas un filtrage réseau. Si les plages de sortie du partenaire ou de la plateforme SaaS sont connues, une règle WAF custom peut bloquer tout ce qui ne vient pas de ces ranges.

bash 09-waf-ip-allowlist.sh
CLIENT_IP_RANGES=(
"198.51.100.0/24"
"198.51.101.0/24"
"203.0.113.0/25"
"203.0.113.128/25"
)

az network application-gateway waf-policy custom-rule create -g "$RG" --policy-name "$WAF_POLICY" -n "deny-non-approved-client-ranges" --priority 10 --rule-type MatchRule --action Block

az network application-gateway waf-policy custom-rule match-condition add -g "$RG" --policy-name "$WAF_POLICY" --name "deny-non-approved-client-ranges" --match-variables RemoteAddr --operator IPMatch --values "${CLIENT_IP_RANGES[@]}" --negation-condition true

La logique est volontairement inversée.

text
Si l’adresse source n’appartient pas aux plages approuvées, bloquer la requête.

Ce type de règle doit être maintenu. Les plages IP d’un fournisseur SaaS peuvent évoluer. Si ce contrôle devient critique pour la production, il faut prévoir une revue régulière des ranges et un processus de mise à jour qui ne dépend pas d’une personne unique.

Charger la chaîne CA cliente approuvée

Application Gateway ne doit pas recevoir le certificat client complet avec sa clé privée. Elle doit recevoir la chaîne d’autorité de certification nécessaire pour vérifier le certificat présenté par le client.

bash 10-client-ca-chain.sh
az network application-gateway client-cert add -g "$RG" --gateway-name "$AGW" -n "$TRUSTED_CLIENT_CA_NAME" --data "./client-mtls-ca-chain.cer"

Le fichier doit contenir la CA racine et, si nécessaire, les intermédiaires. Si la chaîne est refusée, il faut vérifier le format, la taille, l’ordre de la chaîne et le fait que la racine soit bien présente. Dans certains contextes, l’équipe sécurité préfère charger uniquement une racine dédiée ou une chaîne intermédiaire contrôlée pour réduire le risque de faire confiance trop largement.

La validation de l’objet chargé se fait simplement.

bash 11-check-client-ca.sh
az network application-gateway client-cert list -g "$RG" --gateway-name "$AGW" -o table

Créer le profil SSL mTLS

Le profil SSL relie la configuration TLS et l’authentification client. Pour un mode strict, il faut activer l’authentification client et référencer la chaîne CA approuvée.

bash 12-ssl-profile.sh
az network application-gateway ssl-profile add -g "$RG" --gateway-name "$AGW" --name "$SSL_PROFILE" --client-auth-configuration true --trusted-client-certificates "$TRUSTED_CLIENT_CA_NAME" --min-protocol-version TLSv1_2

Selon la version Azure CLI, le nom exact de certains paramètres peut varier légèrement ou être exposé différemment. C’est pour cette raison qu’il faut toujours valider l’aide locale de l’environnement qui exécutera le changement.

bash 13-check-cli-support.sh
az network application-gateway ssl-profile --help
az network application-gateway http-listener create --help
az network application-gateway http-listener update --help

Si la CLI disponible dans le poste d’administration ou la pipeline ne permet pas de définir proprement tous les paramètres mTLS, il vaut mieux créer cette partie via le portail, PowerShell ou Infrastructure as Code plutôt que d’improviser une commande partielle. L’important est le résultat final : un profil SSL dédié, avec authentification client activée, attaché uniquement aux listeners de cette application.

Créer les listeners HTTPS

Chaque environnement a son propre listener. Les trois listeners utilisent le même frontend public et le même port 443, mais se distinguent par le hostname.

bash 14-listeners.sh
az network application-gateway http-listener create -g "$RG" --gateway-name "$AGW" -n "ln-https-app-dev" --frontend-ip "$FRONTEND_IP" --frontend-port "$FRONTEND_PORT_443" --host-name "$DEV_HOST" --ssl-cert "$SERVER_CERT_NAME"

az network application-gateway http-listener create -g "$RG" --gateway-name "$AGW" -n "ln-https-app-qa" --frontend-ip "$FRONTEND_IP" --frontend-port "$FRONTEND_PORT_443" --host-name "$QA_HOST" --ssl-cert "$SERVER_CERT_NAME"

az network application-gateway http-listener create -g "$RG" --gateway-name "$AGW" -n "ln-https-app-prd" --frontend-ip "$FRONTEND_IP" --frontend-port "$FRONTEND_PORT_443" --host-name "$PRD_HOST" --ssl-cert "$SERVER_CERT_NAME"

Il est possible d’attacher le WAF policy et le profil SSL dès la création si les paramètres sont disponibles dans la version CLI utilisée, ou de les appliquer après coup.

bash 15-attach-waf-and-ssl-profile.sh
WAF_POLICY_ID=$(az network application-gateway waf-policy show -g "$RG" -n "$WAF_POLICY" --query id -o tsv)

SSL_PROFILE_ID=$(az network application-gateway ssl-profile show -g "$RG" --gateway-name "$AGW" --name "$SSL_PROFILE" --query id -o tsv)

az network application-gateway http-listener update -g "$RG" --gateway-name "$AGW" -n "ln-https-app-dev" --waf-policy "$WAF_POLICY_ID" --ssl-profile-id "$SSL_PROFILE_ID"

az network application-gateway http-listener update -g "$RG" --gateway-name "$AGW" -n "ln-https-app-qa" --waf-policy "$WAF_POLICY_ID" --ssl-profile-id "$SSL_PROFILE_ID"

az network application-gateway http-listener update -g "$RG" --gateway-name "$AGW" -n "ln-https-app-prd" --waf-policy "$WAF_POLICY_ID" --ssl-profile-id "$SSL_PROFILE_ID"

Il ne faut pas attacher cette politique WAF globalement à l’Application Gateway si elle ne concerne que cette application. Le contrôle doit rester au niveau des listeners concernés. C’est ce qui évite d’impacter d’autres publications existantes sur la même passerelle.

Créer les règles de routage

Les règles de routage lient listener, backend pool et backend setting. Les priorités doivent être choisies dans une plage non utilisée par les règles existantes.

bash 16-routing-rules.sh
az network application-gateway rule create -g "$RG" --gateway-name "$AGW" -n "rule-app-dev" --http-listener "ln-https-app-dev" --address-pool "bp-app-dev" --http-settings "bhs-app-dev" --priority 400

az network application-gateway rule create -g "$RG" --gateway-name "$AGW" -n "rule-app-qa" --http-listener "ln-https-app-qa" --address-pool "bp-app-qa" --http-settings "bhs-app-qa" --priority 410

az network application-gateway rule create -g "$RG" --gateway-name "$AGW" -n "rule-app-prd" --http-listener "ln-https-app-prd" --address-pool "bp-app-prd" --http-settings "bhs-app-prd" --priority 420

Avant de créer ces règles dans un environnement chargé, il est utile de lister les priorités existantes.

bash 17-check-rule-priorities.sh
az network application-gateway rule list -g "$RG" --gateway-name "$AGW" --query "[].{name:name,priority:priority,listener:httpListener.id}" -o table

Ne pas oublier le chemin réseau retour

Un backend healthy ne dépend pas seulement d’Application Gateway. Les flux doivent être autorisés jusqu’aux VM, et le retour doit suivre le chemin attendu. Dans les architectures avec firewall central, appliance réseau ou routage forcé, l’asymétrie peut provoquer des symptômes qui ressemblent à un problème TLS ou applicatif.

Le flux minimal à autoriser ressemble à ceci.

text
Source      : subnet Application Gateway
Destination : 10.10.54.137
Service     : TCP 443
Action      : Allow

Source      : subnet Application Gateway
Destination : 10.10.54.69
Service     : TCP 443
Action      : Allow

Source      : subnet Application Gateway
Destination : 10.10.54.8
Service     : TCP 443
Action      : Allow

Depuis chaque VM backend, vérifier la route vers les IP privées des instances Application Gateway ou vers le subnet concerné permet d’éviter beaucoup de faux diagnostics.

bash 18-routing-checks-on-backend.sh
ip route get 10.20.141.36
ip route get 10.20.141.37

Si un doute persiste, la capture réseau reste le moyen le plus direct de distinguer un problème de chemin IP d’un problème TLS ou applicatif.

bash 19-tcpdump-on-backend.sh
sudo tcpdump -ni eth0 "host 10.20.141.36 or host 10.20.141.37"

Le handshake TCP attendu est simple.

text
Application Gateway -> VM : SYN
VM -> Application Gateway : SYN,ACK
Application Gateway -> VM : ACK

Si le SYN arrive mais que le SYN ACK ne revient jamais au bon endroit, ce n’est pas un problème de certificat. C’est un problème de routage, de firewall ou de retour asymétrique.

Configurer le backend Nginx avec une chaîne complète

Le backend doit présenter un certificat cohérent avec le hostname envoyé par Application Gateway. Dans cet exemple QA, le backend setting utilise app-qa.example.com. Nginx doit donc servir un certificat dont le CN ou les SAN couvrent ce nom.

nginx /etc/nginx/sites-available/app-qa.conf
server {
  listen 443 ssl;
  server_name app-qa.example.com;

  ssl_certificate /etc/nginx/ssl/wildcard.example.com.fullchain.pem;
  ssl_certificate_key /etc/nginx/ssl/wildcard.example.com.key;

  ssl_protocols TLSv1.2 TLSv1.3;

  root /var/www/html;
  index index.html index.php;

  location / {
      try_files $uri $uri/ /index.html =404;
  }

  location /api/external/status/ {
      fastcgi_param APP_ENV qa;
      fastcgi_param INSTANCE_TYPE api;
      fastcgi_param ENV_NAME qa;
      alias /opt/app/backend/www;
      try_files $uri $uri/ /index.php$is_args$args;
      fastcgi_split_path_info ^(.+.php)(/.+)$;
      fastcgi_pass unix:/run/php/php8.2-fpm.sock;
      fastcgi_index index.php;
      include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $request_filename;
  }
}

Après modification, tester puis recharger Nginx.

bash 20-nginx-reload.sh
sudo nginx -t
sudo systemctl reload nginx

La chaîne servie par le backend doit être complète. Un certificat serveur sans intermédiaire peut fonctionner depuis un navigateur ou une machine qui a déjà certaines chaînes en cache, mais échouer côté Application Gateway.

bash 21-check-backend-certificate-chain.sh
openssl s_client -connect 10.10.54.69:443 -servername app-qa.example.com -showcerts </dev/null

Le résultat attendu doit montrer le certificat serveur et les intermédiaires nécessaires.

text
0 CN=*.example.com
1 CN=Intermediate TLS CA
2 CN=Root CA

Si le backend présente uniquement le certificat serveur, il faut corriger le fichier fullchain.pem avant de chercher ailleurs.

Valider la santé backend dans Application Gateway

La validation principale après création des objets consiste à regarder la santé backend.

bash 22-backend-health.sh
az network application-gateway show-backend-health -g "$RG" -n "$AGW" -o table

az network application-gateway show-backend-health -g "$RG" -n "$AGW" -o jsonc

Le résultat attendu est clair.

text
bp-app-dev / 10.10.54.137 : Healthy
bp-app-qa  / 10.10.54.69  : Healthy
bp-app-prd / 10.10.54.8   : Healthy

Si le backend est unhealthy, il faut lire le message précis. Les causes les plus fréquentes sont un hostname de probe incorrect, une chaîne TLS incomplète, une route manquante, un firewall intermédiaire, un timeout applicatif ou un endpoint de santé qui ne répond pas comme attendu.

Valider les logs d’accès et WAF

Les logs Application Gateway doivent être consultables dès les tests QA. C’est aussi pour cette raison que les listeners, règles et policies doivent avoir des noms explicites.

sql access-logs.kql
AGWAccessLogs
| where TimeGenerated > ago(2h)
| where Host has "app"
| project TimeGenerated, Host, RequestUri, ClientIp, HttpStatus, RuleName, TransactionId
| order by TimeGenerated desc

Les logs WAF permettent de distinguer une requête bloquée par l’allowlist IP, une règle OWASP ou une autre condition.

sql waf-logs.kql
AGWFirewallLogs
| where TimeGenerated > ago(2h)
| where Hostname has "app"
| project TimeGenerated, Hostname, RequestUri, ClientIp, Action, RuleId, Message, Details, TransactionId
| order by TimeGenerated desc

Dans certains environnements, les logs sont encore exposés via AzureDiagnostics.

sql azurediagnostics-logs.kql
AzureDiagnostics
| where TimeGenerated > ago(2h)
| where Category in ("ApplicationGatewayAccessLog", "ApplicationGatewayFirewallLog")
| where host_s has "app" or hostname_s has "app"
| order by TimeGenerated desc

Un bon test applicatif doit produire un triplet lisible : une entrée access log, éventuellement une entrée WAF si une règle est déclenchée, et une trace applicative côté backend. Sans corrélation par heure, hostname, URI et transaction ID, le dépannage devient trop lent.

Tester le flux attendu depuis le client externe

Le scénario de test attendu ressemble à ceci.

text
URL appelée : https://app-qa.example.com/api/external/status/506

Résultat attendu :
- l’adresse source appartient aux plages autorisées
- le client présente son certificat mTLS
- Application Gateway valide la chaîne CA cliente
- la politique WAF ne bloque pas le payload attendu
- la règle QA route vers le backend pool QA
- Application Gateway ouvre une connexion HTTPS vers 10.10.54.69
- Nginx présente une chaîne certificat complète
- l’application retourne une réponse valide

Un test manuel peut être fait avec curl si le certificat client et la clé privée sont disponibles dans un environnement de test. En production, la clé privée appartient souvent à la plateforme externe et ne doit pas circuler.

bash 23-mtls-curl-test.sh
curl -v --cert ./client-test.crt --key ./client-test.key https://app-qa.example.com/api/external/status/506

Ce test ne remplace pas un appel réel depuis la plateforme externe, mais il permet d’isoler rapidement la partie TLS et mTLS.

Les erreurs les plus fréquentes

Un 403 Forbidden indique souvent une action WAF. Dans ce design, la première hypothèse à vérifier est l’allowlist IP. Si l’adresse source vue par Application Gateway ne correspond pas aux ranges autorisées, la règle custom bloque la requête. Il faut regarder les logs WAF avant de chercher côté application.

Un message du type No required SSL certificate was sent indique que le listener mTLS attend un certificat client, mais que le client ne l’a pas présenté. Le problème peut venir de la configuration de la plateforme externe, du mauvais certificat configuré côté client, ou d’un test manuel lancé sans certificat.

Un 502 Bad Gateway est plus large. Il peut venir d’un backend unhealthy, d’une absence de route, d’un firewall, d’un certificat backend invalide, d’un hostname incorrect dans le backend setting, d’un probe mal défini ou d’un timeout applicatif. Il faut commencer par show-backend-health, pas par le code applicatif.

Une erreur de chaîne intermédiaire manquante côté backend signifie généralement que Nginx sert seulement le certificat serveur. Il faut utiliser un fichier fullchain qui contient le certificat serveur et les intermédiaires nécessaires.

Une erreur de CN ou SAN mismatch apparaît quand le hostname utilisé par Application Gateway ne correspond pas au certificat présenté par le backend. Avec des backend pools en IP privée, ce problème est courant si le backend setting n’a pas de host-name explicite.

Les objets finaux à retrouver

À la fin du changement, l’inventaire Application Gateway doit être lisible. Pour l’exemple, les objets attendus sont les suivants.

text
WAF policy
waf-agw-app-publication

SSL profile
sslprof-app-mtls-001

Trusted client CA
ca-client-app-chain

Listeners
ln-https-app-dev
ln-https-app-qa
ln-https-app-prd

Backend pools
bp-app-dev
bp-app-qa
bp-app-prd

Backend settings
bhs-app-dev
bhs-app-qa
bhs-app-prd

Probes
probe-app-dev
probe-app-qa
probe-app-prd

Rules
rule-app-dev
rule-app-qa
rule-app-prd

Cette liste n’est pas cosmétique. Elle sert à vérifier que le changement est resté borné. Si une politique globale a été modifiée, si un listener existant a été réutilisé sans raison ou si une règle commune a été altérée, le changement a dépassé son périmètre.

Ce qu’il faut décider avant de généraliser ce modèle

Ce pattern est propre quand l’application exposée a peu de consommateurs, des plages IP connues, un contrat HTTPS stable et un besoin clair d’authentification client. Il devient moins adapté si l’organisation veut gérer des dizaines de consommateurs, publier des versions d’API, imposer des quotas par client, transformer les requêtes ou produire un portail développeur. Dans ce cas, API Management redevient un composant naturel.

Il faut aussi décider qui possède chaque élément dans la durée. Le DNS public, le certificat serveur wildcard, la chaîne CA cliente, les ranges IP du partenaire, la politique WAF, les règles firewall internes et la configuration Nginx ne sont pas toujours administrés par la même équipe. Si cette responsabilité n’est pas claire, l’incident arrivera au renouvellement de certificat, au changement de ranges SaaS ou à la première exception WAF demandée en urgence.

Le vrai gain de cette approche n’est donc pas seulement technique. Le gain vient du découpage. Un listener par nom, un backend pool par environnement, un probe par backend, une politique WAF dédiée, un profil SSL limité au périmètre applicatif, et des validations observables à chaque couche. C’est ce découpage qui rend la publication exploitable au lieu d’être simplement fonctionnelle.