> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/man/man7/firewall.7.md).

# firewall.7

`firewall` — FreeBSD 下的简单防火墙

## 名称

`firewall`

## 防火墙基础

防火墙最常用于保护内部网络免受外部网络侵扰，方法是阻止外部网络向内部网络发起任意连接。防火墙也可用于防止外部实体伪造内部 IP 地址，以及将诸如 NFS 或 SMBFS（Windows 文件共享）等服务隔离在局域网段内。

FreeBSD 防火墙系统还能使用 [dummynet(4)](https://github.com/FreeBSD-Ask/freebsd-man-sc/blob/main/man4/dummynet.4.md) 进行带宽限制。当你需要为关键用途保证一定带宽时，这一功能十分有用。例如，如果你通过办公室的 T1（1.5 Mbits/s）线路在 Internet 上进行视频会议，可能希望将其他所有 T1 流量限制为 1 Mbit/s，以便为视频会议连接保留至少 0.5 Mbits 带宽。类似地，如果你在托管设施中运行一个热门的 Web 或 FTP 站点，可能也想要限制带宽，以免提供商收取过高的带宽费用。

最后，FreeBSD 防火墙还可用于转发数据包或更改数据包的下一跳地址，以帮助将它们路由到正确的目的地。数据包转发最常用于支持 NAT（网络地址转换），使使用私有 IP 地址空间的内部网络能够连接到外部进行浏览或其他用途。

构建防火墙看似简单，但大多数人都会出错。最常见的错误是创建排他型防火墙而非包容型防火墙。排他型防火墙允许所有数据包通过，仅拦截与一组规则匹配的数据包。包容型防火墙仅允许与规则集匹配的数据包通过。包容型防火墙比排他型防火墙安全得多，但正确构建的难度略高。第二种最常见的错误是将除你想要放行的特定端口外的所有流量全部黑洞化。TCP/IP 必须能够获取某些类型的 ICMP 错误才能正常工作——例如，用于实现 MTU 发现。此外，许多常见的系统守护进程会进行反向连接到 **auth** 服务，以尝试对发起连接的用户进行身份验证。auth 服务相当危险，但正确的实现方式是对连接尝试返回 TCP 重置，而不是简单地将数据包黑洞化。我们将在下面的示例防火墙部分中讨论这些以及构建防火墙时涉及的其他注意事项。

## IPFW 内核配置

你无需创建自定义内核即可使用 IP 防火墙功能。如果你在 **/etc/rc.conf** 中启用了防火墙（见下文），ipfw 内核模块会在需要时自动加载。但是，如果你比较谨慎，可以使用 **IPFIREWALL** 选项集将 IPFW 直接编译进 FreeBSD 内核。如果编译进内核，ipfw 默认拒绝所有数据包，这意味着如果你不通过 **/etc/rc.conf** 加载一个宽松的规则集，重启进入新内核将使网络断开。如果你不在控制台前，这可能让你无法访问系统。在更新内核到新版本并在更新二进制文件之前重启也是常见的做法，这可能导致 [ipfw(8)](/man/man8/ipfw.8.md) 程序与内核之间出现不兼容，使其无法在启动序列中运行，同样会导致机器无法访问。由于这些问题，还提供了 **IPFIREWALL\_DEFAULT\_TO\_ACCEPT** 内核选项，将默认防火墙改为放行所有数据包。但请注意，使用此选项可能在引导过程中打开一个小窗口，让你的防火墙放行所有数据包。不过，在你熟悉 FreeBSD 防火墙时它是一个不错的选择。一旦你理解了整个机制，应去掉该选项以关闭这一漏洞。第三个选项名为 **IPDIVERT**，允许你使用防火墙将数据包转发到用户程序，如果你想使用 [natd(8)](/man/man8/natd.8.md) 让私有内部网络访问外部世界，它是必需的。如果你希望能够限制某些类型流量的带宽，必须使用 **DUMMYNET** 选项来启用 *ipfw pipe* 规则。

## 基于 IPFW 的防火墙示例

下面是一个基于 ipfw 的防火墙示例，取自一台装有三个网卡接口的机器。fxp0 连接到"暴露的"局域网。该局域网上的机器是双宿主主机，同时拥有内部 10. IP 地址和 Internet 路由的 IP 地址。在我们的示例中，192.100.5.x 表示 Internet 路由的 IP 地址块，而 10.x.x.x 表示内部网络。虽然与示例无关，但 10.0.1.x 分配给 fxp0 上局域网的内部地址块，10.0.2.x 分配给 fxp1 上的局域网，10.0.3.x 分配给 fxp2 上的局域网。

在此示例中，我们希望将三个局域网都与 Internet 隔离，同时让它们相互隔离，并让所有内部地址通过运行在此机器上的 NAT 网关访问 Internet。为使 NAT 网关工作，除了 fxp0 上的一个内部 10. 地址外，防火墙机器还在 fxp0 上拥有两个 Internet 暴露地址：一个暴露地址（未显示）代表机器的官方地址，第二个暴露地址（示例中为 192.100.5.5）代表 NAT 网关会合 IP。我们让示例更复杂一些，给暴露局域网上的机器同时分配内部 10.0.0.x 地址和暴露地址。这样做的思路是，即使在暴露机器上，你也可以将内部服务绑定到内部地址，同时仍然保护这些服务免受 Internet 攻击。在暴露 IP 地址上运行的唯一服务应该只是那些你希望暴露给 Internet 的服务。

需要注意的是，我们示例中的 10.0.0.x 网络并未受我们的防火墙保护。你必须确保 Internet 路由器保护此网络免受外部伪造。此外，在我们的示例中，当通过内部 IP 地址（10.0.0.x）运行服务时，我们几乎让暴露主机在我们的内部网络上享有自由权限。这存在一定的安全风险：如果某个暴露主机被攻破怎么办？为了消除这一风险并强制所有通过 LAN0 进入的流量都通过防火墙，请移除规则 01010 和 01011。

最后，请注意内部地址的使用构成了我们防火墙保护机制的重要部分。在具备适当的伪造防护的情况下，外部任何东西都无法直接访问内部（LAN1 或 LAN2）主机。

```sh
# /etc/rc.conf
#
firewall_enable="YES"
firewall_type="/etc/ipfw.conf"
# 临时端口绑定范围，让
# 通过防火墙。
#
# 注意：通过防火墙运行的高负载服务可能需要
# 更大的本地端口绑定范围。4000-10000 或 4000-30000
# 可能是更好的选择。
ip_portrange_first=4000
ip_portrange_last=5000
...
```

```sh
# /etc/ipfw.conf
#
# FIREWALL：防火墙机器 / nat 网关
# LAN0	    10.0.0.X 和 192.100.5.X（双宿主）
# LAN1	    10.0.1.X
# LAN2	    10.0.2.X
# sw:	    以太网交换机（非管理型）
#
# 192.100.5.x 表示暴露给 Internet 的 IP 地址
# （即 Internet 可路由）。10.x.x.x 表示内部 IP
# （未暴露）
#
#   [LAN1]
#      ^
#      |
#   FIREWALL -->[LAN2]
#      |
#   [LAN0]
#      |
#      +--> 暴露主机 A
#      +--> 暴露主机 B
#      +--> 暴露主机 C
#      |
#   INTERNET（次级防火墙）
#    ROUTER
#      |
#    [Internet]
#
# 未显示：INTERNET 路由器必须包含规则，
# 拒绝所有源 IP 地址在 10. 块中的数据包，
# 以保护双宿主 10.0.0.x 块。在此示例中，
# 暴露主机未受其他保护——它们应仅将暴露服务
# 绑定到暴露 IP，但可以安全地将内部服务
# 绑定到内部 IP。
#
# NAT 网关的工作方式是接收从内部
# IP 地址发送到外部 IP 地址的数据包，
# 并将它们路由到 natd，natd 在端口 8668 上
# 监听。这由规则 00300 处理。从外部世界
# 返回到 natd 的数据包也必须使用规则 00301
# 路由到 natd。为了让示例更有趣，我们注意到
# 不必将内部对暴露主机的请求通过 natd
# （规则 00290），因为那些暴露主机知道我们的
# 10. 网络。这可以减轻 natd 的负载。还要注意，
# 我们当然不必将内部<->内部流量通过 natd 路由，
# 因为这些主机知道如何路由我们的 10. 内部网络。
# 我们从 /etc/rc.local 运行的 natd 命令如下所示。
# 另见 natd 的内核内版本 ipnat。
#
#	natd -s -u -a 208.161.114.67
#
#
add 00290 skipto 1000 ip from 10.0.0.0/8 to 192.100.5.0/24
add 00300 divert 8668 ip from 10.0.0.0/8 to not 10.0.0.0/8
add 00301 divert 8668 ip from not 10.0.0.0/8 to 192.100.5.5
# 跳过规则以避免将高带宽流量
# 通过整个规则集。允许已建立的 tcp 连接通过，
# 并跳过所有传出数据包，前提是
# 我们只需对传入数据包进行防火墙处理。
#
# 允许已建立的 tcp 连接通过会创建一个小
# 漏洞，但为避免防火墙过载可能是必要的。
# 如果你担心，可以将规则移到伪造
# 检查之后。
#
add 01000 allow tcp from any to any established
add 01001 allow all from any to any out via fxp0
add 01001 allow all from any to any out via fxp1
add 01001 allow all from any to any out via fxp2
# 伪造保护。这取决于你对自己的
# 内部网络的信任程度。通过 fxp1 接收的数据包必须来自
# 10.0.1.x。通过 fxp2 接收的数据包必须来自 10.0.2.x。
# 通过 fxp0 接收的数据包不能来自 LAN1 或 LAN2
# 块。我们在这里无法保护 10.0.0.x，Internet 路由器
# 必须为我们完成此任务。
#
add 01500 deny all from not 10.0.1.0/24 in via fxp1
add 01500 deny all from not 10.0.2.0/24 in via fxp2
add 01501 deny all from 10.0.1.0/24 in via fxp0
add 01501 deny all from 10.0.2.0/24 in via fxp0
# 在此示例规则集中，内部主机之间
# 没有限制，即使是在暴露局域网上的主机（只要
# 它们使用内部 IP 地址）也是如此。这代表一个
# 潜在的安全漏洞（如果暴露主机被攻破怎么办？）。
# 如果你希望三个局域网之间实施全面限制，
# 让它们相互隔离以增加安全性，请移除这两条规则。
#
# 如果你希望隔离 LAN1 和 LAN2，但仍想让
# 暴露主机之间自由通信，请移除
# 规则 01010 并保留规则 01011。
#
# （已注释，取消注释以获得更宽松的防火墙）
#add 01010 allow all from 10.0.0.0/8 to 10.0.0.0/8
#add 01011 allow all from 192.100.5.0/24 to 192.100.5.0/24
#
# 特定局域网允许的特定服务
#
# 如果使用更严格的防火墙，允许特定局域网
# 访问运行在防火墙本身的特定服务。
# 在此情况下，我们假设 LAN1 需要访问运行在
# 防火墙上的文件共享。如果使用更宽松的防火墙
# （允许规则 01010），则不需要这些规则。
#
add 01012 allow tcp from 10.0.1.0/8 to 10.0.1.1 139
add 01012 allow udp from 10.0.1.0/8 to 10.0.1.1 137,138
# 允许跨越内部和暴露局域网的通用服务
#
# 我们允许特定的 UDP 服务通过：DNS 查询、ntalk 和 ntp。
# 注意，内部服务由于具有防伪的内部
# IP 地址（10. 网络）而受到保护，因此这些规则
# 实际上仅适用于绑定到暴露 IP 的服务。我们
# 必须允许 UDP 分片，否则较大的分片 UDP 数据包将
# 无法通过防火墙。
#
# 如果我们希望为诸如 DNS 查询响应之类的内容
# 暴露高端临时服务端口，
# 可以使用端口范围，在此示例中为 4000-65535，
# 并且我们在所有暴露机器上设置 /etc/rc.conf 变量
# 以确保它们将临时端口绑定到
# 暴露端口范围（参见上面的 rc.conf 示例）
#
add 02000 allow udp from any to any 4000-65535,domain,ntalk,ntp
add 02500 allow udp from any to any frag
# 为 TCP 允许类似的服务。同样，这些仅适用于
# 绑定到暴露地址的服务。注意：我们允许 'auth'
# 通过，但并未在任何暴露端口上实际运行 identd 服务器。
# 这允许被认证的机器以
# TCP RESET 响应。丢弃数据包会导致
# 连接到执行反向 ident 查询的远程服务时出现延迟。
#
# 注意，我们不允许 tcp 分片通过，并且我们
# 通常不允许分片（UDP 分片除外）。
# 我们期望 TCP mtu 发现协议正常工作，因此
# 不应该有 TCP 分片。
#
add 03000 allow tcp from any to any http,https
add 03000 allow tcp from any to any 4000-65535,ssh,smtp,domain,ntalk
add 03000 allow tcp from any to any auth,pop3,ftp,ftp-data
# 允许某些 ICMP 类型通过非常重要，这里是
# 通用 ICMP 类型列表。注意，让 ICMP 类型 3
# 通过很重要。
#
#	0	回显应答
#	3	目的地不可达（由 TCP MTU 发现使用，又称
#					数据包过大）
#	4	源抑制（通常不允许）
#	5	重定向（通常不允许——可能危险！）
#	8	回显
#	11	超时
#	12	参数问题
#	13	时间戳
#	14	时间戳应答
#
# 有时人们需要允许 ICMP REDIRECT 数据包，即
# 类型 5，但如果你允许它，请确保你的 Internet 路由器
# 禁止它。
add 04000 allow icmp from any to any icmptypes 0,3,8,11,12,13,14
# 记录任何通过的分片。可能有用，
# 否则不必费心。设置一个最终的拒绝规则作为安全保障，
# 以确保你的防火墙是包容型的，无论内核
# 如何配置。
#
add 05000 deny log ip from any to any frag
add 06000 deny all from any to any
```

## 内部和外部服务的端口绑定

我们已经提到了多宿主主机和将服务绑定到内部或外部地址，但还未真正解释。当一台主机分配了多个 IP 地址时，你可以将该主机上运行的服务绑定到特定的 IP 或接口，而不是所有 IP。以防火墙机器为例：拥有三个接口和其中一个接口上的两个暴露 IP 地址，防火墙机器通过 5 个不同的 IP 地址被识别（10.0.0.1、10.0.1.1、10.0.2.1、192.100.5.5 以及比如说 192.100.5.1）。如果防火墙向 Windows 局域网段（假设是 LAN1）提供文件共享服务，你可以使用 samba 的 'bind interfaces' 指令将其专门绑定到 LAN1 IP 地址。这样文件共享服务就不会对其他局域网段开放。NFS 同理。如果 LAN2 上有你的 UNIX 工程工作站，你可以告诉 nfsd 专门绑定到 10.0.2.1。你几乎可以为机器上的每个服务指定绑定方式，对于不提供此选项的服务，你可以使用一个轻量级的 [jail(8)](/man/man8/jail.8.md) 来间接绑定。

## 参见

[dummynet(4)](https://github.com/FreeBSD-Ask/freebsd-man-sc/blob/main/man4/dummynet.4.md), ipnat(5), [rc.conf(5)](https://github.com/FreeBSD-Ask/freebsd-man-sc/blob/main/man5/rc.conf.5.md), smb.conf(5) (`ports/net/samba`), samba(7) (`ports/net/samba`), [config(8)](/man/man8/config.8.md), [ipfw(8)](/man/man8/ipfw.8.md), ipnat(8), [jail(8)](/man/man8/jail.8.md), [natd(8)](/man/man8/natd.8.md), nfsd(8)

## 延伸阅读

**`Ipfilter`** ipf(5), ipf(8), ipfstat(8)

**`Packet`** [pf.conf(5)](https://github.com/FreeBSD-Ask/freebsd-man-sc/blob/main/man5/pf.conf.5.md), pfctl(8), pflogd(8)

## 历史

`Packet` 手册页最初由 Matthew Dillon 编写，首次出现于 FreeBSD 4.3，2001 年 5 月。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/man/man7/firewall.7.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
