29.5. The IPFILTER (IPF) Firewall

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.

29.5.1. Enabling IPF

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.

29.5.2. Kernel Options

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
options IPFILTER_LOG
options IPFILTER_DEFAULT_BLOCK

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.

29.5.3. Available rc.conf Options

To activate IPF at boot time, the following statements need to be added to /etc/rc.conf:

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 & port to names

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:

gateway_enable="YES"              # Enable as LAN gateway
ipnat_enable="YES"                # Start ipnat function
ipnat_rules="/etc/ipnat.rules"    # rules definition file for ipnat

29.5.4. IPF

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 29.5.9, “Building the Rule Script with Symbolic Substitution”.

29.5.5. IPFSTAT

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:

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 TCP 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
 TCP cksum fails(in): 0 (out): 0
 Packet log flags set: (0)

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:

@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

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:

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

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.

29.5.6. IPMON

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:

ipmon_flags="-Ds" # D = start as daemon
                  # s = log to syslog
                  # v = log tcp window, ack, seq
                  # n = map IP & port to names

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.

29.5.7. IPMON Logging

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:

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

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:

local0.* /var/log/ipfilter.log

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.

29.5.8. The Format of Logged Messages

Messages generated by ipmon consist of data fields separated by white space. Fields common to all messages are:

  1. The date of packet receipt.

  2. The time of packet receipt. This is in the form HH:MM:SS.F, for hours, minutes, seconds, and fractions of a second.

  3. The name of the interface that processed the packet.

  4. The group and rule number of the rule in the format @0:17.

These can be viewed with ipfstat -in.

  1. 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.

  2. 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.

  3. PR followed by the protocol name or number: for example, PR tcp.

  4. 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.

29.5.9. Building the Rule Script with Symbolic Substitution

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:

############# 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 > /etc/ipf.rules << EOF
#
# 2) This can be used to run script "as is":
/sbin/ipf -Fa -f - << EOF

# Allow out access to my ISP's Domain name server.
pass out quick on $oif proto tcp from any to $odns port = 53 $fks
pass out quick on $oif proto udp from any to $odns port = 53 $ks

# Allow out non-secure standard www function
pass out quick on $oif proto tcp from $myip to any port = 80 $fks

# Allow out secure www function https over TLS SSL
pass out quick on $oif proto tcp from $myip to any port = 443 $fks
EOF
################## End of IPF rules script ########################

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.

    #!/bin/sh
    sh /etc/ipf.rules.script

    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.

29.5.10. IPF Rulesets

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.

Warning:

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.

29.5.11. Rule Syntax

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

29.5.11.1. ACTION

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.

29.5.11.2. IN-OUT

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.

29.5.11.3. OPTIONS

Note:

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.

29.5.11.4. SELECTION

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:

29.5.11.5. PROTO

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.

29.5.11.6. SRC_ADDR/DST_ADDR

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.

29.5.11.7. PORT

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 ><

29.5.11.8. TCP_FLAG

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.

29.5.11.9. STATEFUL

keep state indicates that on a pass rule, any packets that match the rules selection parameters should activate the stateful filtering facility.

29.5.12. Stateful Filtering

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.

29.5.13. Inclusive Ruleset Example

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:

#################################################################
# 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 & 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 & 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 & LAN users' non-secure FTP ( both passive & active modes)
# This function uses the IPNAT 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 & 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 #####################################

29.5.14. NAT

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.

29.5.15. IPNAT

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

29.5.16. IPNAT Rules

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:

map 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.

29.5.17. How NAT Works

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.

29.5.18. Enabling IPNAT

To enable IPNAT, add these statements to /etc/rc.conf.

To enable the machine to route traffic between interfaces:

gateway_enable="YES"

To start IPNAT automatically each time:

ipnat_enable="YES"

To specify where to load the IPNAT rules from:

ipnat_rules="/etc/ipnat.rules"

29.5.19. NAT for a Large LAN

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.

29.5.19.1. Assigning Ports to Use

A normal NAT rule would look like:

map dc0 192.168.1.0/24 -> 0/32

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:

map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp 20000:60000

Additionally, the auto keyword tells IPNAT to determine which ports are available for use:

map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp auto

29.5.19.2. Using a Pool of Public Addresses

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:

map dc0 192.168.1.0/24 -> 204.134.75.1

A range of public IP addresses can be specified either with a netmask:

map dc0 192.168.1.0/24 -> 204.134.75.0/255.255.255.0

or using CIDR notation:

map dc0 192.168.1.0/24 -> 204.134.75.0/24

29.5.20. Port Redirection

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:

rdr dc0 20.20.20.5/32 port 80 -> 10.0.10.25 port 80

or:

rdr dc0 0.0.0.0/0 port 80 -> 10.0.10.25 port 80

For a LAN DNS server on a private address of 10.0.10.33 that needs to receive public DNS requests:

rdr dc0 20.20.20.5/32 port 53 -> 10.0.10.33 port 53 udp

29.5.21. FTP and NAT

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.

29.5.21.1. IPNAT Rules

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:

map dc0 10.0.10.0/29 -> 0/32 proxy port 21 ftp/tcp

This rule handles the FTP traffic from the gateway:

map dc0 0.0.0.0/0 -> 0/32 proxy port 21 ftp/tcp

This rule handles all non-FTP traffic from the internal LAN:

map dc0 10.0.10.0/29 -> 0/32

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.

29.5.21.2. IPNAT FTP Filter Rules

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:

# 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 > 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

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>.