前言
本教程不适合小白,本人因为水平有限,也无法解决大家遇到的问题,一旦出现问题还得请各位自行摸索。
首要条件:NAT类型为NAT1,即fullcone;光猫桥接或开启dmz;路由器能刷机或者开启upnp/dmz
建议:路由器能够控制具体的端口开放与否,而不是只能全开或全关。
温馨提示:
本方案不保证成功率和稳定性,而且操作比较繁琐,如果你可以通过社会工程学的方式获取公网ipv4,请尽量尝试;如果你在外访问时可以使用ipv6,请尝试ipv6方案。如果你对以上概念完全没有印象,建议学习基本的网络知识后再来看本教程,再次提醒,本教程不适合小白,我也没有能力解决各位的问题。
常见穿透方案及其局限性
我的需求很简单,能随时随地访问家里的nas,我部署的服务既有需要大带宽的alist、jellyfin等,也有对延迟敏感的teamspeak、远程桌面。目前常见的内网穿透方案有如下几种:
- 公网ip
首选方案肯定是向运营商要公网IP,如果是是电信和联通可能还有希望,其它运营商或是校园网基本没戏。
虽然现在家庭宽带的ipv6基本普及,但例如公司、出租屋、酒店等场景基本没有覆盖,况且我的小白朋友压根不会也不想开启。 - frp或vpn
如果自己租服务器搭建,那么同时能满足我对带宽和延迟需求的服务器价格实在太感人;
使用别人搭建好的服务,的确能找到价格很低的,但是稳定性堪忧。 - 各种P2P打洞组网软件
访问者既要下软件、又要注册账号,对于小白朋友还是太麻烦了;
部分访问场景是NAT4,没法直连,走代理的体验不佳。
那么有没有一种方案,既能满足大带宽、低延迟的需求,同时还免费,甚至稳定性也还可以呢?我经过苦苦搜寻,发现STUN穿透能完美满足我的需求,唯一的缺点就是...非常非常折腾。
NATMAP与lucky:
目前有STUN穿透功能的软件主要有NATMAP和lucky。在这两款软件都能正常运行的前提下,如果只需通过浏览器访问nas上的服务,那么lucky使用起来容易的多;如果需要实现更加复杂的玩法,那么NATMAP更加灵活。然而NATMAP的web ui仅支持官方OpenWrt固件,其他固件、平台只能通过命令行调用,如果要建立多个隧道,还得手动管理各个实例,较为繁琐,而lucky在各个平台都有web ui;同时lucky除了STUN穿透之外,还有各种实用功能,可以省去安装一堆软件的麻烦,故本教程使用lucky。
教程以OpenWrt为例,其它平台请参考官方文档。
如果在路由器以外的设备安装,需要开启路由器的upnp或者dmz。
请参考使用Lucky的STUN内网穿透利用UPNP和NAT1在公网打洞并配置伪DDNS 使用Lucky的STUN内网穿透利用UPNP和NAT1在公网打洞并配置伪DDNS
第一步:安装lucky
登陆openwrt后台后,依次点击系统
-文件传输
-选择文件
,找到下载的lucky插件,之后点击上传,将lucky和luci-app-lucky上传。之后在下方的上传文件列表
中,先安装lucky,再安装luci-app-lucky。
第二步:打开lucky后台并修改用户名和密码
点击服务
- lucky
- admin panel
右侧的网址,打开后台,初次使用时,默认用户名密码均为666。
之后记得在设置里修改账号密码。
第三步,开启STUN内网穿透
点击STUN内网穿透
- 穿透规则列表
- 添加穿透规则
具体设置如下
请注意,如果在路由器之外的设备上安装,需要开启nat-pmp
。
点击保存,稍等一会儿应该就能看见公网ip和端口了,点击公网地址即可复制。
如果不出意外,在在外网设备的浏览器地址栏中粘贴所复制的公网地址(ip:端口),应该就能访问成功了。如果无法访问成功,请编辑刚才创建的穿透规则,手动指定穿透通道监听端口,并在防火墙设置里开启穿透通道监听端口
的端口。至此我们便初步完成了内网穿透的基本操作,在外网环境中,只要输入此公网地址,即可访问绑定的内网服务。
第四步,将公网地址绑定到域名
原理:
使用STUN穿透时,不光ip地址会变,端口也会变,这使得我们无法通过传统ddns来绑定地址,所幸我们可以利用Cloudflare回源和跳转规则。
回源
我们可以设置一条回源规则,令外网设备访问规则内的域名时,Cloudflare的cdn服务器会将流量转发到我们指定的地址和端口,并且这一切对于访问者来说是不可见的,他在浏览器地址栏里只能看到域名。
这个方案看起来很完美,只可惜存在一个致命的缺陷:当我们在通过域名访问时,所有流量都会从CloudFlare的cdn服务器中转,而免费计划中,只能使用海外的cdn服务器,从国内访问速度非常感人。
跳转
本教程中选择使用重定向规则进行跳转。在我们设置里跳转规则后,外网设备通过域名访问时,会先从Cloudflare的cdn上得到一条跳转响应,然后浏览器会自动跳转到规则中设定的地址。
使用这种方法,流量不会经过cdn服务器,访问者在浏览器地址栏里看到的是跳转后的地址。
使用重定向规则进行跳转
解析域名到Cloudflare
首先需要将你的域名解析到Cloudflare,网上的教程很多,开头的链接里也有教,在此不做赘述。
在网站概览界面向下翻,在右侧可以看到区域id
,将其保存下来备用。
添加一条a类型的记录到你需要访问的域名,本教程里名称填入*,代表所有的未指定的二级域名都会返回这条记录,ipv4地址随便填一个有效的即可,代理状态勾选,ttl保持自动,填写完成后保存。
配置重定向规则
点击左侧菜单的规则
-重定向规则
,在单一重定向页面下点击创建规则
,规则名称随意设置。
当传入请求匹配时...
处选择自定义筛选表达式
,字段选择主机名
,运算符选择等于
,值
需填入自行设置的用于跳转的域名;示例为test.yourdomain.com
,test
为自定义的二级域名前缀,yourdomain.com
换成你的顶级域名。
URL 重定向
处选择类型为动态
并勾选保留查询字符串
,状态码选择302
或307
均可;表达式填concat("http://STUN的公网ip:公网端口", http.request.uri.path)
,即将示例中的183.6.66.666:6666
替换为在lucky中复制的地址。
请先不要点击部署
在保存之前先按下F12打开开发者工具,点击右侧开发者工具的网络
或者network
选项卡,再点击左侧Cloudflare页面中的部署
。
接下来可以在开发者工具中看到一串数字+字母的项,点击它,在标头
选项卡下复制并保存下它的请求网址
;然后切换到载荷
选项卡,右键第一行复制并保存它的值。
请求网址形如:
https://dash.cloudflare.com/api/v4/zones/区域id/rulesets/规则集id/rules/规则id
将区域id
、规则集id
、规则id
保存下来备用。
保存的请求载荷形如:
{
"id": "c7087a0fb757480xxxxxxxxxxxxxxxx",
"version": "1",
"action": "redirect",
"expression": "(http.host eq \"test.yourdomain.com\")",
"description": "redirect",
"last_updated": "2024-05-28T08:14:45.555123Z",
"ref": "c7087a0fb757480xxxxxxxxxxxxxxxx",
"enabled": true,
"action_parameters": {
"from_value": {
"status_code": 307,
"target_url": {
"expression": "concat(\"http://183.6.66.666:6666\", http.request.uri.path)"
},
"preserve_query_string": true
}
}
}
你的id
、ref
应该与规则id
一致,description
就是之前设置的规则名称。
删去其中version和last_updated变量(也就是示例中的第3、5行)保存备用。
第五步,利用webhook自动更新重定向规则
lucky提供了webhook接口,可以在ip和端口发生变化时发送一条网络请求,调用Cloudflare的api来更改重定向规则或是dns记录。
让我们返回lucky后台,找到刚才创建的STUN穿透规则,点击编辑,开启webhook(而不是全局webhook),按如下方式配置:
更新重定向规则
接口地址:
https://api.cloudflare.com/client/v4/zones/区域id/rulesets/规则集id/rules/规则id
请注意:这不是上一步抓到的请求网址,请用之前保存的
区域id
、规则集id
、规则id
对以上链接进行替换。
请求方法:PATCH
请求头:
Authorization:Bearer API 令牌
要创建API令牌,点击Cloudflare控制台右上角-我的个人资料
,在左侧菜单点击API令牌
,页面中点击创建令牌
。
在令牌创建页面选择创建自定义令牌
。
权限和区域资源如图设置,区域资源第三列选择你要用于跳转的域名。
点击
继续
-创建令牌
,直至出现以下界面:复制第二行双引号里的内容(不带引号),粘贴到Lucky的Webhook的请求头中。
请求主体:
把之前保存的请求载荷删去last_updated
和version
后,将expression
中的ip:端口
替换为#{ipAddr}
你的id
、ref
应该与规则id
一致,description
就是之前设置的规则名称。
{
"id": "c7087a0fb757480xxxxxxxxxxxxxxxx",
"action": "redirect",
"expression": "(http.host eq \"test.yourdomain.com\")",
"description": "redirect",
"ref": "c7087a0fb757480xxxxxxxxxxxxxxxx",
"enabled": true,
"action_parameters": {
"from_value": {
"status_code": 307,
"target_url": {
"expression": "concat(\"http://#{ipAddr}\", http.request.uri.path)"
},
"preserve_query_string": true
}
}
}
接口调用成功包含的字符串:"success": true
。
这些全部完成后点击Webhook手动触发测试。注意:测试时的数据均为虚拟数据。如果不成功,请先查看webhook里返回的内容是否很长且包含"success": true
,如果包含,那么复制那行并替换接口调用成功包含的字符串里的内容;然后多试几次排查网络问题,再检查各参数设置,配置正确后点击保存。
等待lucky执行成功webhook后,在浏览器无痕模式中输入在重定向规则中设定的域名,即可跳转至对应的公网地址。如需收藏或分享链接,只需将浏览器中的ip:端口
部分重新替换回域名即可。
第六步 将反向代理服务器暴露至公网
经过之前的步骤已经可以方便地通过域名访问内网服务,但倘若有多个服务,则需配置多个重定向规则和穿透隧道,然而Cloudflare免费计划只允许创建10个重定向规则,部分地区的宽带甚至只能打通一个隧道。因此我们可以类比有公网地址的情况,通过反向代理服务,在只开启一个端口的情况下通过不同的二级域名来访问不同的内网服务。
基本思路
有公网IP时,我们只需通过DDNS将泛域名解析到宽带的公网IP,并将反向代理的端口暴露在公网下即可。
使用STUN穿透时,解析IP很容易,stun穿透得到的公网ip跟DDNS通过接口获取的ip一致,这部分操作可以照搬有公网的情况。
端口号不固定这点依旧利用重定向规则来实现跳转,目的是让最后跳转到前缀.顶级域名:端口号/...
的形式,再将所有二级域名指向公网ip。
首先搭建好反向代理,将泛域名通过DDNS解析到接口获取的ipv4地址,将stun穿透的目标地址和端口设置为反向代理的本地地址和https端口,并通过https://域名:公网端口
的形式进行访问,测试没问题之后再进行下一步。如果不配置证书,就把穿透目标端口设为反向代理的http端口,并通过http访问。
如果你还没有配置过DDNS、SSL续签、反向代理,请自行搜索配置方法,相关教程非常多。如果你只有简单的需求,那么可以考虑使用Lucky一站式解决。配置方式可参考winnas进阶篇之 lucky插件的使用 和 也许是目前最通俗易懂的Lucky反向代理演示? ,注意协议要选择ipv4。
期望中的效果
我希望只是用一条规则,就能让泛域名*.bbb.com
匹配到的所有域名跳转到*.aaa.com
对应的子域名去。比如alist.bbb.com
->alist.aaa.com
、emby.bbb.com
->emby.aaa.com
。在此,我姑且将bbb.com
称为用于跳转的主域名,aaa.com
称为跳转后的域名。
这样当你想要访问域名前缀为sv1
的服务,只需在浏览器中输入sv1.bbb.com/
,会自动跳转到https://sv1.aaa.com:[port]/
;如果要指定访问路径,输入sv1.bbb.com/path1/path2/path3
,会自动跳转到https://sv1.aaa.com:[port]/path1/path2/path3
。
如果想要收藏或分享链接,只需把地址栏中的aaa
改回bbb
,并删除端口即可。
如果你想采用同一个域名下,二级域名向三级域名跳转的方案,如alist.example.com
->alist.4.example.com
,那么example.com
为主域名,4.example.com
为跳转后的域名。
配置重定向规则
首先确保你用于跳转的主域名下的泛域名(即*.bbb.com
)已解析到Cloudflare并开启小云朵;本地的反向代理使用跳转后的域名,且该域名下的泛域名(*.aaa.com
)已通过DDNS解析到解析到stun的公网ip。
接下来数出跳转前的主域名有多少个字符,上文例子里bbb.com
就是7个字符,example.com
是11个字符,我们将其记作参数2
,给它加一个比你设置的域名前缀长度更大的数(比如10),可以得到参数1
。
参考第4、5步配置重定向规则,webhook请求主体中的第一个expression改成"true"
(也就是所有传入连接都跳转),target_url的expression改成"concat(\"https://\",substring(http.host , -参数1,-参数2), \"跳转后域名:#{port}\", http.request.uri)"
如果你想通过http访问,请把target url的expression中的
https
改为http
。
即现在的请求主体为
{
"id": "c7087a0fb757480xxxxxxxxxxxxxxxx",
"action": "redirect",
"expression": "true",
"description": "redirect",
"ref": "c7087a0fb757480xxxxxxxxxxxxxxxx",
"enabled": true,
"action_parameters": {
"from_value": {
"status_code": 307,
"target_url": {
"expression": "concat(\"https://\",substring(http.host , -17,-7), \"aaa.com:#{port}\", http.request.uri)"
},
"preserve_query_string": true
}
}
}
结语
本文简单介绍了lucky的STUN穿透功能的使用方法,成功将反向代理服务暴露至公网。通过配置Cloudflare重定向规则实现了直接通过域名访问,近似达到了拥有公网IP的效果,同时满足了低延迟、大带宽、免费的需求,并且相对公益frp要稳定的多。
当然本期只实现了web应用的穿透,并未覆盖到大内网的全部痛点。下一期将利用STUN穿透解决大内网环境BT/PT上传下载难的问题,至于部分应用必须指定端口号的问题,我们放到第三期说。