本章所包含的 '劍譜' 單元﹐應可讓您依招式克敵制勝。然而﹐離開了原理的基本功夫﹐光練招而不練氣﹐縱有劍譜也枉然。所以﹐請先溫故而知新。
您可以用好多方法來做啦。Apache 就可以透過模組來達到某些支援﹐不過我們這裡要教您用 Linux 怎樣做﹐並且舉一反三﹐將其它服務也一併搞定。這些命令都偷師自下面提到的 Jamal Hadi 的演講 。
假設我們有兩個客戶﹐要提供 http、ftp、還有 streaming audio 服務﹐我們只打算賣給他們一定數量的頻寬而已。那我們可以在伺服器上面做手腳。
客戶 A 最多只有 2 megabits﹐而客戶 B 已經支付 5 megabits 的錢了。那我們在伺服器上建立虛擬 IP 位址﹐來將客戶分開來。
# ip address add 188.177.166.1 dev eth0
# ip address add 188.177.166.2 dev eth0
以哪些適合的位址來分配給不同的伺服器﹐則悉從尊便了。所有常用 daemon 都支援這玩意。
接著﹐我們首先將 CBQ adisc 指派給 eth0﹕
# tc qdisc add dev eth0 root handle 1: cbq bandwidth 10Mbit cell 8 avpkt 1000 \
mpu 64
然後﹐為我們的客戶建立類別(classes)﹕
# tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate \
2MBit avpkt 1000 prio 5 bounded isolated allot 1514 weight 1 maxburst 21
# tc class add dev eth0 parent 1:0 classid 1:2 cbq bandwidth 10Mbit rate \
5Mbit avpkt 1000 prio 5 bounded isolated allot 1514 weight 1 maxburst 21
再來﹐我們為這兩個類別增加過濾器﹕
##FIXME: Why this line, what does it do?, what is a divisor?:
##FIXME: A divisor has something to do with a hash table, and the number of
## buckets - ahu
# tc filter add dev eth0 parent 1:0 protocol ip prio 5 handle 1: u32 divisor 1
# tc filter add dev eth0 parent 1:0 prio 5 u32 match ip src 188.177.166.1
flowid 1:1
# tc filter add dev eth0 parent 1:0 prio 5 u32 match ip src 188.177.166.2
flowid 1:2
這樣﹐就大功告成了啦。
FIXME: 為何不用 token bucket 過濾器﹖還是預設的 pfifo_fast 撤出了﹖
根據 Alexey 的 iproute 文件﹐已可以和 netfiler 搭配了﹐且有好些看來不錯的途經。如果您要使用這個﹐請小心調整那些數字﹐針對您的系統給予合理的數值。
假如您想要保護整個網路﹐可以跳過這裡﹐這裡是針對單一主機而已的。
#! /bin/sh -x
#
# sample script on using the ingress capabilities
# this script shows how one can rate limit incoming SYNs
# Useful for TCP-SYN attack protection. You can use
# IPchains to have more powerful additions to the SYN (eg
# in addition the subnet)
#
#path to various utilities;
#change to reflect yours.
#
TC=/sbin/tc
IP=/sbin/ip
IPTABLES=/sbin/iptables
INDEV=eth2
#
# tag all incoming SYN packets through $INDEV as mark value 1
############################################################
$iptables -A PREROUTING -i $INDEV -t mangle -p tcp --syn \
-j MARK --set-mark 1
############################################################
#
# install the ingress qdisc on the ingress interface
############################################################
$TC qdisc add dev $INDEV handle ffff: ingress
############################################################
#
#
# SYN packets are 40 bytes (320 bits) so three SYNs equals
# 960 bits (approximately 1kbit); so we rate limit below
# the incoming SYNs to 3/sec (not very useful really; but
#serves to show the point - JHS
############################################################
$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 1 fw \
police rate 1kbit burst 40 mtu 9k drop flowid :1
############################################################
#
echo "---- qdisc parameters Ingress ----------"
$TC qdisc ls dev $INDEV
echo "---- Class parameters Ingress ----------"
$TC class ls dev $INDEV
echo "---- filter parameters Ingress ----------"
$TC filter ls dev $INDEV parent ffff:
#deleting the ingress qdisc
#$TC qdisc del $INDEV ingress
目前來說﹐分散式服務癱瘓(distributed denial of service)攻擊已經成為 Internet 上頭號騷擾行為。對貴網路使用適當的過濾和速率限制﹐可讓您既避免成為砲灰﹐也避免成為砲手﹐一箭雙雕。
如果您要對網路做過濾﹐那您或許會不讓非本地 IP 來源位址的封包離開貴網路。這可以制止別人以匿名身份對 internet 發送垃圾。
如前面介紹的﹐速率限制考慮得更是週長。再看看下面的 ASCII 圖例﹐幫您重溫一下﹕
[The Internet] ---<E3, T3, whatever>--- [Linux router] --- [Office+ISP]
eth1 eth0
讓我們先將前提部份設定起來吧﹕
# tc qdisc add dev eth0 root handle 10: cbq bandwidth 10Mbit avpkt 1000
# tc class add dev eth0 parent 10:0 classid 10:1 cbq bandwidth 10Mbit rate \
10Mbit allot 1514 prio 5 maxburst 20 avpkt 1000
假如您有 100Mbit﹐或是更快的界面﹐請調整這些數字。現在您需要判定要允許多大的 ICMP 流量。您可以用 tcpdump 進行測量﹐將結果寫進一個檔案﹐然後過一會看看有多少 ICMP 封包通過網路。請不要忘記將測量時間拉長一點。
如果測量結果看起來不切實際﹐那您可以以可用頻寬的 5% 來計算。那就讓我們把類別設好吧﹕
# tc class add dev eth0 parent 10:1 classid 10:100 cbq bandwidth 10Mbit rate \
100Kbit allot 1514 weight 800Kbit prio 5 maxburst 20 avpkt 250 \
bounded
目前的限制為 100Kbit。接下來我們需要一個過濾器﹐將 ICMP 流量撥給這個類別﹕
# tc filter add dev eth0 parent 10:0 protocol ip prio 100 u32 match ip
protocol 1 0xFF flowid 10:100
如果有大量的數據傳入您的線路﹐或是反向傳出﹐而您需要用 telnet 或 ssh 進行某些維護工作﹐這或許不十分理想﹐因為其它封包或會打斷您的鍵盤操作。假如有辦法讓這些互動封包從這些大塊的流量底下暗渡陳倉﹐就最好不過了。Linux 可以幫您做到哦﹗
如前﹐我們需要操縱雙向的流量。顯然﹐如果線路兩端都有 Linux 機器就最理想了﹐當然其它 UNIX's 也可以做得到啦。這點﹐就請教您身邊的 Solaris/BSD 高手囉。
標準的 pfifo_fast 排程方法帶有 3 個不同的 'bands'。當 band1 和 band2 都流量獲得之後﹐在 band0 的流量會先傳送。至為關鍵的是﹐我們的互動流量一定要在 band0 裡面﹗
我們乾脆明目張膽的改編(即將淘汰的) ipchains HOWTO 好了﹕
在 IP 標頭中﹐有 4 個位元並不常用的﹐稱為 Type of Service (TOS) 位元。它們會影響封包被處理的程序﹔這 4 個位元分別是﹕"Minimum Delay"、"Maximum Throughput"、"Maximum Reliability"、和 "Minimum"。它們四者﹐只能設定其一。Ipchains 之 TOS-mangling 作者﹐Rob van Nieuwkerk﹐曾作如下述﹕
對我而言﹐"Minimum Delay" 猶為重要。我為了那些“互動”封包
在我的上傳(Linux) router 上將之打開。我只用一條 33k6 的 modem
線路而已。Linux 將封包的優先順序排進 3 個佇列中。這樣﹐
在我進行大量下載的時候﹐還可以獲得一個可接受的互動效能。
最常見的做法是將 telnet 和 ftp control 連線設為 "Minimum Delay" ﹐而 FTP data 設為 "Maximum Throughput"。在您的上傳 router 上﹐可以如下那樣動作﹕
# iptables -A PREROUTING -t mangle -p tcp --sport telnet \
-j TOS --set-tos Minimize-Delay
# iptables -A PREROUTING -t mangle -p tcp --sport ftp \
-j TOS --set-tos Minimize-Delay
# iptables -A PREROUTING -t mangle -p tcp --sport ftp-data \
-j TOS --set-tos Maximize-Throughput
好了﹐這只對那些 telnet 及從外面送來本地主機的數據有效而已。其它您也要一一設好﹐例如 telnet、ssh、朋友的﹐所有外送封包的 TOS 欄位全自動設好。
如果客戶端並非設定如此﹐那您可以用 netfilter 來做。於您的本機上﹕
# iptables -A OUTPUT -t mangle -p tcp --dport telnet \
-j TOS --set-tos Minimize-Delay
# iptables -A OUTPUT -t mangle -p tcp --dport ftp \
-j TOS --set-tos Minimize-Delay
# iptables -A OUTPUT -t mangle -p tcp --dport ftp-data \
-j TOS --set-tos Maximize-Throughput
本章節由 Internet for Education (泰國) 的讀者 Ram Narula 提供。
以 Linux 來做的話﹐常規技巧上或會採用 ipchains﹐﹐將 "外送" 的 port 80(web) 流量送往伺服器所跑的 squid 服務程式。
有 3 種常見辦法可確定 "外送" 的 port 80(web) 流量會送往伺服器所跑的 squid 服務程式﹐而第 4 種將是這裡的重頭戲。
告訴網關 router 將外送目的端埠口為 80 的封包﹐送到 squid 伺服器的 IP 位址上。
但是
這會額外增加 router 的負載﹐而且有些商業 routers 未必支援這種做法。
Layer 4 switch 絕對能勝任此項工作。
但是
此類設備的成本非常高。一般而言﹐Layer 4 switch 的成本甚至比 router 加一台好的 Linux 伺服器還要高。
您可以強迫所有流量都經過 cache 伺服器。
但是
這有點冒險﹐因為 Squid 會耗掉相當道 cpu 資源﹐或會造成整體的網路效能降低﹐或是伺服器當掉而誰也連不上 internet。
NetFilter 可以提供另一可行手段﹐就是用 NetFilter 來將那些目的端埠口為 89 的封包 "標識" 起來﹐同時用 iproute2 將已 "標識" 的封包送到 Squid 伺服器那裡。
|----------------|
| Implementation |
|----------------|
Addresses used
10.0.0.1 naret (NetFilter server)
10.0.0.2 silom (Squid server)
10.0.0.3 donmuang (Router connected to the internet)
10.0.0.4 kaosarn (other server on network)
10.0.0.5 RAS
10.0.0.0/24 main network
10.0.0.0/19 total network
|---------------|
|Network diagram|
|---------------|
Internet
|
donmuang
|
------------hub/switch----------
| | | |
naret silom kaosarn RAS etc.
首先﹐確定 naret 為預設網關(除 silom 外)﹐讓所有流量均通過它。而 silom 的預設網關必須是 donmuang(10.0.0.3)﹐要不然會產生流量迴圈(loop)。
(網路中的所有伺服器都以 10.0.0.1 為預設網關﹐也就是 donmuang router 的舊 IP 位址﹐所以我將 donmuang 的 IP 設為 10.0.0.3﹐而將 10.0.0.1 給 naret 用。)
Silom
-----
-setup squid and ipchains
在 silom 上面設定 Squid 伺服器﹐要確定它能支援通透性 caching/proxying﹐其預設埠口通常為 3128﹐然後﹐所有給 port 80 的流量都會被轉送到本機埠口 3128。用 ipchains 的話﹐可以這樣做﹕
silom# ipchains -N allow1
silom# ipchains -A allow1 -p TCP -s 10.0.0.0/19 -d 0/0 80 -j REDIRECT 3128
silom# ipchains -I input -j allow1
或是﹐netfilter 來做﹕
silom# iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128
(注意﹕您或許還有其它項目的設定)
關於 Squid 伺服器的更多設定資料﹐請參考 Squid 的 faq ﹕ http://squid.nlanr.net)。
請確定在這台伺服器上面將 ip forwarding 功能打開﹐還有﹐這個伺服器的預設網關是 donmuang (而不是 naret)。
Naret
-----
-setup iptables and iproute2
-disable icmp REDIRECT messages (if needed)
naret# iptables -A PREROUTING -i eth0 -t mangle -p tcp --dport 80 \
-j MARK --set-mark 2
naret# echo 202 www.out >> /etc/iproute2/rt_tables
naret# ip rule add fwmark 2 table www.out
naret# ip route add default via 10.0.0.2 dev eth0 table www.out
naret# ip route flush cache
如果 donmuang 和 naret 都在同一 subnet 上的話﹐那 naret 就不要送出 REDIRECT 的 icmp 訊息了。這時候﹐按如下方法將 icmp REDIRECT 關閉﹕
naret# echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects
naret# echo 0 > /proc/sys/net/ipv4/conf/default/send_redirects
naret# echo 0 > /proc/sys/net/ipv4/conf/eth0/send_redirects
如此﹐所有設定都完成了﹐請回去檢查一下﹕
On naret:
naret# iptables -t mangle -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
MARK tcp -- anywhere anywhere tcp dpt:www MARK set 0x2
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
naret# ip rule ls
0: from all lookup local
32765: from all fwmark 2 lookup www.out
32766: from all lookup main
32767: from all lookup default
naret# ip route list table www.out
default via 203.114.224.8 dev eth0
naret# ip route
10.0.0.1 dev eth0 scope link
10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.1
127.0.0.0/8 dev lo scope link
default via 10.0.0.3 dev eth0
(make sure silom belongs to one of the above lines, in this case
it's the line with 10.0.0.0/24)
|------|
|-DONE-|
|------|
|-----------------------------------------|
|Traffic flow diagram after implementation|
|-----------------------------------------|
INTERNET
/\
||
\/
-----------------donmuang router---------------------
/\ /\ ||
|| || ||
|| \/ ||
naret silom ||
*destination port 80 traffic=========>(cache) ||
/\ || ||
|| \/ \/
\\===================================kaosarn, RAS, etc.
注意﹕ 因為在正常外送路徑上﹐多了一個額外的跳站(hop)﹐
所以此網路為非對稱的。
這樣﹐在 kaosarn 和 internet 之間的封包﹐到這裡都會受到管制。
對於 web/http 流量﹕
kaosarn http request->naret->silom->donmuang->internet
http replies from internet->donmuang->silom->kaosarn
對於 非 web/http 請求(如﹐telnet)﹕
kaosarn outgoing data->naret->donmuang->internet
incoming data from internet->donmuang->kaosarn