概述

iptables 在 Linux 是基本的防火墙工具,同时也是很常见的网络工作,但是 iptables 实际上只是应用层的东西,那么 iptables 是如何和 Linux 的内核网络栈联系起来的,这里我就这个问题进行一个简单的介绍。

NetFilter 框架

Netfilter 框架是 Linux 内核网络为应用提供的注册各种 Handler 的框架,通过 Netfilter 框架,允许用户铜鼓欧 Handler 达到控制 Linux 网络部分功能的效果。

常用的注入点有以下这些:

Iptables

iptables 就是 Linux 内核提供给应用层用于设置 NetFliter 的工具之一,iptables 通过 table 来组织规则,所谓的 table 其实就是对应的不同的功能,例如:

在每个表中,iptables 通过链来组织规则,所谓的链其实就是对应到 NetFilter 中的不同 Hook,其实就是定义规则的执行时机时机顺序:

iptables 链 NetFilter Hook
PREROUTING NF_IP_PRE_ROUTING
INPUT NF_IP_LOCAL_IN
FORWARD NF_IP_FORWARD
OUTPUT NF_IP_LOCAL_OUT
POSTROUTING NF_IP_POST_ROUTING

table 的运行顺序

因为 table 是 iptables 才有的概念,当不同的 table 包含相同链的规则时,最终在 NetFilter 中是需要有一个顺序的,这个顺序用户是不能指定优先级的,但是 iptables 给你定义好了执行顺序,默认的顺序是这么走的:

Tables↓/Chains→ PREROUTING INPUT FORWARD OUTPUT POSTROUTING
(routing decision)
raw
(connection tracking enabled)
mangle
nat (DNAT)
(routing decision)
filter
security
nat (SNAT)

用户自定义链

对于 iptables 来说,所谓的规则可以理解为两部分组成,分别是:

这里所谓的自定义链条就是非终结动作,那么为什么要引入自定义链呢?

引入自定义链

前面说了,默认 iptables 只有 5 条链,虽然 iptables 用表来区分了,但是,还是不够,尤其是在企业级使用中(例如 Istio),往往会创建一大堆的规则,那么当我想查看某一个功能的规则的时候,可能就不好找了,所以这个自定义链就可以起作用了。我们可以将不同的应用/功能的规则放入单独的链中,这样我们需要修改或者查看的时候直接看对应的链就可以了,例如

自定义链的执行实际

因为默认情况下,iptables 只会使用 5 条默认的链,所以自定义的链是不会被执行的。如果想要执行自定义链,就需要在默认的链中主动调用自定义链(所谓的非终止动作),这样就和自定义链配合起来了。一个例子:

  1. [root@liqiang.io]# iptables -I INPUT -p tcp --dport 9090 -j INPUT_CHAIN_ENVOY

就表示在 INPUT 链中添加一个 INPUT_CHAIN_ENVOY 的引用,当 INPUT 链执行的时候,对应就执行这个自定义的链。和其他链的顺序就按照前面的表格顺序执行,在 INPUT 链中的顺序就和你定义的先后顺序从旧往新执行,先执行旧的定义,再执行新的定义。

连接跟踪

iptables 还执行连接追踪的功能,这个也是基于 NetFilter 实现的,这里连接跟踪所谓的连接不单单指我们常见的 TCP 连接,还包括 UDP,甚至 ICMP 也算,这些协议都称为可跟踪协议,跟踪的基础是基于”连接“的元组,例如 TCP 的就是四元组 <SrcIP, SrcPORT, DestIP, DestPort>

连接跟踪的作用

连接跟踪本质上还是为包的动作服务的,例如一个设置了防火墙的 Web 主机,如果我们打开了 80 端口的入口,那么对于出口是全开还是全闭,可能都不合适。那么能不能做到半开半闭,如何半开半闭这就是连接跟踪的功能了,想象一下我们要对谁开放?我们要对访问了我们 80 端口的客户端开放,那么这里就可以先跟踪成功访问 80 端口的客户端的信息,然后再定义一条针对已经打开的连接的出口规则:

  1. [root@liqiang.io]# iptables -A OUTPUT -m state --state NEW,RELATED -j ALLOW

这样就不需要针对特定的客户端来开放出口流量了。

Ref