iptables conntrack
转载自https://www.idzn.cn/?post=206
-m conntrack --ctstate ESTABLISHED,RELATED -j ...
-m state --state ESTABLISHED,RELATED -j ...
我们要对一条连接做处理,可以用以上两种match target来实现。
iptables是最常用的一种Linux主机防火墙,借助于netfilter优秀的性能和扩展,虽历经多年,但仍不落伍。OpenStack中安全组功能,floating IP的实现,以及fwaas的主流方案大多是依赖iptables而实现的。
与包过滤防火墙不同的是,iptables是一种状态防火墙,可以记录和跟踪数据流的状态。正是基于此,才会有以上优秀方案的落地。
从Linux2.6.15的内核版本后,iptables开始支持状态跟踪(conntrack),该功能依赖于netfilter的内核模块nf_conntrack。此后,iptables可以根据包的状态进行二次的过滤拦截和状态跟踪。它也是state/ctstate和nat的主要依赖模块。
状态跟踪
conntrack将数据流的状态信息以Hash表的形式储存在内存中,包括五元组信息以及超时时间等。这里说的状态跟踪并非是指状态协议(如TCP)中连接状态的跟踪,而是conntrack特有的与网络传输协议无关的状态的跟踪。
下面介绍一下conntrack中的状态分类以及在TCP和UDP这两种4层协议中,是如何进行状态跟踪的。
五种状态
conntrack共可以为连接标记五种状态,分别如下:
NEW:
新建连接请求的数据包,且该数据包没有和任何已有连接相关联。
判断的依据是conntrack当前“只看到一个方向数据包(UNREPLIED)”,没有回包。
ESTABLISHED:
该连接是某NEW状态连接的回包,也就是完成了连接的双向关联。
RELATED:
匹配那些属于helper模块定义的特殊协议的网络连接,该连接属于已经存在的一个ESTABLISHED连接的衍生连接。
简而言之,A连接已经是ESTABLISHED,而B连接如果与A连接相关,那么B连接就是RELATED。这部分不理解没有关系,也很难一句话说清,后面章节会用大量笔墨来阐明它。
INVALID:
匹配那些无法识别或没有任何状态的数据包。这可能是由于系统内存不足或收到不属于任何已知连接的ICMP错误消息,也就是垃圾包,一般情况下我们都会DROP此类状态的包。
UNTRACKED :
这是一种特殊状态,或者说并不是状态。它是管理员在raw表中,为连接设置NOTRACK规则后的状态。这样做,便于提高包过滤效率以及降低负载。
conntrack是一种状态跟踪和记录的机制,本身并不能过滤数据包,只是提供包过滤的依据。 有状态是一种过滤依据,无状态实际也是一种过滤依据。
因此,需要小心的是,设置了NOTRACK,不代表是放行,只是它的状态是UNTRACKED。所以如果想要对这种包放行或者处理,同样需要配置相应的规则。具体使用方法可以查看下面“设置NOTRACK”部分的内容。
TCP的状态跟踪
从TCP开始讨论的原因也是因为TCP本身是状态协议。TCP通过三次握手建立连接,分别是SYN,SYN/ACK和ACK,当完成之后连接成为ESTABLISHED状态。也就是说TCP完成ESTABLISHED的时候,C和S两端已经进行了三次交互。
对于iptables而言,每一次握手,都需要对连接过滤,如前面所说NEW和ESTABLISHED状态,分别指的是,当nf_conntrack第一次发现该连接的时候,会将其状态设置为NEW,当反方向也出现包的时候,即认为是ESTABLISHED。如下图,观察在TCP的三次握手过程(SYN,SYN/ACK,ACK)中,conntrack状态(NEW,ESTABLISHED)出现的时机。可以看到,第1次握手与第2次握手间是NEW,第2次握手之后就是ESTABLISHED。某种角度来说,可以认为两次握手对conntrack而言就已经完成了ESTABLISHED
讨论到这里,已经把本章节的两个主角(TCP和conntrack中的ESTABLISHED状态)请了出来,下面举个具体的例子,看下TCP状态的变化。
打开/proc/net/nf_conntrack,可以看到类似如下的内容。
先说下每行entry的意思,第1列是网络层协议名称;第2列是协议代号;第3列代表传输层协议名称;第4列是传输层协议代号,其中tcp是6,udp是17;第5列的117指的是TTL;第6列是TCP的状态;第7列是表示源目地址以及对应端口;第8列是表示是否收到回应包;第9列表示期望收到的回包的源目地址及端口。
ipv4 2 tcp 6 117 SYN_SENT src=192.168.1.5 dst=192.168.1.7 sport=1031 dport=23 [UNREPLIED] src=192.168.1.7 dst=192.168.1.5 sport=23 dport=1031 use=1
1
第1次握手时(SYN),TCP的状态是SYN_SENT,且有 [UNREPLIED] 标记,意味着还没有收到回包。此时,连接是NEW的状态,这点很好理解。
接着往下看
ipv4 2 tcp 6 57 SYN_RECV src=192.168.1.5 dst=192.168.1.7 sport=1031 dport=23 src=192.168.1.7 dst=192.168.1.5 sport=23 dport=1031 use=1
1
第2次握手时(SYN/ACK),TCP连接的状态为SYN_RECV,也就是说,kernel收到了回复(replay), 在初次握手时候的[UNREPLIED]的tag被去除。需要注意的是,此时conntrack已经是ESTABLISHED,再往下看
ipv4 2 tcp 6 431999 ESTABLISHED src=192.168.1.5 dst=192.168.1.7 sport=1031 dport=23 src=192.168.1.7 dst=192.168.1.5 sport=23 dport=1031 [ASSURED] use=1
1
第3次握手时(ACK),TCP连接的状态变为ESTABLISHED,也就是说,Client发出了ACK的确认标记包,此时三次握手完毕,TCP连接建立。
TCP总结: 对Client而言,TCP状态与conntrack状态对比是SYN_SENT(NEW),SYN_RECV(ESTABLISHED),ESTABLISHED(ESTABLISHED);
配置iptables规则
对于Client是192.168.1.5:1031,Server是192.168.1.7:23。需求是Client可以主动连接Server,而反向主动的连接不可以。Client上防火墙规则如下:
#iptables -A INPUT -s 192.168.1.7 -p tcp --dport 1031 --sport 23 -m conntrack --ctstate ESTABLISHED -j ACCEPT
#iptables -A OUTPUT -d 192.168.1.7 -p tcp --sport 1031 --dport 23 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
1
2
3
4
5
6
在这里顺便提及一下-m conntrack --ctstate与-m state --state的关系。
官方给的说法是这样:
The conntrack match is an extended version of the state match, which makes it possible to match packets in a much more granular way. It let’s you look at information directly available in the connection tracking system, without any “frontend” systems, such as in the state match.
大意是ctstate是state的扩展版本(内核版本>=2.5开始支持),包括状态参数也是基本相同,不用过多纠结,不过既然出了新的写法,个人还是推荐新的写法和规则。
UDP的状态跟踪
UDP是无状态的传输协议,不需要三次握手也没有SYN和ACK等各种标签和状态。虽然没有三次握手的概念,但是我们还是来看三次连接的状态记录。
如下图,观察在UDP连接过程中,conntrack状态(NEW,ESTABLISHED)出现的时机。可以看到,第1次连接与第2次连接间是NEW的状态,第2次连接之后就是ESTABLISHED。对UDP而言,也可以认为两次握手对conntrack而言就已经完成了ESTABLISHED
ipv4 2 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
1
首先可以看到的是UDP没有SYN_SENT这种TCP特有的状态标签,但是有 [UNREPLIED] 的标识,说明这个包是初次发出的包,还没有收到回应。此时conntrack中对应的状态是NEW,接着往下看。
ipv4 2 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 [ASSURED] use=1
1
同样还是没有标识,但是 [UNREPLIED] 变成了 [ASSURED] ,表明已经收到了回包,且连接建立完成。另外TTL变成了170,这时由于在该连接状态下,默认的TTL是180,而第一次连接时默认值是30。此时conntrack中定义的状态是ESTABLISHED。接着往下看
ipv4 2 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
1
Client发出第3次包之后,可以发现TTL变为了175,说明TTL更新了,也同时说明第二次和第三次中包的状态或者说标签是一致的,所以TTL默认值才会相同。
UDP总结: 对Client而言,UDP过程与conntrack状态对比是,第一次连接(NEW),第二次连接(ESTABLISHED);
配置iptables规则
对于Client是192.168.1.5:1031,Server是192.168.1.7:23。需求是Client可以主动连接Server,而反向主动的连接不可以。Client上iptables规则如下:
#Client
#iptables -A INPUT -s 192.168.1.7 -p udp --dport 1031 --sport 23 -m conntrack --ctstate ESTABLISHED -j ACCEPT
#iptables -A OUTPUT -d 192.168.1.7 -p udp --sport 1031 --dport 23 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
1
2
3
4
5
6
回头再看
通过对以上知识的整理,iptables的状态跟踪(conntrack)思想若隐若现。
网络防火墙更关心的“进”和“出”,他有自己的考虑和规则,至于进出的包依循的是什么,他并不关心。
当连接初次出现的时候,该连接就是NEW,当出现了对应的反向连接的时候,那么该连接就是ESTABLISHED。看起来有点像UDP,是不是?
至于为什么会设计成这种模式,可能是考虑到防火墙只是涉及进出两个方向,而两次握手已经可以代表两次的方向,也可能考虑到对UDP以及ICMP的兼容等问题,在这就不去深入讨论了。