18.9 使用iptables建立防火墙
iptables是Linux下功能强大的防火墙工具,由于它集成于Linux内核,所以效率极高。该工具在系统安装的过程中会默认安装,如果没有,可以使用RPM或yum安装。如果想自行编译安装最新的版本也很简单:下载最新的源码包,然后执行./configure—prifix=/some/path/&&make&&make install命令就可以了。关于软件的安装过程此处不赘述。
按照对数据包的操作类别分类,iptables可以分为4个表,按照不同的Hook点可区分为5个链。其中4个表分别是filter表(用于一般的过滤)、nat表(地址或端口映射)、mangle表(对特定数据包的修改)、raw表,这里面最常用的是filter表;5个链分别是PREROUTING链(数据包进入路由决策之前)、INPUT(路由决策为本机的数据包)、FORWARD(路由决策不是本机的数据包)、OUTPUT(由本机产生的向外发送的数据包)、POSTROUTING(发送给网卡之前的数据包),最常用的是INPUT、OUTPUT链。
关于iptables防火墙的知识足够专门用一本书来描述,本书无法面面俱到。本节旨在给读者演示一个功能完整的防火墙开发过程,读者可以根据实际需要进行修改后投入真实的生产环境。
防火墙的工作策略一般包含两种方式,第一种是仅接受允许的数据,这种策略一般是设置防火墙的默认策略为拒绝所有数据包(也就是拒绝网卡上出入的数据包),然后有针对性地放开特定的访问;第二种是只防止不允许的数据访问请求,这种策略一般是设置防火墙的默认策略为允许所有数据包,只拒绝已知的非法访问数据。从安全效果而言,前一种防火墙策略表现更为优秀,所以这里使用第一种策略来开发防火墙脚本。
首先在使用iptables之前输入以下两条命令,可以将其理解为防火墙的初始化(这里不做深入介绍)。
- iptables -F #
清空所有规则
iptables -X #
删除所有自定义的链
下面开始建立iptables防火墙规则。我们采取的规则是:默认所有的数据都丢弃,仅接收已知的数据包,所以要有针对性地打开需要的端口。下面两条命令定义默认全部丢弃数据包。
- iptables -P INPUT DROP
iptables -P OUTPUT DROP
#-P
参数的意思是policy
,翻译成策略
#
第一句的意思是:
#
输入(INPUT)
的数据包默认的策略(-P)
是丢弃(DROP)
的
#
第二句的意思是:
#
输出(OUTPUT)
的数据包默认的策略(-P)
是丢弃(DROP)
的
其实到这里它已经是一个有用的防火墙了,只不过没有什么意义——因为这和拔掉网线的操作没有什么不同,而且比没有防火墙更糟糕的是本地数据包都无法通信了。这种类型的防火墙需要一些基本策略来保证一些基本功能可用,所以下面的一些规则也是需要的。
- iptables -A INPUT -p icmp --icmp-type any -j ACCEPT
#
允许icmp
包进入。如果确认不需要icmp
通信,此条可以不写
iptables -A OUTPUT -p icmp --icmp any -j ACCEPT
#
允许icmp
包出去
iptables -A INPUT -s localhost -d localhost -j ACCEPT
#
允许本地数据包出
iptables -A OUTPUT -s localhost -d localhost -j ACCEPT
#
允许本地数据包入
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
#
允许已经建立和相关的数据包进入
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
#
允许已经建立和相关的数据包出去
写完上面的基本策略后,现在需要考虑一些特定策略了,这和你的服务器是什么样的应用类型密切相关。如果是一台Web服务器的话,典型的需要是能访问80端口,但是就目前的策略而言是无法访问的,所以需要允许80端口的访问。命令如下:
- iptables -A INPUT -p tcp --dport 80 -j ACCEPT
如果你认为这样就大功告成的话那就错了,不信你可以尝试访问一下,会发现依然打开不了Web服务器的主页(假设你设置好了Apache服务,并应用了以上的防火墙规则),为什么呢?考虑一下计算机是怎么工作的。假设你的计算机是A,服务器是B,从A发送了一个目的地址为B、目的端口是80的数据包。服务器B收到这个数据包时发现该数据包匹配INPUT链规则,所以这个包可以正常的进入服务器B;然后服务器B在给A回包时,回包会进入本地的OUTPUT链——但是OUTPUT链默认是DROP所有包的,而且没有定义相关允许策略,回包无法出去,于是造成了整个访问的过程不完整。所以就需要下面的命令:
- iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
现在再试试访问Web服务器,一定是成功的。这条命令中使用了状态跟踪模块,意思是对能建立完整的连接以及为了维持该连接需要打开的其他连接所产生的数据包都是可以通过防火墙的OUTPUT链。但是如果需要允许该服务器访问其他的Web服务器,该怎么办呢?只要打开让数据出去的80端口就可以了,这需要两条命令,如下所示:
- iptables -A OUTPUT -p tcp -m state --state NEW --dport 80 -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
如果此时尝试使用服务器访问某个域名,比如www.baidu.com,会发现其实还是不能访问到页面。难道是上面的规则不对吗?考虑一下使用域名访问网站需要经历什么过程。对了,域名解析。因为服务器访问该域名之前需要先解析出它的IP地址,所以防火墙必须允许域名解析的数据包出去,使用如下的命令就可以了。
- iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
到了举一反三的时候了,如果要访问https(默认目标端口为443)的站点,应该打开什么端口呢?这里请读者自己试一下吧。下面还列举了一些常见的需要打开的端口,读者可以参考设置。
#
由于管理需要ssh
到这台服务器,则需要打开22
号端口
iptables -A IPUT -p tcp -dport 22 -j ACCEPT
#
如果只允许一个固定的IP
能ssh
到该服务器的话,上面的语句需要改为
iptables -A INPUT -p tcp —dport 22 -s 192.168.1.10 -j ACCEPT
#
可能还需要从该服务器ssh
到别的服务器
iptables -A OUTPUT -p tcp —dport 22 -j ACCEPT
到这里,一个简单的iptables防火墙就可以使用了,大家可以领悟一下该脚本开发过程中的各个关键点,最重要的是需要了解服务器可以正常工作时对防火墙策略的需求,以及相应的对端口放开INPUT和OUTPUT链上的策略。最后将整个过程脚本化,如下所示:
- #!/bin/bash
#DEFINE VARIABLES
HTTP_PORT=80
SECURE_HTTP_PORT=443
SSH_PORT=22
DNS_PORT=53
ALLOWED_IP=192.168.1.10
IPTABLES=/sbin/iptables
#FLUSH IPTABLES
$IPTABLES -F
$IPTABLES -X
#DEFINE DEFAULT ACTION
$IPTABLES -P INPUT DROP
$IPTABLES -P OUTPUT DROP
#DEFINE INPUT CHAINS
$IPTABLES -A INPUT -p icmp --icmp-type any -j ACCEPT
$IPTABLES -A INPUT -s localhost -d localhost -j ACCEPT
$IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A INPUT -p tcp --dport $SSH_PORT -j ACCEPT
#DEFINE OUTPUT CHAINS
$IPTABLES -A OUTPUT -p icmp --icmp any -j ACCEPT
$IPTABLES -A OUTPUT -s localhost -d localhost -j ACCEPT
$IPTABLES -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A OUTPUT -p tcp -m state --state NEW --dport $HTTP_PORT -j ACCEPT
$IPTABLES -A OUTPUT -p tcp --dport $SECURE_HTTP_PORT -j ACCEPT
$IPTABLES -A OUTPUT -p udp --dport $DNS_PORT -j ACCEPT
$IPTABLES -A OUTPUT -p tcp --dport $SSH_PORT -j ACCEPT