第 33 章. 防火墙

33.1. 概要

防火墙可以过滤通过系统的进出流量。防火墙可以使用一组或多组“规则”来检查网络数据包,这些数据包在进出网络连接时,要么允许流量通过,要么阻止流量。防火墙的规则可以检查数据包的一个或多个特征,例如协议类型、源或目标主机地址以及源或目标端口。

防火墙可以增强主机或网络的安全性。它们可以用于执行以下一项或多项操作

  • 保护内部网络的应用程序、服务和机器免受来自公共互联网的 unwanted traffic 的影响。

  • 限制或禁用内部网络主机对公共互联网服务的访问。

  • 支持网络地址转换(NAT),这允许内部网络使用私有 IP 地址,并使用单个 IP 地址或共享的自动分配的公共地址池来共享单个连接到公共互联网。

FreeBSD 在基本系统中内置了三个防火墙:PF、IPFW 和 IPFILTER,也称为 IPF。FreeBSD 还提供了两个流量整形器用于控制带宽使用:altq(4)dummynet(4)。ALTQ 传统上与 PF 密切相关,而 dummynet 与 IPFW 密切相关。每个防火墙都使用规则来控制数据包进出 FreeBSD 系统的访问,尽管它们以不同的方式进行,并且每个防火墙都有不同的规则语法。

FreeBSD 提供多种防火墙,以满足各种用户的不同需求和偏好。每个用户都应评估哪个防火墙最能满足其需求。

阅读完本章后,您将了解

  • 如何定义数据包过滤规则。

  • FreeBSD 中内置的防火墙之间的区别。

  • 如何使用和配置 PF 防火墙。

  • 如何使用和配置 IPFW 防火墙。

  • 如何使用和配置 IPFILTER 防火墙。

在阅读本章之前,您应该

  • 了解基本的 FreeBSD 和互联网概念。

由于所有防火墙都是基于检查所选数据包控制字段的值来工作的,因此防火墙规则集的创建者必须了解 TCP/IP 的工作原理,数据包控制字段中的不同值以及这些值如何在正常会话对话中使用。有关入门信息,请参阅 Daryl’s TCP/IP Primer

33.2. 防火墙概念

规则集包含一组规则,这些规则根据数据包中包含的值来通过或阻止数据包。主机之间的数据包双向交换构成一个会话对话。防火墙规则集处理从公共互联网到达的数据包以及系统作为对这些数据包的响应而生成的数据包。每个 TCP/IP 服务都由其协议和监听端口预定义。目标特定服务的数据包起源于使用非特权端口的源地址,并以目标地址上的特定服务端口为目标。所有上述参数都可以用作选择标准来创建允许或阻止服务的规则。

要查找未知端口号,请参阅 /etc/services。或者,访问 https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers 并执行端口号查找以查找特定端口号的用途。

查看此链接以了解 木马使用的端口号

FTP 有两种模式:主动模式和被动模式。区别在于如何获取数据通道。被动模式更安全,因为数据通道是由普通 ftp 会话请求者获取的。有关 FTP 和不同模式的良好解释,请参见 http://www.slacksite.com/other/ftp.html

防火墙规则集可以是“排他性”的,也可以是“包含性”的。排他性防火墙允许除与规则集匹配的流量之外的所有流量通过。包含性防火墙则相反,它只允许与规则匹配的流量通过,并阻止其他所有流量。

包含性防火墙对出站流量提供了更好的控制,使其成为为公共互联网提供服务的系统的更好选择。它还可以控制来自公共互联网的哪种类型的流量可以访问私有网络。所有不匹配规则的流量都会被阻止并记录。包含性防火墙通常比排他性防火墙更安全,因为它们显着降低了允许 unwanted traffic 的风险。

除非另有说明,否则本章中的所有配置和示例规则集都创建包含性防火墙规则集。

可以使用“状态防火墙”进一步加强安全性。这种类型的防火墙会跟踪打开的连接,只允许与现有连接匹配的流量或打开新的、允许的连接的流量。

状态过滤将流量视为包含会话的双向数据包交换。当在匹配规则上指定状态时,防火墙会为会话期间交换的每个预期数据包动态生成内部规则。它具有足够的匹配能力来确定数据包对会话是否有效。任何不符合会话模板的数据包都会自动拒绝。

会话完成后,它将从动态状态表中删除。

状态过滤允许您专注于阻止/通过新会话。如果通过新会话,其所有后续数据包将自动允许,并且任何冒充数据包将自动拒绝。如果阻止了新会话,则不允许其任何后续数据包。状态过滤提供了先进的匹配能力,能够防御攻击者使用的各种攻击方法的洪流。

NAT 代表“网络地址转换”。NAT 功能使防火墙后面的私有 LAN 可以共享一个 ISP 分配的 IP 地址,即使该地址是动态分配的。NAT 允许 LAN 中的每台计算机都可以访问互联网,而无需为 ISP 支付多个互联网帐户或 IP 地址的费用。

NAT 会自动将 LAN 上每台系统上的私有 LAN IP 地址转换为单个公共 IP 地址,因为数据包从防火墙退出并绑定到公共互联网。它还会对返回的数据包执行反向转换。

根据 RFC 1918,以下 IP 地址范围保留用于私有网络,这些网络永远不会直接路由到公共互联网,因此可用于 NAT

  • 10.0.0.0/8.

  • 172.16.0.0/12.

  • 192.168.0.0/16.

在使用防火墙规则时,请务必非常小心。某些配置可能导致管理员被锁定在服务器之外。为了安全起见,请考虑从本地控制台执行初始防火墙配置,而不是通过 ssh 远程执行。

33.3. PF

从 FreeBSD 5.3 开始,OpenBSD 的 PF 防火墙移植版本已作为基本系统的一部分集成在内。PF 是一款完整的、功能齐全的防火墙,它可选地支持 ALTQ(可选排队),该功能提供服务质量 (QoS)。

OpenBSD 项目在 PF 常见问题解答 中维护着 PF 的权威参考。Peter Hansteen 在 http://home.nuug.no/~peter/pf/ 上维护着全面的 PF 教程。

阅读 PF 常见问题解答 时,请记住,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

# sysrc pf_enable=yes

可以在启动 PF 时将 pfctl(8) 中描述的其他选项传递给 PF。在 /etc/rc.conf 中添加或更改此条目,并在两个引号 ("") 之间指定任何所需的标志

pf_flags=""                     # additional flags for pfctl startup

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

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

pflog(4) 为 PF 提供日志记录支持。要启用日志记录支持,请将 pflog_enable=yes 添加到 /etc/rc.conf

# sysrc pflog_enable=yes

还可以添加以下行来更改日志文件的默认位置,或指定要传递给 pflog(4) 的任何其他标志,当它启动时

pflog_logfile="/var/log/pflog"  # where pflogd should store the logfile
pflog_flags=""                  # additional flags for pflogd startup

最后,如果防火墙后面有一个 LAN,并且需要转发数据包以供 LAN 上的计算机使用,或者需要 NAT,请启用以下选项

gateway_enable="YES"            # Enable as LAN gateway

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

# service pf start
# service pflog start

默认情况下,PF 从 /etc/pf.conf 读取其配置规则,并根据此文件中指定的规则或定义来修改、删除或传递数据包。FreeBSD 安装包含位于 /usr/share/examples/pf/ 中的几个示例文件。有关 PF 规则集的完整介绍,请参阅 PF 常见问题解答

要控制 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 Collection 中安装。

要密切关注通过 PF 防火墙的流量,请考虑安装 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 连接,特别是那些使用以太网上的 PPP(PPPoE)的连接,正确的外部接口是 tun0,而不是物理以太网接口。

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 地址发生变化,它也能确保网络流量在没有严重中断的情况下运行。

请注意,此规则集可能会允许比所需更多的流量传出网络。一个合理的设置可以创建这个宏

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 流量分配给特定队列。

要了解如何在 PF 和 ftp-proxy(8) 的保护下运行 FTP 服务器,请在反向模式下配置一个单独的 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 的灵活性。例如,与其允许所有 ICMP 报文,不如指定 ping(8)traceroute(8) 使用的报文。首先定义一个用于该类型报文的宏

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 报文类型列表。有关每种报文类型的说明,请参阅 http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml

由于 Unix traceroute 默认使用 UDP,因此需要另一条规则来允许 Unix traceroute

# 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 回声请求报文,因此只需要第一条规则来允许来自这些系统的网络跟踪。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 }"

由于传递规则已经使用该宏,因此不需要修改它来支持新的 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 提供了使表条目过期的功能。例如,此命令将删除未被引用 86400 秒的<bruteforce> 表条目

# pfctl -t bruteforce -T expire 86400

类似的功能由 security/expiretable 提供,该功能会删除在指定时间段内未访问的表条目。

安装完成后,可以运行 expiretable 来删除比指定年龄更大的<bruteforce> 表条目。此示例删除所有超过 24 小时的条目

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

33.3.2.6. 防范垃圾邮件

不要与捆绑在 spamassassin 中的 spamd 守护进程混淆,mail/spamd 可以与 PF 配合使用,以提供针对垃圾邮件的外部防御。此 spamd 使用一组重定向连接到 PF 配置。

垃圾邮件发送者往往会发送大量邮件,垃圾邮件主要来自少数垃圾邮件友好网络和大量被劫持的机器,这些机器都被很快地报告给了黑名单

当收到来自黑名单中地址的 SMTP 连接时,spamd 会显示其横幅,并立即切换到一种模式,在这种模式下,它会逐字节地响应 SMTP 流量。这种旨在尽可能浪费垃圾邮件发送者时间的方法称为陷阱。使用单字节 SMTP 响应的特定实现通常被称为结巴

此示例演示了使用自动更新的黑名单设置 spamd 的基本步骤。有关更多信息,请参阅与 mail/spamd 一起安装的手册页。

