KVM networking: a simple way to manage port forwards and configuration
QEMU/KVM offers powerful networking features, including the ability to create many kinds of networks for your VMs. These can be either an isolated network, a NAT network, or a bridge. There are built-in services that can be configured to serve DNS and DHCP (including PXE support), so you do not need to bother maintaining these services separately, if all you need is basic functionality. Everything is easily editable via libvirt-provided mechanisms, like virt-viewer or virsh.
However, a common problem with QEMU/KVM is how it deals with updates. Many actions, like adding DNS or DHCP entries for a network, will not yield instant results, despite it *looking like it did*. For instance, if you change, for example, an entry for a fixed DHCP host, the change will *not* be reflected until you do the following:
- do a “net-destroy” on the network
- do a “net-start” on the network
- reattach the network interface to each VM that was using your network previously (yes, all interfaces are detached… but not reattached until you restart the VM)
Also, there’s the port forwarding issue. How do you expose a port to the outside world that goes right into that specific VM? You need a set of iptables rules for this. These rules must be added dynamically, via a hook script.
The official libvirtd documentation mentions these problems, and offers two example hook scripts (https://wiki.libvirt.org/page/Networking#Applying_modifications_to_the_network). However, their approach is too generic, and uses too many hardcoded values. For example, it tries to reattach interfaces on ALL VMs, even the ones that are not using the network you’re modifying. Also, they do not integrate well with each other.
My solution is to rewrite them with the following objectives:
- easy port forwarding configuration
- make the process of refreshing the network configuration easy and faster
- only change the affected VMs/interfaces
The resulting scripts are available on my github repository.
Adding port forwards, the easy way
- copy and rename the “qemu-hook-script” file to “/etc/libvirt/hooks/qemu”. Please note this is a *file* called “qemu”, not a directory.
- modify the file to add your forwarding rules. Adding a port forward is as simple as adding a line like this:
addForward <VM NAME> <external port> <internal IP> <internal port>
For example, to redirect incoming connections on port 20022 to an internal address of 192.168.101.200 on port 22, whenever the VM named “caasp-admin” is started, you’d write:
addForward caasp-admin 20022 192.168.101.200 22
3. Set it as executable, or else QEMU/KVM won’t call the hook script:
chmod +x /etc/libvirt/hooks/qemu
The next time you start the VMs listed, the port forwards will be defined properly.
Refreshing network configurations, the easy way
- copy the “kvm-network-restart” script to your path (I recommend /usr/sbin).
- make it executable:
chmod +x /usr/sbin/kvm-network-restart
That’s it. Whenever you change an attribute on a network, say via “virsh net-edit “, you can now make the changes available by running “kvm-network-restart <network name>”.
For example, let’s say you wanted to reserve an IP address for a specific VM, and add it to the DNS to a NAT network called “development”. Just run “virsh net-edit development” and add the following:
<dns> <host ip='192.168.101.200'> <hostname>admin.local</hostname> </host> </dns> <ip address='192.168.101.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.101.128' end='192.168.101.254'/> <host mac='52:54:00:26:37:81' name='admin' ip='192.168.101.200'/> </dhcp> </ip>
Then, run “kvm-network-restart development” and you’re set.