Passerelle APISIX et WAF
Version anglaise : en/waf-gateway.md.
Fichiers : deploy/apisix/config.yaml (réglages du nœud) et deploy/apisix/apisix.yaml (routes déclaratives, mode standalone).
TLS
ssl_protocols: TLSv1.2 TLSv1.3, suites ECDHE avec AES-GCM ou ChaCha20-Poly1305 uniquement, tickets de session désactivés.- Règle globale
redirect: {http_to_https: true}: tout accès HTTP reçoit une redirection 301 vers HTTPS. Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadsur toutes les réponses (plugin_configedge-hardening), en plus deEAI_HSTS_ENABLED=truecôté backend.- Vers le control plane : HTTP à l'intérieur du cluster (
scheme: http, le control plane ne sert que du HTTP), chiffré de nœud à nœud par WireGuard et authentifié par l'authentification mutuelle Cilium (entrée depuis le namespaceapisixavecauthentication.mode: required), etupstream_hostégal àEAI_ALLOWED_HOSTS.
WAF Coraza et OWASP CRS
Le module coraza-proxy-wasm est chargé par la section wasm de config.yaml (fichier /usr/local/apisix/proxywasm/coraza-proxy-wasm.wasm, image miroir dans Harbor épinglée par digest). Le plugin coraza-filter charge @coraza.conf-recommended, @crs-setup.conf.example et @owasp_crs/*.conf avec SecRuleEngine On : mode bloquant, niveau de paranoïa 1, seuil d'anomalie entrant 5.
Pour un faux positif : ajouter une exclusion ciblée (SecRuleUpdateTargetById ou SecRuleRemoveById sur une route précise), jamais SecRuleEngine DetectionOnly en production. Passer d'abord la route en niveau de paranoïa 2 sur un environnement de recette avant de durcir.
En-têtes internes
Le client ne peut jamais fournir une identité ou une adresse :
- le plugin
real-ip(priorité la plus haute) calcule l'adresse du client à partir deX-Forwarded-Foruniquement si le pair est le répartiteur de charge (10.0.0.0/8) ; serverless-pre-functionsupprime ensuite tous les en-têtesX-Dev-*,X-SPIFFE-ID,X-Forwarded-*,X-Real-IPetForwarded, et supprime unX-Request-Idqui n'est pas un jeton court (^[A-Za-z0-9._:-]{8,128}$), ce qui empêche l'injection d'en-têtes et la falsification des journaux ;proxy-rewriteréécritX-Forwarded-For,X-Real-IP,X-Forwarded-Proto,X-Forwarded-HostetX-Forwarded-Portavec les valeurs de la passerelle ;request-idgénère un identifiant UUID lorsque le client n'en a pas fourni de valide.
Côté backend, EAI_TRUSTED_PROXIES ne contient que le CIDR des pods APISIX et du contrôleur d'ingress : un autre pair ne peut pas imposer X-Forwarded-For ni X-SPIFFE-ID.
Limites de taille et de débit
| Route | Corps maximal | Limite |
|---|---|---|
/v1/* (OIDC) | 10 Mio (EAI_MAX_BODY_BYTES) | 600 requêtes par minute par jeton, Redis TLS partagé |
/v1/* (clé API) | 10 Mio | 1200 par minute par clé, 200 connexions simultanées |
routes de téléversement (_UPLOAD de security/limits.py) | 100 Mio (EAI_MAX_UPLOAD_BYTES) | 60 par minute par jeton ou clé |
/v1/gateway/*, /v1/realtime/* | 10 Mio | 20 requêtes par seconde par IP |
/login, /api/auth/* du portail | 64 Kio | 10 par minute par IP |
| toutes les routes publiques | 50 requêtes par seconde par IP (rafale 100) |
Les compteurs partagés utilisent l'utilisateur ACL Redis apisix sur TLS (redis_ssl: true, redis_ssl_verify: true).
Métriques et documentation jamais publiques
La route deny-metrics (priorité 1000) répond 404 pour /metrics, /apisix/prometheus/metrics, /openapi.json, /docs et /redoc. L'exportateur Prometheus d'APISIX écoute sur un port privé (9091) ; Prometheus collecte les pods directement, autorisé par les NetworkPolicies depuis le namespace d'observabilité. EAI_DOCS_ENABLED=false désactive aussi la documentation côté backend en production.
CORS et en-têtes de réponse
- CORS uniquement sur la route OIDC, origine exacte
https://<portal-host>, sans identifiants, liste d'en-têtes explicite,max_age600. - En-têtes ajoutés :
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: no-referrer,Cross-Origin-Opener-Policy,Cross-Origin-Resource-Policy,Permissions-Policy,Cache-Control: no-store;ServeretX-Powered-Bysont retirés,enable_server_tokens: false.
Ingress du portail
templates/portal/ingress.yaml crée deux Ingress (/ et /login) avec TLS cert-manager, redirection HTTPS forcée, TLS 1.2 et 1.3, HSTS, taille de corps limitée et une limite dédiée sur /login (portal.ingress.loginRateLimitPerMinute, 10 par défaut). Les en-têtes de sécurité de bord sont dans la ConfigMap <release>-portal-edge-headers, appliquée par l'annotation custom-headers ; le contrôleur doit les autoriser dans global-allowed-response-headers. Les snippets restent désactivés sur le contrôleur.