--------------------------------------------------------------------------------
目录
环境
网络
目标
准备
规则集
宏
选项
锚
网络地址转换
重定向
过滤规则
完整的规则集
--------------------------------------------------------------------------------
环境
本例中, 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)动态分配的。
目标
目标是:
为每台内部计算机提供不受限制的Internet访问。
使用一个"默认拒绝"过滤规则集。
允许下列来自Internet的进站通讯到防火墙:
SSH (TCP port 22): 这用来从外部维护防火墙计算机。
Auth/Ident (TCP port 113): 有些服务使用的, 例如SMTP 和 IRC。
ICMP Echo Requests: ping(8).使用的IMCP数据包。
重定向访问端口80的TCP的连接(这些连接要访问web服务器)到计算机COMP3。同时也允许这些通讯通过防火墙访问COMP3。
记录外部接口的日志。
默认, 向被阻止数据包的发送者回应一条TCP RST或者ICMP Unreachable信息。
力所能及地简化规则集并使它易于维护。
准备
这篇文章假设OpenBSD主机已经完全被配置为一个路由器, 包括验证IP网络设置, Internet连通性, 并且将 sysctl(3) 的变量 net.inet.ip.forwarding 和/或 net.inet6.ip6.forwarding 设定为 "1"。你也必须用 pfctl(8) 或在 /etc/rc.conf.local 内设置适当的变量启动了PF。
规则集
我们下面将逐步完成规则集, 实现上述目标。
宏
下面定义的宏使维护和阅读这个规则集更简单:
代码: 全选
ext_if="fxp0"
int_if="xl0"
tcp_services="{ 22, 113 }"
icmp_types="echoreq"
comp3="192.168.0.3"
前面两行定义了将要进行过滤的网络接口。这里通过定义它们, 如果我们必须将这个系统移到另一台有不同硬件的计算机上, 我们可以仅修改这两行, 而其它的规则组还可以使用。第三第四行列出了需要对Internet开放提供服务的TCP端口号(SSH 和 ident/auth)和允许通过防火墙的ICMP类型数据包。最后一行定义了COMP3的IP地址。
注意: 如果Internet连接需要 PPPoE, 则过滤和NAT将转到tun0接口上, 而非在fxp0上。
选项
下面两行设置了阻止过滤规则的默认的回应动作并设定了在外部接口上的记录日志:
代码: 全选
set block-policy return
set loginterface $ext_if
每个Unix系统全有"loopback"(还回) 接口。它是一个虚拟的网络接口, 应用程序用此接口在系统内部相同交流。OpenBSD的回环接口是 lo(4)。惯例是不在此接口上进行过滤, 使用 set skip 可以完成这项工作。
代码: 全选
set skip on lo
注意, 我们这里略过了对所有lo接口组的过滤, 这样的话, 我们以后应该增加别的回环接口呢, 我们不必为在规则中修改这部分内容担心。
净化
没有理由不对进站通讯净化, 这也是推荐的, 只用简单的一行:
代码: 全选
scrub in
网络地址转换
下面的规则可以为整个内部网络做NAT:
代码: 全选
nat on $ext_if from !($ext_if) to any -> ($ext_if)
在本例中"!($ext_if)" 可以简单地用"$int_if"代替, 但是如果你增加了多个内部接口, 你需要增加额外的NAT规则, 反之, 如果使用这个结构(译者注:不修改原来的语法结构, 还是用!($ext_if)), NAT会处理所有保护的接口。
因为外部接口的IP地址是动态指派的, 圆括号告诉PF注意它里面的转换接口的IP地址变化。(译者注:如前所述, PF会根据变化及时更新自己的规则)
因为我们想运行FTP代理, 我们也把NAT anchor 放进去:
代码: 全选
nat-anchor "ftp-proxy/*"
重定向
第一个重定向规则是为 ftp-proxy(8) 设置的, 这样本地网络的FTP客户端就可以连接到Internet上的FTP服务器。
代码: 全选
rdr-anchor "ftp-proxy/*"
rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021
注意:这条规则仅能适用于到端口21的FTP连接。如果一台FTP服务器设定的是其它的通讯端口, 那么应该使用一个列表指定的目标端口, 例如: from any to any port { 21, 2121 }。
最后的一条重定向规则捕获任何来自Internet去往防火墙TCP 80端口的连接。那些合法地访问这个端口的连接请求是想访问内部网络的web服务器, 这些连接请求应该重定向到COMP3:
代码: 全选
rdr on $ext_if proto tcp from any to any port 80 -> $comp3
过滤规则
最开始一默认拒绝:
代码: 全选
block in
这里, 所有的进站连接请求将被阻止, 即使是来自内部网络。后面的规则为实现上面的目标会开放防火墙上的所需端口, 同时也开放所需的虚拟接口。
别忘了, PF可以在一个接口上阻止进站或出站的通讯, 你可以轻松地指定PF在一个方向上过滤通信(进站或出站), 而不是让进站通讯的通讯直进直出。本例中, 我们选择过滤进站通讯, 但是一旦通讯被允许进入一个接口, 我们并未打算阻止其离开, 所以我们定义了下面的规则:
代码: 全选
pass out keep state
我们需要为ftp-proxy(8)提供一个锚:
代码: 全选
anchor "ftp-proxy/*"
它可以很好地 阻止欺骗地址:
代码: 全选
antispoof quick for { lo $int_if }
现在打开从Internet上访问网络服务所需的端口。首先, 要允许通讯到防火墙本身:
代码: 全选
pass in on $ext_if inet proto tcp from any to ($ext_if) \
port $tcp_services flags S/SA keep state
用宏 $tcp_services 指定网络端口, 它的优点是当你想添加新的服务时仅需简单地编辑这个宏并重新载入过滤规则。通过建立一个宏 $udp_services 和添加一条过滤规则也可以开放UDP服务, 和上面相似, 指定为 proto udp。
除了增加一条rdr规则将访问web服务器的通讯转发到COMP3, 我们也必须允许这个通讯通过防火墙:
代码: 全选
pass in on $ext_if inet proto tcp from any to $comp3 port 80 \
flags S/SA synproxy state
为了提高一点点儿安全, 我们将利用 握手代理 进一步保护web服务器。
ICMP通讯需要放行:
代码: 全选
pass in inet proto icmp all icmp-type $icmp_types keep state
和宏 $tcp_services 相似, 也可以简单地通过编辑宏 $icmp_types 来定义允许抵达防火墙的ICMP数据包类型。注意这条规则应用于所有网络接口。
现在必须放行内部网络的进出通讯, 我们这里假设内部网用户知道他们在干什么和不会惹麻烦, 这可能不符合实际情况;多数环境需要一个限制更严格的规则集。
pass in quick on $int_if
TCP, UDP, 和 ICMP 通讯被允许通过防火墙去往Internet, 因为前面定义了"pass out keep state" 这行。状态信息被保持了, 所以回程数据包可以通过防火墙。
完整的规则集
# macros
ext_if="fxp0"
int_if="xl0"
tcp_services="{ 22, 113 }"
icmp_types="echoreq"
comp3="192.168.0.3"
# options
set block-policy return
set loginterface $ext_if
set skip on lo
# scrub
scrub in
# nat/rdr
nat on $ext_if from !($ext_if) -> ($ext_if:0)
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"
rdr pass on $int_if proto tcp to port ftp -> 127.0.0.1 port 8021
rdr on $ext_if proto tcp from any to any port 80 -> $comp3
# filter rules
block in
pass out keep state
anchor "ftp-proxy/*"
antispoof quick for { lo $int_if }
pass in on $ext_if inet proto tcp from any to ($ext_if) \
port $tcp_services flags S/SA keep state
pass in on $ext_if inet proto tcp from any to $comp3 port 80 \
flags S/SA synproxy state
pass in inet proto icmp all icmp-type $icmp_types keep state
pass in quick on $int_if