CentOS 中的 iptables 与 firewalld 防火墙基本策略

在 CentOS 中的 firewalld 是基于 iptables 和一些其它程序构建的,firewalld 使用了一些更加友好的配置方式来实现了对 iptables 操作。同时还扩展了一些 iptables 本身不支持的功能,例如定时防火墙规则。完整的程序以及库依赖见 firewalld 官网 https://firewalld.org/

iptables 通过操作 Linux 内核 netfilter 模块来针对网络包处理。

在操作命令之前请临时关闭机器的 firewalld 防火墙,同时需要确保有物理机的访问权限(或者虚拟机中的物理终端操作权限),有些规则会屏蔽 SSH 22 端口的访问导致无法后续操作。

# 停用 firewalld
systemctl stop firewalld

iptables 基础概念和操作

策略链 (Policy Chain)

iptables 的过滤规则是通过匹配策略链上的规则来决定不同情况下对包的处理,包含了三种策略链:

INPUT: 通过此链来控制传入的连接和包,例如允许来自某些 IP 的 SSH 传入连接。

FORWARD: 转发控制链接,如果你需要在机器上配置路由功能时会用到此链。

OUTPUT: 传出链,当前主机发送请求到外部时匹配此链,通常没有特殊需求时也不会给它配置特殊规则。

可以通过下列命令来查看当前设置的 iptables 规则:

~]# iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

策略链默认行为

对应的策略链会有默认的行为。意味着定义了未匹配任何规则的时,链对数据包的处理。

使用如下命令查看当前策略链的行为(同时输出了未匹配此链的数据包数量和流量)。

~]# iptables -L -v | grep policy
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
Chain OUTPUT (policy ACCEPT 821 packets, 293K bytes)

如果当前的策略链的行为不是如上所示,可以通过下列命令来设置接受所有数据的行为:

iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

可以尝试如下命令丢弃所有情况下的包:

iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

注意:此时外部主机将无法访问主机,同时主机内部也无法访问外部。

链的响应

有了默认策略链后,可以添加自定义的规则。匹配到符合要求的规则时(例如:数据的目标端口/来源)希望如何处理数据。

常用的链的“响应”有以下三种 :

ACCEPT: 允许所有连接和数据。

DROP: 丢弃连接。发送方会无感知,就像主机不存在一样。

REJECT: 拒绝连接,同时返回错误信息。发送方会知道被防火墙阻止了请求。

可以通过 ping 命令来测试对端机器配置了不同 INPUT 策略链的行为:

ACCEPT:

# iptables -P INPUT ACCEPT

~]# ping 10.1.1.50 -c 1
PING 10.1.1.50 (10.1.1.50) 56(84) bytes of data.
64 bytes from 10.1.1.50: icmp_seq=1 ttl=64 time=0.227 ms

--- 10.1.1.50 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.227/0.227/0.227/0.000 ms

DROP:

# iptables -P INPUT DROP

~]# ping 10.1.1.50 -c 1
PING 10.1.1.50 (10.1.1.50) 56(84) bytes of data.

--- 10.1.1.50 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

REJECT:

# iptables -A INPUT -j REJECT

~]# ping 10.1.1.50 -c 1
PING 10.1.1.50 (10.1.1.50) 56(84) bytes of data.
From 10.1.1.50 icmp_seq=1 Destination Port Unreachable

--- 10.1.1.50 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms

严谨地说,ACCEPT 和 DROP 为链的目标 (target),而 REJECT 为目标扩展 (target extension),所以 REJECT 不能配置给链,只能给链添加 REJECT 规则。详细可见: iptables(8) (TARGETS 节) iptables-extensions(8) (REJECT 节)

链规则

链配置完成后,可以给其添加规则。对于添加规则,使用下列形式的命令:

iptables -A <链名> [可选匹配规则] -j <目标>

目标包括了上述提到的 ACCEPT/DROP/REJECT 之外,还包括了自定义链,以及其它扩展目标。

在尝试后续操作之前,请牢记下列命令重置防火墙:

iptables -X # 删除所有用户定义
iptables -F # 刷新链的规则

# 如果修改了策略链
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

丢弃所有 ICMP 协议

外部 ping 当前主机时会提示超时 ,使用 traceroute 命令路由跟踪时将无法探测当前机器。

