firewalld 基本概念

firewalld像iptables、nftables等一样,本身并不是防火墙,只是防火墙管理工具,通过定义防火墙规则最终交由内核的netfilter进行包过滤实现防火墙功能。

firewalld软件包提供了firewalld daemon和一系列工具如firewall-cmd、firewall-config、firewall-offline-cmd等。

firewalld由d-bus层和core层构成。d-bus层提供api,其它程序(如firewall-cmd、firewall-config、firewall-applet、甚至是你自己编写的程序或守护进程)通过调用api可定义防火墙规则或查询防火墙状态等等。core层为d-bus层提供服务,负责和后端交互。架构图如下:

image.png

备注:firewalld提供的firewall-offline-cmd不调用d-bus层,而是直接调用core层中的IO来修改防火墙规则。

澄清概念:

  • 许多人觉得执行systemctl stop firewalld可以关闭防火墙功能,就认为firewalld是防火墙,这个理解是错误的。执行systemctl stop firewalld,实际上是对底层包过滤规则清除(类似于nft flush ruleset 或iptables -F ,清除当前的ruleset)相当于关闭了防火墙。
  • firewalld定义的防火墙规则要被内核的netfilter理解,中间的翻译任务由iptables或nftables进行,也就是firewalld后端需要iptables或nftables。

firewalld、iptables和nftables关系

在centos7及之前版本中,iptables也提供了类似firewalld的daemon,用户可以选择iptables或firewalld daemon其一。centos8已弃用iptables,只用nftables。nftables和iptables负责的应该属于数据包过滤框架。nftables是从linux内核3.13开始出现,旨在替代现存的 {ip,ip6,arp,eb}_tables(本文统称为iptables)。

image.png

firewalld zone、network interface、connection关系

firewalld易用特点就是使用zone、service简化防火墙配置任务,另外一个优势就是动态管理。

zone是firewalld一切的基础,在zone中可配置各种规则,比如根据service/port(经常使用)、source等定义,或定义rich rule(富规则),所以说zone是预先定义的一组规则集。这样zone就实现了对网络可信等级的划分。firewalld缺省定义了9个zone(当然可以对其可修改,也可以自己创建额外的zone)。

通过对网卡(实际上是connection,即网卡配置文件。虽然一个网卡可有多个配置文件,但同时只能有一个活动的配置文件,可以在配置文件中使用zone=来定义connection所属zone)切换zone,可快速将网络置于不同的可信等级。

此外,firewalld zone也提供了类似路由器一些功能,如端口转发、ip地址伪装等。

归入zone顺序

对于一个接受到的请求具体使用哪个zone,firewalld是通过三种方法来判断的:

  • source,也就是源地址
  • interface,接收请求的网卡
  • firewalld.conf中配置的默认zone

这三个的优先级按顺序依次降低,也就是说如果按照source可以找到就不会再按interface去查找,如果前两个都找不到才会使用第三个,也就是firewalld.conf中配置的默认zone。

zone分类

zone名称默认配置
trusted允许所有流量
home拒绝除和传出流量相关的,以及ssh,mdsn,ipp-client,samba-client,dhcpv6-client预定义服务之外其它所有传入流量
internal和home相同
work拒绝除和传出流量相关的,以及ssh,ipp-client,dhcpv6-client预定义服务之外的其它所有传入流量
public拒绝除和传出流量相关的,以及ssh,dhcpv6-client预定义服务之外的其它所有传入流量,新加的网卡默认属于public zone
external拒绝除和传出流量相关的,以及ssh预定义服务之外的其它所有传入流量,属于external zone的传出ipv4流量的源地址将被伪装为传出网卡的地址。
dmz拒绝除和传出流量相关的,以及ssh预定义服务之外的其它所有传入流量
block拒绝除和传出流量相关的所有传入流量
drop拒绝除和传出流量相关的所有传入流量(甚至不以ICMP错误进行回应)

