Friday, January 20, 2012

set up PF (packet filter) firewall on FreeBSD 8.2

set up PF (packet filter) firewall on FreeBSD 8.2

Network Structure
Outside World <-----> PF on FreeBSD 8.1 as a router <-----> DMZ network (more FreeBSD boxes)

Quick Notes

[] "pfctl -e" or "pfctl -d" can be used to enable and disable PF respectively. Note that this just enables or disables PF, it doesn't actually load a ruleset. The ruleset must be loaded separately, either before or after PF is enabled. To load the pf.conf ruleset file, run "pfctl -f /etc/pf.conf".

[] Filter rules are evaluated in sequential order, first to last. Unless the packet matches a rule containing the quick keyword, the packet will be evaluated against all filter rules before the final action is taken.

[] The last rule to match is the "winner" and will dictate what action to take on the packet.

[] There is an implicit pass all at the beginning of a filtering ruleset meaning that if a packet does not match any filter rule the resulting action will be pass.

[] The keyword any meaning all addresses.

[] The keyword all which is short for from any to any.

Edit /boot/loader.conf:
# vim /boot/loader.conf
pf_load="YES" # packet filter
pflog_load="YES" # packet filter log

Edit /etc/rc.conf
### LAN
ifconfig_xl0="inet 192.168.3.1 netmask 255.255.255.0"

### WAN
ifconfig_re0="inet 219.17.112.243 netmask 255.255.255.192"

### WAN - extra IP addresses
### Note: the netmask of extra IP addresses must be 255.255.255.255
ifconfig_re0_alias0="inet 219.17.112.244 netmask 255.255.255.255"
ifconfig_re0_alias1="inet 219.17.112.245 netmask 255.255.255.255"

### Default Route
defaultrouter="219.17.112.193"

### LAN static route
static_routes="lan"
route_lan="-net 192.168.5.0/24 192.168.100.9"

### enable PF (Packet Filter Firewall)
pf_enable="YES"
pf_rules="/etc/pf.conf"
pf_flags=""
pflog_enable="YES"
pflog_logfile="/var/log/pf.log"
pflog_flags=""

### enable as LAN gateway.
### You must enable this if you want to do NAT (network address translation).
gateway_enable="YES"

### FTP-Proxy.
### Note: add these two lines to allow people to connect to your FTP server (192.168.100.8) behind NAT.

ftpproxy_enable="YES"
ftpproxy_flags="-R 192.168.100.8"

Note: Since NAT is almost always used on routers and network gateways, it will probably be necessary to enable IP forwarding so that packets can travel between network interfaces on the OpenBSD machine. IP forwarding is enabled using the sysctl(3) mechanism

On FreeBSD, setting gateway_enable="YES" in /etc/rc.conf will automatically turn on net.inet.ip.forwarding=1. You can check this by running:

# sysctl net.inet.ip.forwarding
net.inet.ip.forwarding: 1

If you are using OpenBSD, you can run follow command:

# sysctl net.inet.ip.forwarding=1
# sysctl net.inet6.ip6.forwarding=1 (if using IPv6)

To make this change permanent, the following lines should be added to /etc/sysctl.conf (on OpenBSD. No need on FreeBSD):

net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1

