搜尋此網誌

2016年9月4日 星期日

[工具介紹] 以 Apache myfixip 模組支援 Proxy Protocol

我在前一篇文章中提到,HAProxy 推出 Proxy Protocol 用以解決在代理伺服器架構下無法取得用戶端 IP 位址的困境。在這篇文章中,我將以 HAProxy 搭配 Apache + myfixip 模組,實際展示如何透過 Proxy Protocol 解決用戶端 IP 位址的問題。
本次展示的架構如下 :

其中代理伺服器使用 CentOS 7 的 HAProxy 1.5.14 套件,而 Web 服務器使用 CentOS 7 的 Apache 套件。
我們先在 Web 服務器放置內容如下的 PHP 腳本,而在 PHP 中,$_SERVER["HTTP_X_FROWARDED_FOR"] 這個變數就代表 HTTP 檔頭 X-Forwarded-For。
<?php
echo nl2br("X-Forwarded-For: " . $_SERVER["HTTP_X_FORWARDED_FOR"] . PHP_EOL);
echo nl2br("Remote Address: " . $_SERVER["REMOTE_ADDR"] . PHP_EOL);

用戶端直接連結 Web 服務器

Direct
當用戶端直接連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
HTTP-X-Forwarded-For:
Remote Address: 192.168.10.10
在這種單純的情況下,直接使用 REMOTE_ADDR 變數即可取得用戶端的 IP 位址。

用戶端透過 HAProxy 的  HTTP 模式連往 Web 服務器

HTTP Mode
我們在 HAProxy 使用下列設定:
listen http-proxy
bind 0.0.0.0:80
mode http
option httpchk
option forwardfor
balance source
server web-1 192.168.10.12:80 weight 1 check
當用戶端透過代理伺服器 (192.168.10.11) 連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
X-Forwarded-For: 192.168.10.10
Remote Address: 192.168.10.11
在這種情況下,儘管 REMOTE_ADDR 變數顯示為代理伺服器的 IP 位址,但是使用 HTTP_X_FROWARDED_FOR 變數依舊可取得用戶端的 IP 位址。

用戶端透過 HAProxy 的  HTTP 模式連往 Web 服務器

TCP Mode
我們將 HAProxy 的設定修改成如下:
listen http-proxy
bind 0.0.0.0:80
mode tcp
balance source
server web-1 192.168.10.12:80 weight 1 check
當用戶端透過代理伺服器 (192.168.10.11) 連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
X-Forwarded-For:
Remote Address: 192.168.10.11
在這種情況下,不存在 HTTP_X_FROWARDED_FOR 變數,而 REMOTE_ADDR 顯示的也是代理伺服器 IP 位址。也就是程式無法正確獲得用戶端 IP 位址。

用戶端透過支援 Proxy Protocol 的設定連往 Web 服務器

Proxy Protocol
因為 HAProxy 本身就內建支援 Proxy Protocol,所以不需安裝額外的套件,只要把設定修改成如下即可:
listen http-proxy
bind 0.0.0.0:80
mode tcp
balance source
server web-1 192.168.10.12:80 weight 1 check send-proxy
至於 Apache 的部分,因為 Apache 目前尚未內建 Proxy Protocol 的支援,所以我們需要安裝另外的模組。在此,我以 myfixip 模組為例,相關步驟如下:
yum install git gcc httpd-devel
cd /tmp
git clone https://github.com/ggrandes/apache24-modules.git
cd apache24-modules
apxs –i –c mod_myfixip.c
新增檔案 /etc/httpd/conf.modules.d/00-myfixip.conf,內容如下
LoadModule myfixip_module modules/mod_myfixip.so
<IfModule mod_myfixip.c>
   RewriteIPResetHeader off
   RewriteIPAllow 192.168.10.0/24 127.0.0.1
</IfModule>
systemctl restart httpd
需特別注意的是因為 Proxy Protocol 會改變代理伺服器與 Web 服務器之間的溝通方式,所以需要兩邊同時啟用,否則將無法正常運作,甚至可能導致無法存取服務。
當用戶端再次透過代理伺服器 (192.168.10.11) 連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
X-Forwarded-For:
Remote Address: 192.168.10.10
在這種情況下,即使經過代理伺服器的作用,但是 REMOTE_ADDR 變數依舊可以正確地顯示出用戶端的 IP 位址。

透過 myfixip 模組,可以讓 Apache 支援 Proxy Protocol,有效地取得用戶端 IP 位址。
除了 HAProxy 之外,如果你使用了 AWS ELB 的 TCP 或 SSL 模式,透過 myfixip 擴充 Apache 使其支援 Proxy Protocol 也同樣會有很大的助益。比較麻煩的是,目前 AWS 的管理介面並不支援 Proxy Protocol 的設定,而必須使用命令列模式控制 Proxy Protocol 的開啟或關閉。有需要的讀者可參考官方文件

About