Headscale 是什么
Tailscale 的控制服务器是不开源的,而且对免费用户有诸多限制,这是人家的摇钱树,可以理解。好在目前有一款开源的实现叫 Headscale,这也是唯一的一款,希望能发展壮大。

Headscale 由欧洲航天局的 Juan Font 使用 Go 语言开发,在 BSD 许可下发布,实现了 Tailscale 控制服务器的所有主要功能,可以部署在企业内部,没有任何设备数量的限制,且所有的网络流量都由自己控制。

目前 Headscale 还没有可视化界面,期待后续更新吧。

1、创建初始化目录和数据库

echo 'net.ipv4.ip_forward = 1' | tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.conf
sysctl -p /etc/sysctl.conf

mkdir -p /data/headscale/config
mkdir -p /data/headscale/data
cd /data/headscale/data
touch db.sqlite
cd /data/headscale/config;
#下载模板配置文件
 wget https://github.com/juanfont/headscale/raw/main/config-example.yaml

#代理下载任何GitHub下载加上:https://ghproxy.com/
 wget https://ghproxy.com/https://github.com/juanfont/headscale/raw/main/config-example.yaml

2、修改配置文件

2.1 参数注释

#注意更改公网IP或者换成域名:xxx.com

需要修改的参数有:

#访问服务端的公网IP或者域名

server_url

#开放的端口

listen_addr

#修改/etc/headscale/private.key的存放路径

private_key_path

#修改/etc/headscale/noise_private.key的存放路径

private_key_path

#关闭ipv6,用不到

ip_prefixes

#开启随机端口

randomize_client_port

#定义数据库路径

db_path

#dns改为国内223.5.5.5,可以多个

nameservers

2.2 写入配置

vim /data/headscale/config/config.yaml

---
server_url: http://headscale.xxx.com:8080
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 127.0.0.1:50443
grpc_allow_insecure: false
private_key_path: /var/lib/headscale/private.key
noise:
  private_key_path: /var/lib/headscale/noise_private.key
ip_prefixes:
  - 100.64.0.0/10
derp:
  server:
    enabled: false
    region_id: 999
    region_code: "headscale"
    region_name: "Headscale Embedded DERP"
    stun_listen_addr: "0.0.0.0:3478"
  urls:
    - https://controlplane.tailscale.com/derpmap/default
  paths: []
  auto_update_enabled: true
  update_frequency: 24h
disable_check_updates: false
ephemeral_node_inactivity_timeout: 30m
node_update_check_interval: 10s
db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite
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
acl_policy_path: ""
dns_config:
  override_local_dns: true
  nameservers:
    - 223.5.5.5
    - 114.114.114.114
    - 8.8.8.8
    - 1.1.1.1
  domains: []
  magic_dns: true
  base_domain: example.com
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
logtail:
  enabled: false
randomize_client_port: true

3、启动docker服务

服务端操作,前提是自己安装好docekr服务,百度一大把


docker run -d --name headscale --restart always -v /data/headscale/config:/etc/headscale/ -p 8089:8089 -p 9090:9090 headscale/headscale:0.22.3 headscale serve

#进入容器并创建默认用户
docker exec -it headscale bash
headscale users create default
#生成24小时密钥,注意下面密钥客户端加入需要用到
headscale preauthkeys create --user default --reusable --expiration 24h
#下面是输出信息
a8438d05e623563aa908b1c190d56e2f2547e3713da693b7

#退出容器
exit
#安装客户端--advertise-routes=参数是运行连接客户端可以被访问的网段比如内网(可选)
#如果安装慢可以选择手动安装
curl -fsSL https://tailscale.com/install.sh | sh
#(可选)
tailscale up --login-server=http://xxx.com:8089 --accept-routes=true --accept-dns=false --advertise-routes=10.10.1.0/24 --authkey a8438d05e623563aa908b1c190d56e2f2547e3713da693b7

3.1.1 docker-compose方式



cat > /data/headscale/docker-compose.yml << EOF
version: "3.4"

services:
  headscale:
    image: headscale/headscale:0.22.3
    container_name: headscale
    restart: unless-stopped
    entrypoint: headscale serve
    volumes:
      - /data/headscale/config:/etc/headscale/
      - /data/headscale/data:/var/lib/headscale/
    ports:
      - "53450:8080"
      - "9090:9090"
EOF

#启动容器
cd /data/headscale/;
docker-compose -f docker-compose.yml up -d --force-recreate


代证书https测试


cat > /data/headscale/docker-compose.yml << EOF
version: "3.4"

services:
  headscale:
    image: headscale/headscale:0.22.3
    container_name: headscale
    restart: unless-stopped
    entrypoint: headscale serve
    volumes:
      - /data/headscale/config:/etc/headscale/
      - /data/headscale/data:/var/lib/headscale/
    ports:
      - "53450:8080"
      - "9090:9090"

cat > /data/caddy/docker-compose.yml << EOF
version: "3.4"
services:
  caddy:
    image: caddy:2.7.4
    container_name: caddy
    restart: unless-stopped
    volumes:
      - /data/caddy/Caddyfile:/etc/caddy/Caddyfile
      - /data/caddy/certs:/root/certs
      - caddy_data:/data
      - caddy_conf:/config
    ports:
      - "80:80"
      - "443:443"

