33.4.IPFW
IPFW 是为 FreeBSD 编写的有状态防火墙,支持 IPv4 和 IPv6。它由几个组件组成:内核防火墙过滤规则处理器及其集成的数据包记账设施、日志记录设施、NAT、dummynet(4) 交通整形器、转发设施、桥设施和 ipstealth 设施。
FreeBSD 在/etc/rc.firewall 中提供了一个示例规则集,该规则集定义了几种常见场景的防火墙类型,以帮助新手用户生成适当的规则集。IPFW 提供了一种强大的语法,高级用户可以使用它来制定满足特定环境安全要求的定制规则集。
本节说明了如何启用 IPFW,概述了其规则语法,并展示了几种常见配置场景的规则集。
33.4.1. 启用 IPFW
IPFW 包含在基本的 FreeBSD 安装中,作为一个可加载的内核模块,这意味着不需要定制内核即可启用 IPFW。
对于那些希望在定制内核中静态编译 IPFW 支持的用户,请参阅 IPFW 内核选项。
要配置系统以在启动时启用 IPFW,请将 firewall_enable="YES" 添加到/etc/rc.conf 中:
要使用 FreeBSD 提供的默认防火墙类型之一,请添加另一行指定类型:
可用的类型有:
open :允许所有流量。
client :仅保护此机器。
simple :保护整个网络。
closed :完全禁用除回环接口外的所有 IP 流量。
workstation :仅使用有状态规则保护此计算机。
UNKNOWN :禁用防火墙规则的加载。
文件名:包含防火墙规则集的完整路径。
如果将 firewall_type 设置为 client 或 simple,请修改/etc/rc.firewall 中的默认规则以适应系统配置。
请注意,filename 类型用于加载定制规则集。
加载定制规则集的另一种方法是将 firewall_script 变量设置为包含 IPFW 命令的可执行脚本的绝对路径。本节使用的示例假定 firewall_script 设置为/etc/ipfw.rules:
通过 syslogd(8) 启用日志记录,请包含此行:
没有 /etc/rc.conf 变量可用于设置日志记录限制。为了限制每个连接尝试中记录规则的次数,请在 /etc/sysctl.conf 中使用以下行指定数字:
要通过名为 ipfw0 的专用接口启用日志记录,请将此行添加到 /etc/rc.conf 中:
然后使用 tcpdump 查看被记录的内容:
保存所需的编辑后,启动防火墙。要立即启用日志限制,还需设置上述指定的 sysctl 值:
33.4.2. IPFW 规则语法
当数据包进入 IPFW 防火墙时,它会与规则集中第一个规则进行比较,并依次从上到下逐个规则进行。当数据包匹配规则的选择参数时,该规则的操作将被执行,并且该数据包的规则集搜索终止。这被称为“第一个匹配获胜”。如果数据包不匹配任何规则,则会被强制 IPFW 默认规则编号 65535 捕获,该规则拒绝所有数据包并静默丢弃它们。但是,如果数据包匹配包含 count 、 skipto 或 tee 关键字的规则,则搜索将继续。有关这些关键字如何影响规则处理的详细信息,请参阅 ipfw(8)。
创建 IPFW 规则时,关键字必须按照以下顺序编写。一些关键字是必需的,而其他关键字是可选的。大写字母表示一个变量,小写字母必须在其后跟随变量。符号 # 用于标记注释的开头,并可能出现在规则的结尾或单独一行。空白行将被忽略。
CMD RULE_NUMBER set SET_NUMBER ACTION log LOG_AMOUNT PROTO from SRC SRC_PORT to DST DST_PORT OPTIONS
此部分概述了这些关键字及其选项。这不是每个可能选项的详尽列表。请参考 ipfw(8) 了解创建 IPFW 规则时可使用的规则语法的完整说明。
CMD
每条规则都必须以 ipfw add 开头。
RULE_NUMBER
每条规则都与从 1 到 65534 的数字相关联。该数字用于指示规则处理的顺序。多个规则可以具有相同的编号,在这种情况下,它们按照添加的顺序进行应用。
设置编号
每个规则都与一个从 0 到 31 的编号集相关联。可以单独禁用或启用这些,从而快速添加或删除一组规则。如果未指定 SET_NUMBER,则规则将被添加到 0。
操作
一条规则可以与以下动作之一关联。当数据包与规则的选择条件匹配时,将执行指定的动作。
allow | accept | pass | permit :这些关键词是等效的,并允许匹配规则的数据包通过。
check-state :检查数据包是否与动态状态表匹配。如果找到匹配项,则执行与生成此动态规则的规则关联的动作,否则转到下一条规则。check-state 规则没有选择条件。如果规则集中没有 check-state 规则,则在第一条 keep-state 或 limit 规则处检查动态规则表。
count : 更新所有匹配此规则的数据包的计数器。搜索继续进行到下一个规则。
deny | drop : 任何一个词都会默默地丢弃匹配此规则的数据包。
还有其他可用操作。详情请参阅 ipfw(8)。
记录数量
当数据包与带有 log 关键字的规则匹配时,将向 syslogd(8)记录一个带有 SECURITY 设施名称的消息。仅当针对特定规则记录的数据包数量不超过指定的记录数量时才会发生记录。如果未指定记录数量,则限制将取自 net.inet.ip.fw.verbose_limit 的值。值为零会移除记录限制。若达到限制,可以通过清除该规则的记录计数器或数据包计数器来重新启用记录,使用 ipfw resetlog。
协议
此可选值可用于指定 /etc/protocols 中找到的任何协议名称或编号。
源地址
' from 关键字必须跟随源地址或表示源地址的关键字。地址可以用 any 、 me (此系统上接口配置的任何地址)、 me6 (此系统上接口配置的任何 IPv6 地址)或 table 后跟包含地址列表的查找表的编号来表示。指定 IP 地址时,可以选择性地跟随其 CIDR 掩码或子网掩码。例如,1.2.3.4/25 或 1.2.3.4:255.255.255.128。'
SRC_PORT
可以使用 /etc/services 中的 port 编号或名称指定可选源 port。
目标
to 关键字必须跟随目标地址或表示目标地址的关键字。可以使用与 SRC 部分说明相同的关键字和地址来说明目标。
目标端口
可以使用来自 /etc/services 的编号或名称指定可选目的地 port。
选项
源和目的地后面可以跟几个关键字。顾名思义,选项是可选的。常用选项包括 in 或 out,用于指定数据包流向,icmptypes 后跟 ICMP 消息类型,以及 keep-state。
当匹配 keep-state 规则时,防火墙将创建一个动态规则,该规则匹配源地址和目的地址之间以及ports之间使用相同协议的双向流量。
动态规则功能容易受到 SYN 洪水攻击导致资源耗尽的影响,这将打开大量动态规则。要使用 IPFW 抵御这种类型的攻击,请使用 limit。此选项通过检查打开的动态规则,计算此规则和 IP 地址组合发生的次数来限制同时会话的数量。如果此计数大于 limit 指定的值,则丢弃数据包。
有数十种选项可用。请参考 ipfw(8)以获取每个可用选项的说明。
33.4.3. 示例规则集
本节演示如何创建一个名为 /etc/ipfw.rules 的示例有状态防火墙规则集脚本。在此示例中,所有连接规则使用 in 或 out 来澄清方向。它们还使用 via 接口名称来指定数据包传输的接口。
这将把 ipfw(8) 的默认策略设置得比默认的 deny ip from any to any 更宽松,这样在系统重启后更难被锁定在外。
防火墙脚本首先指示它是一个 Bourne shell 脚本,并清除任何现有规则。然后创建 cmd 变量,这样就不必在每条规则的开头输入 ipfw add。它还定义了 pif 变量,表示连接到互联网的接口的名称。
前两条规则允许在受信任的内部接口和回环接口上的所有流量通过:
下一条规则允许数据包通过,如果它匹配动态规则表中的现有条目:
下一组规则定义内部系统可以创建到互联网主机的有状态连接:
下一组规则控制来自互联网主机对内部网络的连接。它首先拒绝通常与攻击相关的数据包,然后明确允许特定类型的连接。所有来自互联网的授权服务使用 limit 以防止洪水攻击。
最后一条规则记录所有与规则集中任何规则都不匹配的数据包。
33.4.4. 内核内 NAT
FreeBSD 的 IPFW 防火墙有两种 NAT 实现:用户空间实现 natd(8) 和较新的内核内 NAT 实现。两者都与 IPFW 结合使用,以提供网络地址转换。这可以用来提供一个互联网连接共享解决方案,使多个内部计算机可以使用单个公共 IP 地址连接到互联网。
要做到这一点,连接到互联网的 FreeBSD 机器必须作为网关。该系统必须有两个网卡,一个连接到互联网,另一个连接到内部 LAN。每台连接到 LAN 的机器都应分配一个私有网络空间中的 IP 地址,如 RFC 1918 中定义。
需要进行一些额外的配置才能启用 IPFW 的内核 NAT 功能。要在启动时启用内核 NAT 支持,必须在 /etc/rc.conf 中设置以下内容:
当规则集包含有状态规则时,NAT 规则的定位至关重要,并且使用 skipto 操作。skipto 操作需要一个规则编号,以便知道要跳转到哪个规则。下面的示例构建在前一节中显示的防火墙规则集的基础上。它添加了一些额外的条目并修改了一些现有规则,以便为内核 NAT 配置防火墙。它首先添加了一些额外的变量,这些变量表示要跳过的规则编号、 keep-state 选项,以及将用于减少规则数量的一系列 TCP ports。
使用内核 NAT 需要禁用 TCP 分段卸载 (TSO),因为 libalias(3) 的架构问题,该库作为内核模块实现,为 IPFW 提供内核 NAT 功能。可以使用 ifconfig(8) 在每个网络接口上禁用 TSO,或者使用 sysctl(8) 在整个系统上禁用。要在整个系统上禁用 TSO,必须在 /etc/sysctl.conf 中设置以下内容:
还将配置一个 NAT 实例。可以配置多个具有各自配置的 NAT 实例。本示例仅需要一个 NAT 实例,即 NAT 实例编号 1。配置可以采用一些选项,例如: if 表示公共接口,same_ports 确保别名化的 ports 和本地 port 号码映射相同,unreg_only 仅处理未注册(私有)地址空间的 NAT 实例,reset 在 IPFW 机器的公共 IP 地址更改时,帮助保持功能的 NAT 实例。要了解可以传递给单个 NAT 实例配置的所有可能选项,请参阅 ipfw(8)。在配置具有状态的 NAT 防火墙时,需要允许已转换的数据包重新注入防火墙进行进一步处理。可以通过在防火墙脚本的开头禁用 one_pass 行为来实现这一点。
入站 NAT 规则被插入到允许受信和回环接口上所有流量以及重新组装规则之后,但在 check-state 规则之前。在此 NAT 规则中选择的规则编号非常重要,例如在本示例中为 100,必须高于前三条规则并低于第 check-state 条规则。此外,由于内核内部 NAT 的行为,建议在第一个 NAT 规则之前和允许受信接口上的流量规则之后放置一个重新组装规则。通常情况下,不应该发生 IP 分段,但当处理 IPSEC/ESP/GRE 隧道流量时可能会发生,因此在将完整数据包交给内核 NAT 设施之前,需要对片段进行重新组装。
使用用户空间的 natd(8) 时不需要重新组装规则,因为 IPFW divert 操作的内部工作已经在 ipfw(8) 中指出,在将数据包交付给套接字之前已经处理了数据包的重新组装。 此示例中使用的 NAT 实例和规则编号与由 rc.firewall 创建的默认 NAT 实例和规则编号不匹配。rc.firewall 是一个设置 FreeBSD 中默认防火墙规则的脚本。
出站规则已修改为用 $skip 变量替换 allow 操作,表示规则处理将在规则 1000 处继续。七条 tcp 规则已被规则 125 替换,因为 $good_tcpo 变量包含了七个允许的出站ports。
入站规则保持不变,除了最后一条规则删除了 via $pif 以捕获入站和出站规则。NAT 规则必须跟在最后一条出站规则之后,必须比那条最后的规则数字高,并且规则编号必须由 skipto 操作引用。在此规则集中,规则编号 1000 处理将所有数据包传递到我们配置的实例进行 NAT 处理。下一条规则允许任何已经进行 NAT 处理的数据包通过。
在这个例子中,规则 100,101,125,1000 和 1001 控制出站和入站数据包的地址转换,以便动态状态表中的条目始终注册私有 LANIP 地址。
考虑一个内部的网络浏览器,它在port 80 上初始化一个新的出站 HTTP 会话。当第一个出站数据包进入防火墙时,它不匹配规则 100,因为它是出站的,而不是入站的。它通过规则 101,因为这是第一个数据包,并且尚未被发布到动态状态表。该数据包最终匹配规则 125,因为它是在允许的port 上出站,并且具有来自内部 LAN 的源 IP 地址。在匹配此规则时,两个操作同时进行。首先,keep-state 操作向动态状态表添加一个条目,并执行指定的操作 skipto rule 1000。接下来,数据包经过 NAT 处理并发送到互联网。该数据包传输到目标网络服务器,那里生成并发送回一个响应数据包。这个新数据包进入规则集的顶部。它匹配规则 100,并且其目标 IP 地址映射回原始的内部地址。然后,它由 check-state 规则处理,作为现有会话在表中找到,并释放到 LAN。
在入站方面,规则集必须拒绝恶意数据包,只允许授权的服务。与入站规则匹配的数据包被发布到动态状态表,并且该数据包释放到 LAN。作为响应生成的数据包被 check-state 规则识别为属于现有会话。然后,将其发送到规则 1000 经过 NAT 处理,然后释放到出站接口。
33.4.4.1. Port 重定向
NAT 的一个缺点是 LAN 客户端无法从 Internet 访问。LAN 上的客户端可以向世界发出出站连接,但无法接收入站连接。如果试图在 LAN 客户端机器上运行 Internet 服务,则会遇到问题。解决方法之一是将 NAT 主机上选定的 Internet ports 重定向到 LAN 客户端。
例如,IRC 服务器运行在客户端 A 上,而 Web 服务器运行在客户端 B 上。为了使其正常工作,必须将接收到的连接重定向到相应的机器上的 ports 6667(IRC)和 80(HTTP)。
在内核 NAT 中,所有配置都在 NAT 实例配置中完成。关于内核 NAT 实例可以使用的所有选项的完整列表,请参阅 ipfw(8)。IPFW 语法遵循 natd 的语法。redirect_port 的语法如下:
要配置上述示例设置,参数应为:
将这些参数添加到上述规则集中的 NAT 实例 1 的配置后,TCP ports将被转发到运行 IRC 和 HTTP 服务的 LAN 客户端机器。
可以使用 redirect_port 表示个别ports的范围。例如,tcp 192.168.0.2:2000-3000 2000-3000 会将接收到的所有连接重定向到ports 2000 至 3000 到客户端 A 上的ports 2000 到 3000。
33.4.4.2. 地址重定向
地址重定向在有多个 IP 地址可用时非常有用。可以通过 ipfw(8) 为每个 LAN 客户端分配自己的外部 IP 地址,然后 ipfw(8) 将会用相应的外部 IP 地址重写 LAN 客户端的传出数据包,并将传入特定 IP 地址的所有流量重定向回具体的 LAN 客户端。这也被称为静态 NAT。例如,如果 IP 地址 128.1.1.1,128.1.1.2 和 128.1.1.3 可用,128.1.1.1 可以用作 ipfw(8) 机器的外部 IP 地址,而 128.1.1.2 和 128.1.1.3 则被转发回到 LAN 客户端 A 和 B。
redirect_addr 语法如下,其中 localIP 是 LAN 客户端的内部 IP 地址,publicIP 是该 LAN 客户端对应的外部 IP 地址。
在这个例子中,参数将读取:
像 redirect_port 一样,这些参数被放置在 NAT 实例配置中。通过地址重定向,不需要 port 重定向,因为接收到特定 IP 地址的所有数据都会被重定向。
ipfw(8) 机器上的外部 IP 地址必须处于活动状态并被别名到外部接口。有关详细信息,请参阅 rc.conf(5)。
33.4.4.3. 用户空间 NAT
让我们从一个声明开始:用户空间 NAT 实现:natd(8),比内核 NAT 有更多的开销。为了让 natd(8) 翻译数据包,这些数据包必须从内核复制到用户空间然后再复制回来,这带来了额外的开销,而内核 NAT 则没有这种开销。
要在启动时启用用户空间 NAT 守护进程 natd(8),以下是在 /etc/rc.conf 中的最小配置。其中 natd_interface 设置为连接到互联网的 NIC 的名称。natd(8) 的 rc(8) 脚本将自动检查是否使用动态 IP 地址,并配置自己来处理它。
一般来说,上述针对内核 NAT 解释的规则集也可以与 natd(8) 一起使用。例外情况是内核 NAT 实例 (ipfw -q nat 1 config …) 的配置,这在重组规则 99 中是不需要的,因为其功能包含在 divert 动作中。规则编号 100 和 1000 将需要稍作更改,如下所示。
为了配置port或地址重定向,需要使用与内核 NAT 相似的语法。然而,现在,不像使用内核 NAT 时在我们的规则脚本中指定配置,natd(8)的配置最好在配置文件中完成。为了做到这一点,必须通过/etc/rc.conf 传递额外的标志,该标志指定配置文件的路径。
33.4.5. IPFW 命令
ipfw 可用于在运行时对活动防火墙进行手动、单个规则的添加或删除。使用这种方法的问题在于系统重启时所有更改都会丢失。建议改为将所有规则写入文件,并在启动时使用该文件加载规则,并在该文件更改时替换当前运行的防火墙规则。
ipfw 是一种向控制台屏幕显示运行防火墙规则的有用方法。IPFW 记账功能动态地为每个规则创建一个计数器,计算与规则匹配的每个数据包。在测试规则的过程中,列出具有其计数器的规则是确定规则是否按预期工作的一种方法。
要按顺序列出所有运行中的规则:
要列出所有运行中规则的最后匹配时间戳:
下一个例子列出了匹配规则的会计信息和数据包计数,以及规则本身。第一列是规则号,然后是匹配数据包和字节的数量,然后是规则本身。
要列出动态规则以及静态规则:
还要显示已过期的动态规则。
清零计数器:
仅清零编号为 NUM 的规则的计数器:
33.4.5.1. 记录防火墙消息
即使启用了日志记录功能,IPFW 也不会自动生成任何规则记录。防火墙管理员决定哪些规则在规则集中将被记录下来,并在这些规则中添加 log 关键字。通常只有拒绝规则才会被记录下来。习惯上,将“ipfw default deny everything”规则复制一份,并将 log 关键字作为规则集中的最后一条规则。这样,就可以查看未匹配规则集中任何规则的所有数据包。
记录是一把双刃剑。如果不小心,过多的日志数据或 DoS 攻击可能会填满磁盘并产生大量日志文件。日志消息不仅会写入 syslogd,而且还会显示在 root 控制台屏幕上,并很快变得烦人。
IPFIREWALL_VERBOSE_LIMIT=5 内核选项限制了发送到 syslogd(8)有关给定规则的数据包匹配的连续消息数量。当内核中启用此选项时,关于特定规则的连续消息数量将限制为指定数量。从 200 条相同的日志消息中得不到任何好处。将此选项设置为五时,有关特定规则的五条连续消息将被记录到 syslogd 中,其余相同的连续消息将被计数并以以下短语的形式发布到 syslogd:
所有记录的数据包消息默认写入 /var/log/security,该路径在 /etc/syslog.conf 中定义。
33.4.5.2. 构建规则脚本
大多数经验丰富的 IPFW 用户会创建一个包含规则的文件,并以可作为脚本运行的方式编写它们。这样做的主要好处是防火墙规则可以批量刷新,无需重启系统来激活它们。这种方法在测试新规则时非常方便,因为可以根据需要执行该过程多次。作为脚本,可以使用符号替换来将频繁使用的值替换为多个规则中的值。
此示例脚本与 sh(1), csh(1)和 tcsh(1) shells使用的语法兼容。符号替换字段以美元符号($)为前缀。符号字段没有$前缀。填充符号字段的值必须用双引号("")括起来。
像这样启动规则文件:
规则并不重要,因为此示例的重点是如何填充符号替换字段。
如果上面的示例在 /etc/ipfw.rules 中,规则可以通过以下命令重新加载:
/etc/ipfw.rules 可以放在任何地方,文件可以有任何名称。
同样的事情可以通过手动运行这些命令来实现:
33.4.6. IPFW 内核选项
为了将 IPFW 支持静态编译到定制内核中,请参考配置 FreeBSD 内核中的说明。以下选项适用于定制内核配置文件:
最后更新于