Public routes to VMs
By default, all incoming traffic to the VMs must originate from your workstation (or another VM on your workstation) - no traffic is routed to your VMs from any other interface.
If you want to break this rule, and allow public routes into these VMs (DNAT port forwarding), you will need to install the libvirt hook that sets up the iptables forwarding rules:
Download the port-forwarding hook
sudo mkdir -p /usr/local/src/
sudo su -c "cd /usr/local/src && git clone https://github.com/EnigmaCurry/libvirt-hook-qemu.git"
EnigmaCurry/libvirt-hook-qemu is my own fork of saschpe/libvirt-hook-qemu which has been slightly customized for this configuration. Thank you to Sascha Peilicke for creating this hook!
Install the hook files
sudo mkdir -p /etc/libvirt-dnat-hook
sudo cp /usr/local/src/libvirt-hook-qemu/hooks.schema.json /etc/libvirt-dnat-hook
Set config variables
Set some temporary variables the same as from your config:
NAME=debian-dev
IP_ADDRESS=192.168.122.2
Customize the port-forwarding hook
Use the example and schema as a reference, then setup the port mapping you want for each VM:
NAME=${NAME:-debian-dev}
IP_ADDRESS=${IP_ADDRESS:-192.168.122.2}
cat << EOF | jq | sudo tee /etc/libvirt-dnat-hook/hooks.json
{
"${NAME}": {
"interface": "virbr0",
"private_ip": "${IP_ADDRESS}",
"port_map": {
"tcp": [
[2222, 22],
[80, 80],
[443, 443]
]
}
}
}
EOF
This example opens the following public ports:
- Public TCP port
2222
forwards to the VM’s port22
. - Public TCP port
80
forwards to the VM’s port80
. - Public TCP port
443
forwards to the VM’s port443
.
UDP ports need to be in their own section, a sibling of TCP. Each VM needs its own config, mapped at the top level by the VM’s unique name.
Autostart port-forwarding script on boot
Create DNAT service template
VM_ADMIN=${VM_ADMIN:-libvirt-admin}
cat << EOF | sudo tee /etc/systemd/system/libvirt-DNAT@.service
[Unit]
Description=${VM_ADMIN} VM: %i - DNAT port forwarding
Requires=libvirt@%i.service
Requires=network-online.target
After=libvirt@%i.service
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=true
Environment="XDG_RUNTIME_DIR=/run/user/$(id -u ${VM_ADMIN})"
Environment="CONFIG_PATH=/etc/libvirt-dnat-hook"
ExecStart=/usr/bin/python /usr/local/src/libvirt-hook-qemu/hooks %i start
ExecStop=/usr/bin/python /usr/local/src/libvirt-hook-qemu/hooks %i stopped
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
Enable DNAT service once per VM you want to expose
NAME=${NAME:-debian-dev}
sudo systemctl enable --now libvirt-DNAT@${NAME}.service
sudo systemctl status libvirt-DNAT@${NAME}.service
If you want to disable the port mapping, run:
NAME=${NAME:-debian-dev}
sudo systemctl disable --now libvirt-DNAT@${NAME}.service
Or to temporarily stop the port mapping (until you run start
or
reboot):
NAME=${NAME:-debian-dev}
sudo systemctl stop libvirt-DNAT@${NAME}.service
Reboot workstation
Once rebooted, test that your port forward rule exists in iptables rules:
sudo iptables-save | grep 2222
-A DNAT-debian-dev -d 10.13.13.227/32 -p tcp -m tcp --dport 2222 -j DNAT --to-destination 192.168.122.2:22 -A SNAT-debian-dev -s 192.168.122.2/32 -d 192.168.122.2/32 -p tcp -m tcp --dport 2222 -j MASQUERADE