問題
最近電信把我的公網地址收回去了,之前做好的網絡端口映射失效了,在公司已經不能愉快地訪問家里的網絡。原先網絡結構示意圖如下:

(直接訪問方案網絡結構圖)
只需要對電信光貓(也是個路由器)和家用路由器進行端口映射的配置即可,而針對地址是“動態”的這個問題,只需要弄個動態域名解釋即可。而現在變成這樣:

(無公網地址的直接訪問方案網絡結構圖)
電信光貓不具備真正的公網地址了。如何查看自己的光貓是否有公網IP地址?不難,登錄到光貓去,看網絡狀態,會看到它的IP地址:

(電信光貓上的地址查詢)
然后上www.ip138.com,就能看到自己現在暴露在公網上的IP地址:

看看光貓上的地址和這里顯示的地址是否相同,如果相同,那光貓就有公網地址,否則就沒有。
回到問題中來,現在電信的路由器不可能給我做端口映射的,那咋弄?
這就是所謂的“內網穿透”技術了。
群暉的方案
就是quickconnect.to了,原理就是NAS主動連接群暉的服務器,而quickconnect.to也是指向群暉的服務器的,群暉再將請求轉到NAS去,同事負責將從NAS回復的結果轉回給用戶。說白了,這個具有公網地址的群暉就起到了一個中介的作用。

(群暉方案網絡結構圖)
群暉的方案自然是非常簡易的,用戶甚至都不用了解怎么連接這些細節,只需要注冊好賬號密碼,傻瓜式地打開quickconnect.to即可。
但弊端也很明顯,最大的弊端當然是太慢,其次是不靈活,比如我架設了一個第三方的服務,就沒法利用quickconnect.to訪問。
frp方案簡介
frp是國人開發的一套內網穿透工具,免費開源,使用GO語言寫的,其github地址是:https://github.com/fatedier/frp/
它有一個服務器端frps,一個客戶端frpc,其原理如下圖所示:

(frp方案網絡結構圖)
frpc連接至frps,建立起一條數據通道(圖中藍色的虛線),根據配置,任何對公網服務器的某個端口的訪問都經由這條通道告知frpc,再由frpc轉為對局域網內某台主機(NAS)的訪問,最后將訪問結果原路返回給用戶,原理和quickconnect.to是相似的。
必要的准備
要達成這個方案,就需要
一台有公網IP地址的主機,通常是雲主機。個人要搞一台有不錯帶寬的雲主機,價格還是挺高的,可以考慮幾個人拼一台主機,這樣成本就下來了,帶寬也能得到比較充分的利用,或者本來就有自己的公網主機的話那就更好了。
除了公網主機之外,圖中我畫的frpc是放在電腦主機或者路由器上,這是啥意思呢?
frpc是客戶端,要想隨時能夠訪問自己的NAS,客戶端和服務器端這條數據通道是不能斷的,要一直運行着,如果NAS支持安裝frpc,我想應該沒問題,直接把frpc放在NAS上,但實際上我沒在群暉的套件中心里找到frpc,所以只好部署在NAS之外的設備上,另外:我不太確定用docker是否可行,誰如果用docker部署了frpc在NAS上的話可以在下面留言說明。
根據前面說的,如果frpc部署在電腦里,電腦要一直開着機,這個開銷挺大的,所以最好的做法,就是把frpc部署在路由器里。並不是所有的路由器都支持安裝應用。我家使用的路由器是ASUS的RT-AC86U,自己刷一個“梅林固件”,就可以在上面安裝應用了,其中就有frpc,這個過程並不難,網上找一下即可,
支持梅林固件的路由器還是挺多的。

