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

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

#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 }

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

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

#table <bigfix> persist {  }

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

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

#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

Freeradius and OTP

People often wonder how they can harden their OS X environment. There are many methods and tools that can be used to harden a system. Most admins live and die by SSH; however, for those who are not seasoned with SSH it can be a daunting task.

To help protect weak passwords you can set up your OS X infrastructure to use One Time Password (OTP). Here is a the internet standard surrounding OTP. Depending on your environment the prerequisites for setting up OTP on a machine may vary, but you will need these at minimum:

  • OTP Server
  • Auth Module
  • OS X System
  • Static Address
  • Shared Secret

I actually utilize FreeRADIUS pam auth module, version 1.3.17, in this write up. Version 1.3.17 has some bugs, thus the reason I had to write up how I was able to utilize the buggy version. FreeRADIUS has released version 1.4, which is suppose to address the problems in 1.3.17. For those who have not updated or seen 1.4 you can use this write up to get the 1.3.17 module working. 

The first thing to do is download version 1.3.17 from https://github.com/FreeRADIUS/pam_radius. Then, unzip the file and move the folder onto your Desktop. Before you run the Make command, there are some edits that need to be made to Makefile and the pam_radius_auth.c file. If you try to compile without making the edits then this error will occur:

" pam_radius_auth.c:358:23: error: variable has incomplete type 'struct timezone’:

      struct timezone tz;

To counter this error you must change line 358:23 you must add in the follow lines above the struct timeval tv;

struct timeval {
 time_t tv_sec;
 suseconds_t tv_usec;
struct timezone {
 int tz_minuteswest;
 int tz_dsttime;

It should look like the following screenshot:

After this edit your code should be able to compile however what I found if you system is considered newer you will need run a different GCC command in order to compile correctly. The error you receive when running the make command without making a change to the Makefile is:

ld -Bshareable pam_radius_auth.o md5.o -lpam -o pam_radius_auth.so

In order to combat this you must edit the Makefile. Within the Makefile there is a section called Build Shared Library. Inside of Build Shared Library it states “On systems with a newer GCC, you will need to do:" gcc -shared pam_radius_auth.o md5.o -lpam -lc -o pam_radius_auth.so". You will want to uncomment out the code for gcc.

Next copy gcc line and run it in your terminal but with a -v flag at the end:

gcc -shared pam_radius_auth.o md5.o -lpam -lc -o pam_radius_auth.so

The output of the command should look like the following:

Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -dynamic -dylib -arch x86_64 -macosx_version_min 10.10.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk -o pam_radius_auth.so pam_radius_auth.o md5.o -lpam -lc -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/6.0/lib/darwin/libclang_rt.osx.a

ou will need to copy this section of code and enter it into your terminal. Once it completes, run the make command, which should result in a correct compile.

After these steps you will now have a complete and function pam_radius_auth.so. Half of OTP setup is now is complete.

Next, you will need to edit the proper configuration files, move the pam_radius_auth.so into the proper location, and test.

Configuration Files Editing

Navigate to /etc/sshd_config. Only lines that need to be edited inside of the file are the following:

Some lines maybe yes or no, you want to ensure they look like the previous image. 

After that you will need to move pam_radius_auth.so file into its correct place "/usr/lib/pam". Next navigate to “/etc” and create pam_radius.conf file or utilize one with the pam_radius.conf that was located in the zipped pam_radius folder on your desktop. Inside of the .conf file you will be specifiy the information about your OTP server. The information you will need to procure from your identity management or cyber team are:

  • server
  • shared_secret
  • timeout

Here is an example of the file. I would leave your localhost there, as the conf file indicates and add your infrastructure below the localhost.

Next you will need to edit the "/etc/pam.d/sshd". This file will tell your system where to find your pam_radius.conf. It also dictates which .so files to use for pam authorization. A non edited conf file will have nothing commented out. You will need to add in the line 5 into your file.

Using free radius, my file looks like the following:

Once you have all these items in place I would reboot the system and test OTP to ensure authentication is working properly. During testing, remember to have the console open on the device you are setting up OTP on in order to provide possible insight into any errors

If you have any questions please do not hesitate to comment to drop me a line info @ jasonkmiller.com or jason @ jasonkmiller.co

Terminal and Sophos for me

Recently, I have played around with Sophos and the command line tools that come with the product. If we, as sysadmins, can script the updates, installations, and scans of systems without interrupting the user, then everyone is happy. The user is not disrupted and sysadmins are compliant in protecting our users and organization. 

There are plenty of scripts written by people and documentation provided by Sophos to perform silent installations and uninstallations of Sophos Anti-Virus. Sophos has the process documented on their support page. Here is a sample of the some of the commands that you can use to uninstall the product:

This will uninstall unmanaged versions

sudo /Library/Application\ Support/Sophos/opm-sa/Installer.app/Contents/MacOS/InstallationDeployer --remove

This will uninstall managed versions


sudo /Library/Application\ Support/Sophos/opm/Installer.app/Contents/MacOS/InstallationDeployer --remove


sudo /Library/Application\ Support/Sophos/opm/Installer.app/Contents/MacOS/tools/InstallationDeployer --remove

Knowing  this information is great if you want to uninstall Sophos via the command line because a user cannot find the Remove Sophos Antivirus stored in:

/Library/Sophos\ Anti-Virus/Remove\ Sophos\ Anti-Virus.app/

For those who would like to automate this process you can utilize a script written by Rich Trouton.

Another handy tool that Sophos includes with their product is a command line tool. This tool details:

  • Version
  • Virus data version
  • Virus definitions
  • Perform Scans

These are just a few options that one can utilize on the command line. The Sophos binary is:


Here are some options that you can run from the command line:

-sc [*] : Scan dynamically compressed executables
-f [ ] : Full scan
-extensive [ ] : Scan complete contents of files
-di [ ] : Disinfect infected items
-s [*] : Run silently (do not list files swept)
-c [*] : Ask for confirmation before disinfection/deletion
-b [*] : Sound bell on virus detection
-all [*] : Scan all files
-rec [*] : Do recursive scan
-remove [ ] : Remove infected objects
-dn [ ] : Display file names as they are scanned
-ss [ ] : Don't display anything except on error or virus
-eec [ ] : Use extended error codes
-ext=extension,.. : Specify additional extensions to SWEEP
-p= : Write to logfile
-idedir= : Read IDEs from alternative directory
-exclude : Exclude the following objects from scanning
-include : Include the following objects in scanning
-v : Display complete version information
-vv : Display complete version information and details on
-h : Display this help and exit

The command line tool will also let you scan inside compressed files. If your organization utilizes Sophos as the corporate Anti-virus, I would suggest automated scans of the system, compressed files, adware/PUAs, especially after Apple released their Adware documentation.  The binary also allows an administrator to perform scans on extensions that may not be as common or included within a Sophos scan. Finally, being able to determine the Sophos Version & Virus definition version can help with reporting and enforcing the latest patches on your client machines.  

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
/sbin/ifconfig pflog0 create
/usr/sbin/tcpdump -lnettti pflog0 | /usr/bin/logger -t pf -p    local2.info

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>
    <key>Disabled</key>             <false/>
    <key>RunAtLoad</key>            <true/>
    <key>KeepAlive</key>            <true/>


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



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.