iptables -A INPUT -p icmp -j DROP

仅允许连接端口

先配置 INPUT 策略链默认为 DROP 再为其配置允许规则。

iptables -P INPUT DROP
iptables -A INPUT -p tcp --dport=22 -j ACCEPT

丢弃来自指定来源的数据

可以指定丢弃来自固定 IP 的数据:

iptables -A INPUT -s 10.1.1.50 -j DROP

或者是丢弃来源网段下所有的数据:

iptables -A INPUT -s 10.1.1.0/24 -j DROP

自定义链

使用下列命令可以创建链,添加规则后将自定义链加入 INPUT 策略链中:

iptables -N mychain
iptables -A mychain -p ICMP -j DROP
iptables -A INPUT -j mychain

添加完成后配置为:

~]# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
mychain    all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain mychain (1 references)
target     prot opt source               destination
DROP       icmp --  anywhere             anywhere

可以看见 INPUT 策略链的默认行为是 ACCEPT,含有一个任意匹配的规则,目标为 mychain。

所以当匹配输入数据时,所有的数据都会尝试匹配 mychain 的规则。

在 mychain 中,若匹配协议为 ICMP,则丢弃数据。

保存与恢复

若不执行命令来保存配置,所有修改将会在系统重启后被丢弃。

使用 iptables-save 命令来保存修改使重启后生效,同时输出配置保存命令。

使用 iptables-restore 从标准输入读入配置并恢复。

iptables-save > myconfig
iptables-restore < myconfig

firewalld 基础概念与操作

在操作 firewalld 之前确保其已启动。

# 确认状态
systemctl status firewalld
# 启动
systemctl start firewalld

网络区 (ZONE)

防火墙预设了一些网络区,系统默认分配了网络接口 (interface) 到 public 区域。

不同的网络区预定了不通的策略,防火墙预设配置文件定义在 /usr/lib/firewalld/zones 目录下,用户定义的文件在 /etc/firewalld/zones 目录下。详细策略和格式可以参考 firewalld.zone(5) ,或者见文末的链接文章。

在初始状态,通过 firewall-cmd –list-all 命令可以获取默认网络区 public 的状态。

若要修改默认网络区,请编辑 /etc/firewalld/firewalld.conf 文件 DefaultZone 字段。

使用 firewall-cmd –list-all-zones 可以显示所有网络区配置。

~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens192
  sources:
  services: dhcpv6-client ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

开放端口

可以通过直接的命令开放端口,在配置完成后请使用 reload 命令重载防火墙配置:

firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --reload

使用对应的 remove-port 命令可以移除端口访问:

firewall-cmd --zone=public --remove-port=8080/tcp --permanent
firewall-cmd --reload

请注意:若要设置永久性设置,请添加 –permanent 选项,没有特殊情况,下文所有相关防火墙指令都会添加此选项。

通过配置服务文件开放端口。

在初始配置下, firewalld 的 public 网络区仅开放了 dhcpv6-client 和 ssh 两个服务可被外部访问。它们暴露的端口为 546/udp/ipv6 和 22/tcp。

在 /usr/lib/firewalld/services 目录下定义了一组以 XML 描述的服务配置文件:

]# cat /usr/lib/firewalld/services/ssh.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>SSH</short>
  <port protocol="tcp" port="22"/>
</service>

]# cat /usr/lib/firewalld/services/dhcpv6-client.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>DHCPv6 Client</short>
  <port protocol="udp" port="546"/>
  <destination ipv6="fe80::/64"/>
</service>

以 SSH 服务为模板复制到用户配置目录下。并修改 22 端口为 8080,该文件的内容如下:

cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/myweb.xml

]# cat /etc/firewalld/services/myweb.xml

<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>MYWEB</short>
  <port protocol="tcp" port="8080"/>
</service>

接下来就可以通过服务的方式来开放端口:

# 添加
firewall-cmd --zone public --add-service=myweb --permanent
firewall-cmd --reload
# 移除
firewall-cmd --zone public --add-service=myweb --permanent
firewall-cmd --reload

可以通过 Python 来启动一个简单的 HTTP 服务器来测试 8080 端口是否开放成功:

