Skip to main content

Firewalld 初步抵抗端口扫描 - 以及 Zone、Traget、Zone Drifting 概念

偶然使用 nmap 扫描了自己的 IP,突然发现,即使被 Firewalld “关闭/未开启”的端口也可以被扫描到,即使无法连接。

要理解此行为,我们需要进一步了解 Firewalld。
首先,Firewalld 工作于OSI 模型中的 3-4 层,ICMP 位于第 3 层(网络层),而 TCP/UDP 及其端口概念位于第 4 层(传输层)。
Firewalld 是一个前端,它负责帮你操作真正的后端 nftables(一种内核数据包分类框架),nftables 也是 iptables 的替代品。

注:iptables 有两个变体:

  • legacy 通常被称为 iptables-legacy 基于 netfilter;
  • nf_tables 通常被称为 iptables-nft 基于 nftable;

当然,Firewalld 不能 “关闭” 一个端口,否则其他程序该怎么用呢?而是在这之前“过滤”流量。

就像 nftfilter 中有 5 个 hook:
(nftables 重用了现有的 netfilter 子系统,例如现有的 hook 基础设施、连接跟踪系统、NAT、用户空间队列和日志子系统。)

  • NF_IP_PRE_ROUTING:接收到的包进入协议栈后立即触发此 hook,在进行任何路由判断 (将包发往哪里)之前;
  • NF_IP_LOCAL_IN:接收到的包经过路由判断,如果目的是本机,将触发此 hook;
  • NF_IP_FORWARD:接收到的包经过路由判断,如果目的是其他机器,将触发此 hook;
  • NF_IP_LOCAL_OUT:本机产生的准备发送的包,在进入协议栈后立即触发此 hook;
  • NF_IP_POST_ROUTING:本机产生的准备发送的包或者转发的包,在经过路由判断之后, 将触发此 hook;

包经过协议栈时会触发内核模块注册在这里的处理函数 。触发哪个 hook 取决于包的方向(ingress/egress)、包的目的地址、包在上一个 hook 点是被丢弃还是拒绝等等。

然后我们再来了解几个 Firewalld 中的概念,本文假设你已经有了基本的防火墙概念。

本文基于 Red Hat Enterprise Linux 8 中的 Firewalld 0.9.3。

处理流程

对于进入系统的数据包,首先检查其源地址。

检查数据来源的源地址:

  1. 若数据包源地址关联到特定的 Zone,则执行该 Zone 中设定的规则。
  2. 若数据包源地址未关联到特定的 Zone,若该网卡接口关联到了特定的 Zone,则执行该 Zone 中设定的规则。
  3. 若该网卡接口未关联到了特定的 Zone,则执行默认 Zone 中设定的规则。

Zone 区域

Firewalld 有一个 Zone 的概念,是一个规则的集合,可以绑定源地址(Sources),网卡接口(Interfaces)等。一个网卡接口/源地址只能在一个 Zone 中,而一个 Zone 中可以包含很多网卡接口/源地址。

有这么以下几种预配置 Zone,由上至下安全性逐渐提高:

  • trusted  允许所有数据包进出;
  • home  拒绝传入的包,除非与传出的包相关;默认允许 ssh、mdns、ipp-client、amba-client、dhcpv6-client 服务传入连接;
  • Internal  等同于 home Zone;
  • work  拒绝传入的包,除非与传出的包相关;默认允许 ssh、ipp-client与dhcpv6-client 服务传入连接;
  • public  拒绝传入的包,除非与传出的包相关;默认允许 ssh、dhcpv6-client 服务传入连接;
  • external  拒绝传入的包,除非与传出的包相关;默认允许 ssh 服务传入连接;
  • dmz  拒绝传入的包,除非与传出的包相关;默认允许 ssh 服务传入连接;
  • block  拒绝传入的包,除非与传出的包相关,IPv4 显示 icmp-host-prohibited,IPv6 显示 icmp6-adm-prohibited;
  • drop  扔掉传入的包,除非与传出的包相关;

默认情况下,我们的接口会被绑定至 Public Zone。


Target 目标

