iptables note

概念

作用

  • iptables 用于检查、修改、转发、重定向和丢弃 IP 数据包。
  • 过滤 IP 数据包的代码已经内置在内核中,并被组织成一组表,每个表都有特定的用途。

    • 这些表由一组预定义的链组成,链包含按顺序遍历的规则。
  • 每个规则由一个潜在匹配的谓词和一个对应的操作(称为目标)组成。

    • 如果谓词为真,则执行该动作;即条件匹配。

iptables允许用户使用这些链和规则

工作原理

  • iptables工作原理

小写是表,大写是链。

所有(无论内部外部)进入网卡的IP数据包都必须经历这个流程!

  • 如果 IP 数据包到达一个内置链的末端,包括一个空链,那么该链的策略target 便决定 IP 数据包的最终目的地。

本质

  • iptables本质上不是通常定义的防火墙,实践上是做了一个代理。
    • 将用户设置的安全规则在安全框架(netfilter)中执行。

netfilter位于内核空间,是真正的防火墙框架。

iptables在用户空间,可以控制安全框架。

流程

  • 在任何接口上接收的网络数据包都将按照流程图中所示的顺序遍历表的流量控制链。
  • 第一个路由决策涉及决定信息包的最终目的地是本地机器(在这种情况下,信息包通过INPUT链)还是其他机器(在这种情况下,信息包通过FORWARD链)。
  • 随后的路由决策包括决定将哪个接口分配给一个出包
    • 在路径中的每个链上,该链中的每个规则都按顺序计算,每当规则匹配时,就执行相应的目标/跳转操作。3个最常用的目标是ACCEPT、DROP和跳转到用户定义链
    • 虽然内置链可以有默认策略,但用户定义链不能
    • 如果跳过的链中的每个规则都不能提供完全匹配,则包将被丢回调用链中,如下图所示。
    • 如果在任何时候对具有DROP目标的规则实现了完全匹配,则丢弃数据包,不再进行进一步的处理。
    • 如果一个包在一个链中被ACCEPT,它也将在所有超集链中被ACCEPT,并且它将不再进一步遍历任何超集链。但是,包将继续以正常方式遍历其他表中的所有其他链

组成

  • iptables 包含五个表:
    1. raw仅用于配置数据包,使它们免于连接跟踪。
    2. filter是默认表,通常与防火墙相关联的所有操作都发生在该表中。
    3. nat用于网络地址转换(例如端口转发)。
    4. mangle用于专门的数据包更改。
    5. security用于强制访问控制网络规则。

常用的表为filter和nat

  • 表由链组成,链是按顺序遵循的规则列表。

    • 缺省表filter包含三个内置链:INPUT、OUTPUT和FORWARD,它们在包过滤过程的不同点被激活。
    • nat表包括PREROUTING、POSTROUTING和OUTPUT链。
  • 缺省情况下,没有一个链包含规则。将规则附加到想要使用的链上由用户自己决定

  • 链有一个默认策略,通常将其设置为ACCEPT,但如果希望确保没有任何东西能通过规则集,则可以将其重置为DROP。
    • 默认策略总是只应用于链的末端。因此,在应用缺省策略之前,信息包必须通过链中的所有现有规则。
    • 可以添加用户定义链,使规则集更高效或更容易修改。

规则

  • 包过滤是基于规则的,规则由多个匹配项(报文必须满足的条件才能应用该规则)和一个目标(报文匹配所有条件时的动作)指定
    • 规则可能匹配的典型内容是数据包来自哪个网卡(例如eth0或eth1),它是什么类型的数据包(ICMP、TCP或UDP),或者数据包的目的端口。

使用-j或—jump选项指定目标。

  • 目标可以是用户定义链(如果这些条件匹配,则跳转到以下用户定义链并在那里继续处理)、特殊的内置目标之一或目标扩展
    • 内置目标是ACCEPT, DROP, QUEUE和RETURN,如果目标是内置目标,则立即决定数据包的命运,并停止对当前表中的数据包的处理。
    • 目标扩展包括例如REJECT和LOG。
    • 用户定义链,若数据包的命运不是由该链决定的,它将根据原始链的剩余规则进行过滤。目标扩展可以是终止的(作为内置目标)或非终止的(作为用户定义的链)。

