14.9. 防火墙

作者 Gary Palmer and Alex Nash.

对于接入 Internet 的人来说, 防火墙是一个越来越让人感兴趣的领域。 人们甚至在设法寻找一些能够在私有网络中使用并提供更好安全型的应用程序。 这节将介绍防火墙是什么, 如何使用它们, 以及如何利用 FreeBSD 内核提供的机制来实现它。

Note: 人们经常认为在内部网络与 “硕大无朋而臭名昭著的 Internet” 之间建立一个防火墙能够解决所有的安全问题。 这可能会有所帮助, 但糟糕的防火墙设置可能要比没有防火墙更加危险。 防火墙可以为系统增加另一个安全层, 但不可能完全阻止一些入侵高手侵入系统。 如果觉得防火墙能够完全阻止入侵而放松了安全设置, 那可能会让黑客侵入系统变得更加容易。

14.9.1. 防火墙是什么?

目前常见的 Internet 防火墙主要有两种类型。 第一种类型正式的名字叫 包过滤路由器, 这类防火墙通过一台接入多个网段的机器, 以及一组规则来决定转发或阻止数据包的传输。 第二种类型被称为 代理服务器, 依赖服务程序来完成包的转发, 而内核的包转发则可能被禁用。

有时,一些站点同时使用两种类型的防火墙, 这样只有某个特定的机器 (称为 bastion host) 能够通过包过滤路由器将包发给内网。 代理服务器在 bastion host 上运行, 这通常比一般的验证机制更安全。

FreeBSD 有一个内核数据包过滤程序 (也就是人们熟悉的 IPFW), 本节余下的部分将集中介绍它。 可以在 FreeBSD 上通过安装第三方软件来建立代理服务器, 但限于篇幅, 这里将不予介绍。

14.9.1.1. 包过滤路由器

路由器是负责在网络之间转发数据的机器。 包过滤路由器通过将每一个包与一组规则表对比, 然后决定是否转发它来完成其功能。 多数现代的 IP 路由软件都包括了包过滤功能, 并默认转发所有的包。 为了启用过滤器, 需要您定义一组规则。

为了确定一个包是否应该通过, 防火墙需要便利其规则集中所有于包头匹配的规则。 一旦发现一个匹配, 则会采取规则所指定的动作。 规则动作可以是丢弃包, 转发包, 甚至给发包的人发送一个 ICMP 消息。 只有第一个匹配是有效的, 因为规则是按顺序进行搜索的。 因此, 规则表也可以称作 “规则链”。

包的匹配规则随所使用的软件的不同而不同, 但基本上典型的包过滤防火墙都允许基于源 IP 地址、 目的 IP 地址、 源端口号、 目的端口号 (对于那些支持端口号的协议), 甚至协议类型 (UDP, TCP, ICMP, etc) 的过滤。

14.9.1.2. 代理服务器

代理服务器上的那些普通的系统服务 (telnetdftpd等等) 换成了一些特别的服务。 这些服务被称为 代理服务, 因为它们只允许进行一个方向的连接。 这使得您可以在作为防火墙的机器上运行 (举例来说) 一个代理的 telnet 服务, 而人们可以从外面 telnet 到防火墙上, 并通过适当的验证之后, 就能够访问内网 (另外, 代理服务器也可以用来让内网能够访问外网)。

一般而言代理服务器较之普通的服务器更为安全, 而且能够使用更多的验证方式, 包括 “一次性” 口令系统等等, 因此即使有人拿到了您的口令, 他们也没有办法用这个口令做什么,因为口令在第一次用过之后马上就过期了。 由于并没有授予用户使用代理本身的权限, 因此在防火墙上做后门并进而进行更多的破坏会变得困难许多。

代理服务器通常有更多的限制访问的方法, 例如可以设置只有从特定主机才能够访问到服务器。 许多代理服务软件也允许管理员来指定哪些用户可以访问哪些机器。 当然, 能够使用哪些机制很大程度上取决于采用了什么代理软件。

14.9.2. IPFW 能用来做什么?

FreeBSD 提供的 IPFW 软件, 是一个内核级的包过滤和审计系统, 它同时还提供了一个用户界面的控制工具, 即 ipfw(8)。 这两部分配合使用, 您可以轻松地定义和查询内核在进行路由决策时所采用的规则。

IPFW 有两个相关的部分。 防火墙部分实施的是包过滤。 另一部分是 IP 审计, 它将记录路由器的使用, 并采用与防火墙部分类似的规则。 这样, 管理员就能够监视从特定机器上使用了多少路由资源, 举例来说, 它转发了多少了多少 WWW 的访问流量。

