9.8. Netfilter 和 IP Tables (2.4 内核)

在开发 IP 防火墙链时,Paul Russell 认为 IP 防火墙应该更容易使用;他随即开始简化内核防火墙代码中数据报处理的各个方面,并生成了一个更加清晰且更灵活的过滤框架。他将这个新框架称为 netfilter

注意:在编写本书时,netfilter 的设计尚未稳定。如果由于本书编写后发生的更改导致 netfilter 或其相关的配置工具的描述中存在任何错误,我们希望您能谅解。我们认为 netfilter 工作非常重要,值得收录此材料,尽管其中部分内容本质上是推测性的。如果您有任何疑问,相关的 HOWTO 文档将包含有关与 netfilter 配置相关的详细问题的最准确和最新的信息。

那么 IP chains 到底有什么问题呢? 它们极大地提高了防火墙规则的效率和管理。 但是它们处理数据报的方式仍然很复杂,尤其是在结合防火墙相关功能(如 IP 伪装(在第 11 章中讨论))和其他形式的地址转换时。 这种复杂性的一部分存在是因为 IP 伪装和网络地址转换是独立于 IP 防火墙代码开发的,并在以后集成,而不是从一开始就被设计为防火墙代码的真正组成部分。 如果开发人员想要在数据报处理序列中添加更多功能,他将难以找到插入代码的位置,并且将被迫更改内核才能做到这一点。

不过,仍然存在其他问题。 特别是,“input”链将输入描述为整个 IP 网络层的输入。 input 链影响目的地为此主机的报文和此主机路由的报文。 这有点违反直觉,因为它将 input 链的功能与 forward 链的功能混淆了,后者仅适用于要转发的数据报,但始终遵循 input 链。 如果您想以不同于转发的数据报的方式处理发送到此主机的数据报,则需要构建复杂的规则来排除其中一个或另一个。 同样的问题也适用于 output 链。

不可避免地,这种复杂性的一部分会蔓延到系统管理员的工作中,因为它反映在必须设计规则集的方式中。 此外,对过滤的任何扩展都需要直接修改内核,因为所有过滤策略都在那里实现,并且无法为其提供透明的接口。 netfilter 通过在内核中实现一个通用框架来解决旧解决方案的复杂性和刚性,该框架简化了数据报的处理方式,并提供了一种扩展过滤策略的能力,而无需修改内核。

让我们看一下所做的两个主要更改。图 9-8 说明了 IP chains 实现中如何处理数据报,而图 9-9 说明了 netfilter 实现中如何处理它们。 主要区别在于从核心代码中删除了伪装功能,以及 input 和 output 链的位置发生了变化。 为了配合这些更改,创建了一个新的可扩展的配置工具,称为 iptables

在 IP chains 中,input 链适用于主机接收的所有数据报,无论它们是发往本地主机还是路由到其他主机。 在 netfilter 中,input 链适用于发往本地主机的数据报,而 forward 链仅适用于发往另一台主机的数据报。 同样,在 IP chains 中,output 链适用于离开本地主机的所有数据报,无论该数据报是在本地主机上生成的还是从其他主机路由的。 在 netfilter 中,output 链适用于在此主机上生成的数据报,而不适用于从另一主机路由的数据报。 仅此更改就极大地简化了许多防火墙配置。

图 9-8. IP chains 中的数据报处理链

图 9-8中,标记为“demasq”和“masq”的组件是单独的内核组件,负责伪装数据报的传入和传出处理。 这些已重新实现为 netfilter 模块。

考虑这样一种配置,其中每个 input、forward 和 output 链的默认策略是拒绝。 在 IP chains 中,需要六条规则才能允许任何会话通过防火墙主机:input、forward 和 output 链中各有两条规则(一条规则涵盖每个转发路径,另一条规则涵盖每个返回路径)。 您可以想象,当您想要混合可以路由的会话和可以连接到本地主机而不被路由的会话时,这很容易变得非常复杂且难以管理。 IP chains 允许您创建可以简化此任务的链,但设计并不明显,并且需要一定的专业知识。

在带有 iptablesnetfilter 实现中,这种复杂性完全消失了。 对于要跨防火墙主机路由但不在本地主机上终止的服务,只需要两条规则:forward 链中每个正向和反向方向各一条规则。 这是设计防火墙规则的显而易见的方法,并且将极大地简化防火墙配置的设计。

图 9-9. netfilter 中的数据报处理链

PACKET-FILTERING-HOWTO 提供了已进行的更改的详细列表,因此让我们在这里重点关注更实际的方面。

9.8.1. 与 ipfwadm 和 ipchains 的向后兼容性

Linux netfilter 的卓越灵活性体现在它能够模拟 ipfwadmipchains 接口。 模拟使过渡到新一代防火墙软件变得更加容易。

这两个名为ipfwadm.oipchains.onetfilter 内核模块为 ipfwadmipchains 提供向后兼容性。 您一次只能加载其中一个模块,并且仅当未加载ip_tables.o模块时才使用一个模块。 加载适当的模块后,netfilter 的工作方式与以前的防火墙实现完全相同。

