跳至内容

iptables 指南到 firewalld - 介绍

firewalld 作为默认防火墙被引入时(它于 2011 年推出,但我相信它最早出现在 CentOS 7 中),作者仍然继续使用 iptables。这主要有两个原因。首先,当时可用的 firewalld 文档使用了过于简单的规则,并没有展示 firewalld 如何将服务器安全设置细化到 IP 层面。其次,作者在 iptables 方面拥有十多年的经验,继续使用它比学习 firewalld 更容易。

本文档旨在解决大多数 firewalld 参考资料的局限性,并迫使作者使用 firewalld 来模仿那些更精细的防火墙规则。

来自手册页:“firewalld 提供了一个动态管理的防火墙,支持网络/防火墙区域来定义网络连接或接口的信任级别。它支持 IPv4、IPv6 防火墙设置、以太网桥以及运行时配置和永久配置选项的分离。它还支持一个服务或应用程序接口,用于直接添加防火墙规则。”

firewalld 实际上是 Rocky Linux 中 netfilter 和 nftables 内核子系统的前端。

本指南专注于将 iptables 防火墙的规则应用于 firewalld 防火墙。如果您刚开始接触防火墙,本文档可能会对您更有帮助。请同时阅读这两份文档以获得对 firewalld 的最佳理解。

先决条件和假设

  • 在本文档中,假设您是 root 用户或通过 sudo 拥有提升的权限。
  • 对防火墙规则有一定的了解,特别是 iptables,或者至少您想了解一些关于 firewalld 的知识。
  • 您在命令行中输入命令感到舒适。
  • 这里所有的例子都处理 IPv4 IP 地址。

区域 (Zones)

要真正理解 firewalld,您需要理解区域的使用。区域提供了防火墙规则集的精细化。

firewalld 有几个内置区域

区域 示例用途
drop 丢弃入站连接而不回复 - 只允许出站数据包。
block 使用 icmp-host-prohibited 消息拒绝 IPv4 的入站连接,使用 icmp6-adm-prohibited 消息拒绝 IPv6 的入站连接 - 只有从系统内部发起的网络连接才是可能的。
public 用于公共区域 - 只接受选定的入站连接。
external 用于外部网络,开启了 IP 伪装,只接受选定的入站连接。
dmz 用于您的非军事区(demilitarized zone)中可公开访问的计算机,只接受选定的入站连接,对您的内部网络访问有限。
work 用于工作区域的计算机 - 只接受选定的入站连接。
home 用于家庭区域 - 只接受选定的入站连接
internal 用于您的内部网络设备访问 - 只接受选定的入站连接。
trusted 接受所有网络连接。

注意

firewall-cmd 是用于管理 firewalld 守护进程的命令行程序。

要列出系统中已有的区域,请键入

firewall-cmd --get-zones

警告

请记住检查您的防火墙状态,如果 firewalld-cmd 返回错误,可以使用以下任一方式:

firewall-cmd 命令

$ firewall-cmd --state
running

systemctl 命令

$ systemctl status firewalld

作者不喜欢大多数这些区域名称。drop、block、public 和 trusted 非常清晰,但有些区域名称不足以实现完美的精细化安全。以这个 iptables 规则部分为例:

iptables -A INPUT -p tcp -m tcp -s 192.168.1.122 --dport 22 -j ACCEPT

这里您允许来自单个 IP 地址的 SSH(端口 22)连接到服务器。如果您决定使用内置区域,可以为此使用“trusted”。首先,您将 IP 添加到区域,然后,您将规则应用到区域。

firewall-cmd --zone=trusted --add-source=192.168.1.122 --permanent
firewall-cmd --zone trusted --add-service=ssh --permanent

但是,如果此服务器上还有另一个内网,只有分配给您组织的 IP 块才能访问呢?您现在会为该规则应用“internal”区域吗?作者更倾向于创建一个处理管理员 IP(允许 SSH 登录服务器的 IP)的区域。

添加区域 (Adding zones)

要添加区域,您需要使用 firewall-cmd 并带上 --new-zone 参数。您将添加“admin”(用于管理的)作为区域。

firewall-cmd --new-zone=admin --permanent

注意

