Netfilter 2:掌握您的 POM
在 2001 年 9 月号的 LJ 上发表了“驯服野生的 Netfilter”[/article/4815] 之后,我收到了许多电子邮件,大多数是询问关于使用 Netfilter 的更详细信息。为了满足这些请求,这次我将深入探讨一下。对于那些还没有阅读并尝试过基本设置的人,我建议您先这样做。本文稍微高级一些,旨在为那些对前述文章中描述的基础知识有所掌握的人而准备,即使这种掌握可能很薄弱。
为了充分利用 Netfilter 和用户态组件 iptables,您需要升级您的内核和 iptables。虽然您发行版自带的内核和 iptables 没有什么问题,但 Netfilter 代码正在不断开发中。您也肯定可能不知道您的发行版认为适合在 iptables 领域包含哪些补丁(可能没有)。而且,并非所有补丁都显示为 Netfilter 模块或 iptables 匹配扩展。但是,我建议您不要在当前运行的防火墙上首次尝试本文中的内容。请务必在测试系统上进行实验,以了解会发生什么。
上面段落中的最终建议提出了一个非常重要的观点。本文基于 iptables-1.2.4 和 Linux 内核 2.4.17。如果您使用不同的版本,您的结果几乎肯定会有所不同。原理将是相同的。不要惊慌;只需尝试对您想要的东西做出一些明智的决定。还要理解,正如油和水通常不会混合一样,您在模块选择方面所做的一些选择会以同样的方式影响其他模块——也就是说,有些模块与其他模块不能很好地混合。查看 2.4.18-prepatches,为本文应用的一些 iptables 补丁将包含在 2.4.18 中。我建议在最终发布此内核版本时查看 2.4.18 Changelog,以了解您现在不需要尝试修补的内容(无论是否检测到,该补丁都会失败——见下文)。
在本文中,我们将使用 Rusty Russell 的 patch-o-matic,它将同时修补 iptables 和内核源代码。这个 patch-o-matic (POM) 不是完全自动的,未经您的批准,不会尝试修补任何东西。它还会首先测试要应用的补丁,以查看它是否正确应用。如果它没有正确应用,您将被告知并给出几个选项。如果补丁没有成功,您最好和最安全的做法是不应用它继续。但我们将在进行过程中看到这一点。
首先,下载您要使用的最新内核版本(可从 www.kernel.org 获取)。它可以是 2.4.16 或更高版本。我总是建议在最新的稳定版本发布后至少等待一周再尝试它。这样,如果一些小错误进入了最新的内核(例如 2.4.15 的关机文件系统损坏错误),您可能会知道它并避免潜在的糟糕情况。
使用任何您喜欢的方法,打开并配置您的新内核。本文不会介绍内核构建,但许多文章和网站可以帮助您快速了解这一点(权威指南可以在您的内核源代码树下的 Documentation/Changes 中找到)。我建议您将所有 Netfilter 代码配置为模块。目前,您至少需要选择
1. 代码成熟度级别选项-->提示开发和/或不完整的代码/驱动程序
和
2. 网络选项-->网络数据包过滤(取代 ipchains)
并从这里继续进入
-->TCP/IP 网络-->IP:Netfilter 配置(单击进入子页面)
3. 在 IP: Netfilter 配置子页面上配置所有模块。
如果您愿意,请选择 IPv6 协议,然后您也可以配置 IPv6 Netfilter 模块。您至少需要使用此内核进行到“make dep”步骤,才能准备好一切。
顺便说一句,如果您阅读网络数据包过滤选择附带的帮助的底部附近,您会发现如果您的系统将充当路由器,则应选择 Y;否则选择 N,如果不确定,则选择否。我不知道这个建议有多明智。即使是简单的主机也经常需要 Netfilter 可以提供的额外保护。您必须根据您对网络的最佳风险评估以及主机的使用方式来自己决定这个问题。我们将在下面看到 Netfilter 实际上是如何在路由器以外的机器上使用的。
准备好内核后,下载并打开最新的 iptables(可从 netfilter.samba.org 获取)。更改到 iptables 目录,您几乎可以开始了。如果您的内核不在 /usr/src/linux 中,那么您需要告诉 iptables 在哪里找到它。此外,如果您不想将 iptables 安装在 /usr/local/ 中,则需要指定要将其安装在哪里。包含以下每个参数(如下所列)都有原因。由于 iptables 将修补内核,因此它必须知道在哪里找到内核,并且 iptables 二进制文件必须知道在哪里找到扩展。扩展的位置硬编码在二进制文件中,因此您以后不能随意移动它们——您必须重建并重新安装。
以下参数可用于 iptables 构建
KERNEL_DIR=/path/to/kernel/source(默认值:/usr/src/linux)
BINDIR=/path/to/install/binaries(默认值:/usr/local/bin)
LIBDIR=/path/to/install/lib-extensions(默认值:/usr/local/lib)
MANDIR=/path/to/install/manpages(默认值:/usr/local/man)
此时,我必须注意,我经常在 chroot 环境中工作,尤其是在处理内核源代码等时,这样我就不会无意中损坏工作系统。但是,我发现 patch-o-matic 在 chroot 环境中无法正常工作。通常,patch-o-matic 会在内核源代码树上方创建一个临时目录,在其中进行修补和测试,然后从那里替换内核源代码。在 chroot 环境中(至少在我的系统上),永远不会创建此目录,并且内核源代码树上方的目录会变得混乱,因为它充满了内核源代码。我一直疏忽,没有花时间充分研究问题以确定根本原因。但是,如果您在继续之前备份内核源代码,则这并不重要。
您要使用的第一个命令(假设内核源代码目录位于您的 $HOME 目录中)是
make pending-patches KERNEL_DIR=$HOME/linux
您应该不会遇到此目标的问题。它会告诉您它要安装哪些补丁。您应该对所有这些都说“yes”。如果由于某种原因,任何补丁都无法应用(程序可能会告诉您补丁应用失败),请不要担心。该补丁可能已经包含在内核源代码中,但补丁逻辑无法检测到它。第二次,只需告诉脚本“no”。不要强制应用补丁。虽然这是一个选项,但通常会导致脚本中止。一旦 pending-patches 完成,它会告诉您内核已准备好进行编译。但我们还没有完全准备好。
在我的系统上应用或尝试应用的补丁是 ipt_LOG.patch(成功)和 tos-fix.patch(失败)。tos-fix.patch 失败是因为应用了修复,但它与 patch-o-matic 中的补丁不完全对应。
一旦您将所有待处理的补丁应用到内核源代码,您就可以查看尚未合并到内核中的新补丁。
不久前,Netfilter 首席开发人员 Rusty Russell 引入了“make patch-o-matic target”,以帮助人们将新事物合并到内核中,而无需知道如何使用 patch。此目标工作相当好,但不要期望它能完美工作。有时,补丁逻辑足够旧,并且内核源代码发生了足够的更改,以至于特定补丁无法工作。最近几个月,patch-o-matic 增长了很多,一些补丁破坏了其他补丁。因此,Rusty 又合并了另一个目标“most-of-pom”,以允许新的 iptables 构建者访问尽可能多的补丁,但减少失败的可能性。
我对您的建议是运行 make most-of-pom,首先对所有内容都说“no”,但记下您感兴趣的补丁。然后运行 make patch-o-matic,记下 most-of-pom 中没有的任何您可能感兴趣的新补丁。如果您在 patch-o-matic 中对任何新补丁都不感兴趣,请坚持使用 most-of-pom。如果任何新补丁确实让您感兴趣,并且仅在 patch-o-matic 中可用,请仔细注意那些新的、有趣的补丁可能会破坏的其他补丁。截至撰写本文时,最糟糕的罪魁祸首似乎是 drop-table 补丁。由于这个原因,我们不会查看该补丁。但是如果您需要它,只需阅读并注意该补丁以及其他告诉您它们将被破坏的补丁的警告。
在某些情况下,例如 H323-conntrack-nat 补丁,您将无法将补丁应用于我们在本文中使用的内核。如果您不能没有这个特定的补丁,您可能无法使用实验性的 make 目标进行修补(patch-o-matic 或 most-of-pom)。如果您是这种情况(我为一个客户的系统有这种需求),您需要访问 roeder.goe.net/~koepi。那里的补丁包括 newnat5、h323、talk、ftp 和 irc nat 助手。这是一个使用常用 patch 实用程序应用的标准补丁。
在运行 make KERNEL_DIR=$HOME/linux patch-o-matic 时,我选择了以下补丁
NETLINK.patch (successful) NETMAP.patch (successful) iplimit.patch (successful) mport.patch (successful) pkttype.patch (successful) psd.patch (successful) realm.patch (successful) snmp-nat.patch (failed: already in kernel) string.patch (successful) tos-fix.patch (failed: already in kernel) ulog.patch (failed: incompatible with kernel or previously applied patch) LOG.patch.ipv6 (failed: already in kernel) REJECT.patch.ipv6 (successful)
在浏览 pending-patches 和 patch-o-matic 时,您需要注意几件事。屏幕被一条线分隔。线上方是欢迎提示和警告。线下是您应该查看的信息。
首先,您将有一行或多行告诉您哪些补丁已被应用。当您浏览 patch-o-matic 时,您会注意到它列出了 pending-patches 中应用的补丁。事实上,因为我们正在运行 patch-o-matic,所以我们不需要运行 pending-patches;这些补丁也应该在这里应用。您只需要在不运行 most-of-pom 或 patch-o-matic 时运行 pending-patches,例如,如果您决定使用上面提到的 newnat5/h323 补丁。
在已应用行下方,有一行
Testing... Patchname.patch STATUS (comment)
patchname.patch 是正在测试的补丁。STATUS 通常为 NOT APPLIED。注释将是(x 个文件丢失)或(x 个拒绝的 y 个代码块)。文件丢失意味着补丁尚未应用(或特定的对应文件不会丢失),或者补丁与内核源代码中的内容不匹配。一般来说,拒绝意味着补丁已被应用,只是由于某种原因与 patch-o-matic 中的补丁不匹配。最常见的原因是在 patch-o-matic 中的补丁和内核中的补丁之间进行了小的修复。当您看到拒绝时,可以预见该补丁将无法应用。并非所有补丁都能正常工作。请注意,ulog.patch 失败了。此失败可能是因为它与之前的补丁不兼容,或者与自补丁最初创建以来(已更改的)内核源代码不兼容。
第三是关于补丁的信息、作者、补丁状态、补丁是什么、它做什么、通常是一个示例来澄清如何使用它,以及可能的注释。
最后,问题是,您要应用此补丁吗?选项有 No(默认)、yes、test、force、quit 和 help,如 [N/y/t/f/q/?] 所示。
一旦我们添加了我们想要的补丁,我们就完成了。现在内核已准备好进行编译。或者它准备好了吗?嗯,是的。但是,我们已经向内核添加了目标。我建议您返回到内核树,运行 make oldconfig 并选择我们合并的新 Netfilter 匹配项和目标(否则有什么意义?)。现在您可以继续编译内核。在您安装内核并重启进入内核后,您就可以开始使用新的匹配项和目标了。
在编译内核源代码时,有足够的时间来编译和安装 iptables。如果 KERNEL_DIR= 不在 /usr/src/linux 中,并且如果您不希望将新的二进制文件和扩展安装在 /usr/local/ 中,请记住提供 KERNEL_DIR= 以及 BINDIR=、LIBDIR= 和 MANDIR= 参数。
在开始编译之前,一个小小的修复。无论出于何种原因,NETLINK 扩展都无法编译。因此,如果您选择了 NETLINK.patch(就像我一样),则需要进行一些小的调整。只需 cd 进入 extensions 目录,并使用您喜欢的文本编辑器打开 Makefile。第一行是我们的 shebang 行。第二行是空白行。第三行以 PF_EXT_SLIB: 开头,并包含要制作和安装的各种扩展。在行尾添加 NETLINK 并保存文件。
现在 cd 返回到 iptables 源代码树的根目录,并运行您的 make 和 make install,如果需要,添加上面提到的参数。
上面,我们使用修改后的补丁过程来修补内核。如果您像我一样,在内核补丁发布时就立即获取它们,您会发现有些补丁将不再干净地应用,因为内核源代码已被修改。因此,当我想尝试新的内核时,我会保存旧的 .config 文件,清除旧的内核源代码并重新开始。您可以这样做,或者记住在修改内核源代码树之前保存其 tarball。
如果您之前使用 patch-o-matic(或 pending-patches 或 most-of-pom)构建了模块化内核,并且仅添加了几个模块,在使用 make oldconfig 添加新模块后,您可以执行 make modules; make modules_install 并开始使用这些模块。
如果您想再次查看在添加 patch-o-matic 补丁时看到的信息,它可以在 iptables-x.x.x/patch-o-matic/ 目录中找到。文件 *.patch.help 包含该信息。在大多数情况下,这些文件中的示例在内核配置帮助中重复出现。
现在我们已经编译并安装了我们想要的模块,我们准备好开始使用它们了。但在我们开始之前,我们需要确切地决定我们要做什么。为了做到这一点,我们需要奠定一些基础。当我们只有家庭系统并且我们希望让所有人从内部到外部访问,但阻止所有人从外部到内部访问时,这个基础并不那么重要。仅我们的状态表实际上就可以保证我们拥有这些;添加伪装或 SNAT,我们就完成了。这就是我们使用“驯服野生的 Netfilter”文章(2001 年 9 月 LJ)中的基本脚本所拥有的。
但是公司中使用的防火墙很少如此简单。它们要求我们首先理解(甚至可能重组)我们的网络拓扑。我们还需要确切地了解我们希望防火墙完成什么。有时,这与我们的家庭系统没有太大区别,但通常情况下,它会截然不同。我们可以使用公司的网络安全策略来帮助我们(我们确实有 NSP,不是吗?),加上一些关于我们希望从网络访问中获得什么知识。我们不会在这里讨论风险评估[请参阅 Mick Bauer 在 2002 年 1 月号 LJ 中的“实用威胁分析和风险管理”],但应牢记他们的发现,以帮助指导我们制定总体方案。
多年前,我们谈到了我们连接到互联网的主机。它们都直接连接到互联网。没什么大不了的,因为所有系统管理员都彼此认识,而且事情很友好。然后其他人发现了互联网,我们不得不做出一些改变。随着事情失控般地蔓延,我们忘记了或从未知道我们的邻居系统管理员是谁。我们发现我们的系统受到攻击。因此,我们让我们的公共系统直接连接,但开始将我们用户的主机隐藏在数据包过滤器后面,以帮助保护它们。我们的路由器和数据包过滤器之间的系统被称为位于我们的 DMZ 或非军事区。其余的都在数据包过滤器后面的受信任网络上。
今天,很少有公司会以这种方式配置他们的系统。在我们目前的情况下,通常只有蜜罐会被故意置于毫无防备的状态。如今,最常见的两种配置要么是具有两个内部 NIC 的防火墙(一个用于受信任网络,一个用于内部公共访问网络),要么是两个单独的防火墙(第一个允许公共流量进入受控但不受信任的网络,第二个允许进入我们的内部圣所或受信任网络)。
虽然小公司可能会在一个私有内部网络上混合受信任网络和受控网络,但最好尽可能将它们分开。您还应该控制谁有权访问哪个区域。防火墙在阻止坏人进入方面做得很好,但在防止已经进入的坏人方面做得很少。事实上,您可能会发现最好在会计和营销以及工程和生产之间设置防火墙。他们都没有太多业务处理其他人的文件。
由于本文主要关于 iptables,因此我不会更多地介绍网络拓扑。但我们需要理解以上内容,以了解以下配置是如何工作的。从外部防火墙的角度来看,它们实际上并没有太大的不同,只有内部防火墙(如果需要)看起来更像我在第一篇文章中介绍的基本防火墙。也就是说,内部防火墙不会接受来自受信任端的新流量。输出的内容也可以在一定程度上进行调节,我们也将稍微看一下。
有时我们可能希望在非防火墙系统上运行 iptables。尽管您可能已经阅读过相关建议(如上面的“为 iptables 升级准备您的系统”部分最后一段所述),但在某些时候您会希望在简单主机上运行 iptables。最简单但最常见的例子是大学网络上的学生系统。在这种情况下,您真的不应该信任任何其他系统。因此,您可能只想接受相关的、已建立的流量。
另一个例子可能是,如果您已决定使用 XDM 服务器,大多数用户都在那里工作,但您的互联网策略仅允许某些员工拥有浏览 Web 的权限。如何处理这个问题?好吧,幸运的是,我们可以使用以下规则非常简单地处理这个问题
iptables -t filter -I OUTPUT -p tcp --dport 80 -m owner --uid-owner 500 -j REJECT iptables -t filter -I OUTPUT -p tcp --dport 80 -j ACCEPT // only required if OUTPUT // policy is DROP/REJECT
或
iptables -t filter -I OUTPUT -p tcp --dport 80 -m owner --uid-owner 500 -j ACCEPT iptables -t filter -I OUTPUT -p tcp --dport 80 -j REJECT // only required if OUTPUT // policy is ACCEPT当然,您需要一个允许访问的人员列表或被拒绝访问的人员列表。此外,您不会想编写单独的规则。我建议像这样处理规则:对于 i in cat surfweb.txt,执行
iptables -t filter -I OUTPUT -p tcp --dport 80 -m owner --uid-owner $i -j REJECT done只需创建一个用户列表以 REJECT(或 ACCEPT 并更改规则以匹配)作为文件 surfweb.txt。根据需要将用户 ID 添加到此列表。您可能会发现上述结构对于其他重复规则也很有价值。但是请注意,这仅阻止他们从 XDM 服务器浏览,而不是从他们的本地系统浏览。
那么如何阻止他们从本地系统浏览呢?好吧,防火墙可以简单地丢弃或拒绝来自不允许的 IP 的数据包。很简单,对吧?我的意思是,这就是数据包过滤器的全部意义。但是等等,我们正在使用 DHCP,并且不一定提前知道 IP 是什么。看起来我们已经聪明反被聪明误了——或者我们没有?虽然我们可能不知道 IP 地址,但我们可以知道一件事是 MAC 地址。因此,我们从系统中获取 MAC 地址列表(或通过 arp,或从 dhcpd.leases 文件中获取)。然后我们使用如下规则
iptables -t filter -I FORWARD -i eth0 -m mac --mac-source <MAC> -j ACCEPT iptables -t filter -A FORWARD -i eth0 -p tcp --dport 80 -m state --state NEW -j DROP
最好像我们之前所做的那样在循环中完成此操作,MAC 地址在一个文件中,然后循环遍历它们。
注意:要使用 MAC 地址来允许或拒绝系统,请记住它们必须在您的本地网络上——也就是说,直接连接,通过集线器连接到防火墙。如果相关系统位于内部防火墙之后,并且未连接到与外部防火墙相同的 LAN 网段,则必须将此规则放在内部防火墙上。
我的重点至关重要:您必须知道具有规则的系统可以了解它要控制的数据包的哪些信息。只有数据包发起的系统才能知道哪个用户 ID 属于发起数据包的进程。只有本地 LAN 网段上的系统才能知道发起系统的 MAC 地址。在那之后,我们只有 IP 标头中可用的信息。
本月,我们研究了 Rusty 的 patch-o-matic,安装了更新的内核和用户态 iptables 实用程序。可能最重要的部分是确保在出现问题时您可以恢复。与此同时,Rusty 非常努力地确保您不需要恢复。之后,我们研究了几个常见的网络配置。当您深入研究下个月的 Kernel Korner 时,您需要记住这些,这将是本文的第二部分。最后,我们快速了解了如何在非防火墙情况下使用 iptables 来控制网络资源。
下个月,我们将研究管理 NAT 防火墙后面的服务,特别是如何最大限度地利用您的 ISP 分配的 IP,以及如何使用此配置正确处理电子邮件等服务。我们还将研究更多的匹配项、目标、表以及构建规则时的一些常见错误。
