How to Forward Ports to KVM Guests Using NAT in libvirt (iptables Method)


2 views

When using libvirt/KVM with NAT networking (default configuration), VMs get private IP addresses (like 10.0.0.0/24) and connect to outside networks through the host's NAT. To expose services running on VMs to external networks, we need to configure port forwarding rules using iptables.

From your current setup, I see:

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

This shows basic forwarding is allowed between your VMs (10.0.0.0/24) and external networks.

For your specific requirements:

1. Forward host's port 80 to 10.0.0.1:80 (HTTP)

2. Forward host's port 22 to 10.0.0.2:22 (SSH)

# HTTP forwarding
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1:80
iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.1 --dport 80 -j SNAT --to-source 10.0.0.1

# SSH forwarding
iptables -t nat -A PREROUTING -p tcp --dport 22 -j DNAT --to-destination 10.0.0.2:22
iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.2 --dport 22 -j SNAT --to-source 10.0.0.2

On Ubuntu 10.04, you'll need to save the rules and ensure they load at boot:

# Save current rules
iptables-save > /etc/iptables.rules

# Create a pre-up script
echo '#!/bin/sh' > /etc/network/if-pre-up.d/iptablesload
echo 'iptables-restore < /etc/iptables.rules' >> /etc/network/if-pre-up.d/iptablesload
chmod +x /etc/network/if-pre-up.d/iptablesload

You can also define port forwarding in your libvirt network configuration:

<network>
  <name>default</name>
  <forward mode='nat'/>
  <port>
    <start port='1024' end='65535'/>
  </port>
  <ip address='10.0.0.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='10.0.0.2' end='10.0.0.254'/>
    </dhcp>
    <port forwarding>
      <port dev='eth0'>
        <forward port='80' to='10.0.0.1'/>
        <forward port='22' to='10.0.0.2'/>
      </port>
    </port forwarding>
  </ip>
</network>

Then restart the network:

virsh net-destroy default
virsh net-start default

If forwarding doesn't work:

# Check if forwarding is enabled
sysctl net.ipv4.ip_forward

# Check NAT rules
iptables -t nat -L -n -v

# Verify packets are reaching the VM
tcpdump -i virbr1 -n port 80 or port 22

When exposing services:

# Limit source IPs if possible
iptables -t nat -A PREROUTING -p tcp -s 192.168.1.0/24 --dport 22 -j DNAT --to-destination 10.0.0.2:22

# Rate limit connections
iptables -A FORWARD -p tcp --dport 22 -m limit --limit 3/minute --limit-burst 3 -j ACCEPT

When working with KVM/libvirt VMs in NAT mode, the default network configuration creates a virtual bridge (virbr1 in your case) with IP 10.0.0.1/24. Your VMs get private IPs from this subnet (like 10.0.0.2), but need port forwarding to expose services to external networks.

Here's how to forward port 80 to 10.0.0.1 and port 22 to 10.0.0.2:

# Forward HTTP traffic
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1:80
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.1 --dport 80 -j SNAT --to-source 10.0.0.1

# Forward SSH traffic  
sudo iptables -t nat -A PREROUTING -p tcp --dport 22 -j DNAT --to-destination 10.0.0.2:22
sudo iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.2 --dport 22 -j SNAT --to-source 10.0.0.1

On Ubuntu 10.04, you'll need to save the rules and restore them on boot:

sudo iptables-save > /etc/iptables.rules
echo 'pre-up iptables-restore < /etc/iptables.rules' | sudo tee -a /etc/network/interfaces

Run these commands to verify your setup:

# Check NAT rules
sudo iptables -t nat -L -n -v

# Test connectivity
nc -zv 1.2.3.4 80
nc -zv 1.2.3.4 22

For a more maintainable solution, you can modify the libvirt network XML:

<network>
  <name>default</name>
  <forward mode='nat'/>
  <ip address='10.0.0.1' netmask='255.255.255.0'>
    <port start='1024' end='65535'/>
  </ip>
</network>

If connections aren't working:

  1. Ensure IP forwarding is enabled: echo 1 > /proc/sys/net/ipv4/ip_forward
  2. Check VM firewall rules aren't blocking the traffic
  3. Verify the virbr1 interface is up