基于建立私有DERP节点与私有Headscale优化Tailscale组网的实践学习

警告

本文仅用于学习讨论企业内部组网架构且不涉境外流量。本文所讲述的均为技术实践话题且实践内容仅供个人参考,任何企事业单位或个人,因参考本文导致的任何问题均与本人无关!

介绍

  • Tailscale 是一种面向个人和组织的安全网络服务。它建立在 WireGuard 协议之上,WireGuard 相比于传统 VPN 的核心优势是没有 VPN 网关,所有节点之间都可以点对点(P2P)连接,效率更高,速度更快,成本更低,在P2P连接模式下对服务器基本没有什么压力。Tailscale 是一款商业产品,个人用户在接入设备不超过 20 台的情况下是可以免费使用的,和ZeroTier原版限额差不多。
  • DERP 是 Tailscale 的中继服务器(自研协议)。它用于帮助 Tailscale 设备之间建立连接,尤其是在某些情况下,直接点对点连接不可行时,DERP 充当中继站点。它是一个通用目的包中继协议,运行在 HTTP 之上,而大部分网络都是允许 HTTP 通信的。根据目的公钥来中继加密的流量。
  • Headscale 是由欧洲航天局的 Juan Font 使用 Go 语言开发的Tailscale 控制服务器。可以部署在企业内部,没有任何设备数量的限制,且所有的网络流量都由自己控制。有个相关开源项目 Headscale-ui 就是用于图形化管理headscale的,为了安全性考虑暂未部署。

注意

    Headscale只是替代 Tailscale 官方的控制服务器,无法明显起到加速访问的作用,所以还需要部署DERP。

    下方是我绘制的一张流程图,描述了远程客户端配合Headscale + DERP基于Tailscale访问到Openwrt的流程:

为什么不是ZeroTier?

    首先我的Zerotier被流量识别,然后被轻松地ban了。并且尝试通过自建Moon节点服务器规避防火墙,依旧被ban了。虽然有一条L2TP over IPSec作为备选线路,但是我还是需要一个更方便的组网方式。

Tailscale就是一个很好用的工具,它包含了多种高级特性(例如Magic DNS)来方便用户的高级使用需求,tailscale的底层机制与zerotier不同。tailscale只会在需要与peer建立连接的时候才会尝试打洞而且最开始的流量一定是会经过DERP中转服务器。相比之下,zerotier会让每个客户端在启动时立即尝试与其他客户端的打洞,并一直维持这个连接。这意味着,zerotier创建链接时可以非常快速,但需要维护与所有对等节点的打洞链接,占用资源。而tailscale的懒加载机制无需预先维护与其他节点的任何打洞连接,无需预先维护任何状态。但是,每次通过tailscale创建虚拟连接时,初始所创建的连接其延迟很高,这会极大的影响使用体验。Tailscale也极其依赖中继节点,而在P2P 虚拟组网中,自建中继节点是相当重要的。一方面自建中继节点可以比地理位置较远的官方中继节点更好的观察和协调本地两台对等机的p2p过程,另一方面可以在打洞失败后快速中继和转发流量。

    相比之下,Zerotier的中继节点Moon实际上只是一个UDP中继节点。Zerotier会在端口9993上监听UDP连接来中继数据,因此在实际搭建的过程中只需将这一个UDP端口暴露至公网即可,要求极低。而暴露端口有多种方式可以实现,例如内网穿透FRP等等,也因此moon节点甚至都不需要有一个属于自己IP地址。相比之下,Tailscale的中继服务器(称为DERP服务)的搭建与zerotier相比存在“亿”点困难,而网络上的搭建教程真是参差不齐。Tailscale的中继DERP服务就是一个TCP中继节点,与Zerotier完全相反。需要能够公网访问,需要运行HTTPS服务,默认需要分配80端口来运行HTTP服务,需要额外暴露两个端口来运行HTTPS和STUN服务,必须允许ICMP流量的出入。这些要求限制了DERP服务的搭建,但是DERP服务的搭建也是Tailscale的一个优点,因为它提供了更好的安全性和可靠性。

