Packet Firewall version 14 package configuration

Table of Contents

This package is provided to all campus macOS devices as part of the JAMF on-boarding process.

Current Package Version: 14

CHANGES from Version 13:

14.0 - Opened access too Apple services, including Airplay, AirDrop, and continuity

CHANGES from Version 12:

13.0 - Removal of Ivanti ports from edu.utexas.shared.pf.macros file.

13.1 - Fix of pre-install.sh script of package.

13.2 - Added 146.6.161.0/25 subnet to UTNets list in the edu.utexas.shared.pf.macros file.

13.3 - Revision of campus_only_ports and anywhere_ports, their rules, and their descriptions in the appropriate files.

Package Contents


 /Library
 /LaunchDaemons
 edu.utexas.shared.pf.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
     <dict>
          <key>Label</key>
          <string>edu.utexas.shared.pf</string>
          <key>ProgramArguments</key>
          <array>
               <string>/usr/local/bin/pf-control.sh</string>
               <string>restart</string>
          </array>
          <key>RunAtLoad</key>
          <true/>
          <key>StandardErrorPath</key>
          <string>/dev/null</string>
          <key>StandardOutPath</key>
          <string>/dev/null</string>
     </dict>
</plist>
 /etc
 edu.utexas.shared.pf.conf
# =====================================================
# edu.utexas.shared.pf.conf
# Version: 13
# Revised: 03/17/2022
#
# Packet Filter configuration file
#
# Credit
# Hannes Juutilainen <hjuutilainen@mac.com>
# https://github.com/hjuutilainen/pf-conf
# =====================================================
#
# edu.utexas.shared.pf anchor point
#
anchor "edu.utexas.shared.pf"
load anchor "edu.utexas.shared.pf" from "/etc/pf.anchors/edu.utexas.shared.pf.rules"
 /pf.anchors
 edu.utexas.shared.pf.macros
# =====================================================
# edu.utexas.shared.pf.macros
# Version: 13
# Revised: 04/26/2022
## Macros for Packet Filter
#
# Credit
# Hannes Juutilainen <hjuutilainen@mac.com>
# https://github.com/hjuutilainen/pf-conf
# =====================================================
protocol_types = "{ tcp, udp }"

campus_only_ports = "{ 22, 88, 137:139, 389, 443, 445, 515, 548, 554, 631, 636, 2049, 2105, 3031, 3283:3284, 3689, 5350:5354, 5900, 5988:5989, 59000 }"

# Above ports are for the following services in order: SSH, kerberos, wins/smb/netbios, ldap, https, netbios/smb, lpd (printing), afp, rtsp, ipp (printing), ldaps, nfs, kerberos, remote appleevents, ard, airplay, bonjour/mdns, vnc, cim-xml, cad/cti

anywhere_ports = "{ }"
# After further analysis, none of the ports listed below need to be forced opened since the client machine initiates an outbound connection for these services which is already allowed to return by another rule. They are left below for record purposes.

#anywhere_ports = "{ 123, 319:320, 2195:2197, 3970:3972, 4242:4243, 4444:4455, 4500, 5223, 16384-16402, 17500 }"
# Above ports are for the following services in order: ntp, ptp, apns, lanrev, crashplan, i2p, ipsec, apns, facetime, dropbox

# Tables

# UTnet Address Space-https://wikis.utexas.edu/pages/viewpage.action?pageId=50135084

table <UTNets> persist { \
169.254.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, \
128.83.17.230, 128.62.0.0/16, 128.83.0.0/16, \
129.116.0.0/16, 146.6.0.0/16, 146.6.161.0/25, \
198.213.192.0/18, 198.214.80.0/20, 198.214.250.0/23, \
206.76.64.0/18, FE80::/10, 2607:F888::/32 }


table <allow-mdns> persist { \
10.0.0.0/8, 172.16/12, 192.168/16, \
224.0.0.0/24, \
239.0.0.0/8, \
fe80::/10, \
ff00::/13, ff08::/16, ff10::/13, ff18::/16, \
ff20::/13, ff28::/16, ff30::/13, ff38::/16, \
ff40::/13, ff48::/16, ff50::/13, ff58::/16, \
ff60::/13, ff68::/16, ff70::/13, ff78::/16 }
 edu.utexas.shared.pf.rules
