( 全稱:Packet Filter ) 是 UNIX LIKE 系統上進行 TCP/IP 流量過濾和網路地址轉換的軟體系統。PF 同樣也能提供 TCP/IP 流量的整形和控制,並且提供頻寬控制和數據包優先集控制。
基本介紹
- 中文名:PF防火牆
- 類別:網路名詞
- 使用平台:UNIX LIKE
- 目的連線埠:80
基本配置,規則生成捷徑,
基本配置
重定向規則: rdr on tl0 proto tcp from 192.0.2.1 to 24.65.1.13 port 80 \
-> 192.168.1.5 port 8000</strong>
數據包在經過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</strong>
但是,當一個重定向規則被從區域網路上的客戶端進行測試時,它不會正常工作。這是因為重定向規則僅適用於通過指定連線埠($ext_if,外部接口,在上面的例子中)的數據包。從區域網路上的主機連線防火牆的外部地址,並不意味著數據包會實際的通過外部接口。防火牆上的TCP/IP棧會把到來的數據包的目的地址在通過內部接口時與它自己的IP位址或者別名進行對比檢測。那樣的數據包不會真的通過外部接口,棧在任何情況下也不會建立那樣的通道。因而,PF永遠也不會在外部接口上看到那些數據包,過濾規則由於指定了外部接口也不會起作用。
指定第二條針對內部接口的也達不到預想的效果。當本地的客戶端連線防火牆的外部地址時,初始化的TCP握手數據包是通過內部接口到達防火牆的。重定向規則確實起作用了,目標地址被替換成了內部伺服器,數據包通過內部接口轉發到了內部的伺服器。但源地址沒有進行轉換,仍然包含的是本地客戶端的IP位址,因此伺服器把它的回應直接傳送給了客戶端。防火牆永遠收不到應答不可能返回客戶端信息,客戶端收到的應答不是來自它期望的源(防火牆)會被丟棄,TCP握手失敗,不能建立連線。
當然,區域網路里的客戶端仍然會希望象外部客戶一樣透明的訪問這台內部伺服器。有如下的方法解決這個問題:
水平分割 DNS
存在這樣的可能性,即配置DNS伺服器使得它回答內部主機的查詢和回答外部主機的查詢不一樣,因此內部客戶端在進行名稱解析時會收到內部伺服器的地址。它們直接連線到內部伺服器,防火牆根本不牽扯。這會降低本地流量,因為數據包不會被送到防火牆。
將伺服器移到獨立的本地網路
增加單獨的網路接口卡到防火牆,把本地的伺服器從和客戶端同一個網段移動到專用的網段(DMZ)可以讓本地客戶端按照外部重定向連線的方法一樣重定向。使用單獨的網段有幾個優點,包括和保留的內部主機隔離增加了安全性;伺服器(我們的案例中可以從網際網路訪問)一旦被控制,它不能直接存取本地網路因為所有的連線都必須通過防火牆。
TCP 代理
一般而言,TCP代理可以在防火牆上設定,監聽要轉發的連線埠或者將內部接口上到來的連線重定向到它監聽的連線埠。當本地客戶端連線防火牆時,代理接受連線,建立到內部伺服器的第二條連線,在通信雙方間進行數據轉發。
簡單的代理可以使用inetd和nc建立。下面的/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</strong>
下面的重定向規則轉發內部接口的80連線埠到代理:
rdr on $int_if proto tcp from $int_net to $ext_if port 80 -> \
127.0.0.1 port 5000</strong>
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</strong>
這會導致由客戶端發起的初始化連線在收到內部接口的返回數據包時轉換回來,客戶端的源ip地址被防火牆的內部接口地址代替。內部伺服器會回應防火牆的內部接口地址,在轉發給本地客戶端時可以反轉NAT和RDR。這個結構是非常複雜的,因為它為每個反射連線產生了2個單獨的狀態。必須小心配置防止NAT規則套用到了其他流量,例如連線由外部發起(通過其他的重定向)或者防火牆自己。注意上面的rdr規則會導致TCP/IP棧看到來自內部接口帶有目的地址是內部網路的數據包。
規則生成捷徑
簡介
PF提供了許多方法來進行規則集的簡化。一些好的例子是使用宏和列表。另外,規則集的語言或者語法也提供了一些使規則集簡化的捷徑。首要的規則是,規則集越簡單,就越容易理解和維護。
使用宏
宏是非常有用的,因為它提供了硬編碼地址,連線埠號,接口名稱等的可選替代。在一個規則集中,伺服器的IP位址改變了?沒問題,僅僅更新一下宏,不需要弄亂花費了大量時間和精力建立的規則集。
通常的慣例是在PF規則集中定義每個網路接口的宏。如果網卡需要被使用不同驅動的卡取代,例如,用intel代替3com,可以更新宏,過濾規則集會和以前功能一樣。另一個優點是,如果在多台機器上安裝同樣的規則集,某些機器會有不同的網卡,使用宏定義網卡可以使的安裝的規則集進行最少的修改。使用宏來定義規則集中經常改變的信息,例如連線埠號,IP位址,接口名稱等等,建議多多實踐!
# define macros for each network interface
IntIF = "dc0"
ExtIF = "fxp0"
DmzIF = "fxp1"</strong>
另一個慣例是使用宏來定義IP位址和網路,這可以大大減輕在IP位址改變時對規則集的維護。
# define our networks
IntNet = "192.168.0.0/24"
ExtAdd = "24.65.13.4"
DmzNet = "10.0.0.0/24"</strong>
如果內部地址擴展了或者改到了一個不同的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</strong>
看看下面更簡單的例子:
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 }</strong>
這個規則集從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</strong>
注意雖然宏和列表簡化了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</strong>
可以看到,PF擴展僅僅是簡化了編寫和維護pf.conf檔案,實際並不簡化pf(4)對於規則的處理過程。
宏不僅僅用來定義地址和連線埠,它們在PF的規則檔案中到處都可以用:
pre = "pass in quick on ep0 inet proto tcp from "
post = "to any port { 80, 6667 } keep state"</strong>
# David‘s classroom
$pre 21.14.24.80 $post</strong>
# Nick‘s home
$pre 24.2.74.79 $post
$pre 24.2.74.178 $post</strong>
擴展後:
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</strong>
PF 語法
PF的語法相當靈活,因此,允許編寫非常靈活的規則集。PF能夠自動插入某些關鍵字因此它們不必在規則中明確寫出,關鍵字的順序也是隨意的,因此不需要記憶嚴格的語法限制。
減少關鍵字
要定義全部拒絕的策略,使用下面2條規則:
block in all
block out all</strong>
這可以簡化為:
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</strong>
可以簡化為:
block in on rl0
pass in quick log on rl0 proto tcp to port 22 keep state</strong>
第一條規則阻塞rl0上從任意到任意的進入數據包,第