IPTables

1. 引言

CentOS 內置了一個非常強勁的防火牆,統稱為 iptables,但更正確的名稱是 iptables/netfilter。iptables 是一個用戶空間的模塊。作為用戶,你在指令行就是透過它將防火牆規則放進預設的表裡。netfilter 是一個核心模塊,它內置於內核中,進行實際的過濾。iptables 有很多前端圖像介面可以讓用戶新增或定義規則,但它們很多時不及使用指令行般有彈性,而且限制用戶瞭解實際發生的事情。我們將會學習 iptables 的指令行介面。

在我們正式應付 iptables 前,我們必須對它的運作有一個基本的理解。iptables 利用到 IP 位址、協定(tcp、udp、icmp)及連接埠這些概念。我們不需要成為這些方面的專家(因為我們可以找到所需的資訊),但對它們有一般的理解會有幫助。

iptables 將規則放進預設的規則鏈(INPUT、OUTPUT 及 FORWARD),而所有流量(IP 封包)都會被相關的規則鏈檢查,根據當中的規則判斷如何處理每個封包,例如:接納或丟棄它。這些動作稱為目標,而最常見的兩個預設目標就是 DROP 來丟棄封包;或 ACCEPT 來接納封包。

規則鏈

我們可以在過濾表的 3 條預設規則鏈內加入規則,來處理通過這些規則鏈的封包。它們分別是:

我們將會花費最多時間處理 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 個指令,並理解我們實際做了甚麼:

  1. iptables -P INPUT ACCEPT 假如利用遠端連線,我們必須臨時將 INPUT 鏈的預設政策改為 ACCEPT,否則當我們清除現有的規則集時,便會將自己封鎖在伺服器之外。

  2. iptables -F 我們利用 -F 選項來清除一切現存的規則,好讓我們能夠在嶄新的狀態下加入的規則。

  3. iptables -A INPUT -i lo -j ACCEPT 現在是時候加入一些規則了。我們利用 -A 選項來附加(新增)規則到某條鏈,而這裡所指的是 INPUT 鏈。接著我們利用 -i 選項(interface「介面」之意)來指定那些符合或來自 lo(localhost、127.0.0.1)介面的封包。最後我們 -j(jump「跳至」)符合這條規則的目標動作:在這裡是 ACCEPT。所以這條規則會導致所有前往 localhost 介面的對內封包穫得接納。一般來說這是必須的,因為很多軟件預期能夠與 localhost 配接卡溝通。

  4. iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 這是擔負起大部份工作的規則,而我們再一次將它加進(-A)INPUT 鏈內。這裡我們利用 -m 選項來載入一個模塊(state)。state 模塊能夠檢視一個封包並判斷它的狀態是 NEW、ESTABLISHED 抑或 RELATED。NEW 指進入的封包屬於不是由主機起始的新增連線。ESTABLISHED 及 RELATED 指進入的封包隸屬於一條現存的連線,或者與現存的連線有關係。

  5. iptables -A INPUT -p tcp --dport 22 -j ACCEPT 現在我們加入一條規則來容許 SSH 通過 tcp 連接埠 22 來連線。這樣做是要防止我們連接到遠端系統的 SSH 連線意外地被封銷。我們稍後會更詳細解釋這條規則。

  6. iptables -P INPUT DROP 這個 -P 選項設定某條規則鏈上的預設政策。我們現在可以將 INPUT 鏈的預設政策改為 DROP。意思就是,不符合任何一條規則的對內封包將會被丟棄。要是我們透過 SSH 遠端連線而沒有加入上一條規則,此刻我們便會被封鎖於系統之外。

  7. iptables -P FORWARD DROP 同樣地,在這裡我們將 FORWARD 鏈的預設政策設為 DROP,因為我們並不是用電腦作為路由器,所以理應沒有任何封包路經它。

  8. iptables -P OUTPUT ACCEPT 而最後,我們將 OUTPUT 鏈的預設政策設為 ACCEPT,因為我們想容許所有對外的流量(由於我們信任我們的用戶)。

  9. 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,例如:

#!/bin/bash
#
# 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

zh-tw/HowTos/Network/IPTables (last edited 2014-09-17 03:02:51 by TimothyLee)