TAOFirewall2 – Complete revamp of the firewall script

Two and a half months later and after a lot of cursing we are proud to present a completely reworked version of our firewall for Xen Dom0s and “normal” servers, including DomUs. There have been tons of changes and basically there is nothing as it was in the previous versions.

Overview

First of all this version provides both IPv4 and IPv6 support within one set of scripts. This also means that we had to change the way we configure the firewall but let’s talk about the new configuration in the next paragraph. The second most important thing is that it fully integrates into Xen implying it now dynamically adds and deletes rules as DomUs are going on and off line. Additionally it comes with modified scripts for Xen to use IPv6 in the DomUs. To increase execution speed and avoid some problems we now use iptables-restore instead of calling ip(6)tables directly but you can easily switch back to iptables. The last but most certainly not least important new feature is the ability to manage outgoing connections as well. Most striking though might be that we split the script into multiple files in multiple directories. Oh, and by the way: We got rid of the Python code.

Configuration

Configuration of the old firewall was tricky and not very flexible. We hope that the new way is more intuitive to administrators. Combining IPv4 and IPv6 in one script means we should combine the configuration files, too. Thus we now have only /etc/taofirewall with the two subdirectories, ipv4.d and ipv6.d. Aside from those we also have four configuration files: ipv4.conf, ipv6.conf, routing.cfg and the good old taofirewall.conf. The latter lost most of its configuration variables and got quite a few new ones. Most of them are pretty static and only specifies programs to use but there are some which are more interesting.

Prioritization

To understand their purpose we have to look at another change that came along with the new configuration directories: Prioritization. Each chain (INPUT, OUTPUT and FORWARD) can now have up to 99 levels of priority with different “TARGETS” (meaning ACCEPT, DROP or RETURN) so you can weave multiple levels of rules. Most of the time you’ll need only one though.

Structure

The structure of the configuration files has changed as well. There’s now only a header and a settings area. All settings are optional, especially if you disable the firewall to let e.g. a DomU handle it itself. In that case you only need to set the variables in the header, the rest can be deleted or left unchanged. NAT/PAT is now in the settings section and got a setting added which can be used to specify the device the NAT-rules should be applied to.

Protocols and variables

A major change is that now it is possible to specify the protocol a port is opened for which means you can now use UDP and TCP likewise. The firewall defaults to TCP though if no protocol is specified. Also the configuration of restrictions per IP/subnet have changed. There’s now only *one* variable for incoming and one for outgoing connections. The one for incoming is called “USERS” and the one for outgoing “SERVICES“. Port-protocol pairs not opened only to specific IPs and subnets are specified in PUBLIC_PORTS/PUBLIC_PROTOCOLS or OUT_PORTS/OUT_PROTOCOLS.

Xen integration

The next big improvement is the integration with Xen it offers right along with scripts which support IPv6 for DomUs. This means that the firewall always uses only the minimal amount of rules necessary and protects against spoofing of IP addresses.

Xenstore and Antispoof

The firewall uses the information storage space of Xen, called Xenstore, to determine which DomUs are running and to store some additional information about routing when used for a DomU. Aside the obvious advantages of a centralized information structure the Xenstore also holds information about the U (as in DomU) and the virtual interfaces on line. Thanks to that the firewall can offer full spoofing protection; IPs in use by any running DomU will only be accepted as source IPs on the DomUs interface.

Mixed Routed/NAT/Virtual LAN setup

Also provided are some scripts which make it possible to setup a mixed environment where some DomUs have routed, public IPs and other only have access to a virtual private LAN and connect and are connected to via NAT. We’re not using any Xen-NAT-scripts here but the ones for bridging. You can even have multiple separate virtual private networks whose traffic crossing over to another network is subject to the very same firewall rules as traffic from public interfaces. To make some more common situations easier to manage there are so called subnet “friends” which allow traffic to flow without limitations from the “friendly” network.

Manipulating internal routing

Since we’re using OpenVPN and it’s not running on the Dom0 we also had to include ways to manipulate the routing of the Dom0 and keep these things in mind when setting up the firewall rules. Thus you can now tell the firewall to route certain IPs/subnets to other IPs and to leave that traffic alone because it’s not the machines business but the one of the machine the traffic is routed to. Beware that the routing and the “leaving alone” are configured in different locations thus you can still apply rules to routed traffic if you want to.

IPv6 for all DomUs – Hetzner special included

