在nginx中使用proxy protocol協(xié)議
簡(jiǎn)介
我們已經(jīng)介紹了haproxy提出的proxy protocol協(xié)議,通過(guò)proxy protocol協(xié)議,服務(wù)器端可以獲得客戶端的真實(shí)IP地址和端口,從而可以進(jìn)行一些非常有意義的操作。
為什么獲得客戶端的真實(shí)IP地址會(huì)非常有意義呢?
考慮一個(gè)藏在proxy背后的數(shù)據(jù)庫(kù),如果有多個(gè)客戶端通過(guò)proxy進(jìn)行數(shù)據(jù)庫(kù)的連接,事實(shí)上因?yàn)槎际峭ㄟ^(guò)代理進(jìn)行連接,所以數(shù)據(jù)庫(kù)中的所有的操作都是proxy服務(wù)器的IP地址,這在對(duì)數(shù)據(jù)庫(kù)的性能監(jiān)控和優(yōu)化是不利的,因?yàn)槲覀儾恢勒鎸?shí)異常的服務(wù)器IP地址。
這種情況下就需要用到proxy protocol協(xié)議,讓數(shù)據(jù)庫(kù)可以反映出真實(shí)客戶端的IP地址,從而便于數(shù)據(jù)庫(kù)的監(jiān)控和問(wèn)題定位。
事實(shí)上,數(shù)據(jù)庫(kù)只是一個(gè)特定的例子,我們?cè)诤芏嗥渌那闆r下也可能需要知道客戶端真實(shí)IP的情況。
以現(xiàn)在最流行的http服務(wù)器和代理服務(wù)器nginx為例,我們來(lái)看一下如何在nginx中配置proxy protocol。
proxy protocol在nginx中應(yīng)用
我們知道nginx是一個(gè)web服務(wù)器和代理服務(wù)器,它一般工作在proxy server或者負(fù)載均衡軟件(Haproxy,Amazon Elastic Load Balancer (ELB)的后面。
客戶端首先請(qǐng)求proxy server或者LSB負(fù)載均衡軟件,然后再到nginx進(jìn)行真實(shí)的web訪問(wèn)。
因?yàn)榻?jīng)過(guò)了多層軟件,所以客戶端的一些信息比如ip地址,端口號(hào)等可能就會(huì)被隱藏,這對(duì)于我們問(wèn)題分析,數(shù)據(jù)統(tǒng)計(jì)都是不利的。因?yàn)閷?duì)于nginx來(lái)說(shuō),我們希望能夠獲得真實(shí)的客戶端IP地址,這樣才能獲取真實(shí)的請(qǐng)求環(huán)境。
這種情況下就需要用到PROXY protocol了。
如果前面所說(shuō)的proxy或者LSB都實(shí)現(xiàn)了PROXY protocol協(xié)議的話,不管是HTTP, SSL, HTTP/2, SPDY, WebSocket 還是 TCP協(xié)議,nginx都可以拿到客戶端的原始IP地址,從而根據(jù)原始IP地址進(jìn)行一些特殊的操作,比如屏蔽惡意IP的訪問(wèn),根據(jù)IP不同展示不同的語(yǔ)言或者頁(yè)面,或者更加簡(jiǎn)單的日志記錄和統(tǒng)計(jì)等,都非常有效。
當(dāng)然,如果想要支持PROXY protocol,對(duì)nginx的版本也是有要求的,具體版本需求如下:
想要支持PROXY protocol v2,需要NGINX Plus R16或者NGINX Open Source 1.13.11。
想要支持ROXY protocol for HTTP,需要NGINX Plus R3或者NGINX Open Source 1.5.12。
想要支持TCP client?side PROXY protocol,需要NGINX Plus R7或者 NGINX Open Source 1.9.3。
想要支持PROXY protocol for TCP,需要NGINX Plus R11 或者 NGINX Open Source 1.11.4。
在nginx中可以通過(guò)下面的變量來(lái)獲得對(duì)應(yīng)的客戶端信息,具體而言如下所示:
$proxy_protocol_addr和$proxy_protocol_port分別表示的是原始客戶端的IP地址和端口號(hào)。
$remote_addr和$remote_port表示的是load balancer的的IP地址和端口。
如果你使用了RealIP擴(kuò)展模塊,那么這個(gè)模塊會(huì)重寫(xiě)$remote_addr和$remote_port這兩個(gè)值,將其替換成原始客戶端的IP地址和端口號(hào)。
然后使用$realip_remote_addr和$realip_remote_port來(lái)表示load balancer的的IP地址和端口。
在RealIP擴(kuò)展模塊中,$proxy_protocol_addr和$proxy_protocol_port表示的含義不變,還是原始客戶端的IP地址和端口號(hào)。
在nginx中配置使用proxy protocol
上面我們提到了nginx中proxy protocol的基本應(yīng)用,下面來(lái)講一下如何在nginx中進(jìn)行具體的配置。
在nginx中啟用proxy protocol
如果你的nginx已經(jīng)是支持proxy protocol的版本,那么啟用proxy protocol非常簡(jiǎn)單,只需要在server中的listen中添加proxy_protocol即可,如下所示:
可能大家比較熟悉的是http block,這表示的是nginx對(duì)http/https的支持。stream模塊可能大家比較陌生,這是nginx提供的對(duì)tcp/udp協(xié)議的支持。
通過(guò)上面的配置,nginx可以實(shí)現(xiàn)在tcp/udp協(xié)議和http/https協(xié)議同時(shí)支持proxy protocol。
使用Real?IP modules
Real?IP modules是nginx自帶的一個(gè)模塊,可以通過(guò)下面的命令來(lái)查看nginx是否有安裝real-ip模塊:
如果你當(dāng)前使用的版本沒(méi)有real ip,也不要急,這時(shí)候你可能需要從源代碼進(jìn)行編譯。
在編譯的過(guò)程中,我們需要執(zhí)行一個(gè)configure命令,在這個(gè)configure命令中可以指定要開(kāi)啟的功能,比如stream或者h(yuǎn)ttp_ssl_module:
如果要開(kāi)啟real-ip功能,則可以添加:
如果nginx是運(yùn)行在SLB或者proxy之后的,那么可以通過(guò)set_real_ip_from命令來(lái)指定代理或者負(fù)載均衡服務(wù)器的IP范圍,如下所示:
然后我們需要將proxy或者SLB的IP地址替換成為真實(shí)客戶端的地址,那么可以這樣使用:
請(qǐng)求轉(zhuǎn)發(fā)
不管是http還是stream block,都可能遇到請(qǐng)求向后續(xù)的upstream進(jìn)行轉(zhuǎn)發(fā)的情況,對(duì)于upstream來(lái)說(shuō),他們希望收到的是真實(shí)客戶端IP地址,而不是proxy或者slb的地址,那么可以通過(guò)下面的設(shè)置來(lái)解決:
http和stream的設(shè)置方式是不同的。
日志記錄
日志是一個(gè)非常重要的功能,對(duì)于定位問(wèn)題,執(zhí)行數(shù)據(jù)統(tǒng)計(jì)分析都非常有用,當(dāng)然我們需要的是真實(shí)的客戶端IP地址。
我們可以通過(guò)使用變量$proxy_protocol_addr在http和stream block中記錄對(duì)應(yīng)的日志,如下所示:
總結(jié)
通過(guò)上面的設(shè)置,nginx已經(jīng)可以使用proxoy protocol了,這會(huì)讓我們的后續(xù)分析工作變得更加輕松。
更多內(nèi)容請(qǐng)參考 http://www.flydean.com/02-nginx-proxy-protocol/
