Bootstrap

firewalld(8) policies

简介

前面的文章中我们介绍了firewalld的一些基本配置以及NAT的相关配置。在前面的配置中,我们所有的策略都是与zone相关的,例如配置的rich rule,--direct,以及NAT,并且这些配置都是数据包进入zone或者从zone发出时设置的策略。

我们在介绍firewalld的基本原理时候就介绍过,firewalld zone处理数据时的一些原则,更加详细的内容在firewalld(1)基本原理中都有介绍。本篇文章主要介绍firewalld的policies,即如何管理进入和离开某个区域的流量。

firewalld中,策略(Policies)是一种用于在防火墙的不同区域(Zones)之间应用一系列规则的方法。每个策略都定义了一组入口区域(Ingress Zones)和一组出口区域(Egress Zones),这些区域之间的流量将受到策略中定义的规则的影响。

策略policies

策略是一系列规则的集合,这些规则应用于在两个或多个区域之间流动的流量。策略以有状态的单向方式影响流量,例如从区域A到区域B。这种设计允许实现异步的过滤策略。

策略与区域的关系

策略通过指定一组入口区域和一组出口区域来定义与区域的关系。例如,如果入口区域包含“trusted”且出口区域包含“public”,则该策略将影响从“trusted”区域流向“public”区域的所有流量。但需要注意,由于策略是单向的,因此它不会应用于从“public”到“trusted”的流量。入口和出口区域集合中可以包含多个区域。

策略与区域的主要区别

尽管策略和区域在功能上有所重叠,但它们之间存在一些关键的区别:

  1. 方向性:策略允许在所有方向上进行过滤:输入(input)、输出(output)和转发(forwarding)。相比之下,区域通常只支持输入过滤,这对于终端站点的防火墙来说是足够的。但是,对于网络级别的过滤或代表虚拟机和容器进行的过滤,需要更灵活的东西,即策略。

  2. 灵活性:策略提供了更高的灵活性,允许根据流量流动的特定方向来定义规则。

  3. 内部实现:现代 firewalld 中的区域实际上是作为策略集合来内部实现的。这意味着区域可以看作是策略的一种特殊形式或子集。

激活策略的条件

策略只有满足以下条件时才会生效:

  1. 入口区域列表:至少包含一个常规区域或单个符号区域。
  2. 出口区域列表:至少包含一个常规区域或单个符号区域。
  3. 非符号区域的激活状态:对于非符号区域,该区域必须处于激活状态,即必须有接口或源被分配给它。

如果策略未激活,则它不会产生任何效果。

符号区域

firewalld中,除了常规的区域(如publicinternal等)外,还定义了一些符号区域来填补特定场景下的需求。这些符号区域在处理数据包过滤时具有独特的用途和限制。。例如,没有区域能表示流向或来自运行firewalld的主机的流量。因此,引入了一些符号区域来填补这些空白。但符号区域在入口或出口区域集合中是唯一的,例如,不能在入口区域中同时使用“public”和“HOST”。

符号区域包括:

  • HOST
    • 此符号区域用于流向或从运行firewalld的主机流出的流量。这对应于netfilter(iptables/nftables)的INPUT和OUTPUT链。
      • 在入站区域列表中:当HOST用于入站区域列表时,它实际上应用于OUTPUT链。这意味着它过滤的是由主机自身发出的流量。尽管这里提到的是“入站”,但在这个上下文中,它是指从主机视角出发的“出站”流量,即主机发出的流量。
      • 在出站区域列表中:当HOST用于出站区域列表时,它实际上应用于INPUT链。这看起来有些反直觉,因为“出站”通常指的是离开主机的流量,但在这里,它是指进入主机的流量(从主机防火墙的视角来看)。也就是说,它过滤的是进入主机的流量。
  • ANY
    • ANY符号区域在入站和出站区域中充当通配符的角色,但不包括HOST区域。它在你希望某个策略应用于除了主机自身以外的所有区域时非常有用。
      • 在入站区域列表中:当ANY用于入站区域列表时,它适用于来自任何区域(除了主机自身)的流量。这意呀着它过滤的是进入主机的外部流量。
      • 在出站区域列表中:当ANY用于出站区域列表时,它适用于离开主机前往任何区域(除了主机自身)的流量。这意呀着它过滤的是从主机发出的、前往外部目标的流量。

