iptables入门:规则及路由基础

之前在写服务器安全基础的文章的时候,涉及到了iptables的一些知识。本来不打算对iptables做单独的介绍,但是由于网上的文章都太水,没有让人一看就能知道的教程,一些比较简单的东西到处都是,而那些稍微比较复杂一点的,诸位作者就说这个你用不到,然后就略过了。本文则来拾遗这些本来应该被注意的点。

数据包关卡

到来自服务器外的其他用户访问这台服务器时,防火墙软件对数据包设置了多个关卡,根据数据处理的逻辑,在逻辑顺序的某些关键节点,防火墙对数据进行监控,根据规则对数据包进行放行、丢弃、拒绝、修改。而这些关卡就是被人称为“链”的逻辑位置。

用关卡来形容这个过程,是让读者可以理解,防火墙对数据包的放行、丢弃、拒绝、修改仅在这些逻辑位置发生,不是全程跟踪数据包的。我们可以简单的理一理这些关卡有哪些:①数据包达到服务器;②数据包离开服务器。这是我们最容易get到的情况,还有三种情况:③数据包在服务器内进行传播,比如先经过一个程序处理,得到的结果丢给第二个程序继续处理,也有可能是,数据本身是从服务器发出去的,在另外一台机器上处理完以后,又把数据包发回来,或者说就是路由的情况;④服务器作为中转站,数据包到达服务器之后并不实际进入服务器内部,而是问一问路,就去了想去的方向,这种情况出现在用服务器作为代理或路由的时候;⑤也就是问完路离开的那一瞬间,但是问完路也有可能发现要进到服务器里面去,但是第5种情况是不进入服务器,问完路去其他地方。

这5种情况对应iptables的5个链,因此,理解到了这一点,就比较容易理解后续在添加规则的时候,为什么要如此去添加了。

4表5链

4表(nat、filter、mangle、raw)是指在iptables中,所有的规则是被存放在4张表中的,在数据包到达一个关卡的时候,iptables先从nat表中找出与本关卡相关的规则记录,然后按照规则在表中的先后顺序执行,执行完nat表之后,又去filter表执行同样的动作。不过在1.2.9以后版本的iptables中新增的raw表具有更高的优先级。具体的表相关内容,可以阅读这篇参考文献的4表部分。

5链对应上面提到的5种关卡位置,5链包括:INPUT(对应①)、OUTPUT(对应②)、FORWORD(对应③)、PREROUTING(对应④)、POSTROUTING(对应⑤)。

规则顺序

虽然5链把所有情况都囊括了,但是有一个问题是,难道一个数据包必须要严格的经过所有的关卡吗?显然不是,数据包可能仅经过很少的关卡就结束了防火墙的监控。具体的监控逻辑顺序如下图:

iptables-routing

我们来举一个实际环境中遇到过的例子:在php程序中,我们使用区域网内的另外一台服务器作为mysql的服务器运行,在mysql服务器上用iptables进行防火墙控制。那么当php程序连接mysql的时候,iptables是怎么来防护mysql服务器的呢?

首先,无论是公网还是局域网,对于服务器本身而言都是外部访问,都会经过iptables的关卡。当php第一次去请求mysql连接的时候,首先在prerouting处被检测到,但是由于我们并没有在raw、mangle、nat三张表中找到prerouting相关的记录,因此数据进入了INPUT挂载点,这时,在filter中找到了input相关的记录,于是按照记录的顺序逐条进行判断。在这些input记录中,前面几条都与这个数据包不匹配,直到第n条,这条记录作出如下限制:

-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 3306 -j ACCEPT

意思是:如果数据包的来源为网段127.0.0.1/32这个网段(与服务器是同一网段,也就是局域网内),那么3306端口对该数据包进行放行。一旦在表中有一条记录的条件跟数据包条件匹配,那么就不再往下去匹配规则了。所以,该数据包顺利通过了INPUT挂载点filter表中的规则验证,在进入OUTPUT挂载点,都没有相关的规则,所以该数据包在得到mysql的处理结果之后,顺利出站,返回给php程序。

在上面的案例中,假如php所在的服务器不在127.0.0.1/32这个网段,那就不能匹配上面那条规则,如此就会继续往下匹配规则,直到匹配到某一条规则为止。如果所有规则都不匹配,那么OK,数据包通过了所有关卡。

规则动作的后续选择

上面的案例中说,一旦匹配了,就不再往下去匹配同表中的同链规则了。大部分情况下,一旦匹配某条规则,就不再继续匹配同表同链规则了,但是也有例外,那就是一个叫做LOG的动作。因此,必须把规则动作拿出来讲一下。

iptables有这么几种规则动作:ACCEPT、DROP、REJECT、SNAT、MASQUERADE、DNAT、REDIRECT、LOG。在参考文献中,每个动作都做了解释,也就不再赘述它们具体都是干嘛的。但是,除了LOG以外,其他动作确实是执行完以后,就不再继续匹配其他同链的规则动作了,如果一个数据包被ACCEPT了,那么不管后面有没有规则对它进行DROP或REJECT,该数据包在该链内将一直被放行。因此,规则的顺序极其关键。LOG动作就是在某个数据包在通过某个关卡时,正好发现它匹配某些规则,所以把它记录下来,记录下来以后当然不应该阻止它继续往下流动,因此LOG动作除了产生日志文件之外,不会对数据放行、丢弃、拒绝、修改。

