使用Lucky的STUN内网穿透利用UPNP和NAT1在公网打洞并配置伪DDNS

使用Lucky的STUN内网穿透利用UPNP和NAT1在公网打洞并配置伪DDNS

背景/需求

我的要求很简单,就是在外网访问家里NAS服务器上的文件,实现远程办公

这时候有人会说:“简单嘛,要个公网IP不就行了?”

好好好,下面就是本人办过的宽带和要公网IP的经历:

电信宽带,有动态IPv6地址,至于公网IPv4地址,我要不到

联通宽带,有动态IPv6地址,至于动态公网IPv4地址,听说很容易要到,不过我没有联通宽带

移动宽带,有动态IPv6地址,至于IPv4是不是公网,懂得都懂,目前我正在用

4G、5G网络,有IPv6地址,至于IPv4是不是公网,想都别想

  • 公网IPv4地址可以通过NAT网关实现网络地址转换分配给多个设备,这个网关设备通常就是路由器,通过路由器上端口映射、端口转发或DMZ主机等功能,即可通过ip+端口或域名+端口的形式,访问内网的设备

  • 公网IPv6地址不用解释,由于数量多,可以不用NAT技术来解决地址数量问题(当然也是可以NAT的,具体看这篇文章:《OpenWrt iStoreOS X-WRT 配置 IPv6 NAT6》),缺点就是IPv6目前没有做到全面覆盖(未来也不可能做到),IPv4 Only网络下访问非常头疼,此外IPv6地址也是难记得要命

  • 现在大多数IPv6,前端都有防火墙(如光猫防火墙),无法搭建服务,所以这种情况有IPv6地址都是白搭

  • 对于80、443端口,即HTTP、HTTPS默认服务端口,可以配置虚拟主机,实现一个端口绑定多个域名

另外,咱们折腾设备的大多非网络专业,安全意识淡薄。即使您有公网 IP,鉴于国内大部分都无公网 IP,国内厂商的家用路由器基本都没有配置高级防火墙,您对外暴露的网络资产会受到攻击的风险。运营商不提供给您公网 IP,实际上是给您免费添置了一台价值万元以上的防火墙,能够保护您的网络资产安全,毕竟持续的网络攻击肯定会降低您的用网质量。另外,如果您有固定公网 IP,由于 IP 一直不变动,很容易出现 IP 被封的问题,使用动态 IP 能帮您省下很多麻烦。如果您拥有企业级固定公网 IP,从合规性考虑,您需要履行网络安全等级保护,这意味着您每年需要花几十万以上去做这件事。我们只是普通用户,何必这样呢?

不同方案

远程访问家用设备(NAS等)的方法对比:https://zhuanlan.zhihu.com/p/431090794

说到内网穿透,即网络打洞,实际上有很多种方案,如花生壳、frp、ngrok等基于端口映射的方案,这些方案本人都尝试过,对于今天这篇文章,存在以下几个问题:

  • 要求高,需要一台有公网IPv4服务器做端口转发
  • 费用高昂,高带宽服务器普通用户付不起
  • 配置复杂,超出普通用户的知识范围
  • 速度慢,受限于公网IPv4服务器流量和带宽
  • 延迟高,每次连接都需要经过公网IPv4服务器中转

基于以上问题,本人一直在查找方案,经朋友推荐,了解到了一款软硬路由公网神器——Lucky(大吉)

项目地址:

https://lucky666.cn/

https://github.com/gdy666/lucky

不过这边需要注意的是,此工具新版并不开源

使用经历

不过一开始以为是OpenWrt路由专用,我手上又没OpenWrt设备,就不打算试

https://www.right.com.cn/forum/thread-8267181-1-1.html

听说可以实现另一种形式的内网穿透,本人非常激动,然后就看了看与它相关项目Natter,在Windows下用Python试了试没有成功,说有防火墙(实际没有)和没有权限。研究了一下文档,发现在DDNS的实现上还有问题,就放弃了

Natter:https://github.com/MikeWang000000/Natter
NATMap:https://github.com/heiher/natmap

后来了解到Lucky可以在Docker中运行,甚至能直接跑在Windows上,就在群晖的Docker上试着搭建了一下,发现正好满足了我对于内网穿透的要求