预定义服务配置

  • firewall-cmd --get-services: 查看预定义服务列表
  • /usr/lib/firewalld/services/*.xml:预定义服务的配置

三种配置方法

  • firewall-config (firewall-config包)图形工具
  • firewall-cmd (firewalld包)命令行工具
  • /etc/firewalld/zones 添加配置文件

firewall-cmd

添加使用 --permanent 选项表示设置成永久生效,需要重新启动 firewalld 服务或执行 firewall-cmd --reload 命令
重新加载防火墙规则时才会生效。若不带有此选项,表示用于设置运行时规则,但是这些规则在系统或firewalld服务重启、停止时配置将失效。

--runtime-to-permanent :将当前的运行时配置写入规则配置文件中,使之成为永久性配置。

常用命令

--get-zones     列出所有可用区域
--get-default-zone  查询默认区域
--set-default-zone=<ZONE> 设置默认区域
--get-active-zones  列出当前正使用的区域
--add-source=<CIDR>[--zone=<ZONE>]  添加源地址的流量到指定区域,如果无--zone= 选项,使用默认区域
--remove-source=<CIDR> [--zone=<ZONE>]  从指定区域删除源地址的流量,如无--zone= 选项,使用默认区域
--add-interface=<INTERFACE>[--zone=<ZONE>]  添加来自于指定接口的流量到特定区域,如果无--zone= 选项,使用默认区域
--change-interface=<INTERFACE>[--zone=<ZONE>]   改变指定接口至新的区域,如果无--zone= 选项,使用默认区域
--add-service=<SERVICE> [--zone=<ZONE>] 允许服务的流量通过,如果无--zone= 选项,使用默认区域
--add-port=<PORT/PROTOCOL>[--zone=<ZONE>] 允许指定端口和协议的流量,如果无--zone= 选项,使用默认区域
--remove-service=<SERVICE> [--zone=<ZONE>]  从区域中删除指定服务,禁止该服务流量,如果无--zone= 选项,使用默认区域
--remove-port=<PORT/PROTOCOL>[--zone=<ZONE>]    从区域中删除指定端口和协议,禁止该端口的流量,如果无--zone= 选项,使用默认区域
--reload    删除当前运行时配置,应用加载永久配置
--list-services  查看开放的服务
--list-ports   查看开放的端口
--list-all [--zone=<ZONE>]     列出指定区域的所有配置信息,包括接口,源地址,端口,服务等,如果无--zone= 选项,使用默认区域

区域管理

(1)显示当前系统中的默认区域
   firewall-cmd --get-default-zone
(2)显示默认区域的所有规则
   firewall-cmd --list-all
(3)显示当前正在使用的区域及其对应的网卡接口
   firewall-cmd --get-active-zones
(4)设置默认区域
   firewall-cmd --set-default-zone=home
   firewall-cmd --get-default-zone

服务管理

(1)查看默认区域内允许访问的所有服务
   firewall-cmd --list-service

(2)添加httpd 服务到public 区域
   firewall-cmd --add-service=http --zone=public

(3)查看public区域已配置规则
   firewall-cmd --list-all --zone=public

(4)删除public区域的httpd服务
   firewall-cmd --remove-service=http --zone=public

(5)同时添加httpd、https服务到默认区域,设置成永久生效
   firewall-cmd --add-service=http --add-service=https --permanent
   firewall-cmd --reload
   firewall-cmd --list-al1

富规则(rich-rules)

rich规则比基本的firewalld语法实现更强的功能,不仅实现允许/拒绝,还可以实现日志syslog和auditd,也可以实现端口转发,伪装和限制速率

匹配顺序

规则实施顺序:

  • 该区域的端口转发,伪装规则
  • 该区域的日志规则
  • 该区域的允许规则
  • 该区域的拒绝规则

每个匹配的规则生效,所有规则都不匹配,该区域默认规则生效

语法

rule
  [source]
  [destination]
  service|port|protocol|icmp-block|masquerade|forward-port
  [log]
  [audit]
  [accept|reject|drop]

规则选项

--add-rich-rule
--remove-rich-rule
--query-rich-rule
--list-rich-rule

family:指定该规则仅应用于IPv4数据包:如果未提供此关键字,则该规则将同时应用于IPv4和IPv6。
source address:提供数据包必须具有的源地址,才能使用源地址触发规则。
service:指定了规则的服务类型,在本例中为ssh。
reject/drop/accept :提供了数据包与规则匹配时要执行的操作,在本例中为reject.

  • REJECT动作会返回一个拒绝(终止)数据包(TCP FIN或UDP-ICMP-PORT-UNREACHABLE),明确的拒绝对方的连接动作。
  • DROP动作只是简单的直接丢弃数据,并不反馈任何回应。需要Client等待超时,Client容易发现自己被防火墙所阻挡。

访问限制

# 拒绝从192.168.0.11的所有流量,当address 选项使用source 或 destination时,必须用family= ipv4 |ipv6
firewall-cmd --permanent --zone=public --add-rich-rule='rule family=ipv4 source address=192.168.0.11/32 reject'

# 限制每分钟只有两个连接到ftp服务
firewall-cmd --add-rich-rule='rule service name=ftp limit value=2/m accept'

# 抛弃esp( IPsec 体系中的一种主要协议)协议的所有数据包
firewall-cmd --permanent --add-rich-rule='rule protocol value=esp drop'

# 接受所有192.168.1.0/24子网端口5900-5905范围的TCP流量
firewall-cmd --permanent --zone=vnc --add-rich-rule='rule family=ipv4 source address=192.168.1.0/24 port port=5900-5905 protocol=tcp accept'

日志规则

语法

log [prefix="<PREFIX TEXT>" [level=<LOGLEVEL>] [limit value="<RATE/DURATION>"]

<LOGLEVEL> 可以是emerg,alert, crit, error, warning, notice, info, debug.
<DURATION> s:秒, m:分钟, h:小时, d:天
audit [limit value="<RATE/DURATION>"]

实例

#接受ssh新连接,记录日志到syslog的notice级别,每分钟最多三条信息
firewall-cmd --permanent --zone=work --add-rich-rule='rule service name="ssh" log prefix="ssh " level="notice" limit value="3/m" accept'

#从2001:db8::/64子网的DNS连接在5分钟内被拒绝,并记录到日志到audit,每小时最大记录一条信息
firewall-cmd --add-rich-rule='rule family=ipv6 source address="2001:db8::/64" service name="dns" audit limit value="1/h" reject' --timeout=300

firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=172.25.X.10/32 service name="http" log level=notice prefix="NEW HTTP " limit value="3/s" accept'

# 测试
firewall-cmd --reload
tail -f /var/log/messages
curl http://serverX.example.com

伪装NAT

检查防火墙是否支持伪装

firewall-cmd --permanent --zone=<ZONE> --add-masquerade
firewall-cmd --query-masquerade  #检查是否允许伪装
firewall-cmd --add-masquerade   #允许防火墙伪装IP
firewall-cmd --remove-masquerade #禁止防火墙伪装IP

实例

firewall-cmd --add-rich-rule='rule family=ipv4 source address=192.168.0.0/24 masquerade'

端口转发

端口转发:将发往本机的特定端口的流量转发到本机或不同机器的另一个端口。通常要配合地址伪装才能实现

firewall-cmd --permanent --zone=<ZONE> --add-forward-port=port=<PORTNUMBER>:proto=<PROTOCOL>[:toport=<PORTNUMBER>][:toaddr=]

说明:toport= 和toaddr= 至少要指定一个

实例
转发传入的连接9527/TCP,到防火墙的80/TCP到public zone 的192.168.0.254

firewall-cmd --add-masquerade  启用伪装
firewall-cmd --zone=public --add-forward-port=9527:proto=tcp:toport=80:toaddr=192.168.0.254

rich规则的port转发语法

forward-port port=<PORTNUM> protocol=tcp|udp [to-port=<PORTNUM>] [to-addr=<ADDRESS>]

转发从192.168.0.0/24来的,发往80/TCP的流量到防火墙的端口8080/TCP

firewall-cmd --zone=work --add-rich-rule='rule family=ipv4 source address=192.168.0.0/24 forward-port port=80 protocol=tcp to-port=8080'

转发从 172.25.x.10/32来的,发往 443/TCP 端口的流量到防火墙的端口 22/TCP

firewall-cmd --permanent --add-rich-rule 'rule family=ipv4 source address=172.25.X.10/32 forward-port port=443 protocol=tcp to-port=22'
firewall-cmd --reload
ssh -p 443 serverX.example.com

项目实例

目标:验证rich规则的潜在问题

我们允许192.168.50.0/24所有网络访问局域网的10.10.10.1上的远程桌面
允许192.168.50.17的主机通过ssh访问防火墙
拒绝192.168.50.0/24其他主机通过ssh访问防火墙
拒绝所有来自192.168.50.0/24对防火墙的ping请求

image.png

允许192.168.50.0/24所有主机网络访问局域网的10.10.10.1上的远程桌面

firewall-cmd --zone=external --add-forward-port=\ 
port=3389:proto=tcp:toport=3389:toaddr=10.10.10.1 

允许192.168.50.17的主机通过ssh访问防火墙

firewall-cmd --zone=external --add-rich-rule="rule \ 
family="ipv4" \ 
source address="192.168.50.17" \ 
service name="ssh" \ 
log prefix=\"ssh connect from:\" \
 level="notice" \
 accept" 

拒绝192.168.50.0/24的其他主机访问防火墙的ssh服务

firewall-cmd  --zone=external --add-rich-rule="rule \
family="ipv4" \ 
source address="192.168.50.0/24" \ 
service name="ssh" \ 
log prefix=\"ssh reject from:\" \
level="notice" \
drop" 

拒绝所有来自192.168.50.0/24对防火墙的ping请求

firewall-cmd  --zone=external --add-rich-rule="rule \
protocol value="icmp" \
reject" 

查看 rich 规则

firewall-cmd --zone=external --list-rich-rules 

实验的重点:就是要验证第一条rich的规则是否会被优先执行,还是第二条drop规则会先于第一条accept规则执行?

image.png

我们在192.168.50.17的主机上测试一下能否ssh到防火墙?如下图ssh请求无响应且没有错误提示返回,这里至少已经证实

  • 即便在规则列表中accept规则位置先于drop规则,但是drop规则总是会优先于accept规则被匹配。
  • 已经无法ping通防火墙,并且由防火墙返回错误信息。

当前的rich规则的一个问题是,它们是基于规则操作来组织的。 日志规则始终在拒绝规则之前发生。 拒绝规则总是发生在允许规则之前。 这导致用户感到困惑,因为它隐式地对规则进行了重新排序。 这也使得不可能添加全面的rich规则来拒绝流量。因此firewalld的rich规则执行逻辑如下:

  • 日志规则
  • drop/reject规则
  • accept规则

防火墙的端口转发测试,192.168.50.17是一台Ubuntu主机,我们使用Remmina远程桌面软件连接到内网10.10.10.1的主机,
OK,端口转发也是成功的

那么实现遗留的问题,就是rich规则如何能如我们所愿地定的规则顺序执行?这正是本文的重点

Rich规则的priority字段

新版的firewalld添加了新的priority字段。它可以是-32768到32767之间的任何数字,其中数字越小,优先级越高。此范围足够大,以允许从脚本或其他实体自动生成规则。

那么就非常简单:只需在你想优先执行的rich规则中给priority字段定义一个足够小的负数,就能确保能优先于其他drop/reject规则被firewalld匹配。如下图所示

firewall-cmd --zone=external --add-rich-rule="rule \ 
priority="-100" \
family="ipv4" \ 
source address="192.168.50.17" \ 
service name="ssh" \ 
log prefix=\"ssh connect from:\" \
level="notice" \
accept" 

Ok,此时,accept的规则优先于其他drop/reject规则被匹配,这正是我们所希望的。

要记得
将上面的运行时配置转化为持久配置,执行下面命令

firewall-cmd --runtime-to-permanent

nftables

  • nftables 框架替换了 iptables 默认网络数据包过滤工具,可以通过nft命令可编程式的配置防火墙。
  • firewalld也是Linux的防火墙,同时支持iptables和nftables,最新版本默认使用nftables。
  • 简单的说firewalld是基于nftfilter防火墙的用户界面工具。而iptables和nftables是命令行工具。
  • firewalld引入区域的概念,可以动态配置,让防火墙配置及使用变得简便。
  • 从用户的角度来看,nftables添加了一个名为nft的新工具,它取代了iptables,arptables和ebtables中的所有其他工具。
  • 从架构的角度来看,它还取代了处理包过滤规则集的运行时评估的内核部分。

操作

# 查询所有表名
nft list tables

# 查看某个表的规则
nft list table filter

# 增加表
nft add table fillter 
# 增加链
nft add chain filter input { type filter hook input priority 0 \; } # 要和hook(钩子)相关连 
# 增加规则
nft add rule filter input tcp dport 22 accept

只需要把上面的 add 改为 delete 即可

更改链名用rename
更改规则用replace

nft list ruleset # 列出所有规则
nft list tables # 列出所有表
nft list table filter # 列出filter表
nft chain filter input # 列出filter表input链
以上命令后面也可以加 -nn  用于不解析ip地址和端口
加 -a 用于显示 handles