4. 匹配条件

还是从我们最常用的”源地址”说起吧,我们知道,使用-s选项作为匹配条件,可以匹配报文的源地址,但是之前的示例中,我们每次指定源地址,都只是指定单个IP,示例如下。

041917_0537_1

其实,我们也可以在指定源地址时,一次指定多个,用”逗号”隔开即可,示例如下。

041917_0537_2

可以看出,上例中,一次添加了两条规则,两条规则只是源地址对应的IP不同,注意,上例中的”逗号”两侧均不能包含空格,多个IP之间必须与逗号相连。

除了能指定具体的IP地址,还能指定某个网段,示例如下

041917_0537_3

上例表示,如果报文的源地址IP在10.6.0.0/16网段内,当报文经过INPUT链时就会被DROP掉。

其实,我们还可以对匹配条件取反,先看示例,如下。

041917_0537_4

上图中,使用”! -s 192.168.1.146″表示对 -s 192.168.1.146这个匹配条件取反, -s 192.168.1.146表示报文源IP地址为192.168.1.146即可满足匹配条件,使用 “!” 取反后则表示,报文源地址IP只要不为192.168.1.146即满足条件,那么,上例中规则表达的意思就是,只要发往本机的报文的源地址不是192.168.1.146,就接受报文。

此刻,你猜猜,按照上例中的配置,如果此时从146主机上向防火墙所在的主机发送ping请求,146主机能得到回应吗?(此处不考虑其他链,只考虑filter表的INPUT链)

为了给你思考的空间,我把答案写的远一点。

答案是:能,也就是说,按照上例的配置,146主机仍然能够ping通当前主机,为什么呢?我们来分析一下。

上例中,filter表的INPUT链中只有一条规则,这条规则要表达的意思就是:

只要报文的源IP不是192.168.1.146,那么就接受此报文,但是,某些小伙伴可能会误会,把上例中的规则理解成如下含义,

只要报文的源IP是192.168.1.146,那么就不接受此报文,这种理解与上述理解看似差别不大,其实完全不一样,这样理解是错误的,上述理解才是正确的。

换句话说就是,报文的源IP不是192.168.1.146时,会被接收,并不能代表,报文的源IP是192.168.1.146时,会被拒绝。

上例中,因为并没有任何一条规则指明源IP是192.168.1.146时,该执行怎样的动作,所以,当来自192.168.1.146的报文经过INPUT链时,并不能匹配上例中的规则,于是,此报文就继续匹配后面的规则,可是,上例中只有一条规则,这条规则后面没有其他可以匹配的规则,于是,此报文就会去匹配当前链的默认动作(默认策略),而上例中,INPUT链的默认动作为ACCEPT,所以,来自146的ping报文就被接收了,如果,把上例中INPUT链的默认策略改为DROP,那么,146的报文将会被丢弃,146上的ping命令将得不到任何回应,但是如果将INPUT链的默认策略设置为DROP,当INPUT链中没有任何规则时,所有外来报文将会被丢弃,包括我们ssh远程连接。

好了,我们通过上例,不仅了解到了怎样对匹配条件取反,还加深了我们对默认策略的了解,一举两得,我们继续聊。

4.1 匹配条件:目标IP地址

除了可以通过-s选项指定源地址作为匹配条件,我们还可以使用-d选项指定”目标地址”作为匹配条件。

源地址表示报文从哪里来,目标地址表示报文要到哪里去。

除了127.0.0.1回环地址以外,当前机器有两个IP地址,IP如下。

041917_0537_5

假设,我们想要拒绝146主机发来的报文,但是我们只想拒绝146向156这个IP发送报文,并不想要防止146向101这个IP发送报文,我们就可以指定目标地址作为匹配条件,示例如下。

041917_0537_6

上例表示只丢弃从146发往156这个IP的报文,但是146发往101这个IP的报文并不会被丢弃,如果我们不指定任何目标地址,则目标地址默认为0.0.0.0/0,同理,如果我们不指定源地址,源地址默认为0.0.0.0/0,0.0.0.0/0表示所有IP,示例如下。

041917_0537_7

上例表示,所有IP发送往101的报文都将被丢弃。

与-s选项一样,-d选项也可以使用”叹号”进行取反,也能够同时指定多个IP地址,使用”逗号”隔开即可。

但是请注意,不管是-s选项还是-d选项,取反操作与同时指定多个IP的操作不能同时使用。

需要明确的一点就是:当一条规则中有多个匹配条件时,这多个匹配条件之间,默认存在”与”的关系

说白了就是,当一条规则中存在多个匹配条件时,报文必须同时满足这些条件,才算做被规则匹配。

就如下例所示,下图中的规则包含有两个匹配条件,源地址与目标地址,报文必须同时能被这两个条件匹配,才算作被当前规则匹配,也就是说,下例中,报文必须来自146,同时报文的目标地址必须为101,才会被如下规则匹配,两个条件必须同时满足。

041917_0537_8

我们除了能够使用-s选项和-d选项匹配源IP与目标IP以外,还能够匹配”源端口”与”目标端口”,但是我们一会儿再聊怎样匹配端口,我们先聊聊其他选项。

4.2 匹配条件:协议类型

我们可以使用-p选项,指定需要匹配的报文的协议类型。

假设,我们只想要拒绝来自146的tcp类型的请求,那么可以进行如下设置

041917_0537_9

上图中,防火墙拒绝了来自146的tcp报文发往156这个IP,那么我们来测试一下,我们在146上使用ssh连接101这个IP试试(ssh协议的传输层协议属于tcp协议类型)

041917_0537_10

如上图所示,ssh连接被拒绝了,那么我们使用ping命令试试 (ping命令使用icmp协议),看看能不能ping通156。

041917_0537_11

可以看到,PING命令可以ping通156,证明icmp协议并没有被规则匹配到,只有tcp类型的报文被匹配到了。

那么,-p选项都支持匹配哪些协议呢?我们总结一下

centos6中,-p选项支持如下协议类型

tcp, udp, udplite, icmp, esp, ah, sctp

centos7中,-p选项支持如下协议类型

tcp, udp, udplite, icmp, icmpv6,esp, ah, sctp, mh

当不使用-p指定协议类型时,默认表示所有类型的协议都会被匹配到,与使用-p all的效果相同。

4.3 匹配条件:网卡接口

我们再来认识一个新的匹配条件,当本机有多个网卡时,我们可以使用 -i 选项去匹配报文是通过哪块网卡流入本机的。

我们先动手做个小例子,对-i选项有一个初步的了解以后,再结合理论去看。

当前主机的网卡名称为eth4,如下图

img

假设想要拒绝由网卡eth4流入的ping请求报文,则可以进行如下设置。

img

上图中,使用-i选项,指定网卡名称,使用-p选项,指定了需要匹配的报文协议类型,上例表示丢弃由eth4网卡流入的icmp类型的报文。

