前言
本教程不适合小白,本人因为水平有限,也无法解决大家遇到的问题,一旦出现问题还得请各位自行摸索。
首要条件:NAT 类型为 NAT1,即 fullcone;光猫桥接或开启 dmz;路由器能刷机或者开启 upnp/dmz
建议:路由器能够控制具体的端口开放与否,而不是只能全开或全关。
本文中的操作基于 OpenWrt 上的 Lucky,在其它设备使用或是使用 NATMAP 进行穿透同样可以类比。建议在阅读了第一期的基础上阅读本文。
第一步 配置硬件端口转发
首先我们解决上一期埋下的坑:Lucky 使用 CPU 进行端口转发,性能较差,如果只是单纯访问网页还好,进行大文件传输时路由的 CPU 占用较高。如果你的固件带有硬件加速功能,可以用路由器防火墙转发来代替 Lucky 内置转发,从而大大提升性能。即使你的固件不支持硬件转发,也可以尝试一下,该步骤后面会用到。
打开 Lucky
- STUN内网穿透
,编辑上一期建好的规则,手动指定 穿透通道监听端口
为任意未占用端口,并勾选 不使用Lucky内置端口转发
。
进入 OpenWrt 后台,找到 网络
- 防火墙
- 端口转发
,建立一条转发规则。
其中 外部端口
设置与上一步的 穿透通道监听端口
相同,内部IP地址
与 内部端口
则是内网服务器的 IP 与端口,其它同我设置一样,点击添加。NAT 环回个人测试没有影响,大家根据需要设置。
如果你的固件为 OpenWrt,但在防火墙这一栏中没有端口转发,可以进入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' 为自定义识别名称,不可重复
第 6 行改为穿透通道监听的端口
第 7、8 行改为内网服务器的 IP 和端口
之后在 网络
- 防火墙
- 通信规则
中打开路由器端口,与穿透通道监听的端口一致。
设置完成后点击 保存&应用
或执行 /etc/init.d/firewall reload
。
如果你是用的是其它 Linux 系统,可以使用 iptables
指令来配置端口转发。
配置完成后访问上一期设置的域名进行测试,如果失败,可能需要关闭网络加速中的流量分载,一般位于 网络
- Turbo ACC 网络加速
。如果关闭后仍无法连接,且确保设置没有问题,则可能是固件对防火墙转发的支持存在问题,那就只能使用软件转发了。
第二步 为 qBittorrent 创建隧道和端口转发
此处以 qBittorrent 客户端为例。
参考第一期教程,在 Lucky 中为 qBittorrent 的监听端口创建新的 STUN 穿透隧道,并开启防火墙端口,先使用 Lucky 自带的端口转发测试。
此时使用端口扫描软件,可以看到公网上的对应端口已经开启,但是 qBittorrent 中看不到传入连接。这与 tracker 记录 BT 客户端 ip 和端口的方式有关。现在的转发关系大约是:
BT 客户端会向 tracker 上报自己的监听端口,tracker 则从 tcp 报文中得到 BT 客户端的公网 ip,此时 tracker 记录的地址是 公网ip:BT监听端口
。因此要想成功建立连接,BT 客户端的监听端口需设置的与公网端口一致,再修改端口转发规则,使转发关系变成:
操作可参考 基于stun穿透工具LUCKY,使BT客户端绿灯、开放TCP端口的办法(进化版) 基于 stun 穿透工具 LUCKY,使 BT 客户端绿灯、开放 TCP 端口的办法(进化版)
此时下载一个热门种子,应该就能看到传入连接了,传入连接的 ip 应该都是 Lucky 的 ip。
第三步 自动更新 qb 端口和转发规则
如果你是在自己电脑上安装的 qb 或比特彗星,不用时就关闭,那就不用再往下折腾了,要用的时候再设置就好了。如果你将 qb 部署在 nas 上,想在 stun 穿透端口改变时自动更新 qb 端口和转发规则,那么需要编写更新脚本。
端口转发
Lucky 自带的端口转发没有提供 api 文档,不便于使用脚本修改,因此我们要关闭 STUN 穿透中的内置端口转发,并指定穿透通道监听端口,使用其它端口转发方案。
如果使用 OpenWrt,不使用 web 界面添加转发规则,而是进入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'
同样需要按照第一步的方式修改规则名称,src_dport
改为穿透通道监听的端口、dest_ip
、dest_port
改为 qb 的 ip 和 BT 监听端口。
至于其它 Linux 系统,请自行搜索 iptables
或 nftables
的使用方法。
如果你的固件的防火墙转发有问题,那么可以考虑使用命令行调用 socat 进行转发。
注意:如果使用软件转发,bt 客户端里看到的传入 ip 全部会变成转发软件的 ip,因此需要打开
允许同一个ip的多个连接
,并根据实际情况关闭反吸血功能(但是我使用的 qBittorrentee 似乎能正常使用反吸血)。
随便添加一个热门磁链看看效果:
看到 标志
里有 I
就代表成功了,使用防火墙转发时,可以看到传入连接正确的 IP 地址。
编写自动更新脚本
我使用的脚本是通过 OpenWrt 的 uci 指令修改名为 qb_rule
规则的转发目标端口,如果你使用其它方法也可以参考。该脚本使用时需要修改 private_port
为STUN的穿透通道监听端口,qb_username
、qb_password
和 qb_addr
改为你的qb的用户名、密码、访问地址,如果你转发规则的名称跟我设置的不一样,那么也要修改;如果你的 qb 设置里开启了 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的穿透规则,打开自定义脚本触发
,将以上脚本粘贴至自定义脚本
内,再点击确认修改
。
补一个从EkkoG/openwrt-natmap中抄来的Transmission的更新脚本。
首先为Transmission添加一条TCP的STUN穿透规则,并在防火墙里打开STUN的通道监听端口,然后进入OpenWrt的存储目录,打开/etc/config/firewall
,添加以下名为tr_rule的规则,记得修改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穿透,编辑规则,打开自定义脚本触发,将以下脚本粘贴至自定义脚本内,修改TR_USERNAME
、TR_PASSWORD
、TR_ADDR
,最后点击确认修改。
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
第三步剩余的内容仅做记录,Lucky更新之后就不需要了,请跳到第四步。
如何调用更新脚本
Lucky从2.5.1起,支持STUN地址改变时调用脚本,本节内容失效。
Lucky 虽然提供了 webhook,但是并不支持直接调用脚本(真希望作者哪天能把这功能加上),而 NATMAP 支持在 ip 和端口变更时调用脚本。如果你对命令行比较熟悉,不需要 ui 界面,可以考虑使用 NATMAP,关于 qb 和防火墙的设置可以参考 通过 NAT TCP 打洞使 qBittorrent 获得公网 IPv4 的连接性体验 通过 NAT TCP 打洞使 qBittorrent 获得公网 IPv4 的连接性体验。
但是手动管理多个 natmap 进程还是比较麻烦的,如果你跟我一样离不开 Lucky 的 web ui,我们也可以利用 webhook 来迂回一下:在 OpenWrt 上装一个能监听 http 请求并执行脚本的服务器就好了。
安装 webhook 服务器
Lucky从2.5.1起,支持STUN地址改变时调用脚本,本节内容失效。
我使用的是 webhookd。
下载对应的版本之后上传到 OpenWrt 里,我是放到了 /usr/bin
里,并把权限改成 755
新建一个存放 webhook 执行脚本的目录,我是 /usr/bin/webhookd_scripts
先在里面创建一个测试用的脚本 echo.sh
,把权限改成 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,我这里的监听端口设置为 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
,大概是执行脚本时第一行的 #!/bin/bash
识别错误。用 winscp 打开脚本 echo.sh
,在末尾填个空行再保存就行了,具体原因我没有深究,如果有知道的朋友麻烦在评论区教教我。
如果要传入参数,那么访问在 url 后面加 ?参数名=值
就行,比如访问 http://webhookd的ip:1080/echo?msg=mygo
,第五行就会变成 Query parameter: msg=mygo
。
设置 webhookd 自启动
Lucky从2.5.1起,支持STUN地址改变时调用脚本,本节内容失效。
该操作仅针对 OpenWrt,其它 Linux 可能需要使用 systemctl。
关闭刚才启动的 webhook 服务,在 /etc/init.d
下创建 webhookd
文件,并授予 755 权限,修改为
#!/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 执行命令。
别忘了给运行权限!
回到 OpenWrt 后台,在 系统
- 启动项
里应该就能看到执行启动优先级为 80 的 webhookd 了,如果优先级不为 80,还是用 winscp 打开刚才创建的文件,添加空行再保存。确认无误后在 启动项
页面里启用 webhook,并点击启动。
脚本编写
Lucky从2.5.1起,支持STUN地址改变时调用脚本,本节内容失效。
我使用的脚本是通过 OpenWrt 的 uci 指令修改名为 qb_rule
规则的转发目标端口,如果你使用其它方法也可以参考。该脚本需要将公网端口赋值给 port 参数传入,使用时需要修改 qb_username
、qb_password
和 qb_addr
,如果你转发规则的名称跟我设置的不一样,那么也要修改;如果你的 qb 设置里开启了 https 访问,在脚本里也要对应修改。在 webhookd 的脚本目录下创建 qb.sh 并授予 755 权限,内容如下:
#!/bin/bash
# 传入的参数为port,代表公网端口。若使用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 改成你自己的转发规则的名字
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 中设置 webhook
Lucky从2.5.1起,支持STUN地址改变时调用脚本,本节内容失效。
回到 Lucky
- STUN内网穿透
,编辑 qb 的穿透规则,开启 webhook,接口地址填写 http://webhookd的ip:监听端口/qb?port=#{port}
,我的 webhookd 和 Lucky 都在 OpenWrt 上,所以是 http://localhost:1080/qb?port=#{port}
,请求方法选择 GET
,接口调用成功包含的字符串填 success
。点击手动触发测试。如果一切顺利的话,应该可以看到 qb 的监听端口和防火墙转发的目标端口均发生了改变,点击 确认修改
。
完成了以上所有操作后,当公网 ip 和端口发生改变之后,qb 和端口转发的端口也会自动更新了。为了继续使用 Lucky 的 web ui 折腾了这么一大通,不知各位觉得值得吗?如果什么时候 Lucky 能够支持调用脚本就好了...
第四步 开启高位端口
因为 STUN 穿透可能随机到任意高位端口,所以如果你部署 qb 的机器上有防火墙,那可以将未占用的高位端口全部开启,或者通过脚本按需开启。
如果你希望 qb 也使用 ipv6 连接,那么可以进入路由器的通信规则中,建立转发规则,将 qb 所在机器的未占用的高位端口全部允许 ipv6 访问,或者通过脚本按需开启。
请务必确保你不想暴露的端口被防火墙阻止。
结语
经过本期的配置,BT 客户端的使用效果几乎等同于拥有公网 ipv4,不仅更容易上传了,也能提高下载时的连接数。
目前 STUN 穿透方案还有一个明显痛点:绝大部分应用程序需要填写固定的自部署服务器的端口,而不能依靠重定向,甚至有的应用不支持修改端口,这将在下一期解决,补上内网穿透的最后一块拼图。