• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

Iptables数据包状态类型(二)

安全管理 彭东稳 8年前 (2015-11-25) 24942次浏览 已收录 0个评论

nf_conntrack连接跟踪模块

首先要说的是nf_conntrack模块是RHEL6中的引用名,在RHEL5中这个模块被命名为ip_conntrack模块。

数据包状态检测机制是iptables中特殊的一部分,其实它不应该叫状态检测机制,因为它只是一种连接跟踪机制,所以也可称为链接跟踪。连接跟踪可以让Netfilter知道某个特定连接的状态,运行连接跟踪的防火墙称作带有状态机制的防火墙,简称为状态防火墙。状态防火墙比非状态防火墙要安全,因为它允许我们编写更严密的规则。

在iptables里,包是和被跟踪连接的四种不同状态有关的。它们分别是NEW,ESTABLISHED,RELATED和INVALID。后面我们会深入地讨论每一个状态。使用iptables的state模块可以匹配操作这几种状态,我们能很容易地控制“谁或什么能发起新的会话”。为什么需要这种状态跟踪机制呢?比如你的80端口开启,而你的程序被植入反弹式木马,导致服务器主动从80端口向外部发起连接请求,这个时候你怎么控制呢。关掉80端口,那么你的网站也无法正常运行了。但有了连接跟踪你就可以设置只允许回复关于80端口的外部请求(ESATBLISHED状态),而无法发起向外部的请求(NEW状态)。所以有了连接跟踪就可以做到在这种层面上的限制,慢慢往下看就明白了各状态的意义。

所有在内核中由Netfilter的特定框架做的连接跟踪称作conntrack(connection tracking)。conntrack可以作为模块安装,也可以作为内核的一部分。大部分情况下,我们想要也需要更详细的连接跟踪,这是相比于缺省的conntrack而言。也因为此,conntrack中有许多用来处理TCP,UDP或ICMP协议的部件。这些模块从数据包中提取详细的、唯一的信息,因此能保持对每一个数据流的跟踪。这些信息也告知conntrack流当前的状态。例如,UDP流一般由他们的目的地址、源地址、目的端口和源端口唯一确定。

在以前的内核里,我们可以打开或关闭重组功能。然而连接跟踪被引入内核后,这个选项就被取消了。因为没有包的重组,连接跟踪就不能正常工作。现在重组已经整合入conntrack,并且在conntrack启动时自动启动。不要关闭重组功能,除非你要关闭连接跟踪。

除了本地产生的包由OUTPUT链处理外,所有连接跟踪都是在PREROUTING链里进行处理的,意思就是, iptables会在PREROUTING链里从新计算所有的状态。如果我们发送一个流的初始化包,状态就会在OUTPUT链里被设置为NEW,当我们收到回应的包时,状态就会在PREROUTING链里被设置为ESTABLISHED。如果第一个包不是本地产生的,那就会在PREROUTING链里被设置为NEW状态。综上,所有状态的改变和计算都是在nat表中的PREROUTING链和OUTPUT链里完成的。

连接跟踪(conntrack)记录

IP_conntrack模块根据IP地址可以实时追踪本机TCP/UDP/ICMP的连接详细信息并保存在内存中“/proc/net/nf_conntrack”文件中(Centos5中使用的是/proc/net/ip_conntrack)。查看这个文件的记录会有如下信息:

IP_conntrack模块维护的所有信息都包含在这个例子中了,通过它们就可以知道某个特定的连接处于什么状态。首先显示的是IP类型,然后是协议,这里是tcp,接着是十进制的6(tcp的协议类型代码是6)。之后的89是这条conntrack记录的生存时间(TTL),它会有规律地被消耗,直到收到这个连接的更多的包。那时,这个值就会被设为当时那个状态的缺省值。接下来的是这个连接在当前时间点的状态。上面的例子说明这个包处在状态 SYN_SENT,这个值是iptables显示的,以便我们好理解,而内部用的值稍有不同。SYN_SENT说明我们正在观察的这个连接只在一个方向发送了一TCP SYN包。再下面是源地址、目的地址、源端口和目的端口。最后,是希望接收的应答包的信息,他们的地址和端口和前面是相反的。其中有个特殊的词[UNREPLIED],说明这个连接还没有收到任何回应。

