iptables学习笔记

iptables概述

当网络数据包要进入主机时,会先经由NetFilter检查过滤,检查过滤的依据就是iptables中定义的规则。iptables会分析数据包首部资料,并与iptables中定义的一系列规则依序进行比对,检查通过则接受(ACCEPT)进入当前主机取得资源;检查不通过,则可能予以丢弃(DROP)。

iptables过滤机制

那如何确定数据包最终通过与否呢?首先 必需得知道三点:

  1. 被比对的这一组规则是有顺序的。
  2. 规则说明了数据包符合指定条件就执行相应的Action,这个Action通常是接受(ACCEPT)或丢弃(DROP);注意,规则要么说明了什么情况下接受,要么说明了什么情况下丢弃,无法像 if...else... 那样一起描述正反两种情况如何处理。
  3. 不管规则描述的是要接受还是丢弃的情况,只要满足了指定条件,就匹配上了当前规则。

接着 来看具体的处理过程,示意图如下:

一个网络数据包被主机接受之前,会先分析数据包首部资料,并与一组规则按顺序比对 Rule1,Rule 2,…Rule n。如果跟Rule1匹配上了,就执行相应的动作Action1,而不会理会后续的Rule 2,…Rule n了;如果未匹配,则继续匹配后续规则,直到匹配上某条规则;如果最终所有规则都未匹配上,就执行预设的处理策略(Policy)。规则的顺序很重要,如果排列不当,就会造成很严重的错误。 请看下面这个例子:
假设你的主机提供了www服务,现在你想让访问port 80的数据包通过。但是你发现source ip为192.168.100.100老是尝试入侵你的系统,所以你想要丢弃该来源的所有数据包。正确的规则配置如下:

  1. Rule 1 先抵擋 192.168.100.100;
  2. Rule 2 再讓要求 WWW 服務的封包通過;
  3. Rule 3 將所有的封包丟棄。

但如果你搞错了,配成这样:

  1. Rule 1 先讓要求 WWW 服務的封包通過;
  2. Rule 2 再抵擋 192.168.100.100 ;
  3. Rule 3 將所有的封包丟棄。
    就糗大了,因为第一条已经匹配上了,后续的所有规则都不理会了,但却放进来了恶意入侵的数据包。

iptables的内部结构:表(table)与链(chain)

iptables,顾名思义,其由多张表格(tables)组成;每张表格内又有几条链(chains);而链是由多条规则(rules)和预定义的处理策略(Policy)组成。这些表格的用途也不同。
Linux至少有三个表格:filter、nat(Network Address Translation)、mangle(破坏者,较少使用)。当然还可以自定义额外的表格和链,但这超出了本文要讨论的范围。每个表格的用途和使用场景描述如下:

  • raw:主要用于一件事儿,就是给那些不应该被连接跟踪系统处理的数据包设置一个标记。该表格不在本文讨论范围内。
  • filter:主要处理进入当前主机的和它要发送出去的数据包。如果你的 Linux 是作为 www 服务器,那么就需要开放相应的服务端口,就得要配置 filter 的 INPUT 链。
    • INPUT:主要处理进入当前主机的数据包
    • OUTPUT:主要处理当前主机发送出去的数据包
    • FORWARD:与当前主机没啥关系。它可以转发数据包到后端的主机,与nat table相关性较高。
  • nat:Network Address Translation 网络地址转换。主要处理来源与目的IP或port的转换,以实现外部实体与当前主机后面的区域网络内的其他主机的通信,与当前主机关系不大。如果你的 Linux 是作为区域网络的路由器,那么就得要分析 nat 的各個链以及 filter 的 FORWARD 链才行。也就是说, 其实各个表格的链之间是有关系的!
    • PREROUTING:在进行路由判断之前数据包要经过的规则链(DNAT/REDIRECT)
    • POSTROUTING:在进行路由判断之后数据包要经过的规则链(SNAT/MASQUERADE)
    • OUTPUT:主要处理发送出去的数据包
  • mangle(破坏者):主要与数据包带有的特殊的路由标记有关,早期仅有 PREROUTING 及 OUTPUT 链,不过从 kernel 2.4.18 之后加入了 INPUT 及 FORWARD 链。由于这个表格与特殊的路由标记相关性较高,所以很少使用,本文也不会讨论它。

