Pfadapt - Extension for Pf Firewall

On Adaptive Firewalls Idea

Classical firewalls are created with certain (fixed) set of rules which are typically read at firewall startup and which define firewall's behavior toward all hosts it encounters. This usually means specifying certain services from local network to be available to hosts residing outside of LAN, and also allowing hosts inside LAN to access some services offered by the hosts from outside.

This approach can be further improved by allowing firewall to learn about behavior of machines it filters traffic for. For example, firewall could expose more services to the user who has successfully authenticated himself in some way (for example by establishing successful ssh connection), or it could decide to restrict access to some readily available resource to those hosts that are misbehaving in some way (for example trying to open connection to many closed services on firewall - i.e. port scanners). Firewalls able to tune themselves in that manner are usually called adaptive firewalls.

How Pfadapt Fits in?

Pfadapt is very simple program designed to facilitate making of adaptive firewall with OpenBSD's pf firewall. It can do so by facilitating table operations from pf rules. To easier understand how pfadapt can be used it may be useful to take a look at few very simple examples:

Example 1 - blocking port scanner

Background - whenever host A tries to connect to host's B non-open port tcp RESET packet is sent back.

net = "acx0"

table <bad_guys> persist

block in quick on $net from <bad_guys> to any
block out quick on $net from any to <bad_guys>

block out log on $net proto tcp from any flags R/R \
     label ":add_to_table(destination, bad_guys)"

Firewall in this simple example is completely open firewall - it won't block any inbound or outbound traffic unless host is somehow blacklisted. We start with empty table of bad hosts (bad_guys), but using pfadapt command embedded in rule label we can dynamically add hosts to that table. In the previous example host is added to bad_guys if it tries to connect to some non-open port (syntax "proto tcp ... flags R/R" means "if this is tcp packet with RESET flag set" - that flag is normally set to indicate that requested port is closed).

In this example you should note few important things:

Adding host to a table after single foul is probably not very good idea, and that can be solved with other functions pfadapt offers (please also read manual page after installation).

In order this example to work you need to have pf running on your operating system, pflog also (in order to be able to log packets for further inspection by pfadapt), and to run pfadapt.

IMPORTANT: It is important to load set of rules with embedded pfadapt commands in them into pf firewall before starting pfadapt - that is because pfadapt parses embedded actions only at startup for performance reasons.

Example 2 - passive FTP server

Passive FTP protocol specifies that server is listening on port 21, after client connection to that port server opens another (unprivileged port) and sends that port number to client, then client connects to that unprivileged port for data, port 21 remaining open for commands. This can be little tricky to implement in firewall, because firewall needs to keep track of hosts connected to ftp server, however, this can be easily solved with pfadapt:

net = re0

table <ftp_clients> persist

pass quick on lo0

# block everything we don't enable explicitly   
block on $net

# allow dns
pass out on $net proto tcp from any to any
pass out quick on $net proto udp from any to any port domain

# allow ftp clients to connect to nonprivileged ports
pass in quick on $net proto tcp from <ftp_clients> to any port > 1024

# allow access to ftp
# add to ftp_clients table those who establish connection with our ftp server
pass out quick log on $net proto tcp from any port ftp to any flags SA/SA \
    no state label ":add_to_table(destination, ftp_clients)"
pass in log on $net proto tcp from any to any port ftp flags /RF no state 
pass out log on $net proto tcp from any port ftp to any flags /RF no state

# remove from ftp_clients on disconnect
pass in quick log on $net proto tcp from any to any port ftp flags R/R \
    label ":remove_from_table(source, ftp_clients)"
pass in quick log on $net proto tcp from any to any port ftp flags F/F \ 
    label ":remove_from_table(source, ftp_clients)"
pass out quick log on $net proto tcp from any port ftp to any flags R/R \
    label ":remove_from_table(destination, ftp_clients)"
pass out quick log on $net proto tcp from any port ftp to any flags F/F \
    label ":remove_from_table(destination, ftp_clients)"

Example above shows how you can create protected ftp server which exposes additional open ports only to its clients.

NOTE: In order pfadapt to be able to operate correctly you need to have pflog support in your kernel (either compiled or kernel module loaded before pfadapt is started). Also, every packet that is to be inspected by pfadat (ie, every packet that matches rule with pfadapt action embedded inside) needs to be passed to pflog (keyword log in pf rules).

pfadapt functions »