# Python 2.x
python -m SimpleHTTPServer 8080
# Python 3.x
python -m http.server 8080
# 访问地址
curl -v <主机地址>:8080

转发端口

通过配置转发端口,可以把访问指定端口的数据转发到其它指定主机的端口上,这里以当前主机为例,把所有 8080 端口的访问转发到 8081:

firewall-cmd --zone=public --add-forward-port=port=8080:proto=tcp:toport=8081 --permanent
firewall-cmd --reload

通过前文的 Python 命令来启动一个 HTTP 服务器,并使用 8081 端口,此时外部访问当前主机的 8080 端口的数据就可以直接访问到内容。

移除转发也是对应的命令:

firewall-cmd --zone=public --remove-forward-port=port=8080:proto=tcp:toport=8081 --permanent
firewall-cmd --reload

Rich Language

通过 Rich Language 来建立相对复杂的规则配置,例如限制 ipv4 下 tcp 端口 8080 仅可被 10.1.1.0/2 网段访问。

# 添加
firewall-cmd --zone=public --add-rich-rule='rule family=ipv4 source address=10.1.1.0/24 port port=8080 protocol=tcp accept' --permanent
# 移除
firewall-cmd --zone=public --remove-rich-rule='rule family=ipv4 source address=10.1.1.0/24 port port=8080 protocol=tcp accept' --permanent

Rich Languange 是 iptables 工具的抽象表示。

完整格式描述可以参考 firewalld.zone(5),不过是以 XML 格式来描述。

firewalld 与 iptables

iptables 所有表

在讨论 firewalld 与 iptables 关联之前,需要简单了解 iptables 所有的表。

iptables 支持了5个表来提供在各种环境下的配置:

filter

默认的表,其包含了内建的三条链:INPUT (用于发往本地套接字的数据包),FORWARD (用于通过本机路由的数据包),OUTPUT (用于本地产生的数据包)。

nat 表

遇到用于创建新的连接的数据包会查询此表。 其包含了内建的三条链 :PREROUTING (用于在数据包传入时立即修改数据包),OUTPUT (在路由之前修改本地产生的数据包),POSTROUTING (在数据包传出之前修改数据包)。

从 Linux内核 3.7 版本开始支持 IPV6 的 NAT 支持

mangle

此表专用于数据包的修改。直到 Linux 内核 2.4.17 开始仅支持两条链:PREROUTING (用于在路由之前修改传入的数据包) 和 OUTPUT (用于在路由之前修改本地产生的数据包)。从内核 2.4.18 开始,另外三条链得到支持:INPUT (用于发送给本机之前),FORWARD (用于通过本 用于通过本机路由的数据包 ),POSTROUTING (在数据包传出之前修改数据)。

raw

此表主要用于配置与 NOTRACK 目标相结合的 链接跟踪豁免 (exemptions from connection tracking)。它具有更高的 netfilter 钩子注册优先级,在 ip_conntrack 和任何其它 IP 表之前调用。它包含两个链:PREROUTING (对于通过任何网络接口到达的数据包),OUTPUT (对于本地程序产生的任何数据包)。

security

用于强制访问控制 (Mandatory Access Control, MAC) 网络规则。它是一个安全策略,不在文本详细讨论范围中,它包含两个链:INPUT / OUTPUT

常规表之间的调用链顺序:

filter 表基本策略

当 firewalld 防火墙为初始状态时:

~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens192
  sources:
  services: dhcpv6-client ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

INPUT

# iptables -L -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
  677 46644 ACCEPT     all  --  any    any     anywhere             anywhere             ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  lo     any     anywhere             anywhere
    0     0 INPUT_direct  all  --  any    any     anywhere             anywhere
    0     0 INPUT_ZONES_SOURCE  all  --  any    any     anywhere             anywhere
    0     0 INPUT_ZONES  all  --  any    any     anywhere             anywhere
    0     0 DROP       all  --  any    any     anywhere             anywhere             ctstate INVALID
    0     0 REJECT     all  --  any    any     anywhere             anywhere             reject-with icmp-host-prohibited

# iptables -S
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -j INPUT_direct
-A INPUT -j INPUT_ZONES_SOURCE
-A INPUT -j INPUT_ZONES
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -j REJECT --reject-with icmp-host-prohibited

