Sécuriser WebDev sur IIS avec Traefik, mTLS et rate limiting

10 min de lecture
Rédigé par Laurent Glesner - Consultant chez EloNeva
webdev traefik iis
Sécuriser WebDev sur IIS avec Traefik, mTLS et rate limiting

En bref

  • Traefik binaire sur Windows permet de placer une vraie couche edge devant IIS et WebDev sans dépendre d'ARR ni de Docker.
  • Le mTLS doit être appliqué sur un vhost dédié, tandis que le rate limiting reste géré au plus près d'Internet par Traefik.
  • Cette architecture facilite la séparation des accès, la publication de plusieurs applications et l'évolution progressive du socle de sécurité.

Dans beaucoup de projets PC Soft, WebDev est publié directement via IIS. Cette approche fonctionne, mais elle mélange souvent l’hébergement applicatif et les fonctions de sécurité de bordure : terminaison TLS, contrôle fin des flux, séparation des accès publics et sensibles, et évolution vers plusieurs vhosts ou plusieurs applications. Traefik, utilisé ici en binaire Windows avec le file provider, permet justement de clarifier cette frontière : IIS héberge l’application, Traefik devient la couche edge qui expose 80/443, applique les middlewares, et recharge automatiquement la configuration dynamique depuis un répertoire surveillé.

L’angle est simple : ne pas remplacer IIS, mais lui enlever ce qu’il fait moins bien dans ce scénario. On garde WebDev sur un backend privé, on place Traefik devant, et on obtient une politique de sécurité plus lisible : un vhost public, un vhost d’administration en mTLS, des quotas de trafic différents, et une architecture prête à accueillir d’autres applications plus tard sans tout redessiner.

Pourquoi ajouter Traefik devant IIS

L’architecture cible est la suivante :

Internet → Traefik → IIS / WebDev

Concrètement, Traefik prend les ports 80 et 443, redirige HTTP vers HTTPS, présente les certificats serveur, impose le certificat client là où c’est nécessaire, puis reverse-proxy vers IIS sur un port non exposé, par exemple 127.0.0.1:8080. Côté Traefik, le provider fichier permet de déclarer routers, middlewares et services dans des fichiers YAML dédiés, avec surveillance des changements. Côté IIS, le site n’a plus vocation à être la façade publique.

C’est exactement ce qui rend l’approche intéressante pour un intégrateur comme EloNeva :

  • une vraie politique edge centralisée et lisible
  • un rate limiting plus fin que le simple filtrage côté IIS
  • un mTLS structuré au bon niveau
  • plusieurs vhosts, plusieurs applications, plusieurs niveaux d’accès
  • une évolution plus simple vers un frontal mutualisé, sans dépendre d’IIS pour toutes les fonctions de sécurité.

La règle d’architecture à ne pas rater

Le point de conception le plus important concerne le mTLS. Le certificat client est évalué pendant la négociation TLS, donc avant le routage HTTP détaillé. En pratique, cela impose de raisonner par nom d’hôte ou par port, pas par simple chemin applicatif. On évite donc un montage du type https://app.exemple.fr/admin en mTLS alors que https://app.exemple.fr/ resterait public. La bonne approche est plutôt :

  • app.exemple.fr pour le trafic public ;
  • admin.exemple.fr pour l’accès d’administration en mTLS obligatoire.

Deuxième règle : ne faire confiance aux en-têtes X-Forwarded-* que si un vrai équipement amont existe réellement. La documentation Traefik distingue bien forwardedHeaders.trustedIPs, qui sert à déclarer les IP autorisées à fournir ces en-têtes, et forwardedHeaders.insecure, explicitement recommandé uniquement pour des tests, pas pour la production.

Mise en place de Traefik binaire sur Windows Server

Une arborescence simple suffit :

C:\Traefik\
  traefik.exe
  traefik.yml
  logs\
  certs\
  dynamic\
    webdev.yml

Le fichier statique déclare les entry points, les logs et le provider fichier. Traefik recommande l’usage de providers.file.directory, et le mode watch: true permet le rechargement automatique lors d’une modification de fichier.

# C:\Traefik\traefik.yml
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true

  websecure:
    address: ":443"

