65.9K
CodeProject 正在变化。 阅读更多。
Home

状态机

starIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

1.00/5 (13投票s)

2006年4月1日

14分钟阅读

viewsIcon

25297

连接跟踪机中的状态机

 简介:

 状态机是iptables中的一个特殊部分,实际上不应该称为状态机,因为它实际上是一个连接跟踪机。然而,大多数人把它叫做前者。连接跟踪是为了让Netfilter框架了解特定连接的状态。实现此功能的防火墙通常称为有状态防火墙。有状态防火墙通常比无状态防火墙更安全,因为它允许我们编写更严格的规则集。 

 

在iptables中,数据包可以与跟踪的连接以四种不同的所谓状态相关联。它们被称为NEW、ESTABLISHED、RELATED和INVALID。 

 

所有连接跟踪都由内核中的一个称为conntrack的特殊框架完成。Conntrack可以作为模块加载,也可以作为内核本身的内部部分加载。大多数时候,我们需要比默认conntrack引擎所能维护的更具体的连接跟踪。因此,conntrack还有处理TCP、UDP或ICMP等协议的更具体的部件。这些模块从数据包中提取特定、唯一的信息,以便它们可以跟踪每个数据流。conntrack收集的信息然后用于告诉conntrack流当前处于什么状态。例如,UDP流通常由其目标IP地址、源IP地址、目标端口和源端口唯一标识。

 

在以前的内核中,我们可以打开和关闭碎片整理。然而,自iptables和Netfilter引入以来,特别是连接跟踪,这个选项已被取消。原因是连接跟踪如果没有碎片整理就无法正常工作,因此碎片整理已被合并到conntrack中并自动执行。除非关闭连接跟踪,否则无法将其关闭。如果连接跟踪处于打开状态,则始终执行碎片整理。

 

所有连接跟踪都在PREROUTING链中处理,但本地生成的数据包除外,它们在OUTPUT链中处理。这意味着iptables将在PREROUTING链中执行所有状态的重新计算等。如果我们发送流的初始数据包,状态将在OUTPUT链中设置为NEW,当我们收到返回数据包时,状态将在PREROUTING链中更改为ESTABLISHED,依此类推。如果第一个数据包不是我们发起的,那么NEW状态当然是在NAT表的PREROUTING链中设置的。因此,所有状态更改和计算都在NAT表的PREROUTING和OUTPUT链中完成。 

 

NEW状态告诉我们数据包在连接中是新的。这意味着conntrack模块看到的第一个数据包将被匹配。例如,如果我们看到一个SYN数据包,并且它是我们看到的连接的第一个数据包,它就会匹配。然而,数据包也可能不是SYN数据包,仍然被认为是NEW。这在某些情况下可能导致某些问题,但当我们需要拾取来自其他防火墙的丢失连接,或者连接已经超时但实际上并未关闭时,它也可能非常有用。

 

ESTABLISHED状态已经看到了双向流量,然后会持续匹配这些数据包。ESTABLISHED连接相对容易理解。进入ESTABLISHED状态的唯一要求是,一个主机发送一个数据包,然后它从另一台主机收到一个应答。收到发往或穿过防火墙的应答数据包后,NEW状态将更改为ESTABLISHED状态。ICMP错误消息和重定向等也可以被视为ESTABLISHED,如果我们生成了一个数据包,而该数据包又生成了ICMP消息。

 

RELATED状态是比较棘手的一种状态。当一个连接与另一个已ESTABLISHED的连接相关时,该连接被认为是RELATED。这意味着,要使一个连接被认为是RELATED,我们必须首先有一个被认为是ESTABLISHED的连接。然后,ESTABLISHED连接将启动一个主连接之外的连接。如果conntrack模块能够理解新启动的连接是RELATED的,那么它将被认为是RELATED。 

 

INVALID状态表示无法识别该数据包,或者该数据包没有任何状态。这可能由多种原因引起,例如系统内存不足或ICMP错误消息与任何已知连接都不匹配。通常,在这种状态下丢弃所有内容是一个好主意。

 

iptables-save:

