Configuration réseau#
Modèle réseau Linux — stack TCP/IP, netfilter, namespaces#
La pile réseau du noyau#
Linux implémente la pile TCP/IP directement dans le noyau. Chaque paquet entrant ou sortant traverse une série de couches avant d’atteindre l’application ou le réseau physique.
Application (socket)
│
┌────▼────┐
│ Socket │ ← couche 5-7 : API POSIX, domaines AF_INET, AF_INET6, AF_UNIX
├─────────┤
│ TCP/UDP│ ← couche 4 : ports, sessions, contrôle de flux
├─────────┤
│ IP │ ← couche 3 : adressage, routage, fragmentation
├─────────┤
│Netfilter│ ← hooks noyau pour le filtrage et le NAT
├─────────┤
│ Driver │ ← couche 2 : Ethernet, Wi-Fi, tun/tap…
└─────────┘
Le sous-système Netfilter s’insère directement dans la pile IP. Il expose cinq hooks (NF_INET_PRE_ROUTING, NF_INET_LOCAL_IN, NF_INET_FORWARD, NF_INET_LOCAL_OUT, NF_INET_POST_ROUTING) que des modules noyau peuvent intercepter pour analyser ou modifier les paquets. iptables et nftables s’appuient tous deux sur ces hooks.
Namespaces réseau#
Linux supporte les network namespaces (netns), qui isolent complètement une pile réseau : interfaces, tables de routage, règles iptables, sockets. Chaque conteneur Docker possède son propre namespace réseau.
# Créer un namespace réseau isolé
ip netns add test_ns
# Exécuter une commande dans ce namespace
ip netns exec test_ns ip addr
# Relier deux namespaces via une paire veth
ip link add veth0 type veth peer name veth1
ip link set veth1 netns test_ns
# Lister les namespaces
ip netns list
# Supprimer
ip netns del test_ns
Les namespaces sont la brique fondamentale de la conteneurisation réseau. Un processus hérite du namespace de son parent ; seule une appel unshare(CLONE_NEWNET) ou ip netns exec permet d’en changer.
Interfaces réseau#
Types d’interfaces#
Interface |
Rôle |
|---|---|
|
Ethernet physique (nommage prévisible systemd) |
|
Loopback (127.0.0.1, ::1) |
|
Wi-Fi |
|
Agrégation de liens (bonding) |
|
Bridge (pont logiciel) |
|
VLAN 802.1Q |
|
Virtual Ethernet pair (conteneurs) |
|
Interfaces virtuelles (VPN) |
|
Interface de test sans matériel |
Commandes ip link#
# Lister toutes les interfaces
ip link show
# Afficher une interface spécifique
ip link show ens3
# Activer / désactiver
ip link set ens3 up
ip link set ens3 down
# Modifier la MTU
ip link set ens3 mtu 9000
# Modifier l'adresse MAC
ip link set ens3 address 02:11:22:33:44:55
# Créer une interface dummy
ip link add dummy0 type dummy
ip link set dummy0 up
Sortie typique de ip link show :
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
Les flags entre <> sont importants : UP indique que l’interface est activée administrativement, LOWER_UP que le lien physique est détecté.
Exploration via /sys/class/net#
# Vitesse de l'interface (Mb/s)
cat /sys/class/net/ens3/speed
# État opérationnel
cat /sys/class/net/ens3/operstate # up, down, unknown
# Statistiques (octets reçus/émis, erreurs)
cat /sys/class/net/ens3/statistics/rx_bytes
cat /sys/class/net/ens3/statistics/tx_packets
cat /sys/class/net/ens3/statistics/rx_errors
# Driver utilisé
readlink /sys/class/net/ens3/device/driver
Configuration IP#
Adresses avec ip addr#
# Lister les adresses
ip addr show
ip addr show ens3
# Ajouter une adresse IPv4 avec masque CIDR
ip addr add 192.168.1.10/24 dev ens3
# Ajouter une adresse IPv6
ip addr add 2001:db8::1/64 dev ens3
# Supprimer une adresse
ip addr del 192.168.1.10/24 dev ens3
# Vider toutes les adresses d'une interface
ip addr flush dev ens3
Routes avec ip route#
# Table de routage principale
ip route show
ip route show table main
# Ajouter une route statique
ip route add 10.10.0.0/16 via 192.168.1.1 dev ens3
# Ajouter la route par défaut
ip route add default via 192.168.1.254
# Supprimer une route
ip route del 10.10.0.0/16
# Tester quel chemin sera utilisé pour une destination
ip route get 8.8.8.8
Sortie de ip route get 8.8.8.8 :
8.8.8.8 via 192.168.1.254 dev ens3 src 192.168.1.10 uid 1000
cache
Policy routing avec ip rule#
Le policy routing (routage par politique) permet d’utiliser plusieurs tables de routage selon des critères : adresse source, marque de paquet, ToS.
# Lister les règles
ip rule list
# Créer une table de routage personnalisée
echo "200 custom_table" >> /etc/iproute2/rt_tables
# Ajouter une règle : paquets venant de 10.0.0.0/8 → table custom_table
ip rule add from 10.0.0.0/8 table custom_table
# Ajouter une route dans cette table
ip route add default via 10.0.0.1 table custom_table
# Supprimer la règle
ip rule del from 10.0.0.0/8
Les tables de routage prédéfinies sont local (255), main (254) et default (253). Le noyau les consulte dans l’ordre croissant de priorité défini par ip rule.
NetworkManager#
NetworkManager est le gestionnaire de connexions réseau standard sur les distributions modernes (RHEL, Fedora, Ubuntu Desktop, Debian…). Il gère les profils de connexion et supporte Ethernet, Wi-Fi, VPN, bonds, bridges, VLANs.
nmcli — interface en ligne de commande#
# État général
nmcli general status
# Lister les connexions
nmcli connection show
# Lister les périphériques
nmcli device status
# Activer / désactiver une connexion
nmcli connection up "Wired connection 1"
nmcli connection down "Wired connection 1"
# Créer une connexion Ethernet statique
nmcli connection add type ethernet \
con-name "serveur-eth0" \
ifname ens3 \
ipv4.addresses 192.168.1.10/24 \
ipv4.gateway 192.168.1.254 \
ipv4.dns "1.1.1.1,8.8.8.8" \
ipv4.method manual
# Modifier une connexion existante
nmcli connection modify "serveur-eth0" ipv4.dns "9.9.9.9"
# Supprimer une connexion
nmcli connection delete "serveur-eth0"
# Recharger une connexion après modification
nmcli connection reload
Les profils de connexion sont stockés dans /etc/NetworkManager/system-connections/ au format INI (.nmconnection).
# Contenu type d'un profil
cat /etc/NetworkManager/system-connections/serveur-eth0.nmconnection
[connection]
id=serveur-eth0
type=ethernet
interface-name=ens3
[ethernet]
[ipv4]
address1=192.168.1.10/24,192.168.1.254
dns=1.1.1.1;8.8.8.8;
method=manual
[ipv6]
method=auto
Netplan (Ubuntu)#
Netplan est la couche d’abstraction de configuration réseau d’Ubuntu (depuis 17.10). Les fichiers YAML dans /etc/netplan/ sont traduits en configuration pour le backend choisi (NetworkManager ou systemd-networkd).
Structure d’un fichier Netplan#
# /etc/netplan/00-installer-config.yaml
network:
version: 2
renderer: networkd # ou NetworkManager
ethernets:
ens3:
dhcp4: false
addresses:
- 192.168.1.10/24
- 2001:db8::10/64
routes:
- to: default
via: 192.168.1.254
nameservers:
addresses: [1.1.1.1, 8.8.8.8]
search: [example.com, local.example.com]
mtu: 1500
bonds:
bond0:
interfaces: [ens3, ens4]
parameters:
mode: active-backup
primary: ens3
mii-monitor-interval: 100
vlans:
vlan10:
id: 10
link: ens3
addresses: [10.10.10.1/24]
Commandes Netplan#
# Tester la configuration sans l'appliquer
netplan try # revient automatiquement si non confirmé
# Appliquer la configuration
netplan apply
# Générer les fichiers backend sans appliquer
netplan generate
# Déboguer
netplan --debug apply
Backends Netplan
networkd (systemd-networkd) est adapté aux serveurs sans interface graphique. NetworkManager convient aux postes de travail où des connexions Wi-Fi et VPN sont gérées dynamiquement. Le choix du backend n’affecte pas la syntaxe YAML Netplan.
DNS#
/etc/resolv.conf et /etc/hosts#
/etc/resolv.conf définit les serveurs DNS utilisés par la libc (et donc la plupart des programmes) :
nameserver 1.1.1.1
nameserver 8.8.8.8
search example.com local.example.com
options ndots:5 timeout:2 attempts:3
/etc/hosts permet de résoudre des noms localement sans passer par DNS :
127.0.0.1 localhost
::1 localhost ip6-localhost
192.168.1.5 srv-db.example.com srv-db
L’ordre de résolution est défini par /etc/nsswitch.conf :
hosts: files dns myhostname
systemd-resolved#
systemd-resolved est le résolveur DNS intégré à systemd. Il offre un cache local, la validation DNSSEC, le DNS-over-TLS et la résolution par interface.
# État du résolveur
resolvectl status
# Résoudre un nom
resolvectl query github.com
# Statistiques du cache
resolvectl statistics
# Vider le cache
resolvectl flush-caches
# DNS actif par interface
resolvectl dns ens3
# Configurer un serveur DNS pour une interface
resolvectl dns ens3 9.9.9.9
Quand systemd-resolved est actif, /etc/resolv.conf est souvent un lien symbolique vers /run/systemd/resolve/stub-resolv.conf (qui pointe vers 127.0.0.53, le stub resolver).
Outils de diagnostic DNS#
# dig — outil de référence
dig github.com # enregistrement A
dig github.com AAAA # enregistrement AAAA
dig github.com MX # enregistrements MX
dig @8.8.8.8 github.com # requête vers un serveur spécifique
dig +trace github.com # résolution itérative complète
dig -x 140.82.121.4 # résolution inverse (PTR)
dig +short github.com # sortie compacte
# nslookup — outil interactif
nslookup github.com
nslookup -type=MX github.com 8.8.8.8
# host — commande simple
host github.com
host -t NS github.com
Diagnostic réseau#
ss — état des sockets#
ss (socket statistics) remplace netstat et est beaucoup plus rapide car il interroge directement le noyau via Netlink.
# Toutes les connexions TCP établies
ss -t state established
# Ports en écoute (TCP et UDP)
ss -tlunp
# Connexions avec processus propriétaire
ss -tlnp
# Filtrer par port
ss -t sport = :443
# Statistiques globales
ss -s
Sortie de ss -tlnp :
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3))
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=5678,fd=6))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=5678,fd=7))
Connectivité et routage#
# Ping basique
ping -c 4 8.8.8.8
ping6 -c 4 2001:4860:4860::8888
# Traceroute UDP (par défaut)
traceroute 8.8.8.8
# Traceroute ICMP (plus compatible)
traceroute -I 8.8.8.8
# mtr — combinaison ping + traceroute en temps réel
mtr 8.8.8.8
mtr --report --report-cycles 10 8.8.8.8
# Tester la connectivité TCP sans telnet
nc -zv 192.168.1.5 22
nc -zv -w 3 github.com 443
tcpdump — capture de paquets#
# Capturer sur l'interface ens3
tcpdump -i ens3
# Filtrer par host
tcpdump -i ens3 host 192.168.1.5
# Filtrer par port
tcpdump -i ens3 port 80 or port 443
# Capturer et sauvegarder (pour Wireshark)
tcpdump -i ens3 -w capture.pcap
# Lire un fichier de capture
tcpdump -r capture.pcap
# Afficher le contenu ASCII des paquets HTTP
tcpdump -i ens3 -A port 80
# N'afficher que les en-têtes (pas de résolution DNS pour plus de vitesse)
tcpdump -i ens3 -n -q port 22
Performances de tcpdump
Sur un lien chargé, utilisez -c N pour limiter le nombre de paquets capturés et -s snaplen pour capturer uniquement les N premiers octets de chaque paquet (par défaut 262144 octets). Une capture avec -w est nettement plus performante que la sortie texte en temps réel.
Bonding et teaming#
Le bonding agrège plusieurs interfaces physiques en une interface logique unique, offrant redondance et/ou augmentation de bande passante.
Modes de bonding#
Mode |
Nom |
Description |
|---|---|---|
0 |
balance-rr |
Round-robin, répartition des paquets |
1 |
active-backup |
Une seule interface active, bascule en cas de panne |
2 |
balance-xor |
Répartition par hachage src/dst MAC |
3 |
broadcast |
Envoi sur toutes les interfaces |
4 |
802.3ad (LACP) |
Agrégation dynamique IEEE, switch doit supporter LACP |
5 |
balance-tlb |
Répartition adaptative en émission |
6 |
balance-alb |
Répartition adaptative en émission + réception |
Le mode 1 (active-backup) est le plus simple et ne nécessite aucune configuration du switch. Le mode 4 (LACP) offre les meilleures performances mais exige un switch manageable.
Configuration d’un bond avec ip#
# Créer l'interface bond
ip link add bond0 type bond
# Définir le mode
echo active-backup > /sys/class/net/bond0/bonding/mode
echo 100 > /sys/class/net/bond0/bonding/miimon
# Attacher les esclaves
ip link set ens3 down
ip link set ens3 master bond0
ip link set ens4 down
ip link set ens4 master bond0
# Activer le bond
ip link set bond0 up
ip addr add 192.168.1.10/24 dev bond0
# Vérifier l'état
cat /proc/net/bonding/bond0
Avec Netplan (mode LACP) :
bonds:
bond0:
interfaces: [ens3, ens4]
addresses: [192.168.1.10/24]
parameters:
mode: 802.3ad
lacp-rate: fast
mii-monitor-interval: 100
transmit-hash-policy: layer3+4
VLANs — 802.1Q#
Un VLAN (Virtual LAN) segmente logiquement un réseau physique. En 802.1Q, une étiquette de 4 octets est insérée dans l’en-tête Ethernet ; les 12 bits de VLAN ID permettent 4094 VLANs (1-4094).
Configuration avec ip link#
# Créer une interface VLAN sur ens3 pour le VLAN 10
ip link add link ens3 name ens3.10 type vlan id 10
# Activer et configurer l'IP
ip link set ens3.10 up
ip addr add 10.10.10.1/24 dev ens3.10
# Créer le VLAN 20
ip link add link ens3 name ens3.20 type vlan id 20
ip link set ens3.20 up
ip addr add 10.20.20.1/24 dev ens3.20
# Vérifier
ip -d link show ens3.10
cat /proc/net/vlan/config
Avec Netplan :
vlans:
vlan10:
id: 10
link: ens3
addresses: [10.10.10.1/24]
nameservers:
addresses: [10.10.10.53]
vlan20:
id: 20
link: ens3
addresses: [10.20.20.1/24]
Trunk vs Access
Du côté du switch, le port connecté au serveur doit être configuré en mode trunk pour transporter plusieurs VLANs avec leurs étiquettes 802.1Q. Un port access n’appartient qu’à un seul VLAN et ne transmet pas d’étiquettes.
Démonstrations Python#
Module ipaddress — calcul de sous-réseaux#
import ipaddress
# Réseau de base
net = ipaddress.IPv4Network("192.168.0.0/22")
print(f"Réseau : {net}")
print(f"Masque : {net.netmask}")
print(f"Masque joker : {net.hostmask}")
print(f"Adresse réseau: {net.network_address}")
print(f"Broadcast : {net.broadcast_address}")
print(f"Nb d'hôtes : {net.num_addresses - 2}")
print(f"Préfixe : /{net.prefixlen}")
print()
# Décomposer en sous-réseaux /24
print("Sous-réseaux /24 :")
for subnet in net.subnets(new_prefix=24):
hosts = list(subnet.hosts())
print(f" {subnet} → premier hôte: {hosts[0]} dernier: {hosts[-1]}")
print()
# Supernet
supernet = net.supernet()
print(f"Supernet (/21) : {supernet}")
# Vérifier l'appartenance
ip = ipaddress.IPv4Address("192.168.1.42")
print(f"\n{ip} dans {net} : {ip in net}")
print(f"{ip} est privée : {ip.is_private}")
print(f"{ip} est globale: {ip.is_global}")
# IPv6
net6 = ipaddress.IPv6Network("2001:db8::/32")
print(f"\nRéseau IPv6 : {net6}")
print(f"Nb d'adresses : {net6.num_addresses:.2e}")
subnet6 = list(net6.subnets(new_prefix=48))[0]
print(f"Premier /48 : {subnet6}")
Réseau : 192.168.0.0/22
Masque : 255.255.252.0
Masque joker : 0.0.3.255
Adresse réseau: 192.168.0.0
Broadcast : 192.168.3.255
Nb d'hôtes : 1022
Préfixe : /22
Sous-réseaux /24 :
192.168.0.0/24 → premier hôte: 192.168.0.1 dernier: 192.168.0.254
192.168.1.0/24 → premier hôte: 192.168.1.1 dernier: 192.168.1.254
192.168.2.0/24 → premier hôte: 192.168.2.1 dernier: 192.168.2.254
192.168.3.0/24 → premier hôte: 192.168.3.1 dernier: 192.168.3.254
Supernet (/21) : 192.168.0.0/21
192.168.1.42 dans 192.168.0.0/22 : True
192.168.1.42 est privée : True
192.168.1.42 est globale: False
Réseau IPv6 : 2001:db8::/32
Nb d'adresses : 7.92e+28
Premier /48 : 2001:db8::/48
Plan d’adressage — visualisation#
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
# Plan d'adressage d'une infrastructure typique
plan = [
{"VLAN": 1, "Nom": "Management", "Réseau": "10.0.1.0/24", "Hôtes": 10, "Couleur": "#4e79a7"},
{"VLAN": 10, "Nom": "Production", "Réseau": "10.0.10.0/24", "Hôtes": 80, "Couleur": "#f28e2b"},
{"VLAN": 20, "Nom": "Staging", "Réseau": "10.0.20.0/24", "Hôtes": 30, "Couleur": "#59a14f"},
{"VLAN": 30, "Nom": "DMZ", "Réseau": "10.0.30.0/24", "Hôtes": 15, "Couleur": "#e15759"},
{"VLAN": 40, "Nom": "Sauvegarde", "Réseau": "10.0.40.0/24", "Hôtes": 8, "Couleur": "#76b7b2"},
{"VLAN": 50, "Nom": "Supervision", "Réseau": "10.0.50.0/24", "Hôtes": 5, "Couleur": "#edc948"},
]
df = pd.DataFrame(plan)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Tableau
ax = axes[0]
ax.axis("off")
table_data = [[row["VLAN"], row["Nom"], row["Réseau"],
str(row["Hôtes"]), f"{254 - row['Hôtes']} libres"]
for _, row in df.iterrows()]
col_labels = ["VLAN", "Segment", "Réseau", "Utilisés", "Disponibles"]
tbl = ax.table(cellText=table_data, colLabels=col_labels,
loc="center", cellLoc="center")
tbl.auto_set_font_size(False)
tbl.set_fontsize(10)
tbl.scale(1.2, 1.8)
for i, row in enumerate(plan):
for j in range(5):
tbl[(i + 1, j)].set_facecolor(row["Couleur"] + "55")
for j in range(5):
tbl[(0, j)].set_facecolor("#333333")
tbl[(0, j)].set_text_props(color="white", fontweight="bold")
ax.set_title("Plan d'adressage VLAN", fontweight="bold", pad=12)
# Utilisation des hôtes
ax2 = axes[1]
noms = df["Nom"]
utilises = df["Hôtes"]
libres = 254 - df["Hôtes"]
x = np.arange(len(noms))
width = 0.55
bars1 = ax2.bar(x, utilises, width, label="Utilisés", color=[r["Couleur"] for r in plan])
bars2 = ax2.bar(x, libres, width, bottom=utilises, label="Disponibles",
color=[r["Couleur"] + "33" for r in plan], edgecolor=[r["Couleur"] for r in plan],
linewidth=0.8)
ax2.set_xticks(x)
ax2.set_xticklabels(noms, rotation=20, ha="right")
ax2.set_ylabel("Adresses hôtes (/24)")
ax2.set_title("Utilisation par segment", fontweight="bold")
ax2.set_ylim(0, 265)
ax2.legend(loc="upper right")
for bar, val in zip(bars1, utilises):
ax2.text(bar.get_x() + bar.get_width() / 2, val + 4,
str(val), ha="center", va="bottom", fontsize=9)
plt.suptitle("Infrastructure réseau multi-VLAN", fontsize=13, fontweight="bold", y=1.02)
plt.show()
Table de routage — simulation#
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
routes = pd.DataFrame([
{"Destination": "0.0.0.0/0", "Via": "192.168.1.254", "Interface": "ens3", "Métrique": 100, "Proto": "static", "Type": "default"},
{"Destination": "10.0.10.0/24", "Via": "0.0.0.0", "Interface": "ens3", "Métrique": 0, "Proto": "kernel", "Type": "local"},
{"Destination": "10.0.20.0/24", "Via": "10.0.1.2", "Interface": "ens3", "Métrique": 200, "Proto": "static", "Type": "network"},
{"Destination": "10.0.30.0/24", "Via": "10.0.1.3", "Interface": "ens3", "Métrique": 200, "Proto": "static", "Type": "network"},
{"Destination": "172.16.0.0/12", "Via": "10.0.1.1", "Interface": "bond0", "Métrique": 150, "Proto": "ospf", "Type": "network"},
{"Destination": "192.168.1.0/24", "Via": "0.0.0.0", "Interface": "ens3", "Métrique": 0, "Proto": "kernel", "Type": "local"},
{"Destination": "10.8.0.0/24", "Via": "0.0.0.0", "Interface": "tun0", "Métrique": 0, "Proto": "kernel", "Type": "vpn"},
])
color_map = {
"default": "#e15759",
"local": "#4e79a7",
"network": "#59a14f",
"vpn": "#f28e2b",
}
fig, axes = plt.subplots(1, 2, figsize=(15, 5))
# Tableau des routes
ax = axes[0]
ax.axis("off")
tbl = ax.table(
cellText=routes[["Destination", "Via", "Interface", "Métrique", "Proto"]].values,
colLabels=["Destination", "Passerelle", "Interface", "Métrique", "Proto"],
loc="center", cellLoc="center"
)
tbl.auto_set_font_size(False)
tbl.set_fontsize(9)
tbl.scale(1.1, 1.7)
for i, (_, row) in enumerate(routes.iterrows()):
c = color_map[row["Type"]] + "44"
for j in range(5):
tbl[(i + 1, j)].set_facecolor(c)
for j in range(5):
tbl[(0, j)].set_facecolor("#444444")
tbl[(0, j)].set_text_props(color="white", fontweight="bold")
ax.set_title("Table de routage principale", fontweight="bold", pad=12)
# Distribution par protocole
ax2 = axes[1]
proto_counts = routes["Proto"].value_counts()
colors = ["#4e79a7", "#59a14f", "#f28e2b", "#e15759"]
wedges, texts, autotexts = ax2.pie(
proto_counts.values,
labels=proto_counts.index,
autopct="%1.0f%%",
colors=colors[:len(proto_counts)],
startangle=90,
pctdistance=0.75
)
for at in autotexts:
at.set_fontsize(10)
at.set_fontweight("bold")
ax2.set_title("Routes par protocole", fontweight="bold")
legend_patches = [mpatches.Patch(color=color_map[t], label=t.capitalize()) for t in color_map]
axes[0].legend(handles=legend_patches, loc="lower center",
bbox_to_anchor=(0.5, -0.08), ncol=4, fontsize=8)
plt.suptitle("Analyse de la table de routage", fontsize=13, fontweight="bold")
plt.show()
Diagramme d’architecture réseau#
sns.set_theme(style="white", palette="muted", font_scale=1.0)
fig, ax = plt.subplots(figsize=(13, 9))
ax.set_xlim(0, 13)
ax.set_ylim(0, 9)
ax.axis("off")
def boite(ax, x, y, w, h, label, sublabel="", color="#4e79a7", fontsize=9):
rect = plt.Rectangle((x - w/2, y - h/2), w, h,
facecolor=color + "33", edgecolor=color, linewidth=2, zorder=3)
ax.add_patch(rect)
ax.text(x, y + (0.15 if sublabel else 0), label, ha="center", va="center",
fontsize=fontsize, fontweight="bold", zorder=4)
if sublabel:
ax.text(x, y - 0.25, sublabel, ha="center", va="center",
fontsize=7.5, color="#555555", zorder=4)
def fleche(ax, x1, y1, x2, y2, label="", color="#888888"):
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle="<->", color=color, lw=1.5), zorder=2)
if label:
mx, my = (x1 + x2) / 2, (y1 + y2) / 2
ax.text(mx + 0.15, my, label, fontsize=7.5, color=color, va="center")
# Internet
boite(ax, 6.5, 8.3, 2.2, 0.8, "Internet", "", "#888888", fontsize=10)
# Firewall
boite(ax, 6.5, 6.8, 2.4, 0.9, "Pare-feu", "iptables / nftables", "#e15759")
fleche(ax, 6.5, 7.75, 6.5, 7.25, color="#e15759")
# Switch/routeur cœur
boite(ax, 6.5, 5.3, 2.4, 0.9, "Routeur cœur", "bond0 LACP", "#f28e2b")
fleche(ax, 6.5, 6.35, 6.5, 5.75, color="#f28e2b")
# DMZ
boite(ax, 2.5, 3.7, 2.4, 0.9, "DMZ (VLAN 30)", "10.0.30.0/24", "#e15759")
fleche(ax, 5.3, 5.1, 3.7, 4.0, color="#aaaaaa")
# Production
boite(ax, 6.5, 3.7, 2.4, 0.9, "Production (VLAN 10)", "10.0.10.0/24", "#59a14f")
fleche(ax, 6.5, 4.85, 6.5, 4.15, color="#aaaaaa")
# Management
boite(ax, 10.5, 3.7, 2.4, 0.9, "Management (VLAN 1)", "10.0.1.0/24", "#4e79a7")
fleche(ax, 7.7, 5.1, 9.3, 4.0, color="#aaaaaa")
# Serveurs dans les zones
boite(ax, 2.5, 2.2, 1.8, 0.7, "srv-web", "nginx / TLS", "#76b7b2", fontsize=8)
boite(ax, 5.5, 2.2, 1.8, 0.7, "srv-app", "Python / Java", "#59a14f", fontsize=8)
boite(ax, 7.5, 2.2, 1.8, 0.7, "srv-db", "PostgreSQL", "#4e79a7", fontsize=8)
boite(ax, 10.5, 2.2, 1.8, 0.7, "srv-mgmt", "Ansible / Zabbix", "#edc948", fontsize=8)
fleche(ax, 2.5, 3.25, 2.5, 2.55, color="#aaaaaa")
fleche(ax, 6.5, 3.25, 5.5, 2.55, color="#aaaaaa")
fleche(ax, 6.5, 3.25, 7.5, 2.55, color="#aaaaaa")
fleche(ax, 10.5, 3.25, 10.5, 2.55, color="#aaaaaa")
# Légende VLANs
for i, (label, color) in enumerate([
("DMZ", "#e15759"), ("Production", "#59a14f"), ("Management", "#4e79a7")
]):
ax.add_patch(plt.Rectangle((0.3 + i * 3.5, 0.3), 0.35, 0.35,
facecolor=color + "55", edgecolor=color, linewidth=1.5))
ax.text(0.8 + i * 3.5, 0.48, label, fontsize=8.5, va="center")
ax.set_title("Architecture réseau Linux multi-VLAN — vue logique",
fontsize=13, fontweight="bold", pad=14)
plt.show()
Résumé#
La configuration réseau sous Linux repose sur deux niveaux : les outils noyau exposés via la suite iproute2 (ip link, ip addr, ip route, ip rule) et les gestionnaires de haut niveau (NetworkManager, Netplan) qui persistent la configuration au redémarrage.
Points essentiels à retenir :
iproute2est l’ensemble d’outils canonique :ipremplaceifconfig,route,arp. Les modifications sont immédiates mais non persistantes.NetworkManager est adapté aux environnements dynamiques ;
nmclipermet une gestion complète en ligne de commande.Netplan (Ubuntu) abstrait le backend et simplifie la configuration déclarative en YAML.
systemd-resolved centralise la résolution DNS avec cache, DNSSEC et routage par interface.
Le module Python
ipaddresspermet de manipuler des réseaux IP avec précision, sans dépendance externe.Le bonding et les VLANs sont des fonctionnalités noyau accessibles directement via
ip link, indépendamment du gestionnaire de haut niveau.Pour le diagnostic,
ss(sockets),tcpdump(capture),mtr(routage) etresolvectl(DNS) couvrent l’essentiel des situations.
La maîtrise de la pile réseau Linux est un prérequis pour les chapitres suivants sur le filtrage (pare-feu), les services réseau (Nginx, DNS, NFS) et la sécurisation des accès (SSH, VPN).