Howto Build a Firewall & Wireless access point
with OpenBSD 3.0/3.1, PF, NAT & DHCP

Erwan Lemonnier - 2002/04/30

 

We have:

We have at our disposal a Dell latitude xpi133st, 133Mhz, 24 Mb RAM, with two pcmcia pccard slots and a docking station with an inbuilt ethernet card (3com 3c509B). We also have one pccard (16bits) pcmcia ethernet adapter compatible with OpenBSD 3.0+ (3com pccard megahertz or d-link DFE-650TX), and one wireless pcmcia ethernet card (Lucent Orinoco Silver). For the story, the laptop I used has no screen and I found it in a trash can :) Talk about giving a new life to old scrap!

We want:

An OpenBSD firewall with 3 interfaces, including one wireless, connected to the internet on one side, to your wireless home network and home lan on the other side, performing NAT (Network Address Translation) between each home segment and the internet, and offering dhcp leases on the home segments.

 

Step1. Installing OpenBSD 3.0 on a Dell latitude xpi133st

The xpi133 does not have any cdrom drive, only a floppy unit. You might hence wish to do a network installation of OpenBSD. Fetch the apropriate floppy disk image (example: from 'ftp.sunet.se/pub/OpenBSD/3.0/i386/floppy30.fs'), and copy it to a floppy disk with dd ('dd if=floppy30.fs of=/mnt/floppy') or rawrite. The installation should not be a problem in itself. For my system, I simply cut the BSD slice ('d' in fdisk) in 2 partitions: swap ('b') and / ('a').

You may have a problem after rebooting your system with your 3com etherlink 3c509B, the docking station's builtin interface. This interface may be handled by the kernel as a plugnplay isa device, which is not well supported by openbsd. To fix this issue, one solution is to boot under DOS with a usual windows 9x boot floppy, then run a configuration utility provided by 3com in order to edit the card's parameters and disable pnp. The utility is called '3c5x9cfg.exe', fits on a floppy, and is available at 'http://pkl.net/~matt/'.

Step2. Interface Configuration

Our system has 3 interfaces, and we want to give them the following ip configuration:

   	ep1:  external interface -> configured with dhcp by ISP
   	ep0:  internal segment   -> ip 192.168.0.1, on segment 192.168.0.0/24
   	wi0:  wireless segment   -> ip 10.0.0.1, on segment 10.0.0.0/24

To configure these interfaces, edit the files '/etc/hostname.<interface-name>' (cf 'man hostname.if') so that they contain the following lines:

	root@c3rb3r2 /etc>cat hostname.ep0
	inet 192.168.0.1 255.255.255.0 192.168.0.255
root@c3rb3r2 /etc>cat hostname.ep1 dhcp NONE NONE NONE
root@c3rb3r2 /etc>cat hostname.wi0 inet 10.0.0.1 255.255.255.0 10.0.0.255 !wicontrol \$if -e 1 -k "secretkey" -p 1 -c 1 -s "hostname" -n "networkname" -q "networkname" -f 3

As you noticed, 'wi0' has an unusual configuration. Since it is a wireless interface, it requires some additional configuration compared to its fellow ethernet adapters. 'wi0' receives the ip 10.0.0.1, but we also instruct the system to run wicontrol when bringing up wi0. The wicontrol command line above sets wi0 to use wep encryption (-e 1) with a secret key that you should specify yourself (-k "secretkey"), tells wi0 to run in IBSS mode (-p 1 combined with -c 1), to advertise "hostname" as the station name (-s "hostname" -q "hostname") and "networkname" as the wireless network's name (-n "networkname"). Cf 'man wicontrol'. The interface will use the frequency of channel 3 (-f 3). Your card is now running in Independent BSS mode (IBSS), which has many common proprieties with BSS mode, though with some restrictions. Orinoco cards can unfortunately not be run in BSS mode due to a lack of documentation from Lucent. Keep in mind that WEP encryption can easily be breaked and does not hence provide much security. Run a VPN on top of it.

A corresponding client should be configured to use a base station on channel 3, using encryption with the appropriate key, looking for the network "networkname". Under windows, some clients may need to use peer to peer mode.

The dhcp server running on wi0 will then take care of the rest.
Building a wireless network was that simple :)

Step3. Configuring DHCP

