macOS 上的 pf 与 Linux 上的 iptables:差异与配置

在管理防火墙规则和网络包过滤时,macOS 和 Linux 系统各自采用了不同的工具。macOS 使用 pf(Packet Filter),而 Linux 则使用 iptables

这篇文章展示如何在 macOS 上配置 pf 规则以允许特定 IP 地址访问 SSH 端口。

pfiptables 的主要区别

规则处理顺序

  • iptables:按顺序处理规则,从上到下,遇到匹配的规则后就停止继续匹配。因此,规则的顺序非常重要。
  • pf:规则处理更为复杂,并不总是按顺序匹配。pf 会根据规则的类型和其他因素进行匹配,允许更加灵活的规则设置。

配置文件和语法

  • iptables:使用命令行工具进行配置,规则保存为脚本文件(通常是 bash 脚本)。配置文件不具备特定的结构。
  • pf:配置文件(通常是 /etc/pf.conf)有固定的结构和语法,更加人性化和易于管理。

功能特性

  • iptables:基于链的架构,有 filternatmangleraw 等不同的表。每个表包含若干链,每个链包含若干规则。
  • pf:支持锚(anchors)和表(tables),规则可以引用其他规则集,通过使用 scrubnatrdrqueue 等关键字提供高级功能。

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 配置文件,并确保这些规则在系统启动时自动应用。

默认规则:

image-20240729下午51906975

image-20240729下午54033902

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

解释一下:

  1. No ALTQ support in kernel / ALTQ related functions disabled:
    • ALTQ(ALTernate Queueing)是用于流量整形的工具,但我的 macOS 内核没有启用对 ALTQ 的支持,因此与 ALTQ 相关的功能被禁用。
  2. scrub-anchor “com.apple/*” all fragment reassemble:
    • 这个规则表示 macOS 会对所有通过 com.apple/* 锚点的流量进行 scrub 操作。scrub 是一种重新组装 IP 分片的操作,用于处理和纠正 IP 数据包中的潜在问题。
  3. scrub-anchor “com.apple.internet-sharing” all fragment reassemble:
    • 这个规则与上一个类似,但它专门用于通过 com.apple.internet-sharing 锚点的流量。它也会对这些流量进行 scrub 操作,重新组装 IP 分片。
  4. anchor “com.apple/*” all:
    • 这个规则表示所有通过 com.apple/* 锚点的流量将被转发到与 com.apple/* 匹配的子规则进行处理。anchor 是一种将规则分组的方法,允许对特定流量应用一组规则。
  5. anchor “com.apple.internet-sharing” all:
    • 这个规则与上一个类似,但它专门用于通过 com.apple.internet-sharing 锚点的流量。这些流量将被转发到与 com.apple.internet-sharing 匹配的子规则进行处理。

这些规则主要是处理和转发由 macOS 系统创建的流量锚点和规则,特别是那些与互联网共享和其他系统功能相关的规则。

scrub 操作用于重新组装分片的 IP 数据包,以确保它们在传输过程中没有问题,

anchor 规则则用于将流量转发到特定的子规则进行进一步处理。

编辑 pf.conf 文件

  1. 打开并编辑 /etc/pf.conf 文件
    sudo vim /etc/pf.conf
    
  2. 添加新规则: 在合适的位置(例如在 scrub-anchornat-anchor 之后),在我本机上,我放到了最后一行,插入以下规则:

    block in proto tcp from any to any port 22
    pass in proto tcp from xx.xx.xx.xx to any port 22
    
  3. 保存并关闭文件

  4. 重新加载 pf 配置
    sudo pfctl -f /etc/pf.conf  # 重新加载配置
    sudo pfctl -e                # 启用 pf
    sudo pfctl -sr               # 查看当前规则以确认加载
    

在 macOS 上配置配置 pf 自启动脚本

  1. 创建启动脚本文件: 在 /usr/local/bin 目录下创建一个启动脚本文件:

    sudo vim /usr/local/bin/startup.sh
    
  2. 在脚本文件中添加以下内容
    #!/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
    
  3. 使脚本可执行

    sudo chmod +x /usr/local/bin/startup.sh
    

配置启动项:

  1. 创建 LaunchDaemon 配置文件: 在 /Library/LaunchDaemons 目录下创建一个 plist 文件:
    sudo vim /Library/LaunchDaemons/com.custom.startup.plist
    
  2. 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>
    
  3. 设置适当的权限
    sudo chown root:wheel /Library/LaunchDaemons/com.custom.startup.plist
    sudo chmod 644 /Library/LaunchDaemons/com.custom.startup.plist
    

测试和验证

  1. 加载并测试 LaunchDaemon
    sudo launchctl load /Library/LaunchDaemons/com.custom.startup.plist
    
  2. 重启电脑以验证配置: 在重启后,确保 pf 规则和路由已正确应用。可以通过以下命令检查:
    sudo pfctl -sr  # 查看当前的 pf 规则
    sudo netstat -rn     # 查看路由表
    

错误排查参考

遇到 Bootstrap failed: 5: Input/output error 错误时,可能是因为以下几个原因:

  1. 文件格式或内容问题: 确保 plist 文件的格式和内容正确。可以使用 plutil 命令验证文件格式:

    plutil -lint /Library/LaunchDaemons/com.custom.startup.plist
    

    如果文件有错误,plutil 会指出具体问题。

  2. 权限问题: 确保 plist 文件和包含该文件的目录具有正确的权限和所有权。运行以下命令:

    sudo chown root:wheel /Library/LaunchDaemons/com.custom.startup.plist
    sudo chmod 644 /Library/LaunchDaemons/com.custom.startup.plist
    
  3. 检查系统日志: 系统日志可能包含更详细的错误信息。使用以下命令查看日志:

    log show --predicate 'subsystem == "com.apple.launchservicesd"' --info --debug
    
  4. 重启 launchd 服务: 尝试重启 launchd 服务或计算机,确保所有配置生效。

    sudo launchctl unload /Library/LaunchDaemons/com.custom.startup.plist
    sudo launchctl load /Library/LaunchDaemons/com.custom.startup.plist
    
  5. 使用 launchctl bootstrap: 根据提示,可以尝试使用 launchctl bootstrap 命令来加载 plist 文件。确保以 root 身份运行此命令。

    sudo launchctl bootstrap system /Library/LaunchDaemons/com.custom.startup.plist
    

在 Mac 上将当天的日历事项导出为 TXT 文件 大脑健身房 数字心理干预 - 老石谈芯