volumes:
  caddy_data: { }
  caddy_conf: { }
EOF

4、客户端加入

#客户端安装,如果安装慢可以选择手动安装
curl -fsSL https://tailscale.com/install.sh | sh

#--advertise-routes=参数是运行连接客户端可以被访问的网段比如内网
tailscale up --login-server=http://xxx.com:8089 --accept-routes=true --accept-dns=false --advertise-routes=192.168.1.0/24 --authkey a8438d05e623563aa908b1c190d56e2f2547e3713da693b7

#添加多个访问网段,重新加入步骤
tailscale down
tailscale up --login-server=http://xxx.com:8089 --accept-routes=true --accept-dns=false --advertise-routes="192.168.1.0/24,172.168.1.0/24" --reset --authkey a8438d05e623563aa908b1c190d56e2f2547e3713da693b7

5、允许路由访问

#进入容器并创建默认用户
docker exec -it headscale bash

#查看加入的节点信息
root@c532c6ca56ae:/# headscale nodes list
ID | Hostname  | Name      | MachineKey | NodeKey | User | IP addresses | Ephemeral | Last seen           | Expiration          | Online | Expired
1  | localhost | localhost | [HLoyZ]    | [ZIiB/] | test | 100.64.0.1, | false     | 2023-08-16 05:36:02 | 0001-01-01 00:00:00 | online | no

#查看路由信息
root@c532c6ca56ae:/# headscale routes list -i 1
ID | Machine   | Prefix           | Advertised | Enabled | Primary
1  | localhost | 165.198.105.0/24 | true       | false   | false
#允许路由访问
root@c532c6ca56ae:/# headscale routes enable -r 1 "165.198.105.0/24"
#再次查看确认Enabled | Primary 是否等于true
root@c532c6ca56ae:/# headscale routes list -i 1
ID | Machine   | Prefix           | Advertised | Enabled | Primary
1  | localhost | 165.198.105.0/24 | true       | true    | true

#允许所有效果相同于headscale routes enable -r 1 "165.198.105.0/24"命令
root@c532c6ca56ae:/# headscale routes enable -i 1 -a
root@c532c6ca56ae:/# headscale routes list -i 1
ID | Machine   | Prefix           | Advertised | Enabled | Primary
1  | localhost | 165.198.105.0/24 | true       | true    | true

6、测试ping

要放开icmp协议或者防火墙

服务端测试ping
root@test:/# ping 100.64.0.1
PING 100.64.0.1 (100.64.0.1) 56(84) bytes of data.
64 bytes from 100.64.0.1: icmp_seq=1 ttl=64 time=79.1 ms
64 bytes from 100.64.0.1: icmp_seq=2 ttl=64 time=79.2 ms
64 bytes from 100.64.0.1: icmp_seq=3 ttl=64 time=84.9 ms
64 bytes from 100.64.0.1: icmp_seq=4 ttl=64 time=86.0 ms
64 bytes from 100.64.0.1: icmp_seq=5 ttl=64 time=86.5 ms

#客户端测试ping
root@localhost:~# ping 100.64.0.2
PING 100.64.0.2 (100.64.0.2) 56(84) bytes of data.
64 bytes from 100.64.0.2: icmp_seq=1 ttl=64 time=86.4 ms
64 bytes from 100.64.0.2: icmp_seq=2 ttl=64 time=86.5 ms
64 bytes from 100.64.0.2: icmp_seq=3 ttl=64 time=86.1 ms
64 bytes from 100.64.0.2: icmp_seq=4 ttl=64 time=86.5 ms
64 bytes from 100.64.0.2: icmp_seq=5 ttl=64 time=86.7 ms

#服务端测试ping客户端内网IP
root@test:/# ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=63 time=88.8 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=63 time=86.2 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=63 time=87.1 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=63 time=86.1 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=63 time=83.3 ms

7、二进制客户端手动安装篇(可选)

(可选参考)

前面用脚本安装了,就不要操作了,适合手动安装模式

#下载官方二进制包
wget https://pkgs.tailscale.com/stable/tailscale_1.48.1_amd64.tgz
#解压
tar xf tailscale_1.48.1_amd64.tgz

#将二进制文件复制到官方软件包默认的路径下
cp tailscale_1.48.1_amd64/tailscaled /usr/sbin/tailscaled
cp tailscale_1.48.1_amd64/tailscale /usr/bin/tailscale

#将 systemD service 配置文件复制到系统路径下:
cp tailscale_1.48.1_amd64/systemd/tailscaled.service /lib/systemd/system/tailscaled.service

#将环境变量配置文件复制到系统路径下:
cp tailscale_1.48.1_amd64/systemd/tailscaled.defaults /etc/default/tailscaled
#开机启动和启动
systemctl enable --now tailscaled
#查看状态
systemctl status tailscaled

windows客户端提示

最新的tailscale版本(1.34.0 及更高版本)
tailscale在 1.34 版中添加了快速用户切换,您现在可以使用 新的登录命令,用于连接到一个或多个tailscale 服务器。以前使用的配置文件不再起作用。

使用Tailscale的登录命令添加您的个人资料:
tailscale login --login-server https://headscale.amjun.com