当一个连接在两个方向上都有传输时,conntrack记录就删除[UNREPLIED]标志,然后重置。在末尾有[ASSURED]的记录说明两个方向已没有流量。

这样的记录是确定的,在连接跟踪表满时,是不会被删除的,没有[ASSURED]的记录就要被删除。连接跟踪表能容纳多少记录是被一个变量控制的,默认值取决于你的内存大小,128MB可以包含8192条目录,256MB16376条,在拥有较大内存的机器中默认65536条。对于一个高并发的web服务器来说,如果你的请求数大过/proc/sys/net/netfilter/nf_conntrack_max文件中定义的数目,那么就会出现用户连接失败并且报错,报错信息如下:

而我们第一时间想到的办法就是关闭防火墙或是增大默认连接数,修改默认最大值,如下:

但不要盲目增大nf_conntrack_max的值-理解Linux内核内存分配

连接追踪模块属于内核的,所以我们知道所有的连接跟踪信息都是保存于内存中的,因此会考虑单纯放大这个nf_conntrack_max参数会占据多少内存,会权衡内存的占用,如果系统没有太大的内存,就不会将此值设置的太高。但是如果你的系统有很大的内存呢?比如有8G的内存,分个1G给连接跟踪也不算什么啊,这是合理的,然而在传统的32位架构Linux中是做不到,为什么?首先32位架构中最大寻址能力是4G,而Linux内存管理是虚拟内存的方式,4G内存分给内存的是1G,其他3G是理论上分给单个进程的,每个进程认为自己有3G内存可用,最后是根据每个进程实际使用的内存映射到物理内存中去。

内存越来越便宜的今天,Linux的内存映射方式确实有点过时了。然而事实就摆在那里,nf_conntrack处于内核空间,它所需的内存必须映射到内核空间,而传统的32位Linux内存映射方式只有1G属于内核,这1G的地址空间中,前896M是和物理内存一一线性映射的,后面的若干空洞之后,有若干vmalloc的空间,这些vmalloc空间和一一映射空间相比,很小很小,算上4G封顶下面的很小的映射空间,一共可以让内核使用的地址空间不超过1G。对于ip_conntrack来讲,由于其使用slab分配器,因此它还必须使用一一映射的地址空间,这就是说,它最多只能使用不到896M的内存!

为何Linux使用如此“落后”的内存映射机制这么多年还不改进?其实这种对内核空间内存十分苛刻的设计在64位架构下有了很大的改观,也可以放心根据内存调整最大连接追踪条目了。但问题依然存在,即使64位架构,内核也无法做到透明访问所有的物理内存,它同样需要把物理内存映射到内核地址空间后才能访问,对于一一映射,这种映射是事先确定的,对于大小有限(实际上很小)非一一映射空间,需要动态创建页表,页目录等。所以条目太多就会消耗性能且会产生内存碎片。另外如果不需要用到连接跟踪功能可以选择在Iptables中关闭,以此来提高系统的网络连接性能(因为开启会产生大量的IO操作)。卸载ip_conntrack模块如下,必须要先关闭防火墙才能卸载。另外注意当你试图查看iptables规则之后,就会激活ip_conntrack模块,无法真正卸载掉哦。

数据包在用户空间的状态

NEW状态

NEW状态表示新发出请求,且连接追踪文件中不存在此连接的相关信息条目,因此,将其识别为第一次发出的请求。意思就是,这是conntrack模块看到的某个连接的第一个包,它即将被匹配了。比如,我们看到一个SYN 包,是我们所留意的连接的第一个包,就要匹配它。第一个包也可能不是SYN包,但它仍会被认为是NEW状态。

