3. 高级防火墙介绍 这部分文档跟前面的基础文档联系紧密,不仅包含着高级防火墙的观念,还包含着一些ipfilter特性,一旦你熟悉这部分,你将能够建立一个非常健壮的防火墙。 3.1 偏执狂:默认拒绝的规则 根据端口来阻止服务存在一个很大的问题:有时候端口是可以改变的,基于rpc的程序因为端口变化会变得很糟糕的,lockd,statd,甚至是nfsd侦听的端口不是2049。这种情况是很难预料的,更糟糕的是不大可能总是自适应这个变化。我们现在建立一个新的防火墙规则,我们将用的第一条规则是: 3.2 状态规则 防火墙的作用是阻止从A到B的数据包,假设我们有这样一条规则:只要是到达端口23的数据包就可以通过。同样的只要一个数据包有FIN标志就可以通过,我们上面的防火墙并不知道TCP/UDP/ICMP会话的开始段,中间段,结束段。它只是针对所有包的规则。我们希望到达端口23的数据包没有被窃取、改动,就是说我们希望有一个方法将一个正常的TCP/UDP/ICMP数据包跟端口扫描或者是DOS攻击区分开来。这种方法就是所说的keeping state(保持连接状态)。
IP Filter Based Firewalls HOWTO
2.12 tcp,udp端口;关键字"port"
我们已经基于协议对数据包进行过滤,我们还可以基于协议的具体方面进行过滤。最有用的方面是端口号,rsh,rlogin,te.net服务通常是很有用的,但是由于网络嗅探和欺骗攻击,这些服务隐含这一些不安全的因素,一个很重要的妥协方法是只在内部网运行这些服务,阻止外网的访问。这很容易做到,因为rsh,rlogin,telnet使用专用的端口号(514,513,23)。容易建立规则:
block in log quick on tun0 proto tcp from any to 20.20.20.0/24 port = 513
block in log quick on tun0 proto tcp from any to 20.20.20.0/24 port = 514
block in log quick on tun0 proto tcp from any to 20.20.20.0/24 port = 23
这三条规则必须在pass in all之前,这样它们将跟外网隔开(为了简便省略防止欺骗的规则)
pass in quick on tun0 proto icmp from any to 20.20.20.0/24 icmp-type 0
pass in quick on tun0 proto icmp from any to 20.20.20.0/24 icmp-type 11
block in log quick on tun0 proto icmp from any to any
block in log quick on tun0 proto tcp from any to 20.20.20.0/24 port = 513
block in log quick on tun0 proto tcp from any to 20.20.20.0/24 port = 514
block in log quick on tun0 proto tcp from any to 20.20.20.0/24 port = 23
pass in all
你也许想阻止514/udp(syslog),111/tcp&111/udp(portmap),515/tcp(lpd),2049/tcp&udp(nfs),6000/tcp(X11)等等。你可以获得完整的系统正在侦听端口列表,netstat -a(或者lsof -i,如果你已经安装)。
如果要阻止udp,只要将tcp换成udp,下面是针对syslog的
block in log quick on tun0 proto udp from any to 20.20.20.0/24 port = 514
ipf对于同时处理tcp和udp有一个简单的方法,针对portmap:
block in log quick on tun0 proto tcp/udp from any to 20.20.20.0/24 port = 111
block in all
所有的数据报都无法通过。没有什么用处但是很安全。假设你的服务器只跑一个web服务,没有其它的。甚至没有dns查询,只开放80端口,我们可以加入另外一条规则来实现:
block in on tun0 all
pass in quick on tun0 proto tcp from any to 20.20.20.1/32 port = 80
第二条规则将使连接20.20.20.1端口80的数据包通过,其它的数据包则无法通过。作为基本的防火墙,这就够了。
我们想要做到既方便又安全,很多人已经这么做了,像Ciscos有个established条款,这条款允许已经建立连接的tcp的数据包通过。ipfw也有established,ipfwadm有setup/established(译者注:用于建立连接的标志位/已建立连接的标志位),它们都有这个特点,但是它们的名字容易使人误导。当我们第一次看见的时候,我们会认为我们的包过滤器会追踪一个数据包想要做什么,过滤器会知道一个连接是否已经建立。事实上,它们都是根据数据包的部分例如TCP数据包的标志位来判断一个数据包想要做什么,但是udp/icmp没有用于判断的标志位,无法实现这个功能。如果是这样的话,任何人都可以伪造这些标志位,并使一个数据包通过防火墙。
IPF在这些问题上是怎么处理的呢?ipf不像其它的防火墙,它可以真正的追踪每个数据包并判断一个连接是否已经建立。而且ipf可以处理tcp,udp,icmp,不仅仅是tcp。Ipf称之为keep state(保持状态),规则的关键字就是keep state。
现在你已经知道防火墙检查每一个进入的数据包,同样检查每个离开的数据包。事实上,是状态表检查每个进入或者离开的数据包。状态表是整个防火墙规则中可以通过的TCP/UPD/ICMP会话(连接)表。这会不会是一个严重的安全漏洞呢,事实上这是防火墙中最好的。
所有的tcp/ip会话都有开始,中间及结束(尽管有时候这三个阶段都在同一个包里),只有结束阶段而没有中间阶段,或者只有中间阶段而没有开始阶段的会话(正常会话)是不可能的。这就是说,你所要做的就是过滤tcp/udp/icmp会话的开始阶段。如果一个会话的开始阶段允许通过,那么它的中间阶段和结束阶段也可以通过(除非ip栈溢出或者机器当机)。Keeping state允许你忽略中间阶段和结束阶段,而仅仅是集中在阻止或者通过一个新的会话。如果一个新的会话通过,那么它所有的后续数据包都被允许通过。这是一个跑ssh服务的例子(仅仅是ssh):
block out quick on tun0 all
pass in quick on tun0 proto tcp from any to 20.20.20.1/32 port = 22 keep state
你会注意到没有pass out规则。尽管如此,这个规则是完整的。这是keeping state的缘故。一旦一个含有SYN(连接的初始阶段)的数据包到达ssh服务器,会话状态被建立并保存(译者注:这样数据包就可以自由进出)。这是另外一个例子:
block in quick on tun0 all
pass out quick on tun0 proto tcp from 20.20.20.1/32 to any keep state
这种情况下,服务器不跑服务。事实上这不是一台服务器,而是客户机。而且这台客户机不想让未经过认证的包进入ip栈。然而客户机需要访问因特网,同时让回复的包通过。这个简单的规则为每个向外的tcp连接建立状态表。当这个状态表建立以后,这个tcp会话就会畅通无阻,而且没有防火墙规则集的检查(译者注:由状态表检查)。udp和icmp同样适用:
block in quick on tun0 all
pass out quick on tun0 proto tcp from 20.20.20.1/32 to any keep state
pass out quick on tun0 proto udp from 20.20.20.1/32 to any keep state
pass out quick on tun0 proto icmp from 20.20.20.1/32 to any keep state
这样就可以使用ping了,我们已经为tcp,udp,icmp建立连接状态(译者注:严格来讲udp,icmp会话没有建立连接状态,只是防火墙保存了连接状态)。现在我们可以向外连接了,攻击者却无法进入。这是非常方便的,因为我们不必跟踪机器在监听哪些端口,而仅仅是跟踪那些我们想让别人能够连接的端口。
state是非常方便的,但是需要一些机巧。在一些奇怪的令人迷惑的用法上,你有可能“搬石头砸自己的脚”。考虑一下下面的规则:
pass in quick on tun0 proto tcp from any to 20.20.20.1/32 port = 23
pass out quick on tun0 proto tcp from any to any keep state
block in quick all
block out quick all
这看起来像是一个好的防火墙规则,我们允许进入并通过防火墙连接23端口,而且允许所有对外的连接。无疑的,所有连接端口23的数据包都将得到回复,防火墙pass out规则将建立一条状态记录,所有的事情将会很完美,至少我们是这样想的。
不幸的是,60秒的空闲时间以后这条状态记录将会关闭(而不是5天),这是因为这条状态记录无法看到连接端口23的SYN数据包,它只看到SYN ACK(SYN的回复)。IPF很适合于跟踪从开始到结束的tcp连接,但是不擅长对连接的中间进行跟踪,所以应该像这样重写防火墙规则:
pass in quick on tun0 proto tcp from any to 20.20.20.1/32 port = 23 keep state
pass out quick on tun0 proto tcp from any to any keep state
block in quick all
block out quick all
新加上的那条规则将会对每个syn数据包建立状态记录,而且跟我们期望的一样工作得很好,一旦状态引擎发现了一个连接3次握手已经完成,这个连接就会被表记为4/4模式,这种模式意味着一个长时间数据交换的连接已经建立直到这个连接被拆除(ipfstat -s可以看到连接的模式)。