These lines are present but commented out (prefixed with a #) in the default install. Remove the # and save the file. IP forwarding will be enabled when the machine is rebooted.

Create a file for later use:
# touch /etc/pf-blocked_ip

Edit /etc/pf.conf
root@fw1 [/root] # cat /etc/pf.conf

##########################
##### MACROS - User-defined variables that can hold IP addresses, interface names, etc.
##########################
wan_if="re0"
dmz_if="xl0"
#lan_if=""

icmp_types = "{echoreq echorep unreach}"
#icmp_types = "{echoreq}"

tcp_services = "{ssh smtp domain http https ntp nicname}"

udp_services = "{domain ntp}"

allow_ssh_ip = "{219.17.152.199 224.1.1.1}"

dmz_www_ip = "192.168.100.5"

dmz_sql_ip = "192.168.100.6"

dmz_svn_ip = "192.168.100.7"

dmz_ftp0_ip0 = "192.168.100.8"

cn_srv_ip = "222.144.87.188"

ca_pub_ip = "219.17.152.196"

allow_sql_ip = "{" $cn_srv_ip $ca_pub_ip "}"

##########################
##### TABLES - A structure used to hold lists of IP addresses.
##########################
table <blocked_ip> persist file "/etc/pf-blocked_ip"

##########################
##### OPTIONS - Various options to control how PF works.
##########################

### Sets the default behavior for filter rules that specify the block action.
### Return error codes for ports that are blocked. Allows faster error recovery.
#set block-policy return

### Set pf's debugging level.
#set debug misc

### Skip all PF processing on specified interface. This can be useful on loopback interfaces where filtering, normalization, queueing, etc, are not required.
set skip on lo0

##########################
##### NORMALIZATION
##########################
### Traffic normalization for incoming packets - scrub provides a measure of protection against certain kinds of attacks based on incorrect handling of packet fragments.
### One reason not to scrub on an interface is if one is passing NFS through PF. Some non-OpenBSD platforms send (and expect) strange packets -- fragmented packets with the "do not fragment" bit set, which are (properly) rejected by scrub. This can be resolved by use of the no-df option. Another reason is some multi-player games have connection problems passing through PF with scrub enabled. Other than these somewhat unusual cases, scrubbing all packets is a highly recommended practice.
### The scrub directive syntax is very similar to the filtering syntax which makes it easy to selectively scrub certain packets and not others. The no keyword can be used in front of scrub to specify packets that will not be scrubbed. Just as with nat rules, the first matching rule wins.
scrub in all

##########################
##### QUEUEING - Provides bandwidth control and packet prioritization.
##########################

##########################
##### TRANSLATION
##########################
### NAT
nat on $wan_if from $dmz_if/24 to any -> $wan_if

### [HTTP] RDR Outside to DMZ
rdr on $wan_if proto tcp from any to $wan_if port 80 -> $dmz_www_ip

### [MYSQL] RDR Outside to DMZ
rdr on $wan_if proto tcp from $allow_sql_ip to $wan_if port 3306 -> $dmz_sql_ip

### [RSYNCD] RDR Outside to DMZ
rdr on $wan_if proto tcp from $cn_srv_ip to $wan_if port 873 -> $dmz_www_ip

### [MAIL] RDR Outside to DMZ
#rdr on $wan_if proto tcp from any to $wan_if/32 port {25 110} -> 192.168.10.2

### [FTP] RDR Outside to DMZ
#rdr on $wan_if proto tcp from $allow_ftp_ip to $wan_if port 21 -> 192.168.10.8 port 21

### [FTP] RDR DMZ to Outside (through ftp-proxy)
### Note: make sure you have this line "ftpproxy_enable="YES"" in your /etc/rc.conf.
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"
rdr pass proto tcp from any to $wan_if port 21 -> 127.0.0.1 port 8021

### [RAID-3WARE]
#rdr on $wan_if proto tcp from $wan_if/26 to $wan_if port 888 -> $dmz_sql_ip

##########################
##### FILTERING RULES - Allows the selective filtering or blocking of packets as they pass through any of the interfaces.
##### Filter rules can be given parameters to specify network address translation (NAT) and packet redirection.
##########################
### setup a default deny policy - block everything (inbound and outbound on all interfaces).
### Note: if you want to see the log file to debug your filter rules, change following line to "block log all", otherwise use "block all" instead..
block log all

block in quick on $wan_if from <blocked_ip> to any

### activate spoofing protection for all interfaces.
block in quick from urpf-failed

### [DNS] Outside to Router
pass in quick on $wan_if proto {tcp udp} from any to $wan_if port 53

### [DNS] DMZ to Router
pass in quick on $dmz_if proto {tcp udp} from $dmz_if/24 to any port 53

### [DNS] Router to Outside
pass out quick on $wan_if proto {tcp udp} from $wan_if to any port 53

### [DNS] Router to DMZ
pass out quick on $dmz_if proto {tcp udp} from $dmz_if to $dmz_if/24 port 53

### [HTTP] Outside to DMZ
pass in quick on $wan_if proto tcp from any to $dmz_www_ip port 80
pass out quick on $dmz_if proto tcp from any to $dmz_www_ip port 80

### [HTTP] DMZ to Router
### Note: FreeBSD's portsnap command uses port 80 to grab latest FreeBSD ports/patches.
pass in quick on $dmz_if proto tcp from $dmz_if/24 to any port 80

### [HTTP] Router to Outside
pass out quick on $wan_if proto tcp from $wan_if to any port 80

### [MYSQL] DMZ to Outside
pass in quick on $dmz_if proto tcp from $dmz_sql_ip to $cn_srv_ip port 3306
pass out quick on $wan_if proto tcp from $wan_if to $cn_srv_ip port 3306

### [MYSQL] Outside to DMZ
pass in quick on $wan_if proto tcp from $allow_sql_ip to $dmz_sql_ip port 3306
pass out quick on $dmz_if proto tcp from $allow_sql_ip to $dmz_sql_ip port 3306

### [SMTP] DMZ to Outside
pass in quick on $dmz_if proto tcp from $dmz_www_ip to any port 25
pass out quick on $wan_if proto tcp from $wan_if to any port 25

### [RSYNCD] Outside to DMZ
pass in quick on $wan_if proto tcp from $cn_srv_ip to $dmz_www_ip port 873
pass out quick on $dmz_if proto tcp from $cn_srv_ip to $dmz_www_ip port 873

### [SSH] Outside to Router
pass in quick on $wan_if proto tcp from $allow_ssh_ip to $wan_if port 22 flags S/SA keep state

### [SSH] Router to DMZ
pass out quick on $dmz_if proto tcp from $dmz_if to $dmz_if/24 port 22 flags S/SA keep state

### [ICMP] DMZ/LAN to ANY
pass in quick on $dmz_if proto icmp from {$dmz_if/24 $lan_net} to any icmp-type $icmp_types

### [ICMP] Router to Outside
pass out quick on $wan_if proto icmp from $wan_if to any icmp-type $icmp_types

### [ICMP] DMZ to DMZ/LAN
pass out quick on $dmz_if proto icmp from $dmz_if/24 to {$dmz_if/24 $lan_net} icmp-type $icmp_types

### [NTP] DMZ to Router
pass in quick on $dmz_if proto {tcp udp} from $dmz_if/24 to any port 123

### [NTP] Router to Outside
pass out quick on $wan_if proto {tcp udp} from $wan_if to any port 123

### [FTP] We need to have an anchor for ftp-proxy.
anchor "ftp-proxy/*"

### [FTP] from DMZ to ANY
pass in quick log on $dmz_if proto {tcp} from $dmz_if/24 to any port 21
pass in quick log on $dmz_if proto {tcp} from $dmz_if/24 to any port > 1023

### [FTP] from ROUTER to ANY
pass out quick log on $wan_if proto {tcp} from $wan_if to any port 21
pass out quick log on $wan_if proto {tcp} from $wan_if to any port > 1023

### [FTP] from ANY to $dmz_ftp0_ip
pass out quick on $dmz_if proto {tcp} from $dmz_if to $dmz_ftp0_ip0 port 21

### [NICNAME] DMZ to Router
pass in quick on $dmz_if proto tcp from $dmz_if/24 to any port 43

### [NICNAME] Router to Outside
pass out quick on $wan_if proto tcp from $wan_if to any port 43

### [SVN] Router to DMZ
pass out quick on $dmz_if proto tcp from $dmz_if to $dmz_svn_ip port 3690

### [RAID-3WARE]
#pass in quick on $wan_if proto tcp from $wan_if/26 to $dmz_sql_ip port 888
#pass out quick on $dmz_if proto tcp from $wan_if/26 to $dmz_sql_ip port 888

### [TRACEROUTE] Allow outgoing Trace route
#pass out on $ext_if inet proto udp from any to any port 33433 >< 33626 keep state


Solution to No ALTQ support in kernel ALTQ related functions disabled

Recompile your kernel:
# cd /usr/src/sys/`uname -m`/conf
# cp GENERIC MYKERNEL8.2

# vim MYKERNEL8.2
### PF Packet Filter Related settings
device pf
device pflog
device pfsync

options         ALTQ
options         ALTQ_CBQ        # Class Bases Queuing (CBQ)
options         ALTQ_RED        # Random Early Detection (RED)
options         ALTQ_RIO        # RED In/Out
options         ALTQ_HFSC       # Hierarchical Packet Scheduler (HFSC)
options         ALTQ_PRIQ       # Priority Queuing (PRIQ)
options         ALTQ_NOPCC      # Required for SMP build

Note: to view a list of available options:
# cat /usr/src/sys/conf/NOTES
# cat /usr/src/sys/`uname -m`/conf/DEFAULTS

# cd /usr/src
# make buildkernel KERNCONF=MYKERNEL8.2
# make installkernel KERNCONF=MYKERNEL8.2

# sync ; reboot

Note: if the system could not boot properly, press any keys other than enter key when you see the count down number. and type following at boot: prompt:
boot: unload
boot: /kernel.old
http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/boot-blocks.html

Useful Commands

Make sure the pf kernel module has been loaded:
# kldstat | grep pf
2 2 0xc0f8c000 32e98 pf.ko
3 1 0xc61db000 3000 pflog.ko

Diable the packet filter
# pfctl -d

Enable the packet filter
# pfctl -e

Flush all (nat, filter, queue, state, info, table) rules and reload from the file /etc/pf.conf
# pfctl -F all -f /etc/pf.conf

If you are setting up pf remotely, and you are not confident enough with your PF rules, you might be blocked out by the wrong rules. Following command allows you to try the rules, and disable PF after 20 seconds:
# pfctl -f /etc/pf.conf; sleep 20; pfctl -d

This does not actually load any rules, but allows you to check for syntax errors in the file before you do load the ruleset. This is obviously good for testing.
# pfctl -vnf /etc/pf.conf

Report on the currently loaded filter ruleset.
# pfctl -s rules

Report on the currently loaded nat ruleset.
# pfctl -s nat

Report on the currently running state table (very useful).
# pfctl -s state

Tables can be manipulated on the fly by using pfctl(8). For instance, to add entries to the <blocked_ip> table:
# pfctl -t blocked_ip -T add 218.70.0.0/16

This will also create the <blocked_ip> table if it doesn't already exist.

To list the addresses in a table:
# pfctl -t blocked_ip -T show

Note: The -v argument can also be used with -T show to display statistics for each table entry.

To remove addresses from a table:
# pfctl -t blocked_ip -T delete 218.70.0.0/16

Troubleshooting
dump traffic on a network, see packets that match a certain port
# tcpdump -ni re0 port 53

You may also want to limit tcpdump to just show icmp:
# tcpdump -ni re0 icmp

Or just to/from a certain host:
# tcpdump -ni re0 icmp and host 172.16.70.12

Reading a PF (packet filter) log file

The log file written by pflogd is in binary format and cannot be read using a text editor. Tcpdump must be used to view the log.

To view the log file:

# tcpdump -n -e -ttt -r /var/log/pf.log

Note: that using tcpdump(8) to watch the pflog file does not give a real time display. A real time display of logged packets is achieved by using the pflog0 interface:

# ifconfig | grep pflog
# tcpdump -n -e -ttt -i pflog0

Filtering Log Output

Because pflogd logs in tcpdump binary format, the full range of tcpdump features can be used when reviewing the logs. For example, to only see packets that match a certain port:

# tcpdump -n -e -ttt -r /var/log/pf.log port 80

Reference:
set up PF (packet filter) firewall on FreeBSD 8.2
http://gala4th.blogspot.com/2010/12/set-up-pf-packet-filter-on-freebsd.html

PF supports FTP servers and FTP clients behind NAT


http://www.openbsd.org/faq/pf/index.html

http://www.openbsd.org/faq/pf/nat.html

http://www.weithenn.org/cgi-bin/wiki.pl?PF-%E5%88%A9%E7%94%A8_PF_%E8%BC%95%E9%AC%86%E9%81%94%E6%88%90_NAT

http://www.thedeepsky.com/howto/newbie_pf_guide.php

http://home.nuug.no/~peter/pf/en/ftpproblem.html

Active FTP vs. Passive FTP, a Definitive Explanation

Active FTP vs. Passive FTP, a Definitive Explanation

No comments: