8.5. Basic PF Configuration

 < Day Day Up > 

While IFPW and PF provide the same basic firewall services, they are dramatically different to configure and maintain. Some users find the list-based mechanism that IPFW uses easy to understand and use. Others find that the PF provides more flexibility and agility. PF's configuration may be non-intuitive at first, but once you get the hang of it, it can be immensely powerful; especially as rulesets grow.

From a hardware perspective, PF has about the same performance ramifications as IPFW. A simple home PF firewall can run on a Pentium-class machine. More sophisticated setups will require more hardware; however, there is generally no performance-related reason to buy the latest and greatest server for a PF firewall. Considering the criticality of the job, though, you probably want a machine under warranty or to consider an HA architecture.

8.5.1. Kernel and Startup Configuration

As with most OpenBSD features, there is no need to compile in any special kernel options. The default kernel has firewall support built in natively, so all you need to do is enable it. PF can be enabled by adding pf=YES to /etc/rc.conf.local.

By default, PF reads firewall rules and information out of /etc/pf.conf. Unfortunately the default ruleset will not be of much use. You will have to modify pf.conf(5) prior to using the firewall.

The structure of the rules in PF are dramatically different than those in IPFW. IPFW uses a simple list for firewall control. PF, on the other hand, breaks the configuration into sections that allow for greater flexibility. pf.conf has the following sections:


Macros

Variables that can be used elsewhere in the configuration. For instance, the term ext_interface can be assigned the IP address of the external interface on the firewall.


Tables

Variables that represent one or more IP addresses or networks. This allows multiple networks to be grouped together for the sake of simplicity.


Options

Tunable firewall parameters such as TCP timeout values and logging information.


Scrub

Controls the process of defragmenting and normalizing packets.


Queuing

Controls the rate limiting and QoS features of the firewall.


Translation

Rules that control address and port translation go here.


Filters

Core logic of your firewall ruleset.

8.5.2. PF in FreeBSD

Even though PF is maintained as part of OpenBSD, it is available as a port in FreeBSD in the security ports directory for 5.2.1 and before, and its part of the core operating system starting with 5.3.

The port of PF is well maintained and installation should be seamless. You will need to have device bpf, options INET, options PFIL_HOOKS, and options RANDOM_IP_ID enabled in your kernel to get PF to working. And, by default, the configuration information of the ports tree version is stored in /usr/local/etc/pf.conf rather than /etc/pf.conf.

For PF in 5.3 and beyond, using PF instead of IPFW is a bit different. Your kernel must be configured with device pf. For logging capability, your kernel must have device pflog and for high availability synchronization your kernel must have device pfsync. Futher, you need to enable PF in your rc.conf. To stop using IPFW and start using PF, change firewall_enable="NO" and set pf_enable="YES". The pf_rules configuration option allows you to set where the configuration file is stored and pflog_enable turns on logging for PF.

If you are using PF under FreeBSD, this section will still apply; you just need to be careful of where your configuration files are.

8.5.3. Firewall Configuration

Configuring PF can be a complex task. However, a basic firewall ruleset can be created in little time by focusing on the important parts of the ruleset. The rules presented in this section are meant as an example of a common setup. The default pf.conf has many commented out features that you should examine beyond what is in this text. Further, PF examples found in /usr/share/pf can be very useful in understanding the detailed capability of PF.

First, start off by defining the networks and interfaces on the firewall as well as any important hosts on the connected networks. By setting these up as macros in the beginning, the resulting ruleset is more readable and maintainable. For instance, the DMZ network in Figure 8-8 can be defined as follows:

dmz_if="xl0" dmz_net="10.0.0.0/24" dmz_ip="10.0.0.1"

For groups of networks or hosts, tables are a more efficient way for PF to do ruleset matching. Any site specific network groups, such as all internal networks, should go in a table. Also the bogus and RFC 1918 networks that should never be routed across the public Internet should be defined as one table.

table <bogus> {10/8, 172.16/12, 192.168/16}

