GNU/Linux >> Tutoriels Linux >  >> Debian

Espaces de noms Linux

Contexte

À partir du noyau 2.6.24, Linux prend en charge 6 types différents d'espaces de noms. Les espaces de noms sont utiles pour créer des processus plus isolés du reste du système, sans avoir besoin d'utiliser une technologie de virtualisation complète de bas niveau.

  • CLONE_NEWIPC :espaces de noms IPC :les files d'attente de messages SystemV IPC et POSIX peuvent être isolées.
  • CLONE_NEWPID :espaces de noms PID :les PID sont isolés, ce qui signifie qu'un PID virtuel à l'intérieur de l'espace de noms peut entrer en conflit avec un PID en dehors de l'espace de noms. Les PID à l'intérieur de l'espace de noms seront mappés à d'autres PID en dehors de l'espace de noms. Le premier PID à l'intérieur de l'espace de noms sera '1' qui en dehors de l'espace de noms est assigné à init
  • CLONE_NEWNET :espaces de noms réseau :les réseaux (/proc/net, IP, interfaces et routes) sont isolés. Les services peuvent être exécutés sur les mêmes ports dans les espaces de noms et des interfaces virtuelles "dupliquées" peuvent être créées.
  • CLONE_NEWNS :monter les espaces de noms. Nous avons la possibilité d'isoler les points de montage tels qu'ils apparaissent aux processus. En utilisant les espaces de noms de montage, nous pouvons obtenir des fonctionnalités similaires à chroot() mais avec une sécurité améliorée.
  • CLONE_NEWUTS :espaces de noms UTS. L'objectif principal de cet espace de noms est d'isoler le nom d'hôte et le nom NIS.
  • CLONE_NEWUSER :espaces de noms d'utilisateurs. Ici, les ID d'utilisateur et de groupe sont différents à l'intérieur et à l'extérieur des espaces de noms et peuvent être dupliqués.

Examinons d'abord la structure d'un programme C, nécessaire pour démontrer les espaces de noms de processus. Ce qui suit a été testé sur Debian 6 et 7. Tout d'abord, nous devons allouer une page de mémoire sur la pile et définir un pointeur vers la fin de cette page de mémoire. Nous utilisons alloca pour allouer de la mémoire dans la pile plutôt que malloc qui allouerait de la mémoire sur le tas.

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

Ensuite, nous utilisons clone pour créer un processus enfant, en transmettant l'emplacement de notre pile enfant 'mem', ainsi que les drapeaux requis pour spécifier un nouvel espace de noms. Nous spécifions 'callee' comme fonction à exécuter dans l'espace enfant :

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

Après avoir appelé clone, nous attendons la fin du processus enfant avant de terminer le parent. Si ce n'est pas le cas, le flux d'exécution parent continuera et se terminera immédiatement après, effaçant l'enfant avec :

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

Enfin, nous reviendrons au shell avec le code de sortie de l'enfant :

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

Maintenant, regardons la fonction appelée :

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;
}

Ici, nous montons un système de fichiers /proc, puis définissons l'uid (ID utilisateur) et le gid (ID de groupe) sur la valeur 'u' avant de générer le shell /bin/bash. LXC est un outil de virtualisation au niveau du système d'exploitation utilisant des cgroups et des espaces de noms pour l'isolation des ressources. Mettons tout cela ensemble, en définissant 'u' sur 65534 qui est l'utilisateur "nobody" et le groupe "nogroup" sur 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;
}

Pour exécuter le code produit ce qui suit :

[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$ 

Notez que l'UID et le GID sont définis sur personne et nogroup. Notez spécifiquement que la sortie complète de ps ne montre que deux processus en cours d'exécution et que leurs PID sont 1 et 5 respectivement. Passons maintenant à l'utilisation de ip netns pour travailler avec les espaces de noms réseau. Tout d'abord, confirmons qu'aucun espace de noms n'existe actuellement :

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

Dans ce cas, soit ip a besoin d'une mise à jour, soit le noyau en a besoin. En supposant que vous ayez un noyau plus récent que 2.6.24, il s'agit très probablement d'ip. Après la mise à niveau, la liste ip netns ne devrait rien renvoyer par défaut. Ajoutons un nouvel espace de noms appelé 'ns1' :

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

Commençons par lister les interfaces actuelles :

[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

Maintenant, pour créer une nouvelle interface virtuelle et l'ajouter à notre nouvel espace de noms. Les interfaces virtuelles sont créées par paires et sont liées les unes aux autres - imaginez un câble croisé virtuel :

[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 affichera également l'ajout de veth0 et veth1.

Super, maintenant pour assigner nos nouvelles interfaces à l'espace de noms. Notez que ip netns exec est utilisé pour exécuter des commandes dans l'espace de noms :

[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 n'affichera désormais que veth0, car veth1 se trouve dans l'espace de noms ns1.

Devrions-nous supprimer veth0/veth1 :

ip netns exec ns1 ip link del veth1

Nous pouvons maintenant attribuer l'adresse IP 192.168.5.5/24 à veth0 sur notre hébergeur :

ifconfig veth0 192.168.5.5/24

Et assignez veth1 192.168.5.10/24 dans ns1 :

ip netns exec ns1 ifconfig veth1 192.168.5.10/24 up

Pour exécuter la liste d'adresses IP sur notre hôte et dans notre espace de noms :

[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

Pour afficher les tables de routage à l'intérieur et à l'extérieur de l'espace de noms :

[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 

Enfin, pour connecter nos interfaces physiques et virtuelles, nous aurons besoin d'un pont. Relions eth0 et veth0 sur l'hôte, puis utilisons DHCP pour obtenir une adresse IP dans l'espace de noms 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

br0 s'est vu attribuer l'adresse IP 192.168.3.122/24. Passons maintenant à l'espace de noms :

[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

Excellent! veth1 a été attribué 192.168.3.248/24

IO Digital Sec
Consultant Linux


Debian
  1. Démystifier les espaces de noms et les conteneurs sous Linux

  2. Les 7 espaces de noms Linux les plus utilisés

  3. Comment effectuer un chroot avec des espaces de noms Linux ?

  4. Linux - Comment répertorier les espaces de noms sous Linux ?

  5. MX Linux contre Ubuntu

À la commande sous Linux

Commande Df sous Linux

Commande mail sous Linux

Construire un conteneur Linux à la main à l'aide d'espaces de noms

Commande Linux Perf

Linux contre Unix