33.3. PF ======== 从 FreeBSD 5.3 开始,OpenBSD 的 PF 防火墙的移植版本就作为基本系统的集成部分被包含进来了。PF 是一个功能齐全的完整防火墙,具有对 ALTQ(交错队列)的可选支持,后者提供服务质量(QoS)。 OpenBSD 项目在 PF FAQ 中维护了 `PF `__ 的权威参考。Peter Hansteen 在 http://home.nuug.no/~peter/pf/ 维护着一个全面的 PF 教程。 **警告** 在阅读 `PF FAQ `__ 时,请记住,多年以来,FreeBSD 的 PF 版本与上游的 OpenBSD 版本有很大的不同。并非所有功能在 FreeBSD 上的工作方式都与在 OpenBSD 中的工作方式相同,反之亦然。 `FreeBSD 数据包过滤器邮件列表 `__ 是询问有关配置和运行 PF 防火墙的问题的好地方。在提出问题之前,请检查邮件列表存档,因为它可能已经得到解答。 手册的这一部分重点介绍与 FreeBSD 相关的 PF。它演示了如何启用 PF 和 ALTQ。它还提供了几个在 FreeBSD 系统上创建规则集的示例。 33.3.1.启用 PF -------------- 要使用 PF,必须首先加载其内核模块。本节介绍可以添加到 **/etc/rc.conf** 以启用 PF 的条目。 首先添加 ``pf_enable=yes`` 到 **/etc/rc.conf**\ : .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # sysrc pf_enable=yes `pfctl(8) `__ 中提及的其他选项可以在启动时传递给 PF。在 **/etc/rc.conf** 中添加或更改此条目,并在两个引号(\ ``""``\ )之间指定任何必需的参数: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pf_flags="" # additional flags for pfctl startup 如果 PF 找不到其规则集配置文件,则 PF 将不会启动。默认情况下,FreeBSD 不附带规则集,并且没有 **/etc/pf.conf**\ 。可以在 **/usr/share/examples/pf/** 中找到示例规则集。如果自定义规则集已保存在其他位置,请在 **/etc/rc.conf** 中添加一行,指定文件的完整路径: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pf_rules="/path/to/pf.conf" 由 `pflog(4) `__ 提供 PF 的日志记录支持。要启用日志记录支持,请添加 ``pflog_enable=yes`` 到 **/etc/rc.conf**\ : .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # sysrc pflog_enable=yes 还可以添加以下行来更改日志文件的默认位置,或指定在启动时传递给 `pflog(4) `__ 的任何其他参数: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pflog_logfile="/var/log/pflog" # where pflogd should store the logfile pflog_flags="" # additional flags for pflogd startup 最后,如果防火墙后面有 LAN,并且需要为 LAN 上的计算机转发数据包,或者需要 NAT,请启用以下选项: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session gateway_enable="YES" # Enable as LAN gateway 保存所需的编辑后,可以通过键入以下内容来启动 PF 并具有日志记录支持: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # service pf start # service pflog start 默认情况下,PF 从 **/etc/pf.conf** 读取其配置规则,并根据此文件中指定的规则或定义修改、丢弃或传递数据包。FreeBSD 安装包括几个位于 **/usr/share/examples/pf/** 中的样本文件。有关 PF 规则集的完整覆盖范围,请参阅 PF `常见问题解答 `__\ 。 要控制 PF,请使用 ``pfctl``\ 。有用的 `pfctl `__ 选项总结了此命令的一些有用选项。有关所有可用选项的说明,请参阅 `pfctl(8) `__ : **表 28. 有用的 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**\ ,使其如下所示: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session block in all pass out all keep state 默认情况下,第一个规则拒绝所有传入流量。第二条规则允许此系统创建的连接传出,同时保留这些连接的状态信息。此状态信息允许这些连接的返回流量回传,并且应仅在可信任的计算机上使用。可以使用以下命令加载该规则集: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pfctl -e ; pfctl -f /etc/pf.conf 除了保持状态之外,PF 还提供列表和宏,可以定义这些 *列表* 和 *宏* 以用于创建规则。宏可以包含列表,需要在使用前进行定义。例如,在规则集的最顶部插入以下行: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session tcp_services = "{ ssh, smtp, domain, www, pop3, auth, pop3s }" udp_services = "{ domain }" PF 可理解端口名称和端口号,只要这些名称列在 **/etc/services** 中即可。此示例创建了两个宏。第一个是包含七个 TCP 端口名的列表,第二个是一个 UDP 端口名。定义后,可以在规则中使用宏。在此示例中,除了此系统为七个指定的 TCP 服务和一个指定的 UDP 服务启动的连接之外,所有流量都被阻止: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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 将监视响应以将其传递回去。 每当对规则集进行编辑后,都必须加载新规则,以便可以使用它们: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pfctl -f /etc/pf.conf 如果没有语法错误,\ ``pfctl`` 则不会在规则加载期间输出任何消息。在尝试加载规则之前,还可以对其进行测试: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pfctl -nf /etc/pf.conf 包含 ``-n`` 会导致规则仅被解释,但不会加载。这提供了更正任何错误的机会。在任何时候,都将强制执行加载的最后一个有效规则集,直到禁用 PF 或加载新规则集。 **技巧** 在 pfctl 规则集验证或加载中添加 -v,将显示完全解析过的规则,与它们将被加载的方式完全一致。这在调试规则时是非常有用的。 33.3.2.1.具有 NAT 的简单网关 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 本节演示如何配置运行 PF 的 FreeBSD 系统作为至少一台其他机器的网关。网关至少需要两个网络接口,每个接口连接到单独的网络。在此示例中,\ **xl0** 连接到互联网,\ **xl1** 连接到内部网络。 首先,启用网关,让计算机将其在一个接口上收到的网络流量转发到另一个接口。此系统堆栈设置将转发 IPv4 数据包: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # sysctl net.inet.ip.forwarding=1 要转发 IPv6 流量,请使用: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # sysctl net.inet6.ip6.forwarding=1 要在系统启动时启用这些设置,请使用 `sysrc(8) `__ 将它们添加到 **/etc/rc.conf**\ : .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # sysrc gateway_enable=yes # sysrc ipv6_gateway_enable=yes 命令 ``ifconfig`` 可以验证两个接口是否已启动并正在运行。 接下来,创建 PF 规则以允许网关传递流量。虽然以下规则允许来自内部网络主机的有状态流量传递到网关,但 ``to`` 关键字不保证从源到目标的一直传递: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass in on xl1 from xl1:network to xl0:network port $ports keep state 该规则仅允许流量传入内部接口上的网关。要让数据包走得更远,需要一个匹配的规则: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass out on xl0 from xl1:network to xl0:network port $ports keep state 虽然这两个规则将起作用,但很少需要这种特定的规则。对于繁忙的网络管理员,可读的规则集是更安全的规则集。本节的其余部分演示如何使规则尽可能简单,以提高可读性。例如,这两个规则可以替换为一个规则: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass from xl1:network to any port $ports keep state ``interface:network`` 符号可以用一个宏来代替,使规则集更易读。例如,\ ``$localnet`` 宏可以被定义为直接连接到内部接口的网络(\ ``$xl1:network``\ )。另外,\ ``$localnet`` 的定义可以改为 *IP 地址/网络掩码* 符号来表示一个网络,比如 ``192.168.100.1/24`` 表示一个私有地址的子网。 如果需要,\ ``$localnet`` 甚至可以被定义为一个网络的列表。不管具体的需求是什么,一个合理的 ``$localnet`` 定义可以在一个典型的通行规则中使用,如下: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass from $localnet to any port $ports keep state 以下示例规则集允许由内部网络上的计算机发起的所有流量。它首先定义两个宏来表示网关的外部和内部 3COM 接口。 **注意** 对于拨号用户,外部接口将使用 **tun0**\ 。对于 ADSL 连接,特别是那些使用以太网 PPP(PPPoE)的连接,正确的外部接口是 **tun0**\ ,而不是以太网物理接口。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session ext_if = "xl0" # macro for external interface - use tun0 for PPPoE int_if = "xl1" # macro for internal interface localnet = $int_if:network # ext_if IP address could be dynamic, hence ($ext_if) nat on $ext_if from $localnet to any -> ($ext_if) block all pass from { lo0, $localnet } to any keep state 这个规则集引入了 ``nat`` 规则,用来处理从内部网络的不可路由地址到分配给外部接口的 IP 地址的网络地址转换。当外部接口的 IP 地址被动态分配时,\ ``nat`` 规则的最后一部分(\ ``$ext_if``\ )周围的括号被包括在内。它可以确保即使外部 IP 地址发生变化,网络流量的运行也不会出现严重中断。 请注意,此规则集可能允许从网络中传出的流量超过所需的流量。一个合理的设置可以创建此宏: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http, \ https, cvspserver, 2628, 5999, 8000, 8080 }" 在主传递规则中使用: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass inet proto tcp from $localnet to any port $client_out \ flags S/SA keep state 可能需要一些其他的通过规则。这个在外部接口上启用 SSH: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass in inet proto tcp to $ext_if port ssh 此宏定义和规则允许内部客户端使用 DNS 和 NTP: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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**\ : .. raw:: latex \diilbookstyleinputcell .. code:: shell-session ftpproxy_enable="YES" 然后启动代理,通过运行: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # service ftp-proxy start 对于基本配置,需要将三个元素添加到 **/etc/pf.conf** 中。首先,代理将用于插入它为 FTP 会话生成的规则的锚点: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session nat-anchor "ftp-proxy/*" rdr-anchor "ftp-proxy/*" 其次,需要传递规则来允许 FTP 流量进入代理。 第三,重定向和 NAT 规则需要在过滤规则之前定义。在 NAT 规则之后立即插入这个 ``rdr`` 规则: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session rdr pass on $int_if proto tcp from any to any port ftp -> 127.0.0.1 port 8021 最后,允许重定向的流量通过: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass out proto tcp from $proxy to any port ftp 其中,\ ``$proxy`` 扩展到代理守护程序绑定到的地址。 保存 **/etc/pf.conf**\ ,加载新规则,然后在客户端验证 FTP 连接是否正常工作: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pfctl -f /etc/pf.conf 此示例介绍了一个基本设置,其中本地网络中的客户端需要联系其他位置的 FTP 服务器。此基本配置应该适用于 FTP 客户端和服务器的大多数组合。如 `ftp-proxy(8) `__ 所示,通过向行中添加选项 ``ftpproxy_flags=``\ ,可以通过多种方式更改代理的行为。部分客户端或服务器可能有特定设置,必须单独为其增加特定配置。或者需要通过特定方式来集成代理,如将 FTP 流量分配到特定队列 。 要运行受 PF 和 `ftp-proxy(8) `__ 保护的 FTP 服务器,请在具有自己的重定向传递规则的单独端口上使用 ``-R`` 以反向模式配置单独的 FTP 服务器。 33.3.2.3.管理 ICMP ~~~~~~~~~~~~~~~~~~ 用于调试或排除 TCP/IP 网络故障的许多工具都依赖于互联网控制消息协议(ICMP),该协议是专门为调试而设计的。 ICMP 协议在主机和网关之间发送和接收 *控制消息*\ ,主要是为了向发送方提供有关前往目标主机途中的任何异常或困难情况的反馈。路由器使用 ICMP 协议来协商包的大小和其他传输参数,这个过程通常被称为路径 MTU 发现。 从防火墙的角度来看,某些 ICMP 控制消息容易受到已知攻击媒介的攻击。此外,让所有诊断流量无条件通过使调试更容易,但也使其他人更容易提取有关网络的信息。由于这些原因,以下规则可能不是最佳规则: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass inet proto icmp from any to any 一种解决方案是让来自本地网络的所有 ICMP 流量通过,同时阻止来自网络外部的所有探测: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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 消息。首先为该类型的消息定义一个宏: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session icmp_types = "echoreq" 以及使用宏的规则: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass inet proto icmp all icmp-type $icmp_types keep state 如果需要其他类型的 ICMP 数据包,可以将 ``icmp_type`` 扩展为这些数据包类型的列表。输入 ``more /usr/src/sbin/pfctl/pfctl_parser.c`` 可查看 PF 支持的 ICMP 消息类型列表。关于每种消息类型的解释,请参考 http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml\ 。 由于 Unix 的 ``traceroute`` 默认使用 UDP,所以需要另一条规则来允许 Unix 的 ``traceroute``\ 。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # allow out the default range for traceroute(8): pass out on $ext_if inet proto udp from any to any port 33433 >< 33626 keep state 由于 Microsoft Windows 系统上的 ``TRACERT.EXE`` 使用 ICMP echo 请求信息,所以只需要第一条规则就可以允许来自这些系统的网络追踪。Unix 的 ``traceroute`` 也可以被指示使用其他协议,如果使用 ``-I``\ ,将使用 ICMP echo 请求消息。详情请查看 `traceroute(8) `__ 手册页以了解详细信息。 33.3.2.3.1.路径 MTU 发现 ^^^^^^^^^^^^^^^^^^^^^^^^ 互联网协议被设计为与设备无关,设备独立的一个后果是,无法始终可靠地预测给定连接的最佳数据包大小。数据包大小的主要约束是 *最大传输单位*\ (MTU),它设置了接口数据包大小的上限。键入 ``ifconfig`` 以查看系统网络接口的 MTU。 TCP/IP 使用称为路径 MTU 发现的过程来确定连接的正确数据包大小。此过程发送设置了“Do not fragment”标志集的不同大小的数据包,当达到上限时,预计 ICMP 将返回数据包“type 3, code 4”。“type 3”表示“目标无法访问”,“type 4”是“需要分片,但设置了不分段标志”的缩写。要允许路径 MTU 发现以支持与其他 MTU 的连接,请将类型 ``destination unreachable`` 添加到 ``icmp_types`` 宏中: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session icmp_types = "{ echoreq, unreach }" 由于传递规则已使用该宏,因此无需对其进行修改即可支持新的 ICMP 类型: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass inet proto icmp all icmp-type $icmp_types keep state PF 允许对 ICMP 类型和代码的所有变体进行过滤。可能的类型和代码列表记录在 `icmp(4) `__ 和 `icmp6(4) `__ 中。 33.3.2.4.使用表 ~~~~~~~~~~~~~~~ 某些类型的数据与给定时间的筛选和重定向相关,但它们的定义太长,无法包含在规则集文件中。PF 支持使用表,表是定义的列表,无需重新加载整个规则集即可对其进行操作,并且可以提供快速查找。表名始终括在 ``< >`` 中,如下所示: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session table { 192.168.2.0/24, !192.168.2.5 } 在此示例中,网络是 ``192.168.2.0/24`` 表的一部分,但 ``192.168.2.5`` 地址除外,该地址使用 ``!`` 运算符将其排除。还可以从每个项目位于单独行上的文件中加载表,如以下示例 **/etc/clients** 所示: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 192.168.2.0/24 !192.168.2.5 要引用该文件,请按如下方式定义表: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session table persist file "/etc/clients" 定义表后,可以通过规则引用它: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session pass inet proto tcp from to any port $client_out flags S/SA keep state 可以使用 ``pfctl`` 实时操作表的内容。以下示例向表中添加另一个网络: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pfctl -t clients -T add 192.168.1.0/16 注意,以这种方式做出的任何改变都会立即生效,使其成为测试的理想选择,但在断电或重启后将无法继续。要使这些变化成为永久性的,请修改规则集中的表的定义,或者编辑表所指向的文件。我们可以使用 `cron(8) `__ 工作来维护磁盘上的表的副本,该工作会定期将表的内容转储到磁盘上,使用的命令有:\ ``pfctl -t clients -T show >/etc/clients``\ 。另外,\ **/etc/clients** 也可以用内存中的表内容进行更新: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pfctl -t clients -T replace -f /etc/clients 33.3.2.5.使用重载表来保护 SSH ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 在外部接口上运行 SSH 的用户可能在身份验证日志中看到过类似这样的内容: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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 提供了一个更优雅的解决方案。Pass 规则可以包含对连接主机可以执行的操作的限制,并且可以将违规者放逐到被拒绝部分或全部访问的地址表中。甚至可以从超出限制的计算机中删除所有现有连接。 要对此进行配置,请在规则集的表部分中创建此表: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session table persist 然后,在规则集的早期某个位置,添加规则以阻止暴力访问,同时允许合法访问: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session block quick from 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 flush global) 括号中的部分定义了限制,应更改数字以满足本地要求。可以这样理解: ``max-src-conn`` 是允许从一台主机同时连接的次数。 ``max-src-conn-rate`` 是每秒数(\ *5*\ )允许来自任何单个主机的新连接速率(\ *15*\ )。 ``overload `` 意味着任何超过这些限制的主机都会被添加到 ``bruteforce`` 表中。该规则集阻止来自 ``bruteforce`` 表中的地址的所有流量。 最后,\ ``flush global`` 表示,当一个主机达到限制时,该主机的所有(\ ``global``\ )连接将被终止(\ ``flush``\ )。 **注意** 这些规则 *不会* 阻止如 `http://home.nuug.no/\\~peter/hailmary2013/ `__ 中所述的速度较慢的暴力破解程序。 此示例规则集主要用作说明。例如,如果通常需要大量的连接,但在涉及 ssh 时希望更加严格,请在规则集的早期用如下内容补充上述规则: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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 flush global) .. **注意** **可能没有必要阻止所有重载器:** 值得注意的是,重载机制是一种通用技术,不仅适用于 SSH,并且完全阻止违规者的所有流量并不总是最佳的。 例如,重载规则可用于保护邮件服务或 Web 服务,重载表可用于在规则中将违规者分配到具有最小带宽分配的队列或重定向到特定网页。 随着时间的推移,表将被重载规则填充,其大小将逐渐增长,占用更多内存。有时,被阻止的 IP 地址是动态分配的 IP 地址,此后已分配给具有正当理由与本地网络中的主机通信的主机。 对于这样的情况,pfctl 提供了使表项过期的能力。例如,该命令将删除 ``86400`` 秒内未被引用的 ```` 表项。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pfctl -t bruteforce -T expire 86400 `security/expiretable `__ 也提供了类似的功能,它删除了在指定时间段内未被访问的表条目。 安装后,可以运行 expiretable 来删除 ```` 表中超过指定期限的表条目。本示例删除所有早于 24 小时的条目: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session /usr/local/sbin/expiretable -v -d -t 24h bruteforce 33.3.2.6.防止垃圾邮件 ~~~~~~~~~~~~~~~~~~~~~ 不要与与 spamassassin 捆绑在一起的垃圾邮件守护程序混淆,\ `mail/spamd `__ 可以使用 PF 进行配置,以提供针对垃圾邮件的外部防御。此垃圾邮件使用一组重定向挂接到 PF 配置中。 垃圾邮件发送者倾向于发送大量邮件,垃圾邮件主要来自少数垃圾邮件发送者友好的网络和大量被劫持的计算机,这两者都会相当快地报告到 *阻止列表* 中。 当收到来自阻止列表中某个地址的 SMTP 连接时,spamd 会显示其标题,并立即切换到一种模式,在该模式下,它一次一个字节地应答 SMTP 通信。这种技术旨在将尽可能多的时间浪费在垃圾邮件发送者这一端,称为 *缓送*\ 。使用一个字节 SMTP 答复的特定实现通常称为 *断断续续*\ 。 此示例演示了使用自动更新的阻止列表设置垃圾邮件的基本过程。有关详细信息,请参阅随 `mail/spamd `__ 一起安装的手册页。 配置反垃圾邮件的过程: 1. 通过软件包或 port 安装 `mail/spamd `__\ 。要使用 spamd 的灰名单功能,\ `fdescfs(5) `__ 必须挂载在 **/dev/fd**\ 。将以下行添加到 **/etc/fstab**\ : .. code:: shell-session fdescfs /dev/fd fdescfs rw 0 0 然后,挂载文件系统: .. code:: shell-session # mount fdescfs 2. 接下来,编辑 PF 规则集以包括: .. code:: shell-session table persist table persist rdr pass on $ext_if inet proto tcp from to \ { $ext_if, $localnet } port smtp -> 127.0.0.1 port 8025 rdr pass on $ext_if inet proto tcp from ! to \ { $ext_if, $localnet } port smtp -> 127.0.0.1 port 8025 两个表 ```` 和 ```` 是必不可少的。来自 ```` 中所列但不在 ```` 中的地址的 SMTP 流量被重定向到在 8025 端口监听的 spamd 守护进程。 3. 下一步是在 **/usr/local/etc/spamd.conf** 中配置 spamd,并添加一些 **rc.conf** 参数。 `mail/spamd `__ 的安装包括一个示例配置文件(\ **/usr/local/etc/spamd.conf.sample**\ )和一个 **spamd.conf** 的手册页。有关此示例中所示选项之外的其他配置选项,请参阅这些选项。 配置文件中不以 ``#`` 注释符号开头的第一行包含了定义 ``all`` 列表的块,它指定了要使用的列表: .. code:: shell-session all:\ :traplist:allowlist: 此条目添加所需的区块列表,以冒号(:)分隔。要使用白名单从黑名单中减去地址,请在该黑名单的名称之后 *紧跟* 添加白名单的名称。例如:\ ``:blocklist:allowlist:`` 后跟指定的黑名单的定义: .. code:: shell-session 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`` 如何获取列表数据;支持的方法有 ``http``\ 、\ ``ftp``\ 、挂载文件系统中的 ``file`` 和通过外部程序的 ``exec``\ 。最后,\ ``file`` 字段指定了 spamd 期望接收的文件的名称。 指定白名单的定义类似,但省略了该字段,因为不需要消息 ``msg``\ : .. code:: shell-session allowlist:\ :white:\ :method=file:\ :file=/var/mail/allowlist.txt .. **技巧** **谨慎选择数据源:** 使用示例 **spamd.conf** 中的所有阻止列表将阻止互联网的大块区域。管理员需要编辑该文件以创建使用适用数据源并在必要时使用自定义列表的最佳配置。 接下来,将此条目添加到 **/etc/rc.conf**\ 。注释指定的手册页中讲解了其他标志: .. code:: shell-session spamd_flags="-v" # use "" and see spamd-setup(8) for flags 完成后,重新加载规则集,通过输入 ``service obspamd start`` 启动 ``spamd``\ ,并使用 ``spamd-setup`` 完成配置。最后,创建一个 `cron(8) `__ 工作,在合理的时间间隔内调用 ``spamd-setup`` 来更新这些表。 在邮件服务器前面的典型网关上,主机很快就会在几秒钟到几分钟内开始陷入困境。 PF 还支持 *灰名单*\ ,这会暂时拒绝来自具有代码 *45n* 的未知主机的邮件。来自灰名单主机的消息在合理时间内再次尝试,将被允许通过。来自设置为在 RFC 1123 和 RFC 2821 设置的限制范围内运行的发件人的流量将立即通过。 有关灰名单技术的更多信息,请访问 `greylisting.org `__ 网站。除了简单之外,灰名单最令人惊奇的事情是它仍然有效。垃圾邮件发送者和恶意软件编写者在适应绕过这种技术方面非常缓慢。 配置灰名单的基本过程如下: 配置灰名单的过程: 1. 确保 `fdescfs(5) `__ 已按照上一过程的步骤 1 中所述进行挂载。 2. 要在灰名单模式下运行垃圾邮件,请将此行添加到 **/etc/rc.conf**\ : .. code:: shell-session spamd_grey="YES" # use spamd greylisting if YES 有关其他相关参数的说明,请参阅垃圾邮件手册页。 3. 要完成灰名单设置: .. code:: shell-session # service obspamd restart # service obspamlogd start 在幕后,spamdb 数据库工具和 spamlogd 白名单更新程序执行灰名单功能的基本功能。spamdb 是管理员通过 **/var/db/spamdb** 数据库的内容管理块、灰名单和允许列表的主要界面。 33.3.2.7.网络维护 ~~~~~~~~~~~~~~~~~ 本节介绍如何使用 ``block-policy`` 、\ ``scrub`` 和 ``antispoof`` 来使规则集的行为正常。 ``block-policy`` 选项,可以在 ``options`` 规则集的一部分中设置,该部分位于重定向和过滤规则之前。此选项确定 PF 向被规则阻止的主机发送哪些反馈(如果有)。该选项有两个可能的值:\ ``drop`` 没有反馈的被阻止数据包,并 ``return`` 返回状态代码,如 ``Connection refused``\ 。 如果未设置 ``drop``\ ,则缺省策略为 ``block-policy``\ ,要更改,请指定所需的值: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session set block-policy return 在 PF 中,\ ``scrub`` 是启用网络数据包规范化的关键字。此过程将重新组合碎片数据包并丢弃具有无效标志组合的 TCP 数据包。启用 ``scrub`` 功能提供了一种针对基于不正确处理数据包片段的某些类型攻击的保护措施。有许多选项可用,但最简单的形式适用于大多数配置: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session scrub in all 某些服务(如 NFS)需要特定的片段处理选项。有关详细信息,请参阅 https://home.nuug.no/~peter/pf/en/scrub.html。 本示例重新组合片段,清除 “do not fragment” 位,并将最大段大小设置为 1440 字节: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session scrub in all fragment reassemble no-df max-mss 1440 ``antispoof`` 机制可防止来自欺骗或伪造 IP 地址的活动,主要是通过阻止出现在接口上和逻辑上不可能的方向上的数据包。 这些规则清除了来自世界其他地区的欺骗性流量以及源自本地网络的任何欺骗性数据包: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session antispoof for $ext_if antispoof for $int_if 33.3.2.8.处理不可路由的地址 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 即使使用正确配置的网关来处理网络地址转换,也可能必须补偿其他人的错误配置。一个常见的错误配置是让具有不可路由地址的流量流向互联网。由于来自不可路由地址的流量可以在多种 DoS 攻击技术中发挥作用,因此请考虑显式阻止来自不可路由地址的流量通过外部接口进入网络。 在此示例中,定义了一个包含不可路由地址的宏,然后将其用于阻止规则。进出这些地址的流量会悄悄地丢弃在网关的外部接口上。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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。至少需要一个其他选项来指定排队调度程序算法: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session options ALTQ options ALTQ_CBQ # Class Based Queuing (CBQ) options ALTQ_RED # Random Early Detection (RED) options ALTQ_RIO # RED In/Out options ALTQ_HFSC # Hierarchical Packet Scheduler (HFSC) options ALTQ_PRIQ # Priority Queuing (PRIQ) 以下调度程序算法可用: - **CBQ** 基于类的队列(CBQ)用于将连接的带宽划分为不同的类或队列,以根据过滤规则确定流量的优先级。 - **RED** 随机早期检测(RED)用于通过测量队列的长度并将其与队列的最小和最大阈值进行比较来避免网络拥塞。当队列超过最大值时,将随机丢弃所有新数据包。 - **RIO** 在随机早期检测入库和出库(RIO)模式下,RED 保持多个平均队列长度和多个阈值,每个 QOS 级别一个阈值。 - **HFSC** 分层公平服务曲线数据包调度程序(HFSC)在 `http://www-2.cs.cmu.edu/\\~hzhang/HFSC/main.html `__ 中进行了说明。 - **PRIQ** 优先级队列(PRIQ)始终首先传递较高队列中的流量。 有关调度算法和示例规则集的更多信息,请访问 `OpenBSD 的 Web 存档 `__\ 。