Table des matières
CARP
Nous avons deux routeurs pour aller et venir vers/depuis le reste des Internets. Une machine (Brique Internet, NUC, serveur) ne peut avoir qu'un et un seul routeur de sortie configuré à un instant T. Donc un seul des routeurs de l'association. Et si l'on a besoin d'interrompre l'un des routeurs de l'association pour une maintenance ? Et si une panne survient (crash matériel ou logiciel) ? La redondance permise par les deux routeurs ne profitera pas aux machines des abonné-e-s hébergées dans notre baie : la machine perdra son accès Internet. C'est dommage.
Pour éviter cela, on peut utiliser CARP. Il s'agit d'un protocole réseau libre qui autorise plusieurs machines à partager une ou plusieurs adresses IP dites virtuelles. À un instant T, une seule machine du groupe possède l'adresse IP sur une de ses interfaces réseau et rend un service défini (ici le rôle de transférer les paquets IP entre une machine hébergée et Internet). Toutes les machines du groupe se surveillent entre elles et, si la machine qui possède l'IP virtuelle tombe (panne, maintenance), une élection a lieu et la machine gagnante s'attribue l'adresse IP en moins d'une seconde et assure le service par intérim. Cela permet la haute disponibilité : pour que le service ne soit plus rendu, il faut que l'intégralité des machines du groupe devienne hors service.
Niveau terminologie CARP, une machine qui possède l'adresse IP est nommée « master », toutes les autres sont nommées « backup ». Un « backup » peut devenir « master » et inversement.
Ucarp est une implémentation du protocole CARP tournant sous GNU/Linux. Nous l'utilisons sur l'infra.
Par défaut, ucarp a deux défauts :
- Pas de support d'IPv6 ;
- L'IP est assignée à la même interface que celle sur laquelle sont envoyés les battements de cœur qui permettent de vérifier que la machine « master » (celle qui assure le service, donc) n'est pas tombée. Or, chez ARN, nous attribuons un VLAN à chaque abonné qui héberge une ou plusieurs machines dans notre baie. Au niveau des routeurs, chaque VLAN est représenté par une interface réseau. Il nous faudrait donc autant de processus ucarp que nous avons de VLAN, ce qui est insensé. Sans compter que nous “polluerions” le VLAN de nos abonné-e-s avec du CARP qui ne les concerne finalement pas (ça doit fonctionner sans être visible).
Mais, ucarp a aussi un mode dans lequel il effectue toute la partie émission et réception des battements de cœur et exécute un script quand l'état change (script “up” - exécuté quand un backup devient master et devient donc la machine qui possède l'IP virtuelle, script “down” - quand le master perd l'adresse IP). Dans ce mode-là, l'émission d'ARP (IPv4) et de Neighbor Advertisements (IPv6) pour indiquer que les adresses IP (v4 et v6) virtuelles ont changé de machine physique sont à la charge des scripts. C'est ce mode-là que nous utilisons chez ARN.
Pour ceux et celles qui s'interrogent : les émissions sauvages (ne faisant pas suite à une demande explicite d'une machine) d'ARP et de Neighbor Advertisements sont ignorées par les systèmes d'exploitation sauf si l'adresse IP sur laquelle portent ces messages est déjà dans les tables ARP/NA (qui stockent, temporairement, la relation entre une adresse IP (v4 pour ARP, v6 pour NA ;) ) et une adresse MAC. Autrement dit :
- Si la machine d'un abonné est en train d'émettre sur le réseau quand le routeur « master » tombe en panne, elle corrigera ses tables ARP/NA avec la nouvelle adresse MAC et la transition d'un routeur à l'autre se fera en douceur (un ou deux paquets perdus, rien de grave) ;
- Si la machine d'un abonné n'est pas en train d'émettre au moment où le routeur « master » tombe en panne, elle ignorera les messages ARP et NA. Lorsqu'elle voudra émettre sur le réseau, elle exécutera une demande de résolution ARP ou neighbor solicitation. La machine qui aura pris le rôle de « master » répondra de manière tout à fait standard donc la machine d'un abonné acceptera la réponse.
Sur ce point, voir la description de « arp_accept » sur https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt :
Both replies and requests type gratuitous arp will trigger the ARP table to be updated, if this setting is on. If the ARP table already contains the IP address of the gratuitous arp frame, the arp table will be updated regardless if this setting is on or off.
Une dernière chose : il faut bien comprendre que le transfert des paquets d'Internet vers la machine hébergée n'est pas du ressort de CARP. CARP s'occupe uniquement de redonder la sortie du réseau. :) Le paquet arrivera depuis le reste des Internets sur l'un ou l'autre des routeurs de l'association qui le transférera directement à la machine hébergée.
Notes concernant la topologie d'ARN
Ce que nous faisons :
- Sur chaque routeur de l'association, nous avons une interface réseau, configurée dans /etc/network/interfaces, qui représente le VLAN d'un abonné au service « hébergement de machines physiques ». Ce VLAN contient toutes les machines d'un abonné hébergées dans notre baie. Voir la section « IPv6 » ci-dessous pour avoir un exemple d'entrée pour un abonné dans /etc/network/interfaces.
- Nous avons également une interface réseau qui représente le VLAN de contrôle : c'est là que nous faisons circuler les battements de cœur CARP. Ce VLAN ne passe pas sur le lien direct entre nos machines (voir le câble bleu ici : Matériel) mais il remonte jusqu'au switch. L'idée étant de tester toute la chaîne jusqu'aux machines des abonnés soit nos deux routeurs et le switch. C'est aussi ce VLAN que l'on assigne aux admins ARN qui interviennent sur la baie. L'objectif étant d'avoir accès aux routeurs de l'association ainsi qu'à Internet (documentation…) même si l'opération de maintenance nécessite d'interrompre le fonctionnement de l'un des routeurs.
- Sur chacune de ces interfaces, nous attribuons une IPv4 « 169.254.43.1/24 » et une IPv6 « fe80::43:1 ». Ce sont des adresses IP locales au lien : elles ne peuvent pas être routées au-delà du segment réseau. On peut donc les réutiliser sur chaque interface, pour chacun des abonnés !
- Sur la machine d'un abonné, la configuration IP ressemble à ça :
auto eth0 iface eth0 inet static pre-up /sbin/ip link set $IFACE up pre-up /sbin/ip route add 169.254.43.1 dev $IFACE address <adresse_publique_attribuée_à_l'abonné>/32 gateway 169.254.43.1 iface eth0 inet6 static address <IPv6_dans_le_subnet_attribué_à_l'abonné>/56 gateway fe80::43:1 accept_ra 0 autoconf 0 dad-attempts 0
- L'utilisation IPv4 de 169.254.0.0/16 (link-local) peut entrer en conflit avec d'autres programmes qui prennent la main sur ce préfixe, comme dhcpcd ou avahi. Avec dhcpcd par exemple, le “via” (next-hop) est maltraité donc on n'a plus de routage en sortie. Bien penser à désactiver tout ce monde, qui de toutes façons ne servira à rien et donc au mieux générera juste du bruit.
Installation
Ucarp est packagé dans Debian GNU/Linux stable donc :
apt-get install ucarp
Lancement au boot
Ucarp est livré brut de fonderie, sans iniscript sysvinit ni unit systemd. Le plus simple est d'utiliser une unit systemd.
Créons l'unit dans /etc/systemd/system/ucarp.service. Son contenu est disponible sur notre dépôt git.
Explications des paramètres que nous passons à ucarp :
- -z : demande à ucarp d'exécuter le script “down” quand on stoppe ucarp. Chez ARN, stopper CARP, ça indique très certainement qu'un reboot a été lancé et que systemd stoppe donc tous les services et que, au final, la machine exécutant CARP ne pourra pas remplir son rôle. Donc si elle est « master » à ce moment-là, autant purger les IPs sur ses interfaces et gagner du temps de transition.
- -i : le nom de l'interface sur laquelle seront émis les battements de cœur.
- -s : l'adresse IP source réelle à utiliser pour émettre nos battements de cœur.
- -v <VHID> : il s'agit du Virtual Host ID, c'est-à-dire un numéro unique sur un réseau qui permet d'identifier un groupe de redondance CARP sur ce réseau. Les valeurs possibles sont comprises dans l'intervalle [1,255]. C'est cela qui permet d'avoir plusieurs groupes de machines utilisant CARP : le vhid sera différent entre chaque groupe.
- -p <mdp> : un mot de passe afin que des machines intrus ne rejoignent pas le groupe de machines CARP.
- -a : l'IPv4 virtuelle à utiliser.
- -x : un paramètre additionnel qui sera transféré aux scripts. Chez ARN, nous utilisons cette méthode pour faire passer l'IPv6 virtuelle à utiliser.
- -P : active la préemption : cette machine se présumera « master » du groupe CARP, par défaut.
- -k <advskew> : permet d'introduire un biais pour forcer le choix lors de l'élection du maître du groupe de redondance. Plus la valeur de ce paramètre est élevée, moindre sont les chances de cette machine de devenir le maître. On réaffirme ici, en complément de « -P » que cette machine sera la patronne.
- -n : on n'exécute pas le script “down” quand ucarp est lancé et que nous sommes une des machines « backup ».
- -u </chemin/vers/script/up> : le script à exécuter lorsque l'état devient « master ».
- -d </chemin/vers/script down> : le script à exécuter lorsque l'état devient « backup ».
Activons l'unit au boot :
sudo systemctl daemon-reload sudo systemctl enable ucarp
Ça, c'était sur notre premier routeur. Il sera toujours « master » donc il possédera toujours les adresses IP virtuelles donc ça sera lui qui rendra le service de transférer les paquets IP entre la machine d'un abonné et Internet. Sur les routeurs supplémentaires, on utilise la même unit systemd mais on pense à :
- Changer l'IPv4 réelle utilisée pour émettre sur le réseau ;
- Supprimer la préemption (-P) ;
- Changer la valeur de l'advskew pour une valeur beaucoup plus grande (100, par exemple).
Scripts
Voici les scripts que nous utilisons sur l'infra.
Ces scripts nécessitent les packages suivants : iproute2, iputils-arping (permet l'envoi de réponses ARP non sollicitées) et thc-ipv6 (permet l'envoi de Neighbor Advertisements non sollicités).
Concernant la variable « IFLIST » :
- VLANIDREGEX nous permet de trouver facilement et sans erreur possible toutes les interfaces réseau qui représentent le VLAN d'un-e abonné-e car tous les VLAN ID que nous attribuons aux abonné-e-s sont compris entre 500 et 599. Voir L2.
- La regex utilisée avec grep sélectionne le nom des interfaces réseau que l'on reconnaît grâce à leur nom : h-<pseudo_abonné>, « h- » comme « hébergement ». iproute2 affiche les noms d'interface sous la forme : <nom_demandé_par_l'admin>@<interface_physique>. Exemple : h-glucas@eth0 est le nom de l'interface réseau qui représente le VLAN de l'auteur de cette doc' ;) . iproute2 ne reconnaît pas ce format pour le nom de l'interface dans les commandes que nous sommes amenés à lui filer. Il faut donc conserver uniquement le nom, sans le « @<interface_physique> ». C'est tout l'objet de la regex look-ahead.
Ces scripts sont disponibles sur notre dépôt git :
- vip-down.sh : la machine devient « backup » et perd l'IP virtuelle
- vip-up.sh : la machine devient « master » et possède l'IP virtuelle
Le script up nécessite les logiciels arping et atk6-fake_advertise6. Avec Debian, ils sont dans les paquets « iputils-arping » et « thc-ipv6 ».
Subtilité IPv6
En IPv4, la détection d'une adresse IP dupliquée sur le réseau est laissée à la charge d'un script (que l'on n'a pas dans Debian, d'ailleurs). En IPv6, cette détection est implémentée directement dans le noyau. Conclusion : lors d'un changement de machine physique, l'IPv6 virtuelle est flaggée « dadfailed » et est inutilisable pendant de longues minutes. On perd donc tout l'intérêt de CARP !
Il faut donc désactiver cette détection de collisions en passant la valeur de /proc/sys/net/ipv6/conf/<nom_interface>/accept_dad à 0. Côté routeur ARN et côté machine hébergée !
Pour le côté machine hébergée, voir « Notes concernant la topologie d'ARN » ci-dessus
Côté ARN, nous faisons ça dans /etc/network/interfaces, en même temps que la désactivation du traitement des Router Advertisements IPv6 (nos routeurs n'ont pas à prendre en compte ces paquets puisque les seuls routeurs légitimes de l'association sont… eux-mêmes). Exemple :
auto h-carpcontrol iface h-carpcontrol inet manual pre-up /sbin/ip l a link eth0 name $IFACE type vlan id 500 post-up /sbin/ip l s up dev $IFACE post-up /sbin/ip a a 169.254.43.253/24 scope link dev $IFACE post-up /sbin/ip r r <IP_attribué_à_l'abonné> dev $IFACE src 89.234.141.131 proto housing iface h-carpcontrol inet6 manual post-up /sbin/sysctl -w net.ipv6.conf.$IFACE.accept_ra=0 post-up /sbin/sysctl -w net.ipv6.conf.$IFACE.autoconf=0 post-up /sbin/sysctl -w net.ipv6.conf.$IFACE.accept_dad=0 post-up /sbin/ip -6 a a fe80::43:253/112 scope link dev $IFACE post-up /sbin/ip -6 r r <subnet_attribué_à_l'abonné> dev $IFACE src 2a00:5881:8100::131 proto housing post-down /sbin/ip l d $IFACE
Notes :
- La ligne qui fait le taff est « post-up /sbin/sysctl -w net.ipv6.conf.$IFACE.accept_dad=0 ».
- « proto housing » permet de retrouver toutes les routes concernant l'hébergement de machines physiques que nous proposons avec un simple « ip r sh proto housing ». Super pratique lors de séances debug. Il faut déclarer ce nom, « housing », dans /etc/iproute2/rt_protos avant de pouvoir l'utiliser.
- « 89.234.141.131 » et « 2a00:5881:8100::131 » sont les IP de notre routeur. Utiliser « src » force le routeur à causer aux machines hébergées avec son IP publique et n'ont pas avec une IP link-locale. C'est plus propre. Et, lors de debug, on sait à quoi s'attendre (on rend le comportement déterministe). Voir routage
À faire lors de l'installation d'une machine d'un-e abonné-e dans notre baie
- Créer l'interface correspondante sur chaque routeur (dans /etc/network/interfaces puis ifup).
- Exécuter la commande suivante sur hwhost1 (le premier routeur de l'asso qui est donc master). Cela permet à cette machine de passer « backup » puis de redevenir « master » (compte tenu de « -P » et d'un score advskew biaisé ;) ). Le script “up” sera donc exécuté et l'IP virtuelle sera également affectée à la nouvelle interface réseau correspondant au VLAN du/de la nouvel-le abonné-e.
sudo kill -USR2 `pidof ucarp`