We also modified the Xen scripts to finally provide full IPv6 support even to the pseudo-NATted machines in virtual LANs. Hetzner‘s routing is unfortunately a pretty messy thing since they began to bind all IPs to the physical hosts MAC. This makes bridging DomUs to eth0 of the Dom0 ultimately pointless and forces one to use routing which is not trivial – at least compared to bridging. But it’s not a big deal – until you want to use IPv6 how it is meant to be used.

A routed setup means you’ll have to deal with ARP for IPv4 and NDP for IPv6. For IPv4 this is a straight forward thing since in most cases you’ll have discrete IPs you assign to your interfaces and Xen automatically configures ARP proxy for these IPs. This also works with IPv6 using NDP proxy but with IPv6 it’s likely you want to assign a whole subnet, let’s say a /80, to your DomUs. Well – forget it. You’ll have to assign each and every of the roughly 281 trillion IPs separately. But Hetzner knows this is … unfit for most professional clients and thus found a way around this: They route a second /64 onto the second IP (e.g. 2001:db8:a:b::2) of your first /64. You can now use your Dom0 as gateway for the second /64 which effectively solves the MAC problem.

Our scripts are meant to primarily run on hosts within “normal” environments meaning: No double backflips necessary. The problem here is that the Xen scripts (including the ones we fitted with IPv6 support) automatically use the Dom0s main IP for the virtual interface on the Dom0 – which in this case would be the second IP of the first /64 – as IP of the virtual interfaces. This is important for automatic routing configuration in the DomU via NDP – it won’t work for this IP since it’s in a different subnet. To make it work anyway we have to set up a static route and that’s exactly what we’re doing with IPv4 addresses anyway – but this is mainly because IPv4 addresses are scarce. Let’s be honest: It’s gross. A much nicer solution would be to use the second IP of the second /64 as the virtual interface’s IP on the Dom0’s side because then NDP will work flawlessly and we don’t have to use some special configuration on the DomU.

The easiest way to accomplish that is to simply hardcode it into /etc/xen/scripts/vif-common.sh into the function dom0_ip6(). Not perfect but we figured that in this case it’s the appropriate solution. Setting up DomUs with IPv6 just got trivial. Add the subnet to your DomU’s Xen configuration file (use a space to separate it from the vif’s IPv4 address(es)) and add something similar to the following to /etc/network/interfaces in your DomU:

iface eth0 inet6 static
 address 2001:db8:b:b:1:2
 netmask 64
 gateway 2001:db8:b:b::2

Congratulations, your DomU now potentially has IPv6 access!

Downside

A downside of the way it integrates is that there are some situations where DomUs in a mixed routed/NAT set-up cannot see each other. This only affects certain installations e.g. when you have one /64-Subnet and both your bridged and routed DomUs are configured for it, routed DomUs won’t be able to reach the bridged ones and likewise. But this is easy to avoid and thus not that much of a problem. And, for the record, not the firewall’s fault but “by design” of IP, ARP/NDP, bridges and stuff.

Environment

This version was developed on a Debian Squeeze Dom0 with Xen 4.0.1 and Debian Squeeze DomUs although in theory it should work flawlessly on Debian Lenny and Xen 3.2-1 as well. Since we use Debian Lenny on most of our hosts we’ll certainly take the time to put it to the test there too. Check back in a few weeks to see if it worked out.

Conclusion

It ain’t perfect in a lot of ways but we can really do a lot of things far from trivial while keeping configuration quite small and simple. Its new architecture with several files holding different versions of the same function make it a lot easier to modify and adapt to different situations. Last but not least combining IPv4 and IPv6 and using iptables-restore means reducing a lot of overhead and increasing execution speed immensely. We hope you’ll have as much use for it as we do and we’d like to hear any constructive feedback you have to offer.

How to install?

This is outdated, please continue with this update.

Download TAOFirewall2.tar.bz2 to your machine then run as root in a Bash(!):

install -o root -g root -m 755 firewall /etc/init.d
mkdir -p /{etc,usr/local/share}/taofirewall
cp usr-local-share-taofirewall/* /usr/local/share/taofirewall/
if [ -r "/var/run/xenstore.pid" ]
then
 cp -r etc-taofirewall (Xen)/* /etc/taofirewall/
 cp -i etc-xen-scripts/* /etc/xen/scripts/
else
 cp -r etc-taofirewall/* /etc/taofirewall/
fi
chmod +x /usr/local/share/taofirewall/test.sh
exec /usr/local/share/taofirewall/test.sh

Once you’ve configured the firewall you can run

invoke-rc.d firewall start
iptables -L -n -v | more

to see if it’s working properly. If positive you may add it to your rc.d by running

update-rc.d firewall defaults

Good luck!

Sorry, no Gist this time. It’s just too much code.