From 9a474f09badc5530bbadf13606e837107ef8c595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Thu, 11 Feb 2016 22:08:17 +0100 Subject: [PATCH] Add an article about let's encrypt --- content/crypto/lets-encrypt.rst | 130 +++++++++++++++++++++++++ content/static/unsecure-connection.png | Bin 0 -> 11258 bytes 2 files changed, 130 insertions(+) create mode 100644 content/crypto/lets-encrypt.rst create mode 100644 content/static/unsecure-connection.png diff --git a/content/crypto/lets-encrypt.rst b/content/crypto/lets-encrypt.rst new file mode 100644 index 0000000..eb28bba --- /dev/null +++ b/content/crypto/lets-encrypt.rst @@ -0,0 +1,130 @@ +Let's Encrypt + HAProxy +####################### + +:date: 2016-02-11 +:headline: Comment j'ai mis en place des certificats SSL avec Let's Encrypt + derrière haproxy. + +.. epigraph:: + + It’s time for the Web to take a big step forward in terms of security and + privacy. We want to see HTTPS become the default. Let’s Encrypt was built + to enable that by making it as easy as possible to get and manage + certificates. + + -- `Let's Encrypt `_ + +Depuis début Décembre, la nouvelle *authorité de certification* Let's Encrypt +est passée en version *Beta*. Les certificats SSL sont un moyen de 1. chiffrer la +communication entre votre navigateur et le serveur et 2. un moyen d'être sur +que le site Web auquel vous accédez est celui auquel vous pensez vous connecter +(pour éviter des `attaques de l'homme du milieu +`_). + +Jusqu'à maintenant, il était nécessaire de payer une entreprise pour faire en +sorte d'avoir des certificats qui évitent d'avoir ce genre d'erreurs dans vos +navigateurs: + +.. image:: {filename}/static/unsecure-connection.png + :alt: Message de firefox lorsque une connexion n'est pas sécurisée. + +Maintenant, grâce à Let's Encrypt il est possible d'avoir des certificats SSL +**gratuits**, ce qui représente un grand pas en avant pour la sécurité de nos +communications. + +Je viens de mettre en place un procédé (assez simple) qui permet de configurer +votre serveur pour générer des certificats SSL valides avec Let's Encrypt et +le répartiteur de charge `HAProxy `_. + +Je me suis basé pour cet article sur d'`autres +`_ `articles +`_, dont je +vous recommande la lecture pour un complément d'information. + +Validation des domaines par Let's Encrypt +========================================= + +Je vous passe les détails d'installation du client de Let's Encrypt, qui sont +`très bien expliqués sur leur documentation +`_. + +Une fois installé, vous allez taper une commande qui va ressembler à:: + + letsencrypt-auto certonly --renew-by-default + --webroot -w /home/www/letsencrypt-requests/ \ + -d hurl.kinto-storage.org \ + -d forums.kinto-storage.org + +Le *webroot* est l'endroit ou les preuves de détention du domaine vont être +déposées. + +Lorsque les serveurs de Let's Encrypt vont vouloir vérifier que vous êtes bien +à l'origine des demandes de certificats, ils vont envoyer une requête HTTP sur +``http://domaine.org/.well-known/acme-challenge``, ou il voudra trouver des +informations qu'il aura généré via la commande ``letsencrypt-auto``. + +J'ai choisi de faire une règle dans haproxy pour diriger toutes les requêtes +avec le chemin ``.well-known/acme-challenge`` vers un *backend* nginx qui sert +des fichiers statiques (ceux contenus dans +``/home/www/letsencrypt-requests/``). + +Voici la section de la configuration de HAProxy (et `la configuration +complete +`_ +si ça peut être utile):: + + frontend http + bind 0.0.0.0:80 + mode http + default_backend nginx_server + + acl letsencrypt_check path_beg /.well-known/acme-challenge + use_backend letsencrypt_backend if letsencrypt_check + + redirect scheme https code 301 if !{ ssl_fc } !letsencrypt_check + + backend letsencrypt_backend + http-request set-header Host letsencrypt.requests + dispatch 127.0.0.1:8000 + +Et celle de NGINX:: + + server { + listen 8000; + server_name letsencrypt.requests; + root /home/www/letsencrypt-requests; + } + +Installation des certificats dans HAProxy +========================================= + +Vos certificats SSL devraient être générés dans ``/etc/letsencrypt/live``, mais +ils ne sont pas au format attendu par haproxy. Rien de grave, la commande +suivant convertit l'ensemble des certificats en une version compatible avec +HAProxy:: + + cat /etc/letsencrypt/live/domaine.org/privkey.pem /etc/letsencrypt/live/domaine.org/fullchain.pem > /etc/ssl/letsencrypt/domaine.org.pem + +Et ensuite dans la configuration de haproxy, pour le (nouveau) *frontend* https:: + + bind 0.0.0.0:443 ssl no-sslv3 crt /etc/ssl/letsencrypt + +Faites bien attention à avoir un *frontend* `https` pour tous vos sites en HTTPS. +`Pour moi cela ressemble à ça +`_. + +Une fois tout ceci fait, redémarrez votre service haproxy et zou ! + +Automatisation +============== + +Pour automatiser un peu tout ça, j'ai choisi de faire ça comme suit: + +* Un fichier domaine dans ``letsencrypt/domains/domain.org`` qui contient le script ``letsencrypt``. +* Un fichier d'installation de certificats dans + ``letsencrypt/install-certs.sh`` qui s'occupe d'installer les certificats + déjà générés. + +Et voila ! `Le tout est dans un dépot github +`_, si jamais ça peut vous servir, tant mieux ! + diff --git a/content/static/unsecure-connection.png b/content/static/unsecure-connection.png new file mode 100644 index 0000000000000000000000000000000000000000..5e1b25b52df8739966d660c97f67b7027e80e6ad GIT binary patch literal 11258 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 G(Ek9(5=eUh literal 0 HcmV?d00001