A Network Tutorial/How-To Guide For The Freebsd Os: Nick Rogness
A Network Tutorial/How-To Guide For The Freebsd Os: Nick Rogness
FreeBSD Firewall
Nick Rogness [email protected]
Introduction
This section deals with building a firewall. What is a firewall? A firewall (in this context) is a set of
rules that allows or denies certain types of network traffic (packets) from entering or leaving your
FreeBSD machine.
Firewall Concepts
There are several different types of firewalls you can run. Hardware firewalls, sometimes called
Firewall appliances, implement firewalling using hardware. Most of these 'hardware' firewalls are
proprietary and usually run an embedded OS like FreeBSD or Linux. Hardware firewalls usually cost
money and provide a nice graphical configuration tool to deploy your firewall. Some example
hardware firewalls you may have heard of are 'Cisco Systems PIX', 'SonicWall', Linkysys, and many
others.
Software firewalls, on the other hand, are usually a set of tools that run on a specific OS. They
interface with the OS's kernel packet processing system. Many different types of software firewalls
exist, some commercial and some free. Some free software firewall packages include IPFilter,
IPChains, IPTables, and IPFW. Several commercial software firewalls exist the most popular being
CheckPoint's Firewall-1. We will be focusing on FreeBSD's ipfw firewall package. One thing about
free software based firewalls is that they are usually difficult to setup and manage. FreeBSD provides
a nice interface to create, delete and manage your firewall. However, it takes a certain amount of
knowledge about IP packets and routing to understand completely. I recommend you first
understand these basics before you start reading. I will be expecting you understand some of these
basics.
As I mentioned earlier, we will be exploring the FreeBSD ipfw software firewall. This 'tool' is
included in the base system of FreeBSD so you will not need to download any 3rd party software.
You should be familiar with interfaces on FreeBSD. If you are not please read the Interfaces
Section. Interfaces are key to firewalling on any OS as they are the conduits for packets coming and
going on your machine. Also, you may have to build a kernel on your FreeBSD machine to turn on
Quick note
If you want to be real efficient in management of your firewall(s), you should understand one very
important thing that most people miss: Packets flow symmetrically to and from your machine. A
common mistake made by begginers is over looking this fact. You must always look at packets like
with this in mind, i.e. you need to firewall both ways: in AND out. Keep this in mind when you are
reading this article.
Basic Firewall
We will start with a simple setup. One machine connected directly to the internet. We will progress
to a more advanced setup in the next section: Building a Gateway/Router. But for now, let's focus
on the basics: one machine and one interface.
First, lets conceptually look at how packets flow to/from your machine to/from the internet (or
network):
Image
Notice how packets flow both ways: in and out of your machine. This will be crucial in upcoming
paragraphs.
Next, lets enable firewalling on boot up. In order to do this we edit /etc/rc.conf and add:
firewall_enable="YES"
firewall_type="OPEN"
This does 2 things. First off the 'firewall_enable="YES" tells BSD to enable the firewall on bootup.
The firewall_type="OPEN" tells BSD to use a stock OPEN firewall configuration. There are several
different stock preconfiged setups you can choose from to go between the "" marks in te
firewall_type line within /etc/rc.conf:
These /etc/rc.conf options get processed by /etc/rc.firewall upon startup. For now lets focus on 2
different stock configurations we can play with: OPEN and filename. An OPEN firewall just allows
all traffic to and from your machine. By default, if you forget to add this firewall_type="OPEN" to /
etc/rc.conf all traffic will STOP! That is by IPFIREWALL has a default rule to deny traffic from any
to any. Don't do this remotely or you'll have to get to the console and reset it to get back in!! You
can change this behavior by adding an 'options DEFAULT_TO_ACCEPT' to the kernel config file
and rebuilding your kernel. The OPEN statement tells /etc/rc.firewall to add a pass rule to your
Firewall Rules
Firewall rules are just a list of sequentially searched 'rules' that EVERY packet gets compared to
when it leaves/enters an interface. Notice I didn't say machine, I said interface. What I mean by that
is every packet enters an interface and leaves an interface. Each time it passes across an interface,
the kernel compares these packets to these rules to determine whether it should be allowed or
blocked. The matching is done by comparing the packet header information to the rule criteria to
determine if it matches. If a match is encountered then the system looks at the 'action' for the rule to
determine if it should be passed or blocked. IP header information that the firewall can match against
are: source IP, destination IP, source PORT, destination PORT, protocol type, and protocol specific
flags. It is important to remember that the firewall can not inspect any further into a packet than the
header. Therefore, it can not determine payload contents, i.e. it's not a content firewall/proxy.
Let's now look at the syntax of these rules. For example let's say we have a machine with interface
setup:
# ifconfig -a
xl0: flags=8843 mtu 1500
options=3
inet 205.238.129.221 netmask 0xfffffffc broadcast 205.238.129.223
inet6 fe80::250:daff:fe77:cc77%xl0 prefixlen 64 scopeid 0x1
ether 00:50:da:77:cc:77
media: Ethernet autoselect (100baseTX )
status: active
# ipfw l
00100 allow ip from any to any via lo0
00200 deny ip from any to 127.0.0.0/8
00300 deny ip from 127.0.0.0/8 to any
65000 allow ip from any to any
65535 deny ip from any to any
Fairly intimidating. So lets step back for a moment at just adding one of these rules first. Then we'll
worry about the ruleset as a whole. Let's say we want to add a rule to allow all traffic to and from
this machine. We might add something like this:
WOW! What the hell is this? Let's examine it piece by piece. First the 'ipfw' command is the
command line interface tool to add and remove firewall rules from the system. Everything else is an
argument to ipfw. I used the 'l' argument to ipfw above to list the current ruleset. This is a failry
common way to inspect running rulesets.
The number 50 is used as kind of a line number. I said earlier that these rules are sequentially
searched for matching packets. This number 50 just means it is rule number 50. Rule numbers have a
range from 1-65534. Rule number 65535 is always the default deny rule (Unless changed as
mentioned above). Rule numbers don't have to be concurrent added, i.e. you can add rule 100 then
add rule 200. The important thing to rememeber about rule numbers is that they are processed in
ascending order (1 to 65535).
The next argument is 'allow'. This is the action to be taken if a packet matches on this rule. Different
types of actions can be taken if a packet matches this rule. We will only be covering 'allow' and
'deny' in this section. The 'allow' action means that the kernel will let the packet go. The 'deny' action
means the kernel will throw the packet away.
When I said "if a packet matches" I meant exactly that. See, the kernel takes the packet and
compares it to the rest of the line: 'ip from any to any via xl0'. This is the match criteria. If the kernel
decides it matches the criteria then it performs 'action' (allow or deny) for the rule. If the kernel
doesn't match on this rule, it proceeds to compare the packet to the next rule in the firewall ruleset.
Eventually, if the firewall doesn't find a match for any rule number, it will get to the default 65536
rule, which will deny the packet (throw it away). If a match does occur, the action is taken and no
more rules are compared to the current packet, i.e. rule processing stops at that rule number.
The match criteria means exactly what it sounds like. You can think of the match criteria kind of like
an if-else programming statement. Therefore, you can analyze 'ip from any to any via xl0' like so:
// Analyzing: 'ip'
// Meaning: Is protocol type in packet header equal to ip?
if (protocol == ip) {
// Yes, protocol is IP
} else {
} else {
// No, destination IP doesn't equal 'any'
// So skip this rule and move to next rule number
}
} else {
} else {
Yes, matching criteria can get quite complicated with many different combinations to match on.
Although confusing, this complexity gives us extreme amounts of power and flexibility in building
rules.
Now that you have added a rule lets look at it in the firewall within the kernel using the ipfw
command line utility with the 'l' argument (meaning list):
# ipfw l
00050 allow ip from any to any via xl0
I mentioned earlier that you should be aware that packets traverse your interfaces in both directions.
So our single add rule: 'ipfw add 50 allow ip from any to any via xl0' could be added by 2 different
ipfw add statements. Let's take a look:
Notice how I changed the 'via' to 'in via' and 'out via'. The 'in via xl0' means 'Is this packet coming in
through the xl0 interface'. The 'out via xl0' means 'Is this packet leaving out the xl0 interface?'. Also
take note that the rule numbers are 50 and 60. That means EVERY packet coming or leaving first
gets compared to rule number 50, then rule number 60.
SO how do you delete a rule once it's added. That's simple. You delete it by rule number like so:
# ipfw delete 50
Rules Processing
Now that you are an expert at rule syntax (hehe) let's look at the firewall ruleset as a whole as we did
earlier. Using a sample ruleset:
# ipfw l
The idea here is to reinforce the rule processing of how packets are inspected and leave/enter the
machine. This is a key concept of understanding firewalls in general. Lets take you through a quick
example with this ruleset above. Suppose my machine (205.238.129.221) receives a telnet packet
from the internet, i.e. Someone (1.2.3.4) is trying to telnet to my machine from the internet. So a
sample packet would have attributes that look similar to:
This is what I mentioned earlier about packets being symmetrical. Most people forget that the
response packet gets sent back to the sender. Its attributes look similar to this:
So now we repeat the process for the outgoing packet: 1) Kernel compares packet to this rule (100):
Notice in the above steps that if we wouldn't have matched on rule number 65000, then we would
have hit the default rule of 65536 which would 'deny' or drop the packet!
There are several different ways to write rules as mentioned above. We could have matched on that
telnet example above by using several different rules. The first 7 steps which was when the kernel
was processing a packet that was inbound would have matched by any of these rules (which i'll show
you how to add):
There are quite a few combinations I didn't get to either. Not to menton several things which I'm not
covering in this section, which would add even more combinations. So, that poses a good question:
Which rule should I use? The answer is not an easy one. Different people like different techniques. I
can advise one general rule of thumb. The more specific a rule is the more secure you will be (or at
least feel). That is, the rule:
# ipfw add 65 allow tcp from 205.238.129.221 23 to 1.2.3.4 out via xl0
Is better (more tightly secured) than a more general rule such as:
This isn't always the case but when you get to refining your ruleset being more specific about rules
can help you better monitor your machine/network. In a later section, Fun with Firewalling, I will be
covering fine tuning your rulesets and such refinements will be covered.
Firewall Options
So create a file, e.g. /etc/firewall.local, and add your rules to it line by line:
Save the file. You can now add it to the /etc/rc.conf file and when you reboot it will automagically
load your firewall:
firewall_type="/etc/firewall.local"
You can also invoke it from the command like manually if you are too impatient for a reboot by:
# ipfw /etc/firewall.local
The above firewall ruleset also showed a few more details I left out earlier. One being the use of
hostnames (bob.rogness.net) as source or destination address (rule 100). When added like this ipfw
will look up the name 'bob.rogness.net' and replace 'bob.rogness.net' by the IP it resolves to. Beware
that this is not dynamic, if bob.rogness.net changes IPs, ipfw will not automagically change it for
you!
Another trick I used was the keywork 'me' (rule 5000). This referes to any IP referenced on your
machine. This is a great trick for use in DHCP or when your machines interface IPs change
frequently. This 'me' keywork is dynamic, i.e. your IP can change and the rule will still match.
The last trick I used was the keyword 'log' (rule 20000). This tells the firewall to log the packet (in
abbreviated form) to syslog. You must enable this in the kernel before usage with 'options
IPFIREWALL_VERBOSE' and rebuild your kernel. The reason I showed you this is because it is
Well, that's it! Firewalling can be very complex. The only way to get good at such an unbearable act
is practice. Checkout the next firewalling section Fun with Firewalling to go over some more
advanced things you can do with ipfw. Any questions, just email me. Oh and, you the ipfw man
page. It is a very indepth and helpful tool when you hit a roadblock.