pf

PSU Macadmins Conference 2016

PSU MacAdmins is a great conference for MacAdmins across the world to come together to talk about issues that are happening in the Mac world. This year I have the privilege to speak at the conference on a topic that I have blogged about previously Packet Firewall (PF). The talk is similar to the one I gave at MacTech 2015, with updated slides and an example of how to use the ELK stack (Elastisearch, Logstash, Kibana) to build a dashboard of pf.log data. Visualization provides a quick glance at pf data or can provide enough information to determine how often an IP address are hitting all of your clients. Here is the slide deck. 

Links:
How Packet Firewall (PF) Can Protect Your Enterprise(PSUMac 2016)

 

pf.anchor

I have started to talk a bit more about PF in a broad sense. Over the course of my talks or general discussion it has been brought to my attention that people would like to see a sample pf.anchor. Well I have posted on my github a sample of pf.anchor.

Please not that when you place the file inside of pf.anchor you will do some following items in order for it to be successful

  1. Create a com.yourcompany within the pf.anchors folder
  2. Ensure the pf.conf file is set to read all the anchors within pf.anchors
  3. TEST

Here are the samples of both the pf.conf and pf.anchor files along with the link to Github. Happy Trails
#Default PF configuration file.
#

# This file contains the main ruleset, which gets automatically loaded
# at startup.  PF will not be automatically enabled, however.  Instead,
# each component which utilizes PF is responsible for enabling and disabling
# PF via -E and -X as documented in pfctl(8).  That will ensure that PF
# is disabled only when the last enable reference is released.
#
# Care must be taken to ensure that the main ruleset does not get flushed,
# as the nested anchors rely on the anchor point defined here. In addition,
# to the anchors loaded by this file, some system services would dynamically 
# insert anchors into the main ruleset. These anchors will be added only when
# the system service is used and would removed on termination of the service.
#
# See pf.conf(5) for syntax.
#
#
# com.apple anchor point
#
scrub-anchor "com.apple/*"
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"
dummynet-anchor "com.apple/*"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"

scrub-anchor "com.jason"
nat-anchor "com.jason"
rdr-anchor "com.jason"
dummynet-anchor "com.jason"
anchor "com.jason"
load anchor "com.jason" from "/etc/pf.anchors/com.jason"

This is the beginning of the pf.anchor file, which is read by pf.conf

#Macros
tcp_services = "{ rfb }"
casper_ssh = "{ ssh }"
casper_filerep = "{ 443 }"
casper_comms = "{ 8443 }"
udp_services = "{ rfb }"
icmp_types = "{ echorep, echoreq, timex, unreach }"

#Tables
#list out hosts to allow for whitelisting of "our" services

#table <block_hosts> persist
#table <dont_log_block_host> persist
#table <private> const { 10/8 172.16/12 192.168/16 224/8 }
#table <martians> const { 127/8 10/8 172.16/12 192.168/16 169.254/16 240/4 0/8 192.2.0.2/24 }

#Cyber Security Scanners
#table <whitelist_host> persist { \
        129.8.64.0/24 \
        150.342.46.291/27 \
#}

#Your Services
#table <yourhosts> persist { 821.6.14.24 123.4.5.987 198.33.45.11 128.4.98.103 198.7.128.193 100.3.28.14}
#           821.6.14.24      \ #Casper Server
#        123.4.5.987      \ #Bigfix production service
#        198.33.45.11        \ #Test server for Casper infrastructure
#        128.4.98.103       \ #jFuture management server
#        198.7.128.193       \ #Casper Software Repo
#        100.3.28.141       \ #Future management server

#table <bigfix> persist { 123.4.5.987  }

#these are added in if we need to allow SSH via OTP on a client device. 
#table <otp> const { 281.4.56.43 }      

#ssh.server.corp = 113.56.78.987
#otp.example.corp = 281.4.56.43

#Rules Created by "You"

#disable all filtering on loopback, possible Vmware nets
set skip on {lo,vmnet}

#block all inbound traffic
block in log all

