lindev.fr

Aller au contenu | Aller au menu | Aller à la recherche

Mot-clé - certificats

Fil des billets - Fil des commentaires

28 déc. 2016

HAProxy LetsEncrypt et Varnish en front .. rien que ça

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 ).

network_diagram_user_haproxy_varnish_apache.png

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. Capture_d_e_cran_2017-01-03_a__21.15.37.png

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.

07 avr. 2010

Nginx - Vhost - php - perl - ssl

banniere-nginx.png
Bon , le billet qui sert de pont de départ ... pas toujours évident sur un blog de s'y retrouver .. sans sommaire ...
Voici donc le sommaire concernant les billets de Nginx

Installation , et utilisation de base

Installer Nginx manuellement , options de compilation , découvrez les emplacements des différents fichiers de configuration , démarrage automatique , options de base ect ...

Gérer les pages PHP et PERL

Nginx seul c'est bien , mais pouvoir l'utiliser avec php perl et .. tant d'autres , c'est mieux , dans ce billet , est éxpliqué la mise en place de php et perl en fast-cgi , et la liaison avec nginx .

SSL , vhost , et autres petites options

Mise en place de pages sécurisées par ssl , création de certificats , redirection automatique , mod_rewrite , compatibilité avec certains framework qui ont besoin du mod rewrite commezend framework , symphony , etc ...

06 avr. 2010

Nginx , SSL et plus encore ..

logo_openssl.gifVoici le troisième volet sur Nginx et son utilisation .
Nous allons voir aujourd'hui la mise en place d'un certificat pour une connexion sécurisé SSL via OpenSSL , mais aussi un passage sur les réécritures d'url , en fonction d'une classe d'adresse IP ,ou la mise en place de réécritures pour l'utilisation d'un framework tel que le ZF , Sympho ou Autres ...
La mise en cache des données statiques ...
Etc etc ..

Open SSL

Il faut dans un premier temps générer les certificats auto-signés , pour celà il vous faudra installer openssl

apt-get install openssl

Certificats

Ensuite , nous allons générer un certificat , plaçons nous dans un le répertoire de conf de Nginx .

mkdir /etc/nginx/certificats
cd /etc/nginx/certificats
openssl req -new -x509 -nodes -out mon-site.fr.crt -keyout mon-site.fr.key

Vhosts

Nous voilà avec de magnifiques certificats auto-signés , nous allons maintenant modifier notre vhost pour qu'il soit dorénavant en https .
Nous allons donc changer le
listen 80;
par
listen 443;
Et enfin , voici les quelques lignes à ajouter pour utiliser nos certificats fraichement crées .

ssl on;
ssl_certificate /etc/nginx/certificats/mon-site.fr.crt;
ssl_certificate_key /etc/nginx/certificats/mon-site.fr.key;
ssl_session_timeout 5m;

Restart Nginx

Et comme d'habitude , il faut recharger les fichiers de conf de nginx

/usr/local/nginx/sbin/nginx -s reload

info.pngsources de la doc

Redirection 301

Il est parfois ( souvent ) utile de faire des redirection 301 pour une histoire de référencement ou autres , voici la conf du vhost à rediriger :

server {
  listen 80;
  server_name example.com;
  rewrite ^/(.*) http://www.example.com/$1 permanent;
}

Pour mettre un code 302 , il faut juste changer permanent par redirect

info.pngDocumentation du module

Redirection interne / externe

Prenons l'exemple d'un site qui de l'extérieur est accessible en ssl , et de façon standard en local , voilà comment faire une redirection automatique :

    if ($remote_addr !~  "^(10\.0\.0\.\d+)"){
            rewrite ^/(.*)$ https://siteA.mon-site.fr permanent;
    }

Chaque client dont l'adresse IP n'est pas 10.0.0.0-255 se verra obligé de passer par une connexion sécurisée .

Redirection pour framework

Beaucoup de framework tel que le zend-framework , symphony et bien d'autres utilisent des règles de redirections ( via par exemple le mod_rewrite d'apache ) pour rediriger toutes URL différentes de média statiques vers le bootstrap ( index.php ) , qui lui s'occupe de lancer le chef d'orchestre .

Voilà le code à ajouter dans votre vhost pour effectuer cette redirection .

location /  {
    index index.php;

    if (-f $request_filename) {
        access_log off;
        expires max;
    }

    if ($request_filename !~ "\.(js|ico|gif|jpg|jpeg|png|css|txt|pdf|doc|odt|xls|swf|mp3)$") {
        rewrite ^(.*) /index.php last;
    }    

Cache

Nginx peut aussi gérer du cache , pour servir plus rapidement , les données dites statiques , voilà une façon de l'activer :

location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico)$ {
            access_log        off;
            expires           30d;
        }

Une autre méthode , ( qui est présente ci-dessus )

if (-f $request_filename) {
    access_log        off;
    expires max;
    }

warning.pngMais attention , cette deuxiéme méthode , n'est valable que si vous utilisez un framework , qui joue avec les réécritures d'url , car le script ci-dessus se traduit comme ceci :

Si le fichier de la requête existe , alors on le met/ utilise en cache .

Etant donne que l'utilisation de réécriture d'url , implique que toutes les requêtes utilisateurs , sont "factices" , elles ne représentent pas réellement la structure de fichiers/répertoires de votre site , sauf , le fichiers statiques tels que les images , css , js etc ... mais les chargements sont fait automatiquement .
Voilà pourquoi cette deuxième méthode ne fonctionne qu'avec un FW qui utilise la réécriture d'url .

Conclusion

Et bien cette fois nous y voilà , un serveur http complet , prêt à servir de nombreuses pages , à héberger n'importe quel framework , et tout ça avec de trés bonnes performances .
De nombreuses autres options / modules existes , je vous invite à les parcourir :
modules nginx

En espèrent que cette petite série de tuto vous a intéressé , n'hésitez pas à laisser un commentaire , ça fait toujours plaisir ,

Christophe.