正如我们已经解释过的,iptables-save命令是一个工具,用于将当前规则集保存到iptables-restore可以使用的文件中。这个命令实际上很简单,只需要两个参数。看一下下面的例子来理解命令的语法。

 

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">iptables-save [-c] [-t table] 

 

-c参数告诉iptables-save保留字节和数据包计数器中指定的值。例如,如果我们想重新启动我们的主防火墙,但又不丢失可用于统计目的的字节和数据包计数器,这将非常有用。使用-c参数发出iptables-save命令将使我们能够重新启动,而不会破坏我们的统计和记账程序。当然,在发出此命令时,默认值是不保留计数器。

 

-t参数告诉iptables-save命令要保存哪些表。如果没有这个参数,命令将自动将所有可用的表保存到文件中。如果您没有加载任何规则集,以下是您可以从iptables-save命令获得的输出示例。

 

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"># Generated by iptables-save v1.2.6a on Wed Apr 24 <?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /><st1:time Hour="10" Minute="19"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">10:19:17</st1:time><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"> 2002 <o:p></o:p>
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">*filter 

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:INPUT ACCEPT [404:19766] <o:p></o:p>

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:FORWARD ACCEPT [0:0] <o:p></o:p>
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:OUTPUT ACCEPT [530:43376] 
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">COMMIT <o:p></o:p>

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"># Completed on Wed Apr 24 <st1:time Hour="10" Minute="19"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">10:19:17</st1:time><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"> 2002 <o:p></o:p>
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"># Generated by iptables-save v1.2.6a on Wed Apr 24 <st1:time Hour="10" Minute="19"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">10:19:17</st1:time><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"> 2002 
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">*mangle <o:p></o:p>

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:PREROUTING ACCEPT [451:22060] <o:p></o:p>
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:INPUT ACCEPT [451:22060] <o:p></o:p>
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:FORWARD ACCEPT [0:0] 
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:OUTPUT ACCEPT [594:47151] 
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:POSTROUTING ACCEPT [594:47151] 

 
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">COMMIT <o:p></o:p>

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"># Completed on Wed Apr 24 <st1:time Hour="10" Minute="19"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">10:19:17</st1:time><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"> 2002 
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"># 
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">Generated by iptables-save v1.2.6a on Wed Apr 24 <st1:time Hour="10" Minute="19"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">10:19:17</st1:time><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"> 2002 

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">*nat 

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:PREROUTING ACCEPT [0:0] 
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:POSTROUTING ACCEPT [3:450] 
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">:OUTPUT ACCEPT [3:450] 

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">COMMIT 

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"># Completed on Wed Apr 24 <st1:time Hour="10" Minute="19"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">10:19:17</st1:time><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"> 2002 <o:p></o:p>
<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="mso-spacerun: yes"> <o:p></o:p>

这包含一些以#号开头的注释。每个表都标记为*<table-name>,例如*mangle。然后,在每个表内,我们有链规范和规则。链规范看起来像

<chain-name> <chain-policy> [<packet-counter>:<byte-counter>]。

 

 

 chain-name可以是PREROUTING,policy前面已经描述过,可以是ACCEPT。最后,packet-counter和byte-counters与iptables -L -v的输出中的计数器相同。最后,每个表声明都以COMMIT关键字结束。COMMIT关键字告诉我们,在这一点上,我们应该将所有当前在管道中的规则提交到内核。

 

要将其保存到任何文件中,只需将命令输出管道传输到您想要保存的文件即可。这可能看起来像下面这样

 

<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"><SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'">iptables-save -c > /etc/iptables-save<SPAN style="FONT-FAMILY: Arial; mso-bidi-font-family: 'Times New Roman'"> 

 

换句话说,上面的命令会将整个规则集保存到一个名为/etc/iptables-save的文件中,并且字节和数据包计数器仍然保持不变。 

 

Iptables-restore

iptables-restore命令用于恢复用iptables-save命令保存的iptables规则集。它从标准输入读取所有输入,并且在撰写本文时无法从文件中加载,很遗憾。这是iptables-restore的命令语法

