以上的说明只是man ipfw中的一小部份。如果你想要对ipfw更了解,例如如何使用ipfw来限制频宽等,建议你man ipfw。

不知道您看了这么多的规则是否觉得眼花撩乱,如果不了解TCP/IP的原理,彻底了解ipfw的设定还真不容易。没关系,我们下面将举几个简单、常用的设定,这些范例应该够平常使用了。

范例

我将原本的/etc/rc.firewall备份成rc.firewal.old,并将它改成下列内容,请注意,这里只是范例,只供参考:

# 设定我的 IP

myip="1.2.3.4"

# 设定对外的网络卡代号

outif="vr0"

# 设定对内的网络上代号

inif="vr1"

#清除所有的规则

/sbin/ipfw -f flush

# 丢弃RFC 1918 networks

${ipfw} add deny ip from 10.0.0.0/8 to any invia ${outif}

${ipfw} add deny ip from 172.16.0.0/12 to anyin via ${outif}

${ipfw} add deny ip from 192.168.0.0/16 to anyin via ${outif}

# 只允许内部网络对192.168.0.1使用telnet服务

/sbin/ipfw add 200 allow tcp from192.168.0.1/24 to 192.168.0.1 telnet

# 拒绝其它人连到port 23,并记录尝试联机的机器

/sbin/ipfw add 300 deny log tcp from any to me23

# 拒绝任何ICMP封包

/sbin/ipfw add 400 deny icmp from any to any

# 下面这台机器是坏人,不让它进来,并记录下来

/sbin/ipfw add 1100 deny log all from211.21.104.102 to any

# NAT的设定

/sbin/ipfw add divert natd all from any to anyvia vr0

# 限制内部网域对外下载最大频宽为20KBytes/s,上传最大频宽为5KBytes/s

ipfw pipe 20 config bw 20KBytes/s

ipfw add pipe 20 ip from any to 192.168.0.1/24out

ipfw pipe 30 config bw 5KBytes/s

ipfw add pipe 30 ip from 192.168.0.1/24 to anyin

# 允许本机对任何地方联机

/sbin/ipfw add check-state

/sbin/ipfw add 2000 allow udp from ${myip} toany keep-state

/sbin/ipfw add 2100 pass ip from ${myip} to any

# 允许外界使用邮件服务

/sbin/ipfw add 3000 pass tcp from any to${myip} 25 in via ${outif}

# 不允许内部的IP从外部连进来

/sbin/ipfw add 1200 add deny ip from ${myip}/24to any in via ${outif}

# 其它都拒绝,如果没有在kernel中设定

# optionsIPFIREWALL_DEFAULT_TO_ACCEPT则内定就有下列这一条

/sbin/ipfw add 65535 deny all from any to any

存盘后就可以使用sh rc.firewall来执行新的规则了。如果您将规则放在/etc/rc.firewall中,则开机时会自动执行。


一些小建议:

在建立一个封包过滤的防火墙时,应该尽可能阻挡一些不必要的服务。避免开放port1024以下的TCP服务,例如只通过SMTP封包(port 25)给邮件服务器;拒绝所有UDP联机(只有少部份服务如NFS会用到);一些只有内部才会使用的服务,如数据库等也不必对外开放。

另外,同样的防火墙限制可以使用不同的语法来展现,应该要试着让规则数量越少越好,以加快处理速度。

在更新firewall规则时,如果规则没有写好,而你又是以远程登入的方式修改规则,很可能会因此无法继续登入。因此建议更新规则时最好在console前执行,若迫不得已一定要使用远程登入,建议您执行/usr/share/examples/ipfw/change_rules.sh这支程序来编辑规则:

# cd /usr/share/examples/ipfw

# sh change_rules.sh

接着会出现文书编辑软件并自动加载/etc/rc.firewall让你编辑,结束离开后,会询问是否要执行更新。如果执行新的规则后造成断线,它会自动加载旧的规则,让我们可以再次联机。


封包过滤桥接器:

如果您有三台机器全部都有public IP,而您想使用其中一台做为防火墙,在不改变另外二台机器的设定下,我们可以使具封包过滤的桥接器来架设防火墙。只要将这台桥接器放在另外二台和对外网络之间即可。

另外,当我们的内部网络有不同class的主机时,例如内部有140.115.2.3及140.115.5.6这二台计算机时,就无法使用传统的防火墙。如果要在这二台机器连到因特网的途中使用防火墙,我们必须使用新的方式,也可以使用这里介绍的桥接器。

我们可以使用FreeBSD为桥接器,利用它来做封包过滤的动作,而丝毫不影响内部的主机原本的设定。为了达到这个功能,我们必需要有二张支持promiscuous mode的网络卡,现在的网络卡大部份都有支持。二张网络卡当中,一张需要设定IP,另一张不需要。至于您要将IP设定在哪一张卡都可以,建议是设在对外的网络卡上。

首先,我们必须在核心中加入关于桥接器的设定:

# 支援桥接器

options BRIDGE

# 防火墙设定

options IPFIREWALL

options IPFIREWALL_VERBOSE

