打通大内网第四期 部署并穿透DERP服务器 (基于Lucky的STUN穿透)

前言

上一期介绍了如何通过frp的stcp/sudp来固定访问端口,虽然可靠性和安全性都得到了保障,配置还是麻烦了一点,每添加一个服务都要修改两处frpc的配置文件,并需要每个访问者都手动更新。并且对于访问者而言,处理端口冲突、注册服务也需要一点知识储备,显然不如无脑安装软件来的方便。如果你只打算小范围内共享,那么直接使用tailscale也是个不错的选择。

理论上只要有一方为NAT1,是一定能直连的,但由于tailscale的服务器都在墙外,内陆的网络环境也比较复杂,偶尔会出现打不通的情况。然而我们可以另辟蹊径,在本地自建DERP中继服务器,并通过LUCKY穿透到公网上。

第一步 安装并配置Tailscale

Tailscale官网:https://tailscale.com/

关于Tailscale基本的安装、配置在此不多赘述,教程非常多,在此贴一篇OpenWrt的教程:
openwrt使用tailscale组建网对网的一些补充
如果你需要在外访问整个内网,请正确配置子网路由。

如果可能的话,尽量将Tailscale和DERP安装在同一台机器上。即使不行,最好也在部署DERP的机器上安装Tailscale,用于身份验证。
如果用于验证的Tailscale客户端是通过docker安装的,为了让DERP能够与Tailscale交互,请添加路径映射
-v /run/tailscale:/var/run/tailscale
我将Tailscale装在路由器上,DERP安装在路由器下面的Unraid上。如果以插件的形式直接安装,则无法使用本地局域网地址访问Unraid,只能使用Tailscale的100开头的地址,因此我最后用Docker安装,网络选择br0并指定了另外一个ip。

当配置完成,已经可以在外网通过Tailscale访问后,再注册一个用于共享的小号,并将其加入大号的Tailscale网络,理由有以下两点:

  1. Tailscale免费计划只允许3个用户,但是允许100台设备。通过共享账户的方式,可以允许更多人连接。
  2. DERP服务器若不开启验证,那么谁都可以白嫖;但一旦开启验证,只有本机当前登录Tailscale的账户有权访问,其他用户均无法访问。

然后将所有Tailscale客户端上登录的账号换成小号,加入大号的网络(也就是在最后一步,选择connect device的时候,点击大号邮箱)。

第二步 安装并配置DERP服务器

首先先给DERP服务器准备一个域名,并解析到STUN穿透的ipv4公网地址,如果你之前没有用过ddns,直接使用Lucky的DDNS,通过接口获取ipv4即可。

接着申请证书,使用Lucky申请非常方便,并且可以映射证书路径供其它服务使用,以及在证书更新时调用自定义脚本;如果Lucky和DERP没有部署在同一个机器上,可以用Lucky的webdav功能将映射证书的路径分享出来,或者通过自定义脚本运行scp和ssh命令,上传证书并重启DERP。具体过程略过不表。

接下来安装DERP,我使用docker安装,记得修改证书的映射路径、端口映射和域名。我这里将8051作为derp端口,3478作为stun端口。如果不想开启身份验证,请将第七行的true改为false。

docker run --restart always \
  -p 8051:443 -p 3478:3478/udp \
  -e DERP_CERT_MODE=manual \
  -v /your/cert/path:/app/certs \
  -e DERP_ADDR=:443 \
  -e DERP_DOMAIN=derp.xxxx.com \
  -e DERP_VERIFY_CLIENTS=true \
  -v /var/run/tailscale:/var/run/tailscale \
  fredliang/derper

然后为DERP服务器创建STUN穿透规则,需要两条。一条给derp端口,协议选择tcp,一条给stun端口,协议选择udp,不会的朋友可以去看看第一、第二期教程。

如果一切顺利,就可以在浏览器里访问https://derp域名:derp穿透后的公网端口。

看到以上界面说明成功。

第三步 将DERP服务器添加至Tailscale中

用大号进入Tailscale后台,在顶部切换到Access Controls选项卡,在acls后添加derpMap规则。

    // 前面有一些注释,不修改
    "acls": [
        // Allow all connections.
        // Comment this section out if you want to define specific restrictions.
        {"action": "accept", "src": ["*"], "dst": ["*:*"]},
    ],
    "derpMap": {
        "Regions": {
            "900": {
                "RegionID":   900,
                "RegionCode": "myderp",
                "RegionName": "home",
                "Nodes": [{
                    "Name":             "1",
                    "RegionID":         900,
                    "HostName":         "derp.xxxxx.xxx",
                    "DERPPort":         25814,
                    "STUNPort":         25876,
                    "InsecureForTests": true,
                }],
            },
        },
    },
    // Define users and devices that can use Tailscale SSH.
//...后面是ssh的内容,不修改

修改Hostname为你DERP服务器的域名,DERPPort和STUNPort分别改为对应的公网端口(不知道改成哪个端口就把我的规则和我之前的Lucky截图对比一下)。确认无误后点击保存。

在安装了Tailscale的机器(或容器)上执行

tailscale netcheck

如果没有出现测速结果里没有出现myderp,请检查规则是否编写正确,如果没有这个节点没有测速结果,可以稍等一会儿再试。当看到myderp节点的延迟后,证明配置成功。

第四步 自动更新DERP服务器端口

