IPFW 防火墙

31.6. IPFW

IPFIREWALL (IPFW) 是一个由 FreeBSD 发起的防火墙应用软件, 它由 FreeBSD 的志愿者成员编写和维护。 它使用了传统的无状态规则和规则编写方式, 以期达到简单状态逻辑所期望的目标。

标准的 FreeBSD 安装中, IPFW 所给出的规则集样例 (可以在 /etc/rc.firewall 和 /etc/rc.firewall6 中找到) 非常简单, 建议不要不加修改地直接使用。 该样例中没有使用状态过滤, 而该功能在大部分的配置中都是非常有用的, 因此这一节并不以系统自带的样例作为基础。

IPFW 的无状态规则语法, 是由一种提供复杂的选择能力的技术支持的, 这种技术远远超出了一般的防火墙安装人员的知识水平。 IPFW 是为满足专业用户, 以及掌握先进技术的电脑爱好者们对于高级的包选择需求而设计的。 要完全释放 IPFW 的规则所拥有的强大能力, 需要对不同的协议的细节有深入的了解, 并根据它们独特的包头信息来编写规则。 这一级别的详细阐述超出了这本手册的范围。

IPFW 由七个部分组成, 其主要组件是内核的防火墙过滤规则处理器, 及其集成的数据包记帐工具、 日志工具、 用以触发 NAT 工具的 divert (转发) 规则、 高级特殊用途工具、 dummynet 流量整形机制, fwd rule 转发工具, 桥接工具, 以及 ipstealth 工具。 IPFW 支持 IPv4 和 IPv6。

31.6.1. 启用 IPFW

IPFW 是基本的 FreeBSD 安装的一部分, 以单独的可加载内核模块的形式提供。 如果在 rc.conf 中加入 firewall_enable="YES" 语句, 就会自动地加载对应的内核模块。 除非您打算使用由它提供的 NAT 功能, 一般情况下并不需要把 IPFW 编进 FreeBSD 的内核。

如果将 firewall_enable="YES" 加入到 rc.conf 中并重新启动系统, 则下列信息将在启动过程中, 以高亮的白色显示出来:

ipfw2 initialized, divert disabled, rule-based forwarding disabled, default to deny, logging disabled

可加载内核模块在编译时加入了记录日志的能力。 要启用日志功能, 并配置详细日志记录的限制, 需要在 /etc/sysctl.conf 中加入一些配置。 这些设置将在重新启动之后生效:

net.inet.ip.fw.verbose=1
net.inet.ip.fw.verbose_limit=5

31.6.2. 内核选项

把下列选项在编译 FreeBSD 内核时就加入, 并不是启用 IPFW 所必需的, 除非您需要使用 NAT 功能。 这里只是将这些选项作为背景知识来介绍。

options    IPFIREWALL

这个选项将 IPFW 作为内核的一部分来启用。

options    IPFIREWALL_VERBOSE

这个选项将启用记录通过 IPFW 的匹配了包含 log 关键字规则的每一个包的功能。

options    IPFIREWALL_VERBOSE_LIMIT=5

以每项的方式, 限制通过 syslogd(8) 记录的包的个数。 如果在比较恶劣的环境下记录防火墙的活动可能会需要这个选项。 它能够避免潜在的针对 syslog 的洪水式拒绝服务攻击。

options    IPFIREWALL_DEFAULT_TO_ACCEPT

这个选项默认地允许所有的包通过防火墙, 如果您是第一次配置防火墙, 使用这个选项将是一个不错的主意。

options    IPDIVERT

这一选项启用 NAT 功能。

注意: 

如果内核选项中没有加入 IPFIREWALL_DEFAULT_TO_ACCEPT, 而配置使用的规则集中也没有明确地指定允许连接进入的规则, 默认情况下, 发到本机和从本机发出的所有包都会被阻止。

31.6.3. /etc/rc.conf Options

启用防火墙:

firewall_enable="YES"

要选择由 FreeBSD 提供的几种防火墙类型中的一种来作为默认配置, 您需要阅读 /etc/rc.firewall 文件并选出合适的类型, 然后在 /etc/rc.conf 中加入类似下面的配置:

firewall_type="open"

您还可以指定下列配置规则之一:

  • open ── 允许所有流量通过。

  • client ── 只保护本机。

  • simple ── 保护整个网络。

  • closed ── 完全禁止除回环设备之外的全部 IP 流量。

  • UNKNOWN ── 禁止加载防火墙规则。

  • filename ── 到防火墙规则文件的绝对路径。