但同链的规则中断,并不代表该数据包可以顺利通过下一个链的规则匹配。比如在INPUT链顺利通过后,在OUTPUT链中发现数据中带有敏感信息,比如数据要流向你竞争对手的服务器去,那也可以将它拦截下来。

因此,规则动作的后续选择需要通过你在向链增加规则时的顺序而表现出来,如果不思考规则顺序,那么你很有可能得不到想要的结果。

关于这种选择的情况,可以阅读这篇参考文献,了解更多。

如何把握规则顺序

前面说规则顺序至关重要,那么到底规则顺序应该怎么把握呢?一般提供了两个策略:通、堵。所谓通,就是把所有的关卡先关起来,然后只对部分数据包进行放行。堵则是所有关卡先打开,来一个数据包,对它进行检查,发现风险,就堵在外面。

在这两种思路下面,我们怎么来部署规则顺序呢?

“通”的策略风险比较大,因为先把所有出入口封起来之后,对数据包进行匹配的,都是你所能想到的,然而往往会有一些没有想到的情况。一旦一些原本是正常的数据包,不在你的“通”的规则里面,那么就被挡在了外面。当然,从安全性上讲,这无疑是最安全的,因为只有你信任的会被放进来,那些不管是你不信任的,还是不知道的,都被挡着了,不管怎么样,不会有风险进来。而“堵”的策略则风险比较大,因为你堵的,全是有风险的数据包,可是一个人或一群人,都不可能把所有有风险的情况想到,必然有一些有风险的数据包有可能被遗漏进入服务器。

这两种思路“通”的策略更适合初学者,毕竟对于初学者而言,更容易想到你允许的情况,如果今后发现了更多允许的情况,可以往表里增加。

那么“通”的策略怎么来构建呢?原理很简单,就是在规则列表中,先把匹配规则最宽的信任数据包规则加进来,然后再把那些个例的规则加进来,最后用DROP或REJECT把大门一关就可以了。

仍然以上面的php和mysql的交互来举例,规则列表中,首先得允许数据库返回以后,php再来交互时,不用再那么麻烦进行匹配验证,所以再最前面写上:

-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

这就是我说的“最宽的信任”,RELATED,ESTABLISHED这两种状态的数据包,无论是mysql产生的,还是其他什么程序产生的,都被允许。因此这条规则把所有经过验证过的数据都允许了。

接下来是允许mysql这个特例:

-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 3306 -j ACCEPT

为什么说是特例呢?因为在上面一条规则的基础上,当php和mysql第一次连接的时候,并不满足,所以如果直接把大门一关,mysql连不上了。所以,增加这条规则之后,php和mysql第一次可以顺利连接上了。

最后,把大门关上安心睡觉:

-A INPUT -j DROP

上面这三条规则大致说明了“通”这个策略的基本思路。

/etc/systconfig/iptables文件详解

大部分资料中都直接让读者去操作iptables命令,比如直接在Linux命令行输入iptables -A INPUT -j DROP,然后使用service iptables save把新加入的规则保存,最后再service iptables restart使规则生效。

但是这种操作让我们无法清晰的了解规则中都有哪些规则,它们的顺序是什么。而实际上save那个命令的结果是把刚才新增的规则写入/etc/sysconfig/iptables这个文件。这个文件实际上是iptables启动的时候默认去读取的文件,restart操作也是为了去读它。所以我们为什么不直接编辑这个文件呢?

我们用vi打开这个文件,然后看看它的结构吧:

# Generated by iptables-save v1.4.7 on Mon Dec 21 18:37:09 2015
*nat
:PREROUTING ACCEPT [16:1193]
:POSTROUTING ACCEPT [1:60]
:OUTPUT ACCEPT [1:60]
-A POSTROUTING -s 192.168.0.0/25 -o eth0 -j MASQUERADE
COMMIT
# Completed on Mon Dec 21 18:37:09 2015
# Generated by iptables-save v1.4.7 on Sun Dec 20 02:16:49 2015
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [1:140]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 3306 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 3306 -j DROP
COMMIT
# Completed on Sun Dec 20 02:16:49 2015

这是我的一个案例,简单看下它的结构。#开头的行是注释,*nat和*filter表示对哪一个表插入规则。COMMIT表示插入的规则OK,可以开搞。

通过这个文件,可以非常清楚的看到nat、filter两个表中的链。:PREROUTING ACCEPT [16:1193],这样的行其实我们不用去理会,中括号中的数值对于我们无需数据监控而言,也没有什么意义,它们是数据块的数量和每块的大小,不表示任何匹配规则。

在最后一条:和COMMIT之间的就是我们要去发挥的规则列表。我们直接看下面filter表中的规则。

-A INPUT -i lo -j ACCEPT

lo是一种内部接口,比如我们用localhost访问本机,其实localhost是虚拟出来的,只有本机可以访问,实现这个机制,就是通过lo接口来实现的,因此所有这种通过lo接口实现的机制,都用ACCEPT动作。

这个文件中没有使用到“通”的策略,而是仅仅堵住了3306端口,也就是说这台服务器不对外提供mysql服务,这样的话,只有本机或者127.0.0.1/32网段的机器可以使用mysql服务器。而实际上127网段还是本机,只不过我们可以通过类似127.0.0.1来连接mysql,而不单单是localhost。

好了,本文并没有对规则中的各个参数进行讲解,比如-A -i -p等参数,都没有提到,这些参数都可以在其他文章中了解。本文主要理清了一些iptables规则配置的思路,希望对你有用。

2015-12-21