由于 IPFW 的这种设计, 您也可以把 IPFW 用在非路由的哪些机器上, 以便对进入和送出的连接进行包过滤。 这是 IPFW 更为普遍的用法中的一个特例, 而此时您仍然可以使用相同的命令和技巧。

14.9.3. 在 FreeBSD 上启用 IPFW

由于 IPFW 的主要部分是在内核中运行的, 因此会需要在内核配置文件中添加一些选项, 这取决于您需要使用哪些机制, 随后是重新编译内核。 请参见 "Reconfiguring your Kernel" (Chapter 8) 一节来了解如何重新编译内核。

WarningIPFW 的默认策略是 deny ip from any to any。 因此如果您没有在启动时增加其他策略来允许一些包进入, 则如果使用了启用过防火墙的内核, 则您将把自己挡在服务器外面。 我们建议您在第一次使用时在 /etc/rc.conf 中设置 firewall_type=open, 然后再到 /etc/rc.firewall 中慢慢微调它。 保险起见, 您可能会考虑在本地控制台上, 而不是通过 ssh 来完成防火墙的配置。 另一种方法是同时启用 IPFIREWALLIPFIREWALL_DEFAULT_TO_ACCEPT 两个选项,这时 IPFW 的默认规则将是 allow ip from any to any 从而避免了将自己锁在外面的尴尬局面。

目前一共有四个与 IPFW 有关的内核选项:

options IPFIREWALL

将包过滤部分的代码编译进内核。

options IPFIREWALL_VERBOSE

启用通过 syslogd(8) 记录的日志。 如果没有指定这个选项, 即使您在过滤规则中指定记录包, 也不会真的记录它们。

options IPFIREWALL_VERBOSE_LIMIT=10

限制通过 syslogd(8) 记录的每项包规则的记录条数。 在恶劣的环境中如果您想记录防火墙的活动, 而又不想由于 syslog 洪水一般的记录而导致拒绝服务攻击, 那么这个选项将会很有用。

当规则链中的某一项达到这一限制数值时, 它所对应的日志将不再记录。 如果需要恢复, 则需要使用 ipfw(8) 工具来复位对应的计数器:

# ipfw zero 4500

这里 4500 是希望继续记录日志的规则在链中的编号。

options IPFIREWALL_DEFAULT_TO_ACCEPT

这将把默认的规则动作从 “deny” 改为 “allow”。 这可以防止在没有配置防火墙之前使用启用过 IPFIREWALL 支持的内核重启时把自己锁在外面。 另外, 如果您经常使用 ipfw(8) 来解决一些问题时它也非常有用。 尽管如此, 在使用时应该小心, 因为这将使防火墙敞开, 并改变它的行为。

Note: 先前版本的 FreeBSD 包括了一个 IPFIREWALL_ACCT 选项。 它现在已经过时了, 因为新的防火墙代码已经包括了审计机制。

14.9.4. 配置 IPFW

对于 IPFW 软件的配置是通过 ipfw(8) 来完成的。 它的命令看上去很复杂, 但只要您理解了其结构, 就会感到很简单。

目前这个工具有四种不同的命令: 添加/删除、 列表、 清空规则链以及让审计项归零。 添加删除类的命令主要用来建立控制如何接受、拒绝和记录包的规则。 列表类命令用于检视目前的规则集 (有时也称作规则链) 和包计数器 (审计)。 清空规则链则删除链中的所有规则, 而审计项归零则可以让一些审计项重新从零开始计数。

14.9.4.1. 改变 IPFW 的规则

这类命令的格式是:

ipfw [-N] 命令 [编号] 动作 [log(日志)] 协议 地址 [其它选项]



当使用这种形式的命令时,有一个可用的参数:

-N

在输出中解析地址和服务的名字。

给出的 命令 可以简写为能够为一分辨它们的最短形式。 可用的 命令 包括:

add

添加一个防火墙/审计规则到规则链中。

delete

从规则链中删除一项防火墙/审计规则。

先前版本的 IPFW 使用分别的防火墙和审计规则项。 目前的版本则为每一个防火墙规则项进行审计。

如果给出了 编号 值, 则它将决定规则项在链中的位置。 如果没有指定, 则系统会自动分配一个比链的最后一项大 100 的编号 (当然, 不包括默认规则, 即 65535 号规则, deny)。

选项 log 则将使匹配规则的包输出到控制台上, 当然前提是把 IPFIREWALL_VERBOSE 编译进内核。

可用的 动作 包括:

reject

丢掉包, 并回应一个(相应的) ICMP 主机或端口不可达消息给包的来源地址。

allow

让包通过。 (等价的别名: passpermit、 以及 accept)

deny

丢弃包。 但源地址并不会得到相关的 ICMP 消息通知 (因此对它来说就像包没有到达目的地址一样)。

