headscale 组网教程(域名版)
说明:此教程以域名方式部署 Headscale + DERP,并使用 Cloudflare DNS(不要开启小黄云)。
假设服务器 IP 为 1.2.3.4,域名为 abc.xyz,示例配置与命令已包含在下文,请按需替换域名、端口与邮箱等信息。
第一步:域名解析(Cloudflare)
在 Cloudflare 中将域名解析到服务器 IP(不要启用代理/小黄云):
1 2
| derp.abc.xyz -> 1.2.3.4 head.abc.xyz -> 1.2.3.4
|
第二步:设置防火墙规则
建议放行必要端口(可以选择放行所有端口或只放行需要的端口):
1 2 3 4 5
| sudo apt update sudo apt install ufw -y sudo ufw allow 22,80,443,2222,3333/tcp sudo ufw enable sudo ufw status
|
第三步:申请证书(Let’s Encrypt + Cloudflare DNS 插件)
- 安装 Certbot 与 Cloudflare 插件:
1 2 3
| sudo apt update && sudo apt upgrade -y sudo apt install -y certbot python3-certbot-dns-cloudflare sudo apt install micro nano
|
- 创建并配置 Cloudflare API 令牌文件:
1 2
| sudo mkdir -p /etc/cloudflare sudo nano /etc/cloudflare/cloudflare.ini
|
在 cloudflare.ini 中写入(替换为自己的 Token):
1
| dns_cloudflare_api_token = "YOUR_CLOUDFLARE_API_TOKEN"
|
Cloudflare Token 设置建议:
- 权限:
Zone → DNS → Edit
- Zone Resources:Include → Specific zone → 选域名
abc.xyz
设置文件权限:
1
| sudo chmod 600 /etc/cloudflare/cloudflare.ini
|
- 为两个域名分别申请证书:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials /etc/cloudflare/cloudflare.ini \ -d derp.abc.xyz \ --agree-tos \ --email [email protected] \ -n
sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials /etc/cloudflare/cloudflare.ini \ -d head.abc.xyz \ --agree-tos \ --email [email protected] \ -n
|
检查证书路径:
1 2
| sudo ls -l /etc/letsencrypt/live/head.abc.xyz/ sudo ls -l /etc/letsencrypt/live/derp.abc.xyz/
|
证书文件位置示例:
1 2 3 4
| Certificate is saved at: /etc/letsencrypt/live/derp.abc.xyz/fullchain.pem Key is saved at: /etc/letsencrypt/live/derp.abc.xyz/privkey.pem Certificate is saved at: /etc/letsencrypt/live/head.abc.xyz/fullchain.pem Key is saved at: /etc/letsencrypt/live/head.abc.xyz/privkey.pem
|
第四步:搭建 DERP 服务器
以 root 身份创建并运行安装脚本 tailscale.sh。脚本内容如下(保存为 tailscale.sh 并赋可执行权限):
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
| #!/bin/bash set -euo pipefail
read -p "请输入 DERP 域名(例如 derp.example.com): " DERP_DOMAIN read -p "请输入 DERP 监听端口(默认443): " DERP_PORT DERP_PORT=${DERP_PORT:-443} read -p "请输入 HTTP 端口(填0关闭HTTP,可输入0或:0): " HTTP_PORT HTTP_PORT="${HTTP_PORT#:}"
disable_firewall() { if command -v ufw >/dev/null 2>&1; then ufw disable || true; echo "ufw 已关闭" fi if command -v firewall-cmd >/dev/null 2>&1; then systemctl stop firewalld || true systemctl disable firewalld || true echo "firewalld 已关闭" fi if command -v iptables >/dev/null 2>&1; then iptables -F || true; iptables -X || true iptables -t nat -F || true; iptables -t nat -X || true iptables -t mangle -F || true; iptables -t mangle -X || true iptables -t raw -F || true; iptables -t raw -X || true echo "iptables 规则已清除" fi } disable_firewall
echo "== 安装依赖" apt update && apt upgrade -y apt install -y wget git openssl curl tar nginx
echo "== 安装 Go 并构建 derper(二进制放到 /etc/derp/derper)" GO_VERSION="go1.25.4" rm -rf /usr/local/go wget -O /tmp/${GO_VERSION}.linux-amd64.tar.gz "https://go.dev/dl/${GO_VERSION}.linux-amd64.tar.gz" tar -C /usr/local -xzf /tmp/${GO_VERSION}.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin grep -q '/usr/local/go/bin' /etc/profile || echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile
go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct || true
mkdir -p /etc/derp
go install tailscale.com/cmd/derper@latest || { go env -w GOPROXY=direct; go install tailscale.com/cmd/derper@latest; } install -m 0755 "$(go env GOPATH)"/bin/derper /etc/derp/derper
echo "== 使用证书(软链到 /etc/derp)" ln -sf /etc/letsencrypt/live/${DERP_DOMAIN}/fullchain.pem /etc/derp/${DERP_DOMAIN}.crt ln -sf /etc/letsencrypt/live/${DERP_DOMAIN}/privkey.pem /etc/derp/${DERP_DOMAIN}.key ls -l /etc/derp/${DERP_DOMAIN}.crt /etc/derp/${DERP_DOMAIN}.key
echo "== 创建并启动 derper 的 systemd 服务" cat > /etc/systemd/system/derp.service <<EOF [Unit] Description=TS Derper After=network.target Wants=network.target
[Service] User=root Restart=always ExecStart=/etc/derp/derper -hostname ${DERP_DOMAIN} -a :${DERP_PORT} -http-port ${HTTP_PORT} -certmode manual -certdir /etc/derp RestartPreventExitStatus=1
[Install] WantedBy=multi-user.target EOF
systemctl daemon-reload systemctl enable derp systemctl restart derp sleep 1 systemctl status derp --no-pager || true
echo "== 安装 tailscale 客户端(带 DNS 兜底与 apt 源回退)"
if ! getent hosts pkgs.tailscale.com >/dev/null; then IFACE=$(ip route get 1.1.1.1 | awk '{print $5; exit}') if command -v resolvectl >/dev/null 2>&1; then resolvectl dns "$IFACE" 1.1.1.1 8.8.8.8 || true resolvectl domain "$IFACE" '~.' || true resolvectl flush-caches || true else chattr -i /etc/resolv.conf 2>/dev/null || true printf "nameserver 1.1.1.1\nnameserver 8.8.8.8\n" > /etc/resolv.conf fi fi
if ! command -v tailscale >/dev/null 2>&1; then if ! curl -fsSL https://tailscale.com/install.sh | sh; then curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg \ | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list \ | tee /etc/apt/sources.list.d/tailscale.list >/dev/null apt update apt install -y tailscale fi systemctl enable --now tailscaled || true fi
echo "== 快速自检(端口与证书)" echo "- 监听:" ss -ltnup | egrep ":${DERP_PORT}\b" || true ss -lunp | grep ':3478' || true echo "- 证书校验(应为 Verification: OK):" echo | openssl s_client -connect ${DERP_DOMAIN}:${DERP_PORT} -servername ${DERP_DOMAIN} -brief 2>/dev/null | egrep 'Verification|Protocol|Cipher' || true
echo echo "✅ DERP 已使用 Let's Encrypt 证书运行:${DERP_DOMAIN}:${DERP_PORT}(HTTP端口 ${HTTP_PORT},0=关闭)" echo "✅ tailscale 客户端:$(tailscale version 2>/dev/null || echo 已安装)" echo echo "下一步(如需):" echo " 1) 确认 Headscale 的 derp.json 指向 HostName=${DERP_DOMAIN}、DERPPort=${DERP_PORT}" echo " 2) 客户端上执行:tailscale netcheck(应不再出现 self-signed/relay 900 错误)" echo " 3) 将设备登录 Headscale:tailscale up --login-server=https://<你的 Headscale 域名>"
|
保存并赋予执行权限,然后运行:
1
| chmod +x tailscale.sh && ./tailscale.sh
|
在执行时按提示输入 derp.abc.xyz、邮箱、DERP 端口(例如 2222)、HTTP 端口(例如 3333)。如遇 Nginx 错误可重启并检查配置后重试。
第五步:搭建 Headscale 与 Headscale-UI
以 root 身份创建 headscale.sh 并运行(脚本会下载 headscale .deb、配置、部署 nginx 反代及 headscale-ui):
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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
| #!/bin/bash set -euo pipefail [ "$(id -u)" -eq 0 ] || { echo "请用 root 运行"; exit 1; } export DEBIAN_FRONTEND=noninteractive
read -p "请输入 Headscale 域名(例如 hs.example.com): " HS_DOMAIN read -p "请输入 DERP 域名(例如 derp.example.com): " DERP_DOMAIN read -p "请输入 v4 网段(必须在 100.64.0.0/10,例如 100.100.1.0/24): " IP_CIDR read -p "DERP 服务端口(默认443): " DERP_PORT DERP_PORT=${DERP_PORT:-443}
BIND_IP="0.0.0.0"
[[ "$IP_CIDR" == */* ]] || { echo "网段必须是 CIDR 形式,例如 100.100.1.0/24"; exit 1; } _base="${IP_CIDR%/*}"; o1=${_base%%.*}; rest=${_base#*.}; o2=${rest%%.*} if ! [[ "$o1" == "100" && "$o2" =~ ^(6[4-9]|7[0-9]|8[0-9]|9[0-9]|10[0-9]|11[0-9]|12[0-7])$ ]]; then echo "v4 网段必须落在 100.64.0.0/10;当前 ${IP_CIDR} 不合法"; exit 1; fi
echo "== 安装依赖和相关软件" apt update && apt -y install wget unzip curl jq nginx certbot python3-certbot-nginx ca-certificates
echo "== 下载并安装 Headscale (.deb) =="
HS_DEB_URL="https://github.com/juanfont/headscale/releases/download/v0.27.1/headscale_0.27.1_linux_amd64.deb" TMP_DEB="/tmp/headscale_0.27.1_linux_amd64.deb"
echo "下载:${HS_DEB_URL}" wget -q -O "${TMP_DEB}" "${HS_DEB_URL}" if [ ! -f "${TMP_DEB}" ]; then echo "❌ headscale .deb 下载失败" exit 1 fi
dpkg -i "${TMP_DEB}" || apt -f install -y systemctl enable --now headscale || true
echo "== 修正运行用户与目录权限" id -u headscale >/dev/null 2>&1 || useradd --system --home /var/lib/headscale --shell /usr/sbin/nologin headscale mkdir -p /var/lib/headscale /etc/headscale /var/www/letsencrypt /var/www/web chown -R headscale:headscale /var/lib/headscale /etc/headscale chmod 750 /var/lib/headscale
echo "== 写入 Headscale 配置(v0.26 兼容 + 关闭 MagicDNS + 对外监听)" CFG="/etc/headscale/config.yaml" cp -a "$CFG" "${CFG}.bak.$(date +%s)" 2>/dev/null || true cat >"$CFG" <<EOF server_url: https://${HS_DOMAIN}
# 对外监听给 Nginx 反代(官方反代示例) listen_addr: ${BIND_IP}:8080 http_listen_addr: ${BIND_IP}:8080 metrics_listen_addr: ${BIND_IP}:9090 # gRPC 仅本机(供 CLI 使用) grpc_listen_addr: 127.0.0.1:50443 grpc_tls_disable: true grpc_allow_insecure: true
# 由反向代理终止 TLS tls_cert_path: "" tls_key_path: ""
# Tailscale v2 协议所需 noise: private_key_path: /var/lib/headscale/noise_private.key
# DNS:关闭覆盖与 MagicDNS,避免 base_domain 必填 dns: override_local_dns: false magic_dns: false
# v0.26 使用 prefixes(不要再用 ip_prefixes) prefixes: v4: ${IP_CIDR} # 如需 IPv6: # v6: fd7a:115c:a1e0::/48
# 新版数据库写法(修复“invalid database type”) database: type: sqlite sqlite: path: /var/lib/headscale/db.sqlite
# 私钥(WireGuard) private_key_path: /var/lib/headscale/private.key
# 通过站点下发自定义 DERP derp: urls: - https://${HS_DOMAIN}/derp.json EOF chown headscale:headscale "$CFG"
systemctl stop headscale || true rm -f /var/lib/headscale/db.sqlite /var/lib/headscale/db.sqlite-wal /var/lib/headscale/db.sqlite-shm chown -R headscale:headscale /var/lib/headscale systemctl start headscale || true
echo "== [4/9] 配置 Nginx 正式站点(WebSocket + 反代 8080)" grep -q "include /etc/nginx/conf.d/*.conf" /etc/nginx/nginx.conf || \ sed -i '/http[[:space:]]*{/a\ include /etc/nginx/conf.d/*.conf;' /etc/nginx/nginx.conf
cat /etc/nginx/conf.d/websocket_upgrade_map.conf <<'EOF' map $http_upgrade $connection_upgrade { default upgrade; '' close; } EOF
cat >/etc/nginx/sites-available/headscale.conf <<EOF server { listen 80; listen [::]:80; server_name ${HS_DOMAIN}; return 301 https://\$host\$request_uri; }
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ${HS_DOMAIN}; ssl_certificate /etc/letsencrypt/live/${HS_DOMAIN}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/${HS_DOMAIN}/privkey.pem;
location / { proxy_pass http://${BIND_IP}:8080; 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; }
# derp.json 静态 location = /derp.json { types { application/json json; } alias /var/www/derp.json; }
# Headscale UI(/web) location /web { index index.html; alias /var/www/web; } } EOF ln -sf /etc/nginx/sites-available/headscale.conf /etc/nginx/sites-enabled/headscale.conf nginx -t && systemctl reload nginx
echo "== 安装 Headscale UI(显示详细过程)"
UI_ZIP_URL="https://github.com/gurucomputing/headscale-ui/releases/download/2025.08.23/headscale-ui.zip" TMP_UI_ZIP="/tmp/headscale-ui.zip"
echo "下载:${UI_ZIP_URL}" wget -q -O "${TMP_UI_ZIP}" "${UI_ZIP_URL}" if [ ! -f "${TMP_UI_ZIP}" ]; then echo "❌ headscale-ui.zip 下载失败" exit 1 fi
mkdir -p /var/www/web unzip -o "${TMP_UI_ZIP}" -d /var/www/_hs_ui_tmp >/dev/null
UI_INDEX=$(find /var/www/_hs_ui_tmp -maxdepth 3 -type f -name index.html -print -quit || true) if [ -n "${UI_INDEX}" ]; then UI_SRC_DIR=$(dirname "${UI_INDEX}") rm -rf /var/www/web/* cp -a "${UI_SRC_DIR}"/. /var/www/web/ chown -R www-data:www-data /var/www/web rm -rf /var/www/_hs_ui_tmp echo "Headscale UI 部署完成 -> /var/www/web" else echo "⚠️ 没在 zip 中找到 index.html,UI 可能未正确部署" fi
echo "== 下发 derp.json & 重启服务并等待就绪" cat >/var/www/derp.json <<EOF { "Regions": { "900": { "RegionID": 900, "RegionCode": "myderp", "Nodes": [ { "Name": "a", "RegionID": 900, "DERPPort": ${DERP_PORT}, "HostName": "${DERP_DOMAIN}", "IPv4": "", "InsecureForTests": false } ] } } } EOF chown -R www-data:www-data /var/www || true find /var/www -type d -exec chmod 755 {} \; || true find /var/www -type f -exec chmod 644 {} \; || true
systemctl restart headscale nginx -t && systemctl reload nginx
for i in $(seq 1 30); do if bash -c "exec 3<>/dev/tcp/${BIND_IP}/8080" 2>/dev/null; then exec 3>&- 3<&-; break; fi sleep 0.5 done
echo "# 生成API密钥" headscale apikeys create --expiration 9999d echo "安装完成!请使用生成的API密钥和以下链接登陆Headscale-ui面板:" echo "https://${HS_DOMAIN}/web" echo "使用以下命令将设备连接到Headscale服务器:" echo "tailscale up --login-server=https://${HS_DOMAIN}"
|
保存并运行:
1
| chmod +x headscale.sh && ./headscale.sh
|
部署完成后可访问 Headscale UI:https://head.abc.xyz/web
第六步:访问 Headscale UI 并设置(示例)
打开 https://head.abc.xyz/web → 设置:
- Headscale URL:
https://head.abc.xyz
- Headscale API Key:使用脚本运行后
headscale apikeys create --expiration 9999d 输出的密钥(粘贴在 UI 上并保存)
提示:如果更换浏览器或电脑需要重新配置 API Key,可在服务器上重新执行 headscale apikeys create --expiration 9999d 获取新密钥。
左侧菜单 → 用户 → 添加一个用户 → 确定。
在服务器上执行(示例):
1
| tailscale up --login-server=https://head.abc.xyz
|
执行后会生成一个注册密钥链接,例如:
1
| https://head.abc.xyz/register/a_ec5fM_OiwjzZxMXZ--4qsk
|
在 UI 中:左侧设备视图 → 添加新设备 → 粘贴密钥 a_ec5fM_OiwjzZxMXZ--4qsk → 选择用户 → 确认。
如果提示强制登录,可使用:
1
| sudo tailscale up --login-server=https://head.abc.xyz --force-reauth
|
- 其他设备安装:访问
https://head.abc.xyz/windows(或对应平台页面)下载客户端并按提示连接。
连接原理简述:设备执行 tailscale up --login-server=https://head.abc.xyz 会生成一个临时注册密钥,管理员在 Headscale UI 将该密钥与用户绑定,就能完成设备注册与授权。
第七步:其他常用操作与注意事项
- 重载 Derp 配置并重启:
1 2
| systemctl daemon-reload systemctl restart derp
|
- 重启 Headscale 与 Nginx:
1 2
| systemctl restart headscale systemctl restart nginx
|
- 部分 Tailscale 常用命令:
1 2 3 4 5 6 7 8 9 10 11
| tailscale logout
tailscale down
tailscale netcheck
tailscale status
|
- 添加子网路由(示例):
1 2 3 4
| tailscale up --login-server=https://head.abc.xyz --accept-routes=true
tailscale up --login-server=https://head.abc.xyz --accept-routes=true --accept-dns=false --advertise-routes=192.168.2.0/24 --reset
|
- 如果以前安装过旧版 Tailscale/Headache(Windows),删除以前的注册表值:
1 2 3
| reg delete "HKLM\Software\Tailscale IPN" /v LoginURL /f reg delete "HKLM\Software\Tailscale IPN" /v UnattendedMode /f
|
- Windows 服务重启:
1 2
| net stop Tailscale net start Tailscale
|
- 证书续期(自动化建议):
1 2 3 4 5 6 7 8 9 10
| sudo certbot renew --dry-run
sudo systemctl enable certbot.timer
sudo apt update sudo apt install cron -y sudo crontab -e
|
在 crontab 中加入:
1
| 0 3 * * * certbot renew --quiet && systemctl reload nginx && systemctl restart derp && systemctl restart headscale
|
常见问题提示
- Cloudflare 一定要关闭代理(小黄云),否则外网 TLS 终止与端口映射会被 Cloudflare 干扰。
- 端口选择:若你使用非标准端口(非 443/80),请确保 Cloudflare 不代理并且防火墙放行对应端口。
- 备份:部署前备份重要配置与数据库文件(如
/etc/headscale/config.yaml 与 /var/lib/headscale/db.sqlite)。
- 安全:将 Cloudflare Token、API Key、Headscale 私钥等敏感信息保存在受限权限的文件里(如
/etc/cloudflare/cloudflare.ini,权限 600)。
- 日志排查:
journalctl -u headscale -f、journalctl -u derp -f 与 nginx -t、systemctl status 是常见排错起点。