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
这告诉防火墙您
- 正在将区域的目标更改为 ACCEPT
- 正在将桥接接口“lxdbr0”添加到区域
- 正在重新加载防火墙
这一切都意味着您接受来自桥接接口的所有流量。
列出区域 (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
重要提示:活动区域
一个区域只有在满足以下两个条件之一时才能处于活动状态:
- 该区域已分配给网络接口。
- 该区域已分配源 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
测试您的规则以确保其正常工作。测试方法:
- 从您的源 IP(上面是 192.168.1.122)以 root 用户或您的 sudo 用户身份 SSH 登录(使用 root 用户,因为您将在主机上运行需要 root 权限的命令。如果使用 sudo 用户,请记住连接后执行
sudo -s
)。 - 连接后,运行
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)¶
这是用于公开允许 http
和 https
的 iptables
脚本,这些是您提供网页所需的协议。
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 服务器,按照客户的要求构建防火墙,并将责任留给客户负责服务器上的情况。尽管如此,如果您的服务器是公开的,您可能需要提供对 phpmyadmin
或 MariaDB
的其他前端的访问。在这种情况下,您需要关注数据库的密码要求,并将数据库用户设置为默认值以外的值。对作者来说,密码长度是创建密码时的主要考虑因素。
密码安全是另一篇关于此主题的文章的讨论内容。假设您有一个良好的数据库访问密码策略,并且您的防火墙中处理数据库的 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:
已建立连接的关联规则 (Established related 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