预定义策略

防火墙附带了一些预定义的策略。默认情况下,这些选项可能是活动的,也可能不是活动的。

例如默认的预定义策略
allow-host-ipv6

policy配置文件结构

文件位置

  • 用户自定义策略:通常存放在 /etc/firewalld/policies/ 目录下。这里的策略文件是用户可以自定义和修改的。
  • 系统默认策略:可能存放在 /usr/lib/firewalld/policies/ 目录下。这些策略文件通常是系统预定义的,用户不应直接修改它们,除非有特殊需要并且了解可能的影响。

文件命名

策略文件的命名规则是 policy_name.xml,其中 policy_name 是策略的名称这个命名规则确保了文件名在系统中的唯一性,并简化了策略的识别和管理。

命令结构

<?xml version="1.0" encoding="utf-8"?>
<policy [version="versionstring"] [target="CONTINUE|ACCEPT|REJECT|DROP"] [priority="priority"]>
    [ <ingress-zone name="zone"/> ]
    [ <egress-zone name="zone"/> ]
    [ <short>short description</short> ]
    [ <description>description</description> ]
    [ <service name="string"/> ]
    [ <port port="portid[-portid]" protocol="tcp|udp|sctp|dccp"/> ]
    [ <protocol value="protocol"/> ]
    [ <icmp-block name="string"/> ]
    [ <masquerade/> ]
    [ <forward-port port="portid[-portid]" protocol="tcp|udp|sctp|dccp" [to-port="portid[-portid]"] [to-addr="IP address"]/> ]
    [ <source-port port="portid[-portid]" protocol="tcp|udp|sctp|dccp"/> ]
    [
        <rule [family="ipv4|ipv6"] [priority="priority"]>
            [ <source address="address[/mask]"|mac="MAC"|ipset="ipset" [invert="True"]/> ]
            [ <destination address="address[/mask]"|ipset="ipset" [invert="True"]/> ]
            [
                <service name="string"/> |
                <port port="portid[-portid]" protocol="tcp|udp|sctp|dccp"/> |
                <protocol value="protocol"/> |
                <icmp-block name="icmptype"/> |
                <icmp-type name="icmptype"/> |
                <masquerade/> |
                <forward-port port="portid[-portid]" protocol="tcp|udp|sctp|dccp" [to-port="portid[-portid]"] [to-addr="address"]/>
            ]
            [
                <log [prefix="prefix text"] [level="emerg|alert|crit|err|warn|notice|info|debug"]> [<limit value="rate/duration"/>] </log> |
                <nflog [group="group id"] [prefix="prefix text"] [queue-size="threshold"]> [<limit value="rate/duration"/>] </nflog>
            ]
            [ <audit> [<limit value="rate/duration"/>] </audit> ]
            [
                <accept> [<limit value="rate/duration"/>] </accept> |
                <reject [type="rejecttype"]> [<limit value="rate/duration"/>] </reject> |
                <drop> [<limit value="rate/duration"/>] </drop> |
                <mark set="mark[/mask]"> [<limit value="rate/duration"/>] </mark>
            ]
        </rule>
    ]


</policy>
<policy> 标签
必需:这个标签定义了策略的开始和结束,每个策略配置文件中只能使用一次。
    可选属性:
    version="string":为策略指定一个版本号。
    target="CONTINUE|ACCEPT|REJECT|DROP":指定不匹配任何规则(如端口、服务等)的数据包的处理方式。CONTINUE 是默认值,用于非终端策略;ACCEPT 接受数据包;REJECT 拒绝数据包并发送一个响应;DROP 丢弃数据包且不发送任何响应。

<ingress-zone> 和 <egress-zone> 标签
可选:这两个标签可以多次使用,分别指定入站和出站流量的防火墙区域。它们可以是 firewalld 的区域名称之一,或者是符号区域 HOST 或 ANY。

<short> 标签
可选:这是一个可选的开始和结束标签,用于为策略提供一个更易读的名称。

<description> 标签
可选:这也是一个可选的开始和结束标签,用于提供策略的详细描述。

