33.3.PF

自 FreeBSD 5.3 以来,OpenBSD 的 PF 防火墙的移植版已作为基本系统的集成部分。PF 是一个完整的、功能丰富的防火墙,它可选地支持 ALTQ(Alternate Queuing),提供服务质量(QoS)。

OpenBSD 项目维护着 PF 的权威参考文档,详见 PF FAQ。Peter Hansteen 维护了一篇详细的 PF 教程,网址为 http://home.nuug.no/~peter/pf/

注意

在阅读 PF FAQ 时,请记住,FreeBSD 版本的 PF 与上游的 OpenBSD 版本多年来已经有了显著的差异。并非所有功能在 FreeBSD 上的表现都与 OpenBSD 中相同,反之亦然。

FreeBSD 包过滤邮件列表 是一个提问关于配置和运行 PF 防火墙的好地方。在提问之前,请先查阅邮件列表存档,因为问题可能已经被解答过。

本部分手册专注于 PF 在 FreeBSD 中的使用,演示了如何启用 PF 和 ALTQ,并提供了几个在 FreeBSD 系统上创建规则集的示例。

33.3.1. 启用 PF

要使用 PF,必须首先加载其内核模块。本节描述了可以添加到 /etc/rc.conf 中的条目,以启用 PF。

首先,向 /etc/rc.conf 添加 pf_enable=yes

# sysrc pf_enable=yes

还可以在启动 PF 时传递其他选项,这些选项在 pfctl(8) 中有详细描述。将此条目添加或更改到 /etc/rc.conf 中,并在引号("")之间指定任何需要的标志:

pf_flags=""                     # pfctl 启动时的附加标志

如果 PF 找不到其规则集配置文件,它将无法启动。默认情况下,FreeBSD 不带规则集,并且没有 /etc/pf.conf 文件。可以在 /usr/share/examples/pf/ 中找到示例规则集。如果自定义规则集保存在其他地方,可以在 /etc/rc.conf 中添加一行,指定文件的完整路径:

pf_rules="/path/to/pf.conf"

PF 的日志支持由 pflog(4) 提供。要启用日志支持,请在 /etc/rc.conf 中添加 pflog_enable=yes

# sysrc pflog_enable=yes

还可以添加以下行来更改日志文件的默认位置,或指定启动 pflog(4) 时要传递的附加标志:

pflog_logfile="/var/log/pflog"  # pflogd 应该存储日志文件的位置
pflog_flags=""                  # pflogd 启动时的附加标志

最后,如果防火墙后面有局域网,并且需要转发数据包给局域网中的计算机,或需要进行 NAT,启用以下选项:

gateway_enable="YES"            # 启用作为 LAN 网关

保存必要的编辑后,可以通过以下命令启动 PF,并启用日志支持:

# service pf start
# service pflog start

默认情况下,PF 从 /etc/pf.conf 读取其配置规则,并根据该文件中指定的规则或定义来修改、丢弃或放行数据包。FreeBSD 安装包括位于 /usr/share/examples/pf/ 中的几个示例文件。有关 PF 规则集的完整内容,请参阅 PF FAQ

要控制 PF,可以使用 pfctl有用的 pfctl 选项 总结了该命令的一些常用选项。有关所有可用选项的描述,请参阅 pfctl(8)

表 1. 有用的 pfctl 选项

命令
作用

pfctl -e

启用 PF。

pfctl -d

禁用 PF。

pfctl -F all -f /etc/pf.conf

清除所有 NAT、过滤、状态和表规则,并重新加载 /etc/pf.conf

pfctl -s [ rules | nat | states ]

报告过滤规则、NAT 规则或状态表。

pfctl -vnf /etc/pf.conf

检查 /etc/pf.conf 中的错误,但不加载规则集。

技巧

security/sudo 对于运行需要提升权限的命令(如 pfctl)非常有用,可以从 Ports 安装。

