runsisi's

technical notes

iptables 基础

2019-02-03 runsisi#linux

CentOS 7.x 默认安装的防火墙是 firewalld,因此如果要使用 iptables 的话,最好禁用 firewalld:

~$ sudo systemctl disable firewalld
~$ sudo systemctl stop firewalld

iptables 安装包中带了如下一些命令行程序:

~$ rpm -ql iptables
...
/usr/sbin/ip6tables
/usr/sbin/ip6tables-restore
/usr/sbin/ip6tables-save
/usr/sbin/iptables
/usr/sbin/iptables-restore
/usr/sbin/iptables-save
...

iptables/ip6tables 命令定义的规则并不会持久化,重启系统之后就会丢失,注意不要被 iptables-save/ip6tables-save 的名字所欺骗,这两个命令并不会保存(持久化)规则,它们仅用于导出所有已定义的规则到命令行的标准输出流中:

~# iptables-save
# Generated by iptables-save v1.4.21 on Wed Jan 16 10:20:15 2019
*filter
:INPUT ACCEPT [1395:138576]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [743:70251]
COMMIT
# Completed on Wed Jan 16 10:20:15 2019

如果要持久化,可以使用 iptables-save/ip6tables-save 导出规则到文件中,然后在上电的时候使用 iptables-restore/ip6tables-restore 从文件中加载规则(不管最初添加规则是通过 -A 还是 -I,所有保存的规则都转成 -A 形式)。

导出、导入规则的工作可以使用前面提到的命令手工进行,也可以安装 iptables-services 包,通过它提供的辅助脚本完成这些工作。同时比较方便的是,通过 iptables-services 包带的 systemd 服务还可以实现上电自动加载规则:

~$ yum install iptables-services
...
~$ rpm -ql iptables-services
/etc/sysconfig/ip6tables
/etc/sysconfig/iptables
/usr/lib/systemd/system/ip6tables.service
/usr/lib/systemd/system/iptables.service
/usr/libexec/initscripts/legacy-actions/ip6tables
/usr/libexec/initscripts/legacy-actions/ip6tables/panic
/usr/libexec/initscripts/legacy-actions/ip6tables/save
/usr/libexec/initscripts/legacy-actions/iptables
/usr/libexec/initscripts/legacy-actions/iptables/panic
/usr/libexec/initscripts/legacy-actions/iptables/save
/usr/libexec/iptables
/usr/libexec/iptables/ip6tables.init
/usr/libexec/iptables/iptables.init

保存规则可以使用 sysv 脚本:

~# service iptables save
iptables: Saving firewall rules to /etc/sysconfig/iptables:[  OK  ]

~# service ip6tables save
ip6tables: Nothing to save.                                [WARNING]

也可以直接使用 sysv 脚本所封装的底层脚本:

~# /usr/libexec/iptables/iptables.init save
iptables: Saving firewall rules to /etc/sysconfig/iptables:[  OK  ]

~# /usr/libexec/iptables/ip6tables.init save
ip6tables: Nothing to save.                                [WARNING]

规则保存在如下的文件中:

/etc/sysconfig/ip6tables
/etc/sysconfig/iptables

注意 sysv 脚本仅提供了 save 命令,systemd 脚本仅提供了 start/stop/reload 命令(systemctl status iptables.service / ip6tables.service 呈现的是 systemd 服务的状态,而不是底层 iptables 规则)。

如果要实现规则的 stop/reload/status/restart 等完全控制,可以使用 /usr/libexec/iptables/iptables.init 或者 /usr/libexec/iptables/ip6tables.init 底层脚本,当然也可以直接使用最原始的 iptables-save/ip6tables-save、iptables-restore/ip6tables-restore 命令。

iptables 规则

可以使用 iptables/ip6tables 命令行工具定义(操作)防火墙规则。

注:由于 iptables 与 ip6tables 在规则定义上并没有本质差异,后文不再区分 iptables/ip6tables,命令也统一以 iptables 作为示例。

需要注意的是,iptables 命令添加的规则与 ip6tables 命令添加的规则是完全独立的,如果系统上同时存在 IPv4 地址和 IPv6 地址,那么即使是针对同一个端口的保护也需要同时使用 iptables 和 ip6tables 分别添加规则。

此外 IPv6 有大量的 ICMP 报文交互,因此需要注意特别添加 ICMP 相关的规则。

