FreeBSD as a Gateway/Firewall with IPFilter

The original guide is found at www.derrickl.net/guides.

General Information

The purpose of this guide is to provide the reader with enough knowledge to configure FreeBSD with a basic NAT/Firewall setup, allowing you to share your internet connection between all your computers with only one IP address, and to protect your network from hackers.

This is not intended to be an exhaustive guide, merely a reasonably in depth introduction to get you off on the right foot as painlessly as possible.

You will have to modify this guide somewhat to match your particular network configuration. Here are the settings I use for this guide:

xl0 – External (internet facing) interface. Change to suit your configuration.

xl1 – Internal (inside network) interface. Change to suit your configuration.

192.168.0.0/16 – Internal network address space/netmask. Change to suit your configuration.

0.0.0.0/32 – Generic internet address space/netmask. Leave this alone, unless you really know what you’re doing.

Requirements

  1. FreeBSD installed and configured on a system with two (identical) NIC’s
  2. You must rebuild your kernel with the following options, but do not reboot yet:
OPTIONS IPFILTER
OPTIONS IPDIVERT
OPTIONS PFIL_HOOKS (5.0 - 5.2.1)

Configuration

Section A – Configuring rc.conf

There are several options that you must insert into your /etc/rc.conf to tell your system at boot time to enable itself to route packets between interfaces, to load the ipnat and ipfilter software you compiled into your kernel, and where to find the rules for ipfilter and ipnat. Open up your /etc/rc.conf with your favorite editor and insert the following options:

#Enable routing packets between interfaces
gateway_enable="YES"

#Bring up the ipfilter software
ipfilter_enable="YES"

#Tell ipfilter where to get its rules
ipfilter_rules="/etc/ipf.rules"

#Enable ipnat
ipnat_enable="YES"

#Tell ipnat where to get its rules
ipnat_rules="/etc/ipnat.rules"

Section B – Configuring NAT (ipnat.rules)

Now you must configure your rules for NAT (Network Address Translation). NAT allows many computers to access the Internet with only one IP address by mapping the internal IP addresses to the external address, sending the data out, and routing replies from the internet back to the internal system. All the work is done on the system running NAT. All you have to do on the client machine is set its default gateway to the internal IP of the system that you are setting up as a router/firewall.

The configuration file for the IPNAT software on your system will be /etc/ipnat.rules, as set above in /etc/rc.conf. The command syntax is pretty straightforward. For the purposes of this guide I will use a very simple ruleset. You can learn more advanced rules by reading the information linked to in the “Further Reading” section of this guide.

Below is a sample ipnat.conf file. Keep in mind that xl0 is my EXTERNAL (Internet Facing) interface, and that 192.168.1.0/16 is my internal subnet. You must change the xl0 and the 192.168.1.0/16 to match your hardware and network configuration. The first two lines handle FTP support, and must be in this precise order to allow FTP to work properly. The third line will translate everything else.

map xl0 192.168.1.0/16 -> 0.0.0.0/32 proxy port ftp ftp/tcp
map xl0 192.168.1.0/16 -> 0.0.0.0/32 portmap tcp/udp 1025:65000
map xl0 192.168.1.0/16 -> 0.0.0.0/32

You can read the commands like this “Map, on the xl0 (external) interface, the network 192.168.1.0/16 to the network 0.0.0.0/32 (the internet) … etc”. As you read the additional material referenced below, you will learn more advanced techniques such as redirect (rdr), however, that is outside of the scope of this guide.

You may use the ipnat command to control the ipnat software, view active mappings and statistics after you have completed this guide and rebooted (however, do not reboot yet).

Section C – Building your firewall ruleset (ipf.rules)

Now, you must build the rules for your firewall. This will be responsible for blocking or passing data through your network. IPFILTER also handles the state table. If it accepts a packet, and the rule that accepts it tells the firewall to keep state, an entry will be placed into your systems state table. Keeping state is essential to keeping connections from your client computers to the outside world alive. Any connection kept open in the state table will bypass the entire firewall ruleset. Don’t worry, it will have to have been accepted to be placed in the state table in the first place.

So, open up /etc/ipf.rules in you editor of choice, and we shall jump in.

IPFILTER rules are pretty easy to understand. They are pretty much in plain english, and make sense if you have half a clue about what your doing.

The first rules I will show you will handle keeping connection state.

pass out quick on xl0 proto tcp all keep state
pass out quick on xl0 proto udp all keep state
pass out quick on xl0 proto icmp all keep state

What these rules do are pass out packets on the xl0 (which is my external interface, change it to match yours) that use the specified protocols (tcp/udp/icmp) and keep state on those connections. If you are browsing a website on a computer behind your firewall, when the computer sends that packet out to the webserver being accessed, the state table will allow reply packets back in to computer you are browsing on.

