Skip to content

Let’s Encrypt Nginx

lets_nginx_encryptJ’ai récemment migré mon blog de Apache2 vers un stack Nginx PHP-7.0.4 MariaDB, le tout sur du Docker. L’installation de base est déjà très correct cependant pourquoi ne pas optimiser au mieux la connexion HTTPS, la vitesse du site et la configuration de Nginx.

Nous allons commencer par configurer correctement Let’s Encrypt avec un certificat ECDSA, ensuite nous mettrons en place la configuration de Nginx pour qu’il serve uniquement sur TLS v1.2 et nous finirons par optimiser Nginx et WordPress afin d’avoir un super résultat sur Gtmetrix.

I – Let’s Encrypt en ECDSA

Nous allons utiliser la nouvelle norme ECDSA (Elliptic Curve Digital Signature Algorithm) qui permet d’avoir des clés plus courtes que la traditionnelle RSA, mais bien plus costaudes. (« A 384 bit elliptical curve key is equivalent to a 7680 bit RSA key in terms of security »). Je suis pas expert en cryptographie, mais autant utiliser la meilleure technologie qui réduit également l’utilisation des ressources du serveurs.

Pour cela, on va utiliser le client Let’s Encrypt, veuillez vérifier que votre Nginx est joignable sur le Web (port 80) et que votre nom de domaine pointe bien vers votre serveur (niveau DNS).

cd /etc/
git clone https://github.com/letsencrypt/letsencrypt ; cd letsencrypt

Maintenant on va préparer l’arborescence pour nos certificats dans le répertoire /etc/letsencrypt :

mkdir -p /etc/letsencrypt/live-ecdsa/your-domain.com/letmp
cd /etc/letsencrypt/live-ecdsa/your-domain.com

On va ensuite générer notre clé privée à l’aide d’openssl en utilisant la courbe secp384r1 (recommandé dans notre cas) :

openssl ecparam -genkey -name secp384r1 > privkey-p384.pem

On peut maintenant créer notre CSR (Certificate signing request) qui contient les informations de notre demande de certificats auprès de Let’s Encrypt, n’oubliez pas de remplacer your-domain.com par votre nom de domaine (et sous-domaine si besoin) :

openssl req -new -sha256 -key privkey-p384.pem -subj "/CN=your-domain.com" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:your-domain.com")) -outform der -out csr-p384.der

Nous allons réaliser la dernière étape, c’est-à-dire la création du certificat auprès de Let’s encrypt, vérifier bien le path de votre serveur Web (/var/www/html) et votre domaine.

cd /etc/letsencrypt/live-ecdsa/your-domain.com/letmp
/etc/letsencrypt/letsencrypt-auto certonly -a webroot --email [email protected] --webroot-path /var/www/html/ --csr /etc/letsencrypt/live-ecdsa/your-domain.com/csr-p384.der --renew-by-default --agree-tos
cat 0001* > /etc/letsencrypt/live-ecdsa/your-domain.com/chain.pem

Notre certificat est maintenant généré et prêt à être utilisé, cependant il faut configurer Nginx pour qu’il l’utilise.

II – Configuration de Nginx

On va maintenant attaquer la configuration de Nginx, il écoutera sur les ports 80 et 443 dont le premier redirigera vers le second (pour être toujours en HTTPS). La configuration se fait dans le fichier /etc/nginx.conf directement sur votre serveur ou dans un container Docker par exemple. Nous allons également le configurer afin qu’il réponde en HTTP/2 et optimiser quelques paramètres comme la compression Gzip ou l’expiration des ressources.

Voici le fichier, n’oubliez pas de remplacer your-domain.com par votre nom de domaine.

worker_processes  1;
 
events {
    worker_connections  1024;
}
 
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    gzip on;
    gzip_disable "msie6";  
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 32k;
    gzip_min_length  256;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
    
    server {
      listen         80;
      server_name    your-domain.com;
      return         301 https://$server_name$request_uri;
    }
 
    server {
      
      listen 443 ssl http2;
      ssl_certificate /etc/letsencrypt/live-ecdsa/your-domain.com/chain.pem;
      ssl_trusted_certificate /etc/letsencrypt/live-ecsa/your-domain.com/chain.pem;
      ssl_certificate_key /etc/letsencrypt/live-ecdsa/your-domain.com/privkey-p384.pem;
      ssl_session_timeout 1d;
      ssl_session_cache shared:SSL:50m;
      ssl_session_tickets off;
      ssl_protocols TLSv1.2;
      ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
      ssl_prefer_server_ciphers on;
      add_header Strict-Transport-Security max-age=15768000;
      ssl_stapling on;
      ssl_stapling_verify on;
      ssl_ecdh_curve secp384r1;
 
      resolver 8.8.8.8 8.8.4.4 valid=86400;
      root /var/www/html;
      index index.php;

      location / {
        try_files $uri $uri/ /index.php?$args;
      }
 
      location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
        expires 30d;
        add_header Pragma public;
        add_header Cache-Control "public";
      }

      rewrite /wp-admin$ $scheme://$host$uri/ permanent;
        
      location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
        access_log off; log_not_found off; expires max;
      }
 
      location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) {
        	return 404;
        }
          root           /var/www/html;
          fastcgi_pass   localhost:9000;
          fastcgi_index  index.php;
          fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
          include        fastcgi_params;
      }    
    }
}