步骤:配置 spamd
  1. 安装 mail/spamd 软件包或端口。要使用 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" # 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. 确保已按照先前步骤 1 中的说明安装 fdescfs(5)

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

    spamd_grey="YES"  # use spamd greylisting if YES

    有关其他相关参数的描述,请参阅 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

此示例重新组装碎片,清除“不分段”位,并将最大分段大小设置为 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 无法作为可加载内核模块使用。如果系统的接口支持 ALTQ,请使用 配置 FreeBSD 内核 中的说明创建自定义内核。以下内核选项可用。第一个选项是启用 ALTQ 所必需的。至少需要其他选项之一来指定排队调度程序算法

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 的网络档案

33.4. IPFW

IPFW 是为 FreeBSD 编写的有状态防火墙,支持 IPv4 和 IPv6。它包含多个组件:内核防火墙过滤器规则处理器及其集成的包计量功能、日志记录功能、NAT、dummynet(4) 流量整形器、转发功能、桥接功能和 ipstealth 功能。

FreeBSD 在 /etc/rc.firewall 中提供了一个示例规则集,该规则集定义了针对常见场景的几种防火墙类型,以帮助新手用户生成合适的规则集。IPFW 提供了一个强大的语法,高级用户可以使用它来设计满足给定环境的安全要求的自定义规则集。

本节介绍如何启用 IPFW,概述其规则语法,并演示针对常见配置场景的几个规则集。

33.4.1. 启用 IPFW

IPFW 包含在基本 FreeBSD 安装中,作为可加载的内核模块,这意味着无需自定义内核即可启用 IPFW。

对于希望将 IPFW 支持静态编译到自定义内核中的用户,请参阅 IPFW 内核选项

要将系统配置为在启动时启用 IPFW,请将 firewall_enable="YES" 添加到 /etc/rc.conf

# sysrc firewall_enable="YES"

要使用 FreeBSD 提供的默认防火墙类型之一,请添加另一行,指定该类型

# sysrc firewall_type="open"

可用类型有

  • open:通过所有流量。

  • client:仅保护这台机器。

  • simple:保护整个网络。

  • closed:完全禁用 IP 流量,除了环回接口。

  • workstation:仅使用有状态规则保护这台机器。

  • UNKNOWN:禁用加载防火墙规则。

  • filename:包含防火墙规则集的文件的完整路径。

如果 firewall_type 设置为 clientsimple,请修改 /etc/rc.firewall 中的默认规则,以适合系统的配置。

请注意,filename 类型用于加载自定义规则集。

加载自定义规则集的另一种方法是将 firewall_script 变量设置为包含 IPFW 命令的可执行脚本的绝对路径。本节中使用的示例假设 firewall_script 设置为 /etc/ipfw.rules

# sysrc firewall_script="/etc/ipfw.rules"

要通过 syslogd(8) 启用日志记录,请包含以下行

# sysrc firewall_logging="YES"

只有具有 log 选项的防火墙规则才会被记录。默认规则不包含此选项,必须手动添加。因此,建议编辑默认规则集以进行日志记录。此外,如果日志存储在单独的文件中,则可能需要进行日志轮换。

没有 /etc/rc.conf 变量来设置日志记录限制。要限制每个连接尝试记录规则的次数,请在 /etc/sysctl.conf 中使用以下行指定该数字

# echo "net.inet.ip.fw.verbose_limit=5" >> /etc/sysctl.conf

要通过名为 ipfw0 的专用接口启用日志记录,请将以下行添加到 /etc/rc.conf

# sysrc firewall_logif="YES"

然后使用 tcpdump 查看正在记录的内容

# tcpdump -t -n -i ipfw0

除非连接了 tcpdump,否则日志记录不会产生开销。

保存必要的编辑后,启动防火墙。要立即启用日志记录限制,请同时设置上面指定的 sysctl

# service ipfw start
# sysctl net.inet.ip.fw.verbose_limit=5

33.4.2. IPFW 规则语法

当数据包进入 IPFW 防火墙时,它会与规则集中第一个规则进行比较,并按顺序依次处理每个规则,从上到下移动。当数据包与规则的选择参数匹配时,会执行规则的操作,并且对该数据包的规则集搜索将终止。这被称为“首个匹配获胜”。如果数据包与任何规则都不匹配,则它会由强制的 IPFW 默认规则号 65535 捕获,该规则会拒绝所有数据包并静默丢弃它们。但是,如果数据包与包含 countskiptotee 关键字的规则匹配,则搜索将继续。有关这些关键字如何影响规则处理的详细信息,请参阅 ipfw(8)

创建 IPFW 规则时,关键字必须按以下顺序编写。某些关键字是必需的,而其他关键字是可选的。用大写字母显示的单词表示变量,用小写字母显示的单词必须放在其后的变量之前。# 符号用于标记注释的开始,可以出现在规则的末尾或单独一行。空行将被忽略。

CMD RULE_NUMBER set SET_NUMBER ACTION log LOG_AMOUNT PROTO from SRC SRC_PORT to DST DST_PORT OPTIONS

本节概述了这些关键字及其选项。这不是所有可能选项的详尽列表。有关在创建 IPFW 规则时可用的规则语法的完整描述,请参阅 ipfw(8)

CMD

每个规则必须以 ipfw add 开头。

RULE_NUMBER

每个规则都与一个从 165534 的数字相关联。该数字用于指示规则处理的顺序。多个规则可以具有相同的数字,在这种情况下,它们将根据添加的顺序应用。

SET_NUMBER

每个规则都与一个从 031 的集合编号相关联。集合可以单独禁用或启用,从而可以快速添加或删除一组规则。如果未指定 SET_NUMBER,则规则将添加到集合 0

ACTION

规则可以与以下操作之一相关联。当数据包与规则的选择条件匹配时,将执行指定的操作。

allow | accept | pass | permit:这些关键字是等效的,允许与规则匹配的数据包。

check-state:根据动态状态表检查数据包。如果找到匹配项,则执行与生成此动态规则的规则相关联的操作,否则移至下一条规则。check-state 规则没有选择条件。如果规则集中不存在 check-state 规则,则会在第一个 keep-statelimit 规则处检查动态规则表。

count:更新与规则匹配的所有数据包的计数器。搜索将继续进行下一条规则。

deny | drop:这两个词都会静默丢弃与该规则匹配的数据包。

其他操作可用。有关详细信息,请参阅 ipfw(8)

LOG_AMOUNT

当数据包与包含 log 关键字的规则匹配时,将向 syslogd(8) 记录一条消息,其工具名称为 SECURITY。仅当为该特定规则记录的数据包数量不超过指定的 LOG_AMOUNT 时,才会进行日志记录。如果未指定 LOG_AMOUNT,则限制将从 net.inet.ip.fw.verbose_limit 的值中获取。值为零将删除日志记录限制。限制达到后,可以通过清除日志记录计数器或该规则的数据包计数器来重新启用日志记录,使用 ipfw resetlog

日志记录是在满足所有其他数据包匹配条件之后以及对数据包执行最终操作之前进行的。管理员决定要为哪些规则启用日志记录。

PROTO

此可选值可用于指定 /etc/protocols 中找到的任何协议名称或编号。

SRC

from 关键字后必须跟源地址或表示源地址的关键字。地址可以用 anyme(此系统上接口配置的任何地址)、me6(此系统上接口配置的任何 IPv6 地址)或 table 加上包含地址列表的查找表的编号来表示。在指定 IP 地址时,可以选择在其后加上其 CIDR 掩码或子网掩码。例如,1.2.3.4/251.2.3.4:255.255.255.128

SRC_PORT

可以使用来自 /etc/services 的端口号或名称来指定可选的源端口。

DST

to 关键字后必须跟目标地址或表示目标地址的关键字。SRC 部分中描述的相同关键字和地址可用于描述目标。

DST_PORT

可以使用来自 /etc/services 的端口号或名称来指定可选的目标端口。

OPTIONS

源和目标后可以跟几个关键字。顾名思义,OPTIONS 是可选的。常用的选项包括 inout,用于指定数据包流的方向,icmptypes 后跟 ICMP 消息的类型,以及 keep-state

当匹配 keep-state 规则时,防火墙将创建一个动态规则,该规则使用相同的协议匹配源地址和目标地址以及端口之间的双向流量。

动态规则功能容易受到 SYN 泛洪攻击的资源耗尽攻击,该攻击会打开大量动态规则。为了用 IPFW 抵御此类攻击,请使用 limit。此选项通过检查打开的动态规则、计算此规则和 IP 地址组合出现的次数,来限制同时会话的数量。如果此计数大于 limit 指定的值,则会丢弃数据包。

数十种 OPTIONS 可用。有关每个可用选项的描述,请参阅 ipfw(8)

33.4.3. 示例规则集

本节演示如何创建一个名为 /etc/ipfw.rules 的示例有状态防火墙规则集脚本。在此示例中,所有连接规则都使用 inout 来阐明方向。它们还使用 via 接口名称 来指定数据包正在通过的接口。

首次创建或测试防火墙规则集时,请考虑暂时设置此可调参数

net.inet.ip.fw.default_to_accept="1"

这将 ipfw(8) 的默认策略设置为比默认的 deny ip from any to any 更宽松,这使得在重启后立即被锁定在系统之外的难度略微增加。

防火墙脚本首先表明它是一个 Bourne shell 脚本,并清除任何现有的规则。然后,它创建 cmd 变量,以便在每个规则的开头不必键入 ipfw add。它还定义了 pif 变量,该变量代表连接到 Internet 的接口的名称。

#!/bin/sh
# Flush out the list before we begin.
ipfw -q -f flush

# Set rules command prefix
cmd="ipfw -q add"
pif="dc0"     # interface name of NIC attached to Internet

前两条规则允许受信任的内部接口和环回接口上的所有流量

