IPFW 防火牆

31.6. IPFW

IPFIREWALL (IPFW) 是一個由 FreeBSD 發起的防火牆應用軟件, 它由 FreeBSD 的志願者成員編寫和維護。 它使用了傳統的無狀態規則和規則編寫方式, 以期達到簡單狀態邏輯所期望的目標。

標準的 FreeBSD 安裝中, IPFW 所給出的規則集樣例 (可以在 /etc/rc.firewall 和 /etc/rc.firewall6 中找到) 非常簡單, 建議不要不加修改地直接使用。 該樣例中沒有使用狀態過濾, 而該功能在大部分的配置中都是非常有用的, 因此這一節並不以系統自帶的樣例作爲基礎。

IPFW 的無狀態規則語法, 是由一種提供複雜的選擇能力的技術支持的, 這種技術遠遠超出了一般的防火牆安裝人員的知識水平。 IPFW 是爲滿足專業用戶, 以及掌握先進技術的電腦愛好者們對於高級的包選擇需求而設計的。 要完全釋放 IPFW 的規則所擁有的強大能力, 需要對不同的協議的細節有深入的瞭解, 並根據它們獨特的包頭信息來編寫規則。 這一級別的詳細闡述超出了這本手冊的範圍。

IPFW 由七個部分組成, 其主要組件是內核的防火牆過濾規則處理器, 及其集成的數據包記帳工具、 日誌工具、 用以觸發 NAT 工具的 divert (轉發) 規則、 高級特殊用途工具、 dummynet 流量×××機制, fwd rule 轉發工具, 橋接工具, 以及 ipstealth 工具。 IPFW 支持 IPv4 和 IPv6。

31.6.1. 啓用 IPFW

IPFW 是基本的 FreeBSD 安裝的一部分, 以單獨的可加載內核模塊的形式提供。 如果在 rc.conf 中加入 firewall_enable="YES" 語句, 就會自動地加載對應的內核模塊。 除非您打算使用由它提供的 NAT 功能, 一般情況下並不需要把 IPFW 編進 FreeBSD 的內核。

如果將 firewall_enable="YES" 加入到 rc.conf 中並重新啓動系統, 則下列信息將在啓動過程中, 以高亮的白色顯示出來:

ipfw2 initialized, divert disabled, rule-based forwarding disabled, default to deny, logging disabled

可加載內核模塊在編譯時加入了記錄日誌的能力。 要啓用日誌功能, 並配置詳細日誌記錄的限制, 需要在 /etc/sysctl.conf 中加入一些配置。 這些設置將在重新啓動之後生效:

net.inet.ip.fw.verbose=1
net.inet.ip.fw.verbose_limit=5

31.6.2. 內核選項

把下列選項在編譯 FreeBSD 內核時就加入, 並不是啓用 IPFW 所必需的, 除非您需要使用 NAT 功能。 這裏只是將這些選項作爲背景知識來介紹。

options    IPFIREWALL

這個選項將 IPFW 作爲內核的一部分來啓用。

options    IPFIREWALL_VERBOSE

這個選項將啓用記錄通過 IPFW 的匹配了包含 log 關鍵字規則的每一個包的功能。

options    IPFIREWALL_VERBOSE_LIMIT=5

以每項的方式, 限制通過 syslogd(8) 記錄的包的個數。 如果在比較惡劣的環境下記錄防火牆的活動可能會需要這個選項。 它能夠避免潛在的針對 syslog 的洪水式拒絕服務***。

options    IPFIREWALL_DEFAULT_TO_ACCEPT

這個選項默認地允許所有的包通過防火牆, 如果您是第一次配置防火牆, 使用這個選項將是一個不錯的主意。

options    IPDIVERT

這一選項啓用 NAT 功能。

注意: 

如果內核選項中沒有加入 IPFIREWALL_DEFAULT_TO_ACCEPT, 而配置使用的規則集中也沒有明確地指定允許連接進入的規則, 默認情況下, 發到本機和從本機發出的所有包都會被阻止。

31.6.3. /etc/rc.conf Options

啓用防火牆:

firewall_enable="YES"

要選擇由 FreeBSD 提供的幾種防火牆類型中的一種來作爲默認配置, 您需要閱讀 /etc/rc.firewall 文件並選出合適的類型, 然後在 /etc/rc.conf 中加入類似下面的配置:

firewall_type="open"

您還可以指定下列配置規則之一:

  • open ── 允許所有流量通過。

  • client ── 只保護本機。

  • simple ── 保護整個網絡。

  • closed ── 完全禁止除迴環設備之外的全部 IP 流量。

  • UNKNOWN ── 禁止加載防火牆規則。

  • filename ── 到防火牆規則文件的絕對路徑。

