Note: This section is work in progress. The contents might not be accurate at
all times.
The author of IPFILTER is Darren Reed. IPFILTER is not operating system dependent: it
is an open source application and has been ported to FreeBSD, NetBSD, OpenBSD, SunOS™, HP/UX, and Solaris™ operating systems. IPFILTER is actively being
supported and maintained, with updated versions being released regularly.
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 with the ipf(8) utility. The
NAT rules can be set or deleted with the ipnat(1) utility. The
ipfstat(8) utility can
print run-time statistics for the kernel parts of IPFILTER. The ipmon(8) program can
log IPFILTER actions to the system log files.
IPF was originally written using a rule processing logic of “the last matching
rule wins” and used only stateless type of rules. Over time IPF has been enhanced
to include a “quick” option and a stateful “keep state” option
which drastically modernized the rules processing logic. IPF's official documentation
covers the legacy rule coding parameters and the legacy rule file processing logic. The
modernized functions are only included as additional options, completely understating
their benefits in producing a far superior secure firewall.
The instructions contained in this section are based on using rules that contain the
“quick” option and the stateful “keep state” option. This is the
basic framework for coding an inclusive firewall rule set.
An inclusive firewall only allows packets matching the rules to pass through. This way
you can control what services can originate behind the firewall destined for the public
Internet and also control the services which can originate from the public Internet
accessing your private network. Everything else is blocked and logged by default design.
Inclusive firewalls are much, much more secure than exclusive firewall rule sets and is
the only rule set type covered herein.
For detailed explanation of the legacy rules processing method see: https://www.obfuscation.org/ipf/ipf-howto.html#TOC_1 and https://coombs.anu.edu.au/~avalon/ip-filter.html.
The IPF FAQ is at https://www.phildev.net/ipf/index.html.
A searchable archive of the open-source IPFilter mailing list is available at https://marc.theaimsgroup.com/?l=ipfilter.
IPF is included in the basic FreeBSD install as a separate run time loadable module.
The system will dynamically load the IPF kernel loadable module when the rc.conf
statement ipfilter_enable="YES" is used. The loadable module was
created with logging enabled and the default pass all options.
You do not need to compile IPF into the FreeBSD kernel just to change the default to block all, you can do that by just coding a block all rule at the
end of your rule set.
It is not a mandatory requirement that you enable IPF by compiling the following
options into the FreeBSD kernel. It is only presented here as background information.
Compiling IPF into the kernel causes the loadable module to never be used.
Sample kernel config IPF option statements are in the /usr/src/sys/conf/NOTES kernel source and are reproduced here:
options IPFILTER
options IPFILTER_LOG
options IPFILTER_DEFAULT_BLOCK
options IPFILTER enables support for the
“IPFILTER” firewall.
options IPFILTER_LOG enables the option to have IPF log
traffic by writing to the ipl packet logging pseudo--device
for every rule that has the log keyword.
options IPFILTER_DEFAULT_BLOCK changes the default behavior
so any packet not matching a firewall pass rule gets
blocked.
These settings will take effect only after you have built and installed a kernel with
them set.
You need the following statements in /etc/rc.conf to
activate IPF at boot time:
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 you have a LAN behind this firewall that uses the reserved private IP address
ranges, then you need to add the following 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
The ipf command is used to load your rules file. Normally you create a file containing
your custom rules and use this command to replace in mass the currently running firewall
internal rules:
# ipf -Fa -f /etc/ipf.rules
-Fa
means flush all internal rules tables.
-f
means this is the file to read for the rules to
load.
This gives you the ability to make changes to your custom rules file, run the above
IPF command, and thus update the running firewall with a fresh copy of all the rules
without having to reboot the system. This method is very convenient for testing new rules
as the procedure can be executed as many times as needed.
See the ipf(8) manual page for
details on the other flags available with this command.
The ipf(8) command 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 utilizes the power of script symbolic
substitution. For more information, see Section 28.5.9.
The default behavior of ipfstat(8) is to
retrieve and display the totals of the accumulated statistics gathered as a result of
applying the user coded 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 by the ipf -Z command.
See the ipfstat(8) manual page
for details.
The default ipfstat(8) command
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, it 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 number.
ipfstat -on displays the outbound internal rules table with
the rule number.
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 functions of the ipfstat command is
the -t
flag which displays the state table in a way similar
to the way top(1) shows the
FreeBSD running process table. When your firewall is under attack this function gives you
the ability to identify, drill down to, and see the attacking packets. The optional
sub-flags give the ability to select the destination or source IP, port, or protocol that
you want to monitor in real time. See the ipfstat(8) manual page
for details.
In order for ipmon to work properly, the kernel option
IPFILTER_LOG must be turned on. This command has two different modes that it can be used
in. Native mode is the default mode when you type the command on the command line without
the -D
flag.
Daemon mode is for when you want to have a continuous system log file available so
that you can review logging of past events. This is how FreeBSD and IPFILTER are
configured to work together. FreeBSD has a built in facility to automatically rotate
system logs. That is why outputting the log information to syslogd is better than the
default of outputting to a regular file. In the default rc.conf
file you see the ipmon_flags statement uses the -Ds
flags:
ipmon_flags="-Ds" # D = start as daemon
# s = log to syslog
# v = log tcp window, ack, seq
# n = map IP & port to names
The benefits of logging are obvious. It provides the ability to review, after the
fact, information such as which packets had been dropped, what addresses they came from
and where they were going. These all give you a significant edge in tracking down
attackers.
Even with the logging facility enabled, IPF will not generate any rule logging on its
own. The firewall administrator decides what rules in the rule set he wants to log and
adds the log keyword to those rules. Normally only deny rules are logged.
It is very customary to include a default deny everything rule with the log keyword
included as your last rule in the rule set. This way you get to see all the packets that
did not match any of the rules in the rule set.
Syslogd uses its own special method for segregation of log
data. It uses special groupings called “facility” and “level”.
IPMON in -Ds
mode uses security as
the “facility” name. All IPMON logged data goes to security The following levels can be used to further segregate the
logged data if desired:
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
To setup IPFILTER to log all data to /var/log/ipfilter.log,
you will need to create the file. The following command will do that:
# touch /var/log/ipfilter.log
The syslog function is controlled by definition statements in the /etc/syslog.conf file. The syslog.conf
file offers considerable flexibility in how syslog will deal with system messages issued
by software applications like IPF.
Add the following statement to /etc/syslog.conf:
security.* /var/log/ipfilter.log
The security.* means to write all the logged messages to the
coded file location.
To activate the changes to /etc/syslog.conf you can reboot
or bump the syslog task into re-reading /etc/syslog.conf by
running /etc/rc.d/syslogd reload
Do not forget to change /etc/newsyslog.conf to rotate the
new log you just created above.
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 (which can be several digits long).
-
The name of the interface the packet was processed on, e.g. dc0.
-
The group and rule number of the rule, e.g. @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, 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. This is actually three fields: the source address and port (separated
by a comma), the -> symbol, and the destination address and port. 209.53.17.22,80
-> 198.73.220.17,1722.
-
PR followed by the protocol name or number, e.g. PR tcp.
-
len followed by the header length and total length of the
packet, e.g. 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. See the ipmon(8) manual page
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, e.g. 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 you only have to change the value associated with the
symbolic name and when the script is run all the rules containing the symbolic name will
have the value substituted in the rules. Being a script, you can use symbolic
substitution to code frequently used values and substitute them in multiple rules. You
will see this in the following example.
The script syntax used here is compatible with the sh, csh, and tcsh shells.
Symbolic substitution fields are prefixed with a dollar sign: $.
Symbolic fields do not have the $ prefix.
The value to populate the symbolic field must be enclosed with double quotes (").
Start your 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 ########################
That is all there is to it. The rules are not important in this example; how the
symbolic substitution fields are populated and used are. If the above example was in a
file named /etc/ipf.rules.script, you could reload these rules
by entering the following command:
# 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 as
usual, and run script once after each modification to create or update /etc/ipf.rules.
-
Disable IPFILTER in system startup scripts by adding ipfilter_enable="NO" (this is default value) into /etc/rc.conf file.
Add a script like the following to your /usr/local/etc/rc.d/
startup directory. The script should have an obvious name like ipf.loadrules.sh. 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 your system boots, your IPF rules will be loaded.
A rule set is a group of ipf rules coded to 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 rule set processes the packet two times, once on its
arrival from the public Internet host and again as it leaves for its return trip back to
the public Internet host. Each TCP/IP service (i.e. telnet, www, mail, etc.) is
predefined by its protocol, source and destination IP address, or the source and
destination port number. This is the basic selection criteria used to create rules which
will pass or block services.
IPF was originally written using a rules processing logic of “the last matching
rule wins” and used only stateless rules. Over time IPF has been enhanced to
include a “quick” option and a stateful “keep state” option which
drastically modernized the rule processing logic.
The instructions contained in this section are based on using rules that contain the
“quick” option and the stateful “keep state” option. This is the
basic framework for coding an inclusive firewall rule set.
An inclusive firewall only allows services matching the rules through. This way you
can control what services can originate behind the firewall destined for the public
Internet and also control the services which can originate from the public Internet
accessing your private network. Everything else is blocked and logged by default design.
Inclusive firewalls are much, much securer than exclusive firewall rule sets and is the
only rule set type covered herein.
Warning: When working with the firewall rules, be very careful. Some configurations will lock you out of the server. To be on the safe side, you
may wish to consider performing the initial firewall configuration from the local console
rather than doing it remotely e.g. via 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 description see the ipf(8) manual
page.
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. These keywords have to be coded 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 indicates what to do with the packet if it matches the rest of the filter
rule. Each rule must have a 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 coded 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.
Note: These options must be used in the order shown here.
log indicates that the packet header will be written to the
ipl log (as described in the LOGGING section below) 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, allowing a “short-circuit”
path to avoid processing any following rules for this packet. This option is a mandatory
requirement for the modernized rules processing logic.
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 (in/out). This option is a mandatory requirement for the
modernized rules processing logic.
When a packet is logged, the headers of the packet are written to the IPL 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, it is recommended that this
option is also applied so that only the triggering packet is logged and not every packet
which thereafter matches the “keep state” information.
The keywords described in this section are used to describe attributes of the packet
to be interrogated when determining whether rules match or not. 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 and must be coded along with one
of its corresponding keyword sub-option values. The value allows a specific protocol to
be matched against. This option is a mandatory requirement for the modernized rules
processing logic.
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 essentially a synonym for “from any
to any” with no other match parameters.
from src to dst: the from and to keywords are used to match
against IP addresses. Rules must specify BOTH source and destination parameters. any is a special keyword that matches any IP address. Examples of
use: “from any to any” or “from 0.0.0.0/0 to any” or “from
any to 0.0.0.0/0” or “from 0.0.0.0 to any” or “from any to
0.0.0.0”.
IP addresses may be specified as a dotted IP address numeric form/mask-length, or as
single dotted IP address numeric form.
There is no way to match ranges of IP addresses which do not express themselves easily
as mask-length. See this web page for help on writing mask-length: https://jodies.de/ipcalc.
If a port match is included, for either or both of source and destination, then 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. The use of the port option with the to
object is a mandatory requirement for the modernized rules processing logic. Example of
use: “from any to any port = 80”
Port comparisons may be done in a number of forms, with a number of comparison
operators, or port ranges may be specified.
port "=" | "!=" | "<" | ">" | "<=" | ">=" | "eq" | "ne" | "lt" | "gt" |
"le" | "ge".
To specify port ranges, port "<>" | "><"
Warning: Following the source and destination matching parameters, the
following two parameters are mandatory requirements for the modernized rules processing
logic.
Flags are only effective for TCP filtering. The
letters represents one of the possible flags that can be interrogated in the TCP packet header.
The modernized rules processing logic uses the flags S
parameter to identify the tcp session start request.
keep state indicates that on a pass rule, any packets that
match the rules selection parameters should activate the stateful filtering facility.
Note: This option is a mandatory requirement for the modernized rules
processing logic.
Stateful filtering treats traffic as a bi-directional exchange of packets comprising a
session conversation. When activated, keep-state dynamically generates internal rules for
each anticipated packet being exchanged during the bi-directional session conversation.
It has the interrogation abilities to determine if the session conversation between the
originating sender and the destination are following the valid procedure of
bi-directional packet exchange. Any packets that do not properly fit the session
conversation template are automatically rejected as impostors.
Keep state will also allow ICMP packets related to a TCP or UDP session through. So if you get ICMP type 3 code 4 in
response to some web surfing allowed out by a keep state rule, they will be automatically
allowed in. Any packet that IPF can be certain is part of an active session, even if it
is a different protocol, will be let in.
What happens is:
Packets destined to go out the interface connected to the public Internet are first
checked against the dynamic state table, if the packet matches the next expected packet
comprising in a active session conversation, then it exits the firewall and the state of
the session conversation flow is updated in the dynamic state table, the remaining
packets get checked against the outbound rule set.
Packets coming in to the interface connected to the public Internet are first checked
against the dynamic state table, if the packet matches the next expected packet
comprising a active session conversation, then it exits the firewall and the state of the
session conversation flow is updated in the dynamic state table, the remaining packets
get checked against the inbound rule set.
When the conversation completes it is removed from the dynamic state table.
Stateful filtering allows you to focus on blocking/passing new sessions. If the new
session is passed, all its subsequent packets will be allowed through automatically and
any impostors automatically rejected. If a new session is blocked, none of its subsequent
packets will be allowed through. Stateful filtering has technically advanced
interrogation abilities capable of defending against the flood of different attack
methods currently employed by attackers.
The following rule set is an example of how to code a very secure inclusive type of
firewall. An inclusive firewall only allows services matching pass rules through and
blocks all other by default. All firewalls have at the minimum two interfaces which have
to have rules to allow the firewall to function.
All UNIX® flavored systems including FreeBSD are
designed to use 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 unmolested movement of these special
internally used packets.
The interface which faces the public Internet is the one where you place your rules to
authorize and control access out to the public Internet and access requests arriving from
the public Internet. This can be your user PPP tun0 interface
or your NIC that is connected to your DSL or cable modem.
In cases where one or more NICs are cabled to private LANs behind the firewall, those
interfaces must have a rule coded to allow free unmolested movement of packets
originating from those LAN interfaces.
The rules should be first organized into three major sections: all the free unmolested
interfaces, the public interface outbound, and the public 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 rule set only contains 'pass' rules which
contain selection values that uniquely identify the service that is authorized for public
Internet access. All the rules have the 'quick', 'on', 'proto', 'port', and 'keep state'
option coded. The 'proto tcp' rules have the 'flag' option included to identify the
session start request as the triggering packet to activate the stateful facility.
The Inbound section has all the blocking of undesirable packets first, for two
different reasons. The first is that these things being blocked may be part of an
otherwise valid packet which may be allowed in by the later authorized service rules. The
second reason is that by having a rule that explicitly blocks selected packets that I
receive on an infrequent basis and that I do not want to see in the log, they will not be
caught by the last rule in the section which blocks and logs all packets which have
fallen through the rules. The last rule in the section which blocks and logs all packets
is how you create the legal evidence needed to prosecute the people who are attacking
your system.
Another thing you should take note of, is there is no response returned for any of the
undesirable stuff, their packets just get dropped and vanish. This way the attacker has
no knowledge if his packets have reached your system. The less the attackers can learn
about your system, the more time they must invest before actually doing something bad.
The inbound 'nmap OS fingerprint' attempts rule I log the first occurrence because this
is something a attacker would do.
Any time you see log messages on a rule with 'log first'. You should do an ipfstat -hio command to see the number of times the rule has been
matched so you know if you are being flooded, i.e. under attack.
When you log packets with port numbers you do not recognize, look it up in /etc/services or go to https://www.securitystats.com/tools/portsearch.php and do a port number
lookup to find what the purpose of that port number is.
Check out this link for port numbers used by Trojans https://www.simovits.com/trojans/trojans.html.
The following rule set is a complete very secure 'inclusive' type of firewall rule set
that I have used on my system. You can not go wrong using this rule set for your own.
Just comment out any pass rules for services that you do not want to authorize.
If you see messages in your log that you want to stop seeing just add a block rule in
the inbound section.
You have to change the dc0 interface name in every rule to
the interface name of the Nic card that connects your system to the public Internet. For
user PPP it would be tun0.
Add the following statements 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)
# Interrogate session start requests originating from behind the
# firewall on the private network
# or from this gateway server destine 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 secure FTP, Telnet, and SCP
# 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 non-secure Telnet
pass out quick on dc0 proto tcp from any to any port = 23 flags S keep state
# Allow out FBSD CVSUP function
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 for LAN PC 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 enforces the block all by default logic.
block out log first quick on dc0 all
#################################################################
# Interface facing Public Internet (Inbound Section)
# Interrogate packets originating from the public Internet
# destine 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'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.
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 stops a .denial of service. attack targeted
# at filling up your log file space.
# This rule enforces the block all by default logic.
block in log first quick on dc0 all
################### End of rules file #####################################
NAT stands for Network Address Translation. To
those familiar with Linux®, this concept is called IP
Masquerading; NAT and IP Masquerading are the same
thing. One of the many things the IPF NAT function
enables is the ability to have a private Local Area Network (LAN) behind the firewall
sharing a single ISP assigned IP address on the public Internet.
You may ask why would someone want to do this. ISPs normally assign a dynamic IP
address to their non-commercial users. Dynamic means that the IP address can be different
each time you dial in and log on to your ISP, or for cable and DSL modem users when you
power off and then power on your modems you can get assigned a different IP address. This
IP address is how you are known to the public Internet.
Now lets say you have five PCs at home and each one needs Internet access. You would
have to pay your ISP for an individual Internet account for each PC and have five phone
lines.
With NAT you only need a single account with your
ISP, then cable your other four PCs to a switch and the switch to the NIC in your FreeBSD
system which is going to service your LAN as a gateway. NAT will automatically translate the private LAN IP address for
each separate PC on the LAN to the single public IP address as it exits the firewall
bound for the public Internet. It also does the reverse translation for returning
packets.
NAT is most often accomplished without the
approval, or knowledge, of your ISP and in most cases is grounds for your ISP terminating
your account if found out. Commercial users pay a lot more for their Internet connection
and usually get assigned a block of static IP address which never change. The ISP also
expects and consents to their Commercial customers using NAT for their internal private LANs.
There is a special range of IP addresses reserved for NATed private LAN IP address. According to RFC 1918, you can
use the following IP ranges for private nets which will never be routed directly to the
public Internet:
NAT rules are loaded by using the ipnat command. Typically the NAT
rules are stored in /etc/ipnat.rules. See ipnat(1) for
details.
When changing the NAT rules after NAT has been started, make your changes to the file containing
the NAT rules, then run ipnat command with the -CF
flags 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 statistics about your NAT, 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 very 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 see the ipnat(5) manual
page.
The syntax for a NAT rule looks something 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 what your internal clients use
for IP Addressing, usually this is something like 192.168.1.0/24.
The PUBLIC_ADDRESS can either be the external IP
address or the special keyword 0/32, which means to use the IP
address assigned to IF.
A packet arrives at the firewall from the LAN with a public destination. It passes
through the outbound filter rules, NAT gets his turn
at the packet and applies its rules top down, first matching rule wins. NAT tests each of its rules against the packets interface name
and source IP address. When a packets interface name matches a NAT rule then the [source IP address, i.e. private LAN IP
address] of the packet 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 a 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 your 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"
For networks that have large numbers of PC's 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 NATed LAN PC's,
causing collisions. There are two ways to relieve this resource problem.
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 you can tell
IPNAT to only use source ports in a range. For example
the following rule will tell IPNAT to modify the
source port to be within that range:
map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp 20000:60000
Additionally we can make things even easier by using the auto
keyword to tell IPNAT to determine by itself which
ports are available to use:
map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp auto
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, you can
use these addresses as a “pool”, and let IPNAT 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, as
in:
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
A very common practice is to have a web server, email server, database server and DNS
server each segregated to a different PC on the LAN. In this case the traffic from these
servers still have to be NATed, but there has to be
some way to direct the inbound traffic to the correct LAN PCs. IPNAT has the redirection facilities of NAT to solve this problem. Lets say you have your web server on
LAN address 10.0.10.25 and your single public IP address is 20.20.20.5 you would code the rule like this:
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
or for a LAN DNS Server on LAN 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
FTP is a dinosaur left over from the time before the Internet as it is known today,
when research universities were leased lined together and FTP was used to share files
among research Scientists. This was a time when data security was not a consideration.
Over the years the FTP protocol became buried into the backbone of the emerging Internet
and its username and password being sent in clear text was never changed to address new
security concerns. FTP has two flavors, it can run in active mode or passive mode. The
difference is in how the data channel is acquired. Passive mode is more secure as the
data channel is acquired be the ordinal ftp session requester. For a real good
explanation of FTP and the different modes see https://www.slacksite.com/other/ftp.html.
IPNAT has a special 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 only the port number really in use
for the data channel. This eliminates the security risk FTP normally exposes the firewall
to from having large ranges of high order port numbers open.
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 rule goes before our regular map rule. All packets are tested against the
first rule from the top. Matches on interface name, then private LAN source IP address,
and then is it a FTP packet. If all that matches then the special FTP proxy creates temp
filter rules to let the FTP session packets pass in and out, in addition to also NATing the FTP packets. All LAN packets that are not FTP do not
match the first rule and fall through to the third rule and are tested, matching on
interface and source IP, then are NATed.
Only one filter rule is needed for FTP if the NAT
FTP proxy is used.
Without the FTP Proxy you will need the following three rules:
# 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