自建tailscale服务接入headscale服务器实现异地组网
使用 Tailscale + Headscale + 自建 DERP 中继官方仓库: https://github...
扫描右侧二维码阅读全文
30
2024/12

自建tailscale服务接入headscale服务器实现异地组网

使用 Tailscale + Headscale + 自建 DERP 中继
官方仓库: https://github.com/juanfont/headscale
官方教程:https://headscale.net/stable/setup/requirements/

部署

基于 ecs 公网 域名配置,防火墙相关端口自行放行:

协议 端口
tcp headscale web server 端口
tcp derp 的 https 端口
udp derp 的 stun 端口

#headscale 部署和设置
# 下载Headscale
wget -O /usr/bin/headscale https://github.com/juanfont/headscale/releases/download/v0.23.0/headscale_0.23.0_linux_amd64

chmod a+x /usr/bin/headscale

# 配置文件
mkdir /etc/headscale/

# 下载配置,注意版本选择
# 下载配置 headscale/config-example.yaml
wget -O /etc/headscale/config.yaml https://raw.githubusercontent.com/juanfont/headscale/refs/heads/main/config-example.yaml

# 下载二进制同版本的示例配置文件
wget -O /etc/headscale/config.yaml https://raw.githubusercontent.com/juanfont/headscale/v0.23.0/config-example.yaml

创建 headscale daemon systemd 后台文件,官方示例文件:
headscale/docs/packaging/headscale.systemd.service

wget -O /etc/systemd/system/headscale.systemd.service https://raw.githubusercontent.com/juanfont/headscale/refs/heads/main/docs/packaging/headscale.systemd.service

运行在非 root 用户下,添加用户:

useradd \
  --create-home \
  --home-dir /var/lib/headscale/ \
  --system \
  --user-group \
  --shell /usr/sbin/nologin \
  headscale

mkdir -p /var/run/headscale/
#创建空的 SQLite 数据库文件和 derp 文件:
touch /var/lib/headscale/db.sqlite /etc/headscale/derp.yaml
chown -R headscale:headscale /var/run/headscale/ /var/lib/headscale
chmod a+r /etc/headscale/config.yaml /etc/headscale/derp.yam

接下来 vi /etc/headscale/config.yaml 修改配置文件一些内容:

# server_url 写外网访问的 ip+端口
# 由于是ecs,所以写公网 IP,以及后面的端口要和 listen_addr 的一致
# 80 443 8080 需要备案
server_url: http://<ecs_public_ip>:8081
# 改为四个0,或者对应公网 IP 的内网网卡 IP 都行
listen_addr: 0.0.0.0:8081

# ip_prefixes 下的 ipv6 关闭了
ip_prefixes:
#  - fd7a:115c:a1e0::/48
# 如果使用 Aliyun ecs,ipv4 网段需要修改成其他的,因为 Aliyun 底层的 apt 源等都在这个范围内
# 如果 Aliyun ecs 修改此网段,建议看完本章文章再操作,因为后续 tailscale 要修改源码
  - 100.64.0.0/10

derp:
  server:
    # 不适用 headscale 内嵌的 derper 服务器
    enabled: false
  # 注释掉不使用官方的 derp 服务
#  urls:
#    - https://controlplane.tailscale.com/derpmap/default
# 把 paths: [] 修改成使用本地 derp 信息
  paths: [/etc/headscale/derp.yaml] # 参考:https://raw.githubusercontent.com/juanfont/headscale/refs/heads/main/derp-example.yaml

# 关闭 headscale 自动更新,避免 break change 无法启动
disable_check_updates: true

dns_config:
# 改为 false 不覆盖本地 DNS
  override_local_dns: false
  # 关闭 magic_dns
  magic_dns: false
  # 设置为你自己的标识,否则后续 tailscale 端连接上显示是 user@example.com
  base_domain: xxx

# 随机端口要打开, tailscale 客户端会使用41641 端口建立 wireguard 链接,这个端口会被中间网络设备阻止
randomize_client_port: true

安装和设置 headscale 补全,因为 headscale 是 daemon 和 cli 两部分,daemon 起来后 cli 很多命令可以操作

