De quoi parle-t-on donc ?

Note: This is the French version of the post. English version here.

Haproxy :

Haproxy est un logiciel de proxy. Il peut servir à beaucoup de chose, ici nous l'utiliserons pour de l'https et de l'http. Je vous invite à lire mon article à ce sujet si vous voulez en savoir plus.

Dans cet article, nous considérerons que vous avez un serveur avec Haproxy fonctionnel pour les tests.

Let's Encrypt :

Let's Encrypt est un projet soutenu entre autre par la fondation Mozilla et Cisco.

L'idée est de mettre en place une autorité de certification gratuite pour créer des certificats SSL et permettre au plus grand nombre de personne de sécuriser leurs sites web.

Au regard du prix habituel d'un certificat, c'est un projet vraiment très intéressant !

D'autant plus qu'il est vraiment axé sur la facilité d'utilisation, avec notamment une API permettant de créer des certificats de manière simple.

Nous nous appuierons ici sur cette API, à travers un client créé par Let's Encrypt.

EDIT : 04/04/16 : ajout d'une note si vous utilisez des redirect de l'http vers l'https, pour permettre le renouvellement auto de letsencrypt

EDIT : 16/09/16 : Mise à jour avec les nouveaux binaires suite à la sortie de bêta de LE + amélioration et versionning des Scripts

Concept

Let's Encrypt propose plusieurs options via le client pour télécharger et valider un certificat SSL.

Si votre haproxy est sur le même serveur que le serveur web, vous pouvez utiliser l'option --webroot, qui permet de placer un fichier à la racine de votre site, que le site letsencrypt ira vérifier pour valider que vous possédez bien le site web. Si vous souhaitez utiliser cette technique, cet article de blog explique très bien comment le faire interagir en utilisant un serveur apache dédié.

Dans cet article, nous utiliserons l'option --standalone qui permet de lancer un mini serveur web, qui contiendra le fichier de vérification et qui sera utilisé par le site letsencrypt. Cette option présente à première vue l'inconvénient de devoir utiliser le port 80 ou 443 pour lancer le serveur web, ce qui demande evidemment de faire sauter le serveur web principal, et donc de couper les sites pendant la vérification.

Mais nous allons utiliser une option du client qui permet d'utiliser un autre port que ceux par défaut, allié aux capacité frontend/backend de haproxy, pour effectuer la vérification sans aucune coupure de vos sites :-)

Merci à coolaj86 dont l'article m'a donné cette idée d'utiliser haproxy et Let's Encrypt ensembles ;-)

-- Many thanks to coolaj86 as his post give me this idea to use haproxy and Let's Encrypt together ;-) --

 

Récupérer le client Let's Encrypt

Maintenent que LE est officiellement sorti un binaire plus carré est disponible, et beaucoup plus simple à installer que le précédent letsencrypt-auto.

Toutes les info sont présente sur le site officiel de certbot, mais voici les instructions rapides pour l'installer.

cd /root/

mkdir letsencrypt

cd letsencrypt

wget https://dl.eff.org/certbot-auto

chmod a+x certbot-auto


Vous pouvez ensuite directement utiliser le binaire pour activer vos certificats

./certbot-auto certonly  --domains blog.victor-hery.com --renew-by-default --http-01-port 63443 --agree-tos

Configurer haproxy

Frontend

Pour éviter toute coupure, nous allons utiliser votre (vos) frontend existant.

La requête de Let's Encrypt se fera sur l'adresse IP à laquelle répond le site web pour lequel vous générez le certificat, donc si vous avez plusieurs frontend ou plusieurs IP, il faudra configurer chaque frontend selon vos besoins.

L'idée est la suivante : lors de sa requête, Let's Encrypt interrogera l'url du site, suivi simplement de /.well-known/acme-challenge/un-id_unique.

Nous allons donc configurer une acl qui cherchera cette chaine pour la rediriger vers un backend spécifique !