#allow out the tcp and udp traffic
#pass in log proto tcp from <yourhosts> to port $tcp_services 
#pass in log proto udp from <yourhosts> to port $udp_services
#pass in log proto udp from <bigfix> to port $bigfix_udp 
#pass in log quick proto tcp from <otp> to port $casper_ssh
#pass in log quick proto udp from <otp> to port $casper_ssh
#pass in log proto tcp from <yourhosts> to port $casper_ssh
#pass in log proto udp from <yourhosts> to port $casper_ssh
#pass in log proto tcp from any to port $casper_comms
#pass in log proto tcp from <yourhosts> to port $casper_filerep

#Allow whitelist hosts
#pass in log from <whitelist_host> to any

#Allow Your Service hosts
#pass in log from <yourhosts> to any

#Allow control traffic from LBL router
#pass in proto igmp from router_ip_here allow-opts

#ICMP traffic allowed to be passed in
pass in log inet proto icmp icmp-type $icmp_types
#We specified the address family 'inet' because it is required by pf when specifing icmp type

#Trust all outbound
pass out all keep state

#James has these in his pf.conf files to do not log these, but we can take them out if necessary     
block in proto { tcp, udp } to port { 137:139, 17500 }
block in proto { tcp, udp } to port 631 #ipp - printers

Pf logging

In my previous post, PF for me PF for you, I went over how to utilize PF in your environment. One thing that I did not discuss was logging with PF. When PF is enabled, it does not log any of the pass in or blocks for the system. You can obtain the statistics on how well your firewall rules are performing by utilizing the following command:

pfctl -s info

Here is an example of the output:


Output of pfctl -s info. Giving you a listing of how effective is your firewall ruleset. 

But, let's say you wanted to collect more data to output to your log aggregator or just to the internal syslog to investigate;  how would you set this up?  Essentially, we want to create a text file  of traffic, things we block, or things we allow in - otherwise, we are flying blind. There are a few steps to set up logging on the system. (I have included the steps for set up on my Github as well.)

First, enable the syslogging of local2:

echo -e "# gather PF log data\nlocal2.*\t\t\t/private/var/log/pf.log" >> /etc/syslog.conf

Next, create the actual log file and change the permissions on the file:

touch /private/var/log/pf.log
chmod 640 /private/var/log/pf.log 
chown root:wheel /private/var/log/pf.log
killall -HUP syslogd

Next, set up a tcpdump from /dev/pflog0 to syslog:

cat >/usr/local/bin/pflog.sh <<END
#!/bin/sh
/sbin/ifconfig pflog0 create
/usr/sbin/tcpdump -lnettti pflog0 | /usr/bin/logger -t pf -p    local2.info
END

Then, change permissions on the the tcpdump logging:

chown root:wheel /usr/local/bin/pflog.sh
chmod 555 /usr/local/bin/pflog.sh

Next, create a launch daemon that will ensure pf is started at boot and is running:

cat >/Library/LaunchDaemons/name of.plist <

<key>Label</key>                <string>pflog</string>        <key>ProgramArguments</key>
            <array>
                    <string>/usr/local/bin/pflog.sh</string>
            </array>
    <key>Disabled</key>             <false/>
    <key>RunAtLoad</key>            <true/>
    <key>KeepAlive</key>            <true/>

END

Change Permissions:

chown root:wheel /Library/LaunchDaemons/nameof.plist
chmod 444 /Library/LaunchDaemons/nameof.plist

Finally, this step switches the pfctl launch Daemon to start fully rather than enabled on demand.  Add in the -e option into the ProgramArguments array inside of /System/Library/LaunchDaemons/com.apple.pfctl.plist
 

<key>ProgramArguments</key>

 <array>
    <string>pfctl</string>
    <string>-ef</string>
    <string>/etc/pf.conf</string>

Once all of this is in place then check to see if pf is running:

launchctl list | grep pf

Load the pf log plist:

launchctl load -w /Library/LaunchDaemons/nameof.plist

Then, check to ensure that pf log is now running:

launchctl list | grep pf

PF for me PF for you