################################################
Windows 注册表配置(1.32.0 及更低版本)
此页面提供官方 Windows 的 Windows 注册表信息 tailscale客户端。

注册表文件将配置尾标以用作 其控制服务器。https://headscale.amjun.com

谨慎
在安装之前,应始终下载并检查注册表文件 它:

curl https://headscale.amjun.com/windows/tailscale.reg

8、部署derp中转服务器

此教程适合家用家庭公网宽带使用,纯公网IP的。

8.1.1 安装go服务环境

#安装必要的工具
apt install -y wget git openssl curl

cd /root/;mkdir go;cd go;
wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz

rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz

export PATH=$PATH:/usr/local/go/bin
go version

echo "export PATH=$PATH:/usr/local/go/bin" >> /etc/profile
source /etc/profile

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

go install tailscale.com/cmd/derper@main

cd ~/go/pkg/mod/tailscale.com\@v1.1.1-0.20230818020430-af2e4909b6e9/cmd/derper
chmod +w cert.go
vim cert.go


#将这段代码的if hi.ServerName != m.hostname,return nil……,}加上//注释掉,注释3行保存
func (m *manualCertManager) getCertificate(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
        //if hi.ServerName != m.hostname {   这行注释
        // fmt.Errorf("cert mismatch with hostname: %q", hi.ServerName)  这行注释
        //}  这行注释

        // Return a shallow copy of the cert so the caller can append to its
        // Certificate field.
        certCopy := new(tls.Certificate)
        *certCopy = *m.cert
        certCopy.Certificate = certCopy.Certificate[:len(certCopy.Certificate):len(certCopy.Certificate)]
        return certCopy, nil
}

#创建derp目录
mkdir -p /etc/derp
rm -rf /etc/derp/derper && go build -o /etc/derp/derper
ls /etc/derp

8.1.2 创建自签证书和derp服务


openssl req -x509 -newkey rsa:4096 -sha256 -days 36500 -nodes -keyout /etc/derp/derp.poc.test.com.key -out /etc/derp/derp.poc.test.com.crt -subj "/CN=derp.poc.test.com" -addext "subjectAltName=DNS:derp.poc.test.com"


#如果需要防白嫖可以在ExecStart=/etc/derp/derper后面加上这个参数,但derp节点需要加入到同样vpn中
--verify-clients

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.poc.test.com -a :33445 -http-port 33446 -stun-port 13478 -certmode manual -certdir /etc/derp
RestartPreventExitStatus=1
[Install]
WantedBy=multi-user.target
EOF


systemctl daemon-reload
systemctl enable derp
systemctl restart derp

#自己建一个nginx,可以get的到,注意,可以是域名或者ip,如果是家庭公网域名,需要在本地/etc/hosts做本地解析,要不然是连接不上derp服务器。其它客户端不用本地解析
{
    "Regions": {
        "901": {
            "RegionID":   901,
            "RegionCode": "my-home-node",
            "RegionName": "my-home-node-Derper",
            "Nodes": [
                    {
                    "Name":             "901a",
                    "RegionID":          901,
                    "DERPPort":         33445,
                    "STUNPort":         13478,
                    "STUNOnly":         false,
                    "HostName": "xxx.com",
                    "InsecureForTests": true
                }
            ]
        }
    }
}


8.1.3 配置headscale的derp服务

 #修改headscale配置,并重启headscale服务
 vim /data/headscale/config/config.yaml
 derp:
  server:
    # If enabled, runs the embedded DERP server and merges it into the rest of the DERP config
    # The Headscale server_url defined above MUST be using https, DERP requires TLS to be in place,把这个改为true
    enabled: true
    # Region ID to use for the embedded DERP server.
    # The local DERP prevails if the region ID collides with other region ID coming from
    # the regular DERP config.
    region_id: 999
    # Region code and name are displayed in the Tailscale UI to identify a DERP region
    region_code: "headscale"
    region_name: "Headscale Embedded DERP"
    # Listens over UDP at the configured address for STUN connections - to help with NAT traversal.
    # When the embedded DERP server is enabled stun_listen_addr MUST be defined.
    #
    # For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
    stun_listen_addr: "0.0.0.0:3478"
  # List of externally available DERP maps encoded in JSON,把你公开的derp链接放到这里去。注释官方链接
  urls:
    #- https://controlplane.tailscale.com/derpmap/default
    - https://xxx

#重启服务生效
docker restart headscale

8.1.4 把端口映射或者防火墙放开33445/tcp和13478/udp端口

9、二进制安装headscale服务端(测试)

9.1.1 下载服务端文件

#挑选适合自己环境的版本下载
https://github.com/juanfont/headscale/releases

#比如
mkdir /etc/headscale;cd /etc/headscale
touch db.sqlite

wget https://github.com/juanfont/headscale/releases/download/v0.22.3/headscale_0.22.3_linux_amd64

#下载后,放到/etc/headscale目录里
cp headscale_0.22.3_linux_amd64 /usr/bin/headscale
chmod +x /usr/bin/headscale

echo 'net.ipv4.ip_forward = 1' | tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.conf
sysctl -p /etc/sysctl.conf

9.1.2 配置启动文件

cat > /lib/systemd/system/headscale.service << EOF
[Unit]
After=syslog.target
After=network.target
Description=headscale coordination server for Tailscale
X-Restart-Triggers=/etc/headscale/config.yaml

