Forwarding ports to guests in libvirt / KVM

Solution 1:

The latest stable release for libvirt for Ubuntu is version 0.7.5, which doesn't have some newer features (i.e. script hooks and network filters) which make automatic network configuration easier. That said, here's how to enable port forwarding for libvirt 0.7.5 on Ubuntu 10.04 Lucid Lynx.

These iptables rules should do the trick:

iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 80 -j DNAT --to-destination 10.0.0.1:80
iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 22 -j DNAT --to-destination 10.0.0.2:22
iptables -I FORWARD -m state -d 10.0.0.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT

The default KVM NAT config provides a rule similar to the 3rd I gave above, but it omits the NEW state, which is essential for accepting incoming connections.

If you write a startup script to add these rules and you're not careful, libvirt 0.7.5 overrides them by inserting its own. So, in order to make sure these rules are applied properly on startup, you need to make sure libvirt has initialized before you insert your rules.

Add the following lines to /etc/rc.local, before the line exit 0:

(
# Make sure the libvirt has started and has initialized its network.
while [ `ps -e | grep -c libvirtd` -lt 1 ]; do
        sleep 1
done
sleep 10
# Set up custom iptables rules.
iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 80 -j DNAT --to-destination 10.0.0.1:80
iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 22 -j DNAT --to-destination 10.0.0.2:22
iptables -I FORWARD -m state -d 10.0.0.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT
) &

The sleep 10 above is a hack to make sure the libvirt daemon has had a chance to initialize its iptables rules before we add our own. I can't wait until they release libvirt version 0.8.3 for Ubuntu.

Solution 2:

There is a way to set up port redirection on the fly when the guest is using user-mode networking, I blogged about it here:

http://blog.adamspiers.org/2012/01/23/port-redirection-from-kvm-host-to-guest/

You can see the details there, but for convenience, here is the solution I figured out:

virsh qemu-monitor-command --hmp sles11 'hostfwd_add ::2222-:22'

This one-liner is a lot easier than the other answers but only works in some scenarios (user mode network stack).


Solution 3:

A more "official"[1] way to do this is to create a hook script as described at the libvirt website:

http://wiki.libvirt.org/page/Networking#Forwarding_Incoming_Connections

... basically this script will be invoked when a KVM guest is booted-up. The script itself will add the appropriate iptable rules (similar to Isaac Sutherland's answer above) with the 'NEW' connection state correctly added. Note that you must modify the script with the correct values for your hosts and ports.

[1] though the libvirt documentation itself says this is kind of a hack, go figure