Quelques explications, premièrement la ligne rewrite concerne uniquement WordPress, à retirer donc si vous ne l’utilisez pas. Le chemin pour le répertoire web est /var/www/html et la configuration implique que vous utilisez PHP-Fpm ou HHVM sur localhost:9000.

Si vous avez respecté le chemin vers les certificats et bien copier la configuration, on va pouvoir redémarrer Nginx et vérifier la connexion TLS.

service nginx restart

Le meilleur outils selon moi pour tester sa connexion TLS (mieux que SSLab) est CryptCheck. Il va analyser toute les caractéristiques de votre connexion HTTPS (ou d’autres protocoles)  pour vous rendre un résultat complet sur la qualité de votre configuration.

https A+

On constate donc qu’on a visiblement la meilleure implémentation de TLS sur HTTP du moment (on l’a bien mérité quand même).

Vous devez connaître le site GtMetrix, qui permet d’analyser votre site afin de lui donner une note, attribué en rapport à la vitesse et à l’optimisation des ressources. Il utilise les méthodes Pagespeed et Yslow. Avant de mettre en place cette configuration de Nginx, j’avais des scores relativement moyen (B et C).

Après quelques améliorations (Gzip, Expire headers) et à l’optimisation de mes images (ne pas avoir d’images plus grandes sur le serveur que l’affichage sur le site), j’arrive à avoir un résultat relativement correct :

gtmetrix_results

(Yslow enlève 10% si on n’utilise pas de CDN, je trouve ça assez spécial …).

EDIT : Voici un script maison qui Renew votre certificat ECSA. Lancez le sur le serveur ou dans un container Nginx (mon cas).

Voici quelques liens qui m’ont était bien utiles :

  • https://angristan.fr/configurer-https-nginx/
  • https://www.abyssproject.net/2016/02/creer-un-certificat-ecdsa-avec-lets-encrypt/
  • https://www.howtoforge.com/tutorial/how-to-protect-your-debian-and-ubuntu-server-against-the-logjam-attack/#nginx
  • https://dlu.io/lets-encrypt-using-with-nginx-plus-http2-and-enhanced-ssl-rating/

 

Published inDevOpsDockerLinuxTLSTutorielWebWeb server

