Routeur haute disponibilité gérant plusieurs liens internet

From Ultrabug's Wiki

Jump to: navigation, search

Contents

Introduction

Cet article a pour but d'expliquer comment mettre en place un routeur à haute disponibilité sous Linux, exploitant plusieurs connexions Internet.

Le but est double :

  • Offrir un débit plus grand au réseau LAN en utilisant la bande passante de deux connexions internet ou plus.
  • Redonder ce routeur de façon à ce que si une panne survenait, une autre machine puisse prendre le relai sans interruption de la connectivité.

Nous détaillerons dans un premier temps la mise en place du routeur et ses deux liens internet puis nous expliquerons comment le rendonder avec Heartbeat.


Prérequis

  • Disposer d'au moins deux connexions Internet
  • Disposer de deux machines, identiques de préférence
  • Chaque machine doit avoir au moins 3 interfaces réseau mais il est préférable d'en utiliser 4. C'est d'ailleurs sur cette base que nous prendrons nos exemples.

NB : les exemples de code, fichiers init et commandes sont basées sur la distribution Gentoo. Pensez à les adapter à votre distribution.


Packages

  • iproute2 : commandes réseau avancées
  • iptables : pour gérer le NAT et assurer un minimum de sécurité
  • heartbeat : gestion de la tolérance de panne
  • ethtool (optionnel) : peut être pratique pour la configuration avancée des cartes réseau
  • iptraf (optionnel) : statistiques et monitoring avancé de cartes réseaux
  • cpufrequtils (optionnel) : régulation de l'utilisation du CPU
  • freeipmi (optionnel) : monitoring matériel (température, redondance électrique, intrusion chassis)


Principe technique

Nous disposons de 4 cartes réseau :

  • eth0 : interface reliée au LAN sur le réseau 192.168.0.0/24
  • eth1 : interface reliée directement au deuxième routeur de backup sur le réseau 192.168.255.0/24
  • eth2 : interface reliée à la passerelle du 1er lien internet sur le réseau 81.81.81.1/29
  • eth3 : interface reliée à la passerelle du 2ème lien internet sur le réseau 82.82.82.1/29


Note : rien ne nous empèche d'étendre le raisonnement à d'autres liens comme une interface pour une DMZ par exemple.

Notre routeur/firewall sera à stateful inspection, ce qui veut dire qu'il garde en mémoire une table des connexions établies, actives etc. appelée connection tracking. En effet, la table de routage n'étant pas statique, il est primordial que le routeur puisse associer des paquets revenant et sortant par des interfaces différentes à une connexion.

Exemple

Prenons maintenant l'exemple d'une connexion HTTP vers le site 1000mercis.com émise depuis un PC du réseau LAN :

  • CLIENT : demande de nouvelle connexion au serveur web
  • ROUTEUR : réception du paquet sur eth0
    • filtrage de la connexion, si ce type de connexion est autorisée, nous acceptons de la forward vers le WAN
    • choix de l'interface WAN via load balancing -> WAN1 est choisie
    • sortie de la demande de connexion via WAN1, masquage de l'adresses IP locale du client par l'IP WAN1 du routeur
  • SERVEUR : réception de la connexion, réponse
  • ROUTEUR : réception de la réponse pour le CLIENT
    • il s'agit d'une réponse à une connexion dont le routeur connait l'origine
    • renvoi de la réponse à la machine CLIENT sur le réseau LAN en changeant l'adresse IP destination pour qu'elle soit l'IP locale du CLIENT


Reprenons alors cet exemple en utilisant les adresses IP :

  • CLIENT : 192.168.0.12 -> 1000mercis.com:http via 192.168.0.1
  • ROUTEUR : incoming sur eth0, @Source = 192.168.0.12 / @Destination = 1000mercis.com PORT http
    • IPTABLES FILTER table : OK, forward
    • consultation de la table de routage, 2 routes possibles. Choix de la route par algorithme de load balancing. -> WAN1 est choisie
    • IPTABLES NAT table : le choix du routage ayant été fait (POSTROUTING), je modifie le paquet :
      • sortie via eth2, @Source = 81.81.81.2 / @Destination = 1000mercis.com PORT http via 81.81.81.1
  • SERVEUR : 1000mercis.com -> 81.81.81.2
  • ROUTEUR : connexion connue. forward depuis eth2 de 1000mercis.com -> 192.168.0.12 via eth0


