17 September 2006

A fancy home firewall

If you've got a home network or are running any sort of network server that you want open to the internet, then the bare bones home firewall won't work for you. You need something a little fancier.

Before we get to the script and rules there are a couple of requirements. I am using ppp to connect to the internet and that is an important part of this puzzle because I am using ppp's own NAT (network address translation) functionality. If you're at home, you're probably using ppp to connect as well. It's typically used for dial-up, DSL, and cable connections where your provider is also assigning you an address via DHCP. I can't go into all of the nitty-gritty of setting up ppp here. (For that matter, I have to assume that the private/home lan is already properly set-up as well.) But, once ppp's set-up and working, if you want a home network behind your FreeBSD machine to be able to share that connection, you should enable its NAT capability by placing 'ppp_nat="YES"' into /etc/rc.conf. (If you have some sort of direct internet connection and are not running ppp, you might have to use natd and divert ports. The rules below probably will not work for you.)

Your machine still won't be forwarding the home lan traffic until you also ensure that it's set to act as a gateway. (Not enabled by default. A part of the secure-and-sane-defaults philosophy.) To do that place 'gateway_enable="YES"' into /etc/rc.conf.

These changes to rc.conf won't become active unless you reboot. But, who wants to reboot and lose all of that impressive uptime? If you want to enable ppp's NAT immediately, kill the running instance then restart it and include the '-nat' option. To allow your machine to act as a gateway immediately, use sysctl. As root, type 'sysctl net.inet.ip.forwarding=1'. You're ready. Now the rules/script (as before, this should be placed in a file somewhere that we will reference in rc.conf):


MYNET='' #choose carefully [note 1]
TRUST=' or 111.222.333.0/25' #choose very carefully! [note 2]

INSIDE='sis0' #don't get this wrong :) [note 3]

$FWCMD add 00100 allow ip from any to any via lo0
$FWCMD add 00200 deny ip from any to
$FWCMD add 00300 deny ip from to any
$FWCMD add 00500 allow ip from any to any via $INSIDE

# game server ... [note 4, note 5]
$FWCMD add 01101 set 2 allow udp from any to me 28000
$FWCMD add 01102 set 2 allow udp from me 28000 to any
# off by default; type 'ipfw set enable 2' to turn it on
$FWCMD set disable 2

$FWCMD add 04000 check-state

$FWCMD add 05000 allow udp from me to any keep-state
$FWCMD add 05100 allow udp from $MYNET to any keep-state
$FWCMD add 06000 deny udp from any to me

# so you can log in from some place else [note 5]
$FWCMD add 07000 allow tcp from \{ $TRUST \} to me 22
$FWCMD add 07100 allow tcp from me 22 to \{ $TRUST \}

$FWCMD add 07500 allow tcp from me to any keep-state
$FWCMD add 07600 allow tcp from $MYNET to any keep-state
$FWCMD add 08000 deny tcp from any to me

# so you may VPN out, for example
$FWCMD add 09000 allow gre from me to any keep-state
$FWCMD add 09100 allow gre from $MYNET to any keep-state
$FWCMD add 09900 deny gre from any to me

$FWCMD add 50000 allow icmp from me to any keep-state
$FWCMD add 51000 allow icmp from $MYNET to any keep-state
$FWCMD add 52000 allow icmp from \{ $TRUST \} to me
# don't make it easy for them
$FWCMD add 53000 deny icmp from any to me icmptype 8
$FWCMD add 54000 allow log icmp from any to me
#$FWCMD add 65535 deny ip from any to any

Note 1. is the private network in this example. You could choose a different address range for this, but it must be RFC-1918 specified private IP space, which is any sub-range of,, or (You can read RFC 1918 here.)

