驯服狂野的 Netfilter

作者:David A. Bandel

对于那些已经冒险从内核 2.2.X(甚至 2.0.X)升级到 2.4.X 的用户,恭喜你们。如果像许多人一样,您正在使用 ipchains 或 ipfwadm 来运行某种形式的防火墙,您的脚本可能运行良好。但迟早您可能想要升级。

在 2.4.X 内核中,Linux 数据包过滤器大师 Rusty Russell 和他的程序员团队已将 Netfilter 集成到内核中。Netfilter 是 ipchains 或 ipfwadm 的替代品。幸运的是,Netfilter 允许您继续使用 ipchains 或 ipfwadm,直到您可以通过添加一个内核模块来掌握 iptables,该模块允许这些较旧的数据包过滤器运行,从而实现兼容层。但是 Netfilter 有许多令人兴奋的新增功能,您会希望尽快转换这些规则。但是,请注意一点,如果您加载 ipchains 或 ipfwadm 模块,您将无法加载 ip_tables(反之亦然)。所以要么全有要么全无。但是,在阅读本文之后,进行更改应该很容易。

对于那些不熟悉数据包过滤的新手,请忽略 ipchains 的转换,并使用 iptables 示例。虽然并非所有 ipchains 命令和选项都会转换为 iptables,但本文应提供一个关于如何通过将 ipchains 命令转换为 iptables 命令来构建数据包过滤器防火墙的好主意。

您想要升级到 Netfilter 的原因是它与 ipchains 或 ipfwadm 不同,它是状态检测的。这意味着它可以跟踪连接,并允许对传出的请求的传入响应,而无需在防火墙中创建巨大的漏洞。连接跟踪为响应打开一个特定的临时漏洞,并且仅来自联系的服务器。稍后我们将看到这是如何工作的。缺点是,在使用连接跟踪的情况下,Netfilter 将需要使用更多的内存,因为连接是在 RAM 中跟踪的。因此,您的 4MB 386-16 可能不再能胜任这项工作,这取决于您的过滤要求。

背景

实际的 Netfilter 实现分为两部分,内核部分称为 Netfilter,用户空间工具与 Netfilter 接口并创建规则集 iptables。两者都是实现数据包过滤防火墙所必需的。

首先,我们将专注于内核部分。Netfilter 包括对 IPv4 和 IPv6 的支持。但是,它不过滤任何其他协议,因此您的防火墙不应运行 IPX、AppleTalk 或任何其他可能用于规避 iptables 规则的协议。同样,您绝不能启用内核快速交换选项。此项是内核配置菜单网络选项部分中的最后一项之一。该代码允许在 IP 堆栈的低层进行快速交换。Netfilter 代码驻留在更高的层级,因此快速交换有效地完全绕过了 Netfilter。

内核配置

为了开始使用 Netfilter,您需要编译内核以支持 Netfilter。大多数发行版默认包含此支持,因此快速测试是必要的。如果您可以插入模块 ip_tables,那么您无需担心本节。以 root 身份运行命令

modprobe ip_tables

然后运行

lsmod | grep ip_tables
如果 ip_tables 显示出来,那么您的情况良好。如果不是,请不要担心,重新构建内核非常容易。本文不会涵盖完整的内核重建过程,但是有很多资源可以帮助您完成此步骤。如果您发现需要重建内核,侧边栏将为您提供一些关于包含哪些内容以获得完整的启用 Netfilter 的内核的指导。

在您的内核中启用 Netfilter

Netfilter 模块

如果您已构建并安装了所有模块,则当输入规则时,除 ip_tables、ip_nat_ftp 和 ip_conntrack_ftp 外,所有模块都将自动安装。这些可以手动加载,也可以作为 iptables 启动脚本的一部分加载。

完全构建和安装 Netfilter 会生成大量模块,但大多数防火墙只会使用少数几个。未加载的模块不会占用内存,因此不必担心您不使用的模块。

获取和安装 iptables

您的发行版可能已安装 iptables,如果您的内核支持 Netfilter,则几乎可以肯定已安装。但是,如果您想要最新的版本,您可能必须从 Netfilter 站点获取。Netfilter 可在 netfilter.filewatcher.org 获取。按照 INSTALL 文件中的说明下载并编译它。以下说明假定内核源代码位于 /usr/src/linux 中。如果不是,请适当调整以下说明。如果您需要运行

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

