用IPFW實現BSD防火牆(轉)

BSDLite發表於2007-08-16
用IPFW實現BSD防火牆(轉)[@more@]用IPFW實現BSD防火牆

文字文字文字

FreeBSD作業系統本身帶有二種內建的IP資訊包檢查機制:ipfw和ipfilter。在建立決定允許哪些資訊包進入系統、哪些資訊包會被拒之系統門外的規則集方面,二種機制各有自己獨特的語法。在這裡,我們將討論如何使用ipfw配置系統的防火牆。

在能夠使用ipfw防火牆機制之前,我們需要在FreeBSD的核心配置檔案中新增一些選項,並重新編譯核心。如果不太清楚如何編譯FreeBSD的核心,請參閱相關的手冊。

可供ipfw使用的選項有好幾個,我們首先從討論LINT開始。在這裡,我透過使用“/”符號進行搜尋,以便能夠快速地發現恰當的小節:

cd /usr/src/sys/i386/conf
more LINT
/IPFIREWALL
# IPFIREWALL和ipfw軟體,這二者就可以支援IP防火牆的構建。IPFIREWALL_VERBOSE向
# 系統的註冊程式傳送註冊資訊包,IPFIREWALL_VERBOSE_LIMIT限制一臺機器註冊的次
# 數。注意:如果沒有在啟動時新增任何允許IP訪問的規則,IPFIREWALL的預設配置是
# 禁止任何IP資料包進出系統的,這時你甚至不能訪問網路中的其他機器。建議首次使
# 用這一功能時在/etc/rc.conf中設定firewall_type=open,然後在對核心進行測試後
# 再在/etc/rc.firewall仔細地調整防火牆的設定。IPFIREWALL_DEFAULT_TO_ACCEPT使
# 得預設的規則允許所有形式的訪問。在使用這一變數時應該非常小心,如果駭客能夠
# 突破防火牆,就能任意訪問你的系統。

要啟用ipfw,必須設定IPFIREWALL選項,它將通知作業系統的核心檢查每個IP資料包,將它們與規則集進行比較,透過新增IPFIREWALL_VERBOSE選項包括註冊支援是一個好主意,還應該透過新增IPFIREWALL_VERBOSE_LIMIT選項來限制核心註冊的資料包的數量。

除非在規則集中進行了特別的說明,預設情況下ipfw將阻塞所有的IP資料包。由於預設設定可以仔細地控制哪些資料包會被接受,因此我非常喜歡它。我不喜歡核心會接受自己都不清楚內容的資料包,如果需要的資料沒有被系統接受,會得到系統的提示,並修改規則集使系統可以接受它們。這時,如果有沒有預料到的資料包透過系統也不會知道。因此,我不會透過包括IPFIREWALL_DEFAULT_TO_ACCEPT選項來繞過預設的設定。

# IPDIVERT啟用由ipfw divert使用的轉向IP套接字。這一選項需要與natd聯合使用。由
# 於在本例中建立的防火牆僅用於保護一臺機器,因此不需要這個選項。
# IPSTEALTH啟動支援秘密轉發的程式碼,這一選項在使防火牆不被traceroute和類似工具發現時很有用。

這是一個非常有趣的選項,因此我將在防火牆中包含這一選項,並在對防火牆進行測試時看看它的工作原理。

# 接受過濾器中的靜態連線
# options ACCEPT_FILTER_DATA
# options ACCEPT_FILTER_HTTP

在這臺計算機上執行的不是網際網路伺服器,因此無需在編譯時包括這二個選項。

# 下面的選項和系統級變數控制系統如何處理適當的TCP資料包。
#
# TCP_DROP_SYNFIN可以支援包含有SYN+FIN的TCP資料包,它使nmap不能識別TCP/IP棧,# 但可以破壞對RFC1644擴充套件的支援,建議不要在網際網路伺服器中使用。
#
# TCP_RESTRICT_RST支援阻止TCP RST棧的洩出,對於需要大量SYN的系統或者不希望被簡單地掃描到埠的系統非常有用。

我將在防火牆中包括這些選項,在測試防火牆時,仔細看看它們有什麼作用。

# ICMP_BANDLIM根據頻寬限制產生icmp錯誤。一般情況下我們需要這個選項,它有助於
# 你的系統免受D.O.S.攻擊。
#
options ICMP_BANDLIM

FreeBSD核心預設支援這一選項。

# DUMMYNET啟動“dummynet”頻寬限制軟體。還需要有IPFIREWALL選項的支援
# BRIDGE啟動乙太網卡之間的橋接功能

在本例中我不會選這二個選項,因為在獨立的計算機系統上無需對流量進行控制。

在重新編譯FreeBSD核心之前,我將在核心配置檔案中新增下面的內容:

#以預設的、拒絕所有資料包方式啟動IPFW
options IPFIREWALL
options IPFIREWALL_VERBOSE
options IPFIREWALL_VERBOSE_LIMIT=10

#隱藏防火牆
options IPSTEALTH

#使不被nmap發現,如果是網際網路伺服器則去掉該選項。
options TCP_DROP_SYNFIN

#防止埠掃描
options TCP_RESTRICT_RST

在重新編譯核心時,我將再次仔細審查在/etc/rc.conf中新增的選項。下面是手冊中有關各個選項的說明:

man rc.conf
/firewall