iptables 顾名思义,是表(table)的集合,使用 iptables 命令定义规则时需要通过 -t / --table 参数指定需要操作的表,总共有 filter, nat, mangle, raw, security 5 张表,而我们通常操作的都是 filter 表,因此如果在 iptables 命令行上不通过 -t / --table 参数显示指定,将默认使用 filter 这张表,本文的内容也仅限于该表。

查看规则

iptables -L [chain]

-L, --list

iptables -S [chain]

-S, --list-rules

注意前面提到的 -t / --table 参数,由于这里没有显式指定,因此查询的是默认的 filter 表(下同)。

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

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

INPUT(入站规则)/FORWORD(路由转发规则)/OUTPUT(出站规则) 是 filter 表内置(built-in)的规则组(chain),也可以通过 iptables -N / iptables --new-chain 命令新建用户自定义的规则组用于 -j / -g 参数,规则都定义在规则组中,因此增删规则时需要显式指定。

防火墙策略(policy)或者规则(rule),这两个名词通常混用,但在 iptables 里策略即默认规则,当一个规则组中所有规则从上至下依次查找之后如果仍未能找到匹配的规则,则使用该默认规则,如上面默认 INPUT/FORWARD/OUTPUT 的策略都是 ACCEPT,即默认放行报文(另一个选项是 DROP,即丢弃报文)。

-S, --list-rules 以 iptables-save 的形式列出规则:

~# iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-A INPUT -p icmp -j ACCEPT

修改策略

iptables -P chain target

-P, --policy

Linux 机器开启路由转发功能需要启用如下选项:

~# sysctl net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
~# cat /proc/sys/net/ipv4/ip_forward
1

~# sysctl net.ipv6.conf.all.forwarding=1
net.ipv6.conf.all.forwarding = 1
~# cat /proc/sys/net/ipv6/conf/all/forwarding
1

但服务器通常都不会使用该功能,因此我们一般在防火墙中将路由转发规则(FORWARD)配置成默认 DROP

~# iptables --policy FORWARD DROP
~# iptables --list
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy DROP)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

然后通常将入站规则(INPUT)配置成默认 DROP(即拒绝所有的外部报文),出站规则(OUTPUT)配置成默认 ACCEPT(即允许所有从本机发起的报文)。

配置策略为 DROP 时要特别小心,如果是使用 SSH 登陆系统进行配置,且没有显式定义允许 SSH 报文的规则,那么一定不要随意将 INPUT/OUTPUT 配置成 DROP,否则 SSH 连接会断!

删除所有规则

iptables -F [chain]

-F, --flush

当开始一个新系统的配置,或者丢弃所有以前配置的规则时,可以使用该命令,注意该命令只删除规则,不会将策略(policy)重置为默认值。

追加规则

iptables -A chain rule-specification

-A, --append
~# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
~# iptables -A INPUT -p tcp --dport 22 -j ACCEPT
~# iptables -P INPUT DROP

~# iptables -L --line-numbers
Chain INPUT (policy DROP)
num  target     prot opt source               destination
1    ACCEPT     all  --  anywhere             anywhere             state RELATED,ESTABLISHED
2    ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:ssh

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

在指定位置插入规则

iptables -I chain rulenum rule-specification

-I, --insert
~# iptables -I INPUT 1 -p tcp --dport 23 -j ACCEPT
~# iptables -L --line-numbers -n
Chain INPUT (policy DROP)
num  target     prot opt source               destination
1    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:23
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
3    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

修改指定位置的规则

iptables -R chain rulenum rule-specification

-R, --replace
~# iptables -R INPUT 1 -p tcp --dport 24 -j ACCEPT
~# iptables -L --line-numbers -n
Chain INPUT (policy DROP)
num  target     prot opt source               destination
1    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:24
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
3    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

删除指定位置规则

iptables -D chain rulenum

-D, --delete
~# iptables -D INPUT 1
~# iptables -L --line-numbers -n
Chain INPUT (policy DROP)
num  target     prot opt source               destination
1    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
2    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

注意:上面需要用到 rulenum 参数的命令,都是可以先通过 iptables -L --line-numbers 命令查询得到已有规则的编号,切记一旦规则进行增删,已有规则的序号就会发生变化,一定不要复用 iptables -L --line-numbers 命令的结果!

-n / --numeric 选项与 Linux 下大多数网络相关的工具中的 -n 选项类似,即不要对 IP 地址、端口等进行任何名字转义,直接使用数字方式进行输出。

