Dans cet article, nous allons parler de la mise en place dans un environnement de test d'une sécurité avec keepalived.

Pourquoi ?

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.

Préparation de l'environnement de test

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'à !

Installation et configuration de keepalived

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 :

Sur nginx-ha-1

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 :

Sur nginx-ha-2

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

Lancement des services et vérification

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 :

Screenshot_20231129_130042

Que se passe-t-il si le serveur complet nginx-ha-1 tombe ?

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.

Screenshot_20231129_130031

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.

Que se passe-t-il si le service NGINX tombe ?

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

Ajouter un commentaire

Article précédent Article suivant