Considérations techniques importantes

Il est important de comprendre que pour les machines du réseau, le NAT en sortie doit être statique et correspondre à une et une seule des adresses IP WAN. Une même connexion réseau ne peut en effet pas utiliser deux adresses IP sources différentes. De plus, il serait impossible d'associer un paquet à une connexion et d'en garantir l'intégrité. Des paquets dont l'IP source est celle de l'interface WAN1 seront donc routés via l'interface WAN2 et inversement.


  • Une machine LAN doit donc être représentée (NATée) par UNE des adresses IP publiques disponibles, et ce, quelquesoit l'interface de sortie !
  • Un paquet sortant avec l'IP source de WAN1 par l'interface WAN2, reviendra forcément par WAN1 !
  • Le load balancing est donc uniquement réalisé en sortie. Attention donc à vos débits entrants car plus vous NATez de machines sur l'IP d'une interface, plus l'ensemble des réponses de connexion seront concentrées sur cette interface. Il convient donc d'essayer de répartir vos attributions en fonction du trafic et de la capacité de vos liens.


Un paquet doit donc pouvoir être routé par une interface WAN avec une adresse IP source qui n'est pas la sienne. Il faut donc qu'il n'y ai pas de filtrage du coté du FAI de ce coté là.

  • Anti-spoofing : le routeur vérifie que l'IP source du paquet reçu est bien tenue par la machine émettrice.
  • uRPF : Unicast Reverse Path Forwarding de Cisco. Simplement dit, c'est le fait qu'un routeur accepte de router un paquet que s'il est sûr que c'est lui qui en recevra la réponse. [1]


Mise en place du routeur

Configuration du noyau

  • Je vous conseille de compiler les drivers de vos cartes réseau en dur dans le noyau !

J'ai eu de mauvaises surprises avec des drivers non stables d'Intel car ils ne prenaient pas en charge ce genre de routing. Faites donc attention aux drivers de vos cartes réseau !

Console:
      Device Drivers  --->
        [*] Network device support  --->


  • Ensuite, rendez-vous dans la partie Network du menuconfig
Console:
Networking  --->
  Networking support  --->
    Networking options  --->


  • Activez les fonctionnalités avancées de routage
Console:
      TCP/IP networking
        [*] IP: advanced router
        [*] IP: policy routing
        [*] IP: equal cost multipath


  • Activez, la gestion de la QoS si vous souhaitez faire du bandwidth shaping
Console:
      QoS and/or fair queueing  --->


  • Activez Iptables. Personnellement, je compile tout en dur pour ne manquer de rien et faire en sorte que mon noyau soit simplement dupliquable.
  • ATTENTION : vous DEVEZ activer le support lié au tracking des connexions : CONFIG_NF_CONNTRACK_IPV4 !
Console:
     Network packet filtering framework (Netfilter)  --->
        Core Netfilter Configuration  --->
        IP: Netfilter Configuration  --->


  • Enfin, pensons à l'environnement ! Il me parait censé d'activer le CPU Frequency Scaling sur nos machines afin que leurs CPU ne tournent pas à fond tout le temps pour rien. Ajustez les options pour votre processeur (ici Core2 Xeon)
Console:
      Power management options  --->
         [*] ACPI (Advanced Configuration and Power Interface) Support  --->
            <*>   Processor 
            <*>   Thermal Zone
         CPU Frequency scaling  --->
             [*] CPU Frequency scaling
             [ ]   Enable CPUfreq debugging
             <*>   CPU frequency translation statistics
             [*]     CPU frequency translation statistics details
                      Default CPUFreq governor (performance)  --->
             -*-    'performance' governor
             <*>   'powersave' governor
             <*>   'userspace' governor for userspace frequency scaling
             <*>   'ondemand' cpufreq policy governor
             <*>   'conservative' cpufreq governor
                       *** CPUFreq processor drivers ***
             <*>   ACPI Processor P-States driver


