docker compose一键搭建可通过webui控制的headscale和derp中继

0x00 前言 为什么选用tailscale? 1. 笔者有异地组网需求,目前尝试过frp stcp做穿透、wireguard组网,经过一段时间的使用,稍微总结一下:使用frp用stcp发布服务,客户端连接,确实是个很好的办法,但是配置终究太麻烦,需要安装客户端、在添加了穿透之后每个客户端需要独立配

0x00 前言

为什么选用tailscale?

1. 笔者有异地组网需求,目前尝试过frp stcp做穿透、wireguard组网,经过一段时间的使用,稍微总结一下:使用frp用stcp发布服务,客户端连接,确实是个很好的办法,但是配置终究太麻烦,需要安装客户端、在添加了穿透之后每个客户端需要独立配置等等;wireguard可能是当下最好的组网技术,低占用、linux内核级支持、配置简单(wireguard-ui),这些都是它的优点,可是有一个十分致命的点,它是中心化的,流量都需要过一遍server进行转发,这无异于极大的增加了服务器的带宽压力且增加了流量的延迟。经过这一段时间的查阅资料,发现了tailscale这个基于wireguard的项目,他的工作原理可以通过官网进行查看,与此同时又发现了headscale这个对tailscale server的开源实现,这不得折腾一下。

2. 这里可能有人就要问了,为什么在tailscale官方免费提供了3用户100台设备连接、提供acl、p2p、sso的情况下,要选择自建headscale呢,当然是为了固定IP,毕竟随机分配的地址可太难受了。本文将使用docker compose的方式安装headscale、derp、headscale-ui。

3. 如果您觉得tailscale官方提供的设备数量足够你使用,那么你仍然可以使用官方提供的服务,但仍建议您自建一个derp,因为官方未在大陆地区提供derp服务,存在人数较多及访问延迟较高的情况。

所使用端口列表

  • tcp 58080(headscale server)

  • tcp 57070(headscale web ui)

  • tcp 56060 (headscale derp)

  • udp 3478 (headscale derp stun)

如您使用反代请在防火墙放通反代后的端口(本文教程为tcp 80、443)及udp 3478

0x01 前提条件

1. 你需要一台具备公网IP的服务器和该服务器防火墙修改权限

2. 一个域名且带有SSL证书(可以使用acme.sh申请免费证书),需要占用两个子域名

3. 一些基础的计算机网络知识

注意:请将本文全文所涉及到的域名:hs.example.com(headscale-server及ui)、hsderp.example.com(中继)修改为自己的域名,后续将不再另行说明。

0x02 安装docker

注意:该项内容来源于清华大学开源软件镜像站

Docker 提供了一个自动配置与安装的脚本,支持 Debian、RHEL、SUSE 系列及衍生系统的安装。

以下内容假定

  •  您为 root 用户,或有 sudo 权限,或知道 root 密码;

  •  您系统上有 curl 或 wget

export DOWNLOAD_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"

# 如您使用 curl

curl -fsSL https://get.docker.com/ | sh

# 如您使用 wget

wget -O- https://get.docker.com/ | sh

0x03 docker compose文件

本文中compose文件仅部署headscale、headscale-ui、derp、client,请按需进行修改。

注意,要提前配置好config.yaml和derp.yaml,并放入headscale/config文件夹内,同时因derp更新不及时,这里采取了手动构建方式,需将derp.Dockerfile放入dockerfiles文件夹内。可以去GitHUB的代码仓下载config-example.yaml和derp-example.yaml,内容见0x04配置文件部分。

# docker-compose.yaml
networks:
 private:
 driver: bridge
 ipam:
 config:
 - subnet: 172.20.200.0/24
services:
 server:
 image: headscale/headscale:stable
 container_name: headscale-server
 networks:
 - private
 volumes:
 - ./headscale/config:/etc/headscale
 - ./headscale/data:/var/lib/headscale
 - ./headscale/run:/var/run/headscale
 - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
 ports:
 - "58080:8080"
 command: serve
 restart: unless-stopped
 depends_on:
 - derp
 webui:
 image: ghcr.io/gurucomputing/headscale-ui
 container_name: headscale-ui
 networks:
 - private
 environment:
 HTTP_PORT: 7070
 ports:
 - "57070:7070"
 volumes:
 - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
 restart: unless-stopped
 derp:
 build:
 context: ./dockerfiles
 dockerfile: derp.Dockerfile
 container_name: headscale-derp
 networks:
 - private
 environment:
 DERP_DOMAIN: hs.bokro.cn
 DERP_ADDR: :6060
 DERP_CERT_MODE: letsencrypt
 DERP_VERIFY_CLIENTS: true
 ports:
 - "56060:6060" # derp port, TCP
 - "3478:3478/udp" # STUN port, UDP
 volumes:
 - ./tailscale:/var/run/tailscale
 - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
 restart: unless-stopped
 client:
 image: tailscale/tailscale:stable
 container_name: tailscale-client
 network_mode: "host"
 privileged: true
 environment:
 TS_EXTRA_ARGS: --netfilter-mode = off
 volumes:
 - ./tailscale:/var/run/tailscale
 - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime:ro
 - /var/lib:/var/lib
 - /dev/net/tun:/dev/net/tun
 cap_add:
 - net_admin
 - sys_module
 command: tailscaled
 restart: unless-stopped