netfilter 使用以下命令模仿 ipchains 接口
rmmod ip_tables
modprobe ipchains
ipchains ...

9.8.2. 使用 iptables

iptables 实用程序用于配置 netfilter 过滤规则。 其语法在很大程度上借鉴了 ipchains 命令,但在一个非常重要的方面有所不同:它是可扩展的。 这意味着可以在不重新编译的情况下扩展其功能。 它通过使用共享库来管理此技巧。 有一些标准扩展,我们将在稍后探讨其中的一些扩展。

在使用 iptables 命令之前,必须加载 netfilter 内核模块,该模块为其提供支持。 最简单的方法是使用 modprobe 命令,如下所示
modprobe ip_tables

iptables 命令用于配置 IP 过滤和网络地址转换。 为了方便起见,有两个规则表,分别称为 filternat。 如果您不指定-t选项来覆盖它,则假定为 filter 表。 还提供了五个内置链。

INPUTFORWARD链可用于filter表,PREROUTINGPOSTROUTING链可用于nat表,并且OUTPUT链可用于这两个表。 在本章中,我们仅讨论 filter 表。 我们将在第 11 章中介绍 nat

大多数 iptables 命令的通用语法是
iptables command rule-specification extensions
现在,我们将详细介绍一些选项,然后再回顾一些示例。

9.8.2.1. 命令

我们可以使用 iptables 命令以多种方式操纵规则和规则集。 与 IP 防火墙相关的那些是

-A chain

将一个或多个规则追加到指定链的末尾。 如果将主机名作为源或目标提供,并且它解析为多个 IP 地址,则将为每个地址添加一条规则。

-I chain rulenum

将一个或多个规则插入到指定链的开头。 同样,如果在规则规范中提供了主机名,则将为它解析的每个地址添加一条规则。

-D chain

从与规则规范匹配的指定链中删除一个或多个规则。

-D chain rulenum

删除位于rulenum在指定链中。 规则位置从链中的第一个规则的 1 开始。

-R chain rulenum

用提供的规则规范替换位于特定链中的位置rulenum的规则。

-C chain

根据特定链检查规则规范描述的数据报。 此命令将返回一条消息,描述链如何处理数据报。 这对于测试您的防火墙配置非常有用,我们将在稍后详细介绍它。

-L [chain]

列出指定链的规则,如果未指定链,则列出所有链的规则。

-F [chain]

刷新指定链的规则,如果未指定链,则刷新所有链的规则。

-Z [chain]

将指定链的所有规则或所有链(如果未指定链)的数据报和字节计数器归零。

-N 链

创建具有指定名称的新链。 必须不存在同名的链。 这是创建用户自定义链的方式。

-X [链]

删除指定的用户自定义链,如果未指定链,则删除所有用户自定义链。 要使此命令成功,必须没有任何其他规则链引用指定的链。

-P 链 策略

将指定链的默认策略设置为指定的策略。 有效的防火墙策略是ACCEPT, DROP, QUEUERETURN. ACCEPT允许数据报通过。DROP导致数据报被丢弃。QUEUE导致数据报被传递到用户空间以进行进一步处理。 TheRETURN目标导致 IP 防火墙代码返回到调用包含此规则的防火墙链,并从调用规则之后的规则继续。

9.8.2.2. 规则规范参数

有许多 iptables 参数构成规则规范。 无论何时需要规则规范,都必须提供每个参数,否则将采用其默认值。

-p [!]协议

指定将匹配此规则的数据报的协议。 有效的协议名称是tcp, udp, icmp或者一个数字,如果你知道 IP 协议号。[1] 例如,你可以使用4来匹配ipip封装协议。 如果提供了!字符,则规则将被否定,并且数据报将匹配除指定协议之外的任何协议。 如果未提供此参数,则默认匹配所有协议。

-s [!]地址[/掩码]

指定将匹配此规则的数据报的源地址。 该地址可以作为主机名、网络名或 IP 地址提供。 可选的掩码是要使用的网络掩码,可以以传统形式(例如,/255.255.255.0)或现代形式(例如,/24)提供。

-d [!]地址[/掩码]

指定将匹配此规则的数据报的目标地址和端口。 此参数的编码与-s参数的编码相同。

-j 目标

指定当此规则匹配时要采取的操作。 你可以将此参数视为 “跳转到”。 有效的目标是ACCEPT, DROP, QUEUERETURN。 我们之前在 “命令” 部分中描述了每个命令的含义。 你还可以指定用户定义的链的名称,处理将在该链中继续。 你还可以提供扩展提供的目标的名称。 我们稍后会讨论扩展。 如果省略此参数,则根本不会对匹配的数据报执行任何操作,除了更新此规则的数据报和字节计数器。

-i [!]接口名

指定接收数据报的接口。 同样,!反转匹配结果。 如果接口名称以 “+” 结尾,则任何以提供的字符串开头的接口都将匹配。 例如,-i ppp+将匹配任何 PPP 网络设备,并且-i ! eth+将匹配除以太网设备之外的所有接口。

-o [!]接口名

指定要传输数据报的接口。 此参数的编码与-i参数相同。