The options section of pf.conf will vary depending on your site needs. In a standard small office situation, the default options should be fine. However, for firewalls passing much more data such as those in a datacenter, the options section can be used to tune the memory used by the firewall and various timeout values.

Tuning memory and protocol options can be more art than science. For instance, on a firewall with many connections, you may want to force the firewall to expire connections more rapidly than normal to conserve resources. Unfortunately this can have dire consequences on long-term connections such as those with backend servers like databases or legacy systems.

Let's say database replication breaks due to the tuning done for the shorter connections. You have to fix it so you allow longer-lived connections but end up installing more memory to compensate. As you can see, firewall tuning can be like a game of Whack-A-Mole; hit one problem on the head and another pops up. When tuning memory and protocol options, be aware of other changes in your environment that may result.


PF can automatically reassemble and normalize packets before allowing them through the firewall. This feature can stop fragmentation-based attacks dead and prevent attacks against end hosts based on low level packet trickery. However, reassembling packets can use a great deal of memory, especially if you are under attack. In general, all traffic arriving on the external interface of a firewall should be reassembled. If you face a memory problem, it's preferable to buy more memory rather than turn this feature off.

scrub in on $ext_if all fragment reassemble

Finally, it's time to actually enter the rules for the firewall. The logic of the rules is the complete opposite of IPFW. As you recall, in IPFW, the first matching rule "wins" and the packet is handled by that rule. In PF, the last matching rule "wins." So, for the first rule, all traffic should be denied and logged. That way, traffic that does not match any other rule in the ruleset will be blocked.

block in log all

Packets that transit the localhost interface are rarely (if ever) illegitimate. There are many processes on an BSD machine that expect to be able to communicate through the localhost interface. Traffic should be allowed through localhost using the quick keyword. Using the quick keyword, a rule can force a "win" and act immediately on the packet. There is no reason to continue processing localhost traffic.

pass quick on lo0 all

Next, spoofed packets should be denied. Spoofed packets are bad regardless of the targeted service. PF has the ability to automatically create anti-spoofing rules given the network that is configured on an interface. For instance:

antispoof quick for $int_if inet anitspoof quick for $dmz_if inet

Further, the bogus networks should be dropped early in the ruleset as well. Packets sourced from bogus networks are interesting because they should never occur for non-malicious traffic. Using the log keyword, PF will log these packets to the logging interface, pflog0.

block in log quick on $ext_if from <bogus> to any

Finally, rules can be added for the DMZ and internal hosts. For the DMZ, traffic should be allowed from anywhere to the public services. As in the IPFW example, state-tracking should be used to increase the efficiency of the firewall.

pass in on $ext_if proto tcp from any to $websrv http keep-state pass in on $ext_if proto tcp from any to $mailsrv smtp keep-state pass in on $ext_if proto tcp from $int_net to $mailsrv imap keep-state

We use protocol names for readability here. You could also specify ports 80, 25, and 143 respectively. As long as the service is listed in the file /etc/services, however, the name and the port number are interchangeable.


Obviously these rules need to be customized for your network. When initially setting up the firewall, you may want to have your first network allow and log all traffic. That way you have less opportunity of locking yourself out of the host and you can view the logs to determine what other rules need to be created.

8.5.4. Using the Firewall

Like IPFW, PF provides functionality for managing a running firewall. From stopping and starting the firewall functionality to flushing rules to viewing statistics from the firewall, the pfctl(8) utility is the administrative interface to PF.

The firewall can be enabled and disabled by passing pfctl the -e and -d flags, respectively. This can be useful for troubleshooting a firewall problem, especially when you are unsure as to whether the problem is really the firewall or not.

There are a series of statistics and information pfctl can present via the -s flag. The following common keywords can be used with pfctl -s.


rules

Shows the currently loaded firewall ruleset. Can be used with -v for more detailed information.


state

Shows all currently active states. This is very useful if your rules extensively use the keep-state pragma.


info

Shows statistics and counters relevant to the entire firewall.


all

