L'objectif final
Avoir l'ensemble de ses sites accessibles en https, avec des certificats letsencrypt ( gratuits et surtout renouvelés automatiquement ) et un serveur de cache ( varnish ) pour augmenter la capacité de charge ( bref optimiser les perfs des sites ) pour enfin passer le relais au backend ( Apache, Nginx .. peu importe ).
le hic
Il y en a toujours un ... et pas des moindres cette fois, varnish ne gère pas l'https !
La solution - TLS Termination Proxy
L'utilisation d'un proxy ( HAProxy ) pour gérer les transactions Https, puis relayer en clair les requêtes vers varnish qui à son tour si nécessaire va relayer les requêtes au(x) serveur(s) ( Apache/Nginx/... )
Plusieurs avantages à cette solution ( Terminaison SSL ), soulager les serveurs finaux ( Apache/Nginx ... ) de la gestion du SSL et surtout pour nous admins, nous simplifier la tâche pour ce qui est de la gestion des certificats.
Dans ce billet
Dans ce billet, je ne vais parler que de HAProxy et letsencrypt pour commencer, varnish étant un assez gros morceau, il fera l'objet d'un second billet.
Mise en place de la situation
Vous pouvez utiliser un seul et même serveur pour héberger l'ensemble de ces services ... mais franchement ça ne devrait jamais être le cas ! Donc pour ce billet nous allons partir du principe que nous possédons 2 serveurs .
Le premier pour héberger HAProxy et Varnish ( 10.0.0.1 ) et le second pour héberger le serveur HTTP ( 10.0.0.2 )
Les deux serveurs sont sous Debian 8 (Jessie).
Serveur HAProxy
Commençons par Installer HAProxy ( plus quelque trucs utils dans ce "tuto" ) ... qui se fait simplement par
apt install haproxy bc vim
Commençons par copier le fichier de configuration par defaut
cd /etc/haproxy/ cp haproxy.cfg haproxy.cfg.orig
Maintenant nous pouvons éditer le fichier /etc/haproxy/haproxy.cfg
vim /etc/haproxy/haproxy.cfg
Remplacer tout le contenu par :
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 maxconn 2048 tune.ssl.default-dh-param 2048 defaults #log global log /dev/log local0 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 option forwardfor option http-server-close ################################# frontend www-http bind *:80 mode http #option httpclose option forwardfor option httplog option dontlognull reqadd X-Forwarded-Proto:\ http default_backend www-backend frontend www-https bind *:443 ssl crt /etc/haproxy/certs/ mode http reqadd X-Forwarded-Proto:\ https #option httpclose option forwardfor option httplog option dontlognull acl letsencrypt-acl path_beg /.well-known/acme-challenge/ use_backend letsencrypt-backend if letsencrypt-acl default_backend www-backend backend www-backend #option httpclose option forwardfor http-request set-header X-Forwarder-Port %[dst_port] redirect scheme https if !{ ssl_fc } server www-1 10.0.0.2:80 check backend letsencrypt-backend server letsencrypt 127.0.0.1:54321 listen stats *:1936 stats enable stats uri / stats hide-version stats auth username:password
Comme vous l'avez certainement remarqué, nous allons chercher les certificats dans le répertoire /etc/haproxy/certs/ qui n'éxiste pas encore.
Créons le
sudo mkdir /etc/haproxy/certs sudo chmod -R go-rwx /etc/haproxy/certs
Voilà pour HAProxy, nous verrons ensuite dans l'interface de statistics si tout est ok .
Letsencrypt et certbot
Pour la génération des certificats, nous allons utiliser l'outil certbot .
Pour l'installer nous allons devoir activer ( si ce n'est déjà fait ) le dépôt BlackPorts de Debian
sudo echo 'deb http://ftp.debian.org/debian jessie-backports main' > /etc/apt/sources.list.d/blackports.list apt update
On install certbot depuis ce dépôt
apt-get install certbot -t jessie-backports
Afin d'initialiser letsencrypt, nous allons le lancer une première fois
certbot certonly
Celà ne va pas aboutir, c'est normal, c'est juste pour qu'il crée les répertoires de base.
Celà ne vous a pas échappé, le port 80 est déjà utilisé par HAProxy, et ne peu donc être utilisé par Letsencrypt pour générer ou renouveler un certificat.
L'idée de couper HAProxy le temps de cette action est inconcevable, nous allons simplement utiliser un autre port. ( ici : 54321 )
Pour ne pas avoir à le spécifier à chaque commande, nous allons mettre en place un petit fichier de configuration pour définir quelques paramètres qui ne changent pas . ( ce qui va nous permettre de raccourcir nos lignes de commandes plus tard ).
sudo cd /etc/letsencrypt/ && vim cli.ini
Voici le contenu
# This is an example of the kind of things you can do in a configuration file. # All flags used by the client can be configured here. Run Certbot with # "--help" to learn more about the available options. # Use a 4096 bit RSA key instead of 2048 rsa-key-size = 4096 # Uncomment and update to register with the specified e-mail address email = [email protected] # Uncomment to use a text interface instead of ncurses text = True standalone-supported-challenges = http-01
Afin de tester l'ensemble de notre configuration, nous allons générer notre premier certificat ( manuellement ) .
Imaginons que le domaine soit "lindev.fr" :
sudo certbot certonly --standalone --http-01-port 54321 -d lindev.fr -d www.lindev.fr sudo cat /etc/letsencrypt/live/lindev.fr/fullchain.pem /etc/letsencrypt/live/lindev.fr/privkey.pem > /etc/haproxy/certs/lindev.fr.pem sudo service haproxy restart
Bien entendu, il faut que le nom de domaine "lindev.fr" et "www.lindev.fr" pointent sur l'ip de mon serveur ( HAProxy ).
HAProxy est maintenant prêt . Je suppose que le backend ( Nginx ou Apache ) est déjà configuré avec un vhost écoutant sur le port 80 pour le domaine "lindev.fr et www.lindev.fr".
Et voilà le résultat.
Renouvellement automatique
Nous allons maintenant faire en sorte que les certificats LetsEncrypt se régénèrent automatiquement avant expiration et ce tant qu'à faire sans intervention nécessaire d'une admin sys sur le serveur HAProxy.
Le script
Pour celà j'ai concocter un petit script batch générique qui fait très bien le travail.
sudo mkdir /root/scripts & cd /root/scripts vim /root/scripts/renew_all.sh
Le voici
#!/bin/bash #Configuration variables certbot_bin="/usr/bin/certbot" haproxy_pem_path="/etc/haproxy/certs" http_01_port='54321' exp_limit=30 #Then, create domain.pem containing fullchain et privkey for haproxy for domainconf in $(ls /etc/letsencrypt/renewal/); do domain=${domainconf%.conf} cert_file="${haproxy_pem_path}/${domain}.pem" exp=$(date -d "`openssl x509 -in $cert_file -text -noout|grep "Not After"|cut -c 25-`" +%s) datenow=$(date -d "now" +%s) days_exp=$(echo \( $exp - $datenow \) / 86400 |bc) if [ "$days_exp" -gt "$exp_limit" ] ; then echo "[${domain}] : The certificate is up to date, no need for renewal ($days_exp days left)." #exit 0; else echo "The certificate for $domain is about to expire soon. Starting Let's Encrypt (HAProxy:$http_01_port) renewal script..." $certbot_bin certonly --standalone --renew-by-default --http-01-port $http_01_port -d ${domain} -d www.${domain} cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > ${haproxy_pem_path}/${domain}.pem fi done # At the end, reload haproxy echo "$(date +%c) Reload haproxy" /usr/sbin/service haproxy reload
Les deux seuls paramètres que vous pourriez vouloir changer sont :
- http_01_port='54321'
- exp_limit=30
A savoir, le port utiliser pour la génération des certificats, et la limite (en jours) à partir de laquelle vous souhaitez régénérer un certificat .
sudo chmod +x /root/scripts/renew_all.sh sudo -s crontab -e
On ajoute cette ligne dans la crontab ( afin de vérifier chaque jour les certificats à régénérer )
@daily /bin/bash /root/scripts/renew_all.sh 2>&1 | mail -s "[SSL Lindev] Vérification des certificats SSL HAProxy" [email protected]
Et voilà chaque jour, le script sera exécuté et vous recevrez un petit mail résumant le temps restant pour chaque certificat et potentiellement les logs de régénération s'il y en a eu.
Nouveau domaine à gérer
Imaginons que vous souhaitez gérer un autre nom de domaine, vous devrez la première fois créer le nouveau certificat.
Voici un petit script pour vous simplifier la vie.
sudo vim /root/script/new_cert.sh
le voici
#!/bin/bash #Configuration variables certbot_bin="/usr/bin/certbot" haproxy_pem_path="/etc/haproxy/certs" http_01_port='54321' domain=$1 $certbot_bin certonly --standalone --renew-by-default --http-01-port $http_01_port -d ${domain} -d www.${domain} cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > ${haproxy_pem_path}/${domain}.pem # At the end, reload haproxy echo "$(date +%c) Reload haproxy" service haproxy restart
On le rend exécutable
sudo chmod +x /root/scripts/new_cert.sh
A partir de là pour générer un nouveau certificat (qui sera par la suite automatiquement renouvelé), il vous suffit (en root) d'executer ce script comme ceci
cd /root/scripts ./new_cert.sh mon_nouveau_domaine.com
Et voilà , le certificat sera généré, renouvelé automatiquement et pris en charge par HAProxy .
Conclusion
Nous avons mis en place notre front (HAProxy) qui se charge de la partie SSL puis redirige les flus vers un ou plusieurs backends .
L'avantage de cette configuration et la simplicité de gestion des certificats, même avec plusieurs backends physiquement différents ( load balancing, différents services etc ... ).
Prochaine étape intercaler entre HAProxy et le backend, un serveur de cache ( Varnish ) .
Dans un prochain billet...
N'hésitez pas à commenter.
Ch.