I namespaces sono una funzionalità del kernel Linux che permette di suddividere e isolare le risorse del sistema. In questo modo il kernel è in grado di separare i processi tra loro facendo sì che ciascun gruppo abbia una propria visione del sistema indipendente dagli altri.
Nel precedente articolo abbiamo visto chroot, un meccanismo che limita la visione del filesystem ma non isola processi né risorse. Ogni namespace isola un particolare aspetto del sistema come i processi, la rete, il filesystem o gli utenti. È proprio la combinazione di più namespace che consente di creare ambienti fortemente isolati come quelli utilizzati dai container.
Quando si parla di namespaces in generale in realtà ci si riferisce a un insieme di namespaces distinti e non a un singolo meccanismo.
- PID (Process ID): isola i processi e i relativi identificativi
- User (UID/GID): rimappa utenti e privilegi
- Cgroup: isola la gerarchia dei cgroup
- Time: fornisce una percezione isolata del tempo e del boot time
- Mount (MNT): isola filesystem e punti di mount
- IPC (Interprocess Communication): isola i meccanismi di comunicazione tra processi
- Network (NET): fornisce uno stack di rete separato (interfacce, routing, firewall)
- UTS (Unix Time-Sharing): isola hostname e domain name
Linux mette a disposizione alcuni comandi che permettono di osservare e analizzare i namespaces presenti nel sistema. Questi strumenti sono utili per capire quali namespaces esistono e come sono associati ai processi. Uno dei comandi principali è lsns che viene utilizzato per visualizzare i namespaces attualmente presenti sul sistema.
Le informazioni sui namespaces sono in /proc/<PID>/ns per vedere informazioni su namespace di un processo dato il suo PID.
lsns mostra tutti i namespace attivi nel sistema e quali processi li stanno usando:frank@laptop01:~$ lsns
NS TYPE NPROCS PID USER COMMAND
4026531832 mnt 86 1548 frank /usr/bin/dbus-broker-launch --scope user
4026531833 net 74 1548 frank /usr/bin/dbus-broker-launch --scope user
4026531834 time 86 1548 frank /usr/bin/dbus-broker-launch --scope user
4026531835 cgroup 86 1548 frank /usr/bin/dbus-broker-launch --scope user
4026531836 pid 74 1548 frank /usr/bin/dbus-broker-launch --scope user
4026531837 user 74 1548 frank /usr/bin/dbus-broker-launch --scope user
4026531838 uts 86 1548 frank /usr/bin/dbus-broker-launch --scope user
4026531839 ipc 86 1548 frank /usr/bin/dbus-broker-launch --scope user
4026532636 user 12 3392 frank /opt/google/chrome/chrome --type=zygote -- crashpad-handler-pid=3383 --enable-crash-reporter=, --change-stack-guard-on-fork=enable
Il significato delle colonne:
- NS: ID interno del namespace (inode)
- TYPE: tipo di namespace
- NPROCS: quanti processi ci stanno dentro
- PID: processo principale del namespace
- USER: utente proprietario
- COMMAND: comando associato
4026531832 mnt 86 1548 frank /usr/bin/dbus-broker-launch --scope user
- 4026531832 è l'identificativo interno del namespace (inode)
- mnt è il namespace dei mount (filesystem)
- 86 processi condividono questo namespace
- il processo di riferimento è PID 1548
- appartiene all’utente frank
- è stato creato/gestito da dbus
Con il comando lsns è possibile osservare come i namespaces siano usati continuamente dal sistema operativo anche al di fuori dei container. Browser e servizi di sistema creano namespaces dedicati per aumentare isolamento e sicurezza.
PID namespace (Process ID)
Il namespace di tipo PID fa sì che i processi visti all’interno del namespace abbiano un PID differente rispetto a quelli visti dall’esterno. All’interno del namespace si riparte dal PID come se fosse un sistema a sé con un proprio albero di processi.
Esempio PID in un ambiente isolato
Aprire una shell bash dentro un nuovo ambiente isolato usando i namespaces Linux.frank@laptop01:~$ sudo unshare --pid --fork --mount-proc bash
Il comando è così spiegato:
- sudo: serve perché creare namespaces richiede privilegi di root
- unshare: è il comando che chiede al kernel Linux di avviare un processo in un contesto separato, facendo in modo che da quel momento in poi alcune risorse di sistema come processi, filesystem o rete non siano più condivise con l’host
- --pid: crea un nuovo PID namespace
- --fork: indica ad unshare di creare un nuovo processo figlio e di eseguirlo all’interno del namespace appena creato
- --mount-proc: monta un nuovo /proc dentro il namespace, fà sì che /proc mostri solo i processi del namespace e non quelli dell’host.
- bash: è la shell che viene avviata dentro l’ambiente isolato
Dentro l'ambiente isolato nella shell con il comando ps aux vedrai la bash con PID 1 mentre fuori dall'ambiente isolato nell’host con il comando ps aux | grep bash vedrai la stessa bash ma con un PID completamente diverso.
A questo punto ti trovi all’interno del PID namespace. Ora esegui:
root@laptop01:~# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 1.6 0.0 233464 5780 pts/2 S 09:07 0:00 bash
root 40 0.0 0.0 234384 4456 pts/2 R+ 09:07 0:00 ps aux
Noterai che la shell in esecuzione ha PID 1 come se fosse il processo iniziale di un sistema a sé con un proprio albero dei processi.
Aprendo un secondo terminale sull’host e cercando lo stesso processo vedrai invece un PID differente:frank@laptop01:~$ ps aux | grep bash
frank 7311 0.0 0.0 233608 5800 pts/1 Ss 08:49 0:00 /bin/bash
frank 7874 0.0 0.0 233608 5792 pts/3 Ss 08:57 0:00 /bin/bash
root 8520 0.0 0.1 246544 10496 pts/1 S+ 09:07 0:00 sudo unshare --pid --fork --mount-proc bash
root 8554 0.0 0.0 246544 3028 pts/2 Ss 09:07 0:00 sudo unshare --pid --fork --mount-proc bash
root 8555 0.0 0.0 230368 2108 pts/2 S 09:07 0:00 unshare --pid --fork --mount-proc bash
root 8556 0.0 0.0 233464 5784 pts/2 S+ 09:07 0:00 bash
frank 8728 0.0 0.0 231292 2644 pts/3 S+ 09:10 0:00 grep --color=auto bash
All’interno del PID namespace questo processo aveva PID 1 mentre dall’esterno viene visto con PID 8556. È lo stesso processo ma con due PID diversi a seconda del namespace da cui viene osservato.
root 8556 0.0 0.0 233464 5784 pts/2 S+ 09:07 0:00 bash
Questo esempio mostra come il PID namespace non crei nuovi processi ma isoli la vista dei PID uno dei meccanismi fondamentali su cui si basa l’isolamento dei container.
Quindi lo stesso processo può avere due PID diversi:- dentro il namespace: PID = 1
- fuori dal namespace (host): PID = 8556
root@laptop01:~# exit
Sei uscito dalla bash che era PID 1 nel PID namespace e ora sei sull’host ed esegui:
frank@laptop01:~$ ps aux | grep bash
frank 7311 0.0 0.0 233608 5804 pts/1 Ss+ 08:49 0:00 /bin/bash
frank 7874 0.0 0.0 233608 5792 pts/3 Ss 08:57 0:00 /bin/bash
frank 9134 0.0 0.0 231292 2516 pts/3 S+ 09:19 0:00 grep --color=auto bash
Non vedi più nessun sudo unshare, nessun unshare e nessuna bash con PID 8556. Questo conferma che il PID namespace non esiste più e i processi al suo interno sono stati terminati.
INFO: monitorare i processi con htop
- Premi F5 per attivare la Tree View (vista ad albero)
- Premi F3 e cerca NOME_PROCESSO (esempio: stress) per visualizzare solo i processi interessati
- Premi F2 -> Setup -> Display options per migliorare la leggibilità:
- Tree View (se non attiva)
- Show custom thread names
- Highlight program path
Esempio PID in un ambiente isolato con esecuzione dell'applicazione stress
Ora ripetiamo lo stesso esperimento fatto con chroot con il comando stress ma usando un PID namespace.
Creiamo un nuovo PID namespace e apriamo una shell al suo interno:frank@laptop01:~$ sudo unshare --pid --fork --mount-proc bash
All’interno del namespace eseguiamo stress:
root@laptop01:~# stress --cpu 1 --vm 1 --vm-bytes 100M&
Verifichiamo i processi:
root@laptop01:~# ps aux
USER
PID
%CPU
%MEM
VSZ
RSS
TTY
STAT
START
TIME
COMMAND
root
1
0.0
0.0
233464
5708
pts/2
S
10:48
0:00
bash
root
69
0.0
0.0
3552
2184
pts/2
S
11:01
0:00
stress --cpu 1
root
71
98.4
0.0
3552
696
pts/2
R
11:01
0:02
stress --cpu 1
root
72
96.0
0.6
105956
50284
pts/2
R
11:01
0:02
stress --cpu 1
root
80
0.0
0.0
234384
4440
pts/2
R+
11:01
0:00
ps aux
All’interno del PID namespace, stress non eredita i PID dell’host. I processi partono da PID 1 e costruiscono un albero di processi separato.
root@laptop01:~# pstree -p | grep stress
`-stress(69)-+-stress(71)
`-stress(72)
Il processo stress con PID 69 è il processo padre e da esso vengono generati due processi figli con PID 71 e 72.
Usare pidstat per visualizzare l'uso della CPU e della RAM:frank@laptop01:~$ pidstat -u -r -p 69,71,72 1
Time
UID
PID
%usr
%system
%guest
%wait
%CPU
CPU
Command
11:21:17
0
69
0,00
0,00
0,00
0,00
0,00
2
stress
11:21:17
0
71
99,00
0,00
0,00
0,00
99,00
1
stress
11:21:17
0
72
31,00
66,00
0,00
1,00
97,00
2
stress
Time
UID
PID
minflt/s
majflt/s
VSZ
RSS
%MEM
Command
11:21:17
0
69
0,00
0,00
3552
1964
0,02
stress
11:21:17
0
71
0,00
0,00
3552
588
0,01
stress
11:21:17
0
72
156407,00
0,00
105956
16812
0,21
stress
Aprendo un secondo terminale sull’host ed eseguendo:
frank@laptop01:~$ ps aux | grep stress
vediamo comunque i processi stress ma con PID diversi:
USER
PID
%CPU
%MEM
VSZ
RSS
TTY
STAT
START
TIME
COMMAND
root
6526
0.0
0.0
3552
2184
pts/2
S
11:01
0:00
stress --cpu 1 --vm 1 --vm-bytes 100M
root
6528
95.1
0.0
3552
696
pts/2
R
11:01
5:07
stress --cpu 1 --vm 1 --vm-bytes 100M
root
6529
93.0
0.0
105956
6088
pts/2
R
11:01
5:00
stress --cpu 1 --vm 1 --vm-bytes 100M
frank
6819
0.0
0.0
231292
2520
pts/3
S+
11:07
0:00
grep --color=auto stress
notiamo che:
- dentro il namespace i PID sono piccoli e isolati
- fuori dal namespace l’host vede i PID reali
Il processo stress con PID 6526 è il processo padre e da esso vengono generati due processi figli con PID 6528 e 6529.
Il PID namespace non nasconde l’esecuzione dei processi, ma isola la loro visibilità tra l’interno del namespace e il sistema host. All’interno del namespace i processi hanno una propria vista dei PID mentre l’host continua a vederli con i PID reali.
root@laptop01:~# pstree -p | grep stress
| |-konsole(5938)-+-bash(5954)---sudo(6009)---sudo(6044)---unshare(6045)---bash(6047)---stress(6526)-+-str+
Terminare l'esecuzione del processo stress
Soluzione 1: uscendo direttamente dalla shell del namespaceroot@laptop01:~# exit
Soluzione 2: terminare il processo stress dal namespaceDal PID namespace possiamo terminare il processo padre e tutti i processi figli vengono terminati:
root@laptop01:~# kill 69
[1]+ Terminato stress --cpu 1 --vm 1 --vm-bytes 100M
root@laptop01:~# exit
Soluzione 3: terminare il processo stress dall'hostfrank@laptop01:~$ sudo kill 6526
frank@laptop01:~$ ps aux | grep stress
Con i namespaces abbiamo risolto il problema della visibilità: i processi sono isolati all’interno del namespace e i PID risultano separati rispetto al sistema host.
L’esecuzione dei processi continua però a incidere sul sistema host: i PID namespace isolano i processi ma non limitano l’uso delle risorse.
Rimane quindi un problema evidente: stress può ancora saturare CPU e memoria.
Nel prossimo articolo vedremo i cgroups che permettono di limitare e controllare l’uso delle risorse completando il modello dei container Linux.
User namespace (UID/GID)
Il namespace USER isola gli ID utente. Questo significa che lo stesso utente può avere uno User ID diverso all’interno del namespace rispetto a quello che ha al di fuori di esso.
Esempio UID in 1 ambiente isolato
Per capire come funziona la mappatura degli UID (User ID) è possibile eseguire un comando all’interno di un nuovo USER namespace e aprire una shell al suo interno.
Informazioni sull'utente corrente:
frank@laptop01:~$ id
uid=1000(frank) gid=1000(frank) gruppi=1000(frank),36(kvm),971(libvirt) contesto=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
frank@laptop01:~$ unshare -U bash
Il comando è così spiegato:
- sudo non serve
- unshare esegue un processo in un ambiente isolato. E' il comando che chiede al kernel Linux di avviare un processo in un contesto separato, facendo in modo che da quel momento in poi alcune risorse di sistema come processi, filesystem o rete non siano più condivise con l’host
- -U: crea un user namespace
- bash: è la shell che viene avviata dentro l’ambiente isolato
In questo caso, eseguendo il comando id si nota che l’utente non è più frank ma nobody un utente senza privilegi: questo dimostra che gli UID e i permessi all’interno del namespace sono distinti da quelli del sistema host.
nobody@laptop01:~frank@laptop01:~frank@laptop01:~$ id
uid=65534(nobody) gid=65534(nobody) gruppi=65534(nobody) contesto=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Con il flag --map-root-user è possibile far finta di essere root all’interno di un user namespace perché il nostro UID viene rimappato a 0. Dal punto di vista del processo che gira nel namespace sembriamo quindi l’utente root. In realtà si tratta solo di una mappatura interna: non siamo davvero root sul sistema host e provando ad accedere alla directory /root il risultato sarà permesso negato. Ciò garantisce l'aumento della sicurezza perché anche se l’app crede di essere root non può fare danni fuori dal namespace.
frank@laptop01:~$ unshare -U --map-root-user bash
root@laptop01:~# id
uid=0(root) gid=0(root) gruppi=0(root),65534(nobody) contesto=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
root@laptop01:~# ls /root
ls: impossibile aprire la directory '/root': Permesso negato
Cgroup namespace
Il namespace cgroup serve a nascondere al processo a quale cgroup appartiene.
I cgroup sono il meccanismo che Linux usa per dire a un processo quanta CPU, quanta memoria o quante altre risorse può consumare. Nei container servono soprattutto a evitare che un container si prenda tutte le risorse dell’host a scapito degli altri.
All’inizio i cgroup non erano isolati tra loro, quindi un processo poteva intravedere informazioni sui cgroup di altri container. Per migliorare l’isolamento è stato introdotto il cgroup namespace, che fa sì che ogni container veda solo i propri limiti e le proprie regole.
Time namespace
Il namespace TIME permette a un processo di vedere un boot time diverso. Con time non si intende l’ora dell’orologio ma il tempo di avvio del sistema (ad esempio quello usato da uptime). Questo meccanismo è usato soprattutto per mantenere coerente il tempo di esecuzione delle applicazioni quando un container viene spostato da una macchina a un’altra.
Il namespace TIME fa vedere al processo un tempo di avvio diverso del sistema. Non riguarda l’orologio o l’ora corrente ma da quanto tempo il sistema risulta acceso. Questo serve, ad esempio, quando un container viene spostato su un’altra macchina così l’applicazione continua a vedere un tempo coerente.
uptime -s
Questo comando esegue uptime -s all’interno di un nuovo time namespace separato da quello dell’host.
sudo unshare -fT --boottime 13371337 uptime -s
In pratica, al processo viene presentata una percezione del tempo diversa: il valore di boot time non è quello reale del sistema, ma uno artificiale, impostato manualmente con --boottime. Dal punto di vista del processo, il sistema sembra essersi avviato in un momento completamente diverso pur girando sulla stessa macchina.
Mount namespace (MNT)
Il mount namespace (mnt) fornisce a un processo una vista isolata del filesystem, assegnandogli un proprio insieme di mount separato da quello dell’host.
È possibile verificare quali namespace di mount sono utilizzati da un processo ispezionando il filesystem /proc:
- le informazioni si trovano in
/proc/<PID>/mountinfo - in alternativa, possiamo usare uno strumento come
findmntche mostra gli stessi dati in un formato più leggibile.
docker inspect:
docker inspect -f '{{.State.Pid}}' <CONTAINER>
Una volta ottenuto il PID possiamo visualizzare le informazioni di mount del processo con:
findmnt -N <PID>
In alternativa, possiamo leggere direttamente il file del kernel:
cat /proc/<PID>/mountinfo
I container utilizzano un filesystem root memorizzato sotto /var/lib/docker, dove il runtime gestisce i livelli delle immagini tramite OverlayFS per efficienza e prestazioni. Poiché tutti i filesystem dei container risiedono in questa directory è fondamentale proteggerla con permessi adeguati e monitorarla, mentre i dettagli sui mount attivi sono visibili in /proc/[PID]/mountinfo.
ls /var/lib/docker
sudo cat /proc/[PID]/mountinfo
Strumenti Linux come nsenter permettono di interagire direttamente con i namespace di un container ed eseguire comandi al suo interno senza usare la Docker CLI.
sudo nsenter --target [PID] --mount ls /
Esempio MNT confronto del mount namespace in due ambienti isolati e l'host
In questo esempio ci concentriamo esclusivamente sul mount namespace. Creiamo due ambienti separati e confrontiamo il comportamento dei mount tra ciascun ambiente e il sistema host.
L’obiettivo è mostrare che il mount namespace non modifica la vista iniziale del filesystem ma isola le modifiche ai mount.
1. Creazione di due ambienti con mount namespace
Apriamo due shell separate ciascuna con il proprio mount namespace ed esegui lo stesso comando:frank@laptop01:~$ sudo unshare --mount --fork bash
Il comando è così spiegato:
- sudo: necessario perché il mount namespace isola e modifica la visione del filesystem
- unshare è il comando che chiede al kernel Linux di avviare un processo in un contesto separato, facendo in modo che da quel momento in poi alcune risorse di sistema come processi, filesystem o rete non siano più condivise con l’host
- --mount crea un mount namespace: i mount fatti qui dentro non influenzano l’host
- --fork: indica ad unshare di creare un nuovo processo figlio e di eseguirlo all’interno del namespace appena creato
- bash: è la shell che viene avviata dentro l’ambiente isolato
2. Verifica iniziale dei mount
In entrambi gli ambienti e sull’host esegui:frank@laptop01:~$ mount | head
Noterai che la lista dei filesystem montati è inizialmente identica: il mount namespace parte dalla stessa vista dell’host.
3. Modifica dei mount nel primo ambiente
Nel primo ambiente esegui:root@laptop01:/home/frank# mkdir /tmp/mnt-ns1
root@laptop01:/home/frank# mount -t tmpfs tmpfs /tmp/mnt-ns1
root@laptop01:/home/frank# mount | grep mnt-ns1
tmpfs on /tmp/mnt-ns1 type tmpfs (rw,relatime,seclabel,inode64)
Il filesystem tmpfs è ora visibile solo nel primo ambiente.
4. Confronto con il secondo ambiente
Nel secondo ambiente esegui:root@laptop01:/home/frank# mount | grep mnt-ns1
Non verrà mostrato alcun risultato: il mount non è condiviso tra i due ambienti.
5. Confronto con l’host
Sull’host esegui:frank@laptop01:~$ mount | grep mnt-ns1
Anche in questo caso non verrà mostrato nulla: le modifiche al mount restano confinate al namespace.
6. Modifica dei mount nel secondo ambiente
Nel secondo ambiente esegui:root@laptop01:/home/frank# mkdir /tmp/mnt-ns2
root@laptop01:/home/frank# mount -t tmpfs tmpfs /tmp/mnt-ns2
root@laptop01:/home/frank# mount | grep mnt-ns2
tmpfs on /tmp/mnt-ns2 type tmpfs (rw,relatime,seclabel,inode64)
Il mount è visibile solo nel secondo ambiente.
IPC namespace
L’IPC namespace fornisce isolamento per i meccanismi di comunicazione tra processi, come le code di messaggi POSIX, ed è abilitato di default nei container anche se spesso passa inosservato nei casi d’uso comuni.
Network namespace (NET)
Il network namespace (NET) fornisce a un processo un ambiente di rete isolato (interfacce, routing e porte) permettendo ai container di usare le proprie configurazioni di rete senza interferire tra loro.
E' possibile ispezionare il network namespace di un container usando strumenti Linux standard come nsenter, ottenendo prima il PID del container e poi accedendo al namespace di rete per analizzarne configurazione e indirizzi IP:sudo nsenter --target 3619 -n ip addr
Esempio NET con 2 ambienti isolati in comunicazione tra loro e con l’host
In questo esempio creiamo due ambienti isolati: ogni ambiente è realizzato con namespace network in modo da ottenere un’istanza separata dal sistema host collegati tra loro.
Prima di iniziare il laboratorio è utile chiarire il ruolo dei principali elementi utilizzati negli esempi di configurazione dei network namespace.
Network namespace
-
ns1– primo ambiente isolato a livello di rete. Possiede una propria interfaccia di loopback, una tabella di routing indipendente e non vede direttamente la rete dell’host. -
ns2– secondo ambiente isolato a livello di rete completamente separato dans1e dal sistema host.
Interfacce di rete virtuali (veth)
Le interfacce veth funzionano sempre a coppie e rappresentano un collegamento Ethernet virtuale tra due contesti di rete.
-
veth-host1– estremità della coppia veth presente sul sistema host e collegata al namespacens1. -
veth-ns1– estremità della stessa coppia veth spostata all’interno del namespacens1e visibile solo da esso.
-
veth-host2– estremità della coppia veth presente sul sistema host e collegata al namespacens2. -
veth-ns2– estremità della stessa coppia veth spostata all’interno del namespacens2.
Relazione tra host e ambienti isolati
Host
|__ veth-host1 _______ veth-ns1 -> ns1
|__ veth-host2 _______ veth-ns2 -> ns2
In questo laboratorio l’host possiede entrambe le interfacce veth-host1 e veth-host2 e agisce da punto di collegamento (router) tra i due ambienti isolati.
I network namespace forniscono l’isolamento della rete mentre le interfacce veth permettono la comunicazione controllata tra ambienti isolati e sistema host.
1. Creazione della rete degli ambienti isolati
Sull'host eseguiamo i rispettivi comandi:frank@laptop01:~$ sudo ip netns add ns1
frank@laptop01:~$ sudo ip netns add ns2
Questi comandi creano i network namespace che verranno utilizzati dai due ambienti isolati.
Verifica sull’host:frank@laptop01:~$ ip netns list
2. Creazione delle interfacce virtuali
Sull’host creiamo due coppie di interfacce virtuali (veth)
che collegano ciascun ambiente isolato al sistema host.
frank@laptop01:~$ sudo ip link add veth-ns1 type veth peer name veth-host1
frank@laptop01:~$ sudo ip link add veth-ns2 type veth peer name veth-host2
Associare un’estremità di ogni coppia al rispettivo ambiente isolato:
frank@laptop01:~$ sudo ip link set veth-ns1 netns ns1
frank@laptop01:~$ sudo ip link set veth-ns2 netns ns2
3. Configurazione della rete sull’host
Comandi eseguiti sull’host:frank@laptop01:~$ sudo ip addr add 10.0.0.1/24 dev veth-host1
frank@laptop01:~$ sudo ip addr add 10.0.1.1/24 dev veth-host2
frank@laptop01:~$ sudo ip link set veth-host1 up
frank@laptop01:~$ sudo ip link set veth-host2 up
L’host diventa il punto di collegamento tra i due ambienti isolati.
4. Configurazione della rete nei due ambienti isolati
I seguenti comandi vengono lanciati dall’host ma eseguiti all’interno dei due ambienti isolati.
frank@laptop01:~$ sudo ip netns exec ns1 ip addr add 10.0.0.2/24 dev veth-ns1
frank@laptop01:~$ sudo ip netns exec ns1 ip link set veth-ns1 up
frank@laptop01:~$ sudo ip netns exec ns1 ip link set lo up
frank@laptop01:~$ sudo ip netns exec ns2 ip addr add 10.0.1.2/24 dev veth-ns2
frank@laptop01:~$ sudo ip netns exec ns2 ip link set veth-ns2 up
frank@laptop01:~$ sudo ip netns exec ns2 ip link set lo up
5. Abilitare il routing sull’host
Con net.ipv4.ip_forward=1 l’host diventa un punto di passaggio tra reti diverse permettendo la comunicazione tra i network namespace.
Comando eseguito sull’host:frank@laptop01:~$ sudo sysctl -w net.ipv4.ip_forward=1
In questo laboratorio l’host non è solo un sistema di supporto ma svolge il ruolo di router software tra i due ambienti isolati.
6. Rotte e firewall
Ogni ambiente isolato conosce solo la propria rete locale. Per permettere la comunicazione con l’altro ambiente è necessario configurare una rotta di default verso l’host che agisce da router tra le due reti.Nel namespace ns1:
frank@laptop01:~$ sudo ip netns exec ns1 ip route add default via 10.0.0.1
Nel namespace ns2:
frank@laptop01:~$ sudo ip netns exec ns2 ip route add default via 10.0.1.1
Anche se il routing IP è abilitato sull’host il traffico tra i namespace può essere bloccato dal firewall. È quindi necessario consentire il forwarding tra le interfacce veth altrimenti i pacchetti vengono filtrati.
Se usi firewalld (Fedora / AlmaLinux / Rocky Linux)frank@laptop01:~$ sudo firewall-cmd --zone=trusted --add-interface=veth-host1 --permanent
frank@laptop01:~$ sudo firewall-cmd --zone=trusted --add-interface=veth-host2 --permanent
frank@laptop01:~$ sudo firewall-cmd --reload
7. Avvio dei due ambienti isolati completi
Ora avviamo una shell completa all’interno di ciascun ambiente isolato aggiungendo anche il PID e il mount namespace.
Comandi eseguiti sull’host:frank@laptop01:~$ sudo ip netns exec ns1 unshare --pid --mount --fork --mount-proc bash
frank@laptop01:~$ sudo ip netns exec ns2 unshare --pid --mount --fork --mount-proc bash
I due comandi avviano una shell bash in due ambienti isolati distinti ns1 e ns2. Ogni ambiente ha rete, processi e mount separati, con un proprio PID 1 e un /proc dedicato. I due ambienti non vedono né i processi né il filesystem né la rete l’uno dell’altro e dell’host.
8. Test di comunicazione tra ambienti isolati e host
Esecuzione all'interno dell'host:frank@laptop01:~$ ip a
Esecuzione all'interno del primo ambiente isolato:
root@laptop01:~# ip a
root@laptop01:~# ping 10.0.1.2
Esecuzione all'interno del secondo ambiente isolato:
root@laptop01:~# ip a
root@laptop01:~# ping 10.0.0.2
Esecuzione all'interno del primo ambiente isolato verso l’host:
root@laptop01:~# ping 10.0.0.1
Esecuzione all'interno del secondo ambiente isolato verso l’host:
root@laptop01:~# ping 10.0.1.1
9. Esecuzione del comando stress nei due ambienti isolati
All’interno di ciascun ambiente isolato eseguiamo il comando stress.
Comando eseguito all’interno degli ambienti isolati:root@laptop01:~# stress --cpu 1 --vm 1 --vm-bytes 100M&
Verifichiamo i processi nei due ambienti isolati:
root@laptop01:~# ps aux | grep stress
root@laptop01:~# pstree -p | grep stress
Ogni ambiente isolato vede processi con PID che partono da 1 mentre l’host continua a vedere l’esecuzione reale dei processi con PID differenti.
10. Uscita dai due ambienti isolati
Possiamo eliminare il nuovo network namespace appena creato e chiudere la shell al suo interno eseguendo il comando exit nei due ambienti isolati:root@laptop01:~# exit
Ora sull’host esegui:
frank@laptop01:~$ ip a
frank@laptop01:~$ ps aux | grep stress
Il comando ip a eseguito sull’host mostra esclusivamente le
interfacce di rete appartenenti al sistema host.
Dopo l’uscita dai due ambienti isolati con il comando exit rilanciando ip a sull’host si osserva che le interfacce veth-host1 e veth-host2 sono ancora presenti.
Questo comportamento è normale: la chiusura della shell termina i processi e il PID namespace ma non rimuove automaticamente le interfacce di rete né i network namespace.
Solo dopo aver eliminato esplicitamente le interfacce veth con il comando ip link delete e i network namespace con ip netns delete il comando ip a tornerà a mostrare esclusivamente le interfacce di rete originali dell’host.
11. Pulizia: eliminare le interfacce di rete create sull’host
Al termine del laboratorio è buona pratica rimuovere le interfacce veth create sull’host in modo da ripristinare lo stato iniziale della rete. Le interfacce veth-host1 e veth-host2 esistono sul sistema host e quindi vanno eliminate dall’host.
frank@laptop01:~$ sudo ip link delete veth-host1
frank@laptop01:~$ sudo ip link delete veth-host2
Dopo questi comandi verranno eliminati automaticamente anche:
- veth-ns1
- veth-ns2
Verifica che le interfacce non siano più presenti:
frank@laptop01:~$ ip link
Non dovresti più vedere veth-host1 e veth-host2.
Per eliminare completamente anche i network namespace creati per il test:
frank@laptop01:~$ sudo ip netns delete ns1
frank@laptop01:~$ sudo ip netns delete ns2
Verifica che l’elenco sia vuoto:
frank@laptop01:~$ ip netns list
Verifica che l’elenco sia vuoto:
frank@laptop01:~$ ip a
12. Pulizia aggiuntiva (opzionale): IP forwarding e firewalld
Se avevi abilitato il routing IP solo per questo esempio puoi disattivarlo:
frank@laptop01:~$ sudo sysctl -w net.ipv4.ip_forward=0
Se avevi aggiunto le interfacce alla zona trusted di firewalld puoi rimuoverle:
frank@laptop01:~$ sudo firewall-cmd --zone=trusted --remove-interface=veth-host1 --permanent
frank@laptop01:~$ sudo firewall-cmd --zone=trusted --remove-interface=veth-host2 --permanent
frank@laptop01:~$ sudo firewall-cmd --reload
Il comando ip a eseguito sull’host mostra esclusivamente le
interfacce di rete appartenenti al sistema host.
In questo modo è possibile verificare visivamente che l’ambiente di test è stato completamente rimosso e che il sistema è tornato allo stato iniziale.


