tailscale自建headscale和derp中继

20 篇文章 3 订阅

tailscale derp中继服务简介

tailscale是一个基于WireGuard的零配置软件,它可以轻松地在多台设备之间建立点对点加密连接。

derp服务器是tailscale网络的重要组成部分。它作为tailscale客户端之间的中继,帮助客户端找到并连接到其他客户端设备。

在这里插入图片描述

但Tailscale 官方的服务端和 DERP 中继服务器全部在境外,在国内的网络环境中不一定能稳定连接,所以有必要建立自己的headscale服务端和 DERP 服务器。

在这里插入图片描述

准备工作:

  • 需要有自己的云服务器,本示例为阿里云轻量应用服务器
  • 需要有自己的域名并且已备案,本示例为阿里云域名
  • 云服务器已经安装 docker 运行环境
  • 云防火墙或安全组放通 80443 端口

准备两个子域名,示例如下:

  • derper.example.com
  • headscale.example.com
  • traefik.example.com (可选)

DNS解析示例如下,需要将域名解析到云服务器公网IP地址:

在这里插入图片描述

以下所有操作在一台ubuntu 22.04 轻量云服务器进行配置。

创建docker网络

创建名为headscale的docker网络,用于不同容器直接通过名称互访。

docker network create headscale

部署tailscale客户端

在需要搭建 DERP Server 的服务器上, 首先安装一个 Tailscale 客户端,这样做的目的是让搭建的 DERP Server 开启客户端认证, 否则你的 DERP Server 可以被任何人白嫖.

为derper容器提供tailscale.sock套接字,用于后续部署derp时指定-v tailscale:/var/run/tailscale参数.

docker run -d --name tailscaled \
  --restart always \
  -v tailscale:/var/run/tailscale \
  -v /var/lib:/var/lib \
  -v /dev/net/tun:/dev/net/tun \
  --network=host --privileged \
  tailscale/tailscale tailscaled

部署derp中继服务器

执行以下docker命令,部署derper中继服务器,

docker run -d --name derper \
  --restart always \
  -p 8443:8443 -p 3478:3478 \
  -e DERP_ADDR=:8443 \
  -e DERP_DOMAIN=derper.example.com \
  -e DERP_CERT_MODE=letsencrypt \
  -e DERP_VERIFY_CLIENTS=true \
  --net headscale \
  -v tailscale:/var/run/tailscale \
  docker.io/fredliang/derper:latest

参数说明:

  • 3478 端口不要修改
  • 8443 端口可以按照自己的喜好来改,DERP_ADDR 变量后面的端口需要与该端口保持一致,冒号不能丢
  • DERP_VERIFY_CLIENTS=true,DERP 会验证连接的客户端是否与本机的客户端为同一个账号下,从而避免其他客户端白嫖服务器
  • DERP_CERT_MODE=letsencrypt :使用letsencrypt 自动申请ssl证书
  • -v tailscale:/var/run/tailscale 将tailscale.sock套接字接口挂载进容器中

查看运行的容器

root@ubuntu:~# docker ps 
CONTAINER ID   IMAGE                                                     COMMAND                  CREATED       STATUS       PORTS                                                                                  NAMES
445c5fc9e688   fredliang/derper:latest                                   "/bin/sh -c '/app/de…"   6 hours ago   Up 6 hours   0.0.0.0:3478->3478/tcp, :::3478->3478/tcp, 0.0.0.0:8443->8443/tcp, :::8443->8443/tcp   derper

浏览器访问derper域名:https://derper.example.com ,显示以下内容,说明derp服务端运行正常:

在这里插入图片描述

部署headscale

创建headscale目录

mkdir -p /data/headscale/config
touch /data/headscale/config/db.sqlite

下载headscale配置文件

wget -O /data/headscale/config/config.yaml \
 https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yaml

修改headscale配置文件,以下仅显示修改部分,其他保持默认

$ vim /data/headscale/config/config.yaml
server_url: https://headscale.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
private_key_path: /etc/headscale/private.key
noise:
  private_key_path: /etc/headscale/noise_private.key