(梅林固件的“軟件中心”管理界面)
但配置階段我建議還是用電腦,因為你肯定會遇到一些問題,你需要不斷查看日志和調整,等你調試好了,再把配置貼到路由器里即可。
額外的,還需要
一個域名,最好有,一來美觀好記,二來可以啟用https,三來可以用不同域名來區分不同的訪問。域名可以到阿里雲上注冊一個,比如叫 jiangguogang.club,這種域名還是很便宜的,頭一年特價,甚至只需要一元錢,體驗比DDNS那些二級域名好很多。
OK,總結回來,必要的准備就是:
- 一台有公網IP地址的主機,簡稱公網主機,假設主機IP地址為123.123.123.123
- 支持梅林固件的路由器(能夠安裝frpc)
- 一個域名,以jiangguogang.club為例
規划
確定自己需要哪些服務之后,做一下規划,最直接有效的方法就是畫一張規划圖,以我為例,我將自己的需求先列一下:
- 對https://jiangguogang.club的訪問會返回一個“Welcome”的信息
- 對https://jiangguogang.club/photo的訪問會轉至NAS的相冊,局域網的地址是http://192.168.1.200/photo
- 對https://dms.jiangguogang.club的訪問會轉至NAS的管理界面,局域網地址是http://192.168.1.200:5000
- 對https://webdav.jiangguogang.club的訪問會轉至NAS的WebDAV服務,局域網地址是http://192.168.1.200:5005
- 對https://router.jiangguogang.club的訪問會轉至我的ASUS路由器,局域網地址是http://192.168.1.1
- 所有使用http的訪問都會被重定向至https
需求明確。由於本人對NGINX反向代理比較熟悉,所以公網主機統一用NGINX對外提供服務,這樣配置https也比較方便,用戶的請求到達主機之后,是由NGINX轉到frps,再經過數據通道,抵達frpc,由frpc執行本地的請求。