<SPAN style="LETTER-SPACING: 0pt"><SPAN style="LETTER-SPACING: 0pt">iptables-restore [-c] [-n]

-c参数恢复字节和数据包计数器,如果想恢复之前用iptables-save保存的计数器,则必须使用它。此参数也可以写成其长形式--counters。

-n参数告诉iptables-restore不要覆盖要写入的表或表中先前写入的规则。iptables-restore的默认行为是刷新并销毁所有先前插入的规则。短-n参数也可以被长格式--noflush替换。

 

使用iptables-restore命令加载规则集,我们可以通过几种方式做到这一点,但我们在这里主要关注最简单、最常见的方式。

<SPAN style="LETTER-SPACING: 0pt"><SPAN style="LETTER-SPACING: 0pt">cat /etc/iptables-save | iptables-restore -c 

这会cat /etc/iptables-save文件中的规则集,然后将其管道传输到iptables-restore,iptables-restore从标准输入接收规则集,然后恢复它,包括字节和数据包计数器。一开始就是这么简单。

规则集现在应该已正确加载到内核,一切都应该正常工作。如果不行,您可能遇到了这些命令中的一个bug,尽管听起来不太可能。

下面给出的例子使一切都变得清晰

例如,如果您的计算机要向   http://www.yahoo.com/ 发送一个数据包

     要请求一个HTML页面,它将首先通过OUTPUT链。内核将查看链中的规则,看看是否有任何规则匹配。第一个匹配的规则将决定该数据包的结果。如果没有规则匹配,那么整个链的策略将是最终决定者。然后Yahoo!发回的任何回复都将通过INPUT链。就这么简单。

现在我们已经掌握了基本知识,可以开始将其付诸实践了。假设您想阻止来自200.200.200.1的所有数据包。首先,-s用于指定源IP或DNS名称。因此,从这一点来看,要引用来自此地址的流量,我们将使用此

iptables -s 200.200.200.1

但这并没有说明如何处理数据包。-j选项用于指定数据包的处理方式。最常见的三个是ACCEPT、DENY和DROP。现在您可能已经猜到了ACCEPT的作用,但这并不是我们想要的。DENY会发回一条消息,表明这台计算机不接受连接。DROP只是完全忽略该数据包。如果我们对这个特定的IP地址感到非常怀疑,我们可能会更倾向于DROP而不是DENY。所以这是带有结果的命令

iptables -s 200.200.200.1 -j DROP

但是计算机仍然不明白。还有一件事我们需要添加,那就是它属于哪个链。您为此使用-A。它只是将规则附加到您指定的链的末尾。因为我们想阻止计算机与我们通信,所以我们将把它放在INPUT链上。所以这是完整的命令

iptables -A INPUT -s 200.200.200.1 -j DROP

这个单一命令将忽略来自200.200.200.1的所有内容(有例外,但我们稍后会讨论)。选项的顺序无关紧要;-j DROP可以在-s 200.200.200.1之前。我只是喜欢把结果部分放在命令的末尾。好的,我们现在已经能够忽略网络上的某个特定计算机了。如果您想阻止您的计算机与它通信,您只需将INPUT更改为OUTPUT,并将-s更改为-d(目标)。这不算太难,是吗?

那么如果我们只想忽略来自这台计算机的telnet请求呢?那也不是很难。您可能知道端口23是用于telnet的,但您也可以直接使用telnet这个词。至少有3种协议可以指定:TCP、UDP和ICMP。Telnet,像大多数服务一样,运行在TCP上,所以我们选择它。-p选项指定协议。但是TCP并没有告诉它一切;telnet只是TCP的更大协议上的一种特定协议。在我们指定协议是TCP之后,我们可以使用--destination-port来表示他们试图联系我们的端口。确保不要混淆源端口和目标端口。请记住,客户端可以在任何端口上运行,是服务器将在端口23上运行服务。任何时候您想阻止某个服务,您都将使用--destination-port。反之则是--source-port,以防您需要它。所以让我们把这一切放在一起。这应该是完成我们想要的命令

iptables -A INPUT -s 200.200.200.1 -p tcp --destination-port telnet -j DROP