是不是很容易理解,但是,我们需要考虑一个问题,-i选项是用于匹配报文流入的网卡的,也就是说,从本机发出的报文是不可能会使用到-i选项的,因为这些由本机发出的报文压根不是从网卡流入的,而是要通过网卡发出的,从这个角度考虑,-i选项的使用是有限制的。

为了更好的解释-i选项,我们回顾一下在理论总结中的一张iptables全局报文流向图,如下。

img

既然-i选项是用于判断报文是从哪个网卡流入的,那么,-i选项只能用于上图中的PREROUTING链、INPUT链、FORWARD链,这是-i选项的特殊性,因为它只是用于判断报文是从哪个网卡流入的,所以只能在上图中”数据流入流向”的链中与FORWARD链中存在,而上图中的”数据发出流向”经过的链中,是不可能使用-i选项的,比如上图中的OUTPUT链与POSTROUTING链,他们都不能使用-i选项。

理解完-i选项,再来理解-o选项就好办了。

当主机有多块网卡时,可以使用-o选项,匹配报文将由哪块网卡流出,没错,-o选项与-i选项是相对的,-i选项用于匹配报文从哪个网卡流入,-o选项用于匹配报文将从哪个网卡流出。

聪明如你,一定想到了,-i选项只能用于PREROUTING链、INPUT链、FORWARD链,那么-o选项只能用于FORWARD链、OUTPUT链、POSTROUTING链。

因为-o选项是用于匹配报文将由哪个网卡”流出”的,所以与上图中的”数据进入流向”中的链没有任何缘分,所以,-o选项只能用于FORWARD链、OUTPUT链、POSTROUTING链中。

看来,FORWARD链属于”中立国”,它能同时使用-i选项与-o选项。

4.4 扩展匹配条件

好了,现在,我们就要聊聊,怎样匹配报文的”源端口”与”目标端口”。

在上文中,我们总结了”源地址”与”目标地址”以后,就顺便提到了”源端口”与”目标端口”,但是,为什么刚才不介绍”源端口”与”目标端口”,非要现在介绍呢?这是因为”源端口”与”目标端口”属于扩展匹配条件,”源地址”与”目标地址”属于基本匹配条件,上文中介绍到的匹配条件,都属于基本匹配条件,所以,我们单独把”源端口”与”目标端口”,放在后面总结,是为了引出扩展匹配条件的概念。

那么,先来了解一下,什么是扩展匹配条件。

不是基本匹配条件的就是扩展匹配条件,这样说好像是句废话,我们可以这样理解,基本匹配条件我们可以直接使用,而如果想要使用扩展匹配条件,则需要依赖一些扩展模块,或者说,在使用扩展匹配条件之前,需要指定相应的扩展模块才行,这样说不容易明白,我们做个例子,就能够明白。

我们知道,sshd服务的默认端口为22,当我们使用ssh工具远程连接主机时,默认会连接服务端的22号端口,假设,我们现在想要使用iptables设置一条规则,拒绝来自192.168.1.146的ssh请求,我们就可以拒绝146上的报文能够发往本机的22号端口,这个时候,就需要用到”目标端口”选项。

使用选项–dport可以匹配报文的目标端口,–dport意为destination-port,即表示目标端口。

注意,与之前的选项不同,–dport前有两条”横杠”,而且,使用–dport选项时,必须事先指定了使用哪种协议,即必须先使用-p选项,示例如下

img

上图中,我们就使用了扩展匹配条件–dport,指定了匹配报文的目标端口,如果外来报文的目标端口为本机的22号端口(ssh默认端口),则拒绝之,而在使用–dport之前,我们使用-m选项,指定了对应的扩展模块为tcp,也就是说,如果想要使用–dport这个扩展匹配条件,则必须依靠某个扩展模块完成,上例中,这个扩展模块就是tcp扩展模块,最终,我们使用的是tcp扩展模块中的dport扩展匹配条件。

现在,我们再回过头来看看扩展匹配条件的概念,就更加明白了。

扩展匹配条件被使用时,则需要依赖一些扩展模块,或者说,在使用扩展匹配条件之前,需要指定相应的扩展模块才行。

现在你明白了吗? -m tcp表示使用tcp扩展模块,–dport表示tcp扩展模块中的一个扩展匹配条件,可用于匹配报文的目标端口。

注意,-p tcp与 -m tcp并不冲突,-p用于匹配报文的协议,-m 用于指定扩展模块的名称,正好,这个扩展模块也叫tcp。

其实,上例中,我们可以省略-m选项,示例如下。

img

当使用-p选项指定了报文的协议时,如果在没有使用-m指定对应的扩展模块名称的情况下,使用了扩展匹配条件, iptables默认会调用与-p选项对应的协议名称相同的模块。

上例中,我们使用-p选项指定了协议名称,使用扩展匹配条件–dport指定了目标端口,在使用扩展匹配条件的时候,如果没有使用-m指定使用哪个扩展模块,iptables会默认使用”-m 协议名”,而协议名就是-p选项对应的协议名,上例中,-p 对应的值为tcp,所以默认调用的扩展模块就为-m tcp,如果-p对应的值为udp,那么默认调用的扩展模块就为-m udp。

所以,上例中,其实”隐式”的指定了扩展模块,只是没有表现出来罢了。

所以,在使用扩展匹配条件时,一定要注意,如果这个扩展匹配条件所依赖的扩展模块名正好与-p对应的协议名称相同,那么则可省略-m选项,否则则不能省略-m选项,必须使用-m选项指定对应的扩展模块名称,这样说可能还是不是特别明了,在后续的举例中,我们会更加明了的理解这些概念。

有”目标端口”,就有”源端口”,代表”源端口”的扩展匹配条件为–sport

使用–sport可以判断报文是否从指定的端口发出,即匹配报文的源端口是否与指定的端口一致,–sport表示source-port,即表示源端口之意。

因为我们已经搞明白了dport,那么sport我就不再赘述了,示例如下

img

上例中,隐含了”-m tcp”之意,表示使用了tcp扩展模块的–sport扩展匹配条件。

扩展匹配条件是可以取反的,同样是使用”!”进行取反,比如 “! –dport 22″,表示目标端口不是22的报文将会被匹配到。

不管是–sport还是–dsport,都能够指定一个端口范围,比如,–dport 22:25表示目标端口为22到25之间的所有端口,即22端口、23端口、24端口、25端口,示例如下

img

也可以写成如下图中的模样,下图中第一条规则表示匹配0号到22号之间的所有端口,下图中的第二条规则表示匹配80号端口以及其以后的所有端口(直到65535)。

img

刚才聊到的两个扩展匹配条件都是tcp扩展模块的,其实,tcp扩展模块还有一个比较有用的扩展匹配条件叫做”–tcp-flags”,但是由于篇幅原因,以后再对这个扩展匹配条件进行总结。

借助tcp扩展模块的–sport或者–dport都可以指定一个连续的端口范围,但是无法同时指定多个离散的、不连续的端口,如果想要同时指定多个离散的端口,需要借助另一个扩展模块,”multiport”模块。

