哪些可以优化、哪些最好不修改
网络配置固有很大的可调性。 当我们参看pf.conf的用户手册或者其它的参考资料时, 很容易就被一大堆优化设置的选项和设定搞懵。
头脑中一定要有这个概念, PF的默认配置非常适合于绝大多数环境。不管怎样, 一些设置和变量提供了一些调整的可能性, 而其它的一些则强烈建议你只有在非常特殊的情形下才能修改默认配置,我们将在本章中讨论这些情形。
我们从一些你必须了解但不到万不得已最好不修改的的全局设定开始。如果你阅读了pf.conf的用户手册, 你会发现还有一些其它的选项, 但是它们与网络测试和性能调整关系不大。
你要设置的全局选项(global options), 应该在pf.conf文件中位于所有的宏定义之后以及地址转换或者过滤规则之前。在下面几小节中,我们将用实例进行解释。
block-policy
阻止策略(block-policy)这个选项决定怎样反馈信息, 如果有的话, PF将告诉那些尝试连接的主机,连接请求将会被禁止。有两个可选项: drop, 将直接丢弃所有禁止的数据包并不向发送方反馈任何信息, 另一个可选项是 return, 向发送方返回一个带有状态状态码的数据包,例如状态码为连接拒绝(Connection refused)或者其它类似的状态码。
这些年来,有很多关于如何正确地设置阻止策略的讨论。默认的block-policy设置为drop, 也就是意味着这个被阻止的数据包被直接丢弃、不向发送方进行任何信息反馈。不过, 直接丢弃被阻止的数据包增加了一些可能性,也就是发送者可能会继续想你发送这些未被答复的数据包而不是将连接丢弃(断开)。因此, 可能发送者持续这种尝试直至超时。除非你能想出一个更好的理由将其设置为drop, 否则将block policy设置为 return, 像这样
代码: 全选
set block-policy return
例如,如果在被已经加入到<bruteforce> table后,有些骚扰者还在不断的尝试你的网络资源,你可以修改第68页上“打发不速之客”里面的brute force 保护规则,这样可以仍将block-policy设置为return、但是使用 block drop quick from <bruteforce> 来让那些骚扰者(这里是指brute forcers,采用暴力破解的人)浪费额外的能量。 还有一个例子是丢弃从不可路由地址到外部接口(Internet)的通讯。
skip
skip选项让PF排除对指定接口的所有处理。它的效果和在该接口上设置pass all规则及其相似, 例如pass on $int_if。一个显而易见的例子就是在loopback接口上禁用过滤, 尽管在多数接口上过滤可以增加一些安全性或便利性方面的提升:
代码: 全选
set skip on lo0
默认请况下并非设置skip, 也就是所有已经配置的接口都将经过PF的处理。为了简化规则, 将那些不需要设定过滤的接口设置为skip将可以稍微提升一些性能。
state-policy
state-policy选项指定了PF如何如何将数据包匹配到状态表(state table)。可选项是floating和if-bound。两者的区别在于一旦状态表(state table)里为其产生了状态项,接下来如何处理后续的相关数据包。(whoami:两者的区别在于在哪里匹配后续的数据包可能更贴切一些。)
如何采用默认的floating state policy, 通讯可以在所有接口上匹配状态, 并非仅在产生状态的那个接口上匹配。如果使用if-bound policy, 通讯仅在产生状态的接口上进行匹配。在其它接口或组上的通讯将不匹配现有的数据包(whami:这里这样理解可能好一些——其它接口上的通讯即便匹配这个状态,PF也不会放行这些通讯,也就是仅在此接口上放行匹配该状态的通讯)。像block-policy, 这个设置是一个全局的状态匹配策略。如果需要你可以单独修改某条规则的state policy。例如, 在一个规则集里使用的是默认的floating state policy, 你可以将一条规则修改为:
代码: 全选
pass out on egress inet proto tcp to any port $allowed modulate state (if-bound)
警告 使用state-policy if-bound的情形相当的罕见, 通常情况下,我们推荐您使用默认配置。
timeout
timeout选项为那些包含状态项的接口设置超时期限和相关的选项。参数的主要部分是以秒计算的特定协议类型、并且带有前缀 tcp., udp., icmp., 和其它类型.. 不过, adaptive.start 和 adaptive.end 则表示了状态表选项的数值。
警告
这些选项可用来优化设置一提升性能, 但是修改默认的特定协议设置值可能产生巨大的风险,那些有效但空闲的连接可能被过早地丢弃或被完全阻止。
你可修改的 timeout 选项是:
adaptive.start 和 adaptive.end
[INDENT]一旦状态选项的数量达到adaptive.start的设置值,系统会放宽的timeout数值的限制。当状态的数量到达 adaptive.end 时, 所有的timeout都设置为0, 本质上马上将所有的状态置为过期。默认值分别为6000 和 12000 (按照state limit的80%和120%进行设置)。这些设定通过下一小节讨论的limit选项的memory pool limit参数有紧密的关系。
[/INDENT]interval
[INDENT]这个值表示清理清理过期状态和碎片的周期,以秒计。默认值为10秒。
[/INDENT]frag
[INDENT]这个frag值表示碎片在被删除前保持为一个未组装的状态的时间(以秒计)。默认值是30秒。
[/INDENT]src.track
[INDENT]当设置时, src.track 表示当最后一个状态过期后,source-tracking数据还保存多长时间,以秒计。默认值是0秒。
[/INDENT]你可以用命令 pfctl -s timeouts 检查当前所有的timeout参数设定。这里给出的是系统默认的设置值。
代码: 全选
$ sudo pfctl -s timeouts
tcp.first 120s
tcp.opening 30s
tcp.established 86400s
tcp.closing 900s
tcp.finwait 45s
tcp.closed 90s
tcp.tsdiff 30s
udp.first 60s
udp.single 30s
udp.multiple 60s
icmp.first 20s
icmp.error 10s
other.first 60s
other.single 30s
other.multiple 60s
frag 30s
interval 10s
adaptive.start 6000 states
adaptive.end 12000 states
src.track 0s
limit
limit选项指定PF用于状态表和地址表的内存池的大小。这些很难限制, 所以你可能因为种种原因增加或调整这些数值。如果你的网络很繁忙有大量的请求,高于默认值的许可, 或者如果你的设置需要一些包含很多地址的表格或大量的地址表, 那么本小节正是为你准备的。
你要知道一个很重要的事情,内存池里的可用内存量取自内核内存空间, 并且可用内存量仅是总的内核内存空间的一小部分。 在系统启动时内核为自己分配了一个固定的内存量; 不过, 因为内核内存不会被交换, 分配给内核自己的内存量永远不可能等于或者超过系统的物理内存量。如果真发生了这样的情况, 就没有空间可以运行user-mode程序了。究竟内存池可以使用多少内存依赖于你的硬件平台、以及一些与本地系统相关的、难以预料变量。 在i386构架上最大的内核内存在 768MB 到 1GB 之间, 依赖于一系列因素, 这些因素包括系统内硬件设备的数量和种类。实际可用于分配给内存池的内存量受这些因素的综合影响, 同样与系统特定变量相关。
你可以使用 pfctl -sm 检查当前的 limit 设置。典型的输出像这样:
代码: 全选
$ sudo pfctl -sm
states hard limit 10000
src-nodes hard limit 10000
frags hard limit 5000
tables hard limit 1000
table-entries hard limit 200000
要修改这些值, 你可以编辑 pf.conf 文件,直接在上面定义新的限制值t。例如, 你使用下面的设置会state数量限制提升到25,000,表内项目的数量提升到300,000:
代码: 全选
set limit states 25000
set limit table-entries 300000
你还可以同时将几个limit参数设置在一行内,只需将他们放入大括号内, 像这样:
代码: 全选
set limit { states 25000, src-nodes 25000, table-entries 300000 }
debug
debug选项在kern.debug log level时测定PF将产生的什么错误信息。默认值是 urgent, 它的意思是值log严重错误。其它可选项是 none (没有信息), misc (提供的信息比urgent略多), 最后是 loud (为多数操作生成情报信息)。 当我以debug level loud运行家里的网关一段时间后, 这是我的 /var/log/messages 文件的样子:
代码: 全选
$ tail -f /var/log/messages
Oct 4 11:41:11 skapet /bsd: pf_map_addr: selected address 194.54.107.19
Oct 4 11:41:15 skapet /bsd: pf: loose state match: TCP 194.54.107.19:25 194.54.107.19:25
158.36.191.135:62458 [lo=3178647045 high=3178664421 win=33304 modulator=0 wscale=1]
[lo=3111401744 high=3111468309 win=17376 modulator=0 wscale=0] 9:9 R seq=3178647045
(3178647044) ack=3111401744 len=0 ackskew=0 pkts=9:12
Oct 4 11:41:15 skapet /bsd: pf: loose state match: TCP 194.54.107.19:25 194.54.107.19:25
158.36.191.135:62458 [lo=3178647045 high=3178664421 win=33304 modulator=0 wscale=1]
[lo=3111401744 high=3111468309 win=17376 modulator=0 wscale=0] 10:10 R seq=3178647045
(3178647044) ack=3111401744 len=0 ackskew=0 pkts=10:12
Oct 4 11:42:24 skapet /bsd: pf_map_addr: selected address 194.54.107.19
正如你看见的那样, loud level向你提供了一个细致的输出, 这里PF多次报告了当前处理的接口的IP地址。 在selected address之间, PF对同一个数据包警告了两次,这个数据包的序列号非常接近期望范围的边缘。这个细致的输入让人头一眼看过去很吃惊, 但是在某些情况下,学习这样的输出内容是诊断出问题所在的最佳方式,然后修改后再回过头来看一下,你的修改是否奏效。
值得一提的是,这个选项可以在命令行上执行,用 pfctl -x , 后面跟着你想要的debug level。命令
代码: 全选
pfctl -x loud
代码: 全选
pfctl -x none
ruleset-optimization
ruleset-optimization选项设置规则集优化器的模式。默认值是 basic, 它的含义是不自动执行优化。如果你将ruleset-optimization basic这行放进你的pf.conf文件,然后重新导入你的配置, 在被导入前,这个规则集还要经过更多的处理。启用了这个基本优化选项后, 优化器将做如下事情:
- 删除重复的规则
- 删除那些是其它规则的子集的规则
例如, 假设你定义了宏并将之与规则代码: 全选
tcp_services = { ssh, www, https }
代码: 全选
pass proto tcp from any to self port $tcp_services
结合。而在规则集的其它地方, 你有一个不同的规则:代码: 全选
pass proto tcp from any to self port ssh
很明显,第二条规则是第一条规则的子集, 而它们可以整合为一条规则。另一种常见的合并是将一条pass规则,像和一个几乎一样的pass规则,唯一的区别是在目标地址的位置上用 all 替换了 int_if:network。代码: 全选
pass proto tcp from any to int_if:network port $tcp_services
- 如果喜欢,将规则整合进表格
典型的 rule-to-table 优化是那些基于相同标准的pass, redirect, 或者block规则, 除了源和/或目标地址。 - 改变规则顺序来提升性能
随着规则集优化设定到profile, 优化器对应实际网络通讯分析导入的规则集以便确定quick规则的的优化顺序。
你也可以从命令行使用pfctl命令设置优化选项的值:这个例子启用了basic模式的规则集优化。代码: 全选
$ sudo pfctl -o basic
因为优化可能删除或重新排序规则, 这意味着一些统计信息, 每条规则的主要评估(执行)次数将会变的难以预料。不过, 在多数情况下影响不大。
optimization
optimization选项为处理状态超时(state-timeout)指定了profiles。可选项是 normal, high-latency, satellite, aggressive, 和 conservative。
除非你有非常特殊的需要,推荐保持默认的 normal 设定。high-latency 和 satellite 的作用类似, 都是延长状态的有效期以便补偿潜在地高延迟。 aggressive设置更短的过期时限以便节省内存(事先警告读者, 如果你的系统已经关闭了负载和通讯的限制,你有可能冒着丢弃空闲但有效的连接的风险)。最后是 conservative 选项,它设置非常长的状态保持和空闲连接时间, 代价是需要占用额外一些的内存。
清理你的通讯: scrub和antispoof
我们下面要讨论两个特性是 scrub 和 antispoof, 它们提供共享同一个主题: 提供自动的保护以阻止具有潜在危险的通讯破坏你的网络。两者加在一起就是通常大家所说的"网络卫生"工具,因为它们很彻底地消毒(清理)你的网络.
scrub
scrub关键字启用了网络通信正常化。使用scrub, 破碎的数据包被重新组合, 而那些无效的碎片,例如重复的碎片被丢弃, 所以得到的数据包是完整的、干净的(清楚的)。启用scrub能够提供一种保护措施,这种措施可以阻止某些特定类型的攻击, 这些攻击基于系统错误地处理数据包碎片。详说明1
有一系列补充选项可用, 但是这里显示的最简单的模式适合于绝大多数配置:说明1
一些著名的攻击技术, 包括几种以前和现在使用的拒绝服务(DoS) 设置, 利用了碎片处理中的bug, 可能导致 out-of-memory 或其它资源枯竭的情况。一个这样的利用, 瞄准了 Cisco的PIX防火墙系列, 有关情况可查阅 http://www.cisco.com/en/US/products/pro ... e78d.shtml
代码: 全选
scrub in all
antispoof
有一些非常有效和常见的数据包处理方法可以作为PF规则写入, 但是并非没有这些方法规则集就变成了长的、复杂的、和易出错的样板。因此, antispoof应用在一个过滤和阻止的常见的特定情况下。 这个机制阻止那些来自假冒和伪造的IP地址的活动, 主要通过阻止那些出现在接口上一些行为异常的数据包,这些异常数据包的运动方向(轨迹)没有逻辑上的可能性。
有了antispoof, 我们可以指定剔除那些来自外部的假冒进站通讯,以及任何来自内网的假冒通讯(不过,不太可能)。图 F9-1 说明了这个概念。
要建立这类视图上描述的保护措施, 在视图的网络中的两个接口上都指定antispoof,用这两行:
代码: 全选
antispoof for $ext_if
antispoof for $int_if
测试你的设置
现在我们将注意力转向测试, 现在是时候重新研究一下那些让你的设置可发挥作用的正确规划方案了。我们曾将它放到几章之前,目的是让你看到一些需要知道的一些特性, 但是现在你准备好网络规划就显得非常重要了。实际上, 在以往我们进行所有真正琐碎的设置时, 手边有一个规划方案是非常重要的。
我们在本书里曾经实施的最简单的的规划方案大致包含几块:
我们例子中网络物理布局是以一个网关为中心,此网关通过外部接口$ext_if连接到Internet。 连接到网关内部接口$int_if的是一个带有一些工作站的本地网络,也可能还有几台供内部使用的服务器。 最后, 有一个DMZ网络连接到网关的接口$dmz_if上, 这个DMZ网络里的服务器为内部网络和Internet提供服务。图F9-2 显示了这个网络结构布局
相关规则集的详细功能描述像这样:
- 外部的机器应该可以访问到我们提供的服务
- 连接到网关内部接口$int_if的本地网络内的机器应该可以访问所有外部或内部的资源
- 位于DMZ区的机器应该可以访问一些网络服务
表9-1: 简单规则集的测试步骤实例
代码: 全选
====================================================================================================
测试项目 希望的结果
尝试从本地网络连接到DMZ网络的服务器的每一个允许的端口 这个连接应该被允许(pass)
尝试从本地网络连接到外部网络的服务器的每一个允许的端口 这个连接应该被允许(pass)
尝试从DMZ网络的任意端口连接到本地网络 这个连接应该被禁止(block)
尝试从DMZ网络连接到外部网络的服务器的每一个允许的端口 这个连接应该被允许(pass)
尝试从外部网络连接到DMZ网络的$webserver的每一个$webports端口 这个连接应该被允许(pass)
尝试从外部网络连接到DMZ网络的$webserver的端口25(SMTP) 这个连接应该被禁止(block)
尝试从外部网络连接到DMZ网络的$emailserver端口80(HTTP) 这个连接应该被禁止(block)
尝试从外部网络连接到DMZ网络的$emailserver端口25(SMTP) 这个连接应该被允许(pass)
尝试从外部网络连接到本地的一台或多台主机 这个连接应该被禁止(block)
你的配置也许包含其它测试或者在某些特别的地方略有差异。有可能你在真正的环境里应该指定如何log数据包或连接。最重要的事情就是,你需要在测试前先确定自己的网络方案,也就是说你到底希望有什么样的效果。
通常的情况下啊, 你应该使用那些普通用户喜欢用的程序进行测试, 例如在不同操作系统上的web浏览器或email客户端。连接尝试的结果应该很简单,要么成功、要么失败,这个要根据你的方案设置。如果测试中的某项结果出乎意料, 你就要调试你的规则集了。
调试规则集
究竟因为什么使你的配置并未达到预期的效果? 有可能是规则集存在一个逻辑错误, 如果这样你需要找到这个错误并进行修正。在规则集里跟踪逻辑错误可能非常耗时,而且可能会需要手动评估你的规则集, 因为这个错误既有可能在pf.conf文件里, 也有可能在导入版本的宏扩展和优化项里。
在你准备检查规则集以前, 你可以检查的确定一下到底是不是规则集引起的问题。使用pfctl -d命令来暂时禁用规则集,然后看看是否还存在同样的问题,这样的做法可以迅速确定问题所在,减少你的麻烦。
在邮件列表, 新闻组, 或者其它论坛里, 我们经常看到用户最初用户抱怨PF出现的问题,最后发现往往是基本的网络原因。 网络接口的错误 duplex 设置, 坏的netmask, 或者甚至有毛病的硬件等都是一些常见的原因。
如果在没有启用PF时问题依旧, 那么很可能PF配置并非问题所在,你应该调试网络的其它部分。不管怎样, 如果你要调整你的PF配置, 检查并确认PF确实启用和规则集确实导入了是很重要的, 使用如下命令:
代码: 全选
$ sudo pfctl -si | grep Status
Status: Enabled for 20 days 06:28:24 Debug: Loud
代码: 全选
$ sudo pfctl -sr
scrub in all fragment reassemble
block return log all
block return log quick from <bruteforce> to any
anchor "ftp-proxy/*" all
这里的 pfctl -sr 等同于 pfctl -s rules。实际的输出可能比这里显示的多一些, 我们在这里仅是向你举个例子,目的是让你知道规则集确实启用后应该显示什么样的内容。出于调试的目的在pfctl命令行中加上 -vv 选项是很有用处的,因为它可以显示规则序列号以及一些额外的调试信息,像这样:
代码: 全选
$ sudo pfctl -vvsr
@0 scrub in all fragment reassemble
[ Evaluations: 67274995 Packets: 34231784 Bytes: 9800756925 States: 0 ]
[ Inserted: uid 0 pid 1013 ]
@0 block return log all
[ Evaluations: 618114 Packets: 15833 Bytes: 1444217 States: 0 ]
[ Inserted: uid 0 pid 1013 ]
@1 block return log quick from <bruteforce:2> to any
[ Evaluations: 618114 Packets: 13208 Bytes: 792140 States: 0 ]
[ Inserted: uid 0 pid 1013 ]
@2 anchor "ftp-proxy/*" all
[ Evaluations: 604906 Packets: 3498832 Bytes: 2803255822 States: 0 ]
[ Inserted: uid 0 pid 1013 ]
此时, 你应该逐项检查导入的这个规则集。找出匹配这些数据包的规则。哪个是最后一条匹配规则? 如果一个数据包匹配一条以上的规则, 是否其中的一条规则是quick规则? 详说明2
说明2
你可能要翻看一下前面的章节, 当一个数据包匹配了一条quick规则, 余下的规则评估就停止了,无论该这条qucik规则怎样处理这个数据包。
你要一直跟踪规则集的评估直至规则集结束或者数据包匹配了一条quick规则, 因为所有的quick规则意味着评估结束。如果数据包没有到达你希望的处理它的那条规则时评估就意外地终止了(译者注:也就是说其它的规则提前对这个数据包做了最终的处理), 你就找到你的逻辑错误了。
规则的逻辑错误一般存在于这三类情况中:
- 你的规则没有匹配因为其从未被评估。在其前面的一条quick规则匹配了,从而评估终止了。
- 你的规则被评估了,但是最终因为规则集的标准而没有匹配数据包。
- 你的规则被评估了, 这条规则也匹配, 但是这个数据包还匹配另一条规则。最后一条匹配的规则决定连接的命运。
这里,我们用tcpdump在xl0接口上查看TCP通讯(但不显示SSH和SMTP通讯),然后以很详尽的(vvv) 模式打印出结果。
代码: 全选
$ sudo tcpdump -nvvvpi xl0 tcp and not port ssh and not port smtp
tcpdump: listening on xl0, link-type EN10MB
21:41:42.395178 194.54.107.19.22418 > 137.217.190.41.80: S [tcp sum ok]
3304153886:3304153886(0) win 16384 <mss 1460,nop,nop,sackOK,nop,wscale 0,nop,nop,timestamp
1308370594 0> (DF) (ttl 63, id 30934, len 64)
21:41:42.424368 137.217.190.41.80 > 194.54.107.19.22418: S [tcp sum ok]
1753576798:1753576798(0) ack 3304153887 win 5792 <mss 1460,sackOK,timestamp 168899231
1308370594,nop,wscale 9> (DF) (ttl 53, id 0, len 60)
这里显示的是一个成功连接到网站的连接。
你还可以找到自己感兴趣的事情, 例如根据规则不应该发生的连接失败,或者明确禁止的通讯连接成功。
在这种情形下,测试包括通过你的规则跟踪数据包路径。再说一次, 请检查是否启用或禁用PF会有不同的结果。根据最初的测试结果, 你可以采用前面描述的方法进行某些分析。 一旦你有了一个如何让数据包通过规则集和接口的合理的推论, 使用tcpdump依次查看每个接口上的通讯。使用tcpdump的过滤功能仅查看那些在你特定环境里应该匹配的数据包, 例如,在smtp端口上以及目的地址为192.0.2.19的通讯。
在规则里找出不再匹配设计方案相关网络通讯的确切位置。在可能影响方案效果的规则上里加上log, 然后在相关的pflog接口上开启tcpdump以便查看究竟该数据包匹配的是哪条规则。
测试过程的主要原则基本不变。如果你已经判断出pf.conf文件是问题所在, 再说一次,你的工作目标就是找出究竟是哪条规则匹配了该数据包,以及哪条规则最终决定了数据包被放行或阻止。
了解你的网络, 掌握主控权
这本书反复地介绍了怎样用PF集相关工具减轻你的工作量, 作为网络管理员, 你应该掌握网络的主动权并让它按照你的规则工作。换句话说, 本书的主题是关于如何构建一个你想要的网络。
运行一个网络可能很有意思, 希望你喜欢这本书,我认为PF是最好的可用工具。在目前的PF中, 我有意地通过一些有趣和有用配置尽早地向你介绍它的设计思路和方法, 而不是想将这本书写成一本完整的参考教程。完整的参考教程已经在PF的用户手册里了, 它会随着每6个月的OpenBSD新版本的发布而更新。通过本章的阅读你应该可以发现一系列在线的、或印刷的有用文献, 它们带有一些简短的注释, 然后是硬件说明, 各类的支持, 以及怎样和开发者与用户社区互动。
现在你已经有了PF相关的基本知识, 你可以开始根据自己的思路构建一个网络。你自己决定一切, 而现在你可以发现自己提升到了一个新的高度,你知道了怎样在用户手册里找到自己需要的答案。实际上好戏才刚刚开始!