derp:
  urls:
  #  - https://controlplane.tailscale.com/derpmap/default
  paths:
    - /etc/headscale/derp.yaml
dns_config:
  override_local_dns: true
  nameservers:
    - 223.5.5.5
    - 223.6.6.6
db_type: sqlite3
db_path: /etc/headscale/db.sqlite

参数说明:

  • derp.urls: 注释掉默认derp中继服务器地址
  • derp.paths:使用paths指定自建derp服务器配置文件,注意为容器中的路径

创建derp配置文件

$ vim /data/headscale/config/derp.yaml 
regions:
  900:
    regionid: 900
    regioncode: aliyun
    regionname: aliyund-derp
    nodes:
      - name: 900a
        regionid: 900
        hostname: derper.example.com
        stunport: 3478
        stunonly: false
        derpport: 8443

配置说明:

  • regions 是 YAML 中的对象,下面的每一个对象表示一个可用区,每个可用区里面可设置多个 DERP 节点,即 nodes。
  • 每个可用区的 regionid 不能重复。
  • 每个 node 的 name 不能重复。
  • regionname 一般用来描述可用区,regioncode 一般设置成可用区的缩写。
  • stunonly: false 表示除了使用 STUN 服务,还可以使用 DERP 服务。

部署headscale服务端

docker run -d --name headscale \
  --restart always \
  -p 8080:8080 -p 9090:9090 \
  --net headscale \
  -v /data/headscale/config:/etc/headscale \
  headscale/headscale:latest-alpine headscale serve

查看运行的容器

root@ubuntu:~# docker ps 
CONTAINER ID   IMAGE                                                     COMMAND                  CREATED       STATUS       PORTS                                                                                  NAMES
5a1ddc71318a   headscale/headscale:latest-alpine                         "headscale serve"        5 hours ago   Up 5 hours   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:9090->9090/tcp, :::9090->9090/tcp   headscale

部署caddy-server

使用caddy用于访问headscale.example.com和deper.example.com时分别反向代理到后端http服务。

在这里插入图片描述

创建caddy配置文件目录

mkdir -p /data/caddy

创建caddy配置文件,由于在同一个docker 网络,caddy可直接通过容器名称访问headscale和derper后端。

cat >/data/caddy/Caddyfile<<EOF
https://headscale.example.com {
    reverse_proxy * http://headscale:8080
}
https://derper.example.com {
    reverse_proxy * http://derper:8443
}
EOF

部署caddy-server

docker run -d --name caddy \
  --restart always \
  -p 80:80 -p 443:443 \
  --net headscale \
  -v /data/caddy/Caddyfile:/etc/caddy/Caddyfile \
  docker.io/caddy/caddy:latest

使用traefik+docker-compose的示例

version: '3.9'

