日志
前言
之前部署过zerotier组网,尝试将ios端加入自建组网,最后失败了。ios端接入特别麻烦,主要有两种方式,具体如下:
方案 | 描述 | 具体方法 |
---|---|---|
ios越狱方案 | 越狱后安装 ZeroTier,然后替换 planet 文件 |
自行了解 |
WireGuard方案 | 使用 WireGuard 接入到 ZeroTier 网络 | 参考:Issue #192 |
使用tailscale组网,tailscale客户端支持自定义headscale服务,ios端也可以连接,符合需求。
准备
-
一台有公网ip的服务器(我这里使用1panel面板)
-
一个域名(国内服务器需要备案),准备两个二级域名,一个用于headscale和webui,一个用于derp服务
-
域名证书(基于1panel获取SSL证书的方式,之前文章中有写,点击传送,文章页面Ctrl+F搜索
SSL
) -
开启对应端口防火墙,本次部署端口如下(可自行在配置文件中修改)
协议 端口 作用 TCP 58080 headscale服务器 TCP 57070 headscale-admin(webui) TCP 56060 derp中继服器 UDP 3478 derp STUN(发现公网映射、判断 NAT 类型)
部署
首先在服务器上安装docker
和docker-compose
然后,在服务器上创建一个目录,用于存放相关配置文件,结构树如下
headscale
├── docker-compose.yaml
├── dockerfiles
│ └── derp.Dockerfile
└── headscale
└── config
├── config.yaml
└── derp.yaml
配置config.yaml
config.yaml
文件中需要修改的内容复制配置文件模版:config-example.yaml,将其粘贴至config.yaml
文件中并修改其中部分内容,如下:
...
server_url: https://headscale.example.com #这里修改为你自己的headscale server域名,注意必须带https://,否则容器会一直重启
listen_addr: 0.0.0.0:8080 #0.0.0.0才能监听到,可以用公网ip
...
prefixes:
v4: 100.64.0.0/10 #ip段范围,可以自定义
#v6: fd7a:115c:a1e0::/48 #我用不到v6 所以注释
...
derp:
server:
enabled: false #不启用官网自带的derp
urls: #这里留空,下面的paths路径会配derp server的地址
paths:
- /etc/headscale/derp.yaml
...
dns:
magic_dns: false #按需使用,我这里暂时关闭,如配合rustdesk使用,需要配置
override_local_dns: false #改为false不覆盖本地DNS,确保内部域名生效
...
randomize_client_port: true #客户端将不再固定使用 UDP/41641 作为 WireGuard 流量的源端口,而是随机挑选高位端口,以绕过仅拦截 41641 的防火墙
配置derp.yaml
这里有只有hostname
需要修改:
regions:
999:
regionid: 999
regioncode: cnbj
regionname: Aliyun Beijing
nodes:
- name: derp
regionid: 999
hostname: derp.example.com #这里修改为你自己的derp server域名
ipv4: 123.123.123.123 #这里填自己服务器的ip,确保连接
stunport: 3478
port: 443 #注意这里必须是443,组网设备访问derp服务器,需要走443,如果你改为56060端口,存在特定网络环境无法访问到derp服务器的情况
stunonly: false
构建derp镜像
我这里手动构建最新的derp镜像,当然也有现成的derp镜像使用,例如:ghcr.io/yangchuansheng/derper:latest
在derp.Dockerfile文件中编辑如下:
FROM golang:alpine 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 alpine:3.21
WORKDIR /app
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
RUN apk add --no-cache ca-certificates iptables iproute2 ip6tables curl && \
mkdir /app/certs
RUN apk add gcompat
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/* /usr/local/bin/
CMD /usr/local/bin/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
使用docker-compose部署
编辑docker-compose.yaml
文件,文件包含了 headscale、headscale-admin、derp、client,请按需进行修改。
networks:
private:
driver: bridge
ipam:
config:
- subnet: 172.20.200.0/24 #不能与Docker网络的地址空间发生重叠,例如docker已有172.20.0.0,就会与这里subnet重复
services:
server:
image: headscale/headscale:latest-debug
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: goodieshq/headscale-admin:latest
container_name: headscale-ui
networks:
- private
environment:
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: derp.example.com #修改为你自己的derp server的域名
DERP_ADDR: :6060 #自行修改端口
DERP_CERT_MODE: letsencrypt
#DERP_CERT_MODE: manual #如果需要手动,需要配置证书
DERP_VERIFY_CLIENTS: 'true'
ports:
- 56060:6060 #derp port, TCP,自行修改端口
- 3478:3478/udp #STUN port, UDP,自行修改端口
volumes:
#- 证书路径:/app/certs/ #注意,这个是手动模式使用
- ./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
完成上述所有步骤后,在headscale路径下,使用下面的命令一键部署 headscale、derp 和 webui。关于docker-compose
的使用可以参考:Docker Compose的使用 – Clif's Blog
sudo docker-compose up -d
配置反向代理
关于tailscale组网中设备报错无法连接relay server的解决思路,以及tailscale组网中设备连接失败的原因分析:
先说结论:我不小心使用了56060作为derp的外部端口,被网络环境墙掉了,导致无法ping通,应当使用443端口。更精确地说:对客户端暴露的端口最好是 443(写进 derp.yaml
的 port
也最好是 443),下面是分析:
这个报错大概率是因为:对Tailscale客户端来说,连DERP一定是走TLS的,绝不能让客户端直接碰到明文端口,否则就会出现:relay server 无法连接
或组网设备ping不通
等报错。这里有两种情况:
- TLS在Nginx 结束(HTTP 反代)
- TLS在derper 结束(stream 透传)
方案1:使用 Nginx 的 location / … proxy_pass
做http反代,直接将443反代给端口56060,这里可以不用让derp走TLS,虽然后端走的是明文http,但是不影响使用。
方案2:如果你非要让derp走TLS,需要手动配置证书(自动的我没有跑通),并且让derp服务独占443端口,这样TLS握手原样到达 derper。但是1panel面板中的openresty已经占用了443端口,怎么办?这个时候需要使用nginx stream
(L4 透传),将derp.example.com:443
透到本机56060端口来,同时,服务器原本的网站服务透到4443端口或其他端口。(再吐槽一下,这里千万不要让客户端直接连56060或其他,derp.yaml
里 port
必须是443,博主脑子抽了,定位错误花了好些时间。因为56060这类似的端口容易被网络环境墙掉,不能稳定连接),附上nginx stream
的配置:
stream {
#1) 按 SNI 选择后端(把 derp 的域名改成你的,比如 derp.example.com)
map $ssl_preread_server_name $backend {
derp.example.com derp_backend; #访问 derp.example.com → 走 derper
default web_backend; #其它域名 → 走网站(4443)
}
#2) derper 后端:转到宿主 56060(Docker 再转容器 6060)
upstream derp_backend {
server 127.0.0.1:56060;
}
#3) 网站后端:转到 4443(上一步你已把站点改去监听 4443)
upstream web_backend {
server 127.0.0.1:4443;
}
#4) 统一在 443 上接入(TCP 层,纯透传;不要写 ssl)
server {
listen 0.0.0.0:443 reuseport;
proxy_pass $backend;
ssl_preread on;
proxy_timeout 3600s;
proxy_connect_timeout 10s;
}
}
在1panel中配置反向代理headscale.example.com
和derp.example.com
(之前文章中有写,点击传送,文章页面Ctrl+F搜索反向代理
)
在1panel的headscale.example.com
网站中,继续配置webui
的反向代理:
测试
完成了上述所有步骤后,在浏览器中访问headscale.example.com
和derp.example.com
,如果看到下面的页面,说明部署成功了。
headscale.example.com
页面:
derp.example.com
页面:
使用
Web-UI使用
这里只介绍基础功能的使用,其他功能自行探索
进入headscale-server
容器中,获取API key:
docker exec -it headscale-server headscale apikeys create
打开headscale.example.com
,填入apikey
,点击保存设置,出现左侧菜单栏就算成功了:
创建用户:
点击Creat创建preAuth Keys
,这个key有时效,会自动过期:
进入Deploy栏,选择刚刚的preAuth Keys
,点击上方可以复制代码使用。
客户端加入
以Linux为例,进入tailscale客户端容器:
sudo docker exec -it tailscale-client /bin/sh
在容器内使用刚刚在Delpoy栏复制的命令即可,这里列出一些常用的命令,其他指令可以参考官方手册或问AI:
#加入headscale服务器
tailscale up --login-server <server_url> --hostname <设备名自取 需要英文> --accept-routes=true --accept-dns=false --authkey <刚刚生成的 authkey>
#查看derp服务
tailscale netcheck
#查看节点状态(是否打洞成功P2P还是走的derp服务等)
tailscale status
#ping测试
tailscale ping <headscale 分配的ip>
至于其他平台的客户端基本都有界面操作,基本没难度,自行探索一下就行。
参考
使用docker-compose一键部署headscale、derper和webui | 小桶
TailScale开源组网(服务端+headscale-admin管理界面) 全平台实现_网络存储
Tailscale+Headscale+自建Derp踩坑记录 - yyhhyy's blog
Tailscale DERP Docker版教程(国内 无需域名 IP+Port) - 开发调优 - LINUX DO
Tailscale 基础教程:Headscale 的部署方法和使用教程 - 米开朗基杨 - 博客园