有两种加载自定义 ipfw 防火墙规则的方法。 其一是将变量 firewall_type 设为包含不带 ipfw(8) 命令行选项的 防火墙规则 文件的完整路径。 下面是一个简单的规则集例子:

add deny in
add deny out

除此之外, 也可以将 firewall_script 变量设为包含 ipfw 命令的可执行脚本, 这样这个脚本会在启动时自动执行。 与前面规则集文件等价的规则脚本如下:

ipfw 命令是在防火墙运行时, 用于在其内部规则表中手工逐条添加或删除防火墙规则的标准工具。 这一方法的问题在于, 一旦您的关闭计算机或停机, 则所有增加或删除或修改的规则也就丢掉了。 把所有的规则都写到一个文件中, 并在启动时使用这个文件来加载规则, 或一次大批量地替换防火墙规则, 那么推荐使用这里介绍的方法。

ipfw 的另一个非常实用的功能是将所有正在运行的防火墙规则显示出来。 IPFW 的记账机制会为每一个规则动态地创建计数器, 用以记录与它们匹配的包的数量。 在测试规则的过程中, 列出规则及其计数器是了解它们是否工作正常的重要手段。

按顺序列出所有的规则:

# ipfw list

列出所有的规则, 同时给出最后一次匹配的时间戳:

# ipfw -t list

列出所有的记账信息、 匹配规则的包的数量, 以及规则本身。 第一列是规则的编号, 随后是发出包匹配的数量, 进入包的匹配数量, 最后是规则本身。

# ipfw -a list

列出所有的动态规则和静态规则:

# ipfw -d list

同时显示已过期的动态规则:

# ipfw -d -e list

将计数器清零:

# ipfw zero

只把规则号为 NUM 的计数器清零:

# ipfw zero NUM

31.6.4. IPFW 规则集

规则集是指一组编写好的依据包的值决策允许通过或阻止 IPFW 规则。 包的双向交换组成了一个会话交互。 防火墙规则集会作用于来自于 Internet 公网的包以及由系统发出来回应这些包的数据包。 每一个 TCP/IP 服务 (例如 telnet, www, 邮件等等) 都由协议预先定义了其特权 (监听) 端口。 发到特定服务的包会从源地址使用非特权 (高编号) 端口发出, 并发到特定服务在目的地址的对应端口。 所有这些参数 (例如: 端口和地址) 都是可以为防火墙规则所利用的, 判别是否允许服务通过的标准。

当有数据包进入防火墙时, 会从规则集里的第一个规则开始进行比较, 并自顶向下地进行匹配。 当包与某个选择规则参数相匹配时, 将会执行规则所定义的动作, 并停止规则集搜索。 这种策略, 通常也被称作 最先匹配者获胜 的搜索方法。 如果没有任何与包相匹配的规则, 那么它就会根据强制的 IPFW 默认规则, 也就是 65535 号规则截获。 一般情况下这个规则是阻止包, 而且不给出任何回应。

注意: 

如果规则定义的动作是 count、 skipto 或 tee 规则的话, 搜索会继续。

这里所介绍的规则, 都是使用了那些包含状态功能的, 也就是 keep state、 limit、 in、 out 以及 via 选项的规则。 这是编写明示允许防火墙规则集所需的基本框架。

警告: 

在操作防火墙规则时应谨慎行事, 如果操作不当, 很容易将自己反锁在外面。

31.6.4.1. 规则语法

这里所介绍的规则语法已经经过了简化, 只包括了建立标准的明示允许防火墙规则集所必需的那些。 要了解完整的规则语法说明, 请参见 ipfw(8) 联机手册。

规则是由关键字组成的: 这些关键字必须以特定的顺序从左到右书写。 下面的介绍中, 关键字使用粗体表示。 某些关键字还包括了子选项, 这些子选项本身可能也是关键字, 有些还可以包含更多的子选项。

# 用于表示开始一段注释。 它可以出现在一个规则的后面, 也可以独占一行。 空行会被忽略。

CMD RULE_NUMBER ACTION LOGGING SELECTION STATEFUL

31.6.4.1.1. CMD

每一个新的规则都应以 add 作为前缀, 它表示将规则加入内部表。

31.6.4.1.2. RULE_NUMBER

每一条规则都与一个范围在 1 到 65535 之间的规则编号相关联。