ESTABLISHED状态

NEW状态之后,连接追踪文件中为其建立的条目失效之前期间内所进行的通信状态都为ESTABLISHED状态。表示双方已经建立连接了,conntrack会注意到两个方向上的数据传输,在连接追踪文件中的ESTABLISHED状态的条目失效之前,如果这个连接的双方再次发起连接,conntrack会直接匹配其为ESTABLISHED状态,直到条目失效,再次发起连接就会认为是NEW状态。

处于ESTABLISHED状态的连接是非常容易理解的。只要发送并接到应答,连接就是ESTABLISHED的了。一个连接要从NEW变为ESTABLISHED,只需要接到应答包即可,不管这个包是发往防火墙的,还是要由防火墙转发的。ICMP的错误和重定向等信息包也被看作是ESTABLISHED,只要它们是我们所发出的信息的应答。

RELATED状态

RELATED状态表示相关联的连接,什么意思呢?当一个连接和某个已处于ESTABLISHED状态的连接有关系时,就被认为是RELATED的了。换句话说,一个连接要想是RELATED的,首先要有一个ESTABLISHED的连接。这个ESTABLISHED连接再产生一个主连接之外的连接,这个新的连接就是RELATED的了,也就是相关联的连接,当然前提是conntrack模块要能理解RELATED

那这个状态有什么用呢?其实是专门为一些特殊协议设定的,比如FTP是个很好的例子,FTP有两个端口,一个是发送指令的端口21,一个是发送数据的端口20(主动模式),所以FTP协议就是RELATED的,指令连接跟数据连接之间是相关联的。(有兴趣看看FTP章节就明白为什么需要RELATED)还有其他的例子,比如,通过IRCDCC连接。有了这个状态,ICMP答、FTP传输、DCC等才能穿过防火墙正常工作。注意,大部分还有一些UDP协议都依赖这个机制。这些协议是很复杂的,它们把连接信息放在数据包里,并且要求这些信息能被正确理解。

INVALID状态

INVALID状态说明数据包不能被识别属于哪个连接或没有任何状态。有几个原因可以产生这种情况,比如,内存溢出,收到不知属于哪个连接的ICMP 错误信息。一般地,我们DROP这个状态的任何东西。

PS:这些状态可以一起使用(最常用就是NEWESTABLISHED组合使用),以便匹配数据包。这可以使我们的防火墙非常强壮和有效。以前,我们经常需要打开1024以上的所有端口来放行应答的数据。现在,有了状态机制,就不需再这样了。因为我们可以只开放那些有应答数据的端口,其他的都可以关闭。这样就安全多了。

状态在协议连接中的应用

当我们了解了IP_conntrack模块以及数据包状态后,下面说说这些数据包状态在在TCPUDPICMP这三种基本的协议里怎样操作它们。当然,也会讨论其他协议的情况。我们还是从TCP入手,因为它本身就是一个带状态的协议,并且具有很多关于iptables状态机制的详细信息。

1)TCP协议

一个TCP连接是经过三次握手协商连接信息才建立起来的。整个会话由一个SYN包开始,然后是一个 SYN/ACK包,最后是一个ACK包,此时,会话才建立成功,能够发送数据。最大的问题在于连接跟踪怎样控制这个过程,其实非常简单。默认情况下,连接跟踪基本上对所有的连接类型做同样的操作。看看下面的图片,我们就能明白在连接的不同阶段,流是处于什么状态的。

Iptables数据包状态类型(二)