有兩種加載自定義 ipfw 防火牆規則的方法。 其一是將變量 firewall_type 設爲包含不帶 ipfw(8) 命令行選項的 防火牆規則 文件的完整路徑。 下面是一個簡單的規則集例子:

add deny in
add deny out

除此之外, 也可以將 firewall_script 變量設爲包含 ipfw 命令的可執行腳本, 這樣這個腳本會在啓動時自動執行。 與前面規則集文件等價的規則腳本如下:

ipfw 命令是在防火牆運行時, 用於在其內部規則表中手工逐條添加或刪除防火牆規則的標準工具。 這一方法的問題在於, 一旦您的關閉計算機或停機, 則所有增加或刪除或修改的規則也就丟掉了。 把所有的規則都寫到一個文件中, 並在啓動時使用這個文件來加載規則, 或一次大批量地替換防火牆規則, 那麼推薦使用這裏介紹的方法。

ipfw 的另一個非常實用的功能是將所有正在運行的防火牆規則顯示出來。 IPFW 的記賬機制會爲每一個規則動態地創建計數器, 用以記錄與它們匹配的包的數量。 在測試規則的過程中, 列出規則及其計數器是瞭解它們是否工作正常的重要手段。

按順序列出所有的規則:

# ipfw list

列出所有的規則, 同時給出最後一次匹配的時間戳:

# ipfw -t list

列出所有的記賬信息、 匹配規則的包的數量, 以及規則本身。 第一列是規則的編號, 隨後是發出包匹配的數量, 進入包的匹配數量, 最後是規則本身。

# ipfw -a list

列出所有的動態規則和靜態規則:

# ipfw -d list

同時顯示已過期的動態規則:

# ipfw -d -e list

將計數器清零:

# ipfw zero

只把規則號爲 NUM 的計數器清零:

# ipfw zero NUM

31.6.4. IPFW 規則集

規則集是指一組編寫好的依據包的值決策允許通過或阻止 IPFW 規則。 包的雙向交換組成了一個會話交互。 防火牆規則集會作用於來自於 Internet 公網的包以及由系統發出來迴應這些包的數據包。 每一個 TCP/IP 服務 (例如 telnet, www, 郵件等等) 都由協議預先定義了其特權 (監聽) 端口。 發到特定服務的包會從源地址使用非特權 (高編號) 端口發出, 併發到特定服務在目的地址的對應端口。 所有這些參數 (例如: 端口和地址) 都是可以爲防火牆規則所利用的, 判別是否允許服務通過的標準。

當有數據包進入防火牆時, 會從規則集裏的第一個規則開始進行比較, 並自頂向下地進行匹配。 當包與某個選擇規則參數相匹配時, 將會執行規則所定義的動作, 並停止規則集搜索。 這種策略, 通常也被稱作 最先匹配者獲勝 的搜索方法。 如果沒有任何與包相匹配的規則, 那麼它就會根據強制的 IPFW 默認規則, 也就是 65535 號規則截獲。 一般情況下這個規則是阻止包, 而且不給出任何迴應。

注意: 

如果規則定義的動作是 count、 skipto 或 tee 規則的話, 搜索會繼續。

這裏所介紹的規則, 都是使用了那些包含狀態功能的, 也就是 keep state、 limit、 in、 out 以及 via 選項的規則。 這是編寫明示允許防火牆規則集所需的基本框架。

警告: 

在操作防火牆規則時應謹慎行事, 如果操作不當, 很容易將自己反鎖在外面。

31.6.4.1. 規則語法

這裏所介紹的規則語法已經經過了簡化, 只包括了建立標準的明示允許防火牆規則集所必需的那些。 要了解完整的規則語法說明, 請參見 ipfw(8) 聯機手冊。

規則是由關鍵字組成的: 這些關鍵字必須以特定的順序從左到右書寫。 下面的介紹中, 關鍵字使用粗體表示。 某些關鍵字還包括了子選項, 這些子選項本身可能也是關鍵字, 有些還可以包含更多的子選項。

# 用於表示開始一段註釋。 它可以出現在一個規則的後面, 也可以獨佔一行。 空行會被忽略。

CMD RULE_NUMBER ACTION LOGGING SELECTION STATEFUL

31.6.4.1.1. CMD

每一個新的規則都應以 add 作爲前綴, 它表示將規則加入內部表。

31.6.4.1.2. RULE_NUMBER