为了监控通过 PF 防火墙的流量,可以考虑安装包或 Port sysutils/pftop 。安装后,可以运行 pftop 查看流量的实时快照,其格式类似于 top(1)

33.3.2. PF 规则集

本节演示了如何创建自定义规则集。从最简单的规则集开始,逐步构建其概念,并通过几个示例展示 PF 众多功能的实际应用。

最简单的规则集适用于不运行任何服务、只需要访问一个网络(可能是互联网)的单一计算机。要创建这个最小的规则集,请编辑 /etc/pf.conf,使其内容如下:

block in all
pass out all keep state

第一条规则默认拒绝所有传入的流量。第二条规则允许由该系统发起的连接通过,同时保留这些连接的状态信息。这些状态信息允许该连接的返回流量通过,并且应仅在信任的计算机上使用。可以通过以下命令加载规则集:

# pfctl -e ; pfctl -f /etc/pf.conf

除了保持状态外,PF 还提供了 列表,可以在创建规则时使用。宏可以包含列表,且需要在使用之前定义。例如,在规则集的顶部插入以下行:

tcp_services = "{ ssh, smtp, domain, www, pop3, auth, pop3s }"
udp_services = "{ domain }"

PF 理解端口名以及端口号,只要这些名称在 /etc/services 中列出。这个例子创建了两个宏,第一个是七个 TCP 端口名的列表,第二个是一个 UDP 端口名。待定义,宏可以在规则中使用。在这个例子中,除了该系统发起的七个指定的 TCP 服务连接和一个指定的 UDP 服务连接外,所有流量都会被阻止:

tcp_services = "{ ssh, smtp, domain, www, pop3, auth, pop3s }"
udp_services = "{ domain }"
block all
pass out proto tcp to any port $tcp_services keep state
pass proto udp to any port $udp_services keep state

尽管 UDP 被认为是无状态协议,但 PF 能够跟踪某些状态信息。例如,当传递一个 UDP 请求,询问域名服务器关于域名的查询时,PF 会监视返回流量,以便允许它通过。

每当对规则集进行编辑时,必须加载新规则才能使用:

# pfctl -f /etc/pf.conf

如果没有语法错误,pfctl 在加载规则时不会输出任何消息。也可以在尝试加载规则之前先进行测试:

# pfctl -nf /etc/pf.conf

包括 -n 选项会让规则仅被解释,而不会被加载。这提供了一个机会来纠正任何错误。在任何时候,最后加载的有效规则集都会被强制执行,直到 PF 被禁用或加载新规则集为止。

技巧

pfctl 规则集验证或加载时添加 -v 选项,将显示完全解析后的规则,准确地显示它们将如何被加载。这在调试规则时非常有用。

33.3.2.1. 一个简单的网关与 NAT

本节演示如何配置运行 PF 的 FreeBSD 系统,使其作为至少一台其他计算机的网关。网关需要至少两个网络接口,每个接口连接到不同的网络。在这个例子中,xl0 连接到互联网,xl1 连接到内部网络。

首先,启用网关,让机器转发它在一个接口上接收到的网络流量到另一个接口。此 sysctl 设置将转发 IPv4 数据包:

# sysctl net.inet.ip.forwarding=1

要转发 IPv6 流量,使用:

# sysctl net.inet6.ip6.forwarding=1

要在系统启动时启用这些设置,使用 sysrc(8) 将它们添加到 /etc/rc.conf 中:

# sysrc gateway_enable=yes
# sysrc ipv6_gateway_enable=yes

使用 ifconfig 验证两个接口是否已启动并正常运行。

接下来,创建 PF 规则以允许网关转发流量。以下规则允许来自内部网络主机的有状态流量通过网关,但 to 关键字并不保证数据包会从源头到目的地完全通过:

pass in on xl1 from xl1:network to xl0:network port $ports keep state

该规则仅允许流量通过内部接口进入网关。为了让数据包进一步传递,还需要一个匹配的规则:

pass out on xl0 from xl1:network to xl0:network port $ports keep state