make patch-o-matic KERNEL_DIR=/usr/src/linux
那么您需要在继续之前重新编译内核。否则,您可以忽略这两个命令。一般来说,patch-o-matic 适用于有特殊需求的用户,普通用户不感兴趣。

运行完

make KERNEL_DIR=/usr/src/linux

后,运行

make install KERNEL_DIR=/usr/src/linux
您现在可以开始使用 iptables 了。
iptables 命令行

iptables 命令行最多分为六个部分。第一部分是 iptables 命令,本文不再赘述。第二部分是表规范,链名称是第三部分。第四部分是规则规范,它是命令中用于匹配 IP 或 ICMP 标头的部分,但在某些情况下也可以是规则编号。第五部分是目标,第六部分是目标选项。然后,通用命令行如下所示:

iptables [-t table] -ACDI CHAIN rule-specification å -j TARGET [target option]

以上内容并非适用于所有情况,但却是最常见的。您还会发现 -L 命令很有用。本文将介绍它以及其他几个命令行变体。

表和链

Netfilter 有三个您需要关注的表:filter、nat 和 mangle。本文在展示 iptables 命令时,始终会指定表。但是,如果未指定表,则假定为 filter 表。因此,如果您未指定表且您的规则失败,请尝试放入表规范并重试。

每个表都有可用的特定链。用户创建的链将仅属于一个表。您将看到一些内置链属于多个表,但这仅适用于内置链。您不能在用户创建的链中混合来自其他表的链。

filter 表是基本的数据包过滤器表,具有内置链 INPUT、FORWARD 和 OUTPUT。添加到 filter 表中创建的用户创建的链的规则只能包含在 INPUT、FORWARD 或 OUTPUT 链中有效的目标。遍历 filter 表的数据包将仅通过 INPUT、FORWARD 或 OUTPUT 中的一个。仅当数据包的目标是本地系统时,才会遍历 INPUT 链。仅当数据包正在通过本地系统并绑定到另一个系统时,才会遍历 FORWARD 链。OUTPUT 链仅由源自本地系统且具有外部目标的数据包遍历。任何给定数据包都只会遍历一个链。这与始终使用 input 和 output 链的 ipchains 不同,如果数据包正在通过,ipchains 还会使用 forward 链。

请注意,在上一段中,iptables 链名称是大写的,而 ipchains 链名称不是。这是故意的,反映了语法的变化。

nat 表执行网络地址转换。nat 的内置链是 PREROUTING、POSTROUTING 和 OUTPUT。每个链都允许一个特定的目标。PREROUTING 接受 DNAT 目标,其余链接受 SNAT 目标。稍后将详细介绍这些目标。

mangle 表用于更改(修改)标头中 IP 地址以外的信息。它可以用于标记数据包、更改服务类型 (TOS) 或更改生存时间 (ttl) 信息。

规则

每个 iptables 命令的规则规范部分是命令的核心。通过正确地编写规则,您可以精确选择规则应应用于哪些数据包。此选择标准可以像您需要的那么通用或那么具体。在大多数情况下,您需要确保特定标准在更通用标准之前。

也就是说,我不会在这里过多地强调规则,除了补充一点,可以将多个选项串在一起,其中一些选项带有它们自己的选项。但是您确实需要确保规则有意义。例如,不要在输入链中指定输出接口,否则该规则永远不会匹配任何内容。语法可能允许您构建不可能的规则,但这不是一个好主意。如果您对特定规则有疑问,您可以始终将目标设为日志目标,然后发送与该规则匹配的流量,以查看它是否确实触发。如果您需要一个工具来测试您的规则,请查看 SendIP (www.earth.li/projectpurple/progs/sendip.html)。

目标

Netfilter 有四个内置目标:ACCEPT、DROP、QUEUE 和 RETURN。DROP 目标取代了 ipchain 的 DENY 目标。使用的所有其他目标都基于作为目标加载的模块。这些包括 REJECT、LOG、MARK、MASQUERADE、MIRROR、REDIRECT 和 TCPMSS。终端目标,例如 ACCEPT、DROP、REJECT、MASQUERADE、MIRROR 和 REDIRECT,终止链。LOG 目标不会终止链。LOG 也不会 ACCEPT、REJECT 或 DROP 数据包,因此链继续被遍历。因此,ipchains -l 选项现在是另一个目标,但它是一个非终端目标。链的其余部分将被遍历,直到它命中策略规则。

策略规则是链的总体规则。如果您的 FORWARD 链包含 DROP 策略,并且链中没有以上规则匹配,则数据包将在命中策略规则时终止。您的策略规则只能是内置目标之一。您不能将 REJECT 作为策略规则。

