架設 NAT


當您好不容易把您的 Linux 機器連上 Internet 之後﹐別以為就只有它一台機器能上網哦。下面教您的法子是如何用一台 Linux 機器讓本地網路中所有的機器都能同時上網﹗

設定 Proxy

我們在“網路基礎”中的“火牆”那部份已經知道能做到以上要求的可以使用 proxy 。如果您只想讓其他機器到 Internet 上流覽一下網頁或透過 HTTP 下載檔案﹐無疑 proxy 是個不錯的主意﹐而且要設定這樣一個 proxy 也是非常容易的﹐您所要做的是修改 /etc/squid/squid.conf 這個檔案。雖然這個檔案看上去很長﹐但是﹐如果僅是用來做 HTTP proxy 的話﹐您要修改的句子不超過四句﹕

#第761行﹕
reference_age 5 days
#第1090行﹕
cache_mgr root
#在第1006行下面增加一行﹕
acl siyongc.domain src 192.168.0.0/255.255.255.0  # 請修改成您自己的實際設定。
#然後﹐在第1041行下面增加一行﹕
http_access allow siyongc.domain # 請修改成您自己的域名。

其實﹐只要增加最後兩句也就行了。至於其他的設定﹐請參考該檔案的說明自己進行修改﹐這裡不一一說明了。

然後執行﹕
/etc/rc.d/init.d/squid start

(或使用‘restart’﹐如果您的 squid 已經啟動了的話)
再執行 setup --> System services --> [*] squid ﹐這樣您的機器就設定好了。

然後﹐您在其他機器的流覽器上面設為使用 proxy 功能﹐將伺服器 IP 指向跑 squid 的機器﹐和將 port 改成3128 就可以了。

關於更詳細的 squid 設定﹐您可以參考﹕ 優客筆記簿

設定IP Masquerade

雖然在 Linux 上面設定一個簡單的 proxy 看來是很容易的事情﹐但畢竟使用 proxy 是非常有限制的﹐例如﹐您要玩 ICQ、要使用 telnet、要上載 FTP 、要使用外面的 POP 信箱、要閱覽新聞組或BBS、等等﹐恐怕 proxy 就未必能全部應付得來了。不過﹐如果使用 IP Masquerading 技術呢﹖幾乎是﹕您能在那台用來上網的 Linux 機器上使用的 internet 功能﹐在其他機器也一樣擁有﹗

要設定 IP Masquerade 就牽涉到火牆裝置了。老實說﹐要設定一個稱職的火牆並不是一件容易的事情﹐它要求您有非常豐富的 TCP/IP 基礎﹐和嚴密的邏輯頭腦﹐還得加上無以複加的細密測試。但是﹐我在這裡教您的﹐並不是要真正的設定一個火牆﹐只是想讓其他機器上網罷了。如果您真想設定一下火牆﹐您可以看看 Firewall、IP Masquerade 和 IPchains 這幾個 HOWTO 文章﹐或許會有些概念(但未必就設定得來)﹐看的時候要注意文章引用的版本是否和您目前機器使用的相同。

  1. 無論如何﹐第一件事情要做的是要將 IP ForwardingFirewall 功能編進核心去。如果您的核心是按照我以前的例子編的﹐應該就不成問題了。

  2. 然後﹐檢查一下 /etc/sysconfig/network 這個檔案﹐看看 ‘FORWARD_IPV4=’是否為‘yes’。如果是﹐跳到下一步﹐如果不是﹐將之改成‘yes’﹐存檔﹐然後執行 ﹕/etc/rc.d/init.d/network reload

    上面這步驟也可以用一個句子來完成﹕
    echo 1 > /proc/sys/net/ipv4/ip_forward

  3. 如果您不考慮任何安全性﹐現在執行﹕
    ipchains -P forward MASQ

  4. 然後依次執行下面的命令﹕
    /sbin/depmod -a
    /sbin/modprobe ip_masq_ftp
    /sbin/modprobe ip_masq_raudio
    /sbin/modprobe ip_masq_irc
    /sbin/modprobe ip_masq_autofw
    /sbin/modprobe ip_masq_cuseeme
    /sbin/modprobe ip_masq_portfw
    /sbin/modprobe ip_masq_quake
    /sbin/modprobe ip_masq_vdolive
    /sbin/modprobe ip_masq_user
    /sbin/modprobe ip_masq_mfw

    (注意﹕您得確定上面那些 modules 已經出現在 /lib/modules/`uname -r`/ipv4/ 這個目錄裡面﹐否則按“編譯核心”的例子重編您的 kernel。)

  5. 然後﹐將其他的機器的 Default Gateway 指向執行 ipchains 的 Linux 主機﹐就可以上網了﹗

還用不用 proxy 完全是由您自己決定。當然了﹐您或許會覺得繼續使用 proxy 仍不失為一個好主意﹐尤其是當您考量到頻寬問題時﹐甚至您還認為強迫使用者一定要使用 proxy 才可以看 www 網站。那這時候﹐您可以考慮建立一個 transparent proxy 了﹕

  1. 首先您確定您的核心已被編譯成為可以支援 transparent proxy。

  2. 然後﹐修改 /etc/squid/squid.conf﹐將這個行修改成這樣子﹕
    httpd_accel_host virtual
    httpd_accel_port 80
    httpd_accel_with_proxy on
    httpd_accel_uses_host_header on
    

  3. 然後執行﹕
    /sbin/ipchains -A input -p TCP -d 0/0 --dport 80 -j REDIRECT 3128

  4. 最好重新啟動 squid ﹕
    service squid stop
    service squid start

    您要留意的是﹐如果您有在這台伺服器上面跑 http 服務的話﹐可能要略為安排一下 ipchains 的順序了﹐這個等您看完後面的文章自然曉得。

您看﹐一點也不複雜啦﹗但是﹐這樣的一個設定可以說是完全開放的﹐毫無保安可言。雖然我在這裡並不想設定一個真正火牆﹐但起碼的安全也是應該考慮的。所以﹐我建議您將剛才的‘ipchains -P forward MASQ’命令改成下面的句子﹕
ipchains -P forward DENY
ipchains -A forward -j MASQ -s 192.168.0.0/24 -d 0.0.0.0/0
ipchains -A forward -j MASQ -s 203.30.35.128/27 -d 0.0.0.0/0
ipchains -A forward -j MASQ -s 192.168.168.0/24 -d 0.0.0.0/0

這樣﹐我就只限制了三個網路的主機可以使用 IP Masquerading 透過我的火牆﹐而其他都會被 DENY 掉。您必須針對您自己的實際網路修改上面的句子。

當您測試成功之後﹐將上面的兩組句子放進 /etc/rc.d/rc.local 就可以了。那樣﹐只要您的 Linux 主機上網的話﹐其他機器也同時上網了。但使用 IP Masquerading 有個問題是﹐在外面看來﹐所有主機使用的 IP 都只有一個﹕就是通過 ppp 界面從 ISP 那裡得到的 IP。如果您要在其他機器上跑一些 internet 服務的話﹐恐怕就不那麼容易做到了。這時候﹐您可以參考 ipchains 的 HOWTO 文章中第七章(A Serious Example)來設定一個 DMZ (Demilitarized Zone)﹐但在設定上要很有技巧才行。

設定一個簡單的火牆