虽然这两个规则有效,但这种特定的规则通常不需要。对于一个忙碌的网络管理员来说,一个可读的规则集是更安全的规则集。本节其余部分演示了如何使规则尽可能简单以便于阅读。例如,这两个规则可以被一个规则替代:

pass from xl1:network to any port $ports keep state

interface:network 表示法可以替换为宏,以使规则集更具可读性。例如,可以定义一个 $localnet 宏,表示直接连接到内部接口的网络($xl1:network)。或者,$localnet 的定义可以更改为 IP 地址/子网掩码 表示法,来表示网络,例如 192.168.100.1/24 用于表示一个私有地址的子网。

如果需要,$localnet 甚至可以定义为一个网络列表。不论具体需求是什么,合理的 $localnet 定义可以在典型的通过规则中使用,如下所示:

pass from $localnet to any port $ports keep state

以下示例规则集允许所有由内部网络机器发起的流量。它首先定义了两个宏来表示网关的外部和内部 3COM 接口。

注意

对于拨号用户,外部接口将使用 tun0。对于 ADSL 连接,特别是那些使用 PPPoE(以太网协议封装的 PPP) 的连接,正确的外部接口是 tun0,而不是物理以太网接口。

ext_if = "xl0"	# 外部接口的宏 - 对于 PPPoE 使用 tun0
int_if = "xl1"	# 内部接口的宏
localnet = $int_if:network
# ext_if 的 IP 地址可能是动态的,因此使用 ($ext_if)
nat on $ext_if from $localnet to any -> ($ext_if)
block all
pass from { lo0, $localnet } to any keep state

这个规则集引入了 nat 规则,用于处理从内部网络中不可路由的地址到分配给外部接口的 IP 地址的网络地址转换。nat 规则中括号内的部分 ($ext_if) 包括了外部接口的 IP 地址动态分配的情况。这确保了即使外部 IP 地址发生变化,网络流量也能正常运行。

请注意,这个规则集可能允许比实际所需更多的流量通过。一个合理的设置可能会创建这个宏:

client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http, \
    https, cvspserver, 2628, 5999, 8000, 8080 }"

用于主通过规则:

pass inet proto tcp from $localnet to any port $client_out \
    flags S/SA keep state

可能还需要一些其他的通过规则。这个规则启用外部接口上的 SSH:

pass in inet proto tcp to $ext_if port ssh

这个宏定义和规则允许内部客户端使用 DNS 和 NTP:

udp_services = "{ domain, ntp }"
pass quick inet proto { tcp, udp } to any port $udp_services keep state

注意这个规则中的 quick 关键字。由于规则集中包含多个规则,理解规则之间的关系非常重要。规则是按照它们在规则集中出现的顺序从上到下评估的。对于每个被 PF 评估的数据包或连接,最后匹配的规则 是应用的规则。然而,当数据包与包含 quick 关键字的规则匹配时,规则处理会停止,数据包会按照该规则处理。当需要对一般规则做出例外时,这个特性非常有用。

33.3.2.2. 创建 FTP 代理

配置有效的 FTP 规则可能会由于 FTP 协议的特性而变得复杂。FTP 比防火墙早数十年出现,并且在设计上存在不安全的问题。使用 FTP 的最常见问题包括:

  • 密码以明文形式传输。

  • 协议要求至少使用两个 TCP 连接(控制连接和数据连接),并且它们使用不同的端口。

  • 会话建立后,数据通过随机选择的端口进行传输。

在考虑客户端或服务器软件中可能存在的安全漏洞之前,这些问题就已经构成了安全挑战。更安全的文件传输替代方案包括 sftp(1)scp(1),它们都具有认证功能并通过加密连接进行数据传输。

在需要使用 FTP 的情况下,PF 提供了将 FTP 流量重定向到一个名为 ftp-proxy(8) 的小型代理程序,FreeBSD 系统的基本系统中包含了这个程序。代理的作用是动态插入和删除规则集中的规则,通过一组锚点来正确处理 FTP 流量。