打洞原理

图源自natter

这种STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)内网穿透技术,可以帮助解决因NAT技术所带来的网络连接问题

STUN技术允许NAT1用户获取公网端口,通过路由端口转发或者LUCKY内置转发,将内网服务端口暴露到外网,从而实现内网穿透的目的

这个技术有以下几个好处:

  • 不需要一台有公网IPv4服务器做端口转发
  • 无需付费,最多就是购买个域名
  • 配置简单,最多浏览器F12抓抓包
  • 速度快,受限于你宽带的上传带宽
  • 延迟低,和你游戏联机的技术一样

不过也有一些缺点:

  • 访问地址不固定,穿透后端口千变万化
  • 无法DDNS,DNS无法设置解析
  • 稳定性未知,保活比较难做

对于前两点,可以配合WebHook调用相关API,搭配Cloudflare的页面规则等进行301/302跳转,可以近似实现DDNS的功能

前提条件

为了能打洞成功,需要保证您的宽带默认为FullCone NAT(全锥型NAT,NAT1),如果为您的宽带为对称性NAT,那么不好意思,本方法不适合您

此方法的原理类似PCDN,各大运营商都在大力打击,将原先的NAT1改为NAT4,所以无法使用此方法穿透,解决方法就是换一家运营商

本文提供的方法仅供家庭用户轻量级使用,不会对运营商的网络设施造成破坏性干扰

如果您是商业重量级用户,赶紧gun,不要破坏我们的网络环境,本人非常鄙视这种行为,尤其是那些一个点拉N条宽带跑PCDN的人

配置网络

推荐方案:
光猫设置桥接模式,在路由器系统如 OpenWRT 上直接运行 LUCKY(仅经过一层 NAT)

可行方案:
在子网中的主机上运行 LUCKY,在光猫或路由器上对其开启 DMZ 功能,或对需要开放的端口设置端口转发(手动或UPNP)。(经过多层NAT)

我的网络是光猫桥接,爱快软路由拨号

如果你是光猫拨号,请想办法联系客服解决,如果无法解决,请自行百度

大多数的路由器默认使用NAT4以保障安全,如果改为NAT1(通常不支持),理论上可以不用开启UPNP,NAT4是要必须开启UPNP的,所以,最好开启UPNP,如果没有这个功能,只能给那台设备的 IP 设置 DMZ

STUN穿透环境要求

STUN技术的实现需要一些前提条件和环境要求。在使用STUN穿透功能之前,需要确保路由器、光猫等网络设备的设置和网络环境都符合要求。

  1. 光猫拨号的情况下你需要在光猫后台设置DMZ指向路由器。(路由器拨号的跳过这一步)
  2. 建议使用STUN穿透功能的用户将LUCKY运行在主路由上面,如果LUCKY运行在局域网局域网内,以下三种设置方法任选一种
    1. 你需要在路由器设置DMZ指向LUCKY所在的局域网IP,
    2. 在路由器端设置 穿透通道监听端口 指向LUCKY内网IP同样的 穿透通道监听端口,注意前后这两个端口的数组一定要设置是同样的。
    3. 开启路由器端口的UPNP功能。
  3. 上面第二条的方法是只针对使用Lucky内置转发功能的设置,如果你没有使用Lucky内置转发,可以忽略第二条。

开启路由器的UPNP功能:

安装Lucky

对于 OpenWrt ,这里不再重复,请看官方文档

https://lucky666.cn/docs/install

这里采用群晖Docker安装,注意以下配置:

使用Host网络:

启用自动重新启动:

映射存储空间位置:

访问http://你的IP:16601/,用户名:666,密码:666,测试是否可用

配置STUN

程序很强大,从端口转发到DDNS,啥都有,建议参考官方文档进行配置,这边就少胡说八道一些

https://lucky666.cn/docs/modules/stun

菜单->STUN内网穿透->穿透规则列表->添加穿透规则

补充:新版已支持UPNP,可以关掉NAT-PMP,使用UPNP,兼容性更好

启用规则,复制STUN穿透公网地址,用手机连接到另一个网络测试