(應用程序關系圖)
為了更清晰,弄一張表格出來:
用戶 | NGINX | frps | frpc |
https://jiangguogang.club | 直接返回"Welcome" | - | - |
https://jiangguogang.club/photo | 轉127.0.0.1:5002 | TCP通道轉frpc | 轉192.168.1.200:80 |
https://dms.jiangguogang.club | 轉127.0.0.1:5000 | TCP通道轉frpc | 轉192.168.1.200:5000 |
https://webdav.jiangguogang.club | 轉127.0.0.1:5005 | TCP通道轉frpc | 轉192.168.1.200:5005 |
https://router.jiangguogang.club | 轉127.0.0.1:5003 | TCP通道轉frpc | 轉192.168.1.1:80 |
部署及配置NGINX
部署nginx的步驟略。
記得防火牆打開80和443端口。
/etc/nginx/conf.d/jiangguogang.conf
server { server_name jiangguogang.club; location / { add_header Content-Type text/plain; return 200 'Welcome! I am Jiang Guogang.'; #直接返回字符串 } location /photo/ { proxy_pass http://127.0.0.1:5002; #轉至本地的5002端口 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } #啟用https,證書來自於Let's encrypt. 建議使用Certbot,具體參考Let's encrypt的官網 listen 443 ssl; ssl_certificate /etc/letsencrypt/live/jiangguogang.club/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/jiangguogang.club/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; } server { server_name dsm.jiangguogang.club; location / { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } listen 443 ssl; ssl_certificate /etc/letsencrypt/live/dsm.jiangguogang.club/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/dsm.jiangguogang.club/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; } server { server_name webdav.jiangguogang.club; location / { proxy_pass http://127.0.0.1:5005; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } listen 443 ssl; ssl_certificate /etc/letsencrypt/live/webdav.jiangguogang.club/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/webdav.jiangguogang.club/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; } server { server_name router.jiangguogang.club; location / { proxy_pass http://127.0.0.1:5003; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } listen 443 ssl; ssl_certificate /etc/letsencrypt/live/router.jiangguogang.club/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/router.jiangguogang.club/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; } #下面的配置的意思是:對http的訪問將會重定向至https server { if ($host = photo.jiangguogang.club) { return 301 https://$host$request_uri; } listen 80; server_name photo.jiangguogang.club; return 404; } server { if ($host = webdav.jiangguogang.club) { return 301 https://$host$request_uri; } listen 80; server_name webdav.jiangguogang.club; return 404; } server { if ($host = dsm.jiangguogang.club) { return 301 https://$host$request_uri; } listen 80; server_name dsm.jiangguogang.club; return 404; } server { if ($host = jiangguogang.club) { return 301 https://$host$request_uri; } listen 80; server_name jiangguogang.club; return 404; } server { if ($host = router.jiangguogang.club) { return 301 https://$host$request_uri; } listen 80; server_name router.jiangguogang.club; return 404; }
nginx的配置應該還是很直截了當的,需要說明的我也用注釋在上面說明了。
最好設置為開機自動啟動。
frps部署及配置
由於我們的frpc是直接通過路由器的“軟件中心”安裝的,版本是確定的,服務器的版本要和此版本一致,不能直接下載最新版,這個一定要注意。
下載地址當然是github:https://github.com/fatedier/frp/releases
這個服務器端非常的小巧精悍,除了一個配置文件之外,就只有一個可執行文件了,將其放在服務器的某個目錄下,如:/usr/local/frps/
其配置文件去除了各種注釋之后,內容也不多,/usr/local/frps/frps.ini
[common] #服務器監聽端口7000 bind_port = 7000 #服務器web管理界面的訪問端口 dashboard_port = 7001 #服務器web管理界面用戶名 dashboard_user = admin #服務器web管理界面密碼(記得改成你的) dashboard_pwd = 123456 #日志文件 log_file = ./frps.log #日志等級 log_level = info #日志保留天數 log_max_days = 3 #客戶端訪問token(記得改成你的) token = 654321 #服務器端允許客戶端請求的端口范圍是5000到5009 allow_ports = 5000-5009 max_pool_count = 5 max_ports_per_client = 0 authentication_timeout = 900 #使用多路復用 tcp_mux = true
注意:
1,讓防火牆允許7000和7001端口,5000-5009不必打開,因為這都是本地直接使用的端口
2,讓frps自動啟動(推薦通過systemd來實現)
3,啟動參數:
/usr/local/frps/frps -c /usr/local/frps/frps.ini,表示使用frps.ini這個配置文件
4,注意查看日志文件,日志文件就在/usr/local/frps目錄下,注意確保目錄有寫入權限
frpc配置
frpc是位於路由器上的應用。其實路由器本身也是台Linux主機,你可以打開路由器的ssh上去看看,但我還是建議在配置的frpc的時候,先用你自己的電腦嘗試,這樣更方便一些。什么?你沒有Linux?有虛擬機嗎?沒有?WSL了解一下。
我最后的配置文件是這樣的:
[common] #服務器的地址(公網) server_addr = 123.123.123.123 #服務器的監聽端口 server_port = 7000 #服務器的token(改成你的) token = 654321 #日志文件配置,調試的時候就靠這個了 log_file = /var/log/frpc.log log_level = info log_max_days = 3 #啟用多路復用 tcp_mux = true #協議,除了TCP之外還有一個經過改進的協議,但我這邊使用時候遇到了點問題,還是用TCP吧 protocol = tcp #不要登錄失敗就退出 login_fail_exit = false #應用名稱 [dsm] #http是基於TCP協議的,我統一都當成TCP來處理好了 type = tcp #本地IP地址 local_ip = 192.168.1.200 #本地端口 local_port = 5000 #服務器上要打開的端口 remote_port = 5000 #使用加密 use_encryption = true #使用壓縮 use_compression = true [photo] type = tcp local_ip = 192.168.1.200 local_port = 80 remote_port = 5002 use_encryption = true use_compression = true [webdav] type = tcp local_ip = 192.168.1.200 local_port = 5005 remote_port = 5005 use_encryption = true use_compression = true [router] type = tcp local_ip = 192.168.1.1 local_port = 80 remote_port = 5003 use_encryption = true use_compression = true
注意:
1,注意查看日志調試,你不可能一點問題都沒遇到的,見招拆招吧
2,你要實現確定LAN里的各個Web服務本地可用,如果出現問題,要懂得順藤摸瓜,定位問題所在
3,調試好了之后,把配置放到路由器的frpc里,路由器雖然提供了圖形界面,但我覺得不好用,不如直接弄配置文件
最后
本文似乎省略了不少內容,是的,比如nginx具體如何安裝?防火牆具體如何設置?certbot具體怎么用?如何刷梅林固件?我不打算都寫上,否則本文就可能膨脹為“如何使用Linux”了。凡是有能力折騰frp的人,我相信都能通過搜索來解決那些小問題,都能見招拆招。
我當然知道還有別的配置方法,我提供的方法僅僅是其中一種可行的,如果大家覺得有什么更好的方法,可以在評論區留言。