簡介
frp 是一個可用於內網穿透的高性能的反向代理應用,支持 tcp, udp 協議,爲 http 和 https 應用協議提供了額外的能力,且嘗試性支持了點對點穿透。
一、架構
二、使用示例
根據對應的操作系統及架構,從 Release 頁面下載最新版本的程序。
或者使用下面這樣的命令:
wget https://github.com/fatedier/frp/releases/download/v0.29.0/frp_0.29.0_linux_amd64.tar.gz
下載完成後解壓:
tar -xvf frp_0.29.0_linux_amd64.tar.gz
將 frps
及 frps.ini
放到具有公網 IP 的機器上。
將 frpc
及 frpc.ini
放到處於內網環境的機器上。
①通過 ssh 訪問公司內網機器
- 修改
frps.ini
文件,這裏使用了最簡化的配置:
# frps.ini
[common]
bind_port = 7000
- 啓動
frps
:
./frps -c ./frps.ini
- 修改
frpc.ini
文件,假設frps
所在服務器的公網 IP 爲x.x.x.x
;
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000
- 啓動 frpc:
./frpc -c ./frpc.ini
- 通過 ssh 訪問內網機器,假設用戶名爲
test
:
ssh -oPort=6000 [email protected]
②通過自定義域名訪問部署於內網的 web 服務
有時想要讓其他人通過域名訪問或者測試我們在本地搭建的 web 服務,但是由於本地機器沒有公網 IP,無法將域名解析到本地的機器,通過 frp 就可以實現這一功能,以下示例爲 http
服務,https
服務配置方法相同, vhost_http_port
替換爲 vhost_https_port
, type
設置爲 https
即可。
- 修改
frps.ini
文件,設置http
訪問端口爲8080
:
# frps.ini
[common]
bind_port = 7000
vhost_http_port = 8080
- 啓動
frps
:
./frps -c ./frps.ini
- 修改
frpc.ini
文件,假設frps
所在的服務器的 IP 爲x.x.x.x
,local_port
爲本地機器上 web 服務對應的端口, 綁定自定義域名www.yourdomain.com
:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[web]
type = http
local_port = 80
custom_domains = www.yourdomain.com
- 啓動 frpc:
./frpc -c ./frpc.ini
-
將
www.yourdomain.com
的域名A
記錄解析到 IPx.x.x.x
,如果服務器已經有對應的域名,也可以將CNAME
記錄解析到服務器原先的域名。 -
通過瀏覽器訪問
http://www.yourdomain.com:8080
即可訪問到處於內網機器上的 web 服務。
③轉發 DNS 查詢請求
DNS 查詢請求通常使用 UDP
協議,frp 支持對內網 UDP
服務的穿透,配置方式和 TCP
基本一致。
- 修改
frps.ini
文件:
# frps.ini
[common]
bind_port = 7000
- 啓動 frps:
./frps -c ./frps.ini
- 修改
frpc.ini
文件,設置frps
所在服務器的IP
爲x.x.x.x
,轉發到 Google 的 DNS 查詢服務器8.8.8.8
的udp 53
端口:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[dns]
type = udp
local_ip = 8.8.8.8
local_port = 53
remote_port = 6000
- 啓動
frpc
:
./frpc -c ./frpc.ini
- 通過
dig
測試UDP
包轉發是否成功,預期會返回www.google.com
域名的解析結果:
dig @x.x.x.x -p 6000 www.google.com
④轉發 Unix 域套接字
通過 tcp 端口訪問內網的 unix域套接字(例如和 docker daemon 通信)。
frps
的部署步驟同上。
- 啓動
frpc
,啓用unix_domain_socket
插件,配置如下:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[unix_domain_socket]
type = tcp
remote_port = 6000
plugin = unix_domain_socket
plugin_unix_path = /var/run/docker.sock
- 通過
curl
命令查看 docker 版本信息
curl http://x.x.x.x:6000/version
⑤對外提供簡單的文件訪問服務
通過 static_file
插件可以對外提供一個簡單的基於 HTTP 的文件訪問服務。
frps
的部署步驟同上。
- 啓動
frpc
,啓用static_file
插件,配置如下:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[test_static_file]
type = tcp
remote_port = 6000
plugin = static_file
# 要對外暴露的文件目錄
plugin_local_path = /tmp/file
# 訪問 url 中會被去除的前綴,保留的內容即爲要訪問的文件路徑
plugin_strip_prefix = static
plugin_http_user = abc
plugin_http_passwd = abc
- 通過瀏覽器訪問
http://x.x.x.x:6000/static/
來查看位於/tmp/file
目錄下的文件,會要求輸入已設置好的用戶名和密碼。
⑥爲本地 HTTP 服務啓用 HTTPS
通過 https2http
插件可以讓本地 HTTP
服務轉換成 HTTPS
服務對外提供。
- 啓用
frpc
,啓用https2http
插件,配置如下:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[test_htts2http]
type = https
custom_domains = test.yourdomain.com
plugin = https2http
plugin_local_addr = 127.0.0.1:80
# HTTPS 證書相關的配置
plugin_crt_path = ./server.crt
plugin_key_path = ./server.key
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
- 通過瀏覽器訪問
https://test.yourdomain.com
即可。
⑦安全地暴露內網服務
對於某些服務來說如果直接暴露於公網上將會存在安全隱患。
使用 stcp(secret tcp)
類型的代理可以避免讓任何人都能訪問到要穿透的服務,但是訪問者也需要運行另外一個 frpc
。
以下示例將會創建一個只有自己能訪問到的 ssh 服務代理。
frps
的部署步驟同上。
- 啓動
frpc
,轉發內網的ssh
服務,配置如下,不需要指定遠程端口:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[secret_ssh]
type = stcp
# 只有 sk 一致的用戶才能訪問到此服務
sk = abcdefg
local_ip = 127.0.0.1
local_port = 22
- 在要訪問這個服務的機器上啓動另外一個
frpc
,配置如下:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[secret_ssh_visitor]
type = stcp
# stcp 的訪問者
role = visitor
# 要訪問的 stcp 代理的名字
server_name = secret_ssh
sk = abcdefg
# 綁定本地端口用於訪問 ssh 服務
bind_addr = 127.0.0.1
bind_port = 6000
- 通過
ssh
訪問內網機器,假設用戶名爲test
:
ssh -oPort=6000 [email protected]
⑧點對點內網穿透
frp
提供了一種新的代理類型 xtcp
用於應對在希望傳輸大量數據且流量不經過服務器的場景。
使用方式同 stcp
類似,需要在兩邊都部署上 frpc
用於建立直接的連接。
目前處於開發的初級階段,並不能穿透所有類型的 NAT
設備,所以穿透成功率較低。穿透失敗時可以嘗試 stcp
的方式。
frps
除正常配置外需要額外配置一個udp
端口用於支持該類型的客戶端:
bind_udp_port = 7001
- 啓動 frpc,轉發內網的 ssh 服務,配置如下,不需要指定遠程端口:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[p2p_ssh]
type = xtcp
# 只有 sk 一致的用戶才能訪問到此服務
sk = abcdefg
local_ip = 127.0.0.1
local_port = 22
- 在要訪問這個服務的機器上啓動另外一個 frpc,配置如下:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[p2p_ssh_visitor]
type = xtcp
# xtcp 的訪問者
role = visitor
# 要訪問的 xtcp 代理的名字
server_name = p2p_ssh
sk = abcdefg
# 綁定本地端口用於訪問 ssh 服務
bind_addr = 127.0.0.1
bind_port = 6000
- 通過
ssh
訪問內網機器,假設用戶名爲test
:
ssh -oPort=6000 [email protected]
三、功能說明
1. 配置文件
由於 frp
目前支持的功能和配置項較多,未在文檔中列出的功能可以從完整的示例配置文件中發現。
2. 配置文件模版渲染
配置文件支持使用系統環境變量進行模版渲染,模版格式採用 Go 的標準格式。
示例配置如下:
# frpc.ini
[common]
server_addr = {{ .Envs.FRP_SERVER_ADDR }}
server_port = 7000
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = {{ .Envs.FRP_SSH_REMOTE_PORT }}
啓動 frpc 程序:
export FRP_SERVER_ADDR="x.x.x.x"
export FRP_SSH_REMOTE_PORT="6000"
./frpc -c ./frpc.ini
frpc
會自動使用環境變量渲染配置文件模版,所有環境變量需要以 .Envs
爲前綴。
3. Dashboard
通過瀏覽器查看 frp
的狀態以及代理統計信息展示。
注:Dashboard 尚未針對大量的 proxy 數據展示做優化,如果出現 Dashboard 訪問較慢的情況,請不要啓用此功能。
需要在 frps.ini
中指定 dashboard
服務使用的端口,即可開啓此功能:
[common]
dashboard_port = 7500
# dashboard 用戶名密碼,默認都爲 admin
dashboard_user = admin
dashboard_pwd = admin
打開瀏覽器通過http://[server_addr]:7500
訪問 dashboard
界面,用戶名密碼默認爲 admin
。
4. Admin UI
Admin UI
可以幫助用戶通過瀏覽器來查詢和管理客戶端的 proxy
狀態和配置。
需要在 frpc.ini
中指定 admin
服務使用的端口,即可開啓此功能:
[common]
admin_addr = 127.0.0.1
admin_port = 7400
admin_user = admin
admin_pwd = admin
打開瀏覽器通過 http://127.0.0.1:7400
訪問 Admin UI
,用戶名密碼默認爲 admin
。
如果想要在外網環境訪問 Admin UI
,將 7400
端口映射出去即可,但需要重視安全風險。
5. 身份驗證
服務端和客戶端的 common
配置中的 token
參數一致則身份驗證通過。
6. 加密與壓縮
這兩個功能默認是不開啓的,需要在 frpc.ini
中通過配置來爲指定的代理啓用加密與壓縮的功能,壓縮算法使用 snappy
:
# frpc.ini
[ssh]
type = tcp
local_port = 22
remote_port = 6000
use_encryption = true
use_compression = true
如果公司內網防火牆對外網訪問進行了流量識別與屏蔽,例如禁止了 ssh
協議等,通過設置 use_encryption = true
,將 frpc
與 frps
之間的通信內容加密傳輸,將會有效防止流量被攔截。
如果傳輸的報文長度較長,通過設置 use_compression = true
對傳輸內容進行壓縮,可以有效減小 frpc
與 frps
之間的網絡流量,加快流量轉發速度,但是會額外消耗一些 cpu 資源。
TLS
從 v0.25.0 版本開始 frpc
和 frps
之間支持通過 TLS 協議加密傳輸。通過在 frpc.ini
的 common
中配置 tls_enable = true
來啓用此功能,安全性更高。
爲了端口複用,frp
建立 TLS 連接的第一個字節爲 0x17
。
注意: 啓用此功能後除 xtcp
外,不需要再設置 use_encryption
。
7. 客戶端熱加載配置文件
當修改了 frpc 中的代理配置,可以通過 frpc reload
命令來動態加載配置文件,通常會在 10 秒內完成代理的更新。
啓用此功能需要在 frpc 中啓用 admin 端口,用於提供 API 服務。配置如下:
# frpc.ini
[common]
admin_addr = 127.0.0.1
admin_port = 7400
之後執行重啓命令:
frpc reload -c ./frpc.ini
等待一段時間後客戶端會根據新的配置文件創建、更新、刪除代理。
需要注意的是,[common] 中的參數除了 start 外目前無法被修改。
8. 客戶端查看代理狀態
frpc
支持通過 frpc status -c ./frpc.ini
命令查看代理的狀態信息,此功能需要在 frpc
中配置 admin
端口。
9. 端口白名單
爲了防止端口被濫用,可以手動指定允許哪些端口被使用,在 frps.ini
中通過 allow_ports
來指定:
# frps.ini
[common]
allow_ports = 2000-3000,3001,3003,4000-50000
allow_ports
可以配置允許使用的某個指定端口或者是一個範圍內的所有端口,以 ,
分隔,指定的範圍以 -
分隔。
10. 端口複用
目前 frps
中的 vhost_http_port
和 vhost_https_port
支持配置成和bind_port
爲同一個端口,frps
會對連接的協議進行分析,之後進行不同的處理。
例如在某些限制較嚴格的網絡環境中,可以將 bind_port
和 vhost_https_port
都設置爲 443
。
後續會嘗試允許多個 proxy
綁定同一個遠端端口的不同協議。
11. TCP 多路複用
從 v0.10.0 版本開始,客戶端和服務器端之間的連接支持多路複用,不再需要爲每一個用戶請求創建一個連接,使連接建立的延遲降低,並且避免了大量文件描述符的佔用,使 frp
可以承載更高的併發數。
該功能默認啓用,如需關閉,可以在 frps.ini
和 frpc.ini
中配置,該配置項在服務端和客戶端必須一致:
# frps.ini 和 frpc.ini 中
[common]
tcp_mux = false
12. 底層通信可選 kcp 協議
底層通信協議支持選擇 kcp 協議,在弱網環境下傳輸效率提升明顯,但是會有一些額外的流量消耗。
開啓 kcp 協議支持:
- 在 frps.ini 中啓用 kcp 協議支持,指定一個 udp 端口用於接收客戶端請求:
# frps.ini
[common]
bind_port = 7000
# kcp 綁定的是 udp 端口,可以和 bind_port 一樣
kcp_bind_port = 7000
- 在 frpc.ini 指定需要使用的協議類型,目前只支持 tcp 和 kcp。其他代理配置不需要變更:
# frpc.ini
[common]
server_addr = x.x.x.x
# server_port 指定爲 frps 的 kcp_bind_port
server_port = 7000
protocol = kcp
- 像之前一樣使用 frp,需要注意開放相關機器上的 udp 的端口的訪問權限。
13. 連接池
默認情況下,當用戶請求建立連接後,frps
纔會請求 frpc
主動與後端服務建立一個連接。當爲指定的代理啓用連接池後,frp 會預先和後端服務建立起指定數量的連接,每次接收到用戶請求後,會從連接池中取出一個連接和用戶連接關聯起來,避免了等待與後端服務建立連接以及 frpc
和 frps
之間傳遞控制信息的時間。
這一功能比較適合有大量短連接請求時開啓。
- 首先可以在 frps.ini 中設置每個代理可以創建的連接池上限,避免大量資源佔用,客戶端設置超過此配置後會被調整到當前值:
# frps.ini
[common]
max_pool_count = 5
在 frpc.ini 中爲客戶端啓用連接池,指定預創建連接的數量:
# frpc.ini
[common]
pool_count = 1
14. 負載均衡
可以將多個相同類型的 proxy 加入到同一個 group 中,從而實現負載均衡的功能。
目前只支持 TCP 和 HTTP 類型的 proxy。
# frpc.ini
[test1]
type = tcp
local_port = 8080
remote_port = 80
group = web
group_key = 123
[test2]
type = tcp
local_port = 8081
remote_port = 80
group = web
group_key = 123
用戶連接 frps
服務器的 80
端口,frps
會將接收到的用戶連接隨機分發給其中一個存活的 proxy
。這樣可以在一臺 frpc
機器掛掉後仍然有其他節點能夠提供服務。
TCP
類型代理要求 group_key
相同,做權限驗證,且 remote_port
相同。
HTTP
類型代理要求 group_key
, custom_domains
或 subdomain
和 locations
相同。
15. 健康檢查
通過給 proxy
加上健康檢查的功能,可以在要反向代理的服務出現故障時,將這個服務從 frps
中摘除,搭配負載均衡的功能,可以用來實現高可用的架構,避免服務單點故障。
在每一個 proxy
的配置下加上 health_check_type = {type}
來啓用健康檢查功能。
type
目前可選 tcp
和 http
。
tcp
只要能夠建立連接則認爲服務正常,http
會發送一個 http
請求,服務需要返回 2xx
的狀態碼纔會被認爲正常。
tcp 示例配置如下:
# frpc.ini
[test1]
type = tcp
local_port = 22
remote_port = 6000
# 啓用健康檢查,類型爲 tcp
health_check_type = tcp
# 建立連接超時時間爲 3 秒
health_check_timeout_s = 3
# 連續 3 次檢查失敗,此 proxy 會被摘除
health_check_max_failed = 3
# 每隔 10 秒進行一次健康檢查
health_check_interval_s = 10
http 示例配置如下:
# frpc.ini
[web]
type = http
local_ip = 127.0.0.1
local_port = 80
custom_domains = test.yourdomain.com
# 啓用健康檢查,類型爲 http
health_check_type = http
# 健康檢查發送 http 請求的 url,後端服務需要返回 2xx 的 http 狀態碼
health_check_url = /status
health_check_interval_s = 10
health_check_max_failed = 3
health_check_timeout_s = 3
16. 修改 Host Header
通常情況下 frp
不會修改轉發的任何數據。但有一些後端服務會根據 http
請求 header
中的 host
字段來展現不同的網站,例如 nginx
的虛擬主機服務,啓用 host-header
的修改功能可以動態修改 http
請求中的 host
字段。該功能僅限於 http
類型的代理。
# frpc.ini
[web]
type = http
local_port = 80
custom_domains = test.yourdomain.com
host_header_rewrite = dev.yourdomain.com
原來 http
請求中的 host
字段 test.yourdomain.com
轉發到後端服務時會被替換爲 dev.yourdomain.com
。
17. 設置 HTTP 請求的 header
對於 type = http
的代理,可以設置在轉發中動態添加的 header
參數。
# frpc.ini
[web]
type = http
local_port = 80
custom_domains = test.yourdomain.com
host_header_rewrite = dev.yourdomain.com
header_X-From-Where = frp
對於參數配置中所有以 header_
開頭的參數(支持同時配置多個),都會被添加到 http
請求的 header
中,根據如上的配置,會在請求的 header
中加上 X-From-Where: frp
。
18. 獲取用戶真實 IP
HTTP X-Forwarded-For
目前只有 http
類型的代理支持這一功能,可以通過用戶請求的 header
中的 X-Forwarded-For
來獲取用戶真實 IP,默認啓用。
Proxy Protocol
frp
支持通過 Proxy Protocol
協議來傳遞經過 frp
代理的請求的真實 IP,此功能支持所有以 TCP
爲底層協議的類型,不支持 UDP
。
Proxy Protocol
功能啓用後,frpc
在和本地服務建立連接後,會先發送一段 Proxy Protocol
的協議內容給本地服務,本地服務通過解析這一內容可以獲得訪問用戶的真實 IP。所以不僅僅是 HTTP
服務,任何的 TCP
服務,只要支持這一協議,都可以獲得用戶的真實 IP 地址。
需要注意的是,在代理配置中如果要啓用此功能,需要本地的服務能夠支持 Proxy Protocol
這一協議,目前 nginx
和 haproxy
都能夠很好的支持。
這裏以 https 類型爲例:
# frpc.ini
[web]
type = https
local_port = 443
custom_domains = test.yourdomain.com
# 目前支持 v1 和 v2 兩個版本的 proxy protocol 協議。
proxy_protocol_version = v2
只需要在代理配置中增加一行 proxy_protocol_version = v2
即可開啓此功能。
本地的 https
服務可以通過在 nginx
的配置中啓用 Proxy Protocol
的解析並將結果設置在 X-Real-IP
這個 Header
中就可以在自己的 Web 服務中通過 X-Real-IP
獲取到用戶的真實 IP。
19. 通過密碼保護你的 web 服務
由於所有客戶端共用一個 frps
的 http
服務端口,任何知道你的域名和 url
的人都能訪問到你部署在內網的 web
服務,但是在某些場景下需要確保只有限定的用戶才能訪問。
frp
支持通過 HTTP Basic Auth
來保護你的 web
服務,使用戶需要通過用戶名和密碼才能訪問到你的服務。
該功能目前僅限於 http
類型的代理,需要在 frpc
的代理配置中添加用戶名和密碼的設置。
# frpc.ini
[web]
type = http
local_port = 80
custom_domains = test.yourdomain.com
http_user = abc
http_pwd = abc
通過瀏覽器訪問 http://test.yourdomain.com
,需要輸入配置的用戶名和密碼才能訪問。
20. 自定義二級域名
在多人同時使用一個 frps
時,通過自定義二級域名的方式來使用會更加方便。
通過在 frps
的配置文件中配置 subdomain_host
,就可以啓用該特性。之後在 frpc
的 http
、https
類型的代理中可以不配置 custom_domains
,而是配置一個 subdomain
參數。
只需要將 *.{subdomain_host}
解析到 frps
所在服務器。之後用戶可以通過 subdomain
自行指定自己的 web
服務所需要使用的二級域名,通過 {subdomain}.{subdomain_host}
來訪問自己的 web
服務。
# frps.ini
[common]
subdomain_host = frps.com
將泛域名 *.frps.com 解析到 frps 所在服務器的 IP 地址。
# frpc.ini
[web]
type = http
local_port = 80
subdomain = test
frps
和 frpc
都啓動成功後,通過 test.frps.com
就可以訪問到內網的 web 服務。
注:如果 frps
配置了 subdomain_host
,則 custom_domains
中不能是屬於 subdomain_host
的子域名或者泛域名。
同一個 http
或 https
類型的代理中 custom_domains
和 subdomain
可以同時配置。
21. URL 路由
frp
支持根據請求的 URL
路徑路由轉發到不同的後端服務。
通過配置文件中的 locations
字段指定一個或多個 proxy
能夠匹配的 URL
前綴(目前僅支持最大前綴匹配,之後會考慮正則匹配)。例如指定 locations = /news
,則所有 URL
以 /news
開頭的請求都會被轉發到這個服務。
# frpc.ini
[web01]
type = http
local_port = 80
custom_domains = web.yourdomain.com
locations = /
[web02]
type = http
local_port = 81
custom_domains = web.yourdomain.com
locations = /news,/about
按照上述的示例配置後,web.yourdomain.com
這個域名下所有以 /news
以及 /about
作爲前綴的 URL 請求都會被轉發到 web02
,其餘的請求會被轉發到 web01
。
22. 通過代理連接 frps
在只能通過代理訪問外網的環境內,frpc
支持通過 HTTP PROXY
和 frps
進行通信。
可以通過設置 HTTP_PROXY
系統環境變量或者通過在 frpc
的配置文件中設置 http_proxy
參數來使用此功能。
僅在 protocol = tcp
時生效。
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
http_proxy = http://user:[email protected]:8080
23. 範圍端口映射
在 frpc
的配置文件中可以指定映射多個端口,目前只支持 tcp
和 udp
的類型。
這一功能通過 range:
段落標記來實現,客戶端會解析這個標記中的配置,將其拆分成多個 proxy
,每一個 proxy
以數字爲後綴命名。
例如要映射本地 6000-6005, 6007
這6個端口,主要配置如下:
# frpc.ini
[range:test_tcp]
type = tcp
local_ip = 127.0.0.1
local_port = 6000-6006,6007
remote_port = 6000-6006,6007
實際連接成功後會創建 8 個 proxy
,命名爲 test_tcp_0, test_tcp_1 ... test_tcp_7
。
24. 插件
默認情況下,frpc
只會轉發請求到本地 tcp
或 udp
端口。
插件模式是爲了在客戶端提供更加豐富的功能,目前內置的插件有 unix_domain_socket
、http_proxy
、socks5
、static_file
。具體使用方式請查看使用示例。
通過 plugin
指定需要使用的插件,插件的配置參數都以 plugin_
開頭。使用插件後 local_ip
和 local_port
不再需要配置。
使用 http_proxy
插件的示例:
# frpc.ini
[http_proxy]
type = tcp
remote_port = 6000
plugin = http_proxy
plugin_http_user = abc
plugin_http_passwd = abc
plugin_http_user
和 plugin_http_passwd
即爲 http_proxy
插件可選的配置參數。