【轉】Nginx通過二級目錄(路徑)映射不同的反向代理,規避IP+端口訪問

這是我上一家公司的案例總結,發現躺在草稿箱好幾個月了,今天得空就整理髮布一下。

先說一下開發那邊提來的 2 個 case:

①、同一個域名需要反向代理到前臺和後臺(不同機器和端口);

②、需要採用 IP+端口的模式,嵌入到 APP 作爲 DNS 污染後的備選方案。

image

對於第①個問題,很好解決:通過區分二級目錄來反代不同的節點即可,所以代碼類似如下:

server {
listen 80;
server_name demo.domain.com;
#通過訪問 service 二級目錄來訪問後臺
location /service/ {
#DemoBackend1 後面的斜槓是一個關鍵,沒有斜槓的話就會傳遞 service 到後端節點導致 404
proxy_pass http://DemoBackend1/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#其他路徑默認訪問前臺網站
location / {
proxy_pass http://DemoBackend2;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

#簡單的負載均衡節點配置
upstream DemoBackend1 {
server 192.168.1.1;
server 192.168.1.2;
ip_hash;
}
upstream DemoBackend2 {
server 192.168.2.1;
server 192.168.2.2;
ip_hash;
}

如上配置即可實現通過一個域名來反代不同的後端節點,用到的思路就是匹配二級目錄來反代。

對於第②個問題,可能粗略一看,還沒理解是個啥意思吧!

其實就是現在業界流行的一種防 DNS 污染的解決方案之一:手機 APP 裏面除了通過域名來獲取數據,還會額外嵌入一些備用的 IP。APP 在獲取數據時,會先通過域名向服務器發起一個簡單的校驗請求,如果得到的不是預期數據,說明該網絡環境下的 DNS 已被污染,比如被運營商劫持,請求 A 內容卻給你展示 B 內容!這時候,APP 將會啓動備用預案,通過 IP 的方式來請求數據!很明顯,這個做法可以有效避免噁心的 DNS 劫持了(看完這段是不是有所收穫呢?)。

做法很簡單,就是在 APP 中集成多個 IP 和端口作爲備用的訪問途徑。

當開發 GG 找到我,提出的需求是:

需要實現公網 IP+端口來訪問,比如郵件 API 使用 http://192.168.1.10:125

Ps:公網服務器是多線的,那麼就有多個 IP,本文假設電信是 192.168.1.10,聯通是 192.168.2.10,移動是 192.168.3.10 等

說白了就是要用端口來區分不同的 API,此時如果我不深究,順手可能會寫出如下配置:

#API1
server {
listen 125;
server_name 192.168.1.10 192.168.2.10 192.168.3.10;
location / {
proxy_pass http://DemoBackend;
proxy_redirect off;
proxy_set_header Host api1.domain.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
#API2
server {
listen 126;
server_name 192.168.1.1 192.168.2.1 192.168.3.1;
location / {
proxy_pass http://DemoBackend;
proxy_redirect off;
proxy_set_header Host api2.domain.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
##API n 等等....

粗略一看,確實是可以實現開發 GG 的要求啊!再仔細一想,你會發現如此做法會開放越來越多的端口!運維成本以及辨識度低還只是其次,咱說好的安全第一呢?

經過思考和測試,我寫出的最終配置如下:

#新增的 IP 映射配置
server {
listen 80;
server_name 192.168.1.10 192.168.2.10 192.168.3.10;
location /mail_api/ {
proxy_pass http://DemoBackend/; #後面的斜槓不能少,作用是不往後端傳遞/mail-api 這個路徑
proxy_redirect off;
proxy_set_header Host mailapi.domain.com; #傳遞不同的 host 給後方節點,實現 IP 和域名均可以訪問
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /other_api1/ {
proxy_pass http://DemoBackend/;
proxy_redirect off;
proxy_set_header Host otherapi1.domain.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#還可以添加更多映射,通過不同的路徑來映射不同的 API,最後對於直接訪問 IP 則返回 403,防網絡上的掃碼探測
location / {
return 403;
}
}

#原有的域名映射
server {
listen 80;
server_name mailapi.domain.com;
location / {
proxy_pass http://DemoBackend;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 80;
server_name otherapi1.domain.com;
location / {
proxy_pass http://DemoBackend;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
#簡單的節點配置(當這些 API 都用到同一個 Backend 時,上述代碼中的 proxy_set_header 傳遞的 host 就起到了關鍵性作用!)
upstream DemoBackend {
server 192.168.10.1;
server 192.168.10.2;
ip_hash;
}

最終實現的效果就是:你要通過 IP 請求郵件 API,只要請求 http://192.168.1.1/mail_api/ 即可,而不需要開放多餘端口。而且,後續要新增更多 API,只需要定義不同的二級路徑即可,這些二級路徑的辨識度可比端口要好得多!

Ps:正如代碼中的註釋,示例代碼只用了一個 DemoBackend 節點配置,爲的是分享另一個小技巧:當後端節點承載了多個站點而且都是監聽 80 端口時(比如某些小公司同一個 IIS 服務器部署了 N 個站點),反向代理中的 proxy_set_header 參數,可以自定義傳遞一個 host 域名給後端節點,從而正確響應預期內容!

這段解釋有點無力,還是拿實際例子舉例吧!

我之前供職的公司節點用的是 IIS 服務器,前端用 Nginx 反向代理,IIS 服務器上有多個站點,站點之間部分會通過 rewrite 規則聯繫起來。

打個比方:比如 A 網站有個專題內容(www.a.com/zt/)是通過 IIS 僞靜態映射到了 B 網站(content.b.com)。也就是訪問到 http://www.a.com/zt/,其實最後是通過 A 網站映射到了 B 網站上面。

後來發現 IIS 有個僞靜態 BUG,會經常奔潰,就要我用前端的 Nginx 來實現直接映射,而不再走 IIS 的 A 網站中轉。

那麼這個需求就正好用到了 proxy_set_header 技巧,一看就懂:

server {
listen 80;
server_name www.a.com;
location /zt/ {
proxy_pass http://ABackend; #都是相同的節點,此示例代碼我就不寫 upstream 了
proxy_redirect off;
proxy_set_header Host www.b.com; #這裏就是關鍵性作用,傳遞 b 域名給後端 IIS
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

#upstream 略..

很明顯,通過傳遞自定義域名,就可以實現通過 A 網站訪問 Nginx,返回 B 網站內容,和反向代理谷歌的原理是一致的。

當然,上文爲了實現 IP 和域名都可以訪問,這個 proxy_set_header 設置也是必須的。說白了就是在反代過程中,對後端服務器僞裝(傳遞)了一個自定域名,讓後端響應該域名預期內容。

當然,在之前張戈博客分享的《分享幾個 WordPress 本地緩存 gravatar 評論頭像的方案》一文中也用到了這個技巧,感興趣的朋友可以前往查看。

本文分享的經驗,其實比較簡單,主要就是通過不同路徑來反代不同的目標。估計很多大拿早就用爛了吧!不過值得注意的是,通過自定義路徑反代,需要注意 proxy_pass 參數後面是否需要斜槓,避免將自定義的路徑傳遞到後端節點,導致訪問 404!

81

相關文章

APISIX高級路由之301/302跳轉配置3月18日 · 2022年

分享一個APISIX網關返回502的典型案例2月25日 · 2022年

APISIX運維優化之配置文件自動化生成方案11月14日 · 2021年

APISIX插件開發之Kong網關HMAC鑑權插件(附客戶端SDK)11月7日 · 2021年

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章