The quick keyword means that if the packet matches the rule, it stops comparing the packet against the rest of the rules. Without the quick keyword, IPFILTER will continue to compare the packet against the remaining rules, with the last one matching being the one that counts. If the packet didn’t match the first rule, it would be compared against the next one in sequence. If the packet happens to be a udp packet passing out on the xl0 interface, it would match the second rule, and that rule would be applied without comparing against the next rule(s).

Unless you happen to know better, these three rules are rather essential as they are, and should (must) be at the beginning of your ipf.rules.

Next, you will want to block packets coming in from the Internet from address ranges they shouldn’t be coming from. Address ranges they shouldn’t be coming from are those that are non-routable (set aside for networks not on the net). People could use spoofed packets from these ranges to DoS your machine. Some, but not all of these ranges are: 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 127.0.0.0/8, and 192.0.2.0/24.

An example rule blocking one of these addresses is:

block in quick on xl0 from 192.168.0.0/16 to any

This rule blocks (quick) any packets coming in on xl0 from the 192.168.0.0/16 range to any address. Get in the habit of using any in your rules instead of specific IP addresses, unless you only want to block or pass something to/from a specific address. You will most likely want to add rules like this for each of the address ranges above. If a packet comes in from your external (Internet facing) interface, and is from one of those address ranges, it will be dropped and IPFILTER will move on to the next packet.

Now, if your the system you are setting up as a gateway/firewal is hosting services (http, ftp, etc) you will want to allow others on the Internet to access them. First you will need to know what port(s) and protocol the service uses and add a rule to pass those packets in.

Let’s say you are running apache to serve webpages. It uses port 80 and the tcp protocol. Therefore, the rule to let others access it will look like this:

pass in quick on xl0 proto tcp from any to any port = 80 #HTTP

Nothing very different from what we have seen before, with the exception of the port option and the # style comment. You are allowed to put comments in your ruleset provided you use the # before the comment. You will want to add rules like this for each service you operate.

You will now want to block anything that hasn’t matched any rules so far. Otherwise, IPFILTER will just pass these packets.

block in quick on xl0 all

Now that you have some idea how to use IPFILTER rules, heres an example /etc/ipf.rules file:

# Let clients behind the firewall send out to the internet, and replies to come back in by keeping state
pass out quick on xl0 proto tcp all keep state
pass out quick on xl0 proto udp all keep state
pass out quick on xl0 proto icmp all keep state

# Since nothing should be coming from these address ranges, block them
block in quick on xl0 from 192.168.0.0/16 to any
block in quick on xl0 from 172.16.0.0/12 to any
block in quick on xl0 from 10.0.0.0/8 to any
block in quick on xl0 from 127.0.0.0/8 to any
block in quick on xl0 from 192.0.2.0/24 to any

# Let's let people access the services running on this system
pass in quick on xl0 proto tcp from any to any port 30000 >< 50000 flags S keep state #PASV FTP
pass in quick on xl0 proto tcp from any to any port = 21 #FTP
pass in quick on xl0 proto tcp from any to any port = 22 #SSH
pass in quick on xl0 proto tcp/udp from any to any port = 53 #DNS
pass in quick on xl0 proto tcp from any to any port = 80 #WWW

# Block everything else
block in quick on xl0 all

There were two things we have not discussed, yet are in this ruleset. The first is the pasv ftp one. It uses 30000 >< 50000 after the port keyword. Basically, this passes in anything on ports 30000-50000. This is essential to have proper passive FTP (the default one) access for all clients behind your network. Explaining why is outside the scope of this guide, but I will just say it's because the FTP protocol sucks. Next, is the tcp/udp after the proto keyword on the DNS entry. DNS operates on both the udp and tcp protocols. You will need to have both of these open if you are hosting a DNS server. Using tcp/udp for the protocol will apply the rule to packets using either the tcp OR the udp protocol. Otherwise, you would have to have two rules to do the same thing, one for udp, and one for tcp.

Now that you have everything configured, reboot your system and things should work fairly well if you are good at following instructions.

A couple IPFILTER specific command line utilities are now in order. First, ipf is the control program for IPFILTER. It can do a lot, but the most important (in my opinion) is reloading the /etc/ipf.rules when you make a change. To do this, simply execute

# ipf -Fa -f /etc/ipf.rules

This will clear out the current ruleset in memory, and load the new one from the specified file. View the ipf manpages to see what else it can do. The next utility is ipfstat. This utility will show the number of dropped/passed packets, etc. It's very useful for keeping track of how well your firewall is working.

Once again, there are a lot more sophisticated things you could do, but they are outside the scope of this guide. I strongly suggest reading through the additional material referenced below.

Further Reading

Speak Your Mind

*