apt update
apt install -y bash-completion
headscale completion bash > /etc/bash_completion.d/headscale
. /etc/bash_completion.d/headscale

启动 headscale daemon 进程:

# 测试文件
headscale configtest

headscale serve
# 配置文件没问题就 ctrl +c 取消掉使用 systemd 启动
chown -R headscale:headscale /var/lib/headscale
systemctl daemon-reload
systemctl enable --now headscale

derper 部署

Tailscale Derp 服务器见:https://tailscale.com/kb/1118/custom-derp-servers
headscale 是控制层面,下发信息和路由配置,而 derper 是中继和打洞服务器,利用修改版本的 stun 协议打洞,例如两个无公网 IP 但是可以访问到公网的客户端,客户端和另一个客户端建立连接都是先 连 derp 看看自己和对端能否打洞成功,成功就直连对方,否则就走 derper 中继来转发。

先查看机器上的 iptables 模式:

$ iptables -w -V
iptables v1.4.21
iptables v1.8.4 (legacy)
#----
iptables v1.8.9 (nf_tables)

安装Golang

  1. 从 golang 官网下载
    安装文档:https://golang.org/doc/install
  2. 解压和安装

    sudo tar -C /usr/local -xzf <文件路径>
    rm -rf /usr/local/go && tar -C /usr/local -xzf go1.23.4.linux-amd64.tar.gz

    sudo mkdir -p /usr/local/gopath/bin

    编辑文件,配置环境变量

    sudo vim /etc/profile

    export GOROOT=/usr/local/go
    export GOPATH=/usr/local/gopath
    export GOBIN=$GOPATH/bin
    export PATH=$PATH:$GOROOT/bin
    export PATH=$PATH:$GOPATH/bin
    source /etc/profile

    完成安装,运行go version检查是否成功

    go version

    配置代理

    go env -w GOPROXY=https://goproxy.cn,direct

安装derper

go install tailscale.com/cmd/derper@main

无域名IP配置,无需求跳过

进入 /usr/local/gopath/pkg/mod/tailscale.com@xxx/cmd/derper,其中xxx会根据版本变化,可以用tab补全
编辑 cert.go,注释掉下面三行并保存
1933551-20241119182027954-1611406331.png
同路径下执行go build -o /etc/derp/derper
cd /etc/derp,查看是否成功
自签一个假的域名,用来启动derper服务(其中derp.myself.com为假域名,可以替换):

openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout /etc/derp/derp.myself.com.key -out /etc/derp/derp.myself.com.crt -subj "/CN=derp.myself.com" -addext "subjectAltName=DNS:derp.myself.com"

启动服务

systemctl start derper
systemctl enable derper

在云服务管理中开启 udp 3478 端口,以及tcp 33445端口
在本地浏览器打开 https://<;ip地址>:33445/ ,查看是否成功

为了防止 derper 被白嫖,所以 官方文档 推荐开启 --verify-clients 选项,然后同时部署一个 tailscale 客户端,derper 会从这个 tailscale 的 sock 文件获取所有认证过的 peer 信息,所以上面挂载 sock 和 depends_on 以及使用同一版本的 derper 和 tailscale 。

修改 headscale 指定的 derp 文件 vi /etc/headscale/derp.yaml 的内容:

# https://github.com/tailscale/tailscale/blob/main/tailcfg/derpmap.go#L71
regions:
# 900-999 的 region id 是给预留的
# 有条件的可以多个 derper
  900:
    regionid: 900
    regioncode: custom
    regionname: custom_name
    nodes:
      - name: 900a
        regionid: 900
        hostname: my.mydomain.no
        ipv4: <ecs_public_ip>
        #ipv6: "2604:a880:400:d1::828:b001"
        derpport: 12345 # https 端口
        stunport: 3478 # udp port
        stunonly: false
        insecurefortests: true
  # 901:
  #   regionid: 901
  #   regioncode: hs 
  #   regionname: Huawei Shanghai 
  #   nodes:
  #     - name: 901a
  #       regionid: 901
  #       hostname: xxxx
  #       ipv4: xxxx
  #       stunport: 3478
  #       stunonly: false
  #       derpport: 12345
  #       insecurefortests: false