31.6.4.1.3. ACTION

每一个规则可以与下列的动作之一相关联, 所指定的动作将在进入的数据包与规则所指定的选择标准相匹配时执行。

allow | accept | pass | permit

这些关键字都表示允许匹配规则的包通过防火墙, 并停止继续搜索规则。

check-state

根据动态规则表检查数据包。 如果匹配, 则执行规则所指定的动作, 亦即生成动态规则; 否则, 转移到下一个规则。 check-state 规则没有选择标准。 如果规则集中没有 check-state 规则, 则会在第一个 keep-state 或 limit 规则处, 对动态规则表实施检查。

deny | drop

这两个关键字都表示丢弃匹配规则的包。 同时, 停止继续搜索规则。

31.6.4.1.4. LOGGING

log or logamount

当数据包与带 log 关键字的规则匹配时, 将通过名为 SECURITY 的 facility 来把消息记录到 syslogd(8)。 只有在记录的次数没有超过 logamount 参数所指定的次数时, 才会记录日志。 如果没有指定 logamount, 则会以 sysctl 变量 net.inet.ip.fw.verbose_limit 所指定的限制为准。 如果将这两种限制值之一指定为零, 则表示不作限制。 如果达到了限制数, 可以通过将规则的日志计数或包计数清零来重新启用日志, 请参见 ipfw reset log 命令来了解细节。

注意: 

日志是在所有其他匹配条件都验证成功之后, 在针对包实施最终动作 (accept, deny) 之前进行的。 您可以自行决定哪些规则应启用日志。

31.6.4.1.5. SELECTION

这一节所介绍的关键字主要用来描述检查包的哪些属性, 用以判断包是否与规则相匹配。 下面是一些通用的用于匹配包特征的属性, 它们必须按顺序使用:

udp | tcp | icmp

也可以指定在 /etc/protocols 中所定义的协议。 这个值定义的是匹配的协议, 在规则中必须指定它。

from src to dst

from 和 to 关键字用于匹配 IP 地址。 规则中必须 同时 指定源和目的两个参数。 如果需要匹配任意 IP 地址, 可以使用特殊关键字 any。 还有一个特殊关键字, 即 me, 用于匹配您的 FreeBSD 系统上所有网络接口上所配置的 IP 地址, 它可以用于表达网络上的其他计算机到防火墙 (也就是本机), 例如 from me to any 或 from any to me 或 from 0.0.0.0/0 to any 或 from any to 0.0.0.0/0 或 from 0.0.0.0 to any 或 from any to 0.0.0.0 以及 from me to 0.0.0.0。 IP 地址可以通过 带点的 IP 地址/掩码长度 (CIDR 记法), 或者一个带点的 IP 地址的形式来指定。 这是编写规则时所必需的。 使用 net-mgmt/ipcalc port 可以用来简化计算。 关于这个工具的更多信息, 也可参考它的主页: http://jodies.de/ipcalc

port number

这个参数主要用于那些支持端口号的协议 (例如 TCP 和 UDP)。 如果要通过端口号匹配某个协议, 就必须指定这个参数。 此外, 也可以通过服务的名字 (根据 /etc/services) 来指定服务, 这样会比使用数字指定端口号直观一些。

in | out

相应地, 匹配进入和发出的包。 这里的 in 和 out 都是关键字, 在编写匹配规则时, 必需作为其他条件的一部分来使用。

via IF

根据指定的网络接口的名称精确地匹配进出的包。 这里的 via 关键字将使得接口名称成为匹配过程的一部分。

setup

要匹配 TCP 会话的发起请求, 就必须使用它。

keep-state

这是一个必须使用的关键字。 在发生匹配时, 防火墙将创建一个动态规则, 其默认行为是, 匹配使用同一协议的、从源到目的 IP/端口 的双向网络流量。

limit {src-addr | src-port | dst-addr | dst-port}

防火墙只允许匹配规则时, 与指定的参数相同的 N 个连接。 可以指定至少一个源或目的地址及端口。 limit 和 keep-state 不能在同一规则中同时使用。 limit 提供了与 keep-state相同的功能, 并增加了一些独有的能力。

31.6.4.2. 状态规则选项

有状态过滤将网络流量当作一种双向的包交换来处理。 它提供了一种额外的检查能力, 用以检测会话中的包是否来自最初的发送者, 并在遵循双向包交换的规则进行会话。 如果包与这些规则不符, 则将自动地拒绝它们。

