[libvirt] Accepting RELATED, ESTABLISHED (TCP) connections into VM using Network Filters

Wido den Hollander wido at widodh.nl
Fri Jun 16 07:51:37 UTC 2017


Over the past few days I've been trying to get a prototype working of a stateful firewall for a Virtual Machine using Libvirt's network filters.

My goal is to replace the current custom Python/Java code in the Apache CloudStack [0] project by Network Filters of Libvirt.

Both IPv4 and IPv6 should work, but I started off with IPv4 and I have issues with accepting back RELATED,ESTABLISHED connections into the VM.

In the guest's XML I have this defined:

      <filterref filter='nwfilter-test'>
        <parameter name='IP' value=''/>
        <parameter name='IPV6' value='2001:db8:100:0:5054:ff:fe9c:6ce6'/>
        <parameter name='IPV6' value='fe80::5054:ff:fe9c:6ce6'/>

And the filter currently looks like this:

<filter name='nwfilter-test' chain='root'>
    <!-- These are default build-in filters from libvirt and are mainly ipv4 only-->
    <filterref filter='no-mac-spoofing'/>
    <filterref filter='no-ip-spoofing'/>
    <filterref filter='no-arp-spoofing'/>
    <filterref filter='allow-dhcp'/>

    <!-- IPv4 Rules -->
    <rule action='accept' direction='in' priority='100'>
      <all state='RELATED,ESTABLISHED'/>

    <rule action='return' direction='in' priority='500'>

    <rule action='accept' direction='in' priority='500'>
      <tcp dstportstart='22'/>
    <rule action='accept' direction='in' priority='500'>
      <tcp dstportstart='80'/>

    <rule action='reject' direction='in' priority='1000'>

I can SSH into the VM and also visit the Webserver running on it. But going out the VM results in issues:

root at nwfilter-test:~# telnet 80
telnet: Unable to connect to remote host: Connection refused
root at nwfilter-test:~# 

I can however ping the same target:

root at nwfilter-test:~# ping -c 2
PING ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=56 time=13.2 ms
64 bytes from icmp_seq=2 ttl=56 time=14.1 ms

--- ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 13.225/13.703/14.182/0.492 ms
root at nwfilter-test:~#

Looking at iptables-save it seems like the right rules are programmed:

-A FI-vnet1 -p icmp -j RETURN
-A FI-vnet1 -p tcp -m tcp --sport 22 -m conntrack --ctstate ESTABLISHED -m conntrack --ctdir REPLY -j RETURN
-A FI-vnet1 -p tcp -m tcp --sport 80 -m conntrack --ctstate ESTABLISHED -m conntrack --ctdir REPLY -j RETURN
-A FI-vnet1 -j REJECT --reject-with icmp-port-unreachable
-A FO-vnet1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FO-vnet1 -p icmp -j RETURN
-A FO-vnet1 -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -m conntrack --ctdir ORIGINAL -j ACCEPT
-A FO-vnet1 -p tcp -m tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -m conntrack --ctdir ORIGINAL -j ACCEPT
-A FO-vnet1 -j REJECT --reject-with icmp-port-unreachable
-A HI-vnet1 -p icmp -j RETURN
-A HI-vnet1 -p tcp -m tcp --sport 22 -m conntrack --ctstate ESTABLISHED -m conntrack --ctdir REPLY -j RETURN
-A HI-vnet1 -p tcp -m tcp --sport 80 -m conntrack --ctstate ESTABLISHED -m conntrack --ctdir REPLY -j RETURN
-A HI-vnet1 -j REJECT --reject-with icmp-port-unreachable
-A libvirt-host-in -m physdev --physdev-in vnet1 -g HI-vnet1
-A libvirt-in -m physdev --physdev-in vnet1 -g FI-vnet1
-A libvirt-in-post -m physdev --physdev-in vnet1 -j ACCEPT
-A libvirt-out -m physdev --physdev-out vnet1 --physdev-is-bridged -g FO-vnet1

I tried changing 'accept' into 'return' for the incoming RELATED,ESTABLISHED rules, but that didn't help.

I also tried searching for example of more complex network filters, but all I keep finding are the default filters of Libvirt.

Does anybody know what I'm doing wrong here? Or are there any examples of working filters out there?

Thank you!


[0]: http://cloudstack.apache.org/

More information about the libvir-list mailing list