Tailscale支持通过网页后台面板、GitOps和api更新acl规则。使用GitOps可以清晰的看到每一次更新的时间以及是否成功,使用api则更加简单,本文使用api进行更新。

  • 生成OAuth client并获取id和secret

使用大号登录Tailscale后台,在Settings - OAuth clients中点击Generate OAuth client。OAuth client的名称随便填,勾选对ACL的read和write权限。然后点击Generate client,并复制生成的id和secret。

不同于最大有效期为90天的api key,OAuth clients不会过期,免去了手动更新key的麻烦。

  • 配置更新脚本

回到Lucky后台,编辑derp端口的穿透规则,将以下脚本填入自定义脚本中。
记得修改client_idclient_secret为你刚才获取到的值。

#set your oauth client here
client_id=XXXXXXXXXXXXXXXXX
client_secret=tskey-client-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

api_key=`curl -d "client_id=$client_id" \
  -d "client_secret=$client_secret" \
  "https://api.tailscale.com/api/v2/oauth/token"`
tskey=`echo $api_key | awk -F "\"" '{print $4}'`

DERPPort=${port}
tmp='/tmp/tailscale.hujson'
curl "https://api.tailscale.com/api/v2/tailnet/-/acl" \
-H "Authorization: Bearer $tskey" > $tmp
sed -i 's/"DERPPort".*/"DERPPort":         '$DERPPort',/' $tmp
curl "https://api.tailscale.com/api/v2/tailnet/-/acl" \
-H "Authorization: Bearer $tskey" --data-binary @$tmp

编辑STUN端口的穿透规则,将以下脚本填入自定义脚本中。
记得修改client_idclient_secret

#set your oauth client here
client_id=XXXXXXXXXXXXXXXXX
client_secret=tskey-client-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

api_key=`curl -d "client_id=$client_id" \
  -d "client_secret=$client_secret" \
  "https://api.tailscale.com/api/v2/oauth/token"`
tskey=`echo $api_key | awk -F "\"" '{print $4}'`

STUNPort=${port}
tmp='/tmp/tailscale.hujson'
curl "https://api.tailscale.com/api/v2/tailnet/-/acl" \
-H "Authorization: Bearer $tskey" > $tmp
sed -i 's/"STUNPort".*/"STUNPort":         '$STUNPort',/' $tmp
curl "https://api.tailscale.com/api/v2/tailnet/-/acl" \
-H "Authorization: Bearer $tskey" --data-binary @$tmp

目前Lucky并未提供自定义脚本的测试功能,不过只是填写两个变量而已,应该不会出什么幺蛾子...吧。


第五步 为Tailscale添加本地DNS并劫持域名

愿意折腾到这一步的朋友,有很大概率已经开启了ipv6,并配置好了反向代理。如果想要实现“一个域名,在内网/Tailscale中用内网ipv4,在外网中用ipv6”的效果,可以尝试一下添加本地DNS。如果不想配置反向代理,那么可以跳过这步。

本地劫持泛域名

首先在你是用的dns服务中添加HOSTS,劫持你要访问的泛域名。如果不想在内网劫持,仅想在Tailscale中劫持,可以用Docker再装一个SmartDNS,记得用网桥(如br0)把容器IP绑定到与宿主机同网段,并使用53端口。

  • 对于dnsmasq(也就是OpenWrt的默认dns)

    打开/etc/dnsmasq.conf,添加

    address = /mydomain.com/192.168.0.1
    

    并重启dnsmasq,这会劫持mydomain.com和它的所有子域名到192.168.0.1。

  • 对于SmartDNS
    自定义规则域名地址中添加

    address /mydomain.com/192.168.0.1
    

    并重启SmartDNS,这会劫持mydomain.com和它的所有子域名到192.168.0.1。

然后在内网机器上清除dns缓存,使用nslookup查询刚才劫持的域名。

由于OpenWrt会默认劫持所有访问53端口的流量到OpenWrt的53端口,因此如果你是用OpenWrt作为主路由,但自定义DNS不在OpenWrt的ip上,则需要在网络 - 防火墙 - 自定义规则中注释掉所有跟53端口相关的规则。

如果还不成功,大概率是某些科学插件修改或劫持dns导致的,请关闭劫持功能或者直接在科学插件中设置DNS劫持。

在Tailscale中添加本地DNS

进入Tailscale后台,进入DNS,点击Add nameserver - Custom,输入你内网DNS的IP地址,可以为Tailscale的100开头的地址,也可以是添加进Tailscale的子网地址,并开启Restrict to domain,填入需要劫持的顶级域名。(如填入mydomain.com,会劫持它的所有子域名)请注意,Tailscale不支持指定协议和端口,默认使用53端口、udp协议。

可能部分DNS服务默认拒绝外网请求,需要手动开启。以OpenWrt为例,需要在网络 - DHCP/DNS - 基本设置中取消勾选仅本地服务

结语

至此Tailscale和DERP服务器的配置就结束了,经过这一番折腾,我们实现了100%直连率,并统一了内网、Tailscale、ipv6的使用体验。在SVCB/HTTPS记录普及之前,对STUN穿透的折腾大约就止步如此了吧。

如果你仍对安全性抱有担忧,可以自行修改acl规则,默认拒绝共享账户访问子网,仅允许访问特定的ip和端口。如果你想让特定的设备不受用户权限的限制,可以给它打上tag,再对该tag单独设置权限。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容