GNU/Linux >> Linux Esercitazione >  >> Debian

Spazi dei nomi Linux

Sfondo

A partire dal kernel 2.6.24, Linux supporta 6 diversi tipi di namespace. Gli spazi dei nomi sono utili per creare processi più isolati dal resto del sistema, senza la necessità di utilizzare una tecnologia di virtualizzazione di basso livello completa.

  • CLONE_NEWIPC:Spazi dei nomi IPC:le code di messaggi SystemV IPC e POSIX possono essere isolate.
  • CLONE_NEWPID:Spazi dei nomi PID:i PID sono isolati, il che significa che un PID virtuale all'interno dello spazio dei nomi può entrare in conflitto con un PID al di fuori dello spazio dei nomi. I PID all'interno dello spazio dei nomi verranno mappati ad altri PID al di fuori dello spazio dei nomi. Il primo PID all'interno dello spazio dei nomi sarà '1' che al di fuori dello spazio dei nomi è assegnato a init
  • CLONE_NEWNET:Spazi dei nomi di rete:le reti (/proc/net, IP, interfacce e percorsi) sono isolate. I servizi possono essere eseguiti sulle stesse porte all'interno degli spazi dei nomi e possono essere create interfacce virtuali "duplicate".
  • CLONE_NEWNS:Monta spazi dei nomi. Abbiamo la possibilità di isolare i punti di montaggio così come appaiono ai processi. Usando gli spazi dei nomi di montaggio, possiamo ottenere funzionalità simili a chroot() ma con una maggiore sicurezza.
  • CLONE_NEWUTS:spazi dei nomi UTS. Lo scopo principale di questo spazio dei nomi è isolare il nome host e il nome NIS.
  • CLONE_NEWUSER:spazi dei nomi utente. Qui, gli ID utente e gruppo sono diversi all'interno e all'esterno degli spazi dei nomi e possono essere duplicati.

Diamo un'occhiata prima alla struttura di un programma C, necessario per dimostrare gli spazi dei nomi dei processi. Quanto segue è stato testato su Debian 6 e 7. Per prima cosa, dobbiamo allocare una pagina di memoria nello stack e impostare un puntatore alla fine di quella pagina di memoria. Usiamo alloca per allocare la memoria dello stack piuttosto che malloc che allocherebbe la memoria nell'heap.

void *mem = alloca(sysconf(_SC_PAGESIZE)) + sysconf(_SC_PAGESIZE);

Successivamente, utilizziamo clone per creare un processo figlio, passando la posizione del nostro stack figlio 'mem', nonché i flag richiesti per specificare un nuovo spazio dei nomi. Specifichiamo 'chiamato' come funzione da eseguire all'interno dello spazio figlio:

mypid = clone(callee, mem, SIGCHLD | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | CLONE_FILES, NULL);

Dopo aver chiamato clone, aspettiamo che il processo figlio finisca, prima di terminare il genitore. In caso contrario, il flusso di esecuzione padre continuerà e terminerà immediatamente dopo, ripulendo il figlio con esso:

while (waitpid(mypid, &r, 0) < 0 && errno == EINTR)
{
	continue;
}

Infine, torniamo alla shell con il codice di uscita del figlio:

if (WIFEXITED(r))
{
	return WEXITSTATUS(r);
}
return EXIT_FAILURE;

Ora, diamo un'occhiata alla funzione chiamata:

static int callee()
{
	int ret;
	mount("proc", "/proc", "proc", 0, "");
	setgid(u);
	setgroups(0, NULL);
	setuid(u);
	ret = execl("/bin/bash", "/bin/bash", NULL);
	return ret;
}