[Service]
Type=simple
User=root
Group=root
ExecStart=/usr/bin/headscale serve
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

9.1.3 配置服务端配置

vim /etc/headscale/config.yaml

---
server_url: http://xxx.com:8089
listen_addr: 0.0.0.0:8089
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 127.0.0.1:50443
grpc_allow_insecure: false
private_key_path: /etc/headscale/private.key
noise:
  # The Noise private key is used to encrypt the
  # traffic between headscale and Tailscale clients when
  # using the new Noise-based protocol. It must be different
  # from the legacy private key.
  private_key_path: /etc/headscale/noise_private.key
  #private_key_path: /var/lib/headscale/noise_private.key
ip_prefixes:
  #- fd7a:115c:a1e0::/48
  - 100.64.0.0/10
derp:
  server:
    # If enabled, runs the embedded DERP server and merges it into the rest of the DERP config
    # The Headscale server_url defined above MUST be using https, DERP requires TLS to be in place
    enabled: false
    # Region ID to use for the embedded DERP server.
    # The local DERP prevails if the region ID collides with other region ID coming from
    # the regular DERP config.
    region_id: 999
    # Region code and name are displayed in the Tailscale UI to identify a DERP region
    region_code: "headscale"
    region_name: "Headscale Embedded DERP"
    # Listens over UDP at the configured address for STUN connections - to help with NAT traversal.
    # When the embedded DERP server is enabled stun_listen_addr MUST be defined.
    #
    # For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
    stun_listen_addr: "0.0.0.0:3478"
  # List of externally available DERP maps encoded in JSON
  urls:
    - https://controlplane.tailscale.com/derpmap/default
  # Locally available DERP map files encoded in YAML
  #
  # This option is mostly interesting for people hosting
  # their own DERP servers:
  # https://tailscale.com/kb/1118/custom-derp-servers/
  #
  # paths:
  #   - /etc/headscale/derp-example.yaml
  paths: []
  # If enabled, a worker will be set up to periodically
  # refresh the given sources and update the derpmap
  # will be set up.
  auto_update_enabled: true
  # How often should we check for DERP updates?
  update_frequency: 24h
disable_check_updates: false
ephemeral_node_inactivity_timeout: 30m
node_update_check_interval: 10s
db_type: sqlite3
db_path: /etc/headscale/db.sqlite
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:
  # Output formatting for logs: text or json
  format: text
  level: info
acl_policy_path: ""
dns_config:
  # Whether to prefer using Headscale provided DNS or use local.
  override_local_dns: true
  # List of DNS servers to expose to clients.
  nameservers:
    - 223.5.5.5
    - 8.8.8.8
    - 1.1.1.1
  # NextDNS (see https://tailscale.com/kb/1218/nextdns/).
  # "abc123" is example NextDNS ID, replace with yours.
  #
  # With metadata sharing:
  # nameservers:
  #   - https://dns.nextdns.io/abc123
  #
  # Without metadata sharing:
  # nameservers:
  #   - 2a07:a8c0::ab:c123
  #   - 2a07:a8c1::ab:c123
  # Split DNS (see https://tailscale.com/kb/1054/dns/),
  # list of search domains and the DNS to query for each one.
  #
  # restricted_nameservers:
  #   foo.bar.com:
  #     - 1.1.1.1
  #   darp.headscale.net:
  #     - 1.1.1.1
  #     - 8.8.8.8
  # Search domains to inject.
  domains: []
  # Extra DNS records
  # so far only A-records are supported (on the tailscale side)
  # See https://github.com/juanfont/headscale/blob/main/docs/dns-records.md#Limitations
  # extra_records:
  #   - name: "grafana.myvpn.example.com"
  #     type: "A"
  #     value: "100.64.0.3"
  #
  #   # you can also put it in one line
  #   - { name: "prometheus.myvpn.example.com", type: "A", value: "100.64.0.3" }
  # Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/).
  # Only works if there is at least a nameserver defined.
  magic_dns: true
  # Defines the base domain to create the hostnames for MagicDNS.
  # `base_domain` must be a FQDNs, without the trailing dot.
  # The FQDN of the hosts will be
  # `hostname.user.base_domain` (e.g., _myhost.myuser.example.com_).
  base_domain: example.com
unix_socket: /var/run/headscale.sock
unix_socket_permission: "0770"
logtail:
  # Enable logtail for this headscales clients.
  # As there is currently no support for overriding the log server in headscale, this is
  # disabled by default. Enabling this will make your clients send logs to Tailscale Inc.
  enabled: false
randomize_client_port: true

9.1.4 启动服务

systemctl daemon-reload
systemctl restart headscale
systemctl enable headscale

10、家宽公网&泛域名证书配置

前置条件:安装好docker服务和docker-compose服务,需要域名一个,如果没域名,可以参考上面步骤自签域名来实现,自签证书不适合下面教程。

注意,需要在托管商dns里加入*解析,如果是静态公网IP直接修改就行了,如果是动态的IP需要通过ddns-go来实现

10.1.1 部署ddns-go

这里是通过cloudflare来代理的,其它的自行百度ddns-go配置,这里不用ipv6,申请一个dns的toke密钥

