Ajout du support des ponts isolés en espace de noms réseau + ajout de la configuration de la MTU pour simplifier sa personnalisation par modification manuelle du script

master
ycharbi 3 weeks ago
parent 3d49b2fde7
commit 513a5d1efc

@ -1,9 +1,9 @@
# Usage # Usage
Les fichiers *ifup.sh* et *ifdown.sh* sont les scripts de gestion des interfaces réseaux supportant les *VLAN* pour les conteneurs [Systemd-nspawn](https://doc.ycharbi.fr/index.php/Conteneurs_-_systemd) écrit par mes soins. Ils se bases sur la technologie de [filtrage des *VLAN*](https://doc.ycharbi.fr/index.php/Vlan_-_linux) du noyau *Linux*. Les fichiers `ifup.sh` et `ifdown.sh` sont des scripts de gestion d'interfaces réseaux pour les conteneurs [Systemd-nspawn](https://doc.ycharbi.fr/index.php/Conteneurs_-_systemd) écrit par mes soins. Supportant les *VLAN* et les *ponts isolés* en *espace de noms réseau* (sans support du *802.1Q* pour ces derniers), ils se bases sur la technologie de [filtrage des *VLAN*](https://doc.ycharbi.fr/index.php/Vlan_-_linux) du noyau *Linux* pour la gestion du *802.1Q*. Les ponts isolés peuvent quant à eux être utilisés pour empêcher la communication des conteneurs au sein d'un même LAN. Cette approche permet une mise en œuvre d'une défense en profondeur dans la communication inter-conteneur.
* *ifup.sh* permet l'attribution d'une interface physique à un conteneur ainsi que la création et le placement d'une interface réseau virtuelle dans un *VLAN* ou dans un tronc *802.1Q*. Il se charge également de l'intégrer à un pont spécifique (l'existence préalable de ce dernier est requise) * *ifup.sh* permet l'attribution d'une interface physique à un conteneur ainsi que la création et le placement d'une interface réseau virtuelle dans un *VLAN* ou dans un tronc *802.1Q*. Il se charge également de l'intégrer à un pont spécifique. Pour les ponts isolés en espace de noms réseau, les *VLAN* ne sont pas supportés. L'existence préalable des ponts et des espaces de noms réseau (si pont isolé) est requise
* *ifdown* se charge de supprimer les interfaces créées par *Systemd-nspawn* après l'extinction du conteneur * *ifdown* se charge de supprimer les interfaces créées par *Systemd-nspawn* après l'extinction du conteneur
Afin qu'ils s'exécutent au démarrage et à l'arrêt d'un conteneur, il faut [modifier](https://philip-trauner.me/blog/post/machinectl-autologin) le service `systemd-nspawn@.service` pour y ajouter les directives adéquates (notez l'emplacement des scripts dans l'exemple) : Afin qu'ils s'exécutent au démarrage et à l'arrêt d'un conteneur, il faut [modifier](https://philip-trauner.me/blog/post/machinectl-autologin) le service `systemd-nspawn@.service` pour y ajouter les directives adéquates. Notez l'emplacement des scripts dans l'exemple et assurez-vous de leur permission d'exécution :
``` bash ``` bash
systemctl edit systemd-nspawn@.service systemctl edit systemd-nspawn@.service
@ -17,21 +17,40 @@ ExecStartPre=/etc/systemd/nspawn/ifup.sh %i
ExecStopPost=/etc/systemd/nspawn/ifdown.sh %i ExecStopPost=/etc/systemd/nspawn/ifdown.sh %i
``` ```
Si vous voulez revenir en arrière, un `systemctl revert systemd-nspawn@.service` supprimera le fichier de modifications et restaurera l'état initial sans laisser de traces de l'opération précédente.
:bulb: Le traitement des paramètres d'interfaces des conteneurs des fichiers `*.nspawn` s'appuit sur des commentaires spécifiques à placer directement au dessus de chaque directives `Interface=`.
Pour configurer les interfaces réseaux d'un conteneur, il faut : Pour configurer les interfaces réseaux d'un conteneur, il faut :
* pour une interface virtuelle : ajouter deux lignes de commentaires **au dessus** de chaque directive `Interface=` dans le [fichier de configuration](https://doc.ycharbi.fr/index.php/Conteneurs_-_systemd#Fichier_de_configuration) de celui-ci * Interface **physique** : utiliser la directive `Interface=` de façon [normale](https://www.freedesktop.org/software/systemd/man/systemd.nspawn.html#Interface=) en veillant à ne pas avoir l'une des deux lignes de commentaire dans les deux lignes au dessus de celle-ci
* pour une interface physique : utiliser la directive `Interface=` de façon [normale](https://www.freedesktop.org/software/systemd/man/systemd.nspawn.html#Interface=) en veillant à ne pas avoir l'une des deux lignes de commentaire dans les deux lignes au dessus de celle-ci * Interface **virtuelle** : ajouter deux lignes de commentaires **au dessus** de chaque directive `Interface=` dans le [fichier de configuration](https://doc.ycharbi.fr/index.php/Conteneurs_-_systemd#Fichier_de_configuration) de celui-ci
Pour une interface physique :
``` ini
Interface=nomIfacePhy
```
Pour une interface non étiqueté : Pour une interface non étiqueté (mode *access*) :
``` ini ``` ini
#PONT=votrepont #PONT=votrePont
#VLAN=vid #VLAN=vid
Interface=nomIfaceVirt_vid
``` ```
Pour une interface de tronc 802.1Q : Pour une interface de tronc 802.1Q (mode *trunk*) :
``` ini ``` ini
#PONT=votrepont #PONT=votrePont
#8021Q=vid1,vi2,vid3 #8021Q=vid1,vi2,vid3
Interface=nomIfaceVirt_vid
```
Pour une interface en pont isolé :
``` ini
#PONT=votrePont
#ISOLE=votreEspaceDeNomsRéseau
Interface=nomIfaceVirt
``` ```
Il est possible de mixer les notions au travers de plusieurs interfaces comme dans l'exemple suivant (fichier `/etc/systemd/nspawn/web.nspawn`) : Il est possible de mixer les notions au travers de plusieurs interfaces comme dans l'exemple suivant (fichier `/etc/systemd/nspawn/web.nspawn`) :
@ -48,17 +67,105 @@ Interface=web_121
Interface=web_T1 Interface=web_T1
Interface=eth2 Interface=eth2
#PONT=br0 #PONT=br1
#8021Q=181,1109 #8021Q=181,1109
Interface=web_T2 Interface=web_T2
#PONT=br-netns135
#ISOLE=netns135
Interface=nxtcld
``` ```
Le script vérifie que ce que vous avez renseigné est valide. S'il rencontre la moindre erreur, il s'interrompt sans modifier le système et vous oriente sur le problème. En utilisation avec un conteneur, les messages de retour du script sont visibles dans le journal du service via `systemctl status systemd-nspawn@votreConteneur.service` ou `journalctl -xe`. Le script vérifie que ce que vous avez renseigné est valide. S'il rencontre la moindre erreur, il s'interrompt sans modifier le système et vous oriente sur le problème. En utilisation avec un conteneur, les messages de retour du script sont visibles dans le journal du service via `systemctl status systemd-nspawn@votreConteneur.service` ou `journalctl -xe`.
:warning: *J'attire votre attention sur le fait que l'ajout, en cours de fonctionnement, de directives `Interface=` dans le ficher de configuration de votre conteneur a pour effet de neutraliser l'exécution d'*ifdown.sh*. Ceci est intrinsèque à la façon dont il fonctionne. Les interfaces créées par *ifup.sh* devront alors êtres supprimées manuellement.* :warning: :warning: *J'attire votre attention sur le fait que l'ajout, en cours de fonctionnement, de directives `Interface=` dans le ficher de configuration de votre conteneur a pour effet de neutraliser l'exécution de `ifdown.sh`. Ceci est intrinsèque à la façon dont il fonctionne. Les interfaces créées par `ifup.sh` devront alors êtres supprimées manuellement.*
# Environnement
Toute référence à un pont ou à un espace de noms réseau suppose son existence préalable. Cette section propose un exemple de configuration réseau pour un système *GNU/Linux Debian* mettant en œuvre un pont avec filtrage de *VLAN* ainsi qu'un *pont isolé* en *espace de noms réseau*. Il conviendra d'en adapter les valeurs pour coller avec votre besoin.
Fichier `/etc/network/interfaces` :
```
auto lo
iface lo inet loopback
up /usr/local/sbin/confint
```
Fichier `/usr/local/sbin/confint` :
```bash
#!/bin/bash
# Script de configuration des interfaces réseau
nom_pont="br0"
nom_iface1="ens3"
# La MTU peut être passée à 9000 pour l'activation des trames géantes (adapter cette valeur dans le script le cas échéant)
ip link set "${nom_iface1}" mtu 1500
ip link set "${nom_iface1}" up
# Création du pont supportant le 802.1Q
ip link add "${nom_pont}" type bridge vlan_filtering 1
ip link set "${nom_pont}" up
# Ajout de l'interface physique au pont
ip link set "${nom_iface1}" master "${nom_pont}"
# Désactivation du VLAN par défaut sur le pont afin de l'isoler
bridge vlan del dev "${nom_pont}" vid 1 self
bridge vlan del dev "${nom_iface1}" vid 1 master
# VLAN
id_vlan="10"
nom_svi="vlan${id_vlan}"
ip_svi="192.168."${id_vlan}".1/24"
ip link add link "${nom_pont}" name vlan"${id_vlan}" type vlan id "${id_vlan}"
ip link set "${nom_svi}" up
ip address add "${ip_svi}" dev "${nom_svi}"
bridge vlan add dev "${nom_iface1}" vid "${id_vlan}" tagged master
bridge vlan add dev "${nom_pont}" vid "${id_vlan}" tagged self
id_vlan="20"
nom_svi="vlan${id_vlan}"
ip_svi="192.168."${id_vlan}".1/24"
ip link add link "${nom_pont}" name vlan"${id_vlan}" type vlan id "${id_vlan}"
ip link set "${nom_svi}" up
ip address add "${ip_svi}" dev "${nom_svi}"
bridge vlan add dev "${nom_iface1}" vid "${id_vlan}" tagged master
bridge vlan add dev "${nom_pont}" vid "${id_vlan}" tagged self
# Routage
ip route add default via 192.168.10.254 dev vlan10 onlink
# Pont isolé 135
id_rzo="135"
nom_netns="netns${id_rzo}"
nom_pont="br-netns${id_rzo}"
nom_veth="veth${id_rzo}"
ipv4_veth="10.1.${id_rzo}.254/24"
ip netns add "${nom_netns}"
ip netns exec "${nom_netns}" ip link add "${nom_pont}" type bridge stp_state 0
ip link add ${nom_veth} type veth peer name ${nom_veth}_h
ip link set netns "${nom_netns}" dev ${nom_veth}_h
ip netns exec "${nom_netns}" ip link set ${nom_veth}_h master "${nom_pont}"
ip netns exec "${nom_netns}" ip link set dev ${nom_veth}_h type bridge_slave isolated off
ip netns exec "${nom_netns}" sh -c "echo 1 > /proc/sys/net/ipv6/conf/${nom_pont}/disable_ipv6"
ip netns exec "${nom_netns}" sh -c "echo 1 > /proc/sys/net/ipv6/conf/${nom_veth}_h/disable_ipv6"
ip netns exec "${nom_netns}" ip link set ${nom_pont} up
ip netns exec "${nom_netns}" ip link set ${nom_veth}_h up
ip address add "${ipv4_veth}" dev "${nom_veth}"
ip link set "${nom_veth}" up
```
Ne pas oublier d'ajouter les droits d'exécution au script et d'activer le routage :
```bash
chmod +x /usr/local/sbin/confint
# Configuration de routage éphémère
# Utiliser un paramètre sysctl pour le rendre persistent
echo 1 > /proc/sys/net/ipv4/ip_forward
```
# Tests sans altérations # Tests sans altérations
Si vous voulez modifier les scripts, il peut être pratique d'afficher les commandes générées au lieu de les appliquer lors de vos tests. Pour se faire, vous pouvez ajouter la commande `echo` devant l'action d'exécution comme suit (et comme précisé en commentaire dans le script) dans la fonction `execCmdIntRzo()` (le script exige un nom de conteneur en paramètre) : Si vous voulez modifier les scripts, il peut être pratique d'afficher les commandes générées sans les appliquer lors de vos tests. Pour ce faire, il suffit de commenter la ligne suivante (et comme précisé en commentaire dans le script) dans la fonction `execCmdIntRzo()` (le script exige un nom de conteneur en paramètre) :
``` bash ``` bash
echo ${commandes_a_executer["${id_commande_rzo}"]} # ${commandes_a_executer["${id_commande_rzo}"]}
``` ```

@ -13,6 +13,7 @@ genCmdIntRzo(){
# Création d'une interface non étiqueté (access) # Création d'une interface non étiqueté (access)
if [[ "${pont_init}" -eq 1 && "${vlan_init}" -eq 1 && "${interface_init}" -eq 1 ]]; then if [[ "${pont_init}" -eq 1 && "${vlan_init}" -eq 1 && "${interface_init}" -eq 1 ]]; then
commandes_a_executer+=( "ip link add ${interface} type veth peer name ${interface}_h" ) commandes_a_executer+=( "ip link add ${interface} type veth peer name ${interface}_h" )
commandes_a_executer+=( "ip link set ${interface}_h mtu 1500" )
commandes_a_executer+=( "ip link set ${interface}_h up" ) commandes_a_executer+=( "ip link set ${interface}_h up" )
commandes_a_executer+=( "ip link set ${interface}_h master ${pont}" ) commandes_a_executer+=( "ip link set ${interface}_h master ${pont}" )
commandes_a_executer+=( "bridge vlan del dev ${interface}_h vid 1 PVID untagged master" ) commandes_a_executer+=( "bridge vlan del dev ${interface}_h vid 1 PVID untagged master" )
@ -26,6 +27,7 @@ genCmdIntRzo(){
# Création d'une interface étiqueté (trunk) # Création d'une interface étiqueté (trunk)
elif [[ "${pont_init}" -eq 1 && "${ieee8021q_init}" -eq 1 && "${interface_init}" -eq 1 ]]; then elif [[ "${pont_init}" -eq 1 && "${ieee8021q_init}" -eq 1 && "${interface_init}" -eq 1 ]]; then
commandes_a_executer+=( "ip link add ${interface} type veth peer name ${interface}_h" ) commandes_a_executer+=( "ip link add ${interface} type veth peer name ${interface}_h" )
commandes_a_executer+=( "ip link set ${interface}_h mtu 1500" )
commandes_a_executer+=( "ip link set ${interface}_h up" ) commandes_a_executer+=( "ip link set ${interface}_h up" )
commandes_a_executer+=( "ip link set ${interface}_h master ${pont}" ) commandes_a_executer+=( "ip link set ${interface}_h master ${pont}" )
commandes_a_executer+=( "bridge vlan del dev ${interface}_h vid 1 PVID untagged master" ) commandes_a_executer+=( "bridge vlan del dev ${interface}_h vid 1 PVID untagged master" )
@ -37,6 +39,15 @@ genCmdIntRzo(){
pont_init=0 pont_init=0
ieee8021q_init=0 ieee8021q_init=0
interface_init=0 interface_init=0
# Création d'une interface normale en pont isolé dans un espace de nom réseau
elif [[ "${pont_init}" -eq 1 && "${isole_init}" -eq 1 && "${interface_init}" -eq 1 ]]; then
commandes_a_executer+=( "ip link add ${interface} type veth peer name ${interface}_h" )
commandes_a_executer+=( "ip link set netns ${isole_netns} dev ${interface}_h" )
commandes_a_executer+=( "ip netns exec ${isole_netns} ip link set ${interface}_h master ${pont}" )
commandes_a_executer+=( "ip netns exec ${isole_netns} ip link set dev ${interface}_h type bridge_slave isolated on" )
commandes_a_executer+=( "ip netns exec ${isole_netns} ip link set dev ${interface}_h mtu 1500" )
commandes_a_executer+=( "ip netns exec ${isole_netns} ip link set dev ${interface}_h up" )
fi fi
} }
@ -51,7 +62,7 @@ recupParams(){
# Récupération des données d'après le fichier de configuration # Récupération des données d'après le fichier de configuration
if [ "${nom_param}" == "PONT" ]; then if [ "${nom_param}" == "PONT" ]; then
if [[ $(ip link show "${val_param}" 2> /dev/null) ]]; then if [[ $(ip link show "${val_param}" 2> /dev/null || ip netns | cut -d ' ' -f 1 | xargs -I {} ip netns exec {} sh -c "ip link show ${val_param} 2> /dev/null" | grep ${val_param} 2> /dev/null) ]]; then
pont="${val_param}" pont="${val_param}"
pont_init=1 pont_init=1
else else
@ -88,13 +99,26 @@ recupParams(){
exit 1 exit 1
fi fi
done done
elif [ "${nom_param}" == "ISOLE" ]; then
if [[ "${val_param}" =~ ^-?[a-zA-Z0-9_\-]+$ ]]; then
if [[ "${pont_init}" -eq 1 ]]; then
isole_netns="${val_param}"
isole_init=1
else
echo "Erreur: le paramètre \"#ISOLE=espacedenomdupont\" mentionne un espace de nom réseau inexistant."
exit 1
fi
else
echo "Erreur: le nom de l'espace de nom réseau (\"${val_param}\") n'est pas valide. Arrêt immédiat du script \"${0}\" sans aucune modification du système."
exit 1
fi
elif [ "${nom_param}" == "Interface" ]; then elif [ "${nom_param}" == "Interface" ]; then
if [[ ! $(ip link show "${val_param}" 2> /dev/null) ]]; then if [[ ! $(ip link show "${val_param}" 2> /dev/null || ip netns | cut -d ' ' -f 1 | xargs -I {} ip netns exec {} sh -c "ip link show ${val_param} 2> /dev/null" | grep ${val_param} 2> /dev/null) ]]; then
if [[ "${vlan_init}" -eq 1 || "${ieee8021q_init}" -eq 1 ]]; then if [[ "${vlan_init}" -eq 1 || "${ieee8021q_init}" -eq 1 || "${isole_init}" ]]; then
interface="${val_param}" interface="${val_param}"
interface_init=1 interface_init=1
else else
echo "Erreur: le paramètre \"#VLAN=votreVLAN\" est manquant." echo "Erreur: le paramètre \"#VLAN=votreVLAN\" ou \"#ISOLE=espacedenomdupont\" est manquant."
exit 1 exit 1
fi fi
elif [[ $(find /sys/class/net/ -type l ! -lname '*/devices/virtual/net/*' -printf '%f ' | grep "${val_param}" 2> /dev/null) ]]; then elif [[ $(find /sys/class/net/ -type l ! -lname '*/devices/virtual/net/*' -printf '%f ' | grep "${val_param}" 2> /dev/null) ]]; then
@ -118,6 +142,7 @@ execCmdIntRzo(){
for ((id_commande_rzo=0 ; "${#commandes_a_executer[*]}" - "${id_commande_rzo}"; id_commande_rzo++)); do for ((id_commande_rzo=0 ; "${#commandes_a_executer[*]}" - "${id_commande_rzo}"; id_commande_rzo++)); do
# Exécution des commandes préparées # Exécution des commandes préparées
# Pour afficher les commandes sans rien exécuter (dans le cas d'un déboggage/modification du script), il faut précéder la ligne suivante par la commande "echo" # Pour afficher les commandes sans rien exécuter (dans le cas d'un déboggage/modification du script), il faut précéder la ligne suivante par la commande "echo"
echo ${commandes_a_executer["${id_commande_rzo}"]}
${commandes_a_executer["${id_commande_rzo}"]} ${commandes_a_executer["${id_commande_rzo}"]}
done done
} }

Loading…
Cancel
Save