Qui, montiamo un filesystem /proc, quindi impostiamo uid (ID utente) e gid (ID gruppo) sul valore di 'u' prima di generare la shell /bin/bash. LXC è uno strumento di virtualizzazione a livello di sistema operativo che utilizza cgroup e namespace per l'isolamento delle risorse. Mettiamo tutto insieme, impostando 'u' su 65534 che è l'utente "nobody" e il gruppo "nogroup" su Debian:

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <grp.h>
#include <alloca.h>
#include <errno.h>
#include <sched.h>
static int callee();
const int u = 65534;
int main(int argc, char *argv[])
{
	int r;
	pid_t mypid;
	void *mem = alloca(sysconf(_SC_PAGESIZE)) + sysconf(_SC_PAGESIZE);
	mypid = clone(callee, mem, SIGCHLD | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | CLONE_FILES, NULL);
	while (waitpid(mypid, &r, 0) < 0 && errno == EINTR)
	{
		continue;
	}
	if (WIFEXITED(r))
	{
		return WEXITSTATUS(r);
	}
	return EXIT_FAILURE;
}
static int callee()
{
	int ret;
	mount("proc", "/proc", "proc", 0, "");
	setgid(u);
	setgroups(0, NULL);
	setuid(u);
	ret = execl("/bin/bash", "/bin/bash", NULL);
	return ret;
}

Per eseguire il codice si ottiene quanto segue:

[email protected]:~/pen/tmp# gcc -O -o ns.c -Wall -Werror -ansi -c89 ns.c
[email protected]:~/pen/tmp# ./ns
[email protected]:~/pen/tmp$ id
uid=65534(nobody) gid=65534(nogroup)
[email protected]:~/pen/tmp$ ps auxw
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
nobody       1  0.0  0.0   4620  1816 pts/1    S    21:21   0:00 /bin/bash
nobody       5  0.0  0.0   2784  1064 pts/1    R+   21:21   0:00 ps auxw
[email protected]:~/pen/tmp$ 

Si noti che l'UID e il GID sono impostati su nessuno e nogroup. Si noti in particolare che l'output completo di ps mostra solo due processi in esecuzione e che i loro PID sono rispettivamente 1 e 5. Ora, passiamo all'utilizzo di ip netns per lavorare con gli spazi dei nomi di rete. Innanzitutto, confermiamo che attualmente non esistono spazi dei nomi:

[email protected]:~# ip netns list
Object "netns" is unknown, try "ip help".

In questo caso, o IP ha bisogno di un aggiornamento o il kernel lo fa. Supponendo che tu abbia un kernel più recente di 2.6.24, molto probabilmente è ip. Dopo l'aggiornamento, l'elenco ip netns non dovrebbe restituire nulla per impostazione predefinita. Aggiungiamo un nuovo spazio dei nomi chiamato 'ns1':

[email protected]:~# ip netns add ns1
[email protected]:~# ip netns list
ns1

Per prima cosa, elenchiamo le interfacce correnti:

[email protected]:~# ip link list
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT qlen 1000
    link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff

Ora per creare una nuova interfaccia virtuale e aggiungerla al nostro nuovo spazio dei nomi. Le interfacce virtuali sono create in coppia e sono collegate tra loro:immagina un cavo crossover virtuale:

[email protected]:~# ip link add veth0 type veth peer name veth1
[email protected]:~# ip link list
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT qlen 1000
    link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff
3: veth1:  mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether d2:e9:52:18:19:ab brd ff:ff:ff:ff:ff:ff
4: veth0:  mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether f2:f7:5e:e2:22:ac brd ff:ff:ff:ff:ff:ff
ifconfig -a ora mostrerà anche l'aggiunta di veth0 e veth1.

Ottimo, ora per assegnare le nostre nuove interfacce allo spazio dei nomi. Nota che ip netns exec viene utilizzato per eseguire comandi all'interno dello spazio dei nomi:

[email protected]:~# ip link set veth1 netns ns1
[email protected]:~# ip netns exec ns1 ip link list
1: lo:  mtu 65536 qdisc noop state DOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: veth1:  mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether d2:e9:52:18:19:ab brd ff:ff:ff:ff:ff:ff
ifconfig -a ora mostrerà solo veth0, poiché veth1 è nello spazio dei nomi ns1.