注意配置secret:的访问密钥,配置用户名和密码username:xxx password: xxx,更改域名domains: xxx.com

mkdir -p /data/ddns-go

vim /data/ddns-go/.ddns_go_config.yaml
dnsconf:
    - ipv4:
        enable: true
        gettype: url
        url: https://myip4.ipip.net,https://ddns.oray.com/checkip,https://ip.3322.net,https://4.ipw.cn,https://ifconfig.me
        netinterface: ""
        cmd: ""
        domains:
            - '*.xxx.com'
            - xxx.com
      ipv6:
        enable: false
        gettype: netInterface
        url: https://speed.neu6.edu.cn/getIP.php,https://v6.ident.me,https://6.ipw.cn
        netinterface: ""
        cmd: ""
        ipv6reg: ""
        domains:
            - ""
      dns:
        name: cloudflare
        id: ""
        secret: nFE-xxxxxxx
      ttl: ""
user:
    username: xxx
    password: xxx
webhook:
    webhookurl: ""
    webhookrequestbody: ""
    webhookheaders: ""
notallowwanaccess: false


docker run -d --name ddns-go --restart=always -p 9876:9876 -v /data/ddns-go:/root jeessy/ddns-go

10.1.2 注册免费证书

#安装证书服务国外会GitHub下载失败情况
curl https://get.acme.sh | sh -s email=xxx@qq.com

#国内下载
curl https://gitee.com/zerohacker/other-downloads/raw/master/linux/Centos_x86_64/7/acme.sh/install-acme.sh | sh -s email=xxx@qq.com

mkdir -p /data/certs/
#增加定时自动更新证书
crontab -e
10 23 1 */2 * /usr/bin/bash /data/certs/update-certs.sh

#编写自动更新证书脚本,我这里用的是cf的dns托管,每个运营商都不同,具体去GitHub参考acme.sh的项目
#CF_Key=这个是全局api-key那个,不是区域toke
vim /data/certs/update-certs.sh

export CF_Email="xxx@qq.com"
export CF_Key="xxxxxxxx"
/root/.acme.sh/acme.sh --install-cert --issue --dns dns_cf -d xxx.com -d '*.xxx.com' --key-file /data/certs/privkey.pem --fullchain-file /data/certs/fullchain.pem --force
chmod 644 /data/certs/privkey.pem

#systemctl restart derp
#systemctl restart caddy
#docker restart headscale
#systemctl restart tailscaled

#赋予执行权限
chmod +x /data/certs/update-certs.sh
bash /data/certs/update-certs.sh

配置headscale为https访问

前提条件:需要安装好derp服务部署

10.1.3 derp 部署

#derp 部署
#安装必要的工具
apt install -y wget git openssl curl

cd /root/;mkdir go;cd go;
wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz

rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz

export PATH=$PATH:/usr/local/go/bin
go version

echo "export PATH=$PATH:/usr/local/go/bin" >> /etc/profile
source /etc/profile

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

go install tailscale.com/cmd/derper@main

#注意此路径由于安装版本不同,@v1.1xxxx会有所不同,其它路径没变化
cd ~/go/pkg/mod/tailscale.com\@v1.1.1-0.20230818020430-af2e4909b6e9/cmd/derper

#创建derp目录
mkdir -p /etc/derp/tls
#如果编辑过程出现版本问题,需要下载新版重新编译
rm -rf /etc/derp/derper && go build -o /etc/derp/derper
ls /etc/derp

#如果需要防白嫖可以在ExecStart=/etc/derp/derper后面加上这个参数,但derp节点需要加入到同样vpn中
--verify-clients

#注意更改域名,这里默认是使用https方式的需要开放33445https端口和13478/udp端口,stun端口可以改
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.xxx.com -a :33445 -http-port 33446 -stun-port 13478 -certmode manual -certdir /etc/derp/tls --verify-clients
RestartPreventExitStatus=1
[Install]
WantedBy=multi-user.target
EOF

#链接证书过去,注意命名文件格式
cd /etc/derp/tls
ln -s /data/certs/fullchain.pem derp.xxx.com.crt
ln -s /data/certs/privkey.pem derp.xxx.com.key


systemctl daemon-reload
systemctl enable derp
systemctl restart derp

#在本地hosts上添加自己的derp解析
cat >> /etc/hosts << EOF
192.168.x.x derp.xxx.com
EOF

10.1.4 搭建caddy代理服务

443一般都是被禁用的,自己在路由上把443,转发到你要访问的端口就行了

mkdir -p /etc/caddy/{certs,www/derp};cd /etc/caddy
#下载caddy二进制文件
wget https://caddyserver.com/api/download?os=linux&arch=amd64&idempotency=81291252395604
chmod +x caddy_linux_amd64
cp caddy_linux_amd64 /usr/bin/caddy
#配置启动文件
vim /lib/systemd/system/caddy.service

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=root
Group=root
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target


#链接证书
cd /etc/caddy/certs;
ln -s /data/certs/fullchain.pem .
ln -s /data/certs/privkey.pem .

vim /etc/caddy/Caddyfile

