macOS 上的 pf 与 Linux 上的 iptables:差异与配置
2024-07-29 tech mac network iptables linux startup launchd 15 mins 2 图 5297 字
在管理防火墙规则和网络包过滤时,macOS 和 Linux 系统各自采用了不同的工具。macOS 使用 pf
(Packet Filter),而 Linux 则使用 iptables
。
这篇文章展示如何在 macOS 上配置 pf
规则以允许特定 IP 地址访问 SSH 端口。
pf
和 iptables
的主要区别
规则处理顺序
iptables
:按顺序处理规则,从上到下,遇到匹配的规则后就停止继续匹配。因此,规则的顺序非常重要。pf
:规则处理更为复杂,并不总是按顺序匹配。pf
会根据规则的类型和其他因素进行匹配,允许更加灵活的规则设置。
配置文件和语法
iptables
:使用命令行工具进行配置,规则保存为脚本文件(通常是bash
脚本)。配置文件不具备特定的结构。pf
:配置文件(通常是/etc/pf.conf
)有固定的结构和语法,更加人性化和易于管理。
功能特性
iptables
:基于链的架构,有filter
、nat
、mangle
、raw
等不同的表。每个表包含若干链,每个链包含若干规则。pf
:支持锚(anchors)和表(tables),规则可以引用其他规则集,通过使用scrub
、nat
、rdr
、queue
等关键字提供高级功能。
pf
规则处理示例
在 pf
中,规则并非简单地按顺序处理:
# /etc/pf.conf 示例
block in all # 默认阻止所有入站流量
pass in proto tcp from xx.xx.xx.xx to any port 22 # 允许特定 IP 的流量
# 其他规则...
在这个示例中,即使 block in all
在前面,后面的 pass
规则也会正确匹配并允许来自特定 IP 地址的流量连接到 22 端口。这与 iptables
的行为不同,iptables
会在遇到第一个匹配的 block
规则时停止处理。
iptables
规则处理示例
相同的规则在 iptables
中的配置如下:
# 先允许特定 IP 地址的流量
iptables -A INPUT -p tcp -s xx.xx.xx.xx --dport 22 -j ACCEPT
# 然后阻止所有其他流量
iptables -A INPUT -p tcp --dport 22 -j DROP
在 iptables
中,规则的顺序非常重要。我们必须先允许特定的流量,然后再阻止所有其他流量。
在 macOS 上配置 pf
规则
为了在 macOS 上设置防火墙规则,只允许特定的 IP 地址连接到 SSH 端口(22),你需要编辑 pf
配置文件,并确保这些规则在系统启动时自动应用。
默认规则:
pf 的 anchor 和 load anchor 类似于 iptables 的链和表,用于组织和加载规则。
pf 的 nat-anchor 和 rdr-anchor 处理 NAT 和端口重定向,类似于 iptables 的 nat 表和 PREROUTING 链。
pf 的 scrub-anchor 和 dummynet-anchor 提供类似于 iptables 的流量处理和带宽限制功能。
执行命令sudo pfctl -sr
,输出是:
No ALTQ support in kernel
ALTQ related functions disabled
scrub-anchor "com.apple/*" all fragment reassemble
scrub-anchor "com.apple.internet-sharing" all fragment reassemble
anchor "com.apple/*" all
anchor "com.apple.internet-sharing" all
解释一下:
- No ALTQ support in kernel / ALTQ related functions disabled:
- ALTQ(ALTernate Queueing)是用于流量整形的工具,但我的 macOS 内核没有启用对 ALTQ 的支持,因此与 ALTQ 相关的功能被禁用。
- scrub-anchor “com.apple/*” all fragment reassemble:
- 这个规则表示 macOS 会对所有通过
com.apple/*
锚点的流量进行scrub
操作。scrub
是一种重新组装 IP 分片的操作,用于处理和纠正 IP 数据包中的潜在问题。- scrub-anchor “com.apple.internet-sharing” all fragment reassemble:
- 这个规则与上一个类似,但它专门用于通过
com.apple.internet-sharing
锚点的流量。它也会对这些流量进行scrub
操作,重新组装 IP 分片。- anchor “com.apple/*” all:
- 这个规则表示所有通过
com.apple/*
锚点的流量将被转发到与com.apple/*
匹配的子规则进行处理。anchor
是一种将规则分组的方法,允许对特定流量应用一组规则。- anchor “com.apple.internet-sharing” all:
- 这个规则与上一个类似,但它专门用于通过
com.apple.internet-sharing
锚点的流量。这些流量将被转发到与com.apple.internet-sharing
匹配的子规则进行处理。
这些规则主要是处理和转发由 macOS 系统创建的流量锚点和规则,特别是那些与互联网共享和其他系统功能相关的规则。
scrub
操作用于重新组装分片的 IP 数据包,以确保它们在传输过程中没有问题,
anchor
规则则用于将流量转发到特定的子规则进行进一步处理。
编辑 pf.conf
文件
- 打开并编辑
/etc/pf.conf
文件:sudo vim /etc/pf.conf
-
添加新规则: 在合适的位置(例如在
scrub-anchor
和nat-anchor
之后),在我本机上,我放到了最后一行,插入以下规则:block in proto tcp from any to any port 22 pass in proto tcp from xx.xx.xx.xx to any port 22
-
保存并关闭文件。
- 重新加载
pf
配置:sudo pfctl -f /etc/pf.conf # 重新加载配置 sudo pfctl -e # 启用 pf sudo pfctl -sr # 查看当前规则以确认加载
在 macOS 上配置配置 pf 自启动脚本
-
创建启动脚本文件: 在
/usr/local/bin
目录下创建一个启动脚本文件:sudo vim /usr/local/bin/startup.sh
- 在脚本文件中添加以下内容:
#!/bin/bash # Enable and load pf rules sudo pfctl -e sudo pfctl -f /etc/pf.conf # 如果还有其他需求,可以一起加进来,例如增加一条路由 sudo route -n add -net xx.xx.0.0 -netmask 255.255.0.0 xx.xx.xx.1
-
使脚本可执行:
sudo chmod +x /usr/local/bin/startup.sh
配置启动项:
- 创建
LaunchDaemon
配置文件: 在/Library/LaunchDaemons
目录下创建一个plist
文件:sudo vim /Library/LaunchDaemons/com.custom.startup.plist
- 在
plist
文件中添加以下内容:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.custom.startup</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/startup.sh</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <false/> </dict> </plist>
- 设置适当的权限:
sudo chown root:wheel /Library/LaunchDaemons/com.custom.startup.plist sudo chmod 644 /Library/LaunchDaemons/com.custom.startup.plist
测试和验证
- 加载并测试
LaunchDaemon
:sudo launchctl load /Library/LaunchDaemons/com.custom.startup.plist
- 重启电脑以验证配置:
在重启后,确保
pf
规则和路由已正确应用。可以通过以下命令检查:sudo pfctl -sr # 查看当前的 pf 规则 sudo netstat -rn # 查看路由表
错误排查参考
遇到 Bootstrap failed: 5: Input/output error
错误时,可能是因为以下几个原因:
-
文件格式或内容问题: 确保
plist
文件的格式和内容正确。可以使用plutil
命令验证文件格式:plutil -lint /Library/LaunchDaemons/com.custom.startup.plist
如果文件有错误,
plutil
会指出具体问题。 -
权限问题: 确保
plist
文件和包含该文件的目录具有正确的权限和所有权。运行以下命令:sudo chown root:wheel /Library/LaunchDaemons/com.custom.startup.plist sudo chmod 644 /Library/LaunchDaemons/com.custom.startup.plist
-
检查系统日志: 系统日志可能包含更详细的错误信息。使用以下命令查看日志:
log show --predicate 'subsystem == "com.apple.launchservicesd"' --info --debug
-
重启
launchd
服务: 尝试重启launchd
服务或计算机,确保所有配置生效。sudo launchctl unload /Library/LaunchDaemons/com.custom.startup.plist sudo launchctl load /Library/LaunchDaemons/com.custom.startup.plist
-
使用
launchctl bootstrap
: 根据提示,可以尝试使用launchctl bootstrap
命令来加载plist
文件。确保以 root 身份运行此命令。sudo launchctl bootstrap system /Library/LaunchDaemons/com.custom.startup.plist