好了﹐您用上面的方法基本上已經能滿足到您的上網要求了。不過﹐如果您覺得還應該更好保護自己的網路﹐可以自己寫一個類似下面的 script ﹐然後等撥接成功後再執行。雖然還是挺簡單的﹐但也應該難倒一般的駭客了吧﹕
#!/bin/bash
#
# A simple script for masquerading, used in Linux.
# Date: 2000/10/06
#
# Copyleft 2000 by netman (netman@study-area.net).
#
# Redistribution of this file is permitted under the terms of 
# the GNU General Public License (GPL)      
#
PATH=/sbin:/usr/sbin:/bin:/usr/bin
netinfo ()
{
IP=""
MASK=""
NET=""

for NIC in "$@" ; do {

	IP=`ifconfig $NIC |grep 'inet addr' |awk '{print $2}'|sed -e "s/addr\://"` 
	MASK=`ifconfig $NIC |grep 'inet addr' |awk '{print $4}'|sed -e "s/Mask\://"`
	IP1=`echo $IP |awk -F'.' '{print $1}'`
	if [ "$IP1" = "" ]; then
		echo ""
		echo "Warning: there is no IP found on $NIC."
		echo "Action is aborted."
		echo "Please make sure the interface is setup properly, then try again."
		echo ""
		exit 1
	else
	IP2=`echo $IP |awk -F'.' '{print $2}'`
	IP3=`echo $IP |awk -F'.' '{print $3}'`
	IP4=`echo $IP |awk -F'.' '{print $4}'`
	MASK1=`echo $MASK |awk -F'.' '{print $1}'`
	MASK2=`echo $MASK |awk -F'.' '{print $2}'`
	MASK3=`echo $MASK |awk -F'.' '{print $3}'`
	MASK4=`echo $MASK |awk -F'.' '{print $4}'`
	let NET1="$IP1 & $MASK1"
	let NET2="$IP2 & $MASK2"
	let NET3="$IP3 & $MASK3"
	let NET4="$IP4 & $MASK4"
	NET="$NET1.$NET2.$NET3.$NET4"
	fi
	}
done
}

# --- Define interfaces ---
HI="1024:65535"
ALL="0.0.0.0/0"
LO="127.0.0.1"
INT_IF="eth0"
EXT_IF="eth1"

##
##注﹕以上界面請針對您實際情形修改。
##

# --- Determine network infortion ---
netinfo "$INT_IF" 
INT_IP="$IP"
INT_NET="$NET"/"$MASK"

netinfo "$EXT_IF"
EXT_IP="$IP"
EXT_NET="$NET"/"$MASK"

#
#
# -------------啟動封包轉遞-----------
echo "Turning on IP forwarding..."
echo 1 > /proc/sys/net/ipv4/ip_forward

#
#
# -------------啟動偽裝模組-----------
echo "Turning on MASQ modules..."
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_ftp.o ]; then
	modprobe ip_masq_ftp
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_raudio.o ]; then
	modprobe ip_masq_raudio
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_irc.o ]; then
	modprobe ip_masq_irc
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_autofw.o ]; then
	modprobe ip_masq_autofw
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_cuseeme.o ]; then
	modprobe ip_masq_cuseeme
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_portfw.o ]; then
	modprobe ip_masq_portfw
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_quake.o ]; then
	modprobe ip_masq_quake
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_vdolive.o ]; then
	modprobe ip_masq_vdolive
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_user.o ]; then
	modprobe ip_masq_user
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_mfw.o ]; then
	modprobe ip_masq_mfw
fi
#
# -------------清理 ipchains----------
echo "Cleaning up rules..."
ipchains -F
ipchains -X
#
# -------------全部 DENY -------------
echo "Deny all..."
ipchains -P input   DENY
ipchains -P output  DENY
ipchains -P forward DENY
#
# -------------允許本地網路連線 -------------
echo "Allow local network..."
ipchains -A input  -i lo -j ACCEPT
ipchains -A output -i lo -j ACCEPT
ipchains -A input  -i $INT_IF -j ACCEPT
ipchains -A output -i $INT_IF -j ACCEPT
#
# ------------- 防止 IP 詐騙 -------------
echo "Turning on anti-spoofing..."
ipchains -A input  -j DENY -i $EXT_IF -s $EXT_IP -d $ALL
ipchains -A input  -j DENY -i $EXT_IF -s $INT_NET -d $ALL
ipchains -A output -j DENY -i $EXT_IF -s $INT_NET -d $ALL
#
#------------- 設定常用協定及優化處理 -------------
echo "Allow common protocols and optimizing..."
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL telnet -t 0x01 0x10
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL www -t 0x01 0x10
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL https -t 0x01 0x10
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL ftp -t 0x01 0x10
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL ftp-data -t 0x01 0x08
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL nntp  -t 0x01 0x02
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL pop-3  -t 0x01 0x02
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL imap  -t 0x01 0x02
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL smtp  -t 0x01 0x02
#
# ------如果您使用 SSH 的話﹐考慮開放下面句子 -------------
#echo "Allow SSH..."
#ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL ssh -t 0x01 0x10
#ipchains -A output -p UDP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL ssh -t 0x01 0x10
#ipchains -A input -p TCP -j ACCEPT -i $EXT_IF ! -y -s $ALL ssh -d $EXT_IP -t 0x01 0x10
#ipchains -A input -p UDP -j ACCEPT -i $EXT_IF -s $ALL ssh -d $EXT_IP -t 0x01 0x10
#
# -------------只允許回應封包進入 -------------
echo "Accept turn-back only..." 
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF ! -y -s $ALL -d $EXT_IP
#
# -------------開放 DNS -------------
echo "Allow DNS..."
ipchains -A output -p UDP -j ACCEPT -i $EXT_IF -s $EXT_IP --sport $HI -d $ALL domain
ipchains -A input -p UDP -j ACCEPT -i $EXT_IF -s $ALL domain -d $EXT_IP --dport $HI
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP --sport $HI -d $ALL domain
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF ! -y -s $ALL domain -d $EXT_IP --dport $HI
ipchains -A output -p UDP -j ACCEPT -i $EXT_IF -s $EXT_IP domain -d $ALL domain
ipchains -A input -p UDP -j ACCEPT -i $EXT_IF -s $ALL domain -d $EXT_IP domain
#
#-------------允許 ICMP -------------
echo "Allow ICMP..."
ipchains -A output -p ICMP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL
ipchains -A input -p ICMP -j ACCEPT -i $EXT_IF -s $ALL -d $EXT_IP
#
#-------------允許 FTP -------------
echo "Allow FTP..."
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP --sport $HI -d $ALL ftp
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF -s $ALL ftp -d $EXT_IP --dport $HI
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF -s $ALL ftp-data -d $EXT_IP --dport $HI
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP --sport $HI -d $ALL --dport $HI
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF ! -y -s $ALL --sport $HI -d $EXT_IP --dport $HI
#
#-------------允許 AUTH -------------
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $ALL auth -d $ALL 
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF -s $ALL -d $EXT_IP auth
#
#-------------允許 ICQ -------------
echo "Allow ICQ..."
ipchains -A output -p UDP -j ACCEPT -i $EXT_IF -s $EXT_IP/32 --sport $HI -d $ALL 4000 
ipchains -A input -p UDP -j ACCEPT -i $EXT_IF -s $ALL 4000 -d $EXT_IP/32 --dport $HI
#
# -------------設定 Masquerade -------------
echo "Turning on MASQ..."
ipchains -A forward -j MASQ -s $INT_NET -d $ALL
#
#
echo "Current firewall status:"
echo -n "/proc/sys/net/ipv4/ip_forward: "
cat /proc/sys/net/ipv4/ip_forward
echo INT_NET is "$INT_NET" on "$INT_IF" with "$INT_IP"
echo EXT_NET is "$EXT_NET" on "$EXT_IF" with "$EXT_IP"
#

不過﹐由此引起的安全漏洞您就得慢慢修補了。

上面的 script 可以點這裡獲得

架設 NAT

當您會得使用 ipchains 來實現 IP 偽裝和為網路建立簡單的防範之後﹐您應該利用 Linux 來為您做更多的事情﹕其中有一個非常好的功能叫 NAT (Network Address Translating) 。使用 NAT有什麼好處呢﹖

首先﹐如果您當初架設的網路使用了私有位址﹐而現在又想連上 internet ﹐雖然您可以對您的網路重新分配 IP 地址﹐但這樣做是非常費心和隱藏的問題多多。這時候﹐ NAT 就可以幫到您了﹕您只需用一台 Linux 主機安裝兩片網路卡﹐一張使用合法 IP 地址來擔當對外地連線﹐另外一張使用私有 IP 負責和內部網路溝通。當然﹐這台 Linux 主機可以同為上面介紹的負責 IP masquerading 和 fiewalling 的主機。

然後﹐您為那張對外連線的網卡建立多個 IP alias ﹐當收到傳給那些 IP alias 的請求的時候﹐可以把這些請求通過 NAT 轉送給內部真正負責網路服務的伺服器﹐而那些伺服器根本無需更改 IP 地址﹐依舊使用原來的私有 IP 就可以。

使用 NAT 還有一個好處是﹐可以分流網路負擔 (load balancing) ﹐也就是說﹐您可以將同一個 IP alias 的請求分別導向好幾台執行相同服務的伺服器。這樣對於處理大量數據的服務是非常好用的。

好了﹐說了這麼多﹐您是否已經非常蠢蠢欲動了呢﹖且慢﹗要使用到 NAT 您還需要另外一個程式﹕ ipmasqadm ﹐但是 Redhat 6.0 光碟本身卻沒有自帶這個程式。那麼﹐您得先到 http://rufus.w3.org/linux/RPM/點這裡 將 ipmasqadm 的 RPM 下載回來﹐再進行安裝(這個不用教了吧﹖)才可以使用哦。

當您完成了 ipmasqadm 的安裝之後﹐您可以寫一個 script 來啟動 NAT 的功能。在下面的例子中﹐我的 Linux 主機是唯一連接外界的橋樑﹐它有三個網路界面﹕一個連接 internet ﹐雖然我使用 ppp 來測試﹐但相信也適用於 ADSL 和 Cable Modem ﹔另一個界面連接 DMZ 網路﹐我將提供 internet 服務的主機放在一個所謂的 DMZ 網路﹔而最後一個界面則連接私有網路﹐我將私有網路簡單的整個給 masquerade 掉﹐裡面完全不提供任何 internet 服務﹐但私有網路可以直接連接 DMZ 網路上面的服務主機。如圖所示﹕

參考上圖﹐這裡使用的網路環境或許和真實的環境有所不同。有一個地方一定要注意﹕您必須確定 routing 是正確的。在設定 DMZ 最容易犯的錯誤是使用和外部網路一樣的 Net_ID﹐這樣的話﹐無論內部網路還是外部網路﹐都無法和 DMZ 溝通的。如果可以的話﹐用另外一個私有網路來經營 DMZ ﹐然後用 ipmasquerading 技術讓它和外面溝通(也就是下面這個 script 的功能)。

假如您的 DMZ 使用另一個真實 IP 且可以直接和外面路由的話。那麼﹐下面的 script 將不適用﹐您必須將 ipchains 中關於 DMZ 的 forward 部份改為 ACCEPT 而非 MASQ﹐且不要做任何的 ipmasqadm 動作。請一定要留意﹗

NAT Script:
#!/bin/bash
#
# A simple script for NAT, used in Linux.
# Date: 2001/04/03
#
# Copyleft 2000 by netman (netman@study-area.net).
#
# Redistribution of this file is permitted under the terms of
# the GNU General Public License (GPL)      
#
PATH=/sbin:/usr/sbin:/bin:/usr/bin
netinfo ()
{
IP=""
MASK=""
NET=""

for NIC in "$@" ; do
	{
	IP=`ifconfig $NIC |grep 'inet addr' |awk '{print $2}'|sed -e "s/addr\://"` 
	MASK=`ifconfig $NIC |grep 'inet addr' |awk '{print $4}'|sed -e "s/Mask\://"`
	IP1=`echo $IP |awk -F'.' '{print $1}'`
	if [ "$IP1" = "" ]; then
		echo ""
		echo "Warning: there is no IP found on $NIC."
		echo "Action is aborted."
		echo "Please make sure the interface is setup properly, then try again."
		echo ""
		exit 1
	else
	IP2=`echo $IP |awk -F'.' '{print $2}'`
	IP3=`echo $IP |awk -F'.' '{print $3}'`
	IP4=`echo $IP |awk -F'.' '{print $4}'`
	MASK1=`echo $MASK |awk -F'.' '{print $1}'`
	MASK2=`echo $MASK |awk -F'.' '{print $2}'`
	MASK3=`echo $MASK |awk -F'.' '{print $3}'`
	MASK4=`echo $MASK |awk -F'.' '{print $4}'`
	let NET1="$IP1 & $MASK1" 
	let NET2="$IP2 & $MASK2"
	let NET3="$IP3 & $MASK3"
	let NET4="$IP4 & $MASK4"
	NET="$NET1.$NET2.$NET3.$NET4"
	fi
	}
done
}

# --- Define interfaces ---
HI="1024:65535"
ALL="0.0.0.0/0"
LO="127.0.0.1"
INT_IF="eth0"
DMZ_IF="eth2"
EXT_IF="eth1"


# --- determine network infortion ---
netinfo "$INT_IF" 
INT_IP="$IP"
INT_NET="$NET"/"$MASK"

netinfo "$DMZ_IF"
DMZ_IP="$IP"
DMZ_NET="$NET"/"$MASK"

netinfo "$EXT_IF"
EXT_IP="$IP"
EXT_NET="$NET"/"$MASK"

#
# --- Define Service IPs ---
DMZ_WWW_IP="192.168.0.200"
DMZ_DNS_IP="192.168.0.200"
DMZ_FTP_IP="192.168.0.200"
DMZ_SMTP_IP="192.168.0.200"
DMZ_POP_IP="192.168.0.200"
DMZ_IMAP_IP="192.168.0.200"

EXT_WWW_IP="$EXT_IP"
EXT_DNS_IP="$EXT_IP"
EXT_FTP_IP="$EXT_IP"
EXT_SMTP_IP="$EXT_IP"
EXT_POP_IP="$EXT_IP"
EXT_IMAP_IP="$EXT_IP"

##
##注﹕如果使用 IP Alias 作為對外服務 IP 的話﹐
##注﹕請將 $EXT_IP 改為相對的 IP。
##

#
#
echo "Starting NAT script..."
#
#++++++++++++++++++++++++++++++
#-------------- 配置 ipmasqadm ------------
#++++++++++++++++++++++++++++++
echo '-------------------------------'
echo "Configuring ipmasqadm..."
#
# ------------重新設定----------
ipmasqadm portfw -f
ipmasqadm autofw -F
#
#
# ------------轉遞 DNS 請求------------
echo "Forwarding DNS requests..."
ipmasqadm portfw -a -P tcp -L $EXT_DNS_IP 53 -R $DMZ_DNS_IP 53
ipmasqadm portfw -a -P udp -L $EXT_DNS_IP 53 -R $DMZ_DNS_IP 53 
ipmasqadm portfw -a -P tcp -L $EXT_DNS_IP 42 -R $DMZ_DNS_IP 42
#
# ------------轉遞 WWW 請求------------
echo "Forwarding WWW requests..."
ipmasqadm portfw -a -P tcp -L $EXT_WWW_IP 80 -R $DMZ_WWW_IP 80
ipmasqadm portfw -a -P udp -L $EXT_WWW_IP 80 -R $DMZ_WWW_IP 80
ipmasqadm portfw -a -P tcp -L $EXT_WWW_IP 443 -R $DMZ_WWW_IP 443
ipmasqadm portfw -a -P udp -L $EXT_WWW_IP 443 -R $DMZ_WWW_IP 443
#
# ------------轉遞 FTP 請求------------
echo "Forwarding FTP requests..."
ipmasqadm portfw -a -P tcp -L $EXT_FTP_IP 20 -R $DMZ_FTP_IP 20
ipmasqadm portfw -a -P tcp -L $EXT_FTP_IP 21 -R $DMZ_FTP_IP 21
#
# ------------轉遞 MAIL 請求------------
echo "Forwarding MAIL requests..."
ipmasqadm portfw -a -P tcp -L $EXT_SMTP_IP 25 -R $DMZ_SMTP_IP 25
ipmasqadm portfw -a -P tcp -L $EXT_POP_IP 109 -R $DMZ_POP_IP 109
ipmasqadm portfw -a -P udp -L $EXT_POP_IP 109 -R $DMZ_POP_IP 109
ipmasqadm portfw -a -P tcp -L $EXT_POP_IP 110 -R $DMZ_POP_IP 110
ipmasqadm portfw -a -P udp -L $EXT_POP_IP 110 -R $DMZ_POP_IP 110
ipmasqadm portfw -a -P tcp -L $EXT_IMAP_IP 143 -R $DMZ_IMAP_IP 143
ipmasqadm portfw -a -P udp -L $EXT_IMAP_IP 143 -R $DMZ_IMAP_IP 143
ipmasqadm portfw -a -P tcp -L $EXT_IMAP_IP 220 -R $DMZ_IMAP_IP 220
ipmasqadm portfw -a -P udp -L $EXT_IMAP_IP 220 -R $DMZ_IMAP_IP 220
#
#
#
# ======為某些特定程式啟動 auto-forward 功能=====
echo "Turing on AUTO-FORWARD for some special programs..."
#
#------------RealAudio--------
ipmasqadm autofw -A -r udp 6970 7170 -c tcp 7070
ipmasqadm autofw -A -r udp 6970 7170 -c tcp 7075
#
#------------Internet Phone--------
ipmasqadm autofw -A -r udp 22555 22566 -h 192.168.0.15 
ipmasqadm autofw -A -r tcp 25793 25804 -h 192.168.0.15 
ipmasqadm autofw -A -r tcp 1490 1501 -h 192.168.0.15
#
#------------PowWow--------
ipmasqadm autofw -A -r tcp 13223 13223 -c tcp 13223 -u 
ipmasqadm autofw -A -r udp 13223 13223 -c tcp 13223 -u 
ipmasqadm autofw -A -r tcp 23213 23213 -c tcp 13223 -u
#
#------------WebPhone--------
ipmasqadm autofw -A -p tcp 21845 192.168.0.15:21845 
ipmasqadm autofw -A -p udp 21845 192.168.0.15:21845
#
#
#
#
#++++++++++++++++++++++++++++++
#-------------- 配置 ipchains ------------
#++++++++++++++++++++++++++++++
echo '-------------------------------'
echo "Turning on IP forwarding..."
echo 1 > /proc/sys/net/ipv4/ip_forward
#
# -------------清理 ipchains----------
echo "Cleaning up rules..."
ipchains -F
ipchains -X
#
# -------------全部 DENY -------------
echo "Deny all..."
ipchains -P input   DENY
ipchains -P output  DENY
ipchains -P forward DENY
#
# -------------允許本地網路連線 -------------
echo "Allow local network..."
ipchains -A input -i lo -j ACCEPT
ipchains -A output -i lo -j ACCEPT
ipchains -A input -i $INT_IF -j ACCEPT
ipchains -A output -i $INT_IF -j ACCEPT
ipchains -A input -i $DMZ_IF -j ACCEPT
ipchains -A output -i $DMZ_IF -j ACCEPT
#
# ------------- 防止 IP 詐騙 -------------
echo "Turning on anti-spoofing..."
ipchains -A input  -i $EXT_IF -s $EXT_IP -d $ALL -j DENY
ipchains -A input  -i $EXT_IF -s $DMZ_NET -d $ALL -j DENY
ipchains -A output -i $EXT_IF -s $DMZ_NET -d $ALL -j DENY
ipchains -A input  -i $EXT_IF -s $INT_NET -d $ALL -j DENY
ipchains -A output -i $EXT_IF -s $INT_NET -d $ALL -j DENY
#
# -------------允許回應封包進入 -------------
echo "Accept outbound requests only..." 
ipchains -A input -p TCP -i $EXT_IF ! -y -s $ALL -j ACCEPT
#
#-------------允許 ICMP -------------
echo "Allow ICMP..."
ipchains -A output -i $EXT_IF -p ICMP -d $ALL -j ACCEPT
ipchains -A input -i $EXT_IF -p ICMP -s $ALL -j ACCEPT
ipchains -A forward -p ICMP -s $DMZ_NET -j MASQ
ipchains -A forward -p ICMP -d $DMZ_NET -j DENY 
#
#-------------允許 DNS -------------
echo "Accept DNS..."
ipchains -A input -i $EXT_IF -p UDP -s $ALL --sport $HI -d $EXT_DNS_IP/32 domain -j ACCEPT
ipchains -A output -i $EXT_IF -p UDP -s $EXT_DNS_IP/32 domain -d $ALL --dport $HI -j ACCEPT
ipchains -A input -i $EXT_IF -p TCP -s $ALL --sport $HI -d $EXT_DNS_IP/32 domain -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_DNS_IP/32 domain -d $ALL --dport $HI -j ACCEPT
ipchains -A output -i $EXT_IF -p UDP -s $EXT_DNS_IP/32 --sport $HI -d $ALL domain -j ACCEPT
ipchains -A input -i $EXT_IF -p UDP -s $ALL domain -d $EXT_DNS_IP/32 --dport $HI -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_DNS_IP/32 --sport $HI -d $ALL domain -j ACCEPT
ipchains -A input -i $EXT_IF -p TCP -s $ALL domain -d $EXT_DNS_IP/32 --dport $HI -j ACCEPT
ipchains -A output -i $EXT_IF -p UDP -s $EXT_DNS_IP/32 domain -d $ALL domain -j ACCEPT
ipchains -A input -i $EXT_IF -p UDP -s $ALL domain -d $EXT_DNS_IP/32 domain -j ACCEPT
ipchains -A forward -s $DMZ_DNS_IP/32 -d $ALL -j MASQ
#
#-------------允許 HTTP -------------
echo "Accept HTTP..."
ipchains -A input -i $EXT_IF -p TCP -s $ALL --sport $HI -d $EXT_WWW_IP/32 www -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_WWW_IP/32 www -d $ALL --dport $HI -j ACCEPT
ipchains -A input -i $EXT_IF -p UDP -s $ALL --sport $HI -d $EXT_WWW_IP/32 www -j ACCEPT
ipchains -A output -i $EXT_IF -p UDP -s $EXT_WWW_IP/32 www -d $ALL --dport $HI -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_WWW_IP/32 --sport $HI -d $ALL www -j ACCEPT
ipchains -A input -i $EXT_IF -p TCP -s $ALL www -d $EXT_WWW_IP/32 --dport $HI ! -y -j ACCEPT
ipchains -A output -i $EXT_IF -p UDP -s $EXT_WWW_IP/32 --sport $HI -d $ALL www -j ACCEPT
ipchains -A input -i $EXT_IF -p UDP -s $ALL www -d $EXT_WWW_IP/32 --dport $HI -j ACCEPT
ipchains -A forward -s $DMZ_WWW_IP/32 -d $ALL -j MASQ
#
#-------------允許 FTP -------------
echo "Accept FTP..."
ipchains -A input -i $EXT_IF -p TCP -s $ALL --sport $HI -d $EXT_FTP_IP/32 ftp -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_FTP_IP/32 ftp -d $ALL --dport $HI -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_FTP_IP/32 ftp-data -d $ALL $HI -j ACCEPT
ipchains -A input -i $EXT_IF -p TCP -s $ALL --sport $HI -d $EXT_FTP_IP/32 ftp-data -j ACCEPT
ipchains -A input -i $EXT_IF -p TCP -s $ALL --sport $HI -d $EXT_FTP_IP/32 --dport 6000:6003 -j DENY
ipchains -A input -i $EXT_IF -p TCP -s $ALL --sport $HI -d $EXT_FTP_IP/32 --dport $HI -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_FTP_IP/32 --sport $HI -d $ALL --dport $HI -j ACCEPT
ipchains -A forward -s $DMZ_FTP_IP/32 -d $ALL -j MASQ
#-------------允許 MAIL -------------
echo "Accept MAIL ..."
#--- smtp ---
ipchains -A input -i $EXT_IF -p TCP -s $ALL --sport $HI -d $EXT_SMTP_IP/32 smtp -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_SMTP_IP/32 smtp -d $ALL --dport $HI ! -y -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_SMTP_IP/32 --sport $HI -d $ALL smtp -j ACCEPT
ipchains -A input -i $EXT_IF -p TCP -s $ALL smtp -d $EXT_SMTP_IP/32 --dport $HI ! -y -j ACCEPT
ipchains -A forward -p TCP -s $DMZ_SMTP_IP/32 -d $ALL -j MASQ
#-- pop3 --
ipchains -A input -i $EXT_IF -p TCP -s $ALL --sport $HI -d $EXT_POP_IP/32 pop-3 -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_POP_IP/32 pop-3 -d $ALL --dport $HI ! -y -j ACCEPT
ipchains -A input -i $EXT_IF -p UDP -s $ALL --sport $HI -d $EXT_POP_IP/32 pop-3 -j ACCEPT
ipchains -A output -i $EXT_IF -p UDP -s $EXT_POP_IP/32 pop-3 -d $ALL --dport $HI -j ACCEPT
ipchains -A forward -p TCP -s $DMZ_POP_IP/32 -d $ALL -j MASQ
#-- imap3 --
ipchains -A input -i $EXT_IF -p TCP -s $ALL --sport $HI -d $EXT_IMAP_IP/32 imap3 -j ACCEPT
ipchains -A output -i $EXT_IF -p TCP -s $EXT_IMAP_IP/32 imap3 -d $ALL --dport $HI -j ACCEPT
ipchains -A input -i $EXT_IF -p UDP -s $ALL --sport $HI -d $EXT_IMAP_IP/32 imap3 -j ACCEPT
ipchains -A output -i $EXT_IF -p UDP -s $EXT_IMAP_IP/32 imap3 -d $ALL --dport $HI -j ACCEPT
ipchains -A forward -p TCP -s $DMZ_IMAP_IP/32 -d $ALL -j MASQ
#
#
#++++++++++++++++++++++++++++++
#------------- 偽裝內部網路 -------------
#++++++++++++++++++++++++++++++
echo '-------------------------------'
echo "Masquerading internal network..."
#
#------------- 打開 MASQ 模組 -------------
echo "Turning on MASQ modules..."
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_ftp.o ]; then
	modprobe ip_masq_ftp
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_raudio.o ]; then
	modprobe ip_masq_raudio
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_irc.o ]; then
	modprobe ip_masq_irc
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_autofw.o ]; then
	modprobe ip_masq_autofw
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_cuseeme.o ]; then
	modprobe ip_masq_cuseeme
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_portfw.o ]; then
	modprobe ip_masq_portfw
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_quake.o ]; then
	modprobe ip_masq_quake
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_vdolive.o ]; then
	modprobe ip_masq_vdolive
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_user.o ]; then
	modprobe ip_masq_user
fi
if [ -f /lib/modules/$KVERSION/ipv4/ip_masq_mfw.o ]; then
	modprobe ip_masq_mfw
fi
#
#------------- 設定常用協定及優化處理 -------------
echo "Allow common protocols and optimizing..."
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL telnet -t 0x01 0x10
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL www -t 0x01 0x10
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL https -t 0x01 0x10
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL ftp -t 0x01 0x10
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL ftp-data -t 0x01 0x08
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL nntp  -t 0x01 0x02
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL pop-3  -t 0x01 0x02
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL imap  -t 0x01 0x02
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL smtp  -t 0x01 0x02
#
# ------如果您使用 SSH 的話﹐考慮開放下面句子 -------------
#echo "Allow SSH..."
#ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL ssh -t 0x01 0x10
#ipchains -A output -p UDP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL ssh -t 0x01 0x10
#ipchains -A input -p TCP -j ACCEPT -i $EXT_IF ! -y -s $ALL ssh -d $EXT_IP -t 0x01 0x10
#ipchains -A input -p UDP -j ACCEPT -i $EXT_IF -s $ALL ssh -d $EXT_IP -t 0x01 0x10
#
# -------------只允許回應封包進入 -------------
echo "Accept turn-back only..." 
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF ! -y -s $ALL -d $EXT_IP
#
# -------------開放 DNS -------------
echo "Allow DNS..."
ipchains -A output -p UDP -j ACCEPT -i $EXT_IF -s $EXT_IP --sport $HI -d $ALL domain
ipchains -A input -p UDP -j ACCEPT -i $EXT_IF -s $ALL domain -d $EXT_IP --dport $HI
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP --sport $HI -d $ALL domain
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF ! -y -s $ALL domain -d $EXT_IP --dport $HI
ipchains -A output -p UDP -j ACCEPT -i $EXT_IF -s $EXT_IP domain -d $ALL domain
ipchains -A input -p UDP -j ACCEPT -i $EXT_IF -s $ALL domain -d $EXT_IP domain
#
#-------------允許 ICMP -------------
echo "Allow ICMP..."
ipchains -A output -p ICMP -j ACCEPT -i $EXT_IF -s $EXT_IP -d $ALL
ipchains -A input -p ICMP -j ACCEPT -i $EXT_IF -s $ALL -d $EXT_IP
ipchains -A forward -p ICMP -j MASQ -s $INT_NET
ipchains -A forward -p ICMP -j MASQ -s $DMZ_NET
ipchains -A forward -p ICMP -j DENY -d $INT_NET
ipchains -A forward -p ICMP -j DENY -d $DMZ_NET
#
#-------------允許 FTP -------------
echo "Allow FTP..."
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP --sport $HI -d $ALL ftp
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF -s $ALL ftp -d $EXT_IP --dport $HI
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF -s $ALL ftp-data -d $EXT_IP --dport $HI
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -s $EXT_IP --sport $HI -d $ALL --dport $HI
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF ! -y -s $ALL --sport $HI -d $EXT_IP --dport $HI
#
#-------------允許 AUTH -------------
# auth protocol may be needed for both FTP and NNTP
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -b -s $EXT_IP -d $ALL auth 
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF -b -s $ALL auth -d $EXT_IP
ipchains -A output -p TCP -j ACCEPT -b -i $EXT_IF -d $ALL auth 
ipchains -A input -p TCP -j ACCEPT -b -i $EXT_IF -s $ALL auth
#
#-------------允許 ICQ -------------
echo "Allow ICQ..."
ipchains -A output -p UDP -j ACCEPT -i $EXT_IF -s $EXT_IP/32 --sport $HI -d $ALL 4000 
ipchains -A input -p UDP -j ACCEPT -i $EXT_IF -s $ALL 4000 -d $EXT_IP/32 --dport $HI
#
#
# -------------設定 Masquerade -------------
echo "Turning on MASQ..."
ipchains -A forward -j MASQ -s $INT_NET -d $ALL
ipchains -A forward -j MASQ -s $DMZ_NET -d $ALL
#
#------------- 開啟高端------------
#echo "Allow high ports..."
#ipchains -A input -p UDP -i $EXT_IF -b -s $ALL -d $EXT_IP --dport $HI -j ACCEPT
#ipchains -A output -p UDP -i $EXT_IF -b -s $EXT_IP -d $ALL --dport $HI -j ACCEPT
# -- 注意﹕開啟高端會減低安全性能﹐如非不得已勿試﹗ --
#
echo "Current firewall status:"
echo -n "/proc/sys/net/ipv4/ip_forward: "
cat /proc/sys/net/ipv4/ip_forward
echo INT_NET is "$INT_NET" on "$INT_IF" with "$INT_IP"
echo DMZ_NET is "$DMZ_NET" on "$DMZ_IF" with "$DMZ_IP"
echo EXT_NET is "$EXT_NET" on "$EXT_IF" with "$EXT_IP"
#

上面的 script 可以點這裡下載

然而﹐無論您怎樣使用前面兩個 script ﹐最新、最好用的 fw script 則 點這裡 下載﹐我建議您用這隻 script 來設定您的 NAT 伺服器。這隻 script 讓您有更大的設計空間﹐而且使用上也非常方便﹐建議先用 fw help 看看有什麼選擇。不過﹐如果您要修改它以符合您實際的需求﹐我建議您先從前面兩個 script 著手﹐這樣您才會知道要修改哪裡﹐和如何修改。

附註﹕ 我必須強調的是以上提供的範例僅僅是例子。如果您在沒有首先花時間了解您自身的需要、您的環境、以及您想要之服務的含義和複雜性﹐請勿盲目地運用這些例子。您必須認知到那些例子並非很完善﹐它們仍然需要修正。

一些設定技巧

下面就淺略的談一談設定 ipchains 的一些規則和注意的地方。

當我們設定一個 ipchains 的時候﹐我們要告訴系統如何處理一個封包(ACCEPT、或 DENY、或 MASQ、其它)﹐可以通過下面數個元素的組合來決定﹕

  • 傳遞方向 (input / output / forward)
  • 界面 (-i)
  • 協定 (-p)
  • 連線類型 (-y)
  • 來源 (-s)
  • 來源埠口 (--sport)
  • 目的地 (-d)
  • 目的地埠口 (--dport)

如果指定界面 (-i )﹐所謂 input 就是從該界面傳入主機的封包﹐所謂 output 就是從該界面傳出去的封包。如果沒有指定界面﹐則泛指從所有界面 傳入(input) 或 傳出(output) 的封包。而 forward 的動作﹐則是從一個界面進入後﹐再將封包從另外一個界面送出去。如果只有進入﹐沒有送出﹐就算封包到達另一個界面那端﹐也不可以算是一個 forward 動作。另外﹐在每一個封包中﹐都有一個 來源(source) 地址和 目的地(destination) 地址 ﹐ -s 就是來源﹐ -d 就是目的地。如果您這樣寫﹕
ipchains -A input -i eth0 -p TCP -s xxx.xxx.xxx.xxx -d yyy.yyy.yyy.yyy -j ACCEPT

就是說﹕所有從 eth0 傳入的 TCP 封包﹐如果是從 xxx.xxx.xxx.xxx 傳給 yyy.yyy.yyy.yyy的話﹐一律給予接受( -j ACCEPT)。因為我們是用 -A (Append)來增加這行規則﹐它會加在 input 規則的最後一行。注意﹐yyy.yyy.yyy.yyy 未必就是 NAT 本身的地址﹐封包在路由過程中的地址是不會變的﹐除非您經過了 MASQ 或其它轉址處理過。假如您想做出更嚴格的限制﹐還可以增加一些元素進去﹕
ipchains -A input -i eth0 -p TCP -s xxx.xxx.xxx.xxx --sport 1024:65535 -d yyy.yyy.yyy.yyy www -j ACCEPT
(以上為一整行)

這樣的話﹐只有封包是從 xxx.xxx.xxx.xxx 這個客戶端(一般客戶端都會用大於 1024 的埠口來建立連線的)﹐傳給 yyy.yyy.yyy.yyy 這台網頁伺服器的 port 80 (也就是 www)﹐才予以放行。您會發現﹐如果您不特定設定元素﹐ipchains 內定會全部接受它們。如果再這樣寫一行的話﹕
ipchains -I input 1 -s xxx.xxx.xxx.xxx -j DENY

那麼﹐和前面相同的封包就會先行被擋掉了﹐而不會檢查到前面舉例的那行﹐因為 " -I input 1" 是要將這行 rule 加在 input 這個 chain 的第一行。ipchains 在檢查設定的時候﹐會先從規則的第一行往後一直對比下去﹐只要條件符合﹐就不再往下檢查其它 rules 了。如果所有規則都不符合﹐然後會檢查原則 (Policy﹐也就是用 ipchains -P 來制定)。所以﹐設定 ipchains 的順序要格外小心哦~~~ 。

要讓 ipchains 發揮最好的防禦能力﹐最小心的就是設定外部界面的 input 規則。output 相對來說可以鬆一點﹐但為養成良好習慣﹐我們應該根據 input 的範例來設定好每一個 output 規則。僅次於 input﹐還要格外小心的就是 forward 了。如果路由沒問題﹐將 forward 設為 ACCEPT ﹐封包只要能通過一個界面的 input 和另一界面的 output﹐封包就可以通行無阻了。不過﹐如果路由上不能讓封包順利完成傳遞﹐例如使用私有 IP 連接 internet﹐那麼我們就要使用偽裝 (MASQ) 的手段來改寫封包。只有 forward 規則裡面我們才能使用 MASQ﹐經過 MASQ 後的封包之來源地址﹐一律會換成 output 出去的那個界面的地址﹐而且來源埠口的值﹐也會換成另外一個﹐同時會為這個動作做下記錄。所以﹐遠端主機只認為這個封包是來自於 NAT 主機的﹐在回應的時候﹐也是會丟到 NAT 的那個界面去。當 NAT 接到這個回應後﹐根據原先的記錄﹐將封包進行解偽裝(DEMASQ)﹐再傳給真正的來源主機。

好了﹐前面說的 MASQ 是修改來自於內部網路的封包﹐讓它能順利到達外面的網路去。這除了可以解決路由的問題外﹐還可以節省 IP 地址﹐而且在安全上面也是有幫助的。因為內部網路使用的私有 IP 在 internet 上面是不能路由的﹐所以﹐就算您的 FW 被攻破﹐除非入侵者能佔據您的 NAT 主機且以它做據點﹐否則﹐他們還是不能連接到內部網路的主機。不過﹐如果我有些服務﹐例如 WWW、MAIL 等﹐想架在內部網路呢﹖按慣例﹐通常會將那些伺服器架在 DMZ 裡面。如果 DMZ 是可以直接路由到 internet 上去的話﹐我們在 forward 那裡設 ACCEPT 就可以了。但是﹐如果我們的 DMZ 是用另外一個私有 IP 網路來架呢﹖我們已經知道可以用 MASQ 來偽裝所有從 DMZ 出去的連線。但外面要如何進入呢﹖這時﹐我們除了 ipchains 之外﹐還要另外一隻 ipmasqadm 的程式﹐將那些傳到 NAT 外面界面的某些 port (通常是那些固定的服務埠口) 的封包﹐轉到 DMZ 裡面的機器去了。例如﹕
ipmasqadm portfw -a -P tcp -L $EXT_WWW_IP 80 -R $DMZ_WWW_IP 80

要好好的設定 ipchains ﹐要求您對 TCP/IP 各種服務都要有相當程度的了解﹐您必須知道一個連線的建立過程是怎樣的﹐每一個封包動作是怎樣從一端送到另一端。而且﹐設定上是雙向的﹐您除了要照顧從客戶端到伺服器端的請求﹐也要照顧從伺服器到客戶端的回應。您除了要知道每一個封包的來源和目的地址﹐還要知道它們的來源埠口和目的埠口。下面﹐讓我們看一看火牆的設定如何影響 FTP 的運作的。

FTP 的連線模式有兩種﹕主動模式(active)和被動模式(passive)。要了解這兩個模式的不同﹐先得了解 FTP 的連線是怎樣建立的﹕

在正常模式下﹕

  1. FTP client 開啟一個隨機選擇的高於 1024 的 port 呼叫 FTP server 的 port 21建立連線﹐這是命令通道的建立。
  2. client 用一個 port 命令告訴 server ﹐客戶端可以用另一個高於 1024 的 port 做數據通道。
  3. 然後 server 用 port 20 和剛才 client 所告知的 port 建立數據連線。請注意﹐這是從 server 到 client 的連線﹐TCP 封包會有一個 SYN 標籤。
  4. client 會確認數據通道連接﹐這個封包會有一個 ACK 標籤。並完成所有交握(hand shack)手續。
  5. 開始數據傳送。

在 passive 模式下﹕

  1. FTP client 開啟一個隨機選擇的高於 1024 的 port 呼叫 FTP server 的 port 21建立連線﹐這是命令通道的建立。
  2. client 送一個 PASV 命令給 server﹐要求建立 passive 模式。
  3. server 然後像上面所述的正常模式第 2 步驟那樣﹐挑一個高於 1024 的 port 告訴 client 做數據通道。
  4. 然後 client 用另一個高於 1024 的 port 呼叫剛才 server 告知的 port 來建立數據通道。此時封包帶 SYN 標籤。
  5. server 確認後回應一個 ACK 封包。並完成所有交握手續。
  6. 開始數據傳送。

我們設定的 ipchains ﹐可以說是一個過濾性的 Firewall 。火牆的保護對象是內部網路的 client 主機。FW 為了擋掉一些來自外面的危險動作﹐通常會限制那些來自外面的主動連線﹐也就是帶 SYN 標籤的封包(如果您忘記了一個 TCP 連線是如何建立的﹐可以參考一下“ 網路基礎中的 OSI 七層協定 ”)。在這樣的情況下﹐ipchains 或許有這樣的一個設定﹕
ipchains -A input -p TCP -i $EXT_IF ! -y -s $ALL -j ACCEPT

那個 " ! " 就是 NOT 的意思﹐" ! -y " 就是 NOT sYn 之意﹐也就是連線不是從外面主動建立的。整句的意思是﹕只允許非主動連線的 TCP 封包從外部界面進入。一個在 FW 之後的 FTP client 在正常模式下﹐(第 3 步)來自 server 的數據通道之建立請求就會被擋下來﹐數據通道也就無從建立了。雖然您可以 login 進遠端的 FTP 伺服器﹐但如果輸入 ls 後﹐卻看不到結果的。FTP 的 ls 是一個命令﹐因為命令通道的建立並沒問題﹐所以 server 可以收到命令。但 ls 的結果是數據﹐然而數據通道卻未建立。所以 server 在傳送失敗後﹐再用命令通道告訴您﹕ “Can't build data connection: Connection refused.”

明白了﹖

好了﹐如果用 Passive 模式又會如何呢﹖看看正常模式的第 3 步和 Passive 模式的第 4 步﹐然後檢查它們進出 FW 的方向﹐以及 FW 如何處理 SYN 封包就知道了。我不想直接把答案告訴您。自己要懂得思考﹐才會有進步﹗不是嗎﹖

但是﹐我必須再指出﹕上面談的是基於一個 routing 健全的情形下。如果用 ipmasquerading 的話﹐封包的地址將會有所改變。具體情形我又再買關子。您可以參考下面網頁﹕http://www.ncu.edu.tw/~center5/livecd/ipnat/Tips: 無論用正常模式或 Passive 模式﹐server 都看不到 client 的地址﹐server只看到 NAT 的地址而已。故此﹐就算您取消了入站封包的 SYN 限制﹐在正常模式下來自 server 的數據通道連線﹐目的地只會送到 NAT 主機的外面界面那裡﹐但問題是 NAT 並沒有該連線的 MASQ 記錄﹐也就不會將封包 DEMASQ 送回裡面的客戶主機。所以您還要執行下面命令﹐讓 NAT 可以順利傳遞 FTP 的封包回給客戶端(用 passive 模式則不用﹐因為已有 MASQ 記錄)﹕
modprobe ip_masq_ftp

要知道 ip_masq_ftp 這個模組會做些什麼﹖請參考網友 常鍋麵 兄的精解﹕

常鍋麵  wrote in message news:3g4d6d$1q1@bbs.cynix.com.tw...

> ip_masq_ftp.o 的工作,除了幫 masq 建立一個 port 20 的紀錄
> 他最重要的工作還是跟 port 21 相關
>    a.監視 client -> server destination port 21 的封包 (tcp of course)
>      並修改其中帶有 PORT command 的封包...
>            [IP header|TCP header|PORT xxx,xxx,xxx,xxx,xx,xxCRLF]
>    b.修改此封包 IP header 中 total length 欄位
>      紀錄 data length 的變動以維持 port 21 的連線不要斷掉..
>      修改每一個 server -> client source port 21 封包的 ack number(TCP header)
>      修改每一個 client -> server destination port 21 封包的 seq number
> 畢竟 masq 紀錄只有 IP header 的 IP add & TCP header 的 port number
> 其他都要靠 ip_masq_ftp.o
> 如果 ftp server port 不是 21 就完蛋了,因為攔不到 PORT command
> 就更別提 data connection 了...(dir, get 都會送 PORT command)
> 不過寫得彈性一點(可設定)應該也不會困難才對...

前面我們已經說過我們可以將傳到 NAT 外部界面的連線轉到 DMZ 裡面去。假如我們有這樣的要求﹐卻繼續沿用前面那行帶 " ! -y " 的規則的話﹐外面的客戶主機就無從建立連線來連接我們的服務主機了。但我們又不想取消它﹐以免危害到其它非 DMZ 的內部網路。還記得 ipchains 的行為習慣嗎﹕它會自上而下的對比規則﹐找到符合的就不再往下找了。根據這樣的特性﹐我們可以將傳給 NAT 外面界面的連線﹐先於那行把連線 ACCEPT 進來。不過﹐就要非常小心別漏了必要的元素限制﹐有可能的話﹐盡您想象將規則設得最嚴密吧。

關於更多的 ipchains 的指令技巧﹐請參考 簡易防火牆建置與流量統計 一文。

安全考量

防火牆的設定規則因每一個網路環境和要求不同而各異﹐基本上﹐設定網路規劃和規則設定順序是很重要的。

例如設定 DMZ﹐不管您是用真實 IP 還是私有 IP﹐將那些對外伺服器和內部伺服器分開是個明智之舉。往往﹐在 DMZ 裡面的機器﹐雖然也可以受到防火牆的保護﹐但畢竟是對外開放的﹐總有被攻擊的危險。所以﹐我們常會以‘假設 DMZ 主機攻破後怎麼辦﹖’的考量去設定防火牆的預設失效模式。換而言之﹐DMZ 裡面的主機是可以隨時被‘犧牲’掉的﹐但內部網路呢﹖可不能這樣玩了﹐當然任何一位安全專家也無法保證內部網路不被攻破﹐但起碼﹐通往內部網路的防火牆應該是最嚴格的。

