yy / iptables tutorial

Created Sat, 01 Jun 2024 00:00:00 +0800 Modified Sun, 19 Apr 2026 07:54:45 +0200
75381 Words

[toc]

Iptables 教程 1.2.1 (frozentux.net)

前面两章内容简单介绍 ip/tcp/udp/icmp 等,略

第 3 章. IP 过滤介绍

本章将讨论有关 IP 过滤的理论细节、IP 过滤是什么、IP 过滤如何工作以及防火墙放置位置、策略等基本问题。

本章的问题可能是:防火墙究竟应该放在哪里?在大多数情况下,这是一个简单的问题,但在大型企业环境中,这个问题可能会变得更加棘手。策略应该是什么?谁有权限访问哪里?什么是 IP 过滤器?所有这些问题都将在本章后面的内容中得到很好的解答。

3.1. 什么是 IP 过滤器

充分了解什么是 IP 过滤器非常重要。Iptables 是一个 IP 过滤器,如果不充分了解这一点,将来在设计防火墙时就可能会遇到严重问题。

IP过滤器主要在TCP/IP协议栈的第2层运行。不过,Iptables 也能在第 3 层工作,事实上,目前大多数 IP 过滤器都具备这种能力。但根据定义,IP 过滤器工作在第二层。

如果严格按照定义来实现 IP 过滤,那么换句话说,它只能根据 IP 头(源地址和终止地址、TOS/DSCP/ECN、TTL、协议等)来过滤数据包。这些都是 IP 头中实际存在的内容。不过,由于 Iptables 的实现并不完全严格遵循这一定义,因此它也能根据数据包中更深的其他报头(TCP、UDP 等)和更浅的报头(MAC 源地址)过滤数据包。

不过,现在的 iptables 对一件事非常严格。它不会 “跟踪 ”数据流或将数据拼凑在一起。这样做太耗时了。关于这一点的影响,我们还将进一步讨论。它确实会跟踪数据包,并查看它们是否属于同一数据流(通过序列号、端口号等),这与真正的 TCP/IP 协议栈几乎完全相同。这就是所谓的连接跟踪,有了它,我们就可以进行目标和源网络地址转换(一般称为 DNAT 和 SNAT),以及数据包的状态匹配。

如上所述,iptables 无法将不同数据包中的数据相互连接,因此你永远无法完全确定在任何时候都能看到完整的数据。之所以特别提到这一点,是因为在有关 netfilter 和 iptables 的不同邮件列表中,经常会有一些关于这方面的问题,以及如何做一些通常被认为是非常糟糕的事情。例如,每当有新的基于 Windows 的病毒出现时,就会有几个不同的人询问如何丢弃所有包含特定字符串的数据流。这种馊主意很容易被规避。例如,如果我们匹配如下内容 cmd.exe ,现在,如果病毒/漏洞编写者足够聪明,把数据包做得很小,以至于 cmd 在一个数据包中,而 .exe 在下一个数据包中,会发生什么情况呢?或者,如果数据包必须通过一个本身数据包大小就这么小的网络呢?是的,因为这些字符串匹配功能无法跨越数据包的边界,所以数据包还是会通过。

现在有些人可能会问自己,为什么我们不干脆让字符串匹配等功能可以跨数据包边界读取呢?其实这很简单。这样做太耗费处理器时间了。连接跟踪已经耗费了太多的处理器时间,完全不能让人放心。为连接跟踪再增加一层复杂性,可能会导致更多的防火墙死机。更不用说在每台机器上执行这项简单的任务会占用多少内存了。

没有开发这种功能还有第二个原因。有一种技术叫做代理。开发代理是为了处理更高层的流量,因此能更好地满足这些要求。开发代理最初是为了处理下载和常用网页,并帮助你充分利用缓慢的互联网连接。例如,Squid 就是一种网络代理。想要下载网页的人发送请求,代理抓取请求或接收请求并打开与网络浏览器的连接,然后连接到网络服务器并下载文件,下载完成后,代理将文件或网页发送给客户端。现在,如果第二个浏览器想再次阅读同一个页面,文件或页面已经下载到代理服务器,可以直接发送,为我们节省了带宽。

正如你所理解的,代理服务器还有很多功能,可以进入并查看所下载文件的实际内容。正因为如此,它们能更好地查看整个流、文件和网页等内部内容。

3.2. IP 过滤术语和表达式

要充分理解接下来各章的内容,必须了解一些通用术语和表达式,包括有关 TCP/IP 章节的许多细节。下面列出了 IP 过滤中最常用的术语。

  1. Drop/Deny 丢弃/拒绝 - 当一个数据包被丢弃或拒绝时,它只是被删除,不会采取进一步的行动。没有回复告诉主机数据包被丢弃,也没有以任何方式通知接收数据包的主机。数据包就这样消失了。

  2. Reject 拒绝 - 这与丢弃或拒绝目标或策略基本相同,只是我们也会向发送被丢弃数据包的主机发送回复。回复可以是指定的,也可以是自动计算出的某个值。(遗憾的是,迄今为止,iptables 还没有发送数据包通知被拒绝数据包的接收主机发生了什么的功能(即与拒绝目标相反)。这在某些情况下会非常有用,因为接收主机无法阻止拒绝服务攻击的发生。)

  3. State 状态 - 与整个数据包流相比,数据包的特定状态。例如,如果数据包是防火墙看到或知道的第一个数据包,则认为是新数据包(TCP 连接中的 SYN 数据包);如果数据包是防火墙知道的已建立连接的一部分,则认为是已建立连接。状态通过连接跟踪系统获知,该系统会跟踪所有会话。

  4. Chain 链 - 一个链包含一组规则,这些规则应用于穿越该链的数据包。每个链都有特定的目的(如连接到哪个表,该表规定了该链的功能),以及特定的应用领域(如只转发数据包,或只转发给该主机的数据包)。在 iptables 中,有几种不同的链,将在后面的章节中深入讨论。

  5. Table 表 - 每个表都有特定用途,在 iptables 中有 3 个表。nat 表、mangle 表和 filter 表。例如,filter 表专门用于过滤数据包,而 nat 表专门用于 NAT(网络地址转换)数据包。

    常说四表五链,但实际上有五个表

    raw 用于连接跟踪

    security 用于安全防火墙规则,很少使用

  6. Match 匹配 - 在 IP 过滤时,这个词有两种不同的含义。第一种含义是单个匹配,它告诉规则此报头必须包含此信息和此信息。例如,–source 匹配告诉我们源地址必须是特定的网络范围或主机地址。第二个含义是整条规则是否匹配。如果数据包与整条规则匹配,就会执行跳转或目标指令(如丢弃数据包)。

  7. Target 目标 - 规则集中的每条规则通常都有一个目标集。如果规则完全匹配,目标规范就会告诉我们如何处理数据包。例如,是丢弃还是接受,或者 NAT 等。还有一种叫做跳转规范的东西,更多信息请参阅本列表中的跳转说明。最后要说明的是,每条规则可能没有目标或跳转,但也可能有。

  8. Rule 规则 - 在大多数 IP 过滤器实现(包括 iptables 实现)中,规则是一组匹配或多个匹配与一个目标的集合。有些实现允许在每条规则中使用多个目标/动作。

  9. Ruleset 规则集 - 规则集是整个 IP 过滤器实现中的一整套规则。在 iptables 中,这包括在 filter、nat 和 mangle 表以及所有后续链中设置的所有规则。大多数情况下,这些规则都写在某种配置文件中。

  10. Jump 跳转 - 跳转指令与目标密切相关。跳转指令的写法与 iptables 中目标的写法完全相同,不同之处在于你写的不是目标名称,而是另一条链的名称。如果规则匹配,数据包就会被发送到第二个链,并在该链中进行正常处理。

  11. Connection tracking 连接跟踪 - 实现连接跟踪的防火墙能够简单地跟踪连接/流。但这样做往往会占用大量处理器和内存。不幸的是,这在 iptables 中也是如此,但在这方面已经做了很多工作。不过,好的一面是,如果防火墙策略实施者能正确使用连接跟踪功能,防火墙就会更加安全。

  12. Accept 接受 - 接受数据包并允许其通过防火墙规则。这与丢弃或拒绝目标以及拒绝目标相反。

  13. Policy 策略 - 在实施防火墙时,我们经常会提到两种策略。首先是链式策略,它告诉防火墙在没有匹配规则的情况下对数据包采取的默认行为。这是我们在本书中使用的该词的主要用法。第二类策略是安全策略,我们可能已经编写了相关文档,例如针对整个公司或特定网段的安全策略。安全策略是非常好的文档,在开始实际实施防火墙之前,我们应该对其进行适当的思考和研究。

3.3. 如何规划 IP 过滤器

规划防火墙时首先要考虑的是防火墙的位置。这应该是一个相当简单的步骤,因为无论如何,你的大部分网络都应该有相当好的分段。首先想到的一个地方是本地网络与互联网之间的网关。这里应该有相当严密的安全措施。此外,在大型网络中,通过防火墙将不同部门相互隔离可能是个好主意。例如,为什么开发团队可以访问人力资源网络,为什么不保护经济部门不受其他网络的影响?简而言之,你不希望愤怒的员工拿着粉红单篡改工资数据库。

简而言之,上述情况意味着你应该尽可能完善地规划网络,并将其隔离。尤其是大中型网络(100 个工作站或更多,基于网络的不同方面)。在这些较小的网络之间,尽量设置防火墙,只允许你希望的流量。

在网络中创建一个非军事区(DMZ)也不失为一个好主意,以防有服务器可以从互联网接入。DMZ 是一个带有服务器的小型物理网络,它是极端封闭的。这就降低了任何人实际进入 DMZ 中机器的风险,也降低了任何人实际进入并从外部下载任何木马等的风险。之所以称它们为非军事区,是因为它们必须可以从内部和外部到达,因此它们是一种灰色区域(简称 DMZ)。

在防火墙中设置策略和默认行为有几种方法,本节将讨论在实际开始实施防火墙之前应该考虑的实际理论,并帮助你充分考虑你的决定。

在开始之前,你应该明白大多数防火墙都有默认行为。例如,如果特定链中没有规则匹配,默认情况下要么丢弃,要么接受。遗憾的是,每个链只有一个策略,但如果我们想在每个网络接口等处设置不同的策略,这通常很容易解决。

我们通常使用两种基本策略。要么放弃除指定内容以外的所有内容,要么接受除指定内容以外的所有内容。大多数情况下,我们最感兴趣的是丢弃策略,然后接受我们想要特别允许的所有内容。这意味着防火墙默认情况下更加安全,但也可能意味着你需要做更多的工作才能使防火墙正常运行。

你首先要做的决定就是弄清楚应该使用哪种类型的防火墙。安全问题有多大?什么样的应用程序必须能够通过防火墙?某些应用程序对防火墙来说是可怕的,原因很简单,因为它们会协商端口,以便在控制会话中使用数据流。这样,防火墙就很难知道要开放哪些端口。最常见的应用程序可以使用 iptables,但不幸的是,更罕见的应用程序至今仍无法使用。

还有一些应用程序可以部分运行,如 ICQ。正常的 ICQ 使用可以完美运行,但聊天或文件发送功能却不行,因为它们需要特定的代码来处理协议。由于 ICQ 协议没有标准化(属于专有协议,随时可能更改),大多数 IP 过滤器都选择将 ICQ 协议处理程序排除在外,或将其作为可应用于防火墙的补丁程序。Iptables 则选择将它们作为单独的补丁程序。

采用分层安全措施也不失为一个好主意。我们的意思是,你应该同时使用尽可能多的安全措施,而不要依赖于任何一个单一的安全概念。将此作为安全的基本概念,至少可以提高十倍的安全性。让我们举例说明。

LAN       DMZ(Linux Web Servers)
  ↕️       ↕️
  Cisco PIX
      ↕️
   Internet

如您所见,在本例中,我选择在所有三个网络连接的外围放置一个 Cisco PIX 防火墙。它可以对内部局域网进行 NAT,必要时也可以对 DMZ 进行 NAT。它还将阻止除 http 返回流量以及 ftp 和 ssh 流量之外的所有传出流量。它允许从局域网和互联网传入 http 流量,允许从局域网传入 ftp 和 ssh 流量。此外,我们注意到每台网络服务器都是基于 Linux 的,因此我们也在每台机器上安装了 iptables 和 netfilter,并添加了相同的基本策略。

除此之外,我们还可以在每台机器上添加 Snort。Snort 是一个优秀的开源 “网络入侵检测系统”(NIDS),它可以在看到的数据包中查找签名,如果看到某种攻击或入侵的签名,它就可以通过电子邮件通知管理员,甚至可以对攻击做出主动响应,如阻止攻击来源的 IP。需要注意的是,不应轻易使用主动响应,因为 snort 有报告大量误报的不良行为(如报告并非真正攻击的攻击)。

在网络服务器前添加代理也是一个好主意,这样可以捕捉到一些坏数据包,也可以为所有本地生成的网络连接添加代理。有了网络代理,你就可以缩小员工网络流量的使用范围,并在一定程度上限制他们的网络使用。至于连接到自己网站服务器的网络代理,你可以用它来阻止一些最明显的连接通过。值得一用的好代理是 Squid。

另一个预防措施是安装 Tripwire。这是一款出色的最后一道防线应用程序。它的作用是对配置文件中指定的所有文件进行校验,然后每隔一段时间通过 cron 运行一次,检查所有指定文件是否与以前一样,或是否有非法更改。换句话说,这个程序可以查出是否有人真正入侵并篡改了系统。建议在所有网络服务器上运行这个程序。

最后需要注意的是,我们知道,遵循标准总是一件好事。正如你在 ICQ 的例子中看到的,如果不使用标准化系统,事情就会出大错。对于你自己的环境来说,这在某种程度上可以忽略不计,但如果你运行的是宽带服务或调制解调器池,这就变得更加重要了。通过你连接的用户必须始终能够依赖你的标准化,你不能指望每个人都运行你选择的特定操作系统。有些人想运行 Windows,有些人想运行 Linux 甚至 VMS 等等。如果你把安全建立在专有系统的基础上,你就会遇到一些麻烦。

瑞典出现的某些宽带服务就是一个很好的例子,它们将大量的安全性建立在微软网络登录的基础上。这听起来可能是个好主意,但一旦我们开始考虑其他操作系统等,这就不再是个好主意了。运行 Linux 的人如何上网?或者 VAX/VMS?或者 HP/UX?如果不是因为网络管理员拒绝任何运行 Linux 的人使用宽带服务,在这种情况下干脆将他们屏蔽掉,使用 Linux 当然可以做到。不过,本书并不是要从神学角度讨论什么是最好的,所以我们还是把它作为一个例子来说明为什么使用非标准是个坏主意吧。

第 4 章 网络地址转换简介

时至今日,NAT 似乎仍是 Linux 和 Iptables 的最大亮点之一。许多小公司和个人用户都选择使用这些解决方案,而不是使用相当昂贵的第三方解决方案,如 Cisco PIX 等。其中一个主要原因是它既便宜又安全。它需要一台旧电脑、一个可以从网上免费下载的相当新的 Linux 发行版、一两块备用网卡和电缆。

本章将介绍一些有关 NAT 的基本理论、NAT 的用途、工作原理以及在开始工作前应考虑的事项。

4.1. NAT 的用途及基本术语和表达式

基本上,NAT 允许一台或多台主机以某种方式共享同一个 IP 地址。例如,假设我们有一个由 5-10 个客户端组成的本地网络。我们将它们的默认网关设置为指向 NAT 服务器。通常情况下,数据包只会由网关机器转发,但对于 NAT 服务器来说,情况有点不同。

如前所述,NAT 服务器会将数据包的源地址和目标地址转换为不同的地址。NAT 服务器接收数据包,改写源地址或目标地址,然后重新计算数据包的校验和。NAT 最常用的功能之一是 SNAT(源网络地址转换)功能。基本上,在上述例子中,如果我们无法为每个客户提供一个真正的公共 IP,或看不到任何真正的内部网络细节,就会用到这个功能。在这种情况下,我们使用本地网络的一个私有 IP 范围(例如 192.168.1.0/24),然后打开本地网络的 SNAT。SNAT 会将所有 192.168.1.0 地址转换为自己的公网 IP(例如 217.115.95.34)。这样,就会有 5-10 个或更多的客户端使用同一个共享 IP 地址。

还有一种叫做 DNAT 的东西,它在建立服务器等方面非常有用。首先,在节省 IP 空间方面,你可以帮助更多的人。其次,在你的服务器和真正的服务器之间,你可以以一种简单的方式获得一个或多或少完全不可穿透的防火墙,或者简单地为几个服务器共享一个 IP,而这些服务器又被分隔成几个物理上不同的服务器。例如,我们可以在同一台机器上运行一个小型的公司服务器群,其中包含网络服务器和 ftp 服务器,同时还有一台物理上分开的机器,其中包含几个不同的聊天服务,供在家或在路上工作的员工用来与现场的员工保持联系。然后,我们可以通过 DNAT 从外部在同一 IP 上运行所有这些服务。

上面的示例也是基于单独的端口 NAT(通常称为 PNAT)。我们在本书中不会经常提到这一点,因为 netfilter 中的 DNAT 和 SNAT 功能已经涵盖了这一点。

在 Linux 中,实际上可以使用两种不同类型的 NAT:Fast-NAT 或 Netfilter-NAT。Fast-NAT 是在 Linux 内核的 IP 路由代码中实现的,而 Netfilter-NAT 也是在 Linux 内核中实现的,但在 netfilter 代码中。由于本书不会过多地涉及 IP 路由代码,因此除了一些注释外,我们将把它放在这里。Fast-NAT 之所以叫这个名字,是因为它比 netfilter NAT 代码快得多。它不跟踪连接,这是它的主要优点和缺点。连接跟踪需要耗费大量处理器功率,因此速度较慢,这也是 Fast-NAT 比 Netfilter-NAT 快的主要原因之一。我们也说过,Fast-NAT 的缺点是不能跟踪连接,这意味着它不能很好地对整个网络进行 SNAT,也不能对 FTP、IRC 等复杂协议进行 NAT,而 Netfilter-NAT 却能很好地处理这些协议。这是有可能实现的,但所需的工作要比 Netfilter 实现所需的工作多得多。

最后还有一个词基本上是 SNAT 的同义词,即 “伪装”(Masquerade)。在 Netfilter 中,“伪装 ”与 “SNAT ”基本相同,不同之处在于 “伪装 ”会自动将新的源 IP 设置为传出网络接口的默认 IP 地址。

4.2. 使用 NAT 的注意事项

正如我们已经在一定程度上解释过的,使用 NAT 有很多小的注意事项。主要问题是某些协议和应用程序可能根本无法运行。希望这些应用程序在你管理的网络中并不常见,在这种情况下,应该不会造成什么大问题。

第二个较小的问题是只能部分运行的应用程序和协议。这些协议比那些根本无法运行的协议更为常见,这是非常不幸的,但我们似乎对此无能为力。如果继续制定复杂的协议,我们将不得不继续面对这个问题。尤其是在协议没有标准化的情况下。

第三个问题,在我看来也是最大的问题,就是在 NAT 服务器后面上网的用户将无法运行自己的服务器。当然,这也是可以做到的,但需要花费更多的时间和精力来设置。在公司里,这可能比拥有大量由不同员工运行的服务器要好,因为这些服务器都可以从互联网上访问,而且没有任何监管。但是,对于家庭用户来说,这种情况应该尽量避免。作为互联网服务提供商,你绝不能将客户从专用 IP 范围 NAT 到公用 IP。这会给你带来更多麻烦,而且总会有这样或那样的客户希望这个或那个协议能完美运行。当协议不能正常工作时,你就会受到指责。

最后,关于 NAT 的注意事项,应该提到的是,NAT 实际上只是一种黑客手段。NAT 是在 IANA 和其他组织注意到互联网飞速发展、IP 地址即将短缺的情况下提出的一种解决方案。NAT 过去和现在都是解决 IPv4 问题的短期方案(是的,我们以前讨论过的 IP 是 IPv4 的简称,即互联网协议版本 4)。解决 IPv4 地址短缺问题的长期方案是 IPv6 协议,它还能解决大量其他问题。IPv6 的地址分配为 128 位,而 IPv4 的 IP 地址只有 32 位。地址空间的增加令人难以置信。为地球上的每个原子设置一个足够的 IP 地址似乎有些荒谬,但另一方面,也没有人预料到 IPv4 地址范围会太小。

4.3. 理论上的 NAT 机器示例

这是一个小的理论场景,我们希望在两个不同的网络和互联网连接之间建立一个 NAT 服务器。我们要做的是将 2 个网络相互连接起来,并且这 2 个网络都能访问对方和互联网。我们将讨论您应该考虑的硬件问题,以及在实际开始实施 NAT 机器之前应该考虑的其他理论问题。

4.3.1. 构建 NAT 机器需要什么

在进一步讨论之前,我们应该先了解一下构建一台执行 NAT 的 Linux 机器需要什么样的硬件。对于大多数小型网络来说,这应该不成问题,但如果你开始考虑大型网络,这实际上就成了一个问题。NAT 最大的问题在于它消耗资源的速度相当快。对于可能只有 1-10 个用户的小型专用网络来说,32 MB 内存的 486 处理器绰绰有余。但是,如果用户数量达到 100 个或更多,就应该开始考虑使用什么样的硬件了。当然,考虑带宽使用情况以及同时打开多少个连接也是一个好主意。不过,一般来说,备用电脑的性能会很好,这也是使用基于 Linux 的防火墙的一大优点。你可以使用剩下的废旧硬件,因此,与其他防火墙相比,防火墙将非常便宜。

您还需要考虑网卡。有多少个独立的网络将连接到您的 NAT/过滤设备?大多数情况下,只需将一个网络连接到互联网连接即可。如果通过以太网连接互联网,一般应配备 2 块以太网卡等。出于可扩展性考虑,最好选择品牌相对较好的 10/100 mbit/s 网卡,但只要 Linux 内核中有驱动程序,任何类型的网卡都可以使用。需要注意的是:避免使用或购买在 Linux 内核发行版中没有驱动程序的网卡。我曾多次发现,那些在光盘上单独发布驱动程序的网卡/品牌工作得很糟糕。它们一般都没有得到很好的维护,而且如果你一开始就让它们在你选择的内核上运行,那么它们在下一次主要的 Linux 内核升级中实际运行的可能性就非常小了。这在大多数情况下意味着你必须购买价格稍高的网卡,但最终还是值得的。

需要注意的是,如果要在非常老旧的硬件上建立防火墙,建议至少尽量使用 PCI 总线或更好的总线。首先,希望将来升级时可以使用网卡。此外,ISA 总线的运行速度极慢,对 CPU 的占用也很大。这就意味着,给 ISA 网卡增加大量负载几乎会导致机器瘫痪。

最后,还需要考虑的一点是 NAT/防火墙设备的内存容量。如果可能的话,最好至少安装 64 MB 以上的内存,即使 32 MB 内存也可以运行。NAT 的内存消耗并不是非常大,但为了以防万一,最好还是尽可能多地增加内存,以防流量超出预期。

正如您所看到的,在硬件方面需要考虑的事情还真不少。但老实说,在大多数情况下,你根本不需要考虑这些问题,除非你要为一个大型网络构建一个 NAT 机器。大多数家庭用户不需要考虑这些问题,他们可以或多或少地使用手头的任何硬件。在这方面没有完整的比较和测试,但只要稍加了解常识,就会有不错的结果。

4.3.2. 放置 NAT 机器

这看起来应该相当简单,但在大型网络中可能比你最初想象的要难。一般来说,NAT 机器应该和其他过滤机器一样,放置在网络的外围。当然,这在大多数情况下意味着 NAT 和过滤机器是同一台机器。同样值得考虑的是,如果你的网络非常庞大,不妨将网络分割成多个较小的网络,并为每个网络分配一台 NAT/过滤设备。由于 NAT 需要大量的处理能力,这无疑有助于减少往返时间(RTT,即数据包到达目的地和返回数据包所需的时间)。

在我们上面描述的网络示例中,有两个网络和一个互联网连接,换句话说,我们应该看看这两个网络有多大。如果我们可以认为这两个网络规模较小,并且根据客户的要求,在一台像样的 NAT 机器上连接几百个客户应该不成问题。否则,我们可以在较小的 NAT 机器上设置公共 IP,将负载分担到几台机器上,每台机器处理各自较小的网段,然后让流量集中到一台特定的路由机器上。当然,这需要考虑到必须为所有 NAT 机器提供足够的公网 IP,并通过路由器进行路由。

4.3.3. 如何放置代理

不幸的是,在大多数情况下,代理是 NAT 的一个普遍问题,尤其是透明代理。普通代理不会造成太大麻烦,但创建透明代理却很难,尤其是在较大的网络中。第一个问题是,代理和 NAT 一样需要大量的处理能力。如果要处理大量的网络流量,将这两样东西放在同一台机器上是不可取的。第二个问题是,如果对源 IP 和目的 IP 都进行 NAT,代理就无法知道要联系哪些主机。例如,客户端试图联系哪个服务器?在 NAT 转换过程中,所有这些信息都会丢失,因为如果数据包被 NAT 了,就不能再包含这些信息了,所以这是个问题。在本地,解决这个问题的方法是在为数据包创建的内部数据结构中添加这些信息,这样代理(如 squid)就能获取这些信息。

正如您所看到的,问题在于,如果您要运行透明代理,您没有太多选择。当然,也有一些可能性,但实际上并不可取。一种方法是在防火墙外创建一个代理,并创建一个路由条目,将所有网络流量路由到该机器,然后在代理机器上将数据包NAT到代理的适当端口。这样,信息就会一直保留到代理机上,而且在代理机上仍然可用。

第二种方法是在防火墙外创建一个代理,然后阻断所有网络流量,但前往代理的流量除外。这样,你就可以强制所有用户使用代理服务器。这种方法很粗糙,但希望能奏效。

4.3.4. NAT 机器的最后阶段

最后,我们应该将所有这些信息整合起来,看看如何解决 NAT 机器的问题。让我们来看看网络的图片及其外观。我们决定在上述 NAT/过滤设备的外侧放置一个代理,但要从路由器算起。从某种意义上说,这个区域可以算作 DMZ,NAT/过滤机是 DMZ 和两个公司网络之间的路由器。你可以在下图中看到我们讨论的具体布局。

LAN1       LAN2
  ↕️         ↕️
  NAT machine
       ↕️
      DMZ
       ↕️
     Router
       ↕️
    Internet

来自 NAT 网络的所有正常流量都将通过 DMZ 直接发送到路由器,再由路由器将流量发送到互联网。是的,你猜对了,网络流量除外,它会在 NAT 机器的净过滤部分做标记,然后根据标记路由到代理机器。让我们来看看我在说什么。假设 NAT 机器看到了一个 http 数据包。然后,可以使用 mangle 表给数据包打上 netfilter 标记(也称为 nfmark)。以后,当我们要把数据包路由到路由器时,我们就可以检查路由表中的 nfmark,并根据这个标记选择把 http 数据包路由到代理服务器。然后,代理服务器将对数据包进行处理。我们将在本书稍后部分涉及这些主题,尽管大部分基于路由的内容都是在高级路由主题中进行的。

NAT 机器以及路由器和互联网上的其他机器都有一个真实的 IP 地址。NAT 网络内的所有机器都将使用专用 IP,从而节省了大量现金和互联网地址空间。

第 5 章 准备工作

本章旨在帮助你入门,并帮助你了解 Netfilter 和 iptables 在当今 Linux 中扮演的角色。希望本章能让你做好准备,完成实验并安装防火墙。只要花点时间并坚持不懈,你就能让它完全按照你的要求运行。

5.1. 从哪里获取 iptables

iptables 用户空间软件包可从 http://www.netfilter.org/ 下载。iptables 软件包还使用了内核空间设施,这些设施可以在 make configure 时配置到内核中。本文稍后将讨论必要的步骤。