check-state 用来识别在 IPFW 规则集中的包是否符合动态规则机制的规则。 如果匹配, 则允许包通过, 此时防火墙将创建一个新的动态规则来匹配双向交换中的下一个包。 如果不匹配, 则将继续尝试规则集中的下一个规则。

动态规则机制在 SYN-flood 攻击下是脆弱的, 因为这种情况会产生大量的动态规则, 从而耗尽资源。 为了抵抗这种攻击, 从 FreeBSD 中加入了一个叫做 limit 的新选项。 这个选项可以用来限制符合规则的会话允许的并发连接数。 如果动态规则表中的规则数超过 limit 的限制数量, 则包将被丢弃。

31.6.4.3. 记录防火墙消息

记录日志的好处是显而易见的: 它提供了在事后检查所发生的状况的方法, 例如哪些包被丢弃了, 这些包的来源和目的地, 从而为您提供找到攻击者所需的证据。

即使启用了日志机制, IPFW 也不会自行生成任何规则的日志。 防火墙管理员需要指定规则集中的哪些规则应该记录日志, 并在这些规则上增加 log 动作。 一般来说, 只有 deny 规则应记录日志, 例如对于进入的 ICMP ping 的 deny 规则。 另外, 复制 默认的 ipfw 终极 deny 规则, 并加入 log 动作来作为您的规则集的最后一条规则也是很常见的用法。 这样, 您就能看到没有匹配任何一条规则的那些数据包。

日志是一把双刃剑, 如果不谨慎地加以利用, 则可能会陷入过多的日志数据中, 并导致磁盘被日志塞满。 将磁盘填满是 DoS 攻击最为老套的手法之一。 由于 syslogd 除了会将日志写入磁盘之外, 还会输出到 root 的控制台屏幕上, 因此有过多的日志信息是很让人恼火的事情。

IPFIREWALL_VERBOSE_LIMIT=5 内核选项将限制同一个规则发到系统日志程序 syslogd(8) 的连续消息的数量。 当内核启用了这个选项时, 某一特定规则所产生的连续消息的数量将封顶为这个数字。 一般来说, 没有办法从连续 200 条一模一样的日志信息中获取更多有用的信息。 举例来说, 如果同一个规则产生了 5 次消息并被记录到 syslogd, 余下的相同的消息将被计数, 并像下面这样发给 syslogd

last message repeated 45 times

所有记录的数据包包消息, 默认情况下会最终写到 /var/log/security 文件中, 后者在 /etc/syslog.conf 文件里进行了定义。

31.6.4.4. 编写规则脚本

绝大多数有经验的 IPFW 用户会创建一个包含规则的文件, 并且, 按能够以脚本形式运行的方式来书写。 这样做最大的一个好处是, 可以大批量地刷新防火墙规则, 而无须重新启动系统就能够激活它们。 这种方法在测试新规则时会非常方便, 因为同一过程在需要时可以多次执行。 作为脚本, 您可以使用符号替换来撰写那些经常需要使用的值, 并用同一个符号在多个规则中反复地表达它。 下面将给出一个例子。

这个脚本使用的语法同 sh(1)、 csh(1) 以及 tcsh(1) 脚本兼容。 符号替换字段使用美元符号 $ 作为前缀。 符号字段本身并不使用 $ 前缀。 符号替换字段的值必须使用 "双引号" 括起来。

可以使用类似下面的规则文件:

############### start of example ipfw rules script #############
#
ipfw -q -f flush       # Delete all rules
# Set defaults
oif="tun0"             # out interface
odns="192.0.2.11"      # ISP's DNS server IP address
cmd="ipfw -q add "     # build rule prefix
ks="keep-state"        # just too lazy to key this each time
$cmd 00500 check-state
$cmd 00502 deny all from any to any frag
$cmd 00501 deny tcp from any to any established
$cmd 00600 allow tcp from any to any 80 out via $oif setup $ks
$cmd 00610 allow tcp from any to $odns 53 out via $oif setup $ks
$cmd 00611 allow udp from any to $odns 53 out via $oif $ks
################### End of example ipfw rules script ############

这就是所要做的全部事情了。 例子中的规则并不重要, 它们主要是用来表示如何使用符号替换。

如果把上面的例子保存到 /etc/ipfw.rules 文件中。 下面的命令来会重新加载规则。

# sh /etc/ipfw.rules