services:
  tailscaled:
    container_name: tailscaled
    image: docker.io/tailscale/tailscale:v1.50.1
    restart: unless-stopped
    command: tailscaled
    network_mode: "host"
    # Resolve the conflict between aliyun ECS DNS SERVER IP subnet and tailscale subnet 100.64.0.0/10
    environment:
      - TS_EXTRA_ARGS="--netfilter-mod=off"
    volumes:
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
      - /var/lib:/var/lib
      - /dev/net/tun:/dev/net/tun
      - tailscale-sock:/var/run/tailscale
    cap_add:
      - NET_ADMIN
      - NET_RAW

  derper:
    container_name: derper
    image: docker.io/fredliang/derper:latest
    restart: unless-stopped
    ports:
      - "8443:8443"
      - "3478:3478"
    environment:
      - DERP_DOMAIN=derper.example.com
      - DERP_CERT_DIR=/app/certs
      - DERP_CERT_MODE=letsencrypt
      - DERP_ADDR=:8443
      - DERP_STUN=true
      - DERP_HTTP_PORT=-1
      - DERP_VERIFY_CLIENTS=true
    volumes:
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
      - tailscale-sock:/var/run/tailscale
    networks:
      - headscale
    depends_on:
      - tailscaled
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.derper.rule=Host(`derper.example.com`)"
      - "traefik.http.routers.derper.service=derper"
      - "traefik.http.routers.derper.entrypoints=websecure"
      - "traefik.http.routers.derper.tls.certresolver=myresolver"
      - "traefik.http.services.derper.loadbalancer.server.port=8443"

  headscale:
    container_name: headscale
    image: docker.io/headscale/headscale:latest
    command: headscale serve
    restart: unless-stopped
    ports:
      - "8080:8080"
      - "9090:9090"
    volumes:
      - "/usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro"
      - "/data/headscale/config:/etc/headscale"
    networks:
      - headscale
    depends_on:
      - derper
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.headscale.rule=Host(`headscale.example.com`)"
      - "traefik.http.routers.headscale.service=headscale"
      - "traefik.http.routers.headscale.entrypoints=websecure"
      - "traefik.http.routers.headscale.tls.certresolver=myresolver"
      - "traefik.http.services.headscale.loadbalancer.server.port=8080"

  traefik:
    image: docker.io/library/traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    command:
      - "--log.level=INFO"
      - "--api.dashboard=true"
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      # redirect-to-https
      - "--entryPoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
      # letsencrypt
      - "--entryPoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=<your-email@example.com>"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    labels:
      # dashboard with https
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.service=dashboard@internal"
      - "traefik.http.routers.api.rule=Host(`traefik.example.com`) && PathPrefix(`/api`)"
      - "traefik.http.routers.api.service=api@internal"
      - "traefik.http.routers.dashboard.tls=true"
      - "traefik.http.routers.dashboard.tls.certresolver=myresolver"
      - "traefik.http.routers.api.tls=true"
      - "traefik.http.routers.api.tls.certresolver=myresolver"
      - "traefik.http.services.dashboard.loadbalancer.server.port=8080"
      # basic auth
      - "traefik.http.routers.dashboard.middlewares=auth"
      - "traefik.http.middlewares.auth.basicauth.users=admin:$$2y$$05$$Yr3eeJUuuKy6TTlexampleTCQReGNy"
    ports:
      - "80:80"
      - "443:443"
      - "8081:8080"
    volumes:
      - "/data/headscale/traefik-letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    networks:
      - headscale
    depends_on:
      - derper
      - headscale

networks:
  headscale:
volumes:
  tailscale-sock:

说明:使用以下方式生成traefik admin密码

apt install -y apache2-utils
echo $(htpasswd -nbB admin "mypassword") | sed -e s/\\$/\\$\\$/g

headscale客户端注册

headscale服务端配置

创建一个命名空间:

docker exec headscale \
  headscale user create default

生成用于客户端注册的认证key,记录key用于后续客户端注册使用。

docker exec headscale \
  headscale preauthkeys create --user default --reusable --expiration 24h

示例运行结果

root@ubuntu:~# docker exec headscale \
>   headscale preauthkeys create --user default --reusable --expiration 24h
90e20f91f2497c518144254b0fe66cc0619ae5571e8a2e5c 

headscale server端注册客户端

首先在本机将tailscale客户端注册到服务端

docker exec -it tailscaled \
  tailscale up --accept-dns=false --accept-routes=true \
  --auth-key=90e20f91f2497c518144254b0fe66cc0619ae5571e8a2e5c \
  --force-reauth --login-server=https://headscale.example.com --reset

windows客户端注册

以下在远程windows机器执行,该机器可以位于home或公司。

1、安装tailscale windows客户端

2、浏览器访问以下链接,下载页面中的reg注册表文件并执行

https://headscale.example.com/windows

点击下载页面中的reg注册表文件并执行该文件。
在这里插入图片描述

3、注册客户端到headscale控制端

打开CMD命令行窗口,复制以下几行命令粘贴到CMD命令行窗口,并执行

tailscale up --accept-dns=false --accept-routes ^
  --login-server=http://headscale.example.com ^
  --auth-key=90e20f91f2497c518144254b0fe66cc0619ae5571e8a2e5c ^
  --force-reauth --unattended --reset ^
  --advertise-routes=192.168.12.0/24,192.168.13.0/24

