OpenBSD firewall using IP Filter

by Hoang Q. Tran

It is really easy to configure an OpenBSD gateway for a private network. Here are the following steps:

Lock down the box

The first step to lock down the firewall box is to disable all unnecessary running services. Luckily, OpenBSD out of the box is really secure even with ident, comsat, daytime, time, rstatd and rusersd enabled in /etc/inetd.conf. Comment out mentioned services in /etc/inetd.conf and edit /etc/rc.conf and make sure portmap, sendmail and ntpd daemons are disabled as well.
portmap=NO
check_quotas=NO
ntpd=NO
inetd=NO
sendmail_flags=NO
sshd is enabled out of the box. If you don't plan to use it, disable it with sshd_flags=NO

Once you disabled unnecessary services, go to unixcircle to remotely port scan your own box from the outside. Be careful when you do this behind a NAT/firewall box as the port scan script will scan the NAT/firewall instead. If you have another box, use nmap to scan the box from the inside.

Install second Ethernet card in the OpenBSD box

Use any supported Ethernet card for the second NIC in the OpenBSD machine. One card will be given a public IP address (assigned by your ISP or obtained dynamically, e.g., with DHCP) and the other will be given an IP address in a non-routable network. Your choices for private network addresses must come from one of these ranges (see RFC 1918):
10.0.0.1 - 10.255.255.254      netmask 255.0.0.0
172.16.0.1 - 172.31.255.254    netmask 255.240.0.0
192.168.0.1 - 192.168.255.254  netmask 255.255.0.0
Assume the first card is ``ep", create /etc/hostname.ep0 with the following x.x.x.x netmask x.x.x.x where x.x.x.x is what you choose above.
# First NIC - private
192.168.1.1 netmask 255.255.255.0 media 10baseT
And if you have a static IP address for the second NIC, you naturally need to have it configured as /etc/hostname.ep1 as well.
# Second NIC with public IP address
123.121.8.1 netmask 255.190.280.0 media 10baseT
Be sure to indicate a correct IP address and netmask for both interfaces. Once you have chosen a private network address range for your inside machines, stay with that same range.

Whatever address you choose for the first interface in the OpenBSD gateway becomes the default gateway IP address for all machines on the inside private network.

Customize the kernel and install IP Filter

Compile the new kernel and remove any unwanted devices from the kernel.

Retrieve the release kernel source and unpack it as:

# tar xzvf srcsys.tar.gz -C /usr
( kernel source unpacking output... )
...
Or use AnonCVS to get just the kernel source it:
# setenv CVSROOT anoncvs@anoncvs.ca.openbsd.org:/cvs
# cd /usr
# cvs -q get -rOPENBSD_3_1_BASE -P src/sys
( checking out files output... )
...
Retrieve the latest IP Filter and unpack it:
# cd ~
# tar xzvf ip-fil3.4.28.tar.gz

Apply the patches to the kernel source:

# cd /usr/src
# patch < ~/ip_fil3.4.28/OpenBSD-3/3.1-sys-diffs

Add IP Filter to the source code tree:

# cd ~/ip_fil3.4.28
# BSD/kupgrade

Compile new kernel with IP Filter:

# cd /sys/arch/i386/conf
# cp GENERIC firewall
# vi firewall
( remove unused options and devices )
# config firewall
# cd ../compile/firewall
# make depend; make
( kernel building output... )
...
# cp /bsd /bsd.old
# cp bsd /bsd

Build and install IP Filter binaries:

# cd ~/ip_fil3.4.28
# make openbsd
# make install-bsd
# OpenBSD-3/makedevs-3.1

Patch rc scripts in /etc. Make sure you have the original rc.conf.

# cd /etc
# patch < ~/ip_fil3.4.28/OpenBSD-3/3.1-rc-diffs

Turn on ipfilter and ipnat in /etc/rc.conf:

ipfilter=YES
ipnat=YES

# reboot
This will retain the old kernel as /bsd.old just in case something has gone awry with the new one and the box doesn't boot. If that happens you can type 'bsd.old' at the boot: prompt to boot the old kernel.

Enable packet forwarding, dhcp, firewall and network address translation

To enable packet forwarding uncomment the following line in /etc/sysctl.conf and for extra protection, enable encryption on swap pages:
net.inet.ip.forwarding=1        # 1=Permit forwarding (routing) of packets
vm.swapencrypt.enable=1         # 1=Encrypt pages that go to swap
To enable high performance data transfers on hosts according to http://www.psc.edu/networking/perf_tune.html, add the following in addition to ip forwarding to /etc/sysctl.conf:
# 1. Path MTU discovery: enabled by default
# 2. TCP Extension (RFC1323): enabled by default
# 3. Increase TCP Window size for increase in network performance
net.inet.tcp.recvspace=65535
net.inet.tcp.sendspace=65535
# 4. SACK (RFC2018): enabled by default
And if you receive your routable address assignment dynamically through DHCP:
# echo dhcp > /etc/hostname.ep1
and edit /etc/dhclient.conf so that the dhcp client can query the dhcp server for appropriate information in order to connect to the internet. Example of a working /etc/dhclient.conf:
send host-name "crxxxxxx-a"; # Put your client IP here
request subnet-mask, broadcast-address, routers, domain-name-servers;
Filter rule:

Since you don't know what to block yet, you need to open up ingress and outgress traffic to flow through. Edit /etc/ipf.rules and add:

pass in all
pass out all
An example of a working /etc/ipf.rules

Network Address Translation rule:

For NAT and ftp clients behind NAT to work, add the following to /etc/ipnat.rules:

# Use ipfilter ftp proxy for ftp client transfers mode: active
map ep1 192.168.1.0/24 -> 0.0.0.0/32 proxy port ftp ftp/tcp