Packages

Procédons à quelques vérifications préalables, les commandes suivantes doivent fonctionner :

Console:
# iptables -L
# iptables -t nat -L
# iptables -t mangle -L

# ip route


Paramètres IP du noyau

  • Editez le fichier /etc/sysctl.conf :
Config File: /etc/sysctl.conf
# Enable packet forwarding
net.ipv4.ip_forward = 1

# Packet tuning
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 1677216
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.ip_default_ttl = 80

# Basic spoofing and icmp protection
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1


  • Pour prendre en compte ces modifications, faites simplement un sysctl -p

Paramètres de boot de la machine

NB : ces fichiers sont des fichiers pour Gentoo

  • Modifiez le fichier /etc/conf.d/rc :
Config File: /etc/conf.d/rc
RC_PLUG_SERVICES="!net.*"
RC_DOWN_INTERFACE="no"

Le but de ces lignes et de faire en sorte que les interfaces réseau ne soient pas démarées automatiquement au démarrage et que lorsque l'on les désactivent, elle ne soient pas totalement éteintes (ça sera utile plus tard).

  • Démarrage des interfaces et d'iptables au boot
Console:
# rc-update add net.eth0 default
# rc-update add net.eth1 default
# rc-update add net.eth2 default
# rc-update add net.eth3 default
# rc-update add iptables default


Configuration des interfaces

Adresses IP

  • Modifiez le fichier /etc/conf.d/net
Config File: /etc/conf.d/net
# LAN
config_eth0=(
        "192.168.0.1/24"
)
dns_domain_eth0="domain.com"
dns_servers_eth0="125.6.0.1 125.8.0.2"

# WAN 1
config_eth2=(
        "81.81.81.2/29"
)

# WAN 2
config_eth3=(
        "82.82.82.2/29"
)


  • Pour les gens qui n'utilisent pas Gentoo, voici l'équivalent en ligne de commandes
Console:
# ip addr add 192.168.0.1/24 dev eth0
# ip addr add 81.81.81.2/29 dev eth2
# ip addr add 82.82.82.2/29 dev eth3


Load balancing / Routing

  • Ajouter deux routes avec des poids en fonction de la vitesse de chaque connexion. Dans notre cas, les connexions sont identiques donc on un poids identique. Plus de détails
Console:
# ip route add default equalize \
                        nexthop via 81.81.81.1 dev eth2 weight 1 \
                        nexthop via 82.82.82.1 dev eth3 weight 1
  • Sous Gentoo, on peut éditer directement /etc/conf.d/net :
Config File: /etc/conf.d/net
routes_eth3=(
             "ip route add default equalize nexthop via 81.81.81.1 dev eth2 weight 1 nexthop via 82.82.82.1 dev eth3 weight 1"
)

NB : contrairement à ce que l'on peut trouver sur le net à ce propos, il est inutile dans notre cas d'utiliser les règles de routage de la commande ip rule car tout est réglé au niveau du NATing avec connection tracking.

  • Deux options existent pour modifier le comportement du load balancing lors de la commande ip route ci-dessus :
    • ip route add default equalize : le choix de la route est calculée par paquet. Une meme connexion verra ses paquet transiter par les deux liens internet.
    • ip route add default scope global : le choix de la route est fait une fois pour la connexion, tous ses paquets seront routés par le meme lien.

Iptables / NAT

  • Pour router tout le trafic LAN en utilisant l'IP de l'interface WAN 1
Console:
iptables -t nat -A POSTROUTING -o eth2 -s 192.168.0.0/24 -j SNAT --to-source 81.81.81.2
iptables -t nat -A POSTROUTING -o eth3 -s 192.168.0.0/24 -j SNAT --to-source 81.81.81.2
  • Pour sauvegarder les règles de firewalling / NATing : iptables-save