示例

在我们查看示例之前,让我们设置一些东西。脚本很好,一个从 rc.local(无论它在您的系统上的哪个位置)运行的脚本总是好的。因此,让我们编写一个 rc.iptables 脚本作为我们示例的一部分,以在启动期间实现 Netfilter(请参阅列表 1)。(注意:所有 iptables 规则都将以 $IPT 开头,并且应该不间断地继续到行尾,即下一个命令。规则不应断行。)

列表 1. rc.iptables 脚本

我们设置了一些变量来启动,停止了通过系统的转发流量,然后插入了一些模块。ip_tables 模块允许我们开始编写规则。如果我们使用 NAT 表来执行 SNAT 或 MASQUERADE(下面介绍)以允许主动 FTP,则 ip_nat_ftp 模块是必需的。ip_conntrack_ftp 允许 FTP 的连接跟踪。此模块会自动加载 ip_conntrack 模块,因为它依赖于 ip_conntrack。如果您不需要或不想要 ip_conntrack_ftp 模块,但想要确保您的防火墙执行 IP 分片整理(一个好主意),您可以用 ip_conntrack 替换 ip_conntrack_ftp。让我们继续我们的脚本

for i in filter nat mangle
do
$IPT -t $i -F
$IPT -t $i -X
done

以上行将刷新 (-F) 作为 -F 参数显示的链中的所有规则。由于没有给出链名称作为参数,-F 将刷新所有链。请注意,这必须对每个表完成,因此是循环。如果您不使用特定的表,您可以从 for 语句中删除它。随着每个循环的开始,您会注意到一个新的模块已被加载:首先是 iptable_filter 模块,然后是 iptable_nat 模块,最后是 iptables_mangle 模块。如果您从循环中删除 mangle,则 iptables_mangle 模块将不会被加载。未使用的模块可以随时删除。

在 ipchains 下,您将使用类似

ipchains -F -X

来完成同样的事情。

在我们继续之前,让我们假设以下设置:一个家庭用户和三个通过我们的防火墙/工作站 PC 访问 Internet 的系统。访问是通过拨号 (ppp0) 进行的。如果您的设置不同,请将您的外部接口替换为 ppp0。这些系统没有在其自身网络之外提供任何服务,该网络位于 eth0 上。但是,我们希望内部的所有系统都能够上网。对于第一个示例,我们将假设来自外部 ISP 的动态 IP [请参阅列表 2,rc.iptables.dynamic,网址为 ftp.linuxjournal.com/pub/lj/listings/issue89/4815.tgz]。

由于我们的防火墙 PC 也是工作站,它将发起和终止自己的流量。虽然对于防火墙(这些应该是专用系统)来说不是一个好主意,但在家庭网络上,我们真的不需要专用防火墙。考虑到这一点,请记住 iptables 和 ipchains 之间的一个区别在于 INPUT、FORWARD 和 OUTPUT 链。在 ipchains 中,遍历 FORWARD 链的数据包来自 INPUT 并通过 FORWARD 到 OUTPUT,因此我们可以将我们的规则放在 INPUT 链中,并且对于进入 FORWARD 链的数据包是安全的。iptables 实现仅对本地系统使用 INPUT,对其他系统使用 FORWARD。在我们的例子中,我们需要在每个 FORWARD 和 INPUT 链中使用相同的规则。为了防止复制大量规则,让我们创建一个名为 tcprules 的用户链,并从 INPUT 和 FORWARD 调用它。继续我们的脚本,然后

$IPT -t filter -N tcprules

ipchains 等效的规则将是相同的,除了命令的 -t filter 选项

ipchains -N tcprules
现在来一点 Netfilter 的魔法。我们想要阻止某人从外部连接到我们的系统,但允许我们的用户建立与外部的连接。以下规则利用了 Netfilter 的状态检测功能
$IPT -t filter -A tcprules -i ppp+ -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPT -t filter -A tcprules -i ! ppp+ -m state --state NEW -j ACCEPT
$IPT -t filter -A tcprules -i ppp+ -m state --state NEW,INVALID -j DROP
如果您想在 ipchains 中执行类似操作,您最接近的方法是拒绝 syn 数据包到 ppp+ 接口
ipchains -A input -i ppp+ ! -y -j DENY
此时需要注意几点。首先,“!”否定了它后面的任何内容。因此,! ppp+ 与指定所有其他接口(对于我们的家庭用户,lo 和 eth0)相同。ppp 末尾的“+”告诉 Netfilter 此规则适用于所有 ppp 接口。