count

更新包过滤启但并不执行允许/丢弃的动作。 此后将会继续查找规则链中的下一条规则。

每一个 动作 都可以使用其无二义性的最短前缀来代替。

可以指定的 协议 是:

all

匹配任何的 IP 包

icmp

匹配 ICMP 包

tcp

匹配 TCP 包

udp

匹配 UDP 包

可以指定的 地址 是:

from 地址/掩码 [端口] to 地址/掩码 [端口] [via 网络接口名]

只有在使用支持 端口协议 (UDP and TCP) 时才能指定端口。

via 是一个可选的选项, 它可以通过指定来自某个本地网络接口的 IP 地址或名字 (例如 ed0) 来匹配来自那个接口的包。 接口的单元号可以用通配符来指定。例如, 使用 ppp* 来匹配所有的内核 PPP 接口。

指定 地址/掩码 的语法是:

地址
或者
地址/掩码位数
或者
地址:掩码模式


可以在 IP 地址的位置指定一个有效的主机名。 掩码位数 是一个十进制的数, 用以表达地址掩码中的前多少位应当被置一。 例如, 指定 192.216.222.1/24 将建立一个匹配对应 C 类子网的掩码 (在本例中, 192.216.222)。 掩码模式 是一个将与之执行逻辑与操作的 IP 地址。 此外, 还可以用 any 来指定 “任意 IP 地址”。

端口地址范围可以指定为:

端口 [,端口 [,端口 [...]]]

如果想指定一组不连续的端口。 用

端口-端口

来指定一组连续的端口。 这两种格式可以在同一规则中使用, 但连续端口必须先于单个端口指定。

可用的 其它选项 是:

frag

匹配数据报中非第一分片的所有分片。

in

匹配进入的包。

out

匹配送出的包。

ipoptions 标志

匹配 IP 头包含逗号分隔的 标志 的包。 支持的 IP 选项包括: ssrr (严格使用源路由)、 lsrr (使用松散的源路由)、 rr (记录包路由)、 以及 ts (时间戳)。 如果想指定不包括某个标志, 则在选项前加入一个 !

established

匹配属于已经建立的 TCP 连接的包 (也就是说其 RST 或 ACK 是置1的)。 您可以通过在规则链中较早的位置上放置 established 规则来优化防火墙性能。

setup

匹配尝试建立 TCP 连接的包 (SYN 置位而 ACK 没有)。

tcpflags 标志

匹配 TCP 报头包括指定的 标志 的包。 支持的标志是 finsyn, rstpsh, ack, 以及 urg。 如果需要匹配某一位为0, 则在对应的标志前面加 !

icmptypes 类型

匹配 ICMP 类型在 类型 表中的包。 这个表使用逗号分隔, 可以指定任何范围和/或单个的类型。 常见的 ICMP 类型包括: 0 原样回应 (ping 响应), 3 目的不可达、 5 重定向、 8 原样回应请求 (ping 请求), 以及 11 超时 (在 traceroute(8) 这样的程序中用来追踪 TTL超时)。

14.9.4.2. 列出 IPFW 规则

这类命令的格式是:

ipfw [-a] [-c] [-d] [-e] [-t] [-N] [-S] list



七个标志的意义分别是:

-a

当列条目时,显示计数器的值。这个选项是可以看到计数器值的唯一方法。

-c

以紧凑的形式列出规则。

-d

除了静态规则之外, 还列出动态的规则。

-e

如果指定了 -d, 同时列出已经过期的动态规则。

-t

列出每一规则的最后匹配时间。 显示的格式和 ipfw(8) 使用的时间语法并不兼容。

-N

解析地址和服务的名字。

-S

显示每个规则所述的规则集。 如果没有指定标志, 则不会列出已经禁用的规则。

14.9.4.3. 清空 IPFW 规则

用于清空规则链的语法是:

ipfw flush



这将导致防火墙规则链中除了由内核强制的默认规则 (编号是 65535) 的所有规则都被清除。 这么做时需要格外的孝心; 默认 deny 的策略将切断网络连接, 直到新的规则添加进来为止。

14.9.4.4. 将 IPFW 包计数器归零

将某个或某些包计数器归零的命令语法是:

ipfw zero [编号]



当不带 编号 参数使用时, 所有的包计数器都会归零。 如果指定了 编号 参数, 则归零操作只影响那个规则。

14.9.5. 与 ipfw 有关的命令示范

下面的命令将丢弃所有来自主机 evil.crackers.org 到主机 nice.people.org 端口的包:

# ipfw add deny tcp from evil.crackers.org to nice.people.org 23