就如你看到的,连接跟踪的代码不是从用户的观点来看待TCP连接建立的流程的。连接跟踪一看到SYN包,就认为这个连接是NEW状态,一看到返回的SYN/ACK包,就认为这个连接是ESTABLISHED状态。如果你仔细想想第二步,应该能理解为什么有了这个特殊处理,NEWESTABLISHED包就可以发送出本地网络,且只有ESTABLISHED的连接才能有回应信息。如果把整个建立连接的过程中传输的数据包都看作NEW,那么三次握手所用的包都是NEW状态的,这样我们就不能阻塞从外部到本地网络的连接了。因为即使连接是从外向内的,但它使用的包也是NEW状态的,而且为了其他连接能正常传输,我们不得不允许NEW状态的包返回并进入防火墙。更复杂的是,针对TCP连接内核使用了很多内部状态,它们的定义在 RFC 793 – Transmission Control Protocol21-23页。但好在我们在用户空间用不到。后面我们会详细地介绍这些内容。

如上图,正如你看到的,以用户的观点来看,这是很简单的。但是,从内核的角度看这一块还有点困难的。我们来看一个例子。认真考虑一下在/proc/net/nf_conntrack里,连接的状态是如何改变的。

tcp  6 117 SYN_SENT src=192.168.1.5 dst=192.168.1.35 sport=1031 dport=23 [UNREPLIED] src=192.168.1.35 dst=192.168.1.5 sport=23  dport=1031 use=1

从上面的记录可以看出,SYN_SENT状态被设置了,这说明连接已经发出一个SYN包,但应答还没发送过来,这可从[UNREPLIED]标志看出。

tcp  6 57 SYN_RECV src=192.168.1.5 dst=192.168.1.35 sport=1031 dport=23 src=192.168.1.35 dst=192.168.1.5 sport=23 dport=1031 use=1

现在我们已经收到了相应的SYN/ACK包,状态也变为SYN_RECV,这说明最初发出的SYN包已正确传输,并且SYN/ACK包也到达了防火墙,[UNREPLIED]标记就会被删除。这就意味着在连接的两方都有数据传输,因此可以认为两个方向都有相应的回应。当然,这是假设的。

tcp  6 431999 ESTABLISHED src=192.168.1.5 dst=192.168.1.35 sport=1031 dport=23 src=192.168.1.35 dst=192.168.1.5 sport=23 dport=1031 use=1

现在我们发出了三步握手的最后一个包,即ACK包,连接也就进入ESTABLISHED状态了。再传输几个数据包,连接就是[ASSURED]的了。

下面看一下TCP连接在关闭过程中的状态。

Iptables数据包状态类型(二)

如上图,在发出最后一个ACK包之前,连接(指两个方向)是不会关闭的。注意,这只是针对一般的情况。连接也可以通过发送关闭,这用在拒绝一个连接的时候。在RST包发送之后,要经过预先设定的一段时间,连接才能断掉。连接关闭后,进入TIME_WAIT状态,缺省时间是2分钟。之所以留这个时间,是为了让数据包能完全通过各种规则的检查,也是为了数据包能通过拥挤的路由器,从而到达目的地。如果连接是被RST包重置的,就直接变为CLOSE了。这意味着在关闭之前只有10秒的默认时间。RST包是不需要确认的,它会直接关闭连接。

2)UDP连接

UDP连接是无状态的,因为它没有任何的连接建立和关闭过程,而且大部分是无序列号的。以某个顺序收到的两个数据包是无法确定它们的发出顺序的。但内核仍然可以对UDP连接设置状态。我们来看看是如何跟踪UDP连接的,以及conntrack的相关记录。

Iptables数据包状态类型(二)

从上图可以看出,以用户的角度考虑,UDP连接的建立几乎与TCP的一样。虽然conntrack信息看起来有点 儿不同,但本质上是一样的。下面我们先来看看第一个UDP包发出后的conntrack记录。

udp   17 20 src=192.168.1.2 dst=192.168.1.5 sport=137 dport=1025 [UNREPLIED] src=192.168.1.5 dst=192.168.1.2 sport=1025 dport=137 use=1

