使用 ipset 的高级防火墙配置

iptables 是用于在 Linux 内核中配置防火墙规则的用户空间工具。它实际上是更大的 netfilter 框架的一部分。也许是因为 iptables 是 netfilter 框架中最可见的部分,所以该框架通常被统称为 iptables。自 2.4 内核以来,iptables 一直是 Linux 的防火墙解决方案。

ipset 是 iptables 的一个扩展,允许您创建防火墙规则,一次匹配整个“集合”的地址。与正常按线性存储和遍历的 iptables 链不同,IP 集合存储在索引数据结构中,即使处理大型集合,查找也非常高效。

除了您可能想象到的明显有用情况(例如,阻止长列表的“不良”主机,而无需担心耗尽系统资源或导致网络拥塞)之外,IP 集合还为处理防火墙设计的某些方面开辟了新途径,并简化了许多配置场景。

在本文中,在快速讨论 ipset 的安装要求之后,我将花一些时间介绍 iptables 的核心基础知识和概念。然后,我将介绍 ipset 的用法和语法,并展示它如何与 iptables 集成以完成各种配置。最后,我将提供一些详细且相当高级的真实世界示例,说明如何使用 ipset 解决各种问题。

凭借显著的性能提升和强大的附加功能(例如,一次将单个防火墙规则应用于整个主机和网络组的能力),ipset 可能是 iptables 的完美搭配。

由于 ipset 只是 iptables 的一个扩展,因此本文既是关于 iptables 的,也是关于 ipset 的,尽管重点是那些与理解和使用 ipset 相关的功能。

获取 ipset

ipset 在许多发行版中是一个简单的软件包选项,并且由于有大量其他安装资源可用,因此我在此处不会花费太多时间介绍。

重要的是要理解,与 iptables 一样,ipset 由用户空间工具和内核模块组成,因此您需要两者才能使其正常工作。您还需要一个“ipset 感知”的 iptables 二进制文件,才能添加与集合匹配的规则。

首先只需在您的发行版的软件包管理工具中搜索“ipset”。您很有可能找到一个简单的过程,以一劳永逸的方式安装 ipset。在 Ubuntu(和可能的 Debian)中,安装 ipset 和 xtables-addons-source 软件包。然后,运行 module-assistant auto-install xtables-addons,ipset 即可在不到 30 秒的时间内准备就绪。

如果您的发行版没有内置支持,请按照 ipset 主页上列出的手动安装过程(请参阅“资源”)从源代码构建并修补您的内核和 iptables。

本文中使用的版本是 ipset v4.3 和 iptables v1.4.9。

iptables 概述

简而言之,iptables 防火墙配置由一组内置的“链”(分组到四个“表”中)组成,每个链都包含一个“规则”列表。对于每个数据包,在处理的每个阶段,内核都会查阅相应的链,以确定数据包的命运。

链的查阅顺序基于数据包的“方向”(远程到本地、远程到远程或本地到远程)及其当前的“处理阶段”(“路由”之前或之后)。请参阅图 1。

图 1. iptables 内置链遍历顺序

在查阅链时,数据包会按顺序与链的每个规则进行比较,直到匹配到规则。一旦找到第一个匹配项,就会采取规则目标中指定的操作。如果到达链的末尾而没有找到匹配项,则会采取链的默认目标或策略的操作。

链只不过是一个有序的规则列表,而规则只不过是一个匹配/目标组合。一个简单的匹配示例是“TCP 目标端口 80”。一个简单的目标示例是“接受数据包”。目标还可以重定向到其他用户定义的链,这提供了一种对规则进行分组和细分,以及通过多个匹配项和链级联,最终确定要对数据包采取的操作的机制。

从非常短到非常长的每个用于定义规则的 iptables 命令都由三个基本部分组成,这些部分指定表/链(和顺序)、匹配项和目标(图 2)。

图 2. iptables 命令的结构

要配置所有这些选项并创建完整的防火墙配置,您需要按特定顺序运行一系列 iptables 命令。

iptables 功能非常强大且可扩展。除了其许多内置功能外,iptables 还为自定义“匹配扩展”(用于对数据包进行分类的模块)和“目标扩展”(用于在数据包匹配时采取的操作的模块)提供了 API。