# Change xl0 to LAN NIC interface name
$cmd 00005 allow all from any to any via xl0

# No restrictions on Loopback Interface
$cmd 00010 allow all from any to any via lo0

下一条规则允许数据包通过,前提是它与动态规则表中的现有条目匹配

$cmd 00101 check-state

下一组规则定义了内部系统可以创建到 Internet 上主机哪些有状态连接

# Allow access to public DNS
# Replace x.x.x.x with the IP address of a public DNS server
# and repeat for each DNS server in /etc/resolv.conf
$cmd 00110 allow tcp from any to x.x.x.x 53 out via $pif setup keep-state
$cmd 00111 allow udp from any to x.x.x.x 53 out via $pif keep-state

# Allow access to ISP's DHCP server for cable/DSL configurations.
# Use the first rule and check log for IP address.
# Then, uncomment the second rule, input the IP address, and delete the first rule
$cmd 00120 allow log udp from any to any 67 out via $pif keep-state
#$cmd 00120 allow udp from any to x.x.x.x 67 out via $pif keep-state

# Allow outbound HTTP and HTTPS connections
$cmd 00200 allow tcp from any to any 80 out via $pif setup keep-state
$cmd 00220 allow tcp from any to any 443 out via $pif setup keep-state

# Allow outbound email connections
$cmd 00230 allow tcp from any to any 25 out via $pif setup keep-state
$cmd 00231 allow tcp from any to any 110 out via $pif setup keep-state

# Allow outbound ping
$cmd 00250 allow icmp from any to any out via $pif keep-state

# Allow outbound NTP
$cmd 00260 allow udp from any to any 123 out via $pif keep-state

# Allow outbound SSH
$cmd 00280 allow tcp from any to any 22 out via $pif setup keep-state

# deny and log all other outbound connections
$cmd 00299 deny log all from any to any out via $pif

下一组规则控制从 Internet 主机到内部网络的连接。它首先拒绝与攻击相关的典型数据包,然后显式允许特定类型的连接。从 Internet 发起的授权服务都使用 limit 来防止泛洪。

# Deny all inbound traffic from non-routable reserved address spaces
$cmd 00300 deny all from 192.168.0.0/16 to any in via $pif     #RFC 1918 private IP
$cmd 00301 deny all from 172.16.0.0/12 to any in via $pif      #RFC 1918 private IP
$cmd 00302 deny all from 10.0.0.0/8 to any in via $pif         #RFC 1918 private IP
$cmd 00303 deny all from 127.0.0.0/8 to any in via $pif        #loopback
$cmd 00304 deny all from 0.0.0.0/8 to any in via $pif          #loopback
$cmd 00305 deny all from 169.254.0.0/16 to any in via $pif     #DHCP auto-config
$cmd 00306 deny all from 192.0.2.0/24 to any in via $pif       #reserved for docs
$cmd 00307 deny all from 204.152.64.0/23 to any in via $pif    #Sun cluster interconnect
$cmd 00308 deny all from 224.0.0.0/3 to any in via $pif        #Class D & E multicast

# Deny public pings
$cmd 00310 deny icmp from any to any in via $pif

# Deny ident
$cmd 00315 deny tcp from any to any 113 in via $pif

# Deny all Netbios services.
$cmd 00320 deny tcp from any to any 137 in via $pif
$cmd 00321 deny tcp from any to any 138 in via $pif
$cmd 00322 deny tcp from any to any 139 in via $pif
$cmd 00323 deny tcp from any to any 81 in via $pif

# Deny fragments
$cmd 00330 deny all from any to any frag in via $pif

# Deny ACK packets that did not match the dynamic rule table
$cmd 00332 deny tcp from any to any established in via $pif

# Allow traffic from ISP's DHCP server.
# Replace x.x.x.x with the same IP address used in rule 00120.
#$cmd 00360 allow udp from any to x.x.x.x 67 in via $pif keep-state

# Allow HTTP connections to internal web server
$cmd 00400 allow tcp from any to me 80 in via $pif setup limit src-addr 2

# Allow inbound SSH connections
$cmd 00410 allow tcp from any to me 22 in via $pif setup limit src-addr 2

# Reject and log all other incoming connections
$cmd 00499 deny log all from any to any in via $pif

最后一条规则记录所有与规则集中任何规则都不匹配的数据包

# Everything else is denied and logged
$cmd 00999 deny log all from any to any

33.4.4. 内核 NAT

FreeBSD 的 IPFW 防火墙有两种 NAT 实现:用户空间实现 natd(8),以及更新的内核 NAT 实现。两者都与 IPFW 结合使用,以提供网络地址转换。这可用于提供 Internet 连接共享解决方案,以便多台内部计算机可以使用单个公共 IP 地址连接到 Internet。

为此,连接到 Internet 的 FreeBSD 机器必须充当网关。此系统必须有两个网卡,一个连接到 Internet,另一个连接到内部 LAN。连接到 LAN 的每台机器都应在私有网络空间中分配一个 IP 地址,如 RFC 1918 中定义。

为了启用 IPFW 的内核 NAT 功能,还需要一些额外的配置。要在启动时启用内核 NAT 支持,必须在 /etc/rc.conf 中设置以下内容

gateway_enable="YES"
firewall_enable="YES"
firewall_nat_enable="YES"

firewall_nat_enable 设置但 firewall_enable 未设置时,它将没有效果,什么也不做。这是因为内核 NAT 实现仅与 IPFW 兼容。

当规则集包含有状态规则时,NAT 规则的位置至关重要,并且使用 skipto 操作。skipto 操作需要一个规则编号,以便知道要跳转到哪个规则。以下示例基于上一节中显示的防火墙规则集。它添加了一些额外的条目并修改了一些现有的规则,以便将防火墙配置为内核 NAT。它首先添加一些额外的变量,这些变量代表要跳过的规则编号、keep-state 选项以及将用于减少规则数量的 TCP 端口列表。

#!/bin/sh
ipfw -q -f flush
cmd="ipfw -q add"
skip="skipto 1000"
pif=dc0
ks="keep-state"
good_tcpo="22,25,37,53,80,443,110"

使用内核 NAT 时,由于 libalias(3)(一个作为内核模块实现的库,用于提供 IPFW 的内核 NAT 功能)的架构,需要禁用 TCP 分段卸载 (TSO)。可以使用 ifconfig(8) 在每个网络接口的基础上禁用 TSO,或者可以使用 sysctl(8) 在系统范围内禁用。要在系统范围内禁用 TSO,必须在 /etc/sysctl.conf 中设置以下内容

net.inet.tcp.tso="0"

还会配置 NAT 实例。可以有多个 NAT 实例,每个实例都有自己的配置。对于此示例,只需要一个 NAT 实例,NAT 实例编号为 1。配置可以包含一些选项,例如:if 指示公共接口,same_ports 确保别名端口和本地端口号映射相同,unreg_only 将导致仅未注册(私有)地址空间由 NAT 实例处理,reset 将有助于即使在 IPFW 机器的公共 IP 地址更改时也保持 NAT 实例正常运行。有关可以传递给单个 NAT 实例配置的所有可能选项,请咨询 ipfw(8)。在配置有状态 NAT 防火墙时,有必要允许翻译后的数据包被重新注入防火墙以进行进一步处理。这可以通过在防火墙脚本的开头禁用 one_pass 行为来实现。

ipfw disable one_pass
ipfw -q nat 1 config if $pif same_ports unreg_only reset

入站 NAT 规则插入在允许受信任和环回接口上的所有流量的两条规则之后,以及重新组装规则之后,但在 check-state 规则之前。重要的是,为此 NAT 规则选择的规则编号(在此示例中为 100)必须高于前三条规则,并且低于 check-state 规则。此外,由于内核 NAT 的行为,建议在第一个 NAT 规则之前以及允许受信任接口上的流量的规则之后放置一个重新组装规则。通常,IP 分片不应该发生,但当处理 IPSEC/ESP/GRE 隧道流量时,它可能会发生,并且在将完整数据包交给内核 NAT 功能之前,需要重新组装碎片。

在用户空间使用 natd(8) 时,不需要重组规则,因为 IPFW 的 divert 操作的内部机制已经在将数据包传递到套接字之前处理了重组,这一点在 ipfw(8) 中也有说明。

本例中使用的 NAT 实例和规则编号与 rc.firewall 创建的默认 NAT 实例和规则编号不匹配。 rc.firewall 是一个脚本,用于设置 FreeBSD 中存在的默认防火墙规则。

$cmd 005 allow all from any to any via xl0  # exclude LAN traffic
$cmd 010 allow all from any to any via lo0  # exclude loopback traffic
$cmd 099 reass all from any to any in       # reassemble inbound packets
$cmd 100 nat 1 ip from any to any in via $pif # NAT any inbound packets
# Allow the packet through if it has an existing entry in the dynamic rules table
$cmd 101 check-state

修改了出站规则,将 allow 操作替换为 $skip 变量,表示规则处理将在规则 1000 处继续。七个 tcp 规则已被规则 125 替换,因为 $good_tcpo 变量包含七个允许的出站端口。

请记住,IPFW 的性能很大程度上取决于规则集中存在的规则数量。

# Authorized outbound packets
$cmd 120 $skip udp from any to x.x.x.x 53 out via $pif $ks
$cmd 121 $skip udp from any to x.x.x.x 67 out via $pif $ks
$cmd 125 $skip tcp from any to any $good_tcpo out via $pif setup $ks
$cmd 130 $skip icmp from any to any out via $pif $ks

入站规则保持不变,除了最后一个规则,它删除了 via $pif 以捕获入站和出站规则。NAT 规则必须跟随此最后一个出站规则,其编号必须高于最后一个规则的编号,并且规则编号必须由 skipto 操作引用。在此规则集中,规则编号 1000 处理将所有数据包传递到我们配置的实例以进行 NAT 处理。下一条规则允许任何经过 NAT 处理的数据包通过。

