Until the last decade, it was very common to have “one task per systemâ€, or, when running on a shoestring, “a few tasksâ€, such as the case of a software firewall which also worked as a mail transport agent.
Thanks to the magic of hardware and operating system assisted virtualization, we no longer have to directly emulate a complete machine. We can literally take a system and carve it into several smaller slices, each performing it’s own isolated task.
For instance: This webserver running my website isn’t only running a handful of different websites- it has a “heartbeat†monitor system watching my other personal machines that are on the internet, and will alert me to any issue, and several other projects at any one time.
Linux is one of the top KVM virtualization hosts (along with SmartOS and Hyper-V), which allows you to run completely different operating systems beneath it. I like to run my nameservers on OpenBSD as it’s very lean, and it’s PF implementation runs circles around iptables. I now accomplish this utilizing KVM.
Nameservers are a special sort of service- it commonly uses UDP (although TCP is supported), which means that if your machine isn’t fast enough, the client will just go to another one down the list and ask it where to find what it’s looking for. This is a good example where you need to tune your firewall/host rules to keep things fast.
I run my services as NAT KVMs, rather than a bridged MAC address IP, as it keeps all services off of the internet, except those I wish to expose. This adds a bit of trivial overhead to forward the DNS requests on port 53 via TCP/UDP to the client. This can be accomplished with rules similar to the following (showing separately to illustrate what each does):
The following accepts traffic on the bridge device for both TCP and UDP and forwards it to the NAT host located at 192.168.0.100:
/sbin/iptables -A PREROUTING -t nat -i br0 -p tcp --dport 53 \ -j DNAT --to 192.168.0.100:53 /sbin/iptables -A PREROUTING -t nat -i br0 -p udp --dport 53 \ -j DNAT --to 192.168.0.100:53
This just makes sure that data that went to the NAT host makes it back out:
/sbin/iptables -A FORWARD -p tcp -d 192.168.0.100 \ --dport 53 -j ACCEPT /sbin/iptables -A FORWARD -p udp -d 192.168.0.100 \ --dport 53 -j ACCEPT
Now, this is fine, but this traffic can actually hit iptables TWICE. Packets on the bridge first hits iptables’ INPUT, then a second time after it sees that the destination is a local virtual MAC. So, I’ve setup this rule so it only goes through once. Keep in mind that action verbs for broute are inappropriately named (DROP means that the frame has to be routed, ACCEPT means that this frame has to be bridged):
/sbin/ebtables -t broute -A BROUTING -d the:bridge:mac:addy:goes:here \ -j redirect --redirect-target DROP
Note that I have not specified a protocol, as I only use ipv4/ipv6 on this machine. This has removed in-kernel time for manipulating this data, which is pretty important if you have a busy nameserver (which mine isn’t).