删除指定规则

iptables -D chain rule-specification

-D, --delete

可以不需要使用规则编号,直接删除规则:

~# iptables -A OUTPUT -p tcp -m tcp --sport 20 -j DROP
~# iptables -D OUTPUT -p tcp -m tcp --sport 20 -j DROP

给规则增加注释

使用 comment 模块可以给 iptables 规则增加注释:

~# iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -m comment --comment 'nginx'
~# iptables -L -nv | grep 80
    0     0            tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80 ctstate NEW /* nginx */

规则匹配定义

即上面命令中的 rule-specification 字段的定义。

一个完整的匹配规则可能包含如下的所有字段:

[[!] -i name] [[!] -o name] [[!] -p protocol] [[!] -s address/[mask][,...]] [[!] -d address[/mask][,...]] [[-m name [module-options...]],...] [-j target | -g chain]
-4, --ipv4
-6, --ipv6
-i, --in-interface
-o, --out-interface
-p, --protocol
-s, --source, --src
-d, --destination, --dst
-m, --match
-j, --jump
-g, --goto

各选项解释如下:

-4 / -6,未在上述完整的匹配规则中体现,因为仅用于 iptables-restore / ip6tables-restore 命令,匹配规则中 ipv4 / ipv6 的差异应直接由命令行 iptables / ip6tables 自身进行区分;

-i / -o,报文接收 / 发送所在的网口,本地环回口为 lo, -i 仅适用于 INPUT, FORWARD, PREROUTING 规则组,-o 仅适用于 OUTPUT, FORWARD, POSTROUTING 规则组;

如果使用 -i / -o 指定了网口,则 iptables -L 查看是需要加上 -v 参数才能把网口显示出来,因此平时查看规则时建议使用 iptables -L [chain] -n -v

-p,报文协议类型,常见的包括 tcp, udp, icmp, icmp6all 用于表示所有协议类型,如果不指定 -p 选项,则默认为 all,更多协议类型可以参考 /etc/protocols;

-s / -d,报文 IP 地址,可以是一个独立的 IP 地址,一个网段,多个地址/网段之间使用英文逗号(,)分隔,如果指定多个地址/网段会被 iptables 命令解析成多条规则;

-m,指定扩展模块,所有的模块及其选项可以通过 man iptables-extensions 手册页进行查看,常用的模块包括:conntrack, state, tcp, udp,一些比较可能有用的包括:addrtype, connlimit, iprange, limit, multiport, pkttype, quota 等;

state 模块 --state 选项允许的状态是 conntrack 模块 --ctstate 选项允许的状态的子集,常用的状态包括:NEW, ESTABLISHED, RELATED

状态的参数控制(特别是 UDP 流)可以参考 sysctl -a | grep net.netfilter 各系统配置参数(即 /proc/sys/net/netfilter/);

三个状态 NEW, ESTABLISHED, RELATED,iptables-extensions man 手册页的解释如下,更多信息可以阅读[4]:

NEW             The packet has started a new connection or otherwise associated with a connection which has not seen packets in both directions.

ESTABLISHED    	The packet is associated with a connection which has seen packets in both directions.

RELATED    	    The packet is starting a new connection, but is associated with an existing connection, such as an FTP data transfer or an ICMP error.

关于 NEW 的潜在问题可以阅读 [5];

关于 conntrack 的潜在问题以及 conntrack 工具可以阅读 [6][7];

通过 -p, --protocol 选项指定协议类型为 tcp 时会默认启用 tcp 模块,不需要要在规则中通过 -m, --match 选项显式启用 tcp 模块,但是一旦显式启用 tcp 模块,则必须通过 -p, --protocol 显式指定协议类型为 tcp

udp 模块的使用与 tcp 类似;

tcp/udp 模块常用的选项包括 [!] --sport port[:port], [!] --dport port[:port],连续的端口使用英文冒号(:)进行分隔,也可以使用 multiport 扩展模块定义更复杂的端口规则;

--sport, --source-port
--dport, --destination-port

-j,报文的下一步处理,可以是特殊值 ACCEPT, DROP, RETURN,可以是用户自定义的规则组,也可以是 iptables-extensions 中定义的 TARGET EXTENSIONS

ACCEPTDROP 在配置规则组(如 filter 表中的 INPUT, OUTPUT, FORWARDING)策略(policy)时已经提到过,ACCEPT 即允许该报文接收/发送,DROP 即丢弃该报文;