# derp.Dockerfile 放入./dockerfiles文件夹内

FROM golang:latest AS builder
WORKDIR /app
# https://tailscale.com/kb/1118/custom-derp-servers/
RUN go env -w GOPROXY=https://goproxy.io,direct
RUN go install tailscale.com/cmd/derper@latest
FROM ubuntu
WORKDIR /app
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
 apt-get install -y --no-install-recommends apt-utils && \
 apt-get install -y ca-certificates && \
 mkdir /app/certs
ENV DERP_DOMAIN=your-hostname.com
ENV DERP_CERT_MODE=letsencrypt
ENV DERP_CERT_DIR=/app/certs
ENV DERP_ADDR=:443
ENV DERP_STUN=true
ENV DERP_STUN_PORT=3478
ENV DERP_HTTP_PORT=80
ENV DERP_VERIFY_CLIENTS=false
ENV DERP_VERIFY_CLIENT_URL=""
COPY --from=builder /go/bin/derper .
CMD /app/derper --hostname=$DERP_DOMAIN \
 --certmode=$DERP_CERT_MODE \
 --certdir=$DERP_CERT_DIR \
 --a=$DERP_ADDR \
 --stun=$DERP_STUN \
 --stun-port=$DERP_STUN_PORT \
 --http-port=$DERP_HTTP_PORT \
 --verify-clients=$DERP_VERIFY_CLIENTS \
 --verify-client-url=$DERP_VERIFY_CLIENT_URL

 

0x04 服务器配置

config.yaml

server_url: https://hs.example.cn
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
grpc_listen_addr: 0.0.0.0:50443 
noise:
  private_key_path: ./noise_private.key
prefixes:
  v6: fd7a:115c:a1e0::/48
  v4: 100.100.0.0/16
  allocation: sequential
derp:
  server:
    enabled: false
  urls:
    # - https://controlplane.tailscale.com/derpmap/default
  paths:
    - /etc/headscale/derp.yaml
database:
  type: sqlite
  sqlite:
    path: /var/lib/headscale/db.sqlite
    write_ahead_log: true
dns:
  magic_dns: false
  magic_dns: false

derp.yaml

regions:
  999:
    regionid: 999
    regioncode: gz_tencent
    regionname: China Guangzhou Tencent Cloud
    nodes:
      - name: derpA
        regionid: 999
        hostname: hsderp.bokro.cn
        stunport: 3478
        stunonly: false
        derpport: 443
  998:
    regionid: 998
    regioncode: cd_tencent
    regionname: China Chengdu Tencent Cloud
    nodes:
      - name: derpB
        regionid: 998
        hostname: hsderp2.example.cn
        stunport: 3478
        stunonly: true
        derpport: 443

0x05 nginx反向代理配置

