El objetivo de este experimento es tener una máquina virtual local, conectada físicamente a un adaptador de red de una máquina remota. Aunque existe un método más sencillo para reenviar todo el tráfico con sshuttle, esta solución presenta algunas ventajas y aisla totalmente el equipo anfitrión.
Esta solución es como tener una máquina virtual remota, sólo que lo único que es remoto es el adaptador de red, así que irá muy fluida en nuestro equipo local.
En este enlace mucha información sobre como realizarlo [SSH VPNs: Bridged connection to LAN using tap ] pero no termina de funcionar, he tenido que hacer algunos pasos extras, así que lo anoto todo a continuación.
Requisitos
- Disponer de un servidor remoto con privilegios de administrador (root)
- Evidentemente también root en la máquina local.
- Un puerto abierto (el 22) para hacer el túnel SSH.
Preparación del servidor
En el servidor remoto hay que hacer los siguientes preparativos:
- Activar el reenvío de paquetes (forwarding) para posteriormente configurar NAT:
sysctl -w net.ipv4.ip_forward=1
- Creamos una red privada mediante un adaptador TAP. Se solía crear con
tunctl
, pero parece que ahora lo bonito es crearlo con el comandoip
, así que los comandos son:
ip tuntap add dev tap0 mode tap
ip link
....
6: tap0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 52:74:83:0d:15:62 brd ff:ff:ff:ff:ff:ff
....
el segundo comando comprueba si se ha añadido correctamente el adaptador TAP0, pero se puede ver que no está activo state DOWN
- Toca activar el nuevo y de paso habilitar NAT con estos comandos:
ifconfig tap0 192.168.22.1
/sbin/iptables -t nat -A POSTROUTING -s 192.168.22.0/24 -o eth0 -j MASQUERADE
- Hay revisar la configuración del servidor SSH para asegurarnos que permita los túneles. Revisando la información de depuración con
ssh -v
conectando al servidor llegué a activar una opción en /etc/ssh/sshd_config:
PermitTunnel yes
Tras reiniciar el servicio SSH se puede probar la configuración.
Probando el túnel
En el cliente se deben repetir los pasos para crear el adaptador TAP0 y para realizar las pruebas se le debe poner una IP en el rango del servidor 192.168.22.X. con los siguientes comandos:
ip tuntap add dev tap0 mode tap
ifconfig tap0 192.168.22.22
Ahora desde el cliente hay que abrir el túnel con SSH, me llevó un rato buscando por la red como hacerlo ya que los comandos que aparecen en muchos blogs no funcionan correctamente. Misteriosamente SSH crea un TUN en vez de un TAP, y parece que el truco está en usar las opciones SSH en un orden concreto, justamente así:
ssh -o Tunnel=ethernet -w tap-local:tap-remoto <host-remoto>
El siguiente misterio está en la opción -w tap-local:tap-remoto, se debe poner sólo el número de la interfaz TAP, no el nombre completo, en este ejemplo tanto en el servidor como en el cliente la interfaz es TAP0. Así el comando exacto es:
ssh -o Tunnel=ethernet -w 0:0 miservidor.es -v -N
y las opciones: -v es para ver todos los mensajes de depuración y -N para que no abra el shell, quedándose el terminal mostrando continuamente mensajes.
Si todo ha ido bien con estas pruebas comprobamos que el túnel funciona:
- La más fácil ping 192.168.22.1 desde el cliente o ping 192.168.22.22 desde el servidor.
- Para comprobar que efectivamente es un túnel ethernet tanto en el servidor como en el cliente se puede ver el tráfico con tcpdump -i tap0
Iniciando la máquina virtual – QEMU
Esta parte se complica un poco ya que el dispositivo TAP0 está abierto por SSH y no puede compartido por otro proceso. Una vez el túnel está operativo, QEMU no puede usarlo, la solución es hacer un puente con dos interfaces TAP, una para SSH y otra para QEMU.
Ya hay mucha documentación sobre puentes, muestro sólo los comandos que lo crean:
ip tuntap add dev tap0 mode tap
ip tuntap add dev tap1 mode tap
brctl addbr br19
brctl addif br19 tap0 tap1
ip link set up tap0
ip link set up tap1
ip link set up br19
Tras estos comandos se puede usar el interfaz TAP0 para abrir el túnel con SSH:
ssh -o Tunnel=ethernet -w 0:0 miservidor.es -v -N
y el adaptador TAP1 se le puede ceder a QEMU, pero la línea de comandos es complicada, la muestro completa, pero la parte de red comienza en –netdev:
qemu-system-x86_64 -enable-kvm -m 2048 -k es -vga vmware --netdev tap,ifname=tap1,id=net0,script=no,downscript=no -device e1000,netdev=net0,mac=52:54:11:22:33:44 /dev/sdf
El dispositivo usado como imagen en este ejemplo es una unidad USB en /dev/sdf
En el sistema operativo de la máquina virtual hay que configurar la interfaz de red con una IP estática en la red 192.168.22.X.
Usando libvirt
La verdad que QEMU es un engorro, por lo que he probado este experimento con libvirt y configurado la máquina con el Gestor de Máquinas Virtuales (virtmanager) que tiene interfaz gráfica.
Aquí la creación de los adaptadores y el puente se simplifica un poco, sólo hay que crear un adaptador TAP y añadirlo a un puente:
ip tuntap add dev tap0 mode tap
brctl addbr br19
brctl addif br19 tap0
ip link set up tap0
ip link set up br19
Después desde VirtManager se configura la máquina indicando la interfaz de red como se muestra en la captura:
Cuando se inicie la máquina, automáticamente se crea una interfaz TAP nueva y es añadida al puente BR19, quedando TAP0 disponible para lanzar el túnel con:
ssh -o Tunnel=ethernet -w 0:0 miservidor.es -v -N
Mejorando el experimento
Se pueden hacer persistentes tanto los adaptadores TAP como el puente, existe multitud de documentación sobre como hacerlo.
En este ejemplo se pretende hacer lo contrario, no dejar nada persistente y hacer un script de inicialización que configure todo en un paso. Además he instalado el comando tunctl, que aunque está obsoleto sigue funcionando, (fichero ssh-tap-tunnel.sh):
#!/bin/bash
ifconfig tap0 || tunctl
ifconfig tap1 || tunctl
brctl addbr br19
brctl addif br19 tap0 tap1
ip link set up tap0
ip link set up tap1
ip link set up br19
qemu-system-x86_64 -enable-kvm -m 2048 -k es -vga vmware --netdev tap,ifname=tap1,id=net0,script=no,downscript=no -device e1000,netdev=net0,mac=52:54:11:22:33:44 $@ &
ssh -o Tunnel=ethernet -w 0:0 miservidor.es -v -N
A este script hay que pasarle un parámetro, el dispositivo con la imagen de disco.