MoeCEO

MoeCEO

Tailscale 生态全私有化部署

18
0
0
2024-09-27

部署 Headscale

首先还是使用 1Panel 作为部署平台。此处省略其安装部分,直接开始正文。1Panel 应用仅需安装 OpenResty 用于反代。

直接打开 1Panel 的容器编排,使用docker-compose一键部署。

version: '3.5'
services:
  headscale:
    image: headscale/headscale:latest
    container_name: headscale
    network_mode: bridge
    volumes:
      - ./config:/etc/headscale/
    ports:
      - "127.0.0.1:4000:4000"
    restart: always
    command: headscale serve

这时容器会不停的重启,这是因为缺少一个config.yamldb.sqlite数据库文件。先暂时将 Headscale 容器停止。

cd /opt/1panel/docker/compose/headscale
mkdir -p ./config
touch ./config/db.sqlite
wget -O ./config/config.yaml https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yaml

然后,我们打开/opt/1panel/docker/compose/headscale/config文件夹下的config.yaml,对其内容进行个性化修改。

此处我将server_url字段值改为 Headscale 服务的域名https://nat.moe.ceo:443,将listen_addr字段值改为0.0.0.0:4000,将metrics_listen_addr字段值改为0.0.0.0:9090,将grpc_listen_addr字段值改为0.0.0.0:50443。(重要的是server_urllisten_addr字段值)

server_url字段是服务所使用的域名。
listen_addr字段是设定程序监听绑定给服务的端口。由于443端口我需要和其他服务同时使用,所以使用 OpenResty 反代服务域名到程序监听的端口4000上,且为了安全和管理,我部署容器几乎都采用的是443反代到本地端口说,没有将容器端口直接暴露到公网。
metrics_listen_addr字段是用于设定/metrics端点的端口。其作用是访问/metrics端点可以查看服务 Headscale 的运行情况。
grpc_listen_addr字段是设定服务 Headscale 的远程 CLI 调试。注意如果你需要使用 CLI,请一定还要把grpc_allow_insecure值改为true。虽然我修改了metrics_listen_addrgrpc_listen_addr字段,但其实docker-compose中我并没有将其映射出去,实在是平时用不上,有需要的可以自行映射出去。Headscale 自带的stun服务也是一样。
根据个人需求可以将ip_prefixes改为想要用于分配的 IP 网段。具体其他详细参数可以参考配置文件的注释内容与 Tailscale 官方文档进行修改。需要注意的是如果是阿里云的服务器,可能会有一定问题,此处不做讲解,可以查看其他作者的博文进行解决。
如对 dns 有需求的也可以修改dns_config字段下的nameservers部分内容。默认的1.1.1.18.8.8.8也是可行的。

将配置文件config.yaml修改完成后重启 Headscale 容器。

进入网站管理页面,添加 Headscale 服务的域名,网站类型是静态网站,切记不是反向代理。代理地址127.0.0.1:4000我们后续再设置。再前往证书页面为此域名申请一个免费的证书。最好切回网站管理页面,为 Headscale 服务的域名开启 HTTPS 支持。

此时还未完成所有步骤,我们需要对 Headscale 的反向代理进行修改。然后还需要为它配置一个 WebUI 界面,方便我们后续的使用。

此处选择的是gurucomputing/headscale-ui: A web frontend for the headscale Tailscale-compatible coordination server (github.com)项目。

在 Releases 中下载 headscale-ui 压缩包将其上传到 1Panel 设置的域名的文件夹内。如我设置的域名是nat.moe.ceo,则在 1Panel 中打开文件夹/opt/1panel/apps/openresty/openresty/www/sites/nat.moe.ceo/index,上传完headscale-ui.zip,将其解压出来会得到一个web文件夹,其里面就是 headscale-ui 的静态文件代码。

我们返回 1Panel 的网站管理页面,对我们设置的域名进行调整。首先创建一个反向代理,指定根路径反代到127.0.0.1:4000,我们设定的listen_addr字段端口上。然后点击创建的反代的源文选项,我们需要对其进行修改。在反代的配置内容后面加入以下代码:

location /web {
    alias /www/sites/nat.moe.ceo/index/web; 
    index index.html; 
}

需要注意的是alias部分的内容需要根据你的实际域名路径进行修改。

此时我们可以访问几个路径看看是否成功的启动了服务。如/windows子路径查看 Windows 端的配置方法、/apple子路径查看 macOS 与 IOS 端的配置方法。同样可以访问/web路径看看 WenUI 是否正常。

我们此处就需要生成一个apikey用来管理 Headscale 服务了。你可以选择直接在 1Panel 中进入容器终端或者选择 SSH 连上服务器通过docker exec -it headscale /bin/sh命令进入容器终端。

我们输入headscale apikeys create -e 3650d命令用来生成一个时长365天有效期的密钥。其实直接执行docker exec headscale headscale apikeys create -e 3650d也行。

此时可以访问/web进入 WebUI 中,Headscale URL 处填入服务域名(如我则填写https://nat.moe.ceo),Headscale API Key 处填入上面得到的密钥。点击 Test Server Setting 对密钥进行连接测试。连接成功会旁边显示绿色小勾。此时就可以在 User View 与 Device View 中查看设备与用户情况了。

客户端配置

现在我们需要在 User View 中创建一个用户,设置完用户名,创建用户成功后还需要为其设置一个身份密钥 Preauth Keys 用于和服务端进行连接。为了方便可以勾选上 Reusable 生成一个可以重复使用的 KEY,且把有效期拉长点。需要知道的是前面终端生成的apikeys是管理服务的密钥,这里提供 WebUI 生成的是连接这个用户组网用的auth key密钥。

# 创建用户
headscale namespaces create default
# 创建对应用户的authkey
headscale preauthkeys create -e 24h --user default

至于怎么连接可以查看官方仓库juanfont/headscale: An open source, self-hosted implementation of the Tailscale control server (github.com)查看具体内容,也可以通过前面说过的访问对应子路径查看。

这里以 Android 的为例,可以下载官方客户端后,在客户端设置中选择使用自建服务器,填入服务域名即可。连接后会跳转访问自建的服务网页,页面说会有一个命令与说明,我们将命令进入到headscale容器内执行即可,其中NAMESPACE要改为实际的创建的用户名。命令类似于:

headscale nodes register --user default --key SOME_HEX_VALUE

最新的 Android 客户端可以直接使用auth key进行登录了。我们拿创建用户的时候生成的身份密钥 Preauth Keys 填入其中连接就行了。新版本更加方便简洁。不仅可以通过 Android 客户端使用auth key进行登录,同样可以在 WebUI 的 Device View 中使用跳转后给予的命令的SOME_HEX_VALUE值拿到 Device Key 处填入实现组网登录。

至于 Windows 端就直接终端命令吧。如下:

tailscale up --login-server=https://nat.moe.ceo --authkey=877f98d30e7df9f6f760266864220a935eaad6c33dda22ac

其中的auth key值是我随便打的,将login-serverauthkey值改为自己实际上使用与生成的即可。

一切连接完成后,我们就可以通过WebUI看到Device View中出现的新设备了。

客户端常用命令扩展

# 显示组网状态
tailscale status
# 显示网络状态
tailscale netcheck
# 提供出口节点
tailscale up --advertise-exit-node
# 启用子网路由
tailscale up --advertise-routes=192.168.1.0/24
# 接受子网路由
tailscale up --accept-routes
# 断开并且退出
tailscale down

关于客户端命令可以查看 Tailscale 的官网的官方文档。关于 Headscale 的 config 具体配置可以查看注释与官方代码仓库。

部署 DERP

此处还是使用docker-compose一键化部署,且依旧会出现重启问题,先停止,修改完再启动即可。

version: '3.5'
services:
  derper:
    image: fredliang/derper
    container_name: derper
    network_mode: bridge
    volumes:
      - ./certs:/app/certs
      - /opt/1panel/docker/compose/tailscaled/tailscale:/var/run/tailscale
    ports:
      - "3477:3477"
      - "3478:3478/udp"
    restart: always
    environment:
      - DERP_CERT_MODE=manual
      - DERP_ADDR=:3477
      - DERP_VERIFY_CLIENTS=true
      - DERP_DOMAIN=nat.moe.ceo

首先需要知道的是否需要启用DERP_VERIFY_CLIENTS,启用后自建的 DERP 就仅只能自己使用,不会被其他人扫到你自建的中继节点然后白嫖。上述给予的docker-compose案例是启用了此功能的。

如需此功能请先在 DERP 服务器上运行 Tailscale 的客户端。我依旧选择 Docker 容器化部署。

version: '3.5'
services:
  tailscaled:
    container_name: tailscaled
    image: tailscale/tailscale
    network_mode: host
    privileged: true
    restart: always
    cap_add: 
      - net_admin 
      - sys_module 
    volumes: 
      - ./lib/:/var/lib/
      - /dev/net/tun:/dev/net/tun
      - ./tailscale:/var/run/tailscale
    command: sh -c "mkdir -p /var/run/tailscale && ln -s /tmp/tailscaled.sock /var/run/tailscale/tailscaled.sock && tailscaled"

由于我的 DERP 与 Headscale 处于同一个服务器,所以我的 Headscale 自带的stun就没有启用,直接使用 DERP 上的即可。

且我为 DERP 分配的域名也是 Headscale 的域名。因为官方 DERP 方案走的是443端口,我授予的域名443端口还反代着 Headscale 服务,所以采用的是单独的端口。

此处我讲解一下 DERP 的容器compose参数情况。3477端口是 DERP 服务端口,可以在环境变量environment中修改DERP_ADDR值进行调整,需要注意的是填写值的前面有一个:符号。3478端口是stun服务的端口,需要注意走的是udp,不要写错了,且有防火墙的,需要注意放行情况。如果不需要stun服务,环境变量中加入DERP_STUN值为false即可,默认是truecerts路径的映射是用来存放derp服务域名的 TLS 证书的。环境变量中的DERP_CERT_MODE可以设置证书设定方式,可选manualletsencrypt,但是如果是letsencrypt则derp服务使用443端口。因为我并没有让 DERP 使用443端口,且我给予的域名的443反代给了 Headscale 在使用。故此我选择手动,填写manual模式。需要注意的是DERP_DOMAIN值是一定要填写的,我填写的是 Headscale 服务的域名。毕竟在同一个服务器上跑,懒得再单独给一个域名了。且我都单独给域名了,不如443反代。

如果你不需要启用 DERP 的身份验证,公开 DERP 服务的话,那tailscalederp容器的/var/run/tailscale目录是不需要映射的。这个目录映射的作用是使用其目录中的tailscaled.sock用于身份验证。

至于certs的证书,由于我是为 DERP 授予的 Headscale 服务域名,所以直接将/opt/1panel/apps/openresty/openresty/www/sites/nat.moe.ceo/ssl/目录中的证书COPY到/opt/1panel/docker/compose/derper/certs/下即可。需要注意的是一定要修改证书与密钥文件名字。证书密钥文件名一定要是nat.moe.ceo.crtnat.moe.ceo.key。即授予 DERP 的域名为文件名。

为了方便可以写一个定时任务,将证书定时同步到目录下即可。如下:

cp /opt/1panel/apps/openresty/openresty/www/sites/nat.moe.ceo/ssl/fullchain.pem /opt/1panel/docker/compose/derper/certs/nat.moe.ceo.crt

cp /opt/1panel/apps/openresty/openresty/www/sites/nat.moe.ceo/ssl/privkey.pem /opt/1panel/docker/compose/derper/certs/nat.moe.ceo.key

证书文件弄好后就可以正常启动derp容器了。

如果是443端口复用、自动化生成证书且开启身份验证,则docker-composenginx反代如下:

version: '3.5'
services:
  derper:
    image: fredliang/derper
    container_name: derper
    network_mode: bridge
    volumes:
      - ./certs:/app/certs
      - /opt/1panel/docker/compose/tailscaled/tailscale:/var/run/tailscale
    ports:
      - "127.0.0.1:3477:443"
      - "3478:3478/udp"
    restart: always
    environment:
      - DERP_CERT_MODE=letsencrypt
      - DERP_ADDR=:443
      - DERP_VERIFY_CLIENTS=true
      - DERP_DOMAIN=demo.moe.ceo
location ^~ / {
    proxy_pass http://127.0.0.1:3477; 
    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 Cache-Control no-cache; 
    proxy_ssl_server_name off; 

如果是非443端口且开启身份验证,则docker-compose代如下:

version: '3.5'
services:
  derper:
    image: fredliang/derper
    container_name: derper
    network_mode: bridge
    volumes:
      - ./certs:/app/certs
      - /opt/1panel/docker/compose/tailscaled/tailscale:/var/run/tailscale
    ports:
      - "3477:3477"
      - "3478:3478/udp"
    restart: always
    environment:
      - DERP_CERT_MODE=manual
      - DERP_ADDR=:3477
      - DERP_VERIFY_CLIENTS=true
      - DERP_DOMAIN=demo.moe.ceo

如果是非443端口且不开启身份验证,则docker-compose代如下:

version: '3.5'
services:
  derper:
    image: fredliang/derper
    container_name: derper
    network_mode: bridge
    volumes:
      - ./certs:/app/certs
    ports:
      - "3477:3477"
      - "3478:3478/udp"
    restart: always
    environment:
      - DERP_CERT_MODE=manual
      - DERP_ADDR=:3477
      - DERP_VERIFY_CLIENTS=false
      - DERP_DOMAIN=demo.moe.ceo

如果是使用 Tailscale 的官方平台,则登录 Tailscale 进入 Access Controls 界面。为配置内容增加以下部分:

	"derpMap": {
		// OmitDefaultRegions 用来忽略官方的中继节点,一般自建后就看不上官方小水管了
		"OmitDefaultRegions": false,
		"Regions": {
			// 这里的 901 从 900 开始随便取数字
			"900": {
				// RegionID 和上面的相等
				"RegionID": 900,
				// RegionCode 简洁名字
				"RegionCode": "NAT",
				// RegionName 注释名字
				"RegionName": "nat.moe.ceo",
				"Nodes": [
					{
						// Name 保持 1不动
						"Name": "1",
						// 这个也和 RegionID 一样
						"RegionID": 900,
						// 域名
						"HostName": "nat.moe.ceo",
						// 端口号
						"DERPPort": 3477,
					},
				],
			},
		},
	},

当然我们使用的是 Headscale 私有端,必然要讲解一下 Headscale 上怎么配置。

Headscale 是可以通过两种方式来配置自定义 DERP 的。分别是在线 URL,通过JSON方式与本地YAML文件方式。Tailscale 官方使用的是前者。

我们可以打开 Headscale 的config.yaml文件。其中有关于derp配置的部分。其中有官方urls案例。毕竟 Headscale 是可以使用 Tailscale 官方公开的 DERP 节点的。其次就是derp.yaml文件路径的设置,把注释取消掉,在对应路径创建文件。

按照我docker-compose的目录映射来看,直接在config目录内创建derp.yaml文件即可。我的案例如下:

# /etc/headscale/derp.yaml
regions:
  900:
    regionid: 900
    regioncode: NAT 
    regionname: nat.moe.ceo
    nodes:
      - name: 1
        regionid: 900
        hostname: nat.moe.ceo
        ipv4: xx.xx.xx.xx
        stunport: 3478
        stunonly: false
        derpport: 3477

具体的参数看意思也知道了。至于 Headscale 配置文件中derp部分的其他内容请自行看注释。