Recently at work I had a need to put a firewall in front of a few Win32 web servers. The number of IP addresses assigned to these servers made putting a NAT device in front of them a daunting task. This led me to create an ipless bridging firewall using OpenBSD. The bridge allows firewalling without having to change the IP addresses of your hosts, as it just passes the traffic along filtering according to your rules. This article details how I went about setting up the bridge I created at work.
I had an IBM x305 1u server I used for this project. It was nice that it had two NICs built in, so I didn't need to add any to the box. The specs on this box are overkill for it's purpose, with a 2.4Ghz Pentium 4 processor and 1GB of RAM. You could really get by with any Pentium series processor and 32MB of RAM, and that may even be overkill.
Installing OpenBSD is really beyond the scope of this article, but I do have a few tips. Make sure when you slice your disk you make the var and usr partitions a decent size. A few GB each should do the trick. When you choose installation sets, I would just accept the defaults. Answer no to running sshd, ntpd, installing X, and putting the default console on com0. When you are done with the installation, enter "halt" and reboot.
When the system has finished rebooting, log in as root. The first thing we want to do is make sure all network services are shut down. Create an /etc/rc.conf.local file for this, and add the following lines.
sshd_flags=NO portmap=NO inetd=NO pf=YES pf_rules=/etc/pf.conf
Next edit root's crontab to disable the maintenance emails. Your crontab should look like the following one.
SHELL=/bin/sh PATH=/bin:/sbin:/usr/bin:/usr/sbin HOME=/var/log # #minute hour mday month wday command # */10 * * * * /usr/libexec/atrun # # sendmail clientmqueue runner */30 * * * * /usr/sbin/sendmail -L sm-msp-queue -Ac -q # # rotate log files every hour, if necessary 0 * * * * /usr/bin/newsyslog # send log file notifications, if necessary #1-59 * * * * /usr/bin/newsyslog -m # # do daily/weekly/monthly maintenance 30 1 * * * /bin/sh /etc/daily 1>/var/log/daily.out 2>&1 30 3 * * 6 /bin/sh /etc/weekly 1>/var/log/weekly.out 2>&1 30 5 1 * * /bin/sh /etc/monthly 1>/var/log/monthly.out 2>&1
Next, use ifconfig to see what NICs you have. In my case I had two Broadcom NICs, which use the bge driver. The BSD operating systems use the driver to name the interfaces, so, as you can see below, my interfaces are bge0 and bge1.
# ifconfig -a lo0: flags=8049mtu 33224 inet 127.0.0.1 netmask 0xff000000 inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x5 lo1: flags=8008 mtu 33224 bge0: flags=8843 mtu 1500 address: 00:0d:60:0f:35:cc media: Ethernet autoselect (100baseTX full-duplex) status: active inet 192.168.0.10 netmask 0xffffff00 broadcast 192.168.0.255 inet6 fe80::204:acff:fedd:396a%bge0 prefixlen 64 scopeid 0x1 bge1: flags=8843 mtu 1500 address: 00:0d:60:0f:35:cd media: Ethernet autoselect (100baseTX full-duplex) status: active inet 192.168.0.11 netmask 0xffffff00 broadcast 192.168.0.255 inet6 fe80::204:acff:fedd:396b%bge1 prefixlen 64 scopeid 0x2 pflog0: flags=141 mtu 33224
Now we need to configure the interfaces and the brige interface. Since we are create an ipless bridge, we don't need to specify anything for the interfaces except to bring them up.
# echo up > /etc/hostname.bge0 # echo up > /etc/hostname.bge1 # cat > /etc/bridgename.bridge0 add bge0 add bge1 blocknonip bge0 blocknonip bge1 up Ctrl-D
I have seen opposing examples on the net about whether or not the net.inet.ip.forwarding sysctl should be enabled. I enabled it just to be sure, as a few websites say it greatly improves performance. If you want to enable this, just find the following line in /etc/sysctl.conf and uncomment it.
net.inet.ip.forwarding=1
The only thing left to do now is configure pf, the OpenBSD Packet Filter. This is what is going to do our firewalling. Below is an example /etc/pf.conf file. You will want to edit your rules to fit your individual needs.
int_if = "bge1"
ext_if = "bge0"
localnet = "192.168.0.0/24"
trustednets = "{ 192.168.0.0/24, 192.168.1.0/24 }"
# we only want to filter one interface, so pass everything on the inside interface
pass in quick on $int_if all
pass out quick on $int_if all
# block everything by default on the external interface
block in log on $ext_if all
block out log on $ext_if all
# allow UDP DNS traffic
pass out log quick on $ext_if proto udp from $localnet to $trustednets port 53 keep state
# allow FTP, SSH, DNS and HTTP traffic to trusted networks
pass out log quick on $ext_if proto tcp from $localnet to $trustednets \
port { 20, 21, 22, 53, 80, 81, 443 } modulate state
# allow incomming FTP, SSH, and HTTP traffic
pass in log quick on $ext_if proto tcp from any to $localnet \
port { 20, 21, 22, 80, 81, 443 } modulate state
# allow pings
pass in log on $ext_if proto icmp from any to $localnet icmp-type 8 code 0 keep state
pass out log on $ext_if proto icmp from $localnet to any icmp-type 8 code 0 keep state
These rules are fairly restrictive, and the comments should explain pretty well what each rule does.
Now that the all the software was configured, we need to actually put the bridge in it's place on the network. Here is how I did it.
Internet Router
|
Unprotected Switch
|
OpenBSD Bridge
|
Protected Switch
| | |
Server Server Server
Most likely you will find something that does not work. In these instances you can use tcpdump to watch the pflog0 device and see what is being blocked and passed. This is the reason I put "log" on so many of my rules in the pf.conf file.
# tcpdump -enttti pflog0
If you want to limit the results to only packets coming from or to a specific host, you could do the following.
# tcpdump -enttti pflog0 host 192.168.0.25
You could also limit to specific ports on that host.
# tcpdump -enttti pflog0 host 192.168.0.25 and tcp port 80
Once you are done troubleshooting any problems you encounter, it would be a good idea to remove all the log keywords from the file. I would keep a copy of the pf.conf file with the logging enabled in case you need to debug something in the future. To load a config file while pf is running, you can use this command:
# pfctl -f /etc/pf.conf
Setting up an ipless bridging firewall with OpenBSD isn't an easy task, but isn't extremely difficult either. The hardest part is learning how pf works and creating a good rule set that strikes the right balance between security and usefulness.