要启用 FTP 代理,请在 /etc/rc.conf 中添加以下行:

ftpproxy_enable="YES"

然后通过运行以下命令启动代理:

# service ftp-proxy start

对于基本配置,需要在 /etc/pf.conf 中添加三个元素。首先是代理用于插入为 FTP 会话生成的规则的锚点:

nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"

其次,需要一条允许 FTP 流量进入代理的通过规则。

第三,需要在过滤规则之前定义重定向和 NAT 规则。将此 rdr 规则紧接在 nat 规则之后插入:

rdr pass on $int_if proto tcp from any to any port ftp -> 127.0.0.1 port 8021

最后,允许重定向的流量通过:

pass out proto tcp from $proxy to any port ftp

其中,$proxy 展开为代理守护进程绑定的地址。

保存 /etc/pf.conf,加载新规则,并从客户端验证 FTP 连接是否正常工作:

# pfctl -f /etc/pf.conf

这个示例涵盖了一个基本的设置,其中本地网络中的客户端需要联系其他地方的 FTP 服务器。这个基本配置应该适用于大多数 FTP 客户端和服务器的组合。如 ftp-proxy(8) 所示,代理的行为可以通过向 ftpproxy_flags= 行添加选项进行多种方式的更改。一些客户端或服务器可能会有特定的差异,这些差异需要在配置中进行补偿,或者可能需要以特定的方式集成代理,例如将 FTP 流量分配到特定的队列中。

要保护 FTP 服务器并通过 PF 和 ftp-proxy(8) 进行配置,可以在单独的端口上以反向模式配置一个独立的 ftp-proxy,并使用 -R 选项,配合其自己的重定向通过规则。

33.3.2.3. 管理 ICMP

许多用于调试或故障排除 TCP/IP 网络的工具依赖于互联网控制消息协议(ICMP),该协议的设计初衷就是用于调试。

ICMP 协议在主机和网关之间发送和接收 控制消息,主要是向发送方提供有关到达目标主机途中出现的异常或困难的反馈。路由器使用 ICMP 来协商数据包大小和其他传输参数,这一过程通常被称为 路径 MTU 发现

从防火墙的角度来看,一些 ICMP 控制消息易受到已知攻击向量的威胁。此外,允许所有诊断流量不加限制地通过,虽然能够简化调试,但也使得其他人更容易提取关于网络的信息。因此,以下规则可能不是最优的:

pass inet proto icmp from any to any

一种解决方案是允许来自本地网络的所有 ICMP 流量通过,同时阻止来自网络外部的探测请求:

pass inet proto icmp from $localnet to any keep state
pass inet proto icmp from any to $ext_if keep state

PF 还提供了其他选项,展示了其灵活性。例如,可以指定用于 ping(8)traceroute(8) 的消息类型,而不是允许所有 ICMP 消息。首先,定义一个宏来表示这种消息类型:

icmp_types = "echoreq"

然后,使用这个宏的规则:

pass inet proto icmp all icmp-type $icmp_types keep state

如果需要其他类型的 ICMP 包,可以将 icmp_types 扩展为一个包含这些包类型的列表。你可以输入 more /usr/src/sbin/pfctl/pfctl_parser.c 来查看 PF 支持的 ICMP 消息类型列表。也可以参考 IANA 的 ICMP 参数文档 了解每种消息类型的解释。

Unix 的 traceroute 默认使用 UDP,因此还需要一个规则来允许 Unix traceroute 的使用:

# 允许 traceroute(8) 默认的端口范围:
pass out on $ext_if inet proto udp from any to any port 33433 >< 33626 keep state

由于 Microsoft Windows 系统上的 TRACERT.EXE 使用 ICMP 回显请求消息,因此仅需要第一条规则即可允许来自这些系统的网络追踪。Unix 的 traceroute 也可以指示使用其他协议,如果使用 -I 参数,它也会使用 ICMP 回显请求消息。更多细节可以查看 traceroute(8) 的手册页。

