IPFIREWALL (IPFW) Firewall
Contents |
IPFIREWALL (IPFW) Firewall
The IPFIREWALL (IPFW) is a FBSD sponsored firewall software application authored and maintained by FBSD volunteer staff members. It uses the legacy stateless rules and a legacy rule coding technique to achieve what is referred to as simple stateful logic.
The IPFW stateless rule syntax is empowered with technically sophisticated selection capabilities which far surpasses the knowledge level of the customary firewall installer. IPFW is targeted at the professional user or the advanced technical computer hobbyist who has advanced packet selection requirements. A high degree of detailed knowledge into how different protocols use and create their unique packet header information is necessary before the power of the IPFW rules can be unleashed. Providing that level of explanation is out of the scope of this section of the handbook.
IPFW is composed of seven components; the kernel firewall filter rule processor and its integrated packet accounting facility (the primary component), the logging facility, the ‘divert’ rule which triggers the NAT facility, and the advanced special purpose facilities (the dummynet traffic shaper facilities the ‘fwd rule’ forward facility, the bridge facility, and the ipstealth facility).
See the FBSD man pages, 'man ipfw' or 'man ipfirewall' or 'man dummynet' for details.
From this point on I will use IPFW to mean IPFIREWALL.
Enabling IPFW
IPFW is included in the basic FBSD install as a separate run time loadable module. IPFW will dynamically load its kernel loadable module when the rc.conf statement firewall_enable="YES" is used. You do not need to compile IPFW into the FBSD kernel.
Using the IPFW run time loadable module is recommended.
After rebooting your system with firewall_enable="YES" in rc.conf the following white highlighted message is displayed on the screen as part of the boot process.
IP packet filtering initialized, divert disabled, rule-based forwarding enabled, default to deny, logging disabled
You can disregard this message as it’s outdated and no longer is the true status of the IPFW loadable module. The loadable module really does have logging ability.
To set the verbose limit, there is a knob you can set in sysctl.conf by adding this statement to the file:
ee /etc/sysctl.conf net.inet.ip.fw.verbose_limit=5
Kernel options
It is not a mandatory requirement that you enable IPFW by compiling the following options into the FBSD kernel. It’s only presented here as a background information option. Compiling IPFW into the kernel causes the loadable module to never be used.
Sample kernel source IPFW options statements are in the /usr/src/sys/i386/conf/LINT kernel source and are reproduced here.
options IPFIREWALL options IPFIREWALL_VERBOSE options IPFIREWALL_VERBOSE_LIMIT=5 option IPDIVERT
IPFIREWALL This tells the compile to include IPFW as part of the kernel.
IPFIREWALL_VERBOSE enables the option to have IPFW log traffic by printing packet activity to syslogd for every rule that has the "log" keyword.
IPFIREWALL_VERBOSE_LIMIT=5 specifies the default number of packets from a particular rule is to be logged. Without this option each repeated occurrences of the same packet will be logged and eventually consume all the free disk space, resulting in services being denied due to lack of resources. The 5 is the number of consecutive times to log evidence of this unique occurrence.
IPDIVERT adds the userland natd function which is utilized by the divert natd IPFW rule statement.
A complete list of the IPFW options statements are in /usr/src/sys/i386/conf/LINT
Installer note: After compiling IPFW into your kernel you lose the ability to access all private LAN and public Internet networks, until you enable IPFW in rc.conf and reboot.
RC.CONF Options
You have to tell FBSD to active it at boot time. You do that by adding the following statements to /etc/rc.conf:
firewall_enable="YES" # Start IPFW daemon firewall_script="/etc/ipfw.rules" # Use my custom rules. filewall_logging="YES" # Enable packet logging
For a completely open firewall, you have to create the /etc/ipfw.rules file with the following rules
ipfw –f flush ipfw add allow all from any to any
IPFW Command
The ipfw command is the normal vehicle for making manual single rule additions or deletions to the firewall active internal rules while it's running. The problem with using this method is once your system is shutdown or halted, all the rules you added or changed or deleted are lost. Writing all your rules in a file and using that file to load the rules at boot time or to replace in mass the currently running firewall rules with changes you made to the files content is the recommended method used here.
The ipfw command is still a very useful for displaying the running firewall rules to the console screen. The IPFW accounting facility dynamically creates a counter for each rule that counts each packet that matches the rule. During the process of testing a rule, listing the rule with its counter is the only way of determining if the rule is functioning.
You would enter on the FBSD command line one of the following forms of the list command.
ipfw list List all rules in rule number sequence. ipfw -t list List rules in rule number sequence with timestamp of last time that rule was matched. ipfw -a list List the accounting information, packet count for matched rules along with the rules themselves. The first column is the rule number, followed by the number of outgoing matched packets, followed by the number of incoming matched packets, and finally followed by the rule itself. ipfw -d list List dynamic rules in addition to static ones. ipfw -d -e list Also show expired dynamic rules. ipfw zero Clear all the accounting counters. ipfw zero number Clear accounting counter just for this rule number. ipfw show | more
If you have a big rule set with dynamic rules it will scroll off the screen. Suffix the command with ‘ | more’ which will only display the first screen full, and then you have to use the arrow keys or enter key to scroll down through the info.
IPFW Rule Sets
A rule set is a group of ipfw rules coded to allow or deny 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 (IE: telnet, www, mail, etc.) is predefined by its protocol and port number. This is the basic selection criteria used to create rules which will allow or deny services.
When a packet enters the firewall it is compared against the first rule in the rule set and progresses one rule at a time, moving from top to bottom of the set in ascending rule number sequence order. When the packet matches a rule selection parameter, the rule's action field value is executed and the search of the rule set terminates for that packet. This is referred to as the 'first match wins' search method. If the packet does not match any of the rules, it gets caught by the mandatory ipfw default rule, number 65535 which denies all packets and discards them without any reply back to the originating destination.
The instructions contained in this section of the Installers Guide is based on using rules that contain the stateful ‘keep state’ and ‘limit’ options. This is the basic framework for coding an inclusive type 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 destine for the public Internet and also control the services which can originate from the public Internet accessing your private network. Everything else is denied by default design. Inclusive firewalls are much more secure than exclusive firewall rule sets and are the only rule set type covered here in.
Installers Note: Warning, when working with the firewall rules, always, always do it from the root console of the system running the firewall or you can end up locking yourself out.
Rule Syntax
The rule syntax presented here has been simplified to what is necessary to create a standard inclusive type firewall rule set. For a complete rule syntax description see the online ‘man ipfw’ page at
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.
# 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.
Syntax = CMD RULE# ACTION LOGGING SELECTION STATEFUL
CMD Each rule has to be prefixed with the following to add the rule to the internal table,
ipfw add
RULE# Coding rule numbers is not a mandatory requirement. Rule numbers will automatically be assigned when the rules are loaded into the internal IPFW tables. Coding your own rule numbers means the numbers will not change during loading and gives you a fixed rule number which is listed in the log along with other information about the packet being logged. The rule number is how you relate the logged packet back to the rule that caused the packet to be logged. If a rule is entered without a number, ipfw will assign one.
ACTIONS
A rule can be associated with one of the following actions which will be executed when the packet matches the selection criterion of the rule.
allow | accept | pass | permit These all mean the same thing which is to allow packets that match the rule to exit the firewall rule processing. The search terminates. check-state Checks the packet against the dynamic rules table. If a match is found, execute the action associated with the rule which generated this dynamic rule, otherwise move to the next rule. The check-state rule does not have selection criteria. If no check-state rule is present in the rule set, the dynamic rules table is checked at the first keep-state or limit rule. deny | drop Both words mean the same thing which is to discard packets that match this rule. The search terminates.
LOGGING
log or logamount number
When a packet matches a rule with the log keyword, a message will be logged to syslogd with a facility name of SECURITY. The logging only occurs if the number of packets logged so far for that particular rule does not exceed the logamount parameter. If no logamount is specified, the limit is taken from the sysctl variable net.inet.ip.fw.verbose_limit. In both cases, a value of zero removes the logging limit. Once the limit is reached, logging can be re-enabled by clearing the logging counter or the packet counter for that rule. See the ipfw reset log command. Note: logging is done after all other packet matching conditions have been successfully verified and before performing the final action accept, deny) on the packet. It’s up to you to decide which rules you want to enable logging on.
SELECTION
The keywords described in this section are used to describe attributes of the packet to be interrogated when determining whether rules match or don't match the packet. The following general-purpose attributes are provided for matching and must be used in this order:
udp | tcp | icmp or any protocol names found in /etc/protocols are recognized and may be used. The value specified is the protocol to be matched against. This is a mandatory requirement. 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. 'me' is a special keyword that matches any IP address configured on an interface in your FBSD system to represent the PC the firewall is running on. (IE: this box) As in 'from me to any' or from 'any to me' 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' or from 'me to 0.0.0.0' IP addresses are specified as a dotted IP address numeric form/mask-length or as single dotted IP address numeric form. This is a mandatory requirement. See this link for help on writing mask-lengths. http://jodies.de/ipcalc port number For protocols which support port numbers (such as TCP and UDP). It’s mandatory that you code the port number of the service you want to match on. Service names (from /etc/services) may be used instead of numeric port values. in | out Matches incoming or outgoing packets, respectively. in and out are keywords and it’s mandatory that you code one or the other as part of your rule matching criterion. via IFN Matches packets going through the interface specified by exact name. IFN = interface-name. The via keyword causes the interface to always be checked as part of the match process. via is mandatory. setup This is a mandatory keyword that identifies the session start request for TCP packets. keep-state This is a mandatory keyword. Upon a match, the firewall will create a dynamic rule whose default behavior is to match bidirectional traffic between source and destination IP/port using the same protocol. limit {src-addr | src-port | dst-addr | dst-port} The firewall will only allow N connections with the same set of parameters as specified in the rule. One or more of source and destination addresses and ports can be specified. The ‘limit’ and 'keep-state’ cannot be used on same rule. Limit provides the same stateful function as ‘keep-state’ plus its own functions.
Stateful Rule Option
Stateful filtering treats traffic as a bi-directional exchange of packets comprising a 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. This interrogation ability works for all the protocols.
The 'check-state' <action> is used to identify where in the IPFW rules set the packet is to be tested against the dynamic rules facility. On a match the packet exits the firewall to continue on its way and a new rule is dynamic created for the next anticipated packet being exchanged during this bi-directional session conversation. On a no match the packet advances to the next rule in the rule set for testing.
The dynamic rules facility is vulnerable to resource depletion from a SYN-flood attack which would open a huge number of dynamic rules. To counter this attack, FBSD version 4.5 added another new option named limit. This option is used to limit the number of simultaneous session conversations by interrogating the rule's source or destinations fields as directed by the limit option and using the packet's IP address found there. In a search of the open dynamic rules counting the number of times this rule and IP address combination occurred, if this count is greater that the value specified on the limit option, the packet is discarded.
Logging Firewall Messages
The benefits of logging are obvious, provides information like, what packets have been dropped, what addresses they came from, and where they were going. This gives you a significant edge in tracking down attackers.
Even with the logging facility enabled, IPFW 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 verb to those rules. Normally only deny rules are logged, like the deny rule for incoming icmp pings. It's very customary to duplicate the ipfw default deny everything rule with the log verb 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.
Logging is a two edged sword. If you're not careful, you can lose yourself in the over abundance of log data and fill all your free disk space with growing log files. DoS attacks that fill up disk drives is one of the oldest attacks around. These log messages are not only written to syslogd, but also are displayed on the root console screen and soon become very annoying.
The IPFIREWALL_VERBOSE_LIMIT=5 kernel option limits the number of consecutive messages sent to the system logger syslogd concerning the packet matching of a given rule. When this option is enabled in the kernel, the number of consecutive messages concerning a particular rule is capped at the number specified. There is nothing to be gained from 200 log messages saying the same identical thing. For instance, 5 consecutive messages concerning a particular rule would be logged to syslogd, the remainder identical consecutive messages would be counted and posted to the syslogd with a phrase like this:
last message repeated 45 times
All logged packet messages are written by default to /var/log/security file, which is defined in the /etc/syslog.conf file.
Building Rule Script
Most experienced IPFW users create a file containing the rules and code them in a manner compatible with running them as a script. The major benefit of doing this is the firewall rules can be refreshed in mass with out the need of rebooting the system to activate the new rules. This method is very convenient in testing new rules as the procedure can be executed as many times as needed. Being a script, you can use symbolic substitution to code frequent used values and substituting them in multiple rules. You will see this in the following example.
The script syntax used here is compatible with the 'sh', 'csh', '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 in "double quotes".
Start your rules file with this.
############### start of example ipfw rules script ############# # ipfw –q -f flush # Delete all rules # Set defaults oif="tun0" # out interface odns="192.0.2.11" # ISP's dns server IP address cmd="ipfw -q add " # build rule prefix ks="keep-state" # just too lazy to key this each time $cmd 00500 check-state $cmd 00502 deny all from any to any frag $cmd 00501 deny tcp from any to any established $cmd 00600 allow tcp from any to any 80 out via $oif setup $ks $cmd 00610 allow tcp from any to $odns 53 out via $oif setup $ks $cmd 00611 allow udp from any to $odns 53 out via $oif $ks ################### End of example ipfw rules script ############
That's all there is to it. The rules are not important in this example; how the symbolic substitution field are populated and used are.
If the above example was in /etc/ipfw.rules file, I could reload these rules by entering on the FBSD command
sh /etc/ipfw.rules
The /etc/ipfw.rules file could be located anywhere you want and the file could be named anything you wanted.
The same thing could also to accomplished doing it this way as a text file
ipfw -q -f flush ipfw -q add check-state ipfw -q add deny all from any to any frag ipfw -q add deny tcp from any to any established ipfw -q add allow tcp from any to any 80 out via tun0 setup keep-state ipfw -q add allow tcp from any to 192.0.2.11 53 out via tun0 setup keep-state ipfw -q add 00611 allow udp from any to 192.0.2.11 53 out via tun0 keep-state
Stateful Rule Set
The following non-NATed 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 others 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 FBSD are designed to use interface lo0 and IP address 127.0.0.1 for internal communication with in the FBSD 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 which you code 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 cabled to your DSL or cable modem.
In cases where one or more NICs are cabled to private LANs (local area networks) behind the firewall, those interfaces must have rules 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, public interface outbound, and the public interface inbound.
The order of the rules in each of the public interface sections should be in order of the most used rules being placed before less often used rules with the last rule in the section being a block log all packets on that interface and direction.
The outbound section in the following rule set only contains ‘allow’ rules which contain selection values that uniquely identify the service that is authorized for public Internet access. All the rules have the proto, port, in/out, via and keep state options coded. The ‘proto tcp’ rules have the ‘setup’ option included to identify the start session request as the trigger packet to be posted to the keep state stateful table.
The inbound section has all the blocking of undesirable packets first for two different reasons. First is 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 bases and don’t want to see in the log, this keeps them from being 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; the packets just get dropped and vanish. This way the attackers have no knowledge if their packets have reached your system. The less the attackers can learn about your system the more secure it is. When you log packets with port numbers you do not recognize, go to http://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 for Trojans: http://www.simovits.com/trojans/trojans.html
Example Inclusive Rule Set
The following non-NATed rule set is a complete, very secure ‘inclusive’ type of firewall rule set that I have used on my system. You cannot go wrong using this rule set for you own. Just comment out any pass rules for services you don’t want.
If you see messages in your log that you want to stop seeing, just add a deny rule in the inbound section.
You have to change the ‘dc0’ interface name in every rule to the interface name of the NIC that connects your system to the public Internet. For ‘user ppp’ it would be ‘tun0’.
You will see the pattern in the usage of these rules.
All statements that are a request to start a session to the public Internet use keep-state.
All the authorized services that originate from the public Internet have the limit option to stop flooding.
All rules use in or out to clarify direction.
All rules use via interface name to specify the interface the packet is traveling over.
Add the following statements to /etc/ipfw.rules
################ Start of IPFW rules file ############################### # Flush out the list before we begin. ipfw -q -f flush # Set rules command prefix cmd="ipfw -q add" pif="dc0" # public interface name of Nic card # facing the public Internet ################################################################# # No restrictions on Inside Lan Interface for private network # Not needed unless you have Lan. # Change xl0 to your Lan Nic card interface name ################################################################# #$cmd 00005 allow all from any to any via xl0 ################################################################# # No restrictions on Loopback Interface ################################################################# $cmd 00010 allow all from any to any via lo0 ################################################################# # Allow the packet through if it has previous been added to the # the "dynamic" rules table by an allow keep-state statement. ################################################################# $cmd 00015 check-state ################################################################# # Interface facing Public Internet (Outbound Section) # Interrogate session start requests originating from behind the # firewall on the private network or from this gateway server # destine for the public Internet. ################################################################# # Allow out access to my ISP's Domain name server. # x.x.x.x must be the IP address of your ISP’s DNS # Dup these lines if your ISP has more than one DNS server # Get the IP addresses from /etc/resolv.conf file $cmd 00110 allow tcp from any to x.x.x.x 53 out via $pif setup keep-state $cmd 00111 allow udp from any to x.x.x.x 53 out via $pif keep-state # Allow out access to my ISP's DHCP server for cable/DSL configurations. # This rule is not needed for ‘user ppp’ connection to the public Internet. # so you can delete this whole group. # Use the following rule and check log for IP address. # Then put IP address in commented out rule & delete first rule $cmd 00120 allow log udp from any to any 67 out via $pif keep-state #$cmd 00120 allow udp from any to x.x.x.x 67 out via $pif keep-state # Allow out non-secure standard www function $cmd 00200 allow tcp from any to any 80 out via $pif setup keep-state # Allow out secure www function https over TLS SSL $cmd 00220 allow tcp from any to any 443 out via $pif setup keep-state # Allow out send & get email function $cmd 00230 allow tcp from any to any 25 out via $pif setup keep-state $cmd 00231 allow tcp from any to any 110 out via $pif setup keep-state # Allow out FBSD (make install & CVSUP) functions # Basically give user root "GOD" privileges. $cmd 00240 allow tcp from me to any out via $pif setup keep-state uid root # Allow out ping $cmd 00250 allow icmp from any to any out via $pif keep-state # Allow out Time $cmd 00260 allow tcp from any to any 37 out via $pif setup keep-state # Allow out nntp news (IE: news groups) $cmd 00270 allow tcp from any to any 119 out via $pif setup keep-state # Allow out secure FTP, Telnet, and SCP # This function is using SSH (secure shell) $cmd 00280 allow tcp from any to any 22 out via $pif setup keep-state # Allow out whois $cmd 00290 allow tcp from any to any 43 out via $pif setup keep-state # deny and log everything else that’s trying to get out. # This rule enforces the block all by default logic. $cmd 00299 deny log all from any to any out via $pif ################################################################# # Interface facing Public Internet (Inbound Section) # Interrogate packets originating from the public Internet # destine for this gateway server or the private network. ################################################################# # Deny all inbound traffic from non-routable reserved address spaces $cmd 00300 deny all from 192.168.0.0/16 to any in via $pif #RFC 1918 private IP $cmd 00301 deny all from 172.16.0.0/12 to anyin via $pif #RFC 1918 private IP $cmd 00302 deny all from 10.0.0.0/8 to anyin via $pif #RFC 1918 private IP $cmd 00303 deny all from 127.0.0.0/8 to anyin via $pif #loopback $cmd 00304 deny all from 0.0.0.0/8 to anyin via $pif #loopback $cmd 00305 deny all from 169.254.0.0/16 to anyin via $pif #DHCP auto-config $cmd 00306 deny all from 192.0.2.0/24 to anyin via $pif #reserved for doc's $cmd 00307 deny all from 204.152.64.0/23 to anyin via $pif #Sun cluster interconnect $cmd 00308 deny all from 224.0.0.0/3 to anyin via $pif #Class D & E multicast # Deny public pings $cmd 00310 deny icmp from any to anyin via $pif # Deny ident $cmd 00315 deny tcp from any to any 113in via $pif # Deny all Netbios service. 137=name, 138=datagram, 139=session # Netbios is MS/Windows sharing services. # Block MS/Windows hosts2 name server requests 81 $cmd 00320 deny tcp from any to any 137in via $pif $cmd 00321 deny tcp from any to any 138in via $pif $cmd 00322 deny tcp from any to any 139in via $pif $cmd 00323 deny tcp from any to any 81 in via $pif # Deny any late arriving packets $cmd 00330 deny all from any to any frag in via $pif # Deny ACK packets that did not match the dynamic rule table $cmd 00332 deny tcp from any to any established in via $pif # Allow traffic in from ISP's DHCP server. This rule must contain # the IP address of your ISP’s DHCP server as it’s the only # authorized source to send this packet type. # Only necessary for cable or DSL configurations. # This rule is not needed for ‘user ppp’ type connection to # the public Internet. This is the same IP address you captured # and used in the outbound section. #$cmd 00360 allow udp from any to x.x.x.x 67 in via $pif keep-state # Allow in standard www function because I have apache server $cmd 00400 allow tcp from any to me 80 in via $pif setup limit src-addr 2 # Allow in secure FTP, Telnet, and SCP from public Internet $cmd 00410 allow tcp from any to me 22 in via $pif setup limit src-addr 2 # Allow in non-secure Telnet session from public Internet # labeled non-secure because ID & PW are passed over public # Internet as clear text. # Delete this sample group if you do not have telnet server enabled. $cmd 00420 allow tcp from any to me 23 in via $pif setup limit src-addr 2 # Reject & Log all incoming connections from the outside $cmd 00499 deny log all from any to any in via $pif # Everything else is denied by default # deny and log all packets that fell through to see what they are $cmd 00999 deny log all from any to any ################ End of IPFW rules file ###############################
Stateful + NATD Rule Set
There are some additional configuration statements that need to be enabled to activate the NAT function of IPFW. The kernel source needs an 'option divert' statement added to the other IPFIREWALL statements compiled into a custom kernel.
option IPFIREWALL # Adds filtering code into kernel option IPFIREWALL_VERBOSE # enable logging thru syslogd(8) option IPFIREWALL_VERBOSE_LIMIT=5 # stop attack via syslog flooding option IPDIVERT # needed to use natd from IPFW
The rc.conf needs the following statements added to the already mentioned statements which are reproduced here:
firewall_enable="YES" # Start IPFW daemon firewall_script="/etc/ipfw.rules" # use my custom rules. filewall_logging="YES" # Enable packet logging natd_enable="YES" # Enable NATD function natd_interface="rl0" # interface name of public internet Nic natd_flags="-dynamic -m" # -m = preserve port numbers if possible
Utilizing stateful rules with divert natd rules (network address translation) greatly complicates the rule set coding logic. The positioning of the check-state, and 'divert natd' rules in the rule set becomes very, very critical. This is no longer a simple fall-through logic flow. A new action type is used, called 'skipto'. To use the skipto command it is mandatory that you number each rule so you know exactly where the skipto rule number is you are really jumping to.
The following is an uncommented example of one coding method, selected here to explain the sequence of the packet flow through the rule sets.
The processing flow starts with the first rule from the top of the rule file and progress one rule at a time deeper into the file until the end is reach or the packet being tested to the selection criteria matches and the packet is released out of the firewall. It's important to take notice of the location of rule numbers 100 101, 450, 500, and 510. These rules control the translation of the outbound and inbound packets so their entries in the keep-state dynamic table always register the private Lan IP address. Next, notice that all the allow and deny rules specify the direction the packet is going (IE outbound or inbound) and the interface. Also notice that all the start outbound session requests all skipto rule 500 for the network address translation.
Lets say a LAN user uses their web browser to get a web page. Web pages use port 80 to communicate over. So when the packet enters the firewall, it does not match rule 100 because it's headed out not in. It passes rule 101 because this is the first packet so it has not been posted to the keep-state dynamic table yet. The packet finally comes to rule 125 and matches. It's outbound through the NIC facing the public Internet. The packet still has its source IP address as a private LAN IP address. On the match to this rule, two actions take place. The keep-state option will post this rule into the keep-state dynamic rules table and the specified action is executed. The action is part of the info posted to the dynamic table. In this case it's "skipto rule 500". Rule 500 NATs the packet IP address and out it goes. Remember this, this is very important. This packet makes its way to the destination and returns and enters the top of the rule set. This time it does match rule 100 and has its destination IP address mapped back to its corresponding LAN IP address. It then is processed by the check-state rule, it's found in the table as an existing session conversation and is released to the LAN. It goes to the LAN PC that sent it and a new packet is sent requesting another segment of the data from the remote server. This time it gets checked by the check-state rule and its outbound entry is found. The associated action skipto 500 is executed. The packet jumps to rule 500, gets NATed and is released to exit out the external NIC.
On the inbound side, everything coming in that is part of an existing session conversation is being automatically handled by the check-state rule and the properly placed divert natd rules. All we have to address is denying all the bad packets and only allowing in the authorized services. Lets say there is an apache server running on the firewall box and we want people on the public Internet to be able to access the local web site. The new inbound start request packet matches rule 100 and its IP address is mapped to the LAN IP address for the firewall box. The packet is then matched against all the nasty things we want to check for and finally matches against rule 420. On a match two things occur, the limit option is an extension to keep-state. The packet rule is posted to the keep-state dynamic table, but this time any new session requests originating from that same source IP address is limited to two. This defends against DoS attacks of services running on the specified port number. The action is allow so the packet is released to the LAN. On return the check-state rule recognizes the packet as belonging to an existing session conversation, sends it to rule 500 for NATing, and is released to the outbound interface.
#!/bin/sh cmd="ipfw -q add" skip="skipto 500" pif=rl0 ks="keep-state" good_tcpo="22,25,37,43,53,80,443,110,119" ipfw -q -f flush $cmd 002 allow all from any to any via xl0 # exclude Lan traffic $cmd 003 allow all from any to any via lo0 # exclude loopback traffic $cmd 100 divert natd ip from any to any in via $pif $cmd 101 check-state # Authorized outbound packets $cmd 120 $skip tcp from any to xx.168.240.2 53 out via $pif setup $ks $cmd 121 $skip udp from any to xx.168.240.5 53 out via $pif $ks $cmd 125 $skip tcp from any to any $good_tcpo out via $pif setup $ks $cmd 130 $skip icmp from any to any out via $pif $ks $cmd 135 $skip udp from any to any 123 out via $pif $ks # Deny all inbound traffic from non-routable reserved address spaces $cmd 300 deny all from 192.168.0.0/16 to any in via $pif #RFC 1918 private IP $cmd 301 deny all from 172.16.0.0/12 to any in via $pif #RFC 1918 private IP $cmd 302 deny all from 10.0.0.0/8 to any in via $pif #RFC 1918 private IP $cmd 303 deny all from 127.0.0.0/8 to any in via $pif #loopback $cmd 304 deny all from 0.0.0.0/8 to any in via $pif #loopback $cmd 305 deny all from 169.254.0.0/16 to any in via $pif #DHCP auto-config $cmd 306 deny all from 192.0.2.0/24 to any in via $pif #reserved for doc's $cmd 307 deny all from 204.152.64.0/23 to any in via $pif #Sun cluster $cmd 308 deny all from 224.0.0.0/3 to any in via $pif #Class D & E multicast # Authorized inbound packets $cmd 400 allow udp from xx.70.207.54 to any 68 in $ks $cmd 420 allow tcp from any to me 80 in via $pif setup limit src-addr 1 $cmd 450 deny log ip from any to any # This is skipto location for outbound stateful rules $cmd 500 divert natd ip from any to any out via $pif $cmd 510 allow ip from any to any ######################## end of rules ##################
The following is pretty much the same as above, but uses a self documenting coding style full of description comments to help the inexperienced IPFW rule writer to better understand what the rules are doing.
#!/bin/sh ################ Start of IPFW rules file ############################### # Flush out the list before we begin. ipfw -q -f flush # Set rules command prefix cmd="ipfw -q add" skip="skipto 800" pif="rl0" # public interface name of Nic card # facing the public internet ################################################################# # No restrictions on Inside Lan Interface for private network # Change xl0 to your Lan Nic card interface name ################################################################# $cmd 005 allow all from any to any via xl0 ################################################################# # No restrictions on Loopback Interface ################################################################# $cmd 010 allow all from any to any via lo0 ################################################################# # check if packet is inbound and nat address if it is ################################################################# $cmd 014 divert natd ip from any to any in via $pif ################################################################# # Allow the packet through if it has previous been added to the # the "dynamic" rules table by a allow keep-state statement. ################################################################# $cmd 015 check-state ################################################################# # Interface facing Public internet (Outbound Section) # Interrogate session start requests originating from behind the # firewall on the private network or from this gateway server # destine for the public internet. ################################################################# # Allow out access to my ISP's Domain name server. # x.x.x.x must be the IP address of your ISP's DNS # Dup these lines if your ISP has more than one DNS server # Get the IP addresses from /etc/resolv.conf file $cmd 020 $skip tcp from any to x.x.x.x 53 out via $pif setup keep-state $cmd 021 $skip udp from any to x.x.x.x 53 out via $pif keep-state # Allow out access to my ISP's DHCP server for cable/DSL configurations. $cmd 030 $skip udp from any to x.x.x.x 67 out via $pif keep-state # Allow out non-secure standard www function $cmd 040 $skip tcp from any to any 80 out via $pif setup keep-state # Allow out secure www function https over TLS SSL $cmd 050 $skip tcp from any to any 443 out via $pif setup keep-state # Allow out send & get email function $cmd 060 $skip tcp from any to any 25 out via $pif setup keep-state $cmd 061 $skip tcp from any to any 110 out via $pif setup keep-state # Allow out FBSD (make install & CVSUP) functions # Basically give user root "GOD" privileges. $cmd 070 $skip tcp from me to any out via $pif setup keep-state uid root # Allow out ping $cmd 080 $skip icmp from any to any out via $pif keep-state # Allow out Time $cmd 090 $skip tcp from any to any 37 out via $pif setup keep-state # Allow out nntp news (IE: news groups) $cmd 100 $skip tcp from any to any 119 out via $pif setup keep-state # Allow out secure FTP, Telnet, and SCP # This function is using SSH (secure shell) $cmd 110 $skip tcp from any to any 22 out via $pif setup keep-state # Allow out whois $cmd 120 $skip tcp from any to any 43 out via $pif setup keep-state # Allow ntp time server $cmd 130 $skip udp from any to any 123 out via $pif keep-state ################################################################# # Interface facing Public internet (Inbound Section) # Interrogate packets originating from the public internet # destine for this gateway server or the private network. ################################################################# # Deny all inbound traffic from non-routable reserved address spaces $cmd 300 deny all from 192.168.0.0/16 to any in via $pif #RFC 1918 private IP $cmd 301 deny all from 172.16.0.0/12 to any in via $pif #RFC 1918 private IP $cmd 302 deny all from 10.0.0.0/8 to any in via $pif #RFC 1918 private IP $cmd 303 deny all from 127.0.0.0/8 to any in via $pif #loopback $cmd 304 deny all from 0.0.0.0/8 to any in via $pif #loopback $cmd 305 deny all from 169.254.0.0/16 to any in via $pif #DHCP auto-config $cmd 306 deny all from 192.0.2.0/24 to any in via $pif #reserved for doc's $cmd 307 deny all from 204.152.64.0/23 to any in via $pif #Sun cluster $cmd 308 deny all from 224.0.0.0/3 to any in via $pif #Class D & E multicast # Deny ident $cmd 315 deny tcp from any to any 113 in via $pif # Deny all Netbios service. 137=name, 138=datagram, 139=session # Netbios is MS/Windows sharing services. # Block MS/Windows hosts2 name server requests 81 $cmd 320 deny tcp from any to any 137 in via $pif $cmd 321 deny tcp from any to any 138 in via $pif $cmd 322 deny tcp from any to any 139 in via $pif $cmd 323 deny tcp from any to any 81 in via $pif # Deny any late arriving packets $cmd 330 deny all from any to any frag in via $pif # Deny ACK packets that did not match the dynamic rule table $cmd 332 deny tcp from any to any established in via $pif # Allow traffic in from ISP's DHCP server. This rule must contain # the IP address of your ISP's DHCP server as it's the only # authorized source to send this packet type. # Only necessary for cable or DSL configurations. # This rule is not needed for 'user ppp' type connection to # the public internet. This is the same IP address you captured # and used in the outbound section. $cmd 360 allow udp from x.x.x.x to any 68 in via $pif keep-state # Allow in standard www function because I have apache server $cmd 370 allow tcp from any to me 80 in via $pif setup limit src-addr 2 # Allow in secure FTP, Telnet, and SCP from public Internet $cmd 380 allow tcp from any to me 22 in via $pif setup limit src-addr 2 # Allow in non-secure Telnet session from public Internet # labeled non-secure because ID & PW are passed over public # internet as clear text. # Delete this sample group if you do not have telnet server enabled. $cmd 390 allow tcp from any to me 23 in via $pif setup limit src-addr 2 # Reject & Log all unauthorized incoming connections from the public internet $cmd 400 deny log all from any to any in via $pif # Reject & Log all unauthorized out going connections to the public internet $cmd 450 deny log all from any to any out via $pif # This is skipto location for outbound stateful rules $cmd 800 divert natd ip from any to any out via $pif $cmd 801 allow ip from any to any # Everything else is denied by default # deny and log all packets that fell through to see what they are $cmd 999 deny log all from any to any ################ End of IPFW rules file ###############################