第五章 有狀態改進
明確關閉 ECN
我以前提到過應當關閉 ECN(明確擁塞通知),以便因特網通信可以正確工作。雖然您可能會按我的建議禁用了 ECN,但在將來您也許會忘了這樣做。或者,您可能將防火牆腳本傳送給某個人,而那個人啟用了 ECN。由於這些原因,最好使用 /proc 接口來明確禁用 ECN,如下所示:
if [ -e /proc/sys/net/ipv4/tcp_ecn ]
then
echo 0 > /proc/sys/net/ipv4/tcp_ecn
fi
轉發
如果使用 Linux 機器作為路由器,那麼應該啟用 IP 轉發,它給予內核許可權,以允許包在 eth0 和 eth1 之間傳遞,反之亦然。在我們的配置示例中,eth0 連接到 LAN,eth1 連接到因特網,在允許 LAN 經由 Linux 機器連接因特網時,啟用 IP 轉發是必要步驟。要啟用 IP 轉發,請使用以下這行命令:
echo 1 > /proc/sys/net/ipv4/ip_forward
處理拒絕,第 1 部分
目前,我們已經刪除了所有來自因特網的未經請求的通信流。雖然這是一種阻止討厭的網絡活動的有效方法,但是它有一些缺點。這種方法最大的問題是闖入者很容易就可以檢測到我們正在使用防火牆,因為我們的機器沒有應答標准 TCP 復位和 ICMP 端口不可到達響應 -- 一般機器發送會的響應,用於表示對不存在服務的連接失敗。
處理拒絕,第 2 部分
與其讓潛在的闖入者知道我們在運行防火牆(對於在提示他們,我們正在運行一些他們不能得到的有價值服務),還不如假裝我們根本沒有運行服務。通過將以下兩個規則添加到 INPUT 鏈的末端,可以成功地完成此項任務:
iptables -A INPUT -p tcp -i eth1 -j REJECT --reject-with tcp-reset
iptables -A INPUT -p udp -i eth1 -j REJECT --reject-with icmp-port-unreachable
第一個規則負責正確傳遞 TCP 連接,而第二個規則處理 UDP。只要這兩個規則就位,闖入者就很難檢測到我們運行了防火牆;但願,這會使闖入者離開我們的機器,轉而搜索其它更潛在的目標以供他濫用。
處理拒絕,第 3 部分
除了使防火牆變得更“隱蔽”,這些規則還消除了由於連接到某些 FTP 和 irc 服務器帶來的延遲。這個延遲是由於服務器對您的機器執行身份查找(連接到端口 113)而引起的,並最終(大約 15 秒之後)導致超時。現在,防火牆將返回 TCP 復位,身份查找將立即失敗,而不是重試 15 秒(而您正在耐心地等待服務器的響應)。
防止欺騙
在許多發行版中,當建成網絡接口時,還會將舊的 ipchains 規則添加到系統。這些特殊規則是由發行版的創建程序添加的,用於處理電子欺騙問題,即包的源地址已經過調整,這樣它們就包含了無效值(某些腳本騙子做的事)。雖然我們可以創建類似的 iptables 規則來阻攔受到欺騙的包,但還有一種更簡單的方法。目前,內核的內置功能可以刪除受到欺騙的包;我們要做的只是通過簡單的 /proc 接口來啟用它。方法如下。
for x in lo eth0 eth1
do
echo 1 > /proc/sys/net/ipv4/conf/${x}/rp_filter
done
此 shell 腳本將告訴內核刪除接口 lo、eth0 和 eth1 上所有受到欺騙的包。可以將這些行添加到防火牆腳本中,也可以將它們添加到創建 lo、eth0 和 eth1 接口的腳本中。
偽裝
NAT(網絡地址轉換)和 IP 偽裝雖然與防火牆沒有直接關系,但通常與防火牆一起使用。我們將討論您可能需要使用的兩種常用 NAT/偽裝配置。第一個規則負責處理那種用撥號鏈接到使用動態 IP 的因特網 (ppp0) 的情況:
iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
如果您屬於這種情況,那麼還應該轉換防火牆腳本,將對 "eth1"(我們的示例 DSL 路由器)更改成 "ppp0"。如果 ppp0 還不存在,最好添加引用 "ppp0" 的防火牆規則。只要創建了 ppp0,一切立即就會正常工作。請確保還啟用了 IP 轉發。
SNAT
如果使用 DSL 來連接因特網,那麼您或許有兩種可能配置中的一種。一種可能性是 DSL 路由器或調制解調器有其自己的 IP 號碼,並為您執行網絡地址轉換。如果是這種情況,那麼就不需要 Linux 來執行 NAT,因為 DSL 路由器已經這樣處理了。
但是,如果想要更多地控制 NAT 功能,也許應該與 ISP 討論關於 DSL 連接的配置,以便使您的 DSL 連接處於“橋接方式”。在橋接方式中,防火牆將成為 ISP 的網絡中的正式部分,DSL 路由器將會在 ISP 和您的 Linux 機器之間透明的來回轉發 IP 通信流,而不會讓任何人知道它的存在。它不再擁有 IP 號碼;事實上,eth1(在我們的示例中)隱藏了 IP。如果有人從因特網上 ping 您的 IP,他們將從您的 Linux 機器上得到應答,而不是路由器。
使用了這種設置,就應該使用 NAT(源 NAT),而不是偽裝。以下就是您應該添加到防火牆的一行代碼:
iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to 1.2.3.4
在這個示例中,應該將 eth1 更改成直接連接到 DSL 路由器的以太網接口,1.2.3.4 應該更改成靜態 IP(以太網接口的 IP)。再次聲明,請記住要啟用 IP 轉發。
NAT 問題
幸好,NAT 和偽裝與防火牆能夠和睦相處。在編寫防火牆過濾規則時,應忽略正在使用 NAT 的事實。您的規則應該根據包的“真正”源地址和目的地址接受、刪除或拒絕它們。防火牆過濾代碼能夠看到包的原始源地址,以及最終目的地址。這對我們很有用處,因為它可以讓防火牆繼續正常工作,即使我們暫時禁用了 NAT 或偽裝。
了解表
在以上的 NAT/偽裝示例中,我們將規則附加到鏈,但還做了一些略有不同的事。請注意 "-t" 選項。"-t" 選項可以讓我們指定鏈所屬的表。當省略這個選項時,缺省表將缺省為 "filter"。因此,以前所有與非 NAT 相關的命令修改 "filter" 表中的 INPUT 鏈。"filter" 表包含了所有與接收或拒絕包相關的規則,而 "nat" 表(如您假設的)包含了與網絡地址轉換相關的規則。還有其它內置 iptables 鏈,在 iptables 幫助頁面以及 Rusty 的 HOWTO(請參閱本教程結尾處的“參考資料”部分,以獲取鏈接)中詳細描述了這些鏈。
增強的腳本
現在已經討論過一些可能的增強,讓我們看一下第二種更靈活的防火牆啟動/停止腳本:
#!/bin/bash
# An enhanced stateful firewall for a workstation, laptop or router that isn
# running any network services like a web server, SMTP server, ftp server, etc.
#change this to the name of the interface that provides your "uplink"
#(connection to the Internet)
UPLINK="eth1"
#if you e a router (and thus should forward IP packets between interfaces),
#you want ROUTER="yes"; otherwise, ROUTER="no"
ROUTER="yes"
#change this next line to the static IP of your uplink interface for static SNAT, or
#"dynamic" if you have a dynamic IP. If you don need any NAT, set NAT to "" to
#disable it.
NAT="1.2.3.4"
#change this next line so it lists all your network interfaces, including lo
INTERFACES="lo eth0 eth1"
if [ "$1" = "start" ]
then
echo "Starting firewall..."
iptables -P INPUT DROP
iptables -A INPUT -i ! ${UPLINK} -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -i ${UPLINK} -j REJECT --reject-with tcp-reset
iptables -A INPUT -p udp -i ${UPLINK} -j REJECT --reject-with icmp-port-unreachable
#eXPlicitly disable ECN
if [ -e /proc/sys/net/ipv4/tcp_ecn ]
then
echo 0 > /proc/sys/net/ipv4/tcp_ecn
fi
#disable spoofing on all interfaces
for x in ${INTERFACES}
do
echo 1 > /proc/sys/net/ipv4/conf/${x}/rp_filter
done
if [ "$ROUTER" = "yes" ]
then
#we e a router of some kind, enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
if [ "$NAT" = "dynamic" ]
then
#dynamic IP address, use masquerading
echo "Enabling masquerading (dynamic ip)..."
iptables -t nat -A POSTROUTING -o ${UPLINK} -j MASQUERADE
elif [ "$NAT" != "" ]
then
#static IP, use SNAT
echo "Enabling SNAT (static ip)..."
iptables -t nat -A POSTROUTING -o ${UPLINK} -j SNAT --to ${UPIP}
fi
fi
elif [ "$1" = "stop" ]
then
echo "Stopping firewall..."
iptables -F INPUT
iptables -P INPUT ACCEPT
then
#we e a router of some kind, enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
if [ "$NAT" = "dynamic" ]
then
#dynamic IP address, use masquerading
echo "Enabling masquerading (dynamic ip)..."
iptables -t nat -A POSTROUTING -o ${UPLINK} -j MASQUERADE
elif [ "$NAT" != "" ]
then
#static IP, use SNAT
echo "Enabling SNAT (static ip)..."
iptables -t nat -A POSTROUTING -o ${UPLINK} -j SNAT --to ${UPIP}
fi
fi
elif [ "$1" = "stop" ]
then
echo "Stopping firewall..."
iptables -F INPUT
iptables -P INPUT ACCEPT