Edit '/etc/dhcpd.conf' to make it contain something like:

	option  domain-name "home.net";
	option  domain-name-servers 130.244.127.161, 130.244.127.169;

	#interne net
	subnet 10.0.0.0 netmask 255.255.255.0 {
	        option routers 10.0.0.1;
    	    option broadcast-address 10.0.0.255;
    	    range 10.0.0.2 10.0.0.127;
	}

	#wireless net
	subnet 192.168.0.0 netmask 255.255.255.0 {
	        option routers 192.168.0.1;
	        option broadcast-address 192.168.0.255;
	        range 192.168.0.1 192.168.0.127;
	}

Then edit '/etc/dhcpd.interfaces', so as to contain on one line wi0 and ep0. This will tell dhcpd to start running after boot time and serve these interfaces:

	root@c3rb3r2 /etc>cat dhcpd.interfaces 
	#       $OpenBSD: dhcpd.interfaces,v 1.1 1998/08/19 04:25:45 form Exp $
	#
	# List of network interfaces served by dhcpd(8).
	ep0 wi0

Step4. Configuring NAT

The introduction of 'pf' in OpenBSD 3.0 gathered inside a unique module the functions of NAT and IPF, previously separated. Since pf is by default available in the kernel, it is no more needed to recompile a kernel in order to enable NAT. Instead, just edit the '/etc/nat.conf' file. Mine looks like:

	# NAT config file

	# config: 3 interfaces, ep1 = internet
	#                       ep0 = intranet
	#                       wi0 = interne wireless
	
	# gateways
	nat on ep1 from ep0/24 to any -> ep1 
	nat on ep1 from wi0/24 to any -> ep1 

Then, edit '/etc/rc.conf' to enable pf and specify the location of the nat config file. 'rc.conf' should contain:

	pf=YES                  		# Packet filter / NAT
	nat_rules=/etc/nat.conf         # NAT rules file

Step5. Configuring PF

Now, the least you can expect from a firewall is to perform some packet filtering. The Packet Filter configuration file is '/etc/pf.conf' which by default allows everything. In our configuration, our firewall should block all incoming connections from the internet to one of our internal segments, but let pass all connections out. We also want to allow inbound ssh and dhcp connections to the firewall. It is furthermore advised to block between each segment certain sensitive ports (like 135 & 139 for the microsoft shares...). '/etc/rc.conf' should contain:

	pf=YES                  		# Packet filter / NAT
	pf_rules=/etc/pf.conf           # Packet filter rules file

As you will see in the pf config file below, it is convenient with both pf and nat to use the notation 'epX' or 'wiX' in place of ip addresses. In our case, we don't even know the address of ep1 until a dhcp offer has been received from the ISP. The problem is that this dhcp offer comes after that the pf and nat rules have been loaded. Pf won't crash of not knowing ep1's ip, but won't work properly either. You should hence reload the pf and nat rules after that ep1 has been correctly configured. You can do this by adding the following lines to '/etc/rc.local':

	#reloading NAT after dhcp
	echo reloading NAT & PF with DHCP settings...
	pfctl -F nat
	pfctl -N /etc/nat.conf
	pfctl -F rules
	pfctl -R /etc/pf.conf

