第9章 Cache攔截
Cache攔截是讓傳輸流向Squid的流行技術,它不用配置任何客戶端。你可以配置路由器或交換機將HTTP連接轉發到squid運行的主機。squid運行的操作系統被配置成接受外部數據包,並將其遞交給squid進程。爲了讓HTTP攔截生效,你必須配置3個獨立的因素:網絡設備,squid運行的操作系統,和squid自身。
(譯者注:Cache攔截實際上指的是Squid的透明代理)
9.1它如何工作?
Cache攔截包含了某些網絡欺騙,它對理解在客戶端和Squid之間的會話有用。我使用圖9-1和如下的tcpdump示例輸出,來解釋當數據包通過網絡時,如何被攔截。
Figure 9-1. How HTTP interception works
1.用戶代理(user-agent)想請求某個資源,它對原始服務器發起index.html請求,例如:www.oreilly.com。它需要原始服務器的IP地址,所以先發起一個DNS請求:
Packet 1 TIME: 19:54:41.317310 UDP: 206.168.0.3.2459 -> 206.168.0.2.53 DATA: .d...........www.oreilly.com..... --------------------------------------------------------------------------- Packet 2 TIME: 19:54:41.317707 (0.000397) UDP: 206.168.0.2.53 -> 206.168.0.3.2459 DATA: .d...........www.oreilly.com.............PR.....%........PR. ....$........PR...ns1.sonic.net.........PR...ns2.Q........PR ...ns...M...............h.............!.z.......b......
2.現在有了IP地址,用戶代理初始化到原始服務器80端口的TCP連接:
Packet 3 TIME: 19:54:41.320652 (0.002945) TCP: 206.168.0.3.3897 -> 208.201.239.37.80 Syn DATA: <No data>
3.路由器或交換機注意到目的地址是80端口的TCP SYN包。下一步會發生什麼依賴於特定的攔截技術。在4層交換和路由策略上,網絡設備簡單的將TCP包轉發到Squid的數據鏈路地址。當squid直接掛在網絡設備上時,就這樣工作。對WCCP來說,路由器封裝TCP包爲GRE包。因爲GRE包有它自己的IP地址,它可能被通過多個子網進行路由。換句話說,WCCP不要求squid直接掛在路由器上。
4.Squid主機的操作系統接受到攔截包。對4層交換來說,TCP/IP包並沒有改變。假如包使用了GRE封裝,主機會剝離外部的IP和GRE頭部,並將原始的TCP/IP包放在輸入隊列裏。注意squid主機接受到的包是針對外部地址的(原始服務器的)。正常情況下,這個包不匹配任何本地地址,它會被丟棄。爲了讓主機接受外部數據包,你必須在大多數操作系統上激活IP轉發。
5.客戶端的TCP/IP包被包過濾代碼處理。數據包必須匹配某個規則,該規則指示內核轉交這個包給squid。如果沒有這樣的規則,內核簡單的將包按照它自己的方式轉發給原始服務器,這不是你想要的。
注意SYN包的目的端口是80,但squid可能偵聽在不同的端口,例如3128。包過濾規則允許你改變端口號。你不必讓squid偵聽在80端口。通過tcpdump,你能見到這步,因爲轉發的包不會再次通過網絡接口代碼。
即使squid偵聽在80端口,包過濾器的重定向規則仍是必要的。可以讓squid不在這些端口上接受攔截包。重定向規則有點神奇,它轉交外部數據包給squid。
6.Squid接受到新連接的通知,它接受這個連接。內核發送SYN/ACK包返回給客戶端:
Packet 4 TIME: 19:54:41.320735 (0.000083) TCP: 208.201.239.37.80 -> 206.168.0.3.3897 SynAck DATA: <No data>
就象你見到的一樣,源地址是原始服務器,儘管這個包不會抵達原始服務器。操作系統只是簡單的將源地址和目的地址交換一下,並將它放進響應數據包裏。
7.用戶代理接受到SYN/ACK包,建立起完整的TCP連接。用戶代理現在相信它是連接到原始服務器,所以它發送HTTP請求:
Packet 5 TIME: 19:54:41.323080 (0.002345) TCP: 206.168.0.3.3897 -> 208.201.239.37.80 Ack DATA: <No data> --------------------------------------------------------------------------- Packet 6 TIME: 19:54:41.323482 (0.000402) TCP: 206.168.0.3.3897 -> 208.201.239.37.80 AckPsh DATA: GET / HTTP/1.0 User-Agent: Wget/1.8.2 Host: www.oreilly.com Accept: */* Connection: Keep-Alive
8.Squid接受HTTP請求。它使用HTTP Host頭部來轉換局部URL爲完整的URL。在這種情形下,可在access.log文件裏見到http://www.oreilly.com。
9.從這點開始,squid正常的處理請求。一般cache命中會立刻返回。cache丟失會轉發到原始服務器。
10.最後,是squid從原始服務器接受到的響應:
Packet 8 TIME: 19:54:41.448391 (0.030030) TCP: 208.201.239.37.80 -> 206.168.0.3.3897 AckPsh DATA: HTTP/1.0 200 OK Date: Mon, 29 Sep 2003 01:54:41 GMT Server: Apache/1.3.26 (Unix) PHP/4.2.1 mod_gzip/1.3.19.1a mo d_perl/1.27 P3P: policyref="http://www.oreillynet.com/w3c/p3p.xml",CP="C AO DSP COR CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa CONo OUR DELa PUBi OTRa IND PHY ONL UNI PUR COM NAV INT DEM CNT STA P RE" Last-Modified: Sun, 28 Sep 2003 23:54:44 GMT ETag: "1b76bf-b910-3ede86c4" Accept-Ranges: bytes Content-Length: 47376 Content-Type: text/html X-Cache: MISS from www.oreilly.com X-Cache: MISS from 10.0.0.1 Connection: keep-alive
不應該讓交換機或路由器來攔截squid到原始服務器的連接。假如這種情況發生,squid結束與自己的會話,並且不能滿足任何cache丟失。防止這類轉發死循環的最好方法是,確認用戶和squid連接到交換機或路由器的獨立接口。無論何時,應該在指定接口上應用攔截規則。最明顯的,不該在squid使用的接口上激活攔截。
9.2爲何要(或不要)攔截?
許多單位發現,cache攔截很有用,因爲他們不能,或不願意配置所有用戶的web瀏覽器。相對於配置成百上千臺工作站來說,在單個交換機或路由器上做一點網絡欺騙更容易。從我們面臨的許多選擇來看,cache攔截確實有好也有壞。它可能讓你的生活更容易,但也許會更難。
Cache攔截的最明顯的貢獻是,所有HTTP請求通過squid自動離開你的網絡。你不必擔心配置任何瀏覽器,用戶可能在瀏覽器上禁止他們的代理設置。cache攔截讓網絡管理員完全控制HTTP會話。你可以改變,增加,或刪除squid的緩存,而不會顯著影響你的用戶上網衝浪。
關於HTTP攔截的主要不利點就是該技術違背了TCP/IP的標準。這些協議要求路由器或交換機轉發TCP/IP包到目的IP地址裏指定的主機。然而轉發包到cache代理破壞了這些規則。代理僞裝身份接受轉交過來的連接。用戶代理被欺騙了,以爲它們在與真正的web服務器會話。
這樣的混亂導致在老版本的Microsoft IE瀏覽器中產生嚴重問題。瀏覽器的Reload按鈕是刷新HTML頁面的最容易的方法。當瀏覽器被配置成使用cache代理時,reload請求包含了一個Cache-Control:no-cache頭部,它強迫產生cache丟失(或cache確認),並確保響應是最近更新的。假如沒有明確配置使用代理,瀏覽器會忽略該頭部。當使用cache攔截時,瀏覽器認爲它在連接到原始服務器,因此沒必要發送該頭部。在這種情形下,squid不會告知用戶的Reload按鈕,也許不會驗證cache響應。squid的ie_refresh提供瞭解決此bug的局部解決方法(見附錄A)。Microsoft已經在其IE 5.5 SP1中解決了這個問題。
因爲類似的理由,你不能結合cache攔截使用HTTP代理驗證。因爲客戶端不知道這個代理,它不會發送必要的Proxy-Authorization頭部。另外,407(代理驗證請求)響應代碼也不恰當,因爲響應看起來象來自原始服務器,原始服務器從來不會發送如此響應。
也不能在cache攔截中使用RFC 1413 ident查詢(見6.1.2.11章節)。Squid不能對必要的IP地址建立新的TCP Socket連接。操作系統在轉發攔截連接到squid時,它執行欺騙。然後,當squid希望bind新的TCP Socket到外部IP地址時,它不能執行欺騙。它想bind的地址實際上並非真正本地的,所以bind系統調用失敗。
cache攔截也與設計成阻止地址欺騙的IP過濾衝突(見RFC 2267:Network Ingress Filtering: Defeating Denial of Service Attacks Which Employ IP Source AddressSpoofing)。考慮如圖9-2顯示的網絡。路由器有2個LAN接口:lan0和lan1。網絡管理員在路由器上使用包過濾器,以確保沒有內部主機傳送假冒源地址的數據包。路由器只會轉發源地址對應相連網絡的數據包。包過濾規則也許看起來如下:
# lan0 allow ip from 172.16.1.0/24 to any via lan0 deny ip from any to any via lan0 # lan1 allow ip from 10.0.0.0/16 to any via lan1 deny ip from any to any via lan1
Figure 9-2. Interception caching breaks address spoofing filters
現在看看,當路由器和lan1中的squid主機配置成攔截來自lan0中的HTTP連接後,會發生什麼。Squid裝扮成原始服務器,這意味着從squid到用戶的響應TCP包欺騙了源地址。lan0過濾規則導致路由器拒絕這些包。爲了讓cache攔截生效,網絡管理員須移除lan0規則。這樣就讓網絡有漏洞,從而易遭拒絕服務攻擊。
我在先前的章節裏描述過,客戶端在打開連接之前必須先進行DNS查詢。在某些防火牆環境中,這樣做可能有問題。你想進行HTTP攔截的主機必須能夠查詢DNS。如果客戶端了解自己正使用代理(因爲手工配置或代理自動配置),它通常就不去解析主機名。代替的,它簡單的將完整URL轉發給squid,由squid來查詢原始服務器的IP地址。
另一個小問題是,squid接受任意目的IP地址的連接。例如,某個web站點當機了,但它仍然有DNS記錄存在。squid僞裝這個站點接受TCP連接。客戶端會認爲該站點仍然在運行,因爲連接有效。當squid連接到原始服務器失敗時,它強迫返回錯誤消息。
萬一形勢不清,HTTP攔截在初次使用時有些棘手或困難。許多不同的組件必須組合工作,並且要配置正確。甚至,從內存中恢復整個配置也很困難。我強烈建議你在將其應用於生產環境之前,先建立測試環境。一旦你讓它正常運行,請記錄每一步細節。
9.3 網絡設備
現在你瞭解了cache攔截的相關細節,讓我們看看如何實際讓它工作。我們先配置網絡設備,它們用來攔截HTTP連接。
9.3.1 內置Squid
在該配置中,你無需交換或網絡路由設備來攔截HTTP連接。代替的,squid運行的Unix系統,也就是路由器(或網橋),請見圖9-2。
Figure 9-3. A system that combines routing and caching can easily intercept HTTP traffic
該配置本質上跳過了9.1章的頭三步。squid主機充當網絡路由器,它接受HTTP連接包。假如你採用此方法,請直接跳到9.4章。
9.3.2 四層交換
許多單位使用四層交換機來支持HTTP攔截。這些產品提供更多的功能,例如健壯性檢測和負載均衡。我在這裏僅僅講講攔截。關於健壯性檢測和負載均衡的信息,請見O'Reilly's Server Load Balancing and Load Balancing Servers, Firewalls, and Caches (John Wiley & Sons). 下面的章節包含了許多產品和技術的示例配置。
9.3.2.1 Alteon/Nortel
下面的配置來自ACEswitch 180和Alteon's WebOS 8.0.21。網絡設置請見圖9-4。
Figure 9-4. Sample network for layer four switch interception, for Alteon and Foundry examples
客戶端連接到端口1,通過端口2連接到因特網,squid運行在端口3。下面的行是交換機的/cfg/dump命令的輸出。你無須敲入所有這些行。甚至,在Alteon的新版軟件裏,某些命令可能改變了。注意Alteon把這個功能叫做Web Cache重定向(WCR)。如下是處理步驟:
- 1. 首先,你必須分配給Alteon交換機一個IP地址。這是必要的,以便交換機能檢查squid的存活狀態。
/cfg/ip/if 1 ena addr 172.16.102.1 mask 255.255.255.0 broad 172.16.102.255
- 2. Alteon的WCR屬於服務負載均衡(SLB)配置。所以,必須使用如下命令在交換機上激活SLB功能:
/cfg/slb on
- 3. 現在,用squid的IP地址定義real server:
/cfg/slb/real 1 ena rip 172.16.102.66
- 4. 必須定義一個組,並分配給real server一個組號:
/cfg/slb/group 1 health tcp add 1
- 5. 下一步定義2個過濾規則。第1條規則匹配HTTP連接(目的端口是80的TCP包),並重定向它們到組1裏的server。第2條規則匹配所有其他數據包,並正常轉發它們。
/cfg/slb/filt 1 ena action redir sip any smask 0.0.0.0 dip any dmask 0.0.0.0 proto tcp sport any dport http group 1 rport 0 /cfg/slb/filt 224 ena action allow sip any smask 0.0.0.0 dip any dmask 0.0.0.0 proto any
- 6. 最後一步是給SLB配置指定的交換端口。在端口1上,處理客戶端連接(這也是客戶端連接的端口),並增加2條過濾規則。在端口2上,僅須配置它正常服務(例如,向上連接到Internet):
cfg/slb/port 1 client ena filt ena add 1 add 224 /cfg/slb/port 2 server ena
爲了驗證HTTP攔截配置正確並工作良好,你可以使用/stats/slb和/info/slb菜單裏的命令。/info/slb/dump是快速有效的查看整個SLB配置的方法:
>> Main# /info/slb/dump Real server state: 1: 172.16.102.66, 00:c0:4f:23:d7:05, vlan 1, port 3, health 3, up Virtual server state: Redirect filter state: 1: dport http, rport 0, group 1, health tcp, backup none real servers: 1: 172.16.102.66, backup none, up Port state: 1: 0.0.0.0, client filt enabled, filters: 1 224 2: 0.0.0.0, server filt disabled, filters: empty 3: 0.0.0.0 filt disabled, filters: empty
在該輸出裏,注意到交換機顯示Squid在端口3上可到達,並且運行正常。你也能見到過濾規則1應用到端口1。在端口狀態節裏,端口1定義爲客戶端連接端口,端口2簡單的標記爲服務端口。
/stats/slb/real命令顯示real server(squid)的有用統計:
>> Main# /stats/slb/real 1 ------------------------------------------------------------------ Real server 1 stats: Health check failures: 0 Current sessions: 41 Total sessions: 760 Highest sessions: 55 Octets: 0
大部分統計與任務(例如TCP連接)數量相關。假如再次運行該命令,總共的任務計數會增加。最後,/stats/slb/group命令顯示幾乎同樣的信息:
>> Main# /stats/slb/group 1 ------------------------------------------------------------------ Real server group 1 stats: Current Total Highest Real IP address Sessions Sessions Sessions Octets ---- --------------- -------- ---------- -------- --------------- 1 172.16.102.66 65 2004 90 0 ---- --------------- -------- ---------- -------- --------------- 65 2004 90 0
假如不止1個real server在組裏,該輸出會更有趣。
9.3.2.2 Foundry
下面的配置示例來自ServerIron XL,運行的軟件版本是07.0.07T12。跟前面一樣,客戶端在端口1,Internet連接在端口2,squid運行在端口3。然而,這樣的配置少了點東西,因爲這裏可以激活HTTP全局攔截。Foundry的cache攔截的名字叫做Transparent Cache Switching(TCS)。請參考圖9-4。首先請給交換機分配1個IP地址,以便執行健壯性檢測:
ip address 172.16.102.1 255.255.255.0
Foundry允許你在特定端口上激活或禁用TCS。然而簡單起見,這裏全局激活它:
ip policy 1 cache tcp http global
在該行裏,cache是針對TCS功能的關鍵字。下1行定義web cache,我定義其名字爲squid1,並且告訴交換機它的IP地址:
server cache-name squid1 172.16.102.66
最後的步驟是將web cache加進cache組裏:
server cache-group 1 cache-name squid1
假如在轉發連接時有問題,請參閱show cache-group命令的輸出:
ServerIron#show cache-group Cache-group 1 has 1 members Admin-status = Enabled Active = 0 Hash_info: Dest_mask = 255.255.255.0 Src_mask = 0.0.0.0 Cache Server Name Admin-status Hash-distribution squid1 6 3 HTTP Traffic From <-> to Web-Caches Name: squid1 IP: 172.16.102.66 State: 6 Groups = 1 Host->Web-cache Web-cache->Host State CurConn TotConn Packets Octets Packets Octets Client active 441 12390 188871 15976623 156962 154750098 Web-Server active 193 11664 150722 151828731 175796 15853612 Total 634 24054 339593 167805354 332758 170603710
某些輸出有些模糊,但通過重複該命令,並且觀察計數器的增長,你能瞭解攔截是否在進行。
show server real提供幾乎同樣的信息:
ServerIron#show server real squid1 Real Servers Info Name : squid1 Mac-addr: 00c0.4f23.d705 IP:172.16.102.66 Range:1 State:Active Wt:1 Max-conn:1000000 Src-nat (cfg:op):(off:off) Dest-nat (cfg:op):(off:off) squid1 is a TRANSPARENT CACHE in groups 1 Remote server : No Dynamic : No Server-resets:0 Mem:server: 02009eae Mem:mac: 045a3714 Port State Ms CurConn TotConn Rx-pkts Tx-pkts Rx-octet Tx-octet Reas ---- ----- -- ------- ------- ------- ------- -------- -------- ---- http active 0 855 29557 379793 471713 373508204 39425322 0 default active 0 627 28335 425106 366016 38408994 368496301 0 Server Total 1482 57892 804899 837729 411917198 407921623 0
最後,使用show logging命令來觀察交換機是否顯示squid正常或異常:
ServerIron#show logging ... 00d00h11m51s:N:L4 server 172.16.102.66 squid1 port 80 is up 00d00h11m49s:N:L4 server 172.16.102.66 squid1 port 80 is down 00d00h10m21s:N:L4 server 172.16.102.66 squid1 port 80 is up 00d00h10m21s:N:L4 server 172.16.102.66 squid1 is up
注意ServerIron認爲服務運行在80端口。以後你會見到squid運行在3128端口的示例。包過濾規則實際上將包的目的地址從80改變爲3128。這導致一些與狀態檢測有關的有趣結果,我在9.3.2.5節裏會講到。
9.3.2.3 Extreme Networks
在該示例裏,硬件是Summit1i,軟件版本是6.1.3b11。再次將客戶端分配在端口1,Internet在端口2,squid在端口3。網絡配置見圖9-5。
Figure 9-5. Sample network for intercepting with a router, for the Extreme and Cisco policy routing examples
Extreme交換機僅僅對在不同子網間進行路由的數據包進行HTTP連接的攔截。換句話說,如果你配置Extreme交換機使用二層模式(單一VLAN裏),就不能將包轉發給squid。爲了讓HTTP攔截正常工作,必須給用戶,Squid,和Internet配置不同的VLAN。
configure Default delete port 1-8 create vlan Users configure Users ip 172.16.102.1 255.255.255.192 configure Users add port 1 create vlan Internet configure Internet ip 172.16.102.129 255.255.255.192 configure Internet add port 2 create vlan Squid configure Squid ip 172.16.102.65 255.255.255.192 configure Squid add port 3
下一步是激活和配置交換機的路由:
enable ipforwarding configure iproute add default 172.16.102.130
最後,配置交換機重定向HTTP連接到Squid:
create flow-redirect http tcp destination any ip-port 80 source any configure http add next-hop 172.16.102.66
9.3.2.4 Cisco Arrowpoint
下類配置基於我以前的測試筆記。然而,最近我沒有使用這類型的交換機,不能確保如下命令仍然正確:
circuit VLAN1 ip address 172.16.102.1 255.255.255.0 service pxy1 type transparent-cache ip address 172.16.102.66 port 80 protocol tcp active owner foo content bar add service pxy1 protocol tcp port 80 active
9.3.2.5 關於HTTP服務和健壯性檢測的評論
在上面的示例裏,路由器/交換機都直接轉發包,不會改變目的TCP端口。在9.4章裏用到的包過濾規則改變了目的端口。如果試圖在同一主機上運行HTTP服務和squid,那麼就產生了一個有趣的問題。
爲了在3128端口運行Squid的同時,還要在80端口運行HTTP,包過濾配置必須有1條特殊的規則,它接受到本機HTTP服務的TCP連接。否則,連接會直接轉交給Squid。該規則易於建立。假如目的端口是80,並且目的地址是服務器的,那麼主機正常接受這個包。然而所有的攔截包有外部目的地址,所以它們不會匹配該規則。
然而,當路由器/交換機進行HTTP健壯性檢測時,它連接到服務器的IP地址。這樣,健壯性檢測的數據包匹配了上述規則,它不會轉交給Squid。路由器/交換機檢測了錯誤的服務。假如HTTP服務down掉了,而squid還在運行,那健壯性檢測就產生錯誤結果。
解決這個問題的一些選擇是:
- 1.不要在Squid主機上運行HTTP服務;
- 2.增加1條特殊的包過濾規則,將來自路由器/交換機的狀態檢測的包轉交給squid;
- 3.配置路由器/交換機,改變目的端口爲3128;
- 4.禁止4層狀態檢測。
9.3.3 Cisco策略路由
策略路由與4層交換並非不同。它在Cisco和其他公司的路由產品上執行。主要的區別是策略路由不包括任何健壯性檢測。這樣,假如squid超載或完全不可響應,路由器還會繼續轉發包到squid,而不是將包路由到原始服務器。策略路由要求squid位於路由器直接相連的子網中。
在本示例裏,使用了Cisco 7204路由器,運行IOS Version 12.0(5)T。網絡配置與前面的一樣,見圖9-5。
首先的配置步驟是定義訪問列表,匹配來自客戶端的到80端口的數據包。必須確保Squid發起的到80端口的數據包不會被再次攔截。做到這點的方法之一是,定義1個特殊規則,拒絕來自squid的數據包,緊跟1條規則允許所有其他的數據包:
access-list 110 deny tcp host 172.16.102.66 any eq www access-list 110 permit tcp any any eq www 另外,如果Squid和用戶位於不同的子網,你可以僅僅允許來自用戶所在網絡的數據包: access-list 110 permit tcp 10.102.0.0 0.0.255.255 any eq www
下一步是定義路由映射。在這裏你告訴路由器轉發攔截包到何處去:
route-map proxy-redirect permit 10 match ip address 110 set ip next-hop 172.16.102.66
這些命令表明,“假如IP地址匹配訪問列表110,就轉發該包到172.16.102.66”。在route-map行的數字10是一個序列號,假如你有多個路由映射的話。最後一步是應用路由映射到客戶端連接的接口:
interface Ethernet0/0 ip policy route-map proxy-redirect
IOS不對策略路由提供很多調試方法。然而,show route-map命令足夠可用:
router#show route-map proxy-redirect route-map proxy-redirect, permit, sequence 10 Match clauses: ip address (access-lists): 110 Set clauses: ip next-hop 172.16.102.66 Policy routing matches: 730 packets, 64649 bytes
9.3.4 Web Cache Coordination協議
Cisco對4層交換技術的響應叫做Web Cache Coordination Protocol(WCCP).WCCP在許多方面與典型的4層攔截不同。
首先,攔截包被封裝在GRE(路由封裝類)裏。這點允許數據包跨子網傳輸,也就意味着squid不必直接連在路由器上。因爲數據包是封裝的,squid主機必須對其進行解包。並非所有的Unix系統有解開GRE包的代碼。
第二個不同是,路由器如何決定將負載分攤到多個cache上。事實上,路由器不做這個決定,由cache來做。當路由器有一組支持WCCP的cache時,其中一個cache主動成爲組的領導。由領導cache來決定如何分攤負載和通知路由器。在路由器重定向然任何連接之前,這是一個額外的必要步驟。
因爲WCCP使用GRE,路由器可能強迫要求將來自HTTP請求的大TCP包分割成片斷。幸運的是,這點不會經常發生,因爲大部分HTTP請求比以太網MTU size(1500字節)要小。默認的TCP和IP包頭部是20字節,這意味着以太網幀能實際攜帶1460字節的數據。GRE封裝在GRE頭部增加了20字節,另外在第二個IP頭部增加了20字節。這樣來自客戶端的正常的1500字節的TCP/IP包,在封裝後變成了1540字節。這樣數據包就太大而不能在單個以太網幀裏傳輸,所以路由器將原始包分割成兩個片斷。
9.3.4.1 WCCPv1
該節的配置示例在運行IOS Version 12.0(5)T的Cisco 7204路由器上測試。網絡配置跟圖9-5同。
首先,在IOS配置中輸入如下兩行,激活路由器的WCCP:
ip wccp version 1 ip wccp web-cache
接着,必須在單獨的路由器接口上激活WCCP。在HTTP包離開路由器的接口上激活WCCP,也就是路由器連接到外部原始服務器或Internet網關的接口:
interface Ethernet0/1 ip address 172.16.102.129 255.255.255.192 ip wccp web-cache redirect out
請確認保存了配置改變。
你也許想使用訪問列表來阻止某些web站點的攔截。可以使用訪問列表來防止循環轉發。例如:
! don't re-intercept connections coming from Squid: access-list 112 deny tcp host 172.16.102.66 any eq www ! don't intercept this broken web site access-list 112 deny tcp any 192.16.8.7 255.255.255.255 eq www ! allow other HTTP traffic access-list 110 permit tcp any any eq www ip wccp web-cache redirect-list 112
路由器不發送任何數據到squid,直到squid宣稱它自己是路由器。我在9.5.1節裏解釋如何配置squid的WCCP。
9.3.4.2 WCCPv2
當前標準的Squid發佈僅支持WCCPv1。然而,可在http://devel.squid-cache.org/找到WCCPv2的補丁。該代碼仍是實驗性的。注意從路由器發送到Squid的GRE包,包含了額外的4個字節。WCCPv2在GRE頭部和封裝的IP包之間,插入了一個重定向頭部。也許需要修改內核代碼來計算這個額外的頭部。
9.3.4.3 調試
IOS提供許多命令來監視和調試WCCP。show ip wccp web-cache命令提供一些基本的信息:
router#show ip wccp web-cache Global WCCP information: Router information: Router Identifier: 172.16.102.129 Protocol Version: 1.0 Service Identifier: web-cache Number of Cache Engines: 1 Number of routers: 1 Total Packets Redirected: 1424 Redirect access-list: -none- Total Packets Denied Redirect: 0 Total Packets Unassigned: 0 Group access-list: -none- Total Messages Denied to Group: 0 Total Authentication failures: 0
欲瞭解更多細節,在前敘命令後加一個detail單詞:
router#show ip wccp web-cache detail WCCP Cache-Engine information: IP Address: 172.16.102.66 Protocol Version: 0.4 State: Usable Initial Hash Info: 00000000000000000000000000000000 00000000000000000000000000000000 Assigned Hash Info: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF Hash Allotment: 256 (100.00%) Packets Redirected: 1424 Connect Time: 00:17:40
這裏可以看到squid的IP地址和狀態。假如不止一個cache與路由器會話,那麼hash分配信息看起來不同。大多數情況下,每個cache接受到相等的hash值。
注意第二條命令輸出的協議版本值,與第一條命令的不一樣。不幸的是,賦予了版本號太多的意義。show ip wccp web-cache命令看起來報告WCCP協議的主版本號(例如1或2),然而show ip wccp web-cache detail的版本號看起來匹配Squid的wccp_version指令的值。
9.4 操作系統配置
爲了讓cache攔截正常工作,必須在操作系統中激活某些網絡功能。首先,必須激活IP包轉發。這就允許操作系統接受目的地址是外部IP的數據包。接着,必須激活和配置內核中的相關代碼,以重定向外部包到Squid。
9.4.1 Linux
本節的指導適合2.4系列Linux內核。我使用RedHat Linux7.2(內核是2.4.7-10)。假如你使用的版本不同,那可能不能運行。建議搜索下Squid的FAQ或其他地方,找到關於內核的更新的或歷史的信息。
在我測試iptables過程中,不必激活IP轉發。然而,你也可以試試在一開始就激活它,並在一切運行良好後,看看能否禁掉它。激活包轉發的最好的方法是在/etc/sysctl.conf文件裏增加如下行:
net.ipv4.ip_forward = 1
一般來說,在HTTP攔截生效前,不必編譯新內核。假如你不知道如何配置和創建新Linux內核,請參閱O'Reilly's Running Linux by Matt Welsh, Matthias Kalle Dalheimer, and Lar Kaufman。當你配置內核時,請確認如下選項被激活:
o General setup Networking support (CONFIG_NET=y) Sysctl support (CONFIG_SYSCTL=y) o Networking options Network packet filtering (CONFIG_NETFILTER=y) TCP/IP networking (CONFIG_INET=y) Netfilter Configuration Connection tracking (CONFIG_IP_NF_CONNTRACK=y) IP tables support (CONFIG_IP_NF_IPTABLES=y) Full NAT (CONFIG_IP_NF_NAT=y) REDIRECT target support (CONFIG_IP_NF_TARGET_REDIRECT=y) o File systems /proc filesystem support (CONFIG_PROC_FS=y)
另外,請確認該選項沒被激活:
o Networking options Fast switching (CONFIG_NET_FASTROUTE=n)
重定向外部數據包到squid的代碼是Netfilter軟件的一部分。如下是發送HTTP攔截連接到squid的規則:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128
Linux內核維護許多不同的tables。-t nat選項表明我們正在修改NAT(網絡地址轉換)表。本質上,我們使用iptables將原始服務器的TCP/IP地址轉換爲squid的本地TCP/IP地址。
每個iptables表有許多鏈。-A PREROUTING表明我們增加了一條規則到內建的鏈叫做PREROUTING。PREROUTING鏈僅對從外部網絡進入系統的數據包有效。
接下來的三個選項決定哪個包匹配該規則。-i eth0選項限制規則僅對eth0接口上接受的數據包有效。-p tcp選項指定TCP包,--dport 80指定包的目的端口是80。假如這三個條件都是true,那麼包匹配該規則。
-j REDIRECT選項表明對匹配規則的包,採取何種動作。REDIRECT是內建的動作名,它導致iptables改變包的目的地址爲127.0.0.1。--to-port 3128選項也指示iptables改變目的TCP端口爲3128。
假如你在squid主機上運行HTTP服務(例如Apache),就必須增加另外的iptables規則。該必要規則允許連接到HTTP服務。否則,REDIRECT規則導致iptables轉發連接到squid的3128端口。可以使用-I選項在列表頂部插入一條新規則:
iptables -t nat -I PREROUTING -i eth0 -p tcp -d 172.16.102.66 --dport 80 -j ACCEPT
一旦確認所有iptables規則工作正確,記得運行如下命令來保存配置:
/sbin/service iptables save
將當前規則保存到/etc/sysconfig/iptables,當系統重啓時,這些規則自動載入。
9.4.1.1 Linux和WCCP
2.4版本的Linux內核自帶1個GRE僞裝接口。然而,它不能解碼WCCP任務裏封裝的GRE包。問題看起來在於路由器設置了WCCP/GRE包的協議類型域爲0x883E。Linux的GRE驅動不知道如何處理這類型包,因爲它不瞭解0x883E類型的協議。
可以試試給Linux打GRE模塊的補丁,以便它能在WCCP下工作。Squid FAQ包含了對這個補丁的鏈接。然而,假如使用WCCP指定的Linux模塊,事情會容易些。可以在這裏找到它:http://www.squid-cache.org/WCCP-support/Linux/ip_wccp.c
必須編譯ip_wccp.c文件爲可裝載內核模塊。有點棘手的是,依賴於內核版本的不同,編譯選項可能不同。你可以進入內核源代碼目錄,敲入make modules並觀察編譯器命令的滾動。然後拷貝這些命令中的一個,然後改變最後一個參數爲ip_wccp.c。如下是我在2.4.7-10 Linux內核中使用的命令:
% gcc -Wall -D_ _KERNEL_ _ -I/usr/src/linux-2.4.7-10/include / -DMODULE -DMODVERSIONS -DEXPORT_SYMBAB / -include /usr/src/linux-2.4.7-10/include/linux/modversions.h / -O2 -c ip_wccp.c
gcc命令在當前目錄會生成ip_wccp.o文件。下一步使用insmod命令,裝載模塊到內核中:
# insmod ip_wccp.o
注意ip_wccp模塊接受來自任何源地址的GRE/WCCP包。換句話說,惡意用戶可能發送數據到squid cache。假如使用該模塊,應該安裝一條iptables規則,拒絕外部的GRE包。例如:
# iptables -A INPUT -p gre -s 172.16.102.65 -j ACCEPT # iptables -A INPUT -p gre -j DROP
不要忘記敲入/sbin/service iptables save命令來保存配置。
9.4.2 FreeBSD
本節的例子基於FreeBSD-4.8,並可以在任何FreeBSD-4和5系列的後續版本上運行。要激活IP包轉發,在/etc/sysctl.conf中增加如下行:
net.inet.ip.forwarding=1
需要在內核中激活2個特殊選項。假如你不知道如何編譯內核,參見FreeBSD Handbook第9章(http://www.freebsd.org/handbook/index.html). 編輯內核配置文件,確保有如下行:
options IPFIREWALL options IPFIREWALL_FORWARD
假如squid主機位於無人照看的機房中,我也推薦使用IPFIREWALL_DEFAULT_TO_ACCEPT選項。假如你被防火牆的規則困擾,仍然可以登陸系統中。ipfw命令告訴內核重定向攔截連接到squid:
/sbin/ipfw add allow tcp from 172.16.102.66 to any out /sbin/ipfw add allow tcp from any 80 to any out /sbin/ipfw add fwd 127.0.0.1,3128 tcp from any to any 80 in /sbin/ipfw add allow tcp from any 80 to 172.16.102.66 in
第一條規則匹配squid主機發起的數據包。它確保外出的TCP連接不會被重新定向回squid。第二條規則匹配squid響應客戶端的數據包。我在這裏列出它,因爲隨後會有另外的ipfw規則,這些規則會拒絕這些包。第三條規則實際重定向進來的連接到squid。第四條規則匹配從原始服務器返回squid的數據包。這裏又一次假設隨後會有相應的拒絕規則。
假如在squid主機上運行HTTP服務,就必須增加另外的規則,它放過,而不是重定向,目的地址是原始服務器的TCP包。下列規則在fwd規則之前:
/sbin/ipfw add allow tcp from any to 172.16.102.66 80 in
FreeBSD典型的將ipfw規則存儲在/etc/rc.firewall裏。一旦你確認規則設置正確,記得保存它們。將如下行加入/etc/rc.local文件,讓FreeBSD在啓動時自動運行/etc/rc.firewall腳本。
firewall_enable="YES"
9.4.2.1 FreeBSD和WCCP
FreeBSD版本4.8和後續版本內建了對GRE和WCCP的支持。早期的版本需要補丁,你可以在這裏找到: http://www.squid-cache.org/WCCP-support/FreeBSD/ . 內建代碼的性能非常好,它是真正的內核組織編寫的。可能也需要編譯支持GRE的新內核。將如下行加入內核配置文件裏:
pseudo-device gre
對Freebsd-5,使用device代替了pseudo-device。當然,你也需要前面章節裏提到的FIREWALL選項。
在安裝和重啓了新內核後,必須配置GRE通道來接受來自路由器的GRE包。例如:
# ifconfig gre0 create # ifconfig gre0 172.16.102.66 172.16.102.65 netmask 255.255.255.255 up # ifconfig gre0 tunnel 172.16.102.66 172.16.102.65 # route delete 172.16.102.65
ifconfig命令在gre0接口上,增加了一個到路由器(172.16.102.65)的路由表入口。我發現必須刪除該路由,以便squid能與其他路由器會話。
你也許想或必須對來自路由器的GRE包,增加一條ipfw規則:
/sbin/ipfw add allow gre from 172.16.102.65 to 172.16.102.66
9.4.3 OpenBSD
本節的示例基於OpenBSD 3.3。
爲了激活包轉發,在/etc/sysctl.conf文件裏增加該行:
net.inet.ip.forwarding=1現在,在/etc/pf.conf文件裏增加如下類似行,配置包過濾規則:
rdr inet proto tcp from any to any port = www -> 127.0.0.1 port 3128 pass out proto tcp from 172.16.102.66 to any pass out proto tcp from any port = 80 to any pass in proto tcp from any port = 80 to 172.16.102.66
假如你沒有使用OpenBSD的包過濾器,需要在/etc/rc.conf.local文件裏增加一行來激活它:
pf=YES
9.4.3.1 OpenBSD和WCCP
首先,增加如下行到/etc/sysctl.conf文件,告訴系統接受和處理GRE和WCCP包:
net.inet.gre.allow=1 net.inet.gre.wccp=1
然後,用如下命令配置GRE接口:
# ifconfig gre0 172.16.102.66 172.16.102.65 netmask 255.255.255.255 up # ifconfig gre0 tunnel 172.16.102.66 172.16.102.65 # route delete 172.16.102.65
跟Freebsd一樣,我發現必須刪除ifconfig自動產生的路由。最後,依賴於包過濾器的配置,必須增加一條規則以允許GRE包:
pass in proto gre from 172.16.102.65 to 172.16.102.66
9.4.4 在NetBSD和其他系統上的IPFilter
本節的示例基於NetBSD 1.6.1。它們也能運行在Solaris,HP-UX,IRIX,和Tru64上,既然這些系統本身就配備了IPFilter.
激活NetBSD的包轉發,將如下行加進/etc/sysctl.conf:
net.inet.ip.forwarding=1
然後,將如下行插入NAT配置文件/etc/ipnat.conf中:
rdr fxp0 0/0 port 80 -> 172.16.102.66 port 3128 tcp
你的接口名可能與本例的fxp0不同。
9.4.4.1 NetBSD和WCCP
我不能在NetBSD上運行WCCP,即使打了GRE補丁來接受WCCP包。該問題看起來根源在IPFilter rdr規則阻塞了特定的端口。來自路由器的包通過NetBSD的gre0接口(在這裏它們被解包)。然而,包回到路由器時,走另一條通道,未被封裝並且不走同一網絡接口。這樣,IPFilter代碼沒有將squid的本地IP地址轉換回原始服務器的地址。
9.5 配置Squid
假如你使用Linux 2.4和iptables,在運行./configure時,可使用--enable-linux-netfilter選項。它激活某些Linux的特殊代碼,以發現發起請求的原始服務器的IP地址。Squid正常情況下從Host頭部,得到原始服務器的名字(和/或地址)。--enable-linux-netfilter功能僅對沒有Host頭部的請求來說是必要的。統計顯示幾乎所有的請求有Host頭部,所以實際中可以不使用--enable-linux-netfilter選項。
假如正在使用IPFilter包(NetBSD,Solaris,或其他),因爲同樣的理由,你應該使用--enable-ipf-transparent選項。在OpenBSD上,請使用--enable-pf-transparent選項。每次運行./configure時,必須重編譯squid,見3.8章的描述。
在運行了./configure和重編譯了squid後,可以編輯squid.conf文件。作爲起點,請確認下列指令定義了給定的值:
httpd_accel_host virtual httpd_accel_port 80 httpd_accel_uses_host_header on httpd_accel_with_proxy on httpd_accel_single_host off
http_accel_host指令是關鍵。它指示squid接受包含局部URI的HTTP請求。httpd_accel_uses_host_header被激活,允許squid使用Host頭部來重新構建完整URI。virtual關鍵字指示squid在缺乏Host頭部時,將原始服務器的IP地址放進URL裏。
httpd_accel_with_proxy指令控制squid是否既接受HTTP服務(部分URI)請求,又接受代理(完整URI)請求。在cache攔截裏,它應該被激活。如果沒有用戶明確的配置使用squid做代理,那即使httpd_accel_with_proxy沒被激活,squid也能工作。
httpd_acces_single_host指令正常情況下被禁止,在早期版本的squid裏,它可能被默認激活。在cache攔截裏,它應明確被禁止。
假如攔截不止針對80端口,你也許該將httpd_accel_port設爲0。見附錄A的更多信息。
假如你沒有使用WCCP,就該準備開始發起攔截會話到squid了。通過使用瀏覽器來上網衝浪,或者使用squidclient發起測試請求,就可以放手一試。假如你使用WCCP,那麼還有許多步驟要完成。
9.5.1 配置WCCPv1
路由器不發送任何會話到squid,直到squid宣稱它自己是路由器。爲了讓squid那樣做,在squid.conf中增加如下行:
wccp_router 172.16.102.65 wccp_version 4
路由器有多個接口。請確認使用與squid相連的接口的IP地址。這點是必要的,因爲來自路由器的WCCP消息,將源IP地址設置爲外出接口的地址。假如源地址不匹配wccp_router值,squid會拒絕WCCP消息。
WCCPv1文檔規定4作爲協議版本號。然而,某些用戶報告,Cisco IOS 11.2僅支持協議版本3。假如你使用該版本的IOS,請在squid.conf裏改變版本號:
wccp_version 3
9.6 調試問題
HTTP攔截比較複雜,因爲許多不同設備必須組合正確工作。爲了幫助你跟蹤問題,如下是一個問題解答檢查列表:
- * 客戶端數據包正在通過路由器/交換機嗎?
- 在簡單網絡裏,這點顯而易見。你可以trace線纜並觀察指示燈的活動閃爍。然而在大而複雜的網絡,數據包可能走不同的路線。假如你的組織夠大,並有網絡sniffer設備,就可以觀察線路中web客戶端的請求數據包。低技術的方法是,斷開有問題的線路,並觀察是否影響客戶端的web瀏覽。
- * 路由器/交換機配置是否正確?
- 你也許要再次檢查路由器/交換機配置。假如你已配置了某個接口,那能否確保它正確呢?是否新的配置真正在設備上運行?也許在你保存配置之前,路由器/交換機已重啓了。在改變生效前,你或許需要reboot設備。
- * 交換機/路由器能與squid主機會話嗎?
- 能從路由器/交換機上ping通squid嗎?大部分4層攔截配置要求網絡設備和squid在同一子網裏。登陸路由器/交換機,確認能ping通squid的IP地址。
- * 交換機/路由器相信squid在運行嗎?
- 許多傳輸攔截設備不會發送會話到squid,除非它們知道squid是健壯的。使用調試命令來預覽squid的健壯性狀態。也許會發現三層健壯性檢測(例如ICMP ping)比四層檢測(例如HTTP)更容易,它使網絡設備更容易將squid標記爲存活狀態。
- * Squid實際在運行嗎?
- 請再次確認squid真正在運行,特別是在系統近期重啓過的情況下。
- * 數據包正在抵達squid主機嗎?
- 使用tcpdump能見到攔截的TCP連接。如下是示例:
# tcpdump -n -i eth0 port 80
假如使用WCCP,請檢查來自路由器的GRE包:# tcpdump -n -i eth0 ip proto gre
假如沒有看到tcpdump的任何輸出,則路由器/交換機可能沒有發送任何數據。在這種情況下,返回到以前的建議。 注意,假如設備正使用四層健壯性檢測,你可以在tcpdump的輸出裏見到這些。健壯性檢測來自路由器/交換機的IP地址,所以它們容易被認出。假如你見到健壯性檢測,但沒有其他數據,那可能意味着路由器/交換機正把squid的響應理解爲不健壯。例如,設備可能想見到200(OK)響應,但squid返回一個錯誤,例如401(未授權)或404(未發現)。請對access.log運行tail -f命令。 - * 激活了IP轉發嗎?
- 請再次確認squid運行的操作系統配置了IP包轉發。假如沒有,主機可能會丟棄攔截數據包,因爲目的IP地址並非本地。
- * 配置包過濾了嗎?
- 請確認包過濾器(例如ipfw,iptables,pf等)配置正確。當每件事都運行良好時,你能定期運行命令來顯示過濾規則,並觀察計數器增長。例如:
# ipfw show 300 ; sleep 3; ipfw show 300 00300 86216 8480458 fwd 127.0.0.1,3128 tcp from any to any 80 in 00300 86241 8482240 fwd 127.0.0.1,3128 tcp from any to any 80 in
注意該示例在FreeBSD上,包和字節計數器(第二和第三列)正在增長。 - * 環路接口起來和配置了嗎?
- 假如有一條規則轉發/重定向包到127.0.0.1,請確認環路接口(例如lo0,lo等)起來了,並配置過它。假如沒有,內核簡單的跳過這條轉發/重定向規則。
- * WCCP/GRE包被正確解開了嗎?
- 假如使用WCCP,請確認GRE包被正確解開。假如因爲某些理由,系統不知道該如何處理GRE包,那就在netstat -s的輸出裏會見到"unknown/unsupported protocol"計數器在增長。
# netstat -s | grep unknown 46 packets for unknown/unsupported protocol
假如OS有GRE接口,請頻繁運行netstat -i命令,觀察不斷增長的包數量:# netstat -in | grep ^gre0 Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll gre0 1476 <Link#4> 304452 0 0 4 0
另外,在GRE接口上運行tcpdump:# tcpdump -n -i gre0
- * Squid能響應客戶端嗎?
- 有可能路由器/交換機能發送包到squid,但squid不能將包發送回客戶端。這種情況可能發生在:防火牆過濾規則拒絕外出數據包,或squid沒有到客戶端IP地址的路由。爲了檢查這種情況,請運行netstat -n並觀察SYN_RCVD狀態的sockets:
% netstat -n Active Internet connections Proto Recv-Q Send-Q Local Address Foreign Address (state) tcp4 0 0 10.102.129.246.80 10.102.0.1.36260 SYN_RCVD tcp4 0 0 10.102.129.226.80 10.102.0.1.36259 SYN_RCVD tcp4 0 0 10.102.128.147.80 10.102.0.1.36258 SYN_RCVD tcp4 0 0 10.102.129.26.80 10.102.0.2.36257 SYN_RCVD tcp4 0 0 10.102.129.29.80 10.102.0.2.36255 SYN_RCVD tcp4 0 0 10.102.129.226.80 10.102.0.1.36254 SYN_RCVD tcp4 0 0 10.102.128.117.80 10.102.0.1.36253 SYN_RCVD tcp4 0 0 10.102.128.149.80 10.102.0.1.36252 SYN_RCVD
假如你看到這些,請使用ping和traceroute來確認squid能與客戶端雙向通信。 - * Squid能與原始服務器會話嗎?
- 假如squid不能連接到原始服務器,攔截HTTP連接會無法進行。如果這點發生,netstat會顯示許多連接在SYN_SENT狀態:
% netstat -n Active Internet connections Proto Recv-Q Send-Q Local Address Foreign Address (state) tcp4 0 0 172.16.102.66.5217 10.102.129.145.80 SYN_SENT tcp4 0 0 172.16.102.66.5216 10.102.129.224.80 SYN_SENT tcp4 0 0 172.16.102.66.5215 10.102.128.71.80 SYN_SENT tcp4 0 0 172.16.102.66.5214 10.102.129.209.80 SYN_SENT tcp4 0 0 172.16.102.66.5213 10.102.129.62.80 SYN_SENT tcp4 0 0 172.16.102.66.5212 10.102.129.160.80 SYN_SENT tcp4 0 0 172.16.102.66.5211 10.102.128.129.80 SYN_SENT tcp4 0 0 172.16.102.66.5210 10.102.129.44.80 SYN_SENT tcp4 0 0 172.16.102.66.5209 10.102.128.73.80 SYN_SENT tcp4 0 0 172.16.102.66.5208 10.102.128.43.80 SYN_SENT
再次用ping和traceroute來確認squid能與原始服務器會話。 - * 外出連接正被攔截嗎?
- 假如squid能ping通原始服務器,並且仍然見到大量的連接在SYN_SENT狀態,那麼路由器/交換機可能正在攔截squid的外出TCP連接。在某些情況下,squid能檢測到這種轉發循環,並寫警告日誌到cache.log。如此的轉發死循環能迅速耗光squid的所有文件描述符,這樣也會在cache.log裏產生警告。
假如你懷疑這個問題,請使用squidclient程序來發起一些簡單的HTTP請求。例如,該命令發起一條直接到原始服務器的HTTP請求:
% /usr/local/squid/bin/squidclient -p 80 -h slashdot.org /
假如該命令成功,你可以在屏幕上見到來自Slashdot站點的許多難看的HTML。然後通過squid來試試同樣的請求:
% /usr/local/squid/bin/squidclient -r -p 3128 -h 127.0.0.1 http://slashdot.org/
假如沒有在cache.log裏看到錯誤消息,就可以再次在屏幕上看到一些HTML。假如看到轉發循環錯誤,就必須重新配置交換機/路由器,以便它允許squid的外出連接正常通過,而不被攔截。