就这样。如果您想指定一个IP范围,您可以使用200.200.200.0/24。这将指定任何匹配200.200.200.*的IP。现在是时候处理更大的问题了。假设,像我一样,您有一个局域网,然后您有一个互联网连接。我们还假设LAN是eth0,而互联网连接称为ppp0。现在假设我们想允许LAN上的计算机运行telnet作为服务,但不能在不安全的互联网上运行。有一个简单的方法可以做到这一点。我们可以使用-i指定输入接口,使用-o指定输出接口。您总是可以在OUTPUT链上阻止它,但我们宁愿在INPUT链上阻止它,这样telnet守护进程甚至都不会看到请求。因此,我们将使用-i。这应该设置了规则

iptables -A INPUT -p tcp --destination-port telnet -i ppp0 -j DROP

所以这将阻止互联网上的任何人访问该端口,但仍然对LAN开放。现在在我们继续进行更深入的研究之前,我想简要解释一下操作规则的其他方法。-A选项将规则附加到列表的末尾,这意味着任何匹配的规则都将在它之前被处理。如果我们想在链的末尾之前添加一条规则,我们使用-I表示插入。这将把规则放在链中的一个数值位置。例如,如果我们想把它放在INPUT链的顶部,我们将使用“-I INPUT 1”以及命令的其余部分。只需将1更改为您想要的任何位置。

现在假设我们想替换该位置已有的规则。只需使用-R来替换规则。它具有与-I相同的语法,并且工作方式相同,只是它删除了该位置的规则,而不是将所有内容向下推。最后,如果您只想删除一条规则,请使用-D。它也有类似的语法,但您可以使用规则编号,或者键入创建规则时会输入的所有选项。数字方法通常是最佳选择。还有两个简单的选项需要学习。-L列出所有已设置的规则。当您忘记您在哪里时,这显然很有帮助。以及-F刷新特定的链。(它会删除链上的所有规则。)如果您不指定链,它将基本上刷新所有内容。

好吧,让我们变得更高级一些。我们知道这些数据包使用特定的协议,如果该协议是TCP,那么它还使用特定的端口。现在您可能会被迫关闭所有入站流量的端口,但请记住,在您的计算机与其他计算机通信之后,该计算机必须回通信。如果您关闭了所有入站端口,您实际上将使您的连接变得无用。而且对于大多数非服务程序,您无法预测它们将使用哪个端口进行通信。

但仍然有一种方法。当两台计算机通过TCP连接进行通信时,该连接必须首先初始化。这是SYN数据包的任务。SYN数据包只是告诉另一台计算机它已准备好通信。现在只有请求服务的计算机才会发送SYN数据包。

因此,如果您只阻止入站SYN数据包,它会阻止其他计算机在您的计算机上打开服务,但不会阻止您与它们通信。它基本上使您的计算机忽略任何它没有先联系过的内容。这很刻薄,但能完成工作。用于此的选项是--syn,在您指定了TCP协议之后。所以要创建一个规则来阻止互联网上所有入站连接

iptables -A INPUT -i ppp0 -p tcp --syn -j DROP

这是一个您可能会使用的常见规则,除非您运行了一个Web服务。如果您想开放一个端口,例如80(HTTP),也有一个简单的方法可以做到。与许多编程语言一样,感叹号表示“非”。例如,如果您想阻止所有端口上的所有SYN数据包,但80除外,我认为它看起来会像这样

iptables -A INPUT -i ppp0 -p tcp --syn --destination-port ! 80 -j DROP

这有点复杂,但并不难理解。还有最后一件事我想讲,那就是更改链的策略。INPUT和OUTPUT链通常默认为ACCEPT,而FORWARD默认为DENY。如果您想将这台计算机用作路由器,您可能希望将FORWARD策略设置为ACCEPT。好吧,这真的非常简单。您所要做的就是使用-P选项。只需在其后面跟上链名和新策略即可。要将FORWARD链更改为ACCEPT策略,我们将这样做

iptables -P FORWARD ACCEPT

 

© . All rights reserved.