我们可以使用multiport模块的–sports扩展条件同时指定多个离散的源端口。

我们可以使用multiport模块的–dports扩展条件同时指定多个离散的目标端口。

示例如下

img

上图示例表示,禁止来自146的主机上的tcp报文访问本机的22号端口、36号端口以及80号端口。

上图中,”-m multiport –dports 22,36,80″表示使用了multiport扩展模块的–dports扩展条件,以同时指定了多个离散的端口,每个端口之间用逗号隔开。

上图中的-m multiport是不能省略的,如果你省略了-m multiport,就相当于在没有指定扩展模块的情况下,使用了扩展条件(”–dports”),那么上例中,iptables会默认调用”-m tcp”,但是,”–dports扩展条件”并不属于”tcp扩展模块”,而是属于”multiport扩展模块”,所以,这时就会报错。

综上所述,当使用–dports或者–sports这种扩展匹配条件时,必须使用-m指定模块的名称。

其实,使用multiport模块的–sports与–dpors时,也可以指定连续的端口范围,并且能够在指定连续的端口范围的同时,指定离散的端口号,示例如下。

img

上例中的命令表示拒绝来自192.168.1.146的tcp报文访问当前主机的22号端口以及80到88之间的所有端口号,是不是很方便?有没有很灵活?

不过需要注意,multiport扩展只能用于tcp协议与udp协议,即配合-p tcp或者-p udp使用。

再回过头看之前的概念,我想,你应该就更加明白了。

今天,我们只是初步的认识了扩展模块,以及扩展匹配条件,还有一些模块我们并没有总结,好饭不怕晚,后续会有对它们的总结。

4.5 小结

这篇文章中,我们主要总结了一些常用的”基础匹配条件”,并且初步的认识了两个”扩展模块”以及这两个扩展模块中一些常用的扩展条件,为了方便以后回顾,我们将它们总结如下。

首先我们要明确一点,当规则中同时存在多个匹配条件时,多个条件之间默认存在”与”的关系,即报文必须同时满足所有条件,才能被规则匹配。

4.5.1 基本匹配条件总结

-s用于匹配报文的源地址,可以同时指定多个源地址,每个IP之间用逗号隔开,也可以指定为一个网段。

#示例如下

iptables -t filter -I INPUT -s 192.168.1.111,192.168.1.118 -j DROP

iptables -t filter -I INPUT -s 192.168.1.0/24 -j ACCEPT

iptables -t filter -I INPUT ! -s 192.168.1.0/24 -j ACCEPT

-d用于匹配报文的目标地址,可以同时指定多个目标地址,每个IP之间用逗号隔开,也可以指定为一个网段。

#示例如下

iptables -t filter -I OUTPUT -d 192.168.1.111,192.168.1.118 -j DROP

iptables -t filter -I INPUT -d 192.168.1.0/24 -j ACCEPT

iptables -t filter -I INPUT ! -d 192.168.1.0/24 -j ACCEPT

-p用于匹配报文的协议类型,可以匹配的协议类型tcp、udp、udplite、icmp、esp、ah、sctp等(centos7中还支持icmpv6、mh)。

#示例如下

iptables -t filter -I INPUT -p tcp -s 192.168.1.146 -j ACCEPT

iptables -t filter -I INPUT ! -p udp -s 192.168.1.146 -j ACCEPT

-i用于匹配报文是从哪个网卡接口流入本机的,由于匹配条件只是用于匹配报文流入的网卡,所以在OUTPUT链与POSTROUTING链中不能使用此选项。

#示例如下

iptables -t filter -I INPUT -p icmp -i eth4 -j DROP

iptables -t filter -I INPUT -p icmp ! -i eth4 -j DROP

-o用于匹配报文将要从哪个网卡接口流出本机,于匹配条件只是用于匹配报文流出的网卡,所以在INPUT链与PREROUTING链中不能使用此选项。

#示例如下

iptables -t filter -I OUTPUT -p icmp -o eth4 -j DROP

iptables -t filter -I OUTPUT -p icmp ! -o eth4 -j DROP

4.5.2 扩展匹配条件总结

我们来总结一下今天认识的两个扩展模块,以及其中的扩展条件(并非全部,只是这篇文章中介绍过的)

tcp扩展模块

常用的扩展匹配条件如下:

-p tcp -m tcp –sport 用于匹配tcp协议报文的源端口,可以使用冒号指定一个连续的端口范围

-p tcp -m tcp –dport 用于匹配tcp协议报文的目标端口,可以使用冒号指定一个连续的端口范围

#示例如下

iptables -t filter -I OUTPUT -d 192.168.1.146 -p tcp -m tcp --sport 22 -j REJECT

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m tcp --dport 22:25 -j REJECT

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m tcp --dport :22 -j REJECT

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m tcp --dport 80: -j REJECT

iptables -t filter -I OUTPUT -d 192.168.1.146 -p tcp -m tcp ! --sport 22 -j ACCEPT

multiport扩展模块

常用的扩展匹配条件如下:

-p tcp -m multiport –sports 用于匹配报文的源端口,可以指定离散的多个端口号,端口之间用”逗号”隔开

-p udp -m multiport –dports 用于匹配报文的目标端口,可以指定离散的多个端口号,端口之间用”逗号”隔开

#示例如下

iptables -t filter -I OUTPUT -d 192.168.1.146 -p udp -m multiport --sports 137,138 -j REJECT

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m multiport --dports 22,80 -j REJECT

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m multiport ! --dports 22,80 -j REJECT

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m multiport --dports 80:88 -j REJECT

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m multiport --dports 22,80:88 -j REJECT

5. 常用扩展模块

5.1 iprange扩展模块

之前我们已经总结过,在不使用任何扩展模块的情况下,使用-s选项或者-d选项即可匹配报文的源地址与目标地址,而且在指定IP地址时,可以同时指定多个IP地址,每个IP用”逗号”隔开,但是,-s选项与-d选项并不能一次性的指定一段连续的IP地址范围,如果我们需要指定一段连续的IP地址范围,可以使用iprange扩展模块。

使用iprange扩展模块可以指定”一段连续的IP地址范围”,用于匹配报文的源地址或者目标地址。

iprange扩展模块中有两个扩展匹配条件可以使用

–src-range

–dst-range

没错,见名知意,上述两个选项分别用于匹配报文的源地址所在范围与目标地址所在范围。

示例如下:

img

上例表示如果报文的源IP地址如果在192.168.1.127到192.168.1.146之间,则丢弃报文,IP段的始末IP使用”横杠”连接,–src-range与–dst-range和其他匹配条件一样,能够使用”!”取反,有了前文中的知识作为基础,此处就不再赘述了。

5.2 string 扩展模块

使用string扩展模块,可以指定要匹配的字符串,如果报文中包含对应的字符串,则符合匹配条件。

比如,如果报文中包含字符”OOXX”,我们就丢弃当前报文。

首先,我们在IP为146的主机上启动http服务,然后在默认的页面目录中添加两个页面,页面中的内容分别为”OOXX”和”Hello World”,如下图所示,在没有配置任何规则时,126主机可以正常访问146主机上的这两个页面。