# Map all tcp and udp connections from 192.168.1.0/24 to external IP address,
# changing the source port number to something between 40,000 and 60,000 inclusive
map ep1 192.168.1.0/24 -> 0.0.0.0/32 portmap tcp/udp 40000:60000

# For all other IP packets, map to the external IP address
map ep1 192.168.1.0/24 -> 0.0.0.0/32
Make sure all the `proxy' lines are before any generic `portmap' lines, as the first match always wins.

An example of a working /etc/ipnat.rules

Now, turn on NAT/firewall feature in /etc/rc.conf:

ipfilter=YES                #Stateful firewall
ipnat=YES                   #Network Address Translation

For ipmon, you don't need to set anything as it will be invoked automatically
when ipfilter=YES and ipnat=YES. Just make sure /etc/rc.conf has the following
entry:

ipmon=-Dsn                   #Firewall logging

Configure machines behind NAT

All the machines on the private network should be configured to use the address of the private interface of the FreeBSD box as the default gateway.

To set the internal boxes to the default FreeBSD gateway on various operating systems:

Assume the FreeBSD box NAT/firewall has IP address: 192.168.1.1

AIX: edit /etc/rc.net and add /usr/sbin/route add 192.168.1.1 gateway >>$LOGFILE 2>&1
FreeBSD: edit /etc/rc.conf and add defaultrouter="192.168.1.1"
HP-UX: edit /etc/rc.config.d/netconf and add ROUTE_GATEWAY[0]="192.168.1.1"
Linux Redhat: edit /etc/sysconfig/network and add GATEWAY=192.168.1.1
NetBSD:  echo "192.168.1.1" > /etc/mygate
OpenBSD: echo "192.168.1.1" > /etc/mygate
Solaris: echo "192.168.1.1" > /etc/defaultrouter
Win2k: Start-Settings->Control Panel->Network and Dial-up Connections->Local Area Network->
       Properties->Internet Protocol (TCP/IP)->Default Gateway->192.168.1.1
If you don't want to reboot to pick up the IP address for the default gateway, use ``route'' to manually add the default route.

AIX: route add 0 192.168.1.1

HP-UX: route add 192.168.1.1

FreeBSD,NetBSD,OpenBSD,Solaris: route add default 192.168.1.1

Linux Redhat: route add default gw 192.168.1.1

2. /etc/resolv.conf on unix client hosts need to edit/add to have nameserver statements in order to resolve hostnames.

UNIX clients:

$ cat /etc/resolv.conf
nameserver      <ISP DNS IP>
nameserver      <ISP DNS IP>
Win2k :
Start-Settings->Control Panel->Network and Dial-up Connections->Local Area Network->
       Properties->Internet Protocol (TCP/IP)->->Advanced TCP/IP Settings->DNS
and add the ISP DNS IPs.

Familiarize with IPFilter

Once your NAT/firewall is online, you should start to read the IPFILTER-HOWTO at http://www.unixcircle.com/ipf/ and add more blocking/passing rules to /etc/ipf.rules. Some other useful links can be found on the www.ipfilter.org home page.

Each time /etc/ipf.rules or /etc/ipnat.rules is modified, you have to them as follow. Reloading these rules will flush all current active connections.

# /sbin/ipf -Fa -f /etc/ipf.rules
# /sbin/ipnat -CF -f /etc/ipnat.rules
You can use ipfstat to display firewall statistics a la ``top" command:
# /sbin/ipfstat -t

           firewall.muine.org - IP Filter: v3.4.28 - state top           23:01:10

Src = 0.0.0.0  Dest = 0.0.0.0  Proto = any  Sorted by = # bytes

Source IP             Destination IP         ST   PR   #pkts    #bytes       ttl
192.168.1.200,1415    65.92.100.89,6699     4/4  tcp    8245   6923504  42:14:06
23.234.234.2,24064    208.31.160.30,22      4/4  tcp     576    199843 119:59:59
192.168.1.200,2091    64.124.41.191,8888    4/4  tcp     157    118770  51:36:40
192.168.1.200,1094    64.124.41.161,8888    4/4  tcp     125     94190  46:37:34
To find out the ipfilter version:
# ipf -V
ipf: IP Filter: v3.4.28 (328)
Kernel: IP Filter: v3.4.28
Running: yes
Log Flags: 0 = none set
Default: pass all, Logging: available
Active list: 0
Notice the ``block all" setting from our options IPFILTER_DEFAULT_BLOCK in the kernel.

To display the current list of active MAP/Redirect filters and active sessions:

# /sbin/ipnat -l
To find out the ``hit" statistic for each individual rule in /etc/ipf.rules:
# /sbin/ipfstat -hio
See also ipftest(1), mkfilters(1), ipf(4), ipl(4), ipf(8), ipfstat(8), ipmon(8), ipnat(8) for details.

Reference

IPFilter home page:
   http://www.ipfilter.org
IPFilter examples:
   http://coombs.anu.edu.au/~avalon/examples.html
IPFilter how-to:
   http://www.unixcircle.com/ipf/
IPFilter mailing list archive:
   http://false.net/ipfilter
Guido van Rooij has written some real nice IPFilter papers:
   http://www.madison-gurkha.com/all_publications.shtml
Address Allocation for Private Internets:
   http://www.muine.org/rfc/rfc1918.txt
The IP Network Address Translator (NAT):
   http://www.muine.org/rfc/rfc1631.txt
Traditional IP Network Address Translator (Traditional NAT)
   http://www.muine.org/rfc/rfc3022.txt
The Twenty Most Critical Internet Security Vulnerabilities (Updated)
   http://66.129.1.101/top20.htm


last update: July 27, 2003