Note 2. I will use the shell variable TRUST to list all the addresses and address ranges that I trust. In this example, that means I'm going to allow them to log in via ssh/port 22 (via rule 07000). Allowing incoming connections is a huge departure from the bare bones firewall. Anyone with access to a machine with an address listed in TRUST can now try to log into your box. I'm using an or-block here just to show what's possible and make the example more interesting. Such arrangements might be used to allow you to log into your box from your friend's house or work, for example. (Strict security would require that only addresses for machines that you control are listed here. If you're doing anything else, you'd better at least be watching your logs carefully for login attempts.) Also see note 5.

Note 3. Elsewhere, we're using the handy ipfw magic of 'me' to specify any IP address configured on the local machine. But, before that we want to keep the home lan wide open to the FreeBSD firewall/router. This is applied in rule 00500. ipfw processes rules sequentially, so with this rule near the beginning, we can be sure that traffic between machines on our own LAN and the firewall machine is uninhibited. Then, for the remainder of the script we can focus on how to protect ourselves from the public internet.

A security purist might say that the "allow any to any" in rule 00500 is too liberal. My own rule of thumb is that the purpose of a firewall is protect you from the world -- not to protect the world from you! But, I'm envisioning a small home LAN here. If you are running an ISP or otherwise have untrusted or unknown machines on your LAN then, yes, you should have rules to prevent abuse coming from your LAN!!

Note 4. If you want to run a public game server, you don't know who is going to be connecting. So, you've got to allow any address and you've got to put these rules before the later blocks which will deny unsolicited traffic. If you were running a web server instead of a game server, you could change these rules by changing the port (28000 => 80) and the protocol ('udp' => 'tcp').

I also use the 'set' option and then disable the set. The rules are then inactive but may be easily enabled all at once (with 'ipfw set enable 2'). If you have a public service always running, you don't need this and can just comment out the disable line ('$FWCMD set disable 2').

(P.S. A public game server can be a big security risk and doesn't typically have all security features of something like apache. You should seriously look into using a jail or a chroot environment.)

Note 5. Here we allow the trusted addresses to reach our ssh port and login. By having both an in rule and an out rule what we've done is to make this traffic stateless. In fact, we did the same thing with the game server. This does a couple of interesting things. First, it prevents the use of more system resources for more dynamic rules for traffic you're expecting anyway. Usually that's negligible, but in the case of a public server it's not at all! It's a vulnerability to allow unknown addresses to cause dynamic rules to be allocated. (To be clear, that would be the case if we had just allowed the in traffic here and let the out traffic be handled by the later 'keep-state' rules.)

Second, it gives you nice in/out counters for the bytes and packets of that traffic. You can see those using the command 'ipfw -at list'. ... Watching and counting different kinds of traffic will hopefully be the subject of a future post, too.

[ tags: , , ]



At 21/10/06 02:22, Blogger biniar said...

If my home network is on a router/switch can I have a FreeBSD machine get the internet from the router then filter it with this rule set to the rest of the network with using the typical two Ethernet card setup?

At 21/10/06 16:09, Blogger kace said...

biniar, Yes, that should work fine as long as your FreeBSD machine can connect to the router via ppp. Also, you'll want to turn off any firewall running on the router/modem. If the router/modem is in any way controlled or admin'd by your ISP there could be problems, though. Test, test, test, and be prepared to roll back if there are problems. Let me know how it goes.

At 25/10/06 16:26, Blogger biniar said...

Wasn't having much luck with small attempts at the basic configuration(s). I am going to just run it like so:

Router --> --+--+--+--+--+---
| | | | |
Server & other internal(s)

but problem was when I changed the rules to my network specifications, it just blocked all traffic.

/etc/rc.conf had the rulesset and enabled the firewall (IPFW)

Any suggestions for using a single ethernet card from behind the router? :)

Sorry if that sounds confusing.

At 25/10/06 23:59, Blogger kace said...

eddie, That is a little confusing. :) You may want to look at some of the preliminary information in the bare bones FW article. In particular, you said that you put the ruleset into /etc/rc.conf, but it should really go into another file that is referenced in /etc/rc.conf. Also, I don't _think_ the "fancy" FW can be made to work with only one interface on the BSD/FW machine.


Post a Comment

<< Home