【Linux】防火墙与CentOS中的iptables

【iptables】

  参考好文:http://www.zsythink.net/archives/1199。这个博客的作者写了深入浅出的iptables介绍,基本上我就是做个他的读书笔记。

  ■  基本介绍

  说到linux上的iptables,下意识会想到防火墙。其实linux在内核空间中有一个网络通讯包控制组件名为netfilter。而iptables是位于用户空间中,让用户可以配置netfilter的一个工具而已。两者加在一起,能够算是一个完整的linux系统的防火墙。为了描述方便,下面就简单的以iptables这个名称泛泛指代防火墙。

  从感性的认识上来说,防火墙的作用,就是在访问进入服务器内部之前做一个安全检查,对于达标的才放行。所以一个防火墙的示意图可能是这样的:

  由于防火墙的规则必须严肃对待,其开放修改的自由度肯定不能太大,且运行不能被用户所直接影响,所以具体做防火墙工作的模块都被放在了内核态运行。这里input和output是最简单的两个“模块”,分别用于控制进出本服务器的包。

  实际上,除了进出本机的数据,有些数据包会把本机当做路径节点中的一员,其目的地在其他主机。所以防火墙的实际构造比上面的还要复杂一点如下图所示。PREROUTING,FORWARD和PORTROUTING分别代表了当本机作为一个路由节点时,路由前、转发、路由后的控制:

  这里可以看出,由外界发往本机的数据包是经由了PREROUTING和INPUT,由本机发往外界的数据包经由了OUTPUT和POSTROUTING,然后本机作为路由节点的数据包经由PREROUTING,FORWARD,和POSTROUTING发出。

 

  ●  链、表、规则

  上面有时候用了“模块”一次形容上面各个控制单元。更专业的说,上面每个单元都应该被称为“链”。

  链的内部由一条条规则组成。每条规则的要素有匹配条件和动作。基本上来说,数据包到达某个链时会从上至下一条条核对规则的匹配条件,当匹配时就停止核对并且根据要求做相关动作。一条条规则就像链条上一个个环,互相串联在一起,这也是链的名称的由来。正如上图所示,最常见的五个链就是PREROUTING,INPUT,FORWARD,OUTPUT和POSTROUTING。对于一些特殊的场景比如有容器运行着的docker服务器可能还会有DOCKER,DOCKER_ISOLATE之类的链。

  另一方面,规则之间存在许多共性。对类似的规则可以组成一个名为表的集合。iptables中默认有四个表分别是 raw,mangle,nat,filter。每个链中规则的匹配优先度就是按照这个顺序来的。不同的链可以包含四个表中的一到若干个表。

  链和表就像X和Y坐标,确认一个链和一个表之后,就可以将这个链中,功能性上来说属于这个表的那部分规则锁定了。通过iptables来改变防火墙策略,最小操作单位是一条条的规则。而对规则的增删查改,都要先通过链和表来特定一部分规则。所有这个思想在iptables中是比较重要的。

  再来详细说说规则的事情。上面说了规则的两个要素是匹配条件和动作。匹配条件具体且简单来说就是源IP,源端口以及目的IP和目的端口。而动作就很多了,常见的有ACCEPT,DROP,REJECT,DNAT,SNAT,RIDRECT等。两者结合起来,在配合特定的链上,比如我可以在INPUT链中配置源IP和目的端口,并且配置动作为REJECT,这就表明任何来源于源IP的,想要访问本机目的端口的数据包都会在进入应用之前被拒绝掉。

 

■  规则的增删查改

  在linux后台操作iptables基本就是用到了iptables这个命令,然后其中会有很多乱七八糟的参数。最基本的参数是-t(必须,如果不写默认是-t filter)来指定一个表以及-L来制定一个链。下面来分操作看一下

  ●  查询

  iptables -L是最简单的查询命令。它将iptables的filter表中所有规则按照链不同展示出来。如果在-L后面加上某个链名自然就是展示一个链的内容。在-L之前加上v如-vL的话可以看到更加详细的信息。

  查询时加上--line-numbers可以在给出的结果前面加上一个序号。这个序号在后续的删除操作中很有用。

  在查询结果中会有如Chain INPUT (policy ACCEPT 0 pakcets 0 bytes)之类的表示。policy表示这个链的默认策略。当我们将某个链中所有规则都删除或者失效,那么经过这个链的包将会按照policy给出的默认策略来执行操作。后面的0packs和0bytes分别表示,到目前为止所有使用了默认策略匹配的包总数和这些包的总大小。

  -L的全称是--list,看到的是规则的一个列表,比较接近自然语言。和-L参数相对的还有一个-S参数,全称--list-rules,通过这个参数查看到的规则是以命令形式体现的。这个-S参数有利于我们通过现有规则复制一份类似的规则上去。比如iptables -t filter -S INPUT

  ●  增加

  之前我们说了,规则通过表和链的双重定位之后被限制在了相对小的一个范围内。然而即使在这个范围内,规则也还是有先后之分的,增加规则就必然会遇到,新增的规则应该放在何种优先级程度的问题。这个主要是通过不同的增加参数来实现的。

  下面以要对INPUT链的filter表(也是最常变动的部分,控制访问权限)做变更为例。首先查看一下当前的规则:

[root@localhost ~]# iptables -t filter -L INPUT --line-number
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination
1               all  --  anywhere             anywhere
2               all  --  anywhere             anywhere    

 

  可以看到,目前没有对访问做任何控制。接下来运行命令 iptables -t filter -I INPUT -s 192.168.178.141 -j DROP。不难看出,-s指出的是source ip,-j指出的是相关动作。

  然后查看iptables规则的变化:

[root@localhost ~]# iptables -t filter -vnL INPUT
Chain INPUT (policy ACCEPT 658 packets, 55639 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DROP       all  --  *      *       192.168.178.141      0.0.0.0/0
50618 4446K            all  --  *      *       0.0.0.0/0            0.0.0.0/0
34315 3012K            all  --  *      *       0.0.0.0/0            0.0.0.0/0

 

  -n参数是指将ip地址等数字化。比如anywhere改成0.0.0.0/0这样子。新增命令中的-I 意思是insert,所以新增的规则是放在第一条的,类似的有-A代表append等。如果想要插入到特定的某个位置,那么可以通过-I INPUT <num>这样的形式。num表示在当前第num条规则之前插入这条规则。

  有了这条规则之后,从192.168.178.141再对本机发起任何访问,包括ICMP协议的ping,数据包都会被丢弃。所以ping是收不到回应的。

  下面再验证一下,我们-A一条ACCEPT规则到这里,因为是append,所以规则放在第四条,此时从那台主机上发起ping,仍然是无回应的。表明iptables确实是从上到下依次匹配规则的。

  ●  删除

  和新增类似,删除是通过类似这样的办法操作的:

  iptables -t filter -D INPUT 3

  这个命令就是删除了当前INPUT链filter表中的第三条规则。由于规则删除之后其余规则的序号也会有所变化,所以要密切关注这一点。

  一条一条删除未免有些太累,那么我们也可以通过iptables -t filter -D INPUT -s 192.168.178.141 -j ACCEPT这样的方式来做。

  这个命令就是删除了INPUT链filter表中所有指定sourceip,并且做ACCEPT动作的规则。

  更进一步的,iptables -f 表名 -F 链名可以一次性将链中的所有规则清空。

  需要强调的是,在未保存iptables规则之前,谨慎删除。什么是保存规则后面再说

   ●  修改

  iptables中规则的修改,是类似于RESTFUL思想中PUT类型而不是UPDATE类型的修改。即修改的话必须全量修改,而不能只修改规则中要素的一部分。

  具体的修改规则是类似于 iptables -t filter -R INPUT 1 -s 192.168.178.141 -j REJECT之类的。再次强调,虽然在INPUT之后指出了修改的对象是第几条规则,但是依然要加上-s之类的参数,如果不加则修改后原地址会变成anywhere(0.0.0.0/0)。

  上面这个命令是针对单个的一条规则进行修改。此外我们还可以针对整个链进行一个默认策略的修改。比如INPUT链默认策略是ACCEPT,这样:

  iptables -t filter -P INPUT REJECT

  这样就把INPUT这个链的默认策略换成了REJECT。

  ●  保存规则

  上面说完了规则的增删查改。当我们通过iptables命令对规则作出改变时,这是实时生效的。所以有了上面我们一改变规则从特定IP访问过来的包就无法接受的情况。但是这种变化也是暂时的,当我们重启iptables服务或者重启了整个服务器,所做的这些暂时的改动就会消失。

  为了做到能够永久地使iptables发生改动,可以选择在做完规则的变动之后保存规则。保存规则的操作在Centos6和7中不太一样。在6 中,我们可以通过命令service iptables save来永久化当前的iptables规则。执行此命令之后,规则会被保存到/etc/sysconfig/iptables-config文件中。

   针对centos7系统,由于系统默认是不带service这一套启停系统的,所以不能直接通过service命令来保存。不过通过yum,我们可以安装iptables-service组件,有了它之后,先通过systemctl关掉firewalld(centos7的默认防火墙组件),然后再通过systemctl启动iptables即可。

 

■  规则的条件

  上面已经简略地说明了规则的各个匹配条件的设置方法。下面详细来说一下。

  ●  -s 参数

  我们已经知道-s是设置sourceip的。多个ip地址直接用逗号相连可以一次性设置多个IP如 -s 192.168.1.1,192.168.1.2。同时-s后面还可以直接连上一个网段如-s 192.168.0.0/24,在-s参数前面加上!如

  ! -s 192.168.178.141 -j ACCEPT这个规则就是说明接受一切来自非指定IP为源IP的包。但是需要注意,这并不是说来自于这个源IP的包就会被拒绝了。要设置这样的拒绝规则,还是要手动添加一条拒绝的规则。

  ●  -d 参数

  除了-s是设置sourceip的,还有-d参数可以设置destinationip。对于发送到本机的数据包,这个参数的意义就在于当本机有多个IP的时候进行有选择的IP指向。比如设置某个源IP只能发送数据包给一个指定的IP而不是全部。

  ●  -p 参数

  -p参数不是用来设置端口,而是用来指定协议的。比如设置了-s 192.168.178.141 -p tcp -j REJECT之后,如果没有其他规则干扰并且默认规则是ACCEPT的话,此时从141这个机器上ping本机,你可以发现还是可以pinng通的,但是telnet任何一个TCP端口都无法连接。因为ICMP没有拒绝而TCP被拒接了。

  ●  -i/-o 参数

  -i是设置网卡的。当本机有多个网卡的时候,通过-i来指定本条规则适配于从某张特定网卡流入的数据包。由于只是判断流入的,所以这个规则只能存在于PREROUTING,INPUT和FORWARD三个链中,其余的两个链不能设置-i参数。参数的具体值是网卡的具体名称。网卡名称可以通过ifconfig命令查看。

  -o是-i的反面,适配从某张网卡流出的数据包。可以设置-o参数的链除了上面提到不能设置-i的OUTPUT和POSTROUTING之外还有FORWARD也可以。

  ●  -m 参数

  以上的这些参数可以认为是基础条件参数,此外还可以添加一些扩展参数。比如指定源端口和目的端口就是一种扩展参数。为了适应拓展参数,需要我们手动地把某个拓展模块声明出来,此时就要用到-m参数。默认情况下不声明-m时,其默认值是-m tcp。这个tcp不是协议名,而是同名的拓展模块名。tcp这个拓展模块可以支持诸如--dport等等常用参数。

  除了tcp以外还有其他很多拓展模块,下面提到再说。

  ●  --dport/sport 参数

  一言既明,--dport是destination port,指出了本条规则指向的是本机一个特定端口。与之相对的还有--sport意思是source port。不过由于一般发起访问的一方都不会有特定的端口限制,都是从大号端口中随机选择一个发出的。所以--sport并不常用。另外正如上面所说,使用这两个参数时一定要指出tcp作为拓展模块,但是可以不写出-m tcp因为是默认值。

  另外这两个参数的值还可以是类似于22:25(指向22到25一共四个端口),:22(0到22所有端口),22:(22到65535所有端口)这样子。

  ●  --dports/sports 参数

  * 这两个参数需要声明 -m multiport

  这两个参数和上面的很像,只不过在普通的--dport中,端口只能是连续的一段,包含了一到若干个端口而不能是零散的几个。而这个参数可以实现零散的端口指定,端口之间用逗号隔开即可。此外还支持诸如22,8080:8090这样的指定方式。

 

■  常用的拓展模块

  上面有提到,-m参数指出特定的拓展模块之后,我们就可以在参数中输入一些更友好更方便的值来达到我们的目的。下面就介绍几个常用的拓展模块。

  ●  iprange

  -m iprange之后我们可以更加方便地指定一个ip范围。在默认的-s或者-d参数后面说过可以使用逗号连接多个IP,或者直接写网段。那么如果想要直接指定一段有限的范围的话就可以用这个模块。比如:

  -m iprange --src-range 192.168.1.1-192.168.1.5这样子。对因为-s参数实际上是归属于-m的默认值tcp拓展模块的,当使用iprange的时候,参数要使用--src-range了。

  ●  string

  string模块可以匹配报文中指定的字符串,如果出现指定字符串则表示匹配。

  在使用string模块时,通常需要指出--algo参数和--string参数。--string参数自然就是用来指出字符串的,而--algo指定的是匹配算法,可以选择的是bm或者kmp。总的来说就是这样用:

  -m string --algo bm --string "Hello"   保险起见,--string后的字符串可以用引号引起来

  ● time

  time模块顾名思义就是在访问时间上做控制的。可用的参数也很丰富。

  --timestart --timestop可以指定一个格式是HH:MM:SS的时间,匹配这个时间段内发过来的包。--datestart 和 --datestop类似。--weekdays可以指定星期几(取值范围1-7),并可取反。--monthdays可以指定一个月的第几日,也可取反。

  ●  connlimit和limit

  这些模块用于控制连接数之类的指标,那篇文章里讲得详细就不多说了。

 

■  tcp-flags和TCP三次握手

  之前说过,-m的默认参数值是tcp,即使用tcp模块。之前用到过的--dport,--sport等参数都是tcp扩展模块的一部分。除了这些参数,tcp模块还有一个比较重要的模块即tcp-flags可以用。然而在具体说明tcp-flags参数的使用方法之前,还有必要了解一下TCP协议三次握手的具体流程。

  ●  TCP三次握手

  在建立TCP连接之前,客户端和服务端要进行三次数据段(传输层)的传输。仅当这三次交互都顺利完成,两机间的TCP连接才算正式确立。下面首先来简单说明一下三次握手分别干了什么:

  首先需要明确,一个TCP协议的数据段,其由正文和头内容两部分组成。以下提到的所有TCP协议框架内本身的控制信息都是写在头信息中而和正文没有直接关系。对于一个TCP数据段的头信息,它包含了很多信息,其中下面我们主要要说的就是以下这部分:

  seq序号,占32位,用于数据发起方对本数据进行标识使用。

  ack序号,占32位,用于数据接收方表示已经收到数据,并且表示做好了接收seq序号为指定值的数据段的准备。

  这两个序号占据的空间主要用来做存储,而什么时候真的要用到这些存储的信息需要看TCP头信息中的另外一部分,控制位。控制位包括六个一字节的flag,分别是:

  URG(紧急指针flag),ACK(确认序号flag),PSH(加急flag),RST(重置连接flag),SYN(发起连接flag),FIN(释放连接flag)

  好了,知道这些存储的意义之后下面说正式地连接建立过程。

  1. 首先客户端将创建一个数据段,并将随机的一个数字作为此数据段的seq序列号(以x代指),同时置SYN标志为1,ACK标志为0,表示此数据段是一个请求建立连接的数据段。发送出之后,客户端进入SYN_SENT状态。

  2. 服务端接受到此数据段后,首先如果同意建立请求,也会创建一个数据段,并将SYN置为1,ACK置为1,然后将x+1作为ack序号(注意ack序号和ACK标志位的区别),再将随机的一个数字(以y代指)作为自己的seq序号,随后发出此数据段。发出后服务端进入了SYN_RCVD状态。

  3. 客户端收到了服务端回送的数据段后,创建一个新数据段,置ACK为1,将seq序号置为x+1,ack序号置为y+1,发出,发出之后客户端进入ESTABLISHED状态。服务端接受到这个数据段后进入ESTABLISHED状态,至此两机之间的TCP连接就建立了。

  其实还有一个隐形设定没有说,就是说TCP协议的交互中,从一方发出的数据段的序列号是逐渐自增的。比如从自建立连接的第一个数据段开始,每发送一个新的数据段给服务端,其seq序号都会加一。所以可以解释,在三次握手中的第二步,服务端发出的ack=x+1的数据段就说明了服务端已经准备好了接收seq=x+1的数据段了,所以第三步中客户端发出的seq=x+1,而那个包中的ack=y+1就是期待服务端回应的段的seq是y+1的表现。就此,双方就开始了正式的互动了。从逻辑上来说,TCP连接是由两个独立的会话组成的。但是由于seq和ack序列号都被写在TCP的头信息中,所以看起来有点像一个连接。

  还有一个疑问,为什么一定要三次握手?如果只是建立连接,似乎两次握手就够了。网上常给出的原因大致描述是这样的:

  如果客户端发出的一个请求包因为某种原因在传输途中滞留,客户端随机发出第二个包和服务端建立连接。完成工作后释放了连接。然后最开始发出的第一个包才到达服务端。此时服务端会误以为客户端又要建立一个连接,所以就会发回一个响应包。然而客户端此时并不需要连接,且是二次握手,发出的这个包得不到确认,所以服务端将会一直占用这个连接而不做任何工作,浪费了服务资源。

  我看到的更全体的一个解释是,第一次握手时,S确认可以接受C的报文;第二次握手时,C可以确认S能接受到自己的报文且自己可以接受S的报文;第三次握手时,S可以确认C可以接受自己的报文。总的来说,握手过程中双方都要保证自己能接受对方的报文且对方能接受自己的报文(两个会话的四条要求)。因此,总共需要握手三次。

  * 四次挥手

  顺便简单提一下TCP连接断开时的流程,四次挥手。基本流程图如下:

  参照三次握手的图可以比较好懂。对于几点提一下。首先服务端收到FIN=1的报文之后,发出ACK=1的回应报文,并且会随后通知应用层程序断开连接。当应用层程序给出许可之后,再发送FIN=1,ACK=1的报文(第三次挥手)出去。在客户端接受到这个报文之后,会发出ACK=1的应答报文,但此后并不是直接关闭连接,而是等待2MSL。MSL是一个TCP数据段的最大生存时间。之所以要等这么一段时间是为了防止刚才发出的应答报文中途丢失,若丢失,服务端还可以重传一次FIN=1,ACK=1的报文,客户端还可以尝试重新回复一次。这样做避免了因为客户端无应答而服务端无法进入CLOSED状态,从而浪费了服务资源。

  

  好了,绕了一大圈,回到--tcp-flags这个iptables的参数。

  iptables的--tcp-flags指定的就是各个标志位。比如--tcp-flags SYN,ACK,FIN等,表名此条iptables规则匹配特定TCP控制位的数据包。

  更方便的还有--syn参数,这个参数默认是匹配了SYN,RST,ACK,FIN SYN这四个在建立TCP连接时要用到的参数。

 

■  ICMP和UDP的扩展(不写了 参考 http://www.zsythink.net/archives/1588)

 

■  自定义链

  上面说的五个iptables的基本链是其自带的。如果想要创建自定义的链也可以,只不过自定义链并不能直接插入iptables的底层控制,而是通过被其他链调用的形式来引用。创建链通过-N参数进行。如

  iptables -t filter -N IN_WEB

  此时我们就有了一条名为IN_WEB的链,而这条链内并没有规则。首先添加一些规则吧。然后可以通过这种方法使其生效:

  iptables -t filter -I INPUT --dport 80 -j IN_WEB

  把自定义链通过被调用 的形式放到另一条链中。

  iptables -E IN_WEB WEB可以将链名的IN_WEB改成WEB

  iptables -X IN_WEB可以删除一条自定义链。和删除目录类似的,删除时一定要保证链内已经没有规则,否则会删除失败。清除所有链上的规则就是iptables -F,很简单。

  

■  网络防火墙

  上面所说的所有iptables的作用,其角色都没有跳出“主机防火墙”的框架。也就是说,说到底iptables目前为止还只是控制了一台主机上各个网卡的进出限制。

  如果想要iptables来控制一整个网络的进出限制,很自然的想法就是在网络的网关主机上设置iptables即可。和主机防火墙不太一样的是,网络防火墙往往是在FORWARD链上做文章。(很好理解,FORWARD管的是数据的转发,而不是INPUT或OUTPUT需要进入到主机层面)。

  参考文中给出了一个完整的网络防火墙的测试案例(http://www.zsythink.net/archives/1663),针对几点想特别提一下。

  首先linux默认的转发策略是拒绝,这个策略和iptables无关,而是和主机本身的设置相关。可以使用命令sysctl或者修改配置文件/sys/sysctl.conf来改变策略为接受转发。

  其次,利用iptables做防火墙最重要的一点是要有“双向”意识。双向的意思就是说,我再网络防火墙上开放了比如iptables -t filter -A FORWARD -s 10.1.0.0/16 --dport 80 -j ACCEPT,当然本机是10.1.0.0/16网络对外联络的网关。此时内网主机访问外网某个主机的80端口时可能仍然得不到回应。因为防火墙只放行了“出网”的数据,没有放行对面IP应答的数据。所以还要加上如 iptables -t filter -A FORWARD -d 10.1.0.0/16 --sport 80 -j ACCEPT,这样双向放行才能使内网主机顺利访问到外部服务。

 

■  动作

  之前我们说过了iptables中的链,表,以及最小单位的规则。每条规则又可以使用各种模块的各种参数。唯独规则的动作还停留在ACCEPT REJECT DROP这三种。其实动作也还是有很多种可以选择的。和模块类似的,动作也分成了基础动作和扩展动作,在使用方式上不同的是,对于扩展动作不用特别指出什么参数,而是直接写就行了。实际上在最常用的三个动作中,ACCEPT和DROP是基础动作,连REJECT都属于扩展动作的。

  ●  REJECT

  当设置动作为REJECT时,同时还可以指定参数--reject-with。这个参数可以选的值是固定的常量如 icmp-host-unreachable,icmp-port-unreachable等。这个参数的主要作用是当icmp请求发来而被本iptables以REJECT动作拒绝掉,返回的信息。默认返回值是host not reachable、

  ● LOG

  LOG动作会将匹配到此处的包记录到日志中,然后继续向下匹配。换言之,LOG动作并不会对iptables的功能做出任何影响。

  默认的日志是/var/log/messages。有两点想提,第一,由于对于所有获得到匹配的数据包都会将日志记录下来,所以在设置LOG为动作的匹配规则时应该尽量指明详细的条件,以防止日志文件的暴涨。第二,如果想指定另外文件作为日志文件,可以修改/etc/rsyslog.conf(或者/etc/syslog.conf)配置文件中的kern.warning配置项,修改完成后重启rsyslogd(或syslogd服务)即可。

  ●  SNAT和DNAT

  NAT,即网络地址转换协议。简单来说其作用就是以一定规则替换报文中的IP和端口从而实现隐藏一部分子网IP之类的目的。NAT被细分成了SNAT(Source NAT)和DNAT(Dest NAT)。粗看一下两者很好理解,比如AB两机(架设地址是192.168.0.1和192.168.0.2)组成子网,共用一个C机(假设IP是10.1.0.1)作为外界访问的桥梁,并且在C机上部署了NAT服务。

  此时如果从A、B两机想要通过C机访问外网,当数据包来到C机,C机会将其头信息中的源IP和源端口转换成自己的IP和自己的某一个端口。并且记录下这种IP/端口之间的对应关系。转而发出这个数据包。当远端传来回复,C会把回复指向的目的IP和端口根据自己记录的规则转换成子网(AB机网络)中的目的端口和IP,并且发给子网。这一趟A、B机通过C机访问外网的流程就走完了。它分成两部分,前半部分是转换了源IP、地址所以是SNAT,后半部分转换了目的IP、端口所以是DNAT。但是由于所有的网络请求都具有请求和返回两个过程且两个过程一定相反,所以惯例上,我们会把某个流程的前半部分作为整个过程的类型加以指出。比如上述的场景就是一个SNAT操作。

  相反的,如果A、B是两个服务器,通过C机对外提供服务。那么外界发来访问时先DNAT,然后给出回复时是SNAT。整体操作就称为DNAT操作。

  总之,处于NAT笼罩的子网中的是服务的提供方,那么这个NAT就是个DNAT,反之就是SNAT。

 

  一个配置SNAT动作的例子:

  iptables -t nat -A POSTROUTING -s 192.168.0.0/16 -j SNAT --to-source 192.168.0.1

  首先所有的nat操作理应配置在nat表中,所以通过-t参数指出nat表。在centos6中,nat表只能存在于POSTROUTING中,centos7中还可以存在于INPUT中。放在这两个链中的考虑是因为,这两个链分别是发送到别的机器/本机的最后一个关卡,在这里修改能把对报文的影响降到最底。-s指出的是一个网段,具体到SNAT操作,指的就是“所有来源于这个网段的数据包都要被SNAT修改一下”。而修改成什么就由--to-source参数决定了。

 

  一个配置DNAT动作的例子:

  iptables -t nat -A PREROUTING -d 192.168.0.1 -p tcp --dport 80 -j DNAT --to-destination 192.168.0.99:8080

  类似的就不多说了。主要是DNAT场景中,因为被访问的端口往往是固定的,所以通常会很明确地指出端口的映射关系。--dport指定的就是访问的指定端口,而-d指定的就是访问的特定IP。--to-destination参数指出的,自然就是访问指定ip指定端口的数据包被转发到的套接字了。实际上可以观察一下有了端口映射的容器的Docker服务器。iptables -t nat --list-rules DOCKER,就可以看到docker容器的端口映射就是通过iptables的配置来实现的。

  ●  MASQUERADE

  在SNAT操作中,考虑这样一种情形:作为统一出口的IP是动态的,经常会变动。如果通过SNAT写死一个IP,显然每次IP变动时就要重新修改iptables。而MASQUERADE就解决了这个问题。相比于SNAT时指定的--to-source,MASQUERADE动作的参数是-o,其值是一张网卡的名称。而所有匹配规则的数据包在发出前的源地址都会被改成这个网卡当前的地址。

  如 iptables -t nat -A POSTROUTING -s 192.168.0.0/16 -j MASQUERADE -o enp0s8

  ●  REDIRECT

  顾名思义,重定向,将发往本机某端口的数据转发到另外一个端口。相当于一个简化版的DNAT。举例:

  iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080

 

 

■  iptables的配置技巧

  上面讲了很多iptables的配置操作,根据实践,可以得出一些常用的配置技巧

  首先需要明确iptables的匹配是“见好就收”的,即遍历规则一旦发现可匹配的立刻就放弃匹配接下来的所有规则。所以说泛泛而言,某个链的某个表中规则们应该遵循先精细后粗放的原则来配置。

  关于规则的顺序,另外一个需要注意的就是效率问题。虽然iptables匹配过程很快,但终究还是需要耗费时间的。所以,经常被匹配到的应该尽量放在规则序列的前面。比如一个服务器的8080端口每天可能要接受成千上万次的访问,而22端口每天可能就几十次的访问。那么此时--dport 8080的规则显然应该写在--dport 22端口规则的前面。

  另外作为iptables规则的逻辑,有白名单和黑名单两种模式。黑名单很好理解,就是配置默认规则为ACCEPT,然后对指定一些源发出的访问进行拒绝。白名单看似只要反过来配置就行了,但是一般的实践是仍然保持默认规则的ACCEPT,然后在所有规则的最后面加上一条DROP或者REJECT拒绝。之间的规则写入白名单的访问源。这么做的原因是因为如果一下子就配置默认策略为DROP或者REJECT的话,当清空表中规则后,管理员的请求也会被一并拒绝掉,不可取。

posted @ 2018-05-14 19:58  K.Takanashi  阅读(592)  评论(0编辑  收藏  举报