精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>电脑技术>>● FreeBSD>>OpenBSD 数据包过滤 PF FAQ中文版(转)-3

主题:OpenBSD 数据包过滤 PF FAQ中文版(转)-3
发信人: drillwater(灌水)
整理人: sungang(2004-11-15 10:15:17), 站内信件
(续上)

 
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(8) 
 
可载入的规则使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 $ 
============================================================================== 
 
 
PF: 队列和优先级 
 
------------------------------------------------------------------------------ 
 
目录 
 
  * 队列 
  * 日程 
      + 基于类的队列 
      + 优先队列 
      + 随机早期检测 
      + 外部拥塞告知 
  * 配置队列 
  * 为队列分配数据流 
  * 实例1: 小型家庭网络 
  * 实例2: 公司网络 
 
------------------------------------------------------------------------------ 
 
队列 
 
使用队列是为了按顺序保存一些等待处理的数据。在计算机网络中,当数据包由一台主机发出后,他们将进入一个等待队列,由操作系统决定哪个队列或者某个队列中的哪些包将被处理。操作系统选取包进行处理的顺序将影响网络性能。例如,一个用户打开了两个网络程序:SSH和FTP,由于SSH的时效性要求,在理想情况下SSH数据包将先于FTP数据包被处理。当用户在SSH客户端敲入一条命令时,需要有及时的反馈信息,但是FTP数据传输延时一段时间不会引起太大注意是可以忍受的,但是如果系统在处理SSH连接之前正在处理大量的FTP数据包,这时会怎样呢?SSH的数据包会留在队列中(或者由于队列长度限制被丢弃),导致SSH会话滞后。通过定义队列策略,网络带宽可以公平地在不同应用程序、用户和计算机之间分配。 
 
注意队列只是对流出外部接口的数据包起作用。当数据包流入内部接口时再做队列将是非常迟的,因为当内部接口收到这些数据包时他们已经耗用了带宽。唯一的解决办法是在相邻的路由器启用队列,或者,如果接受到数据包的主机被当作是个路由器,那么在数据包流出该路由器的接口上启用队列。 
 
日程 
 
日程用来规定执行哪条队列和执行的顺序。默认情况下,OpenBSD使用先进先出(FIFO)队列。先进先出队列类似于在超市或者银行排队,先进入队列将被优先处理。新到的包被加在队列尾。如果队列满了,新到的包将被丢弃,这就是所谓的“弃尾”。 
 
OpenBSD支持另外两种日程: 
 
  * 基于类的队列 
  * 优先级队列 
 
基于类的队列 
 
基于类的队列(CBQ)是一种排序算法,它将网络连接带宽在很多队列和类中分摊,每个队列都拥有基于源、目的地址,端口号,协议等信息分配的网络流量。一个队列可以被随意配置借用它父队列的带宽,前提是父队列没有占用该带宽。队列也有优先级,例如SSH,它的数据包将提前于含有大量数据流的队列(例如FTP)被处理。 
 
CBQ队列以分等级的方式部署,最高一级是根队列,用来定义全部带宽。子队列建立在根队列之下,每一个子队列可以分配根队列所规定的部分带宽。例如,定义如下队列: 
 
    Root Queue (2Mbps) 
 
        Queue A (1Mbps) 
        Queue B (500Kbps) 
        Queue C (500Kbps) 
 
这里总带宽被设置为2Mbps,然后由子队列分割。 
 
在子队列中仍然可以定义下一级策略。用来在不同用户中平等地分配带宽,同时对他们的流量进行分类,以便使某个协议不会抢占其他协议的带宽。具体队列结构定义如下: 
 
(待续)


----
   

[关闭][返回]