如何在FreeBSD12.1上配置包过滤器(PF)

来自菜鸟教程
跳转至:导航、​搜索

作者选择了 COVID-19 Relief Fund 作为 Write for DOnations 计划的一部分来接受捐赠。

介绍

防火墙可以说是抵御网络攻击的最重要的防线之一。 从头开始配置防火墙的能力是一种授权技能,使管理员能够控制他们的网络。

Packet Filter (PF) 是一个著名的防火墙应用程序,由安全驱动的 OpenBSD 项目在上游维护。 它更准确地表示为一种数据包过滤工具,因此得名,并以其简单的语法、用户友好性和广泛的功能而闻名。 默认情况下,PF 是一个状态防火墙,将有关连接的信息存储在 状态表 中,可以访问以进行分析。 PF 是 FreeBSD 基础系统的一部分,并得到强大的开发人员社区的支持。 尽管 FreeBSD 和 OpenBSD 版本的 PF 在内核架构方面存在差异,但总的来说它们的语法是相似的。 根据它们的复杂性,可以修改通用规则集以相对较少的工作在任一发行版上工作。

在本教程中,您将在带有 PF 的 FreeBSD 12.1 服务器上从头开始构建防火墙。 您将设计一个可用作未来项目模板的基本规则集。 您还将探索 PF 的一些高级功能,例如数据包卫生、暴力预防、监控和日志记录以及其他第三方工具。

先决条件

在开始本教程之前,您需要以下内容:

  • 1G FreeBSD 12.1 服务器(ZFSUFS)。 您可以使用我们的 如何开始使用 FreeBSD 教程 将您的服务器设置为您喜欢的配置。
  • FreeBSD 默认没有启用防火墙——定制是 FreeBSD 精神的标志。 因此,当您第一次启动服务器时,您需要在配置 PF 时进行临时保护。 如果您使用的是 DigitalOcean,您可以在启动服务器后立即启用云防火墙。 有关配置云防火墙的说明,请参阅 DigitalOcean 的 Firewall Quickstart。 如果您使用的是其他云提供商,请在开始之前确定获得即时保护的最快途径。 无论您选择哪种方法,您的临时防火墙都必须只允许入站 SSH 流量,并且可以允许所有类型的出站流量。

第 1 步 - 建立您的初步规则集

您将通过起草一个初步规则集开始本教程,该规则集提供基本保护和对来自 Internet 的关键服务的访问。 此时,您拥有一个正在运行的带有活动云防火墙的 FreeBSD 12.1 服务器。

构建防火墙有两种方法:默认拒绝默认允许。 默认拒绝方法会阻止所有流量,并且只允许规则中指定的内容。 默认的 permit 方法正好相反:它通过所有流量,并且只阻止规则中指定的内容。 您将使用默认拒绝方法。

PF 规则集写在一个名为 /etc/pf.conf 的配置文件中,这也是它的默认位置。 只要在 /etc/rc.conf 配置文件中指定,可以将此文件存储在其他位置。 在本教程中,您将使用默认位置。

使用非 root 用户登录到您的服务器:

ssh freebsd@your_server_ip

接下来创建您的 /etc/pf.conf 文件:

sudo vi /etc/pf.conf

注意:如果您想在教程的任何时候查看完整的基本规则集,可以参考Step 4Step 8中的示例。


PF 根据三个核心动作过滤数据包:blockpassmatch。 当与其他选项结合使用时,它们会形成规则。 当数据包满足规则中指定的标准时,将采取行动。 如您所料,passblock 规则将 passblock 流量。 match 规则在找到匹配条件时对数据包执行操作,但不会通过或阻止它。 例如,您可以对匹配的数据包执行 网络地址转换 (NAT),而不通过或阻止它,它会一直待在那里,直到您告诉它在另一个规则中执行某些操作,例如将其路由到另一个机器或网关。

接下来将第一条规则添加到您的 /etc/pf.conf 文件中:

/etc/pf.conf

block all

此规则阻止各个方向的所有形式的交通。 由于它没有指定方向,因此默认为 inout。 该规则对于需要与外界隔离的本地工作站是合法的,但它在很大程度上是不切实际的,并且在远程服务器上不起作用,因为它不允许 SSH 通信。 事实上,如果你启用了 PF,你就会把自己锁在服务器之外。

修改您的 /etc/pf.conf 文件以允许使用以下突出显示的行进行 SSH 通信:

/etc/pf.conf

block all
pass in proto tcp to port 22

注: 或者,您可以使用协议的名称:

/etc/pf.conf

block all
pass in proto tcp to port ssh

为了保持一致性,我们将使用端口号,除非有正当理由不这样做。 /etc/services 文件中有详细的协议列表及其各自的端口号,鼓励您查看。