img

那么,我们想要达到的目的是,如果报文中包含”OOXX”字符,我们就拒绝报文进入本机,所以,我们可以在126上进行如下配置。

img

上图中,’-m string’表示使用string模块,’–algo bm’表示使用bm算法去匹配指定的字符串,’ –string “OOXX” ‘则表示我们想要匹配的字符串为”OOXX”

设置完上图中的规则后,由于index.html中包含”OOXX”字符串,所以,146的回应报文无法通过126的INPUT链,所以无法获取到页面对应的内容。

那么,我们来总结一下string模块的常用选项

–algo:用于指定匹配算法,可选的算法有bm与kmp,此选项为必须选项,我们不用纠结于选择哪个算法,但是我们必须指定一个。

–string:用于指定需要匹配的字符串。

5.3 time 扩展模块

我们可以通过time扩展模块,根据时间段区匹配报文,如果报文到达的时间在指定的时间范围以内,则符合匹配条件。

比如,”我想要自我约束,每天早上9点到下午6点不能看网页”,擦,多么残忍的规定,如果你想要这样定义,可以尝试使用如下规则。

img

上图中”-m time”表示使用time扩展模块,–timestart选项用于指定起始时间,–timestop选项用于指定结束时间。

如果你想要换一种约束方法,只有周六日不能看网页,那么可以使用如下规则。

img

没错,如你所见,使用–weekdays选项可以指定每个星期的具体哪一天,可以同时指定多个,用逗号隔开,除了能够数字表示”星期几”,还能用缩写表示,例如:Mon, Tue, Wed, Thu, Fri, Sat, Sun

当然,你也可以将上述几个选项结合起来使用,比如指定只有周六日的早上9点到下午6点不能浏览网页。

img

聪明如你一定想到了,既然有–weekdays选项了,那么有没有–monthdays选项呢?必须有啊!

使用–monthdays选项可以具体指定的每个月的哪一天,比如,如下图设置表示指明每月的22号,23号。

img

前文已经总结过,当一条规则中同时存在多个条件时,多个条件之间默认存在”与”的关系,所以,下图中的设置表示匹配的时间必须为星期5,并且这个”星期5″同时还需要是每个月的22号到28号之间的一天,所以,下图中的设置表示每个月的第4个星期5

img

除了使用–weekdays选项与–monthdays选项,还可以使用–datestart 选项与-datestop选项,指定具体的日期范围,如下。

img

上图中指定的日期范围为2017年12月24日到2017年12月27日

上述选项中,–monthdays与–weekdays可以使用”!”取反,其他选项不能取反。

5.4 connlimit 扩展模块

使用connlimit扩展模块,可以限制每个IP地址同时链接到server端的链接数量,注意:我们不用指定IP,其默认就是针对”每个客户端IP”,即对单IP的并发连接数限制。

比如,我们想要限制,每个IP地址最多只能占用两个ssh链接远程到server端,我们则可以进行如下限制。

img

上例中,使用”-m connlimit”指定使用connlimit扩展,使用”–connlimit-above 2″表示限制每个IP的链接数量上限为2,再配合-p tcp –dport 22,即表示限制每个客户端IP的ssh并发链接数量不能高于2。

centos6中,我们可以对–connlimit-above选项进行取反,没错,老规矩,使用”!”对此条件进行取反,示例如下

img

上例表示,每个客户端IP的ssh链接数量只要不超过两个,则允许链接。

但是聪明如你一定想到了,上例的规则并不能表示:每个客户端IP的ssh链接数量超过两个则拒绝链接(与前文中的举例原理相同,此处不再赘述,如果你不明白,请参考之前的文章)。也就是说,即使我们配置了上例中的规则,也不能达到”限制”的目的,所以我们通常并不会对此选项取反,因为既然使用了此选项,我们的目的通常就是”限制”连接数量。

centos7中iptables为我们提供了一个新的选项,–connlimit-upto,这个选项的含义与”! –commlimit-above”的含义相同,即链接数量未达到指定的连接数量之意,所以综上所述,–connlimit-upto选项也不常用。

刚才说过,–connlimit-above默认表示限制”每个IP”的链接数量,其实,我们还可以配合–connlimit-mask选项,去限制”某类网段”的链接数量,示例如下:

(注:下例需要一定的网络知识基础,如果你还不了解它们,可以选择先跳过此选项或者先去学习部分的网络知识)

img

上例中,”–connlimit-mask 24″表示某个C类网段,没错,mask为掩码之意,所以将24转换成点分十进制就表示255.255.255.0,所以,上图示例的规则表示,一个最多包含254个IP的C类网络中,同时最多只能有2个ssh客户端连接到当前服务器,看来资源很紧俏啊!254个IP才有2个名额,如果一个IP同时把两个连接名额都占用了,那么剩下的253个IP连一个连接名额都没有了,那么,我们再看看下例,是不是就好多了。

img

上例中,”–connlimit-mask 27″表示某个C类网段,通过计算后可以得知,这个网段中最多只能有30台机器(30个IP),这30个IP地址最多只能有10个ssh连接同时连接到服务器端,是不是比刚才的设置大方多了,当然,这样并不能避免某个IP占用所有连接的情况发生,假设,报文来自192.168.1.40这个IP,按照掩码为27进行计算,这个IP属于192.168.1.32/27网段,如果192.168.1.40同时占用了10个ssh连接,那么当192.168.1.51这个IP向服务端发起ssh连接请求时,同样会被拒绝,因为192.168.1.51这个IP按照掩码为27进行计算,也是属于192.168.1.32/27网段,所以他们共享这10个连接名额。

聪明如你一定明白了,在不使用–connlimit-mask的情况下,连接数量的限制是针对”每个IP”而言的,当使用了–connlimit-mask选项以后,则可以针对”某类IP段内的一定数量的IP”进行连接数量的限制,这样就能够灵活许多,不是吗?

5.5 limit 扩展模块

刚才认识了connlimit模块,现在来认识一下limit模块。

connlimit模块是对连接数量进行限制的,limit模块是对”报文到达速率”进行限制的。

用大白话说就是,如果我想要限制单位时间内流入的包的数量,就能用limit模块。

我们可以以秒为单位进行限制,也可以以分钟、小时、天作为单位进行限制。

比如,限制每秒中最多流入3个包,或者限制每分钟最多流入30个包,都可以。

那么,我们来看一个最简单的示例,假设,我们想要限制,外部主机对本机进行ping操作时,本机最多每6秒中放行一个ping包,那么,我们可以进行如下设置(注意,只进行如下设置有可能无法实现限制功能,请看完后面的内容)

img

上例中,”-p icmp”表示我们针对ping请求添加了一条规则(ping使用icmp协议),”-m limit”表示使用limit模块, “–limit 10/minute -j ACCEPT”表示每分钟最多放行10个包,就相当于每6秒钟最多放行一个包,换句话说,就是每过6秒钟放行一个包,那么配置完上述规则后,我们在另外一台机器上对当前机器进行ping操作,看看是否能够达到限制的目的,如下图所示。