数据包如何穿越表(table)和链(chain)

General

When a packet first enters the firewall, it hits the hardware and then gets passed on to the proper device driver in the kernel. Then the packet starts to go through a series of steps in the kernel, before it is either sent to the correct application (locally), or forwarded to another host - or whatever happens to it.

穿越表(table)和链(chain)

下图展示了数据包如何穿越表和链,到达最终目的地:

raw、mangle表格很少用到,也不在本文的讨论范围,所以在实际查阅上图时,可以跳过 暗红色的处理步骤

  • 橙色箭头 描述的是以本地主机为目的地的数据包所要经过的过滤步骤。
  • 蓝色箭头 描述的是从本地主机出发,以另一个网络中的另一台主机为目的地的数据包所要经过的处理步骤。
  • 粉红色箭头 描述的是从另一个网络中的另一台主机出发,以本地主机(本地主机是所在区域网络的路由器)所在网络中的另一台主机为目的地的数据包以及响应数据包所要经过的过滤步骤。

注意,没有针对不同网络接口的特定的链或表格。转发给当前这个防火墙或路由器的所有数据包总会通过FORWARD这个规则。警告,在这个使用场景中,不要使用INPUT链来过滤数据包,INPUT链只用于过滤以本地主机为目的地,且不会被路由到任何其他目的地的数据包。

iptables命令行的使用

规则的查看与清除

格式化查看规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ iptables [-t tables] [-L] [-nv]
# 選項與參數:
# -t :後面接 table ,例如 nat 或 filter ,若省略此項目,則使用預設的 filter
# -L :列出目前的 table 的規則
# -n :不進行 IP 與 HOSTNAME 的反查,顯示訊息的速度會快很多!
# -v :列出更多的資訊,包括通過該規則的封包總位元數、相關的網路介面等

# 範例:列出默认的 filter table 三條鏈的規則
$ iptables -L -n
Chain INPUT (policy ACCEPT) #針對 INPUT 鏈,且預設政策為可接受
target prot opt source destination #列首部
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED #第 1 條規則
ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0 #第 2 條規則
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 #第 3 條規則
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 #以下類推
REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
# ------------ 列首部详解 ------------
# target:代表進行的動作, ACCEPT 是放行,而 REJECT 則是拒絕,此外,尚有 DROP (丟棄) 的項目!
# prot:代表使用的封包協定,主要有 tcp, udp 及 icmp 三種封包格式;
# opt:額外的選項說明
# source :代表此規則是針對哪個『來源 IP』進行限制?
# destination :代表此規則是針對哪個『目標 IP』進行限制?
Chain FORWARD (policy ACCEPT) #針對 FORWARD 鏈,且預設政策為可接受
target prot opt source destination
REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited

Chain OUTPUT (policy ACCEPT) #針對 OUTPUT 鏈,且預設政策為可接受
target prot opt source destination

# 範例:列出 nat table 三條鏈的規則
$ iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target prot opt source destination