每一條規則都與一個範圍在 1 到 65535 之間的規則編號相關聯。

31.6.4.1.3. ACTION

每一個規則可以與下列的動作之一相關聯, 所指定的動作將在進入的數據包與規則所指定的選擇標準相匹配時執行。

allow | accept | pass | permit

這些關鍵字都表示允許匹配規則的包通過防火牆, 並停止繼續搜索規則。

check-state

根據動態規則表檢查數據包。 如果匹配, 則執行規則所指定的動作, 亦即生成動態規則; 否則, 轉移到下一個規則。 check-state 規則沒有選擇標準。 如果規則集中沒有 check-state 規則, 則會在第一個 keep-state 或 limit 規則處, 對動態規則表實施檢查。

deny | drop

這兩個關鍵字都表示丟棄匹配規則的包。 同時, 停止繼續搜索規則。

31.6.4.1.4. LOGGING

log or logamount

當數據包與帶 log 關鍵字的規則匹配時, 將通過名爲 SECURITY 的 facility 來把消息記錄到 syslogd(8)。 只有在記錄的次數沒有超過 logamount 參數所指定的次數時, 纔會記錄日誌。 如果沒有指定 logamount, 則會以 sysctl 變量 net.inet.ip.fw.verbose_limit 所指定的限制爲準。 如果將這兩種限制值之一指定爲零, 則表示不作限制。 如果達到了限制數, 可以通過將規則的日誌計數或包計數清零來重新啓用日誌, 請參見 ipfw reset log 命令來了解細節。

注意: 

日誌是在所有其他匹配條件都驗證成功之後, 在針對包實施最終動作 (accept, deny) 之前進行的。 您可以自行決定哪些規則應啓用日誌。

31.6.4.1.5. SELECTION

這一節所介紹的關鍵字主要用來描述檢查包的哪些屬性, 用以判斷包是否與規則相匹配。 下面是一些通用的用於匹配包特徵的屬性, 它們必須按順序使用:

udp | tcp | icmp

也可以指定在 /etc/protocols 中所定義的協議。 這個值定義的是匹配的協議, 在規則中必須指定它。

from src to dst

from 和 to 關鍵字用於匹配 IP 地址。 規則中必須 同時 指定源和目的兩個參數。 如果需要匹配任意 IP 地址, 可以使用特殊關鍵字 any。 還有一個特殊關鍵字, 即 me, 用於匹配您的 FreeBSD 系統上所有網絡接口上所配置的 IP 地址, 它可以用於表達網絡上的其他計算機到防火牆 (也就是本機), 例如 from me to any 或 from any to me 或 from 0.0.0.0/0 to any 或 from any to 0.0.0.0/0 或 from 0.0.0.0 to any 或 from any to 0.0.0.0 以及 from me to 0.0.0.0。 IP 地址可以通過 帶點的 IP 地址/掩碼長度 (CIDR 記法), 或者一個帶點的 IP 地址的形式來指定。 這是編寫規則時所必需的。 使用 net-mgmt/ipcalc port 可以用來簡化計算。 關於這個工具的更多信息, 也可參考它的主頁: http://jodies.de/ipcalc

port number

這個參數主要用於那些支持端口號的協議 (例如 TCP 和 UDP)。 如果要通過端口號匹配某個協議, 就必須指定這個參數。 此外, 也可以通過服務的名字 (根據 /etc/services) 來指定服務, 這樣會比使用數字指定端口號直觀一些。

in | out

相應地, 匹配進入和發出的包。 這裏的 in 和 out 都是關鍵字, 在編寫匹配規則時, 必需作爲其他條件的一部分來使用。

via IF

根據指定的網絡接口的名稱精確地匹配進出的包。 這裏的 via 關鍵字將使得接口名稱成爲匹配過程的一部分。

setup

要匹配 TCP 會話的發起請求, 就必須使用它。

keep-state

這是一個必須使用的關鍵字。 在發生匹配時, 防火牆將創建一個動態規則, 其默認行爲是, 匹配使用同一協議的、從源到目的 IP/端口 的雙向網絡流量。

limit {src-addr | src-port | dst-addr | dst-port}

防火牆只允許匹配規則時, 與指定的參數相同的 N 個連接。 可以指定至少一個源或目的地址及端口。 limit 和 keep-state 不能在同一規則中同時使用。 limit 提供了與 keep-state相同的功能, 並增加了一些獨有的能力。

31.6.4.2. 狀態規則選項

