Tabla de contenidos
Mediante la virtualización en una sola máquina es posible tener varios sistemas, como si en lugar de un PC tuviéramos varios. Así, un ISP que ofrezca servidores dedicados puede ofrecer varios servidores virtuales sobre una máquina: cada uno funcionará como un servidor independiente con su propia IP, se podrán instalar todo tipo de servicios (DNS, mail, Jabber, web, PostgreSQL) y reiniciar y administrar independientemente.
Al sistema sobre el que se ejecuta el virtualizador se le llama host; a veces también se habla de domain 0, porque cada sistema que se ejecuta se le considera un dominio. A cada uno de los sistemas que se ejecutan sobre el virtualizador se les denomina guest. En algunos casos no hay sistema host porque el virtualizador se ejecuta directamente sobre el hardware.
Hay hardware como los mainframes de IBM, que soportan virtualización. Los Pcs no soportan virtualización: hay instrucciones de modo protegido que impiden ejecutar dos sistemas operativos simultáneamente, pero mediante software se pueden suplir (con bastante complejidad) las carencias de hardware. Así mismo mediante sofware se emulan componentes del hardware como discos, tarjeta gráfica, de red y de sonido.
Además de la virtualización de la CPU, está la del resto del hardware: tarjeta de red, discos, tarjeta gráfica... Para esta parte la mayoría de los proyectos libres toman código de Qemu.
Hay varios métodos de implementar la virtualiación del procesador:
Emuladores totales: emulan totalmente el hardware, incluyendo el procesador. Es el caso de Booch. Su rendimiento es muy pobre.
Emuladores con compilación JIT: es el caso de Qemu, cuando se ejecuta sin el módulo kqemu. El código necesita "compilarse" para la máquina virtual, si bien como los compiladores JIT de Java se hace sólo la primera vez que se ejecuta el código, luego ya está compilado. Mucho más rápidos que los emuladores totales, pero más lentos que el resto de soluciones. Se puede ejecutar como usuario normal sin instalar nada como root. Se pueden emular procesadores distintos.
Virtualizadores completos: es el caso de Vmware (software privativo), Qemu con el módulo de aceleración Kqemu (antes privativo ahora libre) y Virtual Box (software con una versión libre y otra más funcional privativa). Se basan en un VMM (Virtual Machine Monitor, también conocido como hypervisor) que mediante complicadas técnicas (como traps y análisis del código antes de su ejecución) detecta en tiempo de ejecución el código que no puede ejecutarse directamente porque afectaría a todo el sistema, modificando dinámicamente las instrucciones conflictivas. El rendimiento varía mucho según lo avanzado que sea el VMM: Vmware tiene varias patentes. Estos tres programas permiten instalar un sistema operativo sobre un virtualizador del mismo modo que sobre un PC: la ventana del virtualizador asemeja el monitor de un PC, podemos entrar en la BIOS, reiniciar el ordenador...
Paravirtualizadores: es el caso de Xen y UML (User mode Linux). En lugar de tener que detectar un VMM los casos conflictivos en tiempo de ejecución, se modifica el código fuente de los sistemas operativos para evitar esos casos conflictivos. En lugar de el VMM tener que analizar el código, es el código quien invoca al VMM cuando sea necesario. Esta técnica simplifica muchísimo el VMM y ofrece muy buen rendimiento, aunque en el caso concreto de UML el rendimiento es mediocre. La pega es que haya que parchear el sistema operativo, sobre todo para poder ejecutar Windows. Xen parcheó un Windows XP en un programa de investigación que permitía acceso al código fuente de Microsoft, pero ese tipo de licencias no permitía distribuir el resultado. UML también se puede considerar dentro de esta categoría, pues es una modificación del kernel de Linux para que pueda ejecutarse dentro de otro Linux. Xen no emula una tarjeta gráfica SVGA "completa" como Qemu, Vmware o VirtualBox, pero utiliza VNC que para el sistema guest se ve como una tarjeta VGA.
Vmware y VirtualBox no son paravirtualizadores, pero utilizan esta técnica para virtualizar la E/S en los drivers especiales que se ejecutan en el sistema guest (por ejemplo el driver de red y el de la tarjeta gráfica: se comunican con el hipervisor en lugar de ser drivers normales sobre el hardware emulado). Lo mismo planea hacer KVM.
Virtualizadores apoyados en el hardware (un tanto pretenciosamente llamados nativos): los nuevos procesadores de Intel (los Core Duo y la mayoría, pero no todos, ver la wikipedia, de los Core Duo2 añaden las extensiones VT) y los más recientes de AMD (a partir de stepping F; las extensiones se llaman SVM) cuentan con nuevas instrucciones que permiten la virtualización de la CPU. De este modo ya no es necesario ni un complicado VMM ni parchear el sistema operativo, si bien sigue siendo necesario virtualizar otros dispositivos y emular discos, tarjetas gráficas, de red... Ejemplos de estas soluciones son KVM (integrado en el kernel desde la versión 2.6.20, utiliza qemu para la virtualización del resto del hardware) y Virtual Iron (basado en Xen, pero ya no es paravirtualizador; en cualquier caso es una solución propietaria, con algo de código bajo GPL). Además Xen soporta también estas instrucciones, como método para poder ejecutar Windows o simplemente sistemas sin paravirtualizar. Los virtualizadores apoyados en el hardware son más lentos que los paravirtualizadores e incluso que los virtualizadores completos, al menos que los que tienen un VMM avanzado. VirtualBox no usa por defecto estas instrucciones, aunque las soporte, por este motivo. Lo mismo ocurre con Vmware, aunque sí lo utiliza para poder utilizar como host un sistema operativo de 64bits (VirtualBox actualmente no permite esta configuración).
Virtualización a nivel de sistema operativo (OS level Virtualization): no permite ejecutar dos sistemas operativos simultáneamente, sino servidores privados virtuales (SVP) dentro de un único servidor, es decir, es un único kernel, pero que permite aislar (isolate) los servidores. Cada servidor tendrá su propia red, espacio de disco, de memoria, se podrá reiniciar.. así mismo tendrá limitación de uso de CPU con el fin de evitar que un servidor virtual esquilme recursos de los otros. También esta tecnología se denomina como Jail, pues es extender el concepto de chroot. Tantao Linux VServer como Virtuozzo lo vienen ofreciendo proveedores de hosting desde hace años. Estas son las dos soluciones libres más destacadas:
Linux VServer: http://linux-vserver.org (no confundir con linux virtual server, que es sobre clusters). Software libre. Una limitación es que no admite usar iptables dentro de cada SVP, sino dentro del host. Tampoco admite migración de procesos. Así mismo está más limitado en lo que se virtualiza, por ejemplo no se virtualiza nada de /proc). Por otro lado la distribución host parece menos restringida que en OpenVZ, que como host sólo admite Fedora, algunas versiones de CentOS y algunas de RHEL. Hay varias distribuciones que funcionan como guest directamente (las instalamos en su partición y luego las usamos, con sus programas y librerías, pero no obviamente con su kernel) aunque otras como Gentoo no.
OpenVZ (http://openvz.org). Virtuozzo es un virtualizador bajo una licencia privativa; OpenVZ es el producto de la misma compañía que es software libre y que es la base de Virtuozzo. Un hecho positivo es que OpenVZ no es un conjunto de código GPL difícil de integrar y sin soporte como ocurre con otros productos en los que hay versión GPL y privativa: también venden soporte para OpenVZ. Admite distintas distribuciones como host, a través de templates, que son repositorios para bajar los paquetes necesarios para esa distribución. Hosting con OpenVZ: http://www.vpslink.com/vps-hosting/. En algún caso es más caro con OpenVZ que con Virtuozzo, por ser menos maduro y requerir más recursos...
Entre las posibilidades más avanzadas de algunos virtualizadores está el migrar en caliente con una indisponibilidad inapreciable un dominio de un servidor a otro. Dentro de los productos libres lo permiten Xen, KVM y OpenVZ. Vmware lo permite sólo en su servidor de pago (EXS). En Vmware Server (antiguo Vmware GSX), ahora gratis (que no libre) no es posible. Para saber más sobre este tema: http://kvm.qumranet.com/kvmwiki/Migration
Un debate interesante es el de los virtualizadores que se ejecutan "bare metal", es decir, directamente sobre el hardware, en lugar de sobre un sistema operativo "host". Vmware EXS sigue esta fórmula de forma pura, de tal modo que sólo funciona con determinado hardware. En el caso de Xen, se ejecuta sobre un host, pero asume parte de las funciones de un sistema operativo, al tener código por ejemplo para planificar la CPU. En cambio KVM utiliza todo lo que aporta Linux, lo que para muchos desarrolladores es la opción preferible pues difícilmente una empresa que desarrolla un producto de virtualización tendrá más experiencia en elementos de sistemas operativos como un planificador, que el grupo de personas que llevan desarrollando estos componentes desde hace años en un sistema tan extendido como el kernel Linux.
Software interesante:
http://virt-manager.et.redhat.com/ interfaz gráfica sobre todo para Xen, pero también soporta
KVM y Qemu. Incluye parte para gestionar redes.
Información muy completa (navegar por los enlaces del marco de la izquierda):
http://virt.kernelnewbies.org/TechOverview
En VMWare a la hora de crear un dispositivo de red se puede elegir entre:
NAT: en el host aparecerá como la interfaz vmnet8, pero no podremos hacer nada con esta interfaz, por ejemplo usar Iptables para restringir las Ips que se pueden alcanzar desde el sistema que se ejecuta dentro de Vmware. El motivo es que no se usa realmente el NAT del núcleo sino un demonio, vmnet-natd, por lo que los paquetes no pasan realmente por vmnet8. Si se quiere abrir puertos, basta con editar el fichero /etc/vmware/vmnet8/nat.conf
Bridge: también se implementa utilizando un demonio privativo, en este caso vmnet-bridge, en lugar de utilizar el soporte del núcleo, por lo que no se puede restringir la red con iptables sobre la interfaz vmnet2. Como en el caso de vmnet8, realmente el tráfico no pasa por esta interfaz.
Host only: aparentemente este modo es el más limitado. Pues bien, en realidad es el más flexible, pues por la interfaz de red que crea, vmnet1, sí que pasan todos los paquetes. De este modo podemos utilizar esta interfaz para hacer NAT a través de iptables, o crear un bridge con brctl. Al usar iptables, podemos restringir el tráfico como con cualquier otra unidad de red.
En Qemu hay dos formas de utilizar la red. Por defecto se usa -net socket, que sería el equivalente al modo NAT de Vmware. Mediante la opción -redir se pueden abrir puertos de servidor. Una diferencia interesante sobre Vmware es que esta solución se implementa enteramente en espacio de usuario, por lo que no se crea interfaz de red ni se precisa cargar ningún módulo del kernel, lo que es bueno porque en el caso de Vmware es un módulo privativo que activa la marca "tained" del kérnel, con lo que perdemos toda opción de soporte. Además así no hay que tener privilegios de superusuario para instalar Qemu (Vmware no requiere privilegios para ejecutarse, pero sí hace falta para insertar el módulo en el kernel).
Las interfaces de red vmnet1, vmnet2, vment8, se crean al ejecutar vmware-config, en la parte de configuración de red. Una posibilidad interesante si vamos a ejecutar varios sistemas simultáneamente o si queremos que un sistema tenga más de una interfaz es crear más de un dispositivo de red para "host only", de modo que aparte de vmnet1 haya otros. De otro modo todas las máquinas virtuales estarán conectadas a la misma red, la de vmnet1. Así, una máquina podrá tener la IP 192.168.152.128, otra la 192.168.152.129 y el host la 192.168.152.1; las dos máquinas virtuales se verán la una a la otra y podrán comunicarse, aunque eso sí, con un sniffer una máquina no verá el tráfico de los otros nodos.
La red vmnet1 que crea Vmware es de máscara de red 255.255.255.0; ejecuta un servidor DHCP automáticamente, cuya configuración y arrendamientos pueden verse en /etc/vmware/vmnet1. Pero nada impide que podamos cambiar esta configuración, pues se emula a una interfaz ethernet. Por ejemplo un nodo puede cambiar su IP a otra de la red o incluso crear un alias y usar una red distinta, tanto en el host como en las máquinas virtuales.
Si usamos vmnet1 para hacer NAT utilizando iptables, hay que tener en cuenta que habrá que configurar la red de la máquina virtual para añadirle una ruta por defecto y la configuración del DNS y que el firewall deberá permitir que llegue al servidor DNS. Para pasar la configuración de DNS y ruta por defecto podemos usar el servidor DHCP de Vmware: "option routers" y "option domain-name-servers".
Un inconveniente de -net socket es que como ocurre con los modos NAT y Bridge de Vmware los paquetes no pasan por ninguna interfaz de red, por lo que no se puede utilizar iptables para restringir la red.
La solución está en el uso del soporte de TUN/TAP del kernel. Consiste en que una aplicación puede abrir el fichero de dispositivo /dev/net/tun y con eso crear una nueva interfaz de red (por defecto tun0). Todo lo que la aplicación escriba en ese dispositivo se recibirá en la interfaz de red recién creada; de igual modo todo lo que llegue a esa interfaz de red (por ejemplo a través de enrutamiento) lo leerá la aplicación del fichero de dispositivo.
Los dispositivos TUN operan a nivel IP y son punto a punto. Los dispositivos TAP operan a nivel 2 y son multipunto, como las interfaces eth*. Un dispositivo tap0 y un dispositivo vmnet1 vienen a funcionar de forma muy similar y a nivel de ejemplos de configuración con iptables o brctl donde aparezca un tap0 podría aparecer un vmnet1 y viceversa. Por lo general se usa tun0 en lugar de tap0; para la mayoría de los usos son equivalentes por lo que es mucho más habitual utilizar tun0 que resulta más sencillo y directo.
Normalmente un dispositivo tun/tap sólo existe mientras el programa no cierra el fichero /dev/net/tun. Esto a veces es problemático, especialmente porque para crear un dispositivo TUN/TAP hacen falta privilegios de superusuario. Afortunadamente, con root se puede abrir un dispositivo en modo persistente para un determinado usuario, de modo que luego un programa ejecutado por ese usario sin privilegios podrá abrir ese dispositivo tun/tap creado para él por el root. Este se puede hacer con el programa tunctl, que forma parte del paquete uml-utilities.
¿Por qué no usa Vmware TUN/TAP en lugar de vmnet1? quizás por unicidad entre plataformas, o por diferencia de implementación; es posible que vmnet1 también permita driver de red de la máquina virtual directamente en espacio del kernel.
TUN/TAP es la solución utilizada también por otros virtualizadores, como Virtual Box. Muy interesantes los documentos sobre red avanzada en la web de Virtual Box: http://www.virtualbox.org/wiki/User_HOWTOS.
Permitir acceso sólo a red local 192.168.10.0 y además excluir el nodo 9 de esa red.
iptables -A FORWARD -i vmnet1 --destination 192.168.10.9 -j REJECT
iptables -A FORWARD -i vmnet1 --destination 192.168.10.0/24 -j ACCEPT
iptables -A FORWARD -i vmnet1 -j REJECT
iptables -t nat -A POSTROUTING -j MASQUERADE -o eth0
echo 1 > /proc/sys/net/ipv4/conf/eth0/forwarding
echo 1 > /proc/sys/net/ipv4/conf/vmnet1/forwarding
Crear un bridge, pero prohibiendo el acceso a la IP 157.88.10.20. Se puede filtrar con ebtables o con iptables.
Ojo, muchas tarjetas wireless no soportan bridge. Un AP generalmente sí.
ifconfig eth0 0.0.0.0
ifconfig vmnet1 0.0.0.0
brctl addbr puente
brctl addbr puente
brctl addif eth0
brctl addif vmnet1
ifconfig puente 192.168.10.1
iptables -O FORWARD -m physdev --physdev-in vmnet1 --destination 157.88.10.20 -j REJECT
Para compilar kqemu no hace falta versión vieja de compilador gcc, ni las fuentes de qemu. Es bastante rápido:
./configure
make
sudo make install
modprobe kqemu
Por defecto, necesitamos permisos de root para leer /dev/kqemu
creamos grupo qemu: sudo addgroup qemu
añadimos nuestro usuario (jomar) al grupo: sudo gpasswd -a jomar qemu
configuramos udev para que cree fichero de dispositivo con permisos para grupo qemu. Para ello editamos fichero /etc/udev/rules.d/60-kqemu.rules con este contenido: KERNEL=="kqemu", NAME="%k", MODE="0666", GROUP="qemu", RUN="/root/prueba.sh"
hacemos lo propio con el fichero /dev/net/tun. A partir del kernel 2.6.18 no pasa nada por dar permiso para todo el mundo, pues nadie sin privilegios puede crear una nueva interfaz si no se ha creado antes por el root para ese usuario. Esto se puede hacer con la herramienta tunctl, que forma parte del paquete uml-utilities. También se puede usar el programa que adjuntamos más adelante, cambiando el tipo de dispositivo de tun a tap. Para crear el dispositivo con tunctl se usa tunctl -u jomar -t tap0; para borrarlo tunctl -d tap0.
Ejemplos:
./qemu -net nic -net tap,script=no ~/linux-0.2.img -net nic,vlan=1 -net socket,vlan=1,listen=:8081 centos.qcow2
ifconfig tap0 up
ifconfig eth0
brctl addif puente tap0
ifconfig eth0 0.0.0.0
brctl addif puente eth0
ifconfig eth0 192.168.15.45
./qemu -net nic -net tap,script=no ~/centos.qcow2 -no-kqemu
qemu-img create -f qcow2 centos.qcow2 3G
Con esta orden se crea una imagen de un disco de 3GB; en realidad con GNU/Linux este fichero no ocupa 3GB; el espacio sin usar del fichero no ocupa espacio en el disco.
Para arrancar del CD de instalación podemos ejecutar:
qemu -kernel-kqemu -cdrom /home/jomar/centos1of6.iso -boot d -m 512 centos.qcow2
La opción -kernel-kqemu es para obtener la máxima aceleración: acelera también el código del espacio del kernel; por defecto sólo se acelera la de usuario. Si se produce algún problema, reintentaremos sin esta opción; si sigue habiendo problemas podemos probar con -no-kqemu a quitar incluso la aceleración de espacio de usuario. Tras la instalación, podemos probar a volver a utilizar aceleración: a veces los posibles problemas sólo se dan durante la instalación, aunque esta situación es más propia de Windows que GNU/Linux.
Con la opción -cdrom le indicamos que el CDROM es en realidad esa ISO; así evitamos el tener que tostar un CD. Con la opción -boot d indicamos que arranque de CD en vez de disco duro. La opción -m 512 establece que la máquina virtual tenga 512 MB de memoria. La cantidad de memoria puede cambiarse de una ejecución a otra, pero hay que tener en cuenta que los instaladores suelen crear una partición de intercambio con el doble de la memoria RAM.
¿Cómo cambiar de CD durante la instalación? Con ctrl-alt-2 (ojo 1, no F1) pasamos al monitor, en el que ejecutamos el comando:
change cdrom /home/jomar/centos2of6.iso
Tras escribir la orden (qemu no indica ni errores ni que la operación se ha realizado con éxito) volvemos a la pantalla de la máquina virtual con ctrl-alt-1.
Una característica interesante de Qemu es que permite crear a partir de un fichero de imagen, otra imagen que sólo contendrá los cambios que se produzcan en la primera, si la primera permanece sin modificar. Esta característica es útil por ejemplo para crear dos imágenes muy parecidas para hacer pruebas de redes entre las dos imágenes, sin que ocupe tanto en el disco.
qemu-img create -b xubuntu.qcow2 xubuntu1.qcow2
qemu-img create -b xubuntu.qcow2 xubuntu2.qcow2
Listado para usar el dispositivo creado para este usuario (en kernels viejos lo crea si no existe): muestra primer paquete que recibe, una salida similar a la de tcpdump -x -i tun0. Para probarlo configuramos con ifconfig el dispositivo tun0, usando pointtopoint para indicar una IP supuestamente destino; podemos hacer un ping a esa dirección y ver lo que recibe el programa.
Listado para crear como persistente el nodo:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <linux/if_tun.h> #include <linux/if.h> #include <string.h> int main() { struct ifreq ifr; int fd, err; char dev[IFNAMSIZ]; fd = open("/dev/net/tun", O_RDWR) ; memset(&ifr, 0, sizeof(ifr)); /* Flags: escoger entre IFF_TAP o IFF_TUN; opcionalmente * añadir IFF_NO_PI * IFF_TAP - Dispositivo TAP: Nivel ethernet (Layer2), * multipunto; útil para hacer bridge o para tráfico no IP * también para Qemu, pues usa TAP * IFF_TUN - Dispositivo TUN: nivel IP (Layer3); punto a * punto; la opción recomendada normalmente si * sólo trafico IP sin bridge * IFF_NO_PI - No añadir a paquetes TUN información adicional * antes del paquete. */ ifr.ifr_flags = IFF_TUN|IFF_NO_PI; if( *dev ) strncpy(ifr.ifr_name, "tun0",5); err= ioctl(fd, TUNSETIFF, (void *) &ifr); if (!(err<0)) { // Hacer dispositivo persistente, es decir, que permanece // creado aunque se cierre dispositivo; normalmente el // dispositivo sólo existe mientras está abierto el // descriptor de fichero a /dev/net/tun. // Cambiar 1 por 0 para borrar un dispositivo persistente. err= ioctl(fd, TUNSETPERSIST, 1); // Poner como dueño del dispositivo al UID 1000; de este // modo puede crear dispositivo proceso ejecutado por // usuario con este UID en lugar de root, si además tiene // permiso de lectura y escritura sobre /dev/net/tun // antes de kernel 2.6.18 no era necesario hacer esto, pero // ahora ya sí. Con Qemu otra alternativa es hacer un // programa lanzador setuid que abra fichero, revoque // privilegios y lance Qemu pasándole handler abierto; o que // un programa con privilegio de root abra fichero y pase el // handler vía un socket UNIX (ver man cmsg) if (!(err<0)) err= ioctl(fd, TUNSETOWNER, 1000); } if( err<0) { perror("falló"); close(fd); return err; } strcpy(dev, ifr.ifr_name); printf("%s\n",ifr.ifr_name); return 0; }
Programa para crear un dispositivo TUN/TAP persistente
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <linux/if_tun.h> #include <linux/if.h> #include <string.h> int main() { struct ifreq ifr; int fd, err; char dev[IFNAMSIZ]; fd = open("/dev/net/tun", O_RDWR) ; memset(&ifr, 0, sizeof(ifr)); /* Flags: escoger entre IFF_TAP o IFF_TUN; opcionalmente * añadir IFF_NO_PI * IFF_TAP - Dispositivo TAP: Nivel ethernet (Layer2), * multipunto. * útil para hacer bridge o para tráfico no IP * también para Qemu, pues usa TAP * IFF_TUN - Dispositivo TUN: nivel IP (Layer3); punto a * punto; la opción recomendada normalmente si * sólo tráfico IP sin bridge * IFF_NO_PI - No añadir a paquetes TUN información adicional * antes del paquete. */ ifr.ifr_flags = IFF_TUN|IFF_NO_PI; if( *dev ) strncpy(ifr.ifr_name, "tun0",5); err= ioctl(fd, TUNSETIFF, (void *) &ifr); if (!(err<0)) { // Hacer dispositivo persistente, es decir, que permanece // creado aunque se cierre dispositivo; normalmente el // dispositivo sólo existe mientras está abierto el // descriptor de fichero a /dev/net/tun. // Cambiar 1 por 0 para borrar un dispositivo persistente. err= ioctl(fd, TUNSETPERSIST, 1); // Poner como dueño del dispositivo al UID 1000; de este // modo puede crear dispositivo proceso ejecutado por // usuario con este UID en lugar de root, si además tiene // permiso de lectura y escritura sobre /dev/net/tun // antes de kernel 2.6.18 no era necesario hacer esto, pero // ahora ya sí. Con Qemu otra alternativa es hacer un // programa lanzador setuid que abra fichero, revoque // privilegios y lance Qemu pasándole handler abierto; o que // un programa con privilegio de root abra fichero y pase el // handler vía un socket UNIX (ver man cmsg) if (!(err<0)) err= ioctl(fd, TUNSETOWNER, 1000); } if( err<0) { perror("falló"); close(fd); return err; } strcpy(dev, ifr.ifr_name); printf("%s\n",ifr.ifr_name); return 0; }