Chain POSTROUTING (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

以更易理解的形式查看完整规则

前一种查看方式输出的规则相对不容易理解,有些规则包含的信息也没有展现完整。如果把输出的INPUT表规则信息说明哈,结果是这样的:

  1. [ACCEPT all – 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED]:只要是封包狀態為 RELATED,ESTABLISHED 就予以接受
  2. [ACCEPT icmp – 0.0.0.0/0 0.0.0.0/0]:只要封包協定是 icmp 類型的,就予以放行
  3. [ACCEPT all – 0.0.0.0/0 0.0.0.0/0 ]:無論任何來源 (0.0.0.0/0) 且要去任何目標的封包,不論任何封包格式 (prot 為 all),通通都接受
  4. [ACCEPT tcp – 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22]:只要是傳給 port 22 的主動式連線 tcp 封包就接受
  5. [REJECT all – 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited]:全部的封包資訊通通拒絕

咦,貌似有问题哇,第三条规则如果全部都通过,那后面的规则都不会起什么作用了。但其实这条规则是针对每个主机都有的环回(loopback)网络接口(lo)而定义的。如果未显示完整,就很容易误解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ iptables-save [-t table]
# 選項與參數:
# -t :可以僅針對某些表格來輸出,例如僅針對 nat 或 filter 等等

$ iptables-save
# Generated by iptables-save v1.4.7 on Fri Jul 22 15:51:52 2011
*filter #星號開頭的指的是表格,這裡為 filter
:INPUT ACCEPT [0:0] #冒號開頭的指的是鏈,三條內建的鏈
:FORWARD ACCEPT [0:0] #三條內建鏈的政策都是 ACCEPT 囉!
:OUTPUT ACCEPT [680:100461]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT #針對 INPUT 的規則
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT #這條很重要!針對本機內部介面開放!
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited #針對 FORWARD 的規則
COMMIT
# Completed on Fri Jul 22 15:51:52 2011

输出的规则中 -i 选项指定具体的网络接口。

清除已有规则

1
2
3
4
5
6
7
8
9
10
$ iptables [-t tables] [-FXZ]
# 選項與參數:
# -F :清除所有已訂定的規則,但不会改变预设策略(policy)
# -X :殺掉所有使用者 "自訂" 的 chain (應該說的是 tables )囉;
# -Z :將所有的 chain 的計數與流量統計都歸零

# 範例:清除本機防火牆 (filter) 的所有規則
$ iptables -F
$ iptables -X
$ iptables -Z

删除具体规则

1
2
3
4
5
6
7
$ iptables [-t 具体表格] -D <链> [<规则编号>]
# 如果只提供了链名,未提供规则编号,则删除整条链中的所有规则
# 每条链中的规则从 1 开始编号,依次递增;不同的链重新开始编号

# 先插入一条规则至 filter 表格的 INPUT 链的最前面,再删除
$ sudo iptables -I INPUT -p tcp -m tcp --dport 8080 -j ACCEPT
$ sudo iptables -t filter -D INPUT 2

设置预设策略

如果你的数据包没有匹配上任何规则,就根据预设的策略(policy)来决定其最终是否通过。在设置预设策略时,假設你對於內部的使用者有信心的話, 那麼 filter 內的 INPUT 鏈可以定義的比較嚴格一點,而 FORWARD 與 OUTPUT 則可以訂定的鬆一些!通常鳥哥(鸟哥私房菜)都是將 INPUT 的 policy 定義為 DROP 啦,其他兩個則定義為 ACCEPT。 至於 nat table 則暫時先不理會他。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ iptables [-t nat] -P [INPUT,OUTPUT,FORWARD] [ACCEPT,DROP]
# 選項與參數:
# -P :定義政策( Policy )。注意,這個 P 為大寫啊!
# ACCEPT :該封包可接受
# DROP :該封包直接丟棄,不會讓 client 端知道為何被丟棄。

# 範例:將本機的 INPUT 設定為 DROP ,其他設定為 ACCEPT
$ iptables -P INPUT DROP
$ iptables -P OUTPUT ACCEPT
$ iptables -P FORWARD ACCEPT
$ iptables-save
# Generated by iptables-save v1.4.7 on Fri Jul 22 15:56:34 2011
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
# Completed on Fri Jul 22 15:56:34 2011
# 由於 INPUT 設定為 DROP 而又尚未有任何規則,所以上面的輸出結果顯示:
# 所有的封包都無法進入你的主機!是不通的防火牆設定!(網路連線是雙向的)

数据包的基础比对:IP, 网域及网络接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ iptables [-AI 鏈名] [-io 網路介面] [-p 協定] \
> [-s 來源IP/網域] [-d 目標IP/網域] -j [ACCEPT|DROP|REJECT|LOG]
# 選項與參數:
# -AI 鏈名:針對某的鏈進行規則的 "插入""累加"
# -A :新增加一條規則,該規則增加在原本規則的最後面。例如原本已經有四條規則,
# 使用 -A 就可以加上第五條規則!
# -I :插入一條規則。如果沒有指定此規則的順序,預設是插入變成第一條規則。
# 例如原本有四條規則,使用 -I 則該規則變成第一條,而原本四條變成 2~5 號
# 鏈 :有 INPUT, OUTPUT, FORWARD 等,此鏈名稱又與 -io 有關,請看底下。
#
# -io 網路介面:設定封包進出的介面規範
# -i :封包所進入的那個網路介面,例如 eth0, lo 等介面。需與 INPUT 鏈配合;
# -o :封包所傳出的那個網路介面,需與 OUTPUT 鏈配合;
#
# -p 協定:設定此規則適用於哪種封包格式
# 主要的封包格式有: tcp, udp, icmp 及 all 。
#
# -s 來源 IP/網域:設定此規則之封包的來源項目,可指定單純的 IP 或包括網域,例如:
# IP :192.168.0.100
# 網域:192.168.0.0/24, 192.168.0.0/255.255.255.0 均可。
# 若規範為『不許』時,則加上 ! 即可,例如:
# -s ! 192.168.100.0/24 表示不許 192.168.100.0/24 之封包來源;
#
# -d 目標 IP/網域:同 -s ,只不過這裡指的是目標的 IP 或網域。
#
# -j :後面接動作,主要的動作有接受(ACCEPT)、丟棄(DROP)、拒絕(REJECT)及記錄(LOG)

接下来看看最基础的规则设置。

1
2
3
# 範例:設定 lo 成為受信任的裝置,亦即進出 lo 的封包都予以接受
# 疑问:进出?INPUT不会影响到从lo出去的数据包吧?
$ iptables -A INPUT -i lo -j ACCEPT

仔细看看上面的命令,并没有使用 -s, -d 等选项,这表示:无论数据包来自哪里或去往哪里,只要是来自lo这个网络接口,就全部接受。这个观念挺重要,就是 没有指定的项目,就表示完全接受。

1
2
3
4
5
6
# 範例:只要是來自内部信任网域 (192.168.100.0/24) 的封包通通接受
$ iptables -A INPUT -i eth1 -s 192.168.100.0/24 -j ACCEPT
# 不过,下达这个命令时要特别小心,这样这个网卡就没有任何防护了。

# 当然,也可以针对单个主机ip进行设置
$ iptables -A INPUT -i eth1 -s 192.168.100.10 -j ACCEPT

将数据包相关资料记入日志

1
2
3
4
$ iptables -A INPUT -s 192.168.2.200 -j LOG
$ iptables -L -n
target prot opt source destination
LOG all -- 192.168.2.200 0.0.0.0/0 LOG flags 0 level 4

只要有数据包来自 192.168.2.200,该数据包的相关信息就会被写入到核心讯息中,即 /var/log/messages 文件。然后该数据包继续进行后续规则的比对。注意,LOG 动作仅进行日志记录,并不会影响数据包与其他规则的比对。

TCP、UDP规则比对:针对端口、数据包状态来设置

TCP、UDP规则有两个比较特殊的方面:端口、数据包状态,例如最常见的 SYN 主动连线的数据包。

1
2
3
4
5
6
$ iptables [-AI 鏈] [-io 網路介面] [-p tcp,udp] \
> [-s 來源IP/網域] [--sport 埠口範圍] \
> [-d 目標IP/網域] [--dport 埠口範圍] -j [ACCEPT|DROP|REJECT]
# 選項與參數:
# --sport 埠口範圍:限制來源的埠口號碼,埠口號碼可以是連續的,例如 1024:65535
# --dport 埠口範圍:限制目標的埠口號碼。

注意,仅有 TCP 和 UDP 数据包才涉及端口,因此要使用 –dport、–sport,得加上 -p tcp 或 -p udp 才行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 範例:想要連線進入本機 port 21 的封包都抵擋掉:
$ iptables -A INPUT -i eth0 -p tcp --dport 21 -j DROP

# 範例:想連到我這部主機的網芳 (upd port 137,138 tcp port 139,445) 就放行
$ iptables -A INPUT -i eth0 -p udp --dport 137:138 -j ACCEPT
$ iptables -A INPUT -i eth0 -p tcp --dport 139 -j ACCEPT
$ iptables -A INPUT -i eth0 -p tcp --dport 445 -j ACCEPT

# 範例:只要來自网域192.168.1.0/24,端口在1024:65535范围内,
# 且想要連線到本機的 ssh port 就予以抵擋
$ iptables -A INPUT -i eth0 -p tcp -s 192.168.1.0/24 \
> --sport 1024:65534 --dport ssh -j DROP

# 範例:將來自任何地方,来源port为 1:1023 的
# 主動連線到当前主机 1:1023 端口范围的连接丢弃
$ iptables -A INPUT -i eth0 -p tcp --sport 1:1023 \
> --dport 1:1023 --syn -j DROP
# 客户端一般会使用大于1024的端口去连接服务器,但这不适用于FTP

针对数据包状态来配置规则的详细指导请看:
鸟哥的私房菜-iptables 外掛模組:mac 與 state
Iptables Tutorial 1.2.2-TCP/UDP State Machine

ICMP 规则比对:针对是否回应 ping 来配置

ICMP数据包类型相当多,很多类型都是用来进行网络检测的。所以最好不要将所有ICMP数据包都丢弃。如果当前主机并不充当区域网络的路由器,那么通常我们会把 ICMP type 8 (echo request) 数据包丢弃,让远端主机不知道我们是否存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ iptables -A INPUT [-p icmp] [--icmp-type 類型] -j ACCEPT
# 選項與參數:
# --icmp-type :後面必須要接 ICMP 的封包類型,也可以使用代號,
# 例如 8 代表 echo request 的意思。

# 範例:讓 0,3,4,11,12,14,16,18 的 ICMP type 可以進入本機:
$ vi somefile
#!/bin/bash
icmp_type="0 3 4 11 12 14 16 18"
for typeicmp in $icmp_type
do
iptables -A INPUT -i eth0 -p icmp --icmp-type $typeicmp -j ACCEPT
done

$ sh somefile

这样就能够开放部分的 ICMP 数据包进入本机以便进行网络检测的工作了。不过,如果你的主机是作为区域网络的路由器,那么建议 ICMP 数据包通通放行。这是因为用户检测网络时,常常会使用 ping 来测试到路由器的线路是否畅通。

永久保存防火墙规则设置

1
2
# 寫入防火牆規則設定檔
$ /etc/init.d/iptables save

蛋疼,在 centos7 运行上面的保存命令,报错 no such file or directory: /etc/init.d/iptables 。经尝试验证 sudo service iptables save 可以保存成功,但注意必须加上 sudo,否则没有报错,但也没有保存成功。老实说,如果你对Linux够熟悉的话,可以直接修改规则配置文件 /etc/sysconfig/iptables,然后重启 iptables 服务,你所做的规则变更就会在重启主机后持续存在了。

参考资料

鸟哥的私房菜-防火墙与NAT伺服器
鸟哥的私房菜-基礎網路概念
Iptables Tutorial 1.2.2

0%