Переадресация портов на гостевые системы в libvirt/KVM

Переадресация портов на гостевые системы в libvirt/KVM

Как перенаправить порты на сервере, на котором запущен libvirt/KVM, на указанные порты виртуальных машин при использовании NAT?

Например, у хоста публичный IP 1.2.3.4. Я хочу перенаправить порт 80 на 10.0.0.1, а порт 22 на 10.0.0.2.

Я предполагаю, что мне нужно добавить правила iptables, но я не уверен, где это уместно и что именно следует указать.

Вывод 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         

Вывод 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)

Я использую Ubuntu 10.04.

решение1

Последняя стабильная версия libvirt для Ubuntu — это версия 0.7.5, в которой нет некоторых новых функций (например, скриптовых хуков и сетевых фильтров), которые упрощают автоматическую настройку сети. Тем не менее, вот как включить переадресацию портов для libvirt 0.7.5 в Ubuntu 10.04 Lucid Lynx.

Эти правила iptables должны помочь:

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

Конфигурация KVM NAT по умолчанию содержит правило, похожее на третье, которое я привел выше, но в нем отсутствует состояние NEW, которое необходимо для приема входящих подключений.

Если вы напишете скрипт запуска, чтобы добавить эти правила, и не будете осторожны, libvirt 0.7.5 переопределит их, вставив свои собственные. Поэтому, чтобы убедиться, что эти правила применяются правильно при запуске, вам нужно убедиться, что libvirt инициализировалдовы вставляете свои правила.

Добавьте следующие строки в /etc/rc.local перед строкой 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
) &

Вышеприведенный sleep 10хак позволяет убедиться, что демон libvirt имел возможность инициализировать свои правила iptables, прежде чем мы добавим свои собственные. Не могу дождаться, когда они выпустят версию libvirt 0.8.3 для Ubuntu.

решение2

Есть способ настроить перенаправление портов на летукогда гость использует сетевой режим пользователя, я писал об этом здесь:

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

Подробности вы можете увидеть там, но для удобства вот решение, которое я нашел:

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

Этот однострочный ответ намного проще других, но работает только в некоторых сценариях (сетевой стек пользовательского режима).

решение3

Более «официальный»[1] способ сделать это — создать скрипт-ловушку, как описано на сайте libvirt:

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

... в основном этот скрипт будет вызываться при загрузке гостя KVM. Сам скрипт добавит соответствующие правила iptable (похожие на ответ Айзека Сазерленда выше) с правильно добавленным состоянием соединения 'NEW'. Обратите внимание, что вы должны изменить скрипт, указав правильные значения для ваших хостов и портов.

[1] хотя в документации libvirt говорится, что это своего рода хак, поймите правильно

решение4

В Ubuntu 20.04 я создал следующий скрипт, который сохранил как /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

ситуация входа такова, что нам не хватает NEW, кстати, флага DNAT для цепочки FORWARDING, который создается автоматически.

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

Мой скрипт добавляет флаг DNAT ко всем определениям.

Таким образом, я просто добавил переадресацию портов в iptables с помощью

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

Вы можете использовать пакеты iptables-persistentдля сохранения этого состояния (но для этого вам обязательно придется удалить цепочки libvirt из дампа)

Или вы используете ufwлюбой другой скрипт брандмауэра.

Мой сценарий основан на выводах из этого обращенияhttps://www.cyberciti.biz/faq/kvm-forward-ports-to-guests-vm-with-ufw-on-linux/

Связанный контент