内置(built-in)规则组中的规则如果 target 是 RETURN,则该报文的处理结果由策略决定,要么 ACCEPT,要么 DROP

用户自定义规则组中的规则如果 target 是 RETURN,则停止当前规则组中规则的匹配,返回至调用者规则所在的规则组的下一条规则继续匹配;

常见的扩展 target 包括:AUDIT, DNAT, MASQUERADE, REJECT, SNAT,其中 REJECTDROP 唯一的差异在于 REJECT 还会发送 ICMP 报文告知错误原因(虽然这个错误是 iptables 配置的);

-g,针对匹配的报文跳转到用户自定义的规则组做进一步处理,-j-g 跳转到自定义规则时的处理方式有明显的差异,请参考后文 “选项 -j / -g 区别” 一节;

规则示例

前文提到过,在使用 SSH 登陆的情况下,不要随意将入站和出站规则的策略配置成 DROP,下面以放行 SSH 报文为基础,以实例的形式介绍 iptables 的配置流程和规则定义。

  1. 首先将 INPUTOUTPUT 的策略配置成 ACCEPT,即放行所有报文;
~# iptables -P INPUT ACCEPT
~# iptables -P OUTPUT ACCEPT

这样 SSH 连接就不会受到 iptables 的影响而断掉,后面我们将修改 INPUT 的策略为 DROP

此外由于我们的服务器不会用来做路由器,因此,将 FORWARD 的策略配置成 DROP

~# iptables -P FORWARD DROP
  1. 删除所有已有的规则,从头开始定义规则;
~# iptables -F

注意前面提到的删除规则不会重置配置的策略

  1. 允许新建入站的 SSH 连接;
~# iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT
  1. 允许已建立连接关系的 SSH 入站报文(这就是通常所谓的状态防火墙,而不是纯粹的包过滤防火墙);
~# iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state ESTABLISHED -j ACCEPT
  1. 此时可以安全的(即不会导致 SSH 连接中断)将 INPUT 策略配置为 DROP
~# iptables -P INPUT DROP
  1. 很多应用都需要使用 127.0.0.1 之类的环回地址,因此通常需要允许 lo 网口的流量;
~# iptables -A INPUT -i lo -j ACCEPT

由于 OUTPUT 的策略为 ACCEPT,因此这里只用为 INPUT 添加规则;

  1. 为了示例的完整性,我们将 OUTPUT 的策略也配置为 DROP,但在此配置之前,必须允许已建立连接关系的 SSH 出站报文;
~# iptables -A OUTPUT -p tcp -m tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
~# iptables -P OUTPUT DROP

注意此规则指定的报文源端口为 22,前面入站的规则中指定的是报文目的端口为 22;

前面 3, 4, 7 就实现了允许入向的 SSH 连接;

  1. 由于已经将 OUTPUT 策略配置成 DROP,因此前面为环回地址配置的规则还需要显式允许出站的流量;
~# iptables -A OUTPUT -o lo -j ACCEPT
  1. 允许对外的 SSH 连接;
~# iptables -A OUTPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT
~# iptables -A OUTPUT -p tcp -m tcp --dport 22 -m state --state ESTABLISHED -j ACCEPT
~# iptables -A INPUT -p tcp -m tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
  1. 允许对外的 ping;
~# iptables -A OUTPUT -p icmp -m icmp --icmp-type ping -j ACCEPT
~# iptables -A INPUT -p icmp -m icmp --icmp-type pong -j ACCEPT
  1. 允许入向的 ping;
~# iptables -A INPUT -p icmp -m icmp --icmp-type ping -j ACCEPT
~# iptables -A OUTPUT -p icmp -m icmp --icmp-type pong -j ACCEPT

总结前面的命令如下,实现的效果就是默认阻止任何报文的入站和出站,仅开放 SSH、ping 业务以及本地环回口的报文:

~# iptables -P INPUT ACCEPT
~# iptables -P OUTPUT ACCEPT
~# iptables -P FORWARD DROP
~#
~# iptables -F
~#
~# iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT
~# iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state ESTABLISHED -j ACCEPT
~# iptables -P INPUT DROP
~#
~# iptables -A INPUT -i lo -j ACCEPT
~#
~# iptables -A OUTPUT -p tcp -m tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
~# iptables -P OUTPUT DROP
~#
~# iptables -A OUTPUT -o lo -j ACCEPT
~#
~# iptables -A OUTPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT
~# iptables -A OUTPUT -p tcp -m tcp --dport 22 -m state --state ESTABLISHED -j ACCEPT
~# iptables -A INPUT -p tcp -m tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
~#
~#
~# iptables -A OUTPUT -p icmp -m icmp --icmp-type ping -j ACCEPT
~# iptables -A INPUT -p icmp -m icmp --icmp-type pong -j ACCEPT
~#
~# iptables -A INPUT -p icmp -m icmp --icmp-type ping -j ACCEPT
~# iptables -A OUTPUT -p icmp -m icmp --icmp-type pong -j ACCEPT

iptables -L -n -v 显示的规则列表如下:

~# iptables -L -nv
Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    1    60 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22 state NEW
  243 16645 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22 state RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0
   22  3997 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp spt:22 state RELATED,ESTABLISHED
    1    84 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0            icmptype 0
    0     0 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0            icmptype 8

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

Chain OUTPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
  134 12693 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp spt:22 state RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  *      lo      0.0.0.0/0            0.0.0.0/0
    1    60 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22 state NEW
   26  4445 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22 state RELATED,ESTABLISHED
    1    84 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0            icmptype 8
    0     0 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0            icmptype 0

配置完备的防火墙规则,需要非常仔细的设计,而且由于规则的匹配是从上至下,因此还需要考虑报文处理效率的问题[8];