33.3.2.3.1. 路径 MTU 发现

互联网协议被设计为设备独立的,设备独立性的一种后果是,给定连接的最佳数据包大小并不总是能够可靠预测的。数据包大小的主要限制是 最大传输单元(MTU),它设置了接口的最大数据包大小限制。可以输入 ifconfig 来查看系统网络接口的 MTU 值。

TCP/IP 使用一种称为路径 MTU 发现的过程来确定连接的正确数据包大小。这个过程会发送不同大小的数据包,并设置“禁止分片”标志,期望在达到上限时接收到类型为 "3,代码 4" 的 ICMP 返回包。类型 3 表示“目的地不可达”,代码 4 表示“需要分片,但已设置禁止分片标志”。为了允许路径 MTU 发现以支持连接到其他 MTU,需要将 destination unreachable 类型添加到 icmp_types 宏中:

icmp_types = "{ echoreq, unreach }"

由于 pass 规则已经使用了该宏,因此无需修改规则以支持新的 ICMP 类型:

pass inet proto icmp all icmp-type $icmp_types keep state

PF 允许基于所有变种的 ICMP 类型和代码进行过滤。可能的类型和代码列表在 icmp(4)icmp6(4) 中有文档说明。

33.3.2.4. 使用表

有些类型的数据在某些时候对过滤和重定向很重要,但它们的定义太长,无法包含在规则集文件中。PF 支持使用表,表是可以在不重新加载整个规则集的情况下操作的定义列表,并且能够提供快速查找。表名总是被 < > 括起来,如下所示:

table <clients> { 192.168.2.0/24, !192.168.2.5 }

在这个例子中,192.168.2.0/24 网络是表的一部分,但地址 192.168.2.5 被排除在外,使用了 ! 操作符。也可以从文件加载表,其中每个项目位于单独的行上,如此例 /etc/clients

192.168.2.0/24
!192.168.2.5

要引用该文件,可以像这样定义表:

table <clients> persist file "/etc/clients"

待定义了表,就可以在规则中引用它:

pass inet proto tcp from <clients> to any port $client_out flags S/SA keep state

可以使用 pfctl 动态操作表的内容。以下示例将另一个网络添加到表中:

# pfctl -t clients -T add 192.168.1.0/16

请注意,通过这种方式所做的任何更改都会立即生效,非常适合测试,但在断电或重启后不会保留。要使更改永久生效,请修改规则集中的表定义或编辑表所引用的文件。可以使用 cron(8) 作业定期将表的内容保存到磁盘,命令示例如 pfctl -t clients -T show >/etc/clients。或者,/etc/clients 可以通过以下命令更新为内存中的表内容:

# pfctl -t clients -T replace -f /etc/clients

33.3.2.5. 使用超载表保护 SSH

运行 SSH 的外部接口的用户可能在身份验证日志中看到如下内容:

Sep 26 03:12:34 skapet sshd[25771]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:34 skapet sshd[5279]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:35 skapet sshd[5279]: Received disconnect from 200.72.41.31: 11: Bye Bye
Sep 26 03:12:44 skapet sshd[29635]: Invalid user admin from 200.72.41.31
Sep 26 03:12:44 skapet sshd[24703]: input_userauth_request: invalid user admin
Sep 26 03:12:44 skapet sshd[24703]: Failed password for invalid user admin from 200.72.41.31 port 41484 ssh2

这表明发生了暴力破解攻击,某些人或程序正在尝试发现正确的用户名和密码以进入系统。

如果需要允许合法用户通过外部 SSH 访问,更改 SSH 使用的默认端口可以提供一定的保护。然而,PF 提供了一种更优雅的解决方案。通过限制连接主机的操作,违反者可以被驱逐到一个地址表中,这些地址被拒绝某些或所有访问权限。甚至可以丢弃超过限制的机器的所有现有连接。

要配置此功能,在规则集的表部分创建该表:

table <bruteforce> persist

然后,在规则集的较早位置添加规则,以阻止暴力破解访问并允许合法访问:

