Il existe bien des manières de configurer son serveur web sans qu’une soit
nécessairement meilleure que les autres. Mais parce que l’on me demande
régulièrement des conseils sur ce sujet et que je veux bien aider mais pas me
répéter, j’explique ici ma manière de voir les choses. Copiez, modifiez,
inspirez-vous, critiquez, c’est open-bar.
À la racine, on retrouve tous les fichiers par défaut fournis avec
l’installation du paquet. Je vous laisse lire la documentation de votre
distribution pour savoir comment les utiliser. Il n’y a véritablement que le
fichier principal, nginx.conf, que j’ai modifié comme suit :
Pour user, laissez donc la valeur indiquée de base qui sera en accord avec
d’éventuels changements dans la conception du paquet de votre distribution.
Ce qu’il est intéressant de voir ici, c’est que j’ai mis l’intégralité des
blocs server dans des fichiers séparés par domaines et localisés dans le
répertoire websites. Bien entendu, default.conf correspond à ce qui sera
servi par défaut si une personne demande un domaine non existant sur l’IPv4.
Pour l’IPv6, on utilisera une adresse différente par serveur afin de ne pas
avoir ce genre de problèmes.
Ici, le premier bloc server sert à rediriger les requêtes HTTP vers HTTPS, à
l’exception des requêtes dont l’URL commence par /.well-known. Ces dernières
sont en effet utilisées pour des taches spéciales, en particulier pour répondre
aux défis HTTP-01 du protocole ACME. Les points relatifs au service lui-même se
trouvent dans le second bloc server qui, lui, est réservé à HTTPS, d’où
l’écoute sur le port 443.
À noter que, bien qu’il ne soit théoriquement pas nécessaire d’utiliser TLS sur
HTTP/2, c’est en réalité imposé par la plupart des implémentations, d’où le
fait qu’on ne le retrouve pas sur le bloc réservé à HTTP.
Notez la présence d’un listen en double sur l’adresse IPv6. Le premier
contient quic reuseport, ce qui permet d’accepter HTTP/3. Si cela fonctionne
également en IPv4, il n’est en revanche possible de le spécifier qu’une seule
fois par adresse IP. Dans la mesure où la plupart du temps l’adresse IPv4 est
réutilisée alors que l’on utilise des adresses IPv6 uniques, j’ai ici préféré
ne le mettre que pour l’IPv6. Au besoin, pensez à adapter à votre situation.
Au nombre de 7, ils sont situés dans le répertoire custom. C’est dans ces
fichiers que je mets des bouts de configuration réutilisables dans différentes
applications, il n’y a qu’à les inclure dans le bloc server correspondant.
S’il y a une chose que je déteste voir sur mon serveur web, ce sont des robots
qui cherchent des failles de sécurité de manière totalement automatisée. J’ai
donc préparé un fichier un peu spécial qui leur sera servi. Pour cela,
commençons par générer un très gros fichier remplis de zéros et compressons le
avec gzip afin qu’il ne prenne qu’environ 300 Mo d’espace sur le disque.
Et maintenant on créé le fichier anti-bots.conf qui, si l’URL contient au
moins un des motifs définis, retourne le contenu de ce fichier. Notez la
présence de gzip_static afin de dire que le fichier est déjà compressé et
qu’il ne faut donc pas le recompresser. Ainsi, le fichier sera renvoyé tel quel
avec l’en-tête HTTP indiquant que le contenu est compressé, ce qui forcera la
bibliothèque HTTP du bot à le décompresser afin d’en lire le contenu.
Bien entendu, pensez à adapter les motifs en fonction de votre situation. Ce
serait dommage d’accidentellement vous servir votre propre bombe de compression
parce que vous avez un phpMyAdmin.
Parfois vous ne souhaitez pas que le client ouvre un fichier dans son
navigateur, vous souhaitez au contraire forcer le téléchargement du fichier en
question. Voici la configuration qui permet de faire ça, il ne reste plus qu’à
l’inclure là où c’est pertinent.
Inclue le fichier précédent et ajoute une politique de sécurité de contenu
très stricte. C’est le fichier que j’utilise par défaut : si une application
nécessite une CSP plus souple j’utilise headers-nocsp.conf afin de préciser
autre chose.
# Recommandations de l'ANSSI
# https://www.ssi.gouv.fr/administration/guide/recommandations-de-securite-relatives-a-tls/
# ANSSI R3 : Privilégier TLS 1.3 et accepter TLS 1.2
# ANSSI R4 : Ne pas utiliser SSLv2, SSLv3, TLS 1.0 et TLS 1.1
ssl_protocolsTLSv1.2TLSv1.3;# ANSSI R13 : Préférer l’ordre de suites du serveur
ssl_prefer_server_cipherson;# ANSSI R6 : Échanger les clés en assurant toujours la PFS
# ANSSI R7 : Échanger les clés avec l’algorithme ECDHE
# ANSSI R9 : Privilégier AES ou ChaCha20
# ANSSI R10 : Utiliser un mode de chiffrement intègre
# ANSSI R12 : Disposer de plusieurs suites cryptographiques
#
# openssl ciphers -v -stdname 'ECDH:!SHA1:!SHA256:!SHA384:!aRSA:!AESCCM8:!ARIA'
# man openssl-ciphers
ssl_ciphersECDH:!SHA1:!SHA256:!SHA384:!aRSA:!AESCCM8:!ARIA;# ANSSI R7 : Échanger les clés avec l’algorithme ECDHE
# openssl ecparam -list_curves
# Nota : la recommandation de l'ANSSI n'est pas à jour et ne prend pas en
# compte l'arrivée de l'algorithme post-quantique hybride basé sur X25519 et
# ML-KEM, nous devons donc compenser.
ssl_ecdh_curveX25519MLKEM768:X25519:prime256v1:secp384r1:secp521r1:brainpoolP256r1:brainpoolP384r1:brainpoolP512r1;# ANSSI R20 : Limiter la durée de vie des tickets
ssl_session_ticketson;ssl_session_timeout2m;ssl_session_cacheshared:SSL:10m;# ANSSI R23 : Ne pas transmettre de données 0-RTT
ssl_early_dataoff;# /!\ ANSSI R35 : Préférer l’agrafage OCSP
# OCSP Stapling: https://en.wikipedia.org/wiki/OCSP_stapling
#
# Bien que l'ANSSI recommande d'activer l'OCSP Stapling, Let's Encrypt ne
# supporte plus cette fonctionnalité.
# https://letsencrypt.org/2024/12/05/ending-ocsp/
ssl_staplingoff;ssl_stapling_verifyoff;ssl_trusted_certificate/etc/ssl/certs/ca-certificates.crt;includecustom/tls-headers.conf;
Il ne reste plus qu’à indiquer dans chaque bloc server où se trouvent la clé
privée ainsi que le certificat. Pour ce dernier, on n’oubliera pas d’inclure
les certificats intermédiaires.
Avec tout ça, si vos logiciels sont à jour, vous devriez avoir un joli A+ sur
les test de configuration les plus stricts. À cette fin, citons le Qualys SSL
Server test, nmap et testssl.sh.
Ce fichier est inclut par le fichier tls.conf, cependant, dans certains cas
de configuration, il ne sera pas possible d’ajouter de header à l’endroit où
l’on configure TLS et l’import ne fonctionnera donc pas. J’ai donc créé un
fichier séparé afin de pouvoir l’inclure là où c’est pertinent.
Afin de montrer un exemple de configuration avec des CSP ajustées au plus près
d’une application, je vous propose ma configuration pour
gogs/gitea/forgejo :
Si vous avez suivi ce billet et que vous avez également bien configuré votre
DNS, alors vous devriez obtenir une excellente notation sur les différents
outils de test. À titre d’information, voici les résultats pour ce blog à date
de la dernière mise à jour du présent billet.