Comments
You can use your Mastodon account to reply to this post.
martes, 19 de septiembre de 2023
Tiempo de lectura 10 minutos
Una vez logré instalar y aprender a utilizar Libvirt, tal como lo he comentado ya en un par de publicaciones, tocaba una parte que era importante para alguno de mis proyectos. Hacer un servidor disponible mediante IPv6.
La razón es muy sencilla. Tengo un ISP que me pone bajo CG-NAT la dirección IPv4, así que no es posible que mis servidores o máquinas virtuales puedan ser accedidas mediante este protocolo, ya que en realidad mi dirección IPv4 se comparte con muchos otros clientes, lo que nos quita la posibilidad de abrir o redireccionar puertos. No obstante, lo que hace mi proveedor es que nos brinda direcciones IPv6 y por lo que sé, a cada dispositivo que lo solicita se le asigna una dirección completamente funcional y a la que es posible conectarse a través de internet, como también ya he explicado en alguna otra ocasión. Ahora bien, lo que quería hacer era, esencialmente, permitir que mis VM’s tuvieran la posibilidad de solicitar su propia dirección IPv6 a mi router, para que este se las asignara y así poder conectarme al SSH o cualquier otro servicio, por ejemplo.
En libvirt, de manera predeterminada, al crear un nuevo dominio, este forma parte de una red en 192.168.122.1/24. Esta red funciona y brinda internet ya que utiliza NAT (network address translation) y redireccionamiento de paquetes para lograr su función. Internamente, Libvirt agrega varias reglas en iptables para lograr que el equipo anfitrión sepa bien cómo enviar hacia todo el internet los paquetes que provienen de la red por defecto de libvirt. A efectos prácticos, esta red predeterminada no se encuentra conectada a ningún dispositivo físico y para muchas máquinas virtuales esta configuración es más que suficiente ya que permite “salida” a todo internet, pero sin más configuraciones, no permite la “entrada” porque las máquinas virtuales no tienen una IP que pueda ser resuelta por ninguna red externa al propio libvirt.
Para aquellos casos donde sí es importante contar con una dirección IP globalmente accesible en internet, no obstante, hay un problema. La red que nos proporciona libvirt no ofrece esa posibilidad, pues en realidad, al no estar conectada a ningún dispositivo “físico”, las direcciones IP que se asignan se hacen de manera interna. En mi caso particular, lo que quería era permitir que mis dispositivos y mis máquinas virtuales pudieran formar parte de la misma red, y pudieran ser servidos por mi router. De esta manera, mi router no vería la diferencia entre una VM y un dispositivo más que está conectado en mi red doméstica. Para hacer esto, básicamente creé un puente de red virtual que va conectado al único dispositivo de red que lleva mi equipo. Al puente de red se le puede conectar de todo: desde otras redes, como es el caso de la red que crea libvirt (aunque para este ejemplo he definido una red completamente nueva), hasta el propio docker o LXD (del que hablaré próximamente). Un puente de red virtual, esencialmente, es un dispositivo al que se pueden conectar otros dispositivos en sus extremos. Por un extremo conectaremos el dispositivo real, que proporciona internet y permite comunicarnos con el router del ISP, mientras que en el otro lado del puente estarán todos los dispositivos virtuales que generarán Libvirt, LXD, docker y demás aplicaciones. El puente tomará los paquetes que envíen las máquinas virtuales y los pasará al router, así mismo pasará todo lo que el router envíe de regreso a través de él y lo comunicará a la máquina virtual que originó la comunicación. Finalmente, también podremos asignar direcciones IP mediante DHCP o SLAAC a cualquiera de los dispositivos virtuales que así lo requieran.
Nota para usuarios de lectores de pantalla: Crear un puente de red virtual implica tener que tocar la configuración de red del equipo. Esto puede causar, si hay un error, que el equipo deje de responder a la conexión de red, y por tanto al SSH, al reiniciar la configuración de red. La manera de solucionar este tipo de cosas normalmente es accediendo físicamente al equipo mediante el teclado, editando el archivo de interfaces y probar a reiniciar la configuración de red. Para quien use Debian, por ejemplo, la cosa es fácil si se ha instalado espeakup durante la instalación del sistema operativo, porque en el peor de los casos solo es cuestión de iniciar sesión en la consola usando espeakup y corregir el fichero de red. Pero si no se tiene ninguna forma de acceder de manera física y accesible al equipo, podría no ser muy recomendable jugar con la configuración de las interfaces de red.
Nota: Estos apuntes se basan únicamente en mi configuración. En una red doméstica, que normalmente es servida por DHCP, esta configuración debería funcionar. No obstante, si otro ISP asigna configuraciones diferentes podría no funcionar sin modificaciones.
Lo primero es darnos cuenta de cuál es el dispositivo de red físico con el que contamos. Este dispositivo se usará para “adjuntarlo” al puente de red que crearemos después. Importante es notar que no puedes hacer un puente de red en dispositivos Wireless, como cualquier adaptador Wifi. En mi caso, he utilizado la tarjeta de red que venía en el equipo, y pude conocer su nombre con este comando:
$ cat /etc/network/interfaces | grep iface
iface lo inet loopback
iface enp34s0 inet dhcp
En este fichero se configuran, en un Debian 12 sin gestores de red adicionales, los dispositivos de red durante la instalación. En mi caso me interesa la segunda línea, la que dice “iface enp34s0 inet dhcp”, ya que “enp34s0” es el nombre de mi dispositivo. Para comprobar que es este dispositivo el que tiene el acceso a internet, podemos ejecutar este comando:
$ ip a
Este comando mostrará muchas cosas en la salida, de entre ellas la más importante es la definición del dispositivo en cuestión. Si aquí podemos ver que el dispositivo físico que queremos usar ya tiene asignada una dirección local IPv4, que pertenece a la LAN, y una o más direcciones IPv6 (una local y otra global), entonces podemos asumir con seguridad que este dispositivo es el que necesitamos conectar a nuestro futuro puente virtual.
Antes de continuar, la wiki de Debian recomienda el paquete bridge-utils, que proporciona el comando brctl y nos permite añadir o visualizar los diferentes puentes de red, así como ver las interfaces que están conectadas a ambos extremos de ellos. No estoy del todo seguro si este paquete es necesario al realizar una configuración manual, pero dado que de todos modos nos resultará de utilidad, es recomendable verificar si el paquete en cuestión está ya instalado:
$ sudo apt-get install bridge-utils
Para añadir un puente de red virtual permanente, es necesario editar el fichero de configuración de interfaces de red, que en Debian es /etc/network/interfaces.
$ sudo nano /etc/network/interfaces
En mi equipo, esta configuración queda como sigue, teniendo en cuenta que he cambiado la configuración por defecto y eh colocado el nombre de mi dispositivo, que es “enp34s0”:
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
auto br0
iface br0 inet dhcp
bridge_ports enp34s0
bridge_fd 0
bridge_stp off
bridge_maxwait 0
Nota: Importante es notar que debes eliminar la definición de la interfaz enp34s0. En este archivo solo puedes utilizar la interfaz una vez. Si en el ejemplo no se eliminara la definición, y se trata de utilizar el mismo dispositivo físico para conectarlo al puente de red virtual y al internet directamente, la configuración de red fallará.
Una vez guardado el archivo, es necesario reiniciar bien el sistema o el servicio de red. En mis pruebas en ocasiones reiniciar el servicio de red no terminaba de funcionar, así que reiniciando el sistema fue la manera más sencilla de asegurar que siempre levantaba el puente de red correctamente.
$ sudo reboot
Después de algo de tiempo, lo justo para configurar la nueva interfaz, deberíamos notar que nuestro equipo está vivo. Ahora bien, si recibimos una configuración de IP mediante DHCP basada en la dirección MAC del dispositivo habrá que prestar atención, ya que el puente virtual trae su propia MAC, y, en mi caso al menos, eso supuso el cambio de mi IP local por una distinta. De todos modos, eso no es nada que no pueda revisarse desde el Router.
Ahora, podemos volver a ejecutar el comando para revisar nuestras conexiones de red:
$ ip a
En mi caso, muestra un montón de conexiones, pero como nuestro dispositivo de puente virtual se llama “br0”, esa es la que podemos buscar. Este es un ejemplo de cómo se podría ver:
5: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 11:22:33:44:55:66 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.9/24 brd 192.168.1.255 scope global dynamic br0
valid_lft 55767sec preferred_lft 55767sec
inet6 2001:db8:3333:4444:5555:6666:7777:8888/64 scope global dynamic mngtmpaddr
valid_lft 428647sec preferred_lft 428647sec
inet6 fe80::44c8:d1ff:fe62:9587/64 scope link
valid_lft forever preferred_lft forever
Finalmente, en mi caso específico y sobre el despliegue IPv6, he tenido que establecer el valor de accept_ra a 2 para el kernel, lo que permite siempre tener disponible la posibilidad de aceptar anuncios del Router con respecto a IPv6. De no activar esto para el dispositivo donde estará conectado libvirt (en este caso br0), la red no puede iniciarse y podría no proporcionar conectividad a internet a las máquinas virtuales. Para hacer esto, se debe incluir la opción “net.ipv6.conf.br0.accept_ra = 2” en el archivo /etc/sysctl.conf y reiniciar el eqipo antes de continuar.
Una vez que esta parte funciona, y si todo lo que necesitamos para trabajar con la red (atención a docker aquí) funciona, podemos proceder a la segunda parte, donde hay que definir una nueva red dentro de libvirt para poder añadirla como “bridged”.
El último paso para poder tener direcciones IPv6 globales en nuestras máquinas virtuales es definir un nuevo tipo de red en libvirt, pues por defecto, como hemos visto, utiliza una red en modo NAT. Para esto, hay que crear un archivo xml con este contenido:
$ sudo nano bridged-network.xml
<network>
<name>bridged-network</name>
<forward mode="bridge" />
<bridge name="br0" />
</network>
Lo único a lo que hay que prestar atención en este archivo es al nombre de la red, que utilizaremos para referenciarlo a las máquinas virtuales durante su creación o al editarlas, y al dispositivo al que vamos a conectarnos, que debe ser el puente virtual que hemos creado, que en este caso se llama br0. Una vez que el archivo esté guardado, utilizaremos virsh para definir esta red así:
$ sudo virsh net-define bridged-network.xml
También hay que iniciar la red, y configurar el inicio automático de la misma cuando el servicio libvirtd arranque:
$ sudo virsh net-start bridged-network
$ sudo virsh net-autostart bridged-network
Finalmente, podemos ver si la red en este momento está operativa con el comando net-list:
$ sudo virsh net-list
Name State Autostart Persistent
----------------------------------------------------
bridged-network active yes yes
default active yes yes
Al momento de crear una nueva máquina virtual, se puede especificar la red que se desea utilizar con el parámetro –network. Nuestro comando anterior, para una máquina Debian, quedaría de la siguiente manera:
$ sudo virt-install --name test --cdrom /var/lib/libvirt/iso/debian-12.1.0-amd64-netinst.iso --os-variant=debian11 --network network=bridged-network --disk size=20,cache=none,bus=virtio --memory 2048 --sound default --graphics spice,port=5901,listen=::,password=test1 --vcpu 2 --noautoconsole
El cambio es el mismo tratándose de una máquina virtual con Windows, solo hay que reemplazar el parámetro “default” en la red por nuestra nueva red, en este caso llamado bridged-network.
En el caso de una máquina virtual que ya haya sido definida, podemos actualizar el fichero con su definición para cambiar la interfaz de red a la que se conecta. Esto lo hacemos con el comando virsh edit “vm”, que abre, generalmente con el editor de línea de comandos predeterminado, el fichero XML con la configuración de la máquina virtual. Normalmente se puede buscar el nodo “interface type=‘network’”. Dentro de esta definición, existe otro nodo llamado “source” con un parámetro “network”, que es donde podemos cambiar y colocar el nombre de nuestra interfaz de red creada dentro de libvirt.
Es siempre recomendable detener y volver a iniciar el dominio una vez se hayan cambiado los parámetros de red, con los comandos virsh shutdown/destroy y virsh start, respectivamente.
Una vez creado el puente de red virtual, podemos hacer que cada máquina que creemos, que necesite por alguna razón ser accesible para la red local, o autoconfigurarse con el router principal, se conecte utilizando la red bridged-network. Este puente virtual también puede servirnos para conectar otras redes, por ejemplo, al configurar LXD, del que hablaremos en alguna otra ocasión, pues ofrece prestaciones más que interesantes.
Administración Linux Tutoriales