5.2. 内核设置

要运行纯粹的 iptables 基本功能,需要在执行 make config 或相关命令时在内核中配置以下选项:

CONFIG_PACKET - 该选项允许应用程序和实用程序直接使用各种网络设备。例如 tcpdump 或 snort。

注意:CONFIG_PACKET 严格来说并不是 iptables 工作所必需的,但由于它有很多用途,所以我选择将其包 含在这里。如果你不需要它,就不要包含它。

CONFIG_NETFILTER - 如果要将计算机用作防火墙或互联网网关,则必须使用此选项。换句话说,本教程中的任何内容都必须使用该选项。既然你正在阅读这篇文章,我想你也会需要这个选项。

当然,你还需要添加适当的驱动程序,以便你的接口(即以太网适配器、PPP 和 SLIP 接口)正常工作。以上只是在 iptables 中添加了一些纯粹的基本功能。老实说,你不能做任何有意义的事情,它只是为内核添加了一个框架。如果你想使用 Iptables 中更高级的选项,就需要在内核中设置适当的配置选项。在此,我们将向你展示在基本的 2.4.9 内核中可用的选项,并作简要解释:

CONFIG_IP_NF_CONTRACK - 该模块用于连接跟踪。连接跟踪主要用于 NAT 和伪装。如果需要对局域网内的机器进行防火墙,则一定要标记此选项。例如,rc.firewall.txt 脚本需要使用该模块。

CONFIG_IP_NF_FTP - 如果要对 FTP 连接进行连接跟踪,则需要使用此模块。由于 FTP 连接在一般情况下很难进行连接跟踪,conntrack 需要一个所谓的辅助工具;此选项可编译辅助工具。如果不添加此模块,就无法正常通过防火墙或网关进行 FTP 传输。

CONFIG_IP_NF_IPTABLES - 如果要进行任何过滤、伪装或 NAT,都需要使用该选项。它将整个 iptables 识别框架添加到内核中。没有这个选项,你根本无法使用 iptables 做任何事情。

CONFIG_IP_NF_MATCH_LIMIT - 这个模块并非必需,但在 rc.firewall.txt 示例中使用了它。该选项提供了 LIMIT 匹配功能,可以控制每分钟匹配多少个数据包,并由相应的规则加以控制。例如,-m limit –limit 3/minute 每分钟最多匹配 3 个数据包。该模块还可用于避免某些拒绝服务攻击。

CONFIG_IP_NF_MATCH_MAC - 它允许我们根据 MAC 地址来匹配数据包。每个以太网适配器都有自己的 MAC 地址。例如,我们可以根据使用的 MAC 地址来阻止数据包,并很好地阻止某台计算机,因为 MAC 地址很少改变。在 rc.firewall.txt 示例或其他任何地方,我们都不会使用这个选项。

CONFIG_IP_NF_MATCH_MARK - 允许我们使用 MARK 匹配。例如,如果使用目标 MARK,我们可以标记一个数据包,然后根据该数据包在表中是否被标记,我们可以根据此标记进行匹配。该选项是实际的匹配 MARK,下面我们将介绍实际的目标 MARK。

CONFIG_IP_NF_MATCH_MULTIPORT - 该模块允许我们匹配一系列目标端口或源端口的数据包。通常情况下这是不可能的,但有了这个匹配模块就可以了。

CONFIG_IP_NF_MATCH_TOS - 通过该匹配模块,我们可以根据数据包的 TOS 字段来匹配数据包。TOS 代表服务类型。TOS 也可以通过 mangle 表中的某些规则和 ip/tc 命令来设置。

CONFIG_IP_NF_MATCH_TCPMSS - 通过该选项,我们可以根据 TCP 数据包的 MSS 字段来匹配数据包。

CONFIG_IP_NF_MATCH_STATE - 与 ipchains 相比,这是最大的新特性之一。使用该模块,我们可以对数据包进行有状态匹配。例如,如果我们已经在 TCP 连接中看到两个方向的流量,则该数据包将被视为 ESTABLISHED。该模块在 rc.firewall.txt 示例中被广泛使用。

CONFIG_IP_NF_MATCH_UNCLEAN - 该模块可让我们匹配不符合类型或无效的 IP、TCP、UDP 和 ICMP 数据包。例如,我们可以丢弃这些数据包,但我们永远不知道它们是否合法。请注意,这种匹配方式仍处于试验阶段,可能无法在所有情况下都完美运行。

CONFIG_IP_NF_MATCH_OWNER - 该选项可让我们根据套接字的所有者进行匹配。例如,我们可以只允许 root 用户访问互联网。该模块最初只是作为新版 iptables 的一个示例。请注意,这种匹配方式仍处于试验阶段,可能并不适用于所有人。

CONFIG_IP_NF_FILTER - 该模块将添加基本过滤表,使你能够进行 IP 过滤。在过滤表中可以找到 INPUT、FORWARD 和 OUTPUT 链。如果计划对接收和发送的数据包进行任何形式的过滤,则需要使用此模块。

CONFIG_IP_NF_TARGET_REJECT - 该目标允许我们指定在回复接收到的数据包时发送 ICMP 错误信息,而不是直接将其丢弃。请记住,与 ICMP 和 UDP 不同,TCP 连接总是通过 TCP RST 数据包重置或拒绝。

CONFIG_IP_NF_TARGET_MIRROR - 允许将数据包弹回给数据包的发送方。例如,如果我们在 INPUT 链上的目标端口 HTTP 上设置了一个 MIRROR 目标,有人试图访问这个端口,我们就会将他的数据包弹回给他,最后他可能会看到自己的主页。

警告:不要轻易使用 MIRROR 目标。它最初是作为测试和示例模块构建的,很可能会对设置它的人造成极大的危险(如造成严重的 DDoS 等)。

CONFIG_IP_NF_NAT - 该模块允许不同形式的网络地址转换或 NAT。该选项允许我们访问 iptables 中的 NAT 表。如果要进行端口转发、伪装等操作,则需要使用该选项。需要注意的是,局域网的防火墙和伪装并不需要这个选项,但除非你能为所有主机提供唯一的 IP 地址,否则还是应该有这个选项。因此,rc.firewall.txt 脚本示例需要使用该选项才能正常工作,如果你没有能力添加上述指定的唯一 IP 地址,在你的网络中也肯定需要使用该选项。

CONFIG_IP_NF_TARGET_MASQUERADE - 该模块用于添加 MASQUERADE 目标。例如,如果我们不知道因特网的 IP 地址,这将是获取 IP 的首选方式,而不是使用 DNAT 或 SNAT。换句话说,如果我们使用 DHCP、PPP、SLIP 或其他为我们分配 IP 的连接,就需要使用该目标而不是 SNAT。伪装会给计算机带来比 NAT 稍高的负载,但在我们事先不知道 IP 地址的情况下也能正常工作。

CONFIG_IP_NF_TARGET_REDIRECT - 例如,该目标可与应用程序代理一起使用。我们不会让数据包直接通过,而是将其重新映射到本地服务器。换句话说,我们可以用这种方法制作透明代理。

CONFIG_IP_NF_TARGET_LOG - 为 iptables 添加 LOG 目标及其功能。我们可以使用该模块将某些数据包记录到 syslogd 中,从而查看数据包发生了什么。这对于安全审计、取证或调试正在编写的脚本都非常有用。

CONFIG_IP_NF_TARGET_TCPMSS - 该选项可用于对抗阻止 ICMP Fragmentation Needed 数据包的互联网服务提供商和服务器。这可能导致网页无法通过、小邮件能通过而大邮件不能、ssh 能正常工作而 scp 在握手后就死机等。这时,我们可以使用 TCPMSS 目标来解决这个问题,将我们的 MSS(最大分段大小)与 PMTU(路径最大传输单元)夹在一起。这样,我们就能处理 Netfilter 作者自己在内核配置帮助中称之为 “脑残 ISP 或服务器 ”的问题了。

CONFIG_IP_NF_COMPAT_IPCHAINS - 添加与过时的 ipchains 兼容的模式。不要把它当作从 Linux 2.2 内核迁移到 2.4 内核的长期解决方案,因为它很可能在 2.6 内核中消失。

CONFIG_IP_NF_COMPAT_IPFWADM - 与过时的 ipfwadm 兼容的模式。绝对不要把它当作真正的长期解决方案。

正如你所看到的,有很多选项。我在这里简要说明了每个模块会有哪些额外的行为。这些只是在 vanilla Linux 2.4.9 内核中可用的选项。如果你想了解更多选项,我建议你看看 Netfilter 用户区的 patch-o-matic (POM) 功能,它能在内核中添加大量其他选项。POM 修正是未来内核中应该添加的新功能,但目前还没有完全进入内核。这些功能应该在未来添加,但尚未完全进入内核。这可能有多种原因,例如补丁还不稳定,Linus Torvalds 无法跟上,或者由于补丁仍是实验性的,所以还不想让它进入主流内核。

要使 rc.firewall.txt 脚本正常工作,你需要在内核中编译以下选项,或将其作为模块。如果需要其他脚本所需的选项帮助,请参阅防火墙脚本示例部分。

CONFIG_PACKET
CONFIG_NETFILTER
CONFIG_IP_NF_CONNTRACK
CONFIG_IP_NF_FTP
CONFIG_IP_NF_IRC
CONFIG_IP_NF_IPTABLES
CONFIG_IP_NF_FILTER
CONFIG_IP_NF_NAT
CONFIG_IP_NF_MATCH_STATE
CONFIG_IP_NF_TARGET_LOG
CONFIG_IP_NF_MATCH_LIMIT
CONFIG_IP_NF_TARGET_MASQUERADE

至少 rc.firewall.txt 脚本需要上述内容。在其他示例脚本中,我将在各自的章节中解释它们的要求。现在,让我们把注意力集中在你现在应该学习的主脚本上。

5.3. 用户界面设置

首先,让我们看看如何编译 iptables 软件包。重要的是要认识到,在大多数情况下,iptables 的配置和编译与内核的配置和编译是同步进行的。某些发行版预装了 iptables 软件包,Red Hat 就是其中之一。不过,在 Red Hat 中,iptables 是默认禁用的。我们将在本章中进一步了解如何启用它,并进一步了解其他发行版。

5.3.1. 编译用户界面应用程序

首先解压 iptables 软件包。这里使用的是 iptables 1.2.6a 软件包和 vanilla 2.4 内核。像往常一样解压,使用 bzip2 -cd iptables-1.2.6a.tar.bz2 | tar -xvf -(也可以使用 tar -xjvf iptables-1.2.6a.tar.bz2,与第一条命令基本相同。不过,旧版本的 tar 可能无法使用)。现在软件包应该已经解压到名为 iptables-1.2.6a 的目录中。更多信息请阅读 iptables-1.2.6a/INSTALL 文件,其中包含编译和运行程序的详细信息。

在这之后,你可以选择配置和安装内核的额外模块和选项等。这里描述的步骤只会检查和安装待加入内核的标准补丁,还有一些更实验性的补丁,可能只有在你执行其他步骤时才能获得。

注意:其中有些补丁是高度试验性的,安装它们可能不是个好主意。不过,在这个安装步骤中,有很多非常有趣的匹配和目标,所以不要害怕,至少可以看看。

要执行这一步,我们可以在 iptables 软件包的根目录下这样做:

make pending-patches KERNEL_DIR=/usr/src/linux/

变量 KERNEL_DIR 应指向内核源代码的实际位置。通常应该是 /usr/src/linux/,但这可能会有所不同,而且很可能你自己就知道内核源码在哪里。

上述命令只询问某些即将进入内核的补丁。Netfilter 开发人员可能会在内核中添加更多补丁,但距离真正实现还有些距离。安装这些补丁的一种方法如下:

make most-of-pom KERNEL_DIR=/usr/src/linux/

上述命令会询问是否安装 Netfilter 中所谓的 patch-o-matic 的部分内容,但仍会跳过可能对内核造成严重破坏的最极端补丁。注意,我们说 “询问”,是因为这些命令实际上就是这么做的。它们会在内核源代码有任何改动之前询问你。要安装所有 patch-o-matic 程序,你需要运行以下命令:

make patch-o-matic KERNEL_DIR=/usr/src/linux/

在执行任何操作之前,别忘了仔细阅读每个补丁的帮助。有些补丁会破坏其他补丁,而有些补丁如果与 patch-o-matic 中的某些补丁一起使用,则可能会破坏内核。

注意:如果不想给内核打补丁,可以完全忽略上述步骤,换句话说,没有必要做上述操作。不过,patch-o-matic 中有一些非常有趣的东西,你可能会想看看,所以运行这些命令看看其中的内容也没什么不好。

完成补丁程序安装后,你就可以利用添加到源代码中的新补丁编译新内核了。不要忘记重新配置内核,因为新补丁很可能没有添加到配置选项中。如果你愿意,可以等到编译完用户态程序 iptables 后再编译内核。

继续编译 iptables 用户版程序。编译 iptables 的命令如下

make KERNEL_DIR=/usr/src/linux

现在,用户地应用程序应该可以正常编译了。如果不能,那就只能靠你自己了,或者你也可以订阅 Netfilter 邮件列表,在那里你可以寻求帮助来解决你的问题。安装 iptables 时可能会出现一些问题,所以如果无法正常工作也不要惊慌。试着用逻辑思维找出问题所在,或者找人帮忙。

如果一切顺利,现在就可以安装二进制文件了。为此,你可以发出以下命令来安装它们:

make install KERNEL_DIR=/usr/src/linux

希望现在程序中一切正常。要使用 iptables 用户区应用程序中的任何更改,如果之前没有重新编译和安装内核和模块,现在应该重新安装。有关从源代码安装用户态应用程序的更多信息,请查看源代码中的 INSTALL 文件,其中包含有关安装的更多信息。

5.3.2. 在 Red Hat 7.1 上安装

本小节有兴趣请查看原文

第 6 章 遍历表和链

在本章中,我们将讨论数据包如何遍历不同的链,以及遍历的顺序。我们还将讨论遍历表的顺序。稍后,当我们编写自己的特定规则时,我们将看到这一点的价值。我们还将了解同样依赖于内核的某些其他组件的作用点。也就是说,不同的路由决定等。如果我们要编写能改变数据包路由模式/规则的 iptables 规则,即数据包为什么会被路由以及如何被路由,这一点就尤为必要,DNAT 和 SNAT 就是很好的例子。当然,不能忘记的还有 TOS 位。

6.1. 基本流程

当一个数据包首次进入防火墙时,它会到达硬件,然后传递给内核中的适当设备驱动程序。然后,数据包开始在内核中经过一系列步骤,最后要么被发送到正确的应用程序(本地),要么被转发到另一台主机,或者发生任何情况。

首先,让我们看看一个发送到本地主机的数据包。该数据包在实际发送到接收它的应用程序之前,会经过以下步骤:

表 6-1. 目标本地主机(我们自己的机器)

StepTableChainComment
1在线路上(例如 Internet)
2从接口进入(如 eth0)
3manglePREROUTING该链通常用于混淆数据包,即更改 TOS 等。这也是进行连接跟踪的地方,我们将在状态机一章中讨论。
4natPREROUTING此链主要用于 DNAT。避免在此链中进行过滤,因为在某些情况下它会被绕过。
5路由决策,即数据包是发送给本地主机还是转发,以及转发到哪里。
6mangleINPUT此时,篡改 INPUT 链被触发。在数据包路由后,但实际发送到机器进程前,我们使用该链对数据包进行篡改。
7filterINPUT在这里,我们对所有以本地主机为目的地的传入流量进行过滤。请注意,所有以本机主机为目的地的传入数据包都会通过此链,无论它们来自哪个接口或哪个方向。
8本地进程/应用程序(即服务器/客户端程序)

请注意,这次数据包通过的是 INPUT 链,而不是 FORWARD 链。非常符合逻辑。在你的眼中,可能只有表和链的遍历在开始时才是真正合乎逻辑的,但如果你继续思考,你会发现它会随着时间的推移变得越来越清晰。

现在我们来看看从本地主机发出的数据包以及它们所经过的步骤。

表 6-2. 源本地主机(我们自己的机器)

StepTableChainComment
1本地进程/应用程序(即服务器/客户端程序)
2路由选择决策。使用哪个源地址、哪个出站接口,以及其他需要收集的必要信息。
3mangleOUTPUT这就是我们混淆数据包的地方,建议不要在此链中进行过滤,因为这会产生副作用。这也是本地生成连接跟踪的地方,我们将在状态机 章节中讨论。
4natOUTPUT该链可用于从防火墙本身对传出数据包进行 NAT。
5路由决定,因为之前的 mangle 和 nat 更改可能已经改变了数据包的路由方式。
6filterOUTPUT我们在这里过滤从本地主机发出的数据包。
7manglePOSTROUTING当我们想在数据包离开主机之前、但在实际路由决策之后对其进行篡改时,主要使用篡改表中的 POSTROUTING 链。无论是穿越防火墙的数据包,还是防火墙本身创建的数据包,都会碰到这个链。
8natPOSTROUTING这就是前面所述的 SNAT。建议不要在这里进行过滤,因为这会产生副作用,即使设置了DROP 的默认策略,某些数据包也可能会漏掉。
9从某个接口发出(如 eth0)
10在线路上(例如 Internet)

在本例中,我们假设数据包的目的地是另一个网络上的另一台主机。数据包按以下方式经过不同的步骤:

表 6-3. 转发的数据包

StepTableChainComment
1在线路上(例如 Internet)
2从接口进入(如 eth0)
3manglePREROUTING该链通常用于混淆数据包,即更改 TOS 等。这也是非本地生成的连接跟踪发生的地方,我们将在状态机 章节中讨论。
4natPREROUTING该链主要用于 DNAT。SNAT 在后面进行。避免在此链中进行过滤,因为在某些情况下它会被绕过。
5路由决策,即数据包是发送给本地主机还是转发,以及转发到哪里。
6mangleFORWARD然后,数据包将被发送到混合表的 FORWARD 链。这可以用于非常特殊的需求,即我们希望在初始路由决定之后,但在数据包发送前的最后一次路由决定之前,对数据包进行纠错。
7filterFORWARD数据包被路由到 FORWARD 链上。只有转发的数据包才会经过这里,我们在这里进行所有过滤。请注意,所有转发的流量都会经过这里(而不仅仅是一个方向),因此在编写规则集时需要考虑到这一点。
8manglePOSTROUTING该链用于特定类型的数据包处理,我们希望在完成各种路由决策后再进行处理,但仍在这台机器上进行。
9natPOSTROUTING此链应首先用于 SNAT。避免在此进行过滤,因为某些数据包可能会通过此链,而不会碰到它。这也是进行伪装的地方。
10在输出接口上输出。(如 eth1)
11再次通过线路(如 LAN)

正如你所看到的,有很多步骤需要通过。数据包可以在任何一个 iptables 链上被拦截,如果是畸形数据包,也可以在其他地方被拦截;不过,我们主要关注的是其中的 iptables 部分。请注意,没有针对不同接口或类似接口的特定链或表。所有通过该防火墙/路由器转发的数据包都会通过 FORWARD。

注意:在前面的情况中,不要使用 INPUT 链进行过滤!INPUT 仅用于转发到本地主机的数据包,不会被路由到任何其他目的地。

现在我们已经看到了在三种不同的情况下如何穿越不同的链。如果我们要绘制出一张很好的图,它应该是这样的:

tables_traverse.jpg (647×1100) (frozentux.net)

为了使流程更加清晰,请考虑一下这个问题。如果我们在第一个路由决策中得到一个数据包,而该数据包的目的地不是本机,那么它将通过 FORWARD 链路由。另一方面,如果数据包的目的地是本地计算机正在监听的 IP 地址,我们就会通过 INPUT 链将数据包发送到本地计算机。

同样值得注意的是,数据包的目的地可能是本地计算机,但在 PREROUTING 链中可以通过 NAT 更改目标地址。由于这发生在第一次路由决策之前,因此数据包将在更改后被查看。因此,路由选择可能会在路由选择决定之前发生改变。请注意,在此图中,所有数据包都将通过其中一条或另一条路径。如果你将一个数据包 DNAT 回它来自的同一个网络,它仍会通过其余的链路,直到回到网络上。

提示:如果您想了解更多信息,可以使用 rc.test-iptables.txt 脚本。该测试脚本会提供必要的规则,以测试表和链是如何遍历的。

6.2. mangle 表

正如我们已经指出的,该表主要用于混淆数据包。换句话说,您可以自由使用该表中的篡改目标来更改 TOS(服务类型)字段等。

注意事:强烈建议不要将此表用于任何过滤;任何 DNAT、SNAT 或 Masquerading 在此表中也不起作用。

以下目标仅在 mangle 表中有效。它们不能在混合表之外使用。

  1. TOS
  2. TTL
  3. MARK

TOS 目标用于设置或更改数据包中的服务类型字段。这可用于在网络上设置有关数据包路由方式等的策略。请注意,这一点尚未完善,也没有在互联网上真正实现,大多数路由器并不关心该字段的值,有时还会对所获得的信息采取错误的行动。换句话说,除非你想使用 iproute2 对前往互联网的数据包进行路由决策,否则不要设置这个值。

TTL 目标用于更改数据包的 TTL(生存时间)字段。我们可以让数据包只具有特定的 TTL,等等。这样做的一个很好的理由是,我们不想让爱管闲事的互联网服务提供商发现自己。有些互联网服务提供商不喜欢用户在单个连接上运行多台计算机,有些互联网服务提供商会发现单个主机产生不同的 TTL 值,并将其视为单个连接上连接了多台计算机的众多迹象之一。

MARK 目标用于为数据包设置特殊标记值。然后,iproute2 程序可以识别这些标记,并根据数据包的标记或没有标记的数据包对其进行不同的路由选择。我们还可以根据这些标记进行带宽限制和基于类别的队列(Class Based Queuing)。

6.3. NAT 表

该表只能用于不同数据包的 NAT(网络地址转换)。换句话说,它只能用来翻译数据包的源字段或目标字段。请注意,如前所述,只有数据流中的第一个数据包才会使用该表。之后,其余数据包将自动执行与第一个数据包相同的操作。实际执行这些操作的目标是

  1. DNAT
  2. SNAT
  3. MASQUERADE
  4. REDIRECT

DNAT 目标主要用于拥有公共 IP 并希望将防火墙的访问重定向到其他主机(例如 DMZ 上的主机)的情况。换句话说,我们要更改数据包的目标地址并将其重定向到主机。

SNAT 主要用于更改数据包的源地址。在大多数情况下,你会隐藏本地网络或 DMZ 等。一个很好的例子是,我们知道防火墙的外部 IP 地址,但需要用防火墙的 IP 地址代替本地网络的 IP 地址。有了这个目标,防火墙就会自动对数据包进行 SNAT 和 De-SNAT,从而实现从局域网到互联网的连接。例如,如果您的网络使用 192.168.0.0/netmask,数据包将永远无法从互联网返回,因为 IANA 已将这些网络(以及其他网络)规定为专用网络,只能在隔离的局域网中使用。

MASQUERADE 目标的使用方法与 SNAT 完全相同,但 MASQUERADE 目标的计算开销稍大。原因是 MASQUERADE 目标每次被数据包击中时,都会自动检查要使用的 IP 地址,而不是像 SNAT 目标那样只使用单个配置的 IP 地址。通过 MASQUERADE 目标器,您可以正常使用 ISP 为您的 PPP、PPPoE 或 SLIP 互联网连接提供的动态 DHCP IP 地址。

6.4. filter 表

filter 表主要用于过滤数据包。我们可以匹配数据包,并以任何方式过滤它们。在这里,我们会对数据包采取实际行动,查看数据包的内容,并根据其内容对其进行 DROP/ACCEPT 处理。当然,我们也可以进行事先过滤;不过,这个特定的表是设计用于过滤的地方。几乎所有目标都可以在这个表中使用。我们将在这里详细介绍过滤表;不过,你现在已经知道,这个表是进行主要过滤的正确位置。

第 7 章 状态机

本章将详细介绍状态机。通读本章后,你将对状态机的工作原理有一个全面的了解。我们还将通过大量示例介绍状态机本身是如何处理状态的。在实际应用中,这些示例应能阐明一切。

7.1. 简介

状态机是 iptables 中的一个特殊部分,其实根本不应该叫状态机,因为它实际上是一个连接跟踪器。不过,大多数人还是用第一个名称来称呼它。在本章中,我或多或少会把这些名称当作同义词来使用。这应该不会造成太大的混淆。连接跟踪是为了让 Netfilter 框架了解特定连接的状态。实现这种功能的防火墙一般称为有状态防火墙。有状态防火墙通常比无状态防火墙更安全,因为它允许我们编写更严密的规则集。

在 iptables 中,数据包可以与处于四种不同状态的跟踪连接相关联。这四种状态分别是新状态(NEW)、已建立状态(ESTABLISHED)、相关状态(RELATED)和无效状态(INVALID)。稍后我们将对每种状态进行更深入的讨论。通过 –state 匹配,我们可以轻松控制允许谁或什么启动新会话。

所有的连接跟踪都是由内核中名为 conntrack 的特殊框架完成的。conntrack 可以作为模块加载,也可以作为内核本身的内部部分加载。大多数情况下,我们需要并希望获得比默认 conntrack 引擎更具体的连接跟踪功能。因此,conntrack 也有更具体的部分来处理 TCP、UDP 或 ICMP 协议等。这些模块从数据包中获取特定、唯一的信息,以便跟踪每个数据流。然后,conntrack 收集到的信息会被用来告诉 conntrack 数据流当前所处的状态。例如,UDP 数据流通常由其目标 IP 地址、源 IP 地址、目标端口和源端口唯一标识。

在以前的内核中,我们可以打开或关闭碎片整理。但自从引入了 iptables 和 Netfilter,特别是连接跟踪功能后,这个选项就被取消了。原因在于,如果不对数据包进行碎片整理,连接跟踪就无法正常工作,因此碎片整理已被纳入 conntrack 并自动执行。除非关闭连接跟踪,否则无法将其关闭。如果连接跟踪功能开启,碎片整理就会一直进行。

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

7.2. conntrack 条目

让我们简要了解一下 conntrack 条目以及如何在 /proc/net/ip_conntrack 中读取它们。这将提供 conntrack 数据库中所有当前条目的列表。如果加载了 ip_conntrack 模块,/proc/net/ip_conntrack 的 cat 可能如下所示:

tcp      6 117 SYN_SENT src=192.168.1.6 dst=192.168.1.9 sport=32775 \
     dport=22 [UNREPLIED] src=192.168.1.9 dst=192.168.1.6 sport=22 \
     dport=32775 [ASSURED] use=2

该示例包含了 conntrack 模块为了解特定连接的状态而维护的所有信息。首先是协议,本例中是 tcp。接下来是以普通十进制编码表示的相同值。然后,我们可以看到该 conntrack 条目的存活时间。该值现在设置为 117 秒,并定期递减,直到我们看到更多流量。然后,该值会重置为该时间点所处特定状态的默认值。接下来是该条目在当前时间点的实际状态。在上述情况中,我们看到的是处于 SYN_SENT 状态的数据包。连接的内部值与 iptables 的外部值略有不同。SYN_SENT 值告诉我们,我们正在查看的连接只在一个方向上看到过一个 TCP SYN 数据包。接下来,我们会看到源 IP 地址、目的 IP 地址、源端口和目的端口。此时,我们会看到一个特定的关键字,它告诉我们这个连接没有返回流量。最后,我们可以看到返回数据包的预期信息。这些信息详细说明了源 IP 地址和目标 IP 地址(两者都是反向的,因为数据包将被导向回我们)。连接的源端口和目的端口也是如此。我们应该对这些值感兴趣。

