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: