GNU/Linux >> Tutoriales Linux >  >> Debian

Espacios de nombres de Linux

Fondo

A partir del kernel 2.6.24, Linux admite 6 tipos diferentes de espacios de nombres. Los espacios de nombres son útiles para crear procesos que están más aislados del resto del sistema, sin necesidad de utilizar tecnología de virtualización completa de bajo nivel.

  • CLONE_NEWIPC:Espacios de nombres IPC:SystemV IPC y POSIX Message Queues pueden aislarse.
  • CLONE_NEWPID:espacios de nombres PID:los PID están aislados, lo que significa que un PID virtual dentro del espacio de nombres puede entrar en conflicto con un PID fuera del espacio de nombres. Los PID dentro del espacio de nombres se asignarán a otros PID fuera del espacio de nombres. El primer PID dentro del espacio de nombres será '1', que fuera del espacio de nombres se asigna a init
  • CLONE_NEWNET:espacios de nombres de red:las redes (/proc/net, direcciones IP, interfaces y rutas) están aisladas. Los servicios se pueden ejecutar en los mismos puertos dentro de los espacios de nombres y se pueden crear interfaces virtuales "duplicadas".
  • CLONE_NEWNS:montar espacios de nombres. Tenemos la capacidad de aislar los puntos de montaje tal como aparecen en los procesos. Usando espacios de nombres de montaje, podemos lograr una funcionalidad similar a chroot() pero con seguridad mejorada.
  • CLONE_NEWUTS:espacios de nombres UTS. El propósito principal de este espacio de nombres es aislar el nombre de host y el nombre NIS.
  • CLONE_NEWUSER:espacios de nombres de usuario. Aquí, los ID de usuario y grupo son diferentes dentro y fuera de los espacios de nombres y se pueden duplicar.

Veamos primero la estructura de un programa en C, necesaria para demostrar los espacios de nombres de procesos. Lo siguiente ha sido probado en Debian 6 y 7. Primero, necesitamos asignar una página de memoria en la pila y establecer un puntero al final de esa página de memoria. Usamos alloca para asignar memoria de pila en lugar de malloc, que asignaría memoria en el montón.

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

A continuación, usamos clon para crear un proceso secundario, pasando la ubicación de nuestra pila secundaria 'mem', así como los indicadores necesarios para especificar un nuevo espacio de nombres. Especificamos 'llamada' como la función a ejecutar dentro del espacio secundario:

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

Después de llamar a clonar, esperamos a que finalice el proceso secundario, antes de finalizar el proceso principal. De lo contrario, el flujo de ejecución del padre continuará y terminará inmediatamente después, aclarando el hijo con él:

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

Por último, volveremos al shell con el código de salida del niño:

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

Ahora, veamos la función de destinatario:

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

Aquí, montamos un sistema de archivos /proc y luego configuramos uid (ID de usuario) y gid (ID de grupo) en el valor de 'u' antes de generar el shell /bin/bash. LXC es una herramienta de virtualización a nivel de sistema operativo que utiliza cgroups y espacios de nombres para el aislamiento de recursos. Pongámoslo todo junto, configurando 'u' en 65534, que es el usuario "nadie" y el grupo "nogroup" en 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;
}

Para ejecutar el código se produce lo siguiente:

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

Tenga en cuenta que el UID y el GID están configurados para nadie y ningún grupo. Observe específicamente que la salida completa de ps muestra solo dos procesos en ejecución y que sus PID son 1 y 5 respectivamente. Ahora, pasemos al uso de ip netns para trabajar con espacios de nombres de red. Primero, confirmemos que actualmente no existen espacios de nombres:

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

En este caso, ip necesita una actualización o el kernel lo necesita. Suponiendo que tiene un kernel más nuevo que 2.6.24, lo más probable es que sea ip. Después de la actualización, la lista de redes IP no debería devolver nada de forma predeterminada. Agreguemos un nuevo espacio de nombres llamado 'ns1':

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

Primero, enumeremos las interfaces actuales:

[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

Ahora, para crear una nueva interfaz virtual y agregarla a nuestro nuevo espacio de nombres. Las interfaces virtuales se crean en pares y se vinculan entre sí; imagine un cable cruzado virtual:

[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 ahora también mostrará la adición de veth0 y veth1.

Genial, ahora para asignar nuestras nuevas interfaces al espacio de nombres. Tenga en cuenta que ip netns exec se usa para ejecutar comandos dentro del espacio de nombres:

[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 ahora solo mostrará veth0, ya que veth1 está en el espacio de nombres ns1.

Si queremos eliminar veth0/veth1:

ip netns exec ns1 ip link del veth1

Ahora podemos asignar la dirección IP 192.168.5.5/24 a veth0 en nuestro host:

ifconfig veth0 192.168.5.5/24

Y asigne veth1 192.168.5.10/24 dentro de ns1:

ip netns exec ns1 ifconfig veth1 192.168.5.10/24 up

Para ejecutar la lista de direcciones IP tanto en nuestro host como en nuestro espacio de nombres:

[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

Para ver las tablas de enrutamiento dentro y fuera del espacio de nombres:

[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 

Por último, para conectar nuestras interfaces físicas y virtuales, necesitaremos un puente. Hagamos un puente entre eth0 y veth0 en el host, y luego usemos DHCP para obtener una IP dentro del espacio de nombres 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 se le ha asignado una IP de 192.168.3.122/24. Ahora para el espacio de nombres:

[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

¡Excelente! veth1 ha sido asignado 192.168.3.248/24

Enlaces

IO Digital Sec
Consultor Linux


Debian
  1. Desmitificando espacios de nombres y contenedores en Linux

  2. comando linux mv

  3. Linux du comando

  4. Comando IP de Linux

  5. Comando cd de linux

Al mando en Linux

Comando Df en Linux

Comando de correo en Linux

Construyendo un contenedor de Linux a mano usando espacios de nombres

Comando de rendimiento de Linux

Linux frente a Unix