连接跟踪条目可能具有一系列不同的值,所有这些值都在 linux/include/netfilter-ipv4/ip_conntrack*.h 文件中的 conntrack 头文件中指定。这些值取决于我们使用的 IP 子协议。TCP、UDP 或 ICMP 协议使用特定的默认值,这些值在 linux/include/netfilter-ipv4/ip_conntrack.h 文件中指定。此外,根据该状态的变化,连接被销毁前的默认时间值也会发生变化。

注意:最近,iptables patch-o-matic 中出现了一个名为 tcp-window-tracking 的新补丁。除其他外,该补丁将上述所有超时添加到特殊的 sysctl 变量中,这意味着可以在系统运行时即时更改超时。因此,每次要更改超时时,都无需重新编译内核。

可以通过使用 /proc/sys/net/ipv4/netfilter 目录中的特定系统调用来更改超时。您尤其应该查看 /proc/sys/net/ipv4/netfilter/ip_ct_* 变量。

当一个连接在两个方向上都有流量时,conntrack 条目会删除[UNREPLIED]标志,然后重置它。告诉我们该连接在两个方向上都没有流量的条目将被[ASSURED]标记所取代,[ASSURED]标记位于条目末尾。[ASSURED]标志告诉我们,该连接是有保证的,如果达到最大可能的跟踪连接数,该连接将不会被删除。因此,标记为[ASSURED]的连接不会被删除,这与非保证连接(未标记为[ASSURED]的连接)相反。连接跟踪表可以容纳多少个连接取决于一个变量,可以通过最近内核中的 ip-sysctl 函数进行设置。该变量的默认值因内存容量不同而有很大差异。如果内存为 128 MB,则可能有 8192 个条目;如果内存为 256 MB,则可能有 16376 个条目。您可以通过 /proc/sys/net/ipv4/ip_conntrack_max 设置读取并设置您的设置。

/proc/sys/net/nf_conntrack_max

另一种更有效的方法是在加载 ip_conntrack 模块后,设置模块的 hashsize 选项。正常情况下,ip_conntrack_max 等于 8 * hashsize。换句话说,将 hashsize 设置为 4096 将导致 ip_conntrack_max 设置为 32768 个 conntrack 条目。举例如下

# modprobe ip_conntrack hashsize=4096
# cat /proc/sys/net/ipv4/ip_conntrack_max
32768

7.3. 用户态状态

正如您所看到的,数据包在内核内可能会有几种不同的状态,这取决于我们所讨论的协议。但是,在内核外部,我们只有前面描述的 4 种状态。这些状态主要与状态匹配结合使用,后者可以根据数据包当前的连接跟踪状态对其进行匹配。有效状态包括新连接(NEW)、已建立连接(ESTABLISHED)、相关连接(RELATED)和无效连接(INVALID)。下表将简要说明每种可能的状态。

表 7-1. 用户态状态

StateExplanation
NEWNEW 状态表示该数据包是我们看到的第一个数据包。这意味着,conntrack 模块在特定连接中看到的第一个数据包将被匹配。例如,如果我们看到一个 SYN 数据包,而且它是我们看到的连接中的第一个数据包,那么它就会被匹配。不过,该数据包也可能不是 SYN 数据包,但仍被视为 NEW。在某些情况下,这可能会导致某些问题,但当我们需要从其他防火墙获取丢失的连接时,或者当连接已经超时但实际上并未关闭时,这也可能非常有用。
ESTABLISHEDESTABLISHED状态下,两个方向都有流量,因此会持续匹配这些数据包。ESTABLISHED 连接非常容易理解。进入ESTABLISHED状态的唯一要求是一台主机发送一个数据包,随后收到另一台主机的回复。在收到发送到防火墙或通过防火墙发送的回复数据包后,NEW状态将变为ESTABLISHED状态。如果我们创建了一个数据包,而该数据包又产生了 ICMP 回复信息,那么 ICMP 回复信息也可被视为ESTABLISHED
RELATEDRELATED状态是比较棘手的状态之一。当一个连接与另一个已ESTABLISHED的连接相关时,该连接就被视为RELATED。这意味着,要将一个连接视为RELATED,我们必须先有一个被视为ESTABLISHED的连接。然后,ESTABLISHED连接会在主连接之外产生一个连接。如果 conntrack 模块能够理解该连接是RELATED,那么新生成的连接将被视为RELATED。可被视为RELATED的连接有:被视为RELATED的 FTP 控制端口的 FTP 数据连接,以及通过 IRC 发送的 DCC 连接。这可用于允许 ICMP 错误信息、FTP 传输和 DCC 通过防火墙正常工作。请注意,依赖于这种机制的大多数 TCP 协议和某些 UDP 协议都相当复杂,会在 TCP 或 UDP 数据段的有效载荷中发送连接信息,因此需要特殊的辅助模块才能正确理解。
INVALIDINVALID 状态表示无法识别数据包或数据包没有任何状态。这可能有多种原因,如系统内存不足或 ICMP 错误信息不响应任何已知连接。一般来说,最好DROP处于这种状态的所有内容。

这些状态可与 --state match 配合使用,根据数据包的连接跟踪状态进行匹配。这也是状态机对于我们的防火墙来说如此强大和高效的原因。以前,我们经常需要打开所有高于 1024 的端口,让所有流量重新回到本地网络。有了状态机后,就不再需要这样做了,因为我们现在只需为返回流量打开防火墙,而无需为其他各种流量打开防火墙。

7.4. TCP 连接

在本节和接下来的章节中,我们将仔细研究 TCP、UDP 和 ICMP 这三种基本协议的状态及其处理方式。此外,如果连接不能归类为这三种协议中的任何一种,我们还将仔细研究默认情况下如何处理连接。我们选择从 TCP 协议开始,因为它本身就是一个有状态的协议,在 iptables 的状态机方面有很多有趣的细节。

TCP 连接总是通过三次握手开始的,三次握手建立并协商发送数据的实际连接。整个会话以 SYN 数据包开始,然后是 SYN/ACK 数据包,最后是确认整个会话建立的 ACK 数据包。至此,连接建立并开始发送数据。最大的问题是,连接跟踪如何与之挂钩?其实很简单。

就用户而言,连接跟踪对所有连接类型的工作原理基本相同。请看下图,了解在连接的不同阶段,数据流进入的具体状态。正如你所看到的,从用户的角度来看,连接跟踪代码并没有真正跟踪 TCP 连接的流程。一旦看到一个数据包(SYN),它就会认为连接是新的。一旦看到返回数据包(SYN/ACK),它就会认为连接已建立。仔细想一想,你就会明白其中的原因。通过这种特殊的实现方式,你可以允许新数据包和 ESTABLISHED 数据包离开本地网络,只允许 ESTABLISHED 连接返回,这样就可以完美地工作了。相反,如果连接跟踪机器将整个连接建立过程视为新连接,我们就永远无法阻止外部连接进入本地网络,因为我们必须允许新数据包再次进入。让事情变得更复杂的是,内核中还有其他一些用于 TCP 连接的内部状态,但我们在用户环境中却无法使用。它们大致遵循 RFC 793 - 传输控制协议(第 21-23 页)中规定的状态标准。我们将在本节中进一步详细介绍。

state-tcp-connecting.jpg (720×316) (frozentux.net)