创建 authkeys

Tailscale 中有一个概念叫 tailnet,你可以理解成租户,租户与租户之间是相互隔离的,具体看参考 Tailscale 的官方文档: What is a tailnet
Headscale 也有类似的实现叫 user,即用户。我们需要先创建一个 user,以便后续客户端接入,例如:

headscale user create default

其他客户端接入需要首先在服务端生成 pre-authkey 的 key :

生成一个过期时间 365d 且可以重复使用的 authkey

headscale preauthkeys --user default create --reusable --expiration 365d 

查看已经生成的 key:

headscale preauthkeys --user default list

tailscale 也是分为 tailscaled 的 daemon 和 tailscale 的 cli 工具,windows、Linux 以及安卓的 Magisk 模块等都可以使用 cli 工具操作和排查,这点很重要。

下面是 tailscale up 时候一些常用通用选项:

  • --login-server: 指定使用的中央服务器地址(必填)
    --advertise-routes: 向中央服务器报告当前客户端处于哪个内网网段下, 便于中央服务器让同内网设备直接内网直连(可选的)或者将其他设备指定流量路由到当前内网(可选),多条路由英文逗号隔开

--accept-routes: 是否接受中央服务器下发的用于路由到其他客户端内网的路由规则(可选)
--accept-dns: 是否使用中央服务器下发的 DNS 相关配置(可选, 推荐关闭)
--hostname: 设置 machine name,否则默认会以 hostname 注册上去,特别安卓的 hostname 无法修改

tailscale cli 官方文档 https://tailscale.com/kb/1080/cli,也可以自己 tailscale --help 看命令帮助。

Linux 接入

derp 上的客户端

先接入 derp 也就是 ecs 上那个 tailscale:

tailscale up --login-server=http://<HEADSCALE_PUB_ENDPOINT>:8081 --accept-routes=true --hostname ecs --accept-dns=false --authkey 49f9cd....

查看信息

headscale node list

Linux 上 tailscale 会利用 tun 创建网卡,路由表在 52 里:

ip route show table 52

另外要注意,由于 derper 依赖这个 tailscale,所以这个 tailscale 不要乱重启(因为是授权信息来源),可能会导致其他端会断开一下子,例如我就遇到安卓端玩游戏会断开下

Linux 客户端

官方相关文档:
https://tailscale.com/kb/1031/install-linux
https://pkgs.tailscale.com/stable/#static

curl -fsSL https://tailscale.com/install.sh | sh

安卓

https://github.com/tailscale/tailscale-android

推荐 https://f-droid.org/packages/com.tailscale.ipn/ 下载,安装后在 右上角 - Accounts - 三个点 - Settings Accounts Use an alternate server,输入 http://xxx:8081,然后下面 Use an auth key 可能会没使用 authkey 跳转到浏览器出现一个 headscale nodes register ... --key nodekey:xxxx ,所以我们需要回到 headscale 上命令授权:

日志里会打印长串 key,所以不需要在其他端复制 nodekey

journalctl -xe --no-pager -u headscale | grep nodekey

headscale nodes register --user default --key nodekey:xxxxx

对于一些没有浏览器也没 tailscale cli 的都可以这样手动授权下。安卓上点击每个 peer 进去的右上角图标等于 tailscale ping xxx ,会显示能否直连和延迟。也可以后续使用 Magisk tailscale,那样可以有 cli 了,另外 apk 是使用 V-P-N 形式,断网和切换流量会断开,而 Magisk tailscale 则不会。

tailscale client 使用

每个 tailscale 端都可以执行命令来查看和排查一些信息。

# debug 命令隐藏在 --help 了,可以 tailscale debug --help 自行查看
# 打印 derp-map 信息
tailscale debug derp-map

查看和检测当前网络,会输出当前 derp 服务器信息:

$ tailscale netcheck

