Attention, cet article a plus d'une année d'ancienneté. Il est possible que les informations présentées ne soient plus à jour, spécialement dans le cadre d'un article technique.
Bonjour à tous,
À la suite de l’article Compiler NGINX, pourquoi ? Comment ?, j’avais envie de vous faire un petit article sur ma recherche personnelle (pour l’instant aucun commit sur github) de la configuration parfaite pour NGINX qui permets de trouver le meilleur équilibre entre les performances et la sécurité (sans négliger l’un ou l’autre).
Je reprécise pour ceux qui lissent en diagonale, ce n’est (peut-être) pas la configuration parfaite, mais j’aimerais un coup de main pour que ça le devienne 🙂
Les besoins :
Avant tout, il faut comprendre mes besoins :
Le fichier de configuration nginx.conf doit fonctionner pour la dernière mainline de NGINX et OpenSSL 1.1.0 (ou la dernière version stable de OpenSSL).
Il est nécessaire d’avoir au minimum les résultats suivants sur différents tests en ligne :
- A sur https://securityheaders.io/
- A+ sur https://tls.imirhil.fr/
- A+ sur https://www.ssllabs.com/
Aussi, la configuration ne doit pas être destructrice ou trop restrictive.
Cela explique deux choses :
- Premièrement, TLS 1.0 et 1.1 sont actifs.
- Deuxièmement, Content-Security-Policy n’est pas dans son réglage le plus strict.
Détail de la configuration actuelle :
La dernière configuration en date est toujours disponible sur mon repo GitHub.
Pour information, je me base sur mon serveur Web qui dispose d’un Intel Xeon D-1531 avec 32Gb de RAM pour faire cette configuration, ce qui explique certaines valeurs assez élevées.
Faites jouer les divisions (ou les multiplications) pour adapter le bordel à votre serveur.
Le début du fichier :
On commence par déclarer l’utilisateur et le pid avec lesquels NGINX va se lancer.
En plus de cela on indique le nombre de processus de Nginx que l’on souhaite lancer en parallèle.
Idéalement, il en faut un ou deux par cœurs / thread.
user www-data; worker_processes 12; # Number of CPU Cores pid /run/nginx.pid;
La partie Events :
La première chose que l’on déclare ici, c’est les worker_connections.
C’est le nombre maximum de personnes que votre serveur NGINX peut servir en simultané. Pas de calcul miracle ici, ajustez le en fonction de vos tests de charges.
Multi_accept est activé ici pour pouvoir démarrer plusieurs nouvelles connexions en parallèle. Si vous n’activez pas ce paramètre, vous aurez vite un blocage.
Finalement, epoll est utilisé en tant que mécanisme d’entrée / sortie pour des performances améliorées lors des fortes charges.
events { worker_connections 16384; multi_accept on; use epoll; }
Enfin, on défini la variable worker_rlimit_nofile, voyez ce post pour plus d’informations.
Pour faire simple :
# worker_rlimit_nofile = (worker_connections * 1) + 500
# worker_rlimit_nofile = (worker_connections * 2) + 500 si on utilise NGINX en tant que reverse proxy.
worker_rlimit_nofile 16884;
Les paramètres TLS :
On passe les paramètres basiques et on attaque directement la partie TLS.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ecdh_curve X25519:sect571r1:secp521r1:secp384r1; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-CHACHA20-POLY1305-D:ECDHE-RSA-CHACHA20-POLY1305-D:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA384'; ssl_prefer_server_ciphers on;
Comme indiqué précédemment, j’autorise tous les protocoles TLS pour une plus large compatibilité.
On utilise uniquement des courbes ECDH pour les échanges, a savoir : X25519, sect571r1, secp521r1 et secp384r1.
Cela nous permet d’être compatible avec beaucoup de monde tout en gardant le meilleur niveau de sécurité possible.
Le timeout des sessions est réglé sur une journée. Le cache entre toutes les sessions est lui réglé sur 50 minutes.
On désactive les tickets de sessions ssl car ce n’est pas sécurisé. Voir ce post pour plus d’informations.
Enfin, on liste spécifiquement tous les chippers que l’on autorise et on force le client a utilisé les préférences du serveur.
Les paramètres d’header :
Ah les headers, l’une des choses les plus importantes pour la sécurité d’un serveur web qui est bien trop souvent complètement oublié au bénéfice des paramètres de caches ou des paramètres d’échange TLS.
Dans un premier temps, on va désactiver toutes les informations renvoyées par NGINX pour que votre numéro de version ne soit pas renvoyé en clair.
server_tokens off;
Ensuite, on va régler tous les headers nécessaires pour la sécurité de vos visiteurs de vos sites.
Dans un premier temps, on active HSTS avec les paramètres recommandés :
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload";
Ensuite, on autorise uniquement les Iframes provenant du même domaine :
add_header X-Frame-Options "SAMEORIGIN" always;
Ensuite, on configure CSP pour autoriser uniquement les contenus externes en HTTS :
add_header Content-Security-Policy "default-src https: data: 'unsafe-inline' 'unsafe-eval'" always;
On active la protection contre les attaques Xss et MIME :
add_header X-Xss-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always;
Pour finir, vous devez aussi activer la validation OCSP pour vos certificats en suivant ce tutoriel.
ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/ssl/private/ocsp-certs.pem resolver 8.8.4.4 8.8.8.8 valid=300s; resolver_timeout 5s;
Les paramètres des logs :
Ici on désactive simplement les access logs pour la performance. On garde uniquement les erreurs :
access_log off; error_log /var/log/nginx/error.log;
Les paramètres GZIP :
Rien de bien nouveau ni de transcendant ici, on règle simplement le niveau de compression sur 6 en indiquant les types de fichiers que l’on compresse.
gzip on; gzip_disable "msie6"; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
Les paramètres de caches et de performances :
On active d’abord sommairement les Threads AIO pour de gros gains de performances. Pour plus d’informations, lisez le post de Nginx.
aio threads;
Ensuite, on active une mitigation très basique contre les attaques par déni de service. Pour plus d’informations, regardez mon article de l’époque.
##Max c/s by ip limit_conn_zone $binary_remote_addr zone=limit_per_ip:10m; limit_conn limit_per_ip 40; ##Max rq/s by ip limit_req_zone $binary_remote_addr zone=allips:10m rate=400r/s; limit_req zone=allips burst=400 nodelay;
On définit tous les paramètres de cache pour tout ce que l’on n’a pas vu avant.
Pas de recette miracle, il faut potasser le wiki de nginx et faire du bench. Voici ce que j’utilise moi.
#PHP fastcgi_buffers 256 32k; fastcgi_buffer_size 256k; fastcgi_connect_timeout 4s; fastcgi_send_timeout 120s; fastcgi_read_timeout 120s; fastcgi_busy_buffers_size 512k; fastcgi_temp_file_write_size 512K; reset_timedout_connection on; #Others open_file_cache max=2000 inactive=20s; open_file_cache_valid 60s; open_file_cache_min_uses 5; open_file_cache_errors off; client_max_body_size 50M; client_body_buffer_size 1m; client_body_timeout 15; client_header_timeout 15; keepalive_timeout 65; send_timeout 15; sendfile on; tcp_nopush on; tcp_nodelay on;
Vous l’aurez compris, pas de miracle, mais beaucoup de doc potassée pendant plusieurs années d’utilisation de NGINX.
Si des gens veulent contribuer et apporter leurs lignes de configurations à l’édifice, il faut aller sur le github : https://github.com/stylersnico/nginx-secure-config/
Le fichier de configuration a jour est toujours disponible ici : https://github.com/stylersnico/nginx-secure-config/blob/master/nginx.conf
Vous aurez besoin d’une version custom de nginx pour utiliser ce fichier de configuration. J’en ai parlé dans le dernier article.
Bonne journée 🙂
Salut,
peut-être une question bête:
dans ta conf Nginx je vois: #ssl_dhparam /etc/ssl/certs/dhparam.pem; #We no longer use DH, only ECDH
concernant ton tuto: Installer ISPConfig 3.1 avec NGINX, MariaDB et PHP 7 sous Debian 8
c’est finalement plus la peine de générer cette clé ou elle sert aussi à autre chose ?
cd /etc/ssl/certs && openssl dhparam -out dhparam.pem 4096
merci pour ton éclairage 😀
Salut,
Si tu utilise mon nouveau fichier de configuration alors il n’est plus utile de générer cette clé.
Tant mieux ! parce que c’était long :s
Merci 😉
Merci à toi pour cet article, il m’a permis de comprendre certains paramètres de ma configuration actuelle. En effet j’avais repris ton fichier de configuration depuis un moment, utilisant ton script pour compiler NGINX lorsque j’étais sur Debian avant de passer sur Arch Linux il y a peu. Par contre, j’aurais une question, sur le site de test https://securityheaders.io j’ai toujours le header HTTP Public Key Pinning de manquant. Après recherche j’ai lu qu’il s’agissait de mettre les empreintes en SHA256 de nos certificats. Ce header a-t-il une utilité réel et pourquoi tu ne l’inclus pas dans ta configuration ?… Voir plus »
Bonjour,
L’HPKP doit faire l’objet d’une implémentation très soigneuse et particulière.
À la moindre erreur, ton site est indisponible pour les 6 prochains moins au minimum.
Ah oui à ce point ? Que fait concrètement ce header pour qu’à la moindre erreur ça rend ton site indisponible pour les six prochains mois ?! 😮
Concrètement, il dit que seulement tel et tel certificat est valide pour accéder au site.
Le problème, c’est que si tu perds les certificats ou qu’ils sont invalidés, alors plus aucun certificat ne sera valide pour la connexion a ton site et donc les visiteurs ne pourront plus y accéder.
https://fr.wikipedia.org/wiki/HTTP_Public_Key_Pinning
Donc cela signifie qu’à chaque changement de certificat, la valeur du header est à modifier. Effectivement il ne faut pas l’oublier par contre.
J’ai tenté de le mettre en place dans le fichier de configuration de mon site, seulement ça a eu comme conséquence d’annuler tous les autres header contenu dans nginx.conf xD
my 2 cents Si tu te plantes avec HPKP tu ne bloques pas l’accès à ton site pendant 6 mois, c’est en fonction du max-age que tu indiques dans le header. Chez moi c’est 1 mois. Le mieux à faire pour mettre en place HPKP c’est commencer par un header en report-only histoire de voir si ces erreurs ressortent. Ensuite tu épingles le HASH de ta clef privée, pas du certificat. Tu t’assures ensuite de ne pas perdre cette clef privée et tu épingles aussi le hash d’une ou deux clefs privée ensuite que tu as en backup. Tu peux… Voir plus »
Salut,
pour activer Naxsi ne faut-il pas ajouter ceci ?
/etc/nginx/nginx.conf:
# nginx-naxsi config
# Uncomment it if you installed nginx-naxsi
include /etc/nginx/naxsi_core.rules;
/etc/nginx/sites-available/default:
# Uncomment to enable naxsi on this location
include /etc/nginx/naxsi.rules;
Salut,
C’est un peu moins facile que cela, il faut que Naxsi soit compilé dans NGINX.
Il me semble que c’est le cas avec ton script Build Nginx, Naxsi & OpenSSL dans l’article: Compiler NGINX, pourquoi ? Comment ?
Je parle uniquement de l’activation (reste surement la configuration de Naxsi)
C’est une option disponible oui mais, on ne l’aborde pas dans cette configuration.
Juste une precision sur le access log off, c’est si et seulement si un autre moyen de log est en amont ( reverse proxy, firewall) et qu’il log les connexions. En France avec la LCEN on doit avoir des traces de toutes les connexions pendant 1 an sinon sanction si lors d’une requête judiciaire il n’y a rien.
Il faut adapter à la législation du pays.
Cette configuration n’est pas dédiée à la France et même en France il y’a peu de chances de recevoir de telles requêtes.
Merci pour les explication, j’ai appris des choses.
Petite astuce pour les workers : Nginx gère très bien la gestion automatique du nombre de worker, ainsi que l’affinité CPU de chaque worker pour maximiser les performances. Ca se règle simplement avec ces deux paramètres en début de conf :
worker_processes auto;
worker_cpu_affinity auto;
Hello,
C’est intéressant ça, je vais tester pour voir ce que ça donne 🙂
Merci
Je mets toujours worker_processes auto;
et ça a l’air de toujours bien fonctionner, justement je voulais poser la question mais depuis j’ai oublié…
Nicolas si tu peux confirmer ça nous permettra d’adapter tes tutos, merci 😉
Je n’ai pas encore testé l’auto tuning, je ne peux donc pas donner mon avis dessus.
Super article, ça va me sauver beaucoup de temps.