过滤网桥

商标

FreeBSD 是 FreeBSD 基金会的注册商标。

3Com 和 HomeConnect 是 3Com 公司的注册商标。

Intel、Celeron、Centrino、Core、EtherExpress、i386、i486、Itanium、Pentium 和 Xeon 是英特尔公司或其子公司在美国和其他国家/地区的商标或注册商标。

制造商和销售商用来区分其产品的许多名称都被宣称为商标。在本文档中出现这些名称的地方,如果 FreeBSD 项目知道商标声明,则这些名称后面会加上“™”或“®”符号。

摘要

通常,将一个物理网络(如以太网)划分为两个独立的段,而无需创建子网并使用路由器将它们连接在一起,这很有用。以这种方式连接两个网络的设备称为网桥。具有两个网络接口的 FreeBSD 系统足以充当网桥。

网桥的工作原理是扫描连接到其每个网络接口的设备的 MAC 层(以太网地址)的地址,然后仅当源和目标位于不同的段时才转发两个网络之间的流量。从许多方面来看,网桥类似于只有两个端口的以太网交换机。


1. 为什么使用过滤网桥?

由于宽带互联网连接(xDSL)成本降低,以及可用 IPv4 地址减少,越来越多的公司 24 小时不间断地连接到互联网,并且使用少量(有时甚至不是 2 的幂)IP 地址。在这些情况下,通常需要一个防火墙来过滤来自和走向互联网的传入和传出流量,但是基于路由器的包过滤解决方案可能不适用,这可能是由于子网问题、路由器由连接供应商(ISP)拥有,或者因为它不支持此类功能。在这些情况下,强烈建议使用过滤网桥。

基于网桥的防火墙可以在 xDSL 路由器和您的以太网集线器/交换机之间配置和插入,而不会出现任何 IP 编址问题。

2. 如何安装

向 FreeBSD 系统添加网桥功能并不困难。从 4.5 版本开始,可以将此类功能作为模块加载,而不是必须重新构建内核,从而大大简化了过程。在以下小节中,我将解释两种安装方式。

不要同时遵循这两条说明:一个过程排除另一个过程。根据您的需求和能力选择最佳选择。

在继续之前,请确保至少有两个以太网卡都支持接收和发送的混杂模式,因为它们必须能够发送任何地址的以太网数据包,而不仅仅是它们自己的地址。此外,为了获得良好的吞吐量,网卡应该是 PCI 总线主控网卡。最佳选择仍然是 Intel EtherExpress™ Pro,其次是 3Com® 3c9xx 系列。为了简化防火墙配置,使用来自不同制造商(使用不同驱动程序)的两张网卡可能很有用,以便清楚地区分哪个接口连接到路由器,哪个接口连接到内部网络。

2.1. 内核配置

所以您已决定使用较旧但经过良好测试的安装方法。首先,您必须将以下行添加到内核配置文件中

options BRIDGE
options IPFIREWALL
options IPFIREWALL_VERBOSE

第一行是编译网桥支持,第二行是防火墙,第三行是防火墙的日志记录功能。

现在需要构建和安装新的内核。您可以在 FreeBSD 手册的构建和安装自定义内核部分找到详细说明。

2.2. 模块加载

如果您选择使用新的、更简单的安装方法,现在唯一需要做的就是在 /boot/loader.conf 中添加以下行

bridge_load="YES"

这样,在系统启动期间,bridge.ko 模块将与内核一起加载。不需要为 ipfw.ko 模块添加类似的行,因为它将在执行下一节中的步骤后自动加载。

3. 最终准备

在重新引导以加载新内核或所需模块(根据先前选择的安装方法)之前,您必须对 /etc/rc.conf 配置文件进行一些更改。防火墙的默认规则是拒绝所有 IP 数据包。最初,我们将设置一个 open 防火墙,以验证其操作,而不会出现与包过滤相关的任何问题(如果您要远程执行此过程,此配置将避免您与网络隔离)。将这些行放入 /etc/rc.conf

firewall_enable="YES"
firewall_type="open"
firewall_quiet="YES"
firewall_logging="YES"

第一行将启用防火墙(如果它未在内核中编译,则将加载模块 ipfw.ko),第二行将其设置为 open 模式(如 /etc/rc.firewall 中所述),第三行是不显示规则加载,第四行是启用日志支持。

