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:
- Ensure IP forwarding is enabled:
echo 1 > /proc/sys/net/ipv4/ip_forward
- Check VM firewall rules aren't blocking the traffic
- Verify the virbr1 interface is up