Часто приходится устанавливать виртуализацию на основе KVM на серверах и создавать правила для проброса портов в виртуальные машины, которые работают за NAT в виртуальной сети.
Честно говоря, впервые столкнувшись, не сразу понял, как это можно сделать. Решение нашёл здесь: https://bozza.ru/art-268.html. Спасибо автору. Здесь выкладываю скрипт для справки себе в том виде, в котором я адаптировал его под свои нужды. Ну и может быть ещё кому-нибудь пригодится.
Смысл идеи состоит в том, чтобы включить правила переадресации портов в скрипт, который помещается в каталоге /etc/libvirt/hooks. Скрипты из этого каталога выполняются при запуске демона libvirtd. Поэтому после сохранения скрипта здесь его нужно перезапустить. В авторском варианте порты переадресуются с заданного адреса хоста на известный адрес виртуальной машины, назначенный либо вручную, либо dhcp KVM. Это не всегда удобно, т.к. я заранее не знаю, какой адрес будет дан хосту в каждом конкретном случае. Поэтому я переадресую порты с внешнего сетевого интерфейса сервера. В данном случае - bond0.
Собственно текст скрипта:
#!/bin/bash
## Название внешнего сетевого интерфейса хоста:
Host_lan=bond0
## Виртуалка с сервером dcm4chee-arc
Guest_name=dicompacs
Guest_ipaddr=192.168.122.10
Host_port=( '8080' '8443' '8843' '11112' '2575' )
Guest_port=( '8080' '8443' '8843' '11112' '2575' )
length=$(( ${#Host_port[@]} - 1 ))
if [ "${1}" = "${Guest_name}" ]; then
if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
for i in 'seq 0 $length'; do
iptables -t nat -D PREROUTING -i ${Host_lan} -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
iptables -D FORWARD -d ${Guest_ipaddr}/32 -p tcp -m state --state NEW -m tcp --dport ${Guest_port[$i]} -j ACCEPT
done
fi
if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
for i in 'seq 0 $length'; do
iptables -t nat -A PREROUTING -i ${Host_lan} -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
iptables -I FORWARD -d ${Guest_ipaddr}/32 -p tcp -m state --state NEW -m tcp --dport ${Guest_port[$i]} -j ACCEPT
done
fi
fi
## Виртуалка с samba-сервером
Guest_name=smbshara
Guest_ipaddr=192.168.122.20
Host_port=( '139' '445' )
Guest_port=( '139' '445' )
length=$(( ${#Host_port[@]} - 1 ))
if [ "${1}" = "${Guest_name}" ]; then
if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
for i in `seq 0 $length`; do
iptables -t nat -D PREROUTING -i ${Host_lan} -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
iptables -D FORWARD -d ${Guest_ipaddr}/32 -p tcp -m state --state NEW -m tcp --dport ${Guest_port[$i]} -j ACCEPT
done
fi
if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
for i in `seq 0 $length`; do
iptables -t nat -A PREROUTING -i ${Host_lan} -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
iptables -I FORWARD -d ${Guest_ipaddr}/32 -p tcp -m state --state NEW -m tcp --dport ${Guest_port[$i]} -j ACCEPT
done
fi
fi
Создаем файл /etc/libvirt/hooks/qemu, обычно у меня установлен Midnight Commander, поэтому у меня есть mcedit:
sudo mcedit /etc/libvirt/hooks/qemu
Копируем в него текст скрипта и сохраняем. Делаем его исполняемым:
sudo chmod 700 /etc/libvirt/hooks/qemu
И перезапускаем libvirtd:
systemctl restart libvirtd.service
Для каждой виртуальной машины можно задать любое количество портов. Понятно, что их количество в массивах Host_port и Guest_port должно быть одинаковое 🙂
Чтобы обеспечить постоянство адреса виртуальной машины, можно воспользоваться утилитой редактирования сети virsh net-edit. Если виртуалки находятся в сети default, то
virsh net-edit default
Будет открыт редактор, установленный по умолчанию, в котором необходимо указать имена виртуальных машин, mac-адреса их виртуальных сетевых адаптеров и назначаемый IP-адрес:
<dhcp>
<range start='192.168.122.100' end='192.168.122.254'/>
<host mac='52:54:00:59:c0:86' name='xraypacs' ip='192.168.122.10'/>
<host mac='52:54:00:00:c4:f0' name='smbshara' ip='192.168.122.20'/>
</dhcp>
Сохраняем и перезапускаем сеть.
Всё.
Александр Кузнецов, 02.11.2021