歡迎您光臨本站 註冊首頁

用IPFW實現BSD防火牆

←手機掃碼閱讀     火星人 @ 2014-03-24 , reply:0

用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 www.freebsd.org
ping: cannot resolve www.freebsd.org: Host name lookup failure

traceroute www.freebsd.org
traceroute: unknown host www.freebsd.org

lynx www.freebsd.org
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 www.freebsd.org
ping: cannot resolve www.freebsd.org: Host name lookup failure

lynx www.freebsd.org
Alert!. Unable to access document.

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

lynx 216.136.204.21

這次我發現自己在www.freebsd.org的主頁上了。我們再來試著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地址訪問www.freebsd.org的網站,因此域名解析沒有成功。當我使用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 www.freebsd.org
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39772
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 5, ADDITIONAL: 5
;; www.freebsd.org, type = ANY, class = IN
www.freebsd.org. 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 www.freebsd.org
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命令與www.freebsd.org站點上的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 www.freebsd.org
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 www.freebsd.org

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 www.freebsd.org
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 www.freebsd.org
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,因此我不能說出為什麼會接收到三個「找不到目標」的信息。

[火星人 ] 用IPFW實現BSD防火牆已經有881次圍觀

http://coctec.com/docs/linux/show-post-133739.html