Haute disponibilité avec Heartbeat

Principe de fonctionnement

Deux machines vont devoir se redonder entre elles. Nous avons vu précédemment que le routeur fonctionne avec 3 cartes réseau, chaque carte réseau ayant une IP bien définie :

  • 192.168.0.1 pour le LAN
  • 81.81.81.2 pour le WAN 1
  • 82.82.82.2 pour le WAN 2

En cas de problème sur une machine, ces IP vont devoir être reprise par l'autre machine (noeud) du cluster. Nous considérerons donc ces IP comme virtuelles, ou flottantes. Elles seront donc gérées par heartbeat.


En pratique cela induit que nos 3 cartes réseau doivent être actives - dans le sens où elles sont UP - en permanence pour pouvoir leur affecter une adresse IP à la vollée en cas de besoin. De plus, nous voulons avoir accès à l'une ou l'autre des machines même si celle-ci n'est pas le noeud actif du cluster. Nous allons donc devoir attribuer une IP fixe à chaque routeur au niveau du LAN pour pouvoir communiquer avec eux.

  • 192.168.0.101 pour router1
  • 192.168.0.102 pour router2

Dorénavant, les cartes réseau eth2 et eth3 n'auront donc plus aucune IP d'attribuée par défaut car c'est justement le rôle de heartbeat de gérer cela. Il en va de même pour l'IP 192.168.0.1, qui ne sera tenue par aucune machine par défaut. En effet, quand les deux noeuds seront actifs, l'un d'entre eux sera appelé maître et l'autre esclave. Seul le maître détiendra les IP flottantes tandis que l'esclave restera en attente d'un problème éventuel de son homologue. Si le maître rencontre un problème quelconque, alors il lâchera les IP, et dans le même temps l'esclave deviendra maître en reprenant les IP flottantes. Il s'assurera de diffuser un broadcast ARP pour que les machines des différents réseaux mettent à jour leur cache ARP.


Heartbeat est là pour gérer tout cela et nous allons d'ailleurs lui dédier une interface réseau pour garantir la sécurité et la propreté des communications entre les deux noeuds : eth1.

Packages

  • Heartbeat : ici en version 2.0.8

Configuration

Les fichiers de configuration de heartbeat sont situés dans le dossier /etc/ha.d/ :

  • ha.cf : fichier de configuration principal
  • ha_logd.cf : configuration des logs
  • haresources : resources gérées par le cluster. Une resource correspond à un service que l'on souhaite monitorer et redonder
  • authkeys : clé de cryptage utilisée pour la communication entre les noeuds du cluster
  • resource.d : programmes spéciaux intégrés à heartbeat

Exemples de fichiers de configuration

ha.cf

  • Le fichier en lui-même est assez commenté. On notera que nous utilisons ici eth1 pour communiquer avec notre deuxième noeud.
  • Les deux machines (noeuds) de notre cluster s'appelent router1 et router2
Config File: ha.cf
# Enable the Cluster Manager
crm off

# Enable logging facility
use_logd on
conn_logd_time 60

# What interfaces to heartbeat over?
bcast eth1

# Enable compression
compression zlib

# When auto_failback is set to on once the master comes back online, it will take
# everything back from the slave.
auto_failback off

# How quickly Heartbeat should decide that a node in a cluster is dead
deadtime 2

# With some configurations, the network takes some time to start working after a reboot.
# This is a separate "deadtime" to handle that case. It should be at least twice the normal deadtime.
initdead 10

# keepalive: how many seconds between heartbeats
# This parameter also sets the time a ping is sent to the PingNodes of ipfail
keepalive 300ms

# Time in seconds before issuing a "late heartbeat" warning in the logs.
warntime 1

# Binary messages will be transmitted directly.
# This is more efficient since it avoids conversion between string and binary values.
msgfmt netstring

# Mandatory. Hostname of machine in cluster as described by uname -n.
node    router1 router2

# Some default uid, gid info, This is required for ipfail
apiauth default uid=nobody gid=cluster
apiauth ipfail uid=cluster
apiauth ping gid=nobody uid=nobody,cluster