img

我们发现,刚才配置的规则并没有如我们想象中的一样,ping请求的响应速率完全没有发生任何变化,为什么呢?我们一起来分析一下。

我们再来回顾一下刚才配置的规则。

img

其实,我们可以把上图中的规则理解为如下含义。

每6秒放行一个包,那么iptables就会计时,每6秒一个轮次,到第6秒时,达到的报文就会匹配到对应的规则,执行对应的动作,而上图中的动作是ACCEPT。

那么在第6秒之前到达的包,则无法被上述规则匹配到。

之前总结过,报文会匹配链中的每一条规则,如果没有任何一条规则能够匹配到,则匹配默认动作(链的默认策略)。

既然第6秒之前的包没有被上述规则匹配到,而我们又没有在INPUT链中配置其他规则,所以,第6秒之前的包肯定会被默认策略匹配到,那么我们看看默认策略是什么。

img

现在再想想,我想你应该明白为什么刚才的ping的响应速率没有变化了。

因为,上例中,第六秒的报文的确被对应的规则匹配到了,于是执行了”放行”操作,第6秒之前的报文没有被上图中配置的规则匹配到,但是被默认策略匹配到了,而恰巧,默认动作也是ACCEPT,所以,相当于所有的ping报文都被放行了,怪不得与没有配置规则时的速率一毛一样了。

那么,知错就改,聪明如你一定想到了,我们可以修改INPUT链的默认策略,或者在上例限制规则的后面再加入一条规则,将”漏网之鱼”匹配到即可,示例如下。

img

如上图所示,第一条规则表示每分钟最多放行10个icmp包,也就是6秒放行一个,第6秒的icmp包会被上例中的第一条规则匹配到,第6秒之前的包则不会被第一条规则匹配到,于是被后面的拒绝规则匹配到了,那么,此刻,我们再来试试,看看ping的报文放行速率有没有发生改变。

如下图所示

img

刚开始还真吓我一跳,难道配置的规则还是有问题?

结果发现,只有前5个ping包没有受到限制,之后的ping包已经开始受到了规则的限制了。

从上图可以看出,除了前5个ping包以外,之后的ping包差不多每6秒才能ping通一次,看来,之后的ping包已经受到了规则的控制,被限制了流入防火墙的速率了,那么,前5个ping包是什么鬼?为什么它们不受规则限制呢?其实,这个现象正好引出另一个话题,出现上图中的情况,是因为另一个选项:”–limit-burst”

limit-burst选项是干什么用的呢?我们先用不准确的大白话描述一遍,”–limit-burst”可以指定”空闲时可放行的包的数量”,其实,这样说并不准确,但是我们可以先这样大概的理解,在不使用”–limit-burst”选项明确指定放行包的数量时,默认值为5,所以,才会出现上图中的情况,前5个ping包并没有受到任何速率限制,之后的包才受到了规则的限制。

如果想要彻底了解limit模块的工作原理,我们需要先了解一下”令牌桶”算法,因为limit模块使用了令牌桶算法。

我们可以这样想象,有一个木桶,木桶里面放了5块令牌,而且这个木桶最多也只能放下5块令牌,所有报文如果想要出关入关,都必须要持有木桶中的令牌才行,这个木桶有一个神奇的功能,就是每隔6秒钟会生成一块新的令牌,如果此时,木桶中的令牌不足5块,那么新生成的令牌就存放在木桶中,如果木桶中已经存在5块令牌,新生成的令牌就无处安放了,只能溢出木桶(令牌被丢弃),如果此时有5个报文想要入关,那么这5个报文就去木桶里找令牌,正好一人一个,于是他们5个手持令牌,快乐的入关了,此时木桶空了,再有报文想要入关,已经没有对应的令牌可以使用了,但是,过了6秒钟,新的令牌生成了,此刻,正好来了一个报文想要入关,于是,这个报文拿起这个令牌,就入关了,在这个报文之后,如果很长一段时间内没有新的报文想要入关,木桶中的令牌又会慢慢的积攒了起来,直到达到5个令牌,并且一直保持着5个令牌,直到有人需要使用这些令牌,这就是令牌桶算法的大致逻辑。

那么,就拿刚才的”令牌桶”理论类比我们的命令,”–limit”选项就是用于指定”多长时间生成一个新令牌的”,”–limit-burst”选项就是用于指定”木桶中最多存放几个令牌的”,现在,你明白了吗??示例如下

img

上例表示,令牌桶中最多能存放3个令牌,每分钟生成10个令牌(即6秒钟生成一个令牌)。

之前说过,使用”–limit”选项时,可以选择的时间单位有多种,如下

/second

/minute

/hour

/day

比如,3/second表示每秒生成3个”令牌”,30/minute表示没分钟生成30个”令牌”。

我不知道我到底解释清楚没有,我感觉我解释清楚了,哥们儿你赶紧动手试试吧。

5.6 小结

老规矩,为了方便以后回顾,我们将上文中提到的命令总结如下。

5.6.1 iprange模块

包含的扩展匹配条件如下

–src-range:指定连续的源地址范围

–dst-range:指定连续的目标地址范围

#示例

iptables -t filter -I INPUT -m iprange --src-range 192.168.1.127-192.168.1.146 -j DROP

iptables -t filter -I OUTPUT -m iprange --dst-range 192.168.1.127-192.168.1.146 -j DROP

iptables -t filter -I INPUT -m iprange ! --src-range 192.168.1.127-192.168.1.146 -j DROP

5.6.2 string模块

常用扩展匹配条件如下

–algo:指定对应的匹配算法,可用算法为bm、kmp,此选项为必需选项。

–string:指定需要匹配的字符串

#示例

iptables -t filter -I INPUT -p tcp --sport 80 -m string --algo bm --string "OOXX" -j REJECT

iptables -t filter -I INPUT -p tcp --sport 80 -m string --algo bm --string "OOXX" -j REJECT

5.6.3 time模块

常用扩展匹配条件如下

–timestart:用于指定时间范围的开始时间,不可取反

–timestop:用于指定时间范围的结束时间,不可取反

–weekdays:用于指定”星期几”,可取反

–monthdays:用于指定”几号”,可取反

–datestart:用于指定日期范围的开始日期,不可取反

–datestop:用于指定日期范围的结束时间,不可取反

#示例

iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --timestart 09:00:00 --timestop 19:00:00 -j REJECT

iptables -t filter -I OUTPUT -p tcp --dport 443 -m time --timestart 09:00:00 --timestop 19:00:00 -j REJECT

iptables -t filter -I OUTPUT -p tcp --dport 80  -m time --weekdays 6,7 -j REJECT

iptables -t filter -I OUTPUT -p tcp --dport 80  -m time --monthdays 22,23 -j REJECT

iptables -t filter -I OUTPUT -p tcp --dport 80  -m time ! --monthdays 22,23 -j REJECT