<service> 标签
可选:这是一个空元素标签,可以多次使用以启用多个服务条目。
    必需属性:
    name="string":要启用的服务的名称。可以使用 firewall-cmd --get-services 命令获取有效的服务名称列表。

<port> 标签
可选:这也是一个空元素标签,可以多次使用以定义多个端口条目。
    必需属性:
    port="portid[-portid]":指定端口号,可以是单个端口号 portid 或端口范围 portid-portid。
    protocol="tcp|udp|sctp|dccp":指定协议类型,可以是 tcp、udp、sctp 或 dccp。

<protocol> 标签
可选:这是一个空元素标签,可以多次使用以定义多个协议条目。
    必需属性:
    value="string":这是一个必需的属性,指定了系统支持的任何协议。要查看系统支持的协议列表,可以查看 /etc/protocols 文件。

<icmp-block> 标签
可选:这也是一个空元素标签,可以多次使用以定义多个 ICMP 阻塞条目。
    必需属性:
    name="string":这是一个必需的属性,指定了要阻塞的 Internet Control Message Protocol (ICMP) 消息类型的名称。可以使用 firewall-cmd --get-icmptypes 命令获取有效的 ICMP 类型列表。

<tcp-mss-clamp> 标签
可选:这是一个空元素标签,可以多次使用(尽管在实践中,通常只设置一次 TCP MSS Clamp 的值)。
    必需属性:
    value="string":这是一个可选的属性。如果留空,则最大段大小(MSS)将设置为“pmtu”(路径最大传输单元)。如果设置了该属性,其值可以是“pmtu”或用户定义的值(该值应大于或等于 536)。TCP MSS Clamp 用于限制 TCP 数据包的最大段大小,这有助于避免在具有较小 MTU(最大传输单元)的网络路径上发生分片。

<masquerade> 标签
可选:这是一个空元素标签,用于启用地址伪装(也称为网络地址转换,NAT)。
限制:这个标签只能使用一次。如果出现在配置文件中,则表示启用了地址伪装。地址伪装允许内部网络(私有地址空间)的设备通过防火墙访问外部网络(公共地址空间),同时隐藏它们的真实 IP 地址。

<forward-port> 标签
可选:这是一个空元素标签,可以多次使用以定义多个端口或数据包转发条目。
    强制属性:
    port="portid[-portid]":指定要转发的本地端口。这可以是一个单独的端口号(portid)或一个端口范围(portid-portid)。
protocol="tcp|udp|sctp|dccp":指定协议类型,可以是 tcp、udp、sctp 或 dccp。
    可选属性:
    to-port="portid[-portid]":指定转发到的目标端口或端口范围。如果省略,则使用 port= 属性的值。
    to-addr="address":指定目标 IP 地址,用于远程转发。这可以是 IPv4 或 IPv6 地址。
<forward-port> 标签允许配置本地或远程端口转发。对于本地转发,通常只需指定 to-port(如果目标端口与源端口不同)。对于远程转发,需要指定 to-addr 和可选的 to-port。

<source-port> 标签
可选:这是一个空元素标签,可以多次使用以定义多个源端口条目。
    所有属性都是强制的:
    port="portid[-portid]":指定源端口或端口范围。
    protocol="tcp|udp|sctp|dccp":指定协议类型。

rich rule命令结构

<rule [family="ipv4|ipv6"] [priority="priority"]>
    [ <source address="address[/mask]"|mac="MAC"|ipset="ipset" [invert="True"]/> ]
    [ <destination address="address[/mask]"|ipset="ipset" [invert="True"]/> ]
    [
        <service name="string"/> |
        <port port="portid[-portid]" protocol="tcp|udp|sctp|dccp"/> |
        <protocol value="protocol"/> |
        <icmp-block name="icmptype"/> |
        <icmp-type name="icmptype"/> |
        <masquerade/> |
        <forward-port port="portid[-portid]" protocol="tcp|udp|sctp|dccp" [to-port="portid[-portid]"] [to-addr="address"]/> |
        <source-port port="portid[-portid]" protocol="tcp|udp|sctp|dccp"/> |
    ]
    [ 
        <log [prefix="prefix text"] [level="emerg|alert|crit|err|warn|notice|info|debug"]> [<limit value="rate/duration"/>] </log> |
        <nflog [group="group id"] [prefix="prefix text"] [queue-size="threshold"]> [<limit value="rate/duration"/>] </nflog>
    ]
    [ <audit> [<limit value="rate/duration"/>] </audit> ]
    [
        <accept> [<limit value="rate/duration"/>] </accept> |
        <reject [type="rejecttype"]> [<limit value="rate/duration"/>] </reject> |
        <drop> [<limit value="rate/duration"/>] </drop> |
        <mark set="mark[/mask]"> [<limit value="rate/duration"/>] </mark>
    ]