/etc/ipfw.rules 这个文件可以放到任何位置, 也可以命名为随便什么别的名字。

也可以手工执行下面的命令来达到类似的目的:

# ipfw -q -f flush# ipfw -q add check-state# ipfw -q add deny all from any to any frag# ipfw -q add deny tcp from any to any established# ipfw -q add allow tcp from any to any 80 out via tun0 setup keep-state# ipfw -q add allow tcp from any to 192.0.2.11 53 out via tun0 setup keep-state# ipfw -q add 00611 allow udp from any to 192.0.2.11 53 out via tun0 keep-state

31.6.4.5. 带状态规则集

以下的这组非-NAT 规则集, 是如何编写非常安全的 '明示允许' 防火墙的一个例子。 明示允许防火墙只允许匹配了 pass 规则的包通过, 而默认阻止所有的其他数据包。 用来保护整个网段的防火墙, 至少需要有两个网络接口, 并且其上必须配置规则, 以便让防火墙正常工作。

所有类 UNIX 操作系统, 也包括 FreeBSD, 都设计为允许使用网络接口 lo0 和 IP 地址 127.0.0.1 来完成操作系统内部的通讯。 防火墙必须包含一组规则, 使这些数据包能够无障碍地收发。

接入 Internet 公网的那个网络接口上, 应该配置授权和访问控制, 来限制对外的访问, 以及来自 Internet 公网的访问。 这个接口很可能是您的用户态 PPP 接口, 例如 tun0, 或者您接在 DSL 或电缆 modem 上的网卡。

如果有至少一个网卡接入了防火墙后的内网 LAN, 则必须为这些接口配置规则, 以便让这些接口之间的包能够顺畅地通过。

所有的规则应被组织为三个部分, 所有应无阻碍地通过的规则, 公网的发出规则, 以及公网的接收规则。

公网接口相关的规则的顺序, 应该是最经常用到的放在尽可能靠前的位置, 而最后一个规则, 则应该是阻止那个接口在那一方向上的包。

发出部分的规则只包含一些 allow 规则, 允许选定的那些唯一区分协议的端口号所指定的协议通过, 以允许访问 Internet 公网上的这些服务。 所有的规则中都指定了 protoport、 in/out、 via 以及 keep state 这些选项。 proto tcp 规则同时指定 setup 选项, 来区分开始协议会话的包, 以触发将包放入 keep state 规则表中的动作。

接收部分则首先阻止所有不希望的包, 这样做有两个不同的原因。 其一是恶意的包可能和某些允许的流量规则存在部分匹配, 而我们希望阻止, 而不是让这些包仅仅与 allow 规则部分匹配就允许它们进入。 其二是, 已经确信要阻止的包被拒绝这件事, 往往并不是我们需要关注的, 因此只要简单地予以阻止即可。 防火墙规则集中的每个部分的最后一条规则都是阻止并记录包, 这有助于为逮捕攻击者留下法律所要求的证据。

另外一个需要注意的事情是确保系统对不希望的数据包不做回应。 无效的包应被丢弃和消失。 这样, 攻击者便无法知道包是否到达了您的系统。 攻击者对系统了解的越少, 其攻击的难度也就越大。 如果不知道端口号, 可以查阅 /etc/services/ 或到 http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers 并查找一下端口号, 以了解其用途。 另外, 您也可以在这个网页上了解常见木马所使用的端口: http://www.sans.org/security-resources/idfaq/oddports.php

31.6.4.6. 明示允许规则集的例子

下面是一个非-NAT 的规则集, 它是一个完整的明示允许规则集。 使用它作为您的规则集不会有什么问题。 只需把那些不需要的服务对应的 pass 规则注释掉就可以了。 如果您在日志中看到消息, 而且不想再看到它们, 只需在接收部分增加一个一个 deny 规则。 您可能需要把 dc0 改为接入公网的接口的名字。 对于使用用户态 PPP 的用户而言, 应该是tun0

这些规则遵循一定的模式。

  • 所有请求 Internet 公网上服务的会话开始包, 都使用了 keep-state

  • 所有来自 Internet 的授权服务请求, 都采用了 limit 选项来防止洪水式攻击。

  • 所有的规则都使用了 in 或者 out 来说明方向。

  • 所有的规则都使用了 via 接口名 来指定应该匹配通过哪一个接口的包。

这些规则都应放到 /etc/ipfw.rules


你可能感兴趣的:(处理器,技术支持,应用软件)