在家庭网络环境中,许多管理员希望能够在特定时间段自动限制网络访问 —— 例如在夜间十点后阻断孩子的设备访问互联网,或在凌晨时段对某些服务实行限流。OpenBSD 自带的 pf(Packet Filter)防火墙本身并不提供 cron 式的内置调度器,但通过与系统定时任务结合,可以优雅地实现这类时间触发的网络策略。本文将详细阐述这一实现路径的核心原理、可落地的配置参数以及监控维护要点。

为什么选择 OpenBSD pf 实现时间调度

OpenBSD pf 是 BSD 系统中最成熟的状态化防火墙之一,其设计哲学强调安全性与代码质量。相比于商业路由器或 OpenWrt 等嵌入式方案,pf 运行在完整的 Unix 系统中,提供更精细的规则控制能力。对于具备一定技术背景的家庭用户而言,在一台退役的 x86 机器上部署 OpenBSD 作为家庭网关,不仅可以实现基于时间策略的网络调度,还能获得透明代理、流量统计、入侵检测等额外能力。

pf 的核心优势在于其规则集的模块化设计。通过 anchors(锚点)机制,可以将主配置文件拆分为多个独立的规则文件,在运行时动态加载或卸载特定规则集。这一特性为时间调度提供了技术基础:预先编写好「白天模式」与「夜间模式」两套规则,通过外部调度器在指定时刻切换即可。

核心架构设计

实现时间规则调度的系统架构包含三个关键组件:主 pf 配置文件、锚点规则文件、以及定时切换脚本。主 pf.conf 作为入口文件,通过 anchor 指令引用外部规则文件;锚点规则文件则包含具体的时间策略逻辑;切换脚本由 cron 调用,负责在恰当时刻加载对应的规则文件。

这种设计的关键优势在于规则切换的原子性。每次切换时,pfctl 工具会重新解析并加载新的规则集,现有连接可能会被中断 —— 这对于家庭网络场景完全可以接受。如果需要更平滑的过渡(例如仅阻止新连接而保持现有连接),可以在规则中使用 max-src-connmax-src-conn-rate 等状态限制参数,但大多数夜间断网场景中,直接刷新规则集更为简单直接。

配置文件详解

首先,在主 pf.conf 中定义一个用于时间调度的锚点。以下是一个基础配置示例:

# /etc/pf.conf - 主配置文件

# 默认策略:允许所有出站流量
block in all
pass out all keep state

# 时间调度锚点
anchor "time_rules"

接下来创建两个锚点规则文件,分别对应白天和夜间模式。白天模式下,通常允许完整的网络访问:

# /etc/pf/time_rules_day.conf - 白天规则(完全开放)
pass in quick on egress from 192.168.1.0/24 to any keep state

夜间模式下,阻断特定网段或特定端口的流量:

# /etc/pf/time_rules_night.conf - 夜间规则(阻断配置)
# 阻断 192.168.1.0/24 网段的所有非本地流量
block in quick on egress from 192.168.1.0/24 to any

# 仅保留 DNS 和本地网关访问
pass in on egress from 192.168.1.1 to any port { 53 } keep state
pass in on egress from 192.168.1.0/24 to 192.168.1.1 keep state

上述规则使用 quick 关键字确保匹配后立即生效,不会被后续规则覆盖。对于家庭场景,一个常见的实际需求是仅阻断儿童设备的互联网访问,同时允许成年人设备不受限制。可以通过 MAC 地址绑定或 IP 地址池分离来实现这一点:

# /etc/pf/time_rules_night.conf - 针对性阻断版本
# 阻断儿童设备网段 192.168.1.100-192.168.1.150
block in quick on egress from 192.168.1.100 to 192.168.1.150 to any

# 成年人设备完全放行
pass in quick on egress from 192.168.1.10 to 192.168.1.50 to any keep state

切换脚本实现

切换脚本是整个系统的调度核心。推荐将脚本放置在 /usr/local/bin/ 目录下,并赋予执行权限:

#!/bin/sh
# /usr/local/bin/pf_time_switch.sh

MODE="${1:-day}"
PFCTL="/sbin/pfctl"
RULES_DIR="/etc/pf"