然后是 Target 概念,当一个 Zone 通过源地址或网卡接口来处理包时,如果没有明确的规则来匹配,那就通过 targets 来决定如何处理:

  • ACCEPT  通过这个包;
  • %%REJECT%%  拒绝这个包,并返回一个拒绝的 ICMP 回复(由 RFC1122 要求);
  • DROP  丢弃这个包,不回复任何信息;
  • default  相当于增强版的 %%REJECT%%,在 %%REJECT%% 的基础上增加了一些默认配置:

默认情况下,Public Zone 会使用 default target。


Zone Drifting 区域漂移

此功能需要在 /etc/firewalld/firewalld.conf 中将 AllowZoneDrifting 设置为 yes,RHEL 8 中默认为 no。

首先来看一个例子

为了演示优先级,唯一网络接口 eth0 绑定至 Public Zone,并在 Public Zone 中添加 http 服务,然后在 internal Zone 中添加源地址 10.1.1.1,以下命令完成此任务:

firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=internal --add-source=10.1.1.1
firewall-cmd --reload

结果如下:

# firewall-cmd --zone=public --list-all
public (default, active)
  interfaces: eth0
  sources:
  services: dhcpv6-client http
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:
# firewall-cmd --permanent --zone=public --get-target
default
# firewall-cmd --zone=internal --list-all
internal (active)
  interfaces:
  sources: 10.1.1.1
  services: dhcpv6-client mdns samba-client ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:
# firewall-cmd --permanent --zone=internal --get-target
default

当 Zone Drifting 开启时会发生什么?

  • 如果有人尝试从 10.1.1.1 访问 ssh 服务,那么请求将成功,因为首先匹配到了 internal Zone 中的源地址规则。
  • 如果有人试图从其他地方访问 ssh 服务,比如 192.168.0.1,就不会匹配到 internal Zone 中的源地址规则。
    然后,请求将传递到绑定了 eth0 接口的 Public Zone。但是 Public 里面没有明确设置 ssh 服务该如何处理,于是将按照 Zone 的 Target 处理。由于 Public 的 Target 是 default,于是 firewalld 拒绝了这个包,并返回拒绝的 ICMP 回复。

  • 如果有人尝试从 10.1.1.1 访问 http 服务会怎么样?internal Zone 中没有 http 规则,但 internal Zone 的 Target 是default,因此请求将传递到绑定了 eth0 接口的 Public Zone,Public 允许访问此服务,因此请求成功。

当 Zone Drifting 关闭时会发生什么?

  • 如果有人尝试从 10.1.1.1 访问 ssh 服务,那么请求将成功,因为首先匹配到了 internal Zone 中的源地址规则。

  • 如果有人试图从其他地方访问 ssh 服务,比如 192.168.0.1,就不会匹配到 internal Zone 中的源地址规则。
    然后,请求将传递到绑定了 eth0 接口的 Public Zone。但是 Public 里面没有明确设置 ssh 服务该如何处理,于是将按照 Zone 的 Target 处理。由于 Public 的 Target 是 default,于是 firewalld 拒绝了这个包,并返回拒绝的 ICMP 回复。

  • 如果有人尝试从 10.1.1.1 访问 http 服务会怎么样?internal Zone 中没有 http 规则,且没有开启 Zone Drifting,于是 firewalld 拒绝了这个包,并返回拒绝的 ICMP 回复。

再来看看配置中的描述

# AllowZoneDrifting
# Older versions of firewalld had undocumented behavior known as "zone
# drifting". This allowed packets to ingress multiple zones - this is a
# violation of zone based firewalls. However, some users rely on this behavior
# to have a "catch-all" zone, e.g. the default zone. You can enable this if you
# desire such behavior. It's disabled by default for security reasons.
# Note: If "yes" packets will only drift from source based zones to interface
# based zones (including the default zone). Packets never drift from interface
# based zones to other interfaces based zones (including the default zone).
# Possible values; "yes", "no". Defaults to "yes".      ——   /etc/firewalld/firewalld.conf