iptables -t filter -I OUTPUT -p tcp --dport 80  -m time --timestart 09:00:00 --timestop 18:00:00 --weekdays 6,7 -j REJECT

iptables -t filter -I OUTPUT -p tcp --dport 80  -m time --weekdays 5 --monthdays 22,23,24,25,26,27,28 -j REJECT

iptables -t filter -I OUTPUT -p tcp --dport 80  -m time --datestart 2017-12-24 --datestop 2017-12-27 -j REJECT

5.6.4 connlimit 模块

常用的扩展匹配条件如下

–connlimit-above:单独使用此选项时,表示限制每个IP的链接数量。

–connlimit-mask:此选项不能单独使用,在使用–connlimit-above选项时,配合此选项,则可以针对”某类IP段内的一定数量的IP”进行连接数量的限制,如果不明白可以参考上文的详细解释。

#示例

iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 2 -j REJECT

iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 20 --connlimit-mask 24 -j REJECT

iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 10 --connlimit-mask 27 -j REJECT

5.6.5 limit模块

常用的扩展匹配条件如下

–limit-burst:类比”令牌桶”算法,此选项用于指定令牌桶中令牌的最大数量,上文中已经详细的描述了”令牌桶”的概念,方便回顾。

–limit:类比”令牌桶”算法,此选项用于指定令牌桶中生成新令牌的频率,可用时间单位有second、minute 、hour、day。

#示例 #注意,如下两条规则需配合使用,具体原因上文已经解释过,忘记了可以回顾。

iptables -t filter -I INPUT -p icmp -m limit --limit-burst 3 --limit 10/minute -j ACCEPT

iptables -t filter -A INPUT -p icmp -j REJECT

6. 扩展匹配条件:-tcp-flags

如果你看过前文,那么你一定知道,前文已经对”tcp扩展模块”做过总结,但是只总结了tcp扩展模块中的”–sport”与”–dport”选项,并没有总结”–tcp-flags”选项,那么此处,我们就来认识一下tcp扩展模块中的”–tcp-flags”。

注:阅读这篇文章之前,需要对tcp协议的基础知识有一定的了解,比如:tcp头的结构、tcp三次握手的过程。

见名知义,”–tcp-flags”指的就是tcp头中的标志位,看来,在使用iptables时,我们可以通过此扩展匹配条件,去匹配tcp报文的头部的标识位,然后根据标识位的实际情况实现访问控制的功能。

既然说到了tcp头中的标志位,那么我们就来回顾一下tcp头的结构,如下图所示。

042817_0124_1

在使用iptables时,使用tcp扩展模块的”–tcp-flags”选项,即可对上图中的标志位进行匹配,判断指定的标志位的值是否为”1″,而tcp header的结构不是我们今天讨论的重点,我们继续聊tcp的标识位,在tcp协议建立连接的过程中,需要先进行三次握手,而三次握手就要依靠tcp头中的标志位进行。

为了更加具象化的描述这个过程,我们可以抓包查看ssh建立连接的过程,如下图所示(使用wireshark在ssh客户端抓包,跟踪对应的tcp流):

042817_0124_2

上图为tcp三次握手中的第一次握手,客户端(IP为98)使用本地的随机端口54808向服务端(IP为137)发起连接请求,tcp头的标志位中,只有SYN位被标识为1,其他标志位均为0。

在上图的下方可以看到”[TCP Flags: ··········S·]”,其中的”S”就表示SYN位,整体表示只有SYN位为1。

上图为tcp三次握手中第一次握手的tcp头中的标志位,下图是第二次握手的,服务端回应刚才的请求,将自己的tcp头的SYN标志位也设置为1,同时将ACK标志位也设置为1,如下图所示。

042817_0124_3

上图中的下方显示的标志位列表也变成了,[TCP Flags: ·······A··S·],表示只有ACK标志位与SYN标志位为1,如上图所示,第三次握手我就不再截图了,说到这里,就已经能够引出我们今天要说的话题了,就是”–tcp-flags”选项,假设,我现在想要匹配到上文中提到的”第一次握手”的报文,则可以使用如下命令:

img

上图中,”-m tcp –dport 22″的含义在前文中已经总结过,表示使用tcp扩展模块,指定目标端口为22号端口(ssh默认端口),”–tcp-flags”就是我们今天要讨论的扩展匹配条件,用于匹配报文tcp头部的标志位,”SYN,ACK,FIN,RST,URG,PSH SYN”是什么意思呢?这串字符就是用于配置我们要匹配的标志位的,我们可以把这串字符拆成两部分去理解,第一部分为”SYN,ACK,FIN,RST,URG,PSH”,第二部分为”SYN”。

第一部分表示:我们需要匹配报文tcp头中的哪些标志位,那么上例的配置表示,我们需要匹配报文tcp头中的6个标志位,这6个标志位分别为为”SYN、ACK、FIN、RST、URG、PSH”,我们可以把这一部分理解成需要匹配的标志位列表。

第二部分表示:第一部分的标志位列表中,哪些标志位必须为1,上例中,第二部分为SYN,则表示,第一部分需要匹配的标志位列表中,SYN标志位的值必须为1,其他标志位必须为0。

所以,上例中的”SYN,ACK,FIN,RST,URG,PSH SYN”表示,需要匹配报文tcp头中的”SYN、ACK、FIN、RST、URG、PSH”这些标志位,其中SYN标志位必须为1,其他的5个标志位必须为0,这与上文中wireshark抓包时的情况相同,正是tcp三次握手时第一次握手时的情况,上文中第一次握手的报文的tcp头中的标志位如下:

img

其实,–tcp-flags的表示方法与wireshark的表示方法有异曲同工之妙,只不过,wireshark中,标志位为0的用”点”表示,标志位为1的用对应字母表示,在–tcp-flags中,需要先指明需要匹配哪些标志位,然后再指明这些标志位中,哪些必须为1,剩余的都必须为0。

那么,聪明如你一定想到了,如果我想要匹配tcp头中的第二次握手时的标志位的情况,该怎么表示呢?

示例如下(此处省略对源地址与目标地址的匹配,重点在于对tcp-flags的示例)

img

上图中,第一条命令匹配到的报文是第一次握手的报文,第二条命令匹配到的报文是第二次握手的报文。

综上所述,只要我们能够灵活的配置上例中的标志位,即可匹配到更多的应用场景中。

其实,上例中的两条命令还可以简写为如下模样

img

没错,我们可以用ALL表示”SYN,ACK,FIN,RST,URG,PSH”。

其实,tcp扩展模块还为我们专门提供了一个选项,可以匹配上文中提到的”第一次握手”,那就是–syn选项

使用”–syn”选项相当于使用”–tcp-flags SYN,RST,ACK,FIN SYN”,也就是说,可以使用”–syn”选项去匹配tcp新建连接的请求报文。

示例如下:

img

6.1 小结

tcp扩展模块常用的扩展匹配条件如下:

6.1.1 –sport