# This is to fail over if the outbound network connection goes down.
respawn cluster /usr/lib/heartbeat/ipfail

# IP to ping to check connectivity
deadping 2
ping_group lan 192.168.0.XX 192.168.0.YY 192.168.0.ZZ


ha_logd.cf

Config File: ha_logd.cf
logfacility     local0


haresources

  • Ce fichier doit être STRICTEMENT identique entre les deux machines du noeud
  • Le premier champ correspond au nom du noeud "préféré" sur lequel le service est censé tourner. Cela sert en fait à différencier le noeud maître des noeuds esclaves.
  • Les champs d'après sont des appels aux programmes situés dans le dossier resource.d et /etc/init.d/.
  • Le programme IPaddr permet de gérer l'assignation dynamique d'une IP à une interface, ce qui est très pratique pour un routeur...
Config File: haresources
router1 IPaddr::192.168.0.1/24/eth0
router1 IPaddr::81.81.81.2/29/eth2
router1 IPaddr::82.82.82.2/29/eth3
router1 net.eth2
router1 net.eth3
router1 iptables
  • Dans cet exemple donc, lorsque le noeud maître sera pret, il executera dans l'ordre les étapes suivantes (gardez-les bien en tête) :
    • ajout de l'IP 192.168.0.1 sur l'interface eth0 + broadcast ARP
    • ajout de l'IP 81.81.81.2 sur l'interface eth2 + broadcast ARP
    • ajout de l'IP 82.82.82.2 sur l'interface eth3 + broadcast ARP
    • appel de l'init script /etc/init.d/net.eth2 start
    • appel de l'init script /etc/init.d/net.eth3 start
    • appel de l'init script /etc/init.d/iptables start

authkeys

Config File: authkeys
auth 1
1 sha1 myPassword


Modification du routeur

Préparation au mode passif

Nous allons déjà configurer le mode passif du routeur, donc quand il démarre et n'est pas encore en état de routeur actif. Dans cet état, la configuration IP est la suivante :

  • eth0 : 192.168.0.101 pour router1 // 192.168.0.102 pour router2
  • eth1 : 192.168.255.1 pour router1 // 192.168.255.2 pour router2
  • eth2 : aucune IP mais interface UP
  • eth3 : aucune IP mais interface UP
  • La route par défaut est l'adresse tenue par le routeur actif : 192.168.0.1


Cela donne dans le fichier /etc/conf.d/net de router1:

Config File: /etc/conf.d/net
#
# ETH 0 // LAN
#
config_eth0=(
        "192.168.0.101/24"
)
routes_eth0=(
        "default via 192.168.0.1"
)
dns_domain_eth0="domain.com"
dns_servers_eth0="125.6.0.1 125.8.0.2"

#
# ETH 1 // HA
#
config_eth1=(
        "192.168.255.1/24"
)

#
# ETH 2 // WAN 1
#
config_eth2=(
        "null"
)

#
# ETH 3 // WAN 2
#
config_eth3=(
        "null"
)


  • Pensons aussi à supprimer du démarrage les interfaces net.eth2, net.eth3 et iptables :
Console:
# rc-update del net.eth2
# rc-update del net.eth3
# rc-update del iptables


  • Ensuite, pensons à ne pas laisser l'IP forwarding activé lorsque nous ne sommes pas en mode routeur. On modifie la ligne suivante dans /etc/sysctl.conf :
Config File: /etc/sysctl.conf
net.ipv4.ip_forward = 0


  • Nous voulons cependant être sûrs que les interfaces WAN soient bien UP au démarrage. Utilisons pour cela la fonction prédéfinie preup() du fichier de conf /etc/conf.d/net :
    • NB : les utilisateurs d'autres distributions devront adapter cela
    • Lors de l'initialisation de la boucle locale, nous mettons UP nos cartes réseau. Ajouter à la fin du fichier :
Config File: /etc/conf.d/net
#
# Heartbeat
#
preup() {
        if [[ ${IFACE} == "lo" ]];
        then
                ifconfig eth2 up
                ifconfig eth3 up
        fi
}