PF 从上到下按顺序处理规则,因此您当前的规则集最初会阻止所有流量,但如果下一行的条件匹配则通过它,在这种情况下是 SSH 流量。

您现在可以通过 SSH 连接到您的服务器,但您仍会阻止所有形式的出站流量。 这是有问题的,因为您无法从 Internet 访问关键服务来安装软件包、更新时间设置等。

要解决此问题,请将以下突出显示的规则附加到 /etc/pf.conf 文件的末尾:

/etc/pf.conf

block all
pass in proto tcp to port { 22 }
pass out proto { tcp udp } to port { 22 53 80 123 443 }

您的规则集现在允许出站 SSHDNSHTTPNTP 和 HTTPS 流量,以及阻止所有入站流量,( SSH 除外)。 您将端口号和协议放在大括号内,形成一个 PF 语法列表,允许您在需要时添加更多端口号。 您还可以在端口 53123 上为 UDP 协议添加传递规则,因为 DNS 和 NTP 经常在 TCP 和 UDP 协议之间切换。 您几乎完成了初步规则集,只需添加几个规则即可实现基本功能。

使用突出显示的规则完成初步规则集:

初步规则集 /etc/pf.conf

set skip on lo0
block all
pass in proto tcp to port { 22 }
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass out inet proto icmp icmp-type { echoreq }

保存并退出文件。

您为环回设备创建了 set skip 规则,因为它不需要过滤流量并且可能会使您的服务器陷入爬网状态。 您为 ICMP 协议添加了 pass out inet 规则,它允许您使用 ping(8) 实用程序进行故障排除。 inet 选项代表 IPv4 地址族。

ICMP 是网络设备用于各种类型通信的多用途消息传递协议。 例如,ping 实用程序使用一种称为 回显请求 的消息,您已将其添加到 icmp_type 列表中。 作为预防措施,您只允许防止不受欢迎的设备联系您的服务器所需的消息类型。 随着您的需求增加,您可以将更多消息类型添加到您的列表中。

您现在有一个为大多数机器提供基本功能的工作规则集。 在下一部分中,让我们通过启用 PF 并测试您的初步规则集来确认一切正常。

第 2 步 — 测试您的初步规则集

在这一步中,您将测试您的初步规则集,并从您的云防火墙过渡到您的 PF 防火墙,让 PF 完全接管。 您将使用 pfctl 实用程序激活您的规则集,它是 PF 的内置命令行工具,也是与 PF 交互的主要方法。

PF 规则集只不过是文本文件,这意味着加载新规则集不涉及任何微妙的过程。 您可以加载新规则集,而旧规则集已消失。 很少(如果有的话)需要刷新现有的规则集。

FreeBSD 使用称为 rc 系统 的 shell 脚本网络来管理服务在启动时的启动方式; 我们在各种 rc 配置文件中指定这些服务。 对于 PF 等全局服务,您使用 /etc/rc.conf 文件。 由于 rc 文件对 FreeBSD 系统的运行至关重要,因此不应直接编辑它们。 相反,FreeBSD 提供了一个名为 sysrc 的命令行实用程序,旨在帮助您安全地编辑这些文件。

让我们使用 sysrc 命令行实用程序启用 PF:

sudo sysrc pf_enable="YES"
sudo sysrc pflog_enable="YES"

通过打印 /etc/rc.conf 文件的内容来验证这些更改:

sudo cat /etc/rc.conf

您将看到以下输出:

Outputpf_enable="YES"
pflog_enable="YES"