$cmd 999 deny log all from any to any
$cmd 1000 nat 1 ip from any to any out via $pif # skipto location for outbound stateful rules
$cmd 1001 allow ip from any to any

在本例中,规则 10010112510001001 控制出站和入站数据包的地址转换,以便动态状态表中的条目始终注册私有 LANIP 地址。

考虑一个内部网页浏览器,它在端口 80 上初始化一个新的出站 HTTP 会话。当第一个出站数据包进入防火墙时,它不匹配规则 100,因为它正在传出而不是传入。它通过规则 101,因为这是第一个数据包,并且它尚未发布到动态状态表中。该数据包最终匹配规则 125,因为它在允许的端口上出站,并且具有来自内部 LAN 的源 IP 地址。匹配此规则时,将执行两个操作。首先,keep-state 操作在动态状态表中添加一个条目,并执行指定的动作 skipto rule 1000。接下来,数据包经过 NAT 处理并发送到 Internet。该数据包到达目标 Web 服务器,在那里生成一个响应数据包并发送回来。此新数据包进入规则集的顶部。它匹配规则 100,并且其目标 IP 地址被映射回原始内部地址。然后,它由 check-state 规则处理,在表中被发现为现有会话,并被释放到 LAN。

在入站方面,规则集必须拒绝不良数据包并仅允许授权的服务。匹配入站规则的数据包将发布到动态状态表中,并将数据包释放到 LAN。作为响应生成的数据包被 check-state 规则识别为属于现有会话。然后将其发送到规则 1000 以进行 NAT 处理,然后再释放到出站接口。

从用户空间 natd(8) 迁移到内核 NAT 乍一看似乎是无缝的,但有一个小问题。当使用 GENERIC 内核时,IPFW 将加载 libalias.ko 内核模块,当 /etc/rc.conf 中启用 firewall_nat_enable 时。 libalias.ko 内核模块只提供基本的 NAT 功能,而用户空间实现 natd(8) 在其用户空间库中拥有所有 NAT 功能,无需任何额外配置。所有功能都指的是以下内核模块,除了标准的 libalias.ko 内核模块外,这些模块也可以在需要时加载: alias_ftp.koalias_bbt.koskinny.koirc.koalias_pptp.koalias_smedia.ko,使用 /etc/rc.conf 中的 kld_list 指令。如果使用自定义内核,用户空间库的全部功能可以在内核中使用 options LIBALIAS 进行编译。

33.4.4.1. 端口重定向

NAT 的普遍缺点是 LAN 客户端无法从 Internet 访问。LAN 上的客户端可以向外部世界建立出站连接,但无法接收传入连接。如果尝试在其中一台 LAN 客户端机器上运行 Internet 服务,则会出现问题。解决此问题的一种简单方法是将 NAT 提供机器上的选定 Internet 端口重定向到 LAN 客户端。

例如,一个 IRC 服务器运行在客户端 A 上,一个 Web 服务器运行在客户端 B 上。为了使其正常工作,接收在端口 6667(IRC)和 80(HTTP)上的连接必须重定向到相应的机器。

使用内核 NAT,所有配置都在 NAT 实例配置中完成。有关内核 NAT 实例可以使用的所有选项的完整列表,请参阅 ipfw(8)。IPFW 语法遵循 natd 的语法。redirect_port 的语法如下

redirect_port proto targetIP:targetPORT[-targetPORT]
  [aliasIP:]aliasPORT[-aliasPORT]
  [remoteIP[:remotePORT[-remotePORT]]]

要配置上述示例设置,参数应为

redirect_port tcp 192.168.0.2:6667 6667
redirect_port tcp 192.168.0.3:80 80

将这些参数添加到上述规则集中的 NAT 实例 1 的配置后,TCP 端口将被端口转发到运行 IRC 和 HTTP 服务的 LAN 客户端机器。

ipfw -q nat 1 config if $pif same_ports unreg_only reset \
  redirect_port tcp 192.168.0.2:6667 6667 \
  redirect_port tcp 192.168.0.3:80 80

可以使用 redirect_port 指示单个端口上的端口范围。例如,tcp 192.168.0.2:2000-3000 2000-3000 将重定向接收在端口 2000 到 3000 上的所有连接到客户端 A 上的端口 2000 到 3000。

33.4.4.2. 地址重定向

如果有多个 IP 地址可用,地址重定向将非常有用。每个 LAN 客户端都可以通过 ipfw(8) 分配其自己的外部 IP 地址,然后 ipfw(8) 将使用正确的外部 IP 地址重写来自 LAN 客户端的出站数据包,并将所有接收在该特定 IP 地址上的流量重定向回特定的 LAN 客户端。这也称为静态 NAT。例如,如果 IP 地址 128.1.1.1128.1.1.2128.1.1.3 可用,128.1.1.1 可以用作 ipfw(8) 机器外部 IP 地址,而 128.1.1.2128.1.1.3 将转发回 LAN 客户端 AB

redirect_addr 语法如下,其中 localIP 是 LAN 客户端的内部 IP 地址,publicIP 是与 LAN 客户端相对应的外部 IP 地址。

redirect_addr localIP publicIP

在本例中,参数将读取

redirect_addr 192.168.0.2 128.1.1.2
redirect_addr 192.168.0.3 128.1.1.3

redirect_port 一样,这些参数放在 NAT 实例配置中。使用地址重定向,不需要端口重定向,因为接收在特定 IP 地址上的所有数据都会被重定向。

ipfw(8) 机器上的外部 IP 地址必须处于活动状态并与外部接口关联。有关详细信息,请参阅 rc.conf(5)

33.4.4.3. 用户空间 NAT

让我们从一个声明开始:用户空间 NAT 实现:natd(8) 比内核 NAT 具有更高的开销。对于 natd(8) 来转换数据包,数据包必须从内核复制到用户空间,然后再返回,这带来了内核 NAT 中不存在的额外开销。

要在启动时启用用户空间 NAT 守护程序 natd(8),以下是在 /etc/rc.conf 中的最小配置。其中 natd_interface 设置为连接到 Internet 的 NIC 的名称。 rc(8) 脚本将自动检查是否使用了动态 IP 地址,并自行配置以处理它。

gateway_enable="YES"
natd_enable="YES"
natd_interface="rl0"

一般来说,上述为内核 NAT 解释的规则集也可以与 natd(8) 一起使用。例外情况是内核 NAT 实例的配置 (ipfw -q nat 1 config …​),它不需要与重组规则 99 一起使用,因为其功能包含在 divert 操作中。规则编号 100 和 1000 将需要稍微更改,如下所示。

$cmd 100 divert natd ip from any to any in via $pif
$cmd 1000 divert natd ip from any to any out via $pif

要配置端口或地址重定向,使用与内核 NAT 相似的语法。但是,现在,不是像内核 NAT 那样在规则集脚本中指定配置,而是最好在配置文件中配置 natd(8)。为此,必须通过 /etc/rc.conf 传递一个额外的标志,该标志指定配置文件的路径。

natd_flags="-f /etc/natd.conf"

指定的文件必须包含一个配置选项列表,每行一个。有关配置文件和可能变量的更多信息,请参阅 natd(8)。下面是两个示例条目,每行一个

redirect_port tcp 192.168.0.2:6667 6667
redirect_addr 192.168.0.3 128.1.1.3

33.4.5. IPFW 命令

ipfw 可用于在防火墙运行时对活动防火墙进行手动、单规则添加或删除。使用这种方法的问题是,所有更改在系统重新启动时都会丢失。建议改为将所有规则写入一个文件,并使用该文件在启动时加载规则,并在该文件发生更改时替换当前正在运行的防火墙规则。

ipfw 是一种有用的方法,可以将正在运行的防火墙规则显示到控制台屏幕上。IPFW 会计功能为每个规则动态创建一个计数器,该计数器统计与该规则匹配的每个数据包。在测试规则的过程中,列出该规则及其计数器是一种确定该规则是否按预期运行的方法。

要按顺序列出所有正在运行的规则

# ipfw list

要列出所有正在运行的规则,并显示上次匹配该规则的时间戳

# ipfw -t list

下一个示例将列出会计信息和匹配规则的数据包计数以及规则本身。第一列是规则编号,其次是匹配的数据包和字节数,最后是规则本身。

# ipfw -a list

要列出动态规则以及静态规则

# ipfw -d list

还要显示已过期的动态规则

# ipfw -d -e list

将计数器归零

# ipfw zero

将编号为 NUM 的规则的计数器归零

# ipfw zero NUM

33.4.5.1. 记录防火墙消息

即使启用了日志记录功能,IPFW 也不会自行生成任何规则日志。防火墙管理员决定规则集中哪些规则需要记录,并在这些规则中添加 log 关键字。通常只记录拒绝规则。习惯上,将包含 log 关键字的 "ipfw default deny everything" 规则复制为规则集中的最后一条规则。这样,就可以看到所有与规则集中任何规则都不匹配的数据包。

日志记录是一把双刃剑。如果不小心,过多的日志数据或 DoS 攻击可能会用日志文件填满磁盘。日志消息不仅写入 syslogd,还会显示在 root 控制台屏幕上,很快就会变得令人厌烦。

IPFIREWALL_VERBOSE_LIMIT=5 内核选项限制了发送到 syslogd(8) 的关于给定规则的数据包匹配的连续消息数量。当在内核中启用此选项时,关于特定规则的连续消息数量将限制在指定数量。从 200 条相同的日志消息中不会有任何收获。将此选项设置为 5 后,关于特定规则的 5 条连续消息将被记录到 syslogd,其余相同的连续消息将被计数并以类似于以下内容的短语发布到 syslogd。

last message repeated 45 times

