7.2 ipmon
ipfstat对收集在系统上发生的事情很有用,但是它还不能够方便且及时的查看日志。ipmon是一个工具,它有能力查看包过滤的日志(关键字 log产生的日志),状态日志,或者是nat日志以及由它们三者共同产生的日志。这个工具可以在前台运行也可以在后台运行(将日志传给syslogd或者一个文件)。如果我们想看状态表的当前情况,可以运行ipmon -o S
01/08/1999 15:58:57.836053 STATE:NEW 100.100.100.1,53 -> 20.20.20.15,53 PR udp
01/08/1999 15:58:58.030815 STATE:NEW 20.20.20.15,123 -> 128.167.1.69,123 PR udp
01/08/1999 15:59:18.032174 STATE:NEW 20.20.20.15,123 -> 128.173.14.71,123 PR udp
01/08/1999 15:59:24.570107 STATE:EXPIRE 100.100.100.1,53 -> 20.20.20.15,53 PR udp Pkts 4 Bytes 356
01/08/1999 16:03:51.754867 STATE:NEW 20.20.20.13,1019 -> 100.100.100.10,22 PR tcp
01/08/1999 16:04:03.070127 STATE:EXPIRE 20.20.20.13,1019 -> 100.100.100.10,22 PR tcp Pkts 63 Bytes 4604
我们可以看到有一条外部机器向我们的dnssever发送dns请求的状态条,两条xntp ping到时间服务器,一条短暂的向外的ssh连接。
ipmon也可以显示哪些数据包被日志记录了。例如
15:57:33.803147 ppp0 @0:2 b 100.100.100.103,443 -> 20.20.20.10,4923 PR tcp len 20 1488 -A
它们的含义分别是时间戳 接口 规则 阻止 源地址,端口 -> 目的地址,端口 协议 tcp 包长度 20 1488 ACK
最后我们看一下NAT表
01/08/1999 05:30:02.466114 @2 NAT:RDR 20.20.20.253,113 <- -> 20.20.20.253,113 [100.100.100.13,45816]
01/08/1999 05:30:31.990037 @2 NAT:EXPIRE 20.20.20.253,113 <- -> 20.20.20.253,113 [100.100.100.13,45816] Pkts 10 Bytes 455
这是一条重定向的规则,用于在我们的防火墙后面提供规则的场合。
8.ipfilter的特殊用法
8.1 基于服务器和标志的deep state
保存状态很有用,但是很容易犯错。通常,你在第一条与数据包进行交互的规则中加入keep state,一个普遍的错误是混合了状态跟踪和基于标志的过滤:
pass in quick proto tcp from any to 20.20.20.20/32 port = 23 flags S
pass out all keep state
这几条规则的本意是允许客户机与服务器20.20.20.20进行telnet连接,如果你在试验这规则你会发现它能够马上起作用,由于我们是对SYN进行匹配的,状态条是不能够做到4/4的(不完整状态),这个状态默认的超时是60秒。
我们可以重写规则来解决这个问题:
1)
block in all
pass in quick proto tcp from any to 20.20.20.20/32 port = 23 keep state
block out all
或者
2)
block in all
pass in quick proto tcp from any to 20.20.20.20/32 port = 23 flags S keep state
pass out all keep state
这两组规则的任何一组都可以为每个连接建立完整的状态条。
8.2 解决ftp
ftp有两种不同的传输模式,防火墙管理员不得不解决ftp的这些问题。更糟的是ftp客户机和服务器的问题是不一样的。
ftp协议里面有两种数据传输模式,主动和被动。主动传输是服务器向客户机的一个开放端口进行连接然后传数据。相反地,被动传输是客户机向服务器发起连接并接受数据。
8.2.1 运行ftp服务
在运行ftp服务中,处理主动ftp的连接是比较简单的。而处理被动ftp则是一个大问题。首先我们先讨论怎样处理主动ftp,然后是被动ftp。通常主动ftp就像一个进入的http或者smtp连接:我们只要打开ftp端口并keep state就可以了:
pass out proto tcp all keep state
这两条规则允许主动ftp连接,这是最常见的类型。
下面是处理被动ftp连接。web浏览器默认是这种情况,它变得越来越流行,因此我们必须支持它。被动ftp的问题是针对每个被动连接,ftp服务器必须重开一个新端口(通常是1023以上)。这就像是在服务器上提供一个新的服务。如果我们的防火墙默认策略是禁止的,那么这个新的服务将被阻止,ftp会话就中断了。但是我们还有别的办法可以解决这个问题。
也许有人会打开所有1023以上的端口来解决这个问题。事实上,这个方法确实能够解决问题,尽管它令人不太满意:
pass out proto tcp all keep state
打开所有1023以上的端口,我们将面临一些潜在的危险。端口1-1023是提供给服务进程使用的,众多的程序决定使用1023以上的端口,比如nfsd和X
幸运的是ftp服务进程可以自己决定哪个端口分配给被动连接。这意味着你可以仅仅打开15001-19999端口作为ftp的端口,而不是打开所有 1023以上的端口。在wu-ftpd里面,可以在ftpaccess中指定被动端口。请参考ftpaccess的手册。ipfilter所需要做的仅仅是建立相应的规则:
pass out proto tcp all keep state
如果这样还不能使你满意的话,你只好修改ipf来支持ftp或者修改ftp来支持ipf。
8.2.2 运行ftp客户程序
尽管ipf对ftp服务的支持不甚完美,但是从3.3.3以后的版本对ftp客户软件的支持已经比较完善。就像ftp服务一样,ftp客户应用也有两个传输模式:主动和被动。
从防火墙的观点来看,ftp最简单的传输模式是被动模式。假设你对所有外出的tcp连接keep state,被动传输就可以正常工作了。如果你还没有这样做的话可以使用下面的规则:
主动模式有一点麻烦,主动模式使服务器打开第二个连接与客户机进行数据交换。当它们之间有防火墙时就会出现问题。为了解决这个问题,ipfilter包含了一个ipnat代理,这个代理临时在防火墙上打开一个洞,使得ftp服务器可以顺利的连接客户机。甚至是你没有使用ipnat来做地址转换,这个代理也是有效的。下面这条规则加入ipnat的配置文件当中(ep0是外网接口):
关于ipfilter内部代理的细节,请看3.6
8.3 内核参数
有一些内核参数是建立ipf所必需的,还有一些让我们可以方便的了解建立防火墙的信息。其中主要的一项是打开ip转发,否则ipf几乎什么也做不了,因为ip栈不会真的路由数据包。
ip转发:
net.inet.ip.forwarding=1
freebsd:
net.inet.ip.forwarding=1
netbsd:
net.inet.ip.forwarding=1
solaris:
ndd -set /dev/ip ip_forwarding 1
端口调整:
net.inet.ip.portfirst = 25000
freebsd:
net.inet.ip.portrange.first = 25000 net.inet.ip.portrange.last = 49151
netbsd:
net.inet.ip.anonportmin = 25000 net.inet.ip.anonportmax = 49151
solaris:
ndd -set /dev/tcp tcp_smallest_anon_port 25000
ndd -set /dev/tcp tcp_largest_anon_port 65535
其它有用的参数:
net.inet.ip.sourceroute = 0
net.inet.ip.directed-broadcast = 0
openbsd:
net.inet.ip.sourceroute = 0
net.inet.ip.directed-broadcast = 0
freebsd:
net.inet.ip.sourceroute=0
net.ip.accept_sourceroute=0
netbsd:
net.inet.ip.allowsrcrt=0
net.inet.ip.forwsrcrt=0
net.inet.ip.directed-broadcast=0
net.inet.ip.redirect=0
solaris:
ndd -set /dev/ip ip_forward_directed_broadcasts 0
ndd -set /dev/ip ip_forward_src_routed 0
ndd -set /dev/ip ip_respond_to_echo_broadcast 0
另外,freebsd有一些sysctl变量:
net.inet.ipf.fr_pass: 514
net.inet.ipf.fr_active: 0
net.inet.ipf.fr_tcpidletimeout: 864000
net.inet.ipf.fr_tcpclosewait: 60
net.inet.ipf.fr_tcplastack: 20
net.inet.ipf.fr_tcptimeout: 120
net.inet.ipf.fr_tcpclosed: 1
net.inet.ipf.fr_udptimeout: 120
net.inet.ipf.fr_icmptimeout: 120
net.inet.ipf.fr_defnatage: 1200
net.inet.ipf.fr_ipfrttl: 120
net.inet.ipf.ipl_unreach: 13
net.inet.ipf.ipl_inited: 1
net.inet.ipf.fr_authsize: 32
net.inet.ipf.fr_authused: 0
net.inet.ipf.fr_defaultauthage: 600
9. 有趣的ipf
这一部分不是教你ipf的一些新东西,但是可能提到一些你没有考虑到的,或者引发你发明一些我们没有考虑过的东西。
9.1 内网过滤
很久以前,有个叫Wietse Venema创建了一个tcp-wrapper包,它被用来保护网络服务,这很好,但是tcp-wrappers也有缺点。首先,就像它名字所说的,它只能保护tcp服务。另外,你必须用inetd来运行服务,而且你还要与libwrap一起编译,并且服务进程跟tcp-wrapper联系起来才能保护你的服务进程。这样系统就留下了一些巨大的安全漏洞,我们可以在本机上使用ipf堵上这些漏洞。例如我的电脑需要经常接入不太可靠的网络,我可以使用以下规则集:
pass out quick on lo0 all
block in log all
block out all
pass in quick proto tcp from any to any port = 113 flags S keep state
pass in quick proto tcp from any to any port = 22 flags S keep state
pass in quick proto tcp from any port = 20 to any port 39999 >< 45000 flags S keep state
pass out quick proto icmp from any to any keep state
pass out quick proto tcp/udp from any to any keep state keep frags
这个规则集对你的本地网来说已经很安全了,如果你想让你的防火墙更安全,你可以使用ftp的应用代理,而且你可以加入防止欺骗的规则集。如果你的机器有很多个用户而且你不想让他们启动一个不属于他们的服务的话,这个规则集是比较合适的。这个规则集不能阻止一个拥有root权限的cracker修改你的 ipf规则或者启动一个服务进程,它可以使你在一个复杂的局域网内更安全,我觉得这一点是很重要的。在一个规则集中加入本地过滤的规则通常可以解决很多性能问题。
9.2 什么是防火墙?透明过滤器
建立一个防火墙必须保证防火墙本身的完整性。有人可以突破你的防火墙并改变防火墙规则吗?这是管理员们必须面对的一个问题。
很多网络管理员对普通的以太网桥很熟悉,它是用来将两个独立的以太网连接成一个以太网的设备。通常是用来连接两栋建筑物,转换网络速度或者扩展网络有效的连接距离。集线器和交换器是普通的网桥,有时它们只有两个接口,我们称之为中继器。现在的Linux,OpenBSD,NetBSD,FreeBSD都可以将一台pc机当成网桥使用。网桥有一个共同的特点是连接两台机器,而这两台机器并不知道网桥的存在。
以太网桥作用于ISO协议栈的第二层,ip栈作用于第三层。IP Filter主要是作用于第三层,还涉及到第二层的网络接口。OpenBSD的网桥设备配合IP filter可以建立一个不可见的防火墙。这个系统不需要IP地址,它甚至不用暴露它的以太网地址(MAC)仅仅是使数据包的延迟稍微提高。
建立这样的规则惊人的简单。在OpenBSD里面,第一个网桥设备名是bridbe0.假设我们的机器上有两块网卡,xl0和xl1.为了使这台机器成为一个网桥,我们所要做的仅仅是输入下面三条命令:
ifconfig xl0 up
ifconfig xl1 up
所有到达xl0的数据包都被送到xl1,所有到达xl1的数据包都被送到xl0。你会注意到两个网卡都没有分配IP地址,也没有必要。最好是不分配地址。
尽管有一个bridge0接口,我们并不是基于这个接口进行过滤的,从一个简单的例子开始,假设我们的网络是这样的:
在20.20.20.1上有一个路由器,所有20.20.20.0/24外出的数据包都是通过20.20.20.1。现在我们加入一个ipf网桥:
我们在ipf桥上设置了这样几条规则:
pass out quick all
这个网络的功能没有改变,现在我们改变一下规则:
pass in quick all
pass out quick all
20.20.20.1和20.20.20.0/24还是认为网络没有改变,只是如果20.20.20.1 ping 20.20.20.2时,它将接受不到回复。事实上20.20.20.2根本就接受不到ping。ipfilter在中间阻止了这个数据包(ping)到达另一端。我们可以在任何地方设置一个ipf网桥。这种方法可以将一个网络缩小到一台主机,只要有足够多的网卡。
阻止来自外网的icmp看起来有点愚蠢,特别的如果你是一个系统管理员,你想ping外网,traceroute,或者改变MTU的大小。让我们建立一个更好的规则集,利用ipf的主要特点:状态检查。
pass in quick on xl1 proto udp keep state
pass in quick on xl1 proto icmp keep state
block in quick on xl0
这种情况下,20.20.20.0/24(也许称为xl1网络会比较好)能够到达外部网,而外网无法进入20.20.20.0/24,甚至是路由器不安全的时候,防火墙还是能够起作用。
现在我们仅仅是基于接口和协议进行过滤。我们还可以基于ip地址进行过滤。通常我们会提供一些服务,因此我们的规则集看起来会是这样:
pass in quick on xl1 proto udp keep state
pass in quick on xl1 proto icmp keep state
block in quick on xl1 # nuh-uh, we're only passing tcp/udp/icmp sir.
pass in quick on xl0 proto udp from any to 20.20.20.2/32 port=53 keep state
pass in quick on xl0 proto tcp from any to 20.20.20.2/32 port=53 flags S keep state
pass in quick on xl0 proto tcp from any to 20.20.20.3/32 port=25 flags S keep state
pass in quick on xl0 proto tcp from any to 20.20.20.7/32 port=80 flags S keep state
block in quick on xl0
现在我们有这么一个网络,20.20.20.2是一个域名服务器,20.20.20.3是一个邮件服务器,20.20.20.7是一个web服务器。
我们必须承认,ipfilter网桥还很不完善。
首先,所有的规则都只有in方向而没有out方向。这是因为OpenBSD网桥的out还没有实现。当初这样设计的本意是不因为使用多个接口而使性能大幅降低。现在性能问题已经得到改善,但是out仍然没有实现。如果你有需要,你可以手动修改代码或者是寻求其他OpenBSD使用者的帮忙。
其次,在ipf网桥中使用NAT是很不明智的。因为这将暴露ipf网桥的存在,而且也没有ip地址可以用来映射。当然你可以给接口设置ip地址使得NAT可以正常工作,但是网桥的优点就会降低。
9.2.1 利用透明过滤修复网络设计失误
很多机构认为建立一个子网一个防火墙是一个好主意。他们拥有c类地址甚至更大地址空间的网络,这个网络包含了所有服务,包括他们的服务器,工作站,路由器。这是很糟糕的!重新调整合适的子网,信任级别,过滤器等等将要付出大量的人力和财力。昂贵的付出通常使大部分机构不想去解决这个问题,这还不包括停工期。这个问题看起来是这样的:
20.20.20.2 unix server 20.20.20.7 nt workstation
20.20.20.3 unix server 20.20.20.8 nt server
20.20.20.4 win98 workstation 20.20.20.9 unix workstation
20.20.20.5 intelligent switch 20.20.20.10 win95 workstation
实际情况可能复杂得多。比较合适的做法是将所有可信任的服务器归为一个子网,其他工作站归为另外一个子网,剩下的就是交换机在同一个子网。现在路由器可以过滤所有子网,限制工作站访问服务器。现在这个C类网络看起来就比较一致了。
首先我们将路由器,工作站,服务器分开。为了达到这个目的,我们需要两个集线器(或者交换机),一台三个网卡的ipf机器.我们将所有的服务器接在一个集线器上,所有的工作站接另一个集线器,通常我们将两个集线器连起来,然后再接到路由器。这里我们将路由器接到ipf的xl0接口,服务器组接到xl1,工作站接到xl2,我们的网络图看起来是这样的:
| 20.20.20.2 unix server
router (20.20.20.1) _____| 20.20.20.3 unix server
| / | 20.20.20.6 unix server
| /xl1 | 20.20.20.7 nt server
-----/xl0 IPF Bridge <
\xl2 | 20.20.20.4 win98 workstation
\______| 20.20.20.8 nt workstation
| 20.20.20.9 unix workstation
| 20.20.20.10 win95 workstation
为了充分的利用filter网桥的优点,我们添加了一些规则:
pass in quick on xl0 proto udp from any to 20.20.20.2/32 port=53 keep state
pass in quick on xl0 proto tcp from any to 20.20.20.2/32 port=53 flags S keep state
pass in quick on xl0 proto tcp from any to 20.20.20.3/32 port=25 flags S keep state
pass in quick on xl0 proto tcp from any to 20.20.20.7/32 port=80 flags S keep state
block in quick on xl0
pass in quick on xl1 proto tcp keep state
pass in quick on xl1 proto udp keep state
pass in quick on xl1 proto icmp keep state
block in quick on xl1 # nuh-uh, we're only passing tcp/udp/icmp sir.
pass in quick on xl2 proto tcp keep state
pass in quick on xl2 proto udp keep state
pass in quick on xl2 proto icmp keep state
block in quick on xl2 # nuh-uh, we're only passing tcp/udp/icmp sir.
这样来自路由器目的地址是服务器的数据包将受到限制,而服务器跟工作站可以自由通信。如果你不想让你的工作站访问服务器可以作些改动:
pass in quick on xl2 proto udp keep state
pass in quick on xl2 proto icmp keep state
block in quick on xl2 # nuh-uh, we're only passing tcp/udp/icmp sir.
改为:
pass in quick on xl2 proto tcp keep state
pass in quick on xl2 proto udp keep state
pass in quick on xl2 proto icmp keep state
block in quick on xl2 # nuh-uh, we're only passing tcp/udp/icmp sir.
或许你想使工作站只可以通过IMAP收发邮件。这也很简单:
pass in quick on xl2 proto tcp from any to 20.20.20.3/32 port=143
block in quick on xl2 from any to 20.20.20.0/24
pass in quick on xl2 proto tcp keep state
pass in quick on xl2 proto udp keep state
pass in quick on xl2 proto icmp keep state
block in quick on xl2 # nuh-uh, we're only passing tcp/udp/icmp sir.
9.3 Drop-Safe Logging With dup-to and to.
前面我们介绍的是如何阻止数据包,现在我们要做的不是阻止数据包,而是将数据包转发到另外一个系统,这个系统在log这个数据包之前对这个数据包进行处理。我们的防火墙系统无论是作为网桥还是路由器都可以有很多个网卡,因此我们可以创建一个drop-safe系统,一个典型的例子是建立一个入侵检测系统。首先我们要隐藏入侵检测系统,使它不被检测到。
在开始之前,我们需要注意一些操作特点,如果我们仅仅是处理被阻止的数据包,我们可以使用关键字to或者fastroute.(我们将解释它们之间的差别)如果我们想要通过一个数据包,我们必须复制这个数据包。
9.3.1 The dup-to Method
假如我们要发送每个数据包的拷贝离开xl3接口前往ed0上的drop-safe网络我们可以使用这条规则:
你也可以直接指明发送到drop-safe网络的一个地址,而不是仅仅发送一个拷贝到那个网络。稍微改变一下规则:
值得注意的是,这个方法改变了副本的目的地址,日志也跟着改变。由于这个原因,我们建议使用我们熟悉的地址作为日志的地址,根据这个地址对应不同的日志 (例如你不应该只使用192.168.254.2来记录web服务器和mail服务器的日志),即使你的系统中没有这个地址。通常ipfilter会用 arp来查找新的目的地址,但是我们可以在ipfilter系统上创建静态的arp记录来避免这个问题。
9.3.2 The to Method
dup-to有一个明显的缺点,因为它为每个数据包创建一个副本,并随意的改变目的地址,这需要一些时间来做这个工作,然后才能处理另外一个数据包。 如果我们不关心通过的数据包,只是阻止数据包的通过,我们可以使用关键字to,让数据包通过路由表,然后送到一个不同的接口。
就像fastroute一样,如果将block换成pass,系统很可能发生混乱。
10.防止伪装的最终规则
由于各种原因,IANA保留了一些地址空间,到这片文档定稿之时这些地址还没有被使用。因为这些地址空间还没有分配出去,如果这些地址出现在源地址或者目的地址是不合法的。
因此,不用再考虑了,这是完整的伪造地址表:
# s/OUTSIDE/outside-interface (eg: fxp0)
# s/MYNET/network-cidr-address (eg: 1.2.3.0/24)
#
block in on OUTSIDE all
block in quick on OUTSIDE from 0.0.0.0/7 to any
block in quick on OUTSIDE from 2.0.0.0/8 to any
block in quick on OUTSIDE from 5.0.0.0/8 to any
block in quick on OUTSIDE from 10.0.0.0/8 to any
block in quick on OUTSIDE from 23.0.0.0/8 to any
block in quick on OUTSIDE from 27.0.0.0/8 to any
block in quick on OUTSIDE from 31.0.0.0/8 to any
block in quick on OUTSIDE from 67.0.0.0/8 to any
block in quick on OUTSIDE from 68.0.0.0/6 to any
block in quick on OUTSIDE from 72.0.0.0/5 to any
block in quick on OUTSIDE from 80.0.0.0/4 to any
block in quick on OUTSIDE from 96.0.0.0/3 to any
block in quick on OUTSIDE from 127.0.0.0/8 to any
block in quick on OUTSIDE from 128.0.0.0/16 to any
block in quick on OUTSIDE from 128.66.0.0/16 to any
block in quick on OUTSIDE from 169.254.0.0/16 to any
block in quick on OUTSIDE from 172.16.0.0/12 to any
block in quick on OUTSIDE from 191.255.0.0/16 to any
block in quick on OUTSIDE from 192.0.0.0/16 to any
block in quick on OUTSIDE from 192.168.0.0/16 to any
block in quick on OUTSIDE from 197.0.0.0/8 to any
block in quick on OUTSIDE from 201.0.0.0/8 to any
block in quick on OUTSIDE from 204.152.64.0/23 to any
block in quick on OUTSIDE from 224.0.0.0/3 to any
block in quick on OUTSIDE from MYNET to any
# Your pass rules come here...
block out on OUTSIDE all
block out quick on OUTSIDE from !MYNET to any
block out quick on OUTSIDE from MYNET to 0.0.0.0/7
block out quick on OUTSIDE from MYNET to 2.0.0.0/8
block out quick on OUTSIDE from MYNET to 5.0.0.0/8
block out quick on OUTSIDE from MYNET to 10.0.0.0/8
block out quick on OUTSIDE from MYNET to 23.0.0.0/8
block out quick on OUTSIDE from MYNET to 27.0.0.0/8
block out quick on OUTSIDE from MYNET to 31.0.0.0/8
block out quick on OUTSIDE from MYNET to 67.0.0.0/8
block out quick on OUTSIDE from MYNET to 68.0.0.0/6
block out quick on OUTSIDE from MYNET to 72.0.0.0/5
block out quick on OUTSIDE from MYNET to 80.0.0.0/4
block out quick on OUTSIDE from MYNET to 96.0.0.0/3
block out quick on OUTSIDE from MYNET to 127.0.0.0/8
block out quick on OUTSIDE from MYNET to 128.0.0.0/16
block out quick on OUTSIDE from MYNET to 128.66.0.0/16
block out quick on OUTSIDE from MYNET to 169.254.0.0/16
block out quick on OUTSIDE from MYNET to 172.16.0.0/12
block out quick on OUTSIDE from MYNET to 191.255.0.0/16
block out quick on OUTSIDE from MYNET to 192.0.0.0/16
block out quick on OUTSIDE from MYNET to 192.168.0.0/16
block out quick on OUTSIDE from MYNET to 197.0.0.0/8
block out quick on OUTSIDE from MYNET to 201.0.0.0/8
block out quick on OUTSIDE from MYNET to 204.152.64.0/23
block out quick on OUTSIDE from MYNET to 224.0.0.0/3
# Your pass rules come here...
如果你想要使用这些规则,我们建议你先查看一下whois.arin.net,因为IANA有可能将其中的一些地址分配出去。
感谢Frank DiGennaro
(http://www.fanqiang.com)








