IPTables
<<TableOfContents: execution failed [Argument "maxdepth" must be an integer value, not "[1]"] (see also the log)>>
1. 引言
CentOS 內置了一個非常強勁的防火牆,統稱為 iptables,但更正確的名稱是 iptables/netfilter。iptables 是一個用戶空間的模塊。作為用戶,你在指令行就是透過它將防火牆規則放進預設的表裡。netfilter 是一個核心模塊,它內置於內核中,進行實際的過濾。iptables 有很多前端圖像介面可以讓用戶新增或定義規則,但它們很多時不及使用指令行般有彈性,而且限制用戶瞭解實際發生的事情。我們將會學習 iptables 的指令行介面。
在我們正式應付 iptables 前,我們必須對它的運作有一個基本的理解。iptables 利用到 IP 位址、協定(tcp、udp、icmp)及連接埠這些概念。我們不需要成為這些方面的專家(因為我們可以找到所需的資訊),但對它們有一般的理解會有幫助。
iptables 將規則放進預設的規則鏈(INPUT、OUTPUT 及 FORWARD),而所有流量(IP 封包)都會被相關的規則鏈檢查,根據當中的規則判斷如何處理每個封包,例如:接納或丟棄它。這些動作稱為目標,而最常見的兩個預設目標就是 DROP 來丟棄封包;或 ACCEPT 來接納封包。
規則鏈
我們可以在過濾表的 3 條預設規則鏈內加入規則,來處理通過這些規則鏈的封包。它們分別是:
- INPUT - 所有以主機為目的地的封包。
- OUTPUT - 所有源自主機的封包。
- FORWARD - 這些封包的目的地或來源地都不是主機,但路經主機(由它選路)。假若你的主機是一個路由器,這條規則鏈將會被應用。
我們將會花費最多時間處理 INPUT 規則鏈,藉以過濾進入我們的機器的封包 —— 亦即是將壞蛋拒諸門外。
規則是以列表的方式被加進每條規則鏈。每個封包會被頭一條規則開始檢查,才至最後一條。假若封包與其中一條規則吻合,相應的動作便會被執行,例如接納(ACCEPT)或丟棄(DROP)封包。一旦有吻合的規則,這個封包便會按照規則來處理,而不再被規則鏈內的其它規則所檢查。假如封包通過所有檢查而不符合任何規則鏈內的任何一條規則,那應這條規則鏈的預設動作將會被執行。這就是所謂的預設政策,可以設定為接納(ACCEPT)或丟棄(DROP)封包。
規則鏈擁有預設政策這個概念帶來兩個基本的可能性,而我們必須考慮它們才能決定如何組織我們的防火牆。
1. 我們可以預設一個政策來丟棄(DROP)所有封包,然後刻意加入規則來接納(ACCEPT)源自被信任的 IP 位址的封包,或者開啟那些提供服務的連接埠,如:bittorrent、FTP 伺服器、網頁伺服器、Samba 檔案伺服器等。
又或者,
2. 我們可以預設一個政策來接納(ACCEPT)所有封包,然後刻意加入規則來攔截(DROP)來自有問題的 IP 位址或系列的封包,也或者阻止封包進出只作私人用途或未提供服務的連接埠。
普遍來說,第一個方法多數用在 INPUT 規則鏈,因為我們會希望控制哪些東西可以存取我們的機器;而第二個方法多數用在 OUTPUT 規則鏈,因為我們多數信賴那些離開(源自)我們機器的封包。
2. 準備開始
在指令行上使用 iptables 需要 root 的權限,因此你必須化身為 root 用戶來做下面的事情。
|
注意: 我們將會停用 iptables 及重設你的防火牆規則,因此假若你依賴你的 Linux 防火牆作為第一道防線,請特別留意這點。 |
iptables 應該預設被安裝在所有 CentOS 5.x 及 6.x 上。你可以這樣來檢查 iptables 是否已安裝在你的系統上:
$ rpm -q iptables iptables-1.4.7-5.1.el6_2.x86_64
要知道 iptables 是否正在運作中,我們可以檢查 iptables 這個模塊是否已被載入,並利用 -L 這個選項來檢視現行的規則:
# lsmod | grep ip_tables ip_tables 29288 1 iptable_filter x_tables 29192 6 ip6t_REJECT,ip6_tables,ipt_REJECT,xt_state,xt_tcpudp,ip_tables
# iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED ACCEPT icmp -- anywhere anywhere ACCEPT all -- anywhere anywhere ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh REJECT all -- anywhere anywhere reject-with icmp-host-prohibited Chain FORWARD (policy ACCEPT) target prot opt source destination REJECT all -- anywhere anywhere reject-with icmp-host-prohibited Chain OUTPUT (policy ACCEPT) target prot opt source destination
從上面我們可看見一台 CentOS 6 系統的預設規則。請留意存取 SSH 服務預設是獲允許的。
如果 iptables 並未被執行,你可以這樣啟用它:
# system-config-securitylevel
3. 建立一組簡單的規則
|
注意: 此刻我們將會清除預設的規則集。如果你是透過 SSH 遠端連線到一台伺服器來進行學習,你有可能會將自己拒諸這台機器之外。你必須將預設的輸入(input)政策改為接納(accept),然後才清除現有規則,接著你要預先加入一條容許你自己存取機器的規則,避免你將自己封鎖在外。 |
我們會採用一個以範例為本的方法來檢視 iptables 的不同指令。在首個範例中,我們會建立一組簡單的規則來設置一個「狀態封包檢驗」(SPI)防火牆,容許對外的連線但攔截一切無用的對內連線:
# iptables -P INPUT ACCEPT # iptables -F # iptables -A INPUT -i lo -j ACCEPT # iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # iptables -A INPUT -p tcp --dport 22 -j ACCEPT # iptables -P INPUT DROP # iptables -P FORWARD DROP # iptables -P OUTPUT ACCEPT # iptables -L -v
你應該得到這樣的輸出:
Chain INPUT (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 ACCEPT all -- lo any anywhere anywhere 0 0 ACCEPT all -- any any anywhere anywhere state RELATED,ESTABLISHED 0 0 ACCEPT tcp -- any any anywhere anywhere tcp dpt:ssh Chain FORWARD (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination
現在讓我們逐一看看以上的 8 個指令,並理解我們實際做了甚麼:
iptables -P INPUT ACCEPT 假如利用遠端連線,我們必須臨時將 INPUT 鏈的預設政策改為 ACCEPT,否則當我們清除現有的規則集時,便會將自己封鎖在伺服器之外。
iptables -F 我們利用 -F 選項來清除一切現存的規則,好讓我們能夠在嶄新的狀態下加入的規則。
iptables -A INPUT -i lo -j ACCEPT 現在是時候加入一些規則了。我們利用 -A 選項來附加(新增)規則到某條鏈,而這裡所指的是 INPUT 鏈。接著我們利用 -i 選項(interface「介面」之意)來指定那些符合或來自 lo(localhost、127.0.0.1)介面的封包。最後我們 -j(jump「跳至」)符合這條規則的目標動作:在這裡是 ACCEPT。所以這條規則會導致所有前往 localhost 介面的對內封包穫得接納。一般來說這是必須的,因為很多軟件預期能夠與 localhost 配接卡溝通。
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 這是擔負起大部份工作的規則,而我們再一次將它加進(-A)INPUT 鏈內。這裡我們利用 -m 選項來載入一個模塊(state)。state 模塊能夠檢視一個封包並判斷它的狀態是 NEW、ESTABLISHED 抑或 RELATED。NEW 指進入的封包屬於不是由主機起始的新增連線。ESTABLISHED 及 RELATED 指進入的封包隸屬於一條現存的連線,或者與現存的連線有關係。
iptables -A INPUT -p tcp --dport 22 -j ACCEPT 現在我們加入一條規則來容許 SSH 通過 tcp 連接埠 22 來連線。這樣做是要防止我們連接到遠端系統的 SSH 連線意外地被封銷。我們稍後會更詳細解釋這條規則。
iptables -P INPUT DROP 這個 -P 選項設定某條規則鏈上的預設政策。我們現在可以將 INPUT 鏈的預設政策改為 DROP。意思就是,不符合任何一條規則的對內封包將會被丟棄。要是我們透過 SSH 遠端連線而沒有加入上一條規則,此刻我們便會被封鎖於系統之外。
iptables -P FORWARD DROP 同樣地,在這裡我們將 FORWARD 鏈的預設政策設為 DROP,因為我們並不是用電腦作為路由器,所以理應沒有任何封包路經它。
iptables -P OUTPUT ACCEPT 而最後,我們將 OUTPUT 鏈的預設政策設為 ACCEPT,因為我們想容許所有對外的流量(由於我們信任我們的用戶)。
iptables -L -v 最後,我們可以列出(-L)剛加入的規則,並檢查它們是否被正確地載入。
我們需要做的最後一件事情,就是儲存我們的規則,好讓它們在下次開機時會自動被重新載入:
# /sbin/service iptables save
這樣做會執行 iptables 的 init 腳本,它會執行 /sbin/iptables-save 並將現有的 iptables 設定寫進 /etc/sysconfig/iptables。開機時,iptables 的 init 腳本會透過 /sbin/iptables-restore 這個指令重新實施儲存在 /etc/sysconfig/iptables 內的規則。
很明顯的,在指令殼內輸入這堆指令會頗乏味,因此運用 iptables 的最簡易方法就是建立一個代你做以上一切的腳本。你可以將上面的指令輸入到你喜歡的文字編輯器內並儲存為 myfirewall,例如:
# # iptables 範例設定腳本 # # 清除 iptables 內一切現存的規則 # iptables -F # # 容讓 SSH 連線到 tcp 連接埠 22 # 當透過 SSH 遠端連線到伺服器,你必須這樣做才能群免被封鎖於系統外 # iptables -A INPUT -p tcp --dport 22 -j ACCEPT # # 設定 INPUT、FORWARD、及 OUTPUT 鏈的預設政策 # iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # # 設定 localhost 的存取權 # iptables -A INPUT -i lo -j ACCEPT # # 接納屬於現存及相關連線的封包 # iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # # 儲存設定 # /sbin/service iptables save # # 列出規則 # iptables -L -v
註: 我們可以在腳本內加入註釋來提醒自己正在做甚麼。
現在令腳本可以被執行:
# chmod +x myfirewall
我們現在可以編輯這個腳本,並在指令殼內用以下指令來執行它:
# ./myfirewall
4. 介面
在上一個範本中,我們看見如何能接納所有來自某個介面的封包,也就是 localhost 介面:
iptables -A INPUT -i lo -j ACCEPT
假設我們現在有兩個獨立的網絡介面,分別是將我們連線到內部網絡的 eth0 及連線到外部互聯網的 ppp0 撥號數據機(或者 eth1 配接卡)。我們或許會想接納所有來自內部網絡的對內封包,但依然過濾那些來自互聯網的封包。我們可以這樣做:
iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -i eth0 -j ACCEPT
讓特別留意 —— 假如你接納來自互聯網介面(例如 ppp0 撥號數據機)的所有封包:
iptables -A INPUT -i ppp0 -j ACCEPT
你便等同於停用了我們的防火牆!
5. IP 位址
將整個介面開放給對內的封包也許不夠嚴謹,而你想擁有更多控制權來決定接納甚麼及拒絕甚麼。現在假設我們擁有一群採用 192.168.0.x 私人網路的電腦。我們可以打開防火牆給來自某個被信任 IP 位址(例如 192.168.0.4)的對內封包:
# 接納來自被信任 IP 位址的封包 iptables -A INPUT -s 192.168.0.4 -j ACCEPT # change the IP address as appropriate
將這個指令分解,我們首先附加(-A)一條規則到 INPUT 鏈,指明來源(-s)IP 位址是 192.168.0.4 的封包都應該被接納(ACCEPT)(請亦留意我們如何利用 # 符號來解釋我們的腳本,因為 # 之後的所有文字都會被視為註釋)。
當然,如果我們想接納來自一系列 IP 位址的封包,我們可以為每個被位任的 IP 位址加入一條規則,而這樣做的確是可行的。但是假如它們的數量很多,一次過加入一系列 IP 位址會比較簡單。要這樣做,我們可以利用一個子網遮罩或標準的斜線記法來指定 IP 位址的範圍。舉個例說,如果我們想將防火牆開放給來自整個 192.168.0.x(當中 x=1 到 254)範圍的封包,我們可以用下面其中一個方法來達致目的:
# 接納來自被信任 IP 位址的封包 iptables -A INPUT -s 192.168.0.0/24 -j ACCEPT # using standard slash notation iptables -A INPUT -s 192.168.0.0/255.255.255.0 -j ACCEPT # using a subnet mask
最後,除了過濾單一的 IP 位址外,我們亦可以配對該裝置的 MAC 位址。要這應做,我們需要載入一個容許過濾 MAC 位址的模塊(mac 模塊)。較早前當我們用 state 模塊來配對 ESTABLISHED 及 RELATED 封包時,我們看見模塊延伸 iptables 功能的例子。在這裡我們除了檢查封包的 IP 位址外,更利用 mac 模塊來檢查來源地的 MAC 位址:
# 接納來自被信任 IP 位址的封包 iptables -A INPUT -s 192.168.0.4 -m mac --mac-source 00:50:8D:FD:E6:32 -j ACCEPT
首先我們用 -m mac 來載入 mac 模塊,然後我們用 --mac-source 來指定來源 IP 位址(192.168.0.4)的 MAC 位址。你要為每個需要過濾的乙太網裝置找出 mac 位址。以 root 的身份執行 ifconfig(無線裝置用 iwconfig)可以將 mac 位址告訴你。
這樣可防止來源地的 IP 位址被偽裝,因為只有真正源於 192.168.0.4(MAC 位址是 00:50:8D:5D:E6:32)的封包才會被接納,而所有假扮源於該位址的封包都會被攔截。請注意,過濾 MAC 位址在互聯網上無法使用,卻絕對能正確地在本地網絡裡運作。
6. 連接埠及協定
由上面我們看見如何將新增規則在防火牆內,用來過濾符合某個介面或來源 IP 位址的封包。 這樣做讓我們能經過防火牆存取某些被信任的來源(主機)。現在我們看看如何過濾協定及連接埠,好叫我們能進一步區別要接納及攔截那些對內的封包。
在我們開始之先,我們須要知道個別服務所使用的協定及連接埠編號。讓我們以 bittorrent 作為一個簡單的範例。bittorrent 在 6881 埠上採用 tcp 協定,因此我們需要容許所有以 6881 為目標埠(它們到逹時所用的連接埠)的 tcp 封包。
# 接納目標埠是 6881 號(bittorrent)的 tcp 封包 iptables -A INPUT -p tcp --dport 6881 -j ACCEPT
在這裡我們附加(-A)一條規則到 INPUT 鏈,配對 tcp 協定(-p tcp)及從 6881 目標埠進入我們的機器(--dport 6881)。
註: 要配對目標或來源埠(--dport 或 --sport),你必須先指定協定(tcp、udp、icmp、all)。
我們亦可以延伸以上的範例來包含一系列的連接埠,例如,接納 6881 至 6890 埠上的所有 tcp 封包:
# 接納目標埠是 6881-6890 號的 tcp 封包 iptables -A INPUT -p tcp --dport 6881:6890 -j ACCEPT
7. 融會貫通
既然我們已經有基本認識,現在便可以合併這些規則。
UNIX/Linux 上一個受歡迎的服務就是容許遠端登入的 SSH 服務。SSH 預設使用 22 號埠及採用 tcp 協定。因此假若我們想允許遠端登入,我們需要容許 tcp 連線到 22 號埠:
# 接納目標埠是 22 號(SSH)的 tcp 封包 iptables -A INPUT -p tcp --dport 22 -j ACCEPT
這樣做會開放 22 號埠(SSH)給所有對內的 tcp 連線,卻會構成潛在的安全性威脅,因為駭客可以強行破入使用易猜測密碼的戶口。然而,假若我們知道那些透過 SSH 作遠端登入的可信任機器的 IP 位址,我們便可以將存取權限制到那些來源 IP 位址。舉個例說,如果我們只想將 SSH 的存取權開放給我們的私人網絡(192.168.0.x),我們可以將來源 IP 位址限制在這個範圍:
# 接納來自私人網絡,目標埠是 22 號(SSH)的 tcp 封包 iptables -A INPUT -p tcp -s 192.168.0.0/24 --dport 22 -j ACCEPT
利用來源 IP 進行過濾容讓我們能安全地開放 22 號埠上的 SSH 給可信任的 IP 位址來存取。舉個例說,我們可以用這個方法允許工作與家用機器之間的登入。對於其它 IP 位址來說,這個連接埠(與及服務)就好像了關閉了一樣,而服務亦依被停用,因此掃描連接埠的駭客多數會略過我們。
8. 總結
這裡只是很初步地介紹 iptables 可以做的事情,但我希望這份教學文檔提供了一個良好的基礎,幫助各位建立更複雜的規則集。
9. 連結
http://www.centos.org/docs/5/html/Deployment_Guide-en-US/ch-fw.html
http://www.centos.org/docs/5/html/Deployment_Guide-en-US/ch-iptables.html
http://ip2location.com/free/visitor-blocker —— 根據 IP 位址攔截某些國家
Translation of revision 36