Displays all available information pfctl has. This is a useful debugging tool that can be croned once a night to output to a file or email for review.

For instance, to see verbose output on the currently loaded ruleset, you would do the following:

% sudo pfctl -s rules -v scrub in on sis0 all fragment reassemble   [ Evaluations: 283       Packets: 111       Bytes: 0           States: 0     ] block drop in log all   [ Evaluations: 6         Packets: 1         Bytes: 229         States: 0     ] pass in quick on lo0 all   [ Evaluations: 3         Packets: 0         Bytes: 0           States: 0     ] block drop in on ! sis0 inet from 192.168.0.0/24 to any ...

This output goes on for all your rules. After a while, the statistics associated with your rules may become so large as to become useless. You can zero out the statistics by issuing pfctl -z.

If you make a change to your pf.conf, you can validate the ruleset before you install it by using pfctl -n.

% sudo pfctl -ng -f /etc/pf.conf /etc/pf.conf:68: syntax error

While this is not the most verbose parsing information you could hope for, it's much better than making a mistake and locking yourself out of your firewall.

Once you've cleaned up the ruleset and validated it, pfctl allows you to flush all existing rules and load rules from a file.

% sudo pfctl -Fa -f /etc/pf.conf rules cleared nat cleared altq cleared states cleared pf: statistics cleared 0 tables deleted.

The -F flag flushes information from the firewall. The a modifier, used here, flushes all the rules. The s flag will flush just the active states in the firewall, a useful debugging tool. Be careful when flushing and reloading rules over the network as you can lock yourself out of the firewall.

8.5.4.1 Logging

The logging capability of PF is quite different than IPFW. PF logs denied packets to a pseudo network interface called pflog0. Data sent to pflog0 is in a pcap data structure. In a nutshell, this means that utilities like tcpdump can sniff pflog0 and interpret the traffic just as they would a normal interface.

However, running tcpdump all the time and watching the output is not exactly a scalable way to run your firewalls. The pflogd(8) daemon solves this problem by writing packets from pflog0 to a logfile (/var/log/pflog by default). Again, the data is written to disk in pcap format so any pcap-aware application can read the logfiles.

pflogd can be passed flags through rc.conf.local (or rc.conf for FreeBSD) at boot time. The most interesting flag is -s, which dictates how much of each packet is captured. By default, only the first 96 bytes are logged. This is generally enough data to capture all the headers. However, if you're interested in the entire payload, set the capture length much higher. For Ethernet networks, the maximum packet size is 1500, so a capture length of 1600 is guaranteed to capture all data in every logged packet.

The logs can be viewed using tcpdump. The tcpdump utility that ships with OpenBSD can understand and display the extra information PF puts into each packet regarding the rule that denied the packet. This information is contained in the link layer information of each packet and can be displayed with the -e flag.

% sudo tcpdump -e -r /var/log/pflog 16:49:28.195569 rule 0/0(match): block in on sis0: 192.168.0.67.50394 > 192. 168.0.11.80: S 3529314353:3529314353(0) win 65535 <mss 1460,nop,wscale 0,nop, nop,timestamp 3683841540 0> (DF) [tos 0x10] 16:49:30.819388 rule 0/0(match): block in on sis0: 192.168.0.67.50394 > 192. 168.0.11.80: S 3529314353:3529314353(0) win 65535 <mss 1460,nop,wscale 0,nop, nop,timestamp 3683841545 0> (DF) [tos 0x10]

Here, for instance, two SYN packets to port 80 were dropped by the 0th (first) rule. tcpdump is a very powerful utility, and a complete description of how to use it to analyze packets is outside the scope of this text. Read the tcpdump(8) man page for a good introduction to packet analysis.

The pfctl(8) and pf.conf(5) manpages are also good sources of information.

     < Day Day Up > 


    Mastering FreeBSD and OpenBSD Security
    Practical Guide to Software Quality Management (Artech House Computing Library)
    ISBN: 596006268
    EAN: 2147483647
    Year: 2003
    Pages: 142
    Authors: John W. Horch

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net