前言

在大企业中防火墙角色主要交给硬件来支持,效果自然没话说只是需要增加一点点成本,但对于大多数个人或者互联网公司来说选择系统自带的 iptables 或者第三方云防火墙似乎是更加合适的选择,通过一些合理的优化和灵活的配置,我们也可以很轻松实现硬件防火墙的部分功能,够用就好。

建立防火墙白名单机制很重要


更新历史

2019 年 08 月 26 日 - 更新配置 lvs/tun 的配置和基于 ansible 修改 iptables 的最佳实践
2019 年 03 月 06 日 - 增加使用 ansible blockinfile 模块管理 iptables 规则
2018 年 07 月 18 日 - 更新 iptables 原理和 NAT 配置
2015 年 07 月 24 日 - 初稿

阅读原文 - https://wsgzao.github.io/post/iptables/

扩展阅读

Linux 防火墙和 iptables - http://liaoph.com/iptables/
iptables 零基础快速入门系列 - http://www.zsythink.net/archives/tag/iptables/
iptables 详解


基础知识

Netfilter 与 iptables 的关系

Linux 系统在内核中提供了对报文数据包过滤和修改的官方项目名为 Netfilter,它指的是 Linux 内核中的一个框架,它可以用于在不同阶段将某些钩子函数(hook)作用域网络协议栈。Netfilter 本身并不对数据包进行过滤,它只是允许可以过滤数据包或修改数据包的函数挂接到内核网络协议栈中的适当位置。这些函数是可以自定义的。

iptables 是用户层的工具,它提供命令行接口,能够向 Netfilter 中添加规则策略,从而实现报文过滤,修改等功能。Linux 系统中并不止有 iptables 能够生成防火墙规则,其他的工具如 firewalld 等也能实现类似的功能。

使用 iptables 进行包过滤

iptables 策略是由一组有序的规则建立的,它告诉内核应该如何处理某些类别的数据包。每一个 iptables 规则应用于一个表中的一个链。一个 iptables 链就是一个规则集,这些规则按序与包含某种特征的数据包进行比较匹配。

iptables 默认有 4 个表

  1. nat 表(地址转换表)
  2. filter 表(数据过滤表)
  3. raw 表(状态跟踪表)
  4. mangle 表(包标记表)

每个表都有一组内置链,用户还可以添加自定义的链。最重要的内置链是 filter 表中的 INPUT、OUTPUT 和 FORWARD 链。

  1. INPUT 链(入站规则)
  2. OUTPUT 链(出站规则)
  3. FORWARD 链(转发规则)
  4. PREROUTING 链(路有前规则)
  5. POSTROUTING 链(路由后规则)

下图展现了一个数据包是如何通过内核中的 net 和 filter 表的:


iptables 的 4 表 5 链

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
iptables --> 四表五链 
添加规则是的考量点:
(1)要实现那种功能:判断添加在那张表上
(2)报文流经的路径:判断添加在那个链上

链 -->> 练上规则的次序,即为检查的次序,因此隐含一定的法则
(1)同类规则(访问同一应用):匹配范围小的放上面
(2)不同类规则(访问不同应用):匹配到报文平率较大的放上面
(3)将可以由条规则描述的多个规则合并为一个
(4)设置默认策略:

功能的优先级次序:raw --> mangle --> nat --> filter

规则:
组成部分:报文的匹配条件,匹配到后处理动作
匹配条件:根据协议报文特征指定匹配条件
基本匹配条件:源 IP,目标 IP,源端口,目标端口
扩展匹配条件:IP 追踪
处理动作:
内建处理机制
自定义处理机制
注意:

报文不会经过自定义链,只能在内置链上通过规则引用后生效
iptables 的规则管理工具
添加、修改、删除、显示等功能
规则和链有计数器:
pkgs:有规则或链所匹配到的报文的个数
bytes: 由规则或链匹配到的所有报文大小之和

匹配

每个 iptables 规则都包含一组匹配和一个目标动作,后者定义了复合规则的数据包应该采取什么处理行为。iptables 匹配指定是数据包必须匹配的条件,只有当数据包满足所有的匹配条件时,iptables 才能根据规则的目标所指定的动作来处理该数据包。

每个匹配都在 iptables 的命令行中指定。下面是一些常用的基本匹配:

参数 作用
-P 设置默认策略
-F 清空规则链
-L 查看规则链
-A 在规则链的末尾加入新规则
-I num 在规则链的头部加入新规则
-D num 删除某一条规则
-s 匹配来源地址 IP/MASK,加叹号 “!” 表示除这个 IP 外
-d 匹配目标地址
-i 网卡名称 匹配从这块网卡流入的数据
-o 网卡名称 匹配从这块网卡流出的数据
-p 匹配协议,如 TCP、UDP、ICMP
–dport num 匹配目标端口号
–sport num 匹配来源端口号

