IPFW:check-state/keep-state advanced stateful rules. By Joe Barbish 07/22/2002 All rights reserved. As most new ipfw users, I had a typical ipfw rules file built from the simple stateful rules in rc.firewall. I had originally been using user ppp with it's internal Nat function, but went to natd as the simple stateful type in rc.firewall showed. Since the sample rc.firewall (simple) was pretty much just what I wanted to do, I just assumed this was the correct and proper way, so I cut out the simple type code from rc.firewall to create my own ipfw firewall rules. In searching FBSD and the many sites found by google search I saw many many other people before me had done the same thing. From a technical point of view the whole rc.firewall file is based on simple stateful rules using setup/established with some stateless rules thrown in. As a new ipfw user I did not know the difference and the comments sure did not call out the difference. When I tried to change my simple stateful [established/setup] to advanced stateful [check-state/keep-state] rules, I kept having trouble with ip address being mismatched. Technically the mismatches showed up in /var/log/security as packets that got denied by the (default deny everything rule) for all packets that reach the end of the rule set with out matching any rule. Configuration looked like this. Divert natd (network address translation) ( ( LAN PC's < -- > IPFW < -- > internet Private IP advanced public ip Address stateful rules address I spend weeks playing around trying different combinations of ipfw rules, but kept having mismatches in the dynamic table. Finally I removed the natd divert rule from the ipfw rules set and deactivated natd in rc.conf and re-activated ppp -Nat in rc.conf, and the advanced stateful [check-state/keep-state] rules started to work. Configuration looked like this. LAN PC's < -- > IPFW < -- > user ppp -nat < -- > internet Private IP advanced network address public ip Address stateful rules translation address In this configuration IPFW only knows the private ip address on the LAN and the advanced stateful rules functioned just like described in the man documentation. I wrote emails to the IPFW authors, gave then 2 documented examples of rules sets using exclusively advanced stateful rules and user ppp dial up ISP, the only difference was one used user ppp -nat and did not have the divert natd rule and worked while the other one had the divert natd rule and no user ppp -nat and did not work. After much conflicting correspondences the results were that they were not going to do anything about it and I was left on my own. The real problem here is ipfw advanced stateful rules are relatively new to the IPFW program (FBSD version 4.0 year 2000) and still does not fit cleanly into the divert natd program logic. IPFW was originally designed as a firewall using stateless rules and/or simple stateful rules which is nothing more than an rules file coding logic technique based on the TP flags setup/established. Using these very primitive type of rules IPFW function's correctly. When advanced stateful rules are used to tighten down the control of packets passing through the firewall by dynamically creating an internal rules table based on the by-directional exchange of packets which have to match the pre-known ip address, flow direction, and packet sequence numbers the divert natd function malfunctions. This problem is not limited to dial up internet access, but also occurs for 'all ways on' environments (DSL, Cable, T1) with or without DHCP support. Many users reach this point using the advanced check-state/keep-state stateful rules and go back to simple stateful rule set using established/setup simple because they can not get the advanced stateful rules to work. The rc.firewall file was created for FBSD 2.0 and has not been updated to exclusively utilize the advanced stateful rule set, so it is a very poor example to be using for your ipfw rules set. Cable internet access became available in my area and I was forced to revisit the divert natd / advanced stateful rules again because (DSL, Cable, T1) 'all ways on' environments normally use the ISP's DHCP server to get it's network configuration information so user ppp -nat is not used in this case. This meant I had to use the divert natd ipfw statement to provide the NAT function so I could use private ip address for my LAN because my cable ISP only issues one dynamic public ip address per customer account. After many days of trial and error testing I finally found an rules coding logic which functioned correctly using exclusively advanced check-state/keep-state stateful rules and the divert natd rule statement. Normally the rule to allow the packets from local LAN Nic cards to pass through the ipfw firewall come before the divert natd rule as seen in the rc.firewall file. But for advanced stateful rules it has to be moved after the divert natd rule and the 'keep-state' option has to be used so the dynamic rules table knows about the packet activity before they get passed through the rules file the second time. Technically this means each packet will have 2 sets of dynamic table rules, one set for the private Nic interface and one for the public Nic interface. This is an resource waste, decreases performance, and not necessary if the nat function is done outside of ipfw. The simplest and best solution to the advanced stateful rules problem is to use 'user ppp -nat' for all dialup ISP environments and have no divert natd rule in the ipfw rules file. For all DSL, cable, and T1 connection where the ISP's DHCP is used to configure FBSD's public network you have to use the divert natd rule in your ipfw rules set followed by this rule for each private Nic interface, 'allow all from any to any via xl0 keep-state' where xl0 is the private Nic card interface device name. This solution has been tested in FBSD version 4.5 & 4.6. The IPFW rules listed below are my current firewall rules file configured for a cable divert natd environment. Here are the matching /etc/rc.conf options ifconfig_rl0="DHCP" ppp_enable="NO" natd_enable="YES" natd_interface="rl0" natd_flags="-dynamic" firewall_enable="YES" firewall_script="/etc/ipfw.rules.conf" For an user ppp dialup modem ISP connection using 'divert natd' make following changes to the ipfw rules below Change oif="rl0" to oif="tun0" Here are the matching /etc/rc.conf options #ifconfig_rl0="DHCP" ppp_enable="YES" ppp_mode="ddial" ppp_profile="papchat" ppp_nat="NO" natd_enable="YES" natd_interface="tun0" natd_flags="-dynamic" firewall_enable="YES" firewall_script="/etc/ipfw.rules.conf For an user ppp dialup modem ISP connection using 'user ppp -nat' make following changes to the ipfw rules below Change oif="rl0" to oif="tun0" Add $cmd 00130 allow all from any to any via xl0 Delete $cmd 00150 divert natd all from any to any via $oif Delete $cmd 00210 allow all from any to any via xl0 keep-state Here are the matching /etc/rc.conf options #ifconfig_rl0="DHCP" ppp_enable="YES" ppp_mode="ddial" ppp_profile="papchat" ppp_nat="YES" natd_enable="NO" #natd_interface="tun0" #natd_flags="-dynamic" firewall_enable="YES" firewall_script="/etc/ipfw.rules.conf Following the rules file below are some other IP stack security options which are specified in the /etc/rc.conf file and kernel that you can use as a guide to configure your own world. /etc/ipfw.rules.conf ########################################################################### # # Define IPFW firewall rules for gateway.poweruser.net # 7/04/2002 Joe Barbish # # Cable modem connection to ISP with dynamic IP addresses assigned. # Private Ip address used inside. # 3 win98 boxes on LAN with DHCP used for auto private network configure. # Protect the whole private network from loss of service attacks # These rules can be reloaded with out rebooting by issuing this command # sh /etc/ipfw.rules.conf # # The use of 'me' in rules means IP address 127.0.0.0 localhost # # Firewall Policy Statement. # Each public internet function must be explicitly allowed by a rule. # Only valid response to the packets I've sent out are allowed in. # All packets must use the IPFW advanced "dynamic" rules function. # No state-less rules or simple-stateful rules are allowed to grant # internet function. ############################################################################# # Flush out the list before we begin. /sbin/ipfw -q -f flush # Set rules command prefix # The -q option on the command is for quite mode. # Do not display rules as they load. Remove during development to see. cmd="/sbin/ipfw -q add" # Set defaults # set your outside interface network device name and # domain name servers IP address to values issued by your ISP. oif="rl0" # Nic card to cable modem public internet connection odns1="24.50.201.66" # ISP's dns server 1 IP address odns2="24.52.201.66" # ISP's dns server 2 IP address # Set these to your inside interface network and ip address range iif="xl0" # Nic card to private internal Local area network # This is the start of the rules. # All traffic coming in from the internet or # leaving the local LAN start here # Internal gateway housekeeping # Rules # 100 exempt everything on localhost behind the firewall from this rules set. # Rules # 110 & 120 deny the reference to the localhost default IP address. $cmd 00100 allow all from any to any via lo0 # allow all localhost $cmd 00110 deny log all from any to 127.0.0.0/8 # deny use of localhost IP $cmd 00120 deny log all from 127.0.0.0/8 to any # deny use of localhost IP # This does the Network Address translation of every packet coming in # or going out over the public internet. $cmd 00150 divert natd all from any to any via $oif #*** TESTING PURPOSES ONLY *** TESTING PURPOSES ONLY *** TESTING PURPOSES ONLY # The following rule if un-commented will change the behavior of this # Firewall rule set from closed to completely open, thus bypassing all of the # following rules. This single rule is placed here for TESTING PURPOSES ONLY. #$cmd 00160 allow log logamount 500 all from any to any #$cmd 00161 allow all from any to any ######## control section ############################################ # Start of IPFW advanced Stateful Filtering using "dynamic" rules. # The check-state statement behavior is to match bi-directional packet traffic # flow between source and destination using protocol/IP/port/sequence number. # The dynamic rule has a limited lifetime which is controlled by a set of # sysctl(8) variables. The lifetime is refreshed every time a matching # packet is found in the dynamic table. # Allow the packet through if it has previous been added to the # the "dynamic" rules table by an allow keep-state statement. $cmd 00200 check-state # Run all private Lan packet traffic through the dynamic rules # table so the IP address are in sync with Natd. $cmd 00210 allow all from any to any via xl0 keep-state # Deny all fragments as bogus packets $cmd 00250 deny all from any to any frag in via $oif # Deny ACK packets that did not match the dynamic rule table $cmd 00260 deny tcp from any to any established in via $oif ######## outbound section ############################################ # Interrogate packets originating from behind the firewall, private net. # Upon a rule match, it's keep-state option will create a dynamic rule. # Allow out non-secure standard www function $cmd 00300 allow tcp from any to any 80 out via $oif setup keep-state # Allow out secure www function https over TLS SSL $cmd 00301 allow tcp from any to any 443 out via $oif setup keep-state # Allow out access to my ISP's Domain name server. $cmd 00310 allow tcp from any to $odns1 53 out via $oif setup keep-state $cmd 00311 allow udp from any to $odns1 53 out via $oif keep-state $cmd 00315 allow tcp from any to $odns2 53 out via $oif setup keep-state $cmd 00316 allow udp from any to $odns2 53 out via $oif keep-state # Allow out send & get email function $cmd 00330 allow tcp from any to any 25 out via $oif setup keep-state $cmd 00331 allow tcp from any to any 110 out via $oif setup keep-state # Allow out & in FBSD (make install & CVSUP) functions # Basically give user id root "GOD" privileges. $cmd 00340 allow tcp from me to any out via $oif setup keep-state uid root # Allow out & in console traceroot command $cmd 00342 allow udp from me to any 33435-33500 out via $oif keep-state $cmd 00343 allow log icmp from any to me icmptype 3,11 in via $oif limit src-addr 2 # Allow out ping $cmd 00350 allow icmp from any to any out via $oif keep-state ############ passive FTP rules to public Internet ###### # Allow passive FTP control channel 21 & data high ports $cmd 00375 allow tcp from me to any 21 out via $oif setup keep-state $cmd 00376 allow tcp from me to any 10000-65000 out via $oif setup keep-state ############ End of passive FTP rules to public Internet ###### # Allow out ssh $cmd 00380 allow tcp from any to any 22 out via $oif setup keep-state # Allow out TELNET $cmd 00390 allow tcp from any to any 23 out via $oif setup keep-state # Allow out Network Time Protocol (NTP) queries #$cmd 00394 allow tcp from any to any 123 out via $oif setup keep-state #$cmd 00395 allow udp from any to any 123 out via $oif keep-state # Allow out Time $cmd 00396 allow tcp from any to any 37 out via $oif setup keep-state $cmd 00397 allow udp from any to any 37 out via $oif keep-state # Allow out ident #$cmd 00400 allow tcp from any to any 113 out via $oif setup keep-state #$cmd 00401 allow udp from any to any 113 out via $oif keep-state # Allow out IRC #$cmd 00410 allow tcp from any to any 194 out via $oif setup keep-state #$cmd 00411 allow udp from any to any 194 out via $oif keep-state # Allow out whois $cmd 00412 allow tcp from any to any 43 out via $oif setup keep-state $cmd 00413 allow udp from any to any 43 out via $oif keep-state # Allow out whois++ #$cmd 00415 allow tcp from any to any 63 out via $oif setup keep-state #$cmd 00416 allow udp from any to any 63 out via $oif keep-state # Allow out finger #$cmd 00420 allow tcp from any to any 79 out via $oif setup keep-state #$cmd 00421 allow udp from any to any 79 out via $oif keep-state # Allow out nntp news $cmd 00425 allow tcp from any to any 119 out via $oif setup keep-state $cmd 00426 allow udp from any to any 119 out via $oif keep-state # Allow out gopher #$cmd 00430 allow tcp from any to any 70 out via $oif setup keep-state #$cmd 00431 allow udp from any to any 70 out via $oif keep-state ######## inbound section ############################################ # Interrogate packets originating from in front of the firewall, public net. # Place statements here to allow public requests for service. # Allow in www $cmd 00600 allow tcp from any to any 80 in via $oif setup keep-state limit src-addr 4 # Allow TCP FTP control channel in & data channel out $cmd 00610 allow tcp from any to me 21 in via $oif setup keep-state limit src-addr 4 $cmd 00611 allow tcp from any 20 to any 1024-49151 out via $oif setup keep limit src-addr 4 # Allow in ssh function $cmd 00620 allow log tcp from any to me 22 in via $oif setup keep-state limit src-addr 4 # Allow in Telnet $cmd 00630 allow tcp from any to me 23 in via $oif setup keep-state limit src-addr 4 # Allow in Ping $cmd 00635 allow log icmp from any to me icmptype 0,8 in via $oif # This sends a RESET to all ident packets. #$cmd 00640 reset log tcp from any to me 113 in via $oif limit src-addr 4 ######## Catch all section ############################################ #### Start Special rules for Adelphia Cable ######################### #valid dhcp broadcast from Adelphia dhcp server $cmd 00700 allow UDP from 0.0.0.0 68 to 255.255.255.255 67 in via rl0 # valid FBSD dhcp client request for dns config info $cmd 00701 allow udp from me 68 to $odns1 67 out via rl0 $cmd 00702 allow udp from $odns1 67 to me 68 in via rl0 # invalid bogus packets on Adelphia Cable network. $cmd 00705 deny udp from any to 255.255.255.255 in via rl0 $cmd 00706 deny udp from 0.0.0.0 to any in via rl0 # P:2 $cmd 00707 deny all from 192.168.100.1 to 224.0.0.1 in via rl0 $cmd 00708 deny udp from $odns1 53 to me in via rl0 #### End Special rules for Adelphia Cable ######################### # Stop & log external redirect requests. $cmd 00720 deny log icmp from any to any icmptype 5 in via $oif # Stop & log spoofing Attack attempts. # Examine incoming traffic for packets with both a source and destination # IP address in my local domain as per CIAC prevention alert. $cmd 00730 deny log ip from me to me in via $oif # Stop & log ping echo attacks # stop echo reply (ICMP type 0), and echo request (type 8). $cmd 00740 deny log icmp from any to me icmptype 0,8 in via $oif # Reject & Log all setup of tcp incoming connections from the outside $cmd 00750 deny log tcp from any to any setup in via $oif # Reject & Log all netbios service. 137=name, 138=datagram, 139=session # netbios is ms/windows sharing services. $cmd 00760 deny log tcp from any to any 137,138,139 in via $oif $cmd 00761 deny log udp from any to any 137,138,139 in via $oif # Reject all port 80 http packets that fall through to here. # These packets are auto spawn web page requests from within # original web page request. $cmd 00770 deny tcp from any to any 80 out via $oif # Everything else is denied by default # deny and log all packets that fell through to see what they are $cmd 00950 deny log logamount 500 all from any to any ################## End Of IPFW Firewall Rules ######################### Other IP stack security options. The main run control configuration file /etc/rc.conf has a whole group of run time security options to control the flood of falsified packets entering the system which get control before IPFW evens knows their coming in. The following is from my rc.conf file. # Required IPFW kernel firewall support # For more info see # www.onlamp.com/pub/a/bsd/2001/04/25/FreeBSD_Basics.html # firewall_enable="YES" # Start daemon firewall_script="/etc/ipfw.stdrules" # run my custom rules if present # sh /etc/ipfw.stdrules will load # new rules file after editing. filewall_logging="YES" # Enable events logging # Extra firewalling options log_in_vain="YES" # NO is default. YES enables logging of # connection attempts to ports that have no # listening socket on them. Put msg on consol icmp_drop_redirect="YES" # YES will cause the kernel to ignore # ICMP REDIRECT packets. tcp_drop_synfin="YES" # YES will cause the kernel to ignore TCP # frames that have both the SYN and FIN flags # set. Only available if the kernel was built # with the TCP_DROP_SYNFIN option. # change to NO if web server behind firewall. tcp_restrict_rst="YES" # YES will cause the kernel to refrain from # emitting TCP RST frames in response to # invalid TCP packets (e.g., frames destined # for closed ports). This option is only # available if the kernel was built with the # TCP_RESTRICT_RST option. syslogd_flags="-ss" # Don't use network sockets so portscan # will not find (security tip) portmap_enable="NO" # Don't allow nfs portmapper (security tip) The log_in_vain="YES" option will post a message to the root console screen every time it stops a packet. This became very annoying so I changed the syslog to put these messages in the security log. All the ipfw messages that were going to the /var/log/security file was also going to the /var/log/message file. I did not think it was wise to be posting ipfw messages in more that one place, so I stopped them from going to the message file. Below are the lines I changed in /etc/syslog.conf to make this happen. The original lines. *.err;kern.debug;auth.notice;mail.crit /dev/console *.notice;kern.debug;lpr.info;mail.crit;news.err /var/log/messages security.* /var/log/security replaced by this lines # kern.info is where the log_in_vain messages come from. The following # will stop the log_in_vain messages from coming out on root console & # put them in the security log. 2/20/2002 Joe Barbish # remove kern.info messages from /dev/console & /var/log/messages # and put them into /var/log/security. *.err;auth.notice;mail.crit /dev/console kern.notice;kern.=debug /dev/console *.notice;lpr.info;mail.crit;news.err /var/log/messages kern.notice;kern.=debug /var/log/messages security.*;kern.=info /var/log/security Another very obscure option is blackhole, new in FBSD 4.4 The blackhole sysctl(8) is used to control system behavior when connection requests are received on TCP or UDP ports where there is no socket listening. Normal behavior, when a TCP SYN segment is received on a port where there is no socket accepting connections, is for the system to return a RST segment, and drop the connection. The connecting system will see this as a "Connection reset by peer". By setting the TCP blackhole MIB to a numeric value of 1, the incoming SYN segment is merely dropped, and no RST is sent, making the system appear as a blackhole. By setting the MIB value to 2, any segment arriving on a closed port is dropped without returning a RST. This provides some degree of protection against stealth port scans. In the UDP instance, enabling blackhole behavior turns off the sending of an ICMP port unreachable message in response to a UDP datagram which arrives on a port where there is no socket listening. It must be noted that this behavior will prevent remote systems from running traceroute(8) to a system. The blackhole behavior is useful to slow down anyone who is port scanning a system, attempting to detect vulnerable services on a system. It could potentially also slow down someone who is attempting a denial of service attack. The sysctl net.inet.tcp.blackhole=2 command can be entered from the command line and will be in effect until the next boot. The sysctl command can also be in the /etc/sysctl.conf file (which you must create) and if present will be activated during the boot process. Read man sysctl for command format to display settings of this option and some others that allow you to change to default dynamic rules time out values. For the really advanced technical ipfw user check out ipfw user patches at http://people.freebsd.org/~cjc/ See http://bsdvault.net/sections.php?op=viewarticle&artid=57 for info on sysctl. See http://www.practicallynetworked.com/sharing/app_port_list.htm for a list of ports used by different applications. /etc/sysctl.conf file contents sysctl net.inet.tcp.blackhole=2 sysctl net.inet.udp.blackhole=1 Here are the statements for the kernel source to include IPFW in the kernel. # # The following options add sysctl variables for controlling how certain # TCP packets are handled by the kernel. # options ICMP_BANDLIM # Enables icmp error response bandwidth # limiting. This will help protect from # D.O.S. packet attacks. option TCP_DROP_SYNFIN # Adds support for ignoring TCP packets # with SYN+FIN. This prevents nmap from # identifying the TCP/IP stack, but # breaks support for RFC1644 extensions # & is not recommended for web servers. # not supported in 4.4 & newer #option TCP_RESTRICT_RST # Adds support for blocking emission of # TCP RST packets. Useful in limiting # SYN floods & port scanning. # Enable kernel IPFW, the FBSD supplied packet filtering and accounting system # Has a FBSD supplied user land control utility ipfw. # option IPFIREWALL # Adds filtering code into kernel option IPFIREWALL_VERBOSE # enable logging thru syslogd(8) option IPFIREWALL_VERBOSE_LIMIT=10 # stop attack via syslog flooding option IPFIREWALL_IPDIVERT # Enable NATD divert function