0%

填坑系列 - Iptables

iptables 是 Linux 中一个非常重要的安全工具,用于配置内核防火墙。

参考自:Arch Wiki - 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
                               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

Basic

iptables 用于检查、修改、转发、丢弃 IP 数据包,其中每一步的详细工作通过 tables 和 chains 这两个结构来完成。

上面那张图的完整情况是这个样子的:

每个节点上的小写字母表示 table ,下面的大写字母表示 chain ,可以把每个节点理解成很多设定好的规则。每一个 IP 数据包都要从上到下经过这样一整个工作流的检查(有的数据包从外面进来到 Local Process 就结束了,有的是从 Local Process 里面产生然后发送到外面去)。

Tables

iptables 中包含5个 tables:

  • raw 用于配置数据包,其中的数据包不会被系统跟踪

  • filter 存放了所有与防火墙相关的操作

  • nat 用于网络地址转换

  • mangle 用于对特定数据包的修改

  • security 用于强制访问控制(…不懂…暂时跳过)

通常情况下常用的是 filter 和 nat ,其他的用于更复杂的情况,所以上面手画的流图里面只画出了这两个 tables 。

Chains

tables 由 chains 组成,例如 filter 表中就有 INPUT 、 OUTPUT 、 FORWARD 3条内建的 chains ,这三条 chains 作用在数据包过滤过程的不同时间点上(参见上面的流程图)。 nat 表包含 PREROUTING 、 POSTROUTING 、 OUTPUT 这三条 chains 。

Configuration

在 Arch Linux 和 CentOS 上 iptables 的启动和关闭都在服务里配置(话说 CentOS 7 已经改用 firewalld 来管理防火墙了):

1
2
# systemctl start iptables
# systemctl stop iptables

然后不同的发行版设置的 iptables 的记录文件的位置还不一定一样,比如 Arch 里面是在 /etc/iptables/iptables.rules , CentOS 就是直接放在 /etc/sysconfig/iptables 这个文件里了, Ubuntu … 恕我没找到。

管理和配置 iptables 可以在 root 权限下用 iptables 命令,或者直接修改配置文件,然后重启 iptables 也行。一般我比较喜欢改文件,一目了然,不容易出错,,,不过像 Ubuntu 这种我没找到写在哪的就坑了。

具体的 iptables 命令还是用的时候 man iptables 慢慢查吧,这里简单记一下几个常用命令的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# iptables -A chain rule
向 chain 中附加一条规则 rule

# iptables -C chain rule
检查 chain 中是否存在规则 rule

# iptables -D chain rule
从 chain 中删除规则 rule

# iptables -I chain [rulenum] rule
向 chain 中插入一条规则 rule
这个跟 -A 的区别是 -A 是加入到 chain 的末尾
-I 如果不指定插入的序号的话则默认序号为1,即插入到 chain 的开头

# iptables -S [chain]
打印 chain 中的所有规则,不指定 chain 的话就是所有 chain

# iptables -L [chain]
列出 chain 中的所有规则,不指定 chain 的话就是所有 chain

上面的每条命令还可以加 -t table 来指定当前的操作对某个特定的表 table 进行。

-A 和 -I 的不同在于, iptables 检查规则时是从上往下按 rules 的顺序依次进行,一旦匹配到一条 rule 那就直接跳出当前 chain 了,因此同样的一些 rules 插入到 iptables 里之后可能因为顺序的不同会有不同的结果。

Rules

比较坑的是我在好几个 wiki 上查了 iptables ,但是上面很少有讲 rules 具体怎么写的, man iptables 返回的信息是在 PARAMETERS 这段中有讲。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[!] -p, --protocol protocol
指定协议,可以是 tcp/udp/udplite/icmp/icmpv6/esp/ah/sctp/mh/all
加了 ! 则表示除了该协议以外,下同

[!] -s, --source address[/mask][,...]
指定源地址

[!] -d, --destination address[/mask][m...]
指定目标地址

[!] -i, --in-interface name
指定数据包进入的网卡设备

[!] -o, --out-interface name
指定数据包流出的网卡设备

-j, --jump target
指定当前 rule 要跳转到的目标,可以是 ACCEPT/DROP/REJECT/LOG
ACCEPT 和 LOG 都是让数据包通过当前的 chain ,区别是 LOG 会记录日志信息
DROP 直接把包从这里丢弃不做任何操作, REJECT 丢弃包并且向源地址发送拒绝响应

-g, --goto chain
跳到某条 chain 中继续执行

Example

列个示例吧,下面是一台 VPN 服务器 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
*filter						# filter 表
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0] # 设置默认规则都是 ACCEPT
-A INPUT -i enp7s0 -j ACCEPT
-A INPUT -i ppp+ -j ACCEPT
-A INPUT -i lo -j ACCEPT # 设置各块网卡都能通过
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 1723 -j ACCEPT
-A INPUT -p udp -m udp --dport 1194 -j ACCEPT
-A INPUT -p udp -m udp --dport 4500 -j ACCEPT
-A INPUT -p esp -j ACCEPT
-A INPUT -p gre -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -d 202.38.64.59/32 -i ppp+ -j DROP # 有个特定的地址不想让它转发
COMMIT

*nat # nat 表
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o enp7s0 -j MASQUERADE # 设置输出数据包地址伪装
COMMIT

默认规则后面的 [0:0] 是用 iptables-save 命令保存当前的 iptables 配置到文件后写的,表示当前收发了多少数据包。我这里是手写的所以就直接设成 0 了,其实没什么用。

数据包进入某个 table 之后是从上往下遍历所有规则,最后再走默认规则。

事实上上面这个文件写的是有问题的,所有输入的数据包的 INPUT 链在网卡这里就直接被 ACCPET 了,所以后面写的一堆规则其实没啥用…

比较好的写法是把默认规则设成 DROP ,然后再写一些需要用的 ACCPET 规则,让数据包从上到下一条一条匹配,如果没有匹配到任何一条规则,那就说明这个包不是我们想要的,默认 DROP 掉。

filter 表的 FORWARD 链这里这条规则表示:从 ppp+ 这些网卡(PPTP 服务生成的虚拟网卡,对应了每个 VPN 的连接)来的,要到 202.38.64.59 这个地址去的数据包全部丢掉,这样 VPN 进来的连接就不能通过这台机子的转发来访问这个地址了。

下面的 nat 表, MASQUERADE 表示动态地址伪装,即 VPN 链接过来的数据从服务器出去的时候把数据包的源地址改成服务器的地址,用于 NAT 转换。


话说其实应该找时间开个虚拟机好好测试一下的…

To be continued…