利用frp來做內網設備,走外部網路開放功能服務

最近在公司工作的專案,研究開發LINE bot的一些功能使用,所以需要有個能根LINE API交握的對外服務網路。
跟公司的MIS交涉這種需求,會有很多種考量花在那邊溝通。
但是花自己的設備、網路、DNS,好像沒辦法吸收(賺錢)這些無形的個人資源的使用成本。
然而,正式部屬前總是要先完成開發專案BETA版的驗證和測試,還是有一定的需求要能讓開發的程式能跟外面的網路作交握。
雖然本來可以用ngrok的產品(https://ngrok.com/),但是這個服務僅有部分程度的免費。
這幾天發現賽門鐵克和微軟的防毒軟體,一直把他們官方的軟體版本,認定為木馬和病毒。會莫名其妙得把我的LINE bot給斷網,損失好幾天的即時記錄運作。
為了備援方案,花了兩天研究,才催生了這篇記錄文件。

tailscale和cloudflare,都可以有方式做到這樣的需求。
但是如前言,我不想要全部把自己手邊的可用資源都投入到公司工作裡面。

另外,一開始是嘗試用BoringproxyPangolin,看能不能成功。
然而,第一個boringproxy設定教學太少,AI指導和詠唱魔法間,我一直設定碰壁。
第二個pangolin,他要用docker掛一個虛擬機運作,整體檔案很大,設定啟用過程也是一直卡關。

快要放棄的時候,AI又介紹一個曾經在使用ngrok前,也有本來考量的使用軟體,frp。
不過當時沒有很深入研究,後來用cloudflare搞定後就沒繼續理解,擱著。
這次就再來研究一次,雖然AI也是一直胡思亂語,但是在它錯的指導方式,是有讓我架設部份成功,有點看到曙光的希望。

套件開發者的GITHUB:
https://github.com/fatedier/frp/
這個作者的設定資料和範例就很明顯的豐富許多,GOOGLE也找到比較多的討論。

首先,需要一個免費的DNS申請到網域,因為只是很單純的IP轉址就好,所以就用這個提供的服務:
https://www.duckdns.org/
一個帳號能有五個免費的,指導設定更新IP值,也很清楚。
假設我申請了一個:linebotservice.duckdns.org

然後,還需要一個能跟外部IP能互通的網際網路設備。
由於不想要花自己的設備和網路流量,所以考量使用最近很夯的虛擬機器服務。
但是這個選項大部分服務供應商都是要花錢的才能使用。
最後選擇GOOGLE CLOUD提供的虛擬機器使用,因為有方案可以在限定條件規範下免費使用(應該?目前還在驗證中)。
扯遠了,以後有心得後再分享。

萬事俱備,只欠東風。
frp的套件,基本上也是會被windows defender擋下來,因為跟上面提到的都是認為不安全的軟體。
作業系統要administrator設定例外放行,才能使用喔。(沒有權限就再第二階掛虛擬機來用(?))
不過因為摸索出來,只說結論:
frp伺服器端,用frps;客戶端是用frpc
我的LINE bot是用PYTHON環境開發的,是用waitress的網路服務。
先稱之為內網電腦A(http://192.168.x.123:54321、然後沒有SSL的使用)

1.客戶端
所以設定檔要這樣用:
### frpc.toml _ internal net client side to GDC
#這個就是剛剛申請到的網址,或者固定IP值也可以,總之要連線的主機。
serverAddr = "linebotservice.duckdns.org"
#這個是連線用的協定PORT,公司通常router封鎖光光,故要找到一個能用的PORT,主機端要配合它用。
serverPort = 7000
#要LOG紀錄,這裡使用檔案。
log.to = "./frpc.log"
# trace, debug, info, warn, error
log.level = "info"
log.maxDays = 3
# disable log colors when log.to is console, default is false
log.disablePrintColor = false
#基本的TLS通訊加密
transport.tls.enable = true
#預設TRUE,要改掉,因為會跟vhostHTTPSPort衝突:connect to server error: remote error: tls: unrecognized name。
transport.tls.disableCustomTLSFirstByte = false
#這裡是設定檔
[[proxies]]
#給自己辨識用的,這裡只是範例用中文
name = "內網電腦A"
type = "http"
#通常是本機的服務127.0.0.1,如果要當中繼站就是別台內網IP值
localIp = "127.0.0.1"
#那個服務的PORT,像我的開發專案是跑這個值
localPort = 54321
#subdomain用法是填定義的子網域名稱
subdomain = "linebotservice"

設定檔案好,執行指令:
$./frpc -c frpc.toml

2.伺服端
主機端的設定就很單純:
### frps.toml _ external net accessable server side for client connection
#如果自己的IP不是固定的或特殊環境,設定0.0.0.0就是開放給全部可以被連線的方式
bindAddr = "0.0.0.0"
#這個是要給客戶端連線的PORT,兩邊都要設定相同。
bindPort = 7000
#這個是要這樣設定,配合內網電腦A的subdomain值;
#注意!如果打成linebotservice.duckdns.org,服務網址會變成linebotservice.linebotservice.duckdns.org。
subdomainHost = "duckdns.org"
#虛擬網域的HTTP和HTTPS服務,可以設定跟bindPort一樣。我這裡是兩個都用80(**後註)。
vhostHTTPPort = 80
vhostHTTPSPort = 80
#要LOG紀錄,這裡使用檔案。
log.to = "./frps.log"
# trace, debug, info, warn, error
log.level = "info"
log.maxDays = 3
# disable log colors when log.to is console, default is false
log.disablePrintColor = false

設定檔案好,執行指令:
$./frps -c frps.toml

3.
如果跟我一樣是linux系統,可以把它改成開機就服務的方式:
$sudo nano frps.service
[Unit]
Description = frp server service
After = network.target syslog.target
Wants = network.target

[Service]
Type = simple
# 這裡請修改成你 frps 檔案的實際路徑
ExecStart = /opt/frp/frps -c /opt/frp/frps.toml
Restart = always
RestartSec = 10s
User = root

[Install]
WantedBy = multi-user.target

把檔案複製到設定開機服務的地方,重整後測試一下有沒有問題,沒報錯問題就可以設定啟用,之後重開機服務就會常駐使用。
$sudo cp ./frps.service /etc/systemd/system/frps.service
$sudo systemctl daemon-reload
$sudo systemctl start frps
$sudo systemctl status frps
$sudo systemctl enable frps

4.
**後註
LINE API一定要有SSL的https才能服務。
然後frp,用https2http方式掛cert檔案,LINE API會一直無法同意使用。
所以上面第一點和第二點的設定方式,內網電腦A現在應該從內網IP http://192.168.x.123:54321 變成 http://linebotservice.duckdns.org:80 服務。
我的替代方案就是改掛nginx來專注處理SSL功能,然後用certbot定期更新。

/etc/nginx/sites-enabled/default範例:
# Virtual Host configuration for example.com
server {
## modify for FRP together JIR 250906
#這裡改成8080是因為處理HTTP業務,會跟frp衝突,會互相搶PORT,無法共存。
listen 8080 ;
listen [::]:8080 ;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name linebotservice.duckdns.org; # managed by Certbot
#我們要利用HTTPS來處理LINE API的需求。
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
#這個要強制啟用
http2 on;
#這個是certbot建立的檔案
ssl_certificate /etc/letsencrypt/live/linebotservice.duckdns.org/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/linebotservice.duckdns.org/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
## frps PROXY-START
# 設定將所有流量轉發到 frps 監聽的內部埠
# 將 443 埠的流量轉發到 frps 監聽client的 58080 埠
# 這裡專門處理 LINE Bot 的 Webhook 請求
#假設我的本機端是http://192.168.x.123:54321/call這樣處理接收,那我開放網路做一點小巧思。
#做點小保護,盡量不被猜出這樣的短尾碼網址https://linebotservice.duckdns.org/call。
location /webhook_callback-linebotapi {
rewrite ^/webhook_callback-linebotapi(.*)$ /call$1 break;
proxy_pass http://127.0.0.1:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
#Persistent connection related configuration
add_header X-Cache $upstream_cache_status;
#Set Nginx Cache
set $static_filemi146aOz 0;
if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" )
{
set $static_filemi146aOz 1;
expires 12h;
}
if ( $static_filemi146aOz = 0 )
{
add_header Cache-Control no-cache;
}
}
##PROXY-END/
# 其他不相關的連線者特殊處理,這裡將所有其他請求重定向到 Google網頁
location / {
return 302 https://www.google.com;
}
}

以上這台主機透過nginx設定HTTPS的需求以外,預計能有兩個功能:
A) LINE API能用的網址設定值,上述範例為:https://linebotservice.duckdns.org/webhook_callback-linebotapi,不外流。
B) 只要是非使用API連線的請求,呼叫/也好,或者/phpAdmin之類的嘗試(甚至即使猜到/call),都會轉成https://www.google.com網頁,做點基本簡單的網頁欺敵戰術。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

請輸入下列驗證碼計算後阿拉伯數字 (Translate it, if not Taiwanese to post reply) *