case "$MODE" in
    day)
        echo "切换至白天模式:开放网络访问"
        $PFCTL -a time_rules -f $RULES_DIR/time_rules_day.conf
        $PFCTL -f $RULES_DIR/pf.conf
        logger -t pf-schedule "Switched to daytime rules"
        ;;
    night)
        echo "切换至夜间模式:限制网络访问"
        $PFCTL -a time_rules -f $RULES_DIR/time_rules_night.conf
        $PFCTL -f $RULES_DIR/pf.conf
        logger -t pf-schedule "Switched to nighttime rules"
        ;;
    *)
        echo "用法: $0 {day|night}"
        exit 1
        ;;
esac

脚本接受命令行参数决定切换到哪种模式,并通过 logger 命令将切换事件记录到系统日志,便于后续审计排查。在执行规则加载前,建议先使用 pfctl -nf /etc/pf.conf 进行语法检查,确保规则文件没有语法错误:

# 添加语法检查的改进版脚本片段
day)
    $PFCTL -nf $RULES_DIR/time_rules_day.conf || { 
        echo "规则语法检查失败,终止切换"; exit 1; 
    }
    $PFCTL -a time_rules -f $RULES_DIR/time_rules_day.conf
    ...

Cron 定时任务配置

/etc/crontab 中添加两条定时任务,分别控制夜间断网与清晨恢复:

# OpenBSD crontab 示例
# 夜间模式:每天晚上十点开始执行
0   22  *  *  *  /usr/local/bin/pf_time_switch.sh night

# 白天模式:每天早上七点自动恢复
0   7   *  *  *  /usr/local/bin/pf_time_switch.sh day

如果需要区分工作日与周末(例如周末允许稍晚入睡),可以借助 crontab 的星期字段:

# 仅工作日夜间十点断网,周末延后至夜间十一点
0   22  *  * 1-5  /usr/local/bin/pf_time_switch.sh night
0   23  *  * 0,6  /usr/local/bin/pf_time_switch.sh night

监控与问题排查

部署完成后,监控切换是否正常执行是运维的重点。可以通过以下几种方式验证:

首先,检查 pf 规则状态。使用 pfctl -a time_rules -s rules 可以查看当前锚点中加载的规则,确认切换后规则已正确应用。其次,查阅系统日志。前面脚本中通过 logger 写入的日志条目会出现在 /var/log/messages 中,通过 grep pf-schedule /var/log/messages 可以快速定位切换时间点。

对于需要长期运行的生产环境,建议配置监控告警。例如,当切换脚本执行失败(返回非零退出码)时,通过邮件通知管理员:

$PFCTL -a time_rules -f $RULES_DIR/time_rules_night.conf || \
    echo "规则加载失败" | mail -s "PF切换异常" admin@example.com

另一个常见的排查手段是启用 pf 的日志记录功能。在规则中添加 log 关键字,可以在 pflog0 接口捕获被阻断的数据包:

block in log quick on egress from 192.168.1.0/24 to any

使用 tcpdump -i pflog0 可以实时观察被拦截的流量,帮助判断规则是否按预期工作。

进阶优化方向

对于更复杂的时间策略需求,可以考虑以下进阶方案。其一是使用 pf tables 存储需要管控的 IP 地址列表,通过脚本动态向 table 中添加或移除条目,相比直接切换整个规则文件更加灵活:

# 在 pf.conf 中定义 table
table <bedtime-hosts> const { 192.168.1.100, 192.168.1.101, 192.168.1.102 }

# 阻断 table 中的主机
block in quick on egress from <bedtime-hosts> to any

其二是结合 devd 实现事件驱动的规则变更。例如当特定设备接入网络时自动应用对应的访问策略。此外,还可以将时间调度与带宽限速(altq)结合,在夜间时段对视频网站等带宽密集型应用实施 QoS 限制,而非完全阻断。

总结

OpenBSD pf 防火墙通过 anchors 与外部调度器的组合,能够可靠地实现基于时间规则的家庭网络自动调度。核心实施要点包括:主配置文件中定义锚点、准备昼夜两套规则文件、编写带语法检查的切换脚本、配置 cron 定时任务、以及建立日志监控机制。对于希望精细控制家庭网络访问的用户,这一方案既保持了 BSD 系统的安全可靠特性,又提供了足够的灵活性以满足差异化的时间策略需求。


参考资料