级别:初学者
Eric Bullen. (ericb-howto@thedeepsky.com)
Sr. Unix Systems Administrator, Incyte Genomics
September 17, 2003
翻译: pengcz (aoma1999@126.com)
从OpenBSD3.0开始,OpenBSD开发团队就采用PF来取代原有IPF。现在,PF已经成为世界级的防火墙解决方案。PF本身提供了许多优秀的
工具来帮助网络工程师创建一个健壮的防护方案来保护公司
的私有网络免受不安全的Internet的侵害。本篇文章的目的是引导你如何一步一步地配置你的PF防火墙,并对每一步进行详细解释,但又不会过于深入免
得初学者混淆。
在继续这篇文章之前你需要弄清楚一些事情,我会概述这些事情使得我们可以深入这篇文章的重要部分。第一件事情是你要有一个OpenBSD
3.0的系统,至小,你应该已经安装了OpenBSD系统。本文是以OpenBSD
3.3系统为例作说明的,当然你也可以找到很多有关配置防火墙的入门书,我的主要目的是教你如何正确地创建和设置PF防火墙。
首先,你要把你的防火墙接入到网络中去(至少网络中有一台电脑)。这样这台电脑可以接收和发送数据包。如果你没有具备这些条件,我们将很难进行测试。通
常,如果一个网络不可用,而你有两台电脑,你可以让两台电脑直接相连(网卡对网卡,用交叉线相连),有关交叉线的制作请参考相关网络资料,通常在电脑配件
市场有这种产品可供购买。如果你计划配置NAT(网络地址转换,这样可以让多台电脑共享一个公网IP地址),那么你的OpenBSD系统要安装有两块网
卡。
没有任何可以比防火墙更保护网络安全的了。当你创建/配置/测试你的防火墙时,对于互联网上的用户来说(理想情况下)这个防火墙应该是不可到达的,这个防
火墙应该位于一个企业网内部。请参考RFC1918 有关私有地址块的事。通常,大部份的内部网络会是192.168.0.0/16
或者10.0.0.0/8。你会发觉设置这些是多么容易的一件事情。如果你发觉设置这些网络有困难,那么我会建议你不要再阅读下去,你应该先去看一些有关
IP网络基础方面参考书。
配置
要激活 PF, 并让系统在启动时自动地加载,请编辑文件你的 /etc/rc.conf.local文件,并添加下面一行到文件中去:
pf=YES。 尽管,你也可以编辑 /etc/rc.conf
文件(这个文件中设置的都是系统的默认值)。但我并不建议你去修改它,我建议你修改/etc/rc.conf.local,系统会在最后阅读
/etc/rc.conf.local文件,并用它的值去覆盖系统的默认设置。在我的OpenBSD
系统上的上,/etc/rc.conf.local 文件内容如下:
例1 . /etc/rc.conf.local
#!/bin/sh -pf=YES
# Packet filter / NATpf_rules=/etc/pf.conf
# Packet
filter rules filepflogd_flags= # add more flags, ie. "-s 256"
下一步,创建一个 "pass all" pf.conf 文件,这样在系统启动时,OpenBSD 会读取里面的内容,下面是一个供你使用的好例子:
例2 . /etc/pf.conf
## Macros
宏定义SYN_ONLY="S/FSRA"
## TABLES 表
## GLOBAL OPTIONS 全局选项#
#
TRAFFIC NORMALIZATION 流量正常化## QUEUEING RULES队列
## TRANSLATION RULES
(NAT)网络地址转换
## FILTER RULES 过滤
pass in log all keep state pass out log
all keep state
在例2中,你可以看到一些地方是以"##"开头的,它主要用来注释从而让配置文件可读。这些“##”放在这里的一个好处是让你明白PF
是按怎样的顺序如何处理这些规则的。当你看到最后两行以“pass”开头的句子时,你会明白它是记录所有进出防火墙的
。当你的系统创建或者收到连接时,防火墙会记录这些内容到你的/var/log/pflog文件中去。我会告诉你如何读取这些内容(它时一个二进制文件,
对于文本编辑器来说它是不可读的)
。理想情况下,当你配置你的防火墙,机器不会收发到什么数据流除非你的机器自身产生的数据流。如果你是制造数据流的人,你可以看到系统是如何记录日志的.
说到这里,我们应该重启机器并让系统让修改生效,并确认所有设置自动启动.
一旦你的系统重启后,你就可以进行检查你所进行的修改有否生效。你需要象例3一样做,确保接口pflog0已经启用和你的规则已经装载到PF中去
例3.
Checking the pflog0 interface and pfctl -s rules output
cerberus:~# /sbin/ifconfig pflog0pflog0:
flags=141
; mtu 33224cerberus:~# cerberus:~#
pfctl -s rulespass in log all keep statepass out log all keep
statecerberus:~#
在例3中,你可以看到接口pflog0已经
"UP"了.这会让你让你接上TCPdump到接口,并实时观察流量。正如我上面所说,如果你的系统负荷过重,显示的信息就会一闪而过,你根本看不到什么
有用的信息。你也可以运行 pfctl -s
rules,这样你可以看到你的PF装载了那些基本规则(注意在上面我们只用允许让所有流量通过的规则而已)。
Taking it for a Spin
现在,你可以观察一些网络流量了,尽管它们看起来有点困难,但我会告诉你将如何看这些代码。一旦你学会看这些代码,你就能更好地调整你的防火墙来处理网络
的流量。下面的输出是来自TCPdump的一些输出,你只要在命令行下输入TCPdump -n -e -ttt -i
就可以看到防火墙是如何工作的了。
例 4. Reading the output of TCPdump -n -e -ttt -i pflog0
cerberus:~# TCPdump -n -e -ttt -i pflog0TCPdump: WARNING: pflog0: no
IPv4 address assignedTCPdump: listening on pflog0Sep 17 17:07:07.833264
rule 37/0(match): pass in on fxp0: 11.22.33.44 >; 192.168.1.2: icmp:
echo request Sep 17 17:07:07.833486 rule 56/0(match): pass in on fxp1:
192.168.1.2 >; 11.22.33.44: icmp: echo reply Sep 17 17:07:32.725126
rule 6/0(match): block in on fxp0: 55.66.77.88.14350 >;
66.92.15.252.7777: S 3925150538:3925150538(0) win 5840 ; (DF) Sep 17
17:07:37.443421 rule 14/0(match): pass in on fxp0: 55.66.77.88.14373
>; 66.92.15.252.22: S 3920978973:3920978973(0) win 5840 ; (DF) Sep 17
17:07:38.115817 rule 37/0(match): pass in on fxp0: 55.66.77.88 >;
192.168.1.2: icmp: echo request Sep 17 17:07:38.116021 rule
56/0(match): pass in on fxp1: 192.168.1.2 >; 55.66.77.88: icmp: echo
reply^C6 packets received by filter0 packets dropped by kernel
Ok, IP地址为 11.22.33.44 和55.66.77.88
都是假的,是经过我修改真实的IP地址而来的,真实的IP地址不会出现在这里了。IP地址
192.168.1.2是一个内部系统的IP地址,属于一个私有的网络并且经过了防火墙的NAT作业。我会在后面告诉你为什么会在日志记录中出现内部私有
地址而不是外部公网IP地址,如果你等不及的话请参考文档最后部份NAT转换章节。我会从下面的一行中进行分析并解释这一行中各个参数的具体含义:
Sep 17 17:07:37.443421 rule 14/0(match): pass in on fxp0:
55.66.77.88.14373 >; 66.92.15.252.22: S 3920978973:3920978973(0) win
5840 (DF)
Sep 17 17:07:37.443421 当前的日期/时间,详细到毫秒级别
rule 14/0(match): 捕捉到的包匹配的规则集号码。你可以输入pfctl -g -s rules| grep '^@'来看PF为那一条规则分配了那一个号码。
pass in on fxp0: 这说明这个包匹配规则并通过,来自fxp0接口, (fxp 是 Intel Pro100 接口). 你可能使用不同的网卡,因此可能会是不同的接口。
55.66.77.88.14373 >; 66.92.15.252.22: S 提示源ip:端口 >;目的ip:端口,说明一个包的来龙去脉。最后面的S表明这个包的旗帜,在这里表明是SYN旗帜
3920978973:3920978973(0) win 5840 (DF) 忽略这些,这已超出本文档的讨论范围
上面的示例给了你一些基础的认知,教你如何创建你的规则集,如何通过TCPdump来监控网络,你将能够根椐你的具体环境对自己的防火墙进行修订。
现在,我们对TCPdump有了感性的认识并知道如何理解它的输出,同时对pf.conf文件架构有了一些基础的认知,现在让我们来学习pfctl命令的一些开关参数(在这里我只会讨论我们常用到的一些参数) 。
pfctl -d 关闭PF
pfctl -e 启用PF
pfctl -Fa -f /etc/pf.conf 刷新所有 (NAT,过滤,队列,状态,通知,表)规则并得重新装载文件 /etc/pf.conf
pfctl -s rules 报告当前装载的过滤规则集
pfctl -s nat 报告当前装载的过NAT规则集
pfctl -s state 报告当前运行的状态表(非常有用)
pfctl -v -n -f /etc/pf.conf 这并不装载什么规则,它只是让你在装载文件前对文件进行检查,对于测试来说这是非常有用的
Building Something Useful
现在,就让我们卷起袖子,好好深入钻研如何创建一个有用的PF配置文件。在这里,我建议你将你的/etc/pf.conf文件放在一边(做好备份)。我们
从一无所有开始创建我们的配置文件。我们将新配置的文件命名为:/etc/pf.conf-new。刚开始我们以一个简单的配置开始,如果你热心一开始就
创建一个复杂的例子,但运行时所得结果与你设想的不一样,你就会很难发现你的配置错在那里了。好了,让我们开始吧,我们让它来实现一个简单的功能。
通常有两条规则:一是所有末经拒绝的都是允许的,另一条是所有末经允许的都是拒绝的,这两条规则有很大的不同。我会讨论第二条规则,因为相对而言它是更安全的规则,-^-
第一步,你要写下那些端口你是想要开放的。至少,我建议你开放下面的端口: ssh (port 22/TCP), auth (port
113/TCP)和ICMP
pings,这是一个好的开始。如果你的机子运行额外的服务(在防火墙上你应该有选择地开启你要的服务,理想情况下你不应该开启额外的服务)你也要打开相
应的服务端口。在下面,我们会用新的规则代替例二中的最后两行规则。
例5. /etc/pf.conf-new
## Macros SYN_ONLY="S/FSRA"EXT_NIC="fxp0"INT_NIC="fxp1"
# Your Internet
IP goes in the EXT_IP variableEXT_IP="11.22.33.44"
# Your private
network IP goes in the INT_IP variable# if you have two NICs on the
machineINT_IP="192.168.1.1"
## TABLES
## GLOBAL OPTIONS
## TRAFFIC
NORMALIZATION
## QUEUEING RULES## TRANSLATION RULES (NAT)
## FILTER
RULES
# Block everything (inbound AND outbound on ALL interfaces) by
default (catch-all)block all
# Default TCP policyblock return-rst in log
on $EXT_NIC proto TCP all pass in log quick on $EXT_NIC proto TCP from
any to $EXT_IP port 22 flags $SYN_ONLY keep state pass in log quick on
$EXT_NIC proto TCP from any to $EXT_IP port 113 flags $SYN_ONLY keep
state# Default UDP policyblock in log on $EXT_NIC proto udp all
# It's
rare to be hosting a service that requires UDP (unless you are hosting
# a dns server for example), so there typically won't be any entries
here.
# Default ICMP policyblock in log on $EXT_NIC proto icmp all pass
in log quick on $EXT_NIC proto icmp from any to $EXT_IP echoreq keep
stateblock out log on $EXT_NIC all pass out log quick on $EXT_NIC from
$EXT_IP to any keep state
# Allow the local interface to talk
unrestrictedpass in quick on lo0 allpass out quick on lo0 all
在这里我并没有用到PF内置的很酷的特性,因为我们是从最简单开始的。对用户来说可能最困惑的是有关过滤规则中的in与out,通常in与out是相对防
火墙而言的,当你看到一个包是out的,那就意味着它是离开防火墙的,这与接口无关的。如果一个包是in的,那就意味着它是走向防火墙的。
另外一件事我会坚持已见的会是保持规则集易于理解(阅读)。这可能会过于冗余,但方便将近似的规则组成一组,这样人们可以很容易描述意境图,特别是在你创建一个较多规则时特别有用。好了,现在我们继续下面的讨论
# Block everything (inbound AND outbound on ALL interfaces) by default (catch-all)
block all
上面的规则会拒绝所有接口上所有协议的数据,这就确保只有你显式注明的规则、接口才能让数据通过。
block return-rst in log on $EXT_NIC proto TCP all
pass in log quick on $EXT_NIC proto TCP from any to $EXT_IP port 22 flags $SYN_ONLY keep state
pass in log quick on $EXT_NIC proto TCP from any to $EXT_IP port 113 flags $SYN_ONLY keep state
上面三行包括了与TCP相关的所有流量。这三行最大的不同在于关键词“quick”,如果没有“quick”选项,规则会将匹配的包作标记,并按规则处
理,但后面的规则会继续匹配这个包,如果没有什么新规则匹配这个包,那么这个包就由匹配的包标记,如果有新规则匹配这个包,则这个包由新规则标记。这对于
你想对包过滤设置默认行为来说非常方便,接着你可以在后续的规则中设置你想对包进行的处理。但在这里,后面的二行有“quick”选项,那么,如果一个包
匹配了端口22/TCP或者 113/TCP (其他的在规则里面指明的端口也一样处理), 接着它们允许通过防火墙
(因为在规则的开头用的是pass)它不会进行其他额外的处理,理解这一点非常重要。在继续下面的讨论前请好好理解它。
block in log on $EXT_NIC proto udp all
# It's rare to be hosting a service that requires UDP (unless you are hosting
# a dns server for example), so there typically won't be any entries here.
这三行与上面三行非常类似,只不过在这里讨论的不再是TCP,而是UDP罢了。正如规则所说,很少机器需要监听UDP端口,除非你的机器提供DNS服务。
通常来说,这部份你不需要过多理会。在这一小节中你应该注意到没有了“quick”选项。因为没有规则匹配,UDP数据包会被静静地丢弃。对于CP端口,
在一端如果端口没有开放,那么对于服务端正确的做法是发回一个 return-reset 信号。但由于UDP 和
ICMP是无状态的,你不用返馈任何其他信息,这也是非常合理的。我想隐藏你有防火墙的事实与你有一个防火墙是一样重要的。没有人需要知道你有一个防火
墙,因为这会给潜在的攻击者提供“你有什么”这方面的信息,如果你有一个服务器在网络上,而这台服务器收到一个UDP包,这个包的目的端口并没有打开监
听,服务器只会丢弃这个包,并不会返馈什么信息。但如果服务器收到的是一个TCP包,服务器会返回一个tcp-reset包告知发送者服务器的发送端并没
有打开这个端口和进行监听。因此,在这点上,如果你的防火墙对一个关闭的UDP端口返馈信息,攻击者就会认为这很大程度上是一个防火墙在目标上,而不是服
务器。
block in log on $EXT_NIC proto icmp all
pass in log quick on $EXT_NIC proto icmp from any to $EXT_IP echoreq keep state
这时这我们讨论ICMP章节。你应该对它有一个清晰的认识,在这里我简单地说一下,正如上所说, ICMP是无状态的,
PF可以灵活地处理UDP和ICMP。例如。如果你限制带外ICMP流量,只允许 ICMP-pings,如果你在规则中加上了 "keep
state"旗帜,那么PF就会聪明地允许ICMP-replies返回而不用你额外指定一条规则,怎 么样?
block out log on $EXT_NIC all
pass out log quick on $EXT_NIC from $EXT_IP to any keep state
还记得在你的配置文件/etc/pf.conf-new 中第一行就是block
all吗,这意味着会阻止所有外出流量和进入的数据(例如:你不能发送任何数据到防火墙外,上面两行表明允许无限制的流量离开防火墙)如果你是极端主义
者,你可以进一步限制网络的流量,只允许指定的数据离开防火墙。如WEB访问。
# Allow the local interface to talk unrestricted
pass in quick on lo0 all
pass out quick on lo0 all
正好上面小节所说, block all规则会阻碍外有接口的所有流量,而这也包括本地接口,我相信你注意到这两行里没有了 "keep state"选项,因为这个选项会占用更多的CPU资源来处理,因此在允许所有流量进出的时候,保持状态没太大的意义。
Final Touches
在这里,使用pfctl命令来来激活你的新规则,并测试它们是否如你所想般工作。由于我喜欢在所有规则上加上“log”选项,因此在pflog0接口上运
行tcpdump时,你会看到记录到的日志条目会记录所有进出的数据,而不论它是允许的还是被拒绝的。如果你看到某些数据被拒绝,现在你想允许它通过,你
可以向你的新配置文件添加规则,并重新装载它,接着你会看到新的规则会起作用了。
更进一步
完成了上面,你就对PF的基础有了一个很好的理解了,在这里,我会介绍一些有关PF更常用的特性,我想最好的方法是将这些特性添加到你的新的配置文件中去,OK。Let ‘s go
宏
首先让我来介绍PF中的宏,它常用来创建命名(变量),并赋值给它们。如果你要修改这些IP地址什么的 ,
那么你要手动更改这些设置.如果你到处使用宏,这些宏不会存在于pf.conf文件中,你只要修改宏定义,接着得新装载配置文件就行了.记住,你可以用宏
来提交任何事情,这包括接口,端口,IP地址,规则等…
表
表是PF提供的一个比较特别的特性。象宏,但你可以更改表而不用重新装载配置文件。如果表中有很多IP(如1000个或以上),PF会做一系列的优化,而这方面宏是没有提供的。你也可以在命令行中添加/删除命令行。PF会自动获取这些新配置。
## TABLES
table ; persist
table ; const { 10/8, 172.16/12, 192.168/16, 224/8 }
通常,你会创一个表,并命名,如"block_hosts",你也可以为这个表添加一个可选的旗帜,如const 或者 persist。 const
意味着一旦你装载了这个表,你就不能从命令行中修改这个表中的值。Persist意味着你这个表是装载到随机访问存储器中去的,因此即使这个表没有什么条
目你也可以加载它,并可向它添加/删除/修改条目或者某些值。通常,如果一个表没有什么条目,PF会卸载它,但persist
这个参数阻止这样的事发生。要想真正在你的规则中添加表,你会象下面的示例一样添加相应的内容,我通常把下面的内容加到我过滤规则 的最头部中去。
# Global filter stuff
block in log quick on $EXT_NIC from ; to any
block in log quick on $EXT_NIC from ; to any
全局选项
这个小段是你定义PF行为的地方。你会发现在这里PF向你提供了大量的选项,我们不会全部涉及,但我会讲一些我们感兴趣的东西。
## GLOBAL OPTIONS
set loginterface $EXT_NIC
set block-policy return
第一行 (set loginterface $EXT_NIC) 通常是每一个用防火墙做解决方案时所必须的选项。这让你能收集一些你感兴趣的统计。通常这些统计是不提供的。下面是一个有关这个统计的输出,我想你也不用我多说到底是什么意思了吧?--
cerberus:~/# pfctl -s info
Status: Enabled for 69 days 07:04:35 Debug: None
Interface Stats for fxp0 IPv4 IPv6
Bytes In 385597759 0
Bytes Out 179194907 0
Packets In
Passed 801631 0
Blocked 190642 0
Packets Out
Passed 713633 0
Blocked 4 0
State Table Total Rate
current entries 16
searches 15272773 2.6/s
inserts 218763 0.0/s
removals 218747 0.0/s
Counters
match 13746071 2.3/s
bad-offset 0 0.0/s
fragment 0 0.0/s
short 0 0.0/s
normalize 0 0.0/s
memory 37403 0.0/s
第二行 (set block-policy return)可以看作是
"catch-all"选项,尽管你会在你的规则选项中指定如何处理每个包,一般较为注重安全的网络均为添加这选项,因为当有一些较新的数据网络时,你不
一定会在你的规则中添加有处理这些新出现的数据包的规则
流量正常化
PF不仅仅是传递包的进出,它同时也提供修正错误包(被篡改的包)的能力。下面是一些有关修正错误包的选项。
## TRAFFIC NORMALIZATION
scrub in on $EXT_NIC all fragment reassemble
scrub out on $EXT_NIC all fragment reassemble random-id no-df
# For NFS
scrub in on $INT_NIC all no-df
scrub out on $INT_NIC all no-df
注意我洗刷了进出我的“external”接口的所有数据包。 选项 random-id
用于阻止他人监控系统从而得知有多小台电脑了进行NAT转换。使用random-id
,其他人无法监测这些内容。no-df选项主要是针对使用了NFS服务的接口(如在我自己的内部接口上)。尽管这些没有指明在流量正常化规范里面,但你最
好还是这样做。当你在你的过滤规则后面添加上 modulate state
选项时,它隐藏了某些操作系统的网络堆栈的缺陷。优点是当你的系统是进行端口转发流量时到不同端口时(如80端口的流量发到linux,而25端口的流量
发到Solaros),
外部监控系统可以检测到这些:1:你运行了两个不同的服务器。每个服务器占用一个端口号。2:你的服务器运行的操作系统。请注意,如果你在TCP过滤规则
中使用了modulate state,你就不需要再使用keep state 选项,同时 modulate state 只与TCP一起工作。
队列
要想清楚地表达PF中有关队列的内容,我想并不是一两句话就可以完成的,在这里我并不想深入地讲述有关队列方面的事,我会专门写一篇PF中队列内容的文档以Unfortunately, to properly discuss PF's
翻译 (NAT)
一个防火墙没有NAT就象一张台只有三条脚一样。防火墙通常是与NAT一起工作的。当然,在你的机器上通常有两块网卡,它们会配有不同网段的IP地址和子
网掩码。在一个典型的防火墙上,你通常会有一个外网接口,用于连接到internet上去,另一个是内网接口,用于连接到内部局域网络,通常这会是集线器
或者交换机。通过NAT,你可以做一些非常酷的工作。如公网IP到私有IP地映射;将从公网上通发过来的流量根据指定的端口请求转发到内网指定的服务器
上;允许私有网络上的机器通过一个公共的IP来共享上网。我会对这三个特性进行一一说明。
rdr on $EXT_NIC proto TCP from any to 33.11.33.55 port 25 ->; 192.168.1.33 port 25
binat on $EXT_NIC from 192.168.1.55 to any ->; 11.22.44.33
nat on $EXT_NIC from 192.168.0.0/16 to any ->; 22.33.11.55
你应该还记得上面的宏,第一行基本上说的是将来自外网接口传递给33.11.33.55端口 25 的数据会传给192.168.1.33 端口25。
第二行表明电脑自身IP为192.168.1.55会有属于它的外网IP地址 11.22.44.33 ,这意味着所有离开92.168.1.55的数据包会发往11.22.44.33,同时注意到,所有的过滤规则仍然有效。
最后一行指明NAT在192.168.0.0/16子网内有效。网内机器对外表现为IP地址是 22.33.11.55的机器
注解:这一小节是人们常会用到的 ,请明白NAT发生在所任何过滤之前,这意味着如果你如果对已进行了NAT的IP地址进行过滤,那么你的过滤规则应该指明过滤后的IP地址,而不是它的外部IP地址(你在端口重定向中会用到),好,让我们再看一下下面的范例
pass in log quick on $EXT_NIC proto TCP from any to 192.168.1.33 port 25 keep state
组
尽管,这算不上一个小节,但组的确算是PF一个非常好的特性,它让你组合简单的近似的规则成为一条规则,这样会使你的规则变得简单整洁,用不着我多说,看看下面的例子你就会明白的了:
例一
pass in log quick on $EXT_NIC proto tcp from any to 11.22.33.44 port 80 keep state
pass in log quick on $EXT_NIC proto tcp from any to 11.22.33.44 port 443 keep state
成为
pass in log quick on $EXT_NIC proto tcp from any to 11.22.33.44 port { 80, 443 } keep state
例二
pass in log quick on $EXT_NIC proto tcp from any to 192.168.1.33 port 53 keep state
pass in log quick on $EXT_NIC proto udp from any to 192.168.1.33 port 53 keep state
成为
pass in log quick on $EXT_NIC proto { tcp, udp } from any to 192.168.1.33 port 53 keep state
怎么样?这可以节省文件空间,同时让你的防火墙配置管理更加容易,在上面的表定义中我就用到{ } ,你也可以在你的pf.conf文件中使用这个特性,在任何地方都可以,哈哈。
结论
我希望本文章对你配置PF防火墙有所帮助。通过这篇文章你可以创建一个坚固的PF防火墙,当然,PF还提供了许多的功能还有说到,当你熟悉了PF后你可以尝 试配置使用这些功能。如果你对本文章有任何建议,请发到本文上面所提到的邮箱中,谢谢。
资源
· man 5 pf.conf
· man 8 pfctl