防火墙的匹配规则

  • 匹配即可停止
  • 匹配有先后顺序
  • 默认规则的优先级最低

基础语法

表名作用:

raw:高级功能,如:网址过滤。
mangle:数据包修改(QOS),用于实现服务质量。
net:地址转换,用于网关路由器。
filter:包过滤,用于防火墙规则。

规则链作用:

INPUT 链:处理输入数据包。
OUTPUT 链:处理输出数据包。
PORWARD 链:处理转发数据包。
PREROUTING 链:用于目标地址转换(DNAT)。
POSTOUTING 链:用于源地址转换(SNAT)。

动作作用:

accept:接收数据包。
DROP:丢弃数据包。
REJECT :拒绝数据包
REDIRECT:重定向、映射、透明代理。
SNAT:源地址转换。
DNAT:目标地址转换。
MASQUERADE:IP 伪装(NAT),用于 ADSL。
LOG:日志记录。

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 规则的观察与清除 

iptables [-t tables] [-L] [-nv]

选项与参数:
-t :后面接 table ,例如 nat 或 filter ,若省略此项目,则使用默认的 filter
-L :列出目前的 table 的规则
-n :不进行 IP 与 HOSTNAME 的反查,显示讯息的速度会快很多!
-v :列出更多的信息,包括通过该规则的封包总位数、相关的网络接口等

iptables [-t tables] [-FXZ]

选项与参数:
-F :清除所有的已订定的规则;
-X :杀掉所有使用者 "自定义" 的 chain (应该说的是 tables )啰;
-Z :将所有的 chain 的计数与流量统计都归零


# 封包的基础比对:IP, 网域及接口装置

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)

#TCP, UDP 的规则比对:针对端口设定

iptables [-AI 链] [-io 网络接口] [-p tcp,udp] [-s 来源 IP / 网域] [--sport 埠口范围] [-d 目标 IP / 网域] [--dport 端口范围] -j [ACCEPT|DROP|REJECT]

选项与参数:
--sport 埠口范围:限制来源的端口号码,端口号码可以是连续的,例如 1024:65535
--dport 埠口范围:限制目标的端口号码。

#iptables 外挂模块:mac 与 state

iptables -A INPUT [-m state] [--state 状态]

选项与参数:
-m :一些 iptables 的外挂模块,主要常见的有:
state :状态模块
mac :网络卡硬件地址 (hardware address)
--state :一些封包的状态,主要有:
INVALID :无效的封包,例如数据破损的封包状态
ESTABLISHED:已经联机成功的联机状态;
NEW :想要新建立联机的封包状态;
RELATED :这个最常用!表示这个封包是与我们主机发送出去的封包有关


#ICMP 封包规则的比对:针对是否响应 ping 来设计

iptables -A INPUT [-p icmp] [--icmp-type 类型] -j ACCEPT

选项与参数:
--icmp-type :后面必须要接 ICMP 的封包类型,也可以使用代号,
例如 8 代表 echo request 的意思。

注意事项与规律

  • 可以不指定表,默认为 filter 表
  • 可以不指定链,默认为对应表的所有链
  • 除非设置默认策略,否则必须指定匹配条件
  • 选项 / 链名 / 目标操作用大写字母,其余都小写

配置 iptables 白名单机制

配置 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

# 清除所有规则(慎用)
iptables -F
iptables -X
iptables -Z

# 查看 iptable 和行号
iptables -nL --line-number

# 保存当前防火墙配置
service iptables save

# 手动编辑防火墙策略
vi /etc/sysconfig/iptables

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# 开放本地和 Ping
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
# 配置内网白名单
-A INPUT -s 10.0.0.0/8 -j ACCEPT
-A INPUT -s 172.16.0.0/12 -j ACCEPT
-A INPUT -s 192.168.0.0/16 -j ACCEPT
# 配置外网白名单
-A INPUT -s 180.168.36.198 -j ACCEPT
-A INPUT -s 180.168.34.218 -j ACCEPT
-A INPUT -s 222.73.202.251 -j ACCEPT
# 控制端口
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 22 -j ACCEPT
# 拒绝其它
-A INPUT -j DROP
-A FORWARD -j DROP
# 开放出口
-A OUTPUT -j ACCEPT
COMMIT

# 重启生效
service iptables restart

# 复查结果

iptables -nL --line-number

