PF (Packet Filter) is a BSD licensed stateful packet filter, a central piece of software for firewalling. It is comparable to iptables, ipfw and ipfilter.

Let’s say you have the following physical setup:

NAT

And you want to use FreeBSD as your firewall and NAT’ing device, here is a small guide on how to set that up.

On your freebsd machine add the following into your /etc/rc.conf file:

defaultrouter="100.138.196.1"  
gateway_enable="YES"  
hostname="thewall.domain.com"  
ifconfig_xl0="inet 192.138.196.100 netmask 255.255.255.0"  
ifconfig_bge0="inet 100.168.196.100 netmask 255.255.255.0"  
ifconfig_xl0_alias0="inet 100.138.196.2 netmask 255.255.255.0"  
ifconfig_xl0_alias1="inet 100.138.196.3 netmask 255.255.255.0"  
ifconfig_xl0_alias2="inet 100.138.196.4 netmask 255.255.255.0"  
pf_enable="YES"  
pf_flags=""  
pf_program="/sbin/pfctl"  
pf_rules="/etc/pf.conf"  
pflog_enable="YES"  
pflog_flags=""  
pflog_logfile="/var/log/pflog"  
pflog_program="/sbin/pflogd"  

Reboot the host and check to make sure pf is running:

ps aux | grep pf

then create the /etc/pf.conf file and place the following into the file:

##### lists/macros/tables##  
# macros  
# hosts  
thewall_public = "100.138.196.100/32"  
thewall_private = "192.168.196.100/32"  
distrib_host = "100.138.2.253/32"  
host1_private = "192.168.196.2/32"  
host2_public = "100.138.196.3/32"  
host2_private = "192.168.196.3/32"  
host3_public="100.138.196.196.4/32"  
host3_private="192.138.196.4/32"  
outside_network = "100.138.0.0/16"  
the_world = "0.0.0.0/0"

# interfaces  
public_interface = "xl0"  
private_interface = "bge0"

# networks  
private_network = "192.168.196.0/24"  
public_network = "100.138.196.0/24"  
outside_128_network = "100.138.128.0/24"  
outside_110_network = "100.138.110.0/24"

#tables  
# monitoring hosts  
table {  
100.138.1.2/32  
100.138.1.3/32  
100.138.1.4/32 }

# domain name servers  
table {  
100.138.2.2/32  
100.138.2.3/32  
100.138.2.4/32  
100.138.2.5/32 }

# ip addresses assigned to nics  
table { self }

## options##  
set skip on lo0  
set state-policy if-bound  
set timeout { interval 1 }

## normalization##  
scrub in all fragment reassemble

## static nat addresses. these all have a 1:1 relationship between  
# public and private addresses and do not use port translation.  
# host1  
binat on $public_interface from 192.168.196.2/32 to any -> 100.138.196.2/32

# host2  
binat on $public_interface from 192.168.196.3/32 to any -> 100.138.196.3/32

# host3  
binat on $public_interface from 192.168.196.4/32 to any -> 100.138.196.4/32

# all other private addresses use regular nat, which means traffic  
# that exits the public interface appears to come from freebsd machine and  
# is subject to port translation.

nat on $public_interface from $private_network to any -> $thewall_public

# hack! redirect systems trying to talk to host2:smtp from the  
# private network to host3:smtp.

rdr on { $private_interface $public_interface }  proto tcp from $private_network to $host2_public port smtp -> $host2_public port smtp

rdr on $private_interface proto tcp from $private_network to $public_interface port smtp -> $host2_public  
no nat on $private_interface proto tcp from $private_interface to $private_network

nat on $private_interface proto tcp from $private_network to $host2_public port smtp -> $private_interface  
nat on $private_interface from $private_network to $private_network -> $private_interface

### filtering##

#default deny  
#block and log traffic entering all interfaces  
block return in log all

# pass traffic exiting all interfaces  
pass out all keep state

# traffic exiting public interface should use modulate state  
pass out on $public_interface all keep state

### traffic entering private interface (leaving private network) ###

# allow all traffic from private hosts out  
pass in quick on $private_interface proto { icmp tcp udp } from { $private_network $public_network } to any modulate state

# broadcast traffic  
pass in on $private_interface from $private_network to $private_network keep state  
pass in on $private_interface from $private_network to $private_interface:broadcast keep state

# alow smtp from host2 to anywhere  
pass in quick on $private_interface proto tcp from $host2_private to any port smtp modulate state

# allow traffic to smtp  
pass in on $private_interface inet proto tcp from $private_network to $host2_private port smtp synproxy state flags S/SA

#allow broadcasts  
pass in quick on $private_interface proto { tcp, udp } from $private_interface:network  to $private_interface:broadcast keep state  
pass in quick on $private_interface from any to 100.138.196.127