配置Headscale控制器

    以下配置需在一台有静态公网IP地址的服务器上进行操作

# 下载headscale
wget https://github.com/juanfont/headscale/releases/download/v0.23.0-alpha1/headscale_0.23.0-alpha1_linux_386 -O /usr/local/bin/headscale
# 大陆服务器可能无法下载,可以自己下载好复制到服务器

# 赋权
chmod +x /usr/local/bin/headscale
# 配置目录
mkdir -p /etc/headscale
# 创建用户
useradd \
  --create-home \
  --home-dir /var/lib/headscale/ \
  --system \
  --user-group \
  --shell /usr/sbin/nologin \
  headscale
# 创建配置目录
mkdir -p /etc/headscale
# 创建目录用来存储数据与证书
mkdir -p /var/lib/headscale
# 创建空的 SQLite 数据库文件:
touch /var/lib/headscale/db.sqlite

修改SystemD配置文件

[Unit]
Description=headscale controller
After=syslog.target
After=network.target

[Service]
Type=simple
User=headscale
Group=headscale
ExecStart=/usr/local/bin/headscale serve -c /etc/headscale/config.yaml
Restart=always
RestartSec=5
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/headscale /var/run/headscale
AmbientCapabilities=CAP_NET_BIND_SERVICE
RuntimeDirectory=headscale

[Install]
WantedBy=multi-user.target

/etc/systemd/system/headscale.service

创建命名空间

headscale namespaces create default

下载官方配置文件案例,并存放到之前创建的/etc/headscale/config.yaml

wget https://github.com/juanfont/headscale/raw/main/config-example.yaml -O /etc/headscale/config.yaml

使用任意编辑器打开config.yaml文件进行以下修改:

server_url: https://xxx:18080

将server_url的值改为公网 IP或域名,我这边使用的是域名,方便上TLS。其中端口是我自定义的,防止和常用端口8080冲突,所以用的是18080

listen_addr: 0.0.0.0:18080

修改为实际监听的端口,与上面的端口一致,我这边上18080

metrics_listen_addr: 127.0.0.1:9090

这是一个本地监听端口,不对外开放。这个端点用于提供有关headscale服务器的指标和统计信息。默认情况下,这个端点只需要在本地访问

grpc_listen_addr: 127.0.0.1:50443

grpc_listen_addr是headscale服务器监听gRPC的地址。gRPC用于通过CLI远程控制headscale服务器。

ip_prefixes:
  - fd7a:115c:a1e0::/48
  - 100.64.0.0/10

可以将网段设置为自己喜欢的网段,仅用于Tailscale内通信,请注意不要产生冲突。

noise:
  private_key_path: /var/lib/headscale/noise_private.key

添加noise的私钥路径,如果没有会自动生成。

tls_cert_path: "/var/lib/headscale/server.crt"
tls_key_path: "/var/lib/headscale/server.key"

这里需要将路径分别改为证书的crt文件路径和key文件路径

magic_dns: false

    MagicDNS是一个自动注册DNS名称的功能,用于为网络中的设备提供人类可读的易记名称。启用MagicDNS后,你可以使用设备的名称而不是Tailscale IP地址来访问设备。如果你禁用了MagicDNS,你需要使用Tailscale IP地址来访问设备。

randomize_client_port: true

    该参数用于随机化WireGuard客户端的端口。默认情况下,Tailscale使用静态端口41641来建立WireGuard连接。但是,这个端口可能会被防火墙或其他网络设备阻止,从而导致Tailscale无法正常工作。通过启用randomize_client_port选项,Tailscale会在每次建立连接时随机选择一个可用的端口,从而避免这个问题。

配置文件修改结束

