OpenBSD pf by Hoang Q. Tran
OpenBSD firewall using pf
by Hoang Q. TranIt is really easy to configure an OpenBSD gateway for a private network. Here are the following steps:
- Lock down the box
- Install second ethernet card in the OpenBSD box
- Customize the kernel
- Enable packet forwarding, dhcp, firewall and network address translation
- Configure machines behind NAT
- Familiarize with pf
- Quality of Service (QoS)
- References
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. Don't disable inetd as you will need it later for ftp-proxy.check_quotas=NO ntpd=NO sendmail_flags=NOsshd 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 firewall box as the port scan script will scan the firewall instead. If you have another box, use nmap to scan the box from the inside.
Get the latest OpenBSD security patches and manually apply or download all the patches in one file or use AnonCVS to synchronize to stable release and build from source.
Make sure you check out the 3.2 stable branch with -rOPENBSD_3_2. Otherwise, you're checking out the ``current'' branch instead.
Finally, readup on SANS's The Twenty Most Critical Internet Security Vulnerabilities (Updated)
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.0Assume 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 10baseTAnd 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.221.8.1 netmask 255.190.280.0 media 10baseTBe 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
Compile the new kernel and remove any unwanted devices from the kernel.Retrieve the 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_2 -P src/sys ( checking out files output... ) ... # cd /sys/arch/i386/confI usually name the kernel to the machine hostname, but you can give it any name. Edit the kernel config file:
Remove any hardware related options that are not relevant to your machine. One way to find out what to keep is to consult the dmesg output and remove all the rest. For all available kernel options, refer to GENERIC in the same directory as your kernel file and /sys/conf/GENERIC or man options(4).
Save the kernel config file and then compile and install it:
# config firewall # cd ../compile/firewall # make depend; make ( kernel building output... ) ... # cp /bsd /bsd.old # cp bsd /bsd # rebootThis 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 swapTo enable high performance data transfers on hosts according to Enabling High Performance Data Transfers on Hosts, add the following 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 defaultAnd if you receive your routable address assignment dynamically through DHCP:
# echo dhcp > /etc/hostname.ep1The dhcp server will assign the IP, netmask and default gateway for interface ``ep1''. /etc/resolv.conf will be created with ``search'' and ``nameservers'' statements from the ISP.
Filter rule:
Starting with OpenBSD 3.2, filter and nat rules are combined into /etc/pf.conf. The order of /etc/pf.conf is really important and the format of /etc/pf.conf must follow this order:
1. Options 2. Scrub 3. NAT & RDR 4. Filter
If there are no filter rules, the default action is pass.
Network Address Translation rule:
For clients behind NAT to work, 1 NAT and 1 RDR rule is sufficient:
# NAT internal IP addresses of range 192.168.1.0/24 to external routable # IP on ep1 interface nat on ep1 from 192.168.1.0/24 to any -> ep1 # Translate outgoing ftp control connections to send them to localhost # for proxying with ftp-proxy(8) running on port 8081 rdr on ep0 proto tcp from any to any port 21 -> 127.0.0.1 port 8081ftp-proxy runs inside inetd, add the following line to /etc/inetd.conf in order for ftp clients behind NAT to work by going through ftp-proxy daemon:
127.0.0.1:8081 stream tcp nowait root /usr/libexec/ftp-proxy ftp-proxyAs a result, ftp port and port 8081 will be opened. ftp-proxy supports -w option which will use tcp_wrappers to control source ftp client as well as destination ftp server access control based on /etc/hosts.allow and /etc/hosts.deny. Assume source ftp client IP 192.168.1.2 doesn't have permission to use ftp, a similar log entry in /var/log/messages when attempted to reach ftp.netbsd.org
Sep 14 15:55:38 firewall ftp-proxy[20970]: tcpwrappers rejected: 192.168.1.2 -> ftp.netbsd.org
An example of a working /etc/pf.conf
Transparent proxy:
If there's a mail server as 192.168.1.2 and a DNS server as 192.168.1.3 inside the private network, use ``rdr'' to transparent proxying. Since NAT happens before ``rdr'', a ``pass in'' is required in /etc/pf.conf for the translated packets to flow into the mail server and DNS server.
/etc/pf.conf:
# Redirect incoming smtp traffic to mail server behind NAT rdr on ep1 proto tcp from any to 157.161.48.183/32 port 25 -> 192.168.1.2 port 25 # Redirect incoming domain traffic to DNS server behind NAT rdr on ep1 proto { tcp,udp } from any to 157.161.48.183/32 port 53 -> 192.168.1.3 port 53Finally, enable pf in /etc/rc.conf:
pf=YES # Packet filter / NAT / logging using pflogd
Configure machines behind NAT
All the machines on the private network should be configured to use the address of the private interface of the OpenBSD box as the default gateway. To set the internal boxes to the default OpenBSD gateway on various operating systems with IP address: 192.168.1.1AIX: 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.1If 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
Familiarize with pf
Once your firewall is online, you should start reading pf.conf(5), nat.conf(5), ftp-proxy(8), pfctl(8), pf(4) and The OpenBSD Packet Filter HOWTO. Also consult IPFILTER-HOWTO since both pf and IP Filter have 90% identical syntax. One noticable difference is OpenBSD pf doesn't support IP Filter ``keep frags'' syntax. The alternative is to use ``scrub'' statement.Each time /etc/pf.conf or /etc/nat.conf are modified, you have to reload them using pfctl. Reloading these rules will flush all current active connections. Unlike IPFilter, pf needs to enable nat and pf rules manually.
Flush current nat rules & reload:
# /sbin/pfctl -F nat && /sbin/pfctl -N /etc/pf.confFlush current filter rules & reload:
# /sbin/pfctl -F rules && /sbin/pfctl -R /etc/pf.confShow filter information (statistics and counters):
# pfctl -s infoTo display the current list of active MAP/Redirect filters and active sessions:
# /sbin/pfctl -s stateTo find out the ``hit" statistic for each individual rule in /etc/pf.conf:
# /sbin/pfctl -s rules -vWatch port scans going by on the screen:
/var/log/pflog is a binary file generated by pflogd so you can't just view it. Use tcpdump instead:
# tcpdump -i pflog0
Read the log for pf activities:
# tcpdump -n -e -ttt -r /var/log/pflog
Quality of Service (QoS)
Bandwidth limiting:OpenBSD 3.2 has ALTQ integrated in the base system. The kernel generic kernel is also compiled with options ALTQ so you're ready to use. Otherwise, download the latest KAME snap kit which has ALTQ bundle from: http://www.kame.net/retrieve.html
Now, to configure a token bucket regulator (tbrconfig) for the interface ep1 to rate limit the pipe from 100Mbps to 10Mbps for outgoing connection:
# tbrconfig ep1 10M auto ep1: tokenrate 10.00M(bps) bucketsize 12.21K(bytes) #To remove the installed token bucket regulator on ep1:
# tbrconfig -d ep1 deleted token bucket regulator on ep1 #Class-based queuing (CBQ):
From man options(4) description of CBQ:
CBQ achieves both partitioning and sharing of link bandwidth by hierarchically structured classes. Each class has its own queue and is assigned its share of bandwidth. A child class can borrow bandwidth from its parent class as long as excess bandwidth is available.
Here is an example of a working /etc/altq.conf. /etc/altq.conf is read by altqd so to enable it on startup, edit /etc/rc.conf and change altqd_flags=NO to altqd_flags="". Or just manually start altqd. altqd won't start if there are errors in /etc/altq.conf. Watch /var/log/messages for any information.
Here's the class hierarchy: cbq.txt
# # ep1: Interface to a 10M link # # interface ep1 bandwidth 10M cbq class cbq ep1 root NULL pbandwidth 100 # # meta classes # class cbq ep1 ctl_class root pbandwidth 4 control class cbq ep1 def_class root borrow pbandwidth 95 default # # Allocate bandwidth for: # firstclass: 70% # businessclass: 15% # generalclass: 5% # class cbq ep1 firstclass def_class borrow pbandwidth 70 class cbq ep1 businessclass def_class borrow pbandwidth 15 class cbq ep1 generalclass def_class borrow pbandwidth 5 # # Allocate bandwidth for firstclass (tcp) data classes: # tcp: 28% # smtp: 10% # http: 30% # dns: 2% # class cbq ep1 tcp firstclass borrow pbandwidth 28 red filter ep1 tcp 0 0 0 0 6 # other tcp class cbq ep1 smtp firstclass borrow pbandwidth 10 red filter ep1 smtp 0 0 0 25 6 # smtp filter ep1 smtp 0 25 0 0 6 # smtp class cbq ep1 http firstclass borrow pbandwidth 30 red filter ep1 http 0 0 0 80 6 # http filter ep1 http 0 80 0 0 6 # http class cbq ep1 dns firstclass borrow pbandwidth 2 red filter ep1 dns 0 0 0 53 6 # dns filter ep1 dns 0 53 0 0 6 # dns # # Allocate bandwidth for businessclass (udp) classes: # udp: 10% # dns: 5% # class cbq ep1 udp businessclass borrow pbandwidth 10 red filter ep1 udp 0 0 0 0 17 # udp class cbq ep1 dns businessclass borrow pbandwidth 5 red filter ep1 dns 0 0 0 53 17 # dns filter ep1 dns 0 53 0 0 17 # dns # # Allocate bandwidth for generalclass (icmp) classe: # icmp: 5% # class cbq ep1 icmp generalclass borrow pbandwidth 5 red filter ep1 icmp 0 0 0 0 1 # icmpNow, run altqstat and monitor the bandwidth. You should see something similar here: cbq stat
Weighted Fair Queueing (WFQ):
To use weighted fair queueing, add the following to kernel file.
option ALTQ_WFQBy default, WFQ allocates 256 queues and packets are mapped into one of the queues by hashing the destination address. So, packets for the same host will be put in the same queue.
To enable WFQ on interface "ep0" and "ep1", add the following lines to your altq.conf(5) and start altqd.
interface ep0 bandwidth 10M wfq interface ep1 bandwidth 10M wfqThe following command can be used to monitor the wfq statistics.
altqstat -i ep1You should see something similar:
% altqstat altqstat: wfq on interface ep1 wfq on ep1: 256 queues are used [QID] WEIGHT QSIZE(KB) SENT(pkts) (KB) DROP(pkts) (KB) bps [ 141] 100 0 14 1 0 0 0.09K [ 103] 100 0 2 0 0 0 0.09K [ 131] 100 0 11 1 0 0 0 [ 155] 100 0 10 0 0 0 0 [ 124] 100 0 9 0 0 0 0 [ 184] 100 0 5 0 0 0 0 [ 12] 100 0 2 0 0 0 0 [ 0] 100 0 0 0 0 0 0 [ 1] 100 0 0 0 0 0 0 [ 2] 100 0 0 0 0 0 0First-In First-Out Queueing (FIFOQ):
To use first-in first-out queueing, add the following to kernel file.
option ALTQ_FIFOQTo enable FIFOQ on interface ep1, add the following line to your altq.conf(5) and start altqd.
interface ep1 bandwidth 10M fifoqRun altqstat and you should see something similar:
% altqstat altqstat: fifoq on interface ep1 q_len:0 q_limit:50 period:2 xmit:2 pkts (108 bytes) drop:0 pkts (0 bytes) throughput: 0.17Kbps q_len:0 q_limit:50 period:2 xmit:2 pkts (108 bytes) drop:0 pkts (0 bytes) throughput: 0bps ...Random Early Detection (RED):
Since RED is part of ALTQ, no kernel option is required.
To enable random early detection on interface ep1, add the following line to your altq.conf(5) and start altqd.
interface ep1 bandwidth 10M redRun altqstat and you should see something similar:
% altqstat altqstat: red on interface ep1 weight:512 inv_pmax:10 qthresh:(5,15) q_len:0 (avg: 0.00), q_limit:60 xmit:1 pkts, drop:0 pkts (forced: 0, early: 0) throughput: 0.09Kbps weight:512 inv_pmax:10 qthresh:(5,15) q_len:0 (avg: 0.00), q_limit:60 xmit:1 pkts, drop:0 pkts (forced: 0, early: 0) throughput: 0bps ...Diffserfv traffic conditioner (CDNR):
>From man options(4):
Traffic conditioners are components to meter, mark, or drop incoming packets according to some rules. As opposed to queueing disciplines, traffic conditioners handle incoming packets at an input interface.
To use conditioner to drop incoming packets from a particular IP address, add the following to kernel file.
option ALTQ_CDNRTo enable conditioner on interface ep1, add the following line to your altq.conf(5) and start altqd.
# interface ep1 # # Drop all packets coming in from 255.255.255.255 (ficticious) # conditioner ep1 dropper <drop> filter ep1 dropper 0 0 255.255.255.255 0 0Run altqstat to monitor the drop packets:
% altqstat altqstat: cdnr on interface _fxp0 actions: pass:471 drop:3 mark:0 next:0 return:0 none:0 actions: pass:501 drop:3 mark:0 next:0 return:0 none:0 ...Priority Queueing (PRIQ):
>From man options(4):
PRIQ implements a simple priority-based queueing. A higher priority class is always served first.
High number has higher priority. Maximum value is 15 and minimum value is 0. Default is 0. A higher priority class is always served first in PRIQ. Priority must be unique for the interface.
To use priority queueing to prioritize based on type of packet, add the following to kernel file.
option ALTQ_PRIQTo enable priority queueing on interface ep1, add the following line to your altq.conf(5) and start altqd.
# # Prioritize based on protocol: # # tcp: high priority # udp: medium priority # icmp: low priority # others: bottom priority # interface ep1 bandwidth 10M priq # class priq ep1 highest_class NULL priority 3 filter ep1 highest_class 0 0 0 0 6 class priq ep1 medium_class NULL priority 2 filter ep1 medium_class 0 0 0 0 17 class priq ep1 lowest_class NULL priority 1 filter ep1 lowest_class 0 0 0 0 1 class priq ep1 bottom_class NULL priority 0 default % altqstat altqstat: priq on interface ep1 ep1: [highest_class] handle:0xe09fd0c0 pri:3 measured: 0.34Kbps qlen: 0 period:180 packets:180 (25637 bytes) drops:0 [medium_class] handle:0xe09f1d40 pri:2 measured: 0bps qlen: 0 period:25 packets:25 (1997 bytes) drops:0 [lowest_class] handle:0xe09fdcc0 pri:1 measured: 0bps qlen: 0 period:19 packets:19 (1862 bytes) drops:0 [bottom_class] handle:0xe09a4680 pri:0 measured: 0bps qlen: 0 period:0 packets:0 (0 bytes) drops:0 ...
References
Daniel Hartmeier the author of pf and his original page: http://www.benzedrine.cx/pf.html The OpenBSD Packet Filter HOWTO http://www.inebriated.demon.nl/pf-howto/ IPFilter how-to: http://www.unixcircle.com/ipf/ 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 ALTQ http://www.csl.sony.co.jp/~kjc/software.html#ALTQ CBQ: http://www.aciri.org/floyd/cbq.html HFSC: http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html RED: http://www.aciri.org/floyd/red.html RIO: http://diffserv.lcs.mit.edu/Papers/exp-alloc-ddc-wf.pdf BLUE: http://thefengs.com/wuchang/blue/ Diffserv: http://www.ietf.org/html.charters/diffserv-charter.html The Twenty Most Critical Internet Security Vulnerabilities (Updated) http://www.sans.org/top20/
last update: Oct 20, 2003