下载贤集网APP入驻自媒体
用IPIW实现BSD防火墙(中)
我们已经通过安装带缺省的禁止所有数据包出入的策略的ipfw,使所有的IP信息包都不能出入我的计算机系统,下面,我们再创建一个能被ipfw读取的规则集,使所需要的信息包能够出入计算机系统。
由于在创建规则集方面没有所谓“最合适”的方法,因此我不能说明如何在规则集中添加“万能”的规则,而只能说明一下在创建规则集时需要遵循的原则。在这里,我假设你已经掌握了ipfw的语法,能够理解我创建的规则。如果对这些知识不大理解,请参阅相关的资料。
在创建规则集时需要注意的是,规则是按给定数字行号的顺序被系统读取的,直到信息包符合一条规则,ipfw才会停止读取规则,也就是说,如果规则400和规则800都适用于一个信息包,系统总是会用到规则400而不会读取规则800。因此,在添加新的规则之前,需要仔细地审查原来的规则,确保新的规则不会被原来的规则所覆盖。
此外,规则对所有的连接-也就是在ifconfig -a的输出中所列出的所有连接都是适用的。如果在象我这样只有一个连接的计算机上自然不会有什么问题,但如果在有多个连接的计算机上,就会有所不同。例如,你的机器上可能有二个连接,一个是互联网连接,一个是内部局域网连接,每个不同的连接需要不同的安全规则,这一点可以通过在ipfw的规则中指定连接的名字来实现。
我的机器是一台运行FreeBSD 4.2、配置有互联网连接的单台计算机。由于这是我在家里使用的计算机,因此可以对向互联网上发送的信息包的类型不作任何限制,而只需要它能够接收是对我发出的信息包有效响应的信息包。
要完成这一任务最好的方法之一是利用ipfw的动态功能。如果你对这一概念还不太熟悉,下面我将对它作一番详尽的解释。
如果使用“动态”的规则,当我向互联网上发送一个信息包时,ipfw将在其状态表中添加一个记录,其中包括有发送的信息包的目标计算机的IP地址和使用的目标计算机的端口。当有信息包从互联网上返回时,如果其IP地址、端口号与在状态表中记录得不一致,计算机就不会接收这一信息包。动态规则只适用于TCP信息包,而不适用于UDP信息包,原因是UDP不创建一个虚拟的连接,它被称作是“无状态”协议,也就不能使用“状态表”。
ipfw手册中的例子部分给出了三条用来创建这个动态信息包过滤装置的规则。由于我决定在/etc/ipfw.rules中创建自己的规则,因此,需要以超级用户的身份创建包含下面内容的文件:
# 只允许向外发送信息包
add 00300 check-state
add 00301 deny tcp from any to any in established
add 00302 allow tcp from any to any out setup keep-state
由于规则100和规则200是预先包含在/etc/rc.firewall中的,因此,我自己添加的规则将从行号300开始。我将给相关的规则以300、301、302等行号,等规则越来越多或创建不相关的规则时,我就会把行号跳到400。不过,从理论上说你可以任意给规则指定行号,只要该行号没有在该规则集文件中出现过就行。
你可能已经注意到规则集出现了几个在ipfw手册中定义的关健字:
check-state:检查信息包是否与动态规则集匹配。如果匹配则搜索中止,否则继续搜索下一条规则。
keep-state:根据匹配情况,防火墙将创建一条动态规则,其功能是在源、目的IP地址/端口之间使用同一协议的流量,这一规则是具有一定的生命周期的(由一系列sysctl(8)变量控制),每当发现匹配协议时其生命周期都会刷新。
established:只适用于TCP信息包,与有RST或ACK位的信息包进行匹配。
setup:只适用于TCP信息包,与有SYN位但不具有ACK位的信息包进行匹配。
换句话说,当有信息包到达网络连接后,ipfw将首先检查它是否在状态表中,如果在状态表中,则允许它进入系统(行号为300的规则执行这一检查工作)。如果它不在状态表中,而且设置了RST或ACK位,ipfw将不允许它进入系统,因为它不是我创建的连接的有效响应(规则301完成这一工作)。只有当ACK标志没有设置,(这意味着它要初始化连接)并且这一信息包是由系统向外发送的时,才允许它向外发送;如果有信息包符合这一规则,则它被加入状态表中。
我们来看看添加上这些规则后对系统有什么影响。对规则集进行检查没有错误后,保存文件,然后输入下面的命令:
killall init
敲Enter键,然后输入:
exit
然后仔细观察启动信息,确保规则在加载时没有出现出错信息。如果在规则加载过程中出现了错误信息,那么可能是系统的安全级别被设置为3或更高了,必须首先在/etc/rc.conf文件中找到下面的这行内容,将kern_securelevel改为较小的值:
kern_securelevel="3"
然后重新执行killall init命令。
重新登录后,我将尝试能否向外发送IP数据包并收到相应的应答数据包:
ping www.freebsd.org
ping: cannot resolve www.freebsd.org: Host name lookup failure
lynx www.freebsd.org
Alert!. Unable to access document.
也许是我的系统上没有安装DNA域名解析功能的缘故,我再使用IP地址试一下:
lynx 216.136.204.21
这次我发现自己在
ping 216.136.204.21
PING 216.136.204.21 (216.136.204.21): 56 data bytes
ping: sendto: Permission denied
ping: sendto: Permission denied
ping: sendto: Permission denied
^C
--- 216.136.204.21 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss
这里我来解释一下这一奇怪的现象吧。很明显的是,一些数据包进入或发出了计算机系统,但一些则没有。我们来仔细发分析一下我在上面的每个例子中使用的协议。
由于我只能使用其IP地址访问
more /etc/resolv.conf
search kico1.on.home.com
nameserver 24.226.1.90
nameserver 24.226.1.20
nameserver 24.2.9.34
似乎问题不是出在这儿,因此应该仔细地搞清楚域名解析的工作原理。我们来看一下能否从在线手册上得到一点帮助:
apropos resolve
dnsquery(1) - 使用解析器查询域名服务器
res_query(3), res_search(3), res_mkquery(3), res_send(3), res_init(3), dn_comp(3), dn_expand(3) - 解析器例程
resolver(5) - 解析器配置文件
man resolver
通过多次查看,下面的内容引起了我的兴趣:RES_USEVC 在查询中使用TCP而不是UDP连接;RES_STAYOPEN RES_USEVC用它来在多次查询期间保持TCP连接。它只在需要进行多个查询的的软件中有用,UDP是最常用的模式。
我可能已经发现问题出在哪了。如果DNS使用的是UDP而不是TCP,而我的规则只允许TCP协议的数据包响应我的TCP连接,域名解析就会失败。
man dnsquery
<只显示我们感兴趣的部分>
-s 使用流格式而非信息包。它使用一个带名字服务器的TCP流式连接而不是UDP,这一选项会设置解析软件选项字段的RES_USEVC位。(缺省状态下使用UDP)
现在,我们来试试这个选项:
dnsquery -s www.freebsd.org
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39772
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 5, ADDITIONAL: 5
;; www.freebsd.org, type = ANY, class = IN
www.freebsd.org. 49m21s IN CNAME freefall.freebsd.org.
freebsd.org. 22m43s IN NS ns1.iafrica.com.
freebsd.org. 22m43s IN NS ns2.iafrica.com.
freebsd.org. 22m43s IN NS ns.gnome.co.uk.
freebsd.org. 22m43s IN NS ns0.freebsd.org.
freebsd.org. 22m43s IN NS ns1.root.com.
ns1.iafrica.com. 1h1m3s IN A 196.7.0.139
ns2.iafrica.com. 1h1m3s IN A 196.7.142.133
ns.gnome.co.uk. 12m37s IN A 193.243.228.142
ns0.freebsd.org. 11h9m9s IN A 216.136.204.126
ns1.root.com. 1h8m12s IN A 209.102.106.178