第十二章 NAT 及防火牆
如果您家中有多台電腦需要同時上網,但卻只有一個 IP 可以使用,這時 NAT 就派上用場了。NAT 可以讓很多電腦經由一台 FreeBSD 伺服器上網,而且還可以讓 FreeBSD 的防火牆功能保護內部網路的電腦。本章將介紹以 FreeBSD 架設 NAT 伺服器及使用 FreeBSD 防火牆功能。讀完本章後,您將了解下列主題:
12.1 概論
這個部份我們將說明如何以 FreeBSD 做為防火牆,介紹 FreeBSD 內建的封包過濾功能。欲建立一台防火牆,就是要將一台機器放在二個網域的中間,並經由它來做封包過濾的工作。 因此我們必須先確定網路封包能通過這台防火牆,再來設定要阻檔的規則。以 FreeBSD 作為二個網域中間的連接器,可以用路由器 (router)、閘道、或是橋接器的方式來實作,再在該機器上設定防火牆的規則。當然,我們也可以只在一台單機上設定防火牆規則,以取代原本只能監控 inetd 服務的 TCP Wrapper。
我們舉二種最常被應用結合防火牆設定來保護整個網路的方法,一個是 NAT,另一個是具封包過濾的橋接器。
12.1.1 NAT
所謂的 NAT 就是 (Network Address Translation),它可以讓你在只有一個 IP 的情形下讓多台電腦一起連上網路。舉個實例而言,一個公司有三十台電腦,而 ISP 所提供的 ADSL 卻只有八個實體 IP,這種情況下,我們可以將每台電腦的 IP 設定為 private IP,再讓它們經由一台有實體 IP 的 NAT 伺服器連上網路即可。
Private IP 是 RFC 所定義的私人 IP,這些 IP 不能夠直接在網際網路中出現,所以必需經由 NAT 的轉換,將它們偽裝成是由 NAT 伺服器連向外部網路。這些可以用的私人 IP 如下:
Class | 範圍 | 子網路遮罩 |
Class A | 10.0.0.0 ~ 10.255.255.255 | 255.0.0.0 |
Class B | 172.16.0.0 ~ 172.31.255.255 | 255.255.0.0 |
Class C | 192.168.0.0 ~ 192.168.255.255 | 255.255.255.0 |
我們只需在 NAT 伺服器中做好設定,再將其他使用 private IP 的電腦設定 gateway 為該伺服器的 IP 即可。另外,我們也可以在伺服器中設定一些防火牆的規則,來保全內部網路。
12.1.2 具封包過濾的橋接器
如果我們的網路中有多台不同網域的電腦,這些電腦都有它們的 IP 及網路設定,我們可以將 FreeBSD 設定成為橋接器 (bridge),讓這台橋接器作封包過濾的工作。這種做法對於網域內其他電腦原本的網路設定不會有影響,如果沒有設定任何防火牆規則,對它們而言幾乎不會發現橋接器的存在。我們可以使用橋接器來過濾同一個網域內的網路交通,如果您有一個很大的網域,想要降低同一網域內彼此網路封包的交互影響,卻又不想將這個大網域分割成數個小網域,您可以使用具封包過濾功能的橋接器來達成網路封包分割的功能。 我們也可以使用路由器來取代橋接器,但是路由器只能遶送二個不同網域,而且設定比較複雜,因此,我會使用橋接器來做為防火牆。
FreeBSD 內建有 ipfw 這個程式可以讓我們輕易的設定一個簡單的防火牆,我們只要在 kernel 中加上一些設定就可以打開它。在這裡我們也將簡單的介紹一些防火牆的語法,讓我們可以保護我們不想、不需要被外界使用的網路服務。
在設定防火牆之前,有個觀念必須先釐清。防火牆並不能夠完全保護我們的網路安全,防火牆只是限制我們不想公開的服務、限制已知的 IP。就算架了防火牆,沒有適當的管理也是枉然。
12.2 NAT
這裡我們假設使用二張網路卡,一張是對外的網卡,代號是 fxp0;另一張是對內的網卡,代號 fxp1。以下的設定中請依您的網卡代號來加以修改。當然,你也可以只使用一張網路卡,將所有的電腦及對外網路都接在一台 HUB 上,再利用 alias 的功能將一張網卡設定二個 IP。在開始前,請先參考「網路設定」一章中的說明設定好第一張對外的網路卡喔。
在開始設定之前,請先檢查一下內部網路的配置是否正確。我們內部網路的線路應該如圖 12-1 所示。
圖 12-1
上圖中,內部網路的電腦全部都接到同一個 HUB 中,而 FreeBSD 有二個網路卡,對內的網路卡 fxp1 接在內部的 HUB 上,而對外的網路卡直接接在 ADSL Modem 或是對外的 HUB 上。上圖的網路配置只是一個建議,您也可以將 fxp0、fxp1、及 ADSL Modem 全部接在內部的 HUB 上,只是這樣 FreeBSD 就沒有真正的隔離內外部網路了。
12.2.1 設定 kernel
首先,我們必須先確定核心有支援 NAT 及防火牆功能。FreeBSD 預設的 GENERIC 核心並未加入此功能,因此,請先編輯您的核心設定檔,加入下列 設定,並重新編譯核心。如果您不知道如何修改核心設定,請參考「編譯核心」一章的說明。假設我們要修改的核心設定檔為 /usr/src/sys/i386/conf/GENERIC,先 cd /usr/src/sys/i386/conf/,再 ee GENERIC 加入下列幾行:
# 防火牆 options IPFIREWALL # 支援 NAT options IPDIVERT # 下面這一行是預設允許所有封包通過,如果沒有這一行, # 就必須在 /etc/rc.firewall 中設定封包的規則。 # 這條規則內定編號是 65535,也就是所有規則的最後一條 # 如果沒有加這一條規則,內定就是拒絕所有封包, # 只允許規則中允許的封包通過。 options IPFIREWALL_DEFAULT_TO_ACCEPT # 這一行是讓你可以在 ipfw 中設定要記錄哪些封包, # 如果沒有這一行,就算設定了要留下記錄也不會有作用。 options IPFIREWALL_VERBOSE # 這一行是限制每一條規則所要記錄的封包數量, # 因為同樣的規則可能有許多記錄,加上這一條可以使 # 同樣的記錄重覆數減少,以避免記錄檔爆增。 options IPFIREWALL_VERBOSE_LIMIT=10 # 下面這一行是用來支援封包轉向, # 當你要使用 fwd 動作時必須要有這一項設定。 options IPFIREWALL_FORWARD # 如果要使用 pipe 來限制頻寬,必須加入下列選項以支援 dummynet。 options DUMMYNET |
我們在上述設定中加入了許多項目,基本上,一定要有的項目為 IPFIREWALL 及 IPDIVERT,其它項目是為了支援限制頻寬或記錄資訊使用。編輯完核心設定後,請重新編譯並安裝新的 核心,重開機之後核心就己經支援防火牆及 NAT 了。
12.2.2 設定 rc.conf
請先參考「網路設定」一章,設定好您的第一張網路卡,並確定可以上網後,才開始下列設定。我們要修改 /etc/rc.conf 以啟動 NAT 功能。我們假設網路卡代號是 fxp0 及 fxp1,請自行變更成您的網路卡代號:
# 設定第二張網路卡的 IP。 ifconfig_fxp1="inet 192.168.0.1 netmask 255.255.255.0" # 設定啟用 gateway 的功能。 gateway_enable="YES" # 設定啟用防火牆功能,並設定防火牆類型為 OPEN。 # FreeBSD 的防火牆設定檔會自動為 NAT 加入相關的設定。 firewall_enable="YES" firewall_type="OPEN" # 設定 NAT 所使用的對外網路卡 natd_interface="fxp0" natd_enable="YES" |
設定結束之後,重開機應該就可以設定其他電腦使用這台 NAT 伺服器來連上網路了。
12.2.3 設定 rc.firewall
我們在 /etc/rc.conf 中設定 firewall_type="OPEN",如果是使用原本 /etc/rc.firewall 的話,這樣就已經就已經驅動了 NAT 的功能。
如果您想要加上更多的防火牆規則,可以編輯 /etc/rc.firewall , 並在檔案最後加上您的設定。例如,我們要設定每一個內部電腦 (192.168.0.0/16) 最多只能使用的上傳頻寬為 64 Kb/s,下傳頻寬為 256 Kb/s,則可以在 rc.firewall 中加入下列設定:
# 限制頻寬 /sbin/ipfw pipe 20 config bw 64Kbits/s /sbin/ipfw pipe 21 config bw 256Kbits/s /sbin/ipfw add pipe 20 ip from 192.168.0.0/16 to any /sbin/ipfw add pipe 21 ip from any to 192.168.0.0/16 |
修改完後,執行 sh /etc/rc.firewall 就可以更新防火牆的設定了。其他關於防火牆規則的詳細說明,請 man ipfw 或參考下一節的說明。
12.2.4 client 端的設定
在內部其它電腦方面,我們必須要再做一些設定才能讓它們經由 FreeBSD 上網。首先,你的網路架構應該如圖 12-1 所示。
而內部的其他的電腦設定方面,我們必須將其他電腦的 IP 設定為和 FreeBSD 伺服器同一個子網路。以上列設定為例,您必須將其它電腦的 IP 設為 192.168.0.X (除了 192.168.0.1 以外的其它 IP)、子網路遮罩是 255.255.255.0,gateway 設定為 FreeBSD 連到區域網路的網路卡 IP,在此範例中是 192.168.0.1。然後再設定你的 DNS 為你 ISP 的 DNS 即可。
完成上述的設定後,我們就能享受以 FreeBSD 為NAT上網了。
如果您的其它電腦還是無法上網,您可以依下列步驟除錯:
12.2.5 NAT Port Forwarding
NAT 還有一個功能叫作 Port Forwarding,它的用途在於從連到本機的封包導向到別的電腦或本機其他連接埠。例如,我們對外有一台防火牆,在 DNS 設定方面,我們設定了 ftp.mydomain.com 及 www.mydomain.com 都指向這台防火牆。但我們希望所有 HTTP 連線都重新導向到內部的 192.168.0.2 這台機器上,而所有 FTP 連線都交由 192.168.0.3 來處理。這時候我們就可以使用 port forwarding 的方式來達成。
首先,我們知道 HTTP 使用 TCP 協定 port 80,而 FTP 使用了 TCP 協定 port 20 及 port 21,接著我們就可以在 /etc 目錄下新增一個 NAT 的設定檔,名為 natd.conf,並編輯內容如下:
redirect_port tcp 192.168.0.2:80 80 redirect_port tcp 192.168.0.3:20 20 redirect_port tcp 192.168.0.3:21 21 |
第一行的目的就是將 port 80 的 tcp 連線重新導向到 192.168.0.2 的 port 80,而二、三行是將 port 20 及 port 21 的連線交由 192.168.0.3 來處理。在 192.168.0.2 及 192.168.0.3 這二台機器上,我們只要設定它們的 gateway 為防火牆的 IP,例如 192.168.0.1 即可。
接下來我們必須在 /etc/rc.conf 中加入下列這一行,讓 natd 在啟動時能讀取 /etc/natd.conf 的設定:
natd_flags="-f /etc/natd.conf" |
重新啟動後,您就可以進行測試了。假設防火牆的對外 IP 是 11.22.33.44,當我們從外部網路使用 HTTP 連線到該 IP 時,雖然該伺服器並未架設 HTTP server,但你卻可以看到網頁,表示網路封包有被重導至內部的另一台伺服器。
|
12.3 防火牆
ipfw 是 FreeBSD 內建的防火牆指令,我們可以用它來管理進出的網路交通。如果防火牆伺服器是扮演著路由器 (gateway 例如上一篇中的 NAT 伺服器) 的角色,則進出的封包會被 ipfw 處理二次,而如果防火牆扮演的是橋接器 (bridge) 的角色,則封包只會被處理一次。這個觀念關係著我們以下所要介紹的語法,有的語法並不適用於橋接器。
另外,我們在設定防火牆時有二種模式, 一種模式是預設拒絕所有連線,再一條一條加入允許的連線;另一種是預設接受所有連線,加入幾條拒絕的規則。如果是非常強調安全性,應該是使用預設拒絕所有連線,再一條一條加入我們允許的規則。
我們會將 firewall 的設定寫在 /etc/rc.firewall 中,每一條設定都是以先入為主 (first match wins) 的方式來呈現,也就是先符合的規則 (rules) 為優先。所有進出的封包都會被這些規則過濾,因此我們會盡量減少規則的數量,以加速處理的速度。
在 kernel 中,關於防火牆的設定有下列幾條:
# 防火牆 options IPFIREWALL # 支援 NAT options IPDIVERT # 下面這一行是預設允許所有封包通過,如果沒有這一行, # 就必須在 /etc/rc.firewall 中設定封包的規則。 # 這條規則內定編號是 65535,也就是所有規則的最後一條 # 如果沒有加這一條規則,內定就是拒絕所有封包, # 只允許規則中允許的封包通過。 options IPFIREWALL_DEFAULT_TO_ACCEPT # 這一行是讓你可以在 ipfw 中設定要記錄哪些封包, # 如果沒有這一行,就算設定了要留下記錄也不會有作用。 options IPFIREWALL_VERBOSE # 這一行是限制每一條規則所要記錄的封包數量, # 因為同樣的規則可能有許多記錄,加上這一條可以使 # 同樣的記錄重覆數減少,以避免記錄檔爆增。 options IPFIREWALL_VERBOSE_LIMIT=10 # 下面這一行是用來支援封包轉向, # 當你要使用 fwd 動作時必須要有這一項設定。 options IPFIREWALL_FORWARD # 如果要使用 pipe 來限制頻寬,必須加入下列選項以支援 dummynet。 options DUMMYNET |
ipfw 也支援狀態維持 (keep-state) 的功能,就是可以讓符合設定的規則以動態的方式來分配增加規則 (位址或連接埠) 來讓封包通過。也就是說防火牆可以記住一個外流的封包所使用的位址及連接埠,並在接下來的幾分鐘內允許外界回應。這種動態分配的規則有時間的限制,一段時間內會檢查連線狀態,並清除記錄。
所有的規則都有計數器記錄封包的數量、位元數、記錄的數量及時間等。而這些記錄可以用 ipfw 指令來顯示或清除。
在說明 ipfw 規則的語法之前,我們先來看這個指令的用法。ipfw 可以使用參數:
指令 | 說明 |
ipfw add [rule] | 新增一條規則。規則 (rule) 的語法請參考下一節的說明。 |
ipfw delete [number] | 刪除一條編號為 number 的規則。 |
ipfw -f flush | 清除所有的規則。 |
ipfw zero | 將計數統計歸零。 |
ipfw list | 列出現在所有規則,可以配合下列參數使用。 |
-a | 使用 list 時,可以列出封包統計的數目。 |
-f | 不要提出確認的詢問。 |
-q | 當新增 (add)、歸零(zero)、或清除 (flush) 時,不要列出任何回應。當使用遠端登入,以 script (如 sh /etc/rc.firewall) 來修改防火牆規則時,內定會列出你修改的規則。但是當下了 flush 之後,會立即關掉所有連線,這時候回應的訊息無法傳達終端機,而規則也將不被繼續執行。此時唯一的方法就是回到該電腦前重新執行了。在修改防火牆規則時,最好在電腦前修改,以免因為一個小錯誤而使網路連線中斷。 |
-t | 當使用 list 時,列出最後一個符合的時間。 |
-N | 在輸出時嘗試解析 IP 位址及服務的名稱。 |
-s [field] | 當列出規則時,依哪一個計數器 (封包的數量、位元數、記錄的數量及時間) 來排序。 |
12.3.1 ipfw 規則
我們在過濾封包時,可以依據下列的幾個封包所包含的資訊來處理該封包:
使用 IP 位址或 TCP/UDP 的埠號來做為規則可能蠻危險的,因為這二種都有可能被以假的資訊所蒙騙 (spoof)。但是這二種卻也是最常被使用的方法。
下列為 ipfw rules 的語法:
[number] action [log] proto from src to dist [interface_spec] [option]
使用 [ ] 包起來的表示可有可無,我們一一為大家說明它們的意義:
number:
number 是一個數字,用來定義規則的順序,因為規則是以先入為主的方式處理,如果你將規則設定放在一個檔案中 ( 如 /etc/rc.firewall ),規則會依每一行排列的順序自動分配編號。你也可以在規則中加上編號,這樣就不需要按順序排列了。如果是在命令列中下 ipfw 指令來新增規則的話,也要指定編號,這樣才能讓規則依我們的喜好排列,否則就會以指令的先後順序來排。這個編號不要重覆,否則結果可能不是你想要的樣子。
action:
action 表示我們這條規則所要做的事,可以用的 action 有下列幾個:
命令 | 意義 |
allow | 允許的規則,符合則通過。也可以使用 pass,permit, accept 等別名。 |
deny | 拒絕通過的規則。 |
reject | 拒絕通過的規則,符合規則的封包將被丟棄並傳回一個 host unreachable 的 ICMP。 |
count | 更新所有符合規則的計數器。 |
check-state | 檢查封包是否符合動態規則,如果符合則停止比對。若沒有 check-state 這條規則,動態規則將被第一個 keep-state 的規則所檢查。 |
divert port | 將符合 divert sock 的封包轉向到指定的 port。 |
fwd ipaddr[,port] | 將符合規則封包的去向轉向到 ipaddr,ipaddr 可以是 IP 位址或是 hostname。如果設定的
ipaddr 不是直接可以到達的位址,則會依本機即有的 routing table 來將封包送出。如果該位址是本地位址 (local
address),則保留本地位址並將封包送原本指定的 IP 位址。這項設定通常用來和 transparent proxy 搭配使用。例如:
# ipfw add 50000 fwd 127.0.0.1,3128 tcp from \ 192.168.1.0/24 to any 80 如果沒有設定 port ,則會依來源封包的 port 將封包送到指定的 IP。使用這項規則時,必須在 kernel 中設定選項 IPFIREWALL_FORWARD。 |
pipe pipe_nr | 傳遞封包給 dummynet(4) "pipe",用以限制頻寬。使用本語法必須先在核心中加入 option
DUMMYNET。請 man ipfw 及 man dummynet。 基本語法是先將要設定頻寬的規則加入: ipfw add pipe pipe_nr .... 再設定該規則的頻寬: ipfw pipe pipe_nr config bw B delay D queue Q plr P 這裡的 pipe_nr 指的是 pipe 規則編號,從 1~65535;B 是指頻寬,可以表示為 bit/s、Kbit/s、Mbit/s、Bytes/s、KBytes/s、或 MBytes/s。D 是延遲多少 milliseconds (1/1000)。Q 是 queue size 的大小 (單位為 packages 或 Bytes)。P 是要隨機丟棄的封包數量。 例如我們要限制內部網域的電腦對外上傳的最大頻寬是 20 KBytes: ipfw add pipe 1 ip from 192.168.0.1/24 to any in ipfw pipe 1 config bw 20KBytes/s |
log:
如果該規則有加上 log 這個關鍵字,則會將符合規則的封包記錄在 /var/log/security 中。前提是在核心中有設定 IPFIREWALL_VERBOSE 的選項。有時因為同樣的封包太多,會使記錄檔保有大量相同的記錄,因此我們會在核心中再設定 IPFIREWALL_VERBOSE_LIMIT 這個選項,來限制要記錄多少相同的封包。
proto:
proto 表示 protocol,即網路協定的名稱,如果使用 ip 或 all 表示所有協定。可以使用的選項有 ip,all,tcp,udp,icmp 等。
src 及 dist:
src 是封包來源;dist 是封包目的地。在這二個項目可以用的關鍵字有 any, me, 或是以 <address/mask>[ports] 的方式明確指定位址及埠號。
若使用關鍵字 any 表示使這條規則符合所有 ip 位址。若使用關鍵字 me 則代表所有在本系統介面的 IP 位址。而使用明確指定位址的方式有下列三種:
而在 me, any 及 指定的 ip 之後還可以再加上連接埠編號 (ports),指定 port 的方法可以是直接寫出 port ,如 23;或給定一個範圍,如 23-80;或是指定數個 ports,如 23,21,80 以逗點隔開。或者是寫出在 /etc/services 中所定義的名稱,如 ftp,在 services 中定義是 21,因此寫 ftp 則代表 port 21。
interface-spec:
interface-spec 表示我們所要指定的網路介面及流入或流出的網路封包。我們可以使用下列幾個關鍵字的結合:
關鍵字 | 意義 |
in | 只符合流入的封包。 |
out | 只符合流出的封包。 |
via ifX | 封包一定要經過介面 ifX,if 為介面的代號,X 為編號,如 vr0。 |
via if* | 表示封包一定要經過介面 ifX,if 為介面的代號,而 * 則是任何編號,如 vr* 代表 vr0,vr1,...。 |
via any | 表示經過任何介面的封包。 |
via ipno | 表示經過 IP 為 ipno 介面的封包。 |
via 會使介面永遠都會被檢查,如果用另一個關鍵字 recv ,則表示只檢查接收的封包,而 xmit 則是送出的封包。這二個選項有時也很有用,例如要限制進出的介面不同時:
ipfw add 100 deny ip from any to any out recv vr0 xmit ed1
recv 介面可以檢查流入或流出的封包,而 xmit 介面只能檢查流出的封包。所以在上面這裡一定要用 out 而不能用 in,只要有使用 xmit 就一定要使用 out。另外,如果 via 和 recv 或 xmit 一起使用是沒有效的。
有的封包可能沒有接收或傳送的介面:例如原本就由本機所送出的封包沒有接收介面,而目的是本機的封包也沒有傳送介面。
options:
我們再列出一些常用的 option 選項 ,更多選項請 man ipfw:
選項名稱 | 意義 |
keep-state | 當符合規則時,ipfw 會建立一個動態規則,內定是讓符合規則的來源及目的地使用相同的協定時就讓封包通過。這個規則有一定的生存期限 (lift time,由 sysctl 中的變數所控制),每當有新的封包符合規則時,便用重設生存期限。 |
bridged | 只符合 bridged 的封包。 |
established | 只適用於 TCP 封包,當封包中有 RST 或 ACK bits 時就符合。 |
uid xxx | 當使用者 uid 為 xxx 則符合該規則。例如,我們如果要限制 Anonymous FTP 的下載速度最大為 64KB/s,則可以使用: ipfw pipe 1 config bw 512Kbit/s ipfw add pipe 1 tcp from me to any uid 21 上列規則第一行是先建一個編號為 1 的 pipe,限制頻寬為 512 Kbit/s (也就是 64 KByte/s),接著第二條是當使用者 uid 為 21 時,從本機 (me) 下載的 tcp 封包都使用編號 1 的 pipe。因為 Anonymous FTP 的使用者是 ftp,它的預設 uid 為 21,所以這條規則會被套用在 Anonymous FTP user 上。 |
setup | 只適用於 TCP 封包,當封包中有 SYN bits 時就符合。 |
以上的說明只是 man ipfw 中的一小部份。如果你想要對 ipfw 更了解,例如如何使用 ipfw 來限制頻寬等,建議你 man ipfw。
不知道您看了這麼多的規則是否覺得眼花撩亂,如果不了解 TCP/IP 的原理,徹底了解 ipfw 的設定還真不容易。沒關係,我們下面將舉幾個簡單、常用的設定,這些範例應該夠平常使用了。
12.3.2 範例
我將原本的 /etc/rc.firewall 備份成 rc.firewal.old,並將它改成下列內容,請注意,這裡只是範例,只供參考:
# 設定我的 IP myip="1.2.3.4" # 設定對外的網路卡代號 outif="vr0" # 設定對內的網路上代號 inif="vr1" #清除所有的規則 /sbin/ipfw -f flush # Throw away RFC 1918 networks ${ipfw} add deny ip from 10.0.0.0/8 to any in via ${oif} ${ipfw} add deny ip from 172.16.0.0/12 to any in via ${oif} ${ipfw} add deny ip from 192.168.0.0/16 to any in via ${oif} # 只允許內部網路對 192.168.0.1 使用 telnet 服務 /sbin/ipfw add 200 allow tcp from 192.168.0.1/24 to 192.168.0.1 telnet # 拒絕其他人連到 port 23,並記錄嘗試連線的機器 /sbin/ipfw add 300 deny log tcp from any to me 23 # 拒絕任何 ICMP 封包 /sbin/ipfw add 400 deny icmp from any to any # 下面這台機器是壞人,不讓它進來,並記錄下來 /sbin/ipfw add 1100 deny log all from 211.21.104.102 to any # NAT 的設定 /sbin/ipfw add divert natd all from any to any via vr0 # 限制內部網域對外下載最大頻寬為 20KBytes/s,上傳最大頻寬為 5KBytes/s ipfw pipe 20 config bw 20KBytes/s ipfw add pipe 20 ip from any to 192.168.0.1/24 out ipfw pipe 30 config bw 5KBytes/s ipfw add pipe 30 ip from 192.168.0.1/24 to any in # 允許本機對任何地方連線 /sbin/ipfw add check-state /sbin/ipfw add 2000 allow udp from ${myip} to any keep-state /sbin/ipfw add 2100 pass ip from ${myip} to any # 允許外界使用郵件服務 /sbin/ipfw add 3000 pass tcp from any to ${myip} 25 in via ${outif} # 不允許內部的 IP 從外部連進來 /sbin/ipfw add 1200 add deny ip from ${myip}/24 to any in via ${oif} # 其他都拒絕,如果沒有在 kernel 中設定 # IPFIREWALL_DEFAULT_TO_ACCEPT 則內定就有下列這一條 /sbin/ipfw 65535 add deny all from any to any |
存檔後就可以使用 sh rc.firewall 來執行新的規則了。如果您將規則放在 /etc/rc.firewall 中,則開機時會自動執行。
12.3.3 一些小建議
在建立一個封包過濾的防火牆時,應該盡可能阻擋一些不必要的服務。避免開放 port 1024 以下的 TCP 服務,例如只通過 SMTP 封包 (port 25) 給郵件伺服器;拒絕所有 UDP 連線 (只有少部份服務如 NFS 會用到);一些只有內部才會使用的服務,如資料庫等也不必對外開放。
另外,同樣的防火牆限制可以使用不同的語法來展現,應該要試著讓規則數量越少越好,以加快處理速度。
在更新 firewall 規則時,如果規則沒有寫好,而你又是以遠端登入的方式修改規則,很可能會因此無法繼續登入。因此建議更新規則時最好在 console 前執行,若迫不得已一定要使用遠端登入,建議您執行 /usr/share/examples/ipfw/change_rules.sh 這支程式來編輯規則:
# cd /usr/share/examples/ipfw # sh change_rules.sh
接著會出現文書編輯軟體並最動載入 /etc/rc.firewall 讓你編輯,結束離開後,會詢問是否要執行更新。如果執行新的規則後造成斷線,它會自動載入舊的規則,讓我們可以再次連線。
12.4 封包過濾橋接器
如果您有三台機器全部都有 public IP,而您想使用其中一台做為防火牆,在不改變另外二台機器的設定下,我們可以使具封包過濾的橋接器來架設防火牆。只要將這台橋接器放在另外二台和對外網路之間即可。
另外,當我們的內部網路有不同 class 的主機時,例如內部有 140.115.2.3 及 140.115.5.6 這二台電腦時,就無法使用傳統的防火牆。如果要在這二台機器連到網際網路中途中使用防火牆,我們必須使用新的方式,也可以使用這裡介紹的橋接器。
我們可以使用 FreeBSD 為橋接器,利用它來做封包過濾的動作,而絲毫不影響內部的主機原本的設定。為了達到這個功能,我們必需要有二張支援 promiscuous mode 的網路卡,現在的網路卡大部份都有支援。二張網路卡當中,一張需要設定 IP,另一張不需要。至於您要將 IP 設定在哪一張卡都可以,建議是設在對外的網路卡上。
首先,我們必須在核心中加入關於橋接器的設定:
# 支援橋接器 options BRIDGE # 防火牆設定 options IPFIREWALL options IPFIREWALL_VERBOSE # 我們這裡不將防火牆預設為接收所有封包 #options IPFIREWALL_DEFAULT_TO_ACCEPT |
如果您要讓橋接器具有流量控制的功能,則可以加上之前提到的選項「options DUMMYNET」。重新編譯核心後,在重開機前,我們先設定一下 /etc/rc.conf:
firewall_enable="YES" firewall_type="open" |
還有一件事要做,當在乙太網路上跑 IP 協定時,事實上使用二種乙太網路協定,一個是 IP,另一個是 ARP。ARP 協定是當機器要找出給定 IP 位址所對應的乙太網路位址時使用的。ARP 並不是 IP 層的一部份,只是給 IP 應用在乙太網路上運作。標準的防火牆規則中並未加入對於 ARP 的支援,幸運的是,高手們的在 ipfirewall 程式碼中加入了對封包過濾橋接器的支援。如果我們在 IP 位址 0.0.0.0 上建立一個特別的 UDP 規則,UDP 埠的號碼將被使用來搭配被橋接封包的乙太網路協定號碼,如此一來,我們的橋接器就可以被設定成傳遞或拒絕非 IP 的協定。請在 /etc/rc.firewall 中接近文件頂端處理 lo0 的那三行之下(就是有寫 Only in rare cases do you want to change these rules 的地方)加入下面一行:
${fwcmd} add allow udp from 0.0.0.0 2054 to 0.0.0.0 |
現在我們就可以重新開機了。重開機之後,先執行下列指令來啟動橋接器:
如果您使用的是 FreeBSD 4.x:
# sysctl -w net.link.ether.bridge_ipfw=1 # sysctl -w net.link.ether.bridge=1
如果您使用的是 FreeBSD 5.x:
# sysctl -w net.link.ether.bridge.ipfw=1 # sysctl -w net.link.ether.bridge.enable=1
現在我們可以將機器放在內外二個網域之間了。因為我們之前在 /etc/rc.conf 中,設定防火牆完全打開,不阻擋任何封包,所以放在二個網域之間時,運作應該沒有問題。我們之前只設了一張網路上的 IP,而在執行了上述的指令之後,第二張網路卡便開始運作。
下一步就是將我們啟動橋接器的指令放在 /etc/rc.local 中,讓系統在開機時自動執行。或者,我們可以在 /etc/sysctl.conf 中加入下面二行:
# 如果您使用的是 FreeBSD 4.x net.link.ether.bridge_ipfw=1 net.link.ether.bridge=1 # 如果您使用的是 FreeBSD 5.2 以後的版本 net.link.ether.bridge.enable=1 net.link.ether.bridge.ipfw=1 |
接下來我們就可以依自己的需求在 /etc/rc.firewall 文件的最後面加上我們自己想要的防火牆規則了。以下是一個簡單的設定規則,假設橋接器的 IP 是 140.115.75.137,內部有二台主機,一台提供網頁服務,一台是 BBS:
us_ip=140.115.75.137 basrv_ip=140.115.3.4 bbs_ip=140.115.5.6 oif=fxp0 iif=fxp1 ipfw="/sbin/ipfw" # Things that we've kept state on before get to go through in a hurry. ${ipfw} 1000 add check-state # Throw away RFC 1918 networks ${ipfw} 1100 add deny ip from 10.0.0.0/8 to any in via ${oif} ${ipfw} 1200 add deny log ip from 172.16.0.0/12 to any in via ${oif} ${ipfw} 1300 add deny log ip from 192.68.0.0/16 to any in via ${oif} # 允許橋接器本身所有想做的連線 (keep state if UDP) ${ipfw} 1400 add pass udp from ${us_ip} to any keep-state ${ipfw} 1500 add pass ip from ${us_ip} to any # 允許內部網路任何想做的連線 (keep state if UDP) ${ipfw} 1600 add pass udp from any to any in via ${iif} keep-state ${ipfw} 1700 add pass ip from any to any in via ${iif} # 允許任何的 ICMP 連線 ${ipfw} 1800 add pass icmp from any to any # 不允許使用 port 888 連線 ${ipfw} 2000 add deny log tcp from any to ${bbs_ip} 888 # TCP section # 任何地方都可以建立 TCP 連線 ${ipfw} 3000 add pass tcp from any to any via ${oif} # Pass the "quarantine" range. ${ipfw} 3100 add pass tcp from any to any 49152-65535 in via ${oif} # Pass ident probes. It's better than waiting for them to timeout ${ipfw} 3200 add pass tcp from any to any 113 in via ${oif} # Pass SSH. ${ipfw} 3300 add pass tcp from any to any 22 in via ${oif} # Pass DNS. 當內部網路有名稱伺服器時才需要 #${ipfw} add pass tcp from any to any 53 in via ${oif} # 只傳遞 SMTP 給郵件伺服器 ${ipfw} 3400 add pass tcp from any to ${bbs_ip} 25 in via ${oif} ${ipfw} 3500 add pass tcp from any to ${basrv_ip} 25 in via ${oif} # UDP section # Pass the "quarantine" range. ${ipfw} 4000 add pass udp from any to any 49152-65535 in via ${oif} # Pass DNS. 當內部網路有名稱伺服器時才需要 #${ipfw} 4100 add pass udp from any to any 53 in via ${oif} # 其他的都拒絕 ${ipfw} 60000 add deny ip from any to any |