# 添加开机自启并立即启动
systemctl enable --now headscale
# 查看headscale运行状态
systemctl status headscale
# 检查端口开放情况
netstat -tunlp | grep "headscale"
tcp        0      0 127.0.0.1:50443         0.0.0.0:*               LISTEN      2113899/headscale   
tcp        0      0 127.0.0.1:9090          0.0.0.0:*               LISTEN      2113899/headscale   
tcp6       0      0 :::18080                :::*                    LISTEN      2113899/headscale   
udp6       0      0 :::3478                 :::*                                2113899/headscale

OpenWrt连接Headscale

安装Tailscale客户端

openwrt-tailscale-enabler-v1.36.1-fb2f6cf-autoupdate.tgz

opkg update 
# 安装依赖 
opkg install libustream-openssl ca-bundle kmod-tun
opkg install tailscale

    如果需要图形化配置还需要额外安装 luci-app-tailscaler 包,但其实图形化没什么用,最终还是要命令操作为主。

验证版本

tailscale up --login-server=http://Headscale服务器地址:18080 --accept-routes=true --accept-dns=false

向Headscale服务器发起注册

随后OpenWrt会回显给你一个链接

To authenticate, visit:
http://你的Headscale服务器地址:18080/register?key=905cf165204*********dbc22be9

使用浏览器打开

# 在Headscale服务器上输入
headscale nodes register --user default --key nodekey:5fec8a64df25d724*******42a6f14ca2ce4a*****0e5b6c0dca67
  • 服务端手动授权注册
  • 请注意"nodekey:"不可删除
  • 这里的default是用户名
  • 可以通过以下方式查看用户名

Headscale服务端回复时 Node openwrt registered 则代表节点(Node)注册成功

接下来宣告路由

# 在OpenWrt命令行输入
tailscale up --login-server=https://xxx:8080 --accept-routes=true --accept-dns=false --advertise-routes=10.10.10.0/24 --reset
  • --login-server的值替换为之前设置的server_url
  • 10.10.10.0/24 替换为自己的Openwrt网段,用于申明自己下面有这些网段
# 在headscale端输入
headscale routes list -i 1
# 其中“1”为openwrt的ID
# 启用ID为1的路由
headscale routes enable -r 1

因为我们之前没有添加过其他节点,所以默认是1

headscale routes list
ID | Node    | Prefix        | Advertised | Enabled | Primary
1  | openwrt | 10.10.10.0/24 | true       | true    | true

查看路由表

创建新接口

修改IP地址和子网掩码

分配防火墙区域

安装macOS客户端

方法一