进入 ipset

ipset 是 iptables 的“匹配扩展”。要使用它,您需要使用 ipset 命令行工具创建和填充唯一命名的“集合”,然后在一条或多条 iptables 规则的匹配规范中单独引用这些集合。

集合只是一个地址列表,为了快速查找而高效存储。

以下是正常的 iptables 命令,它们将阻止来自 1.1.1.1 和 2.2.2.2 的入站流量


iptables -A INPUT -s 1.1.1.1 -j DROP
iptables -A INPUT -s 2.2.2.2 -j DROP

上面的匹配规范语法 -s 1.1.1.1 表示“匹配源地址为 1.1.1.1 的数据包”。要同时阻止 1.1.1.1 和 2.2.2.2,上面定义了两个单独的 iptables 规则,其中包含两个单独的匹配规范(一个用于 1.1.1.1,另一个用于 2.2.2.2)。

或者,以下 ipset/iptables 命令可以达到相同的效果


ipset -N myset iphash
ipset -A myset 1.1.1.1
ipset -A myset 2.2.2.2
iptables -A INPUT -m set --set myset src -j DROP

上面的 ipset 命令创建了一个新的集合(类型为 iphashmyset),其中包含两个地址(1.1.1.1 和 2.2.2.2)。

然后,iptables 命令使用匹配规范 -m set --set myset src 引用该集合,这意味着“匹配源标头与名为 myset 的集合匹配(即,包含在其中)的数据包”。

标志 src 表示匹配“源”。标志 dst 将匹配“目标”,而标志 src,dst 将同时匹配源和目标。

在上面的第二个版本中,无论集合中包含多少个其他 IP 地址,都只需要一个 iptables 命令。尽管此示例仅使用两个地址,但您可以轻松定义 1,000 个地址,并且基于 ipset 的配置仍然只需要一个 iptables 规则,而之前的没有 ipset 的方法将需要 1,000 个 iptables 规则。

集合类型

每个集合都属于特定的类型,该类型定义了可以存储在其中的值的类型(IP 地址、网络、端口等)以及如何匹配数据包(即,应检查数据包的哪个部分以及如何将其与集合进行比较)。除了最常见的检查 IP 地址的集合类型之外,还有其他集合类型可用于检查端口、IP 地址和端口组合、MAC 地址和 IP 地址组合等等。

每种集合类型都有其自己的关于它可以包含的值的类型、范围和分布的规则。不同的集合类型也使用不同类型的索引,并针对不同的场景进行了优化。最佳/最有效的集合类型取决于具体情况。

最灵活的集合类型是 iphash,它存储任意 IP 地址的列表,以及 nethash,它存储各种大小的任意网络 (IP/掩码) 列表。有关所有集合类型的列表和描述,请参阅 ipset 手册页(在撰写本文时共有 11 种)。

特殊的集合类型 setlist 也可用,它允许将多个集合组合成一个集合。如果您想要一个包含单个 IP 地址和网络的集合(例如),则这是必需的。

ipset 的优势

除了性能提升之外,ipset 在许多场景中还允许进行更直接的配置。

如果您想定义一个防火墙条件,该条件将匹配除来自 1.1.1.1 或 2.2.2.2 的数据包之外的所有内容,并在 mychain 中继续处理,请注意以下方法不起作用


iptables -A INPUT -s ! 1.1.1.1 -g mychain
iptables -A INPUT -s ! 2.2.2.2 -g mychain

如果数据包来自 1.1.1.1,它将不匹配第一个规则(因为源地址 1.1.1.1),但它将匹配第二个规则(因为源地址不是 2.2.2.2)。如果数据包来自 2.2.2.2,它将匹配第一个规则(因为源地址不是 1.1.1.1)。这些规则相互抵消——所有数据包都将匹配,包括 1.1.1.1 和 2.2.2.2。

虽然还有其他方法可以正确构造规则并在没有 ipset 的情况下实现所需的结果,但没有一种方法像下面这样直观或直接