providers:
  file:
    directory: "C:/Traefik/dynamic"
    watch: true

api:
  dashboard: false

ping: {}

log:
  level: INFO
  filePath: "C:/Traefik/logs/traefik.log"

accessLog:
  filePath: "C:/Traefik/logs/access.log"
  format: json

Le fichier dynamique porte ensuite la sécurité réelle : certificats serveur, options TLS, mTLS, middlewares HTTP, routers et service backend vers IIS.

# C:\Traefik\dynamic\webdev.yml
tls:
  certificates:
    - certFile: "C:/Traefik/certs/app.exemple.fr.crt"
      keyFile: "C:/Traefik/certs/app.exemple.fr.key"
    - certFile: "C:/Traefik/certs/admin.exemple.fr.crt"
      keyFile: "C:/Traefik/certs/admin.exemple.fr.key"

  options:
    default:
      minVersion: VersionTLS12
      sniStrict: true

    mtls-required:
      minVersion: VersionTLS12
      sniStrict: true
      clientAuth:
        caFiles:
          - "C:/Traefik/certs/client-ca.crt"
        clientAuthType: RequireAndVerifyClientCert

http:
  middlewares:
    secure-headers:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        stsPreload: true
        contentTypeNosniff: true
        frameDeny: true
        referrerPolicy: "strict-origin-when-cross-origin"

    rl-public:
      rateLimit:
        average: 30
        period: 1s
        burst: 60
        sourceCriterion:
          ipStrategy:
            ipv6Subnet: 64

    rl-admin:
      rateLimit:
        average: 10
        period: 1s
        burst: 20
        sourceCriterion:
          ipStrategy:
            ipv6Subnet: 64

    pass-client-cert-info:
      passTLSClientCert:
        info:
          subject:
            commonName: true
          issuer:
            commonName: true
          sans: true
          notAfter: true

  routers:
    webdev-public:
      entryPoints:
        - websecure
      rule: "Host(`app.exemple.fr`)"
      service: webdev-iis
      middlewares:
        - secure-headers
        - rl-public
      tls: {}

    webdev-admin:
      entryPoints:
        - websecure
      rule: "Host(`admin.exemple.fr`)"
      service: webdev-iis
      middlewares:
        - secure-headers
        - rl-admin
        - pass-client-cert-info
      tls:
        options: mtls-required

  services:
    webdev-iis:
      loadBalancer:
        passHostHeader: true
        servers:
          - url: "http://127.0.0.1:8080"

Cette configuration s’appuie directement sur les mécanismes documentés par Traefik : clientAuth dans les TLS options pour le mTLS, RequireAndVerifyClientCert pour imposer un certificat client signé par une CA autorisée, et rateLimit pour définir un contrôle par jetons avec average, period, burst et sourceCriterion.

Préparer IIS et WebDev comme backend privé

La cible n’est plus d’exposer IIS sur 80/443. On retire donc les bindings publics du site WebDev et on le réattache à une écoute locale uniquement.

Exemple avec appcmd :

%windir%\System32\inetsrv\appcmd.exe set site "WebDevSite" /-bindings.[protocol='http',bindingInformation='*:80:']
%windir%\System32\inetsrv\appcmd.exe set site "WebDevSite" /-bindings.[protocol='https',bindingInformation='*:443:']
%windir%\System32\inetsrv\appcmd.exe set site "WebDevSite" /+bindings.[protocol='http',bindingInformation='127.0.0.1:8080:']

Le principe est important : Traefik est le frontal, pas IIS ARR. Si passHostHeader: true est conservé, WebDev doit accepter les noms d’hôte transmis. Dans un montage simple avec une seule application derrière Traefik, un binding local sans host header est souvent le plus robuste.

Côté pare-feu Windows, on n’ouvre que ce qui doit réellement être public :

New-NetFirewallRule -DisplayName "Traefik HTTP" `
  -Direction Inbound -Action Allow -Protocol TCP -LocalPort 80

New-NetFirewallRule -DisplayName "Traefik HTTPS" `
  -Direction Inbound -Action Allow -Protocol TCP -LocalPort 443