从前两个值可知,这是一个UDP包。第一个是协议名称,第二个是协议号,第三个是此状态的生存时间, 默认是30秒。接下来是包的源、目地址和端口,还有期待之中回应包的源、目地址和端口。[UNREPLIED]标记说明还未收到回应。

udp   17 170 src=192.168.1.2 dst=192.168.1.5 sport=137 dport=1025 src=192.168.1.5 dst=192.168.1.2 sport=1025 dport=137 use=1

一旦收到第一个包的回应,[UNREPLIED]标记就会被删除,连接就被认为是ESTABLISHED的,但在记录里并不显示ESTABLISHED标记。相应地,状态的超时时间也变为180秒了。在本例中,只剩170秒了,10秒后, 就会减少为160秒。有个东西是不可少的,虽然它可能会有些变化,就是前面提过的[ASSURED]。要想变为 [ASSURED]状态,连接上必须要再有些流量。

udp   17 175 src=192.168.1.5 dst=195.22.79.2 sport=1025 dport=53 src=195.22.79.2 dst=192.168.1.5 sport=53 dport=1025 [ASSURED] use=1

可以看出来,[ASSURED]状态的记录和前面的没有多大差别,除了标记由[UNREPLIED]变成[ASSURED]。如果这个连接持续不了180秒,那就要被中断。180秒是短了点儿,但对大部分应用足够了。只要遇到这个连接的包穿过防火墙,超时值就会被重置为默认值,所有的状态都是这样的。

3)ICMP连接

ICMP也是一种无状态协议,它只是用来控制而不是建立连接。ICMP包有很多类型,但只有四种类型有应答包,它们是回显请求和应答(Echo request and reply),时间戳请求和应答(Timestamp request and reply),信息请求和应答(Information request and reply),还有地址掩码请求和应答(Address mask request and reply),这些包有两种状态,NEWESTABLISHED。时间戳请求和信息请求已经废除不用了,回显请求还是常用的,比如ping命令就用的到,地址掩码请求不太常用,但是可能有时很有用并且值得使用。看看下面的图,就可以大致了解ICMP连接的NEWESTABLISHED状态了。

Iptables数据包状态类型(二)

如图所示,主机向目标发送一个回显请求,防火墙就认为这个包处于NEW状态。目标回应一个回显应答,防火墙就认为包处于ESTABLISHED了。当回显请求被发送时,ip_conntrack里就有这样的记录了:

icmp   1 25 src=192.168.1.6 dst=192.168.1.10 type=8 code=0 id=33029 [UNREPLIED] src=192.168.1.10 dst=192.168.1.6 type=0 code=0 id=33029 use=1

可以看到,ICMP的记录和TCPUDP的有点区别,协议名称、超时时间和源、目地址都一样,不同之处在于没有了端口,而新增了三个新的字段:typecodeid。字段type说明ICMP的类型。code说明ICMP的代码,这些代码在附录ICMP类型里有说明。idICMP包的ID。每个ICMP包被发送时都被分配一个ID,接受方把同样的ID分配给应答包,这样发送方能认出是哪个请求的应答。

[UNREPLIED]的含义和前面一样,说明数的传输只发生在一个方向上,也就是说未收到应答。再往后,是应答包的源、目地址,还有相应的三个新字段,要注意的是typecode是随着应答包的不同而变化的,id和请求包的一样。和前面一样,应答包被认为是ESTABLISHED的。然而,在应答包之后,这个ICMP 连接就不再有数据传输了。所以,一旦应答包穿过防火墙,ICMP的连接跟踪记录就被销毁了。

以上各种情况,请求被认为NEW,应答是ESTABLISHED。换句话说,就是当防火墙看到一个请求包时,就认为连接处于NEW状态,当有应答时,就是ESTABLISHED状态。