默认情况下,所有记录的数据包消息都写入 /var/log/security,这在 /etc/syslog.conf 中定义。

33.4.5.2. 构建规则脚本

大多数经验丰富的 IPFW 用户会创建一个包含规则的文件,并以与将其作为脚本运行兼容的方式对它们进行编码。这样做的主要好处是,防火墙规则可以批量刷新,而无需重新启动系统以激活它们。这种方法在测试新规则时很方便,因为该过程可以根据需要执行多次。作为一个脚本,符号替换可以用于将常用值替换到多个规则中。

此示例脚本与 sh(1)csh(1)tcsh(1) shell 使用的语法兼容。符号替换字段以美元符号 ($) 为前缀。符号字段没有 $ 前缀。要填充符号字段的值必须用双引号 ("") 括起来。

以这种方式开始规则文件

############### start of example ipfw rules script #############
#
ipfw -q -f flush       # Delete all rules
# Set defaults
oif="tun0"             # out interface
odns="192.0.2.11"      # ISP's DNS server IP address
cmd="ipfw -q add "     # build rule prefix
ks="keep-state"        # just too lazy to key this each time
$cmd 00500 check-state
$cmd 00502 deny all from any to any frag
$cmd 00501 deny tcp from any to any established
$cmd 00600 allow tcp from any to any 80 out via $oif setup $ks
$cmd 00610 allow tcp from any to $odns 53 out via $oif setup $ks
$cmd 00611 allow udp from any to $odns 53 out via $oif $ks
################### End of example ipfw rules script ############

规则并不重要,因为本例的重点是如何填充符号替换字段。

如果上面的例子是在 /etc/ipfw.rules 中,则可以使用以下命令重新加载规则

# sh /etc/ipfw.rules

/etc/ipfw.rules 可以位于任何位置,并且文件可以具有任何名称。

可以通过手动运行以下命令来实现相同的效果

# ipfw -q -f flush
# ipfw -q add check-state
# ipfw -q add deny all from any to any frag
# ipfw -q add deny tcp from any to any established
# ipfw -q add allow tcp from any to any 80 out via tun0 setup keep-state
# ipfw -q add allow tcp from any to 192.0.2.11 53 out via tun0 setup keep-state
# ipfw -q add 00611 allow udp from any to 192.0.2.11 53 out via tun0 keep-state

33.4.6. IPFW 内核选项

要将 IPFW 支持静态编译到自定义内核中,请参考 配置 FreeBSD 内核 中的说明。自定义内核配置文件可以使用以下选项

options    IPFIREWALL			# enables IPFW
options    IPFIREWALL_VERBOSE		# enables logging for rules with log keyword to syslogd(8)
options    IPFIREWALL_VERBOSE_LIMIT=5	# limits number of logged packets per-entry
options    IPFIREWALL_DEFAULT_TO_ACCEPT # sets default policy to pass what is not explicitly denied
options    IPFIREWALL_NAT		# enables basic in-kernel NAT support
options    LIBALIAS			# enables full in-kernel NAT support
options    IPFIREWALL_NAT64		# enables in-kernel NAT64 support
options    IPFIREWALL_NPTV6		# enables in-kernel IPv6 NPT support
options    IPFIREWALL_PMOD		# enables protocols modification module support
options    IPDIVERT			# enables NAT through natd(8)

IPFW 可以作为内核模块加载:上面的选项默认情况下以模块的形式构建,或者可以在运行时使用可调参数设置。

33.5. IPFILTER (IPF)

IPFILTER,也称为 IPF,是一个跨平台的开源防火墙,已移植到多个操作系统,包括 FreeBSD、NetBSD、OpenBSD 和 Solaris™。

IPFILTER 是一个内核级防火墙和 NAT 机制,可以通过用户空间程序进行控制和监控。可以使用 ipf 设置或删除防火墙规则,可以使用 ipnat 设置或删除 NAT 规则,可以使用 ipfstat 打印 IPFILTER 内核部分的运行时统计信息,并且可以使用 ipmon 将 IPFILTER 操作记录到系统日志文件中。

IPF 最初使用 "最后匹配的规则获胜" 的规则处理逻辑编写,并且只使用无状态规则。从那时起,IPF 已得到增强,包括 quickkeep state 选项。

IPF 常见问题解答位于 http://www.phildev.net/ipf/index.html。IPFilter 邮件列表的可搜索存档位于 http://marc.info/?l=ipfilter

手册的本节重点介绍与 FreeBSD 相关的 IPF。它提供了包含 quickkeep state 选项的规则示例。

33.5.1. 启用 IPF

IPF 包含在基本 FreeBSD 安装中,作为可加载的内核模块,这意味着为了启用 IPF,不需要自定义内核。

对于希望将 IPF 支持静态编译到自定义内核中的用户,请参考 配置 FreeBSD 内核 中的说明。可以使用以下内核选项

options IPFILTER
options IPFILTER_LOG
options IPFILTER_LOOKUP
options IPFILTER_DEFAULT_BLOCK

其中 options IPFILTER 启用对 IPFILTER 的支持,options IPFILTER_LOG 启用使用 ipl 数据包日志伪设备的 IPF 日志记录,用于每个包含 log 关键字的规则,IPFILTER_LOOKUP 启用 IP 池以加快 IP 查找速度,options IPFILTER_DEFAULT_BLOCK 更改默认行为,因此任何与防火墙 pass 规则不匹配的数据包都会被阻止。

要将系统配置为在启动时启用 IPF,请将以下条目添加到 /etc/rc.conf。这些条目还将启用日志记录和 default pass all。要将默认策略更改为 block all 而不编译自定义内核,请记住在规则集的末尾添加 block all 规则。

ipfilter_enable="YES"             # Start ipf firewall
ipfilter_rules="/etc/ipf.rules"   # loads rules definition text file
ipv6_ipfilter_rules="/etc/ipf6.rules" # loads rules definition text file for IPv6
ipmon_enable="YES"                # Start IP monitor log
ipmon_flags="-Ds"                 # D = start as daemon
                                  # s = log to syslog
                                  # v = log tcp window, ack, seq
                                  # n = map IP & port to names

如果需要 NAT 功能,也请添加以下行

gateway_enable="YES"              # Enable as LAN gateway
ipnat_enable="YES"                # Start ipnat function
ipnat_rules="/etc/ipnat.rules"    # rules definition file for ipnat

然后,要立即启动 IPF

#  service ipfilter start

要加载防火墙规则,请使用 ipf 指定规则集文件的名称。以下命令可用于替换当前正在运行的防火墙规则

# ipf -Fa -f /etc/ipf.rules

其中 -Fa 刷新所有内部规则表,-f 指定包含要加载的规则的文件。

这提供了对自定义规则集进行更改并将运行的防火墙更新为规则的新副本的能力,而无需重新启动系统。这种方法在测试新规则时很方便,因为该过程可以根据需要执行多次。

有关此命令可用的其他标志的详细信息,请参考 ipf(8)

33.5.2. IPF 规则语法

本节介绍用于创建有状态规则的 IPF 规则语法。创建规则时,请记住,除非 quick 关键字出现在规则中,否则会按顺序读取每个规则,最后匹配的规则 是应用的规则。这意味着,即使第一个与数据包匹配的规则是 pass,如果存在一个后面的匹配规则是 block,则该数据包将被丢弃。示例规则集可以在 /usr/share/examples/ipfilter 中找到。

创建规则时,# 字符用于标记注释的开始,它可以出现在规则末尾以解释该规则的功能,或者出现在它自己的行上。所有空行都会被忽略。

规则中使用的关键字必须按特定顺序从左到右编写。某些关键字是必需的,而其他关键字是可选的。某些关键字具有子选项,这些子选项本身可能也是关键字,并且还包括更多子选项。关键字顺序如下所示,其中大写字母表示变量,小写字母必须在后面的变量之前

ACTION DIRECTION OPTIONS proto PROTO_TYPE from SRC_ADDR SRC_PORT to DST_ADDR DST_PORT TCP_FLAG|ICMP_TYPE keep state STATE

本节介绍了这些关键字及其选项。它不是对所有可能选项的详尽列表。有关在创建 IPF 规则时可以使用的规则语法的完整描述以及使用每个关键字的示例,请参考 ipf(5)

ACTION

action 关键字指示如果数据包与该规则匹配,则如何处理该数据包。每个规则必须有一个动作。以下操作是公认的

block:丢弃数据包。

pass:允许数据包。

log:生成日志记录。

count:计算数据包和字节的数量,可以指示规则的使用频率。

auth:将数据包排队以供另一个程序进一步处理。

call:提供对 IPF 中内置函数的访问权限,这些函数允许执行更复杂的操作。

decapsulate:删除任何标头以处理数据包的内容。

DIRECTION

接下来,每个规则都必须使用以下关键字之一明确说明流量的方向

in:该规则应用于入站数据包。

out:该规则应用于出站数据包。

all:该规则适用于任一方向。

如果系统有多个接口,则可以将接口与方向一起指定。例如 in on fxp0

OPTIONS

选项是可选的。但是,如果指定了多个选项,则必须按此处显示的顺序使用它们。

log:执行指定的 ACTION 时,将数据包标头的内容写入 ipl(4) 数据包日志伪设备。

quick:如果数据包与该规则匹配,则执行该规则指定的 ACTION,并且不会对该数据包的任何后续规则进行进一步处理。

on:必须后跟 ifconfig(8) 显示的接口名称。只有当数据包在指定方向通过指定接口时,该规则才会匹配。

使用 log 关键字时,可以使用以下限定符,按此顺序

body:表示在标头之后将记录数据包内容的前 128 字节。

first:如果 log 关键字与 keep state 选项一起使用,则建议使用此选项,以便只记录触发数据包,而不是记录与有状态连接匹配的每个数据包。

还有其他选项可用于指定错误返回消息。有关更多详细信息,请参考 ipf(5)

PROTO_TYPE

协议类型是可选的。但是,如果规则需要指定 SRC_PORT 或 DST_PORT,则它是强制性的,因为它定义了协议类型。指定协议类型时,使用 proto 关键字后跟 /etc/protocols 中的协议号或名称。示例协议名称包括 tcpudpicmp。如果指定了 PROTO_TYPE 但未指定 SRC_PORT 或 DST_PORT,则该协议的所有端口号将与该规则匹配。

SRC_ADDR

from 关键字是必须的,后面跟着表示数据包来源的关键字。源可以是主机名、IP 地址后跟 CIDR 掩码、地址池或关键字 all。有关示例,请参阅 ipf(5)

无法匹配不能使用点分十进制形式/掩码长度表示法轻松表达的 IP 地址范围。可以使用 net-mgmt/ipcalc 包或端口来简化 CIDR 掩码的计算。有关更多信息,请访问该实用程序的网页:http://jodies.de/ipcalc

SRC_PORT

源端口号是可选的。但是,如果使用它,则需要先在规则中定义 PROTO_TYPE。端口号也必须以 proto 关键字开头。

支持许多不同的比较运算符:=(等于)、!=(不等于)、<(小于)、>(大于)、(小于或等于)和 >=(大于或等于)。

要指定端口范围,请将两个端口号放在 <>(小于和大于)、><(大于和小于)或 :(大于或等于和小于或等于)之间。

DST_ADDR

to 关键字是必须的,后面跟着表示数据包目的地的关键字。与 SRC_ADDR 类似,它可以是主机名、IP 地址后跟 CIDR 掩码、地址池或关键字 all

DST_PORT

与 SRC_PORT 类似,目的地的端口号是可选的。但是,如果使用它,则需要先在规则中定义 PROTO_TYPE。端口号也必须以 proto 关键字开头。

TCP_FLAG|ICMP_TYPE

如果将 tcp 指定为 PROTO_TYPE,则可以将标志指定为字母,其中每个字母代表一个可能的 TCP 标志,用于确定连接的状态。可能的值为:S(SYN)、A(ACK)、P(PSH)、F(FIN)、U(URG)、R(RST)、C(CWN)和 E(ECN)。

如果将 icmp 指定为 PROTO_TYPE,则可以指定要匹配的 ICMP 类型。有关允许的类型,请参阅 ipf(5)

STATE

如果 pass 规则包含 keep state,则 IPF 将在动态状态表中添加一个条目,并允许匹配连接的后续数据包。IPF 可以跟踪 TCP、UDP 和 ICMP 会话的状态。IPF 可以确定的属于活动会话的任何数据包,即使它是不同的协议,也将被允许。

在 IPF 中,目标是通过连接到公共互联网的接口传出的数据包首先会针对动态状态表进行检查。如果数据包匹配构成活动会话对话的下一个预期数据包,它将退出防火墙,并且会话对话流的状态将在动态状态表中更新。不属于已活动会话的数据包将针对出站规则集进行检查。来自连接到公共互联网的接口传入的数据包首先会针对动态状态表进行检查。如果数据包匹配构成活动会话的下一个预期数据包,它将退出防火墙,并且会话对话流的状态将在动态状态表中更新。不属于已活动会话的数据包将针对入站规则集进行检查。

可以在 keep state 后添加几个关键字。如果使用,这些关键字将设置各种选项来控制有状态过滤,例如设置连接限制或连接时长。有关可用选项及其描述的列表,请参阅 ipf(5)

33.5.3. 示例规则集

本节演示如何创建仅允许与 pass 规则匹配的服务并阻止所有其他服务的示例规则集。

FreeBSD 使用回环接口 (lo0) 和 IP 地址 127.0.0.1 进行内部通信。防火墙规则集必须包含规则以允许这些内部使用的数据包自由移动

# no restrictions on loopback interface
pass in quick on lo0 all
pass out quick on lo0 all

连接到互联网的公共接口用于授权和控制所有出站和入站连接的访问。如果一个或多个接口连接到私有网络,则这些内部接口可能需要规则以允许来自 LAN 的数据包在内部网络之间或到连接到互联网的接口之间流动。规则集应组织成三个主要部分:任何受信任的内部接口、通过公共接口的出站连接以及通过公共接口的入站连接。

这两个规则允许所有流量通过名为 xl0 的受信任 LAN 接口。

# no restrictions on inside LAN interface for private network
pass out quick on xl0 all
pass in quick on xl0 all

公共接口的出站和入站部分的规则应将最常匹配的规则放在匹配频率较低的规则之前,并将该部分中的最后一个规则用于阻止和记录该接口和方向的所有数据包。

这组规则定义了名为 dc0 的公共接口的出站部分。这些规则保持状态并识别内部系统被授权用于公共互联网访问的特定服务。所有规则都使用 quick 并指定适当的端口号,以及适用的目的地地址。

# interface facing Internet (outbound)
# Matches session start requests originating from or behind the
# firewall, destined for the Internet.

# Allow outbound access to public DNS servers.
# Replace x.x.x.x with address listed in /etc/resolv.conf.
# Repeat for each DNS server.
pass out quick on dc0 proto tcp from any to x.x.x.x port = 53 flags S keep state
pass out quick on dc0 proto udp from any to x.x.x.x port = 53 keep state

# Allow access to ISP's specified DHCP server for cable or DSL networks.
# Use the first rule, then check log for the IP address of DHCP server.
# Then, uncomment the second rule, replace z.z.z.z with the IP address,
# and comment out the first rule
pass out log quick on dc0 proto udp from any to any port = 67 keep state
#pass out quick on dc0 proto udp from any to z.z.z.z port = 67 keep state

# Allow HTTP and HTTPS
pass out quick on dc0 proto tcp from any to any port = 80 flags S keep state
pass out quick on dc0 proto tcp from any to any port = 443 flags S keep state

# Allow email
pass out quick on dc0 proto tcp from any to any port = 110 flags S keep state
pass out quick on dc0 proto tcp from any to any port = 25 flags S keep state

# Allow NTP
pass out quick on dc0 proto tcp from any to any port = 37 flags S keep state

# Allow FTP
pass out quick on dc0 proto tcp from any to any port = 21 flags S keep state

# Allow SSH
pass out quick on dc0 proto tcp from any to any port = 22 flags S keep state

# Allow ping
pass out quick on dc0 proto icmp from any to any icmp-type 8 keep state

# Block and log everything else
block out log first quick on dc0 all

此公共接口的入站部分中的规则示例首先阻止所有不需要的数据包。这将减少由最后一个规则记录的数据包数量。

# interface facing Internet (inbound)
# Block all inbound traffic from non-routable or reserved address spaces
block in quick on dc0 from 192.168.0.0/16 to any    #RFC 1918 private IP
block in quick on dc0 from 172.16.0.0/12 to any     #RFC 1918 private IP
block in quick on dc0 from 10.0.0.0/8 to any        #RFC 1918 private IP
block in quick on dc0 from 127.0.0.0/8 to any       #loopback
block in quick on dc0 from 0.0.0.0/8 to any         #loopback
block in quick on dc0 from 169.254.0.0/16 to any    #DHCP auto-config
block in quick on dc0 from 192.0.2.0/24 to any      #reserved for docs
block in quick on dc0 from 204.152.64.0/23 to any   #Sun cluster interconnect
block in quick on dc0 from 224.0.0.0/3 to any       #Class D & E multicast

# Block fragments and too short tcp packets
block in quick on dc0 all with frags
block in quick on dc0 proto tcp all with short

# block source routed packets
block in quick on dc0 all with opt lsrr
block in quick on dc0 all with opt ssrr

# Block OS fingerprint attempts and log first occurrence
block in log first quick on dc0 proto tcp from any to any flags FUP

# Block anything with special options
block in quick on dc0 all with ipopts

# Block public pings and ident
block in quick on dc0 proto icmp all icmp-type 8
block in quick on dc0 proto tcp from any to any port = 113

# Block incoming Netbios services
block in log first quick on dc0 proto tcp/udp from any to any port = 137
block in log first quick on dc0 proto tcp/udp from any to any port = 138
block in log first quick on dc0 proto tcp/udp from any to any port = 139
block in log first quick on dc0 proto tcp/udp from any to any port = 81

如果包含 log first 选项的规则存在记录的消息,请运行 ipfstat -hio 来评估该规则匹配了多少次。大量匹配可能表明系统正在遭受攻击。

入站部分中的其余规则定义了允许从互联网启动哪些连接。最后一条规则拒绝所有未被本部分中先前规则明确允许的连接。

# Allow traffic in from ISP's DHCP server. Replace z.z.z.z with
# the same IP address used in the outbound section.
pass in quick on dc0 proto udp from z.z.z.z to any port = 68 keep state

# Allow public connections to specified internal web server
pass in quick on dc0 proto tcp from any to x.x.x.x port = 80 flags S keep state

# Block and log only first occurrence of all remaining traffic.
block in log first quick on dc0 all

33.5.4. 配置 NAT

要启用 NAT,请将以下语句添加到 /etc/rc.conf 并指定包含 NAT 规则的文件的名称

gateway_enable="YES"
ipnat_enable="YES"
ipnat_rules="/etc/ipnat.rules"

NAT 规则很灵活,可以完成许多不同的操作来满足商业和家庭用户的需求。此处介绍的规则语法已简化,以演示常见的用法。有关完整的规则语法说明,请参阅 ipnat(5)

NAT 规则的基本语法如下,其中 map 启动规则,IF 应替换为外部接口的名称

map IF LAN_IP_RANGE -> PUBLIC_ADDRESS

LAN_IP_RANGE 是内部客户端使用的 IP 地址范围。通常,它是一个私有地址范围,例如 192.168.1.0/24PUBLIC_ADDRESS 可以是静态外部 IP 地址,也可以是关键字 0/32,它代表分配给 IF 的 IP 地址。

