// infrastructure — reverse proxy sécurisé

nginx Reverse Proxy

REVERSE PROXY  ·  WAF  ·  SSL/TLS  ·  GEOIP2

nginx 1.26 TLSv1.2 · TLSv1.3 CrowdSec AppSec WAF GeoIP2 · Restriction FR Rate Limiting · Headers
4
VHOSTS CONFIGURÉS
A+
SCORE SSL
6
COUCHES SÉCURITÉ
0
EXPOSITION DIRECTE
// reverse proxy pattern

$ cat architecture.json

nginx agit comme point d'entrée unique. Les backends Apache ne sont jamais exposés directement sur internet. Toutes les requêtes transitent par le reverse proxy qui applique les couches de sécurité successives avant de transférer vers le backend.

Internet
trafic entrant
──▶
WAF CrowdSec AppSec
port 7422 · auth_request
──▶
GeoIP2 Restriction
whitelist IP · restriction FR
nginx Reverse Proxy
Rate Limiting · En-têtes Sécurité · SSL/TLS · server_tokens off
Apache Backend
[domaine A]
|
Apache Backend
[domaine B]
Backends isolés — réseau interne uniquement
aucun accès direct internet
// démarche — mise en place chronologique

$ cat chronologie.txt

Implémentation en 8 phases successives. Chaque couche est validée avant d'ajouter la suivante. Approche défense en profondeur.

01
Base nginx
worker_processes
events, gzip
SSL global
server_tokens off
02
SSL/TLS
certbot
TLSv1.2+1.3
HSTS · dhparam
03
Vhosts & Proxy
server blocks
proxy_pass
proxy_set_header
http2
04
En-têtes Sécurité
snippet réutilisable
HSTS · CSP
X-Frame · X-Content
Permissions-Policy
05
Rate Limiting
limit_req_zone
zones par usage
burst nodelay
06
GeoIP2
restriction géo
whitelist admin+LAN
map $geo_blocked
07
CrowdSec AppSec
auth_request
WAF port 7422
snippet appsec.conf
08
Hardening final
Slowloris timeouts
proxy cache API
filtrage méthodes HTTP
// extraits de configuration — générique

$ cat config-snippets.conf

Patterns de configuration réutilisables. Les valeurs entre [CROCHETS] sont à adapter à votre infrastructure.

// SNIPPET 1 — WORKERS & PERFORMANCE
# nginx.conf — bloc principal worker_processes auto; worker_rlimit_nofile 65535; events { worker_connections 1024; multi_accept on; } # Gzip gzip on; gzip_vary on; gzip_comp_level 6; gzip_types text/plain text/css application/javascript application/json image/svg+xml font/woff2;
// SNIPPET 2 — EN-TÊTES SÉCURITÉ (SNIPPET RÉUTILISABLE)
# /etc/nginx/snippets/security-headers.conf add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self'; img-src 'self' data:; frame-ancestors 'none';" always;
// SNIPPET 3 — RATE LIMITING
# Dans http {} de nginx.conf # Whitelist LAN — bypass rate limiting geo $geo_whitelist { default 0; [LAN_SUBNET] 1; # réseau local [ADMIN_IP]/32 1; # IP fixe admin } map $geo_whitelist $limit_key { default $binary_remote_addr; 1 ""; # clé vide = pas de limite } limit_req_zone $limit_key zone=global:10m rate=120r/m; limit_req_zone $limit_key zone=contact:10m rate=5r/m; limit_req_zone $limit_key zone=api:10m rate=10r/m; # Dans location {} : # limit_req zone=global burst=100 nodelay;
// SNIPPET 4 — GEOIP2 RESTRICTION GÉOGRAPHIQUE
# Dans http {} — après chargement module ngx_http_geoip2_module geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb { auto_reload 1d; $geoip2_country_code country iso_code; } map $geoip2_country_code $geo_country_blocked { default 1; # tout bloqué par défaut FR 0; # France autorisée } # Résultat : whitelisté OU France = autorisé map "$geo_whitelist:$geo_country_blocked" $geo_blocked { default 1; "~^1:" 0; # whitelisté "0:0" 0; # France } # Dans location {} : # if ($geo_blocked) { return 403; }
// pattern vhost — structure type

$ cat vhost-pattern.conf

Structure type d'un vhost sécurisé. Le même pattern est appliqué à chaque domaine avec adaptation du backend et de la CSP.

// VHOST — STRUCTURE COMPLÈTE AVEC TOUTES LES COUCHES
server { server_name [YOUR_DOMAIN] www.[YOUR_DOMAIN]; # En-têtes sécurité globaux include /etc/nginx/snippets/security-headers.conf; # SSL — géré par Certbot listen 443 ssl; http2 on; ssl_certificate /etc/letsencrypt/live/[YOUR_DOMAIN]/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/[YOUR_DOMAIN]/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; # Certbot ACME challenge location /.well-known/acme-challenge/ { root /var/www/html; try_files $uri =404; } # Fichiers cachés — toujours bloquer location ~ /\. { deny all; return 404; } # Proxy principal vers backend location / { limit_req zone=global burst=100 nodelay; if ($geo_blocked) { return 403; } include /etc/nginx/snippets/security-headers.conf; include /etc/nginx/snippets/crowdsec-appsec.conf; proxy_pass http://[BACKEND_IP]; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 10s; proxy_read_timeout 20s; } } server { listen 80; server_name [YOUR_DOMAIN] www.[YOUR_DOMAIN]; return 301 https://$host$request_uri; # redirect HTTP → HTTPS }
// résultats & validation

$ cat resultats.txt

Validation à chaque étape via nginx -t + systemctl reload nginx. Tests avec curl -I et outils SSL Labs.

Test Outil Résultat attendu
Syntaxe config nginx -t configuration file ... syntax is ok
Reload sans coupure systemctl reload nginx 0 downtime, graceful
Score SSL/TLS SSL Labs / testssl.sh A+ / Grade A
Headers sécurité curl -I https://[domaine] HSTS, X-Frame, CSP présents
Rate limiting ab / wrk 429 Too Many Requests au seuil
Restriction GeoIP curl depuis VPN étranger 403 Forbidden
CrowdSec WAF test injection SQLi/XSS Bloqué avant le backend
Backends isolés curl direct [BACKEND_IP]:80 Non accessible depuis internet