有狀態過濾將網絡流量當作一種雙向的包交換來處理。 它提供了一種額外的檢查能力, 用以檢測會話中的包是否來自最初的發送者, 並在遵循雙向包交換的規則進行會話。 如果包與這些規則不符, 則將自動地拒絕它們。

check-state 用來識別在 IPFW 規則集中的包是否符合動態規則機制的規則。 如果匹配, 則允許包通過, 此時防火牆將創建一個新的動態規則來匹配雙向交換中的下一個包。 如果不匹配, 則將繼續嘗試規則集中的下一個規則。

動態規則機制在 SYN-flood ***下是脆弱的, 因爲這種情況會產生大量的動態規則, 從而耗盡資源。 爲了抵抗這種***, 從 FreeBSD 中加入了一個叫做 limit 的新選項。 這個選項可以用來限制符合規則的會話允許的併發連接數。 如果動態規則表中的規則數超過 limit 的限制數量, 則包將被丟棄。

31.6.4.3. 記錄防火牆消息

記錄日誌的好處是顯而易見的: 它提供了在事後檢查所發生的狀況的方法, 例如哪些包被丟棄了, 這些包的來源和目的地, 從而爲您提供找到***者所需的證據。

即使啓用了日誌機制, IPFW 也不會自行生成任何規則的日誌。 防火牆管理員需要指定規則集中的哪些規則應該記錄日誌, 並在這些規則上增加 log 動作。 一般來說, 只有 deny 規則應記錄日誌, 例如對於進入的 ICMP ping 的 deny 規則。 另外, 複製 默認的 ipfw 終極 deny 規則, 並加入 log 動作來作爲您的規則集的最後一條規則也是很常見的用法。 這樣, 您就能看到沒有匹配任何一條規則的那些數據包。

日誌是一把雙刃劍, 如果不謹慎地加以利用, 則可能會陷入過多的日誌數據中, 並導致磁盤被日誌塞滿。 將磁盤填滿是 DoS ***最爲老套的手法之一。 由於 syslogd 除了會將日誌寫入磁盤之外, 還會輸出到 root 的控制檯屏幕上, 因此有過多的日誌信息是很讓人惱火的事情。

IPFIREWALL_VERBOSE_LIMIT=5 內核選項將限制同一個規則發到系統日誌程序 syslogd(8) 的連續消息的數量。 當內核啓用了這個選項時, 某一特定規則所產生的連續消息的數量將封頂爲這個數字。 一般來說, 沒有辦法從連續 200 條一模一樣的日誌信息中獲取更多有用的信息。 舉例來說, 如果同一個規則產生了 5 次消息並被記錄到 syslogd, 餘下的相同的消息將被計數, 並像下面這樣發給 syslogd

last message repeated 45 times

所有記錄的數據包包消息, 默認情況下會最終寫到 /var/log/security 文件中, 後者在 /etc/syslog.conf 文件裏進行了定義。

31.6.4.4. 編寫規則腳本

絕大多數有經驗的 IPFW 用戶會創建一個包含規則的文件, 並且, 按能夠以腳本形式運行的方式來書寫。 這樣做最大的一個好處是, 可以大批量地刷新防火牆規則, 而無須重新啓動系統就能夠激活它們。 這種方法在測試新規則時會非常方便, 因爲同一過程在需要時可以多次執行。 作爲腳本, 您可以使用符號替換來撰寫那些經常需要使用的值, 並用同一個符號在多個規則中反覆地表達它。 下面將給出一個例子。

這個腳本使用的語法同 sh(1)、 csh(1) 以及 tcsh(1) 腳本兼容。 符號替換字段使用美元符號 $ 作爲前綴。 符號字段本身並不使用 $ 前綴。 符號替換字段的值必須使用 "雙引號" 括起來。

可以使用類似下面的規則文件:

############### start of example ipfw rules script #############
#
ipfw -q -f flush       # Delete all rules
# Set defaults
oif="tun0"             # out interface
odns="192.0.2.11"      # ISP's DNS server IP address
cmd="ipfw -q add "     # build rule prefix
ks="keep-state"        # just too lazy to key this each time
$cmd 00500 check-state
$cmd 00502 deny all from any to any frag
$cmd 00501 deny tcp from any to any established
$cmd 00600 allow tcp from any to any 80 out via $oif setup $ks
$cmd 00610 allow tcp from any to $odns 53 out via $oif setup $ks
$cmd 00611 allow udp from any to $odns 53 out via $oif $ks
################### End of example ipfw rules script ############

這就是所要做的全部事情了。 例子中的規則並不重要, 它們主要是用來表示如何使用符號替換。

如果把上面的例子保存到 /etc/ipfw.rules 文件中。 下面的命令來會重新加載規則。

# sh /etc/ipfw.rules

/etc/ipfw.rules 這個文件可以放到任何位置, 也可以命名爲隨便什麼別的名字。

也可以手工執行下面的命令來達到類似的目的:

# ipfw -q -f flush# ipfw -q add check-state# ipfw -q add deny all from any to any frag# ipfw -q add deny tcp from any to any established# ipfw -q add allow tcp from any to any 80 out via tun0 setup keep-state# ipfw -q add allow tcp from any to 192.0.2.11 53 out via tun0 setup keep-state# ipfw -q add 00611 allow udp from any to 192.0.2.11 53 out via tun0 keep-state

31.6.4.5. 帶狀態規則集

以下的這組非-NAT 規則集, 是如何編寫非常安全的 '明示允許' 防火牆的一個例子。 明示允許防火牆只允許匹配了 pass 規則的包通過, 而默認阻止所有的其他數據包。 用來保護整個網段的防火牆, 至少需要有兩個網絡接口, 並且其上必須配置規則, 以便讓防火牆正常工作。

所有類 UNIX 操作系統, 也包括 FreeBSD, 都設計爲允許使用網絡接口 lo0 和 IP 地址 127.0.0.1 來完成操作系統內部的通訊。 防火牆必須包含一組規則, 使這些數據包能夠無障礙地收發。

接入 Internet 公網的那個網絡接口上, 應該配置授權和訪問控制, 來限制對外的訪問, 以及來自 Internet 公網的訪問。 這個接口很可能是您的用戶態 PPP 接口, 例如 tun0, 或者您接在 DSL 或電纜 modem 上的網卡。

如果有至少一個網卡接入了防火牆後的內網 LAN, 則必須爲這些接口配置規則, 以便讓這些接口之間的包能夠順暢地通過。

所有的規則應被組織爲三個部分, 所有應無阻礙地通過的規則, 公網的發出規則, 以及公網的接收規則。

公網接口相關的規則的順序, 應該是最經常用到的放在儘可能靠前的位置, 而最後一個規則, 則應該是阻止那個接口在那一方向上的包。

發出部分的規則只包含一些 allow 規則, 允許選定的那些唯一區分協議的端口號所指定的協議通過, 以允許訪問 Internet 公網上的這些服務。 所有的規則中都指定了 protoport、 in/out、 via 以及 keep state 這些選項。 proto tcp 規則同時指定 setup 選項, 來區分開始協議會話的包, 以觸發將包放入 keep state 規則表中的動作。

接收部分則首先阻止所有不希望的包, 這樣做有兩個不同的原因。 其一是惡意的包可能和某些允許的流量規則存在部分匹配, 而我們希望阻止, 而不是讓這些包僅僅與 allow 規則部分匹配就允許它們進入。 其二是, 已經確信要阻止的包被拒絕這件事, 往往並不是我們需要關注的, 因此只要簡單地予以阻止即可。 防火牆規則集中的每個部分的最後一條規則都是阻止並記錄包, 這有助於爲逮捕***者留下法律所要求的證據。

另外一個需要注意的事情是確保系統對不希望的數據包不做迴應。 無效的包應被丟棄和消失。 這樣, ***者便無法知道包是否到達了您的系統。 ***者對系統瞭解的越少, 其***的難度也就越大。 如果不知道端口號, 可以查閱 /etc/services/ 或到 http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers 並查找一下端口號, 以瞭解其用途。 另外, 您也可以在這個網頁上了解常見***所使用的端口: http://www.sans.org/security-resources/idfaq/oddports.php

31.6.4.6. 明示允許規則集的例子

下面是一個非-NAT 的規則集, 它是一個完整的明示允許規則集。 使用它作爲您的規則集不會有什麼問題。 只需把那些不需要的服務對應的 pass 規則註釋掉就可以了。 如果您在日誌中看到消息, 而且不想再看到它們, 只需在接收部分增加一個一個 deny 規則。 您可能需要把 dc0 改爲接入公網的接口的名字。 對於使用用戶態 PPP 的用戶而言, 應該是tun0

這些規則遵循一定的模式。

  • 所有請求 Internet 公網上服務的會話開始包, 都使用了 keep-state

  • 所有來自 Internet 的授權服務請求, 都採用了 limit 選項來防止洪水式***。

  • 所有的規則都使用了 in 或者 out 來說明方向。

  • 所有的規則都使用了 via 接口名 來指定應該匹配通過哪一個接口的包。

這些規則都應放到 /etc/ipfw.rules


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章