</rule>

以上的内容和参数在我们介绍各部分功能时都有对应的介绍,这里可以再次回顾一下firewalld-cmd的命令结构。

实验环境

配置环境和前面所介绍的环境一致。

和我们在做iptables网络防火墙以及rich rule时的环境是一模一样,内部区域为192.168.140.0/24网段,firewalld配置在主机B上,分为内部trusted区域,和外部public区域。其中主机A的网关是主机B 192.168.140.250,主机B的网关是主机C模拟的公网192.168.170.128。
​​​​​

清除除前面配置的安全策略,可以直接编辑?etc/firewalld/下面的文件进行删除,如我们前面配置的trusted.xml,public.xml,direct.xml文件

查看结果

此时除了接口加入到对应的zone和public zone开启了masquerade外,其他安全策略均已经被删除

测试

此时我们的主机A是可以访问主机C的

主机A可以正常访问主机C

配置policy

firewall-cmd --permanent --new-policy myOutputPolicy
firewall-cmd --permanent --policy myOutputPolicy --add-ingress-zone 
firewall-cmd --permanent --policy myOutputPolicy --add-egress-zone 

创建策略

我这里创建一个policy名为trusted-to-public。注意,此时policy的优先级为-1,默认的那个是-15000,target是CONTINUE

设置策略默认动作

将该policy的默认动作改为ACCEPT模式,即为黑名单模式,默认放行所有,然后在前面设置禁止策略。也可以改为白名单模式,默认禁止所有,可以根据自己的需求进行自定义。下面是可以设置的动作类型
ACCEPT: 允许数据包通过防火墙,不会对其进行任何处理或过滤。

CONTINUE: 与 ACCEPT 类似,但继续查看下一个规则进行匹配,而不是停止执行规则集。

DROP: 静默丢弃数据包,不给发送方任何响应,也不通知接收方数据包未到达目的地。

REJECT: 拒绝数据包,并向发送端发出拒绝通知,告知其连接被主机拒绝

修改策略优先级

数值越小优先级越高

设置policy中的策略

在配置policy的策略的时候,会发现其实policy的策略和我们前面配置的zone的策略非常的相似的,只不过一个是在zone中配置策略,一个是在policy中配置策略

我这里配置策略,拒绝从192.168.140.248即主机A来的数据包去ping 192.168.170.128即主机C其他可以正常访问,那么策略该如何设置呢?因为要设置具体的源和目的地,我们可以通过rich rule来配置。rich rule在firewalld中是非常重要的,因为很多复杂的策略都是通过rich rule来完成的。

如上图所示,reject从192.168.140.248-192.168.170.128的icmp流量

策略与zone关联

将该policy应用到对应的zone中,一定要注意方向,如果方向和自己定义的rich rule不匹配,那么该条策略也是不会生效的。此时在zone中,并没有其他显示信息。

测试

此时主机A ping主机C因为我们策略的动作是Reject所以会显示不可达。
但是http还是可以正常访问的。

需求1

前面我们介绍了普通区域之间的策略,那么现在有一个需求,阻止从任何区域到我们本机的icmp数据包。也就是不允许从任何区域对我们主机进行ping操作。这个我们应该如何实现呢,可以配置我们前面介绍的irch rule,然后将该规则配置到我们所有的区域上,这样从任何区域来的icmp数据包都会被阻止。那么还有其他方式吗?

注意该规则前面应该都很好理解,注意最后调用的区域,--add-egress-zone=HOST,这个HOST的含义在前面我们有专门介绍,在这里虽然调用在egress-zone其实是应用在INPUT链上的。我们可以看看如下显示
root@debian:~# iptables -nvL   IN_any-to-local
Chain IN_any-to-local (2 references)
 pkts bytes target     prot opt in     out     source               destination
  206 17608 IN_any-to-local_pre  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    2   472 IN_any-to-local_log  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    2   472 IN_any-to-local_deny  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    2   472 IN_any-to-local_allow  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    2   472 IN_any-to-local_post  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    2   472 ACCEPT     0    --  *      *       0.0.0.0/0            0.0.0.0/0