15 Comments

  1. Salut 🙂

    N’Oublies pas de configurer la variable ssl_trusted_certificate tel que :
    ssl_certificate /etc/letsencrypt/live-ecdsa/your-domain.com/chain.pem;

    Sinon l’OSCP stapling n’est pas pris en compte

    • valentin valentin

      Merci pour ton attention, je l’ai ajouté dans l’article.

  2. As-tu noté une augmentation de ton activité CPU après avoir mis en place du HTTPS ? J’avoue que ça me trotte dans la tête mais ayant un simple VPS d’OVH à 3,5€/mois, j’ai peur que ça ne surcharge le CPU (mais je me fais peut-être des films et je ne dépasse pas les 30% d’utilisation CPU actuellement donc j’ai un peu de marge).

    Un autre point qui me bloque est que j’ai lu que pour renouveler son certificat avec Let’s Encrypt, il était nécessaire d’arrêter le service. sauf erreur de ma part, tu n’en fais pas mention. Ce n’est peut-être valable que pour Apache ? Utilisant Nginx pour mon blog, ce serait une bonne nouvelle mais j’ai des doutes… :-/

  3. Matthieu Matthieu

    Bonjour,
    J’ai un souci en utilisant le script de création du certificat. J’ai beau regarder le script sous tous les angles, je ne vois pas d’où peut venir le souci…

    ./etc/letsencrypt/certificatssl.sh: line 10: unexpected EOF while looking for matching ` »‘
    ./etc/letsencrypt/certificatssl.sh: line 18: syntax error: unexpected end of file

    Merci pour ton aide si tu peux. ^^

    • valentin valentin

      Hello, il s’agit de quel script ? Celui en fin de mon article ? As-tu bien adapté avec le nom de ton domaine ? Essaie de faire commande par commande sans lancer le script.

  4. Merci pour tes retours d’expérience. Ce serait vraiment top si tu pouvais partager un script Ansible dans un repo Github pour mettre en place ton stack sur n’importe quelle machine Linux (et un « role » wordpress optionnel et configurable), avec une seule ligne de commande !
    Cela permettrait d’itérer, de mettre à jour et de contribuer à ton stack (par exemple, ajouter Varnish et Memcache, HTTP/2…)..

    J’ai regardé du côté de Cloudways et ils sont très bons, mais ils restreignent les machines sur lesquelles leur stack est installé, et ne donnent pas d’accès root.

  5. JulienhokiniIT JulienhokiniIT

    Bonjour,
    Je suis en train de monter une VM docker , avec un owncloud en suivant votre tuto) et plus tard un wordpress.
    Je n’ai pas encore lancer d’image ou autre juste installer les bases , je n’ai donc pas de serveur web pour lancer la génération du Certificat.
    Une solution ?

    • valentin valentin

      Il faut que tu utilises l’image Docker de certbot (lets encrypt) qui va te monter un serveur Web temporaire. Regarde mon article LEMP sur Docker. N’hésites si tu as un soucis.

  6. rsuinux rsuinux

    Bonjour;
    Je fais des recherches pour installer letsencrypt sur mon domaine.
    Juste une petite question:
    Quand vous dite « your-domain.com’ faite vous référence au nom de domaine ou du mon du site « www.your-domain.com »
    Mon idée est celle ci:
    un certificat pour http://www.suinot.org (puisque c’est mon nom de domaine)
    un certificat pour mail.suinot.org pour le serveur de courrier

    Sauf si je peux utiliser le même sans problème…

    Merci de votre avis et conseils.
    Rémi

    • rsuinux rsuinux

      de plus, ligne 10 de votre script renew, il manque à priori, je dirait une apostrophe.

  7. bien le bonjour 🙂
    Voila, j’ai corrigé, dans l’ordre:
    – ma configuration nginx
    – les commandes letsencrypt
    et ça marche!
    J’ai à présent: tous mes sites en https, un par defaut, et des virtual derrière, avec letsencrypt pour le certificat.
    Il vous manque bien un  » dans votre script.

    Merci pour tout.
    R. Suinot.

    • valentin valentin

      Merci, j’ai corrigé 🙂
      Content que ça marche !

  8. Bonjour;
    Je reviens car cela a fonctionné… jusqu’au renouvellement des certificats.
    Et là, c’est le drame! Pas de renouvellement. Dans les log, j’ai toujours eu cette erreur:
    DEBUG:certbot.main:Arguments: [‘-q’]
    et
    DEBUG:certbot.main:Discovered plugins: PluginsRegistry(PluginEntryPoint#webroot,PluginEntryPoint#null,PluginEntryPoint#manual,PluginEntryPoint#standalone)

    A présent, je cherche à renouveler les certificats malgré la date dépassée.
    Première question: est-ce possible?
    Seconde question: comment?
    et pis: pourquoi ça a pas fonctionné, surtout? c’est le plus important.
    ma crontab:
    @daily /etc/letsencrypt/letsencrypt-auto renew >> /var/log/letsencrypt/renewal.log

    Je n’ai pas trouvé mon bonheur dans la doc du site letsencrypt.
    Merci de votre aide.

  9. comme d’habitude, je trouve la réponse, et donc je la donne ici:
    1/ installer le module certbot-nginx, pas fait sur ma machien (!!?) apt install python-certbot-nginx
    2/ j’ai fait un petit script :
    renew-certificat.sh
    #!/usr/bin/env bash
    set -ex
    domain= »xxxxx.org »
    site1= »www.xxxx.org »
    site2= »aaaa.xxxx.org »
    site3= »zzzz.xxxx.org »
    email= »moimê[email protected] »

    cd /etc/letsencrypt
    certbot certonly –nginx -n -d $domain -d $site1 -d $site2 -d $site3

    après ça, l’emplacement des certificats change, là, j’ai pas suivi, mais c’est pas grave. J’ai modifié la config de nginx et après un petit instant de frayeur: Error SSL_ERROR_NO_CYPHER_OVERLAP sur firefox ma fois, ça a fonctionné quand même.
    Voila Si vous avez une idée pour automatiser ca, merci.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Proudly hosted with Open-sources Softwares.