Dans cet article, nous allons parler de la mise en place dans un environnement de test d'une sécurité avec keepalived.
Comme je me suis lancé de manière un peu plus sérieuse dans l'auto-hébergement (merci Proxmox), j'ai également besoin d'avoir une certaine forme de redondance sur des éléments un peu critiques, comme mon reverse proxy, qui tient et gère l'intégralité des certificats et sert de point d'entrée aux différents sites accessibles depuis le web (comme ce blog, que vous lisez).
Lorsque je dois faire une maintenance longue dessus, qui signifie une coupure complète de l'ensemble des sites et services liés, je souhaite avoir un second reverse proxy qui servira de point d'entrée le temps nécessaire, ou si un crash arrive sur un élément clé (par exemple nginx qui s'arrête).
Dans une configuration keepalived, une VIP (Virtual IP) est crée. Cette adresse est basculée d'un nœud à l'autre lorsque l'un ne réponds pas, en utilisant un système de priorité et de poids (le poids le plus lourd l'emporte et prend la VIP pour lui). C'est vers cette VIP que les requêtes web devront être redirigées au niveau du firewall, via le PAT.
Je connaissais de par mes précédents emplois keepalived, j'avais déjà rapidement jeté un œil dessus, mais jamais je n'avais eu l'occasion d'en mettre en place un. Et bien ça tombe bien, puisqu'on va le faire maintenant.
J'ai créé 2 conteneurs dédiés à du nginx, nommés respectivement nginx-ha-1 et nginx-ha-2.
Sur chaque serveur, j'ai installé un serveur web (j'ai choisi nginx, vous ne vous en doutiez pas, hein ?)
Sur nginx-ha-1 j'ai créé un site qui affiche :
NGINX1 coucou et bienvenue sur nginx1
Et sur nginx-ha-2 j'ai créé un site tout pareil qui affiche :
NGINX2 coucou et bienvenue sur nginx2
Chacun est accessible sur son adresse IP. Tout est prêt, il n'y a plus qu'à !
C'est d'une simplicité classique (sur la partie Debian du moins) :
apt update apt install keepalived
Le tout est installé dans /etc/keepalived et un fichier de configuration d'exemple est fourni (nous ne l'utiliserons pas).
Voici comment est configuré mon keepalived (fichier /etc/keepalived/keepalived.conf) et les commentaires liés :
global_defs {
router_id nginx-ha-1-MASTER
}
router_id correspond au nom du noeud au sein de l'instance keepalived. L'information est facultative (le hostname est pris) mais je voulais la présenter
vrrp_script chk_nginx {
script "/usr/bin/pgrep nginx"
interval 2
weight 20
}
Il s'agit d'un script de vérification de nginx. Je lance un pgrep (recherche de process) de nginx, à une intervalle de 2 secondes. Si le code de retour est 0 (succès) alors j'ajoute 20 au poids actuel de cette instance de keepalived.
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1357
}
virtual_ipaddress {
192.168.0.254/24
}
unicast_src_ip 192.168.0.116
unicast_peer {
192.168.0.145
}
La définition de notre instance, nommée VI_1
state : MASTER ou BACKUP. Master signifie qu'il est défini à son lancement comme le propriétaire de la VIP, Backup qu'il est en secours et ne viendra donc pas monter l'interface.
interface : l'interface sur laquelle notre keepalived fonctionnera
virtual_router_id : un identifiant commun à chaque pair dans le cluster keepalived
priority : le poids initial fourni à ce noeud du cluster
advert_int : le temps en secondes entre chaque annonce aux autres noeud
auth_type : le mode d'authentification entre les noeuds
auth_pass : un mot de passe commun pour valider la communication
virtual_ipaddress : la (ou les) fameuse VIP qui sera portée entre chaque noeud
unicast_src_ip : l'adresse source à partir de laquelle vous envoyez des messages aux autres pairs keepalived
unicast_peer : les noeuds vers lesquels communiquer l'état du cluster
track_script {
chk_nginx
}
}
track_script : Une exécution de script (ici chk_nginx, défini plus haut) qui est active
Voici donc notre premier fichier de conf.
Nous allons le recopier sur nginx-ha-2 et y mettre quelques modifications. Je vous mets uniquement les informations modifiées :
global_defs {
router_id nginx-ha-2-BACKUP
}
Nous remplaçons notre nom afin de correspondre à l'hôte actuel.
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 95
advert_int 1
authentication {
auth_type PASS
auth_pass 1357
}
virtual_ipaddress {
192.168.0.254/24
}
unicast_src_ip 192.168.0.145
unicast_peer {
192.168.0.116
}
state : On passe sur BACKUP afin de dire que nous sommes sur le "secours"
priority : on baisse le poids afin de faire en sorte que le MASTER soit le plus haut en temps normal.
unicast_src_ip : on pense à changer l'adresse pour la sienne
unicast_peer : idem, on met l'adresse du ou des pairs concernés
Nous lançons les services keepalived sur les 2 serveurs.
Sur nginx-ha-1 (le noeud maître) nous voyons les informations suivantes dans les status du service:
nginx-ha-1 systemd[1]: Started keepalived.service - Keepalive Daemon (LVS and VRRP).
nginx-ha-1 Keepalived_vrrp[39510]: (VI_1) Entering BACKUP STATE (init)
nginx-ha-1 Keepalived_vrrp[39510]: VRRP_Script(chk_nginx) succeeded
nginx-ha-1 Keepalived_vrrp[39510]: (VI_1) Changing effective priority from 100 to 120
nginx-ha-1 Keepalived_vrrp[39510]: (VI_1) Entering MASTER STATE
Sur nginx-ha-2 (le noeud backup) :
nginx-ha-2 Keepalived[38999]: Startup complete
nginx-ha-2 systemd[1]: Started keepalived.service - Keepalive Daemon (LVS and VRRP).
nginx-ha-2 Keepalived_vrrp[39000]: (VI_1) Entering BACKUP STATE (init)
nginx-ha-2 Keepalived_vrrp[39000]: VRRP_Script(chk_nginx) succeeded
nginx-ha-2 Keepalived_vrrp[39000]: (VI_1) Changing effective priority from 95 to 115
Une fois que le cluster keepalived est monté, nous pouvons utiliser la VIP. Dans mon navigateur, lorsque je saisis http://192.168.0.254 , voici ce que j'obtiens :
Nous pouvons le simuler en arrêtant le service keepalived dessus et observer le comportement sur le serveur nginx-ha-2.
La seconde après sa coupure, le nginx-ha-1 ne communique plus d'information via VRRP. Une nouvelle élection entre les membres restants (ici, nginx-ha-2) se fait pour savoir qui a le plus grand poids, et devenir MASTER. L'adresse de VIP passe donc sous la gestion de nginx-ha-2.
Si nous rafraîchissons la page, nous voyons que l'IP renvoie vers le second serveur web désormais.
Et si nous réactivons le service keepalived sur nginx-ha-1, nous voyons que nginx-ha-2 n'a plus le plus haut poids et deviens à nouveau BACKUP.
nginx-ha-2 Keepalived_vrrp[39000]: (VI_1) Master received advert from 192.168.0.116 with higher priority 120, ours 115
nginx-ha-2 Keepalived_vrrp[39000]: (VI_1) Entering BACKUP STATE
La VIP est de nouveau portée par nginx-ha-1.
Imaginons que vous faites une maintenance sur votre service nginx sur nginx-ha-1. Pour ça, vous devez le couper pendant plusieurs minutes, ou même heures. Keepalived tourne toujours alors que se passera-t-il niveau cluster ? C'est à ce moment que le check_script rentre en compte.
Nous avons vu que lors de l'initialisation, le script était passé, et ajoutait une valeur au poids actuel du serveur. Et bien coupons nginx sur nginx-ha-1 et regardons le comportement.
nginx-ha-1 Keepalived_vrrp[39694]: Script `chk_nginx` now returning 1
nginx-ha-1 Keepalived_vrrp[39694]: VRRP_Script(chk_nginx) failed (exited with status 1)
nginx-ha-1 Keepalived_vrrp[39694]: (VI_1) Changing effective priority from 120 to 100
nginx-ha-1 Keepalived_vrrp[39694]: (VI_1) Master received advert from 192.168.0.145 with higher priority 115, ours 100
nginx-ha-1 Keepalived_vrrp[39694]: (VI_1) Entering BACKUP STATE
Le code de retour du check étant une erreur (code non 0), la valeur est retirée du poids, et à l'annonce suivante, le serveur deviens donc minoritaire. L'IP est désormais portée par nginx-ha-2.
En tout et pour tout, la coupure aura duré moins de 4 secondes, 2 secondes d'intervalle pour l'exécution du script, 1 seconde pour l'annonce du nouveau MASTER et la migration d'IP.
Maintenant, plus qu'à transformer tout ça sur la prod, et synchroniser l'ensemble des VHOSTS nginx !
Mais ça sera le cadre d'un autre article :)