用于匹配tcp协议报文的源端口,可以使用冒号指定一个连续的端口范围

#示例

iptables -t filter -I OUTPUT -d 192.168.1.146 -p tcp -m tcp --sport 22 -j REJECT

iptables -t filter -I OUTPUT -d 192.168.1.146 -p tcp -m tcp --sport 22:25 -j REJECT

iptables -t filter -I OUTPUT -d 192.168.1.146 -p tcp -m tcp ! --sport 22 -j ACCEPT

6.1.2 –dport

用于匹配tcp协议报文的目标端口,可以使用冒号指定一个连续的端口范围

#示例

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m tcp --dport 22:25 -j REJECT

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m tcp --dport :22 -j REJECT

iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m tcp --dport 80: -j REJECT

6.1.3 –tcp-flags

用于匹配报文的tcp头的标志位

#示例

iptables -t filter -I INPUT -p tcp -m tcp --dport 22 --tcp-flags SYN,ACK,FIN,RST,URG,PSH SYN -j REJECT

iptables -t filter -I OUTPUT -p tcp -m tcp --sport 22 --tcp-flags SYN,ACK,FIN,RST,URG,PSH SYN,ACK -j REJECT

iptables -t filter -I INPUT -p tcp -m tcp --dport 22 --tcp-flags ALL SYN -j REJECT

iptables -t filter -I OUTPUT -p tcp -m tcp --sport 22 --tcp-flags ALL SYN,ACK -j REJECT

6.1.4 –syn

用于匹配tcp新建连接的请求报文,相当于使用”–tcp-flags SYN,RST,ACK,FIN SYN”

#示例

iptables -t filter -I INPUT -p tcp -m tcp --dport 22 --syn -j REJECT

7. udp 和 icmp 扩展

7.1 udp 扩展

我们先来说说udp扩展模块,这个扩展模块中能用的匹配条件比较少,只有两个,就是–sport与–dport,即匹配报文的源端口与目标端口。

没错,tcp模块中也有这两个选项,名称都一模一样。

只不过udp扩展模块的–sport与–dport是用于匹配UDP协议报文的源端口与目标端口,比如,放行samba服务的137与138这两个UDP端口,示例如下

img

前文说明过,当使用扩展匹配条件时,如果未指定扩展模块,iptables会默认调用与”-p”对应的协议名称相同的模块,所以,当使用”-p udp”时,可以省略”-m udp”,示例如下。

img

udp扩展中的–sport与–dport同样支持指定一个连续的端口范围,示例如下

img

上图中的配置表示137到157之间的所有udp端口全部对外开放,其实与tcp扩展中的使用方法相同。

但是udp中的–sport与–dport也只能指定连续的端口范围,并不能一次性指定多个离散的端口,没错,聪明如你一定想到,使用之前总结过的multiport扩展模块,即可指定多个离散的UDP端口,如果你忘了multiport模块怎样使用,请回顾前文。

总之有了前文的基础,再理解上述示例就容易多了,此处不再对udp模块的–sport与–dport进行赘述。

7.2 icmp 扩展

最常用的tcp扩展、udp扩展已经总结完毕,现在聊聊icmp扩展,没错,看到icmp,你肯定就想到了ping命令,因为ping命令使用的就是icmp协议。

ICMP协议的全称为Internet Control Message Protocol,翻译为互联网控制报文协议,它主要用于探测网络上的主机是否可用,目标是否可达,网络是否通畅,路由是否可用等。

我们平常使用ping命令ping某主机时,如果主机可达,对应主机会对我们的ping请求做出回应(此处不考虑禁ping等情况),也就是说,我们发出ping请求,对方回应ping请求,虽然ping请求报文与ping回应报文都属于ICMP类型的报文,但是如果在概念上细分的话,它们所属的类型还是不同的,我们发出的ping请求属于类型8的icmp报文,而对方主机的ping回应报文则属于类型0的icmp报文,根据应用场景的不同,icmp报文被细分为如下各种类型。

img

从上图可以看出,所有表示”目标不可达”的icmp报文的type码为3,而”目标不可达”又可以细分为多种情况,是网络不可达呢?还是主机不可达呢?再或者是端口不可达呢?所以,为了更加细化的区分它们,icmp对每种type又细分了对应的code,用不同的code对应具体的场景, 所以,我们可以使用type/code去匹配具体类型的ICMP报文,比如可以使用”3/1″表示主机不可达的icmp报文。

上图中的第一行就表示ping回应报文,它的type为0,code也为0,从上图可以看出,ping回应报文属于查询类(query)的ICMP报文,从大类上分,ICMP报文还能分为查询类与错误类两大类,目标不可达类的icmp报文则属于错误类报文。

而我们发出的ping请求报文对应的type为8,code为0。

了解完上述概念,就好办了,我们来看一些应用场景。

假设,我们现在想要禁止所有icmp类型的报文进入本机,那么我们可以进行如下设置。

img

上例中,我们并没有使用任何扩展匹配条件,我们只是使用”-p icmp”匹配了所有icmp协议类型的报文。

如果进行了上述设置,别的主机向我们发送的ping请求报文无法进入防火墙,我们向别人发送的ping请求对应的回应报文也无法进入防火墙。所以,我们既无法ping通别人,别人也无法ping通我们。

假设,此刻需求有变,我们只想要ping通别人,但是不想让别人ping通我们,刚才的配置就不能满足我们了,我们则可以进行如下设置(此处不考虑禁ping的情况)

img

上图中,使用”-m icmp”表示使用icmp扩展,因为上例中使用了”-p icmp”,所以”-m icmp”可以省略,使用”–icmp-type”选项表示根据具体的type与code去匹配对应的icmp报文,而上图中的”–icmp-type 8/0″表示icmp报文的type为8,code为0才会被匹配到,也就是只有ping请求类型的报文才能被匹配到,所以,别人对我们发起的ping请求将会被拒绝通过防火墙,而我们之所以能够ping通别人,是因为别人回应我们的报文的icmp type为0,code也为0,所以无法被上述规则匹配到,所以我们可以看到别人回应我们的信息。

因为type为8的类型下只有一个code为0的类型,所以我们可以省略对应的code,示例如下

img

除了能够使用对应type/code匹配到具体类型的icmp报文以外,我们还能用icmp报文的描述名称去匹配对应类型的报文,示例如下

img

没错,上例中使用的 –icmp-type “echo-request”与 –icmp-type 8/0的效果完全相同,参考本文最上方的表格即可获取对应的icmp类型的描述名称。

img

注意:名称中的”空格”需要替换为”-“。

7.3 小结

7.3.1 udp 扩展

常用的扩展匹配条件

–sport:匹配udp报文的源地址

–dport:匹配udp报文的目标地址

#示例

iptables -t filter -I INPUT -p udp -m udp --dport 137 -j ACCEPT

iptables -t filter -I INPUT -p udp -m udp --dport 137:157 -j ACCEPT

#可以结合multiport模块指定多个离散的端口

7.3.2 icmp 扩展

常用的扩展匹配条件