模块

  • 有许多模块可用于扩展 iptables,例如 connlimit、conntrack、limit 和 recent。
    • 这些模块添加了额外的功能以允许复杂的过滤规则

配置

  • iptables是一个systemd服务,叫做iptables.service。
    • iptables包在/etc/iptables/iptables.rules中安装了一组空的规则,当你第一次启动iptables.service单元时将加载这些规则。
    • 与其他服务一样,如果希望iptables在引导时自动加载,则必须启用它。

IPv6的iptables规则默认保存在/etc/iptables/ip6tables.rules中,由ip6tables.service读取。

  • 如下所示,通过命令行添加规则后,配置文件不会自动更改,必须手动保存:
1
iptables-save -f /etc/iptables/iptables.rules
  • 如果手动编辑配置文件,则必须重新加载iptables。或者你可以直接通过iptables加载它:
1
iptables-restore /etc/iptables/iptables.rules

使用

显示当前规则

  • 列出当前规则的基本命令是list-rules (-S),其输出格式类似于iptables-save实用程序。
    • 这两个命令的主要区别是,后者默认输出所有表的规则,而所有iptables命令默认只输出filter表
1
2
3
4
student@6858-v20:~$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
  • —list (-L)命令接受更多修饰符并显示更多信息。
    • 例如,可以使用以下命令查看当前的规则集和每条规则的命中次数:
1
2
3
4
5
6
7
8
9
student@6858-v20:~$ sudo iptables -nvL
Chain INPUT (policy ACCEPT 1059 packets, 89762 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 1051 packets, 88652 bytes)
pkts bytes target prot opt in out source destination

如果输出像上面那样,那么在默认的过滤表中没有规则(即没有阻塞)。

可以使用-t选项指定其他表。

  • 要在列出规则时显示行号,请将—line-numbers追加到该输入。当在命令行上编辑规则时,行号是一种有用的速记。

重置规则

  • 可以使用以下命令将iptables刷新和重置为默认值:
1
2
3
4
5
6
7
8
9
10
11
12
13
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。
1
iptables -P FORWARD DROP

Dropbox的局域网同步功能每30秒向它能看到的所有计算机广播数据包。如果我们碰巧与Dropbox客户端在局域网中,并且没有使用该功能,那么我们可能会希望拒绝这些数据包。

1
iptables -A INPUT -p tcp --dport 17500 -j REJECT --reject-with icmp-port-unreachable
1
2
3
4
5
6
7
8
9
10
student@6858-v20:~$ sudo iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 67 packets, 5771 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 55 packets, 5174 bytes)
num pkts bytes target prot opt in out source destination

注意:这里我们使用REJECT而不是DROP,因为RFC 1122要求主机尽可能地返回ICMP错误,而不是丢弃数据包这一页解释了为什么拒绝包几乎总是比DROP包更好。

  • 现在,假设我们改变了对Dropbox的看法,决定在自己的电脑上安装它。我们还想进行局域网同步,但只能与网络上的一个特定IP进行同步。
    • 所以我们应该用-R来代替原来的规则。其中10.0.0.85是我们的其他IP:
1
iptables -R INPUT 1 -p tcp --dport 17500 ! -s 10.0.0.85 -j REJECT --reject-with icmp-port-unreachable
1
2
3
4
student@6858-v20:~$ sudo iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 21 packets, 1849 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

我们现在已经用一个允许10.0.0.85访问17500端口的规则替换了原来的规则。

  • 我们意识到这是不可扩展的。如果我们友好的Dropbox用户试图访问设备上的17500端口,我们应该立即允许他,而不是测试他是否违反任何防火墙规则。
    • 因此,我们编写了一个新的规则,允许我们的信任用户。使用-I将新规则插入到旧规则之前
