answer

<< Back to posts.

No-Bullshit Guide To Port Forwarding with libvirt/KVM


This was made when I was using a Debian host and an Alpine guest, as well as with virt-manager.

This has a heavy assumption that you’re using virt-manager for creating the default network, this may still be valuable, but that might explain things why the default network isn’t configured the way it is and the network for the VM.

This uses NAT forwarding, which does work on wireless cards. I’m running this on a laptop under my bed for a home server.


Inside of virt-manager, after creating the VM and ensuring that networking is working, look at the XML section for the network.

(You can also find this by running sudo virsh edit vm-name)

xml configuration

The MAC address, 52:54:00:c7:cb:b2, will be useful for this upcoming step.


Next, we want to edit the default network, or whichever network you’re using idc but that’s the one that’s created for you by virt-manager.

 <network>
  <name>default</name>
  ...
  <ip address="192.168.122.1" netmask="255.255.255.0">
    <dhcp>
      <range start="192.168.122.2" end="192.168.122.254"/>
      <host mac="52:54:00:c7:cb:b2" name="vmname" ip="192.168.122.100"/>
    </dhcp>
  </ip>
</network>

You’re going to want to find the ip section in the network configuration as is shown above.

Note the host property.

  • You’re going to want to set the mac value to the MAC address value from before.
  • If the VM name is windows-7, you’re going to want to change the name property from "vmname" to "windows-7".
  • Lastly, you’re going to want to set the ip property to be in the range between the start and the end, so since 100 is between 2 and 254, it’s perfectly valid here.

Next step is to set this static IP at /etc/network/interfaces in the guest OS.

auto eth0
iface eth0 inet static
  address 192.168.122.100
  netmask 255.255.255.0
  gateway 192.168.122.1

The address should be set to the ip property that we set for the host, and the gateway and netmask property align with the values of address and netmask in the above configuration.

After rebooting the VM, you should see that the static IP has been configured.

ip addr output


Lastly, you’re going to want create a hook for libvirt to route the ports from that VM through the host to the outside world.

Create this file at /etc/libvirt/hooks/qemu

#!/bin/bash

# IMPORTANT: Change the "VM NAME" string to match your actual VM Name.
# In order to create rules to other VMs, just duplicate the below block and configure
# it accordingly.
if [ "${1}" = "vm-name" ]; then

   # Update the following variables to fit your setup
   GUEST_IP=192.168.122.100
   # You're running an application on port 8080 on the guest
   GUEST_PORT=8080
   # You want to expose port 80 to the internet and have it route to the guest's application
   HOST_PORT=80

   if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
    /sbin/iptables -D FORWARD -o virbr0 -p tcp -d $GUEST_IP --dport $GUEST_PORT -j ACCEPT
    /sbin/iptables -t nat -D PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
   fi
   if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
    /sbin/iptables -I FORWARD -o virbr0 -p tcp -d $GUEST_IP --dport $GUEST_PORT -j ACCEPT
    /sbin/iptables -t nat -I PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
   fi
fi

You’re going to want to set the GUEST_IP, GUEST_PORT, and HOST_PORT ports accordingly.

You’re also going to want to make this executable using chmod: chmod +x /etc/libvirt/hooks/qemu

You’re then going to have to restart libvirtd: sudo systemctl restart libvirtd

For me, I did it on port 4030 and exposed it to port 4030 on the host.

server start

webpage of server being hosted

References