前言
本教程不適合小白,本人因為水平有限,也無法解決大家遇到的問題,一旦出現(xiàn)問題還得請各位自行摸索。
首要條件:NAT 類型為 NAT1,即 fullcone;光貓橋接或開啟 dmz;路由器能刷機(jī)或者開啟 upnp/dmz
建議:路由器能夠控制具體的端口開放與否,而不是只能全開或全關(guān)。
本文中的操作基于 OpenWrt 上的 Lucky,在其它設(shè)備使用或是使用 NATMAP 進(jìn)行穿透同樣可以類比。建議在閱讀了第一期的基礎(chǔ)上閱讀本文。
第一步 配置硬件端口轉(zhuǎn)發(fā)
首先我們解決上一期埋下的坑:Lucky 使用 CPU 進(jìn)行端口轉(zhuǎn)發(fā),性能較差,如果只是單純訪問網(wǎng)頁還好,進(jìn)行大文件傳輸時路由的 CPU 占用較高。如果你的固件帶有硬件加速功能,可以用路由器防火墻轉(zhuǎn)發(fā)來代替 Lucky 內(nèi)置轉(zhuǎn)發(fā),從而大大提升性能。即使你的固件不支持硬件轉(zhuǎn)發(fā),也可以嘗試一下,該步驟后面會用到。
打開 Lucky - STUN內(nèi)網(wǎng)穿透,編輯上一期建好的規(guī)則,手動指定 穿透通道監(jiān)聽端口 為任意未占用端口,并勾選 不使用Lucky內(nèi)置端口轉(zhuǎn)發(fā)。

進(jìn)入 OpenWrt 后臺,找到 網(wǎng)絡(luò) - 防火墻 - 端口轉(zhuǎn)發(fā),建立一條轉(zhuǎn)發(fā)規(guī)則。

其中 外部端口 設(shè)置與上一步的 穿透通道監(jiān)聽端口 相同,內(nèi)部IP地址 與 內(nèi)部端口 則是內(nèi)網(wǎng)服務(wù)器的 IP 與端口,其它同我設(shè)置一樣,點擊添加。NAT 環(huán)回個人測試沒有影響,大家根據(jù)需要設(shè)置。
如果你的固件為 OpenWrt,但在防火墻這一欄中沒有端口轉(zhuǎn)發(fā),可以進(jìn)入OpenWrt的存儲目錄,在 /etc/config/firewall 中添加
config redirect 'nginx'
option target 'DNAT'
option src 'wan'
option dest 'lan'
option proto 'tcp'
option src_dport '25681'
option dest_ip '192.168.0.115'
option dest_port '18443'
option name 'nginx'
第 1 行的 'nginx' 為自定義識別名稱,不可重復(fù)
第 6 行改為穿透通道監(jiān)聽的端口
第 7、8 行改為內(nèi)網(wǎng)服務(wù)器的 IP 和端口
之后在 網(wǎng)絡(luò) - 防火墻 - 通信規(guī)則 中打開路由器端口,與穿透通道監(jiān)聽的端口一致。
設(shè)置完成后點擊 保存&應(yīng)用 或執(zhí)行 /etc/init.d/firewall reload。
如果你是用的是其它 Linux 系統(tǒng),可以使用 iptables 指令來配置端口轉(zhuǎn)發(fā)。
配置完成后訪問上一期設(shè)置的域名進(jìn)行測試,如果失敗,可能需要關(guān)閉網(wǎng)絡(luò)加速中的流量分載,一般位于 網(wǎng)絡(luò) - Turbo ACC 網(wǎng)絡(luò)加速。如果關(guān)閉后仍無法連接,且確保設(shè)置沒有問題,則可能是固件對防火墻轉(zhuǎn)發(fā)的支持存在問題,那就只能使用軟件轉(zhuǎn)發(fā)了。
第二步 為 qBittorrent 創(chuàng)建隧道和端口轉(zhuǎn)發(fā)
此處以 qBittorrent 客戶端為例。
參考第一期教程,在 Lucky 中為 qBittorrent 的監(jiān)聽端口創(chuàng)建新的 STUN 穿透隧道,并開啟防火墻端口,先使用 Lucky 自帶的端口轉(zhuǎn)發(fā)測試。
此時使用端口掃描軟件,可以看到公網(wǎng)上的對應(yīng)端口已經(jīng)開啟,但是 qBittorrent 中看不到傳入連接。這與 tracker 記錄 BT 客戶端 ip 和端口的方式有關(guān)。現(xiàn)在的轉(zhuǎn)發(fā)關(guān)系大約是:

BT 客戶端會向 tracker 上報自己的監(jiān)聽端口,tracker 則從 tcp 報文中得到 BT 客戶端的公網(wǎng) ip,此時 tracker 記錄的地址是 公網(wǎng)ip:BT監(jiān)聽端口。因此要想成功建立連接,BT 客戶端的監(jiān)聽端口需設(shè)置的與公網(wǎng)端口一致,再修改端口轉(zhuǎn)發(fā)規(guī)則,使轉(zhuǎn)發(fā)關(guān)系變成:

操作可參考 基于stun穿透工具LUCKY,使BT客戶端綠燈、開放TCP端口的辦法(進(jìn)化版) 基于 stun 穿透工具 LUCKY,使 BT 客戶端綠燈、開放 TCP 端口的辦法(進(jìn)化版)
此時下載一個熱門種子,應(yīng)該就能看到傳入連接了,傳入連接的 ip 應(yīng)該都是 Lucky 的 ip。
第三步 自動更新 qb 端口和轉(zhuǎn)發(fā)規(guī)則
如果你是在自己電腦上安裝的 qb 或比特彗星,不用時就關(guān)閉,那就不用再往下折騰了,要用的時候再設(shè)置就好了。如果你將 qb 部署在 nas 上,想在 stun 穿透端口改變時自動更新 qb 端口和轉(zhuǎn)發(fā)規(guī)則,那么需要編寫更新腳本。
端口轉(zhuǎn)發(fā)
Lucky 自帶的端口轉(zhuǎn)發(fā)沒有提供 api 文檔,不便于使用腳本修改,因此我們要關(guān)閉 STUN 穿透中的內(nèi)置端口轉(zhuǎn)發(fā),并指定穿透通道監(jiān)聽端口,使用其它端口轉(zhuǎn)發(fā)方案。
如果使用 OpenWrt,不使用 web 界面添加轉(zhuǎn)發(fā)規(guī)則,而是進(jìn)入OpenWrt的存儲目錄,直接打開 /etc/config/firewall,添加
config redirect 'qb_rule'
option target 'DNAT'
option src 'wan'
option dest 'lan'
option proto 'tcp'
option src_dport '25680'
option dest_ip '192.168.0.112'
option name 'qb_rule'
option dest_port '24624'
同樣需要按照第一步的方式修改規(guī)則名稱,src_dport 改為穿透通道監(jiān)聽的端口、dest_ip、dest_port 改為 qb 的 ip 和 BT 監(jiān)聽端口。
至于其它 Linux 系統(tǒng),請自行搜索 iptables 或 nftables 的使用方法。
如果你的固件的防火墻轉(zhuǎn)發(fā)有問題,那么可以考慮使用命令行調(diào)用 socat 進(jìn)行轉(zhuǎn)發(fā)。
注意:如果使用軟件轉(zhuǎn)發(fā),bt 客戶端里看到的傳入 ip 全部會變成轉(zhuǎn)發(fā)軟件的 ip,因此需要打開
允許同一個ip的多個連接,并根據(jù)實際情況關(guān)閉反吸血功能(但是我使用的 qBittorrentee 似乎能正常使用反吸血)。
隨便添加一個熱門磁鏈看看效果:

看到 標(biāo)志 里有 I 就代表成功了,使用防火墻轉(zhuǎn)發(fā)時,可以看到傳入連接正確的 IP 地址。
編寫自動更新腳本
我使用的腳本是通過 OpenWrt 的 uci 指令修改名為 qb_rule 規(guī)則的轉(zhuǎn)發(fā)目標(biāo)端口,如果你使用其它方法也可以參考。該腳本使用時需要修改 private_port為STUN的穿透通道監(jiān)聽端口,qb_username、qb_password 和 qb_addr改為你的qb的用戶名、密碼、訪問地址,如果你轉(zhuǎn)發(fā)規(guī)則的名稱跟我設(shè)置的不一樣,那么也要修改;如果你的 qb 設(shè)置里開啟了 https 訪問,請在腳本里自行替換。
public_port=${port}
# qBittorrent.
#qb_web_port="8080"
qb_username="admin"
qb_password="yourpwd"
qb_addr="192.168.0.112:8080"
# Update qBittorrent listen port.
qb_cookie=$(curl -s -i --header "Referer: http://$qb_addr" --data "username=$qb_username&password=$qb_password" http://$qb_addr/api/v2/auth/login | grep -i set-cookie | cut -c13-48)
curl -X POST -b "$qb_cookie" -d 'json={"listen_port":"'$public_port'"}' "http://$qb_addr/api/v2/app/setPreferences"
#Use uci to config firewall
uci set firewall.qb_rule.dest_port=$public_port
uci commit firewall
/etc/init.d/firewall reload >/dev/null 2>&1
修改完成后,回到Lucky的STUN穿透,編輯qb的穿透規(guī)則,打開自定義腳本觸發(fā),將以上腳本粘貼至自定義腳本內(nèi),再點擊確認(rèn)修改。
補(bǔ)一個從EkkoG/openwrt-natmap中抄來的Transmission的更新腳本。
首先為Transmission添加一條TCP的STUN穿透規(guī)則,并在防火墻里打開STUN的通道監(jiān)聽端口,然后進(jìn)入OpenWrt的存儲目錄,打開/etc/config/firewall,添加以下名為tr_rule的規(guī)則,記得修改src_dport、dest_ip、dest_port,具體操作跟前面完全一樣。
config redirect 'tr_rule'
option target 'DNAT'
option src 'wan'
option dest 'lan'
option proto 'tcp udp'
option src_dport '25683'
option dest_ip '192.168.0.117'
option name 'tr_rule'
option dest_port '14830'
回到LUCKY的STUN穿透,編輯規(guī)則,打開自定義腳本觸發(fā),將以下腳本粘貼至自定義腳本內(nèi),修改TR_USERNAME、TR_PASSWORD、TR_ADDR,最后點擊確認(rèn)修改。
TR_USERNAME=admin
TR_PASSWORD=yourpwd
TR_ADDR=192.168.0.117:9091
TR_RPC_URL='http://'$(echo $TR_ADDR | sed 's/\/$//')
# update port
trauth="-u $TR_USERNAME:$TR_PASSWORD"
trsid=$(curl -s $trauth $TR_RPC_URL/transmission/rpc | sed 's/.*<code>//g;s/<\/code>.*//g')
curl -X POST \
-H "${trsid}" $trauth \
-d '{"method":"session-set","arguments":{"peer-port":'${port}'}}' \
"$TR_RPC_URL/transmission/rpc"
# use uci to config firewall
uci set firewall.tr_rule.dest_port=${port}
uci commit firewall
/etc/init.d/firewall reload >/dev/null 2>&1
第三步剩余的內(nèi)容僅做記錄,Lucky更新之后就不需要了,請?zhí)降谒牟健?/p>
如何調(diào)用更新腳本
Lucky從2.5.1起,支持STUN地址改變時調(diào)用腳本,本節(jié)內(nèi)容失效。
Lucky 雖然提供了 webhook,但是并不支持直接調(diào)用腳本(真希望作者哪天能把這功能加上),而 NATMAP 支持在 ip 和端口變更時調(diào)用腳本。如果你對命令行比較熟悉,不需要 ui 界面,可以考慮使用 NATMAP,關(guān)于 qb 和防火墻的設(shè)置可以參考 通過 NAT TCP 打洞使 qBittorrent 獲得公網(wǎng) IPv4 的連接性體驗 通過 NAT TCP 打洞使 qBittorrent 獲得公網(wǎng) IPv4 的連接性體驗。
但是手動管理多個 natmap 進(jìn)程還是比較麻煩的,如果你跟我一樣離不開 Lucky 的 web ui,我們也可以利用 webhook 來迂回一下:在 OpenWrt 上裝一個能監(jiān)聽 http 請求并執(zhí)行腳本的服務(wù)器就好了。
安裝 webhook 服務(wù)器
Lucky從2.5.1起,支持STUN地址改變時調(diào)用腳本,本節(jié)內(nèi)容失效。
我使用的是 webhookd。
下載對應(yīng)的版本之后上傳到 OpenWrt 里,我是放到了 /usr/bin 里,并把權(quán)限改成 755
新建一個存放 webhook 執(zhí)行腳本的目錄,我是 /usr/bin/webhookd_scripts
先在里面創(chuàng)建一個測試用的腳本 echo.sh,把權(quán)限改成 755(這個腳本在 github 里能下到)
#!/bin/bash
# Usage: http POST :8080/echo msg==hello foo=bar
echo "This is a simple echo hook."
echo "Hook information: name=$hook_name, id=$hook_id, method=$hook_method"
echo "Command result: hostname=`hostname`"
echo "Header variable: User-Agent=$user_agent"
echo "Query parameter: msg=$msg"
echo "Body payload: $1"
運行 webhookd,我這里的監(jiān)聽端口設(shè)置為 1080
# 使用方法:webhookd -listen-addr ":端口" -scripts "存放腳本的目錄"
webhookd -listen-addr ":1080" -scripts "/usr/bin/webhookd_scripts"
然后直接用瀏覽器訪問 http://webhookd的ip:1080/echo,如果能看到類似下面的信息就說明配置對了。
This is a simple echo hook.
Hook information: name=echo, id=15, method=GET
Command result: hostname=
Header variable: User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36
Query parameter: msg=
Body payload:
/usr/bin/webhookd_scripts/echo.sh: line 6: hostname: command not found
如果提示 no such file or directory,大概是執(zhí)行腳本時第一行的 #!/bin/bash 識別錯誤。用 winscp 打開腳本 echo.sh,在末尾填個空行再保存就行了,具體原因我沒有深究,如果有知道的朋友麻煩在評論區(qū)教教我。
如果要傳入?yún)?shù),那么訪問在 url 后面加 ?參數(shù)名=值 就行,比如訪問 http://webhookd的ip:1080/echo?msg=mygo,第五行就會變成 Query parameter: msg=mygo。
設(shè)置 webhookd 自啟動
Lucky從2.5.1起,支持STUN地址改變時調(diào)用腳本,本節(jié)內(nèi)容失效。
該操作僅針對 OpenWrt,其它 Linux 可能需要使用 systemctl。
關(guān)閉剛才啟動的 webhook 服務(wù),在 /etc/init.d 下創(chuàng)建 webhookd 文件,并授予 755 權(quán)限,修改為
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=80
STOP=15
start_service() {
procd_open_instance webhookd
procd_set_param command webhookd -listen-addr ":1080" -scripts "/usr/bin/webhookd_scripts" # command for running app
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-0} # restart app for unlimited times
procd_set_param stdout 1 # forward stdout of the command to logd
procd_set_param stderr 1 # same for stderr
procd_close_instance
}
stop_service(){
procd_kill webhookd
}
第 7 行 command 后改成你自己的 webhook 執(zhí)行命令。
別忘了給運行權(quán)限!
回到 OpenWrt 后臺,在 系統(tǒng) - 啟動項 里應(yīng)該就能看到執(zhí)行啟動優(yōu)先級為 80 的 webhookd 了,如果優(yōu)先級不為 80,還是用 winscp 打開剛才創(chuàng)建的文件,添加空行再保存。確認(rèn)無誤后在 啟動項 頁面里啟用 webhook,并點擊啟動。
腳本編寫
Lucky從2.5.1起,支持STUN地址改變時調(diào)用腳本,本節(jié)內(nèi)容失效。
我使用的腳本是通過 OpenWrt 的 uci 指令修改名為 qb_rule 規(guī)則的轉(zhuǎn)發(fā)目標(biāo)端口,如果你使用其它方法也可以參考。該腳本需要將公網(wǎng)端口賦值給 port 參數(shù)傳入,使用時需要修改 qb_username、qb_password 和 qb_addr,如果你轉(zhuǎn)發(fā)規(guī)則的名稱跟我設(shè)置的不一樣,那么也要修改;如果你的 qb 設(shè)置里開啟了 https 訪問,在腳本里也要對應(yīng)修改。在 webhookd 的腳本目錄下創(chuàng)建 qb.sh 并授予 755 權(quán)限,內(nèi)容如下:
#!/bin/bash
# 傳入的參數(shù)為port,代表公網(wǎng)端口。若使用natmap,則將$port改為$2
public_port="$port"
# qBittorrent.
qb_username="admin"
qb_password="your_pwd"
qb_addr="192.168.0.112:8080"
# Update qBittorrent listen port.
# 如果你的qb開啟了https,需要把下面兩行的http改成https
qb_cookie=$(curl -s -i --header "Referer: http://$qb_addr" --data "username=$qb_username&password=$qb_password" http://$qb_addr/api/v2/auth/login | grep -i set-cookie | cut -c13-48)
curl -X POST -b "$qb_cookie" -d 'json={"listen_port":"'$public_port'"}' "http://$qb_addr/api/v2/app/setPreferences"
# Use uci to config firewall
# qb_rule 改成你自己的轉(zhuǎn)發(fā)規(guī)則的名字
uci set firewall.qb_rule.dest_port=$public_port
uci commit firewall
# restart firewall in background
/etc/init.d/firewall reload >/dev/null 2>&1
echo "success"
在 Lucky 中設(shè)置 webhook
Lucky從2.5.1起,支持STUN地址改變時調(diào)用腳本,本節(jié)內(nèi)容失效。
回到 Lucky - STUN內(nèi)網(wǎng)穿透,編輯 qb 的穿透規(guī)則,開啟 webhook,接口地址填寫 http://webhookd的ip:監(jiān)聽端口/qb?port=#{port},我的 webhookd 和 Lucky 都在 OpenWrt 上,所以是 http://localhost:1080/qb?port=#{port},請求方法選擇 GET,接口調(diào)用成功包含的字符串填 success。點擊手動觸發(fā)測試。如果一切順利的話,應(yīng)該可以看到 qb 的監(jiān)聽端口和防火墻轉(zhuǎn)發(fā)的目標(biāo)端口均發(fā)生了改變,點擊 確認(rèn)修改。
完成了以上所有操作后,當(dāng)公網(wǎng) ip 和端口發(fā)生改變之后,qb 和端口轉(zhuǎn)發(fā)的端口也會自動更新了。為了繼續(xù)使用 Lucky 的 web ui 折騰了這么一大通,不知各位覺得值得嗎?如果什么時候 Lucky 能夠支持調(diào)用腳本就好了...
第四步 開啟高位端口
因為 STUN 穿透可能隨機(jī)到任意高位端口,所以如果你部署 qb 的機(jī)器上有防火墻,那可以將未占用的高位端口全部開啟,或者通過腳本按需開啟。
如果你希望 qb 也使用 ipv6 連接,那么可以進(jìn)入路由器的通信規(guī)則中,建立轉(zhuǎn)發(fā)規(guī)則,將 qb 所在機(jī)器的未占用的高位端口全部允許 ipv6 訪問,或者通過腳本按需開啟。
請務(wù)必確保你不想暴露的端口被防火墻阻止。
結(jié)語
經(jīng)過本期的配置,BT 客戶端的使用效果幾乎等同于擁有公網(wǎng) ipv4,不僅更容易上傳了,也能提高下載時的連接數(shù)。
目前 STUN 穿透方案還有一個明顯痛點:絕大部分應(yīng)用程序需要填寫固定的自部署服務(wù)器的端口,而不能依靠重定向,甚至有的應(yīng)用不支持修改端口,這將在下一期解決,補(bǔ)上內(nèi)網(wǎng)穿透的最后一塊拼圖。