作者在整个过程中大量使用了 --permanent 标志。为了测试,建议在不使用 --permanent 标志的情况下添加规则,进行测试,如果一切正常,然后使用 firewall-cmd --runtime-to-permanent 将规则设为实时,再运行 firewall-cmd --reload。如果风险很低(换句话说,您不会将自己锁定在外面),您可以添加 --permanent 标志,就像这里一样。

在使用此区域之前,您需要重新加载防火墙。

firewall-cmd --reload

技巧

关于自定义区域的说明:如果您需要添加一个可信区域,但该区域只包含特定的源 IP 或接口,而不包含任何协议或服务,并且“trusted”区域对您不起作用,可能是因为它已经被您用于其他地方等。您可以添加一个自定义区域来实现这一点,但您必须将区域的目标从“default”更改为“ACCEPT”(也可以使用 REJECT 或 DROP,具体取决于您的目标)。以下是使用 LXD 计算机上的桥接接口(在本例中为 lxdbr0)的示例。

首先,您添加区域并重新加载,以便可以使用它。

firewall-cmd --new-zone=bridge --permanent
firewall-cmd --reload

接下来,您将区域的目标从“default”更改为“ACCEPT”(请注意,更改目标需要 "--permanent" 选项),然后分配接口,并重新加载。

firewall-cmd --zone=bridge --set-target=ACCEPT --permanent
firewall-cmd --zone=bridge --add-interface=lxdbr0 --permanent
firewall-cmd --reload

这告诉防火墙您

  1. 正在将区域的目标更改为 ACCEPT
  2. 正在将桥接接口“lxdbr0”添加到区域
  3. 正在重新加载防火墙

这一切都意味着您接受来自桥接接口的所有流量。

列出区域 (Listing zones)

在继续之前,您需要检查列出区域的过程。您会得到一个单一的生产列,而不是 iptables -L 提供的表格输出。使用命令 firewall-cmd --zone=[zone_name] --list-all 列出区域。以下是当您列出新创建的“admin”区域时的样子。

firewall-cmd --zone=admin --list-all

admin
  target: default
  icmp-block-inversion: no
  interfaces:
  sources:
  services:
  ports:
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

您可以使用以下命令列出系统中活动的区域:

firewall-cmd --get-active-zones

重要提示:活动区域

一个区域只有在满足以下两个条件之一时才能处于活动状态:

  1. 该区域已分配给网络接口。
  2. 该区域已分配源 IP 地址或网络范围。

从区域中移除 IP 和服务 (Removing an IP and service from a zone)

如果您按照之前的说明将 IP 添加到“trusted”区域,您现在必须将其移除。还记得我们关于使用 --permanent 标志的说明吗?这是一个很好的地方,可以在进行适当的测试(在将规则设为实时之前)时避免使用它。

firewall-cmd --zone=trusted --remove-source=192.168.1.122

您还想从该区域中移除 SSH 服务。

firewall-cmd --zone=trusted --remove-service ssh

然后进行测试。您想确保在执行最后两个步骤之前,可以通过另一个区域的 ssh 方式连接(请参阅下面的警告!)。如果您没有进行其他更改,“public”区域仍然允许 SSH,因为它默认就在那里。

一旦您满意,将运行时规则迁移到永久配置:

firewall-cmd --runtime-to-permanent

然后重新加载:

firewall-cmd --reload

警告

如果您在远程服务器或 VPS 上工作,请暂缓执行最后一条指令。切勿从远程服务器中移除 ssh 服务,除非您有其他方法可以访问 shell(参见下文)。

如果您通过防火墙将自己锁定在 SSH 访问之外,在这种情况下,您将需要(在最坏的情况下)亲自修复您的服务器、联系支持人员,或者可能从您的控制面板重新安装操作系统(取决于服务器是物理的还是虚拟的)。

使用新区域 - 添加管理 IP (Using a new zone - Adding administrative IPs)

现在只需重复我们最初的步骤,使用“admin”区域。

firewall-cmd --zone=admin --add-source=192.168.1.122
firewall-cmd --zone admin --add-service=ssh

列出区域以确保区域看起来正确并且服务已正确添加。

firewall-cmd --zone=admin --list-all

测试您的规则以确保其正常工作。测试方法:

  1. 从您的源 IP(上面是 192.168.1.122)以 root 用户或您的 sudo 用户身份 SSH 登录(使用 root 用户,因为您将在主机上运行需要 root 权限的命令。如果使用 sudo 用户,请记住连接后执行 sudo -s)。
  2. 连接后,运行 tail /var/log/secure,您将看到类似以下的输出:
Feb 14 22:02:34 serverhostname sshd[9805]: Accepted password for root from 192.168.1.122 port 42854 ssh2
Feb 14 22:02:34 serverhostname sshd[9805]: pam_unix(sshd:session): session opened for user root by (uid=0)

这表明我们 SSH 连接的源 IP 就是您刚刚添加到“admin”区域的 IP。您可以安全地将此规则永久化。

firewall-cmd --runtime-to-permanent

完成添加规则后,重新加载。

firewall-cmd --reload

您可能需要将其他服务添加到“admin”区域,但目前 SSH 是最合理的。

警告

默认情况下,“public”区域启用了 ssh 服务,这可能是一个安全隐患。一旦创建了您的管理区域,并将其分配给 ssh 并经过测试,您就可以从 public 区域移除该服务。

如果您需要添加多个管理 IP(这很可能),只需将其添加到区域的源中。在本例中,您将一个 IP 添加到“admin”区域。

firewall-cmd --zone=admin --add-source=192.168.1.151 --permanent

注意

请记住,如果您正在远程服务器或 VPS 上工作,并且互联网连接不总是使用相同的 IP,您可能希望允许您的 ssh 服务访问您的互联网服务提供商或地理区域使用的 IP 地址范围。同样,这是为了避免被自己的防火墙锁定。

许多 ISP 对专用 IP 地址收费,如果它们根本不提供的话,所以这是一个真实的问题。

这里的例子假定您使用的是您自己的私有网络上的 IP 地址来访问同样是本地的服务器。

ICMP 规则 (ICMP rules)

检查我们 iptables 防火墙中的另一行,您希望在 firewalld 中模拟它 - ICMP 规则。

iptables -A INPUT -p icmp -m icmp --icmp-type 8 -s 192.168.1.136 -j ACCEPT

对于新手来说,ICMP 是一种用于错误报告的数据传输协议。它告诉您连接到机器时存在问题。

实际上,您可能会将 ICMP 开放给所有本地 IP(在本例中为 192.168.1.0/24)。我们的“public”和“admin”区域默认就有 ICMP,所以第一步是限制 ICMP 只到那个网络地址,方法是在“public”和“admin”中阻止这些请求。

同样,这是为了演示目的。您肯定希望您的管理员用户能够通过 ICMP 连接到您的服务器,并且他们可能仍然会,因为他们是 LAN 网络 IP 的一部分。

要关闭“public”区域上的 ICMP:

firewall-cmd --zone=public --add-icmp-block={echo-request,echo-reply} --permanent

在我们的“trusted”区域上也执行相同的操作:

firewall-cmd --zone=trusted --add-icmp-block={echo-request,echo-reply} --permanent

这里介绍一个新东西:花括号“{}”允许我们指定多个参数。像往常一样,在进行此类更改后,您需要重新加载。

firewall-cmd --reload

从不允许的 IP 使用 ping 进行测试会得到:

ping 192.168.1.104
PING 192.168.1.104 (192.168.1.104) 56(84) bytes of data.
From 192.168.1.104 icmp_seq=1 Packet filtered
From 192.168.1.104 icmp_seq=2 Packet filtered
From 192.168.1.104 icmp_seq=3 Packet filtered

Web 服务器端口 (Web server ports)

这是用于公开允许 httphttpsiptables 脚本,这些是您提供网页所需的协议。

iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT

而这是您可能已经见过很多次的 firewalld 等效命令:

firewall-cmd --zone=public --add-service=http --add-service=https --permanent

这没问题,但是如果您运行的是 Nextcloud 服务(例如)在 http/https 上,并且您只希望您的受信任网络能够访问它呢?这并不罕见!这类事情经常发生,而只是公开允许流量,而不考虑谁实际需要访问,是一个巨大的安全风险。

您实际上不能使用上面使用的“trusted”区域信息。那是用于测试的。您必须假定至少已将我们的 LAN IP 块添加到“trusted”中。这将看起来像这样:

firewall-cmd --zone=trusted --add-source=192.168.1.0/24 --permanent

将服务添加到区域。

firewall-cmd --zone=trusted --add-service=http --add-service=https --permanent

如果您之前将这些服务添加到了“public”区域,您需要将它们移除。

firewall-cmd --zone=public --remove-service=http --remove-service=https --permanent

重新加载。

firewall-cmd --reload

FTP 端口 (FTP ports)

回到 iptables 脚本。您有以下处理 FTP 的规则:

iptables -A INPUT -p tcp -m tcp --dport 20-21 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 7000-7500 -j ACCEPT

脚本的这一部分处理标准的 FTP 端口(20 和 21)以及一些额外的被动端口。像 VSFTPD 这样的 FTP 服务器通常需要这些类型的规则。通常,这类规则会放在面向公众的 Web 服务器上,用于允许客户进行 FTP 连接。

firewalld 中不存在 ftp-data 服务(端口 20)。这里列出的 7000 到 7500 端口是用于被动 FTP 连接的,并且同样,在 firewalld 中不存在作为服务。您可以切换到 SFTP,这会简化这里的端口允许规则,并且很可能是推荐的方式。

这演示了将一组 iptables 规则转换为 firewalld。为了解决所有这些问题,您可以这样做:

首先,将 ftp 服务添加到与 Web 服务相同的区域。在此示例中,这很可能是“public”。

firewall-cmd --zone=public --add-service=ftp --permanent

添加 ftp-data 端口。

firewall-cmd --zone=public --add-port=20/tcp --permanent

添加被动连接端口。

firewall-cmd --zone=public --add-port=7000-7500/tcp --permanent

然后重新加载。

firewall-cmd --reload

数据库端口 (Database ports)

如果您处理的是 Web 服务器,那么您几乎肯定也在处理数据库。您处理对该数据库的访问的方式与处理其他服务相同。如果不需要从世界各地访问,请将您的规则应用于“public”以外的区域。另一个考虑因素是,您是否需要提供访问权限?同样,这可能取决于您的环境。作者之前工作的单位使用托管 Web 服务器为客户提供服务。许多客户有 Wordpress 网站,而且他们中的绝大多数人实际上并不需要或不要求访问 MariaDB 的任何前端。如果客户需要更多访问权限,我们的解决方案是创建一个 LXD 容器用于他们的 Web 服务器,按照客户的要求构建防火墙,并将责任留给客户负责服务器上的情况。尽管如此,如果您的服务器是公开的,您可能需要提供对 phpmyadminMariaDB 的其他前端的访问。在这种情况下,您需要关注数据库的密码要求,并将数据库用户设置为默认值以外的值。对作者来说,密码长度是创建密码时的主要考虑因素

密码安全是另一篇关于此主题的文章的讨论内容。假设您有一个良好的数据库访问密码策略,并且您的防火墙中处理数据库的 iptables 行如下所示:

iptables -A INPUT -p tcp -m tcp --dport=3600 -j ACCEPT

在这种情况下,为 firewalld 转换将服务添加到“public”区域。

firewall-cmd --zone=public --add-service=mysql --permanent

Postgresql注意事项 (Postgresql considerations)

Postgresql 使用其服务端口。这是 IP tables 规则示例。

iptables -A INPUT -p tcp -m tcp --dport 5432 -s 192.168.1.0/24 -j ACCEPT

虽然在面向公众的 Web 服务器上不太常见,但在内部资源中可能更常见。相同的安全注意事项适用。如果您在受信任的网络(在本例中为 192.168.1.0/24)上有一台服务器,您可能不希望或不需要向该网络上的所有人提供访问权限。Postgresql 有一个访问列表可用于更精细地控制访问权限。我们的 firewalld 规则将如下所示:

firewall-cmd --zone=trusted --add-service=postgresql

DNS 端口 (DNS ports)

拥有私有或公共 DNS 服务器也意味着在编写规则以保护这些服务时要采取预防措施。如果您有一个私有 DNS 服务器,其 iptables 规则如下所示(请注意,大多数 DNS 服务是 UDP 而不是 TCP,但并非总是如此):

iptables -A INPUT -p udp -m udp -s 192.168.1.0/24 --dport 53 -j ACCEPT

那么只允许您的“trusted”区域将是正确的。您已经设置了“trusted”区域的源。您所要做的就是将服务添加到区域。

firewall-cmd --zone=trusted --add-service=dns

对于面向公众的 DNS 服务器,您只需将相同的服务添加到“public”区域。

firewall-cmd --zone=public --add-service=dns

更多关于列出规则 (More on listing rules)

注意

如果您愿意,您可以列出所有规则,方法是列出 nftables 规则。这很丑陋,我不推荐这样做,但如果您非要看,可以执行 nft list ruleset

到目前为止,还没有做太多的是列出规则。这是您可以按区域进行的。以下是您使用过的区域的示例。请注意,您也可以在将规则设为永久之前列出区域,这是一个好主意。

firewall-cmd --list-all --zone=trusted

在这里,您可以看到上面应用的内容。

trusted (active)
  target: ACCEPT
  icmp-block-inversion: no
  interfaces:
  sources: 192.168.1.0/24
  services: dns
  ports:
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks: echo-reply echo-request
  rich rules:

这适用于任何区域。例如,这是到目前为止的“public”区域:

firewall-cmd --list-all --zone=public

public
  target: default
  icmp-block-inversion: no
  interfaces:
  sources:
  services: cockpit dhcpv6-client ftp http https
  ports: 20/tcp 7000-7500/tcp
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks: echo-reply echo-request
  rich rules:

请注意,您已从服务中移除了 SSH 访问,并阻止了 ICMP“echo-reply”和“echo-request”。

在您的“admin”区域,到目前为止看起来是这样的:

firewall-cmd --list-all --zone=admin

  admin (active)
  target: default
  icmp-block-inversion: no
  interfaces:
  sources: 192.168.1.122 192.168.1.151
  services: ssh
  ports:
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

似乎 firewalld 默认情况下会在内部处理以下 iptables 规则:

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

接口 (Interfaces)

默认情况下,firewalld 会监听所有可用接口。在具有多个接口连接到多个网络网关的裸机服务器上,有必要根据网络连接的接口将接口分配给区域。

在我们的示例中没有添加接口,因为实验室使用 LXD 进行测试。在这种情况下,您只有一个接口可以处理。假设您的“public”区域需要配置为使用以太网端口 enp3s0,因为该端口上有公共 IP,并且假设您的“trusted”和“admin”区域在 LAN 接口上,这可能是 enp3s1。

要将这些区域分配给相应的接口,您可以使用以下命令:

firewall-cmd --zone=public --change-interface=enp3s0 --permanent
firewall-cmd --zone=trusted --change-interface=enp3s1 --permanent
firewall-cmd --zone=admin --change-interface=enp3s1 --permanent
firewall-cmd --reload

常用的 firewall-cmd 命令 (Common firewall-cmd commands)

您已经使用了一些命令。这里还有一些更常用的命令以及它们的作用:

命令 结果
firewall-cmd --list-all-zones 类似于 firewall-cmd --list-all --zone=[zone],但它列出了所有区域及其内容。
firewall-cmd --get-default-zone 显示默认区域,默认为“public”,除非您更改它。
firewall-cmd --list-services --zone=[zone] 显示该区域启用的所有服务。
firewall-cmd --list-ports --zone=[zone] 显示该区域打开的所有端口。
firewall-cmd --get-active-zones 显示系统中活动的区域、活动的接口、服务和端口。
firewall-cmd --get-services 显示所有可用的服务。
firewall-cmd --runtime-to-permanent 如果您输入了许多没有 --permanent 选项的规则,请在重新加载之前执行此操作。

这里并未涵盖许多 firewall-cmd 选项,但这为您提供了最常用的命令。

结论

由于 firewalld 是 Rocky Linux 推荐并包含的防火墙,因此了解其工作原理是一个好主意。firewalld 服务应用中包含的简单规则通常不考虑服务器的使用情况,除了公开允许服务外,没有其他选项。这是安全漏洞的一个缺点,本可以避免。

看到这些说明时,请考虑您的服务器用途以及服务是否需要向全世界开放。如果不需要,请考虑如上所述,在您的规则中应用更精细化的设置。

这无意成为 firewalld 的详尽指南,而是作为起点。

作者:Steven Spencer

贡献者:wsoyinka, Antoine Le Morvan, Ezequiel Bruni, qyecst, Ganna Zhyrnova