#### traffic entering public interface (private network)#####

#allow ports 22,25,80,443,993,995 globally on host2  
pass in quick on { $public_interface $private_interface } proto tcp from any to $host2_private port {22 smtp 80 443 993 995} keep state

# allow all traffic from on outside_network to host2_private  
pass in quick on $public_interface proto { tcp udp icmp } from $outside_network to $host2_private keep state

# allow icmp echo from monitoring_hosts to thewall and private network #  
pass in quick on $public_interface proto icmp from { } to { $private_network } keep state

# allow icmp echo from anywhere  
pass in quick on $public_interface proto icmp from any to { $private_network } keep state

# allow ssh from distrib host, public network and Nagios hosts to firewall  
pass in quick on $public_interface proto tcp from { $distrib_host $public_network } to port ssh keep state

# allow ssh from outside_network wide hosts to private network  
pass in quick on $public_interface proto tcp from $outside_network to $private_network port ssh keep state

# allow http from public_subnet and distrib host to private network  
pass in quick on $public_interface proto tcp from { $outside_network } to $private_network port http keep state

# allow https from public_subnet and distrib host to private network  
pass in quick on $public_interface proto tcp from { $outside_network } to $private_network port https keep state

# allow IMAPS from public_subnet and distrib host to private network  
pass in quick on $public_interface proto tcp from { $outside_network } to $private_network port 993 keep state

# allow 995 from public_subnet and distrib host to private network  
pass in quick on $public_interface proto tcp from { $outside_network } to $private_network port 995 keep state

# allow smtp from public_subnet and distrib host to private network  
pass in quick on $public_interface proto tcp from { $outside_network } to $private_network port smtp keep state

# allow port 902 from public_subnet to private host host1  
pass in quick on $public_interface proto tcp from $public_network to $host1_private port 902 keep state

# allow port 443 from public_subnet to private host host1  
pass in quick on $public_interface proto tcp from $public_network to $host1_private port 443 keep state

### Allow NFS mouting from privating network  
# allow sunrpc TCP and UDP port from public_subnet to private nework  
pass in quick on $public_interface proto { tcp udp } from { $public_network $outside_128_network $outside_110_network } to $private_network port sunrpc keep state

# allow 755 UDP port from public_subnet to private nework  
pass in quick on $public_interface proto udp from $public_network to $private_network port 755 keep state

# allow nfsd TCP and UDP port from public_subnet to private nework  
pass in quick on $public_interface proto { tcp udp } from { $public_network $outside_128_network $outside_110_network } to $private_network port nfsd keep state

# allow uuidgen port from public_subnet to private nework  
pass in quick on $public_interface proto tcp from $public_network to $private_network port uuidgen keep state

# allow lanserver port from public_subnet to private nework  
pass in quick on $public_interface proto tcp from $public_network to $private_network port lanserver keep state

# allow entrust-aaas port from public_subnet to private nework  
pass in quick on $public_interface proto tcp from $public_network to $private_network port entrust-aaas keep state

# allow xmpp-client port from public_subnet to private nework  
pass in quick on $public_interface proto tcp from $public_network to $private_network port xmpp-client keep state

# allow rsh from distrib host to firewall and private network  
pass in quick on $public_interface proto tcp from $distrib_host to { $private_network } port shell keep state (tcp.closed 1)

# allow nrpe from monitoring hosts to firewall and private network  
pass in quick on $public_interface proto tcp from to { $private_network } port nrpe keep state

# allow broadcast icmp from public_subnet to private nework and public IPs  
pass in quick on $public_interface proto { tcp udp icmp } from $public_network to any keep state

# garbage to ignore (do not log; silently discard traffic)  
# ignore dhcp requests, ipp, microsoft broadcasts, rip  
block return in quick proto tcp from any to any port auth block drop in quick proto udp from any to any port { bootps dantz ipp netbios-dgm netbios-ns ntp router 2222 9960 }  

After you have that all setup, you can use the following commands to test out your setup:

  • Parse Rules

      pfctl -nf /etc/pf.conf
    
  • Load rules

      pfctl -f /etc/pf.conf
    
  • Watch the logs for blocked packets

      tcpdump -n -e -ttt -i pflog0
    

The RHEL hosts behind the NAT should have the the following under /etc/hosts

192.168.196.2 host1.domain.com host1  
192.168.196.3 host2.domain.com host2  
192.168.196.4 host3.domain.com host3  

and the following under /etc/sysconfig/network

NETWORKING=yes  
NETWORKING_IPV6=no  
HOSTNAME=host#.domain.com  
GATEWAY=192.168.196.100  

and of course set it’s IPs as the private ip.

I have also done a similar setup with iptables, and I will post my example on that next.


Published by Karim Elatov

10 May 2011

Tags