https://derp.xxx.com: {
        root * /etc/caddy/www/derp
        tls /etc/caddy/certs/fullchain.pem /etc/caddy/certs/privkey.pem
        file_server

        header {
                Access-Control-Allow-Origin *.xxx.com
                Strict-Transport-Security "max-age=31536000;"
                X-XSS-Protection "1; mode=block"
                X-Frame-Options "SAMEORIGIN"
                X-Robots-Tag "none"
                -Server
        }
}

在线json格式为:

vim /etc/caddy/www/derp/index.html

{
  "Regions": {
    "901": {
      "RegionID": 901,
      "RegionCode": "my-home-node",
      "RegionName": "my-home-node-Derper",
      "Nodes": [
        {
          "Name": "901a",
          "RegionID": 901,
          "DERPPort": 33445,
          "STUNPort": 13478,
          "STUNOnly": false,
          "HostName": "derp.xxx.com"
        }
      ]
    }
  }
}


#启动服务
systemctl daemon-reload
systemctl enable caddy
systemctl restart caddy

10.1.5 配置headscale服务

注意更改在线json链接,证书路径

echo 'net.ipv4.ip_forward = 1' | tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.conf
sysctl -p /etc/sysctl.conf

mkdir -p /data/headscale/config
mkdir -p /data/headscale/data
cd /data/headscale/data
touch db.sqlite
cd /data/headscale/config;

#配置你的域名
vim config.yaml
vim /data/headscale/config/config.yaml
---
# headscale will look for a configuration file named `config.yaml` (or `config.json`) in the following order:
#
# - `/etc/headscale`
# - `~/.headscale`
# - current working directory

# The url clients will connect to.
# Typically this will be a domain like:
#
# https://myheadscale.example.com:443
#
server_url: https://headscale.xxx.com:53450

# Address to listen to / bind to on the server
#
# For production:
listen_addr: 0.0.0.0:53450
#listen_addr: 127.0.0.1:8080
#tls:
#  cert: /data/caddy/certs/fullchain.pem
#  key: /data/caddy/certs/privkey.pem
#  redirect_http_to_https: false
# Address to listen to /metrics, you may want
# to keep this endpoint private to your internal
# network
#
metrics_listen_addr: 127.0.0.1:9090

# Address to listen for gRPC.
# gRPC is used for controlling a headscale server
# remotely with the CLI
# Note: Remote access _only_ works if you have
# valid certificates.
#
# For production:
# grpc_listen_addr: 0.0.0.0:50443
grpc_listen_addr: 127.0.0.1:50443

# Allow the gRPC admin interface to run in INSECURE
# mode. This is not recommended as the traffic will
# be unencrypted. Only enable if you know what you
# are doing.
grpc_allow_insecure: false

# Private key used to encrypt the traffic between headscale
# and Tailscale clients.
# The private key file will be autogenerated if it's missing.
#
private_key_path: /var/lib/headscale/private.key

# The Noise section includes specific configuration for the
# TS2021 Noise protocol
noise:
  # The Noise private key is used to encrypt the
  # traffic between headscale and Tailscale clients when
  # using the new Noise-based protocol. It must be different
  # from the legacy private key.
  private_key_path: /var/lib/headscale/noise_private.key

# List of IP prefixes to allocate tailaddresses from.
# Each prefix consists of either an IPv4 or IPv6 address,
# and the associated prefix length, delimited by a slash.
# It must be within IP ranges supported by the Tailscale
# client - i.e., subnets of 100.64.0.0/10 and fd7a:115c:a1e0::/48.
# See below:
# IPv6: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#LL81C52-L81C71
# IPv4: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#L33
# Any other range is NOT supported, and it will cause unexpected issues.
ip_prefixes:
#  - fd7a:115c:a1e0::/48
  - 100.64.0.0/10

# DERP is a relay system that Tailscale uses when a direct
# connection cannot be established.
# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp
#
# headscale needs a list of DERP servers that can be presented
# to the clients.
derp:
  server:
    # If enabled, runs the embedded DERP server and merges it into the rest of the DERP config
    # The Headscale server_url defined above MUST be using https, DERP requires TLS to be in place
    enabled: true
#    enabled: true

    # Region ID to use for the embedded DERP server.
    # The local DERP prevails if the region ID collides with other region ID coming from
    # the regular DERP config.
    region_id: 999

    # Region code and name are displayed in the Tailscale UI to identify a DERP region
    region_code: "headscale"
    region_name: "Headscale Embedded DERP"

    # Listens over UDP at the configured address for STUN connections - to help with NAT traversal.
    # When the embedded DERP server is enabled stun_listen_addr MUST be defined.
    #
    # For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
    stun_listen_addr: "0.0.0.0:3478"

  # List of externally available DERP maps encoded in JSON
  urls:
  #  - https://controlplane.tailscale.com/derpmap/default
    - https://xxx.com/derp.json

  # Locally available DERP map files encoded in YAML
  #
  # This option is mostly interesting for people hosting
  # their own DERP servers:
  # https://tailscale.com/kb/1118/custom-derp-servers/
  #
  paths:
  #  - /etc/headscale/derp-example.yaml
  # If enabled, a worker will be set up to periodically
  # refresh the given sources and update the derpmap
  # will be set up.
  auto_update_enabled: true

  # How often should we check for DERP updates?
  update_frequency: 24h

# Disables the automatic check for headscale updates on startup
disable_check_updates: false

# Time before an inactive ephemeral node is deleted?
ephemeral_node_inactivity_timeout: 30m

# Period to check for node updates within the tailnet. A value too low will severely affect
# CPU consumption of Headscale. A value too high (over 60s) will cause problems
# for the nodes, as they won't get updates or keep alive messages frequently enough.
# In case of doubts, do not touch the default 10s.
node_update_check_interval: 10s

# SQLite config
db_type: sqlite3

# For production:
db_path: /var/lib/headscale/db.sqlite

# # Postgres config
# If using a Unix socket to connect to Postgres, set the socket path in the 'host' field and leave 'port' blank.
# db_type: postgres
# db_host: localhost
# db_port: 5432
# db_name: headscale
# db_user: foo
# db_pass: bar

# If other 'sslmode' is required instead of 'require(true)' and 'disabled(false)', set the 'sslmode' you need
# in the 'db_ssl' field. Refers to https://www.postgresql.org/docs/current/libpq-ssl.html Table 34.1.
# db_ssl: false

### TLS configuration
#
## Let's encrypt / ACME
#
# headscale supports automatically requesting and setting up
# TLS for a domain with Let's Encrypt.
#
# URL to ACME directory
acme_url: https://acme-v02.api.letsencrypt.org/directory

# Email to register with ACME provider
acme_email: ""

# Domain name to request a TLS certificate for:
tls_letsencrypt_hostname: ""

# Path to store certificates and metadata needed by
# letsencrypt
# For production:
tls_letsencrypt_cache_dir: /var/lib/headscale/cache

# Type of ACME challenge to use, currently supported types:
# HTTP-01 or TLS-ALPN-01
# See [docs/tls.md](docs/tls.md) for more information
tls_letsencrypt_challenge_type: HTTP-01
# When HTTP-01 challenge is chosen, letsencrypt must set up a
# verification endpoint, and it will be listening on:
# :http = port 80
tls_letsencrypt_listen: ":http"

## Use already defined certificates:
tls_cert_path: "/certs/fullchain.pem"
tls_key_path: "/certs/privkey.pem"

log:
  # Output formatting for logs: text or json
  format: text
  level: info

# Path to a file containg ACL policies.
# ACLs can be defined as YAML or HUJSON.
# https://tailscale.com/kb/1018/acls/
acl_policy_path: ""

## DNS
#
# headscale supports Tailscale's DNS configuration and MagicDNS.
# Please have a look to their KB to better understand the concepts:
#
# - https://tailscale.com/kb/1054/dns/
# - https://tailscale.com/kb/1081/magicdns/
# - https://tailscale.com/blog/2021-09-private-dns-with-magicdns/
#
dns_config:
  # Whether to prefer using Headscale provided DNS or use local.
  override_local_dns: true

  # List of DNS servers to expose to clients.
  nameservers:
    - 223.5.5.5
    - 114.114.114.114
    - 8.8.8.8
    - 1.1.1.1

  # NextDNS (see https://tailscale.com/kb/1218/nextdns/).
  # "abc123" is example NextDNS ID, replace with yours.
  #
  # With metadata sharing:
  # nameservers:
  #   - https://dns.nextdns.io/abc123
  #
  # Without metadata sharing:
  # nameservers:
  #   - 2a07:a8c0::ab:c123
  #   - 2a07:a8c1::ab:c123

  # Split DNS (see https://tailscale.com/kb/1054/dns/),
  # list of search domains and the DNS to query for each one.
  #
  # restricted_nameservers:
  #   foo.bar.com:
  #     - 1.1.1.1
  #   darp.headscale.net:
  #     - 1.1.1.1
  #     - 8.8.8.8

  # Search domains to inject.
  domains: []

  # Extra DNS records
  # so far only A-records are supported (on the tailscale side)
  # See https://github.com/juanfont/headscale/blob/main/docs/dns-records.md#Limitations
  # extra_records:
  #   - name: "grafana.myvpn.example.com"
  #     type: "A"
  #     value: "100.64.0.3"
  #
  #   # you can also put it in one line
  #   - { name: "prometheus.myvpn.example.com", type: "A", value: "100.64.0.3" }

  # Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/).
  # Only works if there is at least a nameserver defined.
  magic_dns: true

  # Defines the base domain to create the hostnames for MagicDNS.
  # `base_domain` must be a FQDNs, without the trailing dot.
  # The FQDN of the hosts will be
  # `hostname.user.base_domain` (e.g., _myhost.myuser.example.com_).
  base_domain: example.com

# Unix socket used for the CLI to connect without authentication
# Note: for production you will want to set this to something like:
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
#
# headscale supports experimental OpenID connect support,
# it is still being tested and might have some bugs, please
# help us test it.
# OpenID Connect
# oidc:
#   only_start_if_oidc_is_available: true
#   issuer: "https://your-oidc.issuer.com/path"
#   client_id: "your-oidc-client-id"
#   client_secret: "your-oidc-client-secret"
#   # Alternatively, set `client_secret_path` to read the secret from the file.
#   # It resolves environment variables, making integration to systemd's
#   # `LoadCredential` straightforward:
#   client_secret_path: "${CREDENTIALS_DIRECTORY}/oidc_client_secret"
#   # client_secret and client_secret_path are mutually exclusive.
#
#   # The amount of time from a node is authenticated with OpenID until it
#   # expires and needs to reauthenticate.
#   # Setting the value to "0" will mean no expiry.
#   expiry: 180d
#
#   # Use the expiry from the token received from OpenID when the user logged
#   # in, this will typically lead to frequent need to reauthenticate and should
#   # only been enabled if you know what you are doing.
#   # Note: enabling this will cause `oidc.expiry` to be ignored.
#   use_expiry_from_token: false
#
#   # Customize the scopes used in the OIDC flow, defaults to "openid", "profile" and "email" and add custom query
#   # parameters to the Authorize Endpoint request. Scopes default to "openid", "profile" and "email".
#
#   scope: ["openid", "profile", "email", "custom"]
#   extra_params:
#     domain_hint: example.com
#
#   # List allowed principal domains and/or users. If an authenticated user's domain is not in this list, the
#   # authentication request will be rejected.
#
#   allowed_domains:
#     - example.com
#   # Note: Groups from keycloak have a leading '/'
#   allowed_groups:
#     - /headscale
#   allowed_users:
#     - alice@example.com
#
#   # If `strip_email_domain` is set to `true`, the domain part of the username email address will be removed.
#   # This will transform `first-name.last-name@example.com` to the user `first-name.last-name`
#   # If `strip_email_domain` is set to `false` the domain part will NOT be removed resulting to the following
#   user: `first-name.last-name.example.com`
#
#   strip_email_domain: true

# Logtail configuration
# Logtail is Tailscales logging and auditing infrastructure, it allows the control panel
# to instruct tailscale nodes to log their activity to a remote server.
logtail:
  # Enable logtail for this headscales clients.
  # As there is currently no support for overriding the log server in headscale, this is
  # disabled by default. Enabling this will make your clients send logs to Tailscale Inc.
  enabled: false

# Enabling this option makes devices prefer a random port for WireGuard traffic over the
# default static port 41641. This option is intended as a workaround for some buggy
# firewall devices. See https://tailscale.com/kb/1181/firewalls/ for more information.
randomize_client_port: true

10.1.1 docker-compose启动

cd /data/headscale/
vim docker-compose.yml
version: "3.4"

services:
  headscale:
    image: headscale/headscale:0.22.3
    container_name: headscale
    restart: unless-stopped
    entrypoint: headscale serve
    volumes:
      - /data/headscale/config:/etc/headscale/
      - /data/headscale/data:/var/lib/headscale/
      - /data/certs:/certs
    ports:
      - "53450:53450"
      - "9090:9090"

#启动docker服务
docker-compose -f docker-compose.yml up -d --force-recreate

10.1.3 配置caddyfile

vim Caddyfile
#增加这条,域名和证书自己准备好
https://tailscale.xxx域名: {
        tls /etc/caddy/fullchain.pem /etc/caddy/privkey.pem
        header {
                Access-Control-Allow-Origin *.xxx.com
                Strict-Transport-Security "max-age=31536000;"
                X-XSS-Protection "1; mode=block"
                X-Frame-Options "SAMEORIGIN"
                X-Robots-Tag "none"
                -Server
        }

        reverse_proxy http://headscale.这里域名为headscale配置文件里的域名和端口:52014
}

10.1.4 注意

然后把你的代理caddy的https端口开放,用https://tailscale.xxx域名:端口,就可以访问你自己的headscale服务了

采用https后手机端设置自己服务器点登录是会一直转圈圈的,命令:docker logs -f headscale 后台看headscale日志,会有弹出手机端发来的请求注册密钥,手动注册就行了。windows就没这个问题,linux也是。

日志如下:

#!/bin/sh
echo "======== start clean docker containers logs ========"
logs=$(find /var/lib/docker/containers/ -name *-json.log)
for log in $logs
do
echo "clean logs : $log"
cat /dev/null > $log
done
echo "======== end clean docker containers logs ========"
# chmod +x clean_docker_log.sh
# ./clean_docker_log.sh

tmp_names=$(docker ps | grep headscale| awk '{print $1}')
tmp_logs=$(find /var/lib/docker/containers/ -name $tmp_names*-json.log)
tail -f $tmp_logs

2023-08-24T13:24:08Z INF go/src/headscale/hscontrol/protocol_common.go:574 > Successfully sent auth url AuthURL=http://headscale.xxx.com:52014/register/nodekey:70bed72eb610435e9c4cd361224f419d1f0ba7533a338b801bd434002a3acb52 machine="Xiaomi 13Pro" noise=true

命令:

#default是用户
手动注册命令:
headscale --user default nodes register --key nodekey:70bed72eb610435e9c4cd361224f419d1f0ba7533a338b801bd434002a3acb52

不停的报错误日志:

#从系统日志可以看到tailscaled服务一直会报这个错误,是因为国内被墙了,是访问不到的
Aug 27 14:19:58 localhost tailscaled[338784]: logtail: dial "log.tailscale.io:443" failed: dial tcp 54.161.152.147:443: i/o timeout (in 30.001s), trying bootstrap...

#解决思路,在有安装tailscale客户端的机器上加上log.tailscale.io,把解析指向本地就好了,但derp服务端要开启443端口的服务caddy,或者nginx,httpd。先把第一台客户端加入headscale里面去就是derp服务端了