至于 ESTABLISHED、RELATED、NEW 和 INVALID 参数,它们比表面上看起来的更多。ESTABLISHED 允许流量继续,因为它之前在两个方向都看到了流量。ESTABLISHED 显然适用于 TCP 连接,但也适用于 UDP 流量,例如 DNS 查询和 traceroute 以及 ICMP ping。实际上,首先检查数据包以查看连接是否存在于连接跟踪表 (/proc/net/ip_conntrack) 中。如果存在,则不运行链,应用原始规则并且数据包通过。在某些情况下,由于此检查,Netfilter 比其前身更快。RELATED 参数涵盖了多种情况。此参数应用于主动 FTP,它在端口 20 上打开相关连接,但也应用于与 TCP 连接相关的 ICMP 流量。NEW 参数应用于仅设置了 SYN 位(并且未设置 ACK 位)的数据包。INVALID 应用于具有无效选项集的数据包,例如 XMAS 树扫描。

以上规则允许内部系统在内部和外部传递新连接,但不允许传入新的或无效的数据包。

由于我们将在防火墙后面伪装我们的网络,因此我们需要设置一个伪装规则。此过程与 ipchains 中使用的过程非常相似。假设我们的内部网络是 192.168.0.0/24,我们将使用以下规则

$IPT -t nat -A POSTROUTING -o ppp+ -s 192.168.0.0/24 -d 0/0 -j MASQUERADE

ipchains 用户应注意的一个区别是使用 -o ppp+ 而不是 -i ppp+。这是因为在 ipchains 中,我们使用 -i 处理接口。在 iptables 中,-i 代表输入接口,-o 代表输出接口。如果您错误地在上面的行中使用 -i ppp+,则不会发生伪装。事实上,该规则应该永远不会匹配。

让我们通过在我们需要的 tcprules 中实现上述规则、实现我们的 filter 表策略并重新打开 ip_forwarding 来完成我们的脚本

$IPT -t filter -A INPUT -j tcprules
$IPT -t filter -A FORWARD -j tcprules
$IPT -t filter -P INPUT DROP
$IPT -t filter -P FORWARD DROP
echo 1 > /proc/sys/net/ipv4/ip_forward

默认情况下,filter 表策略都是 ACCEPT。鉴于 Netfilter 的状态检测性质,这提供了足够的保护,这与 ipchains 不同。如果您考虑一下我们已实现的状态检测规则,您会发现默认策略不会造成任何危害;事实上,任何东西都不应该到达默认策略。但有些人认为,始终以安全为上策,因此我们可以丢弃任何尚未处理的内容。请注意,策略本身没有目标,因此我们不使用 -j。这也是为什么只有内置目标才能成为策略的原因——策略实际上不是目标。如果您真的想要将 REJECT 作为您的策略,您需要添加类似以下内容

$IPT -t filter -A tcprules -i ppp+  -j REJECT --reject-with icmp-host-unreachable
以上规则适用于所有通过您的 ppp 接口传入的数据包。确保此规则是您的最后一条规则,因为任何东西都不会通过此规则。

虽然您可以制定规则

$IPT -f filter -A tcprules -j REJECT --reject-with icmp-host-unreachable

但您会发现您的内部主机也被阻止了,因为此规则将应用于所有接口(您也应该记住策略)。

此时,我应该提到,与 ipchains 不同,目标匹配不一定会终止链。例如,如果您在 tcprules 中使用以下规则

$IPT -t filter -I INPUT -p ICMP -m icmp --icmp-type echo-request -m limit --limit 1/minute -j LOG --log-prefix "ICMP-packet "

下一个规则(可能也可能不匹配此数据包)将被处理。如果规则匹配未丢弃、拒绝、接受或排队数据包,并且不是返回,则链将继续。

ipchains 用户会注意到,在 iptables 下,LOG 本身就是一个目标,而不是像 ipchains 那样在目标之后使用简单的 -l 选项。这允许一定的灵活性,可以从上面的规则中看出。limit 匹配可防止恶意用户淹没您的日志。LOG 目标可以发送带有其消息的前缀,以便可以轻松地从日志中 grep。

在我们继续之前,所有 ipchains 用户都知道您可以使用类似

ipchains -L -n

来查看链。这将向您展示链。使用 Netfilter,我们需要逐表查看链