如果能够访问,说明穿透成功,可以使用STUN穿透公网地址临时访问服务

配置WebHook伪DDNS

注意的是,此时的端口号非永久,IP地址也非永久,注意是两个各自非永久,即使DDNS了IP也无法DDNS端口,需要设置DNS的URL记录才能实现

目前没有DDNS程序支持设置DNS的URL记录,luckily,Lucky支持WebHook,有了WebHook,我们可以调用DNS运营商的API接口实现DDNS(实际上DDNS程序的原理也不过如此)

此处我们使用Cloudflare举例,实际上还可以使用其它支持跳转的API,最主要的就是要F12抓取更新地址的接口

需要注意的是,Cloudflare DNS并不支持URL记录,需要使用它强大的页面规则来设置,这个规则最多可以设置三条,对于一般人来说也够用了

我这波属实是跳进了好几个坑:

  • 路由器非OpenWrt系统
  • 非路由器安装
  • DNS服务器不支持动态端口

Cloudflare

打开Cloudflare Dashboard,在域名的DNS设置里面添加A记录,ip任意填,打开小云朵

接着创建页面规则

点击设置或编辑,保存,F12抓包获取区域ID和规则ID,https://dash.cloudflare.com/api/v4/zones/后面跟着那串就是,pagerules前的是区域ID,pagerules后的是规则ID

回到Lucky,点击编辑规则,找到Webhook,点击开启,如下设置

接口地址:

1
https://api.cloudflare.com/client/v4/zones/区域ID/pagerules/规则ID

请求方法:PUT

请求头:

1
2
X-Auth-Email: 你的CF邮箱
X-Auth-Key: API Key(获取方法同Cloudflare DDNS)

请求主体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"targets": [
{
"target": "url",
"constraint": {
"operator": "matches",
"value": "你的域名/*"
}
}
],
"actions": [
{
"id": "forwarding_url",
"value": {
"url": "http://#{ipAddr}/$1",
"status_code": 301
}
}
],
"priority": 1,
"status": "active"
}

貌似可以进一步简化

接口调用成功包含的字符串:

1
"success":true

点击Webhook手动触发测试按钮,如无异常,应该出现"success":true

然后回到Cloudflare,可以发现配置被修改,已经不是刚才的值,说明伪DDNS配置成功

自定义脚本触发

另外还可以使用自定义脚本触发,配合其他程序,实现更复杂的功能

为什么说它是“伪DDNS”?

目前我们做到了访问域名就跳转到IP+端口的地址,就是301/302跳转,并且我们的方案支持保留链接的参数。你也可以使用一台专门的服务器来实现这个操作,但成本过于高昂而且没有现成API,这里直接白嫖免费的Cloudflare

但由于存在301/302跳转后,浏览器的地址栏内就只有IP地址,没有域名,不是很方便

具体就是如果需要分享一个已经打开的链接,需要重新编辑,把原来IP+端口的部分替换为域名

如http:///test2.example.com,打开后就变成http://66.666.66.66:16666 ,如果你进入了其它目录,如:http://66.666.66.66:16666/Cloud ,你需要分享该链接,就需要改成http://test2.example.com/Cloud

最佳实践

Lucky+DSM实现文件点对点分享

感谢网友 @Rowan 无意发现这个方法,受邀在该文章上写出来,分享给大家

在群辉默认分享的时候,可以设置一下固定地址,可以免去修改地址的繁琐

控制面板>外部访问,填写固定域名,端口默认80,在文件分享时会默认从这个域名的80端口分享,也就是分享的时候不再是ip+端口,避免ip和端口跳动

这样直接访问分享出来的链接就可以跳转到NAS的IP地址+端口了,而且无需手动修改

这样可以避免使用内网穿透,无需 QC,直接连接公网 IP + 端口下载文件,且容易分享

不建议将 DSM 直接暴露在公网上,在对外公开 DSM 前,最好给 DSM 套上 WAF(Web应用防火墙),避免脚本小子和0day漏洞带来数据安全问题

Lucky+本地Alist+云端Alist实现隐藏端口号固定地址访问