New-NetFirewallRule -DisplayName "Block IIS Backend 8080" `
  -Direction Inbound -Action Block -Protocol TCP -LocalPort 8080

Enfin, Traefik peut être enregistré comme service Windows sans dépendance externe :

sc.exe create Traefik binPath= "\"C:\Traefik\traefik.exe\" --configFile=C:\Traefik\traefik.yml" start= auto
sc.exe description Traefik "Traefik edge proxy for IIS and WebDev"
sc.exe start Traefik

Générer une CA cliente et un certificat mTLS

Dans beaucoup d’équipes, la génération des certificats clients se fait depuis un poste d’administration Linux. Voici un exemple Bash simple pour créer une CA cliente, un certificat d’admin et un .pfx exploitable côté Windows.

#!/usr/bin/env bash
set -euo pipefail

mkdir -p pki
cd pki

openssl genrsa -out client-ca.key 4096
openssl req -x509 -new -nodes \
  -key client-ca.key \
  -sha256 -days 1825 \
  -out client-ca.crt \
  -subj "/CN=EloNeva Client CA"

openssl genrsa -out admin-alice.key 2048
openssl req -new \
  -key admin-alice.key \
  -out admin-alice.csr \
  -subj "/CN=alice-admin/O=EloNeva"

cat > client-ext.cnf <<'EOF'
extendedKeyUsage = clientAuth
keyUsage = digitalSignature, keyEncipherment
subjectAltName = email:[email protected]
EOF

openssl x509 -req \
  -in admin-alice.csr \
  -CA client-ca.crt \
  -CAkey client-ca.key \
  -CAcreateserial \
  -out admin-alice.crt \
  -days 365 \
  -sha256 \
  -extfile client-ext.cnf

openssl pkcs12 -export \
  -out admin-alice.pfx \
  -inkey admin-alice.key \
  -in admin-alice.crt \
  -certfile client-ca.crt

Dans ce montage, Traefik n’a besoin que de la CA cliente pour vérifier les certificats présentés sur admin.exemple.fr. Le mode RequireAndVerifyClientCert garantit justement que le certificat est obligatoire et qu’il doit être signé par une autorité approuvée.

Régler le rate limiting avec une logique applicative

Traefik documente le middleware rateLimit comme une implémentation de type token bucket : average et period définissent la vitesse de recharge, burst la tolérance de pointe. Par défaut, le regroupement se fait sur l’adresse distante, et ipStrategy permet d’affiner la logique, notamment pour l’IPv6. La documentation précise aussi que le rate limiting intervient avant le proxying effectif, et que le précédent saut réseau n’est ajouté à X-Forwarded-For qu’ensuite ; au bord d’Internet, le plus sûr reste donc de bucketiser sur l’IP vue par Traefik, sauf architecture amont maîtrisée.

Pour un premier réglage réaliste sur une application WebDev :

  • public : average: 30, burst: 60
  • admin mTLS : average: 10, burst: 20

Ces valeurs ne sont pas magiques. Elles servent de point de départ. Sur une application WebDev riche en appels AJAX, on ajuste ensuite à partir des access logs JSON de Traefik.

Un autre intérêt stratégique apparaît ici : si l’architecture évolue vers plusieurs instances Traefik, la documentation prévoit aussi un mode distributed rate limiting via Redis. Sur un serveur Windows unique, le stockage mémoire par défaut suffit largement ; mais le chemin de montée en charge existe déjà.

Remonter l’identité du certificat client jusqu’à WebDev

Quand l’application a besoin de connaître l’identité du client mTLS, le middleware passTLSClientCert est très utile. Il peut injecter soit le certificat PEM complet dans X-Forwarded-Tls-Client-Cert, soit un sous-ensemble d’informations dans X-Forwarded-Tls-Client-Cert-Info. La documentation Traefik indique aussi que ces informations ne sont transmises que pour les certificats conformes à la politique mTLS définie.

Dans un contexte IIS/WebDev, je recommande de privilégier info et de ne remonter que ce qui est réellement utile :

  • subject.commonName
  • issuer.commonName
  • sans
  • notAfter

Ce choix évite deux problèmes fréquents : d’abord des en-têtes inutilement volumineux, ensuite une logique applicative trop dépendante du format brut du certificat. La documentation Traefik rappelle d’ailleurs que la valeur de X-Forwarded-Tls-Client-Cert peut dépasser la limite de taille d’en-tête de nombreux serveurs web, souvent dans une plage de 4 à 8 KB, alors que IIS permet précisément de contrôler ces limites avec Request Filtering.

Dans WebDev, il faut alors considérer ces en-têtes comme fiables uniquement si le backend n’est joignable que depuis Traefik.

Ce qu’IIS doit encore faire malgré Traefik

Mettre Traefik en frontal ne veut pas dire neutraliser IIS. Au contraire, IIS conserve un rôle de défense complémentaire.

Le premier outil à garder est Request Filtering. Microsoft documente la possibilité de limiter les URL, les query strings, la longueur du contenu, certains verbes, le double échappement, ainsi que la taille des en-têtes HTTP. Les codes de rejet sont également connus, par exemple 404.14 pour une URL trop longue, 404.15 pour une query string trop longue, 413.1 pour un contenu trop volumineux et 431 pour un en-tête trop long.

Exemple minimal :

appcmd set config "WebDevSite" /section:requestfiltering /requestlimits.maxallowedcontentlength:30000000
appcmd set config "WebDevSite" /section:requestfiltering /requestlimits.maxurl:4096
appcmd set config "WebDevSite" /section:requestfiltering /requestlimits.maxquerystring:2048
appcmd set config "WebDevSite" /section:requestfiltering /allowdoubleescaping:false

Le second outil possible est Dynamic IP Restrictions. Microsoft le présente comme une protection contre les attaques par déni de service et par force brute, avec blocage temporaire selon le nombre de connexions simultanées ou le volume de requêtes sur une fenêtre donnée. C’est utile en complément, en observation, ou comme filet de sécurité interne. En revanche, si Traefik est bien votre frontal Internet, le contrôle principal du débit doit rester côté Traefik, là où la décision est prise avant d’atteindre IIS.

Faire évoluer l’architecture sans casser l’existant

C’est ici que Traefik apporte le plus de valeur. Une fois le frontal en place, ajouter une nouvelle application ne revient plus à bricoler une nouvelle pile de sécurité dans IIS. Il suffit d’ajouter un router, un service et éventuellement une nouvelle chaîne de middlewares.

Le même serveur peut alors exposer :

  • app.exemple.fr pour le site public ;
  • admin.exemple.fr pour l’administration en mTLS ;
  • reporting.exemple.fr pour une autre application IIS ;
  • api.exemple.fr pour un service interne avec d’autres quotas.

Cette séparation par vhosts et par middlewares est exactement ce qui permet de faire évoluer progressivement une plateforme Windows/WebDev sans figer la sécurité dans un seul produit. Traefik devient le point d’entrée cohérent, IIS redevient l’hébergeur applicatif, et chaque niveau garde un rôle clair.

Pièges classiques à éviter

Les erreurs reviennent souvent sous les mêmes formes :

  • laisser IIS exposé publiquement en parallèle de Traefik ;
  • essayer de faire du mTLS par chemin au lieu de le penser par host ou port ;
  • activer forwardedHeaders.insecure en production ;
  • transmettre le PEM complet alors que quelques attributs suffisent ;
  • oublier que passHostHeader: true impose une cohérence côté bindings et logique applicative.

Conclusion

Sur Windows Server, sécuriser WebDev avec IIS ne signifie pas forcément tout confier à IIS. Ajouter Traefik en frontal, sans Docker, permet de construire une architecture beaucoup plus nette : une couche edge dédiée, un mTLS proprement segmenté, un rate limiting fin, et une publication plus souple de plusieurs vhosts ou plusieurs applications.

Pour une entreprise comme EloNeva, l’intérêt est très concret : on garde la compatibilité avec l’existant PC Soft, on renforce la sécurité sans réécrire l’application, et on prépare une architecture capable d’évoluer vers un socle mutualisé, plus lisible et plus maintenable. C’est moins une surcouche qu’un changement de responsabilité : Traefik protège et orchestre l’entrée, IIS héberge, WebDev délivre le métier.