Forwarding ports to guests in libvirt / KVM

87,372

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

Solution 4

The "only" way we can make a port forward using KVM (libvirt) with the "default network" (virbr0) is using the hack/workaround informed by @Antony Nguyen . Or more simply you can use libvirt-hook-qemu.

This thread has a complete explanation of how to solve this problem for CentOS 7 (and certainly for other distros) using libvirt-hook-qemu: https://superuser.com/a/1475915/195840 .

Share:
87,372

Related videos on Youtube

steveh7
Author by

steveh7

Updated on September 17, 2022

Comments

  • steveh7
    steveh7 almost 2 years

    How can I forward ports on a server running libvirt/KVM to specified ports on VM's, when using NAT?

    For example, the host has a public IP of 1.2.3.4. I want to forward port 80 to 10.0.0.1 and port 22 to 10.0.0.2.

    I assume I need to add iptables rules, but I'm not sure where is appropriate and what exactly should be specified.

    Output of iptables -L

    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination         
    ACCEPT     udp  --  anywhere             anywhere            udp dpt:domain 
    ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:domain 
    ACCEPT     udp  --  anywhere             anywhere            udp dpt:bootps 
    ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:bootps 
    
    Chain FORWARD (policy ACCEPT)
    target     prot opt source               destination         
    ACCEPT     all  --  anywhere             10.0.0.0/24         state RELATED,ESTABLISHED 
    ACCEPT     all  --  10.0.0.0/24          anywhere            
    ACCEPT     all  --  anywhere             anywhere            
    REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 
    REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination         
    

    Output of ifconfig

    eth0      Link encap:Ethernet  HWaddr 00:1b:fc:46:73:b9  
              inet addr:192.168.1.14  Bcast:192.168.1.255  Mask:255.255.255.0
              inet6 addr: fe80::21b:fcff:fe46:73b9/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:201 errors:0 dropped:0 overruns:0 frame:0
              TX packets:85 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000 
              RX bytes:31161 (31.1 KB)  TX bytes:12090 (12.0 KB)
              Interrupt:17 
    
    lo        Link encap:Local Loopback  
              inet addr:127.0.0.1  Mask:255.0.0.0
              inet6 addr: ::1/128 Scope:Host
              UP LOOPBACK RUNNING  MTU:16436  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
    
    virbr1    Link encap:Ethernet  HWaddr ca:70:d1:77:b2:48  
              inet addr:10.0.0.1  Bcast:10.0.0.255  Mask:255.255.255.0
              inet6 addr: fe80::c870:d1ff:fe77:b248/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:468 (468.0 B)
    

    I'm using Ubuntu 10.04.

    • Manuel Faux
      Manuel Faux almost 14 years
      why using ifconfig? ip is the successor of ifconfig. ;)
    • akaihola
      akaihola over 12 years
      Question 233760 addresses this on never versions of libvirt. serverfault.com/questions/233760
  • steveh7
    steveh7 almost 14 years
    Thank you for this, but with KVM specifically it needed the NEW state flag as well
  • Manuel Faux
    Manuel Faux almost 14 years
    Can you explain how you would do this with current libvirt?
  • Jeremie
    Jeremie almost 14 years
    You don't need the hacked while loop and sleep commands if one of the hook scripts runs after libvirt has initialized its networking. I'm not sure if the /etc/libvirt/hooks/daemon script is run before or after network initialization, but if you use /etc/libvirt/hooks/qemu you could create and destroy the rules when the appropriate virtual machines start and stop. I'm not sure how you would use network filters (if at all), but some of the examples at libvirt.org/firewall.html smell like they could modified to automate the creation of iptables rules.
  • voretaq7
    voretaq7 over 12 years
    Your solution is pretty interesting - Can you include some of the salient details (or at least the how-to bits) in your answer so that it's still useful if your blog is ever down for maintenance? :)
  • Adam Spiers
    Adam Spiers over 12 years
    Done, feel free to help my SF reputation go higher than 1 ;-)
  • Aron Lorincz
    Aron Lorincz almost 9 years
    Great, I can confirm it works, however I haven't tried what happens if I restart the server...
  • Eduardo Lucio
    Eduardo Lucio almost 5 years
    This approach requires the use of a network in user mode which can bring us some uninteresting limitations. See: linux-kvm.org/page/Networking#User_Networking . Another references: topic.alibabacloud.com/a/… , snippets.webaware.com.au/howto/… ]
  • user000001
    user000001 about 4 years
    This is by far the easiest answer, and the only one that worked in my case, +1