# =====================================================
# edu.utexas.shared.pf.rules
# Version: 13
# Revised: 03/17/2022
#
# Packet Filter defaults
#
# Credit
# Hannes Juutilainen <hjuutilainen@mac.com>
# https://github.com/hjuutilainen/pf-conf
# =====================================================

# =====================================================
# Macros
# =====================================================
include "/etc/pf.anchors/edu.utexas.shared.pf.macros"

# =====================================================
# Filter rules
# =====================================================

# Do not filter on the loopback interface
set skip on {lo,vmnet}

# Scrub incoming packets
scrub in all no-df

# Antispoof
antispoof quick for en0
antispoof quick for (en1)
antispoof quick for (en2)
antispoof quick for (en3)

# Block in by default. Previous versions used block drop.
block return in log all

# Allow outgoing traffic
pass out all keep state

# Allow DHCP
pass in log inet proto udp from any port 67 to any port 68

# Allow ICMP from on campus
pass in log proto icmp from <campus_only_ips>

# Allow MDNS from on campus and special list
pass in log proto { tcp, udp } from <UTNets> to port mdns
pass in log proto { tcp, udp } from <allow-mdns> to port mdns
pass in log proto { tcp, udp } from <UTNets> to port 554
pass in log proto { tcp, udp } from <allow-mdns> to port 554

# Allow ports and services listed in macros from anywhere
# After further analysis, the purpose of this rule is already allowed by another rule. The rule is left below for record purposes
# pass in log proto $protocol_types from any to any port $anywhere_ports flags S/SA

# Restricts ports and services listed in macros to campus only IP address space
pass in log proto $protocol_types from <UTNets> to any port $campus_only_ports flags S/SA

#
# Custom rules anchor
#
anchor "custom"
load anchor "custom" from "/etc/pf.anchors/edu.utexas.shared.pf.custom"
 /usr
 /local
 /bin
 pf-control.sh
#!/bin/bash

# ==============================================================================
# pf-control.sh for EPM-University of Texas at Austin
# Version: 13
# Revised: 03/17/2022
#
# Credit:
# Packet Filter control script
# Copyright 2012 Hannes Juutilainen <hjuutilainen@mac.com>
# https://github.com/hjuutilainen/pf-conf
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================

# ========================================
# Declare variables
# ========================================
DEFAULT_RULES_FILE="/etc/pf.anchors/edu.utexas.shared.pf.rules"
MACROS_FILE="/etc/pf.anchors/edu.utexas.shared.pf.macros"
CUSTOM_RULES_FILE="/etc/pf.anchors/edu.utexas.shared.pf.custom"
CONF_DIR_CUSTOM="/etc/pf.anchors/edu.utexas.shared.pf.d"
PFCTL="/sbin/pfctl"
ECHO="/bin/echo"
STAT="/usr/bin/stat"
PRINTF="/usr/bin/printf"
SYSCTL="/usr/sbin/sysctl"
SLEEP="/bin/sleep"
OP_MODE="restart"
# Added for v9. Detection of overwritten pf.conf
PFCONF=$(cat /etc/pf.conf | grep "edu.utexas.shared.pf" | awk -F '"' 'FNR == 1 {print $2}')

# ========================================
$SLEEP 20
# ========================================

# ========================================
function usage () {
# ========================================
echo ""
echo "$0 [-h|--help|h] [start|stop|restart]"
echo ""
echo "Where:"
echo "-h|--help|h Print this message"
echo "start Start the firewall (without flushing)"
echo "stop Stop the firewall and flush rules"
echo "restart Flush and re-read all rules and restart firewall"
exit
}

# ========================================
function checkFilePerms () {
# ========================================
FILESTATS=`$STAT -f "%Su:%Sg, %SHp%SMp%SLp" "$1"`
if [[ $FILESTATS != "root:wheel, rw-r--r--" ]]; then
return 1
else
return 0
fi
}

# ========================================
function checkDirectoryPerms () {
# ========================================
FILESTATS=`$STAT -f "%Su:%Sg, %SHp%SMp%SLp" "$1"`
if [[ $FILESTATS != "root:wheel, rwxr-xr-x" ]]; then
return 1
else
return 0
fi
}