Chain INPUT (policy ACCEPT)
num target prot opt source destination
1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
2 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
3 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0
4 ACCEPT all -- 10.0.0.0/8 0.0.0.0/0
5 ACCEPT all -- 172.16.0.0/12 0.0.0.0/0
6 ACCEPT all -- 192.168.0.0/16 0.0.0.0/0
7 ACCEPT all -- 180.168.36.198 0.0.0.0/0
8 ACCEPT all -- 180.168.34.218 0.0.0.0/0
9 ACCEPT all -- 222.73.202.251 0.0.0.0/0
10 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
11 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
12 DROP all -- 0.0.0.0/0 0.0.0.0/0

Chain FORWARD (policy ACCEPT)
num target prot opt source destination
1 DROP all -- 0.0.0.0/0 0.0.0.0/0

Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0

设置 crontab 脚本

便于运维集中化管理扩展

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

vim /root/start_iptables.sh

#!/bin/bash
#0 0 * * * /root/start_iptables.sh

# 清除配置
/sbin/iptables -P INPUT ACCEPT
/sbin/iptables -F
/sbin/iptables -X
# 开放本地和 Ping
/sbin/iptables -A INPUT -i lo -j ACCEPT
/sbin/iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
/sbin/iptables -A INPUT -p icmp -j ACCEPT
# 配置内网白名单
/sbin/iptables -A INPUT -s 10.0.0.0/8 -j ACCEPT
/sbin/iptables -A INPUT -s 172.16.0.0/12 -j ACCEPT
/sbin/iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT
# 配置外网白名单
/sbin/iptables -A INPUT -s 180.168.36.198 -j ACCEPT
/sbin/iptables -A INPUT -s 180.168.34.218 -j ACCEPT
/sbin/iptables -A INPUT -s 222.73.202.251 -j ACCEPT
# 控制端口
/sbin/iptables -A INPUT -p tcp --dport 80 -j ACCEPT
/sbin/iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 拒绝其它
/sbin/iptables -A INPUT -j DROP
/sbin/iptables -A FORWARD -j DROP
# 开放出口
/sbin/iptables -A OUTPUT -j ACCEPT


chmod 755 /root/start_iptables.sh
crontab -e
0 0 * * * /root/start_iptables.sh

CentOS7 配置 iptables

1
2
3
4
5
6
7
8
9
10
# 配置好 yum 源以后安装 iptables-service
yum install -y iptables-services
# 停止 firewalld
systemctl stop firewalld
# 禁止 firewalld 自动启动
systemctl disable firewalld
# 启动 iptables
systemctl start iptables
# 将 iptables 设置为开机自动启动,以后即可通过 iptables-service 控制 iptables 服务
systemctl enable iptables

开启 Linux 路由转发

1
2
3
4
5
6
# 关闭路由转发
echo 0 > /proc/sys/net/ipv4/ip_forward
# 开启路由转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 注意以上操作仅当前有效,计算机重启后无效,修改 / etc/sysctl.conf 配置文件,可以实现永久有效规则
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf

配置 NAT Tunnel

俗称跳板机

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 配置 iptables
vi /etc/sysconfig/iptables
*nat
:PREROUTING ACCEPT [6:504]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [1:52]
:POSTROUTING ACCEPT [1:52]

-A POSTROUTING -j MASQUERADE
COMMIT

*filter
:INPUT DROP [1029028:53321694]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [44723822:21524638399]

#zabbix
-A INPUT -s 10.65.200.90 -p tcp -m tcp --dport 10050 -j ACCEPT

-A INPUT -p tcp --dport 22 -j ACCEPT

# For keepalived:
# allow vrrp
-A INPUT -p vrrp -j ACCEPT
-A INPUT -p igmp -j ACCEPT
# allow multicast
-A INPUT -d 224.0.0.18 -j ACCEPT

# Drop packets from spoofed networks
-A INPUT -s 169.254.0.0/16 -j DROP
#-A INPUT -s 10.0.0.0/8 -j DROP
#-A INPUT -s 127.0.0.0/8 -j DROP
-A INPUT -s 224.0.0.0/4 -j DROP
-A INPUT -d 224.0.0.0/4 -j DROP
-A INPUT -s 240.0.0.0/5 -j DROP
-A INPUT -d 240.0.0.0/5 -j DROP
-A INPUT -s 0.0.0.0/8 -j DROP
-A INPUT -d 0.0.0.0/8 -j DROP
-A INPUT -d 239.255.255.0/24 -j DROP
-A INPUT -d 255.255.255.255 -j DROP

