发新话题
打印

防火墙技术(PF:OpenBSD数据包过滤)(二)

防火墙技术(PF:OpenBSD数据包过滤)(二)

PF: 网络地址转换(NAT)

------------------------------------------------------------------------------

目录

* 简介
* NAT 如何工作
* NAT 和包过滤
* IP 转发
* 配置 NAT
* 双向映射 (1:1 映射)
* Translation Rule Exceptions
* 检查 NAT 状态

------------------------------------------------------------------------------

简介
网络地址转换是映射整个网络(或者多个网络)到单个IP地址的方法。当你的ISP分配给你的IP地址数目少于你要连上互联网的计算机数目时,NAT是必需的。NAT在RFC1631中描述。

NAT允许使用RFC1918中定义的保留地址族。典型情况下,你的内部网络可以下面的地址族:
10.0.0.0/8 (10.0.0.0 - 10.255.255.255)
172.16.0.0/12 (172.16.0.0 - 172.31.255.255)
192.168.0.0/16 (192.168.0.0 - 192.168.255.255)

担任NAT任务的openbsd系统必须至少要2块网卡,一块连接到因特网,另一块连接内部网络。NAT会转换内部网络的所有请求,使它们看起来象是来自进行NAT工作的openbsd主机系统。

NAT如何工作

当内部网络的一个客户端连接因特网上的主机时,它发送目的是那台主机的IP数据包。这些数据包包含了达到连接目的的所有信息。NAT的作用和这些信息相关。

* 源 IP 地址 (例如, 192.168.1.35)
* 源 TCP 或者 UDP 端口 (例如, 2132)
当数据包通过NAT网关时,它们会被修改,使得数据包看起来象是从NAT网关自己发出的。NAT网关会在它的状态表中记录这个改变,以便:a)将返回的数据包反向转换和b)确保返回的数据包能通过防火墙而不会被阻塞。例如,会发生下面的改变:

* 源 IP: 被网关的外部地址所替换(例如, 24.5.0.5)
* 源端口:被随机选择的网关没有在用的端口替换 (例如, 53136)

内部主机和因特网上的主机都不会意识到发生了这个转变步骤。对于内部主机,NAT系统仅仅是个因特网网关,对于因特网上的主机,数据包看起来直接来自NAT系统,它完全不会意识到内部工作站的存在。

当因特网网上的主机回应内部主机的数据包时,它会使用NAT网关机器的外部地址和转换后的端口。然后NAT网关会查询状态表来确定返回的数据包是否匹配某个已经建立的连接。基于IP地址和端口的联合找到唯一匹配的记录告诉PF这个返回的数据包属于内部主机192.168.1.35。然后PF会进行和出去的数据包相反的转换过程,将返回的数据包传递给内部主机。

ICMP数据包的转换也是类似的,只是不进行源端口的修改。

NAT和包过滤

注意:转换后的数据包仍然会通过过滤引擎,根据定义的过滤规则进行阻塞或者通过。唯一的例外是如果nat规则中使用了pass关键字,会使得经过nat的数据包直接通过过滤引擎。

还要注意由于转换是在过滤之前进行,过滤引擎所看到的是上面“nat如何工作”中所说的经过转换后的ip地址和端口的数据包。

IP 转发

由于NAT经常在路由器和网关上使用,因此IP转发是需要的,使得数据包可以在openbsd机器的不同网络接口间传递。IP转发可以通过sysctl(3)命令打开:

# sysctl -w net.inet.ip.forwarding=1
# sysctl -w net.inet6.ip6.forwarding=1 (if using IPv6)

要使这个变化永久生效,可以增加如下行到/etc/sysctl.conf文件中:

net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1

这些行是本来就存在的,但默认安装中被#前缀注释掉了。删除#,保存文件,IP转发在机器重启后就会发生作用。

配置NAT

一般的NAT规则格式在pf.conf文件中是这个样子:

nat [pass] on interface [af] from src_addr [port src_port] to \
dst_addr [port dst_port] -> ext_addr [pool_type] [static-port]

nat
开始NAT规则的关键字。
pass
使得转换后的数据包完全绕过过滤规则。
interface
进行数据包转换的网络接口。
af
地址类型,inet代表Ipv4,inet6代表Ipv6。PF通常能根据源/目标地址自动确定这个参数。
src_addr
被进行转换的IP头中的源(内部)地址。源地址可以指定为:
+ 单个的Ipv4或者Ipv6地址.
+ CIDR 网络地址.
+ 能够在规则集载入时通过DNS解析到的合法的域名,IP地址会替代规则中的域名。
+ 网络接口名称。网络接口上配置的所有ip地址会替代进规则中。
+ 带有/掩码(例如/24)的网络接口的名称。每个根据掩码确定的CIDR网络地址都会被替代进规则中。.
+ 带有()的网络接口名称。这告诉PF如果网络接口的IP地址改变了,就更新规则集。这个对于使用DHCP或者拨号来获得IP地址的接口特别有用,IP地址改变时不需要重新载入规则集。
+ 带有如下的修饰词的网络接口名称:
o :network - 替代CIDR网络地址段 (例如, 192.168.0.0/24)
o :broadcast - 替代网络广播地址(例如, 192.168.0.255)
o :peer - 替代点到点链路上的ip地址。

另外,:0修饰词可以附加到接口名称或者上面的修饰词后面指示PF在替代时不包括网络接口的其余附加(alias)地址。这些修饰词也可以在接口名称在括号()内时使用。例如:fxp0:network:0

+ 表.
+ 上面的所有项但使用!(非)修饰词
+ 使用列表的一系列地址.
+ 关键字 any 代表所有地址
+ 关键字 all 是 from any to any的缩写。

src_port
4层数据包头中的源端口。端口可以指定为:
+ 1 到 65535之间的整数
+ /etc/services中的合法服务名称
+ 使用列表的一系列端口
+ 一个范围:
o != (不等于)
o < (小于)
o > (大于)
o <= (小于等于)
o >= (大于等于)
o >< (范围)
o <> (反转范围)

最后2个是二元操作符(他们需要2个参数),在范围内不包括参数。

o : (inclusive range)

inclusive range 也是二元操作符但范围内包括参数。

Port选项在NAT规则中通常不使用,因为目标通常会NAT所有的流量而不过端口是否在使用。
dst_addr
被转换数据包中的目的地址。目的地址类型和源地址相似。
dst_port
4层数据包头中的目的目的端口,目的端口类型和源端口类型相似。
ext_addr
NAT网关上数据包被转换后的外部地址。外部地址可以是:
+ 单个的Ipv4或者Ipv6地址.
+ CIDR 网络地址.
+ 能够在规则集载入时通过DNS解析到的合法的域名,IP地址会替代规则中的域名。
+ 网络接口名称。网络接口上配置的所有ip地址会替代进规则中。
+ 带有/掩码(例如/24)的网络接口的名称。每个根据掩码确定的CIDR网络地址都会被替代进规则中。.
+ 带有()的网络接口名称。这告诉PF如果网络接口的IP地址改变了,就更新规则集。这个对于使用DHCP或者拨号来获得IP地址的接口特别有用,IP地址改变时不需要重新载入规则集。
+ 带有如下的修饰词的网络接口名称:
o :network - 替代CIDR网络地址段 (例如, 192.168.0.0/24)
o :broadcast - 替代网络广播地址(例如, 192.168.0.255)
o :peer - 替代点到点链路上的ip地址。

另外,:0修饰词可以附加到接口名称或者上面的修饰词后面指示PF在替代时不包括网络接口的其余附加(alias)地址。这些修饰词也可以在接口名称在括号()内时使用。例如:fxp0:network:0
+ 使用列表的一系列地址.
pool_type
指定转换后的地址池的类型
static-port
告诉PF不要转换TCP和UDP数据包中的源端口


这条规则最简单的形式如下:
nat on tl0 from 192.168.1.0/24 to any -> 24.5.0.5

这条规则是说对从tl0网络接口上到来的所有192.168.1.0/24网络的数据包进行NAT,将源地址转换为24.5.0.5。

尽管上面的规则是正确的,但却不是推荐的形式。因为维护起来有困难,当内部或者外部网络有变化时都要修改这一行。比较一下下面这条比较容易维护的规则:(tl0时外部,dc0是内部):

nat on tl0 from dc0:network to any -> tl0

优点是相当明显的,你可以任意改变2个接口的IP地址而不用改变这条规则。

象上面这样在地址转换中使用接口名称时,IP地址在pf.conf文件载入时确定,并不是凭空的。如果你使用DHCP还配置外部地址,这会存在问题。如果你分配的IP地址改变了,NAT仍然会使用旧的IP地址转换出去的数据包。这会导致对外的连接停止工作。为解决这个问题,应该给接口名称加上括号,告诉 PF自动更新转换地址。

nat on tl0 from dc0:network to any -> (tl0)

这个方法对IPv4 和 IPv6地址都用效。

双向映射 (1:1 映射)

双向映射可以通过使用binat规则建立。Binat规则建立一个内部地址和外部地址一对一的映射。这会很有用,比如,使用独立的外部IP地址用内部网络里的机器提供web服务。从因特网到来连接外部地址的请求被转换到内部地址,同时由(内部)web服务器发起的连接(例如DNS查询)被转换为外部地址。和NAT规则不过,binat规则中的tcp和udp端口不会被修改。


例如:

web_serv_int = "192.168.1.100"
web_serv_ext = "24.5.0.6"

binat on tl0 from $web_serv_int to any -> $web_serv_ext

转换规则例外设置

使用no关键字可以在转换规则中设置例外。例如,如果上面的转换规则修改成这样:

no nat on tl0 from 192.168.1.10 to any
nat on tl0 from 192.168.1.0/24 to any -> 24.2.74.79

则除了192.168.1.10以外,整个192.168.1.0/24网络地址的数据包都会转换为外部地址24.2.74.79。

注意第一条匹配的规则起了决定作用,如果是匹配有no的规则,数据包不会被转换。No关键字也可以在binat和rdr规则中使用。

检查 NAT 状态

要检查活动的NAT转换可以使用pfctl(8)带-s state 选项。这个选项列出所有当前的NAT会话。
# pfctl -s state
fxp0 TCP 192.168.1.35:2132 -> 24.5.0.5:53136 -> 65.42.33.245:22 TIME_WAIT:TIME_WAIT
fxp0 UDP 192.168.1.35:2491 -> 24.5.0.5:60527 -> 24.2.68.33:53 MULTIPLE:SINGLE

解释 (对第一行):

fxp0
显示状态绑定的接口。如果状态是浮动的,会出现self字样。

TCP
连接使用的协议。

192.168.1.35:2132
内部网络中机器的IP地址 (192.168.1.35),源端口(2132)在地址后显示,这个也是被替换的IP头中的地址。 of the machine on the internal network. The
source port (2132) is shown after the address. This is also the address
that is replaced in the IP header.

24.5.0.5:53136
IP 地址 (24.5.0.5) 和端口 (53136) 是网关上数据包被转换后的地址和端口。

65.42.33.245:22
IP 地址 (65.42.33.245) 和端口 (22) 是内部机器要连接的地址和端口。

TIME_WAIT:TIME_WAIT
这表明PF认为的目前这个TCP连接的状态。

------------------------------------------------------------------------------
$OpenBSD: nat.html,v 1.15 2004/05/07 01:55:23 nick Exp $
==============================================================================

PF: 重定向 (端口转发)

------------------------------------------------------------------------------

目录

* 简介
* 重定向和包过滤
* 安全隐患
* 重定向和反射
+ 水平分割 DNS
+ 将服务器移到独立的本地网络
+ TCP 代理
+ RDR 和 NAT 结合

------------------------------------------------------------------------------

简介

如果在办公地点应用了NAT,内部网所有的机器都可以访问因特网。但如何让NAT网关后面的机器能够被从外部访问?这就是重定向的来源。重定向允许外来的流量发送到NAT网关后面的机器。

看个例子:

rdr on tl0 proto tcp from any to any port 80 -> 192.168.1.20

这一行重定向了TCP端口80(web服务器)流量到内部网络地址192.168.1.20。因此,即使192.168.1.20在网关后面的内部网络,外部仍然能够访问它。

上面rdr行中from any to any部分非常有用。如果你明确知道哪些地址或者子网被允许访问web服务器的80端口,你可以在这部分严格限制:

rdr on tl0 proto tcp from 27.146.49.0/24 to any port 80 -> \
192.168.1.20

这样只会重定向指定的子网。注意这表明可以重定向外部不同的访问主机到网关后面不同的机器上。这非常有用。例如,如果你知道远端的用户连接上来时使用的IP地址,你可以让他们使用网关的IP地址和端口访问他们各自的桌面计算机。

rdr on tl0 proto tcp from 27.146.49.14 to any port 80 -> \
192.168.1.20
rdr on tl0 proto tcp from 16.114.4.89 to any port 80 -> \
192.168.1.22
rdr on tl0 proto tcp from 24.2.74.178 to any port 80 -> \
192.168.1.23

重定向和包过滤

注意:转换后的数据包仍然会通过过滤引擎,根据定义的过滤规则进行阻塞或者通过。唯一的例外是如果rdr规则中使用了pass关键字,会使得重定向的数据包直接通过过滤引擎。

还要注意由于转换是在过滤之前进行,过滤引擎所看到的是在匹配rdr规则经过转换后的目标ip地址和端口的数据包。

* 192.0.2.1 - 因特网上的主机
* 24.65.1.13 - openbsd路由器的外部地址
* 192.168.1.5 - web服务器的内部IP地址

重定向规则:

rdr on tl0 proto tcp from 192.0.2.1 to 24.65.1.13 port 80 \
-> 192.168.1.5 port 8000

数据包在经过rdr规则前的模样:

* 源地址: 192.0.2.1
* 源端口: 4028 (由操作系统任意选择)
* 目的地址: 24.65.1.13
* 目的端口: 80

数据包经过rdr规则后的模样:

* 源地址: 192.0.2.1
* 源端口: 4028
* 目的地址: 192.168.1.5
* 目的: 8000

过滤引擎看见的IP数据包时转换发生之后的情况。

安全隐患

重定向确实存在安全隐患。在防火墙上开了一个允许流量进入内部网络的洞,被保护的网络安全潜在的受到了威胁!例如,如果流量被转发到了内部的web服务器,而web服务器上允许的守护程序或者CGI脚本程序存在漏洞,则这台服务器存在会被因特网网上的入侵者攻击危险。如果入侵者控制了这台机器,就有了进入内部网络的通道,仅仅是因为这种流量是允许通过防火墙的。

这种风险可以通过将允许外部网络访问的系统限制在一个单独的网段中来减小。这个网段通常也被称为非军事化区域(DMZ)或者私有服务网络(PSN)。通过这个方法,如果web服务器被控制,通过严格的过滤进出DMZ/PSN的流量,受影响的系统仅限于DMZ/PSN网段。

重定向和反射

通常,重定向规则是用来将因特网上到来的连接转发到一个内部网络或者局域网的私有地址。例如:

server = 192.168.1.40

rdr on $ext_if proto tcp from any to $ext_if port 80 -> $server \
port 80

但是,当一个重定向规则被从局域网上的客户端进行测试时,它不会正常工作。这是因为重定向规则仅适用于通过指定端口($ext_if,外部接口,在上面的例子中)的数据包。从局域网上的主机连接防火墙的外部地址,并不意味着数据包会实际的通过外部接口。防火墙上的TCP/IP栈会把到来的数据包的目的地址在通过内部接口时与它自己的IP地址或者别名进行对比检测。那样的数据包不会真的通过外部接口,栈在任何情况下也不会建立那样的通道。因而,PF永远也不会在外部接口上看到那些数据包,过滤规则由于指定了外部接口也不会起作用。

指定第二条针对内部接口的也达不到预想的效果。当本地的客户端连接防火墙的外部地址时,初始化的TCP握手数据包是通过内部接口到达防火墙的。重定向规则确实起作用了,目标地址被替换成了内部服务器,数据包通过内部接口转发到了内部的服务器。但源地址没有进行转换,仍然包含的是本地客户端的IP地址,因此服务器把它的回应直接发送给了客户端。防火墙永远收不到应答不可能返回客户端信息,客户端收到的应答不是来自它期望的源(防火墙)会被丢弃,TCP握手失败,不能建立连接。

当然,局域网里的客户端仍然会希望象外部客户一样透明的访问这台内部服务器。有如下的方法解决这个问题:

水平分割 DNS

存在这样的可能性,即配置DNS服务器使得它回答内部主机的查询和回答外部主机的查询不一样,因此内部客户端在进行名称解析时会收到内部服务器的地址。它们直接连接到内部服务器,防火墙根本不牵扯。这会降低本地流量,因为数据包不会被送到防火墙。

将服务器移到独立的本地网络

增加单独的网络接口卡到防火墙,把本地的服务器从和客户端同一个网段移动到专用的网段(DMZ)可以让本地客户端按照外部重定向连接的方法一样重定向。使用单独的网段有几个优点,包括和保留的内部主机隔离增加了安全性;服务器(我们的案例中可以从因特网访问)一旦被控制,它不能直接存取本地网络因为所有的连接都必须通过防火墙。

TCP 代理

一般而言,TCP代理可以在防火墙上设置,监听要转发的端口或者将内部接口上到来的连接重定向到它监听的端口。当本地客户端连接防火墙时,代理接受连接,建立到内部服务器的第二条连接,在通信双方间进行数据转发。

简单的代理可以使用inetd(8)和nc(1)建立。下面的/etc/inetd.conf中的条目建立一个监听套接字绑定到lookback地址(127.0.0.1)和端口5000。连接被转发到服务器192.168.1.10的80端口。


127.0.0.1:5000 stream tcp nowait nobody /usr/bin/nc nc -w \
20 192.168.1.10 80

下面的重定向规则转发内部接口的80端口到代理:

rdr on $int_if proto tcp from $int_net to $ext_if port 80 -> \
127.0.0.1 port 5000

RDR 和 NAT 结合

通过对内部接口增加NAT规则,上面说的转换后源地址不变的问题可以解决。

rdr on $int_if proto tcp from $int_net to $ext_if port 80 -> \
$server
no nat on $int_if proto tcp from $int_if to $int_net
nat on $int_if proto tcp from $int_net to $server port 80 -> \
$int_if

这会导致由客户端发起的初始化连接在收到内部接口的返回数据包时转换回来,客户端的源ip地址被防火墙的内部接口地址代替。内部服务器会回应防火墙的内部接口地址,在转发给本地客户端时可以反转NAT和RDR。这个结构是非常复杂的,因为它为每个反射连接产生了2个单独的状态。必须小心配置防止NAT规则应用到了其他流量,例如连接由外部发起(通过其他的重定向)或者防火墙自己。注意上面的rdr规则会导致TCP/IP栈看到来自内部接口带有目的地址是内部网络的数据包。


一般而言,上面提到的解决方法可以互相替代。

------------------------------------------------------------------------------
$OpenBSD: rdr.html,v 1.16 2004/05/07 01:55:24 nick Exp $
==============================================================================

PF: 规则生成捷径

------------------------------------------------------------------------------

目录

* 简介
* 使用宏
* 使用列表
* PF 语法
+ 减少关键字
+ Return 简化
+ 关键字顺序

------------------------------------------------------------------------------

简介

PF提供了许多方法来进行规则集的简化。一些好的例子是使用宏和列表。另外,规则集的语言或者语法也提供了一些使规则集简化的捷径。首要的规则是,规则集越简单,就越容易理解和维护。

使用宏

宏是非常有用的,因为它提供了硬编码地址,端口号,接口名称等的可选替代。在一个规则集中,服务器的IP地址改变了?没问题,仅仅更新一下宏,不需要弄乱你花费了大量时间和精力建立的规则集。

通常的惯例是在PF规则集中定义每个网络接口的宏。如果网卡需要被使用不同驱动的卡取代,例如,用intel代替3com,可以更新宏,过滤规则集会和以前功能一样。另一个优点是,如果在多台机器上安装同样的规则集,某些机器会有不同的网卡,使用宏定义网卡可以使的安装的规则集进行最少的修改。使用宏来定义规则集中经常改变的信息,例如端口号,IP地址,接口名称等等,建议多多实践!

# define macros for each network interface
IntIF = "dc0"
ExtIF = "fxp0"
DmzIF = "fxp1"

另一个惯例是使用宏来定义IP地址和网络,这可以大大减轻在IP地址改变时对规则集的维护。

# define our networks
IntNet = "192.168.0.0/24"
ExtAdd = "24.65.13.4"
DmzNet = "10.0.0.0/24"

如果内部地址扩展了或者改到了一个不同的IP段,可以更新宏为:

IntNet = "{ 192.168.0.0/24, 192.168.1.0/24 }"

当这个规则集重新载入时,任何东西都跟以前一样。

使用列表

来看一下一个规则集中比较好的例子使得RFC1918定义的内部地址不会传送到因特网上,如果发生传送的事情,可能导致问题。

block in quick on tl0 inet from 127.0.0.0/8 to any
block in quick on tl0 inet from 192.168.0.0/16 to any
block in quick on tl0 inet from 172.16.0.0/12 to any
block in quick on tl0 inet from 10.0.0.0/8 to any
block out quick on tl0 inet from any to 127.0.0.0/8
block out quick on tl0 inet from any to 192.168.0.0/16
block out quick on tl0 inet from any to 172.16.0.0/12
block out quick on tl0 inet from any to 10.0.0.0/8

看看下面更简单的例子:

block in quick on tl0 inet from { 127.0.0.0/8, 192.168.0.0/16, \
172.16.0.0/12, 10.0.0.0/8 } to any
block out quick on tl0 inet from any to { 127.0.0.0/8, \
192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }

这个规则集从8行减少到2行。如果联合使用宏,还会变得更好:

NoRouteIPs = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, \
10.0.0.0/8 }"
ExtIF = "tl0"
block in quick on $ExtIF from $NoRouteIPs to any
block out quick on $ExtIF from any to $NoRouteIPs

注意虽然宏和列表简化了pf.conf文件,但是实际是这些行会被pfctl(8)扩展成多行,因此,上面的例子实际扩展成下面的规则:

block in quick on tl0 inet from 127.0.0.0/8 to any
block in quick on tl0 inet from 192.168.0.0/16 to any
block in quick on tl0 inet from 172.16.0.0/12 to any
block in quick on tl0 inet from 10.0.0.0/8 to any
block out quick on tl0 inet from any to 10.0.0.0/8
block out quick on tl0 inet from any to 172.16.0.0/12
block out quick on tl0 inet from any to 192.168.0.0/16
block out quick on tl0 inet from any to 127.0.0.0/8

可以看到,PF扩展仅仅是简化了编写和维护pf.conf文件,实际并不简化pf(4)对于规则的处理过程。

宏不仅仅用来定义地址和端口,它们在PF的规则文件中到处都可以用:

pre = "pass in quick on ep0 inet proto tcp from "
post = "to any port { 80, 6667 } keep state"

# David‘s classroom
$pre 21.14.24.80 $post

# Nick‘s home
$pre 24.2.74.79 $post
$pre 24.2.74.178 $post

扩展后:

pass in quick on ep0 inet proto tcp from 21.14.24.80 to any \
port = 80 keep state
pass in quick on ep0 inet proto tcp from 21.14.24.80 to any \
port = 6667 keep state
pass in quick on ep0 inet proto tcp from 24.2.74.79 to any \
port = 80 keep state
pass in quick on ep0 inet proto tcp from 24.2.74.79 to any \
port = 6667 keep state
pass in quick on ep0 inet proto tcp from 24.2.74.178 to any \
port = 80 keep state
pass in quick on ep0 inet proto tcp from 24.2.74.178 to any \
port = 6667 keep state

PF 语法

PF的语法相当灵活,因此,允许编写非常灵活的规则集。PF能够自动插入某些关键字因此它们不必在规则中明确写出,关键字的顺序也是随意的,因此不需要记忆严格的语法限制。

减少关键字

要定义全部拒绝的策略,使用下面2条规则:

block in all
block out all

这可以简化为:

block all

如果没有指定方向,PF会认为规则适用于数据包传递的进、出2个方向。

同样的, "from any to any" 和 "all" 子句可以在规则中省略,例如

block in on rl0 all
pass in quick log on rl0 proto tcp from any to any port 22 keep state

可以简化为:

block in on rl0
pass in quick log on rl0 proto tcp to port 22 keep state

第一条规则阻塞rl0上从任意到任意的进入数据包,第二条规则允许rl0上端口22的TCP流量通过。

Return 简化

用于阻塞数据包,回应TCP RST或者ICMP不可到达的规则集可以这么写:

block in all
block return-rst in proto tcp all
block return-icmp in proto udp all
block out all
block return-rst out proto tcp all
block return-icmp out proto udp all

可以简化为::

block return

当PF看到return关键字,PF可以智能回复合适应答,或者完全不回复,取决于要阻塞的数据包使用的协议。W

关键字顺序

在大多数情况下,关键字的顺序是非常灵活的。例如,规则可以这么写:

pass in log quick on rl0 proto tcp to port 22 \
flags S/SA keep state queue ssh label ssh

也可以这么写:

pass in quick log on rl0 proto tcp to port 22 \
queue ssh keep state label ssh flags S/SA

其他类似的顺序也能够正常工作。
------------------------------------------------------------------------------
$OpenBSD: shortcuts.html,v 1.12 2004/05/07 01:55:24 nick Exp $
==============================================================================
* 高级配置

PF: 运行选项

------------------------------------------------------------------------------

运行选项是控制pf操作的选择。这些选项在pf.conf中使用set指定。

set block-policy
设定过滤规则中指定的block动作的默认行为。
+ drop - 数据包悄然丢弃.
+ return - TCP RST 数据包返回给遭阻塞的TCP数据包,ICMP不可到达数据包返回给其他。
注意单独的过滤规则可以重写默认的响应。

set debug
设定 pf的调试级别。
+ none - 不显示任何调试信息。
+ urgent - 为严重错误产生调试信息,这是默认选择。
+ misc - 为多种错误产生调试信息。(例如,收到标准化/整形的数据包的状态,和产生失败的状态)。.
+ loud - 为普通条件产生调试信息(例如,收到被动操作系统检测信息状态)。

set fingerprints file
设定应该装入的进行操作系统识别的操作系统特征文件来,默认是 /etc/pf.os.

set limit
frags - 在内存池中进行数据包重组的最大数目。默认是5000。
src-nodes - 在内存池中用于追踪源地址(由stick-address 和 source-track选项产生)的最大数目,默认是10000。
states - 在内存池中用于状态表(过滤规则中的keep state)的最大数目,默认是10000。

set loginterface int
设定PF要统计进/出流量和放行/阻塞的数据包的数目的接口卡。统计数目一次只能用于一张卡。注意 match, bad-offset, 等计数器和状态表计数器不管 loginterface是否设置都会被记录。

set optimization
为以下的网络环境优化PF:
+ normal - 适用于绝大多数网络,这是默认项。
+ high-latency - 高延时网络,例如卫星连接。
+ aggressive - 自状态表中主动终止连接。这可以大大减少繁忙防火墙的内存需求,但要冒空闲连接被过早断开的风险。
+ conservative - 特别保守的设置。这可以避免在内存需求过大时断开空闲连接,会稍微增加CPU的利用率。

set state-policy
设定 PF在状态保持时的行为。这种行为可以被单条规则所改变。见状态保持章节。
+ if-bound - 状态绑定到产生它们的接口。如果流量匹配状态表种条目但不是由条目中记录的接口通过,这个匹配会失败。数据包要么匹配一条过滤规则,或者被丢弃/拒绝。
+ group-bound - 行为基本和if-bound相同,除了数据包允许由同一组接口通过,例如所有的ppp接口等。
+ floating - 状态可以匹配任何接口上的流量。只要数据包匹配状态表条目,不管是否匹配它通过的接口,都会放行。这是默认的规则。

set timeout
interval - 丢弃过期的状态和数据包碎片的秒数。
frag - 不能重组的碎片过期的秒数。

例如:

set timeout interval 10
set timeout frag 30
set limit { frags 5000, states 2500 }
set optimization high-latency
set block-policy return
set loginterface dc0
set fingerprints /etc/pf.os.test
set state-policy if-bound

------------------------------------------------------------------------------
$OpenBSD: options.html,v 1.8 2004/05/07 01:55:23 nick Exp $
==============================================================================

PF: 流量整形 (数据包标准化)

------------------------------------------------------------------------------

目录

* 简介
* 选项

------------------------------------------------------------------------------

简介

流量整形是将数据包标准化避免最终的数据包出现非法的目的。流量整形指引同时也会重组数据包碎片,保护某些操作系统免受某些攻击,丢弃某些带有非法联合标记的TCP数据包。流量整形指引的简单形式是:

scrub in all

这会对所有接口上到来的数据包进行流量整形。

一个不在接口上进行流量整形的原因是要透过PF使用NFS。一些非openbsd的平台发送(和等待)奇怪的数据包,对设置不分片位的数据包进行分片。这会被流量整形(正确的)拒绝。这个问题可以通过设置no-df选项解决。另一个原因是某些多用户的游戏在进行流量整形通过PF时存在连接问题。除了这些极其罕见的案例,对所有的数据包进行流量整形时强烈推荐的设置。

流量整形指引语法相对过滤语法是非常简单的,它可以非常容易的选择特定的数据包进行整形而不对没指定的数据包起作用。

更多的关于数据整形的原理和概念可以在这篇论文中找到:
Network Intrusion Detection: Evasion, Traffic Normalization, and End-to-End
Protocol Semantics.

选项

流量整形有下面的选项:

no-df
在IP数据包头中清除不分片位的设置。某些操作系统已知产生设置不分片位的分片数据包。尤其是对于NFS。流量整形(scrub)会丢弃这类数据包除非设置了no-df选项。某些操作系统产生带有不分片位和用0填写IP包头中分类域,推荐使用no-df和random-id 联合使用解决。
random-id
用随机值替换某些操作系统输出数据包中使用的可预测IP分类域的值这个选项仅适用于在选择的数据包重组后不进行分片的输入数据包。
min-ttl num
增加IP包头中的最小存活时间(TTL)。
max-mss num
增加在TCP数据包头中最大分段值(MSS)。
fragment reassemble
在传递数据包到过滤引擎前缓冲收到的数据包碎片,重组它们成完整的数据包。优点是过滤规则仅处理完整的数据包,忽略碎片。缺点是需要内存缓冲数据包碎片。这是没有设置分片选项时的默认行为。这也是能和NAT一起工作的唯一分片选项。
fragment crop
导致重复的碎片被丢弃,重叠的被裁剪。与碎片重组不同,碎片不会被缓冲,而是一到达就直接传递。
fragment drop-ovl
跟 fragment crop 相似,除了所有重复和重叠的碎片和其他更多的通信碎片一样被丢弃。
reassemble tcp
TCP连接状态标准化。当使用了 scrub reassemble tcp时,方向(进/出)不用说明,会执行下面的标准化过程:
+ 连接双方都不允许减少它们的IP TTL值。这样做是为了防止攻击者发送数据包到防火墙,影响防火墙保持的连接状态,使数据包在到达目的主机前就过期。所有数据包的TTL都为了这个连接加到了最大值。
+ 用随机数字调整TCP数据包头中的 RFC1323 时间戳。这可以阻止窃听者推断主机在线的时间和猜测NAT网关后面有多少主机。

实例:

scrub in on fxp0 all fragment reassemble min-ttl 15 max-mss 1400
scrub in on fxp0 all no-df
scrub on fxp0 all reassemble tcp

------------------------------------------------------------------------------
$OpenBSD: scrub.html,v 1.9 2004/05/07 01:55:24 nick Exp $
==============================================================================

PF: 锚定和命名规则集

------------------------------------------------------------------------------

目录

* 简介
* 命名规则集
* 锚定选项
* 操作已命名规则集

------------------------------------------------------------------------------

简介

除了主要的规则集,PF还可以载入子规则集。由于子规则集可以使用pfctl(8)操作,这提供了一个动态修改活动规则集的方法。正如表被用来保存动态地址列表,子规则用来保存动态过滤设定,nat,rdr和binat规则。

子规则集通过使用锚定附加到主规则集中。有4种类型的锚定规则:

* anchor name - 检测锚定名称中的所有规则
* binat-anchor name - 检测锚定名称中的binat规则。
* nat-anchor name -检测锚定名称中的nat规则。
* rdr-anchor name -检测锚定名称中的rdr规则。

只有主规则集可以包含锚定规则。

命名规则集

命名规则集是被配置了名称一组过滤和/或转换规则。一个锚定点可以包含多于一个的类似规则集。当PF在主规则集中碰到锚定规则,它会按字母顺序检测附加到锚定点的所有规则集。处理过程会在主规则集中继续直到匹配了带quick的规则,或者在锚定中匹配了认为是结束的转换规则,锚定规则和主规则才不再继续执行。

例如:

ext_if = "fxp0"

block on $ext_if all
pass out on $ext_if all keep state
anchor goodguys

这条规则集设定了fxp0接口上默认拒绝进出的所有流量的策略。然后通过的流量保持状态,后面是一个锚定规则集名称是goodguys。锚定可以通过2个方法替换为规则:


* 使用可载入的规则
* 使用 pfctl(

可载入的规则使pfctl通过读入一个文本文件代替指定的锚定和命名规则集。例如:

load anchor goodguys:ssh from "/etc/anchor-goodguys-ssh"

当主规则集载入时,/etc/anchor-goodguys-ssh 文件中列出的规则中命名为ssh的规则集会被载入到名称为goodguys的锚定点。

要使用pfctl为锚定点增加规则,可以使用下面这样的命令:

# echo "pass in proto tcp from 192.0.2.3 to any port 22" \
| pfctl -a goodguys:ssh -f -

这增加了一条防线规则到goodguys锚定点并命名为ssh。PF在主规则中到达goodguys锚定点时会检测这条规则(包括其他加入的过滤规则)。

规则也可以通过文本文件载入和保存:

# cat >> /etc/anchor-goodguys-www
pass in proto tcp from 192.0.2.3 to any port 80
pass in proto tcp from 192.0.2.4 to any port { 80 443 }

# pfctl -a goodguys:www -f /etc/anchor-goodguys-www

这从/etc/anchor-goodguys-www 文件载入规则到goodguys锚定点命名为www。

过滤和转换规则也可以使用同样的语法和选项载入命名规则集,类似主规则集载入普通的规则。一个警告:命名规则集中使用的宏必须同时被定义,在主规则集中定义的宏对命名规则集不可见。

每一个命名规则集,和主规则集一样,和其他规则集独立存在的。对于某个规则集的操作,比如删除一个规则集,不会影响其他的规则集。另外,在主规则集中移动一个锚定点的位置不会影响这个锚定点和附属于这个锚定点的命名规则集。一个命名规则集不会被破坏,除非使用pfctl(8)删除所有规则。如果一个锚定点没有附属的命名规则集,它也就被破坏了!

锚定选项

锚定规则可以随意指定接口,协议,源和目的地址,标记等等,使用的是和过滤规则同样的语法。如果这些信息存在,锚定规则仅处理匹配锚定规则定义的数据包。例如:

ext_if = "fxp0"

block on $ext_if all
pass out on $ext_if all keep state
anchor ssh in on $ext_if proto tcp from any to any port 22

锚定规则ssh仅检测来自fxp0目标为端口22的tcp数据包。规则可以用如下方法添加:

# echo "pass in from 192.0.2.10 to any" | pfctl -a ssh:allowed -f -

因此,尽管过滤规则中没有指定接口,协议,端口,由于锚定规则的定义,主机192.0.2.10只允许使用ssh连接。

操作命名规则集

命名规则集的操作是通过pfctl实现的。可以不用重新载入主规则集就在规则集中删除和增加规则。

要列出附属于ssh锚定规则集中的规则,可以使用:

# pfctl -a ssh:allowed -s rules

要删除这个规则集中所有过滤规则:

# pfctl -a ssh:allowed -F rules

如果规则集名称省略,这个动作应用于锚定里的所有规则。

命令的详细列表,可以查看pfctl(8)。

------------------------------------------------------------------------------
$OpenBSD: anchors.html,v 1.12 2004/05/07 01:55:23 nick Exp $
==============================================================================

TOP

发新话题