ipset -N myset iphash
ipset -A myset 1.1.1.1
ipset -A myset 2.2.2.2
iptables -A INPUT -m set ! --set myset src -g mychain

在上面,如果数据包来自 1.1.1.1,它将不匹配该规则(因为源地址 1.1.1.1 确实与集合 myset 匹配)。如果数据包来自 2.2.2.2,它将不匹配该规则(因为源地址 2.2.2.2 确实与集合 myset 匹配)。

尽管这是一个简单的示例,但它说明了将完整条件拟合到单个规则中的基本好处。在许多方面,单独的 iptables 规则彼此独立,并且将单独的规则合并为单个逻辑条件并非总是直接、直观或最佳的,尤其是在涉及混合使用正常测试和反向测试时。ipset 只是在这些情况下让生活更轻松。

ipset 的另一个好处是,可以独立于活动的 iptables 规则来操作集合。添加/更改/删除条目是一件很简单的事情,因为信息很简单,并且顺序无关紧要。编辑平面列表不需要太多思考。另一方面,在 iptables 中,除了每个规则都是一个明显更复杂的对象之外,规则的顺序也至关重要,因此就地规则修改是更繁重且可能容易出错的操作。

从 NAT 中排除 WAN、VPN 和其他路由网络——正确的方法

出站 NAT(SNAT 或 IP 伪装)允许专用 LAN 中的主机访问 Internet。适当的 iptables NAT 规则匹配来自专用 LAN 的面向 Internet 的数据包,并将源地址替换为网关本身的地址(使网关看起来像是源主机,并将专用“真实”主机隐藏在其后面)。

NAT 会自动跟踪活动连接,以便它可以将返回数据包转发回正确的内部主机(通过将目标从网关的地址更改回原始内部主机的地址)。

以下是一个简单的出站 NAT 规则示例,它可以执行此操作,其中 10.0.0.0/24 是内部 LAN


iptables -t nat -A POSTROUTING \
         -s 10.0.0.0/24 -j MASQUERADE

此规则匹配来自内部 LAN 的所有数据包并对其进行伪装(即,应用“NAT”处理)。如果唯一路由是到 Internet 的路由,其中所有通过流量都是 Internet 流量,则这可能就足够了。但是,如果有到其他专用网络的路由,例如使用 VPN 或物理 WAN 链路,您可能不希望对该流量进行伪装。

克服此限制的一种简单方法(部分地)是基于物理接口而不是网络号来制定 NAT 规则(这是在线示例和教程中给出的最流行的 NAT 规则之一)


iptables -t nat -A POSTROUTING \
         -o eth0 -j MASQUERADE

此规则假定 eth0 是外部接口,并匹配在其上离开的所有数据包。与之前的规则不同,绑定到通过不同接口路由出去的其他网络的数据包将不匹配此规则(例如,使用 OpenVPN 链路)。

尽管许多网络连接可以通过单独的接口路由,但假设所有连接都将通过单独的接口路由是不安全的。KAME-based IPsec VPN 连接(例如 Openswan)就是一个很好的例子,它不使用虚拟接口,而其他用户空间 VPN(例如 OpenVPN)则使用虚拟接口。

上述接口匹配技术不起作用的另一种情况是,如果面向外部(“外部”)接口连接到中间网络,该网络除了到 Internet 的路由外,还具有到其他专用网络的路由。到专用网络的路由可能位于多个跃点之外,并且与到 Internet 的路由位于同一路径上,这完全是合理的。

设计依赖于匹配物理接口的防火墙规则可能会对网络拓扑施加人为的限制和依赖性,这强烈表明如果不是真正必要,则应避免这样做。

事实证明,这是 ipset 的另一个绝佳应用。假设除了充当本地专用 LAN (10.0.0.0/24) 的 Internet 网关之外,您的盒子还直接路由到其他四个专用网络(10.30.30.0/24、10.40.40.0/24、192.168.4.0/23 和 172.22.0.0/22)。运行以下命令


ipset -N routed_nets nethash
ipset -A routed_nets 10.30.30.0/24
ipset -A routed_nets 10.40.40.0/24
ipset -A routed_nets 192.168.4.0/23
ipset -A routed_nets 172.22.0.0/22
iptables -t nat -A POSTROUTING \
         -s 10.0.0.0/24 \
         -m set ! --set routed_nets dst \
         -j MASQUERADE