iptables -t filter -L -n
iptables -t nat -L -n
iptables -t mangle -L -n
您可以添加 -v 以获取有关每个规则的更多信息
iptables -t filter -L -nv
接下来是一个示例,演示新的 SNAT 和 DNAT。如果您了解 mangle 表及其用法,您可能不需要本文,并且会通过电子邮件向我发送您注意到的任何错误。

在此示例中,我们将假设我们的家庭用户具有宽带连接,并且正在使用 eth0 作为内部网络,eth1 作为外部网络。脚本的开头与之前相同,但是当我们到达 tcprules 时,我们将做一些不同的事情。在这里,我们将假设用户具有静态 IP 209.127.112.17,这给了我们更多的自由 [请参阅列表 3,rc.iptables.static,网址为 ftp.linuxjournal.com/pub/lj/listings/issue89/4815.tgz]。事实上,我们还可以假设用户拥有自己的域名,并在端口 25 上运行自己的电子邮件服务器,该服务器位于 IP 为 192.168.0.2 的内部系统上(防火墙为 192.168.0.1)。他的 DNS 条目指向 209.127.112.17 作为他的邮件服务器,如列表 4 所示。

列表 4. 邮件服务器的 DNS 条目

前两个 tcprules 与第一个示例中的相同。但是在我们丢弃所有其他连接之前,我们接受端口 25 上的连接。然后,在 nat 表中,我们获取端口 25 连接并将其转发到同一端口上的另一个内部主机。您可以对所有连接执行此转发。请记住,如果您为 DNS 执行此转发,则需要转发 UDP 以及 TCP。事实上,除非有人会在外部进行区域传输,否则您可以删除 TCP 部分,只传递 UDP。

请注意,现在,我们没有使用 MASQUERADE 作为传出连接的目标,而是使用了 SNAT。如果您想知道,S 代表源,这是正在更改的内容。在 DNAT 的情况下,我们更改了数据包的目的地。参数 --to-source 可以接受一系列 IP,因此您的防火墙可以看起来像多台主机。如果您从 ISP 获得了五个可用的 IP,则可以使用所有五个作为传出 IP。然后,您可以将不同的服务指向不同的 IP,并在防火墙后面最多拥有五台系统来回答 DNS 查询、托管网站、接受邮件等。Netfilter 还允许您执行基本的负载均衡。当 DNAT 使用一系列目标时,连接数最少的系统(不一定是负载最轻的系统)会获得连接。

您可能想要了解的唯一其他入门知识是如何增加跟踪的最大连接数。此数字默认情况下根据您的系统拥有的 RAM 量来确定。但是,该数字是保守的,可以增加。您可以通过以下方式找到此数字

cat /proc/sys/net/ipv4/ip_conntrack_max

在我的系统上,拥有 256MB 的 RAM,该数字是 16376。

结论

使用 Netfilter 的状态检测规则,您实际上可以通过明智地使用其连接跟踪来减少工作量,从而提高家庭系统的安全性。您还可以使用更多选项。本文仅触及皮毛。我建议您使用 Netfilter 站点上提供的 Rusty 的不可靠指南(前面提到过)。

对于需求简单的家庭用户,请保持防火墙的简单性。我不推荐大多数防火墙工具和脚本,因为它们在您的防火墙中添加了不必要的复杂性。如果您不理解某条规则,请不要实施它。前三条状态检测规则(使用 -m state 规则)将使您保持良好状态。如果攻击者已经进入并入侵了系统,这些规则将无济于事。它们也不能保护您免受基于电子邮件的木马的侵害,但它们可以防止直接攻击。我建议,如果您不使用 IRC,您可以记录并丢弃传出的 IRC 连接

$IPT -j filter -I tcprules -p tcp --destination-port 6667 -j LOG --log-prefix "IRC attempt "
$IPT -j filter -I tcprules 2 -p tcp --destination-port 6667 -j DROP

此外,如果您不需要任何人进入您的网络,请不要打开任何端口(就像我们在第二个示例中所做的那样)。本文没有讨论如何正确隔离您的网络,以将可访问互联网的系统与受信任的内部系统隔离。如果您需要这种复杂程度,并且您的风险评估要求这样做,那么可能是时候寻求知识渊博的帮助了。

Taming the Wild Netfilter
David A. Bandel (dbandel@pananix.com) 是一位 Linux/UNIX 顾问,目前居住在巴拿马共和国。他是 Que Special Edition: Using Caldera OpenLinux 的合著者。
加载 Disqus 评论