firewall_enable
(布林型)如果不想在系統啟動時載入防火牆規則集,將其值設定為NO;否則,將其設定為YES。如果它被設定為YES,而核心在編譯時沒有使用IPFIREWALL選項,ipfw核心模組將自動被載入。
firewall_script
(字串型)如果要執行一段防火牆指令碼程式,而不是/etc/rc.firewall,將這一變數設定為指令碼程式的路徑全名。
firewall_type
(字串型)從/etc/rc.firewall或包含規則集的檔案中的防火牆型別中指定防火牆型別。/etc/rc.firewall中可選的防火牆型別為:open-不限制IP訪問;closed-禁止除透過lo0進行的之外的所有IP服務;client-對工作站的基本保護;simple-對LAN的基本保護。如果給的是一個指定的檔名,則必須使用全路徑名。

由於我希望系統啟動時載入防火牆規則,因此將把firewall_enable變數的值設定為YES。由於要使用自己的規則集,需要指定使用firewall_type建立的檔案的全路徑名。

firewall_quiet
(布林型)如果設定為YES,則系統在啟動時,不會在控制檯上顯示ipfw規則。

由於會顯示載入的各條規則,將這個變數設定為YES是一個好主意。如果相關規則中出現了錯誤,則在這個錯誤之後的所有規則都不會被載入。如果在啟動時看著螢幕,就會在成功載入的最後一條規則之後看到一個ipfw語法訊息。這樣就可以在規則集中發現出現的錯誤,然後重新啟動機器,使所有的規則都能夠被成功地載入。

firewall_logging
(布林型)設定為YES會啟動ipfw事件日誌功能,與IPFIREWALL_VERBOSE核心選項的功能相同。
tcp_extensions
(布林型)預設狀態下被設定為NO。設定為YES可以啟動由RFC 1323定義的一些TCP選項。如果連線有隨機的問題出現,將其重新設定為NO,看是否能夠解決問題,因為一些軟、硬體問題都與這個選項有關。
log_in_vain
(布林型)預設狀態下設定為NO。設定為YES將把對埠的連線嘗試記入日誌中。
tcp_keepalive
(布林型)預設狀態下設定為YES。設定為NO會禁止對空閒的TCP連線的探查。
tcp_drop_synfin
(布林型)預設狀態下設定為NO。設定為YES會使核心忽略有SYN和FIN標誌的TCP幀。 雖然這樣會提供作業系統的指紋,但會使一些正常的應用軟體出毛病。只有在編譯核心時使用了TCP_DROP_SYNFIN選項,該選項才有效。

由於在核心中新增了TCP_DROP_SYNFIN選項,我將這一變數的值設定為YES。如果在計算機上執行網際網路伺服器軟體,應該去掉這一選項。

tcp_restrict_rst
(布林型)預設狀態下設定為NO。設定為YES將使核心在響應無效的TCP資料包時不能輸出TCP RST幀。只有在編譯核心時使用了TCP_RESTRICT_RST選項,這一選項才有效。
icmp_drop_redirect
(布林型)預設狀態下設定為NO。設定為YES將使核心忽略ICMP REDIRECT資訊包。
icmp_log_redirect
(布林型)預設狀態下設定為NO。設定為YES將使核心在日誌中記錄ICMP REDIRECT資訊包。由於日誌是沒有什麼限制的,因此只有在對網路維護時才會使用這一選項。

最終,我在系統中的/etc/rc.conf檔案中加入了下面的內容:

#用於支援ipfw的選項
firewall_enable="YES"
firewall_script="/etc/rc.firewall"
firewall_type="/etc/ipfw.rules"
firewall_quiet="NO" #對現有的規則滿意後將其值改為YES
firewall_logging_enable="YES"
#附加的防火牆選項
log_in_vain="YES"
tcp_drop_synfin="YES" #如果要建立網際網路伺服器,將其值改為NO。
tcp_restrict_rst="YES"
icmp_drop_redirect="YES"

在重新啟動機器執行新的核心之前,有一點需要注意。如果LINT檔案顯示出“YOU WILL LOCK YOURSELF OUT”(你將封鎖自己),說明新的規則已經起作用了。在重新建立允許所需的IP資料包進入系統之前,所有的IP資料包都不能進入或傳出計算機。如果想從網際網路上收發電子郵件和下載資料,就需要在重新啟動系統之前完成這些工作。

建立一個好的規則集是一件技術性很強固的工作。如果是第一次建立防火牆,需要有大量的時間進行練習,就會發現ipfw所使用的邏輯與你認為的邏輯不完全相同。

此外,防火牆並非是安裝後就一勞永逸了,需要花些時間對它進行最佳化,在它不能完成你預期的任務時多想想這是為什麼。一旦用有防火牆的新核心啟動機器後,你可能希望完成下面的三項工作。

▲系統地在規則集中新增新的規則,測試每條規則的作用,確保只有你需要的資料包才能夠出入你的系統。

▲決定你要將哪些IP資料包記入日誌並檢視日誌檔案,隨著不斷發現你禁止或允許的一些資料包出入系統,會不斷地修改規則。

▲一旦對防火牆允許或不允許透過哪些資料包滿意了,就需要測試防火牆的效能是否能夠令人滿意。

好了,下面我要重新啟動機器載入新的核心。在啟動時盯著螢幕,在NIC載入後會看到下面的資訊:

Flushed all rules.
00100 allow ip from any to any via lo0
00200 deny ip from any to any to 127.0.0.0/8
Firewall rules loaded, starting divert daemons:.
Additional routing options: tcp extensions=NO ignore ICMP redirect=YES TCP keepalive=YES restrict TCP reset=YES drop SYN+FIN packets=YES.

Additional TCP options: log_in_vain=YES.

你可能會有疑惑,沒有建立包含規則集的檔案,怎麼會顯示頭三行資訊呢?在編輯/etc/rc.conf時,我在其中新增了下面一行的內容:

firewall_script="/etc/rc.firewall"

在啟動時系統會讀取/etc/rc.firewall檔案,該檔案包含下面的內容:

############
# Flush out the list before we begin.
#
${fwcmd} -f flush

############
# 只有在極少數的情況下才需要改變這些規則
#
${fwcmd} add 100 pass all from any to any via lo0
${fwcmd} add 200 deny all from any to 127.0.0.0/8

由於規則100和規則200在啟動時會用到,因此在建立規則時應該從規則300開始。在建立規則之前,我會用下面的方法分二次檢查ipfw是否預設地禁止所有的資訊包出入我的計算機系統。可以透過執行ipfw show命令來進行檢查:

ipfw show
ipfw: socket: Operation not permitted

似乎只有超級使用者才有檢視防火牆規則的許可權,因此我將再次以超級使用者再身份次進行檢查:

su
Password:
ipfw show
00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
65535 115 14092 deny ip from any to any

確實已經禁止所有的資訊包出入計算機系統,我再來試著使用一下網路連線。

ping
ping: cannot resolve Host name lookup failure

traceroute
traceroute: unknown host

lynx
Alert!: Unable to access document.

好了,名字解析也無效,我們再試試ping吧:

ping 24.141.116.1
PING 24.141.116.1 (24.141.116.1): 56 data bytes
ping: sendto: Permission denied
ping: sendto: Permission denied
^C
--- 24.141.116.1 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss

由於我是以超級使用者的身份執行最後一個ping的,因此ipfw確實已經禁止所有資訊包的出入,我已經完全不能使用網路連線了。現在我們來建立一個允許收發我需要的IP資訊包的規則集。

有二種方式可以建立被ipfw讀取的規則:

如果已經在使用ipfw,不用重啟動機器就能使規則生效。但是,如果重新啟動機器後,新增的規則就會丟了。

你還可以在讓ipfw讀取的檔案中新增一條規則,這樣只有機器重新啟動後,新新增的規則才能生效。

由於這臺機器只有我一個人使用,因此,我將把規則直接加進檔案中並重啟機器。我已經在/etc/rc.conf中新增了下面的一行內容:

firewall_type="/etc/ipfw.rules"

因此,我將建立一個名字為/etc/ipfw.rules的檔案。



我們已經透過安裝帶預設的禁止所有資料包出入的策略的ipfw,使所有的IP資訊包都不能出入我的計算機系統,下面,我們再建立一個能被ipfw讀取的規則集,使所需要的資訊包能夠出入計算機系統。

由於在建立規則集方面沒有所謂“最合適”的方法,因此我不能說明如何在規則集中新增“萬能”的規則,而只能說明一下在建立規則集時需要遵循的原則。在這裡,我假設你已經掌握了ipfw的語法,能夠理解我建立的規則。如果對這些知識不大理解,請參閱相關的資料。

在建立規則集時需要注意的是,規則是按給定數字行號的順序被系統讀取的,直到資訊包符合一條規則,ipfw才會停止讀取規則,也就是說,如果規則400和規則800都適用於一個資訊包,系統總是會用到規則400而不會讀取規則800。因此,在新增新的規則之前,需要仔細地審查原來的規則,確保新的規則不會被原來的規則所覆蓋。

此外,規則對所有的連線-也就是在ifconfig -a的輸出中所列出的所有連線都是適用的。如果在象我這樣只有一個連線的計算機上自然不會有什麼問題,但如果在有多個連線的計算機上,就會有所不同。例如,你的機器上可能有二個連線,一個是網際網路連線,一個是內部區域網連線,每個不同的連線需要不同的安全規則,這一點可以透過在ipfw的規則中指定連線的名字來實現。

我的機器是一臺執行FreeBSD 4.2、配置有網際網路連線的單臺計算機。由於這是我在家裡使用的計算機,因此可以對向網際網路上傳送的資訊包的型別不作任何限制,而只需要它能夠接收是對我發出的資訊包有效響應的資訊包。

要完成這一任務最好的方法之一是利用ipfw的動態功能。如果你對這一概念還不太熟悉,下面我將對它作一番詳盡的解釋。

如果使用“動態”的規則,當我向網際網路上傳送一個資訊包時,ipfw將在其狀態表中新增一個記錄,其中包括有傳送的資訊包的目標計算機的IP地址和使用的目標計算機的埠。當有資訊包從網際網路上返回時,如果其IP地址、埠號與在狀態表中記錄得不一致,計算機就不會接收這一資訊包。動態規則只適用於TCP資訊包,而不適用於UDP資訊包,原因是UDP不建立一個虛擬的連線,它被稱作是“無狀態”協議,也就不能使用“狀態表”。

ipfw手冊中的例子部分給出了三條用來建立這個動態資訊包過濾裝置的規則。由於我決定在/etc/ipfw.rules中建立自己的規則,因此,需要以超級使用者的身份建立包含下面內容的檔案:

# 只允許向外傳送資訊包
add 00300 check-state
add 00301 deny tcp from any to any in established
add 00302 allow tcp from any to any out setup keep-state

由於規則100和規則200是預先包含在/etc/rc.firewall中的,因此,我自己新增的規則將從行號300開始。我將給相關的規則以300、301、302等行號,等規則越來越多或建立不相關的規則時,我就會把行號跳到400。不過,從理論上說你可以任意給規則指定行號,只要該行號沒有在該規則集檔案中出現過就行。

你可能已經注意到規則集出現了幾個在ipfw手冊中定義的關健字:

check-state:檢查資訊包是否與動態規則集匹配。如果匹配則搜尋中止,否則繼續搜尋下一條規則。
keep-state:根據匹配情況,防火牆將建立一條動態規則,其功能是在源、目的IP地址/埠之間使用同一協議的流量,這一規則是具有一定的生命週期的(由一系列sysctl(變數控制),每當發現匹配協議時其生命週期都會重新整理。
established:只適用於TCP資訊包,與有RST或ACK位的資訊包進行匹配。
setup:只適用於TCP資訊包,與有SYN位但不具有ACK位的資訊包進行匹配。

換句話說,當有資訊包到達網路連線後,ipfw將首先檢查它是否在狀態表中,如果在狀態表中,則允許它進入系統(行號為300的規則執行這一檢查工作)。如果它不在狀態表中,而且設定了RST或ACK位,ipfw將不允許它進入系統,因為它不是我建立的連線的有效響應(規則301完成這一工作)。只有當ACK標誌沒有設定,(這意味著它要初始化連線)並且這一資訊包是由系統向外傳送的時,才允許它向外傳送;如果有資訊包符合這一規則,則它被加入狀態表中。

我們來看看新增上這些規則後對系統有什麼影響。對規則集進行檢查沒有錯誤後,儲存檔案,然後輸入下面的命令:

killall init

敲Enter鍵,然後輸入:

exit

然後仔細觀察啟動資訊,確保規則在載入時沒有出現出錯資訊。如果在規則載入過程中出現了錯誤資訊,那麼可能是系統的安全級別被設定為3或更高了,必須首先在/etc/rc.conf檔案中找到下面的這行內容,將kern_securelevel改為較小的值:

kern_securelevel="3"

然後重新執行killall init命令。

重新登入後,我將嘗試能否向外傳送IP資料包並收到相應的應答資料包:

ping
ping: cannot resolve Host name lookup failure

lynx
Alert!. Unable to access document.

也許是我的系統上沒有安裝DNA域名解析功能的緣故,我再使用IP地址試一下:

lynx 216.136.204.21

這次我發現自己在的主頁上了。我們再來試著ping一下這個IP地址吧:

ping 216.136.204.21
PING 216.136.204.21 (216.136.204.21): 56 data bytes
ping: sendto: Permission denied
ping: sendto: Permission denied
ping: sendto: Permission denied
^C
--- 216.136.204.21 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

這裡我來解釋一下這一奇怪的現象吧。很明顯的是,一些資料包進入或發出了計算機系統,但一些則沒有。我們來仔細發分析一下我在上面的每個例子中使用的協議。

由於我只能使用其IP地址訪問的網站,因此域名解析沒有成功。當我使用DNS服務時,我會向我的ISP的DNS伺服器傳送域名查詢請求,該DNS伺服器應該向我的機器傳送響應資料包。這些操作都是符合我們的規則的,由於我是在53埠傳送請求的,也應該在埠53上接收到響應資料包。我將複查我傳送請求的DNS伺服器:

more /etc/resolv.conf
search kico1.on.home.com
nameserver 24.226.1.90
nameserver 24.226.1.20
nameserver 24.2.9.34

似乎問題不是出在這兒,因此應該仔細地搞清楚域名解析的工作原理。我們來看一下能否從線上手冊上得到一點幫助:

apropos resolve
dnsquery(1) - 使用解析器查詢域名伺服器
res_query(3), res_search(3), res_mkquery(3), res_send(3), res_init(3), dn_comp(3), dn_expand(3) - 解析器例程
resolver(5) - 解析器配置檔案
man resolver

透過多次檢視,下面的內容引起了我的興趣:RES_USEVC 在查詢中使用TCP而不是UDP連線;RES_STAYOPEN RES_USEVC用它來在多次查詢期間保持TCP連線。它只在需要進行多個查詢的的軟體中有用,UDP是最常用的模式。

我可能已經發現問題出在哪了。如果DNS使用的是UDP而不是TCP,而我的規則只允許TCP協議的資料包響應我的TCP連線,域名解析就會失敗。

man dnsquery

-s 使用流格式而非資訊包。它使用一個帶名字伺服器的TCP流式連線而不是UDP,這一選項會設定解析軟體選項欄位的RES_USEVC位。(預設狀態下使用UDP)

現在,我們來試試這個選項:

dnsquery -s
;; ->>HEADER<;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 5, ADDITIONAL: 5
;; , type = ANY, class = IN
49m21s IN CNAME freefall.freebsd.org.
freebsd.org. 22m43s IN NS ns1.iafrica.com.
freebsd.org. 22m43s IN NS ns2.iafrica.com.
freebsd.org. 22m43s IN NS ns.gnome.co.uk.
freebsd.org. 22m43s IN NS ns0.freebsd.org.
freebsd.org. 22m43s IN NS ns1.root.com.
ns1.iafrica.com. 1h1m3s IN A 196.7.0.139
ns2.iafrica.com. 1h1m3s IN A 196.7.142.133
ns.gnome.co.uk. 12m37s IN A 193.243.228.142
ns0.freebsd.org. 11h9m9s IN A 216.136.204.126
ns1.root.com. 1h8m12s IN A 209.102.106.178

在我們使用TCP連線發出一個DNS請求時,名字解析過程執行得很好。我們再在沒有帶s選項的情況下看使用UDP時的情況如何:

dnsquery
Query failed (h_errno=2) : Host name lookup failure

現在我們明白了,DNS使用的是UDP資料包。由於我沒有在規則集中允許使用UDP資料包,因此DNS名字解析過程不能完成。

現在既然已經解決了這個問題,我們再來看看即使在使用IP地址時也ping不通的原因何在。我們知道,ping在其資料包中使用的是ICMP而非TCP協議。如果用ping傳送ICMP資料包,不在防火牆的規則面前碰一鼻子灰才怪呢。

在向規則集中新增任何新的規則前,必須以超級使用者身份重新登入。我們來看看ipfw的輸出:

su
Password:
ipfw show
00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
00300 0 0 check-state
00301 0 0 deny tcp from any to any in established
00302 21 15144 allow tcp from any to any out keep-state setup
65535 142 10531 deny ip from any to any
## 動態規則:
00302 19 15040 (T 0, # 147) ty 0 tcp, 24.141.119.162 2932 216.136.204.21 80

注意一下動態規則部分,這是一個狀態表。當執行lynx 216.136.204.21命令與站點上的http埠(埠 80)進行連線時,Rule 00302允許發出setup資料包,並在狀態表中新增一個條目。所有從216.136.204.21上的埠80發出或以它為目標地址的資料包都可以進入或者發出我的計算機。

你也許還注意到了標號為00302和65535的規則後面都跟有數字,其中第一個數字為資料包的數量,第二個數字為符合每條規則的位元組數。被規則65535拒之門外的資料包都是失敗的UDP和ICMP資料包。

向規則集中新增新規則時,需要使用ipfw中的zero命令將這些計數器清零,這樣,當對新新增的規則進行測試時,就能知道哪些規則後面又出現了新的統計數字。

下面,我將新增一些允許進行DNS名字解析的規則。由於DNS使用UDP,UDP不進行連線,我不能指定只允許對我的連線的有效的響應資料包進入系統。但是,我可以限制DNS使用的埠(埠 53)進出的資料包,選擇只接受來自我的ISP的DNS伺服器的IP地址發出的資料包。執行more /etc.resolv.conf命令就能發現這些IP地址。我將在/etc/ipfw.rules檔案中新增下面的內容:

#允許 DNS
add 00400 allow udp from 24.226.1.90 53 to any in recv ed0
add 00401 allow udp from 24.226.1.20 53 to any in recv ed0
add 00402 allow udp from 24.2.9.34 53 to any in recv ed0

然後,透過執行killall init命令重新載入規則集,看名字解析是否已經可以成功地執行了:

lynx
Alert!. Unable to access document.

怎麼回事?我已經在規則集中新增了允許使用UDP資料包的規則,怎麼名字解析服務仍然不行呢?我們執行ipfw show命令來看看哪條規則的後面跟有資料包計數字:

su
Password:
ipfw show
00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
00300 0 0 check-state
00301 0 0 deny tcp from any to any in established
00302 0 0 allow tcp from any to any keep-state setup
00400 0 0 allow udp from 24.226.1.90 53 to any in recv ed0
00401 0 0 allow udp from 24.226.1.20 53 to any in recv ed0
00402 0 0 allow udp from 24.2.9.34 53 to any in recv ed0
65535 30 2196 deny ip from any to any
## Dynamic rules:

後面跟有統計數字的唯一的規則是最後一條拒絕服務的規則,說明新增的允許UDP資料包的規則沒有作用。現在我才明白,我還沒有允許向外傳送UDP資料包,沒有UDP資料包返回來也就沒有什麼好奇怪的了。下面我們再往規則集中新增一行內容:

00403 allow udp from any to any out

這樣,我的計算機就可以向外傳送UDP資料包了。然後用ipfw zero清除規則後面的統計數字,執行killall init命令重新再試一次:

lynx

FreeBSD的主頁終於出現了。如果我以超級使用者的身份執行ipfw show命令,就會得到更令人滿意的輸出:

ipfw show
00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
00300 0 0 check-state
00301 0 0 deny tcp from any to any in established
00302 20 15061 allow tcp from any to any keep-state setup
00400 10 1882 allow udp from 24.226.1.90 53 to any in recv ed0
00401 0 0 allow udp from 24.226.1.20 53 to any in recv ed0
00402 0 0 allow udp from 24.2.9.34 53 to any in recv ed0
00403 10 591 allow udp from any to any out
65535 31 2577 deny ip from any to any
## Dynamic rules:
00302 19 15017 (T 0, # 236) ty 0 tcp, 24.141.119.162 4363 216.136.204.21 80

規則00403允許我的計算機發出DNS請求,規則00400允許接受DNS應答,規則00302建立HTTP連線,而且,我在狀態表中有了一個與216.136.204.21之間HTTP連線的條目。

我們已經建立了一個可以執行的網路連線,但這個規則集仍然有很大的改進餘地,下面我們將就這方面的問題進行更詳細的討論。



上面我們已經建立了一個規則集,使ipfw能夠允許對發出的網際網路請求進行響應,並能進行DNS名字解析。下面我們將仔細調節已經建立的規則集的效能,並透過內建的登入工具對它進行測試。

建立的規則集已經可以起作用了,因此可以在DHCP規定的時間用完之前使用網際網路連線了。時間用完後,網際網路連線就不能再使用了。要搞清楚是哪條規則實現網際網路連線的,對DHCP的基本工作原理有一定的瞭解是很有必要的。

DHCP使用UDP資料包,意味著動態規則和狀態表在這裡是不起作用的。因此必須允許在我的計算機和ISP的DHCP伺服器之間傳輸UDP資料包。DHCP需要二個埠:DHCP客戶端使用埠68,ISP的DHCP伺服器使用埠67。

為了搞明白DHCP的工作原理,我們來看一下我的計算機的DHCP“租用”檔案:

more /var/db/dhclient.leases

lease {
interface "ed0";
fixed-address 24.141.119.162;
option subnet-mask 255.255.252.0;
option time-offset -18000;
option routers 24.141.116.1;
option domain-name-servers 24.226.1.90,24.226.1.20,24.2.9.34;
option host-name "my_hostname";
option domain-name "my_domainname";
option broadcast-address 255.255.255.255;
option dhcp-lease-time 604800;
option dhcp-message-type 5;
option dhcp-server-identifier 24.226.1.41;
renew 2 2001/5/15 13:12:11;
rebind 5 2001/5/18 04:12:11;
expire 6 2001/5/19 01:12:11;
}

DHCP伺服器提供了一個IP地址、子網掩碼、預設的閘道器地址、三個DNS伺服器的IP地址、我的主機名和提供服務的DHCP伺服器的IP地址。由於DHCP“租用”契約是一種真正的“契約”,這意味著我必須儲存好這些資訊,最後三行內容與我的DHCP客戶端如何重新修改“租用”契約有關。

以renew開頭的這一行向我的DHCP客戶端表明它何時應該結束,並更新其“租用”契約,這一時間要早於expire行中列出的時間。在2001年5月15日13時12分11秒,我的計算機將會向IP地址為24.226.1.41的DHCP伺服器上的埠67傳送UDP資料包,因此需要新增一條規則允許向外傳送UDP資料包。如果DHCP伺服器收到了我的計算機傳送的UDP資料包,它應該對要求更新“租用”契約的要求作出響應,並且以UDP資料包形式將此資訊發回到我的計算機上的68埠。因此,我另外還需要在規則集中新增一規則,允許ipfw對此資訊作出反應。

如果不在規則集中新增這些規則,或者由於其他原因DCHP伺服器沒有對我的計算機發出的更新“租用”契約的要求作出響應,rebind行將在2001年5月18日4時12分11秒啟動,這時,我的DCHP客戶端就會開始擔心“租用”期滿,將會向DHCP伺服器發出更多的UDP資料包,只是這次將不再向特定的DHCP伺服器傳送資料包,而是會向255.255.255.255傳送資料包,任何伺服器都可以響應發出的請求。

如果沒有DHCP伺服器進行響應,我的計算機的契約會在2001年5月19日1時12分11秒結束,這意味著我的DCHP客戶端不能保證還可以繼續使用這些租用資訊。這時,會有幾種情況出現。客戶端將繼續試圖與DHCP伺服器聯絡,向埠67傳送UDP資料包。它將繼續試圖用ping與預設的閘道器聯絡,檢查其IP地址是否仍然有效。在最壞的情況下,我的客戶端的IP地址已經無效,DHCP伺服器的應答將作為廣播被IP地址為255.255.255.255的機器的68埠接收。

既然已經明白了其工作原理,我們就清楚應該在規則集中新增什麼樣的規則了。在新增規則前,應該對規則進行仔細的檢查,因為規則的順序已經越來越重要了。規則集中的規則越多,前面的規則覆蓋後面新新增的規則的可能性也就越大。設計一個好的規則集的訣竅是讓你希望的資料包使用盡可能少的規則,如果新增的規則過多,儘管防火牆仍然會起作用,但這樣會加重ipfw不必要的負擔,因為在找出一個資料包適應的規則前它需要讀取更多的規則。此外,在你希望搞清楚到底是哪一條規則使系統不能按你的意願執行時,規則太多了會相當的麻煩。

我將以超級使用者的身份執行ipfw show命令檢查當前的規則:

su
Password:
ipfw show

00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
00300 0 0 check-state
00301 0 0 deny tcp from any to any in established
00302 0 0 allow tcp from any to any keep-state setup
00400 0 0 allow udp from 24.226.1.90 53 to any in recv ed0
00401 0 0 allow udp from 24.226.1.20 53 to any in recv ed0
00402 0 0 allow udp from 24.2.9.34 53 to any in recv ed0
00403 0 0 allow udp from any to any out
65535 0 0 deny ip from any to any

由於我需要向外傳送UDP資料包,因此需要指明DHCP埠號和DHCP伺服器的IP地址,作為超級使用者,我將考慮在/etc/ipfw.rules檔案中新增下面的內容:

#allow DHCP
add 00500 allow udp from any 68 to 24.226.1.41 67 out via ed0
add 00501 allow udp from 24.226.1.41 67 to any 68 in via ed0

這些規則可以說是使DHCP客戶端更新其“租用”契約所需要的最少的規則了,是否需要新增更多的規則會因DHCP伺服器可靠性的不同而有所不同。如果DHCP伺服器總是能夠響應我的更新請求,我也就無需採用傳送UDP廣播、ping預設的閘道器或者接收UDP廣播這些方式了。如果DHCP伺服器的可靠性不高,那就還需要新增下面的規則:

add 00502 allow udp from any 68 to 255.255.255.255 67 out via ed0
add 00503 allow udp from any 67 to 255.255.255.255 68 in via ed0

由於我使用的DHCP伺服器是相當可靠的,因此不需要立即新增00502和00503這二條規則。我只是反覆提醒自己,在ISP的DHCP伺服器出了問題或其IP地址有變化的時候就需要考慮這二條規則了。

在儲存修改之前,我將把00500和00501二條規則與其餘的規則進行比較,以確保它們之間沒有任何衝突和重複之處,結果00403和00500之間確實存在著部分重複:

add 00403 allow udp from any to any out
add 00500 allow udp from any 68 to 24.226.1.41 67 out via ed0

因為規則00403允許我的計算機發出任何的UDP資料包,ipfw就不會讀取其他的只從埠68傳送UDP資料包的規則,在這裡,就需要在使用數量最少的規則還是使用把各種可能都考慮在內的數量最多的規則之間進行選擇了。

規則00403是在建立允許DNS解析時新增的,如果刪除了它,就需要新增三條規則才能實現向三個DNS伺服器傳送UDP資料包的功能。另外,如果還需要向其他的伺服器傳送UDP資料包,我就必須再新增規則。因此,如果不使用這樣一條“通用”的規則,ipfw規則集中就會包含一些多餘的規則,使系統負擔不必要的負荷。

這樣作也不符合使用最少數量規則的原則,但我們必須仔細審查一下“通用”規則帶來的潛在後果。如果我限制系統接受UDP資料包,向外傳送UDP資料包是不會有什麼危險的。例如,規則00403允許我的計算機向外傳送任何資料包,但規則00400、00401、0402和00501保證我的計算機只能接受我的ISP的3臺DNS和一臺DHCP伺服器傳送的UDP資料包。因此,對於我的單獨執行的FreeBSD計算機而言,這個規則集還是比較合理的。

如果我在FreeBSD防火牆後面新增新的客戶端機器,就需要重新考慮這個規則集。例如,微軟的客戶端會傳送數量不等的UDP資料包通報其共享資源,讓這些資料包透過防火牆傳送出去,對我而言是不負責任並具有一定安全風險的。在本例中,我將使用只能發出我需要發出的UDP資料包的規則,而刪除可以發出任意UDP資料包的規則。

由於我現在保護的只是一臺單獨的FreeBSD計算機,因此我將保留規則00403,刪除規則00500,因為系統永遠都不會讀取到它。我將對規則進行如下的改變:

#允許DHCP 操作
add 00501 allow udp from 24.226.1.41 67 to any 68 in via ed0

儲存所作的改變,並透過使用killall init命令進行測試,以超級使用者身份重新登入,看看所作改變的效果。

su
Password:
ipfw show

00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
00300 0 0 check-state
00301 0 0 deny tcp from any to any in established
00302 0 0 allow tcp from any to any keep-state setup
00400 8 1322 allow udp from 24.226.1.90 53 to any in recv ed0
00401 0 0 allow udp from 24.226.1.20 53 to any in recv ed0
00402 0 0 allow udp from 24.2.9.34 53 to any in recv ed0
00403 8 469 allow udp from any to any out
00501 4 1592 allow udp from 24.226.1.41 67 to any 68 in recv ed0
65535 29 8591 deny ip from any to any

我好象從IP地址為24.226.1.90的DNS伺服器收到了8個UDP資料包,從IP地址為24.226.1.41的DHCP伺服器收到了4個UDP資料包。現在,我們再來看看DHCP的“租用”時間問題。

more /var/db/dhclient.leases


renew 3 2001/5/16 07:46:25;
rebind 5 2001/5/18 08:50:46;
expire 6 2001/5/19 01:12:14;

在執行killall init命令時,我成功地找到了DHCP伺服器並更新了其“租用”時間,因此,我的DHCP規則是比較成功的。

由於我的規則集還拒絕收發所有的ICMP資料包,下面我們再來研究一下允許ICMP資料包收發的問題。阻止收發ICMP資料包並不是一件好事,因為它會破壞Path-MTU發現並阻止Source Quench資訊。另外,ICMP使用types和codes來指定真正的ICMP資訊。

在建立與ICMP有關的規則時,只能指定ICMP資料包的type而不能指定它的code。我將以超級使用者的身份登入,在/etc/ipfw.rules檔案中新增下面的內容:

#允許接受一些ICMP types (不支援codes)
###########允許雙向的path-mtu
add 00600 allow icmp from any to any icmptypes 3
add 00601 allow icmp from any to any icmptypes 4

我需要考慮是否需要ping我的網路之外的主機或執行traceroute命令,由於二者都需要,並希望收到相應的應答,但我並不希望網際網路上的所有使用者都可以對我執行ping 或traceroute命令,因此,我需要新增下面的規則:

###########允許我對外部的主機執行ping命令,並得到相應的應答
add 00602 allow icmp from any to any icmptypes 8 out
add 00603 allow icmp from any to any icmptypes 0 in

###########允許我執行traceroute命令
add 00604 allow icmp from any to any icmptypes 11 in

ICMP type 8是一個重複的請求,ICMP type 0是反覆的應答。由於我只允許反覆地發出請求並接受應答,從而我可以ping別人而別人不能ping我。

在執行traceroute命令時,就會向外發出UDP資料包,這一點在規則00403中已經得到了保證。如果希望能夠獲得所有應答資訊,我還必須允許系統接受所有的CMP type 11資料包。

好了,我們現在儲存所作的修改,並使用ipfw zero命令對ipfw計數器重新復位。然後執行killall init命令,並試執行ping和traceroute命令:

ping
PING freefall.freebsd.org (216.136.204.21): 56 data bytes
64 bytes from 216.136.204.21: icmp_seq=0 ttl=239 time=85.250 ms
64 bytes from 216.136.204.21: icmp_seq=1 ttl=239 time=88.338 ms
64 bytes from 216.136.204.21: icmp_seq=2 ttl=239 time=83.757 ms
^C
--- freefall.freebsd.org ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 83.757/85.782/88.338/1.908 ms

traceroute
traceroute to freefall.freebsd.org (216.136.204.21), 30 hops max, 40 byte packets
1 10.69.4.1 (10.69.4.1) 8.678 ms 8.739 ms 10.055 ms
2 d226-12-1.home.cgocable.net (24.226.12.1) 9.800 ms 10.642 ms 7.876 ms
3 cgowave-0-158.cgocable.net (24.226.0.15 25.910 ms 15.288 ms 13.693 ms
4 cgowave-busy-core.cgocable.net (24.226.1.1) 26.982 ms 16.521 ms 12.376 ms
5 cgowave-0-202.cgocable.net (24.226.0.202) 14.372 ms 14.224 ms 13.728 ms
6 216.197.153.65 (216.197.153.65) 14.112 ms 13.544 ms 42.612 ms
7 c1-pos8-0.bflony1.home.net (24.7.74.29) 15.093 ms 22.387 ms 18.530 ms
8 c1-pos1-0.hrfrct1.home.net (24.7.65.253) 25.953 ms 26.703 ms 26.514 ms
9 c1-pos3-0.nycmny1.home.net (24.7.69.2) 26.279 ms 29.810 ms 38.940 ms
10 * ibr02-p1-0.jrcy01.exodus.net (24.7.70.122) 32.121 ms 38.211 ms
11 bbr02-g5-0.jrcy01.exodus.net (216.32.223.130) 34.239 ms 34.815 ms 37.106 ms
12 bbr01-p2-0.okbr01.exodus.net (216.32.132.109) 37.643 ms 36.883 ms 36.201 ms
13 216.34.183.66 (216.34.183.66) 37.624 ms 39.455 ms 40.243 ms
14 bbr01-p0-0.snva03.exodus.net (206.79.9.85) 81.494 ms 82.421 ms 83.230 ms
15 64.15.192.34 (64.15.192.34) 79.431 ms 80.981 ms 115.289 ms
16 bbr02-p4-0.sntc05.exodus.net (209.185.9.70) 81.993 ms 99.964 ms 82.169 ms
17 dcr01-g6-0.sntc05.exodus.net (64.56.192.19) 81.324 ms 81.603 ms 80.146 ms
18 g2-1.bas1-m.sc5.yahoo.com (64.56.207.146) 81.867 ms 100.628 ms 94.995 ms
19 freefall.freebsd.org (216.136.204.21) 104.100 ms 95.821 ms 85.909 ms

一切正常。現在我將以超級使用者的身份來看看系統使用了哪些規則。

su
Password:
ipfw show

00100 0 0 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
00300 0 0 check-state
00301 0 0 deny tcp from any to any in established
00302 0 0 allow tcp from any to any keep-state setup
00400 29 5847 allow udp from 24.226.1.90 53 to any in recv ed0
00401 2 163 allow udp from 24.226.1.20 53 to any in recv ed0
00402 3 397 allow udp from 24.2.9.34 53 to any in recv ed0
00403 93 4712 allow udp from any to any out
00501 0 0 allow udp from 24.226.1.41 67 to any 68 in recv ed0
00600 3 168 allow icmp from any to any icmptype 3
00601 0 0 allow icmp from any to any icmptype 4
00602 3 252 allow icmp from any to any out icmptype 8
00603 3 252 allow icmp from any to any in icmptype 0
00604 53 2968 allow icmp from any to any in icmptype 11
65535 29 8591 deny ip from any to any

我們可以發現,執行ping命令時使用了三個反覆的請求資料包(規則00602)和三個反覆的應答資料包(規則00603)。此外,有53個ICMP type 11資料包響應traceroute命令(規則00604),然而,由於我不能夠在規則中指定具體的code,因此我不能說出為什麼會接收到三個“找不到目標”的資訊。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617542/viewspace-960756/,如需轉載,請註明出處,否則將追究法律責任。

相關文章