Dynamic port-forwarding for NAT-ed kvm/libvirt networks

Last week, I've wrote a post about setting up a port forwarding strategy manually on a kvm/libviert network. Today we'll automate this process taking advantage of the many cool feature of libvirt.

As explained in the previous post, We want to to make a service that is on a guest behind a NATed virtual network publicly available. In this article, we'll setup libvirt's "hook" script for qemu to install the necessary iptables rules to forward incoming connections to the host on any given port, to another port on the guest machine.

  • Stop the guest if it's running
virsh shutdown VM_NAME
  • Create the file /etc/libvirt/hooks/qemu and add he following script to it:

      #!/bin/bash
      # used some from advanced script to have multiple ports: use an equal number of guest and host ports
    
      echo `date` hook/qemu "${1}" "${2}" >>/root/hook.log
    
    
      # Update the following variables to fit your setup
    
      ### First VM
      Guest_name=VM_1_NAME
      Guest_ipaddr=192.168.122.4
      Host_port=(  '1234' )
      Guest_port=( '22' )
    
      length=$(( ${#Host_port[@]} - 1 ))
      if [ "${1}" = "${Guest_name}" ]; then
      if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
          for i in `seq 0 $length`; do
                  echo "kvm-Ho." >>/root/hook.log
                  /sbin/iptables -D FORWARD -o virbr0 -d  ${Guest_ipaddr} -j ACCEPT
                  /sbin/iptables -t nat -D PREROUTING -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
          done
      fi
      if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
          for i in `seq 0 $length`; do
                  echo "kvm-Hey." >>/root/hook.log
                  /sbin/iptables -I FORWARD -o virbr0 -d  ${Guest_ipaddr} -j ACCEPT
                  /sbin/iptables -t nat -I PREROUTING -p tcp --dport ${Host_port[$i]} -j DNAT --to ${Guest_ipaddr}:${Guest_port[$i]}
          done
      fi
      fi 
    

you can add several machine to this file.

  • make the file executable
$ chmod +x /etc/libvirt/hooks/qemu`

(of course, you'll need to do this only the first time)

  • Restart the libvirtd service.
$ /etc/init.d/libvirt-bin restart
  • and finally, Start your VM:
$ virsh start VM_NAME

The complete version of the script is available on github. Comments, suggestions and feedback is always welcome ;)