Friday, September 14, 2007

Using pf as a Firewall

I've long been a fan of FreeBSD (although I also use a Mac, Linux, and Windows machine -- right tool for the job, and all that), and one of the things I like best about the various BSDs is the ease with which you can set up a stateful packet-filtering firewall. To put it simply, pf rocks.

Setting it up for the first time, though, can be a bit of a chore. If you are interested in giving pf a look, here's how you do it on FreeBSD.

Recompile your kernel
For the sake of argument, let's assume that we are going to be setting up a machine called "zeus" as a gateway server with a few simple services running on it. We first need to compile the pf stuff into the kernel, and then install our new kernel. First, get to the right directory:

[tcs@zeus] ~> su -
1:28PM up 60 days, 3:54, 1 user, load averages: 0.03, 0.01, 0.00
[root@zeus] ~# cd /usr/src/sys/i386/conf/

Now, copy the file GENERIC to some new file (I'm calling my zeus):

[root@zeus] ~# cp GENERIC ZEUS

Edit the file ZEUS and add the following lines just below "options ADAPTIVE_GIANT":

#PF Support
device pf #PF OpenBSD packet-filter firewall
device pflog #logging support interface for PF
device pfsync #synchronization interface for PF

#ALTQ Support
options ALTQ
options ALTQ_CBQ # Class Bases Queueing
options ALTQ_RED # Random Early Drop
options ALTQ_RIO # RED In/Out
options ALTQ_HFSC # Hierarchical Packet Scheduler
options ALTQ_CDNR # Traffic conditioner
options ALTQ_PRIQ # Priority Queueing

Now, we need to compile and install the kernel. Go to the /usr/src directory and execute this command:

make buildkernel KERNCONF=ZEUS

Wait (quite a while). When it's done, type this:

make installkernel KERNCONF=ZEUS

Wait a bit longer. Reboot. If it comes up, congrats -- you have a new kernel.

Enable PF in rc.conf
We need to tell the OS that we want to use pf. Enter the following in /etc/rc.conf

pf_rules="/etc/pf.conf" # rules definition file for pf
pf_flags="" # additional flags for pfctl startup
pflog_enable="YES" # start pflogd(8)
pflog_logfile="/var/log/pflog" # where pflogd should store the logfile
pflog_flags="" # additional flags for pflogd startup

Set up the firewall: pf.conf
The default rules for pf.conf are very, very detailed. We are going to ignore them, and make our own. First, back up the /etc/pf.conf file that is (probably) there by default:

cd /etc
mv pf.conf pf.conf.dist

Now, edit a new file called pf.conf and enter the file below (each service is mentioned in the comments preceding it):

# define our interfaces. Find yours by typing ifconfig as root
ext_if="fxp1" #your external nic
ext_gw="19x.xx.xx.254" #change this to the ip of your gateway router
int_if="fxp0" #your internal nic
oip1="" #change this to your external ip address

#clean everything up (reset to defaults)
scrub in

# nat for internal clients
nat on $int_if from to any -> ($int_if)

# Pass/block rules

block in

pass quick on { $lo $int_if } all

# we shouldn't have to do this, but seem to have to on freebsd
pass out on $lo all

# allow ssh to local machine
pass in on $ext_if proto tcp to ($ext_if) port ssh keep state

Save, and you are ready to give things a try!

Helpful hints:

To enable the firewall rules:

pfctl -f /etc/pf.conf

To enable pf:

pfctl -e

To disable pf:

pfctl -d