在 IPF 中,当数据包从 LAN 传入防火墙时,其目的地是公共地址,它首先会通过防火墙规则集的出站规则。然后,数据包将传递到 NAT 规则集,该规则集从上到下读取,其中第一个匹配的规则获胜。IPF 会针对数据包的接口名称和源 IP 地址测试每个 NAT 规则。当数据包的接口名称与 NAT 规则匹配时,会检查数据包的源 IP 地址,以查看它是否在 LAN_IP_RANGE 中指定的 IP 地址范围内。匹配后,数据包的源 IP 地址将使用 PUBLIC_ADDRESS 指定的公共 IP 地址进行重写。IPF 会在其内部 NAT 表中发布一个条目,以便当数据包从互联网返回时,可以将其映射回原始私有 IP 地址,然后传递给防火墙规则以进行进一步处理。

对于具有大量内部系统或多个子网的网络,将每个私有 IP 地址导入到单个公共 IP 地址的过程会成为资源问题。有两种方法可以缓解此问题。

第一种方法是分配一组端口用作源端口。通过添加 portmap 关键字,可以将 NAT 指向仅使用指定范围内的源端口

map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp 20000:60000

或者,可以使用 auto 关键字,它告诉 NAT 确定可用于使用的端口

map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp auto

第二种方法是使用公共地址池。当有太多 LAN 地址无法容纳在一个公共地址中,并且有一组公共 IP 地址可用时,这很有用。这些公共地址可以用作池,NAT 从中选择一个 IP 地址,作为数据包在传出时进行映射的地址。

可以使用网络掩码或 CIDR 表示法指定公共 IP 地址范围。这两个规则等效

map dc0 192.168.1.0/24 -> 204.134.75.0/255.255.255.0
map dc0 192.168.1.0/24 -> 204.134.75.0/24

常见做法是将一个公开可访问的 Web 服务器或邮件服务器隔离到内部网络段。来自这些服务器的流量仍然必须经过 NAT,但需要端口重定向才能将入站流量定向到正确的服务器。例如,要将使用内部地址 10.0.10.25 的 Web 服务器映射到其公共 IP 地址 20.20.20.5,请使用以下规则

rdr dc0 20.20.20.5/32 port 80 -> 10.0.10.25 port 80

如果它是唯一的 Web 服务器,则此规则也能正常工作,因为它会将所有外部 HTTP 请求重定向到 10.0.10.25

rdr dc0 0.0.0.0/0 port 80 -> 10.0.10.25 port 80

IPF 具有内置的 FTP 代理,可以与 NAT 一起使用。它会监控所有出站流量,以查找活动或被动 FTP 连接请求,并动态创建包含 FTP 数据通道使用的端口号的临时过滤器规则。这样就无需为 FTP 连接打开大范围的高序端口。

在此示例中,第一个规则调用来自内部 LAN 的出站 FTP 流量的代理。第二个规则将来自防火墙的 FTP 流量传递到互联网,第三个规则处理来自内部 LAN 的所有非 FTP 流量

map dc0 10.0.10.0/29 -> 0/32 proxy port 21 ftp/tcp
map dc0 0.0.0.0/0 -> 0/32 proxy port 21 ftp/tcp
map dc0 10.0.10.0/29 -> 0/32

FTP map 规则位于 NAT 规则之前,以便当数据包与 FTP 规则匹配时,FTP 代理会创建临时过滤器规则,以让 FTP 会话数据包通过并经过 NAT。所有不是 FTP 的 LAN 数据包都不会与 FTP 规则匹配,但如果它们与第三条规则匹配,则会经过 NAT。

如果没有 FTP 代理,则需要以下防火墙规则。请注意,如果没有代理,则需要允许所有高于 1024 的端口

# Allow out LAN PC client FTP to public Internet
# Active and passive modes
pass out quick on rl0 proto tcp from any to any port = 21 flags S keep state

# Allow out passive mode data channel high order port numbers
pass out quick on rl0 proto tcp from any to any port > 1024 flags S keep state

# Active mode let data channel in from FTP server
pass in quick on rl0 proto tcp from any to any port = 20 flags S keep state

每当编辑包含 NAT 规则的文件时,请使用 -CF 运行 ipnat 来删除当前 NAT 规则并刷新动态转换表的內容。包含 -f 并指定要加载的 NAT 规则集的名称

# ipnat -CF -f /etc/ipnat.rules

要显示 NAT 统计信息

# ipnat -s

要列出 NAT 表的当前映射

# ipnat -l

要打开详细模式并显示与规则处理、活动规则和表条目相关的 информацией

# ipnat -v

33.5.5. 查看 IPF 统计信息

IPF 包括 ipfstat(8),可用于检索和显示在数据包匹配规则时通过防火墙收集的统计信息。统计信息从防火墙上次启动或上次使用 ipf -Z 重置为零后开始累积。

默认的 ipfstat 输出如下所示

input packets: blocked 99286 passed 1255609 nomatch 14686 counted 0
 output packets: blocked 4200 passed 1284345 nomatch 14687 counted 0
 input packets logged: blocked 99286 passed 0
 output packets logged: blocked 0 passed 0
 packets logged: input 0 output 0
 log failures: input 3898 output 0
 fragment state(in): kept 0 lost 0
 fragment state(out): kept 0 lost 0
 packet state(in): kept 169364 lost 0
 packet state(out): kept 431395 lost 0
 ICMP replies: 0 TCP RSTs sent: 0
 Result cache hits(in): 1215208 (out): 1098963
 IN Pullups succeeded: 2 failed: 0
 OUT Pullups succeeded: 0 failed: 0
 Fastroute successes: 0 failures: 0
 TCP cksum fails(in): 0 (out): 0
 Packet log flags set: (0)

有几个选项可用。当使用 -i(入站)或 -o(出站)提供时,该命令将检索并显示当前安装并由内核使用的适当过滤器规则列表。要查看规则编号,请包含 -n。例如,ipfstat -on 将显示带有规则编号的出站规则表

@1 pass out on xl0 from any to any
@2 block out on dc0 from any to any
@3 pass out quick on dc0 proto tcp/udp from any to any keep state

包含 -h 以在每个规则前面加上规则匹配次数的计数。例如,ipfstat -oh 将显示出站内部规则表,并在每个规则前面加上其使用次数

2451423 pass out on xl0 from any to any
354727 block out on dc0 from any to any
430918 pass out quick on dc0 proto tcp/udp from any to any keep state

要以类似于 top(1) 的格式显示状态表,请使用 ipfstat -t。当防火墙受到攻击时,此选项可以帮助识别和查看攻击数据包。可选的子标志可以实时选择要监控的目标或源 IP、端口或协议。有关详细信息,请参阅 ipfstat(8)

33.5.6. IPF 日志记录

IPF 提供 ipmon,可用于以人类可读的格式写入防火墙的日志记录信息。它要求首先使用 配置 FreeBSD 内核 中的说明将 options IPFILTER_LOG 添加到自定义内核中。

此命令通常在守护进程模式下运行,以便提供连续的系统日志文件,以便可以查看过去事件的日志记录。由于 FreeBSD 有一个内置的 syslogd(8) 设施来自动旋转系统日志,因此默认的 rc.conf ipmon_flags 语句使用 -Ds

ipmon_flags="-Ds" # D = start as daemon
                  # s = log to syslog
                  # v = log tcp window, ack, seq
                  # n = map IP & port to names

日志记录可以帮助事后查看信息,例如哪些数据包被丢弃,它们来自哪些地址,以及它们将要发送到哪里。此信息有助于追踪攻击者。

rc.conf 中启用日志记录设施并使用 service ipmon start 启动后,IPF 将只记录包含 log 关键字的规则。防火墙管理员决定规则集中哪些规则应被记录,通常只记录拒绝规则。习惯上将 log 关键字包含在规则集中的最后一条规则中。这使得查看与规则集中任何规则都不匹配的所有数据包成为可能。

默认情况下,ipmon -Ds 模式使用 local0 作为日志记录设施。以下日志记录级别可用于进一步细分记录的数据

LOG_INFO - packets logged using the "log" keyword as the action rather than pass or block.
LOG_NOTICE - packets logged which are also passed
LOG_WARNING - packets logged which are also blocked
LOG_ERR - packets which have been logged and which can be considered short due to an incomplete header

要将 IPF 设置为将所有数据记录到 /var/log/ipfilter.log,首先创建空文件

# touch /var/log/ipfilter.log

然后,要将所有记录的消息写入指定文件,请将以下语句添加到 /etc/syslog.conf

local0.* /var/log/ipfilter.log

要激活更改并指示 syslogd(8) 读取修改后的 /etc/syslog.conf,请运行 service syslogd reload

不要忘记编辑 /etc/newsyslog.conf 以旋转新的日志文件。

ipmon 生成的消息由空格分隔的数据字段组成。所有消息中通用的字段是

  1. 数据包接收日期。

  2. 数据包接收时间。格式为 HH:MM:SS.F,表示小时、分钟、秒和小数秒。

  3. 处理数据包的接口名称。

  4. 规则的组号和规则号,格式为 @0:17

  5. 操作:p 表示通过,b 表示阻塞,S 表示短数据包,n 表示与任何规则都不匹配,L 表示日志规则。

  6. 地址以三个字段的形式写出:源地址和端口用逗号分隔,→ 符号,目标地址和端口。例如:209.53.17.22,80 → 198.73.220.17,1722

  7. PR 后跟协议名称或编号:例如,PR tcp

  8. len 后跟数据包的报头长度和总长度:例如,len 20 40

如果数据包是 TCP 数据包,则会有一个额外的字段,以连字符开头,后跟对应于任何已设置标志的字母。有关字母及其标志的列表,请参阅 ipf(5)

