iptables
iptables 是一个命令行工具,用于配置 Linux 内核中由 Netfilter 项目实现的防火墙。术语 iptables 也常用来指代这种内核级防火墙。它可以直接通过 iptables 命令配置,也可以使用许多控制台和图形化前端进行配置。iptables 用于 IPv4,而 ip6tables 用于 IPv6。iptables 和 ip6tables 的语法相同,但某些选项仅适用于 IPv4 或 IPv6。
安装
iptables 软件包是一个兼容层,用于通过 nftables 后端使用类似 iptables 的配置。它是 base 元软件包的间接依赖项,因此应该默认安装在您的系统上。
Arch Linux 原生内核仍然编译了对 iptables 的支持。旧版用户空间工具可以通过安装 iptables-legacy 软件包来获取,这将替换 iptables。
前端
控制台
- Arno's firewall — 用于单机和多宿主机的安全防火墙。易于配置、管理方便且高度可定制。支持:NAT 和 SNAT、端口转发、带有静态和动态分配 IP 的 ADSL 以太网调制解调器、MAC 地址过滤、隐身端口扫描检测、DMZ 和 DMZ-2-LAN 转发、防止 SYN/ICMP 洪水攻击、带有速率限制以防止日志溢出的用户自定义日志记录,以及所有 IP 协议和 VPN(如 IPsec)、插件支持等功能。
- ferm — 用于维护复杂防火墙的工具,无需反复重写复杂的规则。它允许将整个防火墙规则集存储在一个单独的文件中,并使用一条命令加载。防火墙配置类似于结构化编程语言,可以包含层级和列表。
- FireHOL — 一种表达防火墙规则的语言,不仅仅是生成某种防火墙的脚本。它使得构建复杂的防火墙变得简单,正如您所期望的那样。
- Firetable — 用于维护 IPtables 防火墙的工具。每个接口都可以通过其自己的配置文件单独配置,该文件具有简单易读的语法。
- firewalld (firewall-cmd) — 用于配置网络和防火墙区域以及设置和配置防火墙规则的守护进程和控制台接口。
- Shorewall — 用于配置 Netfilter 的高级工具。您可以使用一组配置文件中的条目来描述您的防火墙/网关需求。
- Uncomplicated Firewall (UFW) — iptables 的简单前端。
- PeerGuardian (pglcmd) — 面向隐私的防火墙应用程序。它会阻止连接到巨大的黑名单中指定的主机(成千上万个 IP 范围)。
- Vuurmuur — 强大的防火墙管理器。它具有简单易学的配置,允许进行简单和复杂的设置。可以通过 ncurses GUI 完全配置,从而允许通过 SSH 或控制台进行安全的远程管理。Vuurmuur 支持流量整形,具有强大的监控功能,允许管理员实时查看日志、连接和带宽使用情况。
图形界面
- Firewall Builder — GUI 防火墙配置和管理工具,支持 iptables (netfilter)、ipfilter、pf、ipfw、Cisco PIX (FWSM, ASA) 和 Cisco 路由器扩展访问列表。该程序可在 Linux、FreeBSD、OpenBSD、Windows 和 macOS 上运行,并可管理本地和远程防火墙。
- firewalld (firewall-config) — 用于配置网络和防火墙区域以及设置和配置防火墙规则的守护进程和图形界面。
- PeerGuardian GUI (pglgui) — 面向隐私的防火墙应用程序。它会阻止连接到巨大的黑名单中指定的主机(成千上万个 IP 范围)。
- Portmaster — Portmaster 是一个免费且开源的应用程序防火墙,默认设置可提升您的隐私。
基本概念
iptables 用于检查、修改、转发、重定向和/或丢弃 IP 数据包。过滤 IP 数据包的代码已内置于内核中,并组织成一组表,每个表都有特定的用途。这些表由一组预定义的链组成,链中包含按顺序遍历的规则。每个规则由匹配条件和对应的动作(称为目标/target)组成,如果条件满足则执行动作。如果 IP 数据包到达内置链的末尾(包括空链),则由该链的策略/policy目标决定 IP 数据包的最终命运。iptables 是允许您操作这些链/规则的用户工具。大多数新用户会觉得 Linux IP 路由的复杂性非常令人畏惧,但在实践中,最常见的用例(NAT 和/或基础 Internet 防火墙)的复杂程度要低得多。
理解 iptables 工作原理的关键是这张图表。顶部的小写单词是表,下方的英文大写单词是链。进入任何网络接口的每个 IP 数据包都会从上到下通过该流程图。一个常见的误解是,从例如内部接口进入的数据包与来自面向 Internet 接口的数据包处理方式不同。所有接口的处理方式都相同;由您来定义规则以区别对待它们。当然,有些数据包旨在发往本地进程,因此它们从图表顶部进入并在 <Local Process> 处停止,而其他数据包由本地进程生成;因此它们从 <Local Process> 开始并向下通过流程图。关于此流程图工作原理的详细说明可以在这里找到。
在绝大多数用例中,您根本不需要使用 raw、mangle 或 security 表。因此,下表描述了通过 iptables 的简化网络数据包流。
XXXXXXXXXXXXXXXXXX
XXX Network XXX
XXXXXXXXXXXXXXXXXX
+
|
v
+-------------+ +------------------+
|table: filter| <---+ | table: nat |
|chain: INPUT | | | chain: PREROUTING|
+-----+-------+ | +--------+---------+
| | |
v | v
[local process] | **************** +--------------+
| +---------+ Routing decision +------> |table: filter |
v **************** |chain: FORWARD|
**************** +------+-------+
Routing decision |
**************** |
| |
v **************** |
+-------------+ +------> Routing decision <---------------+
|table: nat | | ****************
|chain: OUTPUT| | +
+-----+-------+ | |
| | v
v | +-------------------+
+--------------+ | | table: nat |
|table: filter | +----+ | chain: POSTROUTING|
|chain: OUTPUT | +--------+----------+
+--------------+ |
v
XXXXXXXXXXXXXXXXXX
XXX Network XXX
XXXXXXXXXXXXXXXXXX
表格
iptables 包含五个表:
raw仅用于配置数据包,使其免受连接跟踪。filter是默认表,通常与防火墙相关的所有操作都在这里进行。nat用于网络地址转换(例如端口转发)。mangle用于专门的数据包修改。security用于强制访问控制网络规则(例如 SELinux——有关详细信息,请参见此文章)。
在大多数常见用例中,您只会使用其中的两个:filter 和 nat。其他表旨在处理涉及多个路由器和路由决策的复杂配置,无论如何,这些都超出了这些入门说明的范围。
链 (Chains)
表由链组成,链是按顺序执行的规则列表。默认表 filter 包含三个内置链:INPUT、OUTPUT 和 FORWARD,它们在数据包过滤过程的不同点激活,如流程图所示。nat 表包含 PREROUTING、POSTROUTING 和 OUTPUT 链。
有关其他表中内置链的描述,请参阅 iptables(8)。
默认情况下,没有任何链包含规则。您可以根据需要将规则附加到您要使用的链中。链确实有默认策略,通常设置为 ACCEPT,但如果您想确保没有任何流量漏过您的规则集,可以将其重置为 DROP。默认策略仅在链的末尾应用。因此,数据包必须经过链中所有现有规则,然后才会应用默认策略。
可以添加用户定义的链,使规则集更高效或更容易修改。有关如何使用用户定义链的示例,请参阅简单有状态防火墙。
规则
数据包过滤基于规则,这些规则由多个匹配项(数据包必须满足的条件才能应用该规则)和一个目标(当数据包满足所有条件时采取的动作)指定。规则可能匹配的典型内容是数据包进入的接口(例如 eth0 或 eth1)、数据包的类型(ICMP、TCP 或 UDP)或数据包的目的端口。
目标使用 -j 或 --jump 选项指定。目标可以是用户定义的链(即如果满足这些条件,跳转到以下用户定义的链并在那里继续处理)、内置的特殊目标之一,或者是目标扩展。内置目标有 ACCEPT、DROP、QUEUE 和 RETURN;目标扩展包括例如 REJECT 和 LOG。如果目标是内置目标,则立即决定数据包的命运,并停止当前表中对该数据包的处理。如果目标是用户定义的链,且数据包的命运未由该链决定,则它将针对原始链的其余规则进行过滤。目标扩展可以是终止性的(如内置目标)或非终止性的(如用户定义的链),详情请参见 iptables-extensions(8)。
遍历链
在任何接口上接收到的网络数据包会按照流程图中显示的顺序遍历表的流量控制链。第一个路由决策涉及决定数据包的最终目的地是否为本地机器(在这种情况下,数据包遍历 INPUT 链)还是其他地方(在这种情况下,数据包遍历 FORWARD 链)。后续的路由决策涉及决定为传出数据包分配什么接口。在路径的每个链中,该链中的每一条规则都会按顺序进行评估,每当规则匹配时,就会执行相应的目标/跳转动作。最常用的 3 个目标是 ACCEPT、DROP 和跳转到用户定义的链。虽然内置链可以有默认策略,但用户定义的链不能。如果跳转到的链中的每条规则都未能提供完整匹配,数据包将被放回调用链;参见以下插图。如果在任何时候为具有 DROP 目标的规则实现了完整匹配,数据包将被丢弃且不再进行进一步处理。如果数据包到达跳转到 ACCEPT 目标的规则,它将不会遍历链的任何后续规则以及表的后续链。其处理将跳转到顺序中的下一个表的第一条链。另请参见 frozentux 教程的遍历表和链以及接受目标章节。
模块
有许多模块可用于扩展 iptables,例如 connlimit、conntrack、limit 和 recent。这些模块增加了额外的功能,以允许复杂的过滤规则。
配置和使用
默认情况下,iptables 在 /etc/iptables/iptables.rules 中提供了一组空规则。为了能够在启动时自动加载 iptables 规则,您可以启用 iptables 提供的 iptables.service 单元,或者启动 systemd 服务单元以立即加载 iptables 规则。
类似地,IPv6 的 iptables 规则默认存储在 /etc/iptables/ip6tables.rules 中,该文件由 ip6tables.service 读取。您可以按照与上述相同的方式启动它。
在通过命令行添加规则(如下节所示)之后,配置文件不会自动更改——您必须手动保存它。
# iptables-save -f /etc/iptables/iptables.rules
如果您手动编辑配置文件,则必须重新加载 iptables。
或者您可以直接通过 iptables 加载它。
# iptables-restore /etc/iptables/iptables.rules
从命令行
显示当前规则
列出当前规则的基本命令是 --list-rules (-S),其输出格式类似于 iptables-save 工具。两者的主要区别在于,后者默认输出所有表的规则,而所有 iptables 命令默认仅针对 filter 表。
在命令行上使用 iptables 时,--list (-L) 命令接受更多修饰符并显示更多信息。例如,您可以使用以下命令检查当前的规则集和每条规则的命中次数:
# iptables -nvL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination
如果输出看起来像上面那样,则默认 filter 表中没有任何规则(即没有被阻止的内容)。可以使用 -t 选项指定其他表。
要在列出规则时显示行号,请在该输入后追加 --line-numbers。当在命令行上#编辑规则时,行号是一个有用的速记方式。
重置规则
您可以使用这些命令刷新并重置 iptables 为默认值:
# iptables -F # iptables -X # iptables -t nat -F # iptables -t nat -X # iptables -t mangle -F # iptables -t mangle -X # iptables -t raw -F # iptables -t raw -X # iptables -t security -F # iptables -t security -X # iptables -P INPUT ACCEPT # iptables -P FORWARD ACCEPT # iptables -P OUTPUT ACCEPT
不带参数的 -F 命令会刷新其当前表中的所有链。同样,-X 会删除表中所有空的非默认链。
可以通过在 -F 和 -X 后面加上 [chain] 参数来刷新或删除单个链。
编辑规则
规则可以通过以下方式进行编辑:使用 -A 将规则附加到链,使用 -I 将其插入到链上的特定位置,使用 -R 替换现有规则,或使用 -D 删除它。以下举例说明了前三个命令。
首先,我们的计算机不是路由器(除非它是一台路由器)。我们想将 FORWARD 链上的默认策略从 ACCEPT 更改为 DROP。
# iptables -P FORWARD DROP
Dropbox LAN 同步功能每 30 秒向它能看到的所有计算机广播数据包。如果我们恰好在具有 Dropbox 客户端的局域网中,并且不使用此功能,那么我们可能希望拒绝这些数据包。
# iptables -A INPUT -p tcp --dport 17500 -j REJECT --reject-with icmp-port-unreachable
# iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 REJECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:17500 reject-with icmp-port-unreachable Chain FORWARD (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination
REJECT 而不是 DROP,因为 RFC 1122 要求主机在可能的情况下返回 ICMP 错误,而不是丢弃数据包。此页面解释了为什么几乎总是最好 REJECT 而不是 DROP 数据包。现在,假设我们改变了主意,决定在我们的计算机上安装 Dropbox。我们也想进行局域网同步,但仅限于我们网络中的一个特定 IP。因此,我们应该使用 -R 来替换我们的旧规则。其中 10.0.0.85 是我们的另一个 IP:
# iptables -R INPUT 1 -p tcp --dport 17500 ! -s 10.0.0.85 -j REJECT --reject-with icmp-port-unreachable
# iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 REJECT tcp -- * * !10.0.0.85 0.0.0.0/0 tcp dpt:17500 reject-with icmp-port-unreachable Chain FORWARD (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination
我们现在已经用一个允许 10.0.0.85 访问我们计算机上 17500 端口的规则替换了原始规则。但现在我们意识到这不可扩展。如果我们的 Dropbox 用户试图访问我们设备上的 17500 端口,我们应该立即允许他们,而不是让他们去匹配可能排在后面的任何防火墙规则!
所以我们写了一个新规则来立即允许我们信任的用户。使用 -I 在我们的旧规则之前插入新规则:
# iptables -I INPUT -p tcp --dport 17500 -s 10.0.0.85 -j ACCEPT -m comment --comment "Friendly Dropbox"
# iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 ACCEPT tcp -- * * 10.0.0.85 0.0.0.0/0 tcp dpt:17500 /* Friendly Dropbox */ 2 0 0 REJECT tcp -- * * !10.0.0.85 0.0.0.0/0 tcp dpt:17500 reject-with icmp-port-unreachable Chain FORWARD (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination
并将我们的第二条规则替换为拒绝 17500 端口上所有内容的规则:
# iptables -R INPUT 2 -p tcp --dport 17500 -j REJECT --reject-with icmp-port-unreachable
我们最终的规则列表现在看起来像这样:
# iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 ACCEPT tcp -- * * 10.0.0.85 0.0.0.0/0 tcp dpt:17500 /* Friendly Dropbox */ 2 0 0 REJECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:17500 reject-with icmp-port-unreachable Chain FORWARD (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination
允许组播流量
使用组播识别的协议(例如搜索网络扫描仪的 SANE)会将流量发送到网络的广播 IP,响应将来自特定客户端的 IP。由于这些 IP 地址不同,iptables 不会将响应识别为 RELATED 或 ESTABLISHED,并会阻止该响应。请参阅 [1] 了解如何在不创建过度宽松的防火墙的情况下接受组播流量。
第一,创建一个 ipset 哈希容器。超时是接受客户端响应的时间窗口。
# ipset create upnp hash:ip,port timeout 3
第二,创建一个规则将传出的组播流量添加到 ipset 哈希中。
# iptables -A OUTPUT -d 239.255.255.250/32 -p udp -m udp -j SET --add-set upnp src,src --exist
第三,创建一个规则来允许与 ipset 哈希匹配的传入流量。
# iptables -A INPUT -p udp -m set --match-set upnp dst,dst -j ACCEPT
最后,记得保存新规则(请参阅 #配置与使用 和 ipset#使 ipset 持久化),并确保 iptables.service 和 ipset.service 已启用,以便在系统启动时加载规则。
指南
日志记录
LOG 目标可用于记录命中规则的数据包。与 ACCEPT 或 DROP 等其他目标不同,数据包在命中 LOG 目标后将继续在链中移动。这意味着为了为所有被丢弃的数据包启用日志记录,您必须在每个 DROP 规则之前添加一个重复的 LOG 规则。由于这降低了效率并使事情变得不再简单,因此可以改为创建一个 logdrop 链。
创建该链:
# iptables -N logdrop
并将以下规则添加到新创建的链中:
# iptables -A logdrop -m limit --limit 5/m --limit-burst 10 -j LOG # iptables -A logdrop -j DROP
有关 limit 和 limit-burst 选项的说明在下文中给出。
现在,每当我们想要丢弃数据包并记录此事件时,我们只需跳转到 logdrop 链,例如:
# iptables -A INPUT -m conntrack --ctstate INVALID -j logdrop
限制日志速率
上面的 logdrop 链使用 limit 模块来防止 iptables 日志变得过大或导致不必要的硬盘写入。如果不加以限制,错误配置的试图连接的服务,或者攻击者,可能会通过导致对 iptables 日志的写入来填满驱动器(或者至少填满 /var 分区)。
limit 模块通过 -m limit 调用。然后,您可以使用 --limit 设置平均速率,并使用 --limit-burst 设置初始突发速率。在上面的 logdrop 示例中:
iptables -A logdrop -m limit --limit 5/m --limit-burst 10 -j LOG
追加了一条将记录所有通过它的数据包的规则。最初的 10 个连续数据包将被记录,此后每分钟仅记录 5 个数据包。“限制突发”计数在每次“限制速率”未被破坏时重置,即日志记录活动自动恢复正常。
查看记录的数据包
记录的数据包作为内核消息显示在 systemd 日志中。
要查看自上次机器启动以来记录的所有数据包:
# journalctl -k --grep="IN=.*OUT=.*"
syslog-ng
假设您正在使用 syslog-ng,您可以在 syslog-ng.conf 中控制 iptables 的日志输出去向。替换:
filter f_everything { level(debug..emerg) and not facility(auth, authpriv); };
更改为
filter f_everything { level(debug..emerg) and not facility(auth, authpriv) and not filter(f_iptables); };
这将停止将 iptables 输出记录到 /var/log/everything.log。
如果您还希望 iptables 记录到 /var/log/iptables.log 以外的文件,您可以简单地在此处(仍在 syslog-ng.conf 中)更改目标 d_iptables 的 file 值:
destination d_iptables { file("/var/log/iptables.log"); };
ulogd
ulogd 是一个专用的用户空间数据包日志记录守护进程,用于 netfilter,可以替代默认的 LOG 目标。
参见
- 维基百科文章
- 端口敲击
- iptables 官方网站
- iptables 教程 1.2.2 作者 Oskar Andreasson
- Debian Wiki - iptables
- 连接跟踪助手的安全使用