# Drop Invalid packets
-A INPUT -m state --state INVALID -j DROP
#-A FORWARD -m state --state INVALID -j DROP
-A OUTPUT -m state --state INVALID -j DROP

# Drop Bogus TCP packets
-A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
-A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
-A INPUT -p icmp --icmp-type redirect -j ACCEPT
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
COMMIT

# 重启生效并检查
service iptables restart
iptables -nvL

常用的 iptables 配置文件

1
2
3
4
5
6
7
8
9
# dnat
-A PREROUTING -s 1.1.1.1/30,2.2.2.2 -p tcp --dport 10000 -j DNAT --to-destination 10.71.12.89:80
# snat
-A POSTROUTING -d 1.1.1.1 -j SNAT --to-source 2.2.2.2
-A POSTROUTING -o bond1 -j SNAT --to-source 3.3.3.3
-A POSTROUTING -j MASQUERADE
# multiport
-A INPUT -s 10.65.200.90 -p tcp -m multiport --dports 10050,10051 -j ACCEPT
-A INPUT -s 10.65.200.90 -p tcp -m multiport --dports 10050:10060 -j ACCEPT

Ansible 管理 iptables

核心先规范好 iptables 模板格式,然后利用 ansible blockinfile 中的 marker 批量修改

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

# lvs-tun
-A INPUT -p ipv4 -j ACCEPT

# keepalived vrrp
-A INPUT -p vrrp -j ACCEPT
-A INPUT -p igmp -j ACCEPT
-A INPUT -d 224.0.0.18 -j ACCEPT

# http/https
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT

# BEGIN ANSIBLE MANAGED BLOCK iptables
-A INPUT -s xxx -p tcp --dport 22 -j ACCEPT
-A INPUT -s xxx -p tcp --dport 22 -j ACCEPT
-A INPUT -s xxx -p tcp --dport 22 -j ACCEPT
# END ANSIBLE MANAGED BLOCK iptables

# BEGIN ANSIBLE MANAGED BLOCK jumphost
-A INPUT -s 10.10.8.151/32 -p tcp --dport 22 -j ACCEPT
-A INPUT -s 10.10.70.149 -p tcp -m tcp --dport 38422 -j ACCEPT
# END ANSIBLE MANAGED BLOCK jumphost

# BEGIN ANSIBLE MANAGED BLOCK userdefined
# END ANSIBLE MANAGED BLOCK userdefined

# Drop packets from spoofed networks
-A INPUT -s 169.254.0.0/16 -j DROP
-A INPUT -s 224.0.0.0/4 -j DROP
-A INPUT -d 224.0.0.0/4 -j DROP
-A INPUT -s 240.0.0.0/5 -j DROP
-A INPUT -d 240.0.0.0/5 -j DROP
-A INPUT -s 0.0.0.0/8 -j DROP
-A INPUT -d 0.0.0.0/8 -j DROP
-A INPUT -d 239.255.255.0/24 -j DROP
-A INPUT -d 255.255.255.255 -j DROP

# Drop Invalid packets
-A INPUT -m state --state INVALID -j DROP
#-A FORWARD -m state --state INVALID -j DROP
-A OUTPUT -m state --state INVALID -j DROP

# Drop Bogus TCP packets
-A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
-A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
-A INPUT -p icmp --icmp-type redirect -j ACCEPT
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
COMMIT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
---
- hosts: all
become: yes
gather_facts: no

tasks:
- name: backup iptables
copy:
src: /etc/sysconfig/iptables
dest: /etc/sysconfig/iptables.bak
remote_src: yes

- name: add iptables line for internal logic service
blockinfile:
path: /etc/sysconfig/iptables
insertafter: '^:OUTPUT ACCEPT'
marker: "# {mark} iptables whitelist only for internal logic service"
block: |
-A INPUT -s 10.0.0.0/8 -j ACCEPT

- name: reload service
service: name=iptables state=reloaded
tags:
- reload
文章目录
  1. 1. 前言
  2. 2. 更新历史
  3. 3. 基础知识
    1. 3.1. Netfilter 与 iptables 的关系
    2. 3.2. 使用 iptables 进行包过滤
    3. 3.3.
    4. 3.4.
    5. 3.5. iptables 的 4 表 5 链
    6. 3.6. 匹配
    7. 3.7. 防火墙的匹配规则
    8. 3.8. 基础语法
  4. 4. 配置 iptables 白名单机制
  5. 5. 设置 crontab 脚本
  6. 6. CentOS7 配置 iptables
    1. 6.1. 开启 Linux 路由转发
  7. 7. 配置 NAT Tunnel
  8. 8. 常用的 iptables 配置文件
  9. 9. Ansible 管理 iptables