如果数据包是 ICMP 数据包,则在最后会有两个字段:第一个始终为“icmp”,下一个为 ICMP 消息和子消息类型,用斜杠分隔。例如:icmp 3/3 表示端口不可达消息。

33.6. Blacklistd

Blacklistd 是一个守护进程,它监听套接字,等待接收来自其他守护进程的通知,这些通知涉及连接尝试失败或成功。它最常用于阻止开放端口上的过多连接尝试。一个典型的例子是运行在互联网上的 SSH 接收来自试图猜测密码并获取访问权限的机器人或脚本的许多请求。使用 blacklistd,守护进程可以通知防火墙创建过滤规则,以在尝试次数过多后阻止来自单个来源的过多连接尝试。Blacklistd 最初是在 NetBSD 上开发的,并在 NetBSD 7 版本中出现。FreeBSD 11 从 NetBSD 导入 blacklistd。

本章介绍如何设置 blacklistd,配置它,并提供有关如何使用它的示例。读者应该熟悉基本的防火墙概念,例如规则。有关详细信息,请参阅防火墙章节。示例中使用 PF,但 FreeBSD 上的其他防火墙也应该能够与 blacklistd 协同工作。

33.6.1. 启用 Blacklistd

blacklistd 的主要配置存储在 blacklistd.conf(5) 中。还提供各种命令行选项来更改 blacklistd 的运行时行为。跨重启的持久配置应存储在 /etc/blacklistd.conf 中。要在系统启动时启用守护进程,请将 blacklistd_enable 行添加到 /etc/rc.conf 中,如下所示

# sysrc blacklistd_enable=yes

要手动启动服务,请运行以下命令

# service blacklistd start

33.6.2. 创建 Blacklistd 规则集

blacklistd 的规则在 blacklistd.conf(5) 中配置,每行一个条目。每条规则包含一个由空格或制表符分隔的元组。规则要么属于 local,要么属于 remote,分别适用于运行 blacklistd 的机器或外部来源。

33.6.2.1. 本地规则

本地规则的 blacklistd.conf 条目示例如下所示

[local]
ssh             stream  *       *               *       3       24h

所有遵循 [local] 部分的规则都被视为本地规则(这是默认设置),适用于本地机器。当遇到 [remote] 部分时,所有遵循它的规则都将被视为远程机器规则。

七个字段由制表符或空格分隔来定义一条规则。前四个字段标识应该被列入黑名单的流量。接下来的三个字段定义 blacklistd 的行为。通配符用星号 (*) 表示,匹配此字段中的任何内容。第一个字段定义位置。在本地规则中,这些是网络端口。位置字段的语法如下

[address|interface][/mask][:port]

地址可以指定为 IPv4 数字格式或 IPv6 方括号。还可以使用像 em0 这样的接口名称。

套接字类型由第二个字段定义。TCP 套接字类型为 stream,而 UDP 表示为 dgram。上面的示例使用 TCP,因为 SSH 正在使用该协议。

协议可以在 blacklistd 规则的第三个字段中使用。可以使用以下协议:tcpudptcp6udp6 或数字。除非有理由按特定协议区分流量,否则通常会使用通配符(如示例中所示)来匹配所有协议。

在第四个字段中,定义了报告事件的守护进程的有效用户或所有者。用户名或 UID 可以在这里使用,通配符也可以使用(参见上面的示例规则)。

数据包过滤器规则名称由第五个字段声明,该字段开始规则的行为部分。默认情况下,blacklistd 将所有阻塞放在 pf.conf 中名为 blacklistd 的 pf 锚点下,如下所示

anchor "blacklistd/*" in on $ext_if
block in
pass out

对于单独的黑名单,可以在此字段中使用锚点名称。在其他情况下,通配符就足够了。当名称以连字符 (-) 开头时,表示应该使用默认规则名称作为前缀的锚点。上面的修改示例使用连字符,如下所示

ssh             stream  *       *               -ssh       3       24h

使用这样的规则,任何新的黑名单规则都会被添加到名为 blacklistd-ssh 的锚点中。

要对单次规则违规阻塞整个子网,可以在规则名称中使用 /。这会导致名称的剩余部分被解释为要应用于规则中指定的地址的掩码。例如,此规则将阻塞与 /24 相邻的每个地址。

22              stream  tcp       *               */24    3       24h

在 اینجا ذکر کردن پروتکل مناسب مهم است. IPv4 و IPv6 با /24 متفاوت رفتار می کنند، به همین دلیل است که * را نمی توان در این قاعده در فیلد سوم استفاده کرد.

这条规则定义了如果该网络中的任何一台主机行为不端,该网络中的所有其他主机也将被阻塞。

第六个字段称为 nfail,它设置了需要阻止远程 IP 的登录失败次数。当在此位置使用通配符时,表示永远不会发生阻塞。在上面的示例规则中,定义了三个的限制,这意味着在对一个连接上的 SSH 进行三次登录尝试后,IP 将被阻止。

blacklistd 规则定义中的最后一个字段指定主机被列入黑名单的时间。默认单位是秒,但也可以指定 mhd 等后缀,分别表示分钟、小时和天。

这条规则的全部含义是,三次对 SSH 进行身份验证后,将导致对该主机的新的 PF 阻止规则。规则匹配是通过首先从最具体到最不具体的检查本地规则来执行的。当发生匹配时,将应用 remote 规则,并且名称、nfail 和 disable 字段将由匹配的 remote 规则更改。

33.6.2.2. 远程规则

远程规则用于指定 blacklistd 如何根据当前正在评估的远程主机更改其行为。远程规则中的每个字段与本地规则中的字段相同。唯一的区别在于 blacklistd 使用它们的方式。为了解释这一点,使用以下示例规则

[remote]
203.0.113.128/25 *      *       *               =/25    =       48h

地址字段可以是 IP 地址(v4 或 v6)、端口或两者。这允许为特定的远程地址范围设置特殊规则,如本示例所示。套接字类型、协议和所有者字段的解释与本地规则中的解释相同。

但是,名称字段有所不同:远程规则中的等号 (=) 告诉 blacklistd 使用匹配的本地规则中的值。这意味着将获取防火墙规则条目,并将 /25 前缀(255.255.255.128 的网络掩码)添加到其中。当来自该地址范围的连接被列入黑名单时,整个子网都会受到影响。您也可以在这里使用 PF 锚点名称,在这种情况下,blacklistd 将为该地址块添加到该名称的锚点中。当指定通配符时,将使用默认表。

可以为地址定义 nfail 列中的自定义失败次数。这对于特定规则的例外情况很有用,例如允许某人更宽松地应用规则或在登录尝试方面更宽容一些。当在第六个字段中使用星号时,将禁用阻止。

远程规则允许对登录尝试的限制进行更严格的执行,与来自本地网络(如办公室)的尝试相比。

33.6.3. Blacklistd 客户端配置

FreeBSD 中有一些软件包可以利用 blacklistd 的功能。最突出的两个是 ftpd(8)sshd(8),用于阻止过多的连接尝试。要在 SSH 守护进程中激活 blacklistd,请将以下行添加到 /etc/ssh/sshd_config

UseBlacklist yes

之后重新启动 sshd 以使这些更改生效。

使用 -B 启用 ftpd(8) 的黑名单,无论是在 /etc/inetd.conf 中,还是在 /etc/rc.conf 中作为标志,如下所示

ftpd_flags="-B"

这足以使这些程序与 blacklistd 通信。

33.6.4. Blacklistd 管理

Blacklistd 提供了一个名为 blacklistctl(8) 的管理实用程序。它显示被阻止的地址和网络,这些地址和网络被 blacklistd.conf(5) 中定义的规则列入黑名单。要查看当前被阻止的主机列表,请使用 dump-b 组合,如下所示。

# blacklistctl dump -b
      address/ma:port id      nfail   last access
213.0.123.128/25:22   OK      6/3     2019/06/08 14:30:19

此示例显示,来自地址范围 213.0.123.128/25 的 22 号端口有 6 次尝试,而允许的尝试次数只有 3 次。尝试次数多于允许次数,因为 SSH 允许客户端在单个 TCP 连接上尝试多个登录。当前正在进行的连接不会被 blacklistd 停止。最后一次连接尝试列在输出的 last access 列中。

要查看此主机在黑名单上的剩余时间,请在之前的命令中添加 -r

# blacklistctl dump -br
      address/ma:port id      nfail   remaining time
213.0.123.128/25:22   OK      6/3     36s

在此示例中,此主机将在 36 秒后不再被阻止。

33.6.5. 从黑名单中删除主机

有时需要在剩余时间到期之前从黑名单中删除主机。不幸的是,blacklistd 中没有这样的功能。但是,可以使用 pfctl 从 PF 表中删除地址。对于每个被阻止的端口,在 /etc/pf.conf 中定义的 blacklistd 锚点内都有一个子锚点。例如,如果有一个用于阻止 22 号端口的子锚点,它将被称为 blacklistd/22。该子锚点内有一个包含被阻止地址的表。该表被称为端口,后面跟着端口号。在本例中,它将被称为 port22。有了这些信息,现在可以使用 pfctl(8) 以如下方式显示所有列出的地址

# pfctl -a blacklistd/22 -t port22 -T show
...
213.0.123.128/25
...

识别要从列表中解除阻止的地址后,以下命令将其从列表中删除

# pfctl -a blacklistd/22 -t port22 -T delete 213.0.123.128/25

该地址现在已从 PF 中删除,但仍会在 blacklistctl 列表中显示,因为它不知道在 PF 中进行的任何更改。blacklistd 数据库中的条目最终会过期并从其输出中删除。如果主机再次匹配 blacklistd 中的某个阻止规则,则该条目将再次添加。


上次修改时间:2024 年 9 月 24 日,由 Wolfram Schneider 修改