前言
上一期介绍了如何通过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网络,理由有以下两点:
- Tailscale免费计划只允许3个用户,但是允许100台设备。通过共享账户的方式,可以允许更多人连接。
- 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_id
和client_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_id
和client_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单独设置权限。