1
iptables -I INPUT -p tcp --dport 17500 -s 10.0.0.85 -j ACCEPT -m comment --comment "Friendly Dropbox"
1
2
3
4
5
6
7
8
9
10
11
student@6858-v20:~$ sudo iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 19 packets, 1857 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 16 packets, 1892 bytes)
num pkts bytes target prot opt in out source destination
  • 把我们的第二条规则替换成拒绝所有17500端口的规则:
1
iptables -R INPUT 2 -p tcp --dport 17500 -j REJECT --reject-with icmp-port-unreachable
1
2
3
4
5
6
7
8
9
10
11
student@6858-v20:~$ sudo iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 19 packets, 1656 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 15 packets, 2224 bytes)
num pkts bytes target prot opt in out source destination

允许组播流量

  • 使用多播标识的协议(例如,SANE搜索网络扫描器)将向网络的广播IP发送流量,并从特定的客户端的IP返回响应。
    • 由于这些IP地址是不同的,所以iptables不会将响应识别为RELATED或ESTABLISHED,它将阻塞响应
  • 首先,创建一个ipset散列容器。
1
ipset create upnp hash:ip,port timeout 3

timeout是接受客户端响应的时间窗口。

  • 其次,创建一个规则,将传出多播流量添加到ipset散列中。
1
iptables -A OUTPUT -d 239.255.255.250/32 -p udp -m udp -j SET --add-set upnp src,src --exist
  • 第三,创建一个规则,允许与ipset散列匹配的传入流量。
1
iptables -A INPUT -p udp -m set --match-set upnp dst,dst -j ACCEPT
  • 最后,记住保存新规则,并确保启用iptables.service和ipset.service,以便在系统启动时加载规则。

日志

  • LOG目标可以用来记录命中规则的数据包。与其他目标(如ACCEPT或DROP)不同,包在命中LOG目标后将继续在链中移动

    • 这意味着,为了对所有丢失的包启用日志记录,必须在每个DROP规则之前添加一个重复的LOG规则。
    • 由于这降低了效率并使事情变得不那么简单,因此可以创建一个logdrop链。
  • 创建链

1
iptables -N logdrop
  • 并将以下规则添加到新创建的链:
1
2
iptables -A logdrop -m limit --limit 5/m --limit-burst 10 -j LOG
iptables -A logdrop -j DROP

limit和limit-burst选项解释如下

  • 现在,当我们想要丢弃一个包并记录这个事件时,我们只需跳转到日志丢弃链,例如:
1
iptables -A INPUT -m conntrack --ctstate INVALID -j logdrop

限制日志速率

  • 上面的logdrop链使用limit模块来防止iptables日志变得太大或导致不必要的硬盘写操作
    • 在不限制错误配置的服务尝试连接的情况下,攻击者可以通过引起对iptables日志的写入来填充驱动器(或至少是/var分区)。
  • limit模块使用-m limit调用
    • 使用—limit来设置平均速率
    • 使用—limit-burst来设置初始突发速率
1
iptables -A logdrop -m limit --limit 5/m --limit-burst 10 -j LOG
  • 上述命令附加一个规则,该规则将记录通过它的所有数据包。
    • 记录前10个连续数据包,从那时起每分钟仅记录5个数据包
    • 每当“限制速率”未被打破时,“限制突发”计数将被重置,即日志记录活动自动恢复正常。

查看记录包

  • 记录的数据包在systemd journal中作为内核消息可见
  • 要查看自计算机最后一次启动以来记录的所有包:
1
journalctl -k --grep="IN=.*OUT=.*"

syslog-ng

  • 可以在syslog-ng.conf中控制iptables的日志输出到哪里
1
2
3
4
# Replace
filter f_everything { level(debug..emerg) and not facility(auth, authpriv); };
# To
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的文件中,可以简单地改变目标d_iptables的文件值(仍然在syslog-ng.conf中):
1
destination d_iptables { file("/var/log/iptables.log"); };

ulogd

  • ulogd是用于netfilter的专用用户空间包日志记录守护进程,它可以替换默认的LOG目标。
  • ulogd包可以在[community]存储库中找到。