root@debian:~# iptables -nvL  INPUT_POLICIES_pre
Chain INPUT_POLICIES_pre (3 references)
 pkts bytes target     prot opt in     out     source               destination
  241 20548 IN_allow-host-ipv6  0    --  *      *       0.0.0.0/0            0.0.0.0/0
  202 16968 IN_any-to-local  0    --  ens36  *       0.0.0.0/0            0.0.0.0/0
    0     0 IN_any-to-local  0    --  ens33  *       0.0.0.0/0            0.0.0.0/0
root@debian:~# iptables -nvL  IN_trusted
Chain IN_trusted (1 references)
 pkts bytes target     prot opt in     out     source               destination
   28  2352 INPUT_POLICIES_pre  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    0     0 IN_trusted_pre  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    0     0 IN_trusted_log  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    0     0 IN_trusted_deny  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    0     0 IN_trusted_allow  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    0     0 IN_trusted_post  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    0     0 INPUT_POLICIES_post  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    0     0 ACCEPT     0    --  *      *       0.0.0.0/0            0.0.0.0/0
root@debian:~# iptables -nvL  INPUT_ZONES
Chain INPUT_ZONES (1 references)
 pkts bytes target     prot opt in     out     source               destination
   28  2352 IN_trusted  0    --  ens33  *       0.0.0.0/0            0.0.0.0/0           [goto]
  435 36844 IN_public  0    --  ens36  *       0.0.0.0/0            0.0.0.0/0           [goto]
    0     0 IN_block   0    --  *      *       0.0.0.0/0            0.0.0.0/0           [goto]
root@debian:~# iptables -nvL  INPUT
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
  523 35381 ACCEPT     0    --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED,DNAT
    0     0 ACCEPT     0    --  lo     *       0.0.0.0/0            0.0.0.0/0
    0     0 DROP       0    --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate INVALID
  477 40372 INPUT_direct  0    --  *      *       0.0.0.0/0            0.0.0.0/0
  477 40372 INPUT_ZONES  0    --  *      *       0.0.0.0/0            0.0.0.0/0
    0     0 REJECT     0    --  *      *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-host-prohibited
root@debian:~#

 我们配置的策略是在IN_any-to-local_pre-------->IN_any-to-local-------->INPUT_POLICIES_pre------->IN_trusted------->INPUT_ZONE-------->INPUT

测试

192.168.170.200在public此时已经无法ping通了

 

此时从主机Aping,192.168.140.250是在trusted区域,也是无法ping通的

注意:

此时从主机B去ping主机A和主机C能否ping通呢?

此时可以ping通,因为我们配置的policy是有方向性的,在策略调用的时候一定要注意策略配置的方向和调用的方向保持一致。

需求2

        在需求1中我们用HOST过滤了从外部任何区域来的到达本机的icmp流量。那么此时我想要过滤任何从本机发出的http流量应该如何做呢?也就是说从本机发出的,目的地址是80端口的不管从那个区域发出,都是不允许的。

先定义一个策略名为local-to-public,注意这个public只是一个名字,与public区域无关。名字可以根据自己的需求自行设置,所有http服务全部被reject
将该策略应用到ingress-zone位HOST,HOST在ingress zone意味着,从本机发出的流量,而egress是ANY表示任何区域。那么该策略的含义就为从本机发出的http流量都会被reject。HOST和ANY在上面有具体的介绍,需要多理解多测试便可以充分掌握这两个参数的含义。

测试

此时主机B已经不能发起任何http流量了
主机A访问主机C的80端口依旧可以正常访问。

总结

通过使用policy我们可以设置基于zone之间的安全策略,并且可以将不同的应用进行整合,不同的应用类型设置不通的policy,提升策略的灵活度。可以根据自己的需求,自定义自己的安全策略模式和风格,这样当策略数量非常庞大且复杂的时候,可以极大的提升策略的可视化程度,提升管理效率。

;