Se vogliamo eliminare veth0/veth1:

ip netns exec ns1 ip link del veth1

Ora possiamo assegnare l'indirizzo IP 192.168.5.5/24 a veth0 sul nostro host:

ifconfig veth0 192.168.5.5/24

E assegna veth1 192.168.5.10/24 entro ns1:

ip netns exec ns1 ifconfig veth1 192.168.5.10/24 up

Per eseguire l'elenco di indirizzi IP sia sul nostro host che all'interno del nostro spazio dei nomi:

[email protected]:~# ip addr list
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0:  mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
    link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.122/24 brd 192.168.3.255 scope global eth0
    inet6 fe80::20c:29ff:fe65:259e/64 scope link 
       valid_lft forever preferred_lft forever
6: veth0:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 86:b2:c7:bd:c9:11 brd ff:ff:ff:ff:ff:ff
    inet 192.168.5.5/24 brd 192.168.5.255 scope global veth0
    inet6 fe80::84b2:c7ff:febd:c911/64 scope link 
       valid_lft forever preferred_lft forever
[email protected]:~# ip netns exec ns1 ip addr list
1: lo:  mtu 65536 qdisc noop state DOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 12:bd:b6:76:a6:eb brd ff:ff:ff:ff:ff:ff
    inet 192.168.5.10/24 brd 192.168.5.255 scope global veth1
    inet6 fe80::10bd:b6ff:fe76:a6eb/64 scope link 
       valid_lft forever preferred_lft forever

Per visualizzare le tabelle di routing all'interno e all'esterno dello spazio dei nomi:

[email protected]:~# ip route list
default via 192.168.3.1 dev eth0  proto static 
192.168.3.0/24 dev eth0  proto kernel  scope link  src 192.168.3.122 
192.168.5.0/24 dev veth0  proto kernel  scope link  src 192.168.5.5 
[email protected]:~# ip netns exec ns1 ip route list
192.168.5.0/24 dev veth1  proto kernel  scope link  src 192.168.5.10 

Infine, per connettere le nostre interfacce fisiche e virtuali, avremo bisogno di un bridge. Colleghiamo eth0 e veth0 sull'host, quindi utilizziamo DHCP per ottenere un IP all'interno dello spazio dei nomi ns1:

[email protected]:~# brctl addbr br0
[email protected]:~# brctl addif br0 eth0
[email protected]:~# brctl addif br0 veth0
[email protected]:~# ifconfig eth0 0.0.0.0
[email protected]:~# ifconfig veth0 0.0.0.0
[email protected]:~# dhclient br0
[email protected]:~# ip addr list br0
7: br0:  mtu 1500 qdisc noqueue state UP 
    link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.122/24 brd 192.168.3.255 scope global br0
    inet6 fe80::20c:29ff:fe65:259e/64 scope link 
       valid_lft forever preferred_lft forever

A br0 è stato assegnato un IP di 192.168.3.122/24. Ora per lo spazio dei nomi:

[email protected]:~# ip netns exec ns1 dhclient veth1
[email protected]:~# ip netns exec ns1 ip addr list
1: lo:  mtu 65536 qdisc noop state DOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 12:bd:b6:76:a6:eb brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.248/24 brd 192.168.3.255 scope global veth1
    inet6 fe80::10bd:b6ff:fe76:a6eb/64 scope link 
       valid_lft forever preferred_lft forever

Eccellente! veth1 è stato assegnato 192.168.3.248/24

IO Digital Sec
Consulente Linux


Debian
  1. Demistificare gli spazi dei nomi e i contenitori in Linux

  2. I 7 spazi dei nomi Linux più utilizzati

  3. comando IP Linux

  4. comando cd di Linux

  5. Visualizza/manipola gli spazi dei nomi di montaggio in Linux

Al comando in Linux

Comando Df in Linux

comando di posta in Linux

Costruire un container Linux a mano usando gli spazi dei nomi

Comando Linux Perf

Linux vs Unix