Libvirt Forwarding

13 Aug 2016

The KVM virtual machine could be managed by libvirt. If the virtual network is set to NAT, the virtual machine could not be accessed from outside the host. Incoming connections to the VM are only allowd from host and other virtual machines on this host.

If you would like to provide services (like ssh or http) from the VM, port forwarding could be setup with iptables on the host.

This article takes libvirt network wiki as reference.

Disable firewalld

There are conflicts between firewalld and libvirtd on CentOS 7. firewalld must be disabled first.

$ sudo systemctl stop firewalld.service
$ sudo systemctl disable firewalld.service

Allow ip forward

Add option to /etc/sysctl.conf to allow ip forward:

net.ipv4.ip_forward = 1

It will not take effect until reboot. Use sysctl command to make it take effect immediately.

$ sudo sysctl -p

iptables forwarding rules

What we need now is to forward the traffic from HOST_IP on HOST_PORT to specified virtual machine’s GUEST_IP:GUEST_PORT.

Run the following iptables commands on host:

$ sudo iptables -I FORWARD -o virbr0 -d $GUEST_IP --dport $GUEST_PORT -j ACCEPT
$ sudo iptables -t nat -I PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT

Then the virtual machine could be connected from HOST_IP:HOST_PORT.

Using these commands is not very convenient, especially when there are a lot of VMs and ports.

Automation with hook script

When VMs are started by virsh start or shutdown, the hook script /etc/libvirt/hooks/qemu will run automatically. We could use this script to add and delete the rules of iptables.

In order to simplify the configuration of port forwarding, I wrote libvirt-forward. It works well on CentOS 6/7.

This program supports json and yaml configuration. In case you are going to use yaml, please install PyYAML first:

$ sudo yum install PyYAML

Copy qemu-hook to /etc/libvirt/hooks directory and create symbolic link:

$ sudo cp libvirt-forward/qemu-hook /etc/libvirt/hooks/qemu-hook
$ sudo ln -s qemu-hook qemu

Create configuration directory:

$ sudo mkdir -p /etc/libvirt/hooks/config
$ sudo cp libvirt-forward/config/config.yaml.example /etc/libvirt/hooks/config/config.yaml

Edit config.yaml:

public_ip: 222.222.222.222

local_range: 192.168.122.0/24

domain:
  MySQL:
    private_ip: 192.168.122.20
    local_range: 192.168.122.0/25
    port_map:
      tcp:
        - [10022, 22]
        - 3306

  Apache:
    private_ip: 192.168.122.30
    port_map:
      tcp:
        - [20022, 22]
        - 80
        - 443

public_ip is the ip address of host. The forward rules for each VM are configured under domain. The domain name must be the same as the libvirt VM name, which could be found by virsh list --all.

private_ip is the ip address of VM. port_map includes the list of ports to be forwarded. If the port number is the same for both the host and guest, only one number is needed, or specify [HOST_PORT, GUEST_PORT].

After the modification of configuration, please shutdown the VM, and use virsh start VMNAME to start the VM, then the rules are automatically added. After shutdown the VM, these rules will be deleted.