您还启用了 pflog 服务,这反过来又启用了 pflogd 守护进程以登录 PF。(您将在后面的步骤中使用登录。

您在 /etc/rc.conf 文件中指定了两个全局服务,但在您重新启动服务器或手动启动它们之前,它们不会初始化。 重新启动服务器,以便您还可以测试您的 SSH 访问。

通过重新启动服务器来启动 PF:

sudo reboot

连接将被断开。 给它几分钟来更新。

现在 SSH 回到服务器:

ssh freebsd@your_server_ip

虽然你已经初始化了你的 PF 服务,但你实际上还没有加载你的 /etc/pf.conf 规则集,这意味着你的防火墙还没有激活。

使用 pfctl 加载规则集:

sudo pfctl -f /etc/pf.conf

如果没有错误或消息,则意味着您的规则集没有错误并且防火墙处于活动状态。

现在 PF 正在运行,您可以将服务器与云防火墙分离。 这可以在您的 DigitalOcean 帐户中的 控制面板 上完成,方法是从您的云防火墙门户中删除您的 Droplet。 如果您使用其他云提供商,请确保禁用您用于临时保护的任何内容。 在服务器上运行两个不同的防火墙几乎肯定会导致问题。

为了更好地衡量,请再次重新启动您的服务器:

sudo reboot

几分钟后,SSH 回到您的服务器:

ssh freebsd@your_server_ip

PF 现在是你的代理防火墙。 您可以通过使用 pfctl 实用程序访问一些数据来确保它正在运行。

让我们用 pfctl -si 查看一些统计数据和计数器:

sudo pfctl -si

你传递了 -si 标志,它代表 显示信息 。 这是您可以与 pfctl 一起使用的众多过滤器参数组合之一,以解析有关您的防火墙活动的数据。

您将看到以下表格数据(值因机器而异):

OutputStatus: Enabled for 0 days 00:01:53           Debug: Urgent

State Table                          Total             Rate
  current entries                        5
  searches                             144            1.3/s
  inserts                               11            0.1/s
  removals                               6            0.1/s
Counters
  match                                 23            0.2/s
  bad-offset                             0            0.0/s
  fragment                               0            0.0/s
  short                                  0            0.0/s
  normalize                              0            0.0/s
  memory                                 0            0.0/s
  bad-timestamp                          0            0.0/s
  congestion                             0            0.0/s
  ip-option                              0            0.0/s
  proto-cksum                            0            0.0/s
  state-insert                           0            0.0/s
  state-limit                            0            0.0/s
  src-limit                              0            0.0/s
  synproxy                               0            0.0/s
  map-failed                             0            0.0/s

由于您刚刚激活了规则集,因此您还不会看到很多信息。 然而,这个输出显示 PF 已经记录了 23 个匹配的规则,这意味着您的规则集的条件匹配了 23 次。 输出还确认您的防火墙正在工作。

您的规则集还允许出站流量从 Internet 访问一些关键服务,包括 ping 实用程序。

让我们通过 ping google.com 检查互联网连接和 DNS 服务:

ping -c 3 google.com

由于您运行了计数标志 -c 3,您将看到三个成功的连接响应:

OutputPING google.com (172.217.0.46): 56 data bytes
64 bytes from 172.217.0.46: icmp_seq=0 ttl=56 time=2.088 ms
64 bytes from 172.217.0.46: icmp_seq=1 ttl=56 time=1.469 ms
64 bytes from 172.217.0.46: icmp_seq=2 ttl=56 time=1.466 ms

--- google.com ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.466/1.674/2.088/0.293 ms

确保您可以使用以下命令访问 pkgs 存储库:

sudo pkg upgrade

如果有任何要升级的软件包,请继续升级它们。

如果这两个服务都在工作,这意味着您的防火墙正在工作,您现在可以继续。 尽管您的初步规则集提供了保护和功能,但它仍然是一个基本规则集,并且可以使用一些增强功能。 在其余部分中,您将完成基本规则集,并使用 PF 的一些高级功能。

第 3 步 — 完成您的基本规则集

在此步骤中,您将构建初步规则集以完成您的基本规则集。 您将重新组织一些规则并使用更高级的概念。

合并宏和表

在您的初步规则集中,您将所有参数硬编码到每个规则中,即构成列表的端口号。 这在未来可能会变得难以管理,具体取决于您的网络的性质。 出于组织目的,PF 包括 macrosliststables。 您已经将列表直接包含在规则中,但您也可以将它们与规则分开,并使用宏将它们分配给变量。

打开文件以将一些参数传输到宏中:

sudo vi /etc/pf.conf

现在将以下内容添加到规则集的最顶部:

/etc/pf.conf

vtnet0 = "vtnet0"
icmp_types = "{ echoreq }"
. . .

使用新变量修改之前的 SSH 和 ICMP 规则:

/etc/pf.conf

. . .
pass in on $vtnet0 proto tcp to port { 22 }
. . .
pass inet proto icmp icmp-type $icmp_types
. . .

您以前的 SSH 和 ICMP 规则现在使用宏。 变量名由 PF 的美元符号语法表示。 您将 vtnet0 接口分配给具有相同名称的变量,这只是一种形式,如果需要,您可以选择在将来重命名它。 面向公众的接口的其他常见变量名称包括 $pub_if$ext_if

接下来,您将实现一个 table,它类似于 macro,但设计用于保存 IP 地址组。 让我们为不可路由的 IP 地址创建一个表,这些地址通常在 拒绝服务攻击 (DOS) 中发挥作用。 您可以使用 RFC6890 中指定的 IP 地址,它定义了专用 IP 地址注册表。 您的服务器不应通过面向公众的接口向或从这些地址发送或接收数据包。

通过直接在 icmp_types 宏下添加以下内容来创建此表:

/etc/pf.conf

. . .
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }
. . .

现在在 set skip on lo0 规则下添加 <rfc6890> 表的规则:

/etc/pf.conf

