Among the three possible firewalls on FreeBSD (choice is always nice) IPFW is the in-house built one. There is a default, easy way, configuration path but if one needs to build a box to act as a dedicated network appliance with packet filtering capacity fine tunning the IPFW firewall configuration is more than desirable.
Before start reading, for those unfamiliar with networking nomenclature I have myself written an informal networking dictionary article. I’d also suggest to have a read on any decent book about networking. If you happen to have Michael W. Lucas’s last edition of ‘Absolute FreeBSD’ a re-read of chapters 7 and 8 can be useful. Obviously the FreeBSD handbook has a detailed and nice entry on IPFW.
A word of warning. The firewall is typically doing the heavy lifting security wise and the rest is left out or other threads are considered harmless. Many organizations fall into this issue because it is an easy one to fall into, not because they are not smart, not because they are just wrong. It is easy to take this approach and because the network applicance is the first line of defense, it is often times the first thing a nefarious actor encounters. But that approach is ‘Wrong’ because security should be composed, since having an only point of failure will eventually drive anyone to a certain fail. Plus it is not always that easy to have a nice firewall ruleset that covers every aspect, it is safe, etc.
The easy way to configure the IPFW firewall on FreeBSD for a simple box can be found on this old article. However that article is way too simple and it doesn’t explain much. Let’s address that here though.
If you find the articles in Adminbyaccident.com useful to you, please consider making a donation.
Use this link to get $200 credit at DigitalOcean and support Adminbyaccident.com costs.
Get $100 credit for free at Vultr using this link and support Adminbyaccident.com costs.
Mind Vultr supports FreeBSD on their VPS offer.
As you may already know, or at least you should if you are using FreeBSD, services are typically configured to fire up setting up entries like the ones in that article inside the /etc/rc.conf file. To configure IPFW in its simplest form we will use the commnads from that article, which are explained here below.
To enable the firewall at boot time:
$ sudo sysrc firewall_enable="YES"
To make it ‘quiet’, so it does not prompt messages to take actions in the shell, we will issue the next one:
$ sudo sysrc firewall_quiet="YES"
To only protect our server we will type:
$ sudo sysrc firewall_type="workstation"
To allow the services we need, SSH and HTTP, we will set the following:
$ sudo sysrc firewall_myservices="22/tcp 80/tcp"
One can add more services, like HTTPS using an entry like ‘443/tcp’, or even setting it by name ‘https’.
To allow any external ip to make use of the above services we will type:
$ sudo sysrc firewall_allowservices="any"
To register those denied accesses, be it because they lack the ssh key, because they are hitting a port we haven’t enabled, etc, we will issue the next command:
$ sudo sysrc firewall_logdeny="YES"
To check the lines have been correctly added to /etc/rc.conf file we can use the following command:
$ cat /etc/rc.conf | grep firewall
As output we should get the following:
firewall_enable="YES"
firewall_quiet="YES"
firewall_type="workstation"
firewall_myservices="22/tcp 443/tcp"
firewall_allowservices="any"
firewall_logdeny="YES"
We are good to go and fire up the IPFW firewall.
$ sudo service ipfw start
To check it is working we will use:
$ sudo service ipfw status
As output we should obatin something like this:
ipfw is enabled
Now. How does this magic work? There is a script file in /etc/ called rc.firewall where some pre-configured sane rules are already written. If you use the cat command or any GUI utility to read the file you will find some of the lines above being described in it. And some other options.
However, is this enough? This may be reasonable and useful for many. For example setting up a workstation type of firewall will suffice for the typical desktop user. Is this enough if one prettends to build a networking control device for a small to medium size company office? Not quite.
There is an option when enabling IPFW firewall in the /etc/rc.conf file making it to read a series of rules from an specific file of our own. This: firewall_script=”/etc/ipfw.rules”. Although we can change the name of the file or its path.
Before getting to write some custom rules for our purpose we need to address some topics. For example, almost all (not to say all) firewalls read the rules found in their configuration files from top to bottom. Some do not take action until they’ve gone through each of them, although you can declare some specific rule that is matched to be executed immediately before getting to the very bottom of the config file. Many firewalls execute rules whenever there is a first match immediately.
A second consideration is rule placement. This can be a tricky one. Two rules can conflict between them. In complex environments you may want to allow a group of users to access certain portions of your network but forbid others accessing that portion, or others including that specific one. Avoiding the ‘any’ word in some configurations is not just desirable but a security necessity. Anyone coming from ‘A’ and going to ‘B’ shouldn’t be allowed can be read in an upper sentence but a bit later one may have written, anyone coming from ‘A’, or say ‘C’ if you so wish, and going to ‘B’ is allowed in a lower sentence. The first rule is invalidating the second one, so people from A won’t get into B. That may be desired. But what happends to people who are in A but happen to be in C too? Furthermore, what happens if that kind of firewall doesn’t act on the first rule matches premise, and reads the whole file first and adds the two rules as action? First it will block access, acting for the first rule, but then it will allow access, because that is what the second rule says.
The optimization of rulesets for packet filtering, nat, etc is not only a necessity to have a working configuration and a safe one but does also impact on performance. The more rules the system has to process, the more it impacts performance. Of course small offices with very little traffic can enjoy good results given the right hardware. But things can get messy quite quickly as things scale up. Order does also impact performance since the quicker a rule is hit the less processing power it takes.
There is no correct nor bad answer but achieving the desired effect. It all depends on the specific need and/or goal. That said, and old version of the FreeBSD handbook has some interesting tips on this particular question of a balanced and effective manual IPFW configuration ruleset.
I quote it straight away since the wording seems excellent, to the point, at least at my eyes. I can’t see why this is not included in today’s handbook as a starting point with some tips.
«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 blocking and logging all packets on that interface and direction.
The Outbound section in the following ruleset 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 option 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. The first is that malicious packets may be partial matches for legitimate traffic. These packets have to be discarded rather than allowed in, based on their partial matches against allow rules. The second reason is that known and uninteresting rejects may be blocked silently, rather than being caught and logged by the last rules in the section. The final rule in each section, blocks and logs all packets and can be used to create the legal evidence needed to prosecute the people who are attacking your system.
Another thing that should be taken care of, is to insure there is no response returned for any of the undesirable stuff. Invalid packets should 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 secure it is. Packets with unrecognized port numbers may be looked up in /etc/services/ or go to http://www.securitystats.com/tools/portsearch.php and do a port number lookup to find the purpose of the particular port number is. Check out this link for port numbers used by Trojans: http://www.simovits.com/trojans/trojans.html.»
With the help of the manual and these tips described from the FreeBSD 7.4 IPFW firewall chapter I have drafted a couple of examples.
Disclaimer: Before making use of the following example of rulesets you are at your own risk and I will not be responsible under any circumstance of any issues and/or damage this may cause to you, your company or organization.
Example of a workstation ruleset:
################ Start of IPFW rules file ###############################
#!/bin/sh
# Flush out the list before we begin.
ipfw -q -f flush
# Set rules command prefix
cmd="ipfw -q add"
net="em0" # interface name of NIC attached to Internet
#################################################################
# No restrictions on the Loopback Interface
#################################################################
$cmd 00005 allow ip from any to any via lo0
#################################################################
# Allow the packet through if it has previously been added to the
# the "dynamic" rules table by an ‘allow keep-state’ statement.
#################################################################
$cmd 00010 check-state :default
#################################################################
# Interface facing Public Internet (Outbound Section)
# Interrogate session start requests originating from behind the
# firewall on the private network or from this gateway server
# destined for the public Internet.
#################################################################
# Allow out access to my ISP's Domain name server.
# 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 00020 allow tcp from any to 192.168.1.1 53 out via $net setup keep-state
$cmd 00025 allow udp from any to 192.168.1.1 53 out via $net keep-state
# Allow out non-secure standard www function
$cmd 00030 allow tcp from any to any 80 out via $net setup keep-state
# Allow out secure www function https over TLS SSL
$cmd 00035 allow tcp from any to any 443 out via $net setup keep-state
# Allow out send and get email function
$cmd 00040 allow tcp from any to any 25 out via $net setup keep-state
$cmd 00041 allow tcp from any to any 110 out via $net setup keep-state
# Allow outbound ping
$cmd 00050 allow icmp from any to any out via $net keep-state
# Allow outbound SSH
$cmd 00060 allow tcp from any to any 22 out via $net setup keep-state
# Allow out whois
$cmd 00090 allow tcp from any to any 43 out via $net setup keep-state
# Deny and log everything else that is trying to get out.
# This rule enforces the block all by default logic.
$cmd 00099 deny log all from any to any out via $net
#################################################################
# Interface facing Public Internet (Inbound Section)
# Check packets originating from the public Internet
# destined 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 $net #RFC 1918 private IP
$cmd 00301 deny all from 172.16.0.0/12 to any in via $net #RFC 1918 private IP
$cmd 00302 deny all from 10.0.0.0/8 to any in via $net #RFC 1918 private IP
$cmd 00303 deny all from 127.0.0.0/8 to any in via $net #loopback
$cmd 00304 deny all from 0.0.0.0/8 to any in via $net #loopback
$cmd 00305 deny all from 169.254.0.0/16 to any in via $net #DHCP auto-config
$cmd 00306 deny all from 192.0.2.0/24 to any in via $net #reserved for docs
$cmd 00307 deny all from 204.152.64.0/23 to any in via $net #Sun cluster interconnect
$cmd 00308 deny all from 224.0.0.0/3 to any in via $net #Class D & E multicast
$cmd 00309 deny all from any to ::1 in via $net
$cmd 00310 deny all from ::1 to any in via $net
# Deny public pings
$cmd 00320 deny icmp from any to any in via $net
# Deny ident
$cmd 00330 deny tcp from any to any 113 in via $pif
# Deny all Netbios services. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
$cmd 00340 deny tcp from any to any 137 in via $net
$cmd 00341 deny tcp from any to any 138 in via $net
$cmd 00342 deny tcp from any to any 139 in via $net
$cmd 00343 deny tcp from any to any 81 in via $net
# Deny any late arriving packets
$cmd 00340 deny all from any to any frag in via $net
# Deny ACK packets that did not match the dynamic rule table
$cmd 00345 deny tcp from any to any established in via $net
# Allow in standard www function because I have apache server
$cmd 00400 allow tcp from any to me 80 in via $net setup limit src-addr 2
$cmd 00401 allow tcp from any to me 443 in via $net setup limit src-addr 2
# Allow inbound SSH
# Ideally you should declare what ip or groups of ips can access
# instead of setting 'any'
$cmd 00450 allow tcp from any to me 22 in via $net setup keep-state
# Reject & Log all incoming connections from the outside
$cmd 00499 deny log all from any to any in via $net
# 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 ###############################
Mind the separation of the above ruleset between the outgoing and ingoing sections as well as the blocking of the reserved block typically used for local addresses. This configuration however allows traffic from the 192.168.0.0 range since that is the network many boxes at home will be sitting on. And that is necessary for services to be able to work. That said, if one wishes to block all LAN traffic, even from the ‘trusted’ local network so infected machines can’t spread their malicious payloads so easily the configuration can be adapted as follows.
For the blocking rules uncomment the 00300 ruleset:
# Deny all inbound traffic from non-routable reserved address spaces
$cmd 00300 deny all from 192.168.0.0/16 to any in via $net #RFC 1918 private IP
$cmd 00301 deny all from 172.16.0.0/12 to any in via $net #RFC 1918 private IP
And if services like SSH are needed from one specific address a rule for that purpose can be put just above the denying block. Why above? Remember, IPFW works as a ‘first match wins’, so the first rule that is matched is the one acting. So we need to allow SSH from the desired IP before blocking all.
# Allow inbound SSH
$cmd 00250 allow tcp from 192.168.1.100 to me 22 in via $net setup keep-state
Other services needed to be used can be added, for example:
# Allow in standard www function because I have an Apache HTTP server
$cmd 00260 allow tcp from any to me 80 in via $net setup limit src-addr 2
$cmd 00265 allow tcp from any to me 443 in via $net setup limit src-addr 2
Ping can also added as incoming packets.
# Allow public pings
$cmd 00270 allow icmp from any to any in via $net limit src-addr 2
These changes are useful when the LAN can’t be trusted. If the FreeBSD box is sitting in an office this may be too extreme, depending on the environment, the needs and the personal criteria of the network and systems admin. However this may be a must have on laptops running around at coffee shops, non trustworthy customer’s networks and the like. Because this is UNIX and changes can be easily applied by modifying text files you may for example want to work on your web content while you are connected to one of those non trusted networks, because you need to. You can adjust the rules as follows so you will be the only able to visit that content your local Apache HTTP is serving.
# Allow in standard www function because I have an Apache HTTP server
$cmd 00260 allow tcp from me to me 80 in via $net setup limit src-addr 1
$cmd 00265 allow tcp from me to me 443 in via $net setup limit src-addr 1
Of course, adding these rule changes is needed but do also remove the old ones, such as 00400, 00401, 00450, so only the specific ips declared on the new adjusted rules are welcome and the rest is denied access.
Let’s move now to a different type of set up, this time a web server.
Example of a web server ruleset:
################ Start of IPFW rules file ###############################
################### Web Server Example ##################################
#!/bin/sh
# Flush out the list before we begin.
ipfw -q -f flush
# Set rules command prefix
cmd="ipfw -q add"
net="em0" # interface name of NIC attached to Internet
#################################################################
# No restrictions on the Loopback Interface
#################################################################
$cmd 00005 allow ip from any to any via lo0
#################################################################
# Allow the packet through if it has previously been added to the
# the "dynamic" rules table by an ‘allow keep-state’ statement.
#################################################################
$cmd 00010 check-state :default
#################################################################
# Interface facing Public Internet (Outbound Section)
# Interrogate session start requests originating from behind the
# firewall on the private network or from this gateway server
# destined for the public Internet.
#################################################################
# Allow out non-secure standard www function
$cmd 00030 allow tcp from me to any 80 out via $net setup keep-state
# Allow out secure www function https over TLS SSL
$cmd 00035 allow tcp from me to any 443 out via $net setup keep-state
# Allow out send and get email function
# Uncomment if you need to use these ports to send email say from WordPress
# $cmd 00040 allow tcp from me to any 25 out via $net setup keep-state
# $cmd 00041 allow tcp from me to any 110 out via $net setup keep-state
# Allow outbound ping
$cmd 00050 allow icmp from any to any out via $net keep-state
# Allow outbound SSH
# Uncomment this rule if you need to access other servers from this one
# $cmd 00060 allow tcp from any to any 22 out via $net setup keep-state
# Deny and log everything else that is trying to get out.
# This rule enforces the block all by default logic.
$cmd 00099 deny log all from any to any out via $net
#################################################################
# Interface facing Public Internet (Inbound Section)
# Check packets originating from the public Internet
# destined 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 $net #RFC 1918 private IP
$cmd 00301 deny all from 172.16.0.0/12 to any in via $net #RFC 1918 private IP
$cmd 00302 deny all from 10.0.0.0/8 to any in via $net #RFC 1918 private IP
$cmd 00303 deny all from 127.0.0.0/8 to any in via $net #loopback
$cmd 00304 deny all from 0.0.0.0/8 to any in via $net #loopback
$cmd 00305 deny all from 169.254.0.0/16 to any in via $net #DHCP auto-config
$cmd 00306 deny all from 192.0.2.0/24 to any in via $net #reserved for docs
$cmd 00307 deny all from 204.152.64.0/23 to any in via $net #Sun cluster interconnect
$cmd 00308 deny all from 224.0.0.0/3 to any in via $net #Class D & E multicast
$cmd 00309 deny all from any to ::1 in via $net
$cmd 00310 deny all from ::1 to any in via $net
# Deny any late arriving packets
$cmd 00320 deny all from any to any frag in via $net
# Deny ACK packets that did not match the dynamic rule table
$cmd 00325 deny tcp from any to any established in via $net
# Allow in HTTP and HTTPS connections
$cmd 00330 allow tcp from any to me 80 in via $net setup limit src-addr 2
$cmd 00331 allow tcp from any to me 443 in via $net setup limit src-addr 2
# Deny ident
$cmd 00340 deny tcp from any to any 113 in via $pif
# Deny all Netbios services. 137=name, 138=datagram, 139=session
# Netbios is MS/Windows sharing services.
# Block MS/Windows hosts2 name server requests 81
$cmd 00350 deny tcp from any to any 137 in via $net
$cmd 00351 deny tcp from any to any 138 in via $net
$cmd 00352 deny tcp from any to any 139 in via $net
$cmd 00353 deny tcp from any to any 81 in via $net
# Allow inbound SSH
# Ideally you should declare what ip or groups of ips can access
# instead of setting 'any'
$cmd 00400 allow tcp from any to me 22 in via $net setup keep-state
# Deny public pings
# Uncomment the rule to deny pings from anywhere
# $cmd 00410 deny icmp from any to any in via $net
# Allow public pings from one source
$cmd 00415 allow icmp from any to any in via $net limit src-addr 1
# Reject & Log all incoming connections from the outside
$cmd 00499 deny log all from any to any in via $net
# 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 ###############################
But how to write these rulesets? Following the same premises described above we have a good starting point.
Premise 1.- First all the free unmolested interfaces, then the outbound and finally the inbound.
Premise 2.- The order of the rules on the public facing interfaces should have in mind the frequency of use of the rules, so the more frequently matched rules are placed before the less likely.
Premise 3.- In the inbound section we will place the blocking rules.
Premise 4.- At each section we will place a ‘final’ rule should block and log all the packets that haven’t matched any of the rules above it.
However we can add some other premises prior to those. Which ones?
Premise a) List your needs. Your needs will tell you what ports are needed to be open and what interfaces will be used.
Premise b) List your security concerns. This will help you select the optimal action for each rule.
Premise c) List your best guess on the most matched rules and plan to even break the order from the numbered premises above.
Arrived to this point we’ve been addressing how to make a rulset but the examples above do only cover one box, serving as a workstation or as a web server. One can adapt any of those examples to other needs, adding services, fine tune which ip addresses are allowed in instead of using the ‘any’ command, etc. But IPFW can go beyond and with what’s been shown combined with other capacities of the tool can become a dedicated network appliance given the right hardware.
IPFW is capable of doing more things. As found in the handbook it can also filter NAT (Network Address Translation) and redirected traffic. In future articles those capabilities will be addressed.
If you find the articles in Adminbyaccident.com useful to you, please consider making a donation.
Use this link to get $200 credit at DigitalOcean and support Adminbyaccident.com costs.
Get $100 credit for free at Vultr using this link and support Adminbyaccident.com costs.
Mind Vultr supports FreeBSD on their VPS offer.