Pf
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
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).