App Store下载:(须要外区账号

https://apps.apple.com/us/app/tailscale/id1475387142

方法二

使用文末的关键词中获取独立分发版

不要同时安装应用商店版本和独立分发版本,同时只能装一个。 

浏览器打开 http(s)://<headscale地址>:18080/apple

找到两个profile下载地址

左边是AppStore版的

右边是独立分发版的配置

接下来类似openwrt的注册步骤

完成macOS的注册

安装Android客户端

Tailscale安卓版从1.30.0版本开始就支持了自定义headscale服务器,下载地址可以是Google Play,也可以是从文末的方式下载。点击右上角的三个点三次,就可以看到“Change server”的按钮了,将你的headscale地址输入后,点击“Sign in with other”就可以访问到熟悉的授权页面了。按照之前的步骤完成授权即可。

安装iOS客户端

iOS客户端在1.38.1版本后支持自定义Headscale服务器啦

App Store

重新打开APP点击Login就可以访问到熟悉的验证页面啦~

安装Windows客户端

官方完整版下载地址:tailscale-setup-full-1.54.0.exe

安装tailiscale客户端后,在命令行输入:

tailscale login --login-server https://你的headscale地址:18080

使用管理员身份的powerShell加入注册表

New-ItemProperty -Path 'HKLM:\Software\Tailscale IPN' -Name UnattendedMode -PropertyType String -Value always
New-ItemProperty -Path 'HKLM:\Software\Tailscale IPN' -Name LoginURL -PropertyType String -Value "https://你的headscale地址:18080"

然后重启客户端,进行登陆,验证操作步骤同上面其他设备一样。

DERP单节点配置与加固

修改/etc/headscale/config.yaml的配置

derp:
  server:
    # 如果启用,运行内嵌的DERP服务器并将其合并到其余的DERP配置中
    # 上述定义的Headscale server_url 必须使用https,DERP要求必须有TLS
    enabled: true

    # 用于内嵌DERP服务器的区域ID。
    # 如果区域ID与来自常规DERP配置的其他区域ID冲突,则本地DERP优先。
    region_id: 999

    # 区域代码和名称在Tailscale UI中显示,用于标识DERP区域
    # 自己拟定
    region_code: "******"
    region_name: "t******P"

    # verify_clients: true


    # 在配置的地址上通过UDP监听STUN连接 - 以帮助NAT穿越。
    # 启用内嵌DERP服务器时,必须定义stun_listen_addr。
    stun_listen_addr: "0.0.0.0:3478"

    # 用于加密headscale DERP和Tailscale客户端之间的流量的私钥。
    # 如果缺少私钥文件,将自动生成私钥文件。
    #
    private_key_path: /var/lib/headscale/derp_server_private.key

  # 以JSON编码的外部可用DERP映射列表
  urls:
    - https://wx.ict.run/derp.json
      #  - https://controlplane.tailscale.com/derpmap/default

  # 以YAML编码的本地可用DERP映射文件
  # 对于托管自己的DERP服务器的人来说,此选项可能会比较有趣:
  # https://tailscale.com/kb/1118/custom-derp-servers/
  #
  paths:
    - /etc/headscale/derp.yaml
      # paths: []

  # 如果启用,将设置一个工作程序定期
  # 刷新给定的来源并更新derpmap
  # 将被设置。
  auto_update_enabled: true

  # 我们应该多久检查一次DERP更新?
  update_frequency: 24h

并且在WEB服务器下添加一个json文件取名“derp.json”
这个文件需要允许被访问,因为这是下发配置用的

{
  "Regions": {
    "901": {
      "RegionID": 901,
      "RegionCode": "******",
      "RegionName": "*******",
      "Nodes": [
        {
          "Name": "909",
          "RegionID": 999,
          "DERPPort": 443,
          "HostName": "域名",
          "IPv4": "实际IPv4地址",
          "InsecureForTests": true
        }
      ]
    }
  }
}

额外配置(可选)

如果需要实现虚拟网络内的DNS劫持(自定义额外DNS解析)
需要额外修改/etc/headscale/config.yaml中的

  extra_records:
     - name: "nas.******.com"
       type: "A"
       value: "10.10.***.***"

文件下载

为保证文件下载链接的有效性与时效性,如需获取本次推文相关文件,请关注下方公众号回复关键字“1229”获取,感谢您的支持!

阅读更多

软件工程笔记

P15 软件危机 在软件开发中要遵循如下几点准则: “软件工程”是在1968年召开的一个讨论“软件危机”问题的 […]

Operation System

操作系统理论 操作系统(Operation System)是配置在计算机硬件上的第一层软件,是对硬件系统的首次 […]

基于建立私有DERP节点与私有Headscale优化Tailscale组网的实践学习

警告 本文仅用于学习讨论企业内部组网架构且不涉境外流量。本文所讲述的均为技术实践话题且实践内容仅供个人参考,任 […]

如何防止微信内置浏览器缓存

如何防止微信内置浏览器缓存 WebView 缓存策略 微信内置浏览器(微信WebView)中的缓存策略可能导致 […]

用了HTTPS 防火墙就不知道你在访问什么了吗?

什么是SNI? SNI(Server Name Indication)是TLS的扩展,用来解决一个服务器拥有多 […]

2 评论

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

富强民主文明和谐