Часто приходится устанавливать виртуализацию на основе 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