关于选项设置:

  • –login-server: 指定使用的headscale服务器地址(必填)
  • –advertise-routes: 向headscale服务器报告当前客户端处于哪个内网网段下, 便于headscale服务器让同内网设备直接内网直连,或者将其他设备指定流量路由到当前内网(可选)
  • –accept-routes: 是否接受headscale服务器下发的用于路由到其他客户端内网的路由规则(可选)
  • –accept-dns: 是否使用headscale服务器下发的 DNS 相关配置(可选)

客户端开启IP转发:

搜索框中搜索注册表编辑器,展开注册表编辑器以下路径,将参数IPEnableRouter的值从0修改为1,然后关闭注册表编辑器并重新启动系统。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters

4、headscale控制端查看注册的节点列表

root@ubuntu:~# docker exec -it headscale bash
root@a9ce9b39f64d:/# headscale nodes list

示例运行结果

root@a9ce9b39f64d:/# headscale nodes list
ID | Hostname    | Name        | NodeKey | Namespace | IP addresses                 | Ephemeral | Last seen           | Online  | Expired
1  | ubuntu      | ubuntu      | [PpOKS] | defaultns | 100.64.0.1, fd7a:115c:a1e0::1 | false     | 2023-10-08 16:35:50 | online  | no     
2  | winpc-laptop | winpc-laptop | [qMO6V] | defaultns | 10.64.0.2, fd7a:115c:a1e0::2 | false     | 2023-10-08 10:41:21 | online| no     
3  | winpc   | willpc      | [6CjhI] | defaultns | 100.64.0.3, fd7a:115c:a1e0::3 | false     | 2023-10-08 16:35:50 | online  | no     

如果需要删除节点执行以下命令,其中2为nodes list显示的节点ID

headscale nodes delete -i 2

5、server端查看客户端发布的subnet

root@a9ce9b39f64d:/# headscale routes list
ID | Machine     | Prefix          | Advertised | Enabled | Primary
3  | winpc      | 192.168.12.0/24 | true       | true    | true
4  | win-laptop | 192.168.13.0/24 | true       | false   | false
5  | winpc      | 192.168.90.0/24 | true       | false   | false
6  | ubuntu      | 172.17.0.0/18   | true       | true    | true

server端启用客户端发布的subnet,这样所有子网接入tailscale网络并能够全部互通。

root@a9ce9b39f64d:/# headscale routes enable -r 3

网络连通性测试

所有客户端按照以上示例注册到headscale-server,并发布本地子网网段后,下图任意网络环境的子网IP可以直接互相通信。
在这里插入图片描述
例如从172.16.1.1所在机器测试ping 192.168.12.1。

derp中继测试

tailscale ping 命令

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

C:\Users\wiinpc>tailscale ping 192.168.12.16
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 49ms
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 117ms
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 46ms
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 38ms
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 80ms
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 42ms
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 163ms
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 68ms
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 41ms
pong from winpc (fd7a:115c:a1e0::2) via DERP(aliyun) in 255ms
direct connection not established

tailscale status 命令

通过 tailscale status 命令可以查看当前节点与其他对等节点的连接方式, 通过此命令可以查看到当前节点可连接的节点以及是否走了 Derper 中继:

C:\Users\winpc>tailscale status
fd7a:115c:a1e0::1 win-laptop          defaultns    windows -
fd7a:115c:a1e0::2 winpc               defaultns    windows active; relay "aliyun", tx 45680 rx 49464

tailscale netcheck 命令

有些情况下我们可以确认是当前主机的网络问题导致没法走 P2P 连接, 但是我们又想了解一下当前的网络环境; 此时可以使用 tailscale netcheck 命令来检测当前的网络环境, 此命令将会打印出详细的网络环境报告:

C:\Users\winpc>tailscale netcheck

Report:
        * UDP: false
        * IPv4: (no addr found)
        * IPv6: no, but OS has support
        * MappingVariesByDestIP:
        * HairPinning:
        * PortMapping:
        * CaptivePortal: true
        * Nearest DERP: aliyund-derp
        * DERP latency:
                - aliyun: 126.3ms (aliyund-derp)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

willops

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值