. . .
set skip on lo0
block in quick on egress from <rfc6890>
block return out quick on egress to <rfc6890>
. . .

在这里,您介绍了 return 选项,它补充了您的 block out 规则。 这将丢弃数据包并向尝试建立这些连接的主机发送 RST 消息 ,这对于分析主机活动很有用。 然后,添加 egress 关键字,它会自动在任何给定接口上找到 默认路由 。 这通常是查找默认路由的一种更简洁的方法,尤其是对于复杂的网络。 quick 关键字立即执行规则,而不考虑规则集的其余部分。 例如,如果 IP 地址不合逻辑的数据包尝试连接到服务器,您希望立即断开连接,并且没有理由通过规则集的其余部分运行该数据包。

保护您的 SSH 端口

由于您的 SSH 端口对公众开放,因此很容易被利用。 攻击者最明显的警告信号之一是大量的登录尝试。 例如,如果同一 IP 地址在一秒钟内尝试登录您的服务器十次,您可以假设这不是人手完成的,而是使用试图破解您的登录密码的计算机软件完成的。 这些类型的系统性攻击通常被称为 暴力 攻击,如果服务器密码较弱,通常会成功。

警告: 我们强烈建议在所有服务器上使用公钥认证。 请参阅 DigitalOcean 的 基于密钥的身份验证 的教程。


PF 具有处理蛮力和其他类似攻击的内置功能。 使用 PF,您可以限制单个主机允许的同时连接尝试次数。 如果主机超过这些限制,连接将被断开,并且它们将被禁止访问服务器。 为此,您将使用 PF 的 过载机制 ,该机制维护一个禁止 IP 地址表。

修改您之前的 SSH 规则以限制来自单个主机的同时连接数,如下所示:

/etc/pf.conf

. . .
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
. . .

添加 keep state 选项,允许您定义过载表的状态标准。 您传递 max-src-conn 参数来指定每秒允许来自单个主机的同时连接数,并传递 max-src-conn-rate 参数来指定每秒允许来自单个主机的新连接数。 您为 max-src-conn 指定 15 连接,为 max-src-conn-rate 指定 3 连接。 如果主机超出这些限制,overload 机制会将源 IP 添加到 <bruteforce> 表中,从而禁止它们进入服务器。 最后,flush global 选项会立即断开连接。

您已在 SSH 规则中定义了一个重载表,但尚未在您的规则集中声明该表。

icmp_types 宏下面添加 <bruteforce> 表:

/etc/pf.conf

. . .
icmp_types = "{ echoreq }"
table <bruteforce> persist
. . .

persist 关键字允许空表存在于规则集中。 没有它,PF 会抱怨表中没有 IP 地址。

这些措施确保您的 SSH 端口受到强大的安全机制的保护。 PF 允许您配置快速解决方案以防止灾难性的利用形式。 在接下来的部分中,您将采取步骤在数据包到达您的服务器时对其进行清理。

清理您的流量

注意: 以下部分描述了 TCP/IP 协议组的基本原理。 如果您计划构建 Web 应用程序或网络,那么掌握这些概念符合您的最大利益。 查看 DigitalOcean 的 网络术语、接口和协议简介 教程。


由于 TCP/IP 协议套件的复杂性以及恶意行为者的持久性,数据包通常会以不一致和模糊的方式到达,例如重叠的 IP 片段、虚假的 IP 地址等。 您必须在流量进入系统之前对其进行清理。 这个过程的技术术语是normalization

当数据通过 Internet 传输时,通常会在其源处将其分解为较小的片段,以适应目标主机的传输参数,然后将其重新组装成完整的数据包。 不幸的是,入侵者可以通过超出本教程范围的多种方式劫持此过程。 但是,使用 PF,您可以使用一条规则管理碎片。 PF 包含一个 scrub 关键字,用于规范化数据包。

block all 规则之前直接添加 scrub 关键字:

/etc/pf.conf

. . .
set skip on lo0
scrub in all fragment reassemble max-mss 1440
block all
. . .

此规则将清理应用于所有传入流量。 您包括防止碎片进入系统的 fragment reassemble 选项。 相反,它们被缓存在内存中,直到它们重新组合成完整的数据包,这意味着您的过滤规则只需与统一的数据包竞争。 您还包括 max-mss 1440 选项,它表示重组 TCP 数据包的 最大段大小 ,也称为 有效负载 。 您指定一个 1440 字节的值,这会在大小和性能之间取得平衡,为标头留出足够的空间。

分段的另一个重要方面是称为 最大传输单元 (MTU) 的术语。 TCP/IP 协议使设备能够协商数据包大小以建立连接。 目标主机使用 ICMP 消息通知其 MTU 的源 IP,该过程称为 MTU 路径发现 。 具体的 ICMP 消息类型是 destination unreachable。 您将通过将 unreach 消息类型添加到 icmp_types 列表来启用 MTU 路径发现。