block quick from <bruteforce>
pass inet proto tcp from any to $localnet port $tcp_services \
    flags S/SA keep state \
    (max-src-conn 100, max-src-conn-rate 15/5, \
    overload <bruteforce> flush global)

括号中的部分定义了限制,数字应根据本地要求进行更改。可以按以下方式解读:

max-src-conn 是允许从单个主机发起的同时连接的数量。

max-src-conn-rate 是允许从任何单个主机发起的新连接速率(15)每多少秒(5)。

overload <bruteforce> 表示任何超出这些限制的主机,其地址将被添加到 bruteforce 表中。规则集会阻止来自 bruteforce 表中地址的所有流量。

最后,flush global 表示当主机达到限制时,所有(global)该主机的连接将被终止(flush)。

注意

这些规则 不会 阻止慢速暴力破解者,如在 http://home.nuug.no/~peter/hailmary2013/ 中所述。

这个示例规则集主要作为一个说明。例如,如果希望允许大量连接,但希望在 SSH 方面更严格,可以在规则集的较早位置使用类似以下的规则进行补充:

pass quick proto { tcp, udp } from any to any port ssh \
    flags S/SA keep state \
    (max-src-conn 15, max-src-conn-rate 5/3, \
    overload <bruteforce> flush global)

注意

可能不需要阻止所有超载者:

值得注意的是,超载机制是一种通用技术,适用于 SSH,但并不限于此,并且并非总是最优选择去完全阻止所有违反者。

例如,超载规则可以用于保护邮件服务或 Web 服务,并且可以在规则中使用超载表将违反者分配到一个队列中,给与最低带宽分配或重定向到特定的网页。

随着时间的推移,表将被超载规则填充,大小将逐渐增大,占用更多内存。有时被阻止的 IP 地址是一个动态分配的地址,这个地址已经被分配给了一个有合法理由与本地网络中的主机进行通信的主机。

对于这种情况,pfctl 提供了过期表项的功能。例如,以下命令将删除 <bruteforce> 表中在 86400 秒内未被引用的表项:

# pfctl -t bruteforce -T expire 86400

类似的功能可以通过 security/expiretable 提供,该工具删除在指定时间段内未被访问的表项。

安装后,可以运行 expiretable 删除 <bruteforce> 表中超出指定时间的条目。此示例删除所有超过 24 小时的条目:

/usr/local/sbin/expiretable -v -d -t 24h bruteforce

33.3.2.6. 防止 SPAM

不要与内置的 spamassassin 配套使用的 spamd 守护进程混淆,mail/spamd 可以与 PF 配合使用,提供防御 SPAM 的外部防线。这个 spamd 使用一组重定向与 PF 配置集成。

垃圾邮件发送者通常发送大量消息,垃圾邮件主要来自一些垃圾邮件友好的网络和大量被劫持的机器,这些机器通常会很快被报告到 黑名单 中。

当从黑名单中的地址接收到一个 SMTP 连接时,spamd 会展示其横幅并立即切换到一种模式,逐字节地回应 SMTP 流量。这种技术旨在尽可能浪费垃圾邮件发送者的时间,称为 tarpitting。这种使用逐字节 SMTP 回复的具体实现通常称为 stuttering

这个示例演示了如何配置 spamd 与自动更新的黑名单。有关更多信息,请参考安装的 mail/spamd 手册页。