使用IP+端口域名+端口访问肯定不是一件长久的事,尤其是Alist文件分享。为了解决这个问题,我们可以使用Lucky的STUN穿透功能,调用Alist的相关API来实现:

  1. 搭建一台本地服务器、一台云服务器

  2. 在本地、云服务器上安装好Alist

  3. 本地服务器上,使用Lucky,把本地Alist的5244端口穿透出去,可以得到一个动态的IP+端口,在浏览器访问该IP+端口,可以正常看到本地搭建的Alist

  4. 进入云服务器的Alist管理,添加一个Alist V3类型的储存,填入服务器链接为该IP+端口,保存后,可以在云服务器的Alist上看到挂载的本地Alist

    • 您最好在这一步把需要的配置项填写好,避免后面改请求主体麻烦
  5. 利用浏览器F12=>网络,抓取Alist更新配置的API调用信息,方法是:

    1. 进入云服务器Alist管理=>存储
    2. F12打开开发人员工具,点击上方的网络一栏
    3. 在Alist管理存储页面中,点击编辑刚才添加的储存后保存,此时浏览器会提交一个POST请求,F12中可以看到,通常名为update
    4. 把请求地址、请求头(只需留意Authorization)、请求主体复制下来,拷贝到Lucky的WebHook中对应的设置里面
      • 请求地址如:https://云服务器地址/api/admin/storage/update
      • 根据官方接口文档,请求头只需含有Authorization: 你的token 一行就行
        • Authorization可以是alist-token,没有时间限制,可以在Alist管理=>设置=>其他=>令牌中获取
        • Authorization也可以是用户登录生成的临时JWT密钥,但会有时间限制,需要修改云服务器Alist配置文件config.jsontoken_expires_in一项,延长过期时间(数值的单位是小时)
    5. 替换请求Body中相关地址为#{ipAddr}
      • 挂载路径、序号等设置项已经在默认的请求主体中了,如果不需要,请自行精简
  6. 测试WebHook,点击 Webhook手动触发测试 ,此时云服务器Alist储存配置中服务器链接会被修改成http://192.168.31.1:xxxxx

  7. 复制请求成功后服务器给出的回应内容到接口调用成功包含的字符串里面,如:

    1
    {"code":200,"message":"success","data":null}
  8. 保存后,如果没有问题,会显示WebHook触发结果为“成功”

    • 如果云服务器已经能够正常连接,但Lucky一直显示从未触发WebHook,或者失败,说明回应内容有问题
      • 请按照实际情况修改接口调用成功包含的字符串

      • 如果云服务器的地址包含端口,使用http协议可能会出错

  9. 返回云服务器的Alist,查看挂载的配置是否正常更新

  10. 之后即可使用云服务器的Alist地址访问到本地的Alist,实现网盘直链私有化,理论上下载速度就为你宽带的上传速度

注意:

  • 如果没有云服务器,但有内网穿透,又不想走穿透流量,可以将上面的云服务器的IP地址改为本机(127.0.0.1),并使用本机Alist的token,即让本地Alist自己给自己WebHook公网IP,这样可以少使用一个Alist程序,本地调用WebHook更加安全稳定
  • 如果没有云服务器,但有IPv6地址,可以使用支持IPv6回源的CDN,将IPv6转换为IPv4
  • 为了防止IP被扫,运营商可能会禁ICMP PING,海外用户的连通性需要使用其他方式测试
  • 对于手头上有大量没有公网IP地址但网络类型为NAT1宽带的用户来说,可以先将文件同步到各个本地服务器节点,然后把这些本地服务器挂载到云服务器Alist,再配合上云服务器Alist的别名驱动和负载均衡功能,这样,客户端使用Aria2c(IDM不行),多次向云服务器请求下载地址,即可实现从各个本地服务器节点同时下载一个文件,提高下载速度

如果懒得折腾,可以使用成品的PCDN储存服务,如大家都知道的123网盘,就是使用这个原理,降低了运营成本,才带来了继蓝奏云之后大文件免登录分享的体验(割韭菜)

您自己购买的家用宽带,就应该为您自己服务,而不是帮助其他企业薅运营商的羊毛,最后得到的收益又去向其他企业。所以赶紧动手用起来吧!


参考:

https://lucky666.cn/