# 我们这里将防火墙预设为接收所有封包

options IPFIREWALL_DEFAULT_TO_ACCEPT

如果您要让桥接器具有流量控制的功能,则可以加上之前提到的选项optionsDUMMYNET。重新编译核心后,在重开机前,我们先设定一下/etc/rc.conf:

firewall_enable="YES"

firewall_type="open"

还有一件事要做,当在以太网络上跑IP协议时,事实上使用二种以太网络协议,一个是IP,另一个是ARP。ARP协定是当机器要找出给定IP地址所对应的以太网络地址时使用的。ARP并不是IP层的一部份,只是给IP应用在以太网络上运作。标准的防火墙规则中并未加入对于ARP的支持,幸运的是,高手们的在ipfirewall程序代码中加入了对封包过滤桥接器的支持。如果我们在IP地址0.0.0.0上建立一个特别的UDP规则,UDP端口的号码将被使用来搭配被桥接封包的以太网络协议号码,如此一来,我们的桥接器就可以被设定成传递或拒绝非 IP的协议。请在/etc/rc.firewall中接近文件顶端处理lo0的那三行之下(就是有写Onlyin rare cases do you want to change these rules的地方)加入下面一行:

${fwcmd} add allow udp from 0.0.0.0 2054 to0.0.0.0

现在我们就可以重新开机了。重开机之后,先执行下列指令来启动桥接器:

如果您使用的是FreeBSD 4.x:

# sysctl -w net.link.ether.bridge_ipfw=1

# sysctl -w net.link.ether.bridge=1

如果您使用的是FreeBSD 5.x:

# sysctl -w net.link.ether.bridge.ipfw=1

# sysctl -w net.link.ether.bridge.enable=1

现在我们可以将机器放在内外二个网域之间了。因为我们之前在/etc/rc.conf中,设定防火墙完全打开,不阻挡任何封包,所以放在二个网域之间时,运作应该没有问题。我们之前只设了一张网络上的IP,而在执行了上述的指令之后,第二张网络卡便开始运作。

下一步就是将我们启动桥接器的指令放在/etc/rc.local中,让系统在开机时自动执行。或者,我们可以在/etc/sysctl.conf中加入下面二行:

# 如果您使用的是FreeBSD 4.x

net.link.ether.bridge_ipfw=1

net.link.ether.bridge=1

# 如果您使用的是FreeBSD 5.2以后的版本

net.link.ether.bridge.enable=1

net.link.ether.bridge.ipfw=1

接下来我们就可以依自己的需求在/etc/rc.firewall文件的最后面加上我们自己想要的防火墙规则了。以下是一个简单的设定规则,假设桥接器的IP是140.115.75.137,内部有二台主机,一台提供网页服务,一台是BBS:

us_ip=140.115.75.137

basrv_ip=140.115.3.4

bbs_ip=140.115.5.6

oif=fxp0

iif=fxp1

ipfw="/sbin/ipfw"

# 定义check-state

${ipfw} 1000 add check-state

# 丢弃RFC 1918 networks

${ipfw} 1100 add deny ip from 10.0.0.0/8 to anyin via ${oif}

${ipfw} 1200 add deny log ip from 172.16.0.0/12to any in via ${oif}

${ipfw} 1300 add deny log ip from 192.68.0.0/16to any in via ${oif}

# 允许桥接器本身所有想做的联机(keep state if UDP)

${ipfw} 1400 add pass udp from ${us_ip} to anykeep-state

${ipfw} 1500 add pass ip from ${us_ip} to any

# 允许内部网络任何想做的联机(keep state if UDP)

${ipfw} 1600 add pass udp from any to any invia ${iif} keep-state

${ipfw} 1700 add pass ip from any to any in via${iif}

# 允许任何的ICMP联机

${ipfw} 1800 add pass icmp from any to any

# 不允许使用port 888联到bbs

${ipfw} 2000 add deny log tcp from any to${bbs_ip} 888

# TCP部分

# 任何地方都可以建立TCP联机

${ipfw} 3000 add pass tcp from any to any via${oif}

# 允许“隔离”范围的端口连接

${ipfw} 3100 add pass tcp from any to any49152-65535 in via ${oif}

# 允许验证服务连接

${ipfw} 3200 add pass tcp from any to any 113in via ${oif}

# 允许ssh连接

${ipfw} 3300 add pass tcp from any to any 22 invia ${oif}

# 允许DNS连接,当内部网络有域名服务器时才需要

${ipfw} 3400 add pass tcp from any to any 53 invia ${oif}

# 只传递SMTP给邮件服务器

${ipfw} 3500 add pass tcp from any to ${bbs_ip}25 in via ${oif}

${ipfw} 3600 add pass tcp from any to${basrv_ip} 25 in via ${oif}

# UDP部分

# 允许“隔离”范围的端口连接

${ipfw} 4000 add pass udp from any to any49152-65535 in via ${oif}

# 允许DNS连接,当内部网络有名称服务器时才需要

${ipfw} 4100 add pass udp from any to any 53 invia ${oif}

# 其它的都拒绝

${ipfw} 60000 add deny ip from any to any