doc/en_US.ISO8859-1/books/handbook/firewalls/chapter.xml
Warren Block 29f9029798 Correct the example that allows internal but blocks external ICMP.
Reviewed by:	Peter N. M. Hansteen <peter@bsdly.net>
2013-06-29 13:19:43 +00:00

4578 lines
179 KiB
XML

<?xml version="1.0" encoding="iso-8859-1"?>
<!--
The FreeBSD Documentation Project
$FreeBSD$
-->
<chapter id="firewalls">
<chapterinfo>
<authorgroup>
<author>
<firstname>Joseph J.</firstname>
<surname>Barbish</surname>
<contrib>Contributed by </contrib>
</author>
</authorgroup>
<authorgroup>
<author>
<firstname>Brad</firstname>
<surname>Davis</surname>
<contrib>Converted to SGML and updated by </contrib>
</author>
</authorgroup>
</chapterinfo>
<title>Firewalls</title>
<indexterm><primary>firewall</primary></indexterm>
<indexterm>
<primary>security</primary>
<secondary>firewalls</secondary>
</indexterm>
<sect1 id="firewalls-intro">
<title>Introduction</title>
<para>Firewalls make it possible to filter the incoming and
outgoing traffic that flows through a system. A firewall can
use one or more sets of <quote>rules</quote> to inspect network
packets as they come in or go out of network connections and
either allows the traffic through or blocks it. The rules of
a firewall can inspect one or more characteristics of the
packets such as the protocol type, source or destination host
address, and source or destination port.</para>
<para>Firewalls can enhance the security of a host or a network.
They can be used to do one or more of the following:</para>
<itemizedlist>
<listitem>
<para>Protect and insulate the applications, services, and
machines of an internal network from unwanted traffic from
the public Internet.</para>
</listitem>
<listitem>
<para>Limit or disable access from hosts of the internal
network to services of the public Internet.</para>
</listitem>
<listitem>
<para>Support network address translation
(<acronym>NAT</acronym>), which allows an internal network
to use private <acronym>IP</acronym> addresses and share a
single connection to the public Internet using either a
single <acronym>IP</acronym> address or a shared pool of
automatically assigned public addresses.</para>
</listitem>
</itemizedlist>
<para>After reading this chapter, you will know:</para>
<itemizedlist>
<listitem>
<para>How to define packet filtering rules.</para>
</listitem>
<listitem>
<para>The differences between the firewalls built into
&os;.</para>
</listitem>
<listitem>
<para>How to use and configure the
<application>PF</application> firewall.</para>
</listitem>
<listitem>
<para>How to use and configure the
<application>IPFILTER</application> firewall.</para>
</listitem>
<listitem>
<para>How to use and configure the
<application>IPFW</application> firewall.</para>
</listitem>
</itemizedlist>
<para>Before reading this chapter, you should:</para>
<itemizedlist>
<listitem>
<para>Understand basic &os; and Internet concepts.</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="firewalls-concepts">
<title>Firewall Concepts</title>
<indexterm>
<primary>firewall</primary>
<secondary>rulesets</secondary>
</indexterm>
<para>A firewall ruleset can be either
<quote>exclusive</quote> or <quote>inclusive</quote>. An
exclusive firewall allows all traffic through except for the
traffic matching the ruleset. An inclusive firewall does the
reverse as it only allows traffic matching the rules through and
blocks everything else.</para>
<para>An inclusive firewall offers better control of the outgoing
traffic, making it a better choice for systems that offer
services to the public Internet. It also controls the type of
traffic originating from the public Internet that can gain
access to a private network. All traffic that does not match
the rules is blocked and logged. Inclusive firewalls are
generally safer than exclusive firewalls because they
significantly reduce the risk of allowing unwanted
traffic.</para>
<note>
<para>Unless noted otherwise, all configuration and example
rulesets in this chapter create inclusive firewall
rulesets.</para>
</note>
<para>Security can be tightened further using a <quote>stateful
firewall</quote>. This type of firewall keeps track of open
connections and only allows traffic which either matches an
existing connection or opens a new, allowed connection. The
disadvantage of a stateful firewall is that it can be vulnerable
to Denial of Service (<acronym>DoS</acronym>) attacks if a lot
of new connections are opened very fast. Most firewalls use a
combination of stateful and non-stateful behavior.</para>
</sect1>
<sect1 id="firewalls-apps">
<title>Firewall Packages</title>
<para>&os; has three firewalls built into the base system:
<emphasis>IPFILTER</emphasis>, also known as
<acronym>IPF</acronym>, <emphasis>IPFIREWALL</emphasis>, also
known as <acronym>IPFW</acronym>, and <acronym>PF</acronym>).
&os; also provides two traffic shapers for controlling bandwidth
usage: &man.altq.4; and &man.dummynet.4;. Dummynet has
traditionally been closely tied with <acronym>IPFW</acronym>,
and <acronym>ALTQ</acronym> with <acronym>PF</acronym>. Each
firewall uses rules to control the access of packets to and from
a &os; system, although they go about it in different ways and
each has a different rule syntax.</para>
<para>&os; provides multiple firewalls in order to meet the
different requirements and preferences for a wide variety of
users. Each user should evaluate which firewall best meets
their needs.</para>
<para>Since all firewalls are based on inspecting the values of
selected packet control fields, the creator of the firewall
ruleset must have an understanding of how
<acronym>TCP/IP</acronym> works, what the different values in
the packet control fields are, and how these values are used in
a normal session conversation. For a good introduction, refer
to <ulink
url="http://www.ipprimer.com/overview.cfm">Daryl's TCP/IP
Primer</ulink>.</para>
</sect1>
<sect1 id="firewalls-pf">
<sect1info>
<authorgroup>
<author>
<firstname>John</firstname>
<surname>Ferrell</surname>
<contrib>Revised and updated by </contrib>
<!-- 24 March 2008 -->
</author>
</authorgroup>
</sect1info>
<title>PF and <acronym>ALTQ</acronym></title>
<indexterm>
<primary>firewall</primary>
<secondary>PF</secondary>
</indexterm>
<para>Since &os;&nbsp;5.3, a ported version of OpenBSD's
<acronym>PF</acronym> firewall has been included as an
integrated part of the base system. <acronym>PF</acronym> is a
complete, full-featured firewall that has optional support for
<acronym>ALTQ</acronym> (Alternate Queuing), which provides
Quality of Service (<acronym>QoS</acronym>).</para>
<para>Since the OpenBSD Project maintains the definitive
reference for <acronym>PF</acronym> in the<ulink
url="http://www.openbsd.org/faq/pf/">PF FAQ</ulink>, this
section of the Handbook focuses on <acronym>PF</acronym> as it
pertains to &os;, while providing some general usage
information.</para>
<para>More information about porting <acronym>PF</acronym> to &os;
can be found at <ulink
url="http://pf4freebsd.love2party.net/"></ulink>.</para>
<sect2>
<title>Using the PF Loadable Kernel Modules</title>
<para>In order to use PF, the PF kernel module must be first
loaded. Add the following line to
<filename>/etc/rc.conf</filename>:</para>
<programlisting>pf_enable="YES"</programlisting>
<para>Then, run the startup script to load the module:</para>
<screen>&prompt.root; <userinput>service pf start</userinput></screen>
<para>The PF module will not load if it cannot find the
ruleset configuration file. The default location is
<filename>/etc/pf.conf</filename>. If the PF ruleset is
located somewhere else, add a line to
<filename>/etc/rc.conf</filename> which specifies the full
path to the file:</para>
<programlisting>pf_rules="<replaceable>/path/to/pf.conf</replaceable>"</programlisting>
<para>The sample <filename>pf.conf</filename>
can be found in <filename
class="directory">/usr/share/examples/pf/</filename>.</para>
<para>The <acronym>PF</acronym> module can also be loaded
manually from the command line:</para>
<screen>&prompt.root; <userinput>kldload pf.ko</userinput></screen>
<para>Logging support for PF is provided by
<varname>pflog.ko</varname> which can be loaded by adding the
following line to <filename>/etc/rc.conf</filename>:</para>
<programlisting>pflog_enable="YES"</programlisting>
<para>Then, run the startup script to load the module:</para>
<screen>&prompt.root; <userinput>service pflog start</userinput></screen>
</sect2>
<sect2>
<title>PF Kernel Options</title>
<indexterm>
<primary>kernel options</primary>
<secondary>device pf</secondary>
</indexterm>
<indexterm>
<primary>kernel options</primary>
<secondary>device pflog</secondary>
</indexterm>
<indexterm>
<primary>kernel options</primary>
<secondary>device pfsync</secondary>
</indexterm>
<para>While it is not necessary to compile
<acronym>PF</acronym> support into the &os; kernel, some of
PF's advanced features are not included in the loadable
module, namely &man.pfsync.4;, which is a pseudo-device that
exposes certain changes to the state table used by
<acronym>PF</acronym>. It can be paired with &man.carp.4; to
create failover firewalls using <acronym>PF</acronym>. More
information on <acronym>CARP</acronym> can be found in <link
linkend="carp">of the Handbook</link>.</para>
<para>The following <acronym>PF</acronym> kernel options can be
found in <filename>/usr/src/sys/conf/NOTES</filename>:</para>
<programlisting>device pf
device pflog
device pfsync</programlisting>
<para><literal>device pf</literal> enables PF support.</para>
<para><literal>device pflog</literal> enables the optional
&man.pflog.4; pseudo network device which can be used to log
traffic to a &man.bpf.4; descriptor. The &man.pflogd.8;
daemon can then be used to store the logging information to
disk.</para>
<para><literal>device pfsync</literal> enables the optional
&man.pfsync.4; pseudo-network device that is used to monitor
<quote>state changes</quote>.</para>
</sect2>
<sect2>
<title>Available <filename>rc.conf</filename> Options</title>
<para>The following &man.rc.conf.5; statements can be used to
configure <acronym>PF</acronym> and &man.pflog.4; at
boot:</para>
<programlisting>pf_enable="YES" # Enable PF (load module if required)
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</programlisting>
<para>If there is a LAN behind the firewall and packets need to
be forwarded for the computers on the LAN, or NAT is required,
add the following option:</para>
<programlisting>gateway_enable="YES" # Enable as LAN gateway</programlisting>
</sect2>
<sect2>
<title>Creating Filtering Rules</title>
<para>By default, <acronym>PF</acronym> reads its configuration
rules from <filename>/etc/pf.conf</filename> and modifies,
drops, or passes packets according to the rules or definitions
specified in this file. The &os; installation includes
several sample files located in
<filename>/usr/share/examples/pf/</filename>. Refer to the
<ulink url="http://www.openbsd.org/faq/pf/">PF FAQ</ulink> for
complete coverage of <acronym>PF</acronym> rulesets.</para>
<warning>
<para>When reading the <ulink
url="http://www.openbsd.org/faq/pf/">PF FAQ</ulink>,
keep in mind that different versions of &os; contain
different versions of PF. Currently,
&os;&nbsp;8.<replaceable>X</replaceable> is using the
same version of <acronym>PF</acronym> as
OpenBSD&nbsp;4.1. &os;&nbsp;9.<replaceable>X</replaceable>
and later is using the same version of <acronym>PF</acronym>
as OpenBSD&nbsp;4.5.</para>
</warning>
<para>The &a.pf; is a good place to ask questions about
configuring and running the <acronym>PF</acronym> firewall.
Do not forget to check the mailing list archives before asking
questions.</para>
<para>To control <acronym>PF</acronym>, use &man.pfctl.8;.
Below are some useful options to this command. Review
&man.pfctl.8; for a description of all available
options:</para>
<informaltable frame="none" pgwide="1">
<tgroup cols="2">
<thead>
<row>
<entry>Command</entry>
<entry>Purpose</entry>
</row>
</thead>
<tbody>
<row>
<entry><command>pfctl
<option>-e</option></command></entry>
<entry>Enable PF.</entry>
</row>
<row>
<entry><command>pfctl
<option>-d</option></command></entry>
<entry>Disable PF.</entry>
</row>
<row>
<entry><command>pfctl <option>-F</option> all
<option>-f</option> /etc/pf.conf</command></entry>
<entry>Flush all NAT, filter, state, and table
rules and reload
<filename>/etc/pf.conf</filename>.</entry>
</row>
<row>
<entry><command>pfctl <option>-s</option> [ rules | nat
state ]</command></entry>
<entry>Report on the filter rules, NAT rules, or state
table.</entry>
</row>
<row>
<entry><command>pfctl <option>-vnf</option>
/etc/pf.conf</command></entry>
<entry>Check <filename>/etc/pf.conf</filename> for
errors, but do not load ruleset.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</sect2>
<sect2>
<title>Enabling <acronym>ALTQ</acronym></title>
<para><acronym>ALTQ</acronym> is only available by compiling its
support into the &os; kernel. <acronym>ALTQ</acronym> is not
supported by all network card drivers. Refer to &man.altq.4;
for a list of drivers that are supported by the release of
&os;.</para>
<para>The following kernel options will enable
<acronym>ALTQ</acronym> and add additional
functionality:</para>
<programlisting>options ALTQ
options ALTQ_CBQ # Class Based Queuing (CBQ)
options ALTQ_RED # Random Early Detection (RED)
options ALTQ_RIO # RED In/Out
options ALTQ_HFSC # Hierarchical Packet Scheduler (HFSC)
options ALTQ_PRIQ # Priority Queuing (PRIQ)
options ALTQ_NOPCC # Required for SMP build</programlisting>
<para><literal>options ALTQ</literal> enables the
<acronym>ALTQ</acronym> framework.</para>
<para><literal>options ALTQ_CBQ</literal> enables
<emphasis>Class Based Queuing</emphasis>
(<acronym>CBQ</acronym>). <acronym>CBQ</acronym>
can be used to divide a connection's bandwidth into different
classes or queues to prioritize traffic based on filter
rules.</para>
<para><literal>options ALTQ_RED</literal> enables
<emphasis>Random Early Detection</emphasis>
(<acronym>RED</acronym>). <acronym>RED</acronym> is
used to avoid network congestion by measuring the length of
the queue and comparing it to the minimum and maximum
thresholds for the queue. If the queue is over the maximum,
all new packets will be dropped. <acronym>RED</acronym> drops
packets from different connections randomly.</para>
<para><literal>options ALTQ_RIO</literal> enables
<emphasis>Random Early Detection In and Out</emphasis>.</para>
<para><literal>options ALTQ_HFSC</literal> enables the
<emphasis>Hierarchical Fair Service Curve Packet
Scheduler</emphasis> <acronym>HFSC</acronym>. For more
information, refer to <ulink
url="http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html"></ulink>.</para>
<para><literal>options ALTQ_PRIQ</literal> enables
<emphasis>Priority Queuing</emphasis>
(<acronym>PRIQ</acronym>). <acronym>PRIQ</acronym> will
always pass traffic that is in a higher queue first.</para>
<para><literal>options ALTQ_NOPCC</literal> enables
<acronym>SMP</acronym> support for <acronym>ALTQ</acronym>.
This option is required on <acronym>SMP</acronym>
systems.</para>
</sect2>
<sect2 id="pf-tutorial">
<sect2info>
<authorgroup>
<author>
<firstname>Peter</firstname>
<surname>Hansteen</surname>
<othername>N. M.</othername>
<contrib>Contributed by </contrib>
</author>
</authorgroup>
</sect2info>
<title><acronym>PF</acronym> Rule Sets and Tools</title>
<para>This section demonstrates some useful
<acronym>PF</acronym> features and <acronym>PF</acronym>
related tools in a series of examples. A more thorough
tutorial is available at <ulink
url="http://home.nuug.no/~peter/pf/">http://home.nuug.no/~peter/pf/</ulink>.</para>
<tip>
<para><filename role="package">security/sudo</filename> is
useful for running commands like <command>pfctl</command>
that require elevated privileges. It can be installed from
the Ports Collection.</para>
</tip>
<sect3 id="pftut-simplest">
<title>The Simplest Rule Set Ever</title>
<para>The simplest possible setup is for a single machine
which will not run any services, and which will talk to one
network which may be the Internet. A minimal
<filename>/etc/pf.conf</filename> looks like this:</para>
<programlisting>block in all
pass out all keep state</programlisting>
<para>Here we deny any incoming traffic, allow traffic we make
ourselves to pass, and retain state information on our
connections. Keeping state information allows return
traffic for all connections we have initiated to pass back
to us. This rule set is used on machines that can be
trusted. The rule set can be loaded with</para>
<screen>&prompt.root; <userinput>pfctl -e ; pfctl -f /etc/pf.conf</userinput></screen>
</sect3>
<sect3>
<title>Tighter and More Elegant</title>
<para>For a slightly more structured and complete setup, we
start by denying everything and then allowing only those
things we know that we need
<footnote><para>Why write the rule set to default deny? The
short answer is, it gives better control at the expense
of some thinking. The point of packet filtering is to
take control, not to run catch-up with what the bad guys
do. Marcus Ranum has written a very entertaining and
informative article about this, <ulink
url="http://www.ranum.com/security/computer_security/editorials/dumb/index.html">The
Six Dumbest Ideas in Computer Security</ulink>, and
it is well written too.</para></footnote>. This gives
us the opportunity to introduce two of the features which
make <acronym>PF</acronym> such a wonderful tool:
<firstterm>lists</firstterm> and
<firstterm>macros</firstterm>.</para>
<para>We will make some changes to
<filename>/etc/pf.conf</filename>, starting with</para>
<programlisting>block all</programlisting>
<para>Then we back up a little. Macros need to be defined
before use, so at the very top of the file, we add:</para>
<programlisting>tcp_services = "{ ssh, smtp, domain, www, pop3, auth, pop3s }"
udp_services = "{ domain }"</programlisting>
<para>Now we have demonstrated several things at once - what
macros look like, that macros may be lists, and that
<acronym>PF</acronym> understands rules using port names
equally well as it does port numbers. The names are the
ones listed in <filename>/etc/services</filename>. This
gives us something to put in our rules, which we edit
slightly to look like this:</para>
<programlisting>block all
pass out proto tcp to any port $tcp_services keep state
pass proto udp to any port $udp_services keep state</programlisting>
<para>At this point some of us will point out that UDP is
stateless, but <acronym>PF</acronym> actually manages to
maintain state information despite this. Keeping state for
a UDP connection means that for example when you ask a name
server about a domain name, you will be able to receive its
answer.</para>
<para>Since we have made changes to our
<filename>pf.conf</filename>, we load the new
rules:</para>
<screen>&prompt.root; <userinput>pfctl -f /etc/pf.conf</userinput></screen>
<para>and the new rules are applied. If there are no syntax
errors, <command>pfctl</command> will not output any
messages during the rule load. The <option>-v</option> flag
will produce more verbose <command>pfctl</command>
output.</para>
<para>If there have been extensive changes to the rule set,
the rules can be tested before attempting to load them. The
command to do this is</para>
<screen>&prompt.root; <userinput>pfctl -nf /etc/pf.conf</userinput></screen>
<para><option>-n</option> causes the rules to be interpreted
only, but does not load them. This provides an opportunity
to correct any errors. Under any circumstances, the last
valid rule set loaded will be in force until
<acronym>PF</acronym> is disabled or a new rule set is
loaded.</para>
<tip>
<title>Use <command>pfctl -v</command> to Show the Parsed
Rule Set</title>
<para>Adding the <option>-v</option> to a
<command>pfctl</command> ruleset load (even a dry run with
<option>-n</option>) will display the fully parsed rules
exactly the way they will be loaded. This is extremely
useful when debugging rules.</para>
</tip>
</sect3>
<sect3 id="pftut-gateway">
<title>A Simple Gateway with NAT</title>
<para>To most users, a single machine setup will be of limited
interest, and at this point we move on to more realistic or
at least more common setups, concentrating on a machine
which is running <acronym>PF</acronym> and also acts as a
gateway for at least one other machine.</para>
<sect4 id="pftut-gwpitfalls">
<title>Gateways and the Pitfalls of <literal>in</literal>,
<literal>out</literal> and <literal>on</literal></title>
<para>In the single machine setup, life is relatively
simple. Traffic created on it should either pass out to
the rest of the world or not, and the administrator
decides what to let in from elsewhere.</para>
<para>On a gateway, the perspective changes from
<quote>me versus the network out there</quote> to
<quote>I am the one who decides what to pass to or from
all the networks I am connected to</quote>. The machine
has at least two network interfaces, each connected to a
separate net.</para>
<para>It is very reasonable to think that for traffic to
pass from the network connected to
<devicename>xl1</devicename> to hosts on the network
connected to <devicename>xl0</devicename>, a rule like
this is needed:</para>
<programlisting>pass in on xl1 from xl1:network to xl0:network port $ports keep state</programlisting>
<para>This rule keeps track of states as well.</para>
<para>However, one of the most common and most
complained-about mistakes in firewall configuration is not
realizing that the <quote>to</quote> keyword does not in
itself guarantee passage all the way there. The rule we
just wrote only lets the traffic pass in to the gateway on
the internal interface. To let the packets get a bit
further, a matching rule is needed which says</para>
<programlisting>pass out on xl0 from xl1:network to xl0:network port $ports keep state</programlisting>
<para>These rules will work, but they will not necessarily
achieve the desired effect.</para>
<para>Rules this specific are rarely needed. For the basic
gateway configurations we will be dealing with here, a
better rule says</para>
<programlisting>pass from xl1:network to any port $ports keep state</programlisting>
<para>This provides local net access to the Internet and
leaves the detective work to the
<firstterm>antispoof</firstterm> and
<firstterm>scrub</firstterm> code. They are both pretty
good these days, and we will get back to them later. For
now we just accept the fact that for simple setups,
interface-bound rules with in/out rules tend to add more
clutter than they are worth to rule sets.</para>
<para>For a busy network admin, a readable rule set is a
safer rule set.</para>
<para>For the remainder of this section, with some
exceptions, we will keep the rules as simple as possible
for readability.</para>
</sect4>
<sect4 id="pftut-whatsthelocalnet">
<title>What is the Local Network, Anyway?</title>
<para>Above, we introduced the
<literal>interface:network</literal> notation. That is a
nice piece of shorthand, but the rule set can be made even
more readable and maintainable by taking the macro use a
tiny bit further.</para>
<para>For example, a <literal>$localnet</literal> macro
could be defined as the network directly attached to your
internal interface (<literal>$xl1:network</literal> in the
examples above).</para>
<para>Alternatively, the definition of
<literal>$localnet</literal> could be changed to an
<emphasis>IP address/netmask</emphasis> notation to denote
a network, such as <literal>192.168.100.1/24</literal> for
a subnet of private addresses.</para>
<para>If required, <literal>$localnet</literal> could even
be defined as a list of networks. Whatever the specific
needs, a sensible <literal>$localnet</literal> definition
and a typical pass rule of the type</para>
<programlisting>pass from $localnet to any port $ports keep state</programlisting>
<para>could end up saving you a few headaches. We will
stick to that convention from here on.</para>
</sect4>
<sect4 id="pftut-gwsimplesetup">
<title>Setting Up</title>
<para>We assume that the machine has acquired another
network card or at any rate there is a network
connection from the local network, via PPP or other
means. We will not consider the specific interface
configurations.</para>
<para>For the discussion and examples below, only the
interface names will differ between a PPP setup and an
Ethernet one, and we will do our best to get rid of the
actual interface names as quickly as possible.</para>
<para>First, we need to turn on gatewaying in order to let
the machine forward the network traffic it receives on one
interface to other networks via a separate interface.
Initially we will do this on the command line with
&man.sysctl.8;, for traditional
<emphasis>IP version four</emphasis>.</para>
<screen>&prompt.root; <userinput>sysctl net.inet.ip.forwarding=1</userinput></screen>
<para>If we need to forward <emphasis>IP version
six</emphasis> traffic, the command is</para>
<screen>&prompt.root; <userinput>sysctl net.inet6.ip6.forwarding=1</userinput></screen>
<para>In order for this to continue working after the
computer has been restarted at some time in the future,
enter these settings into
<filename>/etc/rc.conf</filename>:</para>
<programlisting>gateway_enable="YES" #for ipv4
ipv6_gateway_enable="YES" #for ipv6</programlisting>
<para>Use <command>ifconfig -a</command>, or
<command>ifconfig
<replaceable>interface_name</replaceable></command> to
find out if both of the interfaces to be used are up and
running.</para>
<para>If all traffic initiated by machines on the inside is
to be allowed, <filename>/etc/pf.conf</filename> could
look roughly like this
<footnote>
<para>For dialup users, the external interface is the
<filename>tun0</filename> pseudo-device. Broadband
users such as ADSL subscribers tend to have an
Ethernet interface to play with, however for a
significant subset of ADSL users, specifically those
using PPP over Ethernet (PPPoE), the correct external
interface will be the <filename>tun0</filename>
pseudo-device, not the physical Ethernet
interface.</para>
</footnote>:</para>
<programlisting>ext_if = "xl0" # macro for external interface - use tun0 for PPPoE
int_if = "xl1" # macro for internal interface
localnet = $int_if:network
# ext_if IP address could be dynamic, hence ($ext_if)
nat on $ext_if from $localnet to any -&gt; ($ext_if)
block all
pass from { lo0, $localnet } to any keep state</programlisting>
<para>Note the use of macros to assign logical names to the
network interfaces. Here 3Com cards are used, but this is
the last time during this tutorial we will find this of
any interest whatsoever. In truly simple setups like this
one, we may not gain very much by using macros like these,
but once the rule sets grow somewhat larger, you will
learn to appreciate the readability this provides.</para>
<para>Also note the <literal>nat</literal> rule. This is
where we handle the network address translation from the
non-routable address inside the local net to the sole
official address we assume has been assigned.</para>
<para>The parentheses surrounding the last part of the nat
rule <literal>($ext_if)</literal> are there to compensate
for the possibility that the IP address of the external
interface may be dynamically assigned. This detail will
ensure that network traffic runs without serious
interruptions even if the external IP address
changes.</para>
<para>On the other hand, this rule set probably allows more
traffic to pass out of the network than actually desired.
One reasonable setup could contain the macro</para>
<programlisting>client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http, \
https, cvspserver, 2628, 5999, 8000, 8080 }"</programlisting>
<para>and the main pass rule</para>
<programlisting>pass inet proto tcp from $localnet to any port $client_out \
flags S/SA keep state</programlisting>
<para>This may be a somewhat peculiar selection of ports,
but it is based on a real life example. Individual needs
probably differ at least in some specifics, but this
should cover at least some of the more useful
services.</para>
<para>In addition, we have a few other pass rules. We will
be returning to some of the more interesting ones rather
soon. One pass rule which is useful to those of us who
want the ability to administer our machines from elsewhere
is</para>
<programlisting>pass in inet proto tcp to port ssh</programlisting>
<para>or for that matter</para>
<programlisting>pass in inet proto tcp to $ext_if port ssh</programlisting>
<para>whichever is preferred. Lastly we need to make the
name service work for our clients:</para>
<programlisting>udp_services = "{ domain, ntp }"</programlisting>
<para>This is supplemented with a rule which passes the
traffic we want through our firewall:</para>
<programlisting>pass quick inet proto { tcp, udp } to any port $udp_services keep state</programlisting>
<para>Note the <literal>quick</literal> keyword in this
rule. We have started writing rule sets which consist of
several rules, and it is time to take a look at the
relationships between the rules in a rule set. The rules
are evaluated from top to bottom, in the sequence they are
written in the configuration file. For each packet or
connection evaluated by <acronym>PF</acronym>,
<emphasis>the last matching rule</emphasis> in the rule
set is the one which is applied. The
<literal>quick</literal> keyword offers an escape from the
ordinary sequence. When a packet matches a quick rule,
the packet is treated according to the present rule. The
rule processing stops without considering any further
rules which might have matched the packet. This is very
useful when a few isolated exceptions to the general rules
are needed.</para>
<para>This rule also takes care of <acronym>NTP</acronym>,
which is used for time synchronization. One thing common
to both protocols is that they may under certain
circumstances communicate alternately over TCP and
UDP.</para>
</sect4>
</sect3>
<sect3 id="pftut-ftp">
<title>That Sad Old <acronym>FTP</acronym> Thing</title>
<para>The short list of real life <acronym>TCP</acronym> ports
above contained, among other things, <acronym>FTP</acronym>.
<acronym>FTP</acronym> is a sad old thing and a problem
child, emphatically so for anyone trying to combine
<acronym>FTP</acronym> and firewalls.
<acronym>FTP</acronym> is an old and weird protocol, with a
lot to not like. The most common points against it
are</para>
<itemizedlist>
<listitem>
<para>Passwords are transferred in the clear</para>
</listitem>
<listitem>
<para>The protocol demands the use of at least two
<acronym>TCP</acronym> connections (control and data) on
separate ports</para>
</listitem>
<listitem>
<para>When a session is established, data is communicated
via ports selected at random</para>
</listitem>
</itemizedlist>
<para>All of these points make for challenges security-wise,
even before considering any potential weaknesses in client
or server software which may lead to security issues. These
things have tended to happen.</para>
<para>Under any circumstances, other more modern and more
secure options for file transfer exist, such as &man.sftp.1;
or &man.scp.1;, which feature both authentication and data
transfer via encrypted connections. Competent
<acronym>IT</acronym> professionals should have a preference
for some other form of file transfer than
<acronym>FTP</acronym>.</para>
<para>Regardless of our professionalism and preferences, we
are all too aware that at times we will need to handle
things we would prefer not to. In the case of
<acronym>FTP</acronym> through firewalls, the main part of
our handling consists of redirecting the traffic to a small
program which is written specifically for this
purpose.</para>
<sect4 id="pftut-ftp-proxy">
<title><acronym>FTP</acronym> Via Redirect:
<application>ftp-proxy</application></title>
<para>Enabling <acronym>FTP</acronym> transfers through your
gateway is amazingly simple, thanks to the
<acronym>FTP</acronym> proxy program (called
&man.ftp-proxy.8;) included in the base system on &os; and
other systems which offer <acronym>PF</acronym>. </para>
<para>The <acronym>FTP</acronym> protocol being what it is,
the proxy needs to dynamically insert rules in your rule
set. &man.ftp-proxy.8; interacts with your configuration
via a set of anchors where the proxy inserts and deletes
the rules it constructs to handle your
<acronym>FTP</acronym> traffic.</para>
<para>To enable &man.ftp-proxy.8;, add this line to
<filename>/etc/rc.conf</filename>:</para>
<programlisting>ftpproxy_flags=""</programlisting>
<para>Starting the proxy manually by running
<command>/usr/sbin/ftp-proxy</command> allows testing of
the <acronym>PF</acronym> configuration changes we are
about to make.</para>
<para>For a basic configuration, only three elements need to
be added to <filename>/etc/pf.conf</filename>. First, the
anchors:</para>
<programlisting>nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"</programlisting>
<para>The proxy will insert the rules it generates for the
<acronym>FTP</acronym> sessions here. A pass rule is
needed to let <acronym>FTP</acronym> traffic in to the
proxy.</para>
<para>Now for the actual redirection. Redirection rules and
<acronym>NAT</acronym> rules fall into the same rule
class. These rules may be referenced directly by other
rules, and filtering rules may depend on these rules.
Logically, <literal>rdr</literal> and
<literal>nat</literal> rules need to be defined before the
filtering rules.</para>
<para>We insert our <literal>rdr</literal> rule immediately
after the <literal>nat</literal> rule in
<filename>/etc/pf.conf</filename></para>
<programlisting>rdr pass on $int_if proto tcp from any to any port ftp -> 127.0.0.1 port 8021</programlisting>
<para>In addition, the redirected traffic must be allowed to
pass. We achieve this with</para>
<programlisting>pass out proto tcp from $proxy to any port ftp</programlisting>
<para>where <literal>$proxy</literal> expands to the address
the proxy daemon is bound to.</para>
<para>Save <filename>pf.conf</filename>, then load the new
rules with</para>
<screen>&prompt.root; <userinput>pfctl -f /etc/pf.conf</userinput></screen>
<para>At this point, users will probably begin noticing
that <acronym>FTP</acronym> works before they have been
told.</para>
<para>This example covers a basic setup where the clients in
the local net need to contact <acronym>FTP</acronym>
servers elsewhere. The basic configuration here should
work well with most combinations of <acronym>FTP</acronym>
clients and servers. As shown in the man page, the
proxy's behavior can be changed in various ways by adding
options to the <literal>ftpproxy_flags=</literal> line.
Some clients or servers may have specific quirks that must
be compensated for in the configuration, or there may be a
need to integrate the proxy in specific ways such as
assigning <acronym>FTP</acronym> traffic to a specific
queue. For these and other finer points of
&man.ftp-proxy.8; configuration, start by studying the man
page.</para>
<para>For ways to run an <acronym>FTP</acronym> server
protected by <acronym>PF</acronym> and &man.ftp-proxy.8;,
look into running a separate <command>ftp-proxy</command>
in reverse mode (using <option>-R</option>), on a separate
port with its own redirecting pass rule.</para>
</sect4>
</sect3>
<sect3 id="pftut-icmp">
<title>Easing Troubleshooting</title>
<para>Making network troubleshooting friendly is a potentially
large subject. At most times, the debugging or
troubleshooting friendliness of a <acronym>TCP/IP</acronym>
network depends on treatment of the Internet protocol which
was designed specifically with debugging in mind, the
<emphasis>Internet Control Message Protocol</emphasis>, or
<acronym>ICMP</acronym> as it is usually abbreviated.</para>
<para><acronym>ICMP</acronym> is the protocol for sending and
receiving <emphasis>control messages</emphasis> between
hosts and gateways, mainly to provide feedback to a sender
about any unusual or difficult conditions enroute to the
target host.</para>
<para>There is a lot of <acronym>ICMP</acronym> traffic which
usually just happens in the background while users are
surfing the web, reading mail or transferring files.
Routers use <acronym>ICMP</acronym> to negotiate packet
sizes and other transmission parameters in a process often
referred to as <emphasis>path <acronym>MTU</acronym>
discovery</emphasis>.</para>
<para>Some admins refer to <acronym>ICMP</acronym> as either
<quote>just evil</quote>, or, if their understanding runs a
little deeper, <quote>a necessary evil</quote>. The reason
for this attitude is purely historical. The reason can be
found a few years back when it was discovered that several
operating systems contained code in their networking stack
which could make a machine running one of the affected
systems crash and fall over, or in some cases just do really
strange things, with a sufficiently large
<acronym>ICMP</acronym> request.</para>
<para>One of the companies which was hit hard was Microsoft,
and you can find rather a lot of material on the
<quote>ping of death</quote> bug by using your favorite
search engine. This all happened in the second half of the
1990s, and all modern operating systems, at least the ones
we can read, have thoroughly sanitized their network code
since then. At least that is what we are led to
believe.</para>
<para>One of the early workarounds was to simply block either
all <acronym>ICMP</acronym> traffic or at least
<acronym>ICMP</acronym> ECHO, which is what ping uses. Now
these rule sets have been around for roughly fifteen years,
and the people who put them there are still scared.</para>
<sect4 id="pftut-dowepass">
<title>Then, Do We Let it All Through?</title>
<para>The obvious question then becomes, if
<acronym>ICMP</acronym> is such a good and useful thing,
should we not let it all through, all the time? The
answer is <quote>It depends</quote>.</para>
<para>Letting diagnostic traffic pass unconditionally of
course makes debugging easier, but also makes it
relatively easy for others to extract information about
your network. That means that a rule like</para>
<programlisting>pass inet proto icmp from any to any</programlisting>
<para>might not be optimal if the internal workings of the
local network should be cloaked in a bit of mystery. In
all fairness it should also be said that some
<acronym>ICMP</acronym> traffic might be found quite
harmlessly riding piggyback on
<literal>keep state</literal> rules.</para>
</sect4>
<sect4 id="pftut-icmpstopatgw">
<title>The Easy Way Out: the Buck Stops Here</title>
<para>The easiest solution could very well be to let all
<acronym>ICMP</acronym> traffic from the local net through
and stop probes from elsewhere at the gateway:</para>
<programlisting>pass inet proto icmp from $localnet to any keep state
pass inet proto icmp from any to $ext_if keep state</programlisting>
<para>Stopping probes at the gateway might be an attractive
option anyway, but let us have a look at a few other
options which will show some of <acronym>PF</acronym>'s
flexibility.</para>
</sect4>
<sect4 id="pftut-letpingthru">
<title>Letting <command>ping</command> Through</title>
<para>The rule set we have developed so far has one clear
disadvantage: common troubleshooting commands such as
&man.ping.8; and &man.traceroute.8; will not work. That
may not matter too much to end users, and since it was
<command>ping</command> which scared people into
filtering or blocking <acronym>ICMP</acronym> traffic in
the first place, there are apparently some people who feel
we are better off without it. If you are in my perceived
target audience, you will be rather fond of having those
troubleshooting tools avalable. With a couple of small
additions to the rule set, they will be. &man.ping.8;
uses <acronym>ICMP</acronym>, and in order to keep our
rule set tidy, we start by defining another macro:</para>
<programlisting>icmp_types = "echoreq"</programlisting>
<para>and a rule which uses the definition,</para>
<programlisting>pass inet proto icmp all icmp-type $icmp_types keep state</programlisting>
<para>More or other types of <acronym>ICMP</acronym> packets
may need to go through, and <literal>icmp_types</literal>
can be expanded to a list of those packet types that are
allowed.</para>
</sect4>
<sect4 id="pftut-helptraceroute">
<title>Helping &man.traceroute.8;</title>
<para>&man.traceroute.8; is another command which is quite
useful when users claim that the Internet is not working.
By default, Unix <command>traceroute</command> uses UDP
connections according to a set formula based on
destination. The rule below works with
<command>traceroute</command> on all unixes I've had
access to, including GNU/Linux:</para>
<programlisting># allow out the default range for traceroute(8):
# "base+nhops*nqueries-1" (33434+64*3-1)
pass out on $ext_if inet proto udp from any to any port 33433 &gt;&lt; 33626 keep state</programlisting>
<para>Experience so far indicates that
<command>traceroute</command> implementations on other
operating systems work roughly the same. Except, of
course, on Microsoft Windows. On that platform,
<command>TRACERT.EXE</command> uses ICMP ECHO for this
purpose. So to let Windows traceroutes through, only the
first rule is needed. Unix <command>traceroute</command>
can be instructed to use other protocols as well, and will
behave remarkably like its Microsoft counterpart if
<option>-I</option> is used. Check the &man.traceroute.8;
man page (or its source code, for that matter) for all the
details.</para>
<para>Under any circumstances, this solution was lifted
from an openbsd-misc post. I've found that list, and
the searchable list archives (accessible among other
places from <ulink
url="http://marc.theaimsgroup.com/">http://marc.theaimsgroup.com/</ulink>),
to be a very valuable resource whenever you need OpenBSD
or <acronym>PF</acronym> related information.</para>
</sect4>
<sect4 id="pftut-pathmtudisc">
<title>Path <acronym>MTU</acronym> Discovery</title>
<para>Internet protocols are designed to be device
independent, and one consequence of device independence is
that the optimal packet size for a given connection cannot
always be predicted reliably. The main constraint on
packet size is called the
<firstterm>Maximum Transmission Unit</firstterm>, or
<acronym>MTU</acronym>, which sets the upper limit on the
packet size for an interface. &man.ifconfig.8; shows the
<acronym>MTU</acronym> for the network interfaces.</para>
<para>Modern TCP/IP implementations expect to be able to
determine the right packet size for a connection through a
process which, simply put, involves sending packets of
varying sizes with the <quote>Do not fragment</quote> flag
set, expecting an <acronym>ICMP</acronym> return packet
indicating <quote>type 3, code 4</quote> when the upper
limit has been reached. Now do not dive for the RFCs
right away. Type 3 means <quote>destination
unreachable</quote>, while code 4 is short for
<quote>fragmentation needed, but the do-not-fragment flag
is set</quote>. So if connections to networks which may
have other <acronym>MTU</acronym>s than the local network
seem sub-optimal, and there is no need to be that
specific, the list of <acronym>ICMP</acronym> types can be
changed slightly to let the
<quote>destination unreachable</quote> packets through,
too:</para>
<programlisting>icmp_types = "{ echoreq, unreach }"</programlisting>
<para>As we can see, this means we do not need to change
the pass rule itself:</para>
<programlisting>pass inet proto icmp all icmp-type $icmp_types keep state</programlisting>
<para><acronym>PF</acronym> allows filtering on all
variations of <acronym>ICMP</acronym> types and codes.
For those who want to delve into what to pass (or not) of
<acronym>ICMP</acronym> traffic, the list of possible
types and codes are documented in the &man.icmp.4; and
&man.icmp6.4; man pages. The background information is
available in the <acronym>RFC</acronym>s
<footnote><para>The main internet <acronym>RFC</acronym>s
describing <acronym>ICMP</acronym> and
some related techhiques are RFC792, RFC950, RFC1191,
RFC1256, RFC2521, rfc2765, while necessary updates for
ICMP for IPv6 are found in RFC1885, RFC2463, RFC2466.
These documents are available in a number of places on
the net, such as the
<ulink url="http://www.ietf.org">ietf.org</ulink>
and
<ulink url="http://www.faqs.org">faqs.org</ulink>
web sites.</para></footnote>.</para>
</sect4>
</sect3>
<sect3 id="pftut-tables">
<title>Tables Make Life Easier</title>
<para>By this time it may appear that this gets awfully static
and rigid. There will after all be some kinds of data which
are relevant to filtering and redirection at a given time,
but do not deserve to be put into a configuration file!
Quite right, and <acronym>PF</acronym> offers mechanisms for
handling these situations as well. Tables are one such
feature, mainly useful as lists which can be manipulated
without needing to reload the entire rule set, and where
fast lookups are desirable. Table names are always enclosed
in <literal>&lt; &gt;</literal>, like this:</para>
<programlisting>table &lt;clients&gt; { 192.168.2.0/24, !192.168.2.5 }</programlisting>
<para>Here, the network <literal>192.168.2.0/24</literal>
is part of the table, except the address
<literal>192.168.2.5</literal>, which is excluded using
the <literal>!</literal> operator (logical NOT). It is
also possible to load tables from files where each item is
on a separate line, such as the file
<filename>/etc/clients</filename>.</para>
<programlisting>192.168.2.0/24
!192.168.2.5</programlisting>
<para>which in turn is used to initialize the table in
<filename>/etc/pf.conf</filename>:</para>
<programlisting>table &lt;clients&gt; persist file /etc/clients</programlisting>
<para>Then, for example, one of our earlier rules can be
changed to read</para>
<programlisting>pass inet proto tcp from &lt;clients&gt; to any port $client_out flags S/SA keep state</programlisting>
<para>to manage outgoing traffic from client computers. With
this in hand, the table's contents can be manipulated live,
such as</para>
<screen>&prompt.root; <userinput>pfctl -t clients -T add 192.168.1/16</userinput></screen>
<para>Note that this changes the in-memory copy of the table
only, meaning that the change will not survive a power
failure or other reboot unless there are arrangements to
store the changes.</para>
<para>One might opt to maintain the on-disk copy of the table
using a &man.cron.8; job which dumps the table content to
disk at regular intervals, using a command such as
<command>pfctl -t clients -T show
&gt;/etc/clients</command>. Alternatively,
<filename>/etc/clients</filename> could be edited, replacing
the in-memory table contents with the file data:</para>
<screen>&prompt.root; <userinput>pfctl -t clients -T replace -f /etc/clients</userinput></screen>
<para>For operations performed frequently, administrators will
sooner or later end up writing shell scripts for tasks
such as inserting or removing items or replacing table
contents. The only real limitations lie in individual needs
and creativity.</para>
</sect3>
<sect3 id="pftut-overload">
<title>Overload Tables</title>
<para>Those who run a Secure Shell login service which is
accessible from the Internet have probably seen something
like this in the authentication logs:</para>
<programlisting>Sep 26 03:12:34 skapet sshd[25771]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:34 skapet sshd[5279]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:35 skapet sshd[5279]: Received disconnect from 200.72.41.31: 11: Bye Bye
Sep 26 03:12:44 skapet sshd[29635]: Invalid user admin from 200.72.41.31
Sep 26 03:12:44 skapet sshd[24703]: input_userauth_request: invalid user admin
Sep 26 03:12:44 skapet sshd[24703]: Failed password for invalid user admin from 200.72.41.31 port 41484 ssh2</programlisting>
<para>And so on. This is what a brute force attack looks
like. Essentially somebody, or more likely, a cracked
computer somewhere, is trying by brute force to find a
combination of user name and password which will let them
into your system.</para>
<para>The simplest response would be to write a
<filename>pf.conf</filename> rule which blocks all access.
This leads to another class of problems, including what to
do in order to let people with legitimate business on the
system access it anyway. Some might consider moving the
service to another port, but then again, the ones flooding
on port 22 would probably be able to scan their way to port
22222 for a repeat performance.</para>
<para>Since OpenBSD 3.7, and soon after in &os; version 6.0,
<acronym>PF</acronym> has offered a slightly more elegant
solution. Pass rules can be written so they maintain
certain limits on what connecting hosts can do. For good
measure, violators can be banished to a table of addresses
which are denied some or all access. If desired, it's even
possible to drop all existing connections from machines
which overreach the limits. Here is how it is done:</para>
<para>First, set up the table. In the tables section,
add</para>
<programlisting>table &lt;bruteforce&gt; persist</programlisting>
<para>Then somewhere fairly early in the rule set, add a rule
to block the bruteforcers:</para>
<programlisting>block quick from &lt;bruteforce&gt;</programlisting>
<para>And finally, the pass rule.</para>
<programlisting>pass inet proto tcp from any to $localnet port $tcp_services \
flags S/SA keep state \
(max-src-conn 100, max-src-conn-rate 15/5, \
overload &lt;bruteforce&gt; flush global)</programlisting>
<para>The first part here is identical to the main rule we
constructed earlier. The part in parentheses is the new
stuff which will ease network load even further.</para>
<para><literal>max-src-conn</literal> is the number of
simultaneous connections allowed from one host. In this
example, it is set at 100. Other setups may want a slightly
higher or lower value.</para>
<para><literal>max-src-conn-rate</literal> is the rate of new
connections allowed from any single host, here 15
connections per 5 seconds. Again, the administrator is the
one to judge what suits their setup.</para>
<para><literal>overload &lt;bruteforce&gt;</literal> means
that any host which exceeds these limits gets its address
added to the table <literal>bruteforce</literal>. Our rule
set blocks all traffic from addresses in the bruteforce
table.</para>
<para>Finally, <literal>flush global</literal> says that when
a host reaches the limit, that host's connections will be
terminated (flushed). The global part says that for good
measure, this applies to connections which match other pass
rules too.</para>
<para>The effect is dramatic. From here on, bruteforcers
more often than not will end up with
<computeroutput>&quot;Fatal: timeout before
authentication&quot;</computeroutput> messages, getting
nowhere.</para>
<note>
<para>These rules will <emphasis>not</emphasis> block slow
bruteforcers, sometimes referred to as <ulink
url="http://home.nuug.no/~peter/hailmary2013/">the Hail
Mary Cloud</ulink>.</para>
</note>
<para>Once again, please keep in mind that this example rule
is intended mainly as an illustration. It is not unlikely
that a particular network's needs are better served by
rather different rules or combinations of rules.</para>
<para>If, for example, a generous number of connections in
general are wanted, but the desire is to be a little more
tight fisted when it comes to
<application>ssh</application>, supplement the rule above
with something like the one below, early on in the rule
set:</para>
<programlisting>pass quick proto { tcp, udp } from any to any port ssh \
flags S/SA keep state \
(max-src-conn 15, max-src-conn-rate 5/3, \
overload &lt;bruteforce&gt; flush global)</programlisting>
<para>It should be possible to find the set of parameters
which is just right for individual situations by reading the
relevant man pages and the
<ulink url="http://www.openbsd.org/faq/pf/">PF User
Guide</ulink>, and perhaps a bit of
experimentation.</para>
<note>
<title>It May Not be Necessary to Block All
Overloaders</title>
<para>It is probably worth noting at this point that the
<emphasis>overload</emphasis> mechanism is a general
technique which does not have to apply exclusively to the
<emphasis>ssh</emphasis> service, and it is not always
optimal to block all traffic from offenders
entirely.</para>
<para>For example, an overload rule could be used to
protect a mail service or a web service, and the overload
table could be used in a rule to assign offenders to a
queue with a minimal bandwidth allocation or, in the web
case, to redirect to a specific web page.</para>
</note>
<sect4 id="pftut-expire">
<title>Expiring Table Entries with
<application>pfctl</application></title>
<para>At this point, we have tables which will be filled by
our <literal>overload</literal> rules, and since we could
reasonably expect our gateways to have months of uptime,
the tables will grow incrementally, taking up more memory
as time goes by.</para>
<para>Sometimes an IP address that was blocked last week due
to a brute force attack was in fact a dynamically assigned
one, which is now assigned to a different ISP customer who
has a legitimate reason to try communicating with hosts in
the local network.</para>
<para>Situations like these were what caused Henning Brauer
to add to <application>pfctl</application> the ability to
expire table entries not referenced in a specified number
of seconds (in OpenBSD 4.1). For example, the
command</para>
<screen>&prompt.root; <userinput>pfctl -t bruteforce -T expire 86400</userinput></screen>
<para>will remove <literal>&lt;bruteforce&gt;</literal>
table entries which have not been referenced for 86400
seconds.</para>
</sect4>
<sect4 id="pftut-expiretable">
<title>The <application>expiretable</application>
Tool</title>
<para>Before <application>pfctl</application> acquired the
ability to expire table entries, Henrik Gustafsson had
written <application>expiretable</application>, which
removes table entries which have not been accessed for a
specified period of time.</para>
<para>One useful example is to use the
<application>expiretable</application> program as a way of
removing outdated <literal>&lt;bruteforce&gt;</literal>
table entries.</para>
<para>For example, let
<application>expiretable</application> remove
<literal>&lt;bruteforce&gt;</literal> table entries older
than 24 hours by adding an entry containing the following
to <filename>/etc/rc.local</filename>:</para>
<programlisting>/usr/local/sbin/expiretable -v -d -t 24h bruteforce</programlisting>
<para><application>expiretable</application> is in the
Ports&nbsp;Collection on &os; as <filename
role="package">security/expiretable</filename>.</para>
</sect4>
</sect3>
<sect3 id="pftut-tools">
<title>Other <acronym>PF</acronym> Tools</title>
<para>Over time, a number of tools have been developed which
interact with <acronym>PF</acronym> in various ways.</para>
<sect4 id="pftut-pftop">
<title>The <application>pftop</application> Traffic
Viewer</title>
<para>Can Erkin Acar's <application>pftop</application>
makes it possible to keep an eye on what passes into and
out of the network. <application>pftop</application> is
available through the ports system as
<filename role="package">sysutils/pftop</filename>. The
name is a strong hint at what it does -
<application>pftop</application> shows a running snapshot
of traffic in a format which is strongly inspired by
&man.top.1;.</para>
</sect4>
<sect4 id="pftut-spamd">
<title>The <application>spamd</application> Spam Deferral
Daemon</title>
<para>Not to be confused with the
<application>spamd</application> daemon which comes
bundled with <application>spamassassin</application>, the
<acronym>PF</acronym> companion
<application>spamd</application> was designed to run on a
PF gateway to form part of the outer defense against spam.
<application>spamd</application> hooks into the
<acronym>PF</acronym> configuration via a set of
redirections.</para>
<para>The main point underlying the
<application>spamd</application> design is the fact that
spammers send a large number of messages, and the
probability that you are the first person receiving a
particular message is incredibly small. In addition,
spam is mainly sent via a few spammer friendly networks
and a large number of hijacked machines. Both the
individual messages and the machines will be reported to
blacklists fairly quickly, and this is the kind of data
<application>spamd</application> can use to our advantage
with <firstterm>blacklists</firstterm>.</para>
<para>What <application>spamd</application> does to SMTP
connections from addresses in the blacklist is to
present its banner and immediately switch to a mode
where it answers SMTP traffic one byte at the time. This
technique, which is intended to waste as much time as
possible on the sending end while costing the receiver
pretty much nothing, is called
<firstterm>tarpitting</firstterm>. The specific
implementation with one byte SMTP replies is often
referred to as <firstterm>stuttering</firstterm>.</para>
<sect5 id="pftut-spamd-allblack">
<title>A Basic Blacklisting
<application>spamd</application></title>
<para>Here is the basic procedure for setting up
<application>spamd</application> with automatically
updated blacklists:</para>
<procedure>
<step>
<para>Install the <filename
role="package">mail/spamd/</filename> port. In
particular, be sure to read the package message and
act upon what it says. Specifically, to use
<application>spamd</application>'s greylisting
features, a file descriptor file system (see <ulink
url="http://www.freebsd.org/cgi/man.cgi?query=fdescfs&amp;sektion=5">fdescfs(5)</ulink>)
must be mounted at <filename>/dev/fd/</filename>.
Do this by adding the following line to
<filename>/etc/fstab</filename>:</para>
<programlisting> fdescfs /dev/fd fdescfs rw 0 0</programlisting>
<para>Make sure the <filename>fdescfs</filename> code
is in the kernel, either compiled in or by loading
the module with &man.kldload.8;.</para>
</step>
<step>
<para>Next, edit the rule set to include</para>
<programlisting>table &lt;spamd&gt; persist
table &lt;spamd-white&gt; persist
rdr pass on $ext_if inet proto tcp from &lt;spamd&gt; to \
{ $ext_if, $localnet } port smtp -&gt; 127.0.0.1 port 8025
rdr pass on $ext_if inet proto tcp from !&lt;spamd-white&gt; to \
{ $ext_if, $localnet } port smtp -&gt; 127.0.0.1 port 8025</programlisting>
<para>The two tables &lt;spamd&gt; and
&lt;spamd-white&gt; are essential. SMTP traffic
from the addresses in the first table plus the ones
which are not in the other table are redirected to a
daemon listening at port 8025.</para>
</step>
<step>
<para>The next step is to set up
<application>spamd</application>'s own configuration
in <filename>/usr/local/etc/spamd.conf</filename>
supplemented by <filename>rc.conf</filename>
parameters.</para>
<para>The supplied sample file offers quite a bit of
explanation, and the man page offers additional
information, but we will recap the essentials
here.</para>
<para>One of the first lines without a
<literal>#</literal> comment sign at the start
contains the block which defines the
<literal>all</literal> list, which specifies the
lists actually used:</para>
<programlisting>all:\
:traplist:whitelist:</programlisting>
<para>Here, all the desired black lists are added,
separated by colons (<literal>:</literal>). To use
whitelists to subtract addresses from the blacklist,
add the name of the whitelist immediately after the
name of each blacklist, i.e.,
<literal>:blacklist:whitelist:</literal>.</para>
<para>Next up is a blacklist definition:</para>
<programlisting>traplist:\
:black:\
:msg="SPAM. Your address %A has sent spam within the last 24 hours":\
:method=http:\
:file=www.openbsd.org/spamd/traplist.gz</programlisting>
<para>Following the name, the first data field
specifies the list type, in this case
<literal>black</literal>. The
<literal>msg</literal> field contains the message to
display to blacklisted senders during the SMTP
dialogue. The <literal>method</literal> field
specifies how spamd-setup fetches the list data,
here <literal>http</literal>. The other options are
fetching via <literal>ftp</literal>, from a
<literal>file</literal> in a mounted file system or
via <literal>exec</literal> of an external program.
Finally the <literal>file</literal> field specifies
the name of the file spamd expects to
receive.</para>
<para>The definition of a whitelist follows much the
same pattern:</para>
<programlisting>whitelist:\
:white:\
:method=file:\
:file=/var/mail/whitelist.txt</programlisting>
<para>but omits the message parameters since a
message is not needed.</para>
<tip>
<title>Choose Data Sources with Care</title>
<para>Using all the blacklists in the sample
<filename>spamd.conf</filename> will end up
blacklisting large blocks of the Internet,
including several Asian nations. Administrators
need to edit the file to end up with an optimal
configuration. The administrator is the judge of
which data sources to use, and using lists other
than the ones suggested in the sample file is
possible.</para>
</tip>
<para>Put the lines for spamd and any startup
parameters desired in
<filename>/etc/rc.conf</filename>, for
example:</para>
<programlisting>spamd_flags="-v" # for normal use: "" and see spamd-setup(8)</programlisting>
<para>When done with editing the setup,
reload the rule set, start
<application>spamd</application> with the options
desired using the
<filename>/usr/local/etc/rc.d/obspamd</filename>
script, and complete the configuration using
<command>spamd-setup</command>. Finally, create a
&man.cron.8; job which calls
<command>spamd-setup</command> to update the tables
at reasonable intervals.</para>
</step>
</procedure>
<para>On a typical gateway in front of a mail server,
hosts will start getting trapped within a few seconds to
several minutes.</para>
</sect5>
<sect5 id="pftut-spamd-greylist">
<title>Adding Greylisting to the
<application>spamd</application> Setup</title>
<para><application>spamd</application> also supports
<firstterm>greylisting</firstterm>, which works by
rejecting messages from unknown hosts temporarily with
<replaceable>45n</replaceable> codes, letting messages
from hosts which try again within a reasonable time
through. Traffic from well behaved hosts, that is,
senders which are set up to behave within the limits set
up in the relevant RFCs
<footnote><para>The relevant RFCs are mainly RFC1123
and RFC2821.</para></footnote>, will be let
through.</para>
<para>Greylisting as a technique was presented in a 2003
paper by Evan Harris
<footnote><para>The original
Harris paper and a number of other useful articles
and resources can be found at the <ulink
url="http://www.greylisting.org/">greylisting.org</ulink>
web site.</para></footnote>, and a number of
implementations followed over the next few months.
OpenBSD's <application>spamd</application> acquired its
ability to greylist in OpenBSD 3.5, which was released
in May 2004.</para>
<para>The most amazing thing about greylisting, apart
from its simplicity, is that it still works. Spammers
and malware writers have been very slow to adapt.</para>
<para>The basic procedure for adding greylisting to your
setup follows below.</para>
<procedure>
<step>
<para>If not done already, make sure the
file descriptor file system (see &man.fdescfs.5;) is
mounted at <filename>/dev/fd/</filename>. Do this
by adding the following line to
<filename>/etc/fstab</filename>:</para>
<programlisting>fdescfs /dev/fd fdescfs rw 0 0</programlisting>
<para>and make sure the &man.fdescfs.5; code is in the
kernel, either compiled in or by loading the module
with &man.kldload.8;.</para>
</step>
<step>
<para>To run <application>spamd</application> in
greylisting mode, <filename>/etc/rc.conf</filename>
must be changed slightly by adding</para>
<programlisting>spamd_grey="YES" # use spamd greylisting if YES</programlisting>
<para>Several greylisting related parameters can be
fine-tuned with <command>spamd</command>'s command
line parameters and the corresponding
<filename>/etc/rc.conf</filename> settings. Check
the <application>spamd</application> man page to see
what the parameters mean.</para>
</step>
<step>
<para>To complete the greylisting setup, restart
<application>spamd</application> using the
<filename>/usr/local/etc/rc.d/obspamd</filename>
script.</para>
</step>
</procedure>
<para>Behind the scenes, rarely mentioned and barely
documented are two of <application>spamd</application>'s
helpers, the <application>spamdb</application> database
tool and the <application>spamlogd</application>
whitelist updater, which both perform essential
functions for the greylisting feature. Of the two
<application>spamlogd</application> works quietly in the
background, while <application>spamdb</application> has
been developed to offer some interesting
features.</para>
<note>
<title>Restart <application>spamd</application> to
Enable Greylisting</title>
<para>After following all steps in the tutorial
exactly up to this point,
<application>spamlogd</application> has been started
automatically already. However, if the initial
<application>spamd</application> configuration did not
include greylisting,
<application>spamlogd</application> may not have been
started, and there may be strange symptoms, such as
greylists and whitelists not getting updated
properly.</para>
<para>Under normal circumstances, it should not be
necessary to start <application>spamlogd</application>
by hand. Restarting <application>spamd</application>
after enabling greylisting ensures
<application>spamlogd</application> is loaded and
available too.</para>
</note>
<para><application>spamdb</application> is the
administrator's main interface to managing the black,
grey and white lists via the contents of the
<filename>/var/db/spamdb</filename> database.</para>
</sect5>
</sect4>
<sect4 id="pftut-hygiene">
<title>Network Hygiene: Blocking, Scrubbing and so
On</title>
<para>Our gateway does not feel quite complete without a few
more items in the configuration which will make it behave
a bit more sanely towards hosts on the wide net and our
local network.</para>
<sect5 id="pftut-blockpolicy">
<title><literal>block-policy</literal></title>
<para><literal>block-policy</literal> is an option which
can be set in the <literal>options</literal> part of the
ruleset, which precedes the redirection and filtering
rules. This option determines which feedback, if any,
<acronym>PF</acronym> will give to hosts which try to
create connections which are subsequently blocked. The
option has two possible values, <literal>drop</literal>,
which drops blocked packets with no feedback, and
<literal>return</literal>, which returns with status
codes such as <computeroutput>Connection
refused</computeroutput> or similar.</para>
<para>The correct strategy for block policies has been the
subject of rather a lot of discussion. We choose to
play nicely and instruct our firewall to issue
returns:</para>
<programlisting>set block-policy return</programlisting>
</sect5>
<sect5 id="pftut-scrub">
<title><literal>scrub</literal></title>
<para>In <acronym>PF</acronym> versions up to OpenBSD 4.5
inclusive, <literal>scrub</literal> is a keyword which
enables network packet normalization, causing fragmented
packets to be assembled and removing ambiguity.
Enabling <literal>scrub</literal> provides a measure of
protection against certain kinds of attacks based on
incorrect handling of packet fragments. A number of
supplementing options are available, but we choose the
simplest form which is suitable for most
configurations.</para>
<programlisting>scrub in all</programlisting>
<para>Some services, such as NFS, require some specific
fragment handling options. This is extensively
documented in the <acronym>PF</acronym> user guide and
man pages provide all the information you could
need.</para>
<para>One fairly common example is this,</para>
<programlisting>scrub in all fragment reassemble no-df max-mss 1440</programlisting>
<para>meaning, we reassemble fragments, clear the
<quote>do not fragment</quote> bit and set the maximum
segment size to 1440 bytes. Other variations are
possible, and you should be able to cater to various
specific needs by consulting the man pages and some
experimentation.</para>
</sect5>
<sect5 id="pftut-antispoof">
<title><literal>antispoof</literal></title>
<para><literal>antispoof</literal> is a common special
case of filtering and blocking. This mechanism protects
against activity from spoofed or forged IP addresses,
mainly by blocking packets appearing on interfaces and
in directions which are logically not possible.</para>
<para>We specify that we want to weed out spoofed traffic
coming in from the rest of the world and any spoofed
packets which, however unlikely, were to originate in
our own network:</para>
<programlisting>antispoof for $ext_if
antispoof for $int_if</programlisting>
</sect5>
<sect5 id="pftut-unrouteables">
<title>Handling Non-Routable Addresses from
Elsewhere</title>
<para>Even with a properly configured gateway to handle
network address translation for your own network, you
may find yourself in the unenviable position of having
to compensate for other people's
misconfigurations.</para>
<para>One depressingly common class of misconfigurations
is the kind which lets traffic with non-routable
addresses out to the Internet. Traffic from
non-routeable addresses have also played a part in
several DOS attack techniques, so it may be worth
considering explicitly blocking traffic from
non-routeable addresses from entering your
network.</para>
<para>One possible solution is the one outlined below,
which for good measure also blocks any attempt to
initiate contact to non-routable addresses through the
gateway's external interface:</para>
<programlisting>martians = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, \
10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, \
0.0.0.0/8, 240.0.0.0/4 }"
block drop in quick on $ext_if from $martians to any
block drop out quick on $ext_if from any to $martians</programlisting>
<para>Here, the <literal>martians</literal> macro denotes
the RFC 1918 addresses and a few other ranges which are
mandated by various RFCs not to be in circulation on the
open Internet. Traffic to and from such addresses is
quietly dropped on the gateway's external
interface.</para>
<para>The specific details of how to implement this kind
of protection will vary, among other things according to
your specific network configuration. Your network
design could for example dictate that you include or
exclude other address ranges than these.</para>
<para>This completes our simple NATing firewall for a
small local network. A more thorough tutorial is
available at <ulink
url="http://home.nuug.no/~peter/pf/">http://home.nuug.no/~peter/pf/</ulink>,
where you will also find slides from related
presentations.</para>
</sect5>
</sect4>
</sect3>
</sect2>
</sect1>
<sect1 id="firewalls-ipf">
<title>The IPFILTER (IPF) Firewall</title>
<indexterm>
<primary>firewall</primary>
<secondary>IPFILTER</secondary>
</indexterm>
<para>IPFILTER is a cross-platform, open source firewall which
has been ported to &os;, NetBSD, OpenBSD, &sunos;, HP/UX, and
&solaris; operating systems.</para>
<para>IPFILTER is based on a kernel-side firewall and
<acronym>NAT</acronym> mechanism that can be controlled and
monitored by userland interface programs. The firewall rules
can be set or deleted using &man.ipf.8;. The
<acronym>NAT</acronym> rules can be set or deleted using
&man.ipnat.8;. Run-time statistics for the kernel parts of
IPFILTER can be printed using &man.ipfstat.8;. To log IPFILTER
actions to the system log files, use &man.ipmon.8;.</para>
<para>IPF was originally written using a rule processing logic
of <quote>the last matching rule wins</quote> and only used
stateless rules. Over time, IPF has been enhanced to include a
<quote>quick</quote> option and a stateful
<quote>keep state</quote> option which modernized the rules
processing logic. IPF's official documentation covers only the
legacy rule coding parameters and rule file processing logic and
the modernized functions are only included as additional
options.</para>
<para>The instructions contained in this section are based on
using rules that contain <quote>quick</quote> and
<quote>keep state</quote> as these provide the basic framework
for configuring an inclusive firewall ruleset.</para>
<para>For a detailed explanation of the legacy rules processing
method, refer to <ulink
url="http://www.munk.me.uk/ipf/ipf-howto.html"></ulink>
and <ulink
url="http://coombs.anu.edu.au/~avalon/ip-filter.html"></ulink>.</para>
<para>The IPF FAQ is at <ulink
url="http://www.phildev.net/ipf/index.html"></ulink>.</para>
<para>A searchable archive of the IPFilter mailing list is
available at <ulink
url="http://marc.theaimsgroup.com/?l=ipfilter"></ulink>.</para>
<sect2>
<title>Enabling IPF</title>
<indexterm>
<primary>IPFILTER</primary>
<secondary>enabling</secondary>
</indexterm>
<para>IPF is included in the basic &os; install as a kernel
loadable module. The system will dynamically load
this module at boot time when
<varname>ipfilter_enable="YES"</varname> is added to
<filename>rc.conf</filename>. The module enables logging and
<literal>default pass all</literal>. To change the
default to <literal>block all</literal>, add a
<literal>block all</literal> rule at the end of the
ruleset.</para>
</sect2>
<sect2>
<title>Kernel Options</title>
<indexterm>
<primary>kernel options</primary>
<secondary>IPFILTER</secondary>
</indexterm>
<indexterm>
<primary>kernel options</primary>
<secondary>IPFILTER_LOG</secondary>
</indexterm>
<indexterm>
<primary>kernel options</primary>
<secondary>IPFILTER_DEFAULT_BLOCK</secondary>
</indexterm>
<indexterm>
<primary>IPFILTER</primary>
<secondary>kernel options</secondary>
</indexterm>
<para>For users who prefer to statically compile IPF support
into a custom kernel, the following IPF option statements,
listed in <filename>/usr/src/sys/conf/NOTES</filename>, are
available:</para>
<programlisting>options IPFILTER
options IPFILTER_LOG
options IPFILTER_DEFAULT_BLOCK</programlisting>
<para><literal>options IPFILTER</literal> enables support for
the <quote>IPFILTER</quote> firewall.</para>
<para><literal>options IPFILTER_LOG</literal> enables IPF
logging using the <devicename>ipl</devicename> packet logging
pseudo&mdash;device for every rule that has the
<literal>log</literal> keyword.</para>
<para><literal>options IPFILTER_DEFAULT_BLOCK</literal> changes
the default behavior so that any packet not matching a
firewall <literal>pass</literal> rule gets blocked.</para>
<para>These settings will take effect only after installing a
kernel that has been built with the above options set.</para>
</sect2>
<sect2>
<title>Available <filename>rc.conf</filename> Options</title>
<para>To activate IPF at boot time, the following statements
need to be added to <filename>/etc/rc.conf</filename>:</para>
<programlisting>ipfilter_enable="YES" # Start ipf firewall
ipfilter_rules="/etc/ipf.rules" # loads rules definition text file
ipmon_enable="YES" # Start IP monitor log
ipmon_flags="-Ds" # D = start as daemon
# s = log to syslog
# v = log tcp window, ack, seq
# n = map IP &amp; port to names</programlisting>
<para>If there is a LAN behind the firewall that uses the
reserved private IP address ranges, the following lines have
to be added to enable <acronym>NAT</acronym>
functionality:</para>
<programlisting>gateway_enable="YES" # Enable as LAN gateway
ipnat_enable="YES" # Start ipnat function
ipnat_rules="/etc/ipnat.rules" # rules definition file for ipnat</programlisting>
</sect2>
<sect2>
<title>IPF</title>
<indexterm><primary><command>ipf</command></primary></indexterm>
<para>To load the ruleset file, use &man.ipf.8;. Custom rules
are normally placed in a file, and the following command can
be used to replace the currently running firewall
rules:</para>
<screen>&prompt.root; <userinput>ipf -Fa -f /etc/ipf.rules</userinput></screen>
<para><option>-Fa</option> flushes all the internal rules
tables.</para>
<para><option>-f</option> specifies the file containing the
rules to load.</para>
<para>This provides the ability to make changes to a custom
rules file, run the above IPF command, and thus update the
running firewall with a fresh copy of the rules without having
to reboot the system. This method is convenient for testing
new rules as the procedure can be executed as many times as
needed.</para>
<para>Refer to &man.ipf.8; for details on the other flags
available with this command.</para>
<para>&man.ipf.8; expects the rules file to be a standard text
file. It will not accept a rules file written as a script
with symbolic substitution.</para>
<para>There is a way to build IPF rules that utilize the power
of script symbolic substitution. For more information, see
<xref linkend="firewalls-ipf-rules-script"/>.</para>
</sect2>
<sect2>
<title>IPFSTAT</title>
<indexterm><primary><command>ipfstat</command></primary></indexterm>
<indexterm>
<primary>IPFILTER</primary>
<secondary>statistics</secondary>
</indexterm>
<para>The default behavior of &man.ipfstat.8; is to retrieve
and display the totals of the accumulated statistics gathered
by applying the rules against packets going in and out of the
firewall since it was last started, or since the last time the
accumulators were reset to zero using <command>ipf
-Z</command>.</para>
<para>Refer to &man.ipfstat.8; for details.</para>
<para>The default &man.ipfstat.8; output will look something
like this:</para>
<screen>input packets: blocked 99286 passed 1255609 nomatch 14686 counted 0
output packets: blocked 4200 passed 1284345 nomatch 14687 counted 0
input packets logged: blocked 99286 passed 0
output packets logged: blocked 0 passed 0
packets logged: input 0 output 0
log failures: input 3898 output 0
fragment state(in): kept 0 lost 0
fragment state(out): kept 0 lost 0
packet state(in): kept 169364 lost 0
packet state(out): kept 431395 lost 0
ICMP replies: 0 <acronym>TCP</acronym> RSTs sent: 0
Result cache hits(in): 1215208 (out): 1098963
IN Pullups succeeded: 2 failed: 0
OUT Pullups succeeded: 0 failed: 0
Fastroute successes: 0 failures: 0
<acronym>TCP</acronym> cksum fails(in): 0 (out): 0
Packet log flags set: (0)</screen>
<para>When supplied with either <option>-i</option> for inbound
or <option>-o</option> for outbound, the command will retrieve
and display the appropriate list of filter rules currently
installed and in use by the kernel.</para>
<para><command>ipfstat -in</command> displays the inbound
internal rules table with rule numbers.</para>
<para><command>ipfstat -on</command> displays the outbound
internal rules table with rule numbers.</para>
<para>The output will look something like this:</para>
<screen>@1 pass out on xl0 from any to any
@2 block out on dc0 from any to any
@3 pass out quick on dc0 proto tcp/udp from any to any keep state</screen>
<para><command>ipfstat -ih</command> displays the inbound
internal rules table, prefixing each rule with a count of how
many times the rule was matched.</para>
<para><command>ipfstat -oh</command> displays the outbound
internal rules table, prefixing each rule with a count of how
many times the rule was matched.</para>
<para>The output will look something like this:</para>
<screen>2451423 pass out on xl0 from any to any
354727 block out on dc0 from any to any
430918 pass out quick on dc0 proto tcp/udp from any to any keep state</screen>
<para>One of the most important options of
<command>ipfstat</command> is <option>-t</option> which
displays the state table in a way similar to how &man.top.1;
shows the &os; running process table. When a firewall is
under attack, this function provides the ability to identify
and see the attacking packets. The optional sub-flags give
the ability to select the destination or source IP, port, or
protocol to be monitored in real time. Refer to
&man.ipfstat.8; for details.</para>
</sect2>
<sect2>
<title>IPMON</title>
<indexterm><primary><command>ipmon</command></primary></indexterm>
<indexterm>
<primary>IPFILTER</primary>
<secondary>logging</secondary>
</indexterm>
<para>In order for <command>ipmon</command> to work properly,
the kernel option <literal>IPFILTER_LOG</literal> must be
turned on. This command has two different modes. Native mode
is the default mode when the command is used without
<option>-D</option>.</para>
<para>Daemon mode provides a continuous system log file so that
logging of past events may be reviewed. &os; has a built in
facility to automatically rotate system logs. This is why
outputting the log information to &man.syslogd.8; is better
than the default of outputting to a regular file. The default
<filename>rc.conf</filename>
<literal>ipmon_flags</literal> statement uses
<option>-Ds</option>:</para>
<programlisting>ipmon_flags="-Ds" # D = start as daemon
# s = log to syslog
# v = log tcp window, ack, seq
# n = map IP &amp; port to names</programlisting>
<para>Logging provides the ability to review, after the fact,
information such as which packets were dropped, what addresses
they came from and where they were going. These can all
provide a significant edge in tracking down attackers.</para>
<para>Even with the logging facility enabled, IPF will not
generate any rule logging by default. The firewall
administrator decides which rules in the ruleset should be
logged and adds the log keyword to those rules. Normally,
only deny rules are logged.</para>
<para>It is customary to include a <quote>default deny
everything</quote> rule with the log keyword included as the
last rule in the ruleset. This makes it possible to see all
the packets that did not match any of the rules in the
ruleset.</para>
</sect2>
<sect2>
<title>IPMON Logging</title>
<para>&man.syslogd.8; uses its own method for segregation of log
data. It uses groupings called <quote>facility</quote> and
<quote>level</quote>. By default, IPMON in
<option>-Ds</option> mode uses <literal>local0</literal> as
the <quote>facility</quote> name. The following levels can be
used to further segregate the logged data:</para>
<screen>LOG_INFO - packets logged using the "log" keyword as the action rather than pass or block.
LOG_NOTICE - packets logged which are also passed
LOG_WARNING - packets logged which are also blocked
LOG_ERR - packets which have been logged and which can be considered short</screen>
<!-- XXX: "can be considered short" == "with incomplete header" -->
<para>In order to setup IPFILTER to log all data to
<filename>/var/log/ipfilter.log</filename>, first
create the empty file:</para>
<screen>&prompt.root; <userinput>touch /var/log/ipfilter.log</userinput></screen>
<para>&man.syslogd.8; is controlled by definition statements in
<filename>/etc/syslog.conf</filename>. This file offers
considerable flexibility in how
<application>syslog</application> will deal with system
messages issued by software applications like IPF.</para>
<para>To write all logged messages to the specified file,
add the following statement to
<filename>/etc/syslog.conf</filename>:</para>
<programlisting>local0.* /var/log/ipfilter.log</programlisting>
<para>To activate the changes and instruct &man.syslogd.8;
to read the modified <filename>/etc/syslog.conf</filename>,
run <command>service syslogd reload</command>.</para>
<para>Do not forget to change
<filename>/etc/newsyslog.conf</filename> to rotate the new
log file.</para>
</sect2>
<sect2>
<title>The Format of Logged Messages</title>
<para>Messages generated by <command>ipmon</command> consist
of data fields separated by white space. Fields common to
all messages are:</para>
<orderedlist>
<listitem>
<para>The date of packet receipt.</para>
</listitem>
<listitem>
<para>The time of packet receipt. This is in the form
HH:MM:SS.F, for hours, minutes, seconds, and fractions
of a second.</para>
</listitem>
<listitem>
<para>The name of the interface that processed the
packet.</para>
</listitem>
<listitem>
<para>The group and rule number of the rule in the format
<literal>@0:17</literal>.</para>
</listitem>
</orderedlist>
<para>These can be viewed with
<command>ipfstat -in</command>.</para>
<orderedlist>
<listitem>
<para>The action: <literal>p</literal> for passed,
<literal>b</literal> for blocked, <literal>S</literal> for
a short packet, <literal>n</literal> did not match any
rules, and <literal>L</literal> for a log rule. The order
of precedence in showing flags is: <literal>S</literal>,
<literal>p</literal>, <literal>b</literal>,
<literal>n</literal>, <literal>L</literal>. A capital
<literal>P</literal> or <literal>B</literal> means that
the packet has been logged due to a global logging
setting, not a particular rule.</para>
</listitem>
<listitem>
<para>The addresses written as three fields: the source
address and port separated by a comma, the -&gt; symbol,
and the destination address and port. For example:
<literal>209.53.17.22,80 -&gt;
198.73.220.17,1722</literal>.</para>
</listitem>
<listitem>
<para><literal>PR</literal> followed by the protocol name
or number: for example, <literal>PR tcp</literal>.</para>
</listitem>
<listitem>
<para><literal>len</literal> followed by the header length
and total length of the packet: for example,
<literal>len 20 40</literal>.</para>
</listitem>
</orderedlist>
<para>If the packet is a <acronym>TCP</acronym> packet, there
will be an additional field starting with a hyphen followed by
letters corresponding to any flags that were set. Refer to
&man.ipf.5; for a list of letters and their flags.</para>
<para>If the packet is an ICMP packet, there will be two fields
at the end: the first always being <quote>ICMP</quote> and
the next being the ICMP message and sub-message type,
separated by a slash. For example: ICMP 3/3 for a port
unreachable message.</para>
</sect2>
<sect2 id="firewalls-ipf-rules-script">
<title>Building the Rule Script with Symbolic
Substitution</title>
<para>Some experienced IPF users create a file containing the
rules and code them in a manner compatible with running them
as a script with symbolic substitution. The major benefit
of doing this is that only the value associated with the
symbolic name needs to be changed, and when the script is
run all the rules containing the symbolic name will have the
value substituted in the rules. Being a script, symbolic
substitution can be used to code frequently used values and
substitute them in multiple rules. This can be seen in the
following example.</para>
<para>The script syntax used here is compatible with the
&man.sh.1;, &man.csh.1;, and &man.tcsh.1; shells.</para>
<para>Symbolic substitution fields are prefixed with a
<literal>&dollar;</literal>.</para>
<para>Symbolic fields do not have the &dollar; prefix.</para>
<para>The value to populate the symbolic field must be enclosed
between double quotes (<literal>"</literal>).</para>
<para>Start the rule file with something like this:</para>
<programlisting>############# Start of IPF rules script ########################
oif="dc0" # name of the outbound interface
odns="192.0.2.11" # ISP's DNS server IP address
myip="192.0.2.7" # my static IP address from ISP
ks="keep state"
fks="flags S keep state"
# You can choose between building /etc/ipf.rules file
# from this script or running this script "as is".
#
# Uncomment only one line and comment out another.
#
# 1) This can be used for building /etc/ipf.rules:
#cat &gt; /etc/ipf.rules &lt;&lt; EOF
#
# 2) This can be used to run script "as is":
/sbin/ipf -Fa -f - &lt;&lt; EOF
# Allow out access to my ISP's Domain name server.
pass out quick on &dollar;oif proto tcp from any to &dollar;odns port = 53 &dollar;fks
pass out quick on &dollar;oif proto udp from any to &dollar;odns port = 53 &dollar;ks
# Allow out non-secure standard www function
pass out quick on &dollar;oif proto tcp from &dollar;myip to any port = 80 &dollar;fks
# Allow out secure www function https over TLS SSL
pass out quick on &dollar;oif proto tcp from &dollar;myip to any port = 443 &dollar;fks
EOF
################## End of IPF rules script ########################</programlisting>
<para>The rules are not important in this example as it instead
focuses on how the symbolic substitution fields are populated.
If this example was in a file named
<filename>/etc/ipf.rules.script</filename>, these rules could
be reloaded by running:</para>
<screen>&prompt.root; <userinput>sh /etc/ipf.rules.script</userinput></screen>
<para>There is one problem with using a rules file with embedded
symbolics: IPF does not understand symbolic substitution, and
cannot read such scripts directly.</para>
<para>This script can be used in one of two ways:</para>
<itemizedlist>
<listitem>
<para>Uncomment the line that begins with
<literal>cat</literal>, and comment out the line that
begins with <literal>/sbin/ipf</literal>. Place
<literal>ipfilter_enable="YES"</literal> into
<filename>/etc/rc.conf</filename>, and run the script
once after each modification to create or update
<filename>/etc/ipf.rules</filename>.</para>
</listitem>
<listitem>
<para>Disable IPFILTER in the system startup scripts by
adding <literal>ipfilter_enable="NO"</literal>to
<filename>/etc/rc.conf</filename>.</para>
<para>Then, add a script like the following to <filename
class="directory">/usr/local/etc/rc.d/</filename>.
The script should have an obvious name like
<filename>ipf.loadrules.sh</filename>, where the
<filename>.sh</filename> extension is mandatory.</para>
<programlisting>#!/bin/sh
sh /etc/ipf.rules.script</programlisting>
<para>The permissions on this script file must be read,
write, execute for owner <username>root</username>:</para>
<screen>&prompt.root; <userinput>chmod 700 /usr/local/etc/rc.d/ipf.loadrules.sh</userinput></screen>
</listitem>
</itemizedlist>
<para>Now, when the system boots, the IPF rules will be
loaded.</para>
</sect2>
<sect2>
<title>IPF Rulesets</title>
<para>A ruleset contains a group of IPF rules which pass or
block packets based on the values contained in the packet.
The bi-directional exchange of packets between hosts comprises
a session conversation. The firewall ruleset processes both
the packets arriving from the public Internet, as well as the
packets produced by the system as a response to them.
Each <acronym>TCP/IP</acronym> service is predefined by its
protocol and listening port. Packets destined for a specific
service originate from the source address using an
unprivileged port and target the specific service port on the
destination address. All the above parameters can be used as
selection criteria to create rules which will pass or block
services.</para>
<indexterm>
<primary>IPFILTER</primary>
<secondary>rule processing order</secondary>
</indexterm>
<warning>
<para>When working with the firewall rules, be <emphasis>very
careful</emphasis>. Some configurations <emphasis>can
lock the administrator out</emphasis> of the server. To be
on the safe side, consider performing the initial firewall
configuration from the local console rather than doing it
remotely over <application>ssh</application>.</para>
</warning>
</sect2>
<sect2>
<title>Rule Syntax</title>
<indexterm>
<primary>IPFILTER</primary>
<secondary>rule syntax</secondary>
</indexterm>
<para>The rule syntax presented here has been simplified to
only address the modern stateful rule context and <quote>first
matching rule wins</quote> logic. For the complete legacy
rule syntax, refer to &man.ipf.8;.</para>
<para>A <literal>#</literal> character is used to mark the
start of a comment and may appear at the end of a rule line
or on its own line. Blank lines are ignored.</para>
<para>Rules contain keywords which must be written in a specific
order from left to right on the line. Keywords are identified
in bold type. Some keywords have sub-options which may be
keywords themselves and also include more sub-options. Each
of the headings in the below syntax has a bold section header
which expands on the content.</para>
<!-- This section is probably wrong. See the OpenBSD flag -->
<!-- What is the "OpenBSD flag"? Reference please -->
<para><replaceable>ACTION IN-OUT OPTIONS SELECTION STATEFUL
PROTO SRC_ADDR,DST_ADDR OBJECT PORT_NUM TCP_FLAG
STATEFUL</replaceable></para>
<para><replaceable>ACTION</replaceable> = block | pass</para>
<para><replaceable>IN-OUT</replaceable> = in | out</para>
<para><replaceable>OPTIONS</replaceable> = log | quick | on
interface-name</para>
<para><replaceable>SELECTION</replaceable> = proto value |
source/destination IP | port = number | flags
flag-value</para>
<para><replaceable>PROTO</replaceable> = tcp/udp | udp | tcp |
icmp</para>
<para><replaceable>SRC_ADD,DST_ADDR</replaceable> = all | from
object to object</para>
<para><replaceable>OBJECT</replaceable> = IP address |
any</para>
<para><replaceable>PORT_NUM</replaceable> = port number</para>
<para><replaceable>TCP_FLAG</replaceable> = S</para>
<para><replaceable>STATEFUL</replaceable> = keep state</para>
<sect3>
<title>ACTION</title>
<para>The action keyword indicates what to do with the packet
if it matches the rest of the filter rule. Each rule
<emphasis>must</emphasis> have an action. The following
actions are recognized:</para>
<para><literal>block</literal> indicates that the packet
should be dropped if the selection parameters match the
packet.</para>
<para><literal>pass</literal> indicates that the packet should
exit the firewall if the selection parameters match the
packet.</para>
</sect3>
<sect3>
<title>IN-OUT</title>
<para>A mandatory requirement is that each filter rule
explicitly state which side of the I/O it is to be used
on. The next keyword must be either <literal>in</literal>
or <literal>out</literal> and one or the other has to be
included or the rule will not pass syntax checks.</para>
<para><literal>in</literal> means this rule is being applied
against an inbound packet which has just been received on
the interface facing the public Internet.</para>
<para><literal>out</literal> means this rule is being applied
against an outbound packet destined for the interface facing
the public Internet.</para>
</sect3>
<sect3>
<title>OPTIONS</title>
<note>
<para>These options must be used in the order shown
here.</para>
</note>
<para><literal>log</literal> indicates that the packet header
will be written to the &man.ipl.4; packet log pseudo-device
if the selection parameters match the packet.</para>
<para><literal>quick</literal> indicates that if the selection
parameters match the packet, this rule will be the last
rule checked, and no further processing of any following
rules will occur for this packet.</para>
<para><literal>on</literal> indicates the interface name to
be incorporated into the selection parameters. Interface
names are as displayed by &man.ifconfig.8;. Using this
option, the rule will only match if the packet is going
through that interface in the specified direction.</para>
<para>When a packet is logged, the headers of the packet are
written to the &man.ipl.4; packet logging pseudo-device.
Immediately following the <literal>log</literal> keyword,
the following qualifiers may be used in this order:</para>
<para><literal>body</literal> indicates that the first 128
bytes of the packet contents will be logged after the
headers.</para>
<para><literal>first</literal>. If the <literal>log</literal>
keyword is being used in conjunction with a <literal>keep
state</literal> option, this option is recommended so that
only the triggering packet is logged and not every packet
which matches the stateful connection.</para>
</sect3>
<sect3>
<title>SELECTION</title>
<para>The keywords described in this section are used to
describe attributes of the packet to be checked when
determining whether or not rules match. There is a
keyword subject, and it has sub-option keywords, one of
which has to be selected. The following general-purpose
attributes are provided for matching, and must be used in
this order:</para>
</sect3>
<sect3>
<title>PROTO</title>
<para><literal>proto</literal> is the subject keyword which
must include one of its corresponding keyword sub-option
values. The sub-option indicates a specific protocol to be
matched against.</para>
<para><literal>tcp/udp | udp | tcp | icmp</literal> or any
protocol names found in <filename>/etc/protocols</filename>
are recognized and may be used. The special protocol
keyword <literal>tcp/udp</literal> may be used to match
either a <acronym>TCP</acronym> or a <acronym>UDP</acronym>
packet, and has been added as a convenience to save
duplication of otherwise identical rules.</para>
</sect3>
<sect3>
<title>SRC_ADDR/DST_ADDR</title>
<para>The <literal>all</literal> keyword is equivalent to
<quote>from any to any</quote> with no other match
parameters.</para>
<para><literal>from | to src to dst</literal>: the
<literal>from</literal> and <literal>to</literal>
keywords are used to match against IP addresses. Rules
must specify <emphasis>both</emphasis> the source and
destination parameters. <literal>any</literal> is a special
keyword that matches any IP address. Examples include:
<literal>from any to any</literal>, <literal>from 0.0.0.0/0
to any</literal>, <literal>from any to
0.0.0.0/0</literal>, <literal>from 0.0.0.0 to
any</literal>, and <literal>from any to
0.0.0.0</literal>.</para>
<para>There is no way to match ranges of IP addresses which
do not express themselves easily using the dotted numeric
form / mask-length notation. The <filename
role="package">net-mgmt/ipcalc</filename> port may be
used to ease the calculation. Additional information
is available at the utility's web page: <ulink
url="http://jodies.de/ipcalc"></ulink>.</para>
</sect3>
<sect3>
<title>PORT</title>
<para>If a port match is included, for either or both of
source and destination, it is only applied to
<acronym>TCP</acronym> and <acronym>UDP</acronym> packets.
When composing port comparisons, either the service name
from <filename>/etc/services</filename> or an integer port
number may be used. When the port appears as part of the
<literal>from</literal> object, it matches the source port
number. When it appears as part of the
<literal>to</literal> object, it matches the destination
port number. An example usage is <literal>from any to any
port = 80</literal></para>
<para>Single port comparisons may be done in a number of ways,
using a number of different comparison operators. Instead
of the <literal>=</literal> shown in the example above,
the following operators may be used: <literal>!=</literal>,
<literal>&lt;</literal>, <literal>&gt;</literal>,
<literal>&lt;=</literal>, <literal>&gt;=</literal>,
<literal>eq</literal>, <literal>ne</literal>,
<literal>lt</literal>, <literal>gt</literal>,
<literal>le</literal>, and <literal>ge</literal>.</para>
<para>To specify port ranges, place the two port numbers
between <literal>&lt;&gt;</literal> or
<literal>&gt;&lt;</literal></para>
</sect3>
<sect3>
<title><acronym>TCP</acronym>_FLAG</title>
<para>Flags are only effective for <acronym>TCP</acronym>
filtering. The letters represent one of the possible flags
that can be matched against the <acronym>TCP</acronym>
packet header.</para>
<para>The modernized rules processing logic uses the
<literal>flags S</literal> parameter to identify the TCP
session start request.</para>
</sect3>
<sect3>
<title>STATEFUL</title>
<para><literal>keep state</literal> indicates that on a pass
rule, any packets that match the rules selection parameters
should activate the stateful filtering facility.</para>
</sect3>
</sect2>
<sect2>
<title>Stateful Filtering</title>
<indexterm>
<primary>IPFILTER</primary>
<secondary>stateful filtering</secondary>
</indexterm>
<!-- XXX: duplicated -->
<para>Stateful filtering treats traffic as a bi-directional
exchange of packets comprising a session. When activated,
<literal>keep-state</literal> dynamically generates
internal rules for each anticipated packet being exchanged
during the session. It has sufficient matching capabilities
to determine if a packet is valid for a session. Any packets
that do not properly fit the session template are
automatically rejected.</para>
<para>IPF stateful filtering will also allow
<acronym>ICMP</acronym> packets related to an existing
<acronym>TCP</acronym> or <acronym>UDP</acronym> session. So,
if an <acronym>ICMP</acronym> type 3 code 4 packet is a
response in a session started by a keep state rule, it will
automatically be allowed. Any packet that IPF can be certain
is part of an active session, even if it is a different
protocol, will be allowed.</para>
<para>Packets destined to go out through the interface connected
to the public Internet are first checked against the dynamic
state table. If the packet matches the next expected packet
comprising an active session conversation, it exits the
firewall and the state of the session conversation flow is
updated in the dynamic state table. Packets that do not
belong to an already active session, are checked against the
outbound ruleset.</para>
<para>Packets coming in from the interface connected to the
public Internet are first checked against the dynamic state
table. If the packet matches the next expected packet
comprising an active session, it exits the firewall and the
state of the session conversation flow is updated in the
dynamic state table. Packets that do not belong to an already
active session, are checked against the inbound
ruleset.</para>
<para>When the session completes, it is removed from the
dynamic state table.</para>
<para>Stateful filtering allows one to focus on blocking/passing
new sessions. If the new session is passed, all its
subsequent packets are allowed automatically and any impostor
packets are automatically rejected. If a new session is
blocked, none of its subsequent packets are allowed. Stateful
filtering provides advanced matching abilities capable of
defending against the flood of different attack methods
employed by attackers.</para>
</sect2>
<sect2>
<!-- XXX: This section needs a rewrite -->
<title>Inclusive Ruleset Example</title>
<para>The following ruleset is an example of an inclusive type
of firewall which only allows services matching
<literal>pass</literal> rules and blocks all others by
default. Network firewalls intended to protect other machines
should have at least two interfaces, and are generally
configured to trust the <acronym>LAN</acronym> and to not
trust the public Internet. Alternatively, a host based
firewall might be configured to protect only the system it is
running on, and is appropriate for servers on an untrusted
network or a desktop system not protected by firewall on the
network.</para>
<para>&os; uses interface <devicename>lo0</devicename> and IP
address <hostid role="ipaddr">127.0.0.1</hostid> for internal
communication within the operating system. The firewall rules
must contain rules to allow free movement of these internally
used packets.</para>
<para>The interface which faces the public Internet is the one
specified in the rules that authorize and control access of
the outbound and inbound connections.</para>
<para>In cases where one or more NICs are cabled to private
network segments, those interfaces may require rules to allow
packets originating from those LAN interfaces transit to each
other or to the Internet.</para>
<para>The rules should be organized into three major
sections: the trusted interfaces, then the public
interface outbound, and lastly, the public untrusted interface
inbound.</para>
<para>The rules in each of the public interface sections should
have the most frequently matched rules placed before less
commonly matched rules, with the last rule in the section
blocking and logging all packets on that interface and
direction.</para>
<para>The outbound section in the following ruleset only
contains <literal>pass</literal> rules which uniquely identify
the services that are authorized for public Internet access.
All the rules use <literal>quick</literal>,
<literal>on</literal>, <literal>proto</literal>,
<literal>port</literal>, and <literal>keep state</literal>.
The <literal>proto tcp</literal> rules include
<literal>flag</literal> to identify the session start request
as the triggering packet to activate the stateful
facility.</para>
<para>The inbound section blocks undesirable packets first, for
two different reasons. The first is that malicious packets
may be partial matches for legitimate traffic. These packets
have to be discarded rather than allowed, based on their
partial matches against the <literal>allow</literal> rules.
The second reason is that known and uninteresting rejects may
be blocked silently, rather than being logged by the last rule
in the section.</para>
<para>The ruleset should ensure that there is no response
returned for any undesirable traffic. Invalid packets should
be silently dropped so that the attacker has no knowledge if
the packets reached the system. Rules that include a
<literal>log first</literal> option, will only log the event
the first time they are triggered. This option is included in
the sample <literal>nmap OS fingerprint</literal> rule. The
<filename role="package">security/nmap</filename> utility is
commonly used by attackers who attempt to identify the
operating system of the server.</para>
<para>Any time there are logged messages on a rule with
the <literal>log first</literal> option,
<command>ipfstat -hio</command> should be executed
to evaluate how many times the rule has been matched. A
large number of matches usually indicates that the system is
being flooded or is under attack.</para>
<para>To lookup unknown port numbers, refer to
<filename>/etc/services</filename>. Alternatively, visit
<ulink
url="http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers"></ulink>
and do a port number lookup to find the purpose of a
particular port number.</para>
<para>Check out this link for port numbers used by Trojans
<ulink
url="http://www.sans.org/security-resources/idfaq/oddports.php"></ulink>.</para>
<para>The following ruleset creates an
<literal>inclusive</literal> firewall ruleset which can be
easily customized by commenting out
<literal>pass</literal> rules for services that should not
be authorized.</para>
<para>To avoid logging unwanted messages, add a
<literal>block</literal> rule in the inbound section.</para>
<para>Change the <devicename>dc0</devicename> interface name in
every rule to the interface name that connects the system to
the public Internet.</para>
<para>The following statements were added to
<filename>/etc/ipf.rules</filename>:</para>
<programlisting>#################################################################
# No restrictions on Inside LAN Interface for private network
# Not needed unless you have LAN
#################################################################
#pass out quick on xl0 all
#pass in quick on xl0 all
#################################################################
# No restrictions on Loopback Interface
#################################################################
pass in quick on lo0 all
pass out quick on lo0 all
#################################################################
# Interface facing Public Internet (Outbound Section)
# Match session start requests originating from behind the
# firewall on the private network
# or from this gateway server destined for the public Internet.
#################################################################
# Allow out access to my ISP's Domain name server.
# xxx must be the IP address of your ISP's DNS.
# Dup these lines if your ISP has more than one DNS server
# Get the IP addresses from /etc/resolv.conf file
pass out quick on dc0 proto tcp from any to xxx port = 53 flags S keep state
pass out quick on dc0 proto udp from any to xxx port = 53 keep state
# Allow out access to my ISP's DHCP server for cable or DSL networks.
# This rule is not needed for 'user ppp' type connection to the
# public Internet, so you can delete this whole group.
# Use the following rule and check log for IP address.
# Then put IP address in commented out rule &amp; delete first rule
pass out log quick on dc0 proto udp from any to any port = 67 keep state
#pass out quick on dc0 proto udp from any to z.z.z.z port = 67 keep state
# Allow out non-secure standard www function
pass out quick on dc0 proto tcp from any to any port = 80 flags S keep state
# Allow out secure www function https over TLS SSL
pass out quick on dc0 proto tcp from any to any port = 443 flags S keep state
# Allow out send &amp; get email function
pass out quick on dc0 proto tcp from any to any port = 110 flags S keep state
pass out quick on dc0 proto tcp from any to any port = 25 flags S keep state
# Allow out Time
pass out quick on dc0 proto tcp from any to any port = 37 flags S keep state
# Allow out nntp news
pass out quick on dc0 proto tcp from any to any port = 119 flags S keep state
# Allow out gateway &amp; LAN users' non-secure FTP ( both passive &amp; active modes)
# This function uses the IP<acronym>NAT</acronym> built in FTP proxy function coded in
# the nat rules file to make this single rule function correctly.
# If you want to use the pkg_add command to install application packages
# on your gateway system you need this rule.
pass out quick on dc0 proto tcp from any to any port = 21 flags S keep state
# Allow out ssh/sftp/scp (telnet/rlogin/FTP replacements)
# This function is using SSH (secure shell)
pass out quick on dc0 proto tcp from any to any port = 22 flags S keep state
# Allow out insecure Telnet
pass out quick on dc0 proto tcp from any to any port = 23 flags S keep state
# Allow out FreeBSD CVSup
pass out quick on dc0 proto tcp from any to any port = 5999 flags S keep state
# Allow out ping to public Internet
pass out quick on dc0 proto icmp from any to any icmp-type 8 keep state
# Allow out whois from LAN to public Internet
pass out quick on dc0 proto tcp from any to any port = 43 flags S keep state
# Block and log only the first occurrence of everything
# else that's trying to get out.
# This rule implements the default block
block out log first quick on dc0 all
#################################################################
# Interface facing Public Internet (Inbound Section)
# Match packets originating from the public Internet
# destined for this gateway server or the private network.
#################################################################
# Block all inbound traffic from non-routable or reserved address spaces
block in quick on dc0 from 192.168.0.0/16 to any #RFC 1918 private IP
block in quick on dc0 from 172.16.0.0/12 to any #RFC 1918 private IP
block in quick on dc0 from 10.0.0.0/8 to any #RFC 1918 private IP
block in quick on dc0 from 127.0.0.0/8 to any #loopback
block in quick on dc0 from 0.0.0.0/8 to any #loopback
block in quick on dc0 from 169.254.0.0/16 to any #DHCP auto-config
block in quick on dc0 from 192.0.2.0/24 to any #reserved for docs
block in quick on dc0 from 204.152.64.0/23 to any #Sun cluster interconnect
block in quick on dc0 from 224.0.0.0/3 to any #Class D &amp; E multicast
##### Block a bunch of different nasty things. ############
# That I do not want to see in the log
# Block frags
block in quick on dc0 all with frags
# Block short tcp packets
block in quick on dc0 proto tcp all with short
# block source routed packets
block in quick on dc0 all with opt lsrr
block in quick on dc0 all with opt ssrr
# Block nmap OS fingerprint attempts
# Log first occurrence of these so I can get their IP address
block in log first quick on dc0 proto tcp from any to any flags FUP
# Block anything with special options
block in quick on dc0 all with ipopts
# Block public pings
block in quick on dc0 proto icmp all icmp-type 8
# Block ident
block in quick on dc0 proto tcp from any to any port = 113
# Block all Netbios service. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
block in log first quick on dc0 proto tcp/udp from any to any port = 137
block in log first quick on dc0 proto tcp/udp from any to any port = 138
block in log first quick on dc0 proto tcp/udp from any to any port = 139
block in log first quick on dc0 proto tcp/udp from any to any port = 81
# Allow traffic in from ISP's DHCP server. This rule must contain
# the IP address of your ISP's DHCP server as it is the only
# authorized source to send this packet type. Only necessary for
# cable or DSL configurations. This rule is not needed for
# 'user ppp' type connection to the public Internet.
# This is the same IP address you captured and
# used in the outbound section.
pass in quick on dc0 proto udp from z.z.z.z to any port = 68 keep state
# Allow in standard www function because I have apache server
pass in quick on dc0 proto tcp from any to any port = 80 flags S keep state
# Allow in non-secure Telnet session from public Internet
# labeled non-secure because ID/PW passed over public Internet as clear text.
# Delete this sample group if you do not have telnet server enabled.
#pass in quick on dc0 proto tcp from any to any port = 23 flags S keep state
# Allow in secure FTP, Telnet, and SCP from public Internet
# This function is using SSH (secure shell)
pass in quick on dc0 proto tcp from any to any port = 22 flags S keep state
# Block and log only first occurrence of all remaining traffic
# coming into the firewall. The logging of only the first
# occurrence avoids filling up disk with Denial of Service logs.
# This rule implements the default block.
block in log first quick on dc0 all
################### End of rules file #####################################</programlisting>
</sect2>
<sect2>
<title><acronym>NAT</acronym></title>
<indexterm><primary>NAT</primary></indexterm>
<indexterm>
<primary>IP masquerading</primary>
<see>NAT</see>
</indexterm>
<indexterm>
<primary>network address translation</primary>
<see>NAT</see>
</indexterm>
<para><acronym>NAT</acronym> stands for <emphasis>Network
Address Translation</emphasis>. In &linux;, NAT is called
<quote>IP Masquerading</quote>. The IPF
<acronym>NAT</acronym> function enables the private LAN behind
the firewall to share a single ISP-assigned IP address, even
if that address is dynamically assigned. NAT allows each
computer in the LAN to have Internet access, without
having to pay the ISP for multiple Internet accounts or IP
addresses.</para>
<para><acronym>NAT</acronym> will automatically translate the
private LAN IP address for each system on the LAN to the
single public IP address as packets exit the firewall bound
for the public Internet. It also performs the reverse
translation for returning packets.</para>
<para>According to RFC 1918, the following IP address ranges are
reserved for private networks which will never be routed
directly to the public Internet, and therefore are available
for use with NAT:</para>
<itemizedlist>
<listitem>
<para><literal>10.0.0.0/8</literal>.</para>
</listitem>
<listitem>
<para><literal>172.16.0.0/12</literal>.</para>
</listitem>
<listitem>
<para><literal>192.168.0.0/16</literal>.</para>
</listitem>
</itemizedlist>
</sect2>
<sect2>
<title>IP<acronym>NAT</acronym></title>
<indexterm>
<primary>NAT</primary>
<secondary>and IPFILTER</secondary>
</indexterm>
<indexterm><primary><command>ipnat</command></primary></indexterm>
<para><acronym>NAT</acronym> rules are loaded using
<command>ipnat</command>. Typically, the
<acronym>NAT</acronym> rules are stored in
<filename>/etc/ipnat.rules</filename>. See &man.ipnat.8; for
details.</para>
<para>When the file containing the <acronym>NAT</acronym> rules
is edited after <acronym>NAT</acronym> has been started, run
<command>ipnat</command> with <option>-CF</option> to delete
the internal in use <acronym>NAT</acronym> rules and flush the
contents of the translation table of all active
entries.</para>
<para>To reload the <acronym>NAT</acronym> rules, issue a
command like this:</para>
<screen>&prompt.root; <userinput>ipnat -CF -f
/etc/ipnat.rules</userinput></screen>
<para>To display some <acronym>NAT</acronym> statistics, use
this command:</para>
<screen>&prompt.root; <userinput>ipnat -s</userinput></screen>
<para>To list the <acronym>NAT</acronym> table's current
mappings, use this command:</para>
<screen>&prompt.root; <userinput>ipnat -l</userinput></screen>
<para>To turn verbose mode on and display information relating
to rule processing and active rules/table entries:</para>
<screen>&prompt.root; <userinput>ipnat -v</userinput></screen>
</sect2>
<sect2>
<title>IP<acronym>NAT</acronym> Rules</title>
<para><acronym>NAT</acronym> rules are flexible and can
accomplish many different things to fit the needs of
commercial and home users.</para>
<para>The rule syntax presented here has been simplified to
what is most commonly used in a non-commercial environment.
For a complete rule syntax description, refer to
&man.ipnat.5;.</para>
<para>The syntax for a <acronym>NAT</acronym> rule looks like
this:</para>
<programlisting>map <replaceable>IF</replaceable> <replaceable>LAN_IP_RANGE</replaceable> -&gt; <replaceable>PUBLIC_ADDRESS</replaceable></programlisting>
<para>The keyword <literal>map</literal> starts the rule.</para>
<para>Replace <replaceable>IF</replaceable> with the external
interface.</para>
<para>The <replaceable>LAN_IP_RANGE</replaceable> is used by the
internal clients use for IP Addressing. Usually, this is
something like <hostid
role="ipaddr">192.168.1.0/24</hostid>.</para>
<para>The <replaceable>PUBLIC_ADDRESS</replaceable> can either
be the static external IP address or the special keyword
<literal>0/32</literal> which uses the IP address assigned to
<replaceable>IF</replaceable>.</para>
</sect2>
<sect2>
<title>How <acronym>NAT</acronym> Works</title>
<para>In IPF, when a packet arrives at the firewall from the LAN
with a public destination, it passes through the outbound
filter rules. <acronym>NAT</acronym> gets its turn at the
packet and applies its rules top down, where the first
matching rule wins. <acronym>NAT</acronym> tests each of its
rules against the packet's interface name and source IP
address. When a packet's interface name matches a
<acronym>NAT</acronym> rule, the packet's source IP address in
the private LAN is checked to see if it falls within the IP
address range specified to the left of the arrow symbol on the
<acronym>NAT</acronym> rule. On a match, the packet has its
source IP address rewritten with the public IP address
obtained by the <literal>0/32</literal> keyword.
<acronym>NAT</acronym> posts an entry in its internal
<acronym>NAT</acronym> table so when the packet returns from
the public Internet it can be mapped back to its original
private IP address and then passed to the filter rules for
processing.</para>
</sect2>
<sect2>
<title>Enabling IP<acronym>NAT</acronym></title>
<para>To enable IP<acronym>NAT</acronym>, add these statements
to <filename>/etc/rc.conf</filename>.</para>
<para>To enable the machine to route traffic between
interfaces:</para>
<programlisting>gateway_enable="YES"</programlisting>
<para>To start IP<acronym>NAT</acronym> automatically each
time:</para>
<programlisting>ipnat_enable="YES"</programlisting>
<para>To specify where to load the IP<acronym>NAT</acronym>
rules from:</para>
<programlisting>ipnat_rules="/etc/ipnat.rules"</programlisting>
</sect2>
<sect2>
<title><acronym>NAT</acronym> for a Large LAN</title>
<para>For networks that have large numbers of systems on the LAN
or networks with more than a single LAN, the process of
funneling all those private IP addresses into a single public
IP address becomes a resource problem that may cause problems
with the same port numbers being used many times across many
connections, causing collisions. There are two ways to
relieve this resource problem.</para>
<sect3>
<title>Assigning Ports to Use</title>
<para>A normal NAT rule would look like:</para>
<programlisting>map dc0 192.168.1.0/24 -&gt; 0/32</programlisting>
<para>In the above rule, the packet's source port is unchanged
as the packet passes through IP<acronym>NAT</acronym>. By
adding the <literal>portmap</literal> keyword,
IP<acronym>NAT</acronym> can be directed to only use
source ports in the specified range. For example, the
following rule will tell IP<acronym>NAT</acronym> to modify
the source port to be within the range shown:</para>
<programlisting>map dc0 192.168.1.0/24 -&gt; 0/32 portmap tcp/udp 20000:60000</programlisting>
<para>Additionally, the <literal>auto</literal> keyword tells
IP<acronym>NAT</acronym> to determine which ports are
available for use:</para>
<programlisting>map dc0 192.168.1.0/24 -&gt; 0/32 portmap tcp/udp auto</programlisting>
</sect3>
<sect3>
<title>Using a Pool of Public Addresses</title>
<para>In very large LANs there comes a point where there are
just too many LAN addresses to fit into a single public
address. If a block of public IP addresses is available,
these addresses can be used as a <quote>pool</quote>, and
IP<acronym>NAT</acronym> may pick one of the public IP
addresses as packet addresses are mapped on their way
out.</para>
<para>For example, instead of mapping all packets through a
single public IP address:</para>
<programlisting>map dc0 192.168.1.0/24 -&gt; 204.134.75.1</programlisting>
<para>A range of public IP addresses can be specified either
with a netmask:</para>
<programlisting>map dc0 192.168.1.0/24 -&gt; 204.134.75.0/255.255.255.0</programlisting>
<para>or using CIDR notation:</para>
<programlisting>map dc0 192.168.1.0/24 -&gt; 204.134.75.0/24</programlisting>
</sect3>
</sect2>
<sect2>
<title>Port Redirection</title>
<para>A common practice is to have a web server, email server,
database server, and DNS server each segregated to a different
system on the LAN. In this case, the traffic from these
servers still has to undergo <acronym>NAT</acronym>, but there
has to be some way to direct the inbound traffic to the
correct server. For example, a web server operating on LAN
address <hostid
role="ipaddr">10.0.10.25</hostid> and using a single public
IP address of <hostid role="ipaddr">20.20.20.5</hostid>, would
use this rule:</para>
<programlisting>rdr dc0 20.20.20.5/32 port 80 -&gt; 10.0.10.25 port 80</programlisting>
<para>or:</para>
<programlisting>rdr dc0 0.0.0.0/0 port 80 -&gt; 10.0.10.25 port 80</programlisting>
<para>For a LAN DNS server on a private address of <hostid
role="ipaddr">10.0.10.33</hostid> that needs to receive
public DNS requests:</para>
<programlisting>rdr dc0 20.20.20.5/32 port 53 -&gt; 10.0.10.33 port 53 udp</programlisting>
</sect2>
<sect2>
<title>FTP and <acronym>NAT</acronym></title>
<para>FTP has two modes: active mode and passive mode. The
difference is in how the data channel is acquired. Passive
mode is more secure as the data channel is acquired by the
ordinal ftp session requester. For a good explanation of FTP
and the different modes, see <ulink
url="http://www.slacksite.com/other/ftp.html"></ulink>.</para>
<sect3>
<title>IP<acronym>NAT</acronym> Rules</title>
<para>IP<acronym>NAT</acronym> has a built in FTP proxy option
which can be specified on the <acronym>NAT</acronym> map
rule. It can monitor all outbound packet traffic for FTP
active or passive start session requests and dynamically
create temporary filter rules containing the port number
being used by the data channel. This eliminates the
security risk FTP normally exposes the firewall to as it no
longer needs to open large ranges of high order ports for
FTP connections.</para>
<para>This rule will handle all the traffic for the internal
LAN:</para>
<programlisting>map dc0 10.0.10.0/29 -&gt; 0/32 proxy port 21 ftp/tcp</programlisting>
<para>This rule handles the FTP traffic from the
gateway:</para>
<programlisting>map dc0 0.0.0.0/0 -&gt; 0/32 proxy port 21 ftp/tcp</programlisting>
<para>This rule handles all non-FTP traffic from the internal
LAN:</para>
<programlisting>map dc0 10.0.10.0/29 -&gt; 0/32</programlisting>
<para>The FTP <literal>map</literal> rules go before the
<acronym>NAT</acronym> rule so that when a packet matches an
FTP rule, the FTP proxy creates temporary filter rules to
let the FTP session packets pass and undergo
<acronym>NAT</acronym>. All LAN packets that are not FTP
will not match the FTP rules but will undergo
<acronym>NAT</acronym> if they match the third rule.</para>
</sect3>
<sect3>
<title>IP<acronym>NAT</acronym> FTP Filter Rules</title>
<para>Only one filter rule is needed for FTP if the
<acronym>NAT</acronym> FTP proxy is used.</para>
<para>Without the FTP proxy, the following three rules will be
needed:</para>
<programlisting># Allow out LAN PC client FTP to public Internet
# Active and passive modes
pass out quick on rl0 proto tcp from any to any port = 21 flags S keep state
# Allow out passive mode data channel high order port numbers
pass out quick on rl0 proto tcp from any to any port &gt; 1024 flags S keep state
# Active mode let data channel in from FTP server
pass in quick on rl0 proto tcp from any to any port = 20 flags S keep state</programlisting>
</sect3>
</sect2>
</sect1>
<sect1 id="firewalls-ipfw">
<title>IPFW</title>
<indexterm>
<primary>firewall</primary>
<secondary>IPFW</secondary>
</indexterm>
<para><acronym>IPFW</acronym> is a stateful firewall written for
&os; which also provides a traffic shaper, packet scheduler,
and in-kernel NAT.</para>
<para>&os; provides a sample ruleset in
<filename>/etc/rc.firewall</filename>. The sample ruleset
define several firewall types for common scenarios to assist
novice users in generating an appropriate ruleset.
&man.ipfw.8; provides a powerful syntax which advanced users can
use to craft customized rulesets that meet the security
requirements of a given environment.</para>
<para>IPFW is composed of several components: the kernel firewall
filter rule processor and its integrated packet accounting
facility, the logging facility, the
<literal>divert</literal> rule which triggers
<acronym>NAT</acronym>, the dummynet traffic shaper facilities,
the <literal>fwd rule</literal> forward facility, the bridge
facility, and the ipstealth facility. IPFW supports both IPv4
and IPv6.</para>
<sect2 id="firewalls-ipfw-enable">
<title>Enabling IPFW</title>
<indexterm>
<primary>IPFW</primary>
<secondary>enabling</secondary>
</indexterm>
<para>IPFW is included in the basic &os; install as a run time
loadable module. The system will dynamically load the kernel
module when <filename>rc.conf</filename> contains the
statement <literal>firewall_enable="YES"</literal>. After
rebooting the system, the following white highlighted message
is displayed on the screen as part of the boot process:</para>
<screen>ipfw2 initialized, divert disabled, rule-based forwarding disabled, default to deny, logging disabled</screen>
<para>The loadable module includes logging ability. To enable
logging and set the verbose logging limit, add these
statements to
<filename>/etc/sysctl.conf</filename> before rebooting:</para>
<programlisting>net.inet.ip.fw.verbose=1
net.inet.ip.fw.verbose_limit=5</programlisting>
</sect2>
<sect2 id="firewalls-ipfw-kernel">
<title>Kernel Options</title>
<indexterm>
<primary>kernel options</primary>
<secondary>IPFIREWALL</secondary>
</indexterm>
<indexterm>
<primary>kernel options</primary>
<secondary>IPFIREWALL_VERBOSE</secondary>
</indexterm>
<indexterm>
<primary>kernel options</primary>
<secondary>IPFIREWALL_VERBOSE_LIMIT</secondary>
</indexterm>
<indexterm>
<primary>IPFW</primary>
<secondary>kernel options</secondary>
</indexterm>
<para>For those users who wish to statically compile kernel
IPFW support, the following options are available for the
custom kernel configuration file:</para>
<programlisting>options IPFIREWALL</programlisting>
<para>This option enables IPFW as part of the kernel.</para>
<programlisting>options IPFIREWALL_VERBOSE</programlisting>
<para>This option enables logging of packets that pass through
IPFW and have the <literal>log</literal> keyword specified in
the ruleset.</para>
<programlisting>options IPFIREWALL_VERBOSE_LIMIT=5</programlisting>
<para>This option limits the number of packets logged through
&man.syslogd.8;, on a per-entry basis. This option may be
used in hostile environments, when firewall activity logging
is desired. This will close a possible denial of service
attack via syslog flooding.</para>
<indexterm>
<primary>kernel options</primary>
<secondary>IPFIREWALL_DEFAULT_TO_ACCEPT</secondary>
</indexterm>
<programlisting>options IPFIREWALL_DEFAULT_TO_ACCEPT</programlisting>
<para>This option allows everything to pass through the firewall
by default, which is a good idea when the firewall is being
set up for the first time.</para>
<indexterm>
<primary>kernel options</primary>
<secondary>IPDIVERT</secondary>
</indexterm>
<programlisting>options IPDIVERT</programlisting>
<para>This option enables the use of <acronym>NAT</acronym>
functionality.</para>
<note>
<para>The firewall will block all incoming and outgoing
packets if either the
<literal>IPFIREWALL_DEFAULT_TO_ACCEPT</literal> kernel
option or a rule to explicitly allow these connections is
missing.</para>
</note>
</sect2>
<sect2 id="firewalls-ipfw-rc">
<title><filename>/etc/rc.conf</filename> Options</title>
<para>Enables the firewall:</para>
<programlisting>firewall_enable="YES"</programlisting>
<para>To select one of the default firewall types provided by
&os;, select one by reading
<filename>/etc/rc.firewall</filename> and specify it in
the following:</para>
<programlisting>firewall_type="open"</programlisting>
<para>Available values for this setting are:</para>
<itemizedlist>
<listitem>
<para><literal>open</literal>: passes all traffic.</para>
</listitem>
<listitem>
<para><literal>client</literal>: protects only this
machine.</para>
</listitem>
<listitem>
<para><literal>simple</literal>: protects the whole
network.</para>
</listitem>
<listitem>
<para><literal>closed</literal>: entirely disables IP
traffic except for the loopback interface.</para>
</listitem>
<listitem>
<para><literal>UNKNOWN</literal>: disables the loading of
firewall rules.</para>
</listitem>
<listitem>
<para><filename><replaceable>filename</replaceable></filename>:
absolute path of the file containing the firewall
rules.</para>
</listitem>
</itemizedlist>
<para>Two methods are available for loading custom
<application>ipfw</application> rules. One is to set the
<literal>firewall_type</literal> variable to the absolute
path of the file which contains the firewall rules.</para>
<para>The other method is to set the
<literal>firewall_script</literal> variable to the absolute
path of an executable script that includes
<command>ipfw</command> commands. A ruleset script that
blocks all incoming and outgoing traffic would look like
this:</para>
<programlisting>#!/bin/sh
ipfw -q flush
ipfw add deny in
ipfw add deny out</programlisting>
<note>
<para>If <literal>firewall_type</literal> is set to either
<literal>client</literal> or <literal>simple</literal>,
modify the default rules found in
<filename>/etc/rc.firewall</filename> to fit the
configuration of the system. The examples used in this
section assume that the <literal>firewall_script</literal>
is set to <filename>/etc/ipfw.rules</filename>.</para>
</note>
<para>Enable logging:</para>
<programlisting>firewall_logging="YES"</programlisting>
<warning>
<para><varname>firewall_logging</varname> sets the
<varname>net.inet.ip.fw.verbose</varname> sysctl
variable to the value of <literal>1</literal>. There is no
<filename>rc.conf</filename> variable to set log
limitations, but the desired value can be set using
<command>sysctl</command> or by adding the following
variable and desired value to
<filename>/etc/sysctl.conf</filename>:</para>
<programlisting>net.inet.ip.fw.verbose_limit=5</programlisting>
</warning>
<para>If the machine is acting as a gateway providing
<acronym>NAT</acronym> using &man.natd.8;,
refer to <xref linkend="network-natd"/> for information
regarding the required <filename>/etc/rc.conf</filename>
options.</para>
</sect2>
<sect2 id="firewalls-ipfw-cmd">
<title>The IPFW Command</title>
<indexterm><primary><command>ipfw</command></primary></indexterm>
<para><command>ipfw</command> can be used to make manual,
single rule additions or deletions to the active firewall
while it is running. The problem with using this method is
that all the changes are lost when the system reboots. It is
recommended to instead write all the rules in a file and to
use that file to load the rules at boot time and to replace
the currently running firewall rules whenever that file
changes.</para>
<para><command>ipfw</command> is a useful way to display the
running firewall rules to the console screen. The IPFW
accounting facility dynamically creates a counter for each
rule that counts each packet that matches the rule. During
the process of testing a rule, listing the rule with its
counter is one way to determine if the rule is
functioning as expected.</para>
<para>To list all the running rules in sequence:</para>
<screen>&prompt.root; <userinput>ipfw list</userinput></screen>
<para>To list all the running rules with a time stamp of when
the last time the rule was matched:</para>
<screen>&prompt.root; <userinput>ipfw -t list</userinput></screen>
<para>The next example lists accounting information and the
packet count for matched rules along with the rules
themselves. The first column is the rule number, followed by
the number of matched packets and bytes, followed by the rule
itself.</para>
<screen>&prompt.root; <userinput>ipfw -a list</userinput></screen>
<para>To list dynamic rules in addition to static rules:</para>
<screen>&prompt.root; <userinput>ipfw -d list</userinput></screen>
<para>To also show the expired dynamic rules:</para>
<screen>&prompt.root; <userinput>ipfw -d -e list</userinput></screen>
<para>To zero the counters:</para>
<screen>&prompt.root; <userinput>ipfw zero</userinput></screen>
<para>To zero the counters for just the rule with number
<replaceable>NUM</replaceable>:</para>
<screen>&prompt.root; <userinput>ipfw zero <replaceable>NUM</replaceable></userinput></screen>
</sect2>
<sect2 id="firewalls-ipfw-rules">
<title>IPFW Rulesets</title>
<indexterm>
<primary>IPFW</primary>
<secondary>rule processing order</secondary>
</indexterm>
<para>When a packet enters the <acronym>IPFW</acronym> firewall,
it is compared against the first rule in the ruleset and
progresses one rule at a time, moving from top to bottom of
the set in ascending rule number sequence order. When the
packet matches the selection parameters of a rule, the rule's
action field value is executed and the search of the ruleset
terminates for that packet. This is referred to as
<quote>first match wins</quote>. If the packet does not match
any of the rules, it gets caught by the mandatory IPFW default
rule, number 65535, which denies all packets and silently
discards them. However, if the packet matches a rule that
contains the <literal>count</literal>,
<literal>skipto</literal>, or <literal>tee</literal> keywords,
the search continues. Refer to &man.ipfw.8; for details on
how these keywords affect rule processing.</para>
<para>The examples in this section create an inclusive type
firewall ruleset containing the stateful <literal>keep
state</literal>, <literal>limit</literal>,
<literal>in</literal>, <literal>out</literal> and
<literal>via</literal> options. For a complete rule syntax
description, refer to &man.ipfw.8;.</para>
<warning>
<para>Be careful when working with firewall rules, as it is
easy to lock out even the administrator.</para>
</warning>
<sect3 id="firewalls-ipfw-rules-syntax">
<title>Rule Syntax</title>
<indexterm>
<primary>IPFW</primary>
<secondary>rule syntax</secondary>
</indexterm>
<para>This section describes the keywords which comprise an
<acronym>IPFW</acronym> rule. Keywords must be written in
the following order. <literal>#</literal> is used to mark
the start of a comment and may appear at the end of a rule
line or on its own line. Blank lines are ignored.</para>
<para><replaceable>CMD RULE_NUMBER ACTION LOGGING SELECTION
STATEFUL</replaceable></para>
<sect4>
<title>CMD</title>
<para>Each new rule has to be prefixed with
<parameter>add</parameter> to add the rule to the internal
table.</para>
</sect4>
<sect4>
<title>RULE_NUMBER</title>
<para>Each rule is associated with a rule_number in the
range of <literal>1</literal> to
<literal>65535</literal>.</para>
</sect4>
<sect4>
<title>ACTION</title>
<para>A rule can be associated with one of the following
actions. The specified action will be executed when the
packet matches the selection criterion of the rule.</para>
<para><parameter>allow | accept | pass |
permit</parameter></para>
<para>These keywords are equivalent as they allow packets
that match the rule to exit the firewall rule processing.
The search terminates at this rule.</para>
<para><parameter>check-state</parameter></para>
<para>Checks the packet against the dynamic rules table.
If a match is found, execute the action associated with
the rule which generated this dynamic rule, otherwise
move to the next rule. A <literal>check-state</literal>
rule does not have selection criterion. If no
<literal>check-state</literal> rule is present in the
ruleset, the dynamic rules table is checked at the first
<literal>keep-state</literal> or <literal>limit</literal>
rule.</para>
<para><parameter>deny | drop</parameter></para>
<para>Both words mean the same thing, which is to discard
packets that match this rule. The search
terminates.</para>
</sect4>
<sect4>
<title>Logging</title>
<para>When a packet matches a rule with the
<literal>log</literal> keyword, a message will be logged
to &man.syslogd.8; with a facility name of
<literal>SECURITY</literal>. Logging only occurs if the
number of packets logged for that particular rule does not
exceed the <literal>logamount</literal> parameter. If no
<literal>logamount</literal> is specified, the limit is
taken from the <command>sysctl</command> value of
<varname>net.inet.ip.fw.verbose_limit</varname>. In both
cases, a value of zero removes the logging limit. Once
the limit is reached, logging can be re-enabled by
clearing the logging counter or the packet counter for
that rule, using <command>ipfw reset log</command>.</para>
<note>
<para>Logging is done after all other packet matching
conditions have been met, and before performing the
final action on the packet. The administrator decides
which rules to enable logging on.</para>
</note>
</sect4>
<sect4>
<title>Selection</title>
<para>The keywords described in this section are used to
describe attributes of the packet to be checked when
determining whether rules match the packet or not.
The following general-purpose attributes are provided for
matching, and must be used in this order:</para>
<para><parameter>udp | tcp | icmp</parameter></para>
<para>Any other protocol names found in
<filename>/etc/protocols</filename> can be used. The
value specified is the protocol to be matched against.
This is a mandatory keyword.</para>
<para><parameter>from src to dst</parameter></para>
<para>The <literal>from</literal> and <literal>to</literal>
keywords are used to match against IP addresses. Rules
must specify <emphasis>both</emphasis> source and
destination parameters. <literal>any</literal> is a
special keyword that matches any IP address.
<literal>me</literal> is a special keyword that matches
any IP address configured on an interface in the &os;
system to represent the PC the firewall is running on.
Example usage includes <literal>from me to any</literal>,
<literal>from any to me</literal>, <literal>from 0.0.0.0/0
to any</literal>, <literal>from any to
0.0.0.0/0</literal>, <literal>from 0.0.0.0 to
any</literal>. <literal>from any to 0.0.0.0</literal>,
and <literal>from me to 0.0.0.0</literal>. IP addresses
are specified in dotted IP address format followed by the
mask in CIDR notation, or as a single host in dotted IP
address format. This keyword is a mandatory requirement.
The <filename role="package">net-mgmt/ipcalc</filename>
port may be used to assist the mask calculation.</para>
<para><parameter>port number</parameter></para>
<para>For protocols which support port numbers, such as
<acronym>TCP</acronym> and <acronym>UDP</acronym>, it
is mandatory to include the port number of the service
that will be matched. Service names from
<filename>/etc/services</filename> may be used instead
of numeric port values.</para>
<para><parameter>in | out</parameter></para>
<para>Matches incoming or outgoing packets. It is mandatory
that one or the other is included as part of the rule
matching criterion.</para>
<para><parameter>via IF</parameter></para>
<para>Matches packets going through the interface specified
by device name. The <literal>via</literal> keyword causes
the interface to always be checked as part of the match
process.</para>
<para><parameter>setup</parameter></para>
<para>This mandatory keyword identifies the session start
request for <acronym>TCP</acronym> packets.</para>
<para><parameter>keep-state</parameter></para>
<para>This is a mandatory keyword. Upon a match, the
firewall will create a dynamic rule, whose default
behavior is to match bidirectional traffic between source
and destination IP/port using the same protocol.</para>
<para><parameter>limit {src-addr | src-port | dst-addr |
dst-port}</parameter></para>
<para>The firewall will only allow
<replaceable>N</replaceable> connections with the same
set of parameters as specified in the rule. One or more
of source and destination addresses and ports can be
specified. <literal>limit</literal> and
<literal>keep-state</literal> can not be used on the same
rule as they provide the same stateful function.</para>
</sect4>
</sect3>
<sect3>
<title>Stateful Rule Option</title>
<indexterm>
<primary>IPFW</primary>
<secondary>stateful filtering</secondary>
</indexterm>
<para>The <literal>check-state</literal> option is used to
identify where in the IPFW ruleset the packet is to be
tested against the dynamic rules facility. On a match, the
packet exits the firewall to continue on its way and a new
rule is dynamically created for the next anticipated packet
being exchanged during this session. On a no match, the
packet advances to the next rule in the ruleset for
testing.</para>
<para>The dynamic rules facility is vulnerable to resource
depletion from a SYN-flood attack which would open a huge
number of dynamic rules. To counter this type of attack
with <acronym>IPFW</acronym>, use <literal>limit</literal>.
This keyword limits the number of simultaneous sessions by
checking that rule's source or destinations fields and using
the packet's IP address in a search of the open dynamic
rules, counting the number of times this rule and IP address
combination occurred. If this count is greater than the
value specified by <literal>limit</literal>, the packet is
discarded.</para>
</sect3>
<sect3>
<title>Logging Firewall Messages</title>
<indexterm>
<primary>IPFW</primary>
<secondary>logging</secondary>
</indexterm>
<para>Even with the logging facility enabled, IPFW will not
generate any rule logging on its own. The firewall
administrator decides which rules in the ruleset will be
logged, and adds the <literal>log</literal> keyword to those
rules. Normally only deny rules are logged. It is
customary to duplicate the <quote>ipfw default deny
everything</quote> rule with the <literal>log</literal>
keyword included as the last rule in the ruleset. This
way, it is possible to see all the packets that did not
match any of the rules in the ruleset.</para>
<para>Logging is a two edged sword. If one is not careful,
an over abundance of log data or a DoS attack can fill the
disk with log files. Log messages are not only written to
<application>syslogd</application>, but also are displayed
on the root console screen and soon become annoying.</para>
<para>The <literal>IPFIREWALL_VERBOSE_LIMIT=5</literal>
kernel option limits the number of consecutive messages
sent to &man.syslogd.8;, concerning the packet matching of a
given rule. When this option is enabled in the kernel, the
number of consecutive messages concerning a particular rule
is capped at the number specified. There is nothing to be
gained from 200 identical log messages. With this option
set to five,
five consecutive messages concerning a particular rule
would be logged to <application>syslogd</application> and
the remainder identical consecutive messages would be
counted and posted to <application>syslogd</application>
with a phrase like the following:</para>
<programlisting>last message repeated 45 times</programlisting>
<para>All logged packets messages are written by default to
<filename>/var/log/security</filename>, which is
defined in <filename>/etc/syslog.conf</filename>.</para>
</sect3>
<sect3 id="firewalls-ipfw-rules-script">
<title>Building a Rule Script</title>
<para>Most experienced IPFW users create a file containing
the rules and code them in a manner compatible with running
them as a script. The major benefit of doing this is the
firewall rules can be refreshed in mass without the need
of rebooting the system to activate them. This method is
convenient in testing new rules as the procedure can
be executed as many times as needed. Being a script,
symbolic substitution can be used for frequently used
values to be substituted into multiple rules.</para>
<para>This example script is compatible with the syntax used
by the &man.sh.1;, &man.csh.1;, and &man.tcsh.1; shells.
Symbolic substitution fields are prefixed with a dollar sign
(&dollar;). Symbolic fields do not have the &dollar;
prefix. The value to populate the symbolic field must be
enclosed in double quotes ("").</para>
<para>Start the rules file like this:</para>
<programlisting>############### start of example ipfw rules script #############
#
ipfw -q -f flush # Delete all rules
# Set defaults
oif="tun0" # out interface
odns="192.0.2.11" # ISP's DNS server IP address
cmd="ipfw -q add " # build rule prefix
ks="keep-state" # just too lazy to key this each time
&dollar;cmd 00500 check-state
&dollar;cmd 00502 deny all from any to any frag
&dollar;cmd 00501 deny tcp from any to any established
&dollar;cmd 00600 allow tcp from any to any 80 out via &dollar;oif setup &dollar;ks
&dollar;cmd 00610 allow tcp from any to &dollar;odns 53 out via &dollar;oif setup &dollar;ks
&dollar;cmd 00611 allow udp from any to &dollar;odns 53 out via &dollar;oif &dollar;ks
################### End of example ipfw rules script ############</programlisting>
<para>The rules are not important as the focus of this example
is how the symbolic substitution fields are
populated.</para>
<para>If the above example was in
<filename>/etc/ipfw.rules</filename>, the rules could be
reloaded by the following command:</para>
<screen>&prompt.root; <userinput>sh /etc/ipfw.rules</userinput></screen>
<para><filename>/etc/ipfw.rules</filename> can be located
anywhere and the file can have any name.</para>
<para>The same thing could be accomplished by running these
commands by hand:</para>
<screen>&prompt.root; <userinput>ipfw -q -f flush</userinput>
&prompt.root; <userinput>ipfw -q add check-state</userinput>
&prompt.root; <userinput>ipfw -q add deny all from any to any frag</userinput>
&prompt.root; <userinput>ipfw -q add deny tcp from any to any established</userinput>
&prompt.root; <userinput>ipfw -q add allow tcp from any to any 80 out via tun0 setup keep-state</userinput>
&prompt.root; <userinput>ipfw -q add allow tcp from any to 192.0.2.11 53 out via tun0 setup keep-state</userinput>
&prompt.root; <userinput>ipfw -q add 00611 allow udp from any to 192.0.2.11 53 out via tun0 keep-state</userinput></screen>
</sect3>
<sect3>
<title>An Example Stateful Ruleset</title>
<para>The following sample ruleset is a complete inclusive
type ruleset. Comment out any
<literal>pass</literal> rules for services that are not
required. To avoid logging undesired messages, add a
<literal>deny</literal> rule in the inbound section.
Change the <devicename>dc0</devicename> in every rule to the
device name of the interface that connects the system to the
Internet.</para>
<para>There is a noticeable pattern in the usage of these
rules.</para>
<itemizedlist>
<listitem>
<para>All statements that are a request to start a session
to the Internet use
<literal>keep-state</literal>.</para>
</listitem>
<listitem>
<para>All the authorized services that originate from
the Internet use <literal>limit</literal> to prevent
flooding.</para>
</listitem>
<listitem>
<para>All rules use <literal>in</literal> or
<literal>out</literal> to clarify direction.</para>
</listitem>
<listitem>
<para>All rules use <literal>via</literal>
<replaceable>interface-name</replaceable> to specify
the interface the packet is traveling over.</para>
</listitem>
</itemizedlist>
<para>The following rules go into
<filename>/etc/ipfw.rules</filename>:</para>
<programlisting>################ Start of IPFW rules file ###############################
# Flush out the list before we begin.
ipfw -q -f flush
# Set rules command prefix
cmd="ipfw -q add"
pif="dc0" # public interface name of NIC
# facing the public Internet
#################################################################
# No restrictions on Inside LAN Interface for private network
# Not needed unless you have LAN.
# Change xl0 to your LAN NIC interface name
#################################################################
#&dollar;cmd 00005 allow all from any to any via xl0
#################################################################
# No restrictions on Loopback Interface
#################################################################
&dollar;cmd 00010 allow all from any to any via lo0
#################################################################
# Allow the packet through if it has previous been added to the
# the "dynamic" rules table by a allow keep-state statement.
#################################################################
&dollar;cmd 00015 check-state
#################################################################
# Interface facing Public Internet (Outbound Section)
# Interrogate session start requests originating from behind the
# firewall on the private network or from this gateway server
# destined for the public Internet.
#################################################################
# Allow out access to my ISP's Domain name server.
# x.x.x.x must be the IP address of your ISP.s DNS
# Dup these lines if your ISP has more than one DNS server
# Get the IP addresses from /etc/resolv.conf file
&dollar;cmd 00110 allow tcp from any to x.x.x.x 53 out via &dollar;pif setup keep-state
&dollar;cmd 00111 allow udp from any to x.x.x.x 53 out via &dollar;pif keep-state
# Allow out access to my ISP's DHCP server for cable/DSL configurations.
# This rule is not needed for .user ppp. connection to the public Internet.
# so you can delete this whole group.
# Use the following rule and check log for IP address.
# Then put IP address in commented out rule &amp; delete first rule
&dollar;cmd 00120 allow log udp from any to any 67 out via &dollar;pif keep-state
#&dollar;cmd 00120 allow udp from any to x.x.x.x 67 out via &dollar;pif keep-state
# Allow out non-secure standard www function
&dollar;cmd 00200 allow tcp from any to any 80 out via &dollar;pif setup keep-state
# Allow out secure www function https over TLS SSL
&dollar;cmd 00220 allow tcp from any to any 443 out via &dollar;pif setup keep-state
# Allow out send &amp; get email function
&dollar;cmd 00230 allow tcp from any to any 25 out via &dollar;pif setup keep-state
&dollar;cmd 00231 allow tcp from any to any 110 out via &dollar;pif setup keep-state
# Allow out FBSD (make install &amp; CVSUP) functions
# Basically give user root "GOD" privileges.
&dollar;cmd 00240 allow tcp from me to any out via &dollar;pif setup keep-state uid root
# Allow out ping
&dollar;cmd 00250 allow icmp from any to any out via &dollar;pif keep-state
# Allow out Time
&dollar;cmd 00260 allow tcp from any to any 37 out via &dollar;pif setup keep-state
# Allow out nntp news (i.e., news groups)
&dollar;cmd 00270 allow tcp from any to any 119 out via &dollar;pif setup keep-state
# Allow out secure FTP, Telnet, and SCP
# This function is using SSH (secure shell)
&dollar;cmd 00280 allow tcp from any to any 22 out via &dollar;pif setup keep-state
# Allow out whois
&dollar;cmd 00290 allow tcp from any to any 43 out via &dollar;pif setup keep-state
# deny and log everything else that.s trying to get out.
# This rule enforces the block all by default logic.
&dollar;cmd 00299 deny log all from any to any out via &dollar;pif
#################################################################
# Interface facing Public Internet (Inbound Section)
# Check packets originating from the public Internet
# destined for this gateway server or the private network.
#################################################################
# Deny all inbound traffic from non-routable reserved address spaces
&dollar;cmd 00300 deny all from 192.168.0.0/16 to any in via &dollar;pif #RFC 1918 private IP
&dollar;cmd 00301 deny all from 172.16.0.0/12 to any in via &dollar;pif #RFC 1918 private IP
&dollar;cmd 00302 deny all from 10.0.0.0/8 to any in via &dollar;pif #RFC 1918 private IP
&dollar;cmd 00303 deny all from 127.0.0.0/8 to any in via &dollar;pif #loopback
&dollar;cmd 00304 deny all from 0.0.0.0/8 to any in via &dollar;pif #loopback
&dollar;cmd 00305 deny all from 169.254.0.0/16 to any in via &dollar;pif #DHCP auto-config
&dollar;cmd 00306 deny all from 192.0.2.0/24 to any in via &dollar;pif #reserved for docs
&dollar;cmd 00307 deny all from 204.152.64.0/23 to any in via &dollar;pif #Sun cluster interconnect
&dollar;cmd 00308 deny all from 224.0.0.0/3 to any in via &dollar;pif #Class D &amp; E multicast
# Deny public pings
&dollar;cmd 00310 deny icmp from any to any in via &dollar;pif
# Deny ident
&dollar;cmd 00315 deny tcp from any to any 113 in via &dollar;pif
# Deny all Netbios service. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
&dollar;cmd 00320 deny tcp from any to any 137 in via &dollar;pif
&dollar;cmd 00321 deny tcp from any to any 138 in via &dollar;pif
&dollar;cmd 00322 deny tcp from any to any 139 in via &dollar;pif
&dollar;cmd 00323 deny tcp from any to any 81 in via &dollar;pif
# Deny any late arriving packets
&dollar;cmd 00330 deny all from any to any frag in via &dollar;pif
# Deny ACK packets that did not match the dynamic rule table
&dollar;cmd 00332 deny tcp from any to any established in via &dollar;pif
# Allow traffic in from ISP's DHCP server. This rule must contain
# the IP address of your ISP.s DHCP server as it.s the only
# authorized source to send this packet type.
# Only necessary for cable or DSL configurations.
# This rule is not needed for .user ppp. type connection to
# the public Internet. This is the same IP address you captured
# and used in the outbound section.
#&dollar;cmd 00360 allow udp from any to x.x.x.x 67 in via &dollar;pif keep-state
# Allow in standard www function because I have apache server
&dollar;cmd 00400 allow tcp from any to me 80 in via &dollar;pif setup limit src-addr 2
# Allow in secure FTP, Telnet, and SCP from public Internet
&dollar;cmd 00410 allow tcp from any to me 22 in via &dollar;pif setup limit src-addr 2
# Allow in non-secure Telnet session from public Internet
# labeled non-secure because ID &amp; PW are passed over public
# Internet as clear text.
# Delete this sample group if you do not have telnet server enabled.
&dollar;cmd 00420 allow tcp from any to me 23 in via &dollar;pif setup limit src-addr 2
# Reject &amp; Log all incoming connections from the outside
&dollar;cmd 00499 deny log all from any to any in via &dollar;pif
# Everything else is denied by default
# deny and log all packets that fell through to see what they are
&dollar;cmd 00999 deny log all from any to any
################ End of IPFW rules file ###############################</programlisting>
</sect3>
<sect3>
<title>An Example <acronym>NAT</acronym> and Stateful
Ruleset</title>
<indexterm>
<primary>NAT</primary>
<secondary>and IPFW</secondary>
</indexterm>
<para>There are some additional configuration statements that
need to be enabled to activate the <acronym>NAT</acronym>
function of IPFW. For a customized kernel, the kernel
configuration file needs
<literal>option IPDIVERT</literal> added to the other
<literal>IPFIREWALL</literal> options.</para>
<para>In addition to the normal IPFW options in
<filename>/etc/rc.conf</filename>, the following are
needed:</para>
<programlisting>natd_enable="YES" # Enable <acronym>NAT</acronym>D function
natd_interface="rl0" # interface name of public Internet NIC
natd_flags="-dynamic -m" # -m = preserve port numbers if possible</programlisting>
<para>Utilizing stateful rules with a
<literal>divert natd</literal> rule complicates the ruleset
logic. The positioning of the
<literal>check-state</literal>, and
<literal>divert natd</literal> rules in the ruleset is
critical and a new action type is used, called
<literal>skipto</literal>. When using
<literal>skipto</literal>, it is mandatory that each rule is
numbered, so that the <literal>skipto</literal> rule knows
which rule to jump to.</para>
<para>The following is an uncommented example of a ruleset
which explains the sequence of the packet flow.</para>
<para>The processing flow starts with the first rule from the
top of the ruleset and progresses one rule at a time until
the end is reached or the packet matches and the packet is
released out of the firewall. Take note of the location of
rule numbers 100 101, 450, 500, and 510. These rules
control the translation of the outbound and inbound packets
so that their entries in the dynamic keep-state table always
register the private LAN IP address. All the allow and deny
rules specify the direction of the packet and the interface.
All start outbound session requests will
<literal>skipto rule 500</literal> to undergo NAT.</para>
<para>Consider a web browser which initializes a new HTTP
session over port 80. When the first outbound packet enters
the firewall, it does not match rule 100 because it is
headed out rather than in. It passes rule 101 because this
is the first packet, and it has not been posted to the
dynamic keep-state table yet. The packet finally matches
rule 125 as it is outbound through the NIC facing the
Internet and has a source IP address as a private LAN IP
address. On matching this rule, two actions take place.
<literal>keep-state</literal> adds this rule to the dynamic
keep-state rules table and the specified action is executed
and posted as part of the info in the dynamic table. In
this case, the action is <literal>skipto rule 500</literal>.
Rule 500 <acronym>NAT</acronym>s the packet IP address and
sends it out to the Internet. This packet makes its way to
the destination web server, where a response packet is
generated and sent back. This new packet enters the top of
the ruleset. It matches rule 100 and has it destination IP
address mapped back to the corresponding LAN IP address. It
then is processed by the <literal>check-state</literal>
rule, is found in the table as an existing session, and is
released to the LAN. It goes to the LAN system that sent it
and a new packet is sent requesting another segment of the
data from the remote server. This time it matches the
<literal>check-state</literal> rule, its outbound entry is
found, and the associated action,
<literal>skipto 500</literal>, is executed. The packet
jumps to rule 500, gets <acronym>NAT</acronym>ed, and is
released to the Internet.</para>
<para>On the inbound side, everything coming in that is part
of an existing session is automatically handled by the
<literal>check-state</literal> rule and the properly placed
<literal>divert natd</literal> rules. The ruleset only has
to deny bad packets and allow only authorized services.
Consider a web server running on the firewall where web
requests from the Internet should have access to the local
web site. An inbound start request packet will match rule
100 and its IP address will be mapped to the LAN IP address
of the firewall. The packet is then matched against all the
nasty things that need to be checked and finally matches
rule 425 where two actions occur. The packet rule is posted
to the dynamic keep-state table but this time, any new
session requests originating from that source IP address are
limited to 2. This defends against DoS attacks against the
service running on the specified port number. The action is
<literal>allow</literal>, so the packet is released to the
LAN. The packet generated as a response is recognized by the
<literal>check-state</literal> as belonging to an existing
session. It is then sent to rule 500 for
<acronym>NAT</acronym>ing and released to the outbound
interface.</para>
<para>Example Ruleset #1:</para>
<programlisting>#!/bin/sh
cmd="ipfw -q add"
skip="skipto 500"
pif=rl0
ks="keep-state"
good_tcpo="22,25,37,43,53,80,443,110,119"
ipfw -q -f flush
&dollar;cmd 002 allow all from any to any via xl0 # exclude LAN traffic
&dollar;cmd 003 allow all from any to any via lo0 # exclude loopback traffic
&dollar;cmd 100 divert natd ip from any to any in via &dollar;pif
&dollar;cmd 101 check-state
# Authorized outbound packets
&dollar;cmd 120 &dollar;skip udp from any to xx.168.240.2 53 out via &dollar;pif &dollar;ks
&dollar;cmd 121 &dollar;skip udp from any to xx.168.240.5 53 out via &dollar;pif &dollar;ks
&dollar;cmd 125 &dollar;skip tcp from any to any &dollar;good_tcpo out via &dollar;pif setup &dollar;ks
&dollar;cmd 130 &dollar;skip icmp from any to any out via &dollar;pif &dollar;ks
&dollar;cmd 135 &dollar;skip udp from any to any 123 out via &dollar;pif &dollar;ks
# Deny all inbound traffic from non-routable reserved address spaces
&dollar;cmd 300 deny all from 192.168.0.0/16 to any in via &dollar;pif #RFC 1918 private IP
&dollar;cmd 301 deny all from 172.16.0.0/12 to any in via &dollar;pif #RFC 1918 private IP
&dollar;cmd 302 deny all from 10.0.0.0/8 to any in via &dollar;pif #RFC 1918 private IP
&dollar;cmd 303 deny all from 127.0.0.0/8 to any in via &dollar;pif #loopback
&dollar;cmd 304 deny all from 0.0.0.0/8 to any in via &dollar;pif #loopback
&dollar;cmd 305 deny all from 169.254.0.0/16 to any in via &dollar;pif #DHCP auto-config
&dollar;cmd 306 deny all from 192.0.2.0/24 to any in via &dollar;pif #reserved for docs
&dollar;cmd 307 deny all from 204.152.64.0/23 to any in via &dollar;pif #Sun cluster
&dollar;cmd 308 deny all from 224.0.0.0/3 to any in via &dollar;pif #Class D &amp; E multicast
# Authorized inbound packets
&dollar;cmd 400 allow udp from xx.70.207.54 to any 68 in &dollar;ks
&dollar;cmd 420 allow tcp from any to me 80 in via &dollar;pif setup limit src-addr 1
&dollar;cmd 450 deny log ip from any to any
# This is skipto location for outbound stateful rules
&dollar;cmd 500 divert natd ip from any to any out via &dollar;pif
&dollar;cmd 510 allow ip from any to any
######################## end of rules ##################</programlisting>
<para>The next example is functionally equivalent, but uses
descriptive comments to help the inexperienced IPFW rule
writer to better understand what the rules are doing.</para>
<para>Example Ruleset #2:</para>
<programlisting>#!/bin/sh
################ Start of IPFW rules file ###############################
# Flush out the list before we begin.
ipfw -q -f flush
# Set rules command prefix
cmd="ipfw -q add"
skip="skipto 800"
pif="rl0" # public interface name of NIC
# facing the public Internet
#################################################################
# No restrictions on Inside LAN Interface for private network
# Change xl0 to your LAN NIC interface name
#################################################################
&dollar;cmd 005 allow all from any to any via xl0
#################################################################
# No restrictions on Loopback Interface
#################################################################
&dollar;cmd 010 allow all from any to any via lo0
#################################################################
# check if packet is inbound and nat address if it is
#################################################################
&dollar;cmd 014 divert natd ip from any to any in via &dollar;pif
#################################################################
# Allow the packet through if it has previous been added to the
# the "dynamic" rules table by a allow keep-state statement.
#################################################################
&dollar;cmd 015 check-state
#################################################################
# Interface facing Public Internet (Outbound Section)
# Check session start requests originating from behind the
# firewall on the private network or from this gateway server
# destined for the public Internet.
#################################################################
# Allow out access to my ISP's Domain name server.
# x.x.x.x must be the IP address of your ISP's DNS
# Dup these lines if your ISP has more than one DNS server
# Get the IP addresses from /etc/resolv.conf file
&dollar;cmd 020 &dollar;skip tcp from any to x.x.x.x 53 out via &dollar;pif setup keep-state
# Allow out access to my ISP's DHCP server for cable/DSL configurations.
&dollar;cmd 030 &dollar;skip udp from any to x.x.x.x 67 out via &dollar;pif keep-state
# Allow out non-secure standard www function
&dollar;cmd 040 &dollar;skip tcp from any to any 80 out via &dollar;pif setup keep-state
# Allow out secure www function https over TLS SSL
&dollar;cmd 050 &dollar;skip tcp from any to any 443 out via &dollar;pif setup keep-state
# Allow out send &amp; get email function
&dollar;cmd 060 &dollar;skip tcp from any to any 25 out via &dollar;pif setup keep-state
&dollar;cmd 061 &dollar;skip tcp from any to any 110 out via &dollar;pif setup keep-state
# Allow out FreeBSD (make install &amp; CVSUP) functions
# Basically give user root "GOD" privileges.
&dollar;cmd 070 &dollar;skip tcp from me to any out via &dollar;pif setup keep-state uid root
# Allow out ping
&dollar;cmd 080 &dollar;skip icmp from any to any out via &dollar;pif keep-state
# Allow out Time
&dollar;cmd 090 &dollar;skip tcp from any to any 37 out via &dollar;pif setup keep-state
# Allow out nntp news (i.e., news groups)
&dollar;cmd 100 &dollar;skip tcp from any to any 119 out via &dollar;pif setup keep-state
# Allow out secure FTP, Telnet, and SCP
# This function is using SSH (secure shell)
&dollar;cmd 110 &dollar;skip tcp from any to any 22 out via &dollar;pif setup keep-state
# Allow out whois
&dollar;cmd 120 &dollar;skip tcp from any to any 43 out via &dollar;pif setup keep-state
# Allow ntp time server
&dollar;cmd 130 &dollar;skip udp from any to any 123 out via &dollar;pif keep-state
#################################################################
# Interface facing Public Internet (Inbound Section)
# Check packets originating from the public Internet
# destined for this gateway server or the private network.
#################################################################
# Deny all inbound traffic from non-routable reserved address spaces
&dollar;cmd 300 deny all from 192.168.0.0/16 to any in via &dollar;pif #RFC 1918 private IP
&dollar;cmd 301 deny all from 172.16.0.0/12 to any in via &dollar;pif #RFC 1918 private IP
&dollar;cmd 302 deny all from 10.0.0.0/8 to any in via &dollar;pif #RFC 1918 private IP
&dollar;cmd 303 deny all from 127.0.0.0/8 to any in via &dollar;pif #loopback
&dollar;cmd 304 deny all from 0.0.0.0/8 to any in via &dollar;pif #loopback
&dollar;cmd 305 deny all from 169.254.0.0/16 to any in via &dollar;pif #DHCP auto-config
&dollar;cmd 306 deny all from 192.0.2.0/24 to any in via &dollar;pif #reserved for docs
&dollar;cmd 307 deny all from 204.152.64.0/23 to any in via &dollar;pif #Sun cluster
&dollar;cmd 308 deny all from 224.0.0.0/3 to any in via &dollar;pif #Class D &amp; E multicast
# Deny ident
&dollar;cmd 315 deny tcp from any to any 113 in via &dollar;pif
# Deny all Netbios service. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
&dollar;cmd 320 deny tcp from any to any 137 in via &dollar;pif
&dollar;cmd 321 deny tcp from any to any 138 in via &dollar;pif
&dollar;cmd 322 deny tcp from any to any 139 in via &dollar;pif
&dollar;cmd 323 deny tcp from any to any 81 in via &dollar;pif
# Deny any late arriving packets
&dollar;cmd 330 deny all from any to any frag in via &dollar;pif
# Deny ACK packets that did not match the dynamic rule table
&dollar;cmd 332 deny tcp from any to any established in via &dollar;pif
# Allow traffic in from ISP's DHCP server. This rule must contain
# the IP address of your ISP's DHCP server as it is the only
# authorized source to send this packet type.
# Only necessary for cable or DSL configurations.
# This rule is not needed for 'user ppp' type connection to
# the public Internet. This is the same IP address you captured
# and used in the outbound section.
&dollar;cmd 360 allow udp from x.x.x.x to any 68 in via &dollar;pif keep-state
# Allow in standard www function because I have Apache server
&dollar;cmd 370 allow tcp from any to me 80 in via &dollar;pif setup limit src-addr 2
# Allow in secure FTP, Telnet, and SCP from public Internet
&dollar;cmd 380 allow tcp from any to me 22 in via &dollar;pif setup limit src-addr 2
# Allow in non-secure Telnet session from public Internet
# labeled non-secure because ID &amp; PW are passed over public
# Internet as clear text.
# Delete this sample group if you do not have telnet server enabled.
&dollar;cmd 390 allow tcp from any to me 23 in via &dollar;pif setup limit src-addr 2
# Reject &amp; Log all unauthorized incoming connections from the public Internet
&dollar;cmd 400 deny log all from any to any in via &dollar;pif
# Reject &amp; Log all unauthorized out going connections to the public Internet
&dollar;cmd 450 deny log all from any to any out via &dollar;pif
# This is skipto location for outbound stateful rules
&dollar;cmd 800 divert natd ip from any to any out via &dollar;pif
&dollar;cmd 801 allow ip from any to any
# Everything else is denied by default
# deny and log all packets that fell through to see what they are
&dollar;cmd 999 deny log all from any to any
################ End of IPFW rules file ###############################</programlisting>
</sect3>
</sect2>
</sect1>
</chapter>