如你所见,从用户的角度来看,这其实非常简单。不过,从内核的角度来看,整个结构就有点困难了。我们来看一个例子。看看 /proc/net/ip_conntrack 表中的连接状态到底是如何变化的。第一个状态是在收到连接中的第一个 SYN 数据包时报告的。

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 数据包已发送(SYN_SENT 标志已设置),但尚未收到回复([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_RECV 告诉我们,原始 SYN 已正确发送,SYN/ACK 返回数据包也正确通过了防火墙。此外,这个连接跟踪条目现在已经看到了两个方向的流量,因此被视为已得到回复。这不是明确的,而是假设的,就像上面的 [UNREPLIED] 标记一样。一旦我们看到三方握手中的最后一个 ACK,就完成了最后一步。

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 [ASSURED] use=1

在上一个例子中,我们在三方握手过程中得到了最后一个 ACK,连接进入了ESTABLISHED(已建立)状态,这是 iptables 的内部机制所知道的。正常情况下,此时数据流已被保证(ASSURED)。

连接也可能进入ESTABLISHED(已建立)状态,但不是[ASSURED]。如果我们开启了连接拾取功能(需要使用 tcp-window-tracking 补丁,并将 ip_conntrack_tcp_loose 设为 1 或更高),就会出现这种情况。如果不使用 tcp-window-tracking 补丁,默认情况下就会出现这种行为,且不可更改。

关闭 TCP 连接时,会以下列方式进行,并呈现下列状态。

state-tcp-closing.jpg (720×465) (frozentux.net)

如您所见,在发送最后一个 ACK 之前,连接从未真正关闭。请注意,这张图描述的只是正常情况下的关闭方式。例如,如果连接被拒绝,也可以通过发送 RST(重置)来关闭连接。在这种情况下,连接会立即关闭。

关闭 TCP 连接后,连接将进入 TIME_WAIT 状态,默认设置为 2 分钟。这样,即使连接已经关闭,所有出错的数据包仍能通过我们的规则集。这可以作为一种缓冲时间,让卡在一个或另一个拥塞路由器中的数据包仍能到达防火墙或连接的另一端。

如果连接被 RST 数据包重置,状态将变为关闭。这意味着默认情况下,连接在 10 秒后才会关闭。RST 数据包在任何意义上都不会被确认,而且会直接中断连接。除了上述状态外,还有其他状态。下面是 TCP 流可能出现的状态及其超时值的完整列表。

表 7-2. 内部状态

StateTimeout value
NONE30 minutes
ESTABLISHED5 days
SYN_SENT2 minutes
SYN_RECV60 seconds
FIN_WAIT2 minutes
TIME_WAIT2 minutes
CLOSE10 seconds
CLOSE_WAIT12 hours
LAST_ACK30 seconds
LISTEN>2 minutes

这些值绝对不是绝对值。它们可能会随着内核版本的修改而改变,也可能通过 proc 文件系统中的 /proc/sys/net/ipv4/netfilter/ip_ct_tcp_* 变量进行修改。不过,默认值在实际应用中应该是比较固定的。这些值以秒为单位。早期版本的补丁使用的是 jiffies(这是一个错误)。

注意:还要注意的是,状态机的用户端不会查看 TCP 数据包中设置的 TCP 标志(即 RST、ACK 和 SYN 标志)。这通常是不好的,因为你可能希望允许处于 NEW 状态的数据包通过防火墙,但当你指定 NEW 标志时,在大多数情况下你指的是 SYN 数据包。

而在当前状态下,这种情况不会发生;相反,即使是没有设置位或 ACK 标志的数据包,也会被算作新数据包。这可以用于冗余防火墙等,但对于只有一个防火墙的家庭网络来说,这通常是非常糟糕的。要避免这种情况,可以使用常见问题附录中 “状态为 NEW 数据包但未设置 SYN 位 ”一节中解释的命令。另一种方法是安装 patch-o-matic 的 tcp-window-tracking 扩展,并将 /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_loose 设置为零,这样防火墙就会丢弃所有只设置了 SYN 标志的新数据包。

7.5. UDP 连接

UDP 连接本身不是有状态连接,而是无状态连接。这有几个原因,主要是因为它们不包含任何连接建立或连接关闭;最重要的是,它们缺乏排序。以特定顺序接收两个 UDP 数据报并不能说明它们的发送顺序。不过,我们仍然可以在内核中为连接设置状态。让我们来看看如何跟踪连接以及连接在 conntrack 中的状态。

state-udp-connection.jpg (720×316) (frozentux.net)

如您所见,连接的启动方式与 TCP 连接几乎完全相同。也就是说,从用户界面的角度来看。从内部看,conntrack 信息有很大不同,但本质上细节是一样的。首先,我们来看看发送初始 UDP 数据包后的条目。

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 数据包。第一个值是协议名称,第二个值是协议编号。这与 TCP 连接相同。第三个值表示该状态条目还能存活多少秒。在此之后,我们会得到我们已经看到的数据包的值,以及通过此连接从发起数据包的发送方到达我们这里的数据包的未来预期。这些值包括源、目的、源端口和目的端口。此时,[UNREPLIED] 标志会告诉我们,到目前为止,数据包还没有得到任何回应。最后,我们会得到一个简短的列表,列出对返回数据包的期望。请注意,后面的条目与前面的值顺序相反。此时的超时时间默认设置为 30 秒。

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

此时,服务器已看到对发出的第一个数据包的回复,连接被视为已建立(ESTABLISHED)。如您所见,连接跟踪中并没有显示这一点。主要区别在于 [UNREPLIED] 标记已消失。此外,默认超时时间已变为 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

此时,连接已得到保证。该连接与上一个示例完全相同。如果 180 秒内未使用该连接,连接就会超时。180 秒是一个相对较低的值,但在大多数情况下已经足够。对于每个匹配相同条目并通过防火墙的数据包,该值都会重置为完整值,与所有内部状态相同。

7.6. ICMP 连接

ICMP 数据包远非有状态数据流,因为它们只用于控制,而不应建立任何连接。不过,有四种 ICMP 类型会生成返回数据包,它们有两种不同的状态。这些 ICMP 报文有新状态和建立状态。我们所说的 ICMP 类型包括回送请求和回复、时间戳请求和回复、信息请求和回复以及地址掩码请求和回复。在这些类型中,时间戳请求和信息请求已经过时,很可能会被放弃。不过,Echo 信息在一些设置中使用,如 ping 主机。地址掩码请求并不常用,但有时很有用,值得允许。要了解这种情况,请看下图。

state-icmp-ping.jpg (720×335) (frozentux.net)

如上图所示,主机向目标发送 echo 请求,防火墙将其视为 NEW。然后,目标发送 echo 回复,防火墙将其视为 ESTABLISHED 状态。当看到第一个 echo 请求时,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

如图所示,该条目与 TCP 和 UDP 的标准状态略有不同。协议、超时以及源地址和目标地址都在这里。但问题也随之而来。我们现在有三个新字段,分别称为类型、代码和 id。它们没有任何特殊之处,类型字段包含 ICMP 类型,代码字段包含 ICMP 代码。这些信息都可在 ICMP 类型附录中找到。最后一个 id 字段包含 ICMP ID。每个 ICMP 数据包在发送时都会设置一个 ID,当接收方收到 ICMP 报文时,就会在新的 ICMP 报文中设置相同的 ID,这样发送方就能识别回复,并能将其与正确的 ICMP 请求连接起来。

下一个字段,我们又一次看到了我们之前看到过的 [UNREPLIED] 标志。和以前一样,这个标志告诉我们,我们目前看到的连接跟踪条目只看到了一个方向的流量。最后,我们可以看到回复 ICMP 数据包的回复期望值,它是原始源 IP 地址和目标 IP 地址的反转。至于类型和代码,这些都被改为返回数据包的正确值,因此 echo request 被改为 echo reply,以此类推。ICMP ID 保留在请求数据包中。

如前所述,回复数据包被视为已建立。不过,我们可以肯定,在 ICMP 回复之后,同一连接中就绝对不会再有合法流量了。因此,一旦回复通过 Netfilter 结构,连接跟踪条目就会被销毁。

在上述每种情况下,请求都被视为 “新”(NEW),而回复则被视为 “已建立”(ESTABLISHED)。让我们更仔细地考虑一下这个问题。当防火墙看到一个请求数据包时,它会将其视为新数据包。当主机向请求发送回复数据包时,它被视为 ESTABLISHED。

注意:请注意,这意味着回复数据包必须符合连接跟踪条目给出的标准,才能被视为已建立,就像所有其他流量类型一样。

ICMP 请求的默认超时时间为 30 秒,您可以在 /proc/sys/net/ipv4/netfilter/ip_ct_icmp_timeout 项中更改超时时间。一般来说,这应该是一个不错的超时值,因为它可以捕获大多数传输中的数据包。

ICMP 的另一个非常重要的部分是,它用来告诉主机特定 UDP 和 TCP 连接或连接尝试的情况。由于这个简单的原因,ICMP 的回复通常被认为是对原始连接或连接尝试的 RELATED。一个简单的例子就是 ICMP Host unreachable 或 ICMP Network unreachable。如果我们的主机尝试与其他主机进行不成功的连接,这些信息就会返回给我们的主机,但有关网络或主机可能已经宕机,因此试图到达有关站点的最后一个路由器会回复一条 ICMP 消息,告诉我们有关情况。在这种情况下,ICMP 回应被视为 RELATED 数据包。下图说明了这种情况。

state-tcp-icmp-reply.jpg (720×316) (frozentux.net)

在上例中,我们向一个特定地址发送 SYN 数据包。防火墙认为这是一个新连接。然而,数据包试图到达的网络是不可达的,因此路由器向我们返回了网络不可达 ICMP 错误。由于已经添加了跟踪条目,连接跟踪代码可以将该数据包识别为 RELATED,因此 ICMP 回复会正确发送到客户端,客户端有望终止连接。与此同时,由于防火墙知道这是一条错误信息,它已经销毁了连接跟踪条目。

如果 UDP 连接遇到上述问题,也会出现与上述相同的行为。所有回复 UDP 连接的 ICMP 报文都被视为 RELATED。请看下图。

state-udp-icmp-reply.jpg (720×316) (frozentux.net)

这时会向主机发送一个 UDP 数据包。该 UDP 连接被视为新连接。但是,网络在传输途中被某个防火墙或路由器禁止。因此,我们的防火墙会收到一个 ICMP Network Prohibited(网络禁止)。防火墙知道这条 ICMP 错误信息与已经打开的 UDP 连接有关,并将其作为 RELATED 数据包发送给客户端。此时,防火墙会销毁连接跟踪条目,客户端收到 ICMP 消息,希望能中止连接。

7.7. 默认连接

在某些情况下,conntrack 不知道如何处理特定协议。如果它不了解该协议,或不知道它是如何工作的,就会出现这种情况。在这种情况下,机器会返回默认行为。默认行为用于 NETBLT、MUX 和 EGP 等。这种行为看起来与 UDP 连接跟踪差不多。第一个数据包被视为 “新”(NEW),而回复流量等被视为 “已建立”(ESTABLISHED)。

使用默认行为时,所有这些数据包都将获得相同的默认超时值。这可以通过 /proc/sys/net/ipv4/netfilter/ip_ct_generic_timeout 变量来设置。默认值为 600 秒或 10 分钟。根据您试图通过使用默认连接跟踪行为的链接发送的流量,可能需要更改该值。特别是在通过卫星等跳转流量时,这可能需要很长时间。

7.8. 复杂协议和连接跟踪

某些协议比其他协议更复杂。在连接跟踪方面,这意味着此类协议可能更难正确跟踪。ICQ、IRC 和 FTP 协议就是很好的例子。这些协议中的每一个都在数据包的实际数据有效载荷中携带信息,因此需要特殊的连接跟踪辅助工具才能正常运行。

下面列出了 linux 内核支持的复杂协议,以及引入这些协议的内核版本。

表 7-3. 复杂协议支持

Protocol nameKernel versions
FTP2.3
IRC2.3
TFTP2.5
Amanda2.5

让我们以 FTP 协议为例。FTP 协议首先会打开一个名为 FTP 控制会话的单一连接。当我们通过该会话发出命令时,其他端口也会被打开,以传输与该特定命令相关的其他数据。这些连接有两种方式,一种是主动连接,另一种是被动连接。主动连接时,FTP 客户端会向服务器发送一个端口和 IP 地址。然后,FTP 客户端打开端口,服务器从一个随机的非特权端口(>1024)连接到指定的端口,并通过它发送数据。

这里的问题是,防火墙不会知道这些额外的连接,因为它们是在协议数据的实际有效载荷中协商好的。正因为如此,防火墙无法知道它应该让服务器通过这些特定端口连接到客户端。

解决这个问题的办法是在连接跟踪模块中添加一个特殊的辅助程序,它将扫描控制连接中的数据,查找特定的语法和信息。当遇到正确的信息时,它会将该特定信息添加为 RELATED,服务器就能通过该 RELATED 条目跟踪连接。请看下图,了解 FTP 服务器与客户端建立连接后的状态。

state-tcp-server-subconn.jpg (720×335) (frozentux.net)

被动 FTP 的工作方式正好相反。FTP 客户端告诉服务器它需要某些特定数据,服务器会回复一个 IP 地址,并说明要连接到哪个端口。客户端收到该数据后,将从自己的 20 端口(FTP 数据端口)连接到该特定端口,并获取相关数据。换句话说,如果你的防火墙后面有一个 FTP 服务器,那么除了标准的 iptables 模块外,你还需要这个模块,才能让互联网上的客户端正常连接到 FTP 服务器。同样,如果你对用户的限制非常严格,只允许他们访问互联网上的 HTTP 和 FTP 服务器,而禁止所有其他端口,也需要使用该模块。请看下图及其与被动 FTP 的关系。

state-tcp-client-subconn.jpg (720×316) (frozentux.net)

内核本身已经提供了一些 conntrack 助手。更具体地说,在撰写本文时,FTP 和 IRC 协议已经有了 conntrack 帮助程序。如果无法在内核中找到所需的 conntrack 辅助工具,则应查看用户界面 iptables 中的 patch-o-matic 树。patch-o-matic 树可能包含更多的 conntrack 帮助程序,如 ntalk 或 H.323 协议。如果在 patch-o-matic 树中找不到这些帮助程序,你有很多选择。你可以查看 iptables 的 CVS 源码(如果它最近已进入该树),或者联系 Netfilter-devel 邮件列表询问是否可用。如果没有,也没有添加计划,那就只能靠你自己了,你很可能需要阅读 Rusty Russell 的《Unreliable Netfilter Hacking HOW-TO》(不可靠的 Netfilter 黑客 HOW-TO),该书链接自 “其他资源和链接 ”附录。

Conntrack 辅助程序可以静态编译到内核中,也可以作为模块编译。如果编译为模块,可以使用以下命令加载它们

modprobe ip_conntrack_ftp
modprobe ip_conntrack_irc
modprobe ip_conntrack_tftp
modprobe ip_conntrack_amanda

请注意,连接跟踪与 NAT 无关,因此如果要对连接进行 NAT,可能需要更多模块。例如,如果要对 FTP 连接进行 NAT 和跟踪,则还需要 NAT 模块。所有 NAT 辅助模块都以 ip_nat_ 开头,并遵循该命名约定;例如,FTP NAT 辅助模块命名为 ip_nat_ftp,IRC 模块命名为 ip_nat_irc。连接跟踪帮助程序遵循相同的命名约定,因此 IRC 连接跟踪帮助程序将命名为 ip_conntrack_irc,而 FTP 连接跟踪帮助程序将命名为 ip_conntrack_ftp。

第 8 章 保存和恢复大型规则集

iptables 软件包中还有两个非常有用的工具,尤其是在处理较大的规则集时。这两个工具分别叫做 iptables-save 和 iptables-restore,用于将规则集保存和恢复为特定的文件格式,与本教程其他部分的标准 shell 代码有很大不同。

提示:iptables-restore 可以与脚本语言一起使用。最大的问题是,你需要将结果输出到 iptables-restore 的 stdin 中。如果要创建一个很大的规则集(几千条规则),这可能是个好主意,因为插入所有新规则的速度会快很多。例如,你可以运行 make_rules.sh | iptables-restore。

8.1. 速度考虑

使用 iptables-save 和 iptables-restore 命令的最大原因之一是,它们能大大加快加载和保存较大规则集的速度。运行包含 iptables 规则的 shell 脚本的主要问题是,脚本中每次调用 iptables 都会首先从 Netfilter 内核空间提取整个规则集,然后插入或追加规则,或根据特定命令的需要对规则集进行任何修改。最后,它会将新的规则集从自己的内存中插入内核空间。通过使用 shell 脚本,我们需要对每一条要插入的规则都做这样的操作,而每做一次,提取和插入规则集所需的时间就会更长。

为了解决这个问题,可以使用 iptables-save 和 restore 命令。iptables-save 命令用于将规则集保存到格式特殊的文本文件中,而 iptables-restore 命令则用于将该文本文件再次加载到内核中。iptables-save会从内核中抓取整个规则集,并一次性保存到文件中,而iptables-restore则会将每个表的特定规则集一次性上传到内核中。换句话说,对于真正的大型规则集,我们不需要从内核中删除规则集约 3 万次,然后再上传到内核那么多次,现在我们只需一次移动就能将整个规则集保存到文件中,然后根据你使用的表的数量,只需三次移动就能上传整个规则集。

正如你所理解的,如果你正在处理需要插入的大量规则集,那么这些工具绝对适合你。不过,它们也有缺点,我们将在下一节详细讨论。

8.2. restore 的缺点

你可能已经想过,iptables-restore 能处理任何脚本吗?目前还不能,而且很可能永远也不能。这是使用 iptables-restore 的主要缺陷,因为你无法用这些文件做大量的事情。例如,如果你有一个动态分配 IP 地址的连接,而你想在每次电脑启动时获取这个动态 IP,然后在脚本中使用这个值,该怎么办?如果使用 iptables-restore,这几乎是不可能的。

要解决这个问题,可以先编写一个小脚本,采集你想在脚本中使用的值,然后在 iptables-restore 文件中查找特定关键字,并用小脚本采集的值替换它们。此时,你可以将其保存到临时文件中,然后使用 iptables-restore 加载新值。但这样做会带来很多问题,你将无法正常使用 iptables-save,因为它很可能会删除你在还原脚本中手动添加的关键字。换句话说,这是一个笨拙的解决方案。

第二种方法是如前所述。制作一个脚本,以 iptables-restore 格式输出规则,然后将其输入到 iptables-restore 的标准输入中。对于非常大的规则集,这比运行 iptables 本身更可取,因为它有一个坏习惯,就是在非常大的规则集上耗费大量处理能力,这在本章已有描述。

另一种解决方案是先加载 iptables-restore 脚本,然后加载一个特定的 shell 脚本,将更多的动态规则插入到适当的位置。当然,正如你所理解的,这和第一种解决方案一样笨拙。iptables-restore 根本不适合动态分配 IP 地址给防火墙的配置,也不适合根据配置选项等要求不同行为的配置。

iptables-restore和iptables-save的另一个缺点是,在撰写本文时,它的功能还不完善。问题很简单,目前使用它的人还不多,因此发现 bug 的人也不多,反过来,一些匹配和目标就会被错误地插入,这可能会导致一些你意想不到的奇怪行为。尽管存在这些问题,我还是强烈建议大家使用这些工具,只要这些规则集不包含一些新的目标或匹配项,它就能很好地处理大多数规则集。

8.3. iptables-save

正如我们已经解释过的,iptables-save 命令是将当前规则集保存到文件中,供 iptables-restore 使用的工具。这条命令其实很简单,只需要两个参数。请看下面的示例,了解该命令的语法。

命令
	iptables-save — 转储 iptables 规则
	ip6tables-save — 转储 iptables 规则
用法
	iptables-save [-M modprobe] [-c] [-t table] [-f filename]
	ip6tables-save [-M modprobe] [-c] [-t table] [-f filename]
说明
	iptables-save 和 ip6tables-save 用于将 IP 或 IPv6 表的内容以易于解析的格式转储到 STDOUT 或指定文件中。

	-M, --modprobe modprobe_program
		指定 modprobe 程序的路径。默认情况下,iptables-save 会检查 /proc/sys/kernel/modprobe,以确定可执行文件的路径。
	-f, --file filename
		指定输出日志的文件名。如果未指定,iptables-save 将记录到 STDOUT。
    -c, --counters
		在输出中包含所有数据包和字节计数器的当前值
	-t, --table tablename
		限制只向一个表输出。如果内核配置为自动加载模块,则会尝试为该表加载适当的模块。  
		如果尚未加载,将尝试加载该表的相应模块。
		如果未指定,输出将包括所有可用的表。

-c参数告诉iptables-save保留字节和数据包计数器中指定的值。例如,如果我们想重启主防火墙,但又不想丢失用于统计目的的字节和数据包计数器,这就很有用。使用 -c 参数发布 iptables-save 命令,就可以在不中断统计和记账程序的情况下重启防火墙。当然,默认值是在发出该命令时不保留计数器。

-t参数告诉iptables-save命令保存哪些表格。如果没有这个参数,命令会自动将所有可用的表保存到文件中。下面是一个例子,说明如果没有加载任何规则集,iptables-save 命令会有哪些输出。

# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:17 2002
*filter
:INPUT ACCEPT [404:19766]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [530:43376]
COMMIT
# Completed on Wed Apr 24 10:19:17 2002
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:17 2002
*mangle
:PREROUTING ACCEPT [451:22060]
:INPUT ACCEPT [451:22060]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [594:47151]
:POSTROUTING ACCEPT [594:47151]
COMMIT
# Completed on Wed Apr 24 10:19:17 2002
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:17 2002
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [3:450]
:OUTPUT ACCEPT [3:450]
COMMIT
# Completed on Wed Apr 24 10:19:17 2002

其中包含一些以 # 号开头的注释。每个表都标记为 *<表名>,例如 *mangle。然后,在每个表中都有链规范和规则。链规范看起来像 :<chain-name> <chain-policy> [<packet-counter>:<byte-counter>]。链名可以是 PREROUTING 等,策略如前所述,可以是 ACCEPT 等。最后,数据包计数器和字节计数器与 iptables -L -v 输出的计数器相同。最后,每个表声明都以 COMMIT 关键字结束。COMMIT 关键字告诉我们,此时应将管道中的所有规则提交给内核。

上面的示例非常基本,因此我认为再举一个包含很小的 Iptables-save 规则集的简短示例再合适不过了。如果我们运行 iptables-save,输出结果将如下所示:

# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002
*filter
:INPUT DROP [1:229]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 
-A FORWARD -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT 
-A FORWARD -i eth1 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT 
-A OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT 
COMMIT
# Completed on Wed Apr 24 10:19:55 2002
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002
*mangle
:PREROUTING ACCEPT [658:32445]
:INPUT ACCEPT [658:32445]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [891:68234]
:POSTROUTING ACCEPT [891:68234]
COMMIT
# Completed on Wed Apr 24 10:19:55 2002
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002
*nat
:PREROUTING ACCEPT [1:229]
:POSTROUTING ACCEPT [3:450]
:OUTPUT ACCEPT [3:450]
-A POSTROUTING -o eth0 -j SNAT --to-source 195.233.192.1 
COMMIT
# Completed on Wed Apr 24 10:19:55 2002

正如你所看到的,由于我们使用了 -c 参数,因此每条命令的前缀都是字节计数器和数据包计数器。除此以外,命令行与脚本完全一致。现在唯一的问题是如何将输出保存到文件中。这很简单,如果你以前用过 Linux,应该已经知道如何做了。只需将命令输出导入要保存的文件即可。如下所示

iptables-save -c > /etc/iptables-save

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

8.4. iptables-restore

iptables-restore 命令用于恢复用 iptables-save 命令保存的 iptables 规则集。该命令从标准输入中获取所有输入,但遗憾的是,截至本文编写时,它还不能从文件中加载。这是 iptables-restore 命令的语法:

命令
	iptables-restore — 恢复 IP 表
	ip6tables-restore — 恢复 IPv6 表
简述
	iptables-restore [-chntvV] [-w secs] [-W usecs] [-M modprobe] [-T name] [file]
	ip6tables-restore [-chntvV] [-w secs] [-W usecs] [-M modprobe] [-T name] [file]
说明
    iptables-restore 和 ip6tables-restore 用于从 STDIN 或文件中指定的数据恢复 IP 表和 IPv6 表。使用 shell 提供的 I/O 重定向 从文件中读取,或将文件指定为参数。

	-c, --counters
		恢复所有数据包和字节计数器的值
	-h, --help
        打印简短的选项摘要。
	-n, --noflush
        不刷新表中以前的内容。如果未指定,两条命令都会刷新(删除)相应表的所有先前内容
	-t, --test
        只解析和构建规则集,但不提交。
	-v, --verbose
		在规则集处理过程中打印额外的调试信息。
	-V, --version
        打印程序版本号。
	-w, --wait [seconds]
        等待 xtables 锁。为防止程序的多个实例同时运行,程序启动时会尝试获取独占锁。  
        默认情况下,如果无法获得锁,程序将退出。该选项将使程序等待(无限期或可选秒数),直到获得独占锁。
	-W, --wait-interval microseconds
        每次迭代等待的时间间隔。在运行对延迟敏感的应用程序时,可能无法接受长时间等待 xtables 锁。
        选项将使每次迭代花费指定的时间。默认时间间隔为 1 秒。该选项仅与 -w 一起使用。
	-M, --modprobe modprobe_program
        指定 modprobe 程序的路径。默认情况下,iptables-restore 会检查 /proc/sys/kernel/modprobe,以确定可执行文件的路径。
	-T, --table name
        只恢复指定的表,即使输入流中包含其他表。

如果要恢复以前用 iptables-save 保存的计数器,必须使用 -c 参数。该参数也可以写成长形式–counters。

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

使用 iptables-restore 命令加载规则集有几种方法,但这里主要介绍最简单、最常用的方法。

cat /etc/iptables-save | iptables-restore -c

下面的方法也行得通:

iptables-restore -c < /etc/iptables-save

这将捕捉到位于 /etc/iptables-save 文件中的规则集,然后将其导入 iptables-restore,iptables-restore 从标准输入中获取规则集,然后将其还原,包括字节和数据包计数器。就是这么简单。这个命令可以不断变化,直到被遗忘,我们还可以展示不同的管道可能性,不过,这有点超出本章的范围,因此我们将跳过这一部分,留作读者的实验练习。

现在,规则集应该已经正确加载到内核,并且一切正常。如果没有,则可能是这些命令出现了错误。

第 9 章 如何创建规则

本章和接下来的三章将详细讨论如何建立自己的规则。规则可以说是防火墙在特定链中阻止或允许不同连接和数据包时所遵循的方向。你写入链中的每一行都应被视为一条规则。我们还将讨论可用的基本匹配和使用方法,以及不同的目标和我们如何构建自己的新目标(即新的子链)。

本章将讨论如何创建规则、如何编写规则并输入规则以便用户空间程序 iptables 接受规则、不同的表以及可以向 iptables 发出的命令等基本知识。之后,我们将在下一章中了解 iptables 可用的所有匹配规则,然后更详细地介绍每种类型的目标和跳转。

9.1. iptables 命令的基础知识

正如我们已经解释过的,每条规则都是内核用来查找如何处理数据包的一行。如果符合所有条件(或匹配),我们就执行目标指令(或跳转指令)。通常,我们会用类似下面的语法编写规则:

iptables [-t table] command [match] [target/jump]

没有规定目标指令必须是行中的最后一个函数。不过,为了获得最佳的可读性,你通常会遵守这种语法。总之,你会看到的大多数规则都是这样写的。因此,如果你读了别人的脚本,你很可能会认出这种语法,并很容易理解规则。

如果想使用默认表以外的表,可以在指定 [table] 的地方指定表。不过,没有必要明确说明使用什么表,因为默认情况下,iptables 使用 filter 表执行所有命令。也不必在规则的这一点上指定表。它可以设置在规则的任何地方。不过,把表的指定放在开头也是一种标准做法。

但有一点需要注意: command 应始终放在第一位,或者直接放在表格说明之后。我们使用 “command”来告诉程序要做什么,例如插入一条规则或在链的末尾添加一条规则,或者删除一条规则。我们将在下文中进一步探讨。

match 是我们发送给内核的规则部分,它详细说明了数据包的具体特征,也就是数据包与其他所有数据包的不同之处。在这里,我们可以指定数据包来自哪个 IP 地址、哪个网络接口、目标 IP 地址、端口、协议或其他。我们可以使用多种不同的匹配方式,本章将进一步详细介绍。

最后是数据包的目标。如果一个数据包符合所有匹配条件,我们就可以告诉内核如何处理它。例如,我们可以告诉内核将数据包发送到我们自己创建的另一个链,它是这个特定表的一部分。我们可以告诉内核丢弃数据包,不再做进一步处理,也可以告诉内核向发送者发送指定的回复。与本节的其他内容一样,我们将在本章的后续部分仔细研究。

9.2. Tables

选项 -t 指定了要使用的表。默认情况下使用 filter 表。我们可以使用 -t 选项指定以下表格之一。请注意,这只是对 “遍历表和链 ”一章中部分内容的简要概括。

表 9-1. tables

TableExplanation
natnat 表主要用于网络地址转换。根据我们的规则,“NAT ”数据包的 IP 地址会被更改。数据流中的数据包只在该表中遍历一次。我们假设数据流的第一个数据包是允许的。同一数据流中的其他数据包会自动 “NAT ”或 “伪装”,并采取与第一个数据包相同的措施。换句话说,这些数据包不会再次通过此表,但仍会像流中的第一个数据包一样被处理。这就是为什么不应在此表中进行任何过滤的主要原因,我们将在后面详细讨论。PREROUTING 链用于更改进入防火墙的数据包。OUTPUT 链用于更改本地生成的数据包(即在防火墙上),然后再进行路由决策。最后是 POSTROUTING 链,用于在数据包即将离开防火墙时对其进行修改。
mangle该表主要用于混淆数据包。除其他外,我们还可以更改不同数据包的内容及其报头的内容。例如更改TTLTOSMARK。请注意,MARK 并不是真正对数据包进行更改,而是在内核空间为数据包设置一个标记值。其他规则或程序可能会在防火墙中进一步使用该标记来过滤或进行高级路由选择;tc 就是一个例子。该表包括五个内置链:PREROUTING、POSTROUTING、OUTPUT、INPUT 和 FORWARD 链。PREROUTING 用于在数据包进入防火墙和路由决策之前对其进行修改。POSTROUTING 用于在做出所有路由决定后修改数据包。OUTPUT 用于在数据包进入路由决策后更改本地生成的数据包。INPUT 用于在数据包路由到本地计算机后,但在用户空间应用程序实际看到数据之前更改数据包。FORWARD 用于在数据包进入第一个路由决定之后,但在实际进入最后一个路由决定之前,对数据包进行篡改。请注意,mangle 不能用于任何类型的网络地址转换或伪装,nat 表就是为这类操作而设计的。
filterfilter 表应专门用于过滤数据包。例如,我们可以DROPLOGACCEPTREJECT数据包,不会出现问题,其他表也是如此。该表内置了三个链。第一个链名为 “FORWARD”,用于处理所有不以本地主机为目的地的非本地生成数据包(换句话说,就是防火墙)。INPUT 用于所有以本地主机(防火墙)为目的地的数据包,最后 OUTPUT 用于所有本地生成的数据包。

通过以上详细介绍,您应该对三种不同的表有了基本的了解。它们的用途完全不同,你应该知道每条链的用途。如果不了解它们的用途,很可能会在防火墙中给自己挖一个坑,一旦有人发现并把你推进去,你就会掉进去。我们已经在 “遍历表和链 ”一章中详细讨论了必要的表和链。如果你还没有完全理解,我建议你再回头读一遍。

9.3. Commands

在本节中,我们将介绍所有不同的命令以及如何使用它们。命令告诉 iptables 如何处理我们发送给解析器的其余规则。通常,我们希望在某个表或其他表中添加或删除某些内容。以下命令可供 iptables 使用:

表 9-2. Commands

Command-A, –append
Exampleiptables -A INPUT …
Explanation该命令将规则附加到链的末尾。换句话说,该规则将始终放在规则集的最后,因此也是最后被检查的规则,除非稍后添加更多规则。
Command-D, –delete
Exampleiptables -D INPUT –dport 80 -j DROP, iptables -D INPUT 1
Explanation该命令删除链中的一条规则。有两种方法:一种是输入要匹配的整条规则(如第一个示例),另一种是指定要匹配的规则编号。如果使用第一种方法,您的输入必须与链中的输入完全匹配。如果使用第二种方法,则必须匹配要删除的规则编号。规则从每个链的顶端开始编号,从 1 号开始。
Command-R, –replace
Exampleiptables -R INPUT 1 -s 192.168.0.1 -j DROP
Explanation该命令替换指定行的旧条目。其工作方式与**–delete**命令相同,但不是完全删除条目,而是用新条目替换。该命令的主要用途可能是在使用 iptables 进行实验时使用。
Command-I, –insert
Exampleiptables -I INPUT 1 –dport 80 -j ACCEPT
Explanation在链中插入一条规则。规则会按照我们指定的实际编号插入。换句话说,上述示例将作为规则 1 插入 INPUT 链中,因此从现在起它将是链中的第一条规则。
Command-L, –list
Exampleiptables -L INPUT
Explanation这条命令会列出指定链中的所有条目。在上述情况中,我们将列出 INPUT 链中的所有条目。不指定任何链也是合法的。在最后一种情况下,命令将列出指定表中的所有链(要指定表,请参阅Tables 部分)。具体的输出会受到发送给解析器的其他选项的影响,例如**-n-v**选项等。
Command-F, –flush
Exampleiptables -F INPUT
Explanation该命令会刷新指定链中的所有规则,相当于逐条删除每条规则,但速度要快得多。该命令可以在不带选项的情况下使用,然后会删除指定表中所有链上的所有规则。
Command-Z, –zero
Exampleiptables -Z INPUT
Explanation该命令指示程序将特定链或所有链中的所有计数器清零。如果您在使用**-L命令时使用了-v**选项,您可能已经看到了每个字段开头的数据包计数器。要将该数据包计数器清零,请使用 -Z 选项。该选项的作用与 -L 相同,只是 -Z 不会列出规则。如果同时使用 -L-Z(这是合法的),将首先列出链,然后将数据包计数器清零。
Command-N, –new-chain
Exampleiptables -N allowed
Explanation该命令要求内核在指定表中创建一个指定名称的新链。在上例中,我们创建了一个名为allowed 的链。请注意,不能已有同名的链或目标。
Command-X, –delete-chain
Exampleiptables -X allowed
Explanation这条命令将从表中删除指定的链。要使该命令生效,必须没有任何规则引用要删除的链。换句话说,在实际删除链之前,必须替换或删除所有引用该链的规则。如果在不带任何选项的情况下使用此命令,除了指定表中的内置链外,所有链都将被删除。
Command-P, –policy
Exampleiptables -P INPUT DROP
Explanation该命令要求内核在链上设置指定的默认目标或策略。所有不匹配任何规则的数据包都将被迫使用链上的策略。合法的目标是DROPACCEPT(可能还有更多,如果有,请给我发邮件)。
Command-E, –rename-chain
Exampleiptables -E allowed disallowed
Explanation-E命令告诉iptables将链的第一个名称改为第二个名称。换句话说,在上面的例子中,我们将把链的名称从 “允许 ”改为 “不允许”。请注意,这不会影响表格的实际运行方式。换句话说,这只是表格外观上的改动。

除非只想列出 iptables 的内置帮助或获取命令的版本,否则应始终输入完整的命令行。要获取版本信息,使用 -v 选项;要获取帮助信息,使用 -h 选项。换句话说,就像往常一样。接下来是一些可用于不同命令的选项。注意,我们会告诉你这些选项可以与哪些命令一起使用,以及会产生什么效果。还要注意的是,这里不包括任何影响规则或匹配的选项。我们将在本章后半部分介绍匹配和目标。

表 9-3. Options

Option-v, –verbose
Commands used with–list, –append, –insert, –delete, –replace
Explanation该命令提供详细输出,主要与**–list** 命令一起使用。如果与**–list** 命令一起使用,它会输出接口地址、规则选项和 TOS 屏蔽。如果设置了**–verbose选项,–list命令还会包含每条规则的字节和数据包计数器。这些计数器使用 K(x1000)、M(x1,000,000)和 G(x1,000,000,000)乘数。要想改写这一设置并获得精确的输出结果,可以使用-x选项,详见下文。如果该选项与–append**、–insert–delete或**–replace**命令一起使用,程序将输出详细信息,说明规则是如何解释的,以及是否正确插入等。
Option-x, –exact
Commands used with–list
Explanation该选项扩展了数值。换句话说,–list 的输出将不包含 K、M 或 G 乘数。相反,我们将从数据包和字节计数器中获得符合相关规则的数据包和字节的精确输出。请注意,该选项只在 –list 命令中使用,与其他任何命令都无关。
Option-n, –numeric
Commands used with–list
Explanation该选项指示 iptables 输出数值。IP 地址和端口号将使用数值打印,而不是主机名、网络名或应用程序名。该选项仅适用于**–list** 命令。在可能的情况下,该选项会覆盖将所有数值解析为主机和名称的默认值。
Option–line-numbers
Commands used with–list
Explanation–line-numbers 命令与 –list 命令一起用于输出行号。使用该选项,每条规则都会输出其编号。在插入规则时,可以方便地知道哪条规则的行号。该选项仅适用于 –list 命令。
Option-c, –set-counters
Commands used with–insert, –append, –replace
Explanation该选项在创建规则或以某种方式修改规则时使用。然后,我们可以使用该选项初始化规则的数据包和字节计数器。语法如下:–set-counters 20 4000,这会告诉内核将数据包计数器设置为 20,将字节计数器设置为 4000。
Option–modprobe
Commands used withAll
Explanation-modprobe选项用于告诉iptables在探测模块或将模块添加到内核时使用哪个模块。如果modprobe命令不在搜索路径中,也可以使用该选项。在这种情况下,可能有必要指定该选项,以便程序知道在所需模块未加载的情况下该怎么办。该选项可用于所有命令。

第 10 章 Iptables matches

在本章中,我们将进一步讨论 matches。我将匹配分为五个不同的子类别。首先是通用匹配,可用于所有规则。然后是 TCP 匹配,只能用于 TCP 数据包。UDP 匹配规则只能用于 UDP 数据包,ICMP 匹配规则只能用于 ICMP 数据包。最后是特殊匹配,如状态、所有者和限制匹配等。这些最终匹配又被缩小到更多的子类别,尽管它们不一定是完全不同的匹配。我希望这样的分类是合理的,也希望所有的人都能理解。

阅读过前面章节的读者可能已经明白,匹配是指在数据包中指定一个必须为真(或假)的特殊条件。一条规则可以包含多个此类匹配条件。例如,我们可能想匹配来自局域网上特定主机的数据包,而且只匹配来自该主机上特定端口的数据包。然后,我们可以使用匹配来告诉规则只对具有特定源地址的数据包应用目标或跳转规范,这些数据包通过连接局域网的接口进入,而且必须是指定的端口之一。如果其中任何一个匹配失败(例如,源地址不正确,但其他都正确),整条规则就会失败,并在数据包上测试下一条规则。但如果所有匹配都为真,则会应用规则指定的目标。

10.1. 通用matches

本节将讨论通用匹配。通用匹配是一种无论我们使用何种协议或加载了何种匹配扩展名都始终可用的匹配。换句话说,使用这些匹配不需要任何特殊参数。我还在这里加入了 –protocol 匹配,尽管它对协议匹配更有针对性。例如,如果我们想使用 TCP 匹配,就需要使用 –protocol 匹配,并将 TCP 作为选项发送给匹配。不过,–protocol 本身也是一种匹配,因为它可以用来匹配特定的协议。以下匹配协议始终可用。

表 10-1. 通用matches

Match-p, –protocol
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp
Explanation该匹配用于检查某些协议。协议示例包括 TCP、UDP 和 ICMP。协议必须是内部指定的 TCP、UDP 或 ICMP 协议之一。它也可以使用/etc/protocols 文件中指定的值,如果在该文件中找不到该协议,它将以错误回复。protocl 也可以是一个整数值。例如,ICMP 协议为整数值 1,TCP 为 6,UDP 为 17。最后,它的值也可以是 ALL。ALL 表示只匹配 TCP、UDP 和 ICMP。如果该匹配项的整数值为 0 (0),则表示所有协议,如果不使用 –协议匹配项,则默认为所有协议。该匹配也可以用 ! 符号反转,因此 –protocol ! tcp 表示匹配 UDP 和 ICMP。
Match-s, –src, –source
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -s 192.168.1.1
Explanation这是源IP匹配,用于根据数据包的源 IP 地址进行匹配。主格式可用于匹配单个 IP 地址,如192.168.1.1。它还可以与 CIDR “位 ”形式的网络掩码一起使用,具体方法是指定网络掩码左侧的 1(1)个数。这意味着,我们可以添加 /24 来使用 255.255.255.0 网络掩码。这样,我们就可以匹配整个 IP 范围,例如我们的本地网络或防火墙后面的网段。这样,一行的内容就像 192.168.0.0/24。这将匹配 192.168.0.x 范围内的所有数据包。另一种方法是使用 255.255.255.255 形式的常规网络掩码(即 192.168.0.0/255.255.255.0)。我们还可以像以前一样,用 ! 反转匹配。换句话说,如果我们使用 –source !192.168.0.0/24,我们将匹配源地址不在 192.168.0.x范围内的所有数据包。默认值是匹配所有 IP 地址。
Match-d, –dst, –destination
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -d 192.168.1.1
Explanation–destination 匹配用于根据数据包的目标地址进行匹配。它的工作原理与**–source匹配基本相同,语法也相同,只是匹配的是数据包的目的地。要匹配一个 IP 范围,我们可以添加一个净掩码,可以是精确的净掩码形式,也可以是从净掩码位左边开始数的 1 的个数。例如 192.168.0.0/255.255.255.0192.168.0.0/24。这两种匹配都是等价的。我们还可以用 ! ** 符号反转整个匹配,就像以前一样。–destination !192.168.0.1 换句话说,将匹配除以 192.168.0.1 IP 地址为目的地的数据包之外的所有数据包。
Match-i, –in-interface
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -i eth0
Explanation该匹配用于数据包进入的接口。请注意,该选项只在 INPUT、FORWARD 和 PREROUTING 链中有效,在其他地方使用时将返回错误信息。如果未指定特定接口,该匹配的默认行为是假定字符串值为**++** 值用于匹配由字母和数字组成的字符串。换句话说,单个 + 会告诉内核匹配所有数据包,而不考虑数据包来自哪个接口。**+字符串还可以附加到接口类型上,因此eth+将匹配所有以太网设备。我们还可以借助!**符号反转该选项的含义。这样,该行的语法将类似于 -i ! eth0,它将匹配除 eth0 以外的所有输入接口。
Match-o, –out-interface
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A FORWARD -o eth0
Explanation–out–interface匹配用于处理从哪个接口离开的数据包。请注意,这种匹配只在 OUTPUT、FORWARD 和 POSTROUTING 链中可用,实际上与 –in-interface 匹配正好相反。除此之外,它的工作原理与**–in-interface匹配基本相同。+扩展被理解为匹配所有相似类型的设备,因此eth+将匹配所有eth设备,以此类推。要反转匹配的含义,可以使用 符号,使用方法与 –in-interface 匹配完全相同。如果未指定–out-interface**,该匹配的默认行为是匹配所有设备,而不管数据包的目的地是哪里。
Match-f, –fragment
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -f
Explanation该匹配用于匹配分片数据包的第二和第三部分。这样做的原因是,在分片数据包的情况下,无法知道分片的源端口或目的端口,也无法知道 ICMP 类型等。此外,在比较特殊的情况下,碎片数据包可能会被用来对其他计算机进行复合攻击。这样的数据包片段不会被其他规则匹配,因此就有了这种匹配方式。该选项也可与**!符号结合使用;但在这种情况下,符号必须在匹配之前,即!-f**. 反向匹配时,我们会匹配所有报头片段和/或未片段数据包。这意味着,我们匹配分片数据包的所有第一个分片,而不是第二个、第三个,以此类推。我们还匹配所有在传输过程中未被分片的数据包。还需注意的是,你可以使用内核中非常好的碎片整理选项。另外,如果使用连接跟踪,就不会看到任何碎片数据包,因为它们在进入iptables中的任何链或表之前就已被处理。

10.2. 隐式matches

本节将介绍隐式加载的匹配。隐式匹配是隐含的、理所当然的、自动的。例如,当我们匹配 –protocol tcp 时,不需要任何其他条件。目前有三种不同协议的隐式匹配。它们是 TCP 匹配、UDP 匹配和 ICMP 匹配。基于 TCP 的匹配包含一组仅适用于 TCP 数据包的唯一标准。基于 UDP 的匹配包含另一组仅适用于 UDP 数据包的标准。ICMP 数据包也是如此。另一方面,也有明确加载的显式匹配。显式匹配不是隐含的或自动的,必须特别指定。对于这些匹配,需要使用 -m 或 –match 选项,我们将在下一节讨论。

10.2.1. TCP 匹配

这些匹配是特定于协议的,只有在处理 TCP 数据包和数据流时才可用。要使用这些匹配,需要在尝试使用前在命令行中指定 –protocol tcp。请注意,–protocol tcp 匹配必须位于特定协议匹配的左边。从某种意义上说,这些匹配是隐式加载的,就像 UDP 和 ICMP 匹配是隐式加载一样。其他匹配将在本节的 TCP 匹配部分之后继续讨论。

表 10-2. TCP 匹配

Match–sport, –source-port
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp –sport 22
Explanation–source-port匹配用于根据源端口匹配数据包。如果没有它,我们会暗示所有源端口。该匹配可以使用服务名称或端口号。如果指定服务名称,该服务名称必须在 /etc/services 文件中,因为 iptables 使用该文件进行查找。如果用端口号指定端口,规则的加载速度会稍快一些,因为iptables无需检查服务名称。不过,匹配结果可能会比使用服务名称时更难读取。如果要编写一个由 200 条或更多规则组成的规则集,一定要使用端口号,因为两者之间的差别非常明显。(在慢速电脑上,如果您配置了包含 1000 条左右规则的大型规则集,这可能会造成 10 秒钟的差异)。您也可以使用**–source-port匹配任何范围的端口,例如–source-port 22:80**。这个例子将匹配 22 和 80 之间的所有源端口。如果省略指定第一个端口,则假定端口为 0(隐含)。因此,–source-port :80 将匹配 0 至 80 的端口。如果省略最后一个端口指定,则假定端口为 65535。 如果你写 –source-port 22:,你就指定了从端口 22 到端口 65535 的所有端口的匹配。如果反转端口范围,iptables 会自动反转。如果你写 –source-port 80:22,它就会被简单地解释为 –source-port 22:80。您也可以通过添加**!符号来反转匹配。例如,–source-port !22** 表示要匹配除端口 22 以外的所有端口。反转也可以与端口范围一起使用,这样就会像 –source-port !22:80,意思是要匹配除端口 22 到 80 之外的所有端口。请注意,这种匹配方式无法处理多个分隔的端口和端口范围。更多相关信息,请查看多端口匹配扩展。
Match–dport, –destination-port
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp –dport 22
Explanation该匹配用于根据 TCP 数据包的目的端口进行匹配。它使用的语法与**–source-port** 匹配完全相同。它能理解端口和端口范围规格,也能理解反转。如上所述,它还能反转端口范围规格中的高端口和低端口。如果在端口范围规格中遗漏了高端口或低端口,匹配也会假定其值为 0 和 65535。换句话说,与**–source-port** 语法完全相同。请注意,这种匹配方式不处理多个分隔的端口和端口范围。更多相关信息,请查看多端口匹配扩展。
Match–tcp-flags
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -p tcp –tcp-flags SYN,FIN,ACK SYN
ExplanationThis match is used to match on the TCP flags in a packet. First of all, the match takes a list of flags to compare (a mask) and secondly it takes list of flags that should be set to 1, or turned on. Both lists should be comma-delimited. The match knows about the SYN, ACK, FIN, RST, URG, PSH flags, and it also recognizes the words ALL and NONE. ALL and NONE is pretty much self describing: ALL means to use all flags and NONE means to use no flags for the option.–tcp-flags ALL NONE would in other words mean to check all of the TCP flags and match if none of the flags are set. This option can also be inverted with the ! sign. For example, if we specify ! SYN,FIN,ACK SYN, we would get a match that would match packets that had the ACK and FIN bits set, but not the SYN bit. Also note that the comma delimitation should not include spaces. You can see the correct syntax in the example above.这种匹配方式用于匹配数据包中的 TCP 标志。首先,该匹配方法需要一个要比较的标志列表(掩码),其次,它还需要一个应设置为 1 或打开的标志列表。两个列表都应以逗号分隔。匹配器知道 SYN、ACK、FIN、RST、URG、PSH 标志,也能识别 ALL 和 NONE 字样。ALL 和 NONE 几乎可以自圆其说: ALL 表示使用所有标记,NONE 表示不使用任何标记。换句话说,–tcp-flags ALL NONE 表示检查所有 TCP 标志,如果没有设置任何标志,则进行匹配。该选项也可以用**!**符号反转。例如,如果我们指定 !SYN,FIN,ACK SYN,我们将得到一个匹配结果,该结果将匹配设置了 ACK 和 FIN 位,但未设置 SYN 位的数据包。还要注意,逗号分隔不应包含空格。您可以在上面的示例中看到正确的语法。
Match–syn
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -p tcp –syn
Explanation-syn匹配或多或少是ipchains时代的遗留物,现在仍然存在是为了向后兼容,并使两者之间的过渡更容易。它用于匹配 SYN 位已设置、ACK 和 RST 位未设置的数据包。换句话说,该命令与 –tcp-flags SYN,RST,ACK SYN 匹配完全相同。此类数据包主要用于向服务器请求新的 TCP 连接。如果阻止了这些数据包,就能有效阻止所有传入的连接尝试。但是,你并没有阻止传出连接,而现在很多漏洞利用都会使用这种连接(例如,黑客入侵一个合法服务,然后安装一个程序或类似程序,使其能够启动与主机的现有连接,而不是在主机上打开一个新端口)。这种匹配也可以用 ! -syn方式反转。这将匹配所有设置了 RST 或 ACK 位的数据包,换句话说,就是已经建立连接的数据包。
Match–tcp-option
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -p tcp –tcp-option 16
Explanation此匹配用于根据数据包的 TCP 选项对其进行匹配。TCP 选项是报文头的一个特定部分。这部分由 3 个不同的字段组成。第一个字段有 8 比特长,告诉我们该数据流中使用了哪些选项,第二个字段也有 8 比特长,告诉我们选项字段有多长。设置这个长度字段的原因是,TCP 选项是可选的。为了符合标准,我们不需要实现所有选项,而只需查看是哪种选项,如果不支持,则只需查看长度字段,然后跳过该数据。根据不同 TCP 选项的十进制值,这种匹配方式可用于匹配不同的 TCP 选项。也可以使用 **! ** 标志将其反转,以便匹配所有 TCP 选项,但不匹配给定的选项。如需查看所有选项的完整列表,请访问互联网工程任务组,该组织维护着一份互联网上使用的所有标准数字的列表。

10.2.2. UDP 匹配

本节介绍仅与 UDP 数据包一起使用的匹配。当您指定 –protocol UDP 匹配时,这些匹配会被隐式加载,并在此规范之后可用。请注意,UDP 数据包不是面向连接的,因此不存在在数据包中设置不同标志来提供数据报应做的事情,例如打开或关闭连接,或者只是发送数据。UDP 数据包也不需要任何形式的确认。如果数据包丢失了,就直接丢失(不考虑 ICMP 错误信息等)。这就意味着 UDP 数据包的匹配度要比 TCP 数据包低得多。请注意,即使 UDP 或 ICMP 数据包被视为无连接协议,状态机也能处理所有类型的数据包。状态机在 UDP 数据包上的工作原理与在 TCP 数据包上基本相同。

表 10-3. UDP 匹配

Match–sport, –source-port
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p udp –sport 53
Explanation这种匹配的工作原理与 TCP 匹配完全相同。它用于根据 UDP 源端口对数据包执行匹配。它以相同的语法支持端口范围、单端口和端口反转。要指定 UDP 端口范围,可以使用 22:80,这将匹配 UDP 端口 22 到 80。如果省略第一个值,端口 0 将被假定。如果省略最后一个端口,则假定端口为 65535。如果高端口在低端口之前,端口会自动相互转换。单个 UDP 端口匹配如上例所示。要反转端口匹配,请添加**!** 符号,即**–source-port !53**. 这将匹配除端口 53 以外的所有端口。只要服务名称在 /etc/services 文件中可用,匹配就可以理解。需要注意的是,这种匹配方式无法处理多个分隔的端口和端口范围。有关这方面的更多信息,请参阅多端口匹配扩展。
Match–dport, –destination-port
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p udp –dport 53
Explanation该匹配与上述**–source-port** 相同。它与相应的 TCP 匹配完全相同,但这里适用于 UDP 数据包。它根据 UDP 目的地端口匹配数据包。匹配可处理端口范围、单端口和反转。要匹配单个端口,可以使用**–destination-port 53**;要反转端口,可以使用 –destination-port !53. 前者将匹配所有发送到 53 端口的 UDP 数据包,而后者则只匹配发送到目的端口 53 的数据包。要指定端口范围,可以使用 –destination-port 9:19。这个例子将匹配所有指向 UDP 端口 9 到 19 的数据包。如果省略第一个端口,则假定端口为 0。如果省略第二个端口,则假定端口为 65535。如果高端口被置于低端口之前,它们会自动交换位置,因此低端口会被置于高端口之前。请注意,这种匹配方式无法处理多个端口和端口范围。有关这方面的更多信息,请查看多端口匹配扩展。

10.2.3. ICMP 匹配

这些是 ICMP 匹配信息。这些数据包比 UDP 数据包更加短暂,也就是说寿命更短,因为它们是无连接的。ICMP 协议主要用于错误报告和连接控制等。ICMP 不是从属于 IP 协议的协议,而更像是一个增强 IP 协议并帮助处理错误的协议。ICMP 数据包的报头与 IP 报头非常相似,但有许多不同之处。该协议的主要特征是类型报头,它告诉我们数据包的用途。例如,如果我们试图访问一个无法访问的 IP 地址,我们通常会收到一个 ICMP 主机无法访问的回报。有关 ICMP 类型的完整列表,请参阅 ICMP 类型附录。对于 ICMP 数据包,只有一种 ICMP 特定匹配方式可用,希望这样就足够了。当我们使用 –protocol ICMP 匹配时,该匹配会被隐式加载,我们可以自动访问它。请注意,我们还可以使用所有的通用匹配,因此除其他外,我们还可以匹配源地址和目标地址。

表 10-4. ICMP 匹配

Match–icmp-type
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p icmp –icmp-type 8
Explanation该匹配用于指定要匹配的 ICMP 类型。ICMP 类型可以通过数值或名称指定。RFC 792 中指定了数值。要查找 ICMP 名称值的完整列表,请执行iptables –protocol icmp –help 或查看 ICMP types 附录。这种匹配也可以用 ! 符号反转,即 –icmp-type !8 方式。需要注意的是,有些 ICMP 类型已经过时,有些则可能对未受保护的主机造成 “危险”,因为它们可能会将数据包重定向到错误的地方。类型和代码也可以通过类型名、数字类型和类型/代码来指定。例如,–icmp-type network-redirect–icmp-type 8–icmp-type 8/0。注意:请注意 netfilter 使用 ICMP 类型 255 来匹配所有 ICMP 类型。如果您尝试匹配此 ICMP 类型,结果将是匹配所有 ICMP 类型。

10.3. 显式匹配

显式匹配是指必须使用 -m 或 –match 选项进行特定加载的匹配。例如,状态匹配要求在输入要使用的实际匹配之前使用 -m state 指令。其中有些匹配可能是特定于协议的。有些可能与任何特定协议无关–例如连接状态。这些状态可能是 NEW(尚未建立的连接的第一个数据包)、ESTABLISHED(已在内核中注册的连接)、RELATED(由已建立的旧连接创建的新连接)等。有一些可能只是为了测试或实验目的,或者只是为了说明 iptables 的能力。这也就意味着,并非所有这些匹配都能派上用场。尽管如此,你很可能会发现某些明确匹配的用途。而且,随着每一个新版 iptables 的发布,都会有新的匹配机制出现。至于你是否能找到它们的用途,取决于你的想象力和需求。隐式加载匹配与显式加载匹配的区别在于,隐式加载匹配会在匹配 TCP 数据包属性时自动加载,而显式加载匹配永远不会自动加载–这取决于你是否发现并激活显式匹配。

10.3.1. AH/ESP 匹配

这些匹配用于 IPSEC AH 和 ESP 协议。IPSEC 用于在不安全的互联网连接上创建安全隧道。IPSEC 使用 AH 和 ESP 协议创建这些安全连接。AH 和 ESP 协议实际上是两个独立的协议,但由于它们看起来非常相似,而且都用于相同的功能,因此在此都进行了描述。

我不会在此详细介绍 IPSEC,请查阅以下网页和文档了解更多信息:

  1. RFC 2401 - 互联网协议的安全架构
  2. FreeS/WAN
  3. IPSEC 指南
  4. Linux 高级路由和流量控制指南

互联网上还有大量相关文档,您可以根据需要自由查找。

要使用 AH/ESP 匹配,需要使用 -m ah 加载 AH 匹配,使用 -m esp 加载 ESP 匹配。

注意:在 2.2 和 2.4 内核中,Linux 使用名为 FreeS/WAN 的东西来实现 IPSEC,但从 Linux 内核 2.5.47 及以上版本开始,Linux 内核可以直接实现 IPSEC,无需修补内核。这是对 Linux 上 IPSEC 实现的全面重写。

表 10-5. AH 匹配选项

Match–ahspi
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p 51 -m ah –ahspi 500
Explanation这与 AH 数据包的 AH 安全参数索引 (SPI) 编号一致。请注意,您还必须指定协议,因为 AH 是在不同于标准 TCP、UDP 或 ICMP 协议的协议上运行的。SPI 号与源地址、目标地址和密钥一起用于创建安全关联(SA)。SA 可唯一标识所有主机的每一条 IPSEC 隧道。SPI 用于唯一区分连接在相同两个对等设备之间的每条 IPSEC 隧道。使用**–ahspi** 匹配,我们可以根据数据包的 SPI 匹配数据包。通过使用:符号,该匹配可以匹配整个 SPI 值范围,如 500:520,它将匹配整个 SPI 范围。

表 10-6. ESP 匹配选项

Match–espspi
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p 50 -m esp –espspi 500
ExplanationESP 对应的安全参数索引(SPI)的使用方法与 AH 变体完全相同。匹配结果看起来完全一样,只是 ESP/AH 不同。当然,这种匹配可以匹配整个 SPI 数字范围以及 SPI 匹配的 AH 变体,例如 –espspi 200:250 可以匹配整个 SPI 范围。

10.3.2. conntrack 匹配

Conntrack 匹配是状态匹配的扩展版本,可以更精细地匹配数据包。它可以让你直接查看连接跟踪系统中的信息,而无需任何 “前端 ”系统,如状态匹配。有关连接跟踪系统的更多信息,请参阅状态机一章。

在 Conntrack 匹配中,有许多不同的匹配项,分别针对连接跟踪系统中的几个不同字段。这些匹配信息汇总在下面的列表中。要加载这些匹配信息,需要指定 -m conntrack。

表 10-7. Conntrack 匹配选项

Match–ctstate
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m conntrack –ctstate RELATED
Explanation该匹配用于根据 conntrack 状态匹配数据包的状态。它用于匹配与原始state匹配中几乎相同的状态。该匹配的有效项包括:INVALID ESTABLISED NEW RELATED SNAT DNAT。例如,-m conntrack –ctstate ESTABLISHED,RELATED。也可以在 –ctstate前面加上 ! 例如 -m conntrack !–ctstateESTABLISHED,RELATED,它匹配除ESTABLISHEDRELATED状态之外的所有状态。
Match–ctproto
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m conntrack –ctproto TCP
Explanation与**–protocol** 的作用相同。它可以接受相同类型的值,并使用 ! 符号反转。例如,-m conntrack !–ctproto TCP 匹配除 TCP 协议外的所有协议。
Match–ctorigsrc
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m conntrack –ctorigsrc 192.168.0.0/24
Explanation–ctorigsrc 根据数据包相关的 conntrack 条目的原始源 IP 规格进行匹配。可以在 –ctorigsrc 和 IP 规格之间使用 ! 来反转匹配,如 –ctorigsrc !192.168.0.1. 也可以使用 CIDR 形式的网络掩码,如 –ctorigsrc 192.168.0.0/24
Match–ctorigdst
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m conntrack –ctorigdst 192.168.0.0/24
Explanation该匹配的使用方法与**–ctorigsrc** 完全相同,不同之处在于它匹配的是 conntrack 条目的目标字段。其他方面的语法都相同。
Match–ctreplsrc
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m conntrack –ctreplsrc 192.168.0.0/24
Explanation–ctreplsrc 匹配用于根据数据包的原始 conntrack 回复源进行匹配。基本上,这与**–ctorigsrc**相同,但我们匹配的是即将到来的数据包的回复源。当然,这个目标也可以反转,与本类中的前几个目标一样,可以寻址整个地址范围。
Match–ctrepldst
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m conntrack –ctrepldst 192.168.0.0/24
Explanation–ctrepldst 匹配与 –ctreplsrc 匹配相同,不同之处在于它匹配的是与数据包相匹配的 conntrack 条目的回复目的地。与**–ctreplsrc**匹配一样,它也可以反转,并接受范围。
Match–ctstatus
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m conntrack –ctstatus RELATED
Explanation这与状态机 章节中描述的连接状态相匹配。NONE - 该连接没有任何状态。EXPECTED - 该连接是预期连接,由某个预期处理程序添加。SEEN_REPLY - 该连接已看到回复,但尚未得到保证。ASSURED - 该连接已得到保证,不会被删除,直到超时或两端中的任何一端关闭连接。例如 -m conntrack !–ctstatus ASSURED 将匹配除 ASSURED 状态外的所有状态。
Match–ctexpire
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m conntrack –ctexpire 100:150
Explanation该匹配用于根据 conntrack 条目过期计时器的剩余时间(以秒为单位)来匹配数据包。它既可以取一个值进行匹配,也可以取一个范围(如上例)进行匹配。也可以使用 ! 号反转,如**-m conntrack !–ctexpire 100**。这将匹配所有过期时间,而过期时间不正好是 100 秒。

10.3.3. DSCP 匹配

该匹配用于根据数据包的 DSCP(差异化服务代码点)字段进行匹配。这在 RFC 2638 - A Two-bit Differentiated Services Architecture for the Internet RFC 中有详细说明。通过指定 -m dscp,可以显式加载匹配。匹配可以使用两个相互排斥的选项,如下所述。

表 10-8. DSCP 匹配选项

Match–dscp
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m dscp –dscp 32
Explanation该选项使用十进制或十六进制的 DSCP 值。如果选项值为十进制,则写成 32 或 16 等。如果写成十六进制,则前缀为 0x,如下所示: 0x20. 也可以使用 ! 字符反转,如下所示:-m dscp !–dscp 32.
Match–dscp-class
Kernel2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m dscp –dscp-class BE
Explanation–dscp-class 匹配用于匹配数据包的 DiffServ 类别。其值可以是各种 RFC 中指定的任何 BE、EF、AFxx 或 CSx 类别。与 –dscp 选项一样,这种匹配也可以反转。

注意:请注意,–dscp 和 –dscp-class 选项是相互排斥的,不能同时使用。

10.3.4. ECN 匹配

ECN 匹配用于匹配 TCP 和 IPv4 报头中的不同 ECN 字段。ECN 在 RFC 3168 - The Addition of Explicit Congestion Notification (ECN) to IP RFC 中有详细描述。在命令行中使用 -m ecn 可以显式加载匹配。ECN 匹配器有三个不同的选项,如下所述。

表 10-9. ECN 匹配选项

Match–ecn
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m ecn –ecn-tcp-cwr
Explanation如果 CWR(已收到拥塞窗口)位被设置,则该匹配用于匹配该位。设置 CWR 标志是为了通知连接的另一端点,他们已收到 ECE,并已对此做出反应。默认情况下,如果 CWR 位被设置,则会与之匹配,但也可以使用感叹号反向匹配。
Match–ecn-tcp-ece
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m ecn –ecn-tcp-ece
Explanation这种匹配可用于匹配 ECE(ECN-Echo)位。一旦其中一个端点收到一个由路由器设置了 CE 位的数据包,ECE 就会被设置。然后,端点会在返回的 ACK 数据包中设置 ECE,以通知另一个端点它需要减速。然后,另一个端点会发送一个 CWR 数据包,如**–ecn-tcp-cwr**解释中所述。如果设置了 ECE 位,则默认情况下会匹配,但也可以使用感叹号来反转。
Match–ecn-ip-ect
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m ecn –ecn-ip-ect 1
Explanation–ecn-ip-ect匹配用于匹配 ECT(ECN Capable Transport)编码点。ECT 编码点有几种用途。主要是通过将两个位中的一个设为 1 来协商连接是否支持 ECN。路由器也使用 ECT 将两个 ECT 编解码点都设为 1 来表示遇到拥塞。ECT 值均可在下面的 ECN Field in IP 表中找到。可以使用感叹号反转匹配,例如 !–ecn-ip-ect 2 将匹配所有 ECN 值,但不匹配 ECT(0) 编码点。在 iptables 中,有效值范围为 0-3。其值见上表。

表 10-10. IP 中的 ECN 字段

Iptables valueECTCE[Obsolete] RFC 2481 names for the ECN bits.
000Not-ECT, ie. non-ECN capable connection.
101ECT(1), New naming convention of ECT codepoints in RFC 3168.
210ECT(0), New naming convention of ECT codepoints in RFC 3168.
311CE (Congestion Experienced), Used to notify endpoints of congestion

10.3.5. Helper 匹配

与其他匹配方式相比,这是一种比较另类的匹配方式,因为它使用了一些特殊的语法。该匹配用于根据数据包与哪个 Conntrack 助手相关来匹配数据包。例如,我们来看看 FTP 会话。控制会话已打开,数据会话的端口/连接是在控制会话中协商确定的。ip_conntrack_ftp 辅助模块将找到这些信息,并在 conntrack 表中创建相关条目。现在,当数据包进入时,我们可以看到它与哪个协议相关,并根据使用了哪个辅助程序,在规则集中匹配数据包。

表 10-11. 辅助匹配选项

Match–helper
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m helper –helper ftp-21
Explanation–helper选项用于指定一个字符串值,告诉匹配器要匹配哪个连接器。其基本形式可以是 –helper irc。这就是语法与普通语法的不同之处。我们还可以选择只根据原始期望捕获的端口来匹配数据包。例如,FTP 控制会话通常通过 21 端口传输,但也可能是 954 端口或其他端口。然后,我们就可以指定应在哪个端口上捕获预期数据包,如 –helper ftp-954

10.3.6. IP range 匹配

IP 范围匹配用于匹配 IP 范围,就像 –source 和 –destination 匹配一样。不过,这种匹配增加了一种不同的匹配方式,即它能够以从 IP - 到 IP 的方式进行匹配,而 –source 和 –destination 匹配则无法做到这一点。在某些特定的网络设置中可能需要这种匹配方式,而且这种匹配方式更加灵活。

表 10-12. IP 范围匹配选项

Match–src-range
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m iprange –src-range 192.168.1.13-192.168.2.19
Explanation这与源 IP 地址的范围相匹配。该范围包括从第一个到最后一个的所有 IP 地址,因此上面的例子包括从 192.168.1.13 到 192.168.2.19 的所有地址。也可以通过添加**!来反转匹配。上例就会变成-m iprange !–src-range 192.168.1.13-192.168.2.19**,这将匹配所有 IP 地址,指定的地址除外。
Match–dst-range
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m iprange –dst-range 192.168.1.13-192.168.2.19
Explanation–dst-range的工作原理与**–src-range**匹配完全相同,只是它匹配的是目标 IP 而不是源 IP。

10.3.8. Limit 匹配

限制匹配扩展必须使用 -m limit 选项显式加载。例如,这种匹配可以用来限制特定规则的日志记录等。例如,可以用它来匹配所有不超过给定值的数据包,并在超过该值后限制记录相关事件。考虑时间限制:您可以限制特定规则在一定时间内的匹配次数,例如减少 DoS syn flood 攻击的影响。这是它的主要用途,当然还有更多用途。限制匹配也可以通过在限制匹配前添加 ! 标志来反转。这样就可以表示为 -m limit ! –limit 5/s。这意味着所有数据包在突破限制后都将被匹配。

为了进一步解释限制匹配,它基本上就是一个令牌桶过滤器。假设有一个泄漏桶,该桶在每个时间单位内泄漏 X 个数据包。X 的定义取决于我们获得匹配数据包的数量,因此,如果我们获得 3 个数据包,那么该桶在该时间单位内就会泄漏 3 个数据包。–limit(限制)选项告诉我们在每个时间单位内要给水桶补充多少个数据包,而–limit-burst(限制-突发)选项则告诉我们水桶最初有多大。因此,设置–limit 3/minute –limit-burst 5,然后收到 5 个匹配包,就会清空邮筒。20 秒后,水桶会重新装满另一个令牌,以此类推,直到再次达到–limit-burst 或它们被用完为止。

请参考下面的示例,进一步了解这种情况。

  1. 我们用 -m limit –limit 5/second –limit-burst 10/second 设置了一条规则。limit-burst 标记桶初始设置为 10。每个符合规则的数据包都使用一个标记。
  2. 我们会收到 1-2-3-4-5-6-7-8-9-10 个符合规则的数据包,所有数据包都在 1/1000 秒内到达。
  3. 令牌桶现在是空的。令牌桶清空后,符合规则的数据包将不再与规则匹配,并进入下一条规则(如果有),或进入链式策略。
  4. 每 1/5 秒没有匹配的数据包,令牌计数就会增加 1,最多不超过 10。收到 10 个数据包 1 秒后,我们将再次剩下 5 个令牌。
  5. 当然,每收到一个数据包,就会清空 1 个令牌。

表 10-14. 限制匹配选项

Match–limit
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m limit –limit 3/hour
Explanation这将设置limit 匹配的最大平均匹配率。您可以用一个数字和一个可选的时间单位来指定它。目前可识别的时间单位如下 /second /minute /hour /day. 这里的默认值是每小时 3 个,即 3/hour。这将告诉 limit 匹配在每个时间单位(如每分钟)内允许匹配多少次。
Match–limit-burst
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m limit –limit-burst 5
Explanation这是limit匹配的burst limit的设置。它告诉iptables在给定时间单位内匹配的最大数据包数量。如果事件重复发生,计数器将再次递增,直到达到突发限制。以此类推。默认的**–limit-burst**值是 5。要想简单地了解其工作原理,可以使用示例 Limit-match.txt 单规则脚本。使用该脚本,您只需以不同的时间间隔和不同的突发数发送 ping 数据包,就能亲眼看到限制规则是如何工作的。所有回声回复都将被阻止,直到再次达到突发限制的阈值。

10.3.9. MAC 匹配

MAC(以太网媒体访问控制)匹配可用于根据数据包的 MAC 源地址进行匹配。在撰写本文档时,这种匹配方式还受到一些限制,但将来可能会有更多发展,也可能会更有用。如前所述,这种匹配方法只能用于根据 MAC 源地址匹配数据包。

注意:请注意,要使用该模块,我们必须使用 -m mac 选项显式加载它。我这样说的原因是,很多人都想知道是否不应该使用 -m mac-source(其实不应该)。

表 10-15. MAC 匹配选项

Match–mac-source
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m mac –mac-source 00:00:00:00:00:01
Explanation此匹配用于根据数据包的 MAC 源地址进行匹配。指定的 MAC 地址必须是XX:XX:XX:XX:XX:XX,否则不合法。匹配可以用 ! 符号反转,看起来像 –mac-source !00:00:00:00:00:01. 换句话说,这将反转匹配的含义,因此除了来自该 MAC 地址的数据包外,其他所有数据包都将被匹配。请注意,由于 MAC 地址只用于以太网类型的网络,因此这种匹配只能用于以太网接口。MAC 匹配只在 PREROUTING、FORWARD 和 INPUT 链中有效,在其他地方无效。

10.3.10. Mark 匹配

标记匹配扩展用于根据数据包设置的标记来匹配数据包。标记是一个特殊字段,只在内核中维护,在数据包通过计算机时与之关联。不同的内核例程可以使用标记来完成流量整形和过滤等任务。目前,Linux 中只有一种设置标记的方法,即 iptables 中的 MARK 目标。以前是通过 ipchains 中的 FWMARK 目标来实现这一功能的,这也是为什么人们在高级路由领域仍然使用 FWMARK 的原因。标记字段目前设置为无符号整数,在 32 位系统中可能的值为 4294967296。换句话说,在相当长的一段时间内,您可能不会遇到这个限制。

表 10-16. 标记匹配选项

Match–mark
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -t mangle -A INPUT -m mark –mark 1
Explanation该匹配用于匹配先前已被标记的数据包。标记可以用MARK 目标来设置,我们将在下一节讨论。所有通过 Netfilter 的数据包都会有一个特殊的标记字段。请注意,这个标记字段不会以任何方式在数据包内外传播。它只保留在制作数据包的计算机内。如果标记字段与标记相匹配,则表示匹配。标记字段是一个无符号整数,因此最多可以有 4294967296 个不同的标记。您也可以在标记中使用掩码。例如,–mark 1/1。如果指定了掩码,那么在实际比较之前,掩码将与指定的标记进行逻辑且计算。

10.3.11. Multiport 匹配 多端口匹配扩展可用于指定多个目标端口和端口范围。如果没有多端口匹配功能,就必须使用多个相同类型的规则来匹配不同的端口。

注意:不能同时使用标准端口匹配和多端口匹配,例如,不能写入 –sport 1024:63353 -m multiport –dport 21,23,80。这根本行不通。事实上,如果你这样做,iptables 会尊重规则中的第一个元素,而忽略多端口指令。

表 10-17. 多端口匹配选项

Match–source-port
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m multiport –source-port 22,53,80,110
Explanation这种匹配方式可匹配多个源端口。最多可指定 15 个独立端口。端口必须以逗号分隔,如上例。该匹配只能与**-p tcp** 或**-p udp** 匹配一起使用。它主要是普通**–source-port**匹配的增强版。
Match–destination-port
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m multiport –destination-port 22,53,80,110
Explanation该匹配用于匹配多个目的端口。它的工作方式与上述源端口匹配完全相同,只是它匹配的是目的端口。它也有 15 个端口的限制,只能与**-p tcp** 和**-p udp** 结合使用。
Match–port
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m multiport –port 22,53,80,110
Explanation该匹配扩展可用于匹配基于目的端口和源端口的数据包。其工作方式与上述**–source-port–destination-port匹配相同。它最多可使用 15 个端口,并且只能与 -p tcp-p udp 结合使用。请注意,–port** 匹配只会匹配从同一端口进入和进入同一端口的数据包,例如,端口 80 到端口 80,端口 110 到端口 110,以此类推。

10.3.12. Owner 匹配

所有者匹配扩展用于根据创建数据包的进程身份来匹配数据包。所有者可以指定为发出相关命令的用户的进程 ID、组的进程 ID、会话的进程 ID 或命令本身的进程 ID。该扩展最初是作为 iptables 的一个示例而编写的。由于显而易见的原因,所有者匹配只在 OUTPUT 链中有效: 几乎不可能找到从另一端发送数据包的实例的任何身份信息,也不可能找到通往真正目的地的中间跳。即使在 OUTPUT 链中也不太可靠,因为某些数据包可能没有所有者。这类臭名昭著的数据包包括(除其他外)不同的 ICMP 响应。ICMP 响应永远不会匹配。

表 10-18. 所有者匹配选项

Match–uid-owner
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m owner –uid-owner 500
Explanation如果数据包是由给定的用户 ID(UID)创建的,则该数据包匹配将与之匹配。这可用于根据创建者匹配传出数据包。一个可能的用途是阻止 root 以外的任何其他用户在防火墙外打开新连接。另一种可能的用途是阻止除 http 用户以外的所有人从 HTTP 端口发送数据包。
Match–gid-owner
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m owner –gid-owner 0
Explanation该匹配用于根据数据包的组 ID (GID) 匹配所有数据包。也就是说,我们根据创建数据包的用户所在的组来匹配所有数据包。这可以用来阻止除网络组用户以外的所有用户上网,或者如上所述,只允许 http 组的成员创建从 HTTP 端口发出的数据包。
Match–pid-owner
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m owner –pid-owner 78
Explanation这种匹配是根据负责发送数据包的进程 ID (PID) 来匹配数据包。这种匹配比较难用,但有一个例子是只允许 PID 94 从 HTTP 端口发送数据包(当然,如果 HTTP 进程没有线程)。另外,我们还可以编写一个小脚本,从ps输出中获取特定守护进程的 PID,然后为其添加一条规则。例如,可以使用 Pid-owner.txt 示例中的规则。
Match–sid-owner
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m owner –sid-owner 100
Explanation这种匹配是根据相关程序使用的会话 ID 来匹配数据包。SID 或进程的会话 ID 的值是进程本身以及由原始进程产生的所有进程的值。后者可以是线程,也可以是原始进程的子进程。因此,举例来说,如果我们的 HTTPD 是线程式的(大多数 HTTPD 都是线程式的,例如 Apache 和 Roxen),那么所有 HTTPD 进程的 SID 都应该与它们的父进程(原始 HTTPD 进程)相同。为了举例说明这一点,我们创建了一个名为Sid-owner.txt 的小脚本。这个脚本可以每隔一小时左右运行一次,并添加一些额外的代码来检查 HTTPD 是否真的在运行,并在必要时再次启动它,然后在需要时刷新并重新输入我们的 OUTPUT 链。

10.3.13. Packet type 匹配

数据包类型匹配用于根据数据包的类型对其进行匹配。也就是说,这些数据包是发送给某个特定的人、每个人,还是发送给某个特定的机器或用户组。这三组通常称为单播、广播和组播,详见 TCP/IP 重复章节。通过使用 -m pkttype 加载匹配。

表 10-19. 数据包类型匹配选项

Match–pkt-type
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m pkttype –pkt-type unicast
Explanation–pkt-type 匹配用于告诉数据包类型匹配器匹配哪种数据包类型。它可以将单播、广播或组播作为参数,如示例所示。它也可以通过使用 **! ** 例如 -m pkttype –pkt-type ! broadcast,这将匹配所有其他数据包类型。

10.3.14. Recent 匹配

最近匹配是一个相当庞大和复杂的匹配系统,它允许我们根据以前匹配过的最近事件来匹配数据包。例如,如果我们看到一个传出的 IRC 连接,我们可以将 IP 地址设置为主机列表,并制定另一条规则,允许在看到原始数据包后 15 秒内从 IRC 服务器发回 identd 请求。

在仔细研究这种匹配之前,我们先来解释一下它是如何工作的。首先,我们使用几种不同的规则来实现最近匹配。最近匹配使用几个不同的最近事件列表。默认使用的列表是 DEFAULT 列表。我们使用设置选项在列表中创建一个新条目,因此一旦规则完全匹配(设置选项总是匹配),我们也会在指定的最近列表中添加一个条目。列表条目包含时间戳和触发设置选项的数据包中使用的源 IP 地址。一旦这样做了,我们就可以使用一系列不同的最近选项来匹配这些信息,并更新条目的时间戳等。

最后,如果我们出于某种原因想要删除列表条目,可以使用最近模块中的删除匹配来实现。所有使用最近匹配的规则都必须像往常一样加载最近模块(-m recent)。在举例说明最近匹配之前,我们先来看看所有的选项。

表 10-20. 最近匹配选项

Match–name
Kernel2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m recent –name examplelist
Explanationname 选项给出了要使用的列表名称。默认情况下使用 DEFAULT 列表,如果使用多个列表,这可能不是我们想要的。
Match–set
Kernel2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m recent –set
Explanation这将在已命名的最近列表中创建一个新的列表条目,其中包含时间戳和触发规则的主机源 IP 地址。该匹配将始终返回成功,除非前面有 ! 符号,否则将返回失败。
Match–rcheck
Kernel2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m recent –name examplelist –rcheck
Explanation–rcheck 选项将检查数据包的源 IP 地址是否在指定列表中。如果是,匹配结果将返回 “true”,否则返回 “false”。该选项可以使用 ! 符号反转。在后一种情况下,如果源 IP 地址不在列表中,则返回 true;如果在列表中,则返回 false。
Match–update
Kernel2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m recent –name examplelist –update
Explanation如果源组合在指定列表中可用,则该匹配为真,同时也会更新列表中最后一次看到的时间。也可以通过在匹配前面设置**!** 例如,!–update
Match–remove
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m recent –name example –remove
Explanation此匹配将尝试在列表中查找数据包的源地址,如果数据包在其中,则返回 true。它还会从列表中删除相应的列表条目。该命令还可以用 **! ** 符号反转。
Match–seconds
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m recent –name example –check –seconds 60
Explanation该匹配只能与**–check–update匹配一起使用。–seconds**匹配用于指定最近列表中 “上次查看 ”列的更新时间。如果 “上次查看 ”列的更新时间超过了这个秒数,匹配结果将返回错误。除此以外,最近匹配与正常匹配一样,因此源地址必须仍在列表中,匹配才会返回真值。
Match–hitcount
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m recent –name example –check –hitcount 20
Explanation–hitcount匹配必须与**–check–update匹配一起使用,它将限制匹配只包括至少看到过 hitcount 数量的数据包。如果此匹配与–seconds匹配一起使用,它将要求在特定的时间范围内看到指定的 hitcount 数据包。也可以在匹配前面添加 符号来反向匹配。与–seconds**匹配一起,这意味着在指定的时间范围内最多只能看到这个数量的数据包。如果两个匹配项都相反,则在最后的最小秒数内最多只能看到这个数量的数据包。
Match–rttl
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m recent –name example –check –rttl
Explanation–rttl匹配用于验证当前数据包的 TTL 值是否与用于在最近列表中设置原始条目的原始数据包相同。这可以用来验证是否有人利用最近匹配来欺骗自己的源地址,从而拒绝他人访问你的服务器。
Match–rsource
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m recent –name example –rsource
Explanation–rsource匹配用于告诉最近匹配将源地址和端口保存在最近列表中。这是最近匹配的默认行为。
Match–rdest
Kernel2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m recent –name example –rdest
Explanation–rdest匹配与**–rsource**匹配相反,它告诉最近匹配将目标地址和端口保存到最近列表中。

我创建了一个关于如何使用最近匹配的小型示例脚本,您可以在 Recent-match.txt 部分找到它。

简而言之,这是 netfilter 中可用的状态引擎的劣质替代品。这个版本是以 http 服务器为对象创建的,但也适用于任何 TCP 连接。首先,我们创建了两个链,分别名为 http-recent 和 http-recent-final。http-recent 链用于连接的起始阶段和实际数据传输,而 http-recent-final 链则用于最后的 FIN、FIN/ACK 握手。

警告:这是一个非常糟糕的内置状态引擎替代品,无法处理状态引擎可以处理的所有可能性。不过,它是一个很好的示例,说明了在不太具体的情况下,可以用最近的匹配做些什么。不要在现实环境中使用这个示例。它运行速度慢,对特殊情况的处理能力差,一般只能作为示例使用。

例如,它不能处理连接端口关闭、非同步 FIN 握手(连接的一方关闭,而另一方继续发送数据)等情况。

让我们通过示例规则集跟踪一个数据包。首先,数据包进入 INPUT 链,然后我们将其发送到 http-recent 链。

  1. 第一个数据包应该是 SYN 数据包,并且不应该设置 ACK、FIN 或 RST 位。因此,我们使用 –tcp-flags SYN,ACK,FIN,RST SYN 行对其进行匹配。此时,我们使用 -m recent –name httplist –set 行将连接添加到 httplist 中。最后,我们接受数据包。
  2. 在第一个数据包之后,我们应该会收到一个 SYN/ACK 数据包,以确认收到了 SYN 数据包。这可以使用 –tcp-flags SYN,ACK,FIN,RST SYN,ACK 行进行匹配。FIN 和 RST 此时也应为非法。此时,我们使用 -m recent –name httplist –update 更新 httplist 中的条目,最后接受数据包。
  3. 此时,我们应该会从连接的原始创建者那里收到最后一个 ACK 数据包,以确认服务器发送的 SYN/ACK。此时,SYN、FIN 和 RST 都是非法的,所以这一行应该是 –tcp-flags SYN,ACK,FIN,RST ACK。我们以与上一步完全相同的方式更新列表,并接受它。
  4. 至此,数据传输开始。现在的连接不应包含任何 SYN 数据包,但会包含 ACK 数据包,以确认发送的数据包。每次看到这样的数据包时,我们都会更新列表并接受数据包。
  5. 传输可以通过两种方式结束,最简单的是 RST 数据包。RST 只需重置连接,连接就会终止。使用 FIN 时,另一个端点会回复 FIN、ACK,从而关闭连接,这样 FIN 的原始数据源就不能再发送任何数据。FIN 的接收方仍能发送数据,因此我们将连接发送到一个 “最后 ”阶段链,以处理剩下的工作。
  6. 在 http-recent-final 链中,我们会检查数据包是否还在 httplist 中,如果还在,我们就把它发送到 http-recent-final1 链。在该链中,我们会从 httplist 中移除连接,并将其添加到 http-recent-final 列表中。如果连接已被移除并转到 http-recent-final 列表,我们就会把数据包发送到 http-recent-final2 链。
  7. 在最后的 http-recent-final2 链中,我们会等待未关闭的一方发送完数据,并关闭其连接。一旦完成,连接就会被完全删除。

正如你所看到的,最近列表可能会变得相当复杂,但它会在必要时为你提供大量的可能性。不过,请记住不要重复发明轮子。如果您需要的功能已经实现,请尝试使用它,而不是试图创建自己的解决方案。

10.3.15. State 匹配

状态匹配扩展与内核中的连接跟踪代码结合使用。状态匹配从连接跟踪机器访问数据包的连接跟踪状态。这样我们就能知道连接处于什么状态,并适用于几乎所有协议,包括 ICMP 和 UDP 等无状态协议。在所有情况下,连接都会有一个默认超时时间,然后会从连接跟踪数据库中删除。这种匹配需要通过在规则中添加 -m state 语句来显式加载。这样,你就可以访问一个名为 state 的新匹配。状态匹配的概念在 “状态机 ”一章中会有更全面的介绍,因为这是一个很大的主题。

表 10-21. 状态匹配

Match–state
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -m state –state RELATED,ESTABLISHED
Explanation这个匹配选项告诉state 匹配,数据包必须处于什么状态才能被匹配。目前有 4 种状态可以使用。INVALIDESTABLISHEDNEWRELATEDINVALID 表示数据包与已知数据流或连接无关,可能包含错误数据或报头。ESTABLISHED 表示数据包是已经建立的连接的一部分,该连接在两个方向上都接收过数据包,并且完全有效。NEW 表示数据包已经或将要启动一个新的连接,或者它与一个尚未在两个方向上都看到过数据包的连接相关联。最后,RELATED 表示数据包正在启动一个新连接,并与一个已建立的连接相关联。例如,这可能意味着 FTP 数据传输,或与 TCP 或 UDP 连接相关的 ICMP 错误。请注意,NEW 状态不会在试图启动新连接的 TCP 数据包中查找 SYN 位,因此,在只有一个防火墙且不同防火墙之间没有负载平衡的情况下,不应在未修改的情况下使用NEW 状态。不过,有时这可能会有用。有关如何使用的更多信息,请阅读状态机 一章。

10.3.16. TCPMSS 匹配

tcpmss 匹配用于根据 TCP 中的最大分段大小匹配数据包。此匹配仅对 SYN 和 SYN/ACK 数据包有效。有关 MSS 值的更完整解释,请参阅 TCP 选项附录、RFC 793 - 传输控制协议和 RFC 1122 - 互联网主机要求 - 通信层文档。该匹配使用 -m tcpmss 加载,只有一个选项。

表 10-22. TCPMSS 匹配选项

Match–mss
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp –tcp-flags SYN,ACK,RST SYN -m tcpmss –mss 2000:2500
Explanation–mss 选项告诉 tcpmss 要匹配的最大分段大小。这个值可以是一个特定的 MSS 值,也可以是一系列用 : 分隔的 MSS 值。也可以使用 ! 符号反转该值,如下面的示例:-m tcpmss !–mss 2000:2500。此示例将匹配所有 MSS 值,2000 至 2500 范围内的值除外。

10.3.17. TOS 匹配

TOS 匹配可用于根据数据包的 TOS 字段进行匹配。TOS 代表服务类型,由 8 位组成,位于 IP 头中。通过在规则中添加 -m tos,可以显式加载这种匹配。TOS 通常用于告知中间主机数据流及其内容的优先级(其实不然,但它会告知对数据流的任何特定要求,如必须以最快速度发送,或需要发送尽可能多的有效载荷)。不同路由器和管理员处理这些值的方式各不相同。大多数路由器根本不关心,而其他路由器则会尽力处理好相关数据包及其提供的数据。

表 10-23. TOS 匹配

Match–tos
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A INPUT -p tcp -m tos –tos 0x16
Explanation这种匹配的使用方法如上所述。它可以根据数据包的 TOS 字段及其值进行匹配。除其他用途外,它还可与 Linux 中的iproute2和高级路由功能一起用于标记数据包,以便日后使用。该匹配以十六进制或数值作为选项,也可能是 “iptables -m tos -h”产生的名称之一。在撰写本文时,它包含以下命名值: 最小化延迟 16 (0x10),最大化吞吐量 8 (0x08),最大化可靠性 4 (0x04),最小化成本 2 (0x02),正常服务 0 (0x00)。最小化延迟是指最大限度地减少数据包的传输延迟,需要这样做的标准服务包括 telnet、SSH 和 FTP 控制。Maximize-Throughput(最大吞吐量)是指找到一条允许尽可能大吞吐量的路径–标准协议是 FTP-data。可靠性最大化是指最大限度地提高连接的可靠性,并尽可能使用可靠的线路–几个典型的例子就是 BOOTP 和 TFTP。最小化成本是指最大限度地降低数据包通过各链路到达客户端或服务器的成本;例如,寻找传输成本最低的线路。使用此功能的正常协议包括 RTSP(实时流控制协议)和其他流媒体视频/无线电协议。最后,“正常服务 ”指的是没有特殊需求的任何正常协议。

10.3.18. TTL 匹配

TTL 匹配用于根据 IP 头中的 TTL(生存时间)字段匹配数据包。TTL 字段包含 8 位数据,每次由客户端和接收端主机之间的中间主机处理时都会递减一次。如果 TTL 为 0,就会向发送数据包的一方发送 ICMP 类型 11 代码 0(传输过程中 TTL 等于 0)或代码 1(重新组装过程中 TTL 等于 0),告知其问题所在。这种匹配只用于根据数据包的 TTL 进行匹配,而不会改变任何内容。顺便提一下,后者适用于所有类型的匹配。要加载此匹配,需要在规则中添加 -m ttl。

表 10-24. TTL 匹配

Match–ttl
Kernel2.3, 2.4, 2.5 and 2.6
Exampleiptables -A OUTPUT -m ttl –ttl 60
Explanation该匹配选项用于指定要匹配的 TTL 值。它使用一个数值,并在数据包中匹配该值。没有反转,也没有其他具体的匹配条件。例如,它可用于调试本地网络–如局域网主机在连接互联网主机时似乎出现问题-或查找木马等可能的入口。不过,使用范围相对有限;其实用性完全取决于你的想象力。其中一个例子是查找默认 TTL 值不良的主机(可能是由于 TCP/IP 协议栈实施不良,也可能只是配置错误)。

10.3.19. Unclean 匹配

unclean 匹配不需要任何选项,只需在使用时明确加载即可。需要注意的是,该选项被视为试验性的,不一定在任何时候都能起作用,也不能解决所有不干净或有问题的包。unclean匹配会尝试匹配看似畸形或不正常的数据包,如带有不良报头或校验和的数据包等。例如,它可以用来 DROP 连接和检查不良数据流;但你应该知道,它可能会破坏合法连接。

第 11 章 Iptables targets 和 jumps

target/jumps 告诉规则如何处理与规则匹配部分完全匹配的数据包。有几个基本目标,即 ACCEPT 和 DROP 目标,我们将首先处理这两个目标。不过,在此之前,让我们先简单了解一下跳转是如何完成的。

跳转指定的方式与目标定义完全相同,只是需要跳转到同一表中的链。当然,要跳转到特定的链,前提条件是该链必须存在。正如我们已经解释过的,用户定义的链是用 -N 命令创建的。例如,我们在过滤表中创建一个名为 tcp_packets 的链,如下所示

iptables -N tcp_packets

然后,我们可以像这样添加一个跳转目标:

iptables -A INPUT -p tcp -j tcp_packets

然后,我们将从 INPUT 链跳转到 tcp_packets 链,并开始遍历该链。当我们到达该链的末端,会返回 INPUT 链,然后数据包从它跳转到另一条链(本例中为 tcp_packets)的下一步规则开始遍历。如果数据包在其中一个子链中被 ACCEPT,那么它在超集链中也会被 ACCEPT,不会再遍历任何超集链。但请注意,数据包将以正常方式遍历其他表中的所有其他链。有关表和链遍历的更多信息,请参阅表和链的遍历章节。

另一方面,目标指定了要对相关数据包采取的操作。例如,我们可以根据需要对数据包进行 DROP 或 ACCEPT。此外,我们还可以采取一些其他操作,本节将进一步介绍。跳转目标可能会导致不同的结果。有些目标会导致数据包停止遍历该特定链和上级链,如上所述。DROP 和 ACCEPT 就是这类规则的很好例子。被停止的规则将不会通过链上或上级链中的任何规则。其他目标可能会对数据包采取某种措施,然后数据包将继续通过其他规则。一个很好的例子就是 LOG、ULOG 和 TOS 目标。这些目标可以记录数据包,对其进行处理,然后将其传递给同一组链中的其他规则。例如,我们可能需要这样做,这样我们就可以同时修改特定数据包/流的 TTL 和 TOS 值。有些目标会接受额外的选项(使用什么 TOS 值等),而有些目标则不一定需要任何选项,但我们可以根据需要加入这些选项(日志前缀、伪装端口等)。我们将尝试在目标描述中涵盖所有这些内容。让我们看看有哪些类型的目标。

11.1. ACCEPT 目标

该目标无需其他选项。只要完全满足了数据包的匹配规范,并且我们指定 ACCEPT 为目标,规则就会被接受,并且不会继续遍历当前链或同一表中的任何其他链。但要注意的是,在一个链中被接受的数据包仍有可能穿越其他表中的链,并有可能在那里被丢弃。这个目标没有什么特别之处,它不需要也不可能为目标添加选项。要使用此目标,我们只需指定 -j ACCEPT。

注意:适用于 Linux 内核 2.3、2.4、2.5 和 2.6。

11.2. CLASSIFY 目标

CLASSIFY 目标可用于对数据包进行分类,以便供多个不同的 qdiscs(队列规范)使用。例如,atm、cbq、dsmark、pfifo_fast、htb 和 prio qdiscs。有关 qdiscs 和流量控制的更多信息,请访问 Linux 高级路由和流量控制 HOW-TO 网页。

CLASSIFY 目标仅在 mangle 表的 POSTROUTING 链中有效。

表 11-1. CLASSIFY 目标选项

Option–set-class
Exampleiptables -t mangle -A POSTROUTING -p tcp –dport 80 -j CLASSIFY –set-class 20:10
ExplanationCLASSIFY目标只接受一个参数,即**–set-class**。它告诉目标如何对数据包进行分类。class有两个值,以逗号分隔,如 MAJOR:MINOR。再次强调,如果你想了解更多相关信息,请查看Linux 高级路由和流量控制 HOW-TO网页。

注意:可在 Linux 内核 2.5 和 2.6 下运行。

11.3. DNAT 目标

DNAT 目标用于进行目标网络地址转换,即重写数据包的目标 IP 地址。如果一个数据包与此规则目标相匹配,则该数据包和同一数据流中的所有后续数据包都将被翻译,然后路由到正确的设备、主机或网络。例如,当你在局域网内有一台运行网络服务器的主机,但没有能在互联网上使用的真正 IP 时,这个目标就非常有用。这时,你可以告诉防火墙将所有指向它自己的 HTTP 端口的数据包转发到局域网内真正的网络服务器上。我们还可以指定一系列目标 IP 地址,DNAT 机制将为每个数据流随机选择目标 IP 地址。因此,我们可以通过这种方式实现负载平衡。

请注意,DNAT 目标只能在 nat 表中的 PREROUTING 和 OUTPUT 链,以及从这些链中调用的任何链中使用。请注意,包含 DNAT 目标的链不能用于任何其他链,如 POSTROUTING 链。

表 11-2. DNAT 目标

Option–to-destination
Exampleiptables -t nat -A PREROUTING -p tcp -d 15.45.23.67 –dport 80 -j DNAT –to-destination 192.168.1.1-192.168.1.10
Explanation–to-destination选项告诉 DNAT 机制在 IP 头中设置哪个目标 IP,并将匹配的数据包发送到哪里。上述示例将把以 IP 地址 15.45.23.67 为目的地的所有数据包发送到一个 LAN IP 范围,即 192.168.1.1 至 10。请注意,如前所述,单个数据流将始终使用相同的主机,每个数据流将随机获得一个 IP 地址,在该数据流中,它将始终以该 IP 地址为目标地址。我们也可以只指定一个 IP 地址,这样就可以始终连接到同一台主机。还要注意的是,我们可以添加一个端口或端口范围,将流量重定向到该端口或端口范围。例如,我们可以在要 DNAT 数据包的 IP 地址上添加 :80 语句。例如,一条规则可以是 –to-destination 192.168.1.1:80,如果要指定端口范围,则可以是 –to-destination 192.168.1.1:80-100。正如你所看到的,DNAT 目标和SNAT 目标的语法基本相同,尽管它们做的是完全不同的事情。请注意,端口规范只适用于使用 –protocol 选项指定 TCP 或 UDP 协议的规则。

由于 DNAT 需要很多工作才能正常工作,我决定对如何使用 DNAT 进行更详细的说明。让我们举一个简单的例子来说明正常情况下如何操作。我们想通过互联网连接发布我们的网站。我们只有一个 IP 地址,HTTP 服务器位于内部网络。防火墙的外部 IP 地址是 $INET_IP,HTTP 服务器的内部 IP 地址是 $HTTP_IP,最后防火墙的内部 IP 地址是 $LAN_IP。首先要做的是在 nat 表的 PREROUTING 链中添加以下简单规则:

iptables -t nat -A PREROUTING --dst $INET_IP -p tcp --dport 80 -j DNAT --to-destination $HTTP_IP

现在,所有从互联网发送到防火墙 80 端口的数据包都会被重定向(或 DNAT)到我们的内部 HTTP 服务器。如果从互联网上进行测试,一切都应该运行正常。那么,如果尝试从与 HTTP 服务器位于同一本地网络的主机进行连接,会发生什么情况呢?根本无法正常工作。这其实就是路由问题。我们先来分析一下正常情况下会发生什么。为了保持可读性,外部网络的 IP 地址为 $EXT_BOX。

  1. 数据包离开连接主机,前往 $INET_IP ,源地址是 $EXT_BOX。

  2. 数据包到达防火墙。

  3. 防火墙 DNAT 数据包,并通过所有不同的链等运行数据包。

  4. 数据包离开防火墙,到达 $HTTP_IP。

  5. 数据包到达 HTTP 服务器,如果路由数据库将 HTTP 端口设置为 $EXT_BOX 的网关,则 HTTP 端口会通过防火墙回复数据包。通常,这就是 HTTP 服务器的默认网关。

    此处的意思就是,从http服务器响应的数据包目的IP是$EXT_BOX,与http服务器的IP不在同一个网络,因此该数据包会被发送到默认网关,经由Un-DNAT,该数据包的源IP是$INET_IP

  6. 防火墙再次对数据包进行 Un-DNAT,因此数据包看起来就像是从防火墙本身回复的。

  7. 回复数据包照常回到客户端 $EXT_BOX。

现在,我们来看看如果数据包是由与 HTTP 服务器处于同一网络的客户端生成的,会发生什么情况。客户端的 IP 地址是 $LAN_BOX,而其他机器的设置保持不变。

  1. 数据包从 $LAN_BOX 发送到 $INET_IP。
  2. 数据包到达防火墙。
  3. 数据包被 DNAT’ed,并执行所有其他必要操作,但数据包未被 SNAT’ed,因此数据包使用相同的源 IP 地址。
  4. 数据包离开防火墙,到达 HTTP 服务器。
  5. HTTP 服务器尝试响应数据包,并在路由数据库中发现数据包来自同一网络的本地机箱,因此尝试将数据包直接发送到原始源 IP 地址(现在变成了目标 IP 地址)。
  6. 数据包到达客户机后,客户机感到困惑,因为返回的数据包并非来自发送原始请求的主机。因此,客户端丢弃了回复数据包,等待 “真正的 ”回复。

解决这个问题的简单方法是对所有进入防火墙的数据包进行 SNAT,并对我们知道的主机或 IP 进行 DNAT。例如,考虑上述规则。我们对进入防火墙的以 $HTTP_IP 80 端口为目标的数据包进行 SNAT,使它们看起来像是来自 $LAN_IP。这将迫使 HTTP 服务器将数据包发回我们的防火墙,而我们的防火墙会解除对数据包的 SNAT,并将其发送给客户端。规则如下:

iptables -t nat -A POSTROUTING -p tcp --dst $HTTP_IP --dport 80 -j SNAT --to-source $LAN_IP

请记住,POSTROUTING 链是最后处理的链,因此数据包一旦到达该特定链,就已经被 DNAT 了。这就是我们根据内部地址匹配数据包的原因。

警告:最后一条规则会严重损害您的日志记录,因此建议不要使用这种方法,但整个示例仍然有效。会发生的情况是这样的:数据包来自互联网,被 SNAT 和 DNAT,最后到达 HTTP 服务器(如上)。HTTP 服务器现在只看到来自防火墙的请求,因此会将所有来自互联网的请求当作来自防火墙的请求来记录。

这还会产生更严重的影响。以局域网上的 SMTP 服务器为例,它允许来自内部网络的请求,而防火墙的设置是将 SMTP 流量转发给它。现在你实际上已经创建了一个开放式中继 SMTP 服务器,而且日志记录非常糟糕!

解决这个问题的一个办法是,只需让 SNAT 规则的匹配部分更加具体,而且只对从局域网接口进来的数据包有效。换句话说,在整个命令中添加 –src $LAN_IP_RANGE。这将使规则只对来自局域网的数据流起作用,因此不会影响源 IP,所以除了来自局域网的数据流外,日志看起来都是正确的。

换句话说,要解决这些问题,最好的办法是为局域网设置一个单独的 DNS 服务器,或者实际设置一个单独的 DMZ,如果有钱,最好选择后者。

你认为这些已经足够了,确实如此,除非考虑到整个方案的最后一个方面。如果防火墙本身试图访问 HTTP 服务器,它会去哪里?从现在的情况来看,很不幸,它会尝试访问自己的 HTTP 服务器,而不是驻留在 $HTTP_IP 上的服务器。为了解决这个问题,我们还需要在 OUTPUT 链中添加 DNAT 规则。按照上面的示例,应该如下所示:

iptables -t nat -A OUTPUT -dst $INET_IP -p tcp -dport 80 -j DNAT --to-destination $HTTP_IP

添加这最后一条规则后,一切就可以正常运行了。所有与 HTTP 服务器不在同一网络上的独立网络都能顺利运行,所有与 HTTP 服务器在同一网络上的主机都能连接,最后,防火墙也能正常连接。现在一切正常,不会出现任何问题。

注意:每个人都应该认识到,这些规则只会影响数据包如何被正确 DNAT 和 SNAT。除了这些规则外,您可能还需要在过滤表(FORWARD 链)中添加额外的规则,以允许数据包通过这些链。不要忘记,所有数据包都已通过 PREROUTING 链,因此其目标地址应已被 DNAT 重写。

注意:可在 Linux 内核 2.3、2.4、2.5 和 2.6 下运行。

11.4. DROP 目标

DROP 目标的作用和它说的一样,就是将数据包丢弃,不再进行任何处理。完全符合规则的数据包会被拦截,然后被丢弃。请注意,这种操作在某些情况下可能会产生不必要的影响,因为它可能会在任一主机上留下死套接字。在可能出现这种情况的情况下,更好的解决办法是使用 REJECT 目标,尤其是当你想阻止端口扫描程序获取过多信息时,如被过滤的端口等。还要注意的是,如果一个数据包在子链中被执行了 DROP 操作,那么该数据包将不会在当前或其他表中的任何主链中被处理。换句话说,该数据包已完全死亡。如前所述,目标不会向任何方向发送任何信息,也不会向路由器等中介发送信息。

注意:可在 Linux 内核 2.3、2.4、2.5 和 2.6 下运行。

11.5. DSCP 目标

这是一个可以更改数据包内 DSCP(差异化服务字段)标记的目标。DSCP 目标可以设置 TCP 数据包内的任何 DSCP 值,这是一种告诉路由器相关数据包优先级的方式。有关 DSCP 的更多信息,请参阅 RFC 2474 - IPv4 和 IPv6 报头 RFC 文件中差异化服务字段(DS 字段)的定义。

基本上,DSCP 是一种将不同服务区分为不同类别的方法,并在此基础上通过路由器给予不同的优先级。这样,您就可以为交互式 TCP 会话(如 telnet、SSH、POP3)提供非常快速的连接,但这种连接可能不太适合大容量传输。另一方面,如果连接的重要性较低(SMTP,或任何被您归类为低优先级的连接),则可以通过延迟比其他网络更差的大型笨重网络发送,而这种网络的使用成本比速度更快、延迟更低的连接更低。

表 11-3. DSCP 目标选项

Option–set-dscp
Exampleiptables -t mangle -A FORWARD -p tcp –dport 80 -j DSCP –set-dscp 1
Explanation将 DSCP 值设置为指定值。可通过类(见下文)或**–set-dscp**(取整数值或十六进制值)设置值。
Option–set-dscp-class
Exampleiptables -t mangle -A FORWARD -p tcp –dport 80 -j DSCP –set-dscp-class EF
Explanation这将根据预定义的 DiffServ 类别设置 DSCP 字段。可能的值包括 EF、BE 以及可用的 CSxx 和 AFxx 值。更多信息请访问Implementing Quality of Service Policies with DSCP 网站。请注意,-set-dscp-class-set-dscp命令是互斥的,这意味着您不能在同一命令中同时使用这两个命令!

注意:可在 Linux 内核 2.3、2.4、2.5 和 2.6 下运行。

11.6. ECN 目标

如果使用方法正确,该目标可以发挥巨大作用。简单地说,ECN 目标可用于重置 IPv4 标头中的 ECN 位,或者正确地说,至少将其重置为 0。由于 ECN 在网络上是一个相对较新的东西,因此存在一些问题。一些路由器和其他互联网设备不会转发将这些位设置为 1 的数据包。如果你想利用主机的至少部分 ECN 功能,你可以将特定网络的 ECN 位重置为 0,因为你知道由于 ECN 的存在,你在访问这些网络时会遇到麻烦。

注意:请注意,不可能在数据流中间打开 ECN。根据 RFC,这是不允许的,而且无论如何也不可能。数据流的两个端点必须协商 ECN。如果我们将其打开,那么其中一台主机就不会意识到这一点,也就无法正确响应 ECN 通知。

表 11-4. ECN 目标选项

Option–ecn-tcp-remove
Exampleiptables -t mangle -A FORWARD -p tcp –dport 80 -j ECN –ecn-tcp-remove
ExplanationECN 目标只接受一个参数,即**–ecn-tcp-remove** 参数。这将告诉目标删除 TCP 头中的 ECN 位。更多信息请阅读上文。

注意:可在 Linux 内核 2.5 和 2.6 下运行。

11.7. LOG 目标

LOG 目标专门用于记录数据包的详细信息。例如,这些信息可能被视为非法信息。或者,日志记录纯粹用于查找bug和错误。LOG 目标将返回数据包的特定信息,如大部分 IP 头信息和其他有趣的信息。它通过内核日志设施(通常是 syslogd)来实现。这些信息可以直接通过 dmesg、syslogd 日志或其他程序或应用程序读取。这是调试规则集的绝佳目标,可以查看哪些数据包去了哪里,哪些规则应用于哪些数据包。还要注意的是,当你在生产防火墙上测试一条你还不能完全确定的规则时,使用 LOG 目标而不是 DROP 目标可能是一个非常好的主意,因为规则集中的语法错误可能会给你的用户带来严重的连接问题。还需注意的是,如果使用的日志记录非常广泛,ULOG 目标可能会很有趣,因为 ULOG 目标支持直接记录到 MySQL 数据库等。

注意:请注意,如果出现不希望直接记录到控制台的日志,这不是 iptables 或 Netfilter 的问题,而是 syslogd 配置(很可能是 /etc/syslog.conf)造成的问题。有关此类问题的更多信息,请阅读 man syslog.conf。

您可能还需要调整 dmesg 设置。dmesg 命令可更改内核中哪些错误应显示在控制台上。dmesg -n 1 应阻止所有信息显示在控制台上,panic 信息除外。dmesg 的信息级别与 syslogd 的级别完全一致,而且只适用于内核设施的日志信息。更多信息,请参阅 man dmesg。

如果您有特定的信息需求,或想将不同的选项设置为特定值,那么 LOG 目标器目前需要五个选项。下面列出了所有这些选项。

表 11-5. LOG 目标选项

Option–log-level
Exampleiptables -A FORWARD -p tcp -j LOG –log-level debug
Explanation该选项用于告诉iptablessyslog 使用哪种日志级别。有关日志级别的完整列表,请阅读 “syslog.conf ”手册。通常有以下日志级别或优先级:debug、info、notice、warning、warn、err、error、critical、alert、emerg 和 panic。关键字 error 与 err 相同,warn 与 warning 相同,panic 与 emerg 相同。请注意,这三个关键字都已废弃,换句话说,请勿使用 error、warn 和 panic。优先级定义了所记录信息的严重程度。所有信息都是通过内核设施记录的。换句话说,在 “syslog.conf ”文件中设置kern.=info /var/log/iptables ,然后让iptables中的所有LOG信息都使用日志级别info,这样所有信息都会出现在“/var/log/iptables ”文件中。请注意,这里可能还有内核其他部分使用 info 优先级的其他信息。有关日志的更多信息,我建议你阅读syslogsyslog.conf手册以及其他HOWTO等。
Option–log-prefix
Exampleiptables -A INPUT -p tcp -j LOG –log-prefix “INPUT packets”
Explanation该选项可让iptables在所有日志信息前加上特定前缀,这样就可以很容易地与grep或其他工具结合使用,跟踪特定问题和不同规则的输出。前缀长度最多为 29 个字母,包括空格和其他特殊符号。
Option–log-tcp-sequence
Exampleiptables -A INPUT -p tcp -j LOG –log-tcp-sequence
Explanation该选项将记录 TCP 序列号和日志信息。TCP 序列号是一种特殊的数字,用于识别每个数据包及其在 TCP 序列中的位置,以及数据流应如何重新组合。请注意,如果未经授权的用户或全世界都能读取日志,那么该选项就会构成安全风险。任何包含iptables输出的日志也是如此。
Option–log-tcp-options
Exampleiptables -A FORWARD -p tcp -j LOG –log-tcp-options
Explanation–log-tcp-options选项记录了TCP包头中的不同选项,在调试可能出错的地方或实际出错的地方时非常有用。该选项不包含任何变量字段或类似内容,就像大多数LOG选项一样。
Option–log-ip-options
Exampleiptables -A FORWARD -p tcp -j LOG –log-ip-options
Explanation–log-ip-options 选项将记录大部分 IP 包头选项。其作用与**–log-tcp-options**选项完全相同,只是针对的是IP选项。这些日志信息在尝试调试或追踪特定罪魁祸首以及调试时可能很有价值–其方式与前一个选项相同。

注意:可在 Linux 内核 2.3、2.4、2.5 和 2.6 下运行。

11.8. MARK 目标

MARK 目标用于设置与特定数据包相关联的 Netfilter 标记值。此目标只在混淆表中有效,在混淆表之外不起作用。MARK 值可与 Linux 中的高级路由功能结合使用,通过不同的路由发送不同的数据包,并告诉它们使用不同的队列规则(qdisc)等。有关高级路由的更多信息,请查看 Linux 高级路由和流量控制 HOW-TO。请注意,标记值不是在实际软件包中设置的,而是在内核中与数据包相关联的一个值。换句话说,您不能为数据包设置 MARK,然后期望在另一台主机上仍有 MARK。如果您想这样做,最好使用 TOS 目标,它将在 IP 标头中修改 TOS 值。

表 11-6. MARK 目标选项

Option–set-mark
Exampleiptables -t mangle -A PREROUTING -p tcp –dport 22 -j MARK –set-mark 2
Explanation设置标记需要使用**–set-mark** 选项。-set-mark匹配值为整数。例如,我们可以对特定的数据包流或来自特定主机的所有数据包设置标记 2,然后对该主机进行高级路由选择,以减少或增加网络带宽等。

注意:可在 Linux 内核 2.3、2.4、2.5 和 2.6 下运行。

11.9. MASQUERADE 目标

MASQUERADE 目标的使用方法与 SNAT 目标基本相同,但它不需要任何 –to-source 选项。这样做的原因是,MASQUERADE 目标是为拨号连接或 DHCP 连接(在连接到相关网络时获得动态 IP 地址)等而设计的。这意味着 MASQUERADE 目标机只能与动态分配的 IP 连接一起使用,因为我们在任何时候都不知道这些连接的实际地址。如果使用的是静态 IP 连接,则应使用 SNAT 目标。

当你伪装连接时,这意味着我们设置的是特定网络接口上使用的 IP 地址,而不是 –to-source 选项,IP 地址会自动从特定接口的信息中获取。MASQUERADE 目标的另一个作用是,当某个接口宕机时,连接也会被遗忘。如果我们使用 SNAT 目标,可能会留下大量旧的连接跟踪数据,这些数据会被放置好几天,吞噬掉有用的连接跟踪内存。一般来说,在处理拨号线路时,这样做是正确的,因为拨号线路每次启动都可能分配到不同的 IP。如果我们被分配了不同的 IP,连接就会丢失,因此保留该条目多少有些愚蠢。

即使您有一个静态 IP,也可以使用 MASQUERADE 目标而不是 SNAT,但这样做并不可取,因为这会增加额外的开销,而且将来可能会出现不一致的情况,使您现有的脚本无法使用。

请注意,MASQUERADE 目标和 SNAT 目标一样,只在 nat 表中的 POSTROUTING 链内有效。MASQUERADE 目标需要使用下面指定的一个选项,该选项是可选的。

表 11-7. MASQUERADE 目标

Option–to-ports
Exampleiptables -t nat -A POSTROUTING -p TCP -j MASQUERADE –to-ports 1024-31000
Explanation–to-ports选项用于设置外发数据包要使用的源端口。既可以指定一个端口,如**–to-ports 1025**,也可以指定一个端口范围,如**-to-ports 1024-3000**。换句话说,下端口范围分隔符和上端口范围分隔符之间用连字符隔开。这将改变 SNAT target 部分所述的 SNAT 默认端口选择。只有在规则匹配部分使用 –protocol 匹配指定 TCP 或 UDP 协议时,–to-ports 选项才有效。

注意:可在 Linux 内核 2.3、2.4、2.5 和 2.6 下运行。

11.10. MIRROR 目标

警告:请注意,MIRROR 是危险的,它只是作为新 conntrack 和 NAT 代码的示例代码而开发的。如果使用不当,它可能会导致危险的事情发生,并可能造成非常严重的 DDoS/DoS。请尽量避免使用!由于其不良的安全影响,它已从 2.5 和 2.6 内核中移除!

MIRROR 目标只是一个实验性和演示性目标,警告您不要使用它,因为它可能会导致非常糟糕的循环,从而导致严重的拒绝服务。MIRROR 目标用于反转 IP 头中的源字段和目标字段,然后重新传输数据包。这可能会产生一些非常有趣的效果,我敢打赌,由于这个目标的存在,现在不只一个面红耳赤的破解者破解了自己的盒子。至少可以说,使用这个目标的效果非常明显。如果主机 B 来自 yahoo.com,并试图访问主机 A 的 HTTP 服务器,MIRROR 目标器将返回 yahoo 主机自己的网页(因为请求来自这里)。

请注意,MIRROR 目标只在 INPUT、FORWARD 和 PREROUTING 链以及从这些链调用的任何用户定义链中有效。还要注意的是,MIRROR 目标导致的传出数据包不会被过滤器、NAT 或 mangle 表中的任何正常链看到,这可能会导致循环和其他问题。这可能会使目标造成无法预料的麻烦。例如,一台主机可能会向另一台使用 MIRROR 命令的主机发送 TTL 为 255 的欺骗数据包,同时欺骗自己的数据包,使其看起来像是来自第三台使用 MIRROR 命令的主机。然后,该数据包将不停地来回跳转,直到跳转次数达到要求为止。如果只有 1 跳,数据包将来回跳转 240-255 次。换句话说,对于破解者来说,发送 1500 字节的数据并占用 380 kbyte 的连接还算不错。请注意,这是对破解者或脚本小子(无论我们怎么称呼他们)来说最好的情况。

注意:适用于 Linux 内核 2.3 和 2.4。由于其固有的不安全性,已从 2.5 和 2.6 内核中删除。请勿使用此目标!

11.11. NETMAP 目标

NETMAP 是 SNAT 和 DNAT 目标的一种新实现,其中 IP 地址的主机部分不会改变。它为整个网络提供了 1:1 NAT 功能,这是标准 SNAT 和 DNAT 功能所不具备的。例如,假设我们有一个包含 254 台使用私有 IP 地址的主机的网络(一个 /24 网络),而我们刚刚获得了一个新的 /24 公有 IP 网络。我们不需要走来走去更改每台主机的 IP,而只需使用 NETMAP 目标,如 -j NETMAP -to 10.5.6.0/24,然后所有主机在离开防火墙时都会显示为 10.5.6.x。例如,192.168.0.26 将变为 10.5.6.26。 表 11-8. NETMAP 目标选项

Option–to
Exampleiptables -t mangle -A PREROUTING -s 192.168.1.0/24 -j NETMAP –to 10.5.6.0/24
Explanation这是NETMAP目标的唯一选项。在上例中,192.168.1.x 主机将被直接转换为 10.5.6.x。

注意:可在 Linux 内核 2.5 和 2.6 下运行。

11.12. QUEUE 目标

QUEUE 目标用于将数据包队列到用户区域程序和应用程序。它可与 iptables 以外的程序或实用程序结合使用,例如用于网络会计,或用于代理或过滤数据包的特定高级应用程序。我们不会深入讨论这一目标,因为此类应用程序的编码不在本教程的范围之内。首先,这将耗费太多时间,其次,此类文档与 Netfilter 和 iptables 的编程方面无关。所有这些在《Netfilter 黑客 HOW-TO》中都有详细介绍。

注意:适用于 Linux 内核 2.3、2.4、2.5 和 2.6。

11.13. REDIRECT 目标

REDIRECT 目标用于将数据包和数据流重定向到机器本身。例如,我们可以将所有指向 HTTP 端口的数据包重定向到自己主机上的 HTTP 代理(如 squid)。本地生成的数据包会映射到 127.0.0.1 地址。换句话说,这会将转发数据包的目标地址改写为我们自己的主机,或者类似的东西。当我们需要透明代理(局域网主机完全不知道代理的存在)时,REDIRECT 目标非常适合使用。

请注意,REDIRECT 目标只在 nat 表的 PREROUTING 和 OUTPUT 链中有效。它也在用户定义的链中有效,这些链只能被调用,而不能被其他链调用。REDIRECT 目标只接受一个选项,如下所述。

表 11-9. REDIRECT 目标

Option–to-ports
Exampleiptables -t nat -A PREROUTING -p tcp –dport 80 -j REDIRECT –to-ports 8080
Explanation–to-ports选项指定要使用的目标端口或端口范围。如果不使用**-to-ports选项,目标端口将不会改变。如上文所述,-to-ports 8080** 是为了防止只指定一个端口。如果我们想指定一个端口范围,可以使用**-to-ports 8080-8090**,告诉REDIRECT目标将数据包重定向到 8080 至 8090 端口。请注意,该选项只适用于使用 –protocol 匹配器指定 TCP 或 UDP 协议的规则,因为它在其他地方没有任何意义。

注意:适用于 Linux 内核 2.3、2.4、2.5 和 2.6。

11.14. REJECT 目标

REJECT 目标的工作原理与 DROP 目标基本相同,但它会向发送被拦截数据包的主机发回一条错误信息。目前,REJECT 目标只在 INPUT、FORWARD 和 OUTPUT 链或其子链中有效。毕竟,只有在这些链中使用该目标才有意义。请注意,所有使用 REJECT 目标值的链只能由 INPUT、FORWARD 和 OUTPUT 链调用,否则将不起作用。目前只有一个选项可以控制该目标的工作性质,不过这可能会产生大量变量。如果你有 TCP/IP 的基本知识,大多数变量都很容易理解。

表 11-10. 拒绝目标

Option–reject-with
Exampleiptables -A FORWARD -p TCP –dport 22 -j REJECT –reject-with tcp-reset
Explanation该选项告诉REJECT目标应向发送我们要拒绝的数据包的主机发送什么回复。一旦收到符合指定目标规则的数据包,我们的主机首先会发送相关的回复,然后数据包就会被丢弃,就像DROP目标会丢弃数据包一样。以下拒绝类型目前有效:icmp-net-unreachable、icmp-host-unreachable、icmp-port-unreachable、icmp-proto-unreachable、icmp-net-prohibited 和 icmp-host-prohibited。默认错误信息是向主机发送 port-unreachable 。上述所有错误信息都是 ICMP 错误信息,可以根据需要进行设置。你可以在附录 ICMP types 中进一步了解它们的各种用途。最后,还有一个名为 “tcp-reset”的选项,只能与 TCP 协议一起使用。tcp-reset选项将告诉REJECT向发送主机发送一个 TCP RST 数据包作为回复。TCP RST 数据包用于优雅地关闭打开的 TCP 连接。有关 TCP RST 的更多信息,请阅读RFC 793 - 传输控制协议。正如iptables手册中所述,这主要用于阻止标识探测,这种探测在向损坏的邮件主机发送邮件时经常出现,否则这些主机不会接受你的邮件。

11.15. RETURN 目标

RETURN 目标将使当前数据包停止通过它碰到规则的链。如果是另一条链的子链,数据包将继续通过上级链,就像什么都没发生过一样。如果该链是主链,例如 INPUT 链,则数据包将采用默认策略。默认策略通常设置为 ACCEPT、DROP 或类似。

例如,假设一个数据包进入 INPUT 链,然后碰到一条与之匹配的规则,该规则告诉它 –jump EXAMPLE_CHAIN。然后,数据包将开始遍历 EXAMPLE_CHAIN,突然,它遇到了一条设置了 –jump RETURN 目标的特定规则。然后,它将跳回 INPUT 链。另一个例子是,如果数据包碰到了 INPUT 链中的 –jump RETURN 规则。然后,它将被丢弃到默认策略中,如前所述,在此链中将不再采取任何行动。

11.16. SAME 目标

SAME 目标的工作方式与 SNAT 目标器几乎相同,但仍有区别。基本上,SAME 目标将尝试对网络上由一台主机发起的所有连接始终使用相同的传出 IP 地址。例如,您有一个 /24 网络(192.168.1.0)和 3 个 IP 地址(10.5.6.7-9)。现在,如果 192.168.1.20 第一次通过 .7 地址出去,防火墙就会尝试让该机器始终通过该 IP 地址出去。

表 11-11. SAME 目标选项

Option–to
Exampleiptables -t mangle -A PREROUTING -s 192.168.1.0/24 -j SAME –to 10.5.6.7-10.5.6.9
Explanation–to 参数使用两个 IP 地址,并用“-”号绑定在一起。这些 IP 地址以及中间的所有 IP 地址都是我们使用SAME算法进行 NAT 的 IP 地址。
Option–nodst
Exampleiptables -t mangle -A PREROUTING -s 192.168.1.0/24 -j SAME –to 10.5.6.7-10.5.6.9 –nodst
Explanation在正常情况下,SAME 目标会根据目标和源 IP 地址计算后续连接。使用 –nodst 选项时,它只使用源 IP 地址来确定 NAT 功能应为特定连接使用哪个出站 IP。如果不使用该参数,它将使用目标 IP 地址和源 IP 地址的组合。

11.17. SNAT 目标

SNAT 目标用于进行源网络地址转换,这意味着该目标将重写数据包 IP 头中的源 IP 地址。例如,当几台主机必须共享一个互联网连接时,我们就需要这样做。这样,我们就可以在内核中打开 IP 转发,并编写一条 SNAT 规则,将所有从本地网络发出的数据包翻译成我们自己互联网连接的源 IP。如果不这样做,外界就不知道该向哪里发送回复数据包,因为我们的本地网络大多使用 IANA 指定的 IP 地址,这些地址是为局域网网络分配的。如果我们原封不动地转发这些数据包,互联网上的任何人都不会知道这些数据包实际上来自我们自己。SNAT 目标器可以完成这种工作所需的所有转换,让所有离开局域网的数据包看起来就像来自一台主机,也就是我们的防火墙。

SNAT target 仅在 nat 表的 POSTROUTING 链中有效。换句话说,这是唯一可以使用 SNAT 的链。只有连接中的第一个数据包才会被 SNAT 更改,此后使用同一连接的所有数据包都会被 SNAT 更改。此外,POSTROUTING 链中的初始规则将应用于同一数据流中的所有数据包。

表 11-12. SNAT 目标选项

Option–to-source
Exampleiptables -t nat -A POSTROUTING -p tcp -o eth0 -j SNAT –to-source 194.236.50.155-194.236.50.160:1024-32000
Explanation–to-source选项用于指定数据包应使用哪个源。该选项最简单的用法是在IP 头部中使用一个 IP 地址作为源 IP地址。如果我们想在多个 IP 地址之间取得平衡,可以使用一系列 IP 地址,中间用连字符隔开。例如,“–to–source IP ”的数字可以是上例中的 194.236.50.155-194.236.50.160。我们打开的每个数据流的源 IP 将从中随机分配,单个数据流中的所有数据包将始终使用相同的 IP 地址。我们还可以指定SNAT使用的端口范围。这样,所有源端口都将被限制在指定的端口范围内。这样,规则的端口位就会像上面的示例中那样:1024-32000。只有在相关规则的匹配中指定了 -p tcp 或 -p udp 时,这才有效。Iptables 会尽可能避免更改任何端口,但如果两台主机试图使用相同的端口,Iptables 会将其中一台主机映射到另一台主机的端口上。如果没有指定端口范围,那么如果需要,所有低于 512 的源端口都会映射到低于 512 的其他端口。介于源端口 512 和 1023 之间的端口将被映射到低于 1024 的端口。所有其他端口将被映射到 1024 或以上的端口。如前所述,Iptables 将始终保持实际连接工作站使用的源端口。请注意,这与目标端口无关,因此如果客户端试图与防火墙外的HTTP服务器建立联系,它将不会被映射到FTP 控制端口。

11.18. TCPMSS 目标

TCPMSS 目标可用于更改防火墙看到的 TCP SYN 数据包的 MSS(最大分段大小)值。MSS 值用于控制特定连接的最大数据包大小。在正常情况下,这意味着 MTU(最大传输单元)值的大小减去 40 字节。这用于克服某些 ISP 和服务器阻止 ICMP 分片所需的数据包,这可能会导致非常奇怪的问题,主要表现为防火墙/路由器上的一切工作都很正常,但防火墙后的本地主机却无法交换大数据包。这可能意味着邮件服务器能发送小邮件,但不能发送大邮件;网络浏览器能连接,但挂起后收不到数据;ssh 能正常连接,但 scp 在初始握手后挂起。换句话说,所有使用大数据包的程序都将无法运行。

TCPMSS 目标可以通过改变通过连接发出的数据包大小来解决这些问题。请注意,我们只需在 SYN 数据包上设置 MSS,因为之后的 MSS 会由主机处理。目标接收两个参数。

表 11-13. TCPMSS 目标选项

Option–set-mss
Exampleiptables -t mangle -A POSTROUTING -p tcp –tcp-flags SYN,RST SYN -o eth0 -j TCPMSS –set-mss 1460
Explanation–set-mss参数明确设置所有发出数据包的特定 MSS 值。在上面的例子中,我们将所有通过 eth0 接口发出的 SYN 数据包的 MSS 设置为 1460 字节 – 以太网的正常 MTU 是 1500 字节,减去 40 字节就是 1460 字节。只需在 SYN 数据包中正确设置 MSS,对等主机就会自动处理 MSS。
Option–clamp-mss-to-pmtu
Exampleiptables -t mangle -A POSTROUTING -p tcp –tcp-flags SYN,RST SYN -o eth0 -j TCPMSS –clamp-mss-to-pmtu
Explanation–clamp-mss-to-pmtu会自动将 MSS 设置为合适的值,因此无需明确设置。它会自动设置为 PMTU(路径最大传输单元)减去 40 字节,这对大多数应用来说都是一个合理的值。

11.19. TOS 目标

TOS 目标用于设置 IP 头中的服务类型字段。TOS 字段由 8 位组成,用于帮助路由数据包。这是可以在 iproute2 及其子系统中直接用于路由策略的字段之一。值得注意的是,如果要处理多个独立的防火墙和路由器,这是在这些路由器和防火墙之间的实际数据包中传播路由信息的唯一方法。如前所述,MARK 目标(设置与特定数据包相关的 MARK)只能在内核中使用,不能与数据包一起传播。如果您觉得有必要传播特定数据包或数据流的路由信息,您应该设置 TOS 字段,它就是为此而开发的。

目前,互联网上有很多路由器在这方面做得很差,因此,从目前来看,在将数据包发送到互联网之前尝试对 TOS 进行处理可能会被证明有点无用。最好的情况是,路由器不会注意 TOS 字段。最坏的情况是,路由器会查看 TOS 字段并做错误的事情。不过,如上所述,如果您有一个拥有多个路由器的大型广域网或局域网,TOS 字段绝对可以派上用场。事实上,您可以根据数据包的 TOS 值为其提供不同的路由和优先选择,尽管这可能仅限于您自己的网络。

注意:TOS 目标只能为数据包设置特定值或命名值。这些预定义的 TOS 值可以在内核包含文件中找到,更确切地说,可以在 Linux/ip.h 文件中找到。原因有很多,实际上你根本不需要设置任何其他值;不过,还是有办法绕过这一限制的。要绕过只能在数据包上设置指定值的限制,可以使用由 Matthew G. Marsh 维护的 Paksecured Linux 内核补丁网站上提供的 FTOS 补丁。不过,使用此补丁时要谨慎!除非在极端情况下,否则不需要使用默认值以外的其他值。

注意:请注意,此目标仅在混淆表内有效,不能在混淆表外使用。

注意:还要注意的是,某些旧版本(1.2.2 或以下)的 iptables 提供了该目标的一个残缺实现,在混淆时不修复数据包校验和,从而导致数据包变坏并需要重新传输。这反过来又很可能导致进一步的混淆,使连接永远无法正常工作。

如下所述,TOS 目标机只有一个选项。

表 11-14. TOS 目标

Option–set-tos
Exampleiptables -t mangle -A PREROUTING -p TCP –dport 22 -j TOS –set-tos 0x10
Explanation–set-tos选项告诉TOS目标在匹配的数据包上设置什么 TOS 值。该选项使用十六进制或十进制数值。由于 TOS 值由 8 位组成,因此数值可以是 0-255,或十六进制 0x00-0xFF。请注意,在标准 TOS 目标中,您只能使用可用的命名值(这些值或多或少应该是标准化的),这在前面的警告中已经提到。这些值包括最小化延迟(十进制值 16,十六进制值 0x10)、最大化吞吐量(十进制值 8,十六进制值 0x08)、最大化可靠性(十进制值 4,十六进制值 0x04)、最小化成本(十进制值 2,十六进制值 0x02)或正常服务(十进制值 0,十六进制值 0x00)。大多数数据包的默认值都是 Normal-Service,即 0。 请注意,您当然可以使用实际名称而不是实际十六进制值来设置 TOS 值;事实上,一般建议这样做,因为与名称相关的值将来可能会更改。有关 “描述值 ”的完整列表,请执行 iptables -j TOS -h。截至 iptables 1.2.5,该列表已经完整,希望能保持一段时间。

11.20. TTL 目标

注意:此补丁需要使用 http://www.netfilter.org/ 基本目录中 patch-o-matic 树中的 TTL 补丁。

TTL 目标用于修改 IP 头中的 “生存时间 ”字段。其中一个有用的应用是在所有发出的数据包中将所有 “生存时间 ”值改为相同的值。这样做的一个原因是,如果你的 ISP 不允许你有多台机器连接到同一个互联网连接上,而你的 ISP 又在积极争取这样做。将所有 TTL 值设置为相同值,可以有效地让他们更难察觉到你在这样做。然后,我们可以将所有发出数据包的 TTL 值重设为一个标准值,如 Linux 内核中规定的 64。

有关如何设置 Linux 默认值的更多信息,请阅读 ip-sysctl.txt,您可以在 “其他资源和链接 ”附录中找到它。

TTL 目标仅在混淆表中有效,在其他地方无效。在撰写本文时,它有 3 个选项,所有选项均在下表中说明。

表 11-15. TTL 目标值

Option–ttl-set
Exampleiptables -t mangle -A PREROUTING -i eth0 -j TTL –ttl-set 64
Explanation–ttl-set选项告诉TTL目标在相关数据包上设置哪个 TTL 值。一个好的值是 64 左右。这个值不会太长,也不会太短。不要把这个值设得太高,因为这可能会影响你的网络,而且把这个值设得太高也有点不道德,因为数据包可能会开始在两个配置错误的路由器之间来回跳转,在这种情况下,TTL 越高,不必要占用的带宽就越多。这个目标可以用来限制客户端的距离。DNS 服务器就是一个很好的例子,我们不希望客户端离得太远。
Option–ttl-dec
Exampleiptables -t mangle -A PREROUTING -i eth0 -j TTL –ttl-dec 1
Explanation–ttl-dec选项告诉TTL目标按**–ttl-dec选项后指定的数值递减 “生存时间 ”值。换句话说,如果传入数据包的 TTL 值是 53,而我们设置了 –ttl-dec 3,那么数据包离开主机时的 TTL 值就是 49。原因是网络代码会自动将 TTL 值递减 1,因此数据包会递减 4 个步骤,从 53 到 49。例如,当我们想限制使用我们服务的用户的距离时,就可以使用这种方法。例如,用户应始终使用附近的 DNS,因此我们可以匹配所有离开 DNS 服务器的数据包,然后将其递减几级。当然,–set-ttl** 可能是更好的选择。
Option–ttl-inc
Exampleiptables -t mangle -A PREROUTING -i eth0 -j TTL –ttl-inc 1
Explanation–ttl-inc选项告诉TTL目标按**–ttl-inc选项指定的值递增 “生存时间 ”值。这意味着我们应该用 –ttl-inc 选项中指定的值来增加 TTL 值,如果我们指定了 –ttl-inc 4,那么以 53 的 TTL 进入的数据包将以 56 的 TTL 离开主机。请注意,这里的情况与前面–ttl-dec**选项的例子相同,网络代码会自动将 TTL 值减去 1,而这也是网络代码的一贯做法。这可以使我们的防火墙更加隐蔽,以便追踪路径等。通过将所有传入数据包的 TTL 设置高一个值,我们可以有效地使防火墙不被跟踪路由发现。跟踪路由让人又爱又恨,因为它们能提供有关连接问题及其发生地点的绝佳信息,但与此同时,如果黑客/破解者盯上了你,它们也能提供一些有关你的上行数据流的有用信息。如需了解如何使用此功能,请参阅 Ttl-inc.txt 脚本。

11.21. ULOG 目标

ULOG 目标用于提供匹配数据包的用户空间日志。如果匹配到数据包并设置了 ULOG 目标,则数据包信息将与整个数据包一起通过网链套接字进行组播。然后,一个或多个用户空间进程可以订阅各种多播组并接收数据包。换句话说,这是一种更完整、更复杂的日志记录设施,迄今为止只有 iptables 和 Netfilter 使用过,它包含更好的数据包日志记录设施。该目标使我们能将信息记录到 MySQL 数据库和其他数据库中,从而使搜索特定数据包和对日志条目进行分组变得更加简单。你可以在 ULOGD 项目页面找到 ULOGD 用户地应用程序。

表 11-16. ULOG 目标

Option–ulog-nlgroup
Exampleiptables -A INPUT -p TCP –dport 22 -j ULOG –ulog-nlgroup 2
Explanation–ulog-nlgroup选项告诉ULOG目标要将数据包发送到哪个网链路组。有 32 个网链组,简单地指定为 1-32。如果我们想发送到网链路组 5,只需写入**–ulog-nlgroup 5**即可。默认使用的网链路组是 1。
Option–ulog-prefix
Exampleiptables -A INPUT -p TCP –dport 22 -j ULOG –ulog-prefix “SSH connection attempt: “
Explanation–ulog-prefix选项的作用与标准LOG目标的前缀值相同。该选项会在所有日志条目前加上用户指定的日志前缀。前缀长度可以是 32 个字符,用于区分不同的日志信息及其来源。
Option–ulog-cprange
Exampleiptables -A INPUT -p TCP –dport 22 -j ULOG –ulog-cprange 100
Explanation–ulog-cprange 选项告诉ULOG目标要向ULOG的用户空间守护进程发送多少字节的数据包。如果我们指定 100 字节,就会将整个数据包的 100 字节复制到用户空间,其中包括整个数据包头,以及实际数据包中的一些前导数据。如果指定为 0,则无论数据包大小如何,都会将整个数据包复制到用户空间。默认值为 0,因此整个数据包都将复制到用户空间。
Option–ulog-qthreshold
Exampleiptables -A INPUT -p TCP –dport 22 -j ULOG –ulog-qthreshold 10
Explanation–ulog-qthreshold选项告诉ULOG目标在向用户空间发送数据之前,要在内核内部排队等候多少个数据包。例如,如果我们将阈值设为 10,内核就会先在内部累积 10 个数据包,然后以单个网链多部分报文的形式发送到用户空间。这里的默认值是 1,因为出于向后兼容的考虑,用户空间守护进程以前不知道如何处理多部分报文。

第 12 章 调试脚本 调试脚本

编写自己的规则集有一个被低估的重要方面,那就是如何自行调试规则集,以及如何找到规则集中的错误所在。本章将向你介绍调试脚本的几个基本步骤,找出脚本中的问题所在,以及一些需要注意的细节,以及如何避免在不小心运行了错误规则集的情况下无法连接到防火墙。 这里所讲的大部分内容都是基于规则集是用 bash shell 脚本编写的这一假设,但它们也很容易在其他环境中应用。不幸的是,用 iptables-save 保存的规则集完全是另一段代码,这些调试方法几乎都不会给你带来什么好处。另一方面,iptables-save 文件要简单得多,而且因为它们不能包含任何创建特定规则的脚本代码,所以调试起来也简单得多。

12.1. 调试,必需品

在使用 iptables 和 netfilter 以及大多数防火墙时,调试或多或少都是必要的。99% 的防火墙的问题在于,最终都是由人来决定策略和规则集的创建方式,而且我可以向你保证,在编写规则集时很容易出错。有时,这些错误很难被肉眼发现,也很难发现它们在防火墙上造成的漏洞。您不知道或无意中在脚本中出现的漏洞会给您的网络造成严重破坏,并为攻击者创造一个方便的入口。这些漏洞大多可以通过一些好工具轻易找到。 除此之外,您还可能以其他方式在脚本中写入漏洞,从而造成无法登录防火墙的问题。在运行脚本之前,使用一点小聪明也可以解决这个问题。几乎所有经验丰富的 Unix 管理员都已经注意到了这一点,而我们在调试脚本时基本上也是这样做的。

12.2. Bash 调试技巧

使用 bash 可以做很多事情来帮助调试包含规则集的脚本。查找 bug 的首要问题之一是知道问题出现在哪一行。有两种不同的方法可以解决这个问题,一种是使用 bash -x 标志,另一种是简单地输入一些 echo 语句来找到出现问题的地方。理想情况下,你可以使用 echo 语句,在代码中每隔一定时间添加类似下面的 echo 语句:

  ...
  echo "Debugging message 1."
  ...
  echo "Debugging message 2."
  ...

就我而言,我一般会使用几乎没有价值的信息,只要这些信息中有一些独一无二的内容,这样我就可以通过简单的 grep 或搜索在脚本文件中找到错误信息。现在,如果错误信息出现在 “Debugging message 1.“之后,而在 “Debugging message 2.“之前,那么我们就知道错误的代码行就在这两条调试信息之间的某个地方。正如你所理解的,bash 有一个不算太坏但至少很奇特的想法,那就是即使之前的某个命令出错了,也会继续执行该命令。在 netfilter 中,这会给你带来一些非常有趣的问题。上述简单地使用 echo 语句查找错误的想法非常简单,但同时也非常好,因为您可以将整个问题缩小到一行代码,并直接看到问题所在。

查找上述问题的第二种方法是使用 bash 的 -x 变量,就像我们之前提到的那样。当然,这可能会带来一些小问题,尤其是当你的脚本比较大、控制台缓冲区不够大的时候。-x 变量的意思很简单,它告诉脚本将脚本中的每一行代码都回放到 shell 的标准输出(通常是控制台)中。你要做的就是将脚本的正常起始行改为

#!/bin/bash
#!/bin/bash -x

正如你所看到的,这将使你的输出从几行字变成大量的数据输出。代码会向你显示执行的每一条命令行,以及所有变量的值等,这样你就不必费力去弄清代码到底在做什么了。简而言之,执行的每一行都会输出到屏幕上。有一件事可能会让你很高兴,那就是 bash 输出的所有行都以 + 号作为前缀。这使得从实际脚本中分辨错误或警告信息更容易一些,而不仅仅是一个大网状的输出。

对于调试其他几个比较常见的问题,-x 选项也非常有趣,你可能会在使用稍微复杂一点的规则集时遇到这些问题。第一个问题是,你以为只是一个简单的循环,比如 for、if 或 while 语句,但在实际运行中会发生什么?例如,我们来看一个例子。

  #!/bin/bash 
  iptables="/sbin/iptables"
  $iptables -N output_int_iface
  cat /etc/configs/machines | while read host; do
    $iptables -N output-$host
    $iptables -A output_int_iface -p tcp -d $host -j output-$host

    cat /etc/configs/${host}/ports | while read row2; do
      $iptables -A output-$host -p tcp --dport $row2 -d $host -j ACCEPT
    done
  done

这套规则看起来很简单,但我们还是遇到了问题。通过使用简单的 echo 调试方法,我们得到了以下错误信息,知道这些信息来自上述代码。

work3:~# ./test.sh
Bad argument `output-'
Try `iptables -h' or 'iptables --help' for more information.
cat: /etc/configs//ports: No such file or directory

因此,我们打开 bash 的 -x 选项并查看输出结果。输出结果如下所示,你可以看到其中有一些非常奇怪的地方。有几条命令中的 $host 和 $row2 变量被替换为空值。仔细观察,我们会发现只有最后一次迭代的代码才会出现问题。要么是我们的程序出错了,要么就是数据有问题。在本例中,数据出现了一个简单的错误,在文件末尾多了一个换行符。这导致循环最后一次遍历,而这是不应该的。只需删除文件尾部的换行符,问题就解决了。这可能不是一个非常优雅的解决方案,但对于私人工作来说应该足够了。否则,你可以添加代码,查看 $host 和 $row2 变量中是否有数据。

work3:~# ./test.sh
+ iptables=/sbin/iptables
+ /sbin/iptables -N output_int_iface
+ cat /etc/configs/machines
+ read host
+ /sbin/iptables -N output-sto-as-101
+ /sbin/iptables -A output_int_iface -p tcp -d sto-as-101 -j output-sto-as-101
+ cat /etc/configs/sto-as-101/ports
+ read row2
+ /sbin/iptables -A output-sto-as-101 -p tcp --dport 21 -d sto-as-101 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-101 -p tcp --dport 22 -d sto-as-101 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-101 -p tcp --dport 23 -d sto-as-101 -j ACCEPT
+ read row2
+ read host
+ /sbin/iptables -N output-sto-as-102
+ /sbin/iptables -A output_int_iface -p tcp -d sto-as-102 -j output-sto-as-102
+ cat /etc/configs/sto-as-102/ports
+ read row2
+ /sbin/iptables -A output-sto-as-102 -p tcp --dport 21 -d sto-as-102 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-102 -p tcp --dport 22 -d sto-as-102 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-102 -p tcp --dport 23 -d sto-as-102 -j ACCEPT
+ read row2
+ read host
+ /sbin/iptables -N output-sto-as-103
+ /sbin/iptables -A output_int_iface -p tcp -d sto-as-103 -j output-sto-as-103
+ cat /etc/configs/sto-as-103/ports
+ read row2
+ /sbin/iptables -A output-sto-as-103 -p tcp --dport 21 -d sto-as-103 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-103 -p tcp --dport 22 -d sto-as-103 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-103 -p tcp --dport 23 -d sto-as-103 -j ACCEPT
+ read row2
+ read host
+ /sbin/iptables -N output-
+ /sbin/iptables -A output_int_iface -p tcp -d -j output-
Bad argument `output-'
Try `iptables -h' or 'iptables --help' for more information.
+ cat /etc/configs//ports
cat: /etc/configs//ports: No such file or directory
+ read row2
+ read host

如果你通过 SSH 执行防火墙脚本,而控制台在执行脚本的过程中挂起,控制台根本无法返回,你也无法再次通过 SSH 连接,这时你遇到的第三个也是最后一个问题可以借助 -x 选项得到部分解决。在 99.9% 的情况下,这意味着脚本中的某些规则出现了问题。打开 -x 选项,你就能看到脚本到底在哪一行锁死了,希望至少是这样。遗憾的是,有几种情况并非如此。例如,如果脚本设置了一条阻止传入流量的规则,但由于 ssh/telnet 服务器首先将 echo 作为传出流量发送,netfilter 会记住该连接,因此,如果上面有一条处理连接状态的规则,就会允许传入流量。

正如你所看到的,最终要调试规则集的全部内容可能会变得相当复杂。不过,这并非不可能。如果你通过 SSH 等方式远程操作防火墙,你可能还会注意到,当你加载错误的规则集时,防火墙可能会挂起。在这种情况下,还有一件事可以救急。Cron 就是一种很好的救急方法。例如,你在 50 公里外的防火墙上工作,添加了一些规则,删除了一些其他规则,然后删除并插入新更新的规则集。防火墙锁死了,你无法访问它。解决这个问题的唯一办法就是前往防火墙的物理位置,从那里解决问题,除非你采取了预防措施!

12.3. 用于调试的系统工具

针对被锁定的防火墙,最好的预防措施之一就是使用 cron 添加一个脚本,每 5 分钟左右运行一次,重置防火墙。cron 脚本行可以如下所示,使用 crontab -e 命令输入。

*/5 * * * /etc/init.d/rc.flush-iptables.sh stop

在你开始做一些你认为会或可能会冻结你正在运行的服务器的事情之前,请绝对确保该行将实际工作并完成你期望它做的事情。

另一个经常用于调试脚本的工具是 syslog 设备。它记录了大量不同程序创建的所有日志信息。事实上,几乎所有大型程序都支持 syslog 日志,包括内核。所有发送到 syslog 的信息都有两个非常重要的基本变量,即设施和日志级别/优先级。

设施会告诉 syslog 服务器日志条目来自哪个设施,以及在哪里记录。有几种指定的设施,但现在要讨论的是 Kern 设施,也可称为内核设施。所有 netfilter 生成的信息都将使用该设施发送。

日志级别告诉 syslog 日志信息的优先级。有以下几种优先级可供选择。

  1. debug
  2. info
  3. notice
  4. warning
  5. err
  6. crit
  7. alert
  8. emerg

根据这些优先级,我们可以使用 syslog.conf 将它们发送到不同的日志文件。例如,要将所有来自 kern 设备的警告优先级信息发送到一个名为 /var/log/kernwarnings 的文件,我们可以按下图所示操作。这一行应写入 /etc/syslog.conf。

kern.warning /var/log/kernwarnings

如你所见,这非常简单。现在,你有望在 /var/log/kernwarnings 文件中找到 netfilter 日志(重启或 HUP syslog 服务器后)。当然,这也取决于你在 netfilter 日志规则中设置的日志级别。日志级别可以用 –log-level 选项来设置。

输入该文件的日志将为你提供你希望通过规则集中的特定日志规则记录的所有数据包的信息。有了这些信息,你就能知道是否有什么具体的问题。例如,您可以在所有链的末尾设置日志规则,以查看是否有任何数据包越过了链的边界。日志条目可能与下面的示例类似,包含大量信息。

  Oct 23 17:09:34 localhost kernel: IPT INPUT packet died: IN=eth1 OUT= 
MAC=08:00:09:cd:f2:27:00:20:1a:11:3d:73:08:00 SRC=200.81.8.14 DST=217.215.68.146 
LEN=78 TOS=0x00 PREC=0x00 TTL=110 ID=12818 PROTO=UDP SPT=1027 DPT=137 LEN=58

正如你所理解的,系统日志在调试规则集时确实能帮到你。查看这些日志可以帮助你理解为什么你想打开的某个端口无法工作。

12.4. Iptables 调试

有时,Iptables 的调试会很困难,因为 iptables 本身的错误信息在任何时候都不是很友好。因此,最好先了解一下从 iptables 收到的最常见错误信息,以及收到这些信息的原因。

首先要查看的错误信息之一是 “Unknown arg “错误。出现这种情况有多种原因。例如,请看下面。

work3:~# iptables -A INPUT --dport 67 -j ACCEPT
iptables v1.2.9: Unknown arg `--dport'
Try `iptables -h' or 'iptables --help' for more information.

由于我们只使用了一个参数,所以这个错误比一般的错误更容易解决。通常情况下,你可能使用了很长很长的命令,然后得到这个错误信息。上述情况的问题在于,我们忘记了使用 –protocol 匹配项,因此无法使用 –dport 匹配项。添加 –protocol 匹配也可以解决这个问题。请务必确保没有遗漏任何使用特定匹配所需的特殊前提条件。

另一个非常常见的错误是在命令行中漏掉了破折号 (-),如下所示。正确的解决办法是只需添加破折号,命令就能正常运行。

work3:~# iptables -A INPUT --protocol tcp -dport 67 -j ACCEPT
Bad argument `67'
Try `iptables -h' or 'iptables --help' for more information.

最后,还有一种简单的拼写错误,也相当常见。如下图所示。您会发现,错误信息与忘记在规则中添加另一个先决条件匹配时的错误信息完全相同,因此需要仔细查看。

work3:~# iptables -A INPUT --protocol tcp --destination-ports 67 -j ACCEPT
iptables v1.2.9: Unknown arg `--destination-ports'
Try `iptables -h' or 'iptables --help' for more information.

上面显示的 “未知参数 “错误还有一个可能的原因。如果参数写得很好,而且先决条件中也没有可能的错误,那么目标/匹配/表可能只是没有编译到内核中。例如,假设我们忘记将过滤表支持编译到内核中,那么就会出现下面这种情况:

work3:~# iptables -A INPUT -j ACCEPT
iptables v1.2.9: can't initialize iptables table `filter': Table does not exist 
(do you need to insmod?)
Perhaps iptables or your kernel needs to be upgraded.

通常情况下,iptables 应该能够自动 modprobe 内核中尚未存在的特定模块,所以这通常是使用新内核重启后没有正确进行 depmod 的迹象,或者是你已经忘记了该模块。如果有问题的模块是一个匹配模块,那么错误信息就会更加隐晦难懂。例如,请看这条错误信息。

work3:~# iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
iptables: No chain/target/match by that name

在这种情况下,我们忘记了编译状态模块,正如你所看到的,错误信息并不是很好理解。不过,它还是提示了问题所在。最后,我们又遇到了同样的错误,但这次目标丢失了。从错误信息中可以看出,这两个错误(缺少匹配和/或目标)的错误信息完全相同,因此变得相当复杂。

work3:~# iptables -A INPUT -m state --state ESTABLISHED -j REJECT
iptables: No chain/target/match by that name

最简单的方法就是查看模块所在的目录,以确定是我们忘记了 depmod,还是模块确实丢失了。这是 /lib/modules/2.6.4/kernel/net/ipv4/netfilter 目录。所有以大写字母书写的 ipt_* 文件都是目标文件,而所有以小写字母书写的文件都是匹配文件。例如,ipt_REJECT.ko 是目标文件,而 ipt_state.ko 是匹配文件。

警告:在 2.4 及更早的内核中,所有内核模块的文件扩展名都是 .o,而在 2.6 内核中,文件扩展名变为了 .ko。

另一种从 iptables 本身获得帮助的方法是简单地注释掉脚本中的整条链,看看是否能解决问题。这是一种不得已的解决问题的方法,如果你甚至不知道是哪条链导致了问题,那么这种方法可能会非常有效。通过删除整个链并简单地设置默认策略为 “ACCEPT”,然后进行测试,如果效果更好,那么这就是导致问题的链。如果效果不佳,那就是另一条链的问题,你可以去其他地方找问题。

12.5. 其他调试工具

当然,在调试防火墙脚本时,还有其他一些非常有用的工具。本节将简要介绍最常用的几种工具,以便快速了解防火墙的各方面(内部、外部等)情况。我在这里选择的工具是 nmap 和 nessus 工具。

12.5.1. Nmap

Nmap 是一款出色的工具,可以从纯粹的防火墙角度查看,并找出哪些端口处于打开状态以及更多底层信息。它支持操作系统指纹、多种不同的端口扫描方法、IPv6 和 IPv4 支持以及网络扫描。

扫描的基本形式通过非常简单的命令行语法完成。别忘了用 -p 选项指定要扫描的端口,例如 -p 1-1024。请看下面的示例。

$ nmap -p 1-1024 192.168.0.1

Starting nmap 3.50 ( http://www.insecure.org/nmap/ ) at 2004-03-18 17:19 CET
Interesting ports on firewall (192.168.0.1):
(The 1021 ports scanned but not shown below are in state: closed)
PORT    STATE SERVICE
22/tcp  open  ssh
25/tcp  open  smtp
587/tcp open  submission

Nmap run completed -- 1 IP address (1 host up) scanned in 3.877 seconds

它还能通过操作系统指纹自动猜测被扫描主机的操作系统。虽然指纹识别需要 root 权限,但用来了解大多数人对主机的看法可能也非常有趣。使用操作系统指纹识别可能会像下面的示例列表一样。

# nmap -O -p 1-1024 192.168.0.1

Starting nmap 3.50 ( http://www.insecure.org/nmap/ ) at 2004-03-18 17:38 CET
Interesting ports on firewall (192.168.0.1):
(The 1021 ports scanned but not shown below are in state: closed)
PORT    STATE SERVICE
22/tcp  open  ssh
25/tcp  open  smtp
587/tcp open  submission
Device type: general purpose
Running: Linux 2.4.X|2.5.X
OS details: Linux Kernel 2.4.0 - 2.5.20
Uptime 6.201 days (since Fri Mar 12 12:49:18 2004)

Nmap run completed -- 1 IP address (1 host up) scanned in 14.303 seconds

如你所见,操作系统指纹识别并不完美,但对你和攻击者来说,它都有助于缩小范围。因此,你也有必要了解一下。最好的办法是尽量少给攻击者提供适当的指纹,有了这些信息,你就能很清楚地知道攻击者对你的操作系统了解多少。

当然,nmap 工具的用途远不止这些,您可以在 nmap 主页了解更多信息。更多信息,请查看 Nmap 资源。

正如您所理解的,这是一个测试您主机的绝佳工具,可以找出哪些端口是开放的,哪些是不开放的。例如,在完成设置后,使用 nmap 看看您是否真的成功完成了您想做的事情。是否从正确的端口得到了正确的响应,等等。

12.5.2. Nessus

nmap 更像是一个低级扫描程序,显示开放端口等,而 nessus 程序则是一个真正的安全扫描程序。它尝试连接不同的端口,最多只能找出不同服务器运行的版本。Nessus 在此基础上更进一步,它能找到所有开放的端口,找出在特定端口上运行的程序和版本,然后测试该程序是否存在不同的安全威胁,最后创建一份关于所有安全威胁的完整报告。

正如你所理解的,这是一个非常有用的工具,可以帮助你更多地了解主机。该程序是以服务器客户端的方式构建的,因此从外部使用外部 nessus 守护进程,或从内部使用内部 nessus 守护进程,都可以很容易地发现防火墙的更多信息。客户端是一个图形用户界面,您可以登录到 nessus 守护进程,进行设置,并指定要扫描漏洞的主机。生成的报告可能与下面的示例类似。

nessus-report.jpg (720×609) (frozentux.net)

注意:使用 Nessus 时应谨慎,因为它可能会导致指定攻击的机器或服务崩溃。幸运的是,那些可能导致机器崩溃的攻击默认是关闭的。

第 13 章 rc.firewall 文件

本章将介绍一个防火墙设置示例以及如何查看脚本文件。我们使用了一个基本设置,并深入探讨了它的工作原理和我们在其中所做的工作。在实际使用脚本之前,应先了解如何解决不同的问题以及可能需要考虑的事项。在对变量进行一些修改后,它可以原样使用,但不建议这样做,因为它可能无法与你的网络设置完美配合。不过,只要你有一个非常基本的设置,只需稍加修改,它就能非常顺利地运行。

注意:请注意,可能有更有效的方法来创建规则集,但编写脚本的目的是为了便于阅读,以便每个人都能理解它,而无需在阅读本脚本之前了解太多 BASH 脚本知识。

13.1. rc.firewall 示例

好了,一切都准备就绪,可以查看示例配置脚本了。如果你已经走了这么远,至少应该已经准备好了。这个示例 rc.firewall.txt(也包含在示例脚本代码库附录中)相当大,但没有很多注释。与其寻找注释,我建议你先通读脚本文件,对其有一个基本的了解,然后再回到这里了解整个脚本的细节。