"Nginx TLS"

TLS sur Nginx

Dans cet article je vais essayer de vous expliquer comment déployer du TLS moderne dans Nginx.

La base :

Pour commencer vous avez besoins d'un nom de domaine (ou NDD), en effet Let's Encrypt ne fournit pas de certificat pour une adresse IP, et un certificat non signé est totalement inutile dans le cas où vos visiteurs n'ont pas eu au préalable le fingerprint du certificat. Pourquoi ? Simplement parce que n'importe qui peut remplacer le certificat à la volée (voilà pourquoi vous ne devriez pas accepter les erreurs de certificat).

Pré-configuration :

Une fois les records A (page 12) et AAAA renseignés dans la zone de votre NDD on peux s'attaquer au serveur.

Récupérez un shell sur le serveur distant, installez y le paquet Nginx (https://www.nginx.com/resources/wiki/start/topics/tutorials/install/). Puis installez cerbot ().

Configuration

nginx.conf

Pour commencer configurons le fichier /etc/nginx/nginx.conf, on peux y renseigner la configuration suivante :

user  www-data; 
worker_processes  auto;

error_log  /var/log/nginx/error.log warn; #fichier de log par défault pour les erreurs
pid        /var/run/nginx.pid; #fichier de log par défault pour le pid de Nginx


events {
    worker_connections  1024;
}


http {
    server_tokens off; #permet de masquer la version utilisée
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local) "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"'; 

    access_log  /var/log/nginx/access.log  main; #log d'accès pour chaque url

    sendfile        on;

    keepalive_timeout  65;

    gzip  on; #active la compression
    gzip_min_length 500; #les fichiers de moins de 500 octets ne seront pas impactés
    gzip_buffers 4 8k;
    gzip_types text/plain text/css text/javascript application/javascript application/x-javascript;
    gzip_vary on;

    include /etc/nginx/sites-enabled/*; #inclut la configuration du dossier sites-enabled
}

N'oubliez pas de vérifier que l'user www-data existe.

Génération du certificat via certbot

Pour générer un certificat vous pouvez sois faire un wildcard sois prendre un certificat valide uniquement pour un NDD.

Wildcard :

Pour générer le wildcard on va utiliser la vérification par DNS, donc certbot va vous demander de rentrer plusieurs records la commande a utiliser est :

cerbot certonly --rsa-key-size 4096 --manual --preferred=challenges=dns --email "jo@example.com" -d \*.example.com -d example.com -d \*.example.org  -d example.org 

L'option certonly dit de juste générer le certificat sans configurer un vhost, --rsa-key-size 4096 spécifie la taille de la clé rsa, 4096 étant le plus élevé et sécurisé (hors courbes elliptiques mais celles ci ne sont pas possibles via certbot), --preffered-challenges=dns dit de passer par le DNS pour la vérification --email spécifie l'email (pour éviter une question interactive) -d spécifie le domaine, ici j'ai un certificat qui sera valide pour les sous domaines de example.org et example.com et pour les NDD example.org et example.com (certbot permet de générer pour plusieurs domaine, c'est assez pratique)

i vous voulez uniquement pour un seul NDD vous pouvez plus simplement faire :

certbot certonly --rsa-key-size 4096 --email "jo@example.com" -d example.com

Configuration des blocks Nginx

Maintenant que vous avez votre joli certificat vous aller pouvoir configurer vos blocks nginx, ici je vais juste faire celui par default mais il suffit d'adapter en fonction de vos besoins.\ vous pouvez placer dans /etc/nginx/sites-enabled/site.conf la configuration suivante :

server {
    listen 80 ; #Port d'ecoute HTTPS pour l'ipv4
    listen [::]:80 ; #Port d'ecoute HTTPS pour l'ipv6
    listen 443 ssl http2; #Port d’écoute HTTPS en ipv4
    listen [::]:443 ssl http2; #Port d’écoute HTTPS en ipv6
    server_name example.org example.com; # Nom de domain
    if ($scheme != "https") {
        rewrite ^ https://$server_name/$uri permanent;
    }
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; #chemin d'acces pour la chaine complete, remplacez example.com par votre NDD
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; #chemin d'acces pour le clé prive, remplacez example.com par votre NDD
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/cert.pem; #chemin pour la clé pubique du CA
    include ssl.conf; #inclut la configuration de SSL
    #https://content-security-policy.com/ pour plus de détail, dans la configuration actuelle vous ne pourrez rien charger dautre que de lHTML.
    add_header Content-Security-Policy "frame-ancestors 'none';default-src 'none';";
    #vous pouvez changer par :
    #add_header Content-Security-Policy "frame-ancestors 'none';default-src 'self';";
    #avec ça vous pourrez charger n'importe quelle type de contenu qui proviens de votre site.
    include header.conf; #inclut la configuration des headers

    include cache.conf;

    #inclut cache.conf pour la configuration du cache et de la compression
    root /var/www/monsite; #chemin daccès vers les fichiers du site 
}

mettez dans /etc/nginx/ssl.conf :

ssl_session_timeout 1d; 
ssl_session_cache  builtin:1000  shared:SSL:10m;
ssl_session_tickets on;
#ssl_dhparam /etc/nginx/dhparam.pem; potentielle faille et obsolète
ssl_ecdh_curve X25519:secp521r1:secp384r1:secp256k1;
ssl_protocols TLSv1.3 TLSv1.2; #support de tl1.2 et 1.3 uniquement
ssl_prefer_server_ciphers off; #laisse le client choisir le cipher qu'il préfère
ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:EECDH+CHACHA20:EECDH+AESGCM:EECDH+AES
ssl_stapling on; #permet de vérifier la révoquation du certificat
ssl_stapling_verify on; 
resolver 80.67.169.12 80.67.169.40 [2001:910:800::12] [2001:910:800::40] valid=300s; #Resolveur DNS, ici FDN mais préferez un resolveur hébergé sur la même LAN ou directement sur la machine avec Nginx (mais rajoute donc un service potentiellement vulnérable sur une machine exposée au web)

Ici la ligne "Strict-Transport-Security" permet de dire au naviguateur que pour la prochaine visite on doit directement utiliser HTTPS ça évite une attaque par downgrade qui peux être utiliser lors d'un MITM pour ne plus s'occuper d'HTTPS (on appelle ça HSTS).\ dans /etc/nginx/header.conf

add_header X-Frame-Options "SAMEORIGIN" always; 
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "same-origin";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Permitted-Cross-Domain-Policies master-only;
add_header Expect-CT 'max-age=60, enforce'; 
add_header Feature-policy "accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'" always;

Pour finir rajoutez dans /etc/nginx/cache.conf

gzip_vary on;
gzip_static 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 application/activity+json application/atom+xml;

Il n’y a pas la configuration de HPKP car c’est déprécié et trop punitif. (la documentation de mozilla est pas trop mal si vous voulez quand même déployer)

Il y'a d'autre paramètre que la configuration pure de nginx, vous pouvez dans un premier temps déployer un record CAA qui dit quelle est l'autorité de certification qui peux délivrer un certificat pour votre domaine, ici vu qu'on utilise du Let’s Encrypt on peux simplement rajouter :

example.org.               3600    IN      CAA     0 issuewild "letsencrypt.org"

On peux biens sur rajouter du TLSA le TLSA permet de mettre un hash de la clé publique sous forme de record dans votre zone DNS. Il y'a le site qui permet simplement de le faire, vous pouvez récuperer la clé publique simplement : openssl s_client -connect example.org:443

TLSA est un enregistrement DNS qui permet de savoir la validité d'un certificat avec une vérification suplémentaire via le DNS.

Maintenant vous êtes censé avoir un site qui sur à un résultat "A+" même chose sur .

(Re)Source :