Below is a (probably not perfect) 'pf.conf' that work in our case:


	##----------------------------------------------------------------------
	## C3RB3R2 pfctl rules
	## 2002/04/29
	## erwan
	##----------------------------------------------------------------------
	
	
	## MACROS  - define interfaces: internet, intranet, wireless net
	
	if_ext = "ep1"
	if_int = "ep0"
	if_wir = "wi0"
	
	bad_ports = "69,135,137,138,139,445,524,548,1433,6000,31337,666,12345"
	
	no_route = "{ 127.0.0.1/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 255.255.255.255/32 }"
	
	
	###----------------------------------------------------------------------
	###
	### DEFAULT RULES
	###
	
	# INCOMING DEFAULT: block and normalize all
	block in all
	scrub in all
	
	# OUTGOING DEFAULT: block all
	block out all
	
	
	# SPECIAL IMMEDIATE BLOCKS:
	
	# block bad ports and external broadcasts
	block in quick  proto { udp,tcp }  from any to any port { = $bad_ports }
	block in quick  on $if_ext         from any to 255.255.255.255  
	
	# block weird tcp packets on if_ext:
	block in quick on $if_ext inet proto tcp from any to any flags FUP/FUP
	block in quick on $if_ext inet proto tcp from any to any flags SF/SFRA
	block in quick on $if_ext inet proto tcp from any to any flags /SFRA
	
	# don't allow anyone to spoof non-routeable addresses
	block in  quick on $if_ext from $no_route to any
	block out quick on $if_ext from any to $no_route
	
	
	
	
	###----------------------------------------------------------------------
	###
	### LOOPBACK
	###
	
	pass in quick on lo0 all
	pass out quick on lo0 all
	
	
	
	###----------------------------------------------------------------------
	###
	### EXTERNAL INTERFACE
	###
	
	
	# INCOMING: accept ssh 
	pass in quick on $if_ext  proto tcp  from any to $if_ext/32  port = 22         flags S/SA keep state
	
	# INCOMING DEFAULT: block all incoming
	
	
	# OUTGOING: block non nated packets, pass the others
	block out quick on $if_ext                          from !$if_ext/32 to any
	pass out quick  on $if_ext  proto tcp               from $if_ext/32 to any flags S/SA        keep state
	pass out quick  on $if_ext  proto { udp,icmp }      from $if_ext/32 to any  keep state
	
	# OUTGOING DEFAULT: block all outgoing
	
	
	
	
	###----------------------------------------------------------------------
	###
	### INTERNAL INTERFACE
	###
	
	# INCOMING: traffic to fw, accept ssh & dhcp only, block the rest
	pass in quick   on $if_int  proto tcp          from $if_int/24 to $if_int/32  port = 22  flags S/SA keep state
	pass in quick   on $if_int  proto { tcp,udp }  from $if_int/24 to $if_int/32  port = 67  keep state
	block in quick  on $if_int                     from any        to $if_int/32
	
	# INCOMING: forward traffic to all over destinations (except bad ports & broadcasts)
	pass in quick   on $if_int                     from $if_int/24 to any
	
	# INCOMING DEFAULT: block the rest (spoofed packets...)
	
	
	# OUTGOING: pass all.
	pass out quick  on $if_int  proto { tcp,udp,icmp } from any to $if_int/24         keep state
	
	
	
	###----------------------------------------------------------------------
	###
	### WIRELESS INTERFACE
	###
	
	# INCOMING: traffic to fw, accept ssh & dhcp only, block the rest
	pass in quick   on $if_wir  proto tcp          from $if_wir/24 to $if_wir/32  port = 22  flags S/SA keep state
	pass in quick   on $if_wir  proto { tcp,udp }  from $if_wir/24 to $if_wir/32  port = 67  keep state
	block in quick  on $if_wir                     from any        to $if_wir/32
	
	# INCOMING: forward traffic to all over destinations (except bad ports & broadcasts)
	pass in quick   on $if_wir                     from $if_wir/24 to any

	# INCOMING DEFAULT: block the rest (spoofed packets...)
	
	
	# OUTGOING: pass all.
	pass out quick  on $if_wir  proto { tcp,udp,icmp } from any to $if_wir/24         keep state
	

Step6. Cleanup

We are almost done. Remains only to clean a little bit our default OpenBSD installation from all superfluous services. First edit '/etc/inetd.conf' and comment out everything. Then edit '/etc/rc.conf'. You should disable sendmail, by commenting out endmail_flags and adding a sendmail_enable entry as in:

	#sendmail_flags="-L sm-mta -C/etc/mail/localhost.cf -bd -q30m"
	sendmail_enable=NO

Be sure you have the following values:

	identd_flags=NO         # for non-inetd use: "-b -u nobody -elo"
	pf=YES                  # Packet filter / NAT
	portmap=NO              # almost always needed
	inetd=NO                # almost always needed
	ntpd=NO                 # run ntpd if it exists
	pf_rules=/etc/pf.conf           # Packet filter rules file
	nat_rules=/etc/nat.conf         # NAT rules file

Then edit '/etc/sshd_config' and change 'PermitRootLogin yes' into 'PermitRootLogin no'.

Last but far from least, edit '/etc/sysctl.conf' and enable ip forwarding, without which your firewall will never become a gateway :)

	net.inet.ip.forwarding=1        # 1=Permit forwarding (routing) of packets

And you are done, congratulations !!

Here is a picture of our achievement: