在第 3 章用防火墙保护您的服务器-第 1 部分中,我们介绍了 iptables 和 ufw,一个对 iptables 用户友好的前端。他们已经存在很多年了,他们确实在工作。然而,在这一章,我们将看看一些更新的技术,可以更有效地完成这项工作。
首先,我们来看看 nftables。我们将看看它的结构、命令和配置。然后,我们将对 firewalld 进行同样的操作。在这两种情况下,你都会得到大量的实践。
我们将在本章中讨论以下主题:
- nftables——一种更通用的防火墙系统
- 红帽系统的防火墙
本章的代码文件可以在这里找到:https://github . com/packt publishing/Mastering-Linux-Security-and-Harding-第二版。
现在,让我们把注意力转向 nftables,这个街区的新来的孩子。那么,nftables 给表带来了什么?(是的,这个双关语是有意的。):
- 对于所有不同的网络组件,您可以忘记需要单独的守护程序和实用程序。iptables、ip6tables、ebtables 和 arptables 的功能现在都合并在一个简洁的包中。nft 实用程序现在是您唯一需要的防火墙实用程序。
- 使用 nftables,您可以创建多维树来显示您的规则集。这使得故障排除变得非常容易,因为现在可以更容易地通过所有规则跟踪数据包。
- 使用 iptables,默认情况下会安装过滤器、NAT、mangle 和安全表,无论您是否使用每一个。
- 使用 nftables,您只创建您想要使用的表,从而提高性能。
- 与 iptables 不同,您可以在一个规则中指定多个操作,而不必为每个操作创建多个规则。
- 与 iptables 不同,新规则是自动添加的。(这是一种花哨的说法,表示不再需要为了添加一个规则而重新加载整个规则集。)
- nftables 有自己的内置脚本引擎,允许您编写效率更高、可读性更强的脚本。
- 如果您已经有许多 iptables 脚本需要使用,您可以安装一套实用程序来帮助您将它们转换为 nftables 格式。
nftables 唯一真正的缺点是它仍然是一项正在进行的工作。它将完成大部分 iptables 的功能,但是 Ubuntu 到 19.04 版本的 nftables 仍然缺少一些更高级的功能,这些功能在基于 iptables 的解决方案中可能是理所当然的。话虽如此,最新版本的 nftables 已经在一定程度上解决了这个问题,但在撰写本文时,它并不在 Ubuntu 的当前版本中。(当你读到这篇文章时,这可能会改变。)要查看您拥有哪个版本的 nftables,请使用以下命令:
sudo nft -v
如果该版本显示为版本 0.9.1 或更高版本,则您可以使用的功能比以前更多。
如果你习惯了 iptables,你可能会认识到一些 nftables 术语。唯一的问题是,有些术语的用法不同,含义也不同。让我们看一些例子,这样你就知道我在说什么了:
- 表:nftables 中的表指的是特定的协议家族。表格类型有 ip、ip6、inet、arp、网桥和 netdev。
- 链:nftables 中的链大致等同于 iptables 中的表。例如,在 nftables 中,可以有过滤器、路由或 NAT 链。
让我们从 Ubuntu 虚拟机的干净快照开始,禁用 ufw,然后安装 nftables 包,如下所示:
sudo apt install nftables
现在,让我们看看已安装的表的列表:
sudo nft list tables
你没看到任何桌子,是吗?所以,让我们加载一些。
如果您需要使用较旧的 Ubuntu 16.04,您会看到/etc
目录中的默认nftables.conf
文件已经有了基本的 nft 防火墙配置的开始:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;
iif lo accept
ct state established,related accept
# tcp dport { 22, 80, 443 } ct state new accept
ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
counter drop
}
}
我们将在稍后介绍如何使用它。
在我们将要使用的 Ubuntu 18.04 虚拟机上,默认的nftables.conf
文件只不过是一个毫无意义的占位符。您需要的文件在别处,您将复制它来替换默认的nftables.conf
文件。让我们来看看。
首先,我们将进入存储示例配置的目录,并列出示例配置文件:
cd /usr/share/doc/nftables/examples/syntax
ls -l
您应该会看到类似以下内容的内容:
donnie@munin:/usr/share/doc/nftables/examples/syntax$ ls -l
total 60
-rw-r--r-- 1 root root 150 Feb 2 2018 arp-filter
-rw-r--r-- 1 root root 218 Feb 2 2018 bridge-filter
-rw-r--r-- 1 root root 208 Feb 2 2018 inet-filter
. . .
. . .
-rw-r--r-- 1 root root 475 Feb 2 2018 README
-rw-r--r-- 1 root root 577 Feb 2 2018 workstation
donnie@munin:/usr/share/doc/nftables/examples/syntax
如果查看workstation
文件的内容,会发现和 Ubuntu 16.04 上的旧默认nftables.conf
文件是一样的。
接下来,我们将工作站文件复制到/etc
目录,将其名称更改为nftables.conf
(注意,这将覆盖旧的nftables.conf
文件,这是我们想要的):
sudo cp workstation /etc/nftables.conf
对于 Ubuntu 16.04 或 Ubuntu 18.04,以下是您将在/etc/nftables.conf
文件中看到的内容的详细信息:
#!/usr/sbin/nft -f
:虽然可以用 nftables 命令创建普通的 Bash shell 脚本,但是最好使用 nftables 附带的内置脚本引擎。这样,我们就可以使我们的脚本更具可读性,并且我们不必在我们想要执行的所有内容前面键入nft
。flush ruleset
:我们想从头开始,所以我们会清除任何可能已经加载的规则。table inet filter
:这将创建一个 inet 系列过滤器,适用于 IPv4 和 IPv6。这个表的名字是filter
,但它也可以是更具描述性的东西。chain input
:在第一对花括号内,我们有一个名为input
的链。(再说一遍,这个名字本可以更具描述性。)type filter hook input priority 0;
:在下一对花括号内,我们定义我们的链并列出规则。该链条被定义为filter
型。hook input
表示该链用于处理传入的数据包。因为这个链有一个hook
和一个priority
,它将直接接受来自网络堆栈的数据包。- 最后,我们有一个非常基本的主机防火墙的标准规则,从输入接口 ( iif )规则开始,它允许环回接口接受数据包。
- 接下来是标准的连接跟踪(
ct
)规则,该规则接受响应来自该主机的连接请求的流量。 - 然后,有一个注释掉的规则来接受安全外壳以及安全和不安全的网络流量。
ct state
new 表示防火墙将允许其他主机在这些端口上发起到我们服务器的连接。 ipv6
规则接受邻居发现数据包,允许 IPv6 功能。- 最后的
counter drop
规则无声地阻塞所有其他流量,并计算数据包的数量和它阻塞的字节数。(这是一个示例,说明如何让一个 nftables 规则执行多个不同的操作。)
如果你在你的 Ubuntu 服务器上需要的只是一个基本的、简单的防火墙,你最好的选择是编辑这个/etc/nftables.conf
文件,这样它就适合你自己的需要。首先,让我们将其设置为与我们为 iptables 部分创建的设置相匹配。换句话说,假设这是一个 DNS 服务器,我们需要允许连接到端口22
和端口53
。去掉tcp dport
线前的注释符号,去掉端口80
和443
,增加端口53
。该行现在应该如下所示:
tcp dport { 22, 53 } ct state new accept
注意如何使用一个 nftables 规则打开多个端口。
DNS 也使用端口53/udp
,所以我们给它加一行:
udp dport 53 ct state new accept
当您只打开一个端口时,您不需要用花括号将端口号括起来。打开多个端口时,只需将逗号分隔的列表包含在花括号中,每个逗号后、第一个元素前和最后一个元素后都有一个空格。
加载配置文件并查看结果:
donnie@ubuntu-nftables:/etc$ sudo systemctl reload nftables
donnie@ubuntu-nftables:/etc$ sudo nft list ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy accept;
iif "lo" accept
ct state established,related accept
tcp dport { ssh, domain } ct state new accept
udp dport domain ct state new accept
icmpv6 type { nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
counter packets 1 bytes 32 drop
}
}
donnie@ubuntu-nftables:/etc$
counter drop
规则是 nftables 规则如何做多件事的另一个例子。在这种情况下,规则会丢弃并计数不需要的数据包。到目前为止,该规则已经阻止了一个数据包和 32 个字节。为了演示这是如何工作的,假设我们想要在数据包被丢弃时创建一个日志条目。只需将log
关键字添加到drop
规则中,如下所示:
counter log drop
为了更容易找到这些消息,请在每个日志消息中添加一个标签,如下所示:
counter log prefix "Dropped packet: " drop
现在,当你需要仔细阅读/var/log/kern.log
文件来查看你已经丢失了多少数据包时,只需搜索Dropped packet
文本字符串。
现在,假设我们想要阻止某些 IP 地址到达这台机器的安全外壳端口。这样做,我们可以编辑文件,在打开端口22
的规则上面放置一个drop
规则。文件的相关部分如下所示:
tcp dport 22 ip saddr { 192.168.0.7, 192.168.0.10 } log prefix "Blocked SSH packets: " drop
tcp dport { 22, 53 } ct state new accept
重新加载文件后,我们将阻止来自两个不同 IPv4 地址的 SSH 访问。从这两个地址中的任何一个登录的任何尝试都将创建带有Blocked SSH packets
标签的/var/log/kern.log
消息。请注意,我们将drop
规则置于accept
规则之前,因为如果accept
规则先被读取,drop
规则将不起作用。
接下来,我们需要允许所需类型的 ICMP 数据包,如下所示:
ct state new,related,established icmp type { destination-unreachable, time-exceeded, parameter-problem } accept
ct state established,related,new icmpv6 type { destination-unreachable, time-exceeded, parameter-problem } accept
在这种情况下,需要为 ICMPv4 和 ICMPv6 制定单独的规则。
最后,我们将通过向过滤器表添加新的预路由链来阻止无效数据包,如下所示:
chain prerouting {
type filter hook prerouting priority 0;
ct state invalid counter log prefix "Invalid Packets: " drop
tcp flags & (fin|syn|rst|ack) != syn ct state new counter log drop
}
现在,我们可以保存文件并关闭文本编辑器。
Due to formatting constraints, I can't show the entire completed file here. To see the whole file, download the code file from the Packt website, and look in the Chapter 4
directory. The example file you seek is the nftables_example_1.conf
file.
现在,让我们加载新规则:
sudo systemctl reload nftables
另一个需要注意的很酷的事情是,我们如何在同一个配置文件中混合了 IPv4 (ip)规则和 IPv6 (ip6)规则。另外,除非我们另外指定,否则我们创建的所有规则都将适用于 IPv4 和 IPv6。这就是使用 inet 型桌子的好处。为了简单和灵活,您将希望尽可能多地使用 inet 表,而不是单独的 IPv4 和 IPv6 表。
很多时候,当你所需要的只是一个简单的主机防火墙时,你最好的选择就是用这个nftables.conf
文件作为你的起点,编辑这个文件以适合你自己的需要。然而,还有一个命令行组件,您可能会发现它很有用。
我更喜欢的使用 nftables 的方法是从一个模板开始,按照我的喜好手工编辑它,就像我们在上一节中所做的那样。但是对于那些宁愿从命令行做所有事情的人来说,有 nft 实用程序。
Even if you know that you'll always create firewalls by hand-editing nftables.conf
, there are still a couple of practical reasons to know about the nft utility.
Let's say that you've observed an attack in progress, and you need to stop it quickly without bringing down the system. With an nft
command, you can create a custom rule on the fly that will block the attack. Creating nftables rules on the fly also allows you to test the firewall as you configure it, before making any permanent changes.
And if you decide to take a Linux security certification exam, you might see questions about it. (I happen to know.)
有两种方法可以使用 nft 实用程序。首先,您可以直接从 Bash shell 中做任何事情,用nft
开始您想要执行的每个动作,然后是nft
子命令。另一种方法是在交互模式下使用 nft。就我们目前的目的而言,我们将只使用 Bash shell。
首先,让我们删除之前的配置,创建一个inet
表,因为我们想要同时适用于 IPv4 和 IPv6 的东西。我们想给它起一个有点描述性的名字,所以我们称它为ubuntu_filter
:
sudo nft delete table inet filter
sudo nft list tables
sudo nft add table inet ubuntu_filter
sudo nft list tables
接下来,我们将向刚刚创建的表中添加一个输入过滤器链(注意,由于我们是从 Bash shell 中完成的,因此我们需要用反斜杠对分号进行转义):
sudo nft add chain inet ubuntu_filter input { type filter hook input priority 0\; policy drop\; }
我们本可以给它起一个更具描述性的名字,但现在,input
起作用了。在这一对花括号中,我们设置了这个链的参数。
每个 nftables 协议族都有自己的一组钩子,这些钩子定义了如何处理数据包。目前,我们只关注 ip/ip6/inet 系列,它们有以下挂钩:
- 预路由
- 投入
- 向前
- 输出
- 后路由
其中,我们只关注输入和输出挂钩,这适用于过滤器类型的链。通过为我们的输入链指定一个钩子和一个优先级,我们说我们希望这个链是一个基本链,它将直接接受来自网络堆栈的数据包。您还会看到,某些参数必须以分号结束,如果您从 Bash shell 运行命令,则需要用反斜杠进行转义。最后,我们指定一个默认策略drop
。如果我们没有指定drop
作为默认策略,那么默认情况下该策略应该是accept
。
Every nft command that you enter takes effect immediately. So, if you're doing this remotely, you'll drop your Secure Shell connection as soon as you create a filter chain with a default drop
policy.
Some people like to create chains with a default accept
policy, and then add a drop
rule as the final rule. Other people like to create chains with a default drop
policy, and then leave off the drop
rule at the end. Be sure to check your local procedures to see what your organization prefers.
验证链是否已添加。你应该看到这样的东西:
donnie@ubuntu2:~$ sudo nft list table inet ubuntu_filter
[sudo] password for donnie:
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
}
}
donnie@ubuntu2:~$
那太好了,但是我们仍然需要一些规则。让我们从连接跟踪规则和打开安全外壳端口的规则开始。然后,我们将验证它们是否已添加:
sudo nft add rule inet ubuntu_filter input ct state established accept
sudo nft add rule inet ubuntu_filter input tcp dport 22 ct state new accept
donnie@ubuntu2:~$ sudo nft list table inet ubuntu_filter
table inet ubuntu_filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established accept
tcp dport ssh ct state new accept
}
}
donnie@ubuntu2:~
好吧,看起来不错。您现在有了一个允许安全外壳连接的基本工作防火墙。除此之外,正如我们在第 3 章、中所做的那样,用防火墙保护您的服务器-第 1 部分,我们忘记创建一个规则来允许环回适配器接受数据包。由于我们希望此规则位于规则列表的顶部,因此我们将使用insert
代替add
:
sudo nft insert rule inet ubuntu_filter input iif lo accept
donnie@ubuntu2:~$ sudo nft list table inet ubuntu_filter
table inet ubuntu_filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established accept
tcp dport ssh ct state new accept
}
}
donnie@ubuntu2:~$
现在,我们都准备好了。但是如果我们想在特定的位置插入一个规则呢?为此,您需要使用带有-a
选项的列表来查看规则句柄:
donnie@ubuntu2:~$ sudo nft list table inet ubuntu_filter -a
table inet ubuntu_filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept # handle 4
ct state established accept # handle 2
tcp dport ssh ct state new accept # handle 3
}
}
donnie@ubuntu2:~$
如你所见,手柄的编号方式没有真正的韵律或原因。假设我们想插入阻止某些 IP 地址访问安全外壳端口的规则。我们可以看到 SSH accept
规则是handle 3
,所以我们需要在它之前插入我们的drop
规则。该命令如下所示:
sudo nft insert rule inet ubuntu_filter input position 3 tcp dport 22 ip saddr { 192.168.0.7, 192.168.0.10 } drop
donnie@ubuntu2:~$ sudo nft list table inet ubuntu_filter -a
table inet ubuntu_filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept # handle 4
ct state established accept # handle 2
tcp dport ssh ip saddr { 192.168.0.10, 192.168.0.7} drop # handle 6
tcp dport ssh ct state new accept # handle 3
}
}
donnie@ubuntu2:~$
因此,要将规则放在带有handle 3
标签的规则之前,我们必须将其插入3
位置。我们刚刚插入的新规则有标签handle 6
。要删除规则,我们必须指定规则的句柄号:
sudo nft delete rule inet ubuntu_filter input handle 6
donnie@ubuntu2:~$ sudo nft list table inet ubuntu_filter -a
table inet ubuntu_filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept # handle 4
ct state established accept # handle 2
tcp dport ssh ct state new accept # handle 3
}
}
donnie@ubuntu2:~$
与 iptables 的情况一样,一旦重新启动机器,您从命令行所做的一切都将消失。为了使其永久化,让我们将list
子命令的输出重定向到nftables.conf
配置文件(当然,我们希望已经存在的文件有一个备份副本,以防我们想要恢复到它):
sudo sh -c "nft list table inet ubuntu_filter > /etc/nftables.conf"
由于 Bash shell 中的一个怪癖,我们不能以正常的方式将输出重定向到/etc
目录中的一个文件,即使当我们使用sudo
时也是如此。这就是为什么我必须添加sh -c
命令,用双引号将nft list
命令括起来。此外,请注意,该文件必须命名为nftables.conf
,因为这是 nftables systemd 服务要寻找的。现在,当我们查看文件时,我们会发现缺少了一些东西:
table inet ubuntu_filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established accept
tcp dport ssh ct state new accept
}
}
你们中那些眼尖的人会发现我们错过了flush
规则和 shebang 线来指定我们想要解释这个脚本的外壳。让我们添加它们:
#!/usr/sbin/nft -f
flush ruleset
table inet ubuntu_filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established accept
tcp dport ssh ct state new accept
}
}
好多了。让我们通过加载新配置并观察list
输出来测试这一点:
sudo systemctl reload nftables
donnie@ubuntu2:~$ sudo nft list table inet ubuntu_filter
table inet ubuntu_filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established accept
tcp dport ssh ct state new accept
}
}
donnie@ubuntu2:~$
这就是创建您自己的简单主机防火墙的全部内容。当然,从命令行运行命令,而不仅仅是在文本编辑器中创建脚本文件,确实有助于更多的输入。但是,它确实允许您在创建规则时动态测试规则。以这种方式创建您的配置,然后将list
输出重定向到您的新配置文件,可以减轻您在尝试手工编辑文件时必须跟踪所有这些花括号的负担。
也可以将我们刚刚创建的所有nft
命令放入一个常规的、老式的 Bash shell 脚本中。不过,相信我,你真的不想那么做。就像我们在这里所做的那样,只需使用 nft 原生脚本格式,您将拥有一个性能更好、可读性更强的脚本。
在本实验中,您将需要您的 Ubuntu 18.04 虚拟机的干净快照:
- 将您的 Ubuntu 虚拟机恢复到干净的快照,以清除您之前创建的任何防火墙配置。(或者,如果您愿意,从新的虚拟机开始。)禁用
ufw
并验证不存在防火墙规则:
sudo systemctl disable --now ufw
sudo iptables -L
您应该不会看到 iptables 的规则列表。
- 安装 nftables 包:
sudo apt install nftables
- 将
workstation
模板复制到/etc
目录,并将其重命名为nftables.conf
:
sudo cp /usr/share/doc/nftables/examples/syntax/workstation /etc/nftables.conf
- 编辑
/etc/nftables.conf
文件,创建新的配置。(请注意,由于格式限制,我不得不将它分成三个不同的代码块。)使文件的顶部看起来像这样:
#!/usr/sbin/nft -f flush ruleset
table inet filter {
chain prerouting {
type filter hook prerouting priority 0;
ct state invalid counter log prefix "Invalid Packets: " drop
tcp flags & (fin|syn|rst|ack) != syn ct state new counter log prefix "Invalid Packets 2: " drop
}
- 使文件的第二部分如下所示:
chain input {
type filter hook input priority 0;
# accept any localhost traffic
iif lo accept
# accept traffic originated from us
ct state established,related accept
# activate the following line to accept common local services
tcp dport 22 ip saddr { 192.168.0.7, 192.168.0.10 } log prefix "Blocked SSH packets: " drop
tcp dport { 22, 53 } ct state new accept
udp dport 53 ct state new accept
ct state new,related,established icmp type { destination-unreachable, time-exceeded, parameter-problem } accept
- 使文件的最后部分看起来像这样:
ct state new,related,established icmpv6 type { destination-unreachable, time-exceeded, parameter-problem } accept
# accept neighbour discovery otherwise IPv6 connectivity breaks.
ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
# count and drop any other traffic
counter log prefix "Dropped packet: " drop
}
}
- 保存文件并重新加载 nftables:
sudo systemctl reload nftables
- 查看结果:
sudo nft list tables
sudo nft list tables
sudo nft list table inet filter
sudo nft list ruleset
- 从您的主机或另一个虚拟机,对 Ubuntu 虚拟机执行窗口扫描:
sudo nmap -sW ip_address_of_UbuntuVM
- 查看数据包计数器,查看触发了哪个阻止规则(提示:它在预路由链中):
sudo nft list ruleset
- 这一次,对虚拟机进行空扫描:
sudo nmap -sN ip_address_of_UbuntuVM
- 最后,看看这次触发了哪个规则(提示:这是预路由链中的另一个规则):
sudo nft list ruleset
- 在
/var/log/kern.log
文件中,搜索Invalid Packets
文本字符串,查看关于丢弃的无效数据包的消息。
本实验到此结束–祝贺您!
在这一节中,我们研究了 nftables 的来龙去脉,并研究了如何配置它来帮助防止某些类型的攻击。接下来,我们将把注意力转向 firewalld 的奥秘。
到目前为止,我们已经看到了 iptables 和 ufw,iptables 是一个通用的防火墙管理系统,可以在所有 Linux 发行版上使用,ufw 可以在 Debian/Ubuntu 类型的系统上使用。接下来,我们将注意力转向 firewalld,它是红帽企业版 Linux 7/8 及其所有后代上的默认防火墙管理器。
但是事情变得有些混乱。在 RHEL/CentOS 7 上,防火墙的实现方式与在 RHEL/CentOS 8 上不同。这是因为,在 RHEL/CentOS 7 上,firewalld 使用 iptables 引擎作为后端。在 RHEL/CentOS 8 上,firewalld 使用 nftables 作为后端。无论哪种方式,您都不能用普通的 iptables 或 nftables 命令创建规则,因为 firewalld 以不兼容的格式存储规则。
Until very recently, firewalld was only available for RHEL 7/8 and their offspring. Now, however, firewalld is also available in the Ubuntu repositories. So, if you want to run firewalld on Ubuntu, you finally have that choice.
如果你在台式机上运行红帽或 CentOS,你会看到在应用菜单中有一个用于 firewalld 的 GUI 前端。然而,在文本模式服务器上,你所拥有的只是 firewalld 命令。出于某种原因,红帽族并没有像在旧版红帽上为 iptables 配置那样,为文本模式服务器创建一个 ncurses 类型的程序。
firewalld 的一大优势是它是动态管理的。这意味着您可以更改防火墙配置,而无需重新启动防火墙服务,也无需中断与服务器的任何现有连接。
在我们了解 RHEL/CentOS 7 和 RHEL/CentOS 8 firewalld 版本之间的差异之前,让我们先来看看两者的相同之处。
让我们从验证 firewalld 的状态开始。有两种方法可以做到这一点。对于第一种方式,我们可以使用firewall-cmd
的--state
选项:
[donnie@localhost ~]$ sudo firewall-cmd --state
running
[donnie@localhost ~]$
或者,如果我们想要更详细的状态,我们可以只检查守护程序,就像我们检查 systemd 机器上的任何其他守护程序一样:
[donnie@localhost ~]$ sudo systemctl status firewalld
firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled;
vendor preset: enabled)
Active: active (running) since Fri 2017-10-13 13:42:54 EDT; 1h 56min ago
Docs: man:firewalld(1)
Main PID: 631 (firewalld)
CGroup: /system.slice/firewalld.service
└─631 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid
. . .
Oct 13 15:19:41 localhost.localdomain firewalld[631]: WARNING: reject-
route: INVALID_ICMPTYPE: No supported ICMP type., ignoring for run-time.
[donnie@localhost ~]$
接下来,让我们看看防火墙区域。
Firewalld 是一种相当独特的动物,因为它带有几个预配置的区域和服务。如果你查看你的 CentOS 机器的/usr/lib/firewalld/zones
目录,你会看到分区文件,都是.xml
格式的:
[donnie@localhost ~]$ cd /usr/lib/firewalld/zones
[donnie@localhost zones]$ ls
block.xml dmz.xml drop.xml external.xml home.xml internal.xml public.xml
trusted.xml work.xml
[donnie@localhost zones]$
每个区域文件指定在各种给定情况下哪些端口是开放的,哪些端口是阻塞的。区域还可以包含 ICMP 消息规则、转发端口、伪装信息和丰富的语言规则。
例如,设置为默认的公共区域的.xml
文件如下所示:
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>Public</short>
<description>For use in public areas. You do not trust the other
computers on networks to not harm your computer. Only selected incoming
connections are accepted.</description>
<service name="ssh"/>
<service name="dhcpv6-client"/>
</zone>
在service name
行中,您可以看到唯一开放的端口用于安全外壳访问和 DHCPv6 发现。如果您查看home.xml
文件,您会看到它还打开了多播域名系统的端口,以及允许这台机器从 Samba 服务器或窗口服务器访问共享目录的端口:
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>Home</short>
<description>For use in home areas. You mostly trust the other computers
on networks to not harm your computer. Only selected incoming connections
are accepted.</description>
<service name="ssh"/>
<service name="mdns"/>
<service name="samba-client"/>
<service name="dhcpv6-client"/>
</zone>
firewall-cmd
实用程序是您用来配置防火墙的工具。您可以使用它来查看系统上的区域文件列表,而不必将cd
放入区域文件目录:
[donnie@localhost ~]$ sudo firewall-cmd --get-zones
[sudo] password for donnie:
block dmz drop external home internal public trusted work
[donnie@localhost ~]$
查看每个区域如何配置的快速方法是使用--list-all-zones
选项:
[donnie@localhost ~]$ sudo firewall-cmd --list-all-zones
block
target: %%REJECT%%
icmp-block-inversion: no
interfaces:
sources:
services:
ports:
protocols:
masquerade: no
forward-ports:
. . .
. . .
当然,这只是输出的一部分,因为所有区域的列表都超出了我们的显示范围。您可能只想查看一个特定区域的信息:
[donnie@localhost ~]$ sudo firewall-cmd --info-zone=internal
internal
target: default
icmp-block-inversion: no
interfaces:
sources:
services: ssh mdns samba-client dhcpv6-client
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
[donnie@localhost ~]$
因此,internal
区允许ssh
、mdns
、samba-client
和dhcpv6-client
服务。这对于在内部局域网上设置客户端机器非常方便。
任何给定的服务器或客户端都将安装一个或多个网络接口适配器。一台机器中的每个适配器可以被分配一个并且只有一个防火墙区域。要查看默认区域,请使用以下代码:
[donnie@localhost ~]$ sudo firewall-cmd --get-default-zone
public
[donnie@localhost ~]$
这很好,只是它没有告诉你哪个网络接口与这个区域相关联。要查看该信息,请使用以下代码:
[donnie@localhost ~]$ sudo firewall-cmd --get-active-zones
public
interfaces: enp0s3
[donnie@localhost ~]$
当您第一次安装红帽或 CentOS 时,防火墙已经处于活动状态,公共区域作为默认区域。现在,假设您正在非军事区设置您的服务器,并且您希望确保它的防火墙为此被锁定。您可以将默认区域更改为dmz
区域。让我们看看dmz.xml
文件,看看这对我们有什么帮助:
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>DMZ</short>
<description>For computers in your demilitarized zone that are publicly-
accessible with limited access to your internal network. Only selected
incoming connections are accepted.</description>
<service name="ssh"/>
</zone>
因此,非军事区唯一允许通过的是安全壳。好吧。现在已经足够好了,所以让我们将dmz
区域设置为默认值:
[donnie@localhost ~]$ sudo firewall-cmd --set-default-zone=dmz
[sudo] password for donnie:
success
[donnie@localhost ~]$
让我们验证一下:
[donnie@localhost ~]$ sudo firewall-cmd --get-default-zone
dmz
[donnie@localhost ~]$
我们都很好。然而,非军事区中面向互联网的服务器可能需要允许的不仅仅是 SSH 连接。这是我们使用防火墙服务的地方。但是在我们看之前,让我们考虑一个更重要的点。
您永远不想修改/usr/lib/firewalld
目录中的文件。每当您修改 firewalld 配置时,您会看到修改后的文件显示在/etc/firewalld
目录中。到目前为止,我们只修改了默认区域。因此,我们将在/etc/firewalld
看到以下内容:
[donnie@localhost ~]$ sudo ls -l /etc/firewalld
total 12
-rw-------. 1 root root 2003 Oct 11 17:37 firewalld.conf
-rw-r--r--. 1 root root 2006 Aug 4 17:14 firewalld.conf.old
. . .
我们可以对这两个文件做一个diff
来看看它们之间的区别:
[donnie@localhost ~]$ sudo diff /etc/firewalld/firewalld.conf /etc/firewalld/firewalld.conf.old
6c6
< DefaultZone=dmz
---
> DefaultZone=public
[donnie@localhost ~]$
因此,两个文件中较新的一个显示 dmz 区域现在是默认的。
To find out more about firewalld zones, enter the man firewalld.zones
command.
每个服务文件都包含需要为特定服务打开的端口列表。可选地,服务文件可以包含一个或多个目的地地址,或者调用任何需要的模块,例如用于连接跟踪。对于某些服务,您只需要打开一个端口。其他服务,如 Samba 服务,需要打开多个端口。无论哪种方式,有时记住每个服务的服务名比记住端口号更方便。
服务文件在/usr/lib/firewalld/services
目录中。您可以使用firewall-cmd
命令查看它们,就像您可以查看区域列表一样:
[donnie@localhost ~]$ sudo firewall-cmd --get-services
RH-Satellite-6 amanda-client amanda-k5-client bacula bacula-client bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc ceph ceph-mon cfengine condor-collector ctdb dhcp dhcpv6 dhcpv6-client dns docker-registry dropbox-lansync elasticsearch freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp ganglia-client ganglia-master high-availability http https imap imaps ipp ipp-client ipsec iscsi-target kadmin kerberos kibana klogin kpasswd kshell ldap ldaps libvirt libvirt-tls managesieve mdns mosh mountd ms-wbt mssql mysql nfs nrpe ntp openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy proxy-dhcp ptp pulseaudio puppetmaster quassel radius rpc-bind rsh rsyncd samba samba-client sane sip sips smtp smtp-submission smtps snmp snmptrap spideroak-lansync squid ssh synergy syslog syslog-tls telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server
[donnie@localhost ~]$
在添加更多服务之前,让我们检查哪些服务已经启用:
[donnie@localhost ~]$ sudo firewall-cmd --list-services
[sudo] password for donnie:
ssh dhcpv6-client
[donnie@localhost ~]$
这里,ssh 和 dhcpv6-client 是我们的全部。
dropbox-lansync
服务对我们 Dropbox 用户来说非常方便。让我们看看这会打开哪些端口:
[donnie@localhost ~]$ sudo firewall-cmd --info-service=dropbox-lansync
[sudo] password for donnie:
dropbox-lansync
ports: 17500/udp 17500/tcp
protocols:
source-ports:
modules:
destination:
[donnie@localhost ~]$
看起来 Dropbox 在 UDP 和 TCP 上使用了端口17500
。
现在,假设我们在非军事区设置了网络服务器,将dmz
区域设置为默认区域:
[donnie@localhost ~]$ sudo firewall-cmd --info-zone=dmz
dmz (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3
sources:
services: ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
[donnie@localhost ~]$
正如我们之前看到的,安全外壳端口是唯一打开的端口。让我们解决这个问题,以便用户可以真正访问我们的网站:
[donnie@localhost ~]$ sudo firewall-cmd --add-service=http
success
[donnie@localhost ~]$
当我们再次查看dmz
区域的信息时,我们将看到以下内容:
[donnie@localhost ~]$ sudo firewall-cmd --info-zone=dmz
dmz (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3
sources:
services: ssh http
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
[donnie@localhost ~]$
在这里,我们可以看到http
服务现在被允许通过。但是看看当我们将--permanent
选项添加到这个info
命令时会发生什么:
[donnie@localhost ~]$ sudo firewall-cmd --permanent --info-zone=dmz
dmz
target: default
icmp-block-inversion: no
interfaces:
sources:
services: ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
[donnie@localhost ~]$
哎呀!http
服务不在这里。这是怎么回事?
对于区域或服务的几乎每一个命令行变更,您需要添加--permanent
选项,以使变更在重新启动后保持不变。但是如果没有--permanent
选项,更改会立即生效。使用--permanent
选项,您必须重新加载防火墙配置,更改才会生效。为了演示这一点,我将重新启动虚拟机以摆脱http
服务。
好的;我重新启动了http
服务,现在它已经不存在了:
[donnie@localhost ~]$ sudo firewall-cmd --info-zone=dmz
dmz (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3
sources:
services: ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
[donnie@localhost ~]$
这一次,我将用一个命令添加两个服务,并指定更改将是永久性的:
[donnie@localhost ~]$ sudo firewall-cmd --permanent --add-service={http,https}
[sudo] password for donnie:
success
[donnie@localhost ~]$
您可以用一个命令添加任意多的服务,但是您必须用逗号分隔它们,并用一对花括号将整个列表括起来。此外,与我们刚才看到的 nftables 不同,我们不能在花括号内有空格。让我们看看结果:
[donnie@localhost ~]$ sudo firewall-cmd --info-zone=dmz
dmz (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3
sources:
services: ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
[donnie@localhost ~]$
自从我们决定将这种配置永久化后,它还没有生效。但是,如果我们将--permanent
选项添加到--info-zone
命令中,我们会看到配置文件确实已经更改:
[donnie@localhost ~]$ sudo firewall-cmd --permanent --info-zone=dmz
dmz
target: default
icmp-block-inversion: no
interfaces:
sources:
services: ssh http https
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
[donnie@localhost ~]$
现在,我们需要重新加载配置以使其生效:
[donnie@localhost ~]$ sudo firewall-cmd --reload
success
[donnie@localhost ~]$
现在,如果您再次运行sudo firewall-cmd --info-zone=dmz
命令,您将看到新配置生效。
要从区域中删除服务,只需将--add-service
替换为--remove-service
。
Note that we never specified which zone we're working with in any of these service commands. That's because if we don't specify a zone, firewalld just assumes that we're working with the default zone. If you want to add a service to something other than the default zone, just add the --zone=
option to your commands.
拥有服务文件很方便,除了不是每个需要运行的服务都有自己的预定义服务文件。假设您已经在服务器上安装了 Webmin,这需要打开端口10000/tcp
。快速 grep 操作将显示端口10000
不在我们任何预定义的服务中:
donnie@localhost services]$ pwd
/usr/lib/firewalld/services
[donnie@localhost services]$ grep '10000' *
[donnie@localhost services]$
因此,让我们将该端口添加到我们的默认区域,它仍然是 dmz 区域:
donnie@localhost ~]$ sudo firewall-cmd --add-port=10000/tcp
[sudo] password for donnie:
success
[donnie@localhost ~]$
同样,这不是永久性的,因为我们没有包括--permanent
选项。让我们再次这样做并重新加载:
[donnie@localhost ~]$ sudo firewall-cmd --permanent --add-port=10000/tcp
success
[donnie@localhost ~]$ sudo firewall-cmd --reload
success
[donnie@localhost ~]$
您还可以通过将逗号分隔的列表包含在一对花括号中来一次添加多个端口,就像我们对服务所做的那样(我特意省略了--permanent
-稍后您会看到原因):
[donnie@localhost ~]$ sudo firewall-cmd --add-port={636/tcp,637/tcp,638/udp}
success
[donnie@localhost ~]$
当然,你可以用--remove-port
代替--add-port
来删除一个区域的端口。
如果不想每次创建新的永久规则时都键入--permanent
,那就不要输入。然后,当您创建完规则后,通过键入以下内容,一次性将它们全部永久化:
sudo firewall-cmd --runtime-to-permanent
现在,让我们把注意力转向控制 ICMP。
让我们再来看看默认公共区域的状态:
[donnie@localhost ~]$ sudo firewall-cmd --info-zone=public
public (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3
sources:
services: ssh dhcpv6-client
ports: 53/tcp 53/udp
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
[donnie@localhost ~]$
向着底部,我们可以看到icmp-block
线,旁边什么都没有。这意味着我们的公共区域允许所有 ICMP 数据包通过。当然,这并不理想,因为我们想要阻止某些类型的 ICMP 数据包。在我们阻止任何事情之前,让我们看看所有可用的 ICMP 类型:
[donnie@localhost ~]$ sudo firewall-cmd --get-icmptypes
[sudo] password for donnie:
address-unreachable bad-header communication-prohibited destination-unreachable echo-reply echo-request fragmentation-needed host-precedence-violation host-prohibited host-redirect host-unknown host-unreachable ip-header-bad neighbour-advertisement neighbour-solicitation network-prohibited network-redirect network-unknown network-unreachable no-route packet-too-big parameter-problem port-unreachable precedence-cutoff protocol-unreachable redirect required-option-missing router-advertisement router-solicitation source-quench source-route-failed time-exceeded timestamp-reply timestamp-request tos-host-redirect tos-host-unreachable tos-network-redirect tos-network-unreachable ttl-zero-during-reassembly ttl-zero-during-transit unknown-header-type unknown-option
[donnie@localhost ~]$
正如我们对区域和服务所做的那样,我们可以查看关于不同 ICMP 类型的信息。在本例中,我们将查看一种 ICMPv4 类型和一种 ICMPv6 类型:
[donnie@localhost ~]$ sudo firewall-cmd --info-icmptype=network-redirectnetwork-redirect destination: ipv4
[donnie@localhost ~]$ sudo firewall-cmd --info-icmptype=neighbour-advertisementneighbour-advertisement
destination: ipv6
[donnie@localhost ~]$
我们已经看到,我们没有阻止任何 ICMP 数据包。我们还可以看到我们是否阻止了任何特定的 ICMP 数据包:
[donnie@localhost ~]$ sudo firewall-cmd --query-icmp-block=host-redirect
no
[donnie@localhost ~]$
我们已经确定重定向可能是一件坏事,因为它们可能被利用。因此,让我们阻止主机重定向数据包:
[donnie@localhost ~]$ sudo firewall-cmd --add-icmp-block=host-redirect
success
[donnie@localhost ~]$ sudo firewall-cmd --query-icmp-block=host-redirect
yes
[donnie@localhost ~]$
现在,让我们检查一下状态:
[donnie@localhost ~]$ sudo firewall-cmd --info-zone=public
public (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3
sources:
services: ssh dhcpv6-client
ports: 53/tcp 53/udp
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks: host-redirect
rich rules:
[donnie@localhost ~]$
太酷了——成功了。现在,让我们看看是否可以用一个命令阻止两种 ICMP 类型:
[donnie@localhost ~]$ sudo firewall-cmd --add-icmp-block={host-redirect,network-redirect}
success
[donnie@localhost ~]$
和以前一样,我们将检查状态:
[donnie@localhost ~]$ sudo firewall-cmd --info-zone=public
public (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks: host-redirect network-redirect
rich rules:
[donnie@localhost ~]$
这也奏效了,这意味着我们取得了冷静。然而,由于我们没有将--permanent
包含在这些命令中,这些 ICMP 类型只会被阻止,直到我们重新启动计算机。所以,让我们把它们永久化:
[donnie@localhost ~]$ sudo firewall-cmd --runtime-to-permanent
[sudo] password for donnie:
success
[donnie@localhost ~]$
有了这些,我们变得更加冷静。(当然,我所有的猫都已经觉得我很酷了。)
你刚刚看到了坏人试图闯入你系统的证据。你是做什么的?嗯,一个选择是激活panic
模式,切断所有网络通讯。
I can just see this now in the Saturday morning cartoons when some cartoon character yells, Panic mode, activate!
要使用panic
模式,请使用以下命令:
[donnie@localhost ~]$ sudo firewall-cmd --panic-on
[sudo] password for donnie:
success
[donnie@localhost ~]$
当然,如果您远程登录,您的访问将被切断,您必须到本地终端才能重新登录。要关闭panic
模式,请使用以下命令:
[donnie@localhost ~]$ sudo firewall-cmd --panic-off
[sudo] password for donnie:
success
[donnie@localhost ~]$
如果是远程登录,不需要检查panic
模式的状态。如果开着,你就不能进入机器。但是如果你坐在本地控制台前,你可能需要检查一下。只需使用以下代码:
[donnie@localhost ~]$ sudo firewall-cmd --query-panic
[sudo] password for donnie:
no
[donnie@localhost ~]$
这就是panic
模式的全部内容。
这是另一个你肯定会喜欢的省时方法。如果您想在数据包被阻止时创建日志条目,只需使用--set-log-denied
选项。在此之前,让我们看看它是否已经启用:
[donnie@localhost ~]$ sudo firewall-cmd --get-log-denied
[sudo] password for donnie:
off
[donnie@localhost ~]$
它不是,所以让我们打开它并再次检查状态:
[donnie@localhost ~]$ sudo firewall-cmd --set-log-denied=all
success
[donnie@localhost ~]$ sudo firewall-cmd --get-log-denied
all
[donnie@localhost ~]$
我们已经设置好记录所有被拒绝的数据包。然而,你可能并不总是想要这样。您的其他选择如下:
unicast
broadcast
multicast
例如,如果您只想记录发往多播地址的被阻止的数据包,请执行以下操作:
[donnie@localhost ~]$ sudo firewall-cmd --set-log-denied=multicast
[sudo] password for donnie:
success
[donnie@localhost ~]$ sudo firewall-cmd --get-log-denied
multicast
[donnie@localhost ~]$
到目前为止,我们刚刚设置了运行时配置,一旦我们重新启动机器,它就会消失。为了使这个永久化,我们可以使用任何我们已经使用过的方法。现在,让我们这样做:
[donnie@localhost ~]$ sudo firewall-cmd --runtime-to-permanent
success
[donnie@localhost ~]$
与我们在 Debian/Ubuntu 发行版中看到的不同,我们的数据包拒绝消息没有单独的kern.log
文件。相反,RHEL 类型的发行版将拒绝数据包的消息记录在/var/log/messages
文件中,这是 RHEL 世界的主要日志文件。已经定义了几个不同的消息标签,这将更容易审计日志中丢失的数据包。例如,这里有一条消息告诉我们被阻止的广播数据包:
Aug 20 14:57:21 localhost kernel: FINAL_REJECT: IN=enp0s3 OUT= MAC=ff:ff:ff:ff:ff:ff:00:1f:29:02:0d:5f:08:00 SRC=192.168.0.225 DST=255.255.255.255 LEN=140 TOS=0x00 PREC=0x00
TTL=64 ID=62867 DF PROTO=UDP SPT=21327 DPT=21327 LEN=120
标签是FINAL_REJECT
,它告诉我们这个消息是由在我们输入链末端的包罗万象的最终REJECT
规则创建的。DST=255.255.255.255
部分告诉我们这是一个广播信息。
这里还有一个例子,我对这台机器进行了一次 Nmap 空扫描:
sudo nmap -sN 192.168.0.8
Aug 20 15:06:15 localhost kernel: STATE_INVALID_DROP: IN=enp0s3 OUT= MAC=08:00:27:10:66:1c:00:1f:29:02:0d:5f:08:00 SRC=192.168.0.225 DST=192.168.0.8 LEN=40 TOS=0x00 PREC=0x00 TTL=42 ID=27451 PROTO=TCP SPT=46294 DPT=23 WINDOW=1024 RES=0x00 URGP=0
在这种情况下,我触发了阻止INVALID
数据包的规则,如STATE_INVALID_DROP
标签所示。
所以,现在你说,但是等等。我们刚刚测试的这两个规则在我们目前看到的 firewalld 配置文件中找不到。什么给的?你是对的。这些默认的、预先配置的规则的位置显然是红帽人想要对我们隐藏的。然而,在以下针对 RHEL/CentOS 7 和 RHEL/CentOS 8 的部分中,我们会破坏它们的乐趣,因为我可以告诉你这些规则在哪里。
到目前为止,我们所看到的可能是您在一般使用场景中所需要的全部内容,但是为了进行更精细的控制,您需要了解丰富的语言规则。(是的,他们真的是这么叫的。)
与 iptables 规则相比,丰富的语言规则不那么神秘,更接近普通英语。因此,如果您是编写防火墙规则的新手,您可能会发现丰富的语言更容易学习。另一方面,如果你已经习惯于编写 iptables 规则,你可能会发现富语言的一些元素有点古怪。让我们看一个例子:
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="200.192.0.0/24" service name="http" drop'
在这里,我们添加了一个丰富的规则,阻止从 IPv4 地址的整个地理区块的网站访问。请注意,整个规则由一对单引号包围,每个参数的赋值由一对双引号包围。有了这个规则,我们就说我们在使用 IPv4,我们想静默地阻止http
端口接受来自200.192.0.0/24
网络的数据包。我们没有使用--permanent
选项,因此当我们重新启动机器时,该规则将消失。让我们看看我们的区域在新规则下是什么样子:
[donnie@localhost ~]$ sudo firewall-cmd --info-zone=dmz
dmz (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3
sources:
services: ssh http https
ports: 10000/tcp 636/tcp 637/tcp 638/udp
. . .
. . .
rich rules:
rule family="ipv4" source address="200.192.0.0/24" service name="http"
drop
[donnie@localhost ~]$
富人规则显示在底部。在我们测试了这条规则以确保它能做我们需要它做的事情之后,我们将使它永久存在:
sudo firewall-cmd --runtime-to-permanent
您可以通过将family="ipv4"
替换为family="ipv6"
并提供适当的 IPv6 地址范围来轻松编写 IPv6 规则。
有些规则是通用的,适用于 IPv4 或 IPv6。假设我们想要记录关于 IPv4 和 IPv6 的网络时间协议 ( NTP )数据包的消息,并且我们想要每分钟记录不超过一条消息。创建该规则的命令如下所示:
sudo firewall-cmd --add-rich-rule='rule service name="ntp" audit limit value="1/m" accept'
当然,丰富的语言规则比我们在这里展示的要多得多。但是现在,你知道最基本的。有关更多信息,请参考手册页:
man firewalld.richlanguage
If you go to the official documentation page for Red Hat Enterprise Linux 8, you'll see no mention of rich rules. However, I've just tested them on an RHEL 8-type machine and they work fine.
To read about rich rules, you'll need to go to the documentation page for Red Hat Enterprise Linux 7. What's there also applies to RHEL 8. But even there, there's not much detail. To find out more, see the man page on either RHEL/CentOS 7 or RHEL/CentOS 8.
要使规则永久化,只需使用我们已经讨论过的任何方法。当您这样做时,该规则将显示在默认区域的.xml
文件中。在我的情况下,默认区域仍然设置为公共。那么,让我们看看/etc/firewalld/zones/public.xml
文件:
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>Public</short>
<description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
<service name="ssh"/>
<service name="dhcpv6-client"/>
<service name="cockpit"/>
<rule family="ipv4">
<source address="192.168.0.225"/>
<service name="http"/>
<drop/>
</rule>
</zone>
我们丰富的规则显示在文件底部的rule family
块中。
现在,我们已经介绍了 firewalld 的 RHEL/CentOS 7 和 RHEL/CentOS 8 版本之间的共同点,让我们看看每个不同版本的独特之处。
RHEL 7 及其后代使用 iptables 引擎作为防火墙后端。您不能使用普通的 iptables 命令创建规则。但是,每次使用 firewall-cmd 命令创建规则时,iptables 后端都会创建适当的 iptables 规则,并将其插入适当的位置。您可以使用iptables -L
查看活动规则。这是一个很长的输出的第一部分:
[donnie@localhost ~]$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT all -- anywhere anywhere
INPUT_direct all -- anywhere anywhere
INPUT_ZONES_SOURCE all -- anywhere anywhere
INPUT_ZONES all -- anywhere anywhere
DROP all -- anywhere anywhere ctstate INVALID
REJECT all -- anywhere anywhere reject-with icmp-host-prohibited
就像 Ubuntu 上的 ufw 一样,已经为我们配置了很多。在顶部的INPUT
链中,我们可以看到连接状态规则和阻止无效数据包的规则已经存在。该链的默认策略是ACCEPT
,但是该链的最终规则被设置为REJECT
,这是不允许的。在这两者之间,我们可以看到将其他数据包定向到其他链进行处理的规则。现在,让我们看看下一部分:
Chain IN_public_allow (1 references)
target prot opt source destination
ACCEPT tcp -- anywhere anywhere tcp dpt:ssh ctstate NEW
ACCEPT tcp -- anywhere anywhere tcp dpt:domain ctstate NEW
ACCEPT udp -- anywhere anywhere udp dpt:domain ctstate NEW
Chain IN_public_deny (1 references)
target prot opt source destination
REJECT icmp -- anywhere anywhere icmp host-redirect reject-with icmp-host-prohibited
在很长的输出的底部,我们可以看到IN_public_allow
链,它包含我们为打开防火墙端口创建的规则。紧挨着它的是IN_public_deny
链,它包含了REJECT
规则来阻止不需要的 ICMP 类型。在INPUT
链和IN_public_deny
链中,REJECT
规则都会返回一条 ICMP 消息,通知发送方数据包被阻止。
现在,记住有很多这个IPTABLES -L
输出我们没有显示。所以,你自己看看,看看有什么。当你这样做的时候,你可能会问自己,这些默认规则存储在哪里?为什么我没有在 /etc/firewalld
目录中看到它们?
为了回答这个问题,我不得不做一些相当广泛的调查。出于某种真正奇怪的原因,红帽人完全没有记录这一点。我终于在/usr/lib/python2.7/site-packages/firewall/core/
目录中找到了答案。这里有一组设置初始默认防火墙的 Python 脚本:
[donnie@localhost core]$ ls
base.py fw_config.pyc fw_helper.pyo fw_ipset.py fw_policies.pyc fw_service.pyo fw_zone.py icmp.pyc ipset.pyc logger.pyo rich.py base.pyc fw_config.pyo fw_icmptype.py fw_ipset.pyc fw_policies.pyo fw_test.py fw_zone.pyc icmp.pyo ipset.pyo modules.py rich.pyc base.pyo fw_direct.py fw_icmptype.pyc fw_ipset.pyo fw.py fw_test.pyc fw_zone.pyo __init__.py ipXtables.py modules.pyc rich.pyo ebtables.py fw_direct.pyc fw_icmptype.pyo fw_nm.py fw.pyc fw_test.pyo helper.py __init__.pyc ipXtables.pyc modules.pyo watcher.py ebtables.pyc fw_direct.pyo fw_ifcfg.py fw_nm.pyc fw.pyo fw_transaction.py helper.pyc __init__.pyo ipXtables.pyo prog.py watcher.pyc ebtables.pyo fw_helper.py fw_ifcfg.pyc fw_nm.pyo fw_service.py fw_transaction.pyc helper.pyo io logger.py prog.pyc watcher.pyo fw_config.py fw_helper.pyc fw_ifcfg.pyo fw_policies.py fw_service.pyc fw_transaction.pyo icmp.py ipset.py logger.pyc prog.pyo
[donnie@localhost core]$
完成大部分工作的剧本是ipXtables.py
剧本。如果您查看它,您会看到它的 iptables 命令列表与iptables -L
输出相匹配。
正如我们所见,每当我们在 RHEL/CentOS 7 上使用正常的firewall-cmd
命令做任何事情时,firewalld 都会自动将这些命令翻译成 iptables 规则,并将它们插入适当的位置(或者,如果您已经发出了某种删除命令,它会删除这些规则)。然而,有些事情我们不能用普通的 firewalld-cmd 命令来完成。例如,我们不能使用普通的 firewall-cmd 命令将规则放在特定的 iptables 链或表中。为了做到这一点,我们需要使用直接的配置命令。
firewalld.direct
手册页和红帽网站上的文档都警告您,只有在其他方法都不起作用时,才使用直接配置作为绝对的最后手段。这是因为,与普通的防火墙-cmd 命令不同,直接命令不会自动将新规则放入适当的位置,以便一切正常工作。使用直接命令,您可以通过在错误的位置放置规则来破坏整个防火墙。
在上一节的输出示例中,在默认规则集中,您看到过滤器表的INPUT
链中有一个阻止无效数据包的规则。在用 iptables 阻止无效数据包部分,您可以看到此规则会遗漏某些类型的无效数据包。因此,我们想添加第二条规则来阻止第一条规则遗漏的内容。我们还希望将这些规则放入破坏表的PREROUTING
链中,以增强防火墙性能。为此,我们需要创建一些直接的规则。(如果您熟悉正常的 iptables 语法,这并不难。)那么,让我们开始吧。
首先,让我们验证我们没有任何有效的直接规则,比如:
sudo firewall-cmd --direct --get-rules ipv4 mangle PREROUTING
sudo firewall-cmd --direct --get-rules ipv6 mangle PREROUTING
这两个命令都不会有输出。现在,让我们用以下四个命令为 IPv4 和 IPv6 添加两个新规则:
sudo firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 0 -m conntrack --ctstate INVALID -j DROP
sudo firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 1 -p tcp ! --syn -m conntrack --ctstate NEW -j DROP
sudo firewall-cmd --direct --add-rule ipv6 mangle PREROUTING 0 -m conntrack --ctstate INVALID -j DROP
sudo firewall-cmd --direct --add-rule ipv6 mangle PREROUTING 1 -p tcp ! --syn -m conntrack --ctstate NEW -j DROP
direct
命令语法与普通 iptables 命令非常相似。因此,我不会重复我已经在 iptables 部分介绍过的解释。然而,我确实想指出每个命令中0
和PREROUTING
之后的1
。那些代表规则的优先权。数字越小,优先级越高,规则在链中的位置越高。因此,0
优先级的规则是各自链中的第一规则,而1
优先级的规则是各自链中的第二规则。如果为您创建的每个规则赋予相同的优先级,则不能保证在每次重新启动时顺序保持不变。所以,一定要给每个规则分配不同的优先级。
现在,让我们验证我们的规则是否有效:
[donnie@localhost ~]$ sudo firewall-cmd --direct --get-rules ipv4 mangle PREROUTING
0 -m conntrack --ctstate INVALID -j DROP
1 -p tcp '!' --syn -m conntrack --ctstate NEW -j DROP
[donnie@localhost ~]$ sudo firewall-cmd --direct --get-rules ipv6 mangle PREROUTING
0 -m conntrack --ctstate INVALID -j DROP
1 -p tcp '!' --syn -m conntrack --ctstate NEW -j DROP
[donnie@localhost ~]$
我们可以看到他们是。当你使用iptables -t mangle -L
命令和ip6tables -t mangle -L
命令时,你会看到规则出现在PREROUTING_direct
链中(我只显示一次输出,因为两个命令都一样):
. . .
. . .
Chain PREROUTING_direct (1 references)
target prot opt source destination
DROP all -- anywhere anywhere ctstate INVALID
DROP tcp -- anywhere anywhere tcp flags:!FIN,SYN,RST,ACK/SYN ctstate NEW
. . .
. . .
为了说明它的工作原理,我们可以对虚拟机执行一些 Nmap 扫描,就像我在用 iptables 阻止无效数据包部分向您展示的那样。(如果你不记得怎么做,不要烦恼。您将在即将到来的实践实验室中看到该过程。)然后,我们可以使用sudo iptables -t mangle -L -v
和sudo ip6tables -t mangle -L -v
来查看这两个规则阻塞的数据包和字节。
我们没有在这些命令中使用--permanent
选项,所以它们还不是永久的。现在让我们将它们永久化:
[donnie@localhost ~]$ sudo firewall-cmd --runtime-to-permanent
[sudo] password for donnie:
success
[donnie@localhost ~]$
现在,我们来看看/etc/firewalld
目录。在这里,你会看到一个以前没有的direct.xml
文件:
[donnie@localhost ~]$ sudo ls -l /etc/firewalld
total 20
-rw-r--r--. 1 root root 532 Aug 26 13:17 direct.xml
. . .
. . .
[donnie@localhost ~]$
查看文件内部;你会看到新的规则:
<?xml version="1.0" encoding="utf-8"?>
<direct>
<rule priority="0" table="mangle" ipv="ipv4" chain="PREROUTING">-m conntrack --ctstate INVALID -j DROP</rule>
<rule priority="1" table="mangle" ipv="ipv4" chain="PREROUTING">-p tcp '!' --syn -m conntrack --ctstate NEW -j DROP</rule>
<rule priority="0" table="mangle" ipv="ipv6" chain="PREROUTING">-m conntrack --ctstate INVALID -j DROP</rule>
<rule priority="1" table="mangle" ipv="ipv6" chain="PREROUTING">-p tcp '!' --syn -m conntrack --ctstate NEW -j DROP</rule>
</direct>
官方的红帽 7 文档页面确实包含了直接的规则,但只是简短的。有关更多详细信息,请参见firewalld.direct
手册页。
RHEL 8 及其后代使用 nftables 作为默认防火墙后端。每次使用firewall-cmd
命令创建规则时,都会创建适当的 nftables 规则并将其插入适当的位置。为了查看当前有效的规则集,我们将使用与在 Ubuntu 上使用 nftables 相同的 nft 命令:
[donnie@localhost ~]$ sudo nft list ruleset
. . .
. . .
table ip firewalld {
chain nat_PREROUTING {
type nat hook prerouting priority -90; policy accept;
jump nat_PREROUTING_ZONES_SOURCE
jump nat_PREROUTING_ZONES
}
chain nat_PREROUTING_ZONES_SOURCE {
}
. . .
. . .
[donnie@localhost ~]$
同样,我们可以看到一个非常长的默认预配置防火墙规则列表。(要查看整个列表,请自己运行命令。)你会在/usr/lib/python3.6/site-packages/firewall/core/nftables.py
脚本中找到这些默认规则,每次开机都会运行。
在这一章的开始,我告诉过你,由于 RHEL 7/CentOS 7 和 RHEL 8/CentOS 8 之间的差异,firewalld 可能会有点混乱。但是事情变得非常奇怪。即使直接规则命令创建 iptables 规则,RHEL 8/CentOS 8 使用 nftables 作为 firewalld 后端,您仍然可以创建直接规则。就像在 RHEL/CentOS 7 防火墙部分创建直接规则一样,创建并验证它们。显然,firewalld 允许这些 iptables 规则与 nftables 规则和平共处。但是,如果您需要在生产系统上这样做,请确保在投入生产之前彻底测试您的设置。
红帽 8 文档中没有这方面的内容,但是如果您想了解更多信息,可以查看firewalld.direct
手册页。
通过完成本实验,您将获得一些基本 firewalld 命令的练习:
- 登录到您的 CentOS 7 或 CentOS 8 虚拟机,并运行以下命令。观察每一个之后的输出:
sudo firewall-cmd --get-zones
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zones
- 简要查看涉及
firewalld.zones
的手册页:
man firewalld.zones
man firewalld.zone
(是的,有两个。一个解释区域配置文件,而另一个解释区域本身。)
- 查看所有可用区域的配置信息:
sudo firewall-cmd --list-all-zones
- 查看预定义服务的列表。然后,查看
dropbox-lansync
服务的信息:
sudo firewall-cmd --get-services
sudo firewall-cmd --info-service=dropbox-lansync
- 将默认区域设置为
dmz
。查看zon
相关信息,添加http
和https
服务,然后再次查看zone
信息:
sudo firewall-cmd --permanent --set-default-zone=dmz
sudo firewall-cmd --permanent --add-service={http,https}
sudo firewall-cmd --info-zone=dmz
sudo firewall-cmd --permanent --info-zone=dmz
- 重新加载
firewall
配置,再次查看zone
信息。另外,查看允许的服务列表:
sudo firewall-cmd --reload
sudo firewall-cmd --info-zone=dmz
sudo firewall-cmd --list-services
- 永久打开端口
10000/tcp
并查看结果:
sudo firewall-cmd --permanent --add-port=10000/tcp
sudo firewall-cmd --list-ports
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
sudo firewall-cmd --info-zone=dmz
- 删除您刚刚添加的端口:
sudo firewall-cmd --permanent --remove-port=10000/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
sudo firewall-cmd --info-zone=dmz
- 添加丰富的语言规则来阻止 IPv4 地址的地理范围:
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="200.192.0.0/24" service name="http" drop'
- 阻止
host-redirect
和network-redirect
ICMP 类型:
sudo firewall-cmd --add-icmp-block={host-redirect,network-redirect}
- 添加指令以记录所有丢弃的数据包:
sudo firewall-cmd --set-log-denied=all
- 查看
runtime
和permanent
配置,并注意它们之间的区别:
sudo firewall-cmd --info-zone=public
sudo firewall-cmd --info-zone=public --permanent
- 进行
runtime
配置permanent
并验证其生效:
sudo firewall-cmd --runtime-to-permanent
sudo firewall-cmd --info-zone=public --permanent
- 在 CentOS 7 上,您可以使用以下命令查看有效防火墙规则的完整列表:
sudo iptables -L
- 在 CentOS 8 上,您可以使用以下命令查看有效防火墙规则的完整列表:
sudo nft list ruleset
- 创建
direct
规则,以阻止来自破坏表的PREROUTING
链的无效数据包:
sudo firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 0 -m conntrack --ctstate INVALID -j DROP
sudo firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 1 -p tcp ! --syn -m conntrack --ctstate NEW -j DROP
sudo firewall-cmd --direct --add-rule ipv6 mangle PREROUTING 0 -m conntrack --ctstate INVALID -j DROP
sudo firewall-cmd --direct --add-rule ipv6 mangle PREROUTING 1 -p tcp ! --syn -m conntrack --ctstate NEW -j DROP
- 验证
rules
生效,并使其成为permanent
:
sudo firewall-cmd --direct --get-rules ipv4 mangle PREROUTING
sudo firewall-cmd --direct --get-rules ipv6 mangle PREROUTING
sudo firewall-cmd --runtime-to-permanent
- 查看您刚刚创建的
direct.xml
文件的内容:
sudo less /etc/firewalld/direct.xml
- 针对虚拟机对 IPv4 和 IPv6 执行 XMAS Nmap 扫描。然后,观察扫描触发了哪个规则:
sudo nmap -sX ipv4_address_of_CentOS-VM
sudo nmap -6 -sX ipv6_address_of_CentOS-VM
sudo iptables -t mangle -L -v
sudo ip6tables -t mangle -L -v
- 重复步骤 19 ,但这次使用的是窗口扫描:
sudo nmap -sW ipv4_address_of_CentOS-VM
sudo nmap -6 -sW ipv6_address_of_CentOS-VM
sudo iptables -t mangle -L -v
sudo ip6tables -t mangle -L -v
- 查看 firewalld 的主页列表:
apropos firewall
实验到此结束,祝贺你!
在这一章中,我们看了最新的 Linux 防火墙技术 nftables。然后,我们看了 firewalld,它曾经是专门针对红帽类型发行版的,但现在也在 Ubuntu 存储库中可用。
在分配给我的篇幅中,我介绍了使用这些技术设置单主机保护的基础知识。我还展示了一些关于 firewalld 内部的细节,这些细节在其他地方是找不到的,包括在红帽官方文档中。
在下一章中,我们将了解有助于保护您的数据隐私的各种加密技术。到时候见。
- 您会使用哪个 nftables 命令来查看特定规则丢弃了多少数据包?
- RHEL 7 上的防火墙和 RHEL 8 上的防火墙有什么主要区别?
- 哪个防火墙系统自动加载规则?
- firewalld 以下列哪种格式存储规则?
a .
.txt
T5】b ..config
T6】c ..html
T7】d ..xml
- 您会使用哪个 nft 命令将一个规则置于另一个规则之前?
a .
insert
T5】b .add
T6】c .place
T7】d .position
- 您会使用以下哪个命令来列出系统上的所有防火墙区域?
a .
sudo firewalld --get-zones
T5】b .sudo firewall-cmd --list-zones
T6】c .sudo firewall-cmd --get-zones
T7】d .sudo firewalld --list-zones
- 以下关于 nftables 的陈述中,哪一项是错误的? A .有了 nftables,规则被自动添加。 B .对于 nftables,表指的是特定的协议家族。 C .通过 nftables,端口及其相关规则被捆绑到区域中。 D .使用 nftables,您可以用普通的 bash shell 脚本编写脚本,也可以用内置于 nftables 中的脚本引擎编写脚本。
- 您的系统设置了防火墙,您需要打开端口
10000/tcp
。您会使用以下哪个命令? a .sudo firewall-cmd --add-port=10000/tcp
b .sudo firewall-cmd --add-port=10000
c .sudo firewalld --add-port=10000
d .sudo firewalld --add-port=10000/tcp
- nfable wiki:https://wiki . nfable . org/wiki-nfable/index . PHP/main _ page
- RHEL 7 的防火墙文档:https://access . RedHat . com/documentation/en-us/red _ hat _ enterprise _ Linux/7/html/security _ guide/sec-use _ firewall
- RHEL 防火墙文档 8:https://access . RedHat . com/documents/en-us/red _ hat _ enterprise _ Linux/8/html/secure _ networks/使用和配置防火墙 _ secure-networks
- 防火墙主页:https://firewalld.org/
- nftables 示例:https://wiki.gentoo.org/wiki/Nftables/Examples