如果有條件﹐多架一個防火牆在 DMZ 和內部網路之間﹐而該主機是內部網路的唯一出路﹐同時將 DMZ 置於內部網路和外部路由器之間。也就是說﹐作為內部網路的防火牆﹐分別使用 DMZ 和內部 IP﹐它必須再經一道外部防火牆出去。再加上在 DMZ 裡面建置隱蔽性的檢查裝置﹐和充份的記錄﹐以及有效的預警機制。那麼當第一個防火牆失效之後﹐起碼還有第二道防火牆來保護內部網路﹐從而提供更多預警時間在入侵者攻破第二道防火牆之前做出適當反應。當然﹐兩個防火牆的設定﹐將比設定一個防火牆要複雜很多﹐而且測試也費時許多﹐因為往往在判定問題(通常是過嚴)出在哪個防火牆是頗難確定的。如果覺得還不夠﹐那就再使用代理服務器﹐而只允許代理服務器的封包進出這個閘道。

無論如何﹐防火牆自身的安全設定是至關重要的﹐其中一個守則應該尊守﹕一切從嚴﹗在我們架設防火牆的時候﹐應該採取“預設拒絕狀態”﹐也就是﹕將 Allow 的必要控制在最低限度﹐然後 Deny All 。

而作為連接外部網路的防火牆主機﹐盡可能安裝儘量少的服務﹐諸如 Telnet 、r-serials 等危險性高的服務﹐甚至連 FTP 和 HTTP 等普通服務也一概移除。這裡說的移除﹐並非指單純的不啟動服務﹐而是根本就不在主機上面安裝該等服務(或從核心中移除)。同時﹐在那些提供 internet 服務的主機上面﹐除了它所提供的服務外﹐也不要安裝其它服務﹐而且最好還是只跑單一服務。

防火牆的設計是非常複雜和多樣性的﹐很難提供一個唯一方案而適合所有的網路狀況。任何微小的疏忽﹐都可能導致您所有的努力付諸東流。 如果以我個人認識來看﹐了解網路環境、評估服務要求、設定防火牆、和測試﹐這四者的工作時間比例﹐將是 30%﹐30%﹐20%﹐20%。

測試

好了﹐現在您要做的是﹐點這裡 將網中人最後修改的另一隻 script 抓回去。解壓後﹐請先輸入 fw help 看看怎麼使用就知道了﹐很容易的啦~~~~

在測試過程中如果失敗﹐多數是因為 ipchains 設定得過於嚴格所至﹐您可以先從最寬鬆的設定開始。要檢查您目前的 ipchains 設定狀況﹐輸入 ipchains -L 看一看目前設定﹐然後執行 ipchains -D將一些過嚴的限制暫時解除掉。或是將 ipchains 進行 reset﹕
ipchains -F
ipchains -X
ipchains -P input ACCEPT
ipchains -P output ACCEPT
ipchains -P forward ACCEPT
ipchains -P forward MASQ

這樣﹐所有設定都會打回原形﹐然後逐漸地增加限制。

不過﹐您也可以掉過來﹐用 "ipchain -I" 將一些寬松的規則插 (Insert) 到前面去﹐以確定是否那些元素影響到連線。再不然﹐用 ipchians -P 來改變原則 (Policy) 設定也可以將問題限定(或排除)於某一大範圍之內。例如﹕如果我們輸入 ipchains -P output ACCEPT 之後能打通限制﹐那麼您在檢查的時候﹐多留意 output 的規則就是了。

另一個要留意的地方是 routing 的設定﹐您必須確定其他機器有通往上網主機的 gateway ﹐也要確定 Linux 主機能 route 回到各機器。有時候﹐您或許無意中在 Linux 主機上面設定了自己的 Default Gateway ﹐您要確定它在撥接之後的 Default Gateway 要指向 ISP 那端。任何連接到 NAT 的網路﹐不要使用相同的 network ID﹐如果網路經過切割的話﹐那就要更加小心了。

您可以到 http://www.tsmservices.com/masq/ 這個網站詳細參考一下 IP Masquerading 的技術文檔和最新消息。

假如您使用 2.4.x 的核心﹐那麼﹐您就可以使用 netfilter 來取代 ipchains ﹐而提供更多的功能。請參考 Linux 2.4 Packet Filtering HOWTOLinux 2.4 NAT HOWTO 兩篇文章﹐還有 小州兄 的一些經驗﹕ 關於 iptables 和 NAT 的一些技巧 。但是﹐如果您還是喜歡用 ipchains 的話﹐那就在編核心的時候要確定 ipchains 被編成模組﹐然後在開機後將之載入﹕
insmod /lib/modules/2.4.1/kernel/net/ipv4/netfileter/ipchains.o

關於語音功能

在測試過程中﹐因為擔當 IPMASQ 的主機未能夠自動為某些特定程式啟動 autofw 功能,故此需要加入一些命令。例如,你可以將下面這些句子加進上面的script去,就可以讓 VoxPhone3.0 成功運作了,但一次只能夠讓一台機器使用:
#------------VoxPhone3.0--------
VOX_HOST="192.168.0.15"	#設定masq後面的 voxphone 主機
ipmasqadm autofw -A -p tcp 12380 $VOX_HOST:12380 -v
ipmasqadm autofw -A -p udp 12380 $VOX_HOST:12380 -v
ipchains -A input -p TCP -j ACCEPT -i $EXT_IF -b -s $VOX_HOST/32 -d $ALL
ipchains -A input -p UDP -j ACCEPT -i $EXT_IF -b -s $VOX_HOST/32 -d $ALL
ipchains -A output -p TCP -j ACCEPT -i $EXT_IF -b -s $VOX_HOST/32 -d $ALL
ipchains -A output -p UDP -j ACCEPT -i $EXT_IF -b -s $VOX_HOST/32 -d $ALL

假如您欲在 MASQ 後面使用 netmeeting 的話﹐請參考 http://www.coritel.it/coritel/ip/sofia/nat/nat2/nat2.htm 那裡的說明。將 http://www.coritel.it/coritel/ip/sofia/nat/nat2/ip_masq_h323.c 或是 點這裡 下載回來。(如果用 netscape 請按著 shift 鍵點這個聯結)。如果您看到的是源碼﹐那麼將之另存為 ip_masq_h323.c

然後依次執行下面的動作﹕

  1. 將 ip_masq_h323.c 放在 /usr/src/linux/net/ipv4。

  2. 然後修改 /usr/src/linux/net/ipv4/Makefile 這個檔案﹐找到這樣一行﹕
    M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o ip_masq_quake.o
    

  3. 續在這個句子後面加入﹕" ip_masq_h323.o" (別忘了用空白和前面的句子隔開)。

  4. 執行﹕
    cd /usr/src/linux
    make modules modules_install

    假如您碰到編譯問題﹐請參考“ 編譯核心 ”重新編譯您的核心﹐記得要選擇“Enable loadable module support”。

  5. 然後執行﹕
    depmod -a
    modprobe ip_masq_h323

  6. 輸入 lsmod 看看 ip_masq_h323 是否成功載入。如果不行﹐就要重頭做起了﹐直到您可以成功載入為止。

這樣﹐您就可以從 MASQ 裡面的主機呼叫外面的 netmeeting 主機了。如果對方和您一樣也使用 MASQ 的話﹐恐怕就無辦法了。而且﹐別人也無非從外面先呼叫您。

如果您的 ipchains 除了單純的 MASQ 外﹐還有其它的限制﹐那可能要將 UDP 的 hi-ports 開放出來﹐才能成功讓雙方順利交換語音。否則的話﹐通常是對方可以聽到您的聲音﹐而您卻聽不到對方。使用這個方法仍然有一個限制﹕在 MASQ 後面的網路﹐每次只能允許一台主機和外面進行對講。

無論如何﹐我在這裡要特別感謝 Eddie Chang 兄提供的線索﹗

 

 


© 2000 Netman 網中人
Last Updated: April 22, 2001