IPFILTER is a cross-platform, open source firewall which has been ported to FreeBSD, NetBSD, OpenBSD, SunOS™, HP/UX, and Solaris™ operating systems.
IPFILTER is based on a kernel-side firewall and NAT mechanism that can be controlled and monitored by userland interface programs. The firewall rules can be set or deleted using ipf(8). The NAT rules can be set or deleted using ipnat(8). Run-time statistics for the kernel parts of IPFILTER can be printed using ipfstat(8). To log IPFILTER actions to the system log files, use ipmon(8).
IPF was originally written using a rule processing logic of “the last matching rule wins” and only used stateless rules. Over time, IPF has been enhanced to include a “quick” option and a stateful “keep state” 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.
The instructions contained in this section are based on using rules that contain “quick” and “keep state” as these provide the basic framework for configuring an inclusive firewall ruleset.
For a detailed explanation of the legacy rules processing method, refer to http://www.munk.me.uk/ipf/ipf-howto.html and http://coombs.anu.edu.au/~avalon/ip-filter.html.
The IPF FAQ is at http://www.phildev.net/ipf/index.html.
A searchable archive of the IPFilter mailing list is available at http://marc.theaimsgroup.com/?l=ipfilter.
IPF is included in the basic FreeBSD install as a kernel
loadable module. The system will dynamically load
this module at boot time when
ipfilter_enable="YES"
is added to
rc.conf
. The module enables logging and
default pass all
. To change the
default to block all
, add a
block all
rule at the end of the
ruleset.
For users who prefer to statically compile IPF support
into a custom kernel, the following IPF option statements,
listed in /usr/src/sys/conf/NOTES
, are
available:
options IPFILTER
enables support for
the “IPFILTER” firewall.
options IPFILTER_LOG
enables IPF
logging using the ipl
packet logging
pseudo—device for every rule that has the
log
keyword.
options IPFILTER_DEFAULT_BLOCK
changes
the default behavior so that any packet not matching a
firewall pass
rule gets blocked.
These settings will take effect only after installing a kernel that has been built with the above options set.
To activate IPF at boot time, the following statements
need to be added to /etc/rc.conf
:
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 NAT functionality:
To load the ruleset file, use ipf(8). Custom rules are normally placed in a file, and the following command can be used to replace the currently running firewall rules:
#
ipf -Fa -f /etc/ipf.rules
-Fa
flushes all the internal rules
tables.
-f
specifies the file containing the
rules to load.
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.
Refer to ipf(8) for details on the other flags available with this command.
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.
There is a way to build IPF rules that utilize the power of script symbolic substitution. For more information, see Section 30.5.9, “Building the Rule Script with Symbolic Substitution”.
The default behavior of 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 ipf
-Z
.
Refer to ipfstat(8) for details.
The default ipfstat(8) output will look something like this:
When supplied with either -i
for inbound
or -o
for outbound, the command will retrieve
and display the appropriate list of filter rules currently
installed and in use by the kernel.
ipfstat -in
displays the inbound
internal rules table with rule numbers.
ipfstat -on
displays the outbound
internal rules table with rule numbers.
The output will look something like this:
ipfstat -ih
displays the inbound
internal rules table, prefixing each rule with a count of how
many times the rule was matched.
ipfstat -oh
displays the outbound
internal rules table, prefixing each rule with a count of how
many times the rule was matched.
The output will look something like this:
One of the most important options of
ipfstat
is -t
which
displays the state table in a way similar to how top(1)
shows the FreeBSD 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
ipfstat(8) for details.
In order for ipmon
to work properly,
the kernel option IPFILTER_LOG
must be
turned on. This command has two different modes. Native mode
is the default mode when the command is used without
-D
.
Daemon mode provides a continuous system log file so that
logging of past events may be reviewed. FreeBSD has a built in
facility to automatically rotate system logs. This is why
outputting the log information to syslogd(8) is better
than the default of outputting to a regular file. The default
rc.conf
ipmon_flags
statement uses
-Ds
:
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.
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.
It is customary to include a “default deny everything” 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.
syslogd(8) uses its own method for segregation of log
data. It uses groupings called “facility” and
“level”. By default, IPMON in
-Ds
mode uses local0
as
the “facility” name. The following levels can be
used to further segregate the logged data:
In order to setup IPFILTER to log all data to
/var/log/ipfilter.log
, first
create the empty file:
#
touch /var/log/ipfilter.log
syslogd(8) is controlled by definition statements in
/etc/syslog.conf
. This file offers
considerable flexibility in how
syslog will deal with system
messages issued by software applications like IPF.
To write all logged messages to the specified file,
add the following statement to
/etc/syslog.conf
:
To activate the changes and instruct syslogd(8)
to read the modified /etc/syslog.conf
,
run service syslogd reload
.
Do not forget to change
/etc/newsyslog.conf
to rotate the new
log file.
Messages generated by ipmon
consist
of data fields separated by white space. Fields common to
all messages are:
The date of packet receipt.
The time of packet receipt. This is in the form HH:MM:SS.F, for hours, minutes, seconds, and fractions of a second.
The name of the interface that processed the packet.
The group and rule number of the rule in the format
@0:17
.
These can be viewed with
ipfstat -in
.
The action: p
for passed,
b
for blocked, S
for
a short packet, n
did not match any
rules, and L
for a log rule. The order
of precedence in showing flags is: S
,
p
, b
,
n
, L
. A capital
P
or B
means that
the packet has been logged due to a global logging
setting, not a particular rule.
The addresses written as three fields: the source
address and port separated by a comma, the -> symbol,
and the destination address and port. For example:
209.53.17.22,80 ->
198.73.220.17,1722
.
PR
followed by the protocol name
or number: for example, PR tcp
.
len
followed by the header length
and total length of the packet: for example,
len 20 40
.
If the packet is a TCP packet, there will be an additional field starting with a hyphen followed by letters corresponding to any flags that were set. Refer to ipf(5) for a list of letters and their flags.
If the packet is an ICMP packet, there will be two fields at the end: the first always being “ICMP” 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.
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.
The script syntax used here is compatible with the sh(1), csh(1), and tcsh(1) shells.
Symbolic substitution fields are prefixed with a
$
.
Symbolic fields do not have the $ prefix.
The value to populate the symbolic field must be enclosed
between double quotes ("
).
Start the rule file with something like this:
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
/etc/ipf.rules.script
, these rules could
be reloaded by running:
#
sh /etc/ipf.rules.script
There is one problem with using a rules file with embedded symbolics: IPF does not understand symbolic substitution, and cannot read such scripts directly.
This script can be used in one of two ways:
Uncomment the line that begins with
cat
, and comment out the line that
begins with /sbin/ipf
. Place
ipfilter_enable="YES"
into
/etc/rc.conf
, and run the script
once after each modification to create or update
/etc/ipf.rules
.
Disable IPFILTER in the system startup scripts by
adding ipfilter_enable="NO"
to
/etc/rc.conf
.
Then, add a script like the following to /usr/local/etc/rc.d/
.
The script should have an obvious name like
ipf.loadrules.sh
, where the
.sh
extension is mandatory.
The permissions on this script file must be read,
write, execute for owner root
:
#
chmod 700 /usr/local/etc/rc.d/ipf.loadrules.sh
Now, when the system boots, the IPF rules will be loaded.
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 TCP/IP 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.
When working with the firewall rules, be very careful. Some configurations can lock the administrator out 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 ssh.
The rule syntax presented here has been simplified to only address the modern stateful rule context and “first matching rule wins” logic. For the complete legacy rule syntax, refer to ipf(8).
A #
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.
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.
ACTION IN-OUT OPTIONS SELECTION STATEFUL
PROTO SRC_ADDR,DST_ADDR OBJECT PORT_NUM TCP_FLAG
STATEFUL
ACTION
= block | pass
IN-OUT
= in | out
OPTIONS
= log | quick | on
interface-name
SELECTION
= proto value |
source/destination IP | port = number | flags
flag-value
PROTO
= tcp/udp | udp | tcp |
icmp
SRC_ADD,DST_ADDR
= all | from
object to object
OBJECT
= IP address |
any
PORT_NUM
= port number
TCP_FLAG
= S
STATEFUL
= keep state
The action keyword indicates what to do with the packet if it matches the rest of the filter rule. Each rule must have an action. The following actions are recognized:
block
indicates that the packet
should be dropped if the selection parameters match the
packet.
pass
indicates that the packet should
exit the firewall if the selection parameters match the
packet.
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 in
or out
and one or the other has to be
included or the rule will not pass syntax checks.
in
means this rule is being applied
against an inbound packet which has just been received on
the interface facing the public Internet.
out
means this rule is being applied
against an outbound packet destined for the interface facing
the public Internet.
These options must be used in the order shown here.
log
indicates that the packet header
will be written to the ipl(4) packet log pseudo-device
if the selection parameters match the packet.
quick
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.
on
indicates the interface name to
be incorporated into the selection parameters. Interface
names are as displayed by ifconfig(8). Using this
option, the rule will only match if the packet is going
through that interface in the specified direction.
When a packet is logged, the headers of the packet are
written to the ipl(4) packet logging pseudo-device.
Immediately following the log
keyword,
the following qualifiers may be used in this order:
body
indicates that the first 128
bytes of the packet contents will be logged after the
headers.
first
. If the log
keyword is being used in conjunction with a keep
state
option, this option is recommended so that
only the triggering packet is logged and not every packet
which matches the stateful connection.
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:
proto
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.
tcp/udp | udp | tcp | icmp
or any
protocol names found in /etc/protocols
are recognized and may be used. The special protocol
keyword tcp/udp
may be used to match
either a TCP or a UDP
packet, and has been added as a convenience to save
duplication of otherwise identical rules.
The all
keyword is equivalent to
“from any to any” with no other match
parameters.
from | to src to dst
: the
from
and to
keywords are used to match against IP addresses. Rules
must specify both the source and
destination parameters. any
is a special
keyword that matches any IP address. Examples include:
from any to any
, from 0.0.0.0/0
to any
, from any to
0.0.0.0/0
, from 0.0.0.0 to
any
, and from any to
0.0.0.0
.
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 net-mgmt/ipcalc
port may be
used to ease the calculation. Additional information
is available at the utility's web page: http://jodies.de/ipcalc.
If a port match is included, for either or both of
source and destination, it is only applied to
TCP and UDP packets.
When composing port comparisons, either the service name
from /etc/services
or an integer port
number may be used. When the port appears as part of the
from
object, it matches the source port
number. When it appears as part of the
to
object, it matches the destination
port number. An example usage is from any to any
port = 80
Single port comparisons may be done in a number of ways,
using a number of different comparison operators. Instead
of the =
shown in the example above,
the following operators may be used: !=
,
<
, >
,
<=
, >=
,
eq
, ne
,
lt
, gt
,
le
, and ge
.
To specify port ranges, place the two port numbers
between <>
or
><
Flags are only effective for TCP filtering. The letters represent one of the possible flags that can be matched against the TCP packet header.
The modernized rules processing logic uses the
flags S
parameter to identify the TCP
session start request.
Stateful filtering treats traffic as a bi-directional
exchange of packets comprising a session. When activated,
keep-state
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.
IPF stateful filtering will also allow ICMP packets related to an existing TCP or UDP session. So, if an ICMP 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.
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.
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.
When the session completes, it is removed from the dynamic state table.
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.
The following ruleset is an example of an inclusive type
of firewall which only allows services matching
pass
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 LAN 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.
FreeBSD uses interface lo0
and IP
address 127.0.0.1
for internal
communication within the operating system. The firewall rules
must contain rules to allow free movement of these internally
used packets.
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.
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.
The rules should be organized into three major sections: the trusted interfaces, then the public interface outbound, and lastly, the public untrusted interface inbound.
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.
The outbound section in the following ruleset only
contains pass
rules which uniquely identify
the services that are authorized for public Internet access.
All the rules use quick
,
on
, proto
,
port
, and keep state
.
The proto tcp
rules include
flag
to identify the session start request
as the triggering packet to activate the stateful
facility.
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 allow
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.
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
log first
option, will only log the event
the first time they are triggered. This option is included in
the sample nmap OS fingerprint
rule. The
security/nmap
utility is
commonly used by attackers who attempt to identify the
operating system of the server.
Any time there are logged messages on a rule with
the log first
option,
ipfstat -hio
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.
To lookup unknown port numbers, refer to
/etc/services
. Alternatively, visit
http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
and do a port number lookup to find the purpose of a
particular port number.
Check out this link for port numbers used by Trojans http://www.sans.org/security-resources/idfaq/oddports.php.
The following ruleset creates an
inclusive
firewall ruleset which can be
easily customized by commenting out
pass
rules for services that should not
be authorized.
To avoid logging unwanted messages, add a
block
rule in the inbound section.
Change the dc0
interface name in
every rule to the interface name that connects the system to
the public Internet.
The following statements were added to
/etc/ipf.rules
:
NAT stands for Network Address Translation. In Linux®, NAT is called “IP Masquerading”. The IPF NAT 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.
NAT 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.
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:
10.0.0.0/8
.
172.16.0.0/12
.
192.168.0.0/16
.
NAT rules are loaded using
ipnat
. Typically, the
NAT rules are stored in
/etc/ipnat.rules
. See ipnat(8) for
details.
When the file containing the NAT rules
is edited after NAT has been started, run
ipnat
with -CF
to delete
the internal in use NAT rules and flush the
contents of the translation table of all active
entries.
To reload the NAT rules, issue a command like this:
#
ipnat -CF -f
/etc/ipnat.rules
To display some NAT statistics, use this command:
#
ipnat -s
To list the NAT table's current mappings, use this command:
#
ipnat -l
To turn verbose mode on and display information relating to rule processing and active rules/table entries:
#
ipnat -v
NAT rules are flexible and can accomplish many different things to fit the needs of commercial and home users.
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 ipnat(5).
The syntax for a NAT rule looks like this:
IF
LAN_IP_RANGE
-> PUBLIC_ADDRESS
The keyword map
starts the rule.
Replace IF
with the external
interface.
The LAN_IP_RANGE
is used by the
internal clients use for IP Addressing. Usually, this is
something like 192.168.1.0/24
.
The PUBLIC_ADDRESS
can either
be the static external IP address or the special keyword
0/32
which uses the IP address assigned to
IF
.
In IPF, when a packet arrives at the firewall from the LAN
with a public destination, it passes through the outbound
filter rules. NAT gets its turn at the
packet and applies its rules top down, where the first
matching rule wins. NAT tests each of its
rules against the packet's interface name and source IP
address. When a packet's interface name matches a
NAT 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
NAT rule. On a match, the packet has its
source IP address rewritten with the public IP address
obtained by the 0/32
keyword.
NAT posts an entry in its internal
NAT 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.
To enable IPNAT, add these statements
to /etc/rc.conf
.
To enable the machine to route traffic between interfaces:
To start IPNAT automatically each time:
To specify where to load the IPNAT rules from:
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.
A normal NAT rule would look like:
In the above rule, the packet's source port is unchanged
as the packet passes through IPNAT. By
adding the portmap
keyword,
IPNAT can be directed to only use
source ports in the specified range. For example, the
following rule will tell IPNAT to modify
the source port to be within the range shown:
Additionally, the auto
keyword tells
IPNAT to determine which ports are
available for use:
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 “pool”, and IPNAT may pick one of the public IP addresses as packet addresses are mapped on their way out.
For example, instead of mapping all packets through a single public IP address:
A range of public IP addresses can be specified either with a netmask:
or using CIDR notation:
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 NAT, 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 10.0.10.25
and using a single public
IP address of 20.20.20.5
, would
use this rule:
or:
For a LAN DNS server on a private address of 10.0.10.33
that needs to receive
public DNS requests:
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 http://www.slacksite.com/other/ftp.html.
IPNAT has a built in FTP proxy option which can be specified on the NAT 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.
This rule will handle all the traffic for the internal LAN:
This rule handles the FTP traffic from the gateway:
This rule handles all non-FTP traffic from the internal LAN:
The FTP map
rules go before the
NAT 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
NAT. All LAN packets that are not FTP
will not match the FTP rules but will undergo
NAT if they match the third rule.
Only one filter rule is needed for FTP if the NAT FTP proxy is used.
Without the FTP proxy, the following three rules will be needed:
All FreeBSD documents are available for download at http://ftp.FreeBSD.org/pub/FreeBSD/doc/
Questions that are not answered by the
documentation may be
sent to <freebsd-questions@FreeBSD.org>.
Send questions about this document to <freebsd-doc@FreeBSD.org>.