其首先接受所有已建立的连接,以及接受来自 lo 网络接口 (本地回环) 的所有数据 :

-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT

目标为 INPUT_direct / INPUT_ZONES_SOURCE 的两条自定义链规则为空,为特定 firewalld 配置功能保留。

# iptables -L -v
Chain INPUT_ZONES_SOURCE (1 references)
 pkts bytes target     prot opt in     out     source               destination

Chain INPUT_direct (1 references)
 pkts bytes target     prot opt in     out     source               destination

# iptables -S
-A INPUT -j INPUT_direct
-A INPUT -j INPUT_ZONES_SOURCE

INPUT_ZONES 目标下包含了网络区的配置:

# iptables -L -v
Chain INPUT_ZONES (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 IN_public  all  --  ens192 any     anywhere             anywhere            [goto]
    0     0 IN_public  all  --  +      any     anywhere             anywhere            [goto]

Chain IN_public (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 IN_public_log  all  --  any    any     anywhere             anywhere
    0     0 IN_public_deny  all  --  any    any     anywhere             anywhere
    0     0 IN_public_allow  all  --  any    any     anywhere             anywhere
    0     0 ACCEPT     icmp --  any    any     anywhere             anywhere

Chain IN_public_allow (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:ssh ctstate NEW,UNTRACKED

Chain IN_public_deny (1 references)
 pkts bytes target     prot opt in     out     source               destination

Chain IN_public_log (1 references)
 pkts bytes target     prot opt in     out     source               destination

# iptables -S
-A INPUT_ZONES -i ens192 -g IN_public
-A INPUT_ZONES -g IN_public
-A IN_public -j IN_public_log
-A IN_public -j IN_public_deny
-A IN_public -j IN_public_allow
-A IN_public -p icmp -j ACCEPT
-A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,UNTRACKED -j ACCEPT

INPUT_ZONES 链内包含了以 IN_<网络区>命名的链,同时会匹网络接口。且默认网络区会作为最后的匹配规则:

-A INPUT_ZONES -i ens192 -g IN_public # ens192 被分配到了 public 网络区中
-A INPUT_ZONES -g IN_public # 默认网络区为 public

IN_public 下包含了4个规则,添加了三个链目标: IN_public_log / IN_public_deny / IN_public_allow,且接受所有 ICMP 的请求。其中 IN_public_log / IN_public_deny 规则为空为特定功能保留。

-A IN_public -j IN_public_log
-A IN_public -j IN_public_deny
-A IN_public -j IN_public_allow
-A IN_public -p icmp -j ACCEPT

IN_public_allow 中包含了实际的通行规则,它允许了 22 端口:

# iptables -L -v
Chain IN_public_allow (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:ssh ctstate NEW,UNTRACKED

# iptables -S
-A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,UNTRACKED -j ACCEPT

在 INPUT 的链的最后,若没有匹配到规则,则丢弃所有无效包,拒绝其它所有请求:

-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -j REJECT --reject-with icmp-host-prohibited

FORWARD 链

FORWARD 链在 filter 表中与 INPUT 链非常类似,只不过针对网络区,区分了 IN 和 OUT 的自定义链:

# iptables -L -v
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  any    any     anywhere             anywhere             ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  lo     any     anywhere             anywhere
    0     0 FORWARD_direct  all  --  any    any     anywhere             anywhere
    0     0 FORWARD_IN_ZONES_SOURCE  all  --  any    any     anywhere             anywhere
    0     0 FORWARD_IN_ZONES  all  --  any    any     anywhere             anywhere
    0     0 FORWARD_OUT_ZONES_SOURCE  all  --  any    any     anywhere             anywhere
    0     0 FORWARD_OUT_ZONES  all  --  any    any     anywhere             anywhere
    0     0 DROP       all  --  any    any     anywhere             anywhere             ctstate INVALID
    0     0 REJECT     all  --  any    any     anywhere             anywhere             reject-with icmp-host-prohibited

# iptables -S
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i lo -j ACCEPT
-A FORWARD -j FORWARD_direct
-A FORWARD -j FORWARD_IN_ZONES_SOURCE
-A FORWARD -j FORWARD_IN_ZONES
-A FORWARD -j FORWARD_OUT_ZONES_SOURCE
-A FORWARD -j FORWARD_OUT_ZONES
-A FORWARD -m conntrack --ctstate INVALID -j DROP
-A FORWARD -j REJECT --reject-with icmp-host-prohibited

在 FORWARD_IN_ZONES 链中,匹配所有从网络界面的输入数据:

# iptables -L -v
Chain FORWARD_IN_ZONES (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 FWDI_public  all  --  ens192 any     anywhere             anywhere            [goto]
    0     0 FWDI_public  all  --  +      any     anywhere             anywhere            [goto]

Chain FWDI_public (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 FWDI_public_log  all  --  any    any     anywhere             anywhere
    0     0 FWDI_public_deny  all  --  any    any     anywhere             anywhere
    0     0 FWDI_public_allow  all  --  any    any     anywhere             anywhere
    0     0 ACCEPT     icmp --  any    any     anywhere             anywhere

# iptables -S
-A FORWARD_IN_ZONES -i ens192 -g FWDI_public
-A FORWARD_IN_ZONES -g FWDI_public
-A FWDI_public -j FWDI_public_log
-A FWDI_public -j FWDI_public_deny
-A FWDI_public -j FWDI_public_allow
-A FWDI_public -p icmp -j ACCEPT

在 FORWARD_OUT_ZONES 链中,匹配所有输出到网络界面的所有数据:

# iptables -L -v
Chain FORWARD_OUT_ZONES (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 FWDO_public  all  --  any    ens192  anywhere             anywhere            [goto]
    0     0 FWDO_public  all  --  any    +       anywhere             anywhere            [goto]

Chain FWDO_public (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 FWDO_public_log  all  --  any    any     anywhere             anywhere
    0     0 FWDO_public_deny  all  --  any    any     anywhere             anywhere
    0     0 FWDO_public_allow  all  --  any    any     anywhere             anywhere

# iptables -S
-A FORWARD_OUT_ZONES -o ens192 -g FWDO_public
-A FORWARD_OUT_ZONES -g FWDO_public
-A FWDO_public -j FWDO_public_log
-A FWDO_public -j FWDO_public_deny
-A FWDO_public -j FWDO_public_allow

不过在初始的 firewalld 配置下,FORWARD 链下除了在 FWDI_public 中匹配到允许从 ens192 网络接口传入 ICMP 协议之外,所有数据都会被丢弃或拒绝。

OUTPUT 链

OUTPUT 链的规则很简单:

Chain OUTPUT (policy ACCEPT 699 packets, 117K bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  any    lo      anywhere             anywhere
  699  117K OUTPUT_direct  all  --  any    any     anywhere             anywhere

Chain OUTPUT_direct (1 references)
 pkts bytes target     prot opt in     out     source               destination

对于本地回环数据直接通过,否则匹配 OUTPUT_direct 链接用于 firewalld 的规则配置,初始规则为空。若未匹配,则使用 OUTPUT 链的策略 ACCEPT 允许通过所有数据。

firewalld 添加端口策略

在 firewalld 中通过服务或者命令直接添加端口,通过 Rich Language 也是如此:

firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --zone=public --add-rich-rule='rule family=ipv4 source address=10.1.1.0/24 port port=8081 protocol=tcp accept' --permanent
firewall-cmd --reload

会直接添加到 IN_public_allow 中:

INPUT
  |
  | - INPUT_direct
  | - INPUT_ZONES_SOURCE
  | - INPUT_ZONES
         |
         | - IN_public (-A INPUT_ZONES -i ens192 -g IN_public)
               |
               | - IN_public_log
               | - IN_public_deny
               | - IN_public_allow
               |      |
               |      | -A IN_public_allow -p tcp -m tcp --dport 8080 -m conntrack --ctstate NEW,UNTRACKED -j ACCEPT
               |      | -A IN_public_allow -s 10.1.1.0/24 -p tcp -m tcp --dport 8081 -m conntrack --ctstate NEW,UNTRACKED -j ACCEPT
               | - ACCEPT

或者可以使用 firewalld 的直接添加规则命令指定 iptables 的表和链:

firewall-cmd --direct --add-rule ipv4 filter IN_public_allow 0 -m tcp -p tcp --dport 8080 -j ACCEPT

也可以直接添加到 INPUT_direct 表中,它将优先在网络区链 INPUT_ZONES 之前匹配规则。

nat 表基本策略

PREROUTING 与 POSTROUTING 链

nat 表中 PREROUTING / POSTROUTING 链与 filter 表中 INPUT 链有着相似的配置策略,这里仅列出 PREROUTING:

PREROUTING (policy ACCEPT)
  |
  | - PREROUTING_direct
  | - PREROUTING_ZONES_SOURCE
  | - PREROUTING_ZONES
      |
      | - PRE_public (-A PREROUTING_ZONES -i ens192 -g PRE_public)
            |
            | - PRE_public_log
            | - PRE_public_deny
            | - PRE_public_allow
      | - PRE_public (-A POSTROUTING_ZONES -g POST_public)

INPUT 与 OUTPUT 链

在 nat 表中 firewalld 提供的默认配置很简单:

# iptables -t nat -L -v

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 16 packets, 2377 bytes)
 pkts bytes target     prot opt in     out     source               destination
   16  2377 OUTPUT_direct  all  --  any    any     anywhere             anywhere

firewalld 添加端口转发策略

前文的端口转发配置为例:

firewall-cmd --zone=public --add-forward-port=port=8080:proto=tcp:toport=8081 --permanent
firewall-cmd --reload

firewalld 在 mangle 表和 nat 表中各自添加了以下规则:

# mangle
-A PRE_public_allow -p tcp -m tcp --dport 8080 -j MARK --set-xmark 0x64/0xffffffff
# nat
-A PRE_public_allow -p tcp -m mark --mark 0x64 -j DNAT --to-destination :8081

mange 表中在 PREROUTING 阶段给来自 8080/tcp 端口的所有数据包标记一个 0x64/0xffffffff,前者为值,后者为掩码。其结果会被异或加入到 ctmark 中。 ctmark 是一个 netfilter 提供的32位标记值。

# set-xmark 伪代码描述
xmark = xmark | (0x64 & 0xffffffff)

关于 ctmark 的所有扩展操作,见 iptables-extensions(8) CONNMARK 小节

在 nat 表中添加了一个若成功匹配 0x64/0xffffffff ctmark 值的数据包,并将其加入到 DNAT 目标。指定目标参数为转发到本机 8081 端口。

DNAT 目标仅可以在 nat 表中的 PREROUTING 和 OUPUT 链以及相关调用链中使用。

另外可以尝试在未启动 firewalld 的情况下直接配置 iptables 进行端口转发:

iptables -t mangle -A PREROUTING -p tcp -m tcp --dport 8080 -j MARK --set-xmark 0x64/0xffffffff
iptables -t nat -A PREROUTING -p tcp -m mark --mark 0x64 -j DNAT --to-destination :8081

结尾

本文简述了在 CentOS 为测试环境下的 iptables 的基础操作以及 firewalld 的基础操作。以及 firewalld 的基础操作如何配置了 iptables。

firewalld 可以较高的抽象来配置 iptables,例如内部维护了端口转发的 ctmark 值。

对于未指定ip协议版本的端口开放,firewalld 同时维护了 ipv4 和 ipv6 两组配置,可以通过下列命令分别查看:

iptables -L -v
ip6tables -L -v

在其它 Linux 发行版中也会有与 firewalld 类似的工具,iptables 存在于大多数发行版中。

参考文章

https://www.howtogeek.com/177621/the-beginners-guide-to-iptables-the-linux-firewall/
https://www.netfilter.org/documentation/
https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html
https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture
https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/7/html/security_guide/sec-using_firewalls

《CentOS 中的 iptables 与 firewalld 防火墙基本策略》有2条评论

  1. 这篇文章写的很棒,文章这个格式是如何做的? 自动生成的么?

    PREROUTING (policy ACCEPT)
    |
    | – PREROUTING_direct
    | – PREROUTING_ZONES_SOURCE
    | – PREROUTING_ZONES
    |
    | – PRE_public (-A PREROUTING_ZONES -i ens192 -g PRE_public)
    |
    | – PRE_public_log
    | – PRE_public_deny
    | – PRE_public_allow
    | – PRE_public (-A POSTROUTING_ZONES -g POST_public)

    回复

发表评论