使用 Frp 爲你的 Web 服務添加 https 支持
frp 是一個可用於內網穿透的高性能的反向代理應用,支持 tcp, udp 協議,爲 http 和 https 應用協議提供了額外的能力,且嘗試性支持了點對點穿透。
在衆多反向代理應用中,frp 的最大特點就在於內網穿透。所以,如果你有將內網對外提供 Web 服務的需求,就可以考慮使用 frp 爲你的 Web 服務提供 https 支持。
下載 frp
前往 GitHub 下載 frp:
有適用於各種不同操作系統的 frp,如果你對外提供的公網服務器和實際提供 Web 服務的服務器不是同一臺機器的話,需要爲各自機器下載對應版本的 frp。
準備好 Web 服務和 SSL 證書
你可以用任何方式開發你的 Web 服務,注意你的 Web 服務需要監聽一個本機端口。
對於準備 SSL 證書,你可以參考我的另一篇博客:
對於本文的後續內容,你需要將證書導出成 Nginx 格式,即一個 crt 文件和一個 key 文件。
配置 frp
你需要準備運行一個 frp 服務端和一個 frp 客戶端。它們可以運行在不同的機器上,也可以運行在同一臺機器上。
鑑於 frp 的內網穿透的優勢,如果你將這兩個端部署在不同的機器上,就能夠實現 https 支持的同時也做到內網穿透——即你可以將 NAT 網絡中的一臺電腦對全球公開的互聯網提供服務。
當然,你也可以部署到同一臺機器上,這樣的優勢就是一個端口可以服務很多的 Web 服務,同時支持 https。
接下來的描述中,我用 A 機器表示 frp 服務端(也就是對公衆開放服務的一端),B 機器表示 frp 客戶端(提供 Web 服務的一端)。它們可以是同一臺機器,也可以是不同的機器。
反向代理服務端
A 機器需要修改 frps.ini 文件:
[common] bind_port = 7000 vhost_http_port = 80 vhost_https_port = 443
▲ bind_port 是 frp 服務端口,客戶端如果要使用 frp 服務則連接這個端口;vhost_http_port 是代理 http 的端口;vhost_https_port 是代理 https 的端口
配置完成之後,運行 frp 程序:
./frps -c ./frps.ini
▲ 對於 Linux 系統
./frps.exe -c ./frps.ini
▲ 對於 Windows 系統
於是,A 機器就配置好了。
反向代理客戶端
B 機器的配置將是 https 支持的重點:
[common] # 這裏填寫 A 機器的 IP 或者域名 server_addr = 100.13.*.* # 填寫 A 機器開放的 frp 服務端口,也就是 frps.ini 配置文件中 bind_port 的值 server_port = 7000 [walterlv_example_http] # 依然支持 http 訪問 type = http # 本地 Web 服務的端口 local_port = 10000 # 需要反向代理的域名(當訪客通過此域名訪問 A 機器時,纔會將請求反向代理到此 Web 服務) custom_domains = example.walterlv.com [walterlv_example] # 配置 https 訪問 type = https # 本地 Web 服務的端口(與前面的配置一樣,都對應同一個 Web 服務) local_port = 10000 # 需要反向代理的域名(當訪客通過此域名訪問 A 機器時,纔會將請求反向代理到此 Web 服務) custom_domains = example.walterlv.com # 接下來的配置是支持 https 的重點配置 # 配置插件,將 https 請求轉換成 http 請求後再發送給本地 Web 服務程序 plugin = https2http # 轉換成 http 後,發送到本機的 10000 端口 plugin_local_addr = 127.0.0.1:10000 # 可能是 frp 的 Bug?這裏必須寫成 127.0.0.1,稍後解釋 plugin_host_header_rewrite = 127.0.0.1 # 指定代理方式爲 frp plugin_header_X-From-Where = frp # 指定成你在前面部分導出的證書的路徑 plugin_crt_path = C:/Samples/_.walterlv.com_chain.crt plugin_key_path = C:/Samples/_.walterlv.com_key.key
這就是 frp 的特色,重點配置都放到了反向代理的客戶端中。這樣的配置方式安全性自然成了問題,但也正因爲如此,纔可以真正實現帶有內網穿透的反向代理。
接下來介紹以下這個文件裏面爲什麼是這樣配置的。
[Common]
節點是爲了與 frp 服務端取得聯繫的。所以 server_addr
和 server_port
自然成了必要,畢竟連接一個 Web 服務這是兩個必要的參數。如果你的兩個端部署在同一臺電腦上,那麼這裏可以填寫 127.0.0.1
。
[walterlv_example_http]
節點和 [walterlv_example]
兩個節點的名稱是隨便取的,不需要滿足什麼規律。唯一的要求是,連接到此 frp 服務端的所有客戶端之間,這個名稱都不能重複。frp 的服務端通過此名稱來區分不同的客戶端配置。因此,通常將這個名稱命名成域名或者功能名。
[walterlv_example_http]
節點配置來兼容 http 訪問。如果不配置這一個節點,那麼使用 http 訪問的訪客將得到 frp 服務器返回的 403 狀態碼。這裏的三項配置表示,如果使用 http 協議訪問此 frp 服務端,且訪問域名是 example.walterlv.com
(http 頭裏寫的),那麼將此請求轉發到 frp 客戶端本機的 10000
端口。
[walterlv_example]
節點的前三項與 [walterlv_example_http]
一樣,含義也是一樣的。接下來就是啓用 https2http
插件,將訪問 frp 服務端的 https 流量全部轉換成 http 流量,然後轉發給本機的 http 服務。 plugin_local_addr
就是指定轉發到本機的 10000
端口。當然你也可以寫成非本機的 http 服務,例如 walterlv.github.io:80
,這樣,https 流量轉換成 http 流量後會發給對應的機器。 plugin_host_header_rewrite
在目前(frp 0.31.1 版本),這個值必須寫成 127.0.0.1
,否則會出現錯誤的重定向(例如,如果指定成 example.walterlv.com
會導致流量回流到 frp 服務端,這絕對是反向代理的一個 Bug!)這個值的含義是修改 http 的請求頭,將請求頭中的域名部分改寫成 127.0.0.1
(在改寫之前,頭是 example.walterlv.com
)。 plugin_crt_path
和 plugin_key_path
指定爲 SSL 證書的路徑。 plugin_header_X-From-Where
則不是必須的。
工作原理
使用 frp 讓 Web 服務支持 https 的流程是一個典型的反向代理服務器的工作流程。
訪客在瀏覽器中輸入網址 https://blog.walterlv.com 後,瀏覽器會查詢
這裏值得注意的是,由於 frp 反向代理系統中,使用 SSL 證書的一端在 frp 客戶端,這意味着 frp 服務端完全無法得知此 https 請求的內容。於是在轉發後也無法得知此請求的真實來源(訪客 IP),這樣,真實的 Web 服務將無法得知真實的訪客信息。這也是 frp 在此設計下必然出現的缺陷。
如果你希望你的 Web 服務在 https 下破除這些限制,那麼建議使用其他的反向代理服務器。關於其他配置 https 的方法,你可以閱讀:
- 三種方法爲 ASP.NET Core 對外服務添加 https 支持(kestrel / frp / nginx)
- 使用 Kestrel 爲你的 ASP.NET Core 服務添加 https 支持
- 使用 Nginx 爲你的 Web 服務添加 https 支持
除了 frp 以外的方法都可以獲得真實的訪客信息。
參考資料