注意,为了便于笔者管理,以下给出的nginx配置文件仅为proxy相关conf,请自行在域名配置文件里incloud,相关语法:include /www/hsderp.example.cn/proxy/*.conf 。您也可以根自己喜好直接写入网站配置内。

headscale-server和derp正常进行反代就行了,ui端因为跨域的原因,需要在同一个域里。如您按照本文提供的内容进行创建的话,那么你可以直接使用此nginx配置,否则请自行修改相应端口。

hsderp.example.com反代设置

# hsderp.conf
location ^~ / {
 proxy_pass http://127.0.0.1:56060;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header REMOTE-HOST $remote_addr;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection "upgrade";
 proxy_set_header X-Forwarded-Proto $scheme;
 proxy_http_version 1.1;
 add_header X-Cache $upstream_cache_status;
 add_header Strict-Transport-Security "max-age=31536000";
}

* hs.example.com反代设置
# hs.conf
location ^~ / {
 proxy_pass http://127.0.0.1:58080;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header REMOTE-HOST $remote_addr;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection "upgrade";
 proxy_set_header X-Forwarded-Proto $scheme;
 proxy_http_version 1.1;
 add_header X-Cache $upstream_cache_status;
 add_header Strict-Transport-Security "max-age=31536000";
 add_header Cache-Control no-cache;
}

#web.conf
location ^~ /web {
 proxy_pass http://127.0.0.1:57070;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header REMOTE-HOST $remote_addr;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection "upgrade";
 proxy_set_header X-Forwarded-Proto $scheme;
 proxy_http_version 1.1;
 add_header X-Cache $upstream_cache_status;
 add_header Strict-Transport-Security "max-age=31536000";
 add_header Cache-Control no-cache;
}

至此,您在浏览器中打开https://hs.example.com/web及https://haderp.example.com均可看到相关页面

0x06 headscale-ui配置

1. 为了便捷控制,首先生成api key供headscale-ui进行控制

docker exec headscale-server headscale apikeys create -e 720d

其中-e参数后面指定的是该apikey的过期时间,这里设置为720天

2. ui端配置

2.1 打开ui端页面:https://hs.example.com/web

2.2 点击”Settings”

2.3 添加”Headscale URL”,本例为https://hs.example.com

2.4 将刚才生成的api key粘贴进“Headscale API Key”中

2.5 点击“Test Server Settings”,出现绿色对号后UI端就可以通过ui控制服务端了

2.6 进入“User View”,点击“+New User”,添加一个用户

2.7 为该用户生成一个Preauth Key,供客户端连接使用。为了便捷性,最好设置为“Reusable”,并“Active”

0x07 客户端连接

客户端可以在tailscale官网进行下载及查看使用文档。

如您docker-compose文件中将derp部分的DERP_VERIFY_CLIENTS设置为了true,这代表您开启了客户端验证,如关闭则您的derp可被所有人使用。开启后需要对docker的client进行配置,参考linux客户端,如下为使用docker exec配置示例

docker exec -it tailscale-client tailscale up --netfilter-mode=off --login-server=https://hs.example.com --auth-key=YOUR AUTH KEY

linux客户端

1. 使用官方脚本安装tailscale客户端

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

2. 连接服务器

注意:以下命令二选一即可,有关路由转发描述见0x08 路由转发

# 需路由转发
tailscale up --netfilter-mode=off \
 --accept-routes \
 --advertise-routes=192.168.0.0/24 \
 --login-server=https://hs.example.com \
 --auth-key=YOUR AUTH KEY

# 不需路由转发
tailscale up --netfilter-mode=off \
 --login-server=https://hs.example.com \
 --auth-key=YOUR AUTH KEY

windows客户端

1. 在官网下载并安装客户端后,首次需打开cmd或powershell,输入以下命令进行连接

tailscale up -netfilter-mode=off -login-server=https://hs.example.com -auth-key=YOUR AUTH KEY

 IOS客户端

1. 您需要使用非中国大陆地区APP Store账号进行下载并安装tailscale。本文不提供相关教程,请自行搜索美区、港区APPLE ID注册教程。

2. 安装后点击右上角头像,在弹出“Accounts”窗口,点击右上角三个点,并选择“Use a custom coordination server”

3. 输入您的域名https://hs.example.com点击“Login in”

4. 复制弹窗的以mkey:xxxx开头的key

5. 浏览器打开headscale-ui,点击“Device view”,并点击“New Device”

6. 将步骤4中复制的key粘贴到“Device Key”中,并选择对应用户,点后面的勾。

 其他客户端

因笔者设备原因,android/macos等平台不做其他描述,手动添加设备的教程同IOS客户端中第3步以后所示。

 0x08 路由转发

路由转发为接受路由转发,当你启动tailscale时加了--accept-routes参数之后,您启动命令中advertise-routes需要附带您当前设备能够访问的子网,这将会被通告出去。

举个例子,家中网段为192.168.0.0/24,设备A的ip地址为192.168.0.1,当开启路由转发后,从手机等设备访问192.168.0.0/24这个网段将通过设备A进行转发,可以直接联通家中所有局域网。

如您在`0x07 客户端连接`中的`linux客户端`教程中使用了路由转发功能,需要额外进行配置

# eth0为出口网卡名,tailscale0为tailscale网卡名,需根据实际修改
iptables -I FORWARD -i eth0 -j ACCEPT
iptables -I FORWARD -o eth0 -j ACCEPT
iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
iptables -I FORWARD -i tailscale0 -j ACCEPT
iptables -I FORWARD -o tailscale0 -j ACCEPT
iptables -t nat -I POSTROUTING -o tailscale0 -j MASQUERADE
sysctl -w net.ipv4.ip_forward=1

0x09 后记

安装完成后使用命令进行测试,

tailscale status

可以查看状态及错误信息,

tailscale ping <IP>

可以查看是否打洞成功。如下图

本文参考了:

LICENSED UNDER CC BY-NC-SA 4.0
Comment