# ========================================
function verifyFiles () {
# ========================================
$ECHO ""
$ECHO "Verifying configuration security:"

FORMAT="%-50s%-10s\n"
INSECURE="Failed (Insecure, will not be loaded)"
VERIFIED="OK"
NOT_FOUND="Failed (No such file or directory)"
OBJECT=""
RESULT=""

OBJECT=$CONF_DIR_CUSTOM
if [[ -d "$CONF_DIR_CUSTOM" ]]; then
if checkDirectoryPerms "$CONF_DIR_CUSTOM"; then
RESULT=$VERIFIED
fi
else
RESULT=$NOT_FOUND
fi
$PRINTF "$FORMAT" "$OBJECT" "$RESULT"

OBJECT=$CUSTOM_RULES_FILE
if [[ -f "$CUSTOM_RULES_FILE" ]]; then
if checkFilePerms "$CUSTOM_RULES_FILE"; then
RESULT=$VERIFIED
else
RESULT=$INSECURE
fi
else
RESULT=$NOT_FOUND
fi
$PRINTF "$FORMAT" "$OBJECT" "$RESULT"

if [[ -d $CONF_DIR_CUSTOM ]]; then
shopt -s nullglob
DID_FIND_CUSTOMRULE_FILES=0
CUSTOM_RULES=/etc/pf.anchors/edu.utexas.shared.custom.d/*
for f in $CUSTOM_RULES
do
OBJECT=$f
if checkFilePerms "$f"; then
RESULT=$VERIFIED
DID_FIND_CUSTOMRULE_FILES=1
else
RESULT=$INSECURE
fi
$PRINTF "$FORMAT" "$OBJECT" "$RESULT"
done
shopt -u nullglob
fi
}

# ========================================
function enablePfctl () {
# ========================================
$ECHO ""
$ECHO "Starting Packet Filter and reading default rules"
$PFCTL -E > /dev/null 2>&1
$PFCTL -f /etc/edu.utexas.shared.pf.conf > /dev/null 2>&1
}

# ========================================
function disablePfctl () {
# ========================================
$ECHO ""
$ECHO "Disabling Packet Filter"
$PFCTL -d
}

# Added for v9 to ensure that pf.conf contains required anchors after upgrades or updates
# ========================================
function fixPFConf () {
# ========================================
$ECHO ""
$ECHO "Checking and repairing Packet Filter Configuration"
if [ -z "$PFCONF" ];then
# ============================================
# Add PF anchors to /etc/pf.conf
# ============================================
FILE='/etc/pf.conf'
LINE1='anchor "edu.utexas.shared.pf"'
LINE2='load anchor "edu.utexas.shared.pf" from "/etc/pf.anchors/edu.utexas.shared.pf.rules"'
grep '\banchor "edu.utexas.shared.pf"\b' $FILE || echo $LINE1 >> $FILE
grep '\bload anchor "edu.utexas.shared.pf" from "/etc/pf.anchors/edu.utexas.shared.pf.rules"\b' $FILE || echo $LINE2 >> $FILE
fi
}

# ========================================
function loadCustomRules () {
# ========================================
FILES_TO_LOAD=( "$MACROS_FILE" )

# Add custom rule file
if [[ -f "$CUSTOM_RULES_FILE" ]]; then
$ECHO ""
$ECHO "Loading $CUSTOM_RULES_FILE:"
if checkFilePerms "$CUSTOM_RULES_FILE"; then
$ECHO "---> $CUSTOM_RULES_FILE"
FILES_TO_LOAD=( "${FILES_TO_LOAD[@]}" "$CUSTOM_RULES_FILE" )
fi
fi

# Add each file in custom rule directory
$ECHO ""
$ECHO "Loading rule files in /etc/pf.anchors/edu.utexas.shared.pf.d/"
shopt -s nullglob
DID_FIND_CUSTOMRULE_FILES=0
CUSTOM_RULES=$CONF_DIR_CUSTOM/*
for f in $CUSTOM_RULES
do
if checkFilePerms "$f"; then
$ECHO "---> $f"
FILES_TO_LOAD=( "${FILES_TO_LOAD[@]}" "$f" )
DID_FIND_CUSTOMRULE_FILES=1
fi
done
shopt -u nullglob
[ $DID_FIND_CUSTOMRULE_FILES -eq 0 ] && $ECHO "---> Directory is empty"

# Read all files that passed permissions check
# and feed the results to pfctl
cat ${FILES_TO_LOAD[@]} | $PFCTL -a "edu.utexas.shared.pf/custom" -f- > /dev/null 2>&1
}

# ========================================
function showCurrentRules () {
# ========================================
$ECHO ""
$ECHO "Current rules:"
$PFCTL -a '*' -sr
}

# ========================================
function configureKernelParameters () {
# ========================================
$SYSCTL -w net.inet.ip.fw.enable=1 > /dev/null 2>&1
$SYSCTL -w net.inet.ip.fw.verbose=2 > /dev/null 2>&1
$SYSCTL -w net.inet6.ip6.fw.verbose=0 > /dev/null 2>&1
$SYSCTL -w net.inet.ip.fw.verbose_limit=0 > /dev/null 2>&1
$SYSCTL -w net.inet.ip.forwarding=0 > /dev/null 2>&1
}


while test -n "$1"; do
case $1 in
-h|--help|h)
usage
;;
start)
OP_MODE="start"
shift
;;
stop)
OP_MODE="stop"
shift
;;
restart)
OP_MODE="restart"
shift
;;
*)
usage
;;
esac
done

# Check for root
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 2>&1
exit 1
else
verifyFiles

if [[ $OP_MODE == "start" ]]; then
configureKernelParameters
# Added for v9 - fix for overwritten pf.conf
fixPFConf
enablePfctl
loadCustomRules
showCurrentRules

elif [[ $OP_MODE == "stop" ]]; then
disablePfctl

elif [[ $OP_MODE == "restart" ]]; then
configureKernelParameters
disablePfctl
# Added for v9 - fix for overwritten pf.conf
fixPFConf
enablePfctl
loadCustomRules
showCurrentRules

else
echo "Unknown operation mode..."
usage
exit 1
fi
fi

exit 0
 pf-restart.sh
#!/bin/bash
# ==============================================================================
# pf-restart.sh for EPM-University of Texas at Austin
# Version: 13
# Revised: 03/17/2022
#
# Restart pf firewall and display rules
#
# Credit:
# Copyright 2012 Hannes Juutilainen <hjuutilainen@mac.com>
# https://github.com/hjuutilainen/pf-conf
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
# Change to match the actual item (if needed):
LAUNCHD_ITEM_NAME="edu.utexas.shared.pf"
LAUNCHCTL="/bin/launchctl"
PFCTL="/sbin/pfctl"
PF_LAUNCHD_ITEM="/Library/LaunchDaemons/$LAUNCHD_ITEM_NAME.plist"
# Check for root
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 2>&1
exit 1
else
if [[ -f "$PF_LAUNCHD_ITEM" ]]; then
# Unload the launchd item if already loaded
$LAUNCHCTL list $LAUNCHD_ITEM_NAME
if [[ $? -eq 0 ]]; then
echo ""
echo "# Unloading $PF_LAUNCHD_ITEM"
$LAUNCHCTL unload "$PF_LAUNCHD_ITEM"
fi
# Load the launchd item
echo "# Loading $PF_LAUNCHD_ITEM"
$LAUNCHCTL load -w $PF_LAUNCHD_ITEM
# Reset the counters
# echo "# Flushing filter parameters"
# $PFCTL -F all
# echo ""
# Show current rules
sleep 1
echo "# Current active rules:"
$PFCTL -a "*" -sr
echo ""
else
echo "$PF_LAUNCHD_ITEM not found" 2>&1
exit 1
fi
fi
## Turn off application firewall
/usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off

Package Scripts


 preinstall.sh
#!/bin/bash

# ==============================================================================
# Preflight script for EPM-University of Texas at Austin
# Version: 13
# Revised: 04/22/2022
#
# Copyright 2012 Hannes Juutilainen <hjuutilainen@mac.com>
# https://github.com/hjuutilainen/pf-conf
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================

ITEM_NAME="edu.utexas.shared.pf"
NOW=$(date +"%Y-%m-%d-%H%M%S")
LAUNCHCTL="/bin/launchctl"
PF_LAUNCH_DAEMON="$3/Library/LaunchDaemons/$ITEM_NAME.plist"
BACKUP_DIR="$3/var/backups/$ITEM_NAME-$NOW"
mkdir $BACKUP_DIR

# =================================================
# Define the items to backup
# =================================================
FILES_TO_BACKUP=(
"$3/etc/edu.utexas.shared.pf.conf"
"$3/etc/pf.anchors/edu.utexas.shared.pf.rules"
"$3/etc/pf.anchors/edu.utexas.shared.pf.macros"
"$3/etc/pf.anchors/edu.utexas.shared.pf.custom"
"$3/etc/pf.conf"
PF_LAUNCH_DAEMON
)
DIRECTORIES_TO_BACKUP=(
"$3/etc/pf.anchors/edu.utexas.shared.pf.d"
)

# =================================================
# Backup
# =================================================
for A_FILE in "${FILES_TO_BACKUP[@]}"
do
if [[ -f "$A_FILE" ]]; then
echo "$SCRIPT_NAME: Backing up $A_FILE to $BACKUP_DIR/"
cp "$A_FILE" "$BACKUP_DIR/"
fi
done

for A_DIRECTORY in "${DIRECTORIES_TO_BACKUP[@]}"
do
if [[ -d "$A_DIRECTORY" ]]; then
echo "$SCRIPT_NAME: Backing up $A_DIRECTORY to $BACKUP_DIR/$NOW-$A_DIRECTORY"
cp -R "$A_DIRECTORY" "$BACKUP_DIR/"
fi
done

# =================================================
# Unload the launchd item
# =================================================
if [[ -f "$PF_LAUNCH_DAEMON" ]]; then
$LAUNCHCTL list $ITEM_NAME
if [[ $? -eq 0 ]]; then
$LAUNCHCTL unload -w $PF_LAUNCH_DAEMON
fi
fi

exit 0
 postinstall.sh
#!/bin/bash

# ==============================================================================
# Postflight script for packet filter installer for EPM-University of Texas at Austin
# Version: 13
# Revised: 03/17/2022
#
# Credit:
# Copyright 2012 Hannes Juutilainen <hjuutilainen@mac.com>
# https://github.com/hjuutilainen/pf-conf
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================

echo $SCRIPT_NAME: Starting

# ===========================================
# Create a rule template if it doesn't exist
# ===========================================
CUSTOM_RULE_FILE="$3/etc/pf.anchors/edu.utexas.shared.pf.custom"
if [[ ! -f $CUSTOM_RULE_FILE ]]; then
echo $SCRIPT_NAME: Creating example custom rules file $CUSTOM_RULE_FILE
echo "# =====================================" >> $CUSTOM_RULE_FILE
echo "# Custom rules for pf firewall" >> $CUSTOM_RULE_FILE
echo "# Macros: /etc/pf.anchors/edu.utexas.shared.macros"
echo "# =====================================" >> $CUSTOM_RULE_FILE
echo "" >> $CUSTOM_RULE_FILE
echo "# Example rule" >> $CUSTOM_RULE_FILE
echo "# pass in proto tcp from IP-ADDRESS to port 22" >> $CUSTOM_RULE_FILE
echo "" >> $CUSTOM_RULE_FILE
fi

# ============================================
# Create a directory for other custom files
# ============================================
CUSTOM_RULE_DIR="$3/etc/pf.anchors/edu.utexas.shared.pf.d"
if [[ ! -d "$CUSTOM_RULE_DIR" ]]; then
mkdir "$CUSTOM_RULE_DIR"
chown root:wheel "$CUSTOM_RULE_DIR"
chmod 755 "$CUSTOM_RULE_DIR"
fi

# ============================================
# Add ACTS PF anchors to /etc/pf.conf for Cisco anchor overrule
# ============================================
FILE='/etc/pf.conf'
LINE1='anchor "edu.utexas.shared.pf"'
LINE2='load anchor "edu.utexas.shared.pf" from "/etc/pf.anchors/edu.utexas.shared.pf.rules"'
grep '\banchor "edu.utexas.shared.pf"\b' $FILE || echo $LINE1 >> $FILE
grep '\bload anchor "edu.utexas.shared.pf" from "/etc/pf.anchors/edu.utexas.shared.pf.rules"\b' $FILE || echo $LINE2 >> $FILE

# ============================================
# Start if installed on startup volume
# ============================================
if [[ "$3" == "/" ]]; then
echo $SCRIPT_NAME: Installed on startup volume
echo $SCRIPT_NAME: Starting firewall
if [[ -f "/usr/local/bin/pf-restart.sh" ]]; then
/usr/local/bin/pf-restart.sh
fi
else
echo $SCRIPT_NAME: Installed on non-startup volume
echo $SCRIPT_NAME: Skipping firewall startup
fi

echo $SCRIPT_NAME: Done

exit 0