Recently I have been tasked with expanding the firewall on OS X. By default the Application firewall is enabled on most devices as a standard safety procedure. Nothing is wrong with the Application firewall Apple included in OS X but, my infrastructure required something a bit more robust. If you would like more reading about the OS X: Application firewall, click this link, which is the official blurb from Apple.

I did not have a laundry list of settings,. If anything, my settings were based on the output of nmap scans. OS X now ships with PF, the OpenBSD Packet Filter. PF allows administrators to tightly control the packet firewall on a device however, it is primarily a terminal-based configuration. There is a GUI application for it but, I found it easier to work with terminal and vi for configuration.

When determining your firewall philosophy you should ask yourself whether your default allows everything or does you default block everything? If yous default allows everything then you must decide what to block. This list could be excruciating to navigate because there are 65,535 ports. If your default blocks everything then this allows an administrator to only open ports that are truly needed.

Personally, I choose to go with the default block route, only opening the specific ports that my organization and users need in order to conduct business. Before implementing my rules I researched and read information to ensure I had a strong grasp of PF. I Googled the topic and also read The Book of PF. I highly recommend the book if you are going to start utilizing PF. After reading this book I was able to build a proper rule-set.

The first thing to do is to learn how to turn on and off the pf firewall. It does need elevated or root privileges to enable and disable.

Enable PF
pfctl -e

Disable pf
pfctl -d

The next thing is to determine where to place your configurations. I would suggest not placing the file in the pf.conf file located in /etc/ but to create an anchor within the /etc/pf.anchor directory. I advise this because Apple update could undo your changes

If you look at the contents within that /etc/pf.anchors there is a com.apple. If you look at the contents of the pf.conf file it tells pf to read rules from the pf.anchor directory.  Because of this I believe it is best to create an anchor and have pf.conf read my anchor.

As I stated earlier I decided to go with the default to block all traffic into the machine, while logging all blocked traffic.
block in log all

I wanted to allow certain types of traffic from certain hosts. I decided to utilize macros and tables within my anchor. OpenBSD documentation defines macros as " user-defined variables that can hold IP addresses, port numbers, interface names, etc. Macros can reduce the complexity of a PF rule-set and also make maintaining a rule-set much easier." In the same documentation a table is defined as " used to hold a group of IPv4 and/or IPv6 address. Looks against a table are very fast and consume less memory and processor time than lists."  With a better understanding of how macros and tables are used I decided to use macros for ports and tables for hosts. A rule could potentially look like this:

table <host_list> persist { 123.4.456.789 }

Only add persist or const to your table if you need a persistent or constant connection. 

When building my rule-set I defined each port which its on macro. I also created many different tables for my collection host.  I explicitly labeled my macros and tables. I did this because I create in my anchor file each rule line corresponds to a single macro and a single table. This helps because when pf has a command where you can print out your rule-set in plain english. It tends to put everything in perspective and allows everyone to easily see what is being allowed and not allowed within your config.

Print plain English pf rules:
pfctl -sr

Here is an example:


pass in log proto tcp from <host> to any port = 5900 flags S/SA keep state
pass in log proto udp from <host> to any port = 5900 keep state
pass in log quick proto tcp from <otp> to any port = 22 flags S/SA keep state
pass in log proto tcp from <host> to any port = 22 flags S/SA keep state
pass in log proto udp from <host> to any port = 22 keep state


Lastly pf has a syntax parser. This parser will give you the line the error has occurred on and if the rule-set was loaded. I would highly recommend users run this command prior to trying to enable a rule-set.

Parse Rule-set:
pfctl -nvf /etc/pf.conf

Here is an example of the output of this command:

/etc/pf.conf:53: syntax error

Pf is a powerful tool, that admins can use to secure the workstations in their environment or secure there servers. I took about two days of reading and playing around within terminal to get a strong grasp on Pf. Logging in Pf is interesting because there is not logging. You must setup the logging which I will cover in a later post.

Does anyone in the community utilize Pf?  If so in what way? If not, what is stopping you?

If you would like more in depth information about Pf please do not hesitate to contact me.