frontend http-in
    acl app_letsencrypt  path_beg   /.well-known/acme-challenge/
    [...]
    use_backend bk-letsencrypt if app_letsencrypt
  • path_beg : cherche les URL dont le path (ce qui est après le premier / de l'url) commence par .well-known/acme-challenge/

Ainsi, toutes les requêtes de vérification de Let's Encrypt seront redirigées vers le backend bk-letsencrypt.

Attention : Si vous redirigez de manière forcée vos sites en HTTP vers l'HTTPS, alors haproxy va rediriger aussi les requêtes let's encrypt vers votre frontend HTTS.

Il faut alors ajouter l'acl et le use_backend bk-letsencrypt dans votre frontend https !

Backend

Concernant le backend, nous allons le configurer pour rediriger les requêtes vers le serveur qui sera lancé en local par le client Let's Encrypt.

backend bk-letsencrypt
    log global
    mode http
    server srv_letsencrypt 127.0.0.1:63443
  • mode http : permet, tant qu'à faire, de vérifier qu'il s'agit bien d'une requête http qui passe par ce backend
  • server : la ligne redirige vers le serveur que le client Let's Encrypt aura lancé sur localhost, sur le port 63443

Ce serveur ne tournera pas en permanence, uniquement lorsque le client Let's Encrypt est en attente de vérification, en temps normal haproxy renverra donc une erreur 503 si quelqu'un tente d'accéder à une URL qui correspond à l'acl du frontend.

Bien sûr, rechargez haproxy après ces modifications.

systemctl reload haproxy.service

Configurer et utiliser Let's Encrypt

Configuration :

Pour simplifier la ligne de commande et faciliter l'automatisation, nous allons directement configurer un fichier pour Let's Encrypt.

Let's Encrypt utilise par défaut le fichier /etc/letsencrypt/cli.ini. Nous allons donc taper dedans directement.

rsa-key-size = 4096
email = your_admin_email
authenticator = standalone
standalone-supported-challenges = http-01
  • rsa-key-size ; permet de générer directement des certificats de 4096 bit, plus robustes que le 2048 par défaut. Vous pouvez redescendre à 2048 (mais pas en dessous !) si votre serveur est peu puissant pour gagner du temps de génération.
  • email : utilisez une adresse valide, car ce sera l'adresse utilisée pour récupérer le certificat sur le site web  de Let's Encrypt si jamais le besoin s'en faisait sentir
  • authenticator : comme vu plus haut, nous utilisons le mode standalone
  • standalone-supported-challenges : Cette option est spécifique au mode standalone, et permet de spécifier la méthode à utiliser pour la vérification, parmi http-01 ou tls-sni-01. Nous utilisons http-01 car notre site n'a pas (forcément) encore de certificat, et donc lors d'un appel sur 403 haproxy n'aura pas de certificat valide à présenter.

Utiliser http pour la vérification ne pose pas de problème, car seule la requête de validation passe en clair, et elle n'a rien de secrète.

Génération du certificat

La ligne de commande à utiliser pour générer votre certificat est la suivante :

/root/letsencrypt/certbot-auto certonly --domains yourdomain.tld --renew-by-default --http-01-port 63443 --agree-tos
  • certonly : précise que l'on souhaite simplement générer le certificat, et pas utiliser un plugin d'installation pour stocker les certificats quelque part
  • domains : le domaine ou sous domaine pour lequel vous voulez générer ce certificat
  • renew-by-default : indique que si le certificat existe déjà, il faut le renouveler. S'il n'existe pas, il sera créé dans la base de Let's Encrypt
  • http-01-port : permet de spécifier le port à utiliser pour le serveur de validation temporaire. C'est le port utilisé dans le backend haproxy plus haut
  • agree-tos : indique d'accepter les conditions d'utilisation de Let's Encrypt (vous les avez lues et êtes ok pour les accepter, pas vrai ?)

Mettre en place le certificat

Les certificats sont mis en place dans le répertoire /etc/letsencrypt/live/yourdomain.tld/ par le client lors de la génération.

Vous y trouverez plusieurs fichiers :

  • cert.pem : le certificat (le crt)
  • chain.pem : la chaine de confiance (les certificats de l'authorité)
  • fullchain.pem : une concaténation des 2 premiers
  • privkey.pem : la clef privée du certificat

Pour haproxy, vous devez concaténer fullchain.pem et privkey.pem dans un seul fichier, et le placer là ou votre haproxy lit ses certificats.

cat fullchain.pem privkey.pem > domain.tld.pem

Un exemple :

frontend https-in
    bind  IP:443 ssl crt /etc/haproxy/cert/

Ici, nous devons mettre notre fichier.pem dans le répertoire /etc/haproxy/cert/

Une fois le certificat placé là, rechargez haproxy pour qu'il le lise, et ça y est tout sera Ok.

N'oubliez pas que pour que votre site fonctionne en HTTPS, il faudra un frontend https dans haproxy, avec les acl qui vont bien.

En général, un copier/coller de votre frontend HTTP classique en changeant le port vers 443 fait l'affaire.

Limitations

Renouveler le certificat

Même sorti de bêta, LE a pris la décision de ne fournir des certificat valides que 3 mois (90 jours), ce afin d'éviter les abus et faire "vivre" le certificat.

Notez que contrairement au binaire de bêta, certbot-auto peut scanner vos certificats disponibles (via le répertoire /etc/letsencrypt/renewal/) pour les renouveller automatiquement.

Pensez donc à configurer une tache cron sur votre serveur pour renouveler le certificat régulièrement

crontab -e
#renew certificate
30 01 01,10,20,30 * * /root/letsencrypt/certbot-auto renew

Cette tâche permettra de lancer la commande tous les 10 jours, afin d'être sur d'avoir des certificats à jour.

N'oubliez pas qu'il faudra quand même aller récupérer le fullchain.pem et le .key pour aller les mettre dans haproxy. Voyez plus bas pour un peu d'automatisation sur cette tâche !

Nombre de certificat par domaine

A cause de sa gratuité, Let's Encrypt dispose d'un système évitant de générer trop violemment des certificats pour le même domaine.

Vous aurez donc une erreur si vous tentez de générer trop vite ou trop souvent des certificats avec le même domaine.

Attention, c'est valable également pour les sous-domaine ! La limitation prend en compte tout ce qui se termine par domain.tld.

iDN :

Actuellement Let's Encrypt ne gère pas les noms de domaines internationaux.

Il est donc impossible de générer un certificat pour un domaine ou un sous-domaine contenant des accents ou des caractères spéciaux.

Impossible par exemple de générer pour l'instant un certificat pour blog.héry.com malheureusement.

Une requête existe actuellement à ce sujet sur le github de Let's Encrypt, mais plusieurs mois après le passage en stable, ce n'est toujours pas prêt malheureusement. Affaire à suivre (et à aider dans la mesure de ses moyens ;-) )

Bonus : des scripts

Histoire de faciliter la génération et la mise en place sur haproxy, j'ai écris quelques petits scripts maison permettant de générer le certificat et le placer dans le bon répertoire, ainsi que gérer proprement le renouvellement.

Ceux-ci sont disponibles sur mon dépôt git, vous pouvez donc directement les cloner sur votre serveur :

git clone https://git.lecygnenoir.info/LecygneNoir/letsencrypt-haproxy.git

Le README décrit rapidement comment utiliser les scripts, c'est globalement assez simple.

create_certificate permet de créer un certificat pour le domaine passé en paramètre, génère le .pem pour haproxy, le place dans l'arborescence haproxy et reload haproxy.

renew-certificates se contente de renouveler tous les certificats, de générer les fichiers pour haproxy et de reload ce dernier. Vous pouvez utiliser renew-certificates dans une tâche cron comme indiqué plus haut.

Vérifiez les chemins dans les scripts, notamment où placer les certificats et le chemin vers le binaire certbot

 

Voila, avec tout ça vous devriez pouvoir générer autant de certificat que vous le souhaitez, et les utiliser directement dans haproxy, alors bienvenue dans le monde de l'HTTPS !


Victor Avatar Victor est le rédacteur principal du blog.
Comments

Si vous avez des questions, si quelque chose n'est pas clair, n'hésitez pas à commenter !

  • Avatar
    Permalink

    Arnaud

    Posted on

    Wha super article

  • Avatar
    Permalink

    Pierre-Philippe

    Posted on

    Bonjour,
    Je ne comprend pas le principe, en gros Let's Encrypt génère une clef pour un site http qui n'existe pas, et quand une demande de redirection sur un site géré par Haproxy une authentification https géré avant le redirection vers un des sites redirigé par haproxy ?

    Dans mon cas haproxy identifie des sous domaines et redirige en fonction des réglés ecl sur des backends (adresse de sites).
    J'aurai aimé pouvoir avoir une authentification https unique puis redirection sur un des sites.

    Donc je ne comprend pas comment identifier le serveur haproxy (domaine) pour qu'il soit reconnue par Let's Encrypt.
    Car le serveur haproxy n'a pas de nom de sous domaine identifier sur le net, il ne sert que d’aiguillage vers des serveurs http qui ont un nom de sous domaine identifié sur le net.
    quand je lance le scrypt de génération de certificat la réponse est:

    Domain: xxx.ndm.dl ndm.dl est mon domaine
    xxx est un des sous domaine

    Type:   unauthorized  
    Detail: Error parsing key authorization file: Invalid key  
    authorization: 9 parts  
    To fix these errors, please make sure that your domain name was  
    entered correctly and the DNS A record(s) for that domain  
    contain(s) the right IP address.  
    - Your account credentials have been saved in your Let's Encrypt  
    configuration directory at /etc/letsencrypt. You should make a  
    secure backup of this folder now. This configuration directory will  
    also contain certificates and private keys obtained by Let's  
    Encrypt so making regular backups of this folder is ideal
    

    donc je nage un peut
    Merci pour le tuto


    • Avatar
      Permalink

      Victor

      Posted on

      Hello Pierre Philippe,

      Le concept de Let's Encrypt est de créer un certificat tout ce qu'il y a de plus classique. Ici, l'astuce est que normalement, tes DNS pointent sur ton haproxy, puisque c'est lui qui route ensuite via les ACL vers les backend.
      C'est donc lui qui doit posséder le certificat HTTPS pour le servir aux visiteurs. En l'occurence, haproxy va déchiffrer le flux avec le certificat, puis envoyer le flux non chiffré vers les backend. (Il est possible de rechiffrer le flux avant de l'envoyer, mais ce n'est pas le sujet)

      Donc normalement, tu dois utiliser comme nom de domaine celui pour lequel tu veux un certificat, et lancer let's encrypt depuis le serveur en question. Il faut également (si tu as plusieurs adresses IP sur le haproxy), que haproxy écoute sur l'adresse où Let's encrypt va envoyer la requête de vérification.

      Voila pour le concept :)

      Ensuite pour ton problème plus précisément, à première vue ta configuration semble correct. Le invalid key semble montrer que let's encrypt n'est pas content de la clef généré, mais pourtant c'est lui même qui la génère.
      Est ce que tu as modifié la configuration de Let's Encrypt pour générer une clef plus petite que 1024bits ?
      Egalement, quelle version d'openSSL utilise-tu ? Il est possible avec les différentes failles de sécurité sorties ces derniers temps que Let's Encrypt ait certain critères à ce propos même si je ne l'ai pas remarqué.

      N'hésite pas à me préciser plus comment tu utilise Let's Encrypt, je pourrais peut être t'en dire plus :) (sur quel OS, la ligne exacte que tu lances, en cachant le domaien bien sur, la version d'openssl, etc)

      Bon courage !

    • Avatar
      Permalink

      Pierre-Philippe

      Posted on

      Bonjour et merci pour ta réponse aussi rapide.

      J'ai déjà un serveur Haproxy qui redirige en fonction des sous domaines vers les serveurs appropriés.
      J'ai donc une autre machine ou je test Haproxy pour le certificat.

      Le bute est d'avoir un seul certificat par domaine pour un ensemble de sous domaine et Haproxy me parais la solution.
      D'autant plus que mes appli web ne tournent pas sur Apache ou Nginx et donc pas facile pour les certificats.

      HAPROXY config
      global
              log /dev/log    local0
              log /dev/log    local1 notice
              chroot /var/lib/haproxy
              stats socket /run/haproxy/admin.sock mode 660 level admin
              stats timeout 30s
              user haproxy
              group haproxy
              daemon
      
              # Default SSL material locations
              #ca-base /etc/ssl/certs
              #crt-base /etc/ssl/private
      
              # Default ciphers to use on SSL-enabled listening sockets.
              # For more information, see ciphers(1SSL). This list is from:
              #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
              #ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
              #ssl-default-bind-options no-sslv3
      
      defaults
              log     global
              mode    http
              option  httplog
              option  dontlognull
              timeout connect 5000
              timeout client  50000
              timeout server  50000
              errorfile 400 /etc/haproxy/errors/400.http
              errorfile 403 /etc/haproxy/errors/403.http
              errorfile 408 /etc/haproxy/errors/408.http
              errorfile 500 /etc/haproxy/errors/500.http
              errorfile 502 /etc/haproxy/errors/502.http
              errorfile 503 /etc/haproxy/errors/503.http
              errorfile 504 /etc/haproxy/errors/504.http
      

      frontend http-in acl app_letsencrypt path_beg /.well-known/acme-challenge/

      use_backend bk-letsencrypt if app_letsencrypt
      

      backend bk-letsencrypt log global mode http server srv_letsencrypt 127.0.0.1:63443

      La commande Let's Encrypt
      letsencrypt-auto certonly --domains xx-mydomaine.fr --renew-by-default --http-01-port 63443 --agree-tos

      le fichier conf de Let's Encrypt

      rsa-key-size = 4096
      email = ppb@itgreen.fr
      authenticator = standalone
      standalone-supported-challenges = http-01
      

      Par contre Let's Encrypt génère bien 2 clefs une csr et une keys.
      Je peut aussi envoyer un schéma format svg de l'instal.
      Bonne et Belle journée
      Pierre-Philippe

    • Avatar
      Permalink

      Victor

      Posted on

      Bonjour Pierre Philippe,

      Mmh, j'ai peut être une idée pour ton problème, notamment à propos du fait que pour que la chaine de certification fonctionne, il faut que la ligne let's encrypt soit lancée sur la machine qui porte l'adresse IP du site que tu veux certifier (car les serveurs de let's encrypt vont essayer de contacter le domaine répondant dans les DNS)

      Je veux bien t'aider à tester, est ce que ça te convient si on continue cette discussion par courriel plutôt ? Ça me semble plus pratique pour échanger rapidement et facilement que mon système de commentaire (qui a malheureusement ses limites avec des gros commentaires ^^)

      N'hésite pas à me contacter via le formulaire de contact, ou à me dire si tu souhaite en discuter via commentaires :)

      A plush' !

Ajouter un commentaire / Add a Comment

Vous pouvez utiliser la syntaxe Markdown pour formatter votre commentaire.

You can use the Markdown syntax to format your comment.

Flux Atom pour commentaire / Comment Atom Feed

Published

Category

Système

Tags

Restez en contact