过程:配置 spamd

  1. 安装 mail/spamd 软件包和 Ports 。要使用 spamd 的灰名单功能,必须将 fdescfs(5) 挂载到 /dev/fd。将以下行添加到 /etc/fstab

    fdescfs /dev/fd fdescfs rw 0 0

    然后,挂载文件系统:

    # mount fdescfs
  2. 接下来,编辑 PF 规则集以包含:

    table <spamd> persist
    table <spamd-white> persist
    rdr pass on $ext_if inet proto tcp from <spamd> to \
        { $ext_if, $localnet } port smtp -> 127.0.0.1 port 8025
    rdr pass on $ext_if inet proto tcp from !<spamd-white> to \
        { $ext_if, $localnet } port smtp -> 127.0.0.1 port 8025

    <spamd><spamd-white> 两个表是必需的。来自 <spamd> 中列出的地址但不在 <spamd-white> 中的 SMTP 流量会被重定向到监听 8025 端口的 spamd 守护进程。

  3. 接下来,在 /usr/local/etc/spamd.conf 中配置 spamd,并添加一些 rc.conf 参数。 安装 mail/spamd 后,包含一个示例配置文件(/usr/local/etc/spamd.conf.sample)和 spamd.conf 的手册页。请参考这些文件,以获取更多配置选项。

    配置文件中,第一个不以 # 注释符号开头的行包含了定义 all 列表的块,该列表指定了要使用的黑名单:

    all:\
        :traplist:allowlist:

    该条目添加了所需的黑名单,多个黑名单之间用冒号(:)分隔。如果要使用白名单从黑名单中扣除地址,应将白名单的名称 紧接 在黑名单名称之后。例如::blocklist:allowlist:

    这后面是所指定的黑名单定义:

    traplist:\
        :black:\
        :msg="SPAM. Your address %A has sent spam within the last 24 hours":\
        :method=http:\
        :file=www.openbsd.org/spamd/traplist.gz

    第一行是黑名单的名称,第二行指定了列表类型。msg 字段包含在 SMTP 对话中要显示给黑名单发送者的消息。method 字段指定了 spamd-setup 获取列表数据的方法;支持的方式有 httpftp、从挂载的文件系统中的 file,以及通过外部程序的 exec。最后,file 字段指定了 spamd 期望接收的文件名。

    指定的白名单定义类似,但省略了 msg 字段,因为不需要消息:

    allowlist:\
        :white:\
        :method=file:\
        :file=/var/mail/allowlist.txt

技巧

谨慎选择数据源:

使用示例 spamd.conf 中的所有黑名单将会阻塞大量的互联网地址。管理员需要编辑该文件,创建一个最佳配置,使用适当的数据源,并在必要时使用自定义列表。

接下来,将以下条目添加到 /etc/rc.conf。有关其他标志的描述,请参见手册页:

spamd_flags="-v" # 使用 "" 并查看 spamd-setup(8) 获取标志

完成后,重新加载规则集,输入 service obspamd start 启动 spamd,并使用 spamd-setup 完成配置。最后,创建一个 cron(8) 任务,定期调用 spamd-setup 更新表。

在典型的位于邮件服务器前的网关上,主机通常会在几秒钟到几分钟内开始被拦截。

PF 还支持 灰名单,即临时拒绝来自未知主机的消息,使用 45n 错误码。来自灰名单主机的消息,如果在合理时间内再次尝试,会被允许通过。遵循 RFC 1123 和 RFC 2821 限制的发送者的流量会立即通过。

有关灰名单技术的更多信息,请参见 greylisting.org 网站。灰名单最令人惊讶的地方,除了它的简单性外,就是它仍然有效。垃圾邮件发送者和恶意软件编写者适应绕过这种技术的速度非常慢。

过程:配置灰名单

  1. 确保按照前述步骤 1 挂载了 fdescfs(5)

  2. 要以灰名单模式运行 spamd,请在 /etc/rc.conf 中添加以下行:

    spamd_grey="YES"  # 如果是 YES,则使用 spamd 灰名单

    请参考 spamd 手册页,以了解其他相关参数的描述。

  3. 完成灰名单设置:

    # service obspamd restart
    # service obspamlogd start

在后台,spamdb 数据库工具和 spamlogd 白名单更新程序为灰名单功能执行必要的操作。spamdb 是管理员通过 /var/db/spamdb 数据库内容管理黑名单、灰名单和白名单的主要界面。

33.3.2.7. 网络卫生

本节描述了如何使用 block-policyscrubantispoof 来使规则集行为更加合理。