ICMP的另一个非常重要的作用是,告诉UDPTCP连接或正在努力建立的连接发生了什么,这时ICMP应答 被认为是RELATED的。主机不可达和网络不可达就是这样的例子。当试图连接某台机 子不成功时(可能那台机子被关上了),数据包所到达的最后一台路由器就会返回以上的ICMP信息,它们就 RELATED的,如下图:

Iptables数据包状态类型(二)

我们发送了一个SYN包到某一地址,防火墙认为它的状态是NEW。但是,目标网络有问题不可达,路由器就会返回网络不可达的信息,这是RELATED的。连接跟踪会认出这个错误信息是哪个连接的,连接会中断,同时相应的记录删除会被删除。

UDP连接遇到问题时,同样会有相应的ICMP信息返回,当然它们的状态也是RELATED。我们发送一个UDP包,当然它是NEW的。但是,目标网络被一些防火墙或路由器所禁止。我们的防火墙就会收到网络被禁止的信息。防火墙知道它是和哪个已打开的UDP连接相关的,并且把这个信息(状态是RELATED)发给它,同时,把相应的记录删除。客户机收到网络被禁止的信息,连接将被中断。

缺省连接操作

有时,conntrack机制并不知道如何处理某个特殊的协议,尤其是在它不了解这个协议或不知道协议如何工作时,比如,NETBLTMUX还有EGP。这种情况下,conntrack使用缺省的操作。这种操作很象对UDP连接的 操作,就是第一个包被认作NEW,其后的应答包等等数据都是ESTABLISHED。使用缺省操作的包的超时值都是一样的,600秒,也就是10分钟。当然,这个值可以通过/proc/sys/net/netfilter/nf_conntrack_generic_timeout更改,以便适应你的通信量。

复杂协议连接跟踪

有些协议比其他协议更复杂,这里复杂的意思是指连接跟踪机制很难正确地跟踪它们,比如,ICQIRCFTP,它们都在数据包的数据域里携带某些信息,这些信息用于建立其他的连接。因此,需要一些特殊的helper来完成工作。下面以FTP作为例子。FTP协议先建立一个单独的连接——FTP控制会话。我们通过这个连接发布命令,其他的端口就会打开以便传输和这个命令相关的数据。这些连接的建立方法有两种:主动模式和被动模式。先看看主动模式,主动模式:服务器主动发起数据连接,首先由客户端向服务器的21端口建立ftp控制连接,当需要传输数据时客户端以port命令告知服务器我打开了某端口你过来连接我,于是服务器从20端口向客户端的该端口发送请求并建立数据连接。

问题在于防火墙不知道这些额外的连接(相对于控制会话而言),因为这些连接在建立时的磋商信息都在协议数据包的数据域内,而不是在可分析的协议头里。因此,防火墙就不知道是不是该放这些从服务器到客户机的连接过关。

解决的办法是为连接跟踪模块增加一个特殊的helper,以便能检测到那些信息。这样,那些从FTP服务器到客户机的连接就可以被跟踪了,状态是RELATED,过程如下图所示:

Iptables数据包状态类型(二)

被动FTP工作方式下,data连接的建立过程和主动FTP的相反。首先由客户机向服务器的21端口建立ftp控制连接,当需要传输数据时,服务器以pasv命令告知客户端我打开了某端口你来连接我,于是客户端向服务器的该端口(非20端口)发送请求并建立连接。如果FTP服务器在防火墙后面,或你对用户限制的比较严格,只允许他们访问HTTPFTP,而封闭了其他所有端口,为了让在Internet是的客户机能访问到FTP,也需要增加上面提到的helper。下面是被动模式下data连接的建立过程:

Iptables数据包状态类型(二)

查看状态信息

PS:这都是Centos 6.x的主机可以使用的文件,在Centos6上把netfilter相关文件都放在了/proc/sys/net/netfilter/目录下,而在Centos 5.x上大部分都在/proc/sys/net/ipv4/netfilter/目录下,并且把大部分以ip*开头的文件都改为nf*了,包括模块名称。


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (2)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!