您将使用服务器的默认 MTU 1500 字节,可以使用 ifconfig 命令确定:

ifconfig

您将看到以下输出,其中包含您当前的 MTU:

Outputvtnet0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=6c07bb<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,TSO4,TSO6,LRO,VLAN_HWTSO,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
. . .

更新 icmp_types 列表以包含 destination unreachable 消息类型:

/etc/pf.conf

vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach}"
. . .

现在您已经制定了处理碎片的策略,进入系统的数据包将是统一且一致的。 这是可取的,因为有如此多的设备通过互联网交换数据。

您现在将努力防止另一个称为 IP 欺骗 的安全问题。 攻击者经常更改其源 IP,使其看起来好像位于组织内的受信任节点上。 PF 包含一个 antispoofing 指令,用于处理欺骗的源 IP。 当应用于特定接口时,反欺骗会阻止来自该接口网络的所有流量(除非它源自该接口)。 例如,如果您对位于 5.5.5.1/24 的接口应用反欺骗,则来自 5.5.5.0/24 网络的所有流量都无法与系统通信,除非它源自该接口。

添加以下突出显示的内容以对您的 vtnet0 界面应用反欺骗:

/etc/pf.conf

. . .
set skip on lo0
scrub in
antispoof quick for $vtnet0
block all
. . .

保存并退出文件。

该反欺骗规则表示来自 vtnet0 网络的所有流量只能通过 vtnet0 接口,否则将立即使用 quick 关键字丢弃。 不良行为者将无法隐藏在 vtnet0 的网络中并与其他节点通信。

为了演示您的反欺骗规则,您将以详细的形式将规则集打印到屏幕上。 PF 中的规则通常以缩写形式编写,但也可以以详细形式编写。 以这种方式编写规则通常是不切实际的,但出于测试目的,它可能很有用。

使用 pfctl 和以下命令打印 /etc/pf.conf 的内容:

sudo pfctl -nvf /etc/pf.conf

这个 pfctl 命令采用 -nvf 标志,它打印规则集并在不实际加载任何内容的情况下对其进行测试,也称为 试运行 。 您现在将以详细的形式看到 /etc/pf.conf 的全部内容。

您将在反欺骗部分看到类似于以下输出的内容:

Output. . .
block drop in quick on ! vtnet0 inet from your_server_ip/20 to any
block drop in quick on ! vtnet0 inet from network_address/16 to any
block drop in quick inet from your_server_ip to any
block drop in quick inet from network_address to any
block drop in quick on vtnet0 inet6 from your_IPv6_address to any
. . .

您的反欺骗规则发现它是 your_server_ip/20 网络的一部分。 它还检测到(对于本教程的示例)服务器是 network_address/16 网络的一部分,并且有一个额外的 IPv6 地址。 反欺骗阻止所有这些网络与系统通信,除非它们的流量通过 vtnet0 接口。

您的反欺骗规则是基本规则集中的最后一个补充。 在下一步中,您将启动这些更改并执行一些测试。

第 4 步 — 测试您的基本规则集

在此步骤中,您将检查并测试您的基本规则集,以确保一切正常运行。 最好避免在未测试的情况下一次实施太多规则。 最佳实践是从基本要素开始,逐步扩展,并在进行配置更改时备份工作。

这是您的完整基本规则集:

基本规则集 /etc/pf.conf

vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach }"
table <bruteforce> persist
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }

set skip on lo0
scrub in all fragment reassemble max-mss 1440
antispoof quick for $vtnet0
block in quick on $vtnet0 from <rfc6890>
block return out quick on egress to <rfc6890>
block all
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass inet proto icmp icmp-type $icmp_types

在继续之前,请确保您的 /etc/pf.conf 文件与此处的完整基本规则集相同。 然后保存并退出文件。

您的完整基本规则集为您提供:

  • 可以定义关键服务和设备的宏集合。
  • 解决数据包碎片和不合逻辑的 IP 地址的网络卫生策略。
  • 默认拒绝 过滤结构,可阻止所有内容并仅允许您指定的内容。
  • 入站 SSH 访问限制了主机可以同时建立的连接数。
  • 出站流量策略使您可以从 Internet 访问某些关键服务。
  • 提供对 ping 实用程序和 MTU 路径发现的访问权限的 ICMP 策略。

运行以下 pfctl 命令进行试运行:

sudo pfctl -nf /etc/pf.conf

您传递 -nf 标志告诉 pfctl 在不加载它的情况下运行规则集,如果出现任何错误,这将引发错误。

现在,没有遇到错误,加载规则集:

sudo pfctl -f /etc/pf.conf

