Pf

From Hackepedia
Jump to: navigation, search

pf basics

If you're using the OpenBSD's pf, which also works on FreeBSD, make sure it's enabled. You should watch Pflogd for the logging interface. If you use pf, you should also check out spamd while you're here.

# pfctl -si
Status: Enabled

I've been bitten by this while debugging.

# pfctl -N -f /etc/pf.conf

This will reload the nat rules only.. often best to disable the firewall rules when testing nat, so do

# pfctl -Fr

to flush the rules, and just

# pfctl -R -f /etc/pf.conf

to use them again.

# pfctl -Fs 

to flush the current nat states, just remember the existing natted connections will drop when you do this.

# pfctl -ss 

to show the current nat states.

in summary, -Fn the F means to Flush. -sn the s means to show. -N and -R are to load only the Nat or filter Rules respectively.


If you're satisfied with your pf ruleset, you might be interesting in looking into ALTQ. Alternate queuing (ALTQ) is a framework that allows to shape network traffic.


helpful rules for pf.conf

These can mostly be found by logging all of your block rules and then watching with:

# tcpdump -vvv -e -ttt -i pflog0

Once you see something you want to block, usually a reoccuring sequence, you can add a block rule without log enabled.

# block Microsoft Calendar
block in quick on $ext_if proto udp from any to any port {1024 1025 1026 1027 1028 1029 1030 }
# block nmap OS detection scans somewhat (-O)
block in quick proto tcp flags FUP/WEUAPRSF
block in quick proto tcp flags WEUAPRSF/WEUAPRSF
block in quick proto tcp flags SRAFU/WEUAPRSF
block in quick proto tcp flags /WEUAPRSF
block in quick proto tcp flags SR/SR
block in quick proto tcp flags SF/SF


Don't let the flags confuse you, the simplest way to block any sort of OS detection scans is to block anything and not return a packet. The scanner will only know that the packet never came back. You need to suit this with the blocking policy of your ISP though. It's interesting to find out when an IP is not allocated by an end-user whether the ISP's network access server reply anything in its place or whether the IP is blackholed. Early access servers showed a routing loop in a traceroute, my particular ISP blackholes non-used IP's. Getting to my point, if your ISP blackholes non-used IP's then using a block that doesn't reply anything shows no distinction whether there is a "live" computer behind an IP. If an ISP replies with some sort of packet perhaps the pf rules should be adapted to reply the same, so that a transparency appears.


Allowing torrents in pf

In this example, $torrentclient is the IP of the host running a torrent client, and I have specified in my torrent application to only use port 15001. I added the following to my pf.conf and ran "pfctl -f /etc/pf.conf" as root:

rdr on $ext_if proto tcp from !$nat_net to any port 15001 -> $torrentclient port 15001
pass in quick on $ext_if inet proto { udp tcp } from any to any port 15001 keep state

If you have a firewall on $torrentclient, make sure you allow the port 15001 traffic as well.


Verify your configuration

The following parses your configuration without loading the rules
# pfctl -vvnf pf.conf 

Official PF page

Rules based on user (UID/GID)

In the sample below, I will block UID 1012 from accepting packets on the rl0 interface. Then I will also block the outbound on the same interface for the same user. For the second example, I will use the username instead of the UID. You can use either:

block drop in log quick on rl0 all user = 1012
block drop out log quick on rl0 all user = droopy

pageknock to internal machine

First I installed [pageknock]. To start pageknock I used the command:

# pageknock &

Then I setup some rules in my pf.conf:

rdr on $ext_if proto tcp from any to $ext_if port 3233 -> 192.168.1.18 port 22

This means packets hitting 3233 on my external interface will hit port 22 (sshd) on my 192.168.1.18 machine. Now for the firewall rules:

pass in quick on $tun_if proto tcp from <sshknock> to $ext_if port 323 keep state
pass in quick on $tun_if proto tcp from <sshknock> to 192.168.1.18 port 22 keep state

In place of the firewall rules, a simple 'pass' in the rdr rule should have worked:

rdr pass on $ext_if proto tcp from <sshknock> to $ext_if port 3233 -> 192.168.1.18 port 22

and restarting pf:

# pfctl -f /etc/pf.conf

To debug it you can run tcpdump on your external interface as well as check your pflog

# tcpdump -i hme0 port 3233
# tcpdump -vvv -e -ttt -i pflog0

Now when traveling I can

$ ssh -X $my_machine

after sshknocking.


Recent changes in OpenBSD 4.7

OpenBSD 4.7 introduced the "match" rule and changed the way rdr and nat rules behave. Here is a partial working config:

ext_if="em0"
int_if="em1"
table <my_int_nets> const { 192.168.0.0/24, 172.16.0.0/24 }
table <my_ext_addy> const { PR.IV.AT.E0 }
pass in on $int_if inet from any to any keep state
pass out on $ext_if inet from <my_int_nets> to any keep state
pass out on $ext_if inet from <my_ext_addy> to any keep state
match out on $ext_if inet from <my_int_nets> to any nat-to <my_ext_addy>
pass in on $ext_if inet proto tcp from any to any port 25 keep state
match in on $ext_if proto tcp from any to any port 25 rdr-to 192.168.0.35
pass out on $int_if inet proto tcp from any to any port 25 keep state
pass in on $ext_if inet proto tcp from any to any port 1022 keep state
match in on $ext_if proto tcp from any to any port 1022 rdr-to 192.168.0.35 port 22
pass out on $int_if inet proto tcp from any to any port 22 keep state

Notice that nat-to and rdr-to can now be intermixed with other rules at any point. Do notice as well that when you make a match'ing rule that you must give it a pass rule as well to allow the rdr to work (on internal and external interfaces).