Fixing the Default KVM Host Firewall

I recently installed KVM on my laptop and all was going reasonably well until I tried to connect to the internet from one of the guests. For some reason that I couldn’t fathom at first the guest couldn’t talk to the internet and the local network couldn’t talk to the guest. This surprised me because I’d already checked that the host could reach the internet and the local network could reach the virtual bridge and the host. I was also fairly sure that I had checked my first guest could access the internet but I couldn’t be sure.

UPDATE: What do you know the interface name was wrong in the default network configuration. I hadn’t realized that libvirt was installing the rules into the firewall each time it started but once I’d changed the name of the interface in the network definition it changed automatically in the firewall.

After checking all the obvious things I started looking at the firewall on the host machine. I didn’t immediately pick the firewall as the problem because KVM adds some very lax rules and I couldn’t believe they were causing the problem. When I checked though this is what I saw:

$ sudo iptables -vL
Chain INPUT (policy ACCEPT 47157 packets, 9794K bytes)
 pkts bytes target     prot opt in     out     source               destination         
    8   486 ACCEPT     udp  --  virbr0 any     anywhere             anywhere             udp dpt:domain
    0     0 ACCEPT     tcp  --  virbr0 any     anywhere             anywhere             tcp dpt:domain
    2   656 ACCEPT     udp  --  virbr0 any     anywhere             anywhere             udp dpt:bootps
    0     0 ACCEPT     tcp  --  virbr0 any     anywhere             anywhere             tcp dpt:bootps
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     all  --  eth00  virbr0  anywhere            
    0     0 ACCEPT     all  --  virbr0 eth00          anywhere            
    0     0 ACCEPT     all  --  virbr0 virbr0  anywhere             anywhere            
  144  8640 REJECT     all  --  any    virbr0  anywhere             anywhere             reject-with icmp-port-unreachable
   34  2728 REJECT     all  --  virbr0 any     anywhere             anywhere             reject-with icmp-port-unreachable
Chain OUTPUT (policy ACCEPT 46508 packets, 7354K bytes)
 pkts bytes target     prot opt in     out     source               destination         
    2   656 ACCEPT     udp  --  any    virbr0  anywhere             anywhere             udp dpt:bootpc

At first glance everything looks correct. My VM network is and there are entries in the FORWARD table. I’d already checked and turned on forwarding in sysctl as well. But wait, why is that network interface called eth00 rather than eth0 as it actually is on the machine?

To very quickly test my suspicion that these odd rules were causing the problem I added these two rules to the FORWARD table:

sudo iptables -I FORWARD -d -j ACCEPT
sudo iptables -I FORWARD -s -j ACCEPT

These are broader than I would like but they provided the point that it was the firewall that was causing the problem. To put in a better fix first run this command to list the firewall rules with numbers:

sudo iptables -vL --line-numbers

Now use this command to delete the offending rules:

sudo iptables -D FORWARD 1

Where you replace 1 with the line number you want to delete. Note that if you start at the highest number and work backwards the line numbers don’t change as you delete items. Once you are done add two new rules like this:

sudo iptables -I FORWARD -i eth0 -o virbr0 -d -j ACCPET
sudo iptables -I FORWARD -i virbr0 -o eth0 -s -j ACCEPT

The first rule allows connections in from the LAN (or Internet depending on how you’ve configured the rest of the system) to the guests on the virtual subnet. The second rule allows connections out from the guests to the LAN / Internet. I’m less concerned about connections outbound than I am inbound.

If you want you can tighten the first rule up by only allowing connections when the source is within the subnet of your LAN:

sudo iptables -I FORWARD -i eth0 -o virbr0 -s -d -j ACCEPT

I think it is reasonable to assume that connections will only be coming from within the LAN. If connections were to be coming from the wider Internet then it’s likely they would be port forwarded from a public facing machine that is on the LAN.