Dialup firewalling with FreeBSD

$FreeBSD: doc/en_US.ISO8859-1/articles/dialup-firewall/article.sgml,v 1.32 2003/11/05 10:59:33 ceri Exp $

FreeBSD is a registered trademark of Wind River Systems, Inc. This is expected to change soon.

Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this document, and the FreeBSD Project was aware of the trademark claim, the designations have been followed by the ``™'' or the ``®'' symbol.

This article documents how to set up a firewall using a PPP dialup with FreeBSD and IPFW, and specifically with firewalling over a dialup with a dynamically assigned IP address. This document does not cover setting up your PPP connection in the first place.


1 Preface

Dialup Firewalling with FreeBSD

This document covers the process that is required to set up firewalling with FreeBSD when an IP address is assigned dynamically by your ISP. While every effort has been made to make this document as informative and correct as possible, you are welcome to mail your comments/suggestions to the .


2 Kernel Options

The first thing you will need to do is recompile your kernel. If you need more information on how to recompile the kernel, then the best place to start is the kernel configuration section in the Handbook. You need to add the following options into your kernel configuration file:

options IPFIREWALL

Enables the kernel's firewall code.

options IPFW2

Enables the new version of IPFW.

Important: Only do this if you're running FreeBSD 4.X, this is the default in newer versions of FreeBSD.

options IPFIREWALL_VERBOSE

Sends logged packets to the system logger.

options IPFIREWALL_VERBOSE_LIMIT=100

Limits the number of times a matching entry is logged. This prevents your log file from filling up with lots of repetitive entries. 100 is a reasonable number to use, but you can adjust it based on your requirements.

options IPDIVERT

Enables divert sockets, which will be shown later.

There are some other optional items that you can compile into the kernel for some added security. These are not required in order to get firewalling to work, but some more paranoid users may want to use them.

options TCP_DROP_SYNFIN

This option ignores TCP packets with SYN and FIN. This prevents tools like security/nmap from identifying the TCP/IP stack of the machine, but breaks support for RFC1644 extensions. This is not recommended if the machine will be running a web server.

Do not reboot once you have recompiled the kernel. Hopefully, we will only need to reboot once to complete the installation of the firewall.


3 Changing /etc/rc.conf to load the firewall

We now need to make some changes to /etc/rc.conf in order to tell it about the firewall. Simply add the following lines:

firewall_enable="YES"
firewall_script="/etc/firewall/fwrules"
natd_enable="YES"
natd_interface="tun0"
natd_flags="-dynamic"

For more information on the functions of these statements take a look at /etc/defaults/rc.conf and read rc.conf(5)


4 Disable PPP's network address translation

You may already be using PPP's built in network address translation (NAT). If that is the case then you will have to disable it, as these examples use natd(8) to do the same.

If you already have a block of entries to automatically start PPP, it probably looks like this:

ppp_enable="YES"
ppp_mode="auto"
ppp_nat="YES"
ppp_profile="profile"

If so, you will need to specifically disable ppp_nat by making sure you have ppp_nat="NO" in /etc/rc.conf. You will also need to remove any nat enable yes or alias enable yes in /etc/ppp/ppp.conf.


5 The rule set for the firewall

We are nearly done now. All that remains now is to define the firewall rules and then we can reboot and the firewall should be up and running. I realize that everyone will want something slightly different when it comes to their rule base. What I have tried to do is write a rule base that suits most dialup users. You can obviously modify it to your needs by using the following rules as the foundation for your own rule base. First, let's start with the basics of closed firewalling. What you want to do is deny everything by default and then only open up for the things you really need. Rules should be in the order of allow first and then deny. The premise is that you add the rules for your allows, and then everything else is denied. :)

Now, let's make the dir /etc/firewall. Change into the directory and edit the file fwrules as we specified in rc.conf. Please note that you can change this filename to anything you wish. This guide just gives an example of a filename.

Now, let's look at a sample firewall file, that is commented nicely.

# Define the firewall command (as in /etc/rc.firewall) for easy
# reference.  Helps to make it easier to read.
fwcmd="/sbin/ipfw"

# Force a flushing of the current rules before we reload.
$fwcmd -f flush

# Divert all packets through the tunnel interface.
$fwcmd add divert natd all from any to any via tun0

# Allow all connections that have dynamic rules built for them,
# but deny established connections that don't have a dynamic rule.
# See ipfw(8) for details.
$fwcmd add check-state
$fwcmd add deny tcp from any to any established

# Allow all localhost connections
$fwcmd add allow tcp from me to any out via lo0 setup keep-state
$fwcmd add deny  tcp from me to any out via lo0
$fwcmd add allow ip  from me to any out via lo0 keep-state

# Allow all connections from my network card that I initiate
$fwcmd add allow tcp from me to any out xmit any setup keep-state
$fwcmd add deny  tcp from me to any
$fwcmd add allow ip from me to any out xmit any keep-state