如您所见,ipset 可以轻松准确地定位您想要匹配的内容以及您不想要匹配的内容。 此规则将伪装从您的内部 LAN (10.0.0.0/24) 通过该盒子的所有流量,但绑定到您的 routed_nets 集合中的任何网络的那些数据包除外,从而保留到这些网络的正常直接 IP 路由。由于此配置完全基于网络地址,因此您不必担心使用的连接类型(VPN 类型、跃点数等),也不必担心物理接口和拓扑。

就应该是这样。由于这是一个纯粹的第 3 层(网络层)实现,因此实现它所需的底层分类也应该是纯粹的第 3 层。

限制某些 PC 仅访问某些公共主机

假设老板担心某些员工在 Internet 上玩耍而不是工作,并要求您限制他们的 PC 只能访问他们工作需要访问的一组特定站点,但他不希望这影响所有 PC(例如他的 PC)。

要限制三台 PC(10.0.0.5、10.0.0.6 和 10.0.0.7)只能外部访问 worksite1.com、worksite2.com 和 worksite3.com,请运行以下命令


ipset -N limited_hosts iphash
ipset -A limited_hosts 10.0.0.5
ipset -A limited_hosts 10.0.0.6
ipset -A limited_hosts 10.0.0.7
ipset -N allowed_sites iphash
ipset -A allowed_sites worksite1.com
ipset -A allowed_sites worksite2.com
ipset -A allowed_sites worksite3.com
iptables -I FORWARD \
         -m set --set limited_hosts src \
         -m set ! --set allowed_sites dst \
         -j DROP

此示例在单个规则中与两个集合匹配。如果源与 limited_hosts 匹配,并且目标与 allowed_sites 不匹配,则数据包将被丢弃(因为 limited_hosts 仅允许与 allowed_sites 通信)。

请注意,由于此规则位于 FORWARD 链中,因此它不会影响与防火墙本身之间的通信,也不会影响内部流量(因为该流量甚至不会涉及防火墙)。

阻止除某些 PC 之外的所有主机访问(反向场景)

假设老板想要阻止 LAN 上所有主机访问一组站点,但他的 PC 和他的助理的 PC 除外。为了多样化,在此示例中,让我们按 MAC 地址而不是 IP 地址匹配老板和助理的 PC。假设 MAC 地址分别为 11:11:11:11:11:11 和 22:22:22:22:22:22,并且要阻止其他所有人的站点是 badsite1.com、badsite2.com 和 badsite3.com。

为了代替使用第二个 ipset 来匹配 MAC 地址,让我们利用多个带有 MARK 目标的 iptables 命令来标记数据包,以便在同一链中的后续规则中进行处理


ipset -N blocked_sites iphash
ipset -A blocked_sites badsite1.com
ipset -A blocked_sites badsite2.com
ipset -A blocked_sites badsite3.com
iptables -I FORWARD -m mark --mark 0x187 -j DROP
iptables -I FORWARD \
         -m mark --mark 0x187 \
         -m mac --mac-source 11:11:11:11:11:11 \
         -j MARK --set-mark 0x0
iptables -I FORWARD \
         -m mark --mark 0x187 \
         -m mac --mac-source 22:22:22:22:22:22 \
         -j MARK --set-mark 0x0
iptables -I FORWARD \
         -m set --set blocked_sites dst \
         -j MARK --set-mark 0x187

如您所见,由于您没有像上一个示例那样使用 ipset 来完成所有匹配工作,因此命令要复杂得多。由于有多个 iptables 命令,因此有必要认识到它们的顺序至关重要。

请注意,这些规则是使用 -I 选项(插入)而不是 -A(追加)添加的。当插入规则时,它会被添加到链的顶部,将所有现有规则向下推送。由于每个规则都是插入的,因此有效顺序是相反的,因为当添加每个规则时,它会被插入到前一个规则之上。

