如何在macOS正确地使用Packet Filter
Lastmod: 2023-06-01

初初使用macOS,遇到以下的提示框。

「该应用从互联网下载,您是否打开?」,「该应用签名未经验证。」「是否允许应用接入传输?」,或者是,启动项出现了奇怪的名字。

这种警告提示,给我带来隐隐的不安。因为对背后原理的未知与不确定。

macOS披上一层优雅的外衣,内里的运行机制不会有说明,官方只有简略浅显的「用户帮助使用手册」。

其实Windows也会有这种顿然割裂的提示框,例如UAC、防火墙等弹窗,只不过看的时间长了,迷惑的感受淡化了。

我宁愿一个漂亮的系统从头到尾地蒙蔽我,界面一直绚丽,别具一格。可是,一旦露馅出现了不协调,刚建立的感觉容易顿失,Flyme、锤子系统也有类似的经历。

macOS的应用防火墙(ApplicationFirewall)就是一个例子,它图形界面没有缺陷,披着漂亮界面,内里却运行得一团糟。

我反复观察仍不得要领。

它按应用划分做网络准入控制。可是我却不能肯定它什么时候弹出防火墙允许的窗口。自定义添加应用白名单,竟然导致它暗里崩溃,而你全然不知。

我妥协,把防火墙关了。然而,开关按钮并没有真的把防火墙关了,我还要重启电脑!矛盾的是,我常常跟一帮黑客在同一个公司,同一个局域网,我还是需要防火墙的啊。

摒弃原装防火墙的道路,了解到macOS自带了另一套防火墙/包过滤器Packet Filter,简称PF。以下是我断续捣腾了几天才调通的一些方法。

我的需求很简单,默认屏蔽所有入站流量,仅按需要开放我临时用到的TCP端口,不要影响我访问互联网。

首先关闭原装防火墙。在图形界面,点击设置应用-网络-防火墙-关闭,重启电脑即可。以下是命令行版本。

#查看ApplicationFirewall规则
/usr/libexec/ApplicationFirewall/socketfilterfw --list
#命令行关闭ApplicationFirewal
sudo defaults write /Library/Preferences/com.apple.alf globalstate -int 0
sudo launchctl unload /System/Library/LaunchAgents/com.apple.alf.useragent.plist
sudo launchctl unload /System/Library/LaunchDaemons/com.apple.alf.agent.plist
sudo defaults read /Library/Preferences/com.apple.alf globalstate

PF常用的命令

#查看pf防火墙状态
sudo pfctl -s info
#启用pf防火墙:
sudo pfctl -e
#禁用pf防火墙:
sudo pfctl -d
#查看pf规则
sudo pfctl -sr

创建自己的PF配置文件,anchor可理解为一种封装。

sudo vim /etc/pf.ma22ko.local.conf

###
anchor "ma22ko.local.pf"
load anchor "ma22ko.local.pf" from "/etc/pf.anchors/ma22ko.local"
###

接下来具体写过滤规则。

sudo vim /etc/pf.anchors/ma22ko.local
###
# Network interfaces
# 主要针对上网的网卡做设置,我的是en0
ether=en0
# Don't filter on local loopback
set skip on lo0
# Table allow IPs
# table <local> persist file "/etc/auth_ips"
# Block all traffic on LAN interface en0 by default
# 封禁所有入站流量
block in on $ether all
# Allow all traffic in/out in the local subnet
# pass on $ether from <local>
# 放通所有主动访问互联网的流量
pass out on $ether proto { tcp, udp }
# Allow 9999,9209 and echoreq ICMP type
pass in on $ether proto tcp to port 9999 keep state
pass in on $ether proto tcp to port 9209 keep state
# 允许PING
pass inet proto icmp from any icmp-type echoreq
# 允许DHCP协议
pass in on en0 proto udp from any to any port 67:68 keep state
###

用以下命令,反复调试,终于调通。

#激活规则
sudo pfctl -E -f /etc/pf.ma22ko.local.conf
#查看运行状态
sudo pfctl -s info | grep Status
#查看运行的规则
sudo pfctl -sr

PF默认开机不启动,需要创建开机启动的守护进程。

sudo vim /Library/LaunchDaemons/local.ma22ko.pfctl.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>local.ma22ko.pfctl.plist</string>
  <key>Program</key>
      <string>/usr/local/bin/firewall.sh</string>
  <key>RunAtLoad</key>
      <true/>
  <key>LaunchOnlyOnce</key>
      <true/>
  <key>StandardOutPath</key>
      <string>/Users/jyufu/Downloads/pfctl_log.log</string>
  <key>StandardErrorPath</key>
      <string>/Users/jyufu/Downloads/pfctl_error.log</string>
</dict>
</plist>

编写启动脚本

sudo vim /usr/local/bin/firewall.sh
#!/bin/bash
/bin/sleep 5
/usr/sbin/ipconfig waitall
/sbin/pfctl -E -f /etc/pf.ma22ko.local.conf

#赋予执行权限
chmod 764 /usr/local/bin/firewall.sh

结尾就是这么朴素。

参考阅读

Setting up correctly Packet Filter (pf) firewall on any macOS (from Sierra to Big Sur)

man pf.conf