[!] -f

指定此规则仅适用于分片数据报的第二个和后续片段,而不适用于第一个片段。

9.8.2.3. 选项

以下 iptables 选项本质上更通用。 其中一些控制了 netfilter 软件的相当深奥的功能。

-v

导致 iptables 在其输出中是冗长的; 它将提供更多信息。

-n

导致 iptables 将 IP 地址和端口显示为数字,而不尝试将其解析为其对应的名称。

-x

导致 iptables 输出中的任何数字都扩展到其确切值,而无需舍入。

- -line-numbers

导致在列出规则集时显示行号。 行号将对应于规则在链中的位置。

9.8.2.4. 扩展

我们之前说过,可以通过可选的共享库模块来扩展 iptables 实用程序。 有一些标准扩展提供了一些 ipchains 提供的功能。 要使用扩展,必须通过-m 名称参数指定其名称到 iptables。 以下列表显示了-m-p选项,这些选项设置扩展的上下文,以及该扩展提供的选项。

9.8.2.4.1. TCP 扩展:与 -m tcp -p tcp 一起使用

- -sport [!] [端口[:端口]]

指定数据报源必须使用的端口才能匹配此规则。 可以通过使用冒号作为分隔符来指定范围的上限和下限来将端口指定为范围。 例如,20:25描述了从 20 到 25(包括 25)的所有端口。 同样,!字符可用于否定值。

- -dport [!] [端口[:端口]]

指定数据报目标必须使用的端口才能匹配此规则。 参数的编码与- -sport选项相同。

- -tcp-flags [!] 掩码 比较

指定当数据报中的 TCP 标志与指定的标志匹配时,此规则应匹配掩码比较. 掩码是标志的逗号分隔列表,在进行测试时应检查这些标志。比较是必须设置的标志的逗号分隔列表,规则才能匹配。 有效的标志是:SYNACKFINRSTURGPSHALLNONE。 这是一个高级选项:有关每个标志的含义和含义的描述,请参阅 TCP 协议的良好描述,例如 RFC-793。 The!字符否定该规则。

[!] - -syn

指定规则仅匹配设置了SYN位和清除了ACKFIN位的 数据报。 具有这些选项的数据报用于打开 TCP 连接,因此此选项可用于管理连接请求。 此选项是
- -tcp-flags SYN,RST,ACK SYN
的简写。 当你使用否定运算符时,该规则将匹配所有未同时设置SYNACK位的 数据报。

9.8.2.4.2. UDP 扩展:与 -m udp -p udp 一起使用

- -sport [!] [端口[:端口]]

指定数据报源必须使用的端口才能匹配此规则。 可以通过使用冒号作为分隔符来指定范围的上限和下限来将端口指定为范围。 例如,20:25描述了从 20 到 25(包括 25)的所有端口。 同样,!字符可用于否定值。

- -dport [!] [端口[:端口]]

指定数据报目标必须使用的端口才能匹配此规则。 参数的编码与- -sport选项相同。

9.8.2.4.3. ICMP 扩展:与 一起使用-m icmp -p icmp

- -icmp-type [!] 类型名

指定此规则将匹配的 ICMP 消息类型。 可以通过数字或名称指定类型。 一些有效的名称是echo-request, echo-reply, source-quench, time-exceeded, destination-unreachable, network-unreachable, host-unreachable, protocol-unreachableport-unreachable.

9.8.2.4.4. MAC 扩展:与 一起使用-m mac

- -mac-source [!] 地址

指定传输此规则将匹配的数据报的主机的以太网地址。 这仅在输入或转发链中的规则中才有意义,因为我们将传输任何通过输出链的数据报。

9.8.3. 再次回顾我们最初的简单示例

要使用 netfilter 实现我们最初的简单示例,你可以简单地加载ipchains.o模块,并假装它是 ipchains 版本。 相反,我们将使用 iptables 重新实现它,以说明它的相似之处。

再次假设我们在我们的组织中有一个网络,并且我们正在使用基于 Linux 的防火墙机器来允许我们的用户能够访问 Internet 上的 WWW 服务器,但不允许传递任何其他流量。

如果我们的网络具有 24 位网络掩码(C 类)并且地址为 172.16.1.0,那么我们将使用以下 iptables 规则
# modprobe ip_tables
# iptables -F FORWARD
# iptables -P FORWARD DROP
# iptables -A FORWARD -m tcp -p tcp -s 0/0 --sport 80 -d 172.16.1.0/24 /
    --syn -j DROP
# iptables -A FORWARD -m tcp -p tcp -s 172.16.1.0/24 --sport /
    80 -d 0/0 -j ACCEPT
# iptables -A FORWARD -m tcp -p tcp -d 172.16.1.0/24 --dport 80 -s 0/0 -j /
    ACCEPT

在此示例中,iptables 命令的解释方式与等效的 ipchains 命令完全相同。 主要例外是ip_tables.o模块必须加载。 请注意,iptables 不支持-b选项,因此我们必须为每个方向提供一个规则。

注释

[1]

查看/etc/protocols以获取协议名称和数字。