# Everyone on the Internet is allowed to connect to the following
# services on the machine.  This example specifically allows connections
# to sshd and a webserver.
$fwcmd add allow tcp from any to me dst-port 22,80 in recv any setup keep-state

# This sends a RESET to all ident packets.
$fwcmd add reset log tcp from any to me 113 in recv any

# Enable ICMP: remove type 8 if you don't want your host to be pingable
$fwcmd add allow icmp from any to any icmptypes 0,3,8,11,12,13,14

# Deny all the rest.
$fwcmd add deny log ip from any to any

You now have a fully functional firewall that will allow on connections to ports 22 and 80 and will log any other connection attempts. Now, you should be able to safely reboot and your firewall should come up fine. If you find this incorrect in anyway or experience any problems, or have any suggestions to improve this page, please email me.


6 Questions

6.1. Why are you using natd(8) and ipfw(8) when you could be using the built in ppp(8) filters?
6.2. I get messages like ``limit 100 reached on entry 2800'' and after that I never see more denies in my logs. Is my firewall still working?
6.3. If I am using private addresses internally, such as in the 192.168.0.0 range, could I add a command like $fwcmd add deny all from any to 192.168.0.0:255.255.0.0 via tun0 to the firewall rules to prevent outside attempts to connect to internal machines?
6.4. There must be something wrong. I followed your instructions to the letter and now I am locked out.

6.1. Why are you using natd(8) and ipfw(8) when you could be using the built in ppp(8) filters?

I will have to be honest and say there is no definitive reason why I use ipfw and natd instead of the built in ppp filters. From the discussions I have had with people the consensus seems to be that while ipfw is certainly more powerful and more configurable than the ppp filters, what it makes up for in functionality it loses in being easy to customize. One of the reasons I use it is because I prefer firewalling to be done at a kernel level rather than by a userland program.

6.2. I get messages like ``limit 100 reached on entry 2800'' and after that I never see more denies in my logs. Is my firewall still working?

This merely means that the maximum logging count for the rule has been reached. The rule itself is still working, but it will no longer log until such time as you reset the logging counters. You can reset the logging counters with the ipfw resetlog command. Alternatively, you may increase the log limit in your kernel configuration with the IPFIREWALL_VERBOSE_LIMIT option as described above. You may also change this limit (without recompiling your kernel and having to reboot) by using the net.inet.ip.fw.verbose_limit sysctl(8) value.

6.3. If I am using private addresses internally, such as in the 192.168.0.0 range, could I add a command like $fwcmd add deny all from any to 192.168.0.0:255.255.0.0 via tun0 to the firewall rules to prevent outside attempts to connect to internal machines?

The simple answer is no. The reason for this is that natd is doing address translation for anything being diverted through the tun0 device. As far as it is concerned incoming packets will speak only to the dynamically assigned IP address and not to the internal network. Note though that you can add a rule like $fwcmd add deny all from 192.168.0.4:255.255.0.0 to any via tun0 which would limit a host on your internal network from going out via the firewall.

6.4. There must be something wrong. I followed your instructions to the letter and now I am locked out.

This tutorial assumes that you are running userland-ppp, therefore the supplied rule set operates on the tun0 interface, which corresponds to the first connection made with ppp(8) (a.k.a. user-ppp). Additional connections would use tun1, tun2 and so on.

You should also note that pppd(8) uses the ppp0 interface instead, so if you start the connection with pppd(8) you must substitute tun0 for ppp0. A quick way to edit the firewall rules to reflect this change is shown below. The original rule set is backed up as fwrules_tun0.

       % cd /etc/firewall
        /etc/firewall% su
        Password:
        /etc/firewall# mv fwrules fwrules_tun0
        /etc/firewall# cat fwrules_tun0 | sed s/tun0/ppp0/g > fwrules
     

To know whether you are currently using ppp(8) or pppd(8) you can examine the output of ifconfig(8) once the connection is up. E.g., for a connection made with pppd(8) you would see something like this (showing only the relevant lines):

       % ifconfig
        (skipped...)
        ppp0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1524
                    inet xxx.xxx.xxx.xxx --> xxx.xxx.xxx.xxx netmask 0xff000000
        (skipped...)
       

On the other hand, for a connection made with ppp(8) (user-ppp) you should see something similar to this:

       % ifconfig
        (skipped...)
        ppp0: flags=8010<POINTOPOINT,MULTICAST> mtu 1500
        (skipped...)
        tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1524
                (IPv6 stuff skipped...)
                    inet xxx.xxx.xxx.xxx --> xxx.xxx.xxx.xxx netmask 0xffffff00
                    Opened by PID xxxxx
            (skipped...)

This, and other documents, can be downloaded from ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

For questions about FreeBSD, read the documentation before contacting <questions@FreeBSD.org>.
For questions about this documentation, e-mail <doc@FreeBSD.org>.