基于应用的防火墙配置会比单纯的包过滤规则更简单,但是 iptables 并不支持 :( [9];

给服务器简单配置防火墙的思路

  1. 配置默认策略
~# iptables -P INPUT DROP
~# iptables -P FORWARD DROP
~# iptables -P OUTPUT ACCEPT
  1. 作为服务端允许客户端接入
~# iptables -A INPUT -p tcp -m tcp --dport <svc port> -m state --state NEW -j ACCEPT
  1. 允许客户端的入向流量,以及允许自身作为客户端时服务端返回的流量
~# iptables -A INPUT -p tcp -m tcp --dport <svc port> -m state --state ESTABLISHED -j ACCEPT
~# iptables -A INPUT -p tcp -m tcp --sport <svc port> -m state --state ESTABLISHED -j ACCEPT
  1. 如果服务是 udp 业务,与上面 tcp 业务类似,同样使用 state 模块允许 udp 端口的流量
  2. 此外把 lo 的流量放开
~# iptables -A INPUT -i lo -j ACCEPT
  1. 然后放开一些如 ping 之类的非 tcp / udp 流量
~# iptables -A INPUT -p icmp -m icmp --icmp-type pong -j ACCEPT
~# iptables -A INPUT -p icmp -m icmp --icmp-type ping -j ACCEPT

最后通过 multiport 模块合并一些规则。

其中第 3 步除非是对安全有严格要求的系统,否则一般会去掉 --sport 的指定,而直接使用如下的规则[13][14][15]:

~# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

或者:

~# iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT

选项 -j / -g 区别

-j / -g 的参数都可以是自定义规则组,但两者得到处理有非常大的不同:

  • -j,如果报文在自定义规则组中没能得到处理,将回到 caller 规则组中的下一条规则继续处理,类似于 C/C++ 中的函数调用;
  • -g,如果报文在自定义规则组中没能得到处理,将一直向上返回到非 -g 类型 caller 规则组中的下一条规则继续处理,类似于 C/C++ 中的函数调用之后紧跟一条 return 语句;

下面以一台具有 192.168.20.41 和 192.168.21.41 两个 IP 地址的机器为例进行说明,请参考其中的注释。

ping 192.168.20.41 通,ping 192.168.21.41 不通

iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -F

iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state ESTABLISHED -j ACCEPT
iptables -P INPUT DROP

iptables -X ceph-outer 2> /dev/null || true
iptables -X ceph-inner 2> /dev/null || true

iptables -N ceph-outer
iptables -N ceph-inner

iptables -A ceph-outer -p icmp -g ceph-inner
# 这里存在类似 C/C++ 的 return 语句
# 因此,下面的规则没有机会匹配到 ceph-inner 未处理的报文,返回到 caller 规则组 INPUT 继续处理
iptables -A ceph-outer -p icmp -j ACCEPT

iptables -A ceph-inner -d 192.168.20.41/32 -p icmp -j ACCEPT

iptables -A INPUT -p icmp -g ceph-outer
# 这里存在类似 C/C++ 的 return 语句
# 因此,下面的这条规则没有机会匹配到 ceph-inner 和 ceph-outer 未处理的报文,报文的继续处理由 INPUT 的策略控制,即 DROP
iptables -A INPUT -p icmp -j ACCEPT

ping 192.168.20.41 通,ping 192.168.21.41 通

iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -F

iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state ESTABLISHED -j ACCEPT
iptables -P INPUT DROP

iptables -X ceph-outer 2> /dev/null || true
iptables -X ceph-inner 2> /dev/null || true

iptables -N ceph-outer
iptables -N ceph-inner

iptables -A ceph-outer -p icmp -g ceph-inner
# 这里存在类似 C/C++ 的 return 语句
# 因此,下面的这条规则没有机会匹配到 ceph-inner 未处理的报文,返回到 caller 规则组 INPUT 继续处理
iptables -A ceph-outer -p icmp -j ACCEPT

iptables -A ceph-inner -d 192.168.20.41/32 -p icmp -j ACCEPT

iptables -A INPUT -p icmp -j ceph-outer
# ceph-inner 和 ceph-outer 未处理的报文匹配到下面的这条规则,即 ACCEPT
iptables -A INPUT -p icmp -j ACCEPT

ping 192.168.20.41 通,ping 192.168.21.41 通

iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -F

iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state ESTABLISHED -j ACCEPT
iptables -P INPUT DROP

iptables -X ceph-outer 2> /dev/null || true
iptables -X ceph-inner 2> /dev/null || true

iptables -N ceph-outer
iptables -N ceph-inner

iptables -A ceph-outer -p icmp -j ceph-inner
# ceph-inner 未处理的报文匹配到下面的这条规则,即 ACCEPT
iptables -A ceph-outer -p icmp -j ACCEPT

iptables -A ceph-inner -d 192.168.20.41/32 -p icmp -j ACCEPT

iptables -A INPUT -p icmp -g ceph-outer
# 这里存在类似 C/C++ 的 return 语句
# 但由于 ceph-outer 已经匹配到了 ceph-inner 未处理的报文,因此即使下面的这条规则规则没有机会匹配也无所谓
iptables -A INPUT -p icmp -j ACCEPT

nftables[10][11]

Linux 内核从 3.13 版本(2014 年 1 月 19 日)引入了 nftables 框架用于取代原有的 {ip,ip6,arp,eb}tables 等多个框架,其配置工具 nft 也将取代原有的 {ip,ip6,arp,eb}tables 等多个工具。

参考文档

[1] iptables 手册页

man iptables

man iptables-extensions

[2] IPTables

https://wiki.centos.org/HowTos/Network/IPTables

[3] What’s the difference between iptables “state” and “ctstate”?

https://superuser.com/questions/1071656/whats-the-difference-between-iptables-state-and-ctstate

[4] The state machine

https://www.frozentux.net/iptables-tutorial/chunkyhtml/c1276.html

[5] State NEW packets but no SYN bit set

https://www.linuxtopia.org/Linux_Firewall_iptables/x6193.html

[6] Caveats about Linux connection tracking and high traffic servers

https://i-admin.blogspot.com/2014/02/caveats-about-linux-connection-tracking.html

[7] CONNTRACK

http://conntrack-tools.netfilter.org/conntrack.html

[8] When using iptables firewall rules, why assert NEW state on all allowed ports?

https://serverfault.com/questions/578730/when-using-iptables-firewall-rules-why-assert-new-state-on-all-allowed-ports/578781

[9] create iptables rule per process/service

https://stackoverflow.com/questions/4314163/create-iptables-rule-per-process-service

[10] nftables

https://en.wikipedia.org/wiki/Nftables

[11] nftables

https://wiki.archlinux.org/index.php/nftables

[12] A Deep Dive into Iptables and Netfilter Architecture

https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture

[13] iptables: difference between NEW, ESTABLISHED and RELATED packets

https://serverfault.com/questions/371316/iptables-difference-between-new-established-and-related-packets

[14] Is accepting RELATED,ESTABLISHED for all sources in iptables considered “too open”?

https://unix.stackexchange.com/questions/323546/is-accepting-related-established-for-all-sources-in-iptables-considered-too-ope

[15] How to Make a Linux Stateless Firewall for Performance and Resilience

https://strongarm.io/blog/linux-stateless-firewall/