• tailscale自建headscale和derp中继


    tailscale derp中继服务简介

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

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

    在这里插入图片描述

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

    在这里插入图片描述

    准备工作:

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

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

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

    在这里插入图片描述

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

    创建docker网络

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

    docker network create headscale
    
    • 1

    部署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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    部署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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    参数说明:

    • 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
    
    • 1
    • 2
    • 3

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

    在这里插入图片描述

    部署headscale

    创建headscale目录

    mkdir -p /data/headscale/config
    touch /data/headscale/config/db.sqlite
    
    • 1
    • 2

    下载headscale配置文件

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

    修改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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    参数说明:

    • 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    配置说明:

    • 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    查看运行的容器

    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
    
    • 1
    • 2
    • 3

    部署caddy-server

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

    在这里插入图片描述

    创建caddy配置文件目录

    mkdir -p /data/caddy
    
    • 1

    创建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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    部署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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用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="
          - "--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:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

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

    apt install -y apache2-utils
    echo $(htpasswd -nbB USER "PASS") | sed -e s/\\$/\\$\\$/g
    
    • 1
    • 2

    headscale客户端注册

    headscale服务端配置

    创建一个命名空间:

    docker exec headscale \
      headscale user create default
    
    • 1
    • 2

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

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

    示例运行结果

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

    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
    
    • 1
    • 2
    • 3
    • 4

    windows客户端注册

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

    1、安装tailscale windows客户端

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

    https://headscale.example.com/windows
    
    • 1

    点击下载页面中的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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    关于选项设置:

    • –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
    
    • 1

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

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

    示例运行结果

    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     
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    headscale nodes delete -i 2
    
    • 1

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

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

    网络连通性测试

    所有客户端按照以上示例注册到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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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
    
    • 1
    • 2
    • 3

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    六月集训(第28天) —— 动态规划
    软件测试未来主要发展的5个趋势
    知识经济时代的基石:知识协同
    瑞萨e2studio(25)----电容触摸配置(2)
    解决flutter不识别yaml里面配置的git项目
    OpenWrt的内核启动分析
    Java基础之《Ajax+JQuery(JavaEE开发进阶Ⅱ)》—JQuery DOM操作(2)
    CSS-表格属性(1)
    竞赛 题目:基于深度学习的手势识别实现
    Compose 打造一个Home页面
  • 原文地址:https://blog.csdn.net/networken/article/details/133687620