誠如 DEVCORE 在 “如何正確的取得使用者 IP?” 這篇文章中所提到,因為所有用戶端的資訊都是不可靠的,再加上眾多代理伺服器從中攪局,所以如何”正確的”取得用戶端 IP 位址這個看似再簡單不過的問題卻變得相當複雜,甚至無法在所有的情況下都能順利完成目的。
我在此先直接引用自該文章的結論
”那我們該怎麼處理呢?我的建議是記錄所有相關的 header 欄位存入資料庫,包含「REMOTE_ADDR」「X-Forwarded-For」等等,真正有犯罪事件發生時,就可以調出所有完整的 IP 資訊進行人工判斷,找出真正的 IP。當然從 header 存入的數值也可能會遭到攻擊者竄改插入特殊字元嘗試 SQL Injection,因此存入值必須先經過過濾,或者使用 Prepared Statement 進行存放。
可以參考的 HTTP Header(依照可能存放真實 IP 的順序)
- HTTP_CLIENT_IP
- HTTP_X_FORWARDED_FOR
- HTTP_X_FORWARDED
- HTTP_X_CLUSTER_CLIENT_IP
- HTTP_FORWARDED_FOR
- HTTP_FORWARDED
- REMOTE_ADDR (真實 IP 或是 Proxy IP)
- HTTP_VIA (參考經過的 Proxy)”
基本上,HAProxy 有兩種運作模式,一種稱之為 HTTP 模式。在 HTTP 模式下,可以對用戶端送來的檔頭做判斷與處理,當然也包含檔頭的新增與內容修改。而在 TCP 模式下,這些功能都無法獲得。或許有讀者會質疑運作在 TCP 模式下的 HAProxy 是否還算是一個代理伺服器?這種定義的問題,我並不想在此討論,而就實務的情況來看,這種情況就是真實存在的。
既然 TCP 模式比之 HTTP 模式就像被閹割般的無能,那為什麼我們還會需要使用 TCP 模式呢?常見的情況有二,一個是因為 SSL/TLS 加密的因素。如果你的網站支援 SSL/TLS 加密,那麼使用 HAProxy 後你有兩種選擇,一種是把 SSL/TLS 加解密交給 HAProxy,這種情況又稱之為 SSL Termination (請參考下圖)。SSL Termination 常用於 SSL 加速,另外也可能是為了 IDS/IDP/DLP 這類需要分析封包內容的安全機制而必須先將 SSL/TLS 連線予以解密。
但是因為 HAProxy 本身也是軟體形式,所以對於 SSL 加速沒有甚麼幫助,甚至有可能反而造成效能的瓶頸,所以第二種選擇就是把 SSL/TLS 加解密維持在原有的地方,例如 Web Server 本身。在這種情境下,因為所有經過 HAProxy 的封包內容都已經經過加密,所以 HAProxy 僅能透過 TCP 模式與後端 Web Server 溝通,而無法使用 HTTP 模式。
另外一個因素則是如果你需要代理的服務不是 HTTP,而是其他的網路服務 (像是 MySQL),當然就不能使用 HTTP 模式,而必需使用 TCP 模式。
而不幸的是,不管是在 HTTP 模式或 TCP 模式之下,都是由代理伺服器代替用戶端傳送封包,所以 REMOTE_ADDR 皆會顯示為代理伺服器的 IP 位址。還好在 HTTP 模式下,HAProxy 會加上 X-Forwarded-For 這個檔頭,所以我們還是有機會取得用戶端的真實 IP 位址。但是在 TCP 模式下,HARroxy 無法新增任何檔頭,也讓我們失去獲得用戶端真實 IP 位址的機會。
那麼我不用 HAProxy 不就沒事了?當然不是。即使是其他的 HTTP 代理伺服器,只要考慮到 SSL/TLS 加解密,就都有類似的問題。舉例來說,現在很熱門的 AWS ELB 在某些模式時也會有跟 HAProxy TCP 模式一樣的情形。
有鑑於此,HAProxy 特別訂出了所謂的 Proxy Protocol。只要是支援 Proxy Protocol 的代理伺服器與網路服務相互搭配,透過適當的設定就可以取得用戶端的 IP 位址。當然,這個用戶端 IP 位址,不一定是真實的用戶端 IP 位址,不過通常至少是連往代理伺服器的 IP 位址,而不是全部變成代理伺服器的 IP 位址。除了 HAProxy 本身的支援外,許多代理服務器或服務也都內建或透過外接模組的方式,提供此一功能的支援。詳細清單可參考這裡。
在下一篇文章中,我將使用 HAProxy + Apache 的 myfixip 模組當作範例,實際展示 Proxy Protocol 的運作。
相關連結:
沒有留言:
張貼留言