自建 Tailscale 的 DERP 流量中转服务

经验

最近几个月一直在用 Tailscale 做远程办公的 VPN,真的感觉这是近年来用过最实用的应用之一。 这两天尝试配置使用自定义的 DERP 中继服务器,记录一下配置过程。

DERP 是 tailscale 提出的流量中转协议,虽然官方给出了自建 DERP 服务的文档,但是这是直接安装, 用 docker 会产生一些细节上的问题,我配合万能的网友的博客内容才得到了解决。 之前也配置过 zerotier 的 moon 服务器,感觉还是 tailscale 的用户体验更好一些。

我的方案与网上现有博客的主要区别是 用 Caddy 注册 derper 的 https 证书。 最后的效果是如果不用 443 端口可正常使用,但尝试与其他服务共同使用 443 端口失败了,原因未查明,应该是反向代理哪里没配置对。

前提条件

搭建自己的 DERP 服务器需要的前提条件:

  • 一个有公网 IP 的服务器
  • 一个域名 (也有人提出不用域名的方案,我没尝试过)
  • 允许开放一个 TCP 端口及一个 UDP 端口

配置 Caddy

虽然 Tailscale 开源的官方中转服务程序 derper 也可以自动申请 https 证书, 但我还是感觉 caddy 更稳妥一些,毕竟我用了多年在这方面也没出问题。

这里首先从域名商那里将要用的域名绑定到对应的 ip,然后写一下 caddy 的配置。 下面是失败了的配置,虽然能反向代理 derper 的 https 流量,但是始终无法中转流量

<domain name here> {
  @websockets {
    path /derp
      header Connection *Upgrade*
      header Upgrade websocket
  }
  reverse_proxy @websockets https://127.0.0.1:<port>
  reverse_proxy https://127.0.0.1:<port> {
    transport http {
      tls_server_name <domain name here>
    }
  }
}

我看有人说用 nginx 能成功,但没人给贴出完整的配置。 网上相关内容不多,基本都是用 caddy 来给 tailnet 里的机器上的服务签证书,而不是给 derp 服务器。 因为我在 caddy 上跑了一些服务,不打算换掉,就不再继续折腾了,无非是不太优雅,用着也没啥差别。

配置 DERP 服务

这里我用的现成的 docker 镜像,感谢制作镜像的网友。

给出一个能用的 docker-compose.yml 文件

version: "3.0"

services:  
  derper:
    image: fredliang/derper
    restart: always
    ports:
      - <port>:443
      - 3478:3478/udp
    environment:
      DERP_DOMAIN: "<domain name here>"
      DERP_CERT_MODE: "manual"
      DERP_ADDR: ":443"
      DERP_VERIFY_CLENTS: "true"
    volumes:
      - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
      - /root/derp/<domain name here>:/app/certs

需要注意的主要是后面的环境变量和文件映射:

  • 这里设置了 tailscale 客户端验证,因此首先要在服务器上安装 tailscale 并加入到目标网络中去, 然后将 与守护进程通信用的 .sock 文件传进去
  • 证书方面我禁止了 derper 来签,然后我用 caddy 的 apt 源安装后,默认证书的存放位置是在 /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/<domain name here>,我这里给这个目录做了个软连接到/root/derp/<domain name here>,把这个传进容器使用 (刚好命名方式跟镜像制作者要求的一样)

配置 tailscale ACL

{
  // 前面其他配置没动
  "derpMap": {
    "OmitDefaultRegions": true,
    "Regions": {
      "900": {
        "RegionID":   900,
        "RegionCode": "myderp",
        "RegionName": "myderp",
        "Nodes": [{
          "Name":     "myderp",
          "RegionID": 900,
          "HostName": "<domain name here>",
          "DERPPort": 8443,
        }],
      },
    },
  }
}

这里我是禁止了官方提供的 derp 服务,只用自己的。 因为官方的服务器虽然看起来时延会比我低,但感觉上不如自己的服务器的线路稳定。 其实用国内服务器最好,但一个是低配且低价的不好买,还有就是实名制和备案方面有点麻烦。

坑的部分

  • 在 tailscale 的实现里,即使没配置好 https 也会在控制页面显示 derp 服务可用,但实际不能用,也没有看到日志输出,难排查错误
  • caddy 反向代理 https 但上游用的是 ip 地址时,加上 tls_server_name 选项,这个我找了好久,虽然解决了对最后也没啥帮助,相关讨论。 之前没有折腾过,一直以为 https 的反向代理就是一句配置就完事了,但事实上比想象中要复杂。

总结

流量中转是远程组网非常重要的功能,由于特殊的网络环境,对国内用户来说这个功能能比较大的提升用户体验。 这次虽然没折腾到完美,还是足够水一篇博客了嘿嘿。

顺便一说,我个人对 Tailscale 的实现也是很感兴趣的,官方的博客对于原理部分介绍得也算细致, 等今后有时间时打算仔细研究一下它的代码,希望我也能写出有趣有意义的网络应用。