block-policy 是可以在规则集的 options 部分设置的选项,该部分位于重定向和过滤规则之前。此选项决定了当规则阻止主机时,PF 是否会发送反馈。该选项有两个可能的值:drop 会丢弃被阻止的数据包,不发送反馈;return 会返回如 Connection refused 之类的状态码。

如果未设置,则默认策略为 drop。要更改 block-policy,指定所需的值:

set block-policy return

在 PF 中,scrub 是一个启用网络数据包规范化的关键字。该过程会重新组装分段的数据包,并丢弃具有无效标志组合的 TCP 数据包。启用 scrub 提供了一定的保护,防止某些基于不正确处理数据包碎片的攻击。虽然有多个选项可供选择,但最简单的形式适用于大多数配置:

scrub in all

一些服务,如 NFS,需要特定的碎片处理选项。有关更多信息,请参考 https://home.nuug.no/~peter/pf/en/scrub.html

这个例子会重新组装碎片,清除 "do not fragment" 标志,并将最大分段大小设置为 1440 字节:

scrub in all fragment reassemble no-df max-mss 1440

antispoof 机制保护免受伪造或伪装的 IP 地址的攻击,主要通过阻止那些出现在接口和方向上逻辑上不可能的数据包。

这些规则会过滤来自外部世界的伪造流量,以及源自本地网络的任何伪造数据包:

antispoof for $ext_if
antispoof for $int_if

33.3.2.8. 处理不可路由地址

即使正确配置了网关来处理网络地址转换,有时也需要补偿他人的错误配置。一种常见的错误配置是允许来自不可路由地址的流量访问互联网。由于来自不可路由地址的流量可能会参与多种拒绝服务(DoS)攻击技术,因此建议明确阻止来自不可路由地址的流量进入网络的外部接口。

在这个例子中,首先定义了一个包含不可路由地址的宏,然后在阻止规则中使用它。流量往返这些地址会在网关的外部接口上静默丢弃。

martians = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, \
	      10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, \
	      0.0.0.0/8, 240.0.0.0/4 }"

block drop in quick on $ext_if from $martians to any
block drop out quick on $ext_if from any to $martians

33.3.3. 启用 ALTQ

在 FreeBSD 上,ALTQ 可以与 PF 一起使用,以提供服务质量(QOS)。待启用 ALTQ,就可以在规则集中定义队列,这些队列决定了出站数据包的处理优先级。

在启用 ALTQ 之前,请参阅 altq(4) 以确定系统上安装的网络卡驱动是否支持它。

ALTQ 不是一个可加载的内核模块。如果系统的接口支持 ALTQ,请按照 配置 FreeBSD 内核 中的说明创建一个自定义内核。以下是可用的内核选项。第一个选项是启用 ALTQ 所必需的。至少需要其他一个选项来指定排队调度算法:

options         ALTQ
options         ALTQ_CBQ        # 基于类别的排队(CBQ)
options         ALTQ_RED        # 随机早期检测(RED)
options         ALTQ_RIO        # RED 输入/输出(RIO)
options         ALTQ_HFSC       # 层次化分组调度器(HFSC)
options         ALTQ_PRIQ       # 优先级排队(PRIQ)

以下是可用的调度算法:

CBQ 基于类别的排队(CBQ)用于将连接的带宽划分为不同的类别或队列,根据过滤规则优先处理流量。

RED 随机早期检测(RED)用于通过测量队列长度并将其与队列的最小值和最大值进行比较,避免网络拥塞。当队列长度超过最大值时,所有新数据包会被随机丢弃。

RIO 在随机早期检测输入/输出(RIO)模式下,RED 维护多个平均队列长度和多个阈值,每个 QOS 级别有一个阈值。

HFSC 层次化公平服务曲线分组调度器(HFSC),详见 http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html

PRIQ 优先级排队(PRIQ)始终优先通过较高队列中的流量。

有关调度算法和示例规则集的更多信息,可以参考 OpenBSD 的网页档案

最后更新于