关于网络接口的配置,最常用的方法是仅为其中一个网卡分配 IP,但即使两个接口或没有任何接口配置 IP,网桥也能正常工作。在最后一种情况下(无 IP),网桥机器将更加隐藏,因为无法从网络访问:要配置它,您必须从控制台或通过与网桥分开的第三个网络接口登录。有时,在系统启动期间,某些程序需要网络访问,例如域名解析:在这种情况下,需要为外部接口(连接到互联网的接口,DNS 服务器驻留在该接口上)分配 IP,因为网桥将在启动过程结束时激活。这意味着 fxp0 接口(在本例中)必须在 /etc/rc.conf 文件的 ifconfig 部分中提及,而 xl0 则不必。除非在启动过程中应用程序应该访问两个以太网段上的服务,否则为两个网卡都分配 IP 并没有多大意义。

还有另一件重要的事情需要了解。当在以太网上运行 IP 时,实际上使用了两个以太网协议:一个是 IP,另一个是 ARP。ARP 将主机 IP 地址转换为其以太网地址(MAC 层)。为了允许网桥分隔的两个主机之间的通信,网桥必须转发 ARP 数据包。此协议不包含在 IP 层中,因为它仅存在于以太网上的 IP 中。FreeBSD 防火墙仅在 IP 层上进行过滤,因此所有非 IP 数据包(包括 ARP)都将在未经过滤的情况下转发,即使防火墙配置为不允许任何内容。

现在是重新引导系统并像以前一样使用它的时间了:将会有关于网桥和防火墙的一些新消息,但网桥不会被激活,并且防火墙处于 open 模式,不会阻止任何操作。

如果存在任何问题,您应该在继续之前解决它们。

4. 启用网桥

此时,要启用网桥,您必须执行以下命令(要有灵活性,用您自己的名称替换两个网络接口 fxp0xl0 的名称)

# sysctl net.link.ether.bridge.config=fxp0:0,xl0:0
# sysctl net.link.ether.bridge.ipfw=1
# sysctl net.link.ether.bridge.enable=1

第一行指定网桥应该激活哪些接口,第二行将在网桥上启用防火墙,最后一行将启用网桥。

此时,您应该能够将机器插入两组主机之间,而不会影响它们之间的任何通信能力。如果是这样,下一步是将这些行的 net.link.ether.bridge.[blah]=[blah] 部分添加到 /etc/sysctl.conf 文件中,以使其在启动时执行。

5. 配置防火墙

现在是时候创建您自己的包含自定义防火墙规则的文件,以保护内部网络的安全了。这样做会有一些复杂性,因为并非所有防火墙功能都可以在桥接数据包上使用。此外,正在转发的包和本地机器接收的包之间存在差异。通常,传入数据包仅通过防火墙运行一次,而不是两次,如通常情况那样;事实上,它们只在收到时被过滤,因此使用 outxmit 的规则将永远不会匹配。就我个人而言,我使用 in via,这是一个较旧的语法,但在您阅读时有意义。另一个限制是您只能对网桥过滤的数据包使用 passdrop 命令。像 divertforwardreject 这样的复杂操作不可用。此类选项仍然可以使用,但仅适用于到或来自网桥机器本身的流量(如果它有 IP 地址)。

FreeBSD 4.0 中的新增功能是状态过滤的概念。对于 UDP 流量来说,这是一个很大的改进,UDP 流量通常是发出请求,随后不久会以相同的 IP 地址和端口号(但源和目标相反)进行响应。对于没有状态保持的防火墙,几乎无法将这种类型的流量视为单个会话。但是,对于可以“记住”传出 UDP 数据包并在接下来的几分钟内允许响应的防火墙,处理 UDP 服务非常简单。以下示例说明了如何执行此操作。也可以对 TCP 数据包执行相同的操作。这使您可以避免一些拒绝服务攻击和其他恶意技巧,但它通常也会使您的状态表快速增长。

让我们来看一个示例配置。首先要注意,在/etc/rc.firewall 文件顶部,已经存在环回接口lo0的标准规则,所以我们不再需要关心它们。自定义规则应该放在一个单独的文件中(例如/etc/rc.firewall.local),并在系统启动时加载,方法是修改/etc/rc.conf 中定义open防火墙的那一行。

firewall_type="/etc/rc.firewall.local"

您必须指定完整路径,否则它将不会被加载,存在与网络隔离的风险。

在我们的示例中,假设fxp0 接口连接到外部(互联网),xl0 接口连接到内部(局域网)。网桥机器的 IP 地址为 1.2.3.4(您的 ISP 不可能给您这样的地址,但对于我们的示例来说,它很合适)。

# Things that we have kept state on before get to go through in a hurry
add check-state

