前言
最近有了远程连接家里面电脑的需求,于是研究了一下P2P内网穿透的方案,在折腾了Netmaker、Tailscale、ZeroTier等软件之后最终选择了Tailscale。由于Tailscale的官方DERP中继服务器都在国外,且线路质量也不好,所以就想到自建Tailscale,并配置DERP中继。
那么什么是Headscale?
An open source, self-hosted implementation of the Tailscale control server. —— Headscale项目介绍
通过Headscale,我们能在自己服务器上部署Tailscale的控制端,并且Headscale内置了DERP配置,不需要安装额外的软件即可一并完成DERP部署。
硬件要求
- 需要一台VPS,配置不用很高,但是需要有公网IPv4。
- 需要一个域名,该域名指向该VPS的IP地址。
目标
- 使用Docker部署Headscale及其WebUI
- 配置DERP
- 配置Nginx反向代理
部署
开放端口
Tailscale客户端与Headscale之间的通信一般通过443/TCP
完成;客户端与DERP中继节点的通信通过3478/UDP
完成。如果在服务器内开启了或服务商自带了防火墙,需要提前开放这两个端口。以下以阿里云轻量应用服务器的防火墙为例。
安装
以下内容参考自Headscale的官方文档:Running headscale in a container。
首先需要确保服务器上已安装了Docker,如果VPS在国内的话需要配置Docker镜像源。
然后SSH连接至服务器
mkdir -p ./headscale/config
cd ./headscale/config
然后将配置文件下载到服务器并重命名,其中版本号v0.25.1
需要参考官方教程
wget -O config.yaml https://raw.githubusercontent.com/juanfont/headscale/v0.25.1/config-example.yaml
然后编辑配置文件,每个参数都有说明,可以自行参考,这里我给出我使用的配置文件
---
# url需要对应自己配置的域名地址
server_url: https://hs.example.com:443
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
grpc_listen_addr: 0.0.0.0:50443
grpc_allow_insecure: false
noise:
private_key_path: /var/lib/headscale/noise_private.key
prefixes:
# 下面的地址表示分配给Tailscale客户端的IP地址
v4: 100.65.64.0/24
v6: fd7a:115c:a1e0:2000::/48
allocation: sequential
derp:
server:
enabled: true
region_id: 999
# DERP名字
region_code: "my-hangzhou"
region_name: "Headscale Embedded DERP"
stun_listen_addr: "0.0.0.0:3478"
private_key_path: /var/lib/headscale/derp_server_private.key
automatically_add_embedded_derp_region: true
# 需要将IPv4和IPv6条目注释或改成服务器实际地址,不然会连不上DERP
# ipv4: 1.2.3.4
# ipv6: 2001:db8::1
urls:
- https://controlplane.tailscale.com/derpmap/default
paths: []
auto_update_enabled: true
update_frequency: 24h
disable_check_updates: false
ephemeral_node_inactivity_timeout: 30m
database:
type: sqlite
debug: false
gorm:
prepare_stmt: true
parameterized_queries: true
skip_err_record_not_found: true
slow_threshold: 1000
sqlite:
path: /var/lib/headscale/db.sqlite
write_ahead_log: true
wal_autocheckpoint: 1000
acme_url: https://acme-v02.api.letsencrypt.org/directory
acme_email: ""
tls_letsencrypt_hostname: ""
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
tls_letsencrypt_challenge_type: HTTP-01
tls_letsencrypt_listen: ":http"
tls_cert_path: ""
tls_key_path: ""
log:
format: text
level: info
policy:
mode: file
path: ""
dns:
magic_dns: true
# 表示连接设备的域名
base_domain: nuotian.my
nameservers:
global:
# 可以添加国内的DNS地址
- 114.114.114.114
- 1.1.1.1
- 1.0.0.1
- 2606:4700:4700::1111
- 2606:4700:4700::1001
split:
{}
search_domains: []
extra_records: []
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
logtail:
enabled: false
randomize_client_port: false
然后保存,回到./headscale
目录,创建docker-compose.yml
,并编辑
cd ..
vim docker-compose.yml
下面是我使用的Docker Compose配置,主要完善了WebUI、数据存储目录、Nginx反向代理端口,可以参考。其中"127.0.0.1:9090:9090"
是Headscale的图表暴露的端口,可以选择不暴露出来;WebUI我选择的是headscale-ui,其他WebUI可以查看这里。
services:
headscale:
image: headscale/headscale:0.25.1
restart: unless-stopped
container_name: headscale
ports:
- "127.0.0.1:10010:8080"
- "127.0.0.1:9090:9090"
- "0.0.0.0:3478:3478/udp"
volumes:
- "./config:/etc/headscale"
- "./data:/var/lib/headscale"
command: serve
cap_add:
- NET_ADMIN
- NET_RAW
- SYS_MODULE
sysctls:
- net.ipv6.conf.all.forwarding=1
- net.ipv4.ip_forward=1
environment:
- TZ=Asia/Shanghai
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:2025.03.21
restart: unless-stopped
container_name: headscale-ui
ports:
- "127.0.0.1:10011:8080"
environment:
- TZ=Asia/Shanghai
保存之后,启动容器,并创建API Key以在WebUI上使用
docker compose up -d
# 检查服务是否正常启动
curl http://127.0.0.1:9090/metrics
# 创建API Key,有效期999天
docker exec -it headscale headscale apikeys create --expiration 999d
将输出的API Key复制,并保存在安全的地方。
反向代理
由于我服务器上还有其他网页,所以需要使用Nginx反向代理。这里也直接给出配置文件,SSL证书的获取可以通过Certbot或acme.sh。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
port_in_redirect off;
server_name hs.furcode.cn;
if ($ssl_protocol = "") {
return 301 https://$host$request_uri;
}
ssl_certificate /etc/letsencrypt/live/hs.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hs.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://127.0.0.1:10010;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $server_name;
proxy_redirect http:// https://;
proxy_buffering off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
}
location /web {
proxy_pass http://localhost:10011;
proxy_http_version 1.1;
proxy_set_header Host $server_name;
proxy_buffering off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
}
}
然后保存之后加载配置文件即可
nginx -t
nginx -s reload
访问WebUI
在浏览器内访问https://<你的域名>/web
既可打开WebUI。首先点击左侧的Settings
,在Headscale URL处输入https://<你的域名>,然后在Headscale API Key输入生成的API Key,点击Test Server Settings
,如果一切正常的话旁边会显示一个勾,至此服务端配置就完成了。需要注意的是,我使用的这个WebUI是纯前端实现的,连接密钥和地址都保存在本地浏览器中,所以需要将API Key保存下来以便后续使用。
使用
在添加客户端之前先要在WebUI上添加一个用户,点击WebUI左侧的User View
,点击顶部的+ New User
,输入用户名后点击右侧的✓。
Windows
首先在官网下载Tailscale客户端,并安装。
然后打开Tailscale,再打开Powershell或CMD,输入
tailscale login --login-server https://<你的域名>
回车后Tailscale会有一个通知,点击通知打开浏览器,即可看到下面的内容
复制--key
后面的字符串,打开Headscale的WebUI,进入Device View
界面,点击顶部的+ New Device
,Device Key粘贴刚刚复制的字符串,然后选择一个用户,然后点击✓。
在WebUI添加好设备之后,本地的Tailscale客户端应该会立即上线。然后打开Powershell或CMD,输入
tailscale netcheck
即可看到当前连接状态和各个DERP中继服务器的延迟。
MacOS
首先在官网下载Tailscale客户端,安装软件并打开。
然后在Account界面点击底部的按钮,在弹出框内输入https://<你的域名>
,然后点击Add Account
然后浏览器会打开一个网页,网页中会出现Key,接下来的步骤与Windows端相同,这里就直接略过。
Android
首先在官网下载Tailscale客户端,安装软件并打开。打开软件后可能会提示登录Tailscale官方的网页,直接关掉界面即可。打开右上角的设置,再打开顶部的账号界面,点击右上角三个点,选择Use an alternate server
然后输入https://<你的域名>
,点击Add account
之后同样会打开浏览器,出现Key,接下来的操作同样与Windows端相同,不再赘述。