上面的最后一个 iptables 命令实际上成为 FORWARD 链中的第一个规则。此规则匹配目标与 blocked_sites ipset 匹配的所有数据包,然后使用 0x187(任意选择的十六进制数)标记这些数据包。接下来的两条规则仅匹配来自要排除的主机且已标记为 0x187 的数据包。然后,这两条规则将这些数据包上的标记设置为 0x0,这将“清除”0x187 标记。

最后,最后一个 iptables 规则(由上面的第一个 iptables 命令表示)丢弃所有带有 0x187 标记的数据包。这应该匹配目标位于 blocked_sites 集合中的所有数据包,但来自任一排除的 MAC 地址的数据包除外,因为这些数据包上的标记在到达 DROP 规则之前已被清除。

这只是解决问题的一种方法。除了使用第二个 ipset 之外,另一种方法是使用用户定义的链。

如果您想使用第二个 ipset 而不是标记技术,您将无法实现与上述完全相同的结果,因为 ipset 没有 machash 集合类型。但是,有一种 macipmap 集合类型,但这需要同时匹配 IP 和 MAC,而不是像上面那样仅匹配 MAC。

注意事项:在大多数实际情况下,此解决方案实际上不适用于网站,因为许多可能成为 blocked_sites 集合候选者的主机(如 Facebook、MySpace 等)可能具有多个 IP 地址,并且这些 IP 可能会频繁更改。iptables/ipset 的一个通用限制是,只有当主机名解析为单个 IP 时才应指定主机名。

此外,主机名查找仅在运行命令时发生,因此如果 IP 地址更改,防火墙规则将不会意识到更改,并且仍然会引用旧 IP。因此,实现这些类型的 Web 访问策略的更好方法是使用 HTTP 代理解决方案,例如 Squid。该主题显然超出了本文的范围。

自动禁止尝试访问无效服务的主机

ipset 还为 iptables 提供了“目标扩展”,该扩展提供了一种基于任何 iptables 规则动态添加和删除集合条目的机制。您无需使用 ipset 命令手动添加条目,而是可以让 iptables 动态为您添加条目。

例如,如果远程主机尝试连接到端口 25,但您没有运行 SMTP 服务器,则它可能是不怀好意。为了拒绝该主机主动尝试任何其他操作的机会,请使用以下规则


ipset -N banned_hosts iphash
iptables -A INPUT \
         -p tcp --dport 25 \
         -j SET --add-set banned_hosts src
iptables -A INPUT \
         -m set --set banned_hosts src \
         -j DROP

如果数据包到达端口 25,例如源地址为 1.1.1.1,则它会立即添加到 banned_hosts 中,就像运行了此命令一样


ipset -A banned_hosts 1.1.1.1

从那时起,来自 1.1.1.1 的所有流量都被阻止,因为 DROP 规则。

请注意,这也将禁止尝试运行端口扫描的主机,除非他们以某种方式知道要避开端口 25。

清除正在运行的配置

如果您想清除 ipset 和 iptables 配置(集合、规则、条目)并重置为全新的开放防火墙状态(在防火墙脚本的顶部很有用),请运行以下命令


iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -t filter -F
iptables -t raw -F
iptables -t nat -F
iptables -t mangle -F
ipset -F
ipset -X

“正在使用”的集合(这意味着被一条或多条 iptables 规则引用)无法销毁(使用 ipset -X)。因此,为了确保从任何状态完全“重置”,必须首先刷新 iptables 链(如上所示)。

结论

ipset 为已经非常强大的 netfilter/iptables 套件添加了许多有用的功能和特性。如本文所述,ipset 不仅提供了新的防火墙配置可能性,而且还简化了许多难以构建、笨拙或效率较低的 iptables 单独配置。

任何时候您想要一次将防火墙规则应用于主机组或地址组时,都应该使用 ipset。正如我在几个示例中展示的那样,您还可以将 ipset 与一些更奇特的 iptables 功能(例如数据包标记)结合使用,以完成各种设计和网络策略。

下次您处理防火墙设置时,请考虑将 ipset 添加到组合中。我认为您会惊讶于它的实用性和灵活性。

资源

Netfilter/iptables 项目主页:http://www.netfilter.org

ipset 主页:http://ipset.netfilter.org

加载 Disqus 评论