Report:
    * UDP: true
    * IPv4: yes, xx.x.xx.xxx:54417
    * IPv6: no, but OS has support
    * MappingVariesByDestIP: 
    * PortMapping: 
    * Nearest DERP: tx
    * DERP latency:
        - custom: 500µs   (tx)

tailscale ping 命令可以用于测试 IP 连通性, 同时可以看到时如何连接目标节点的. 默认情况下 Ping 命令首先会使用 Derper 中继节点通信, 然后尝试 P2P 连接; 一旦 P2P 连接成功则自动停止 Ping:

$ tailscale ping 100.64.0.3
pong from redmi8 (100.64.0.3) via DERP(custom) in 30ms
pong from redmi8 (100.64.0.3) via 192.168.0.107:47316 in 32ms

status 查看以及 peer 的信息:

tailscale status
tailscale status --json

修改当前节点信息,支持修改的属性 –help 自行查看:

# tailscale set --help
tailscale set --hostname=xxx

或者可以 down 后 up 带单一需要修改的参数执行下:

tailscale down
tailscale up --xxx
# 然后会打印全部参数,复制执行下

derp 调试

curl -vk https://127.0.0.1:12345/debug/vars

打通内网

Linux 端都要开启转发,windows 和安卓转发自行查找怎么配置。

echo 'net.ipv4.ip_forward = 1' | tee /etc/sysctl.d/ipforwarding.conf
echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.d/ipforwarding.conf
sysctl -p /etc/sysctl.d/ipforwarding.conf

然后在 server 端查看 node ID :

$ headscale node list
An updated version of Headscale has been found (0.23.0-beta1 vs. your current v0.22.3). Check it out https://github.com/juanfont/headscale/releases
ID | Hostname  | Name   | MachineKey | NodeKey | User    | IP addresses | Ephemeral | Last seen           | Expiration          | Online  | Expired
1  | ecs       | ecs    | [3ixoT]    | [VFZTB] | default | 100.64.0.1, | false     | 2024-07-26 07:12:50 | 0001-01-01 00:00:00 | online  | no
2  | localhost | ax18   | [egfcx]    | [LzS5J] | default | 100.64.0.2, | false     | 2024-07-26 07:13:14 | 0001-01-01 00:00:00 | online  | no
3  | localhost | redmi8 | [uqIVP]    | [l6sL7] | default | 100.64.0.3, | false     | 2024-07-26 07:13:06 | 0001-01-01 00:00:00 | online  | no

假设 ID==1 的局域网是 192.168.31.0/24 网段,我们希望其他 ID 设备上能访问到,先查看路由:

$ headscale routes list
ID | Machine | Prefix          | Advertised | Enabled | Primary
.....
1  | ax18    | 192.168.31.0/24 | true       | false   | false

headscale routes enable -r 1
其他节点查看路由结果:

$ ip route show table 52 | grep "192.168.31.0/24"
192.168.31.0/24 dev tailscale0

其他节点启动时需要增加 --accept-routes=true 选项来声明 “我接受外部其他节点发布的路由”。

现在你在任何一个 Tailscale 客户端所在的节点都可以 ping 通家庭内网的机器了,你在公司或者星巴克也可以像在家里一样用同样的 IP 随意访问家中的任何一个设备。

一个正在运行的节点增加路由可以使用 set 命令:

多条用英文逗号间隔

tailscale set --advertise-routes xx.xx.xx.0/24,xx,xxx.xxx.00.00/16

信息修改
例如安卓的 hostname 由于没有 cli 无法修改,而默认 db 使用 sqlite,可以修改:

$ sqlite3 /var/lib/headscale/db.sqlite
.tables
update machines set given_name="redmi8" where id=3;
# ip_addresses
修改 ip 需要先 tailscale down 改好后再 tailscale up

其他的修改自己琢磨。

源码修改 tailscale 网段
修改 tailscale 避免阿里云 100.64.0.0/16 上出问题,见漠然博客阿里云安装客户端后无法更新软件

https://zhangguanzhang.github.io/2024/07/25/headscale/#/%E4%BF%A1%E6%81%AF%E4%BF%AE%E6%94%B9

Last modification:December 30th, 2024 at 01:28 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment