Reenvío de puertos a invitados en libvirt/KVM

Reenvío de puertos a invitados en libvirt/KVM

¿Cómo puedo reenviar puertos en un servidor que ejecuta libvirt/KVM a puertos específicos en VM cuando uso NAT?

Por ejemplo, el host tiene una IP pública de 1.2.3.4. Quiero reenviar el puerto 80 al 10.0.0.1 y el puerto 22 al 10.0.0.2.

Supongo que necesito agregar reglas de iptables, pero no estoy seguro de dónde es apropiado y qué se debe especificar exactamente.

Salida de iptables -L

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     udp  --  anywhere             anywhere            udp dpt:domain 
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:domain 
ACCEPT     udp  --  anywhere             anywhere            udp dpt:bootps 
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:bootps 

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             10.0.0.0/24         state RELATED,ESTABLISHED 
ACCEPT     all  --  10.0.0.0/24          anywhere            
ACCEPT     all  --  anywhere             anywhere            
REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 
REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Salida de ifconfig

eth0      Link encap:Ethernet  HWaddr 00:1b:fc:46:73:b9  
          inet addr:192.168.1.14  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::21b:fcff:fe46:73b9/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:201 errors:0 dropped:0 overruns:0 frame:0
          TX packets:85 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:31161 (31.1 KB)  TX bytes:12090 (12.0 KB)
          Interrupt:17 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

virbr1    Link encap:Ethernet  HWaddr ca:70:d1:77:b2:48  
          inet addr:10.0.0.1  Bcast:10.0.0.255  Mask:255.255.255.0
          inet6 addr: fe80::c870:d1ff:fe77:b248/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:468 (468.0 B)

Estoy usando Ubuntu 10.04.

Respuesta1

La última versión estable de libvirt para Ubuntu es la versión 0.7.5, que no tiene algunas características más nuevas (es decir, enlaces de script y filtros de red) que facilitan la configuración automática de la red. Dicho esto, aquí se explica cómo habilitar el reenvío de puertos para libvirt 0.7.5 en Ubuntu 10.04 Lucid Lynx.

Estas reglas de iptables deberían funcionar:

iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 80 -j DNAT --to-destination 10.0.0.1:80
iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 22 -j DNAT --to-destination 10.0.0.2:22
iptables -I FORWARD -m state -d 10.0.0.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT

La configuración predeterminada de KVM NAT proporciona una regla similar a la tercera que proporcioné anteriormente, pero omite el estado NUEVO, que es esencial para aceptar conexiones entrantes.

Si escribe un script de inicio para agregar estas reglas y no tiene cuidado, libvirt 0.7.5 las anula insertando las suyas propias. Entonces, para asegurarse de que estas reglas se apliquen correctamente al inicio, debe asegurarse de que libvirt se haya inicializadoantesinsertas tus reglas.

Agregue las siguientes líneas a /etc/rc.local, antes de la línea exit 0:

(
# Make sure the libvirt has started and has initialized its network.
while [ `ps -e | grep -c libvirtd` -lt 1 ]; do
        sleep 1
done
sleep 10
# Set up custom iptables rules.
iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 80 -j DNAT --to-destination 10.0.0.1:80
iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 22 -j DNAT --to-destination 10.0.0.2:22
iptables -I FORWARD -m state -d 10.0.0.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT
) &

Lo sleep 10anterior es un truco para asegurar que el demonio libvirt haya tenido la oportunidad de inicializar sus reglas de iptables antes de que agreguemos las nuestras. No puedo esperar hasta que lancen la versión 0.8.3 de libvirt para Ubuntu.

Respuesta2

Hay una manera de configurar la redirección de puertos sobre la marchacuando el invitado está utilizando la red en modo de usuario, escribí un blog sobre esto aquí:

http://blog.adamspiers.org/2012/01/23/port-redirection-from-kvm-host-to-guest/

Puede ver los detalles allí, pero por conveniencia, aquí está la solución que descubrí:

virsh qemu-monitor-command --hmp sles11 'hostfwd_add ::2222-:22'

Esta frase breve es mucho más fácil que las otras respuestas, pero solo funciona en algunos escenarios (pila de red en modo usuario).

Respuesta3

Una forma más "oficial"[1] de hacer esto es crear un script de enlace como se describe en el sitio web de libvirt:

http://wiki.libvirt.org/page/Networking#Forwarding_Incoming_Connections

... básicamente, este script se invocará cuando se inicie un invitado KVM. El script en sí agregará las reglas de iptable apropiadas (similar a la respuesta anterior de Isaac Sutherland) con el estado de conexión 'NUEVO' agregado correctamente. Tenga en cuenta que debe modificar el script con los valores correctos para sus hosts y puertos.

[1] aunque la documentación de libvirt dice que esto es una especie de truco, imagínate

Respuesta4

En Ubuntu 20.04 hice el siguiente script, que se guarda como /etc/libvirt/hooks/allow-portfw(chmod +x):

#!/bin/bash

CHAIN=LIBVIRT_FWI
IPTCMD="iptables -L $CHAIN -vn"
FILTERCMD="grep -v -e Chain -e pkts -e reject-with -e DNAT"

while $IPTCMD | grep ESTABLISHED | grep -v DNAT >/dev/null
do
    IF=$($IPTCMD | $FILTERCMD | awk '{print $7}' | head -n1)
    NET=$($IPTCMD | $FILTERCMD | awk '{print $9}' | head -n1)
    iptables -D $CHAIN -o $IF -d $NET -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    iptables -I $CHAIN 1 -o $IF -d $NET -m conntrack --ctstate DNAT,RELATED,ESTABLISHED -j ACCEPT
done

La situación de entrada es que nos falta NUEVO por cierto. el indicador DNAT para la cadena FORWARDING, que se crea automáticamente.

sudo iptables -L LIBVIRT_FWI -vn
Chain LIBVIRT_FWI (1 references)
 pkts bytes target     prot opt in     out     source               destination         
1741K 2661M ACCEPT     all  --  *      virbr0  0.0.0.0/0            192.168.122.0/24     ctstate RELATED,ESTABLISHED
   38  1972 REJECT     all  --  *      virbr0  0.0.0.0/0            0.0.0.0/0            reject-with icmp-port-unreachable

Mi script agrega la bandera DNAT a todas las definiciones.

De esa manera, simplemente agregué el reenvío de puertos en iptables con

iptables -A PREROUTING -i mylanIF -p tcp --dport 3389 -j DNAT --to-destination 192.168.122.110:3389

Puede usar los paquetes iptables-persistentpara guardar este estado (pero seguro que debe eliminar las cadenas libvirt del volcado)

O utiliza ufwcualquier otro script de firewall para ello.

Mi guión se basa en los hallazgos de esta dirección.https://www.cyberciti.biz/faq/kvm-forward-ports-to-guests-vm-with-ufw-on-linux/

información relacionada