下一个例子则将拒绝并记录来自整个 crackers.org 网络 (C类) 到 nice.people.org 机器的流量 (任何端口)。

# ipfw add deny log tcp from evil.crackers.org/24 to nice.people.org

如果不希望人们发送 X 到您的内网 (一个 C 类子网), 则可以使用下面的规则:

# ipfw add deny tcp from any to my.org/28 6000 setup

察看审计记录:

# ipfw -a list
或者以简写的形式
# ipfw -a l


可以通过下面的方法察看每个规则上次匹配的时间:

# ipfw -at l

14.9.6. 建立包过滤的防火墙

Note: 注意:下面的建议仅仅是:建议。 每个防火墙的要求是不同的, 我们不能告诉如何建构符合特殊要求的防火墙。

当一开始设置防火墙时, 除非有一个测试平台来在可控的环境中测试防火墙主机, 否则强烈建议使用启用了日志的命令, 并启用内核中的日志功能。 这将帮助您快速地确定问题所在并加以修复。 即使初始安装已经完成, 仍然建议记录 `deny' 因为它能够记录攻击企图, 并帮助您在必要时在事后调整防火墙规则。

Note: 如果使用了带日志的 accept 命令, 则可能产生 大量的 日志数据。 通过防火墙的所有包都会被记录, 因此大的 FTP/http 传输等等, 都会让系统变得很慢。 由于在数据包通过之前会要求内核做更多的工作, 因此这些更大的延迟。 另外, syslogd 将会使用更多的处理器时间来吧所有那些额外的数据记录到磁盘上, 这也很容易把 /var/log 所在的分区填满。

一般情况下应该从 /etc/rc.conf.local/etc/rc.conf 启动防火墙。 相关的联机手册会解释如何设置和列出当前的防火墙配置。 如果不使用当前的配置, ipfw list 列表将输出当前的规则设置, 您可以把这些设置放到 rc.conf 中。 如果不想用 /etc/rc.conf.local/etc/rc.conf 来启用防火墙, 则确认防火墙是在任何 IP 接口之前已经配置好就很重要了。

下一个问题是防火墙实际上 做了 些什么! 着很大程度上取决于您希望外界如何访问您的网络, 以及允许什么样的访问被发到外网。 下面是一些通用的规则:

另外可以到 CERT 的网站去查看一下防火墙配置的推荐步骤, 其网址是 http://www.cert.org/tech_tips/packet_filtering.html

如前面提到的, 这些只是 指导原则。 必须根据具体情况决定使用什么过滤规则。 即使按照上面提到的方法做了, 也没办法保证一定没有人能够侵入您的网络。

14.9.7. IPFW 的开销和优化

许多人希望知道 IPFW 会给系统增加多少开销。 答案很大程度上取决于您的规则集大小以及处理器速度。 对于运行在一台网上的绝大多数应用和小规则集来说, 这种开销是 “可忽略的”。 如果您仍然像知道实际的大小来满足好奇心, 则请继续读下去。

下面的测试是在 486-66 上运行的 2.2.5-STABLE 上完成的。 (虽然 IPFW 在之后版本的 FreeBSD 上有很多小规模的修改, 但它们的速度仍然接近)。 IPFW 经过了修改以给出在 ip_fw_chk 子程序上消耗的时间, 并在每处理1000个数据包之后在控制台上打出结果。

使用两个各包含 1000 条规则的规则集进行了测试。 第一个规则集是用来展现最差情况的, 它重复下面的规则:

# ipfw add deny tcp from any to any 55555

通过使绝大多数 IPFW 的包检查子程序被执行之后才能完成决策, 这样做将会展现最差情况, 因为所有的包都不可能匹配这些规则 (因为端口号)。 在重复 999 之有一条 allow ip from any to any规则。

第二组规则用于在规则的开始就停止判断:

# ipfw add deny ip from 1.2.3.4 to 1.2.3.4

由于源 IP 地址不匹配上述规则将很快被挑过。 像之前一样, 第 1000 条规则也是 allow ip from any to any

第一个情况下处理大约需要 2.703 毫秒/包, 大致相当于每条规则 2.7 毫秒。 这种情况下最大的包处理能力达约是每秒 370 个包。 假设在 10 Mbps 以太网上, 包的大小大致是 ~1500  字节, 则只能达到 55.5% 的带宽利用率。

对于后一种情况每个包的处理大约需要 1.172 毫秒, 大致相当于每条规则 1.2 毫秒。 理论上每秒大约能够处理 853 个包, 能够完全占满 10 Mbps 以太网的带宽。

测试中这些过多的规则数目和规则本身是无法模拟实际的情况的 -- 它们只是用来产生这里需要的计时信息。 在建立规则时请牢记下面一些技巧: