[OpenBSD]

[上一小节:使用CARP和pfsync构建冗余防火墙] [总目录]

PF: 例子: 为家庭和小型办公环境搭建防火墙


目录


环境

本例中, PF运行在OpenBSD计算机上作为一个小型的家庭或办公环境网络的防火墙和NAT网关。整体目标是为内部网络提供Internet访问及有限地允许来自Internet访问防火墙主机, 并且让内部的web服务器为Internet提供服务。这篇文章将提供一个完成此功能所需的完整规则集。

网络

建立的网络环境像这样:
  [ COMP1 ]    [ COMP3 ]
      |            |                               
   ---+------+-----+------- xl0 [ OpenBSD ] fxp0 -------- ( Internet )
             |
         [ COMP2 ]

内部网络上有一些计算机; 上面的图表仅显示了3台, 但实际数量并非如此。 除了COMP3还提供还运行一个小型的web服务器, 这些计算机是标准的工作站, 平时用来上网冲浪、收发电子邮件、聊天等。这些内部计算机使用192.168.0.0/255.255.255.0网段。

运行OpenBSD防火墙的是一台Celeron 300计算机, 带有两块网卡: 一块 3com 3c905B (xl0) 和一块 Intel EtherExpress Pro/100 (fxp0)。防火墙通过一根网线连接到Internet上并且使用NAT将这个连接共享给内部网络。 外部接口上的IP地址是因特网提供商(ISP)动态分配的。

目标

目标是:

准备

这篇文章假设OpenBSD主机已经被正确地配置为一个路由器, 包括验证IP网络设置, Internet连通性, 并且将 sysctl(3) 的变量 net.inet.ip.forwarding 和/或 net.inet6.ip6.forwarding 设定为 "1"。 你也必须用 pfctl(8) 或在 /etc/rc.conf.local 内设置适当的变量启动了PF。

规则集

下面将逐步完成一个实现上述目标的规则集。

Macros

定义下面的macros可以使维护和阅读规则集更容易:
int_if="xl0"

tcp_services="{ 22, 113 }"
icmp_types="echoreq"

comp3="192.168.0.3"

第一行定义了进行过滤的内部的网络接口。 通过定义它们, 如果我们需要将这个系统移动到另一台具有不同硬件的计算机上, 我们只需修改这两行就可以了, 而规则集的其余部分仍将保持有效。 (就本例来说, 外部的接口将使用 egress 接口组来控制。 这是自动设置到所有连接到默认路由的接口上的, 本例中是fxp0). 第二和第三行列出了服务的TCP端口号,这些端口将对Internet开放 (SSH 和 ident/auth) ,而且防火墙允许ICMP类型的数据包通过。 最后一行定义了COMP3的IP地址。

说明: 如果Internet连接需要PPPoE, 那么过滤和NAT将替换到 pppoe0 接口上,而 不是fxp0上。

选项

下面两个选项将设定 block 过滤规则的默认反应,并且会在外部接口上"打开"统计记录:
set block-policy return
set loginterface fxp0

每个Unix系统都有一个 "loopback" 接口。 它是一个用于系统内部的应用程序相互联络的虚拟的网络接口I。 OpenBSD的loopback接口是 lo(4)。 惯例是不在此接口上进行任何过滤。 使用 set skip 可以完成这个任务。

set skip on lo
备注:这应该是一个接口组,但是PF(至少在目前的4.8版本中)只能将子字符串匹配到接口名称上。 这里请注意,我们正在略过所有的 lo 接口, 这样的话, 我们在后面要加上额外的loopback接口吗, 我们不必为规则中修改这部分内容担心。

防火墙规则

我们的规则先以支持使用 ftp-proxy(8) 开始,这样本地网络就可以连接到Internet上的FTP服务器。 当一个FTP连接建立时,通过动态地插入规则完成此任务。 这是通过使用一个 anchor来完成:
anchor "ftp-proxy/*"

现在我们需要添加一条重定向FTP连接的规则,这样使这些连接看起来是由ftp-proxy(8)完成的:

pass in quick on $int_if inet proto tcp to any port ftp \
    rdr-to 127.0.0.1 port 8021

这条规则将访问21端口的FTP连接重定向到在8021端口上运行的一个 ftp-proxy(8) 实例上, 通过使用 quick 关键字, 匹配的数据包就无需再次经过后续规则的审查了。 如果用户正常情况下连接到FTP服务器的其它端口, 那么这里应该用一个list来指定所使用的目标端口, 例如: to any port { 21, 2121 }.

注意:无论是 anchor 或者 ftp-proxy(8) 重定向规则都需要在与NAT有关的 match 所有规则的前面,否则 ftp-proxy(8) 将无法正常工作。

现在我们来考虑一些 match 规则。 就其本身而言, 一条 match 规则无法决定是否放行一个数据包。 而是匹配这条规则的数据包的参数将被记录下来; 这些参数会被以后处理该数据包的所有 pass 规则用到。

这很强大: 像 NATqueueing 这样的参数可以应用到某类特定的数据包上, 然后可以分别为它们定义访问权限。

要为整个内部网络提供NAT功能,我们需要用下面的 match 规则:

match out on egress inet from !(egress:network) to any nat-to (egress:0)

本例中, 这个 "!(egress:network)" 可以简单地替换为一个 "$int_if:network", 不过,如果你添加多个内部接口, 你就不得不设置额外的NAT规则, 而用这种形式, NAT将在所有被保护的接口上处理数据包。

因为外部接口的IP地址是动态分配的, 我们将转换接口放在圆括号内,这样在IP地址变化时,PF可以做出相应的规则调整。 这里使用后缀 :0 是表示如果外部接口有多个地址, 则仅转换第一个地址。

最后, 指定了协议族为 inet (IPv4)。 这避免了转换所有可能收到的 inet6 (IPv6) 数据包。

接下来是控制访问许可。 最开始先是默认拒绝:

block in log

执行到在这条规则时,所有到接口上的进站通讯将被阻止, 即便是来自内网的通讯。 这些数据包还会被logged。 接下来的规则将根据上面所提的目标打开防火墙和所需虚拟接口。

一定要记住, pf可以阻止一个接口上的进站及出站通讯。 如果你只选择过滤一个方向上的通讯而不是尝试着在过滤时直接让什么进来什么出去,可以使你的思路简单一些。 在本例中, 我们选择仅过滤进站通讯, 一旦通讯被允许进入一个接口, 我们并不像阻止其出站, 所以我们的规则如下:

pass out quick
通过使用 quick 关键字, 出站通讯可以避免再经过后续规则的检查, 这样可以提高性能。

阻塞欺骗数据包 是很好的做法:

antispoof quick for { lo $int_if }

现在打开可以被Internet访问的那些服务的端口。 首先, 是去往防火墙本身的通讯:

pass in on egress inet proto tcp from any to (egress) \
    port $tcp_services

在 macro $tcp_services 里指定网络端口可以使你在增加额外的Internet服务时只需简单地编辑 macro 、然后重新加载规则就可以了。UDP 服务也可以通过创建一个 $udp_services macro 并添加一条过滤规则来打开, 和上面的那个类似, 通过指定 proto udp

下一条规则是扑捉来自Internet访问防火墙的TCP端口80的连接请求。访问这个端口的合法请求是来自那些企图访问该网络web服务器的用户。这些连接请求需要重定向到COMP3:

pass in on egress inet proto tcp to (egress) port 80 \
    rdr-to $comp3 synproxy state

为了增加一些安全性, 我们利用 TCP SYN Proxy 来更好地保护web服务器。

需要放行ICMP通讯:

pass in inet proto icmp all icmp-type $icmp_types

$tcp_services macro 类似, 可以简单地通过编辑这个 $icmp_types macro 来改变允许抵达防火墙ICMP数据包的类型。注意:这条规则应用于所有的接口。

现在必须放行却网和来自内部网络的通讯。我们假设内网的用户知道他们在干什么和不会制造麻烦。在很多情况下这个假设未必成立; 许多场合也许需要一个限制更严格的规则集。

pass in on $int_if

TCP, UDP和ICMP通讯被允许离开防火墙前往Internet,这是因为前面的 "pass out" 这条规则。 因为已经保留了状态信息,所以防火墙将放行回程通讯。

完整的规则集

# macros

int_if="xl0"

tcp_services="{ 22, 113 }"
icmp_types="echoreq"

comp3="192.168.0.3"

# options

set block-policy return
set loginterface fxp0
set skip on lo

# FTP Proxy rules

anchor "ftp-proxy/*"

pass in quick on $int_if inet proto tcp to any port ftp \
    rdr-to 127.0.0.1 port 8021

# match rules

match out on egress inet from !(egress) to any nat-to (egress:0)

# filter rules

block in log
pass out quick

antispoof quick for { lo $int_if }

pass in on egress inet proto tcp from any to (egress) \
    port $tcp_services

pass in on egress inet proto tcp to (egress) port 80 \
    rdr-to $comp3 synproxy state

pass in inet proto icmp all icmp-type $icmp_types

pass in on $int_if

[上一小节:使用CARP和pfsync构建冗余防火墙] [总目录]


[back] www@openbsd.org
$OpenBSD: example1.html, v 1.37 2009/01/25 18:09:49 jasper Exp $