Préparation au mode actif

  • Intéressons nous maintenant à ce qu'il se passe quand heartbeat active notre routeur et assigne les IP flottantes à nos interfaces. Le routeur passe alors en étant actif et va tenir les IP, mais nous devons alors gérer la mise en place des régles de routage et l'IP forwarding. Nous allons pour cela définir la route load balancée quand la dernière interface réseau WAN sera appelée, à savoir, eth3.
Code:
postup() {
        #
        # WAN 2 actions
        #
        if [[ ${IFACE} == "eth3" ]];
        then
                #
                # Delete temporary default route
                #
                ip route del default

                #
                # Add IP Forwarding
                #
                echo 1 > /proc/sys/net/ipv4/ip_forward

                #
                # Add load balanced route
                #
                ip route add default equalize \
                        nexthop via 81.81.81.1 dev eth2 weight 1 \
                        nexthop via 82.82.82.1 dev eth3 weight 1
        fi
}


  • A l'inverse, quand le routeur passera à nouveau en mode passif, nous devons le remettre en l'état d'origine.
Code:
postdown() {
        #
        # Restore a default route so the passive node still has access to internet
        #
        if [[ ${IFACE} == "eth3" ]];
        then
                #
                # Remove IP Forwarding
                #
                echo 0 > /proc/sys/net/ipv4/ip_forward

                #
                # Setup default route
                #
                ip route del default
                ip route add default via 192.168.0.1
        fi
}


Fichier final /etc/conf.d/net

Config File: /etc/conf.d/net
#
# ETH 0 // LAN
#
config_eth0=(
        "192.168.0.101/24"
)
routes_eth0=(
        "default via 192.168.0.1"
)
dns_domain_eth0="domain.com"
dns_servers_eth0="125.6.0.1 125.8.0.2"

#
# ETH 1 // HA
#
config_eth1=(
        "192.168.255.1/24"
)

#
# ETH 2 // WAN 1
#
config_eth2=(
        "null"
)

#
# ETH 3 // WAN 2
#
config_eth3=(
        "null"
)

#
# Heartbeat
#
preup() {
        if [[ ${IFACE} == "lo" ]];
        then
                ifconfig eth2 up
                ifconfig eth3 up
        fi
}

postup() {
        #
        # WAN 2 actions
        #
        if [[ ${IFACE} == "eth3" ]];
        then
                #
                # Delete temporary default route
                #
                ip route del default

                #
                # Add IP Forwarding
                #
                echo 1 > /proc/sys/net/ipv4/ip_forward

                #
                # Add load balanced route
                #
                ip route add default equalize \
                        nexthop via 81.81.81.1 dev eth2 weight 1 \
                        nexthop via 82.82.82.1 dev eth3 weight 1
        fi
}

postdown() {
        #
        # Restore a default route so the passive node still has access to internet
        #
        if [[ ${IFACE} == "eth3" ]];
        then
                #
                # Remove IP Forwarding
                #
                echo 0 > /proc/sys/net/ipv4/ip_forward

                #
                # Setup default route
                #
                ip route del default
                ip route add default via 192.168.0.1
        fi
}


  • Au final, si nous appelons les init script net.eth2 et net.eth3 lors de la prise de contrôle de heartbeat, ce n'est juste que pour utiliser ces fonctions prédéfinies. Dans cet exemple ici, nous pourrions d'ailleurs ne pas appeler net.eth2 car nous n'avons aucune action particulière à faire lors de son démarrage. Je le laisse cependant pour ceux qui souhaiteraient mettre en place du trafic shaping sur leurs interfaces réseau par exemple.

Conclusion

Lorsque j'ai cherché à mettre une telle solution en place, je n'ai trouvé que peu d'articles sur le net, et des franchement pas récents. J'espère redonner une petite jeunesse à ce sujet que je trouve extrêmement intéressant !


Liens utiles

Gérer plusieurs connexions internet :


Heartbeat :


Cisco :

Personal tools
Navigation