–icmp-type:匹配icmp报文的具体类型

#示例

iptables -t filter -I INPUT -p icmp -m icmp --icmp-type 8/0 -j REJECT

iptables -t filter -I INPUT -p icmp --icmp-type 8 -j REJECT

iptables -t filter -I OUTPUT -p icmp -m icmp --icmp-type 0/0 -j REJECT

iptables -t filter -I OUTPUT -p icmp --icmp-type 0 -j REJECT

iptables -t filter -I INPUT -p icmp --icmp-type "echo-request" -j REJECT

8. state 扩展

当我们通过http的url访问某个网站的网页时,客户端向服务端的80端口发起请求,服务端再通过80端口响应我们的请求,于是,作为客户端,我们似乎应该理所应当的放行80端口,以便服务端回应我们的报文可以进入客户端主机,于是,我们在客户端放行了80端口,同理,当我们通过ssh工具远程连接到某台服务器时,客户端向服务端的22号端口发起请求,服务端再通过22号端口响应我们的请求,于是我们理所应当的放行了所有22号端口,以便远程主机的响应请求能够通过防火墙,但是,作为客户端,如果我们并没有主动向80端口发起请求,也没有主动向22号端口发起请求,那么其他主机通过80端口或者22号端口向我们发送数据时,我们可以接收到吗?应该是可以的,因为我们为了收到http与ssh的响应报文,已经放行了80端口与22号端口,所以,不管是”响应”我们的报文,还是”主动发送”给我们的报文,应该都是可以通过这两个端口的,那么仔细想想,这样是不是不太安全呢?如果某些与你敌对的人,利用这些端口”主动”连接到你的主机,你肯定会不爽的吧,一般都是我们主动请求80端口,80端口回应我们,但是一般不会出现80端口主动请求我们的情况吧。

你心里可能会这样想:我知道哪些主机是安全的,我只要针对这些安全的主机放行对应的端口就行了,其他IP一律拒绝,比如,我知道IP为123的主机是安全的,所以,我对123主机开放了22号端口,以便123主机能够通过22号端口响应我们的ssh请求,那么,如果你需要管理的主机越来越多呢?你是不是每次都要为新的主机配置这些规则呢?如果有30台主机呢?如果有300台主机呢?80端口就更别提了,难道你每次访问一个新的网址,都要对这个网址添加信任吗?这显然不太合理。

你心里可能又会想:针对对应的端口,我用–tcp-flags去匹配tcp报文的标志位,把外来的”第一次握手”的请求拒绝,是不是也可以呢?那么如果对方使用的是UDP协议或者ICMP协议呢?似乎总是有一些不完美的地方。

那么我们仔细的思考一下,造成上述问题的”根源”在哪里,我们为了让”提供服务方”能够正常的”响应”我们的请求,于是在主机上开放了对应的端口,开放这些端口的同时,也出现了问题,别人利用这些开放的端口,”主动”的攻击我们,他们发送过来的报文并不是为了响应我们,而是为了主动攻击我们,好了,我们似乎找到了问题所在?

问题就是:怎样判断这些报文是为了回应我们之前发出的报文,还是主动向我们发送的报文呢?

我们可以通过iptables的state扩展模块解决上述问题,但是我们需要先了解一些state模块的相关概念,然后再回过头来解决上述问题。

从字面上理解,state可以译为状态,但是我们也可以用一个高大上的词去解释它,state模块可以让iptables实现”连接追踪”机制。

那么,既然是”连接追踪”,则必然要有”连接”。

咱们就来聊聊什么是连接吧,一说到连接,你可能会下意识的想到tcp连接,但是,对于state模块而言的”连接”并不能与tcp的”连接”画等号,在TCP/IP协议簇中,UDP和ICMP是没有所谓的连接的,但是对于state模块来说,tcp报文、udp报文、icmp报文都是有连接状态的,我们可以这样认为,对于state模块而言,只要两台机器在”你来我往”的通信,就算建立起了连接,如下图所示

img

而报文在这个所谓的链接中是什么状态的呢?这是我们后面讨论的话题。

对于state模块的连接而言,”连接”其中的报文可以分为5种状态,报文状态可以为NEW、ESTABLISHED、RELATED、INVALID、UNTRACKED

那么上述报文的状态都代表什么含义呢?我们先来大概的了解一下概念,然后再结合示例说明。

注意:如下报文状态都是对于state模块来说的。

NEW:连接中的第一个包,状态就是NEW,我们可以理解为新连接的第一个包的状态为NEW。

ESTABLISHED:我们可以把NEW状态包后面的包的状态理解为ESTABLISHED,表示连接已建立。

或许用图说话更容易被人理解

img

RELATED:从字面上理解RELATED译为关系,但是这样仍然不容易理解,我们举个例子。

比如FTP服务,FTP服务端会建立两个进程,一个命令进程,一个数据进程。

命令进程负责服务端与客户端之间的命令传输(我们可以把这个传输过程理解成state中所谓的一个”连接”,暂称为”命令连接”)。

数据进程负责服务端与客户端之间的数据传输 ( 我们把这个过程暂称为”数据连接” )。

但是具体传输哪些数据,是由命令去控制的,所以,”数据连接”中的报文与”命令连接”是有”关系”的。

那么,”数据连接”中的报文可能就是RELATED状态,因为这些报文与”命令连接”中的报文有关系。

(注:如果想要对ftp进行连接追踪,需要单独加载对应的内核模块nf_conntrack_ftp,如果想要自动加载,可以配置/etc/sysconfig/iptables-config文件)

INVALID:如果一个包没有办法被识别,或者这个包没有任何状态,那么这个包的状态就是INVALID,我们可以主动屏蔽状态为INVALID的报文。

UNTRACKED:报文的状态为untracked时,表示报文未被追踪,当报文的状态为Untracked时通常表示无法找到相关的连接。

上述5种状态的详细解释可以参考如下文章的”User-land states”章节

http://www.iptables.info/en/connection-state.html

好了,我们已经大致了解了state模块中所定义的5种状态,那么现在,我们回过头想想刚才的问题。

刚才问题的根源就是:怎样判断报文是否是为了回应之前发出的报文。

刚才举例中的问题即可使用state扩展模块解决,我们只要放行状态为ESTABLISHED的报文即可,因为如果报文的状态为ESTABLISHED,那么报文肯定是之前发出的报文的回应,如果你还不放心,可以将状态为RELATED或ESTABLISHED的报文都放行,这样,就表示只有回应我们的报文能够通过防火墙,如果是别人主动发送过来的新的报文,则无法通过防火墙,示例如下。

img

当前主机IP为104,当放行ESTABLISHED与RELATED状态的包以后,并没有影响通过本机远程ssh到IP为77的主机上,但是无法从77上使用22端口主动连接到104上。

对于其他端口与IP来说,也是相同的,可以从104主动发送报文,并且能够收到响应报文,但是其他主机并不能主动向104发起请求。

好了,state模块就总结到这里,希望这篇文章能够对你有所帮助。