# Throw away RFC 1918 networks
add drop all from 10.0.0.0/8 to any in via fxp0
add drop all from 172.16.0.0/12 to any in via fxp0
add drop all from 192.168.0.0/16 to any in via fxp0

# Allow the bridge machine to say anything it wants
# (if the machine is IP-less do not include these rows)
add pass tcp from 1.2.3.4 to any setup keep-state
add pass udp from 1.2.3.4 to any keep-state
add pass ip from 1.2.3.4 to any

# Allow the inside hosts to say anything they want
add pass tcp from any to any in via xl0 setup keep-state
add pass udp from any to any in via xl0 keep-state
add pass ip from any to any in via xl0

# TCP section
# Allow SSH
add pass tcp from any to any 22 in via fxp0 setup keep-state
# Allow SMTP only towards the mail server
add pass tcp from any to relay 25 in via fxp0 setup keep-state
# Allow zone transfers only by the secondary name server [dns2.nic.it]
add pass tcp from 193.205.245.8 to ns 53 in via fxp0 setup keep-state
# Pass ident probes.  It is better than waiting for them to timeout
add pass tcp from any to any 113 in via fxp0 setup keep-state
# Pass the "quarantine" range
add pass tcp from any to any 49152-65535 in via fxp0 setup keep-state

# UDP section
# Allow DNS only towards the name server
add pass udp from any to ns 53 in via fxp0 keep-state
# Pass the "quarantine" range
add pass udp from any to any 49152-65535 in via fxp0 keep-state

# ICMP section
# Pass 'ping'
add pass icmp from any to any icmptypes 8 keep-state
# Pass error messages generated by 'traceroute'
add pass icmp from any to any icmptypes 3
add pass icmp from any to any icmptypes 11

# Everything else is suspect
add drop log all from any to any

之前配置过防火墙的用户可能会注意到缺少了一些东西。特别是,没有反欺骗规则,事实上我们没有添加

add deny all from 1.2.3.4/8 to any in via fxp0

也就是说,丢弃来自外部声称来自我们网络的数据包。通常这样做是为了确保没有人尝试通过生成看起来来自内部的恶意数据包来逃避数据包过滤器。问题在于,外部接口上至少有一个主机您不想忽略:路由器。但通常情况下,ISP 会在其路由器上进行反欺骗,所以我们不必太担心。

最后一条规则看起来与默认规则完全相同,即不允许通过任何未明确允许的内容。但它们之间存在差异:所有可疑流量都将被记录。

有两条规则用于将 SMTP 和 DNS 流量传递到邮件服务器和名称服务器(如果您有的话)。显然,整个规则集应该根据个人喜好进行调整,这只是一个具体的示例(规则格式在ipfw(8) 手册页中有准确描述)。请注意,为了使“中继”和“ns”工作,名称服务查找必须在网桥启用之前进行。这是一个确保您在正确的网卡上设置 IP 的示例。或者,可以指定 IP 地址而不是主机名(如果机器没有 IP 地址,则需要这样做)。

习惯于设置防火墙的人可能也习惯于为 ident 数据包(TCP 端口 113)设置resetforward 规则。不幸的是,这在网桥中不是一个可用的选项,所以最好的方法是简单地将它们传递到其目标。只要目标机器未运行 ident 守护进程,这相对来说是无害的。另一种方法是丢弃端口 113 上的连接,这会给 IRC 等服务带来一些问题(ident 探测必须超时)。

您可能注意到的另一件稍微奇怪的事情是,有一条规则允许网桥机器通信,另一条规则用于内部主机。请记住,这是因为这两组流量将通过内核和数据包过滤器采用不同的路径。内部网络将通过网桥,而本地机器将使用普通的 IP 堆栈进行通信。因此,这两条规则用于处理不同的情况。in via fxp0 规则适用于这两条路径。通常,如果您在整个过滤器中使用in via 规则,则需要为本地生成的数据包创建例外,因为它们没有通过我们的任何接口传入。

6. 贡献者

本文的许多部分都取自 Nick Sayer 编辑的关于桥接的旧文本,并进行了更新和改编。Steve Peterson 关于桥接的介绍也给了我们启发。

非常感谢 Luigi Rizzo 在 FreeBSD 中实现了桥接代码,以及他花时间回答我所有相关问题。

还要感谢 Tom Rhodes 审阅了我将本文从意大利语(原文)翻译成英语的工作。


最后修改于:2023 年 12 月 29 日,作者:Benedict Reuschling