如果没有错误,则表示您的基本规则集处于活动状态且运行正常。 如本教程前面所述,您将对规则集执行一些测试。

互联网连接和 DNS 服务的首次测试:

ping -c 3 google.com

您将看到以下输出:

OutputPING google.com (172.217.0.46): 56 data bytes
64 bytes from 172.217.0.46: icmp_seq=0 ttl=56 time=2.088 ms
64 bytes from 172.217.0.46: icmp_seq=1 ttl=56 time=1.469 ms
64 bytes from 172.217.0.46: icmp_seq=2 ttl=56 time=1.466 ms

--- google.com ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.466/1.674/2.088/0.293 ms

然后,检查您是否到达 pkgs 存储库:

sudo pkg upgrade

如果需要,再次升级软件包。

最后,重启你的服务器:

sudo reboot

给你的服务器几分钟重新启动。 您已经完成并实施了基本规则集,这是您进步的重要一步。 您现在已准备好探索 PF 的一些高级功能。 在下一步中,您将继续防止暴力攻击。

第 5 步 — 管理您的重载表

随着时间的推移,<bruteforce> 过载表将充满恶意 IP 地址,需要定期清除。 攻击者不太可能继续使用相同的 IP 地址,因此将它们长时间存储在过载表中是违反直觉的。

您将使用 pfctl 通过以下命令手动清除已存储在过载表中 48 小时或更长时间的 IP 地址:

sudo pfctl -t bruteforce -T expire 172800

您将看到类似于以下内容的输出:

Output0/0 addresses expired.

你传递了 -t bruteforce 标志,它代表 table bruteforce-T 标志,它可以让你运行一些内置命令。 在这种情况下,您运行 expire 命令以清除 -t bruteforce 中的所有条目,时间值以秒为单位。 由于您正在使用新服务器,因此过载表中可能还没有 IP 地址。

此规则适用于快速修复,但更强大的解决方案是使用 FreeBSD 的作业调度程序 cron 自动执行该过程。 让我们创建一个运行此命令序列的 shell 脚本。

/usr/local/bin目录下创建一个shell脚本文件:

sudo vi /usr/local/bin/clear_overload.sh

将以下内容添加到 shell 脚本中:

/usr/local/bin/clear_overload.sh

#!/bin/sh

pfctl -t bruteforce -T expire 172800

使用以下命令使文件可执行:

sudo chmod 755 /usr/local/bin/clear_overload.sh

接下来,您将创建一个 cron 作业。 这些作业将根据您指定的时间重复运行。 它们通常用于备份或需要每天在同一时间运行的任何进程。 您使用 crontab 文件创建 cron 作业。 请参阅手册页以了解有关 cron(8)crontab(5) 的更多信息。

使用以下命令创建 root 用户 crontab 文件:

sudo crontab -e

现在将以下内容添加到 crontab 文件中:

crontab

# minute hour    mday    month   wday    command

  *             0     *       *     *     /usr/local/bin/clear_overload.sh

保存并退出文件。

注意: 如果在添加内容时没有正确对齐,请将每个值与其对应的表格条目对齐以提高可读性。


cron 作业 每天午夜运行 clear_overload.sh 脚本,从 过载表 <bruteforce> 中删除 48 小时前的 IP 地址。 接下来,您将向规则集中添加锚点。

第 6 步——在规则集中引入锚点

在这一步中,您将介绍 anchors,它们用于将规则手动或从外部文本文件中获取到主规则集中。 锚点可以包含规则片段、表格,甚至其他锚点,称为 嵌套锚点。 让我们通过将表添加到外部文件并将其采购到您的基本规则集中来演示锚点的工作原理。 您的表将包含一组您希望阻止其连接到外部世界的内部主机。

创建一个名为 /etc/blocked-hosts-anchor 的文件:

sudo vi /etc/blocked-hosts-anchor

将以下内容添加到文件中:

/etc/blocked-hosts-anchor

table <blocked-hosts> { 192.168.47.1 192.168.47.2 192.168.47.3 }

block return out quick on egress from <blocked-hosts>

保存并退出文件。

这些规则声明并定义了<blocked-hosts>表,然后阻止<blocked-hosts>表中的每个IP地址访问外部世界的服务。 您使用 egress 关键字作为查找到 Internet 的默认路由或出路的首选方法。

您仍然需要在 /etc/pf.conf 文件中声明锚点:

sudo vi /etc/pf.conf

现在在 block all 规则之后添加以下锚定规则:

/etc/pf.conf

. . .
block all
anchor blocked_hosts
load anchor blocked_hosts from "/etc/blocked-hosts-anchor"
. . .

保存并退出文件。

这些规则声明 blocked_hosts 并将锚定规则从 /etc/blocked-hosts-anchor 文件加载到您的主规则集中。

现在通过使用 pfctl 重新加载您的规则集来启动这些更改:

sudo pfctl -f /etc/pf.conf

如果没有错误,则意味着您的规则集中没有错误并且您的更改处于活动状态。

使用 pfctl 验证您的锚点是否正在运行:

sudo pfctl -s Anchors

-s Anchors 标志代表“显示锚点”。 您将看到以下输出:

Outputblocked_hosts

pfctl 实用程序还可以使用 -a-s 标志解析锚的特定规则:

sudo pfctl -a blocked_hosts -s rules

您将看到以下输出:

Outputblock return out quick on egress from <blocked-hosts> to any

锚点的另一个特点是它们允许您按需添加规则,而无需重新加载规则集。 这对于测试、快速修复、紧急情况等很有用。 例如,如果内部主机行为异常,并且您想阻止它进行外部连接,则可以设置一个锚点,使您可以从命令行快速干预。

让我们打开 /etc/pf.conf 并添加另一个锚点:

sudo vi /etc/pf.conf

您将锚点命名为 rogue_hosts,并将其放在 block all 规则中:

/etc/pf.conf

. . .
block all
anchor rogue_hosts
. . .

保存并退出文件。

要启动这些更改,请使用 pfctl 重新加载规则集:

sudo pfctl -f /etc/pf.conf

再次使用 pfctl 验证锚点是否正在运行:

sudo pfctl -s Anchors

这将生成以下输出:

Outputblocked_hosts
rogue_hosts

现在锚点正在运行,您可以随时向其添加规则。 通过添加以下规则对此进行测试:

sudo sh -c 'echo "block return out quick on egress from 192.168.47.4" | pfctl -a rogue_hosts -f -'

这会调用 echo 命令及其字符串内容,然后通过 | 符号将其通过管道传送到 pfctl 实用程序,在此将其处理为锚定规则。 您使用 sh -c 命令打开另一个 shell 会话。 这是因为您在两个进程之间建立了管道,但需要 sudo 权限才能在整个命令序列中持续存在。 有多种解决方法; 在这里,您使用 sudo sh -c 打开一个具有 sudo 权限的附加 shell 进程。

现在,再次使用 pfctl 来验证这些规则是否处于活动状态:

sudo pfctl -a rogue_hosts -s rules

这将生成以下输出:

Outputblock return out quick on egress inet from 192.168.47.4 to any

锚点的使用完全是情境性的,而且通常是主观的。 与任何其他功能一样,使用锚点也有优缺点。 一些应用程序,如 blacklistd 设计与锚点接口。 接下来,您将专注于使用 PF 进行日志记录,这是网络安全的一个关键方面。 如果您看不到防火墙在做什么,您的防火墙就没有用处。

第 7 步 — 记录防火墙的活动

在这一步中,您将使用 PF 日志记录,它由名为 pflog 的伪接口管理。 在引导时通过将 pflog_enabled=YES 添加到 /etc/rc.conf 文件来启用日志记录,您在步骤 2 中执行了此操作。 这将启用 pflogd 守护程序,该守护程序会启动名为 pflog0 的接口并将二进制格式的日志写入名为 /var/log/pflog 的文件。 日志可以从界面实时解析,或者使用 tcpdump(8) 实用程序从 /var/log/pflog 文件中读取。

首先从 /var/log/pflog 文件中访问一些日志:

sudo tcpdump -ner /var/log/pflog

您传递 -ner 标志来格式化输出以提高可读性,并指定要从中读取的文件,在您的情况下是 /var/log/pflog

您将看到以下输出:

Outputreading from file /var/log/pflog, link-type PFLOG (OpenBSD pflog file)

在这些早期阶段,/var/log/pflog 文件中可能没有任何数据。 在很短的时间内,日志文件将开始增长。

您还可以使用以下命令从pflog0界面实时查看日志:

sudo tcpdump -nei pflog0

您传递了 -nei 标志,它还格式化输出以提高可读性,但这次指定一个接口,在您的情况下是 pflog0

您将看到以下输出:

Outputtcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on pflog0, link-type PFLOG (OpenBSD pflog file), capture size 262144 bytes

您现在将实时看到连接。 如果可能,从远程计算机 ping 您的服务器,您将看到正在发生的连接。 服务器将保持此状态,直到您退出。

要退出此状态并返回命令行,请点击 CTRL + Z

互联网上有大量关于tcpdump(8)的信息,包括官方网站

使用 pftop 访问日志文件

pftop 实用程序是用于快速实时查看防火墙活动的工具。 它的名字受到著名的 Unix top 实用程序的影响。

要使用它,您需要安装 pftop 包:

sudo pkg install pftop

现在运行 pftop 二进制文件:

sudo pftop

这将生成以下输出(您的 IP 会有所不同):

OutputPR    DIR SRC                   DEST                           STATE                AGE       EXP   PKTS BYTES
tcp   In  251.155.237.90:27537  157.225.173.58:22     ESTABLISHED:ESTABLISHED  00:12:35  23:59:55   1890  265K
tcp   In  222.186.42.15:25884   157.225.173.58:22       TIME_WAIT:TIME_WAIT    00:01:25  00:00:06     22  3801
udp   Out 157.245.171.59:4699   67.203.62.5:53           MULTIPLE:SINGLE       00:00:14  00:00:16      2   227

创建额外的日志接口

与任何其他接口一样,可以使用 /etc/hostname 文件创建和命名多个日志接口。 您可能会发现这对组织目的很有用,例如,如果您想单独记录某些类型的活动。

创建一个名为 pflog1 的附加日志记录接口:

sudo vi /etc/hostname.pflog1

将以下内容添加到 /etc/hostname.pflog1 文件中:

/etc/hostname.pflog1

up

现在在您的 /etc/rc.conf 文件中在启动时启用设备:

sudo sysrc pflog1_enable="YES"

您现在可以监控和记录您的防火墙活动。 这使您可以查看谁正在与您的服务器建立连接以及正在建立的连接类型。

在本教程中,您已将一些高级概念纳入您的 PF 规则集中。 只需要在需要时实现高级功能。 也就是说,在下一步中,您将恢复到基本规则集。

第 8 步 — 恢复到您的基本规则集

在这最后一部分中,您将恢复到您的基本规则集。 这是一个快速的步骤,它将带您回到功能的极简状态。

使用以下命令打开基本规则集:

sudo vi /etc/pf.conf

删除文件中的当前规则集并将其替换为以下基本规则集:

/etc/pf.conf

vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach }"
table <bruteforce> persist
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }

set skip on lo0
scrub in all fragment reassemble max-mss 1440
antispoof quick for $vtnet0
block in quick on $vtnet0 from <rfc6890>
block return out quick on egress to <rfc6890>
block all
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass inet proto icmp icmp-type $icmp_types

保存并退出文件。

重新加载规则集:

sudo pfctl -f /etc/pf.conf

如果命令没有错误,那么您的规则集中没有错误并且您的防火墙运行正常。

您还需要禁用您创建的 pflog1 接口。 由于您可能还不知道是否需要它,您可以使用 sysrc 实用程序禁用 pflog1

sudo sysrc pflog1_enable="NO"

现在从 /etc 目录中删除 /etc/hostname.pflog1 文件:

sudo rm /etc/hostname.pflog1

在注销之前,再次重新启动服务器以确保您的所有更改都是有效且持久的:

sudo reboot

等待几分钟,然后再登录到您的服务器。

或者,如果您想使用 Web 服务器实现 PF,以下是此场景的规则集。 对于大多数 Web 应用程序来说,这个规则集是一个足够的起点。

简单的 Web 服务器规则集

vtnet0 = "vtnet0"
icmp_types = "{ echoreq unreach }"
table <bruteforce> persist
table <webcrawlers> persist
table <rfc6890> { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16          \
                  172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.2.0/24 192.88.99.0/24    \
                  192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24            \
                  240.0.0.0/4 255.255.255.255/32 }

set skip on lo0
scrub in all fragment reassemble max-mss 1440
antispoof quick for $vtnet0
block in quick on $vtnet0 from <rfc6890>
block return out quick on egress to <rfc6890>
block all
pass in on $vtnet0 proto tcp to port { 22 } \
    keep state (max-src-conn 15, max-src-conn-rate 3/1, \
        overload <bruteforce> flush global)
pass in on $vtnet0 proto tcp to port { 80 443 } \
    keep state (max-src-conn 45, max-src-conn-rate 9/1, \
        overload <webcrawlers> flush global)
pass out proto { tcp udp } to port { 22 53 80 123 443 }
pass inet proto icmp icmp-type $icmp_types

这将创建一个名为 <webcrawlers> 的重载表,该表具有比基于 max-src-conn 45max-src-conn-rate 值的 SSH 端口更自由的重载策略。 这是因为并非所有重载都来自不良行为者。 它们也可能源自非恶意网络机器人,因此您可以避免对端口 80443 采取过多的安全措施。 如果您决定实施 webserver 规则集,则需要将 <webcrawlers> 表添加到 /etc/pf.conf,并定期从表中清除 IP。 参考步骤5

结论

在本教程中,您在 FreeBSD 12.1 上配置了 PF。 您现在有了一个基本规则集,可以作为所有 FreeBSD 项目的起点。 有关 PF 的更多信息,请查看 pf.conf(5) 手册页。

访问我们的 FreeBSD 主题页面更多教程和问答。