旧版本的 firewalld 具有称为“Zone Drifting”的未记录行为。
这允许数据包进入多个 Zone - 这是对基于 Zone 的防火墙的一种违反。 然而,一些用户依靠这种行为来拥有一个“包罗万象”的 Zone,例如:默认 Zone。
如果您希望这样的行为,您可以启用此功能。 出于安全原因,它默认禁用。(注:与下方描述冲突
注意:如果配置为 “yes” 数据包只会从基于源的 Zone 漂移到基于接口的 Zone(包括默认 Zone)。
数据包永远不会从基于接口的 Zone 漂移到其他基于接口的 Zone(包括默认 Zone)。
可用值; “yes”、“no”。 默认为“yes”



发生了什么

通过上面的学习,我们可以发现,当 Zone 的 Target 是 default 或 %%REJECT%% 时,遇到不匹配的请求时 Firewalld 会拒绝并返回拒绝回复。
于是 nmap 分析了 Firewalld 拒绝的 ICMP 回复,判断此端口有服务。

但难道你不回复就不能分析了吗,nmap 可以处理没有任何回复的情况且不会影响扫描速度,只能说能分析的数据变少了?


如何初步抵抗端口扫描

将 Public Zone 的 Target 设置为 DROP,不返回任何包,攻击者也就无法进行分析,从而减少攻击面。


使用此命令:
firewall-cmd --permanent --zone=public --set-target=DROP

或修改配置文件 /etc/firewalld/zones/public.xml 开头的 <zone><zone target="DROP">

别忘了  firewall-cmd --reload

注意,设置为 DROP 后 ICMP 协议将被过滤(ping 是 ICMP 协议)。
这是因为 ICMP 是第 3 层协议,没有端口的概念,这与需要绑定到端口的服务不同。
在将 Public Zone 的 Target 设置为 DROP 之前,ping 可以通过防火墙,因为 default target 会进行 Zone 漂移,从而将包转交给允许此协议的 Zone,而 DROP target 会直接丢弃这个包。


使用以下命令:
firewall-cmd --permanent --zone=public --add-rich-rule='rule protocol value="icmp" accept'
有 IPv6 的情况下还应该使用:
firewall-cmd --permanent --zone=public --add-rich-rule='rule protocol value="ipv6-icmp" accept'
不确定是否需要:
firewall-cmd --permanent --zone=public --add-rich-rule='rule icmp-type name="router-advertisement" accept'
firewall-cmd --permanent --zone=public --add-rich-rule='rule icmp-type name="neighbour-advertisement" accept'
firewall-cmd --permanent --zone=public --add-rich-rule='rule icmp-type name="neighbour-solicitation" accept'

或修改配置文件 /etc/firewalld/zones/public.xml
</zone> 前添加:

  <rule>
    <protocol value="icmp"/>
    <accept/>
  </rule>
  <rule>
    <protocol value="ipv6-icmp"/>
    <accept/>
  </rule>

不确定是否需要:

  <rule>
    <icmp-type name="router-advertisement"/>
    <accept/>
  </rule>
    <rule>
    <icmp-type name="neighbour-advertisement"/>
    <accept/>
  </rule>
  <rule>
    <icmp-type name="neighbour-solicitation"/>
    <accept/>
   </rule>

别忘了  firewall-cmd --reload


究竟应该 %%REJECT%% 还是 DROP

这篇博文对此进行了一些探讨:https://www.chiark.greenend.org.uk/~peterb/network/drop-vs-reject

这篇博文认为:

场景 REJECT DROP
应用程序连接到不存在的服务 故障及时报告给用户 应用程序暂停很久,然后失败
使用操作系统 “connect” 进行简单的网络扫描 扫描速度很快 扫描没问题,但前提是设置了超时
使用专业程序(例如 nmap)进行网络扫描 扫描速度很快 扫描速度很快

因此 DROP 不会为敌对势力提供有效的屏障,但会大大降低合法用户运行的应用程序的速度。通常不应使用 DROP。

有状态防火墙和无状态防火墙

有状态防火墙会记录它看到的每个包,当收到下一个包时,会利用这些信息(状态)来判断应该做什么。
因此有状态防火墙通常会默认配置为以下行为:

  • 允许所有出向连接;
  • 禁止所有入向连接;
  • 允许入向连接,只要和出向连接相关联;

我们常见的软件防火墙大多都是有状态防火墙,比如:

  • Windows Defender firewall (Window 防火墙)
  • Uncomplicated Firewall (UFW)
  • Packet Filter (PF)
  • Firewalld

而无状态防火墙不会记录包的状态,只能利用数据包的来源、目的地和其他参数来查明数据是否会构成威胁。
这些参数必须由管理员或制造商通过事先输入。 

推荐扩展阅读

主要参考