Headscale官方的镜像大小使用docker images查看时发现只有 80M,如果我们自己使用 Dockerfile 打包镜像,发现会很大。

但是源码中给的Dockerfile文件都没法实现官方镜像的大小(毕竟是开发环境打包的),然后我就去找官方镜像是怎么打包的,在dockerhub里发现ko工具的身影,所以就尝试用ko打包headscale镜像。

ko 是一个用于构建和打包 Go 应用程序的轻量级工具,特别适用于容器化环境(如 DockerKubernetes),目标是简化 Go 程序构建为容器镜像的过程,无需编写 Dockerfile

ko 安装

我使用的 Ubuntu24.04 系统,在普通用户下安装了 go 程序。
使用 go 安装 ko

1
go install github.com/google/ko@latest

安装后,在用户目录下可以找到ko文件,我的电脑在 /home/Users/go/bin/ko 位置。

ko 打包

在源码目录下运行命令:

1
2
3
4
# --local 表示不推送镜像到仓库
/home/djc/go/bin/ko build --local ./cmd/headscale

# 打包完成后会出现 ko.local 开头的镜像
1
2
3
4
5
6
7
8
9
# 打包完成后,使用 docker images 即可查看镜像
djc@jetron-djc:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
headscale v0.26.1-r bf66da388ca1 6 days ago 87.5MB
ko.local/headscale-f40b3d8640713cd381403459ebd67e78 38aefca56cab7d9b11692c61968915fb59fdf1dce134e52fed02ae2fa3a0e871 bf66da388ca1 6 days ago 87.5MB
ko.local/headscale-f40b3d8640713cd381403459ebd67e78 latest bf66da388ca1 6 days ago 87.5MB
ghcr.io/juanfont/headscale v0.26.1 b9e7b75fd3b0 N/A 80.8MB

# 后面使用 docker tag 可重命名镜像名称

镜像运行

程序运行需要config.yaml配置文件,以及/var/run/headscale//var/lib/headscale/ 目录的读取写入权限。

1
2
3
4
5
6
7
8
# 可在 config.yaml 中将
unix_socket: /var/run/headscale/headscale.sock

# 修改成
unix_socket: /var/lib/headscale/headscale.sock

# 然后只需要挂载 /var/lib/headscale/ 目录即可运行
docker run -d -v .headscale/config.yaml:/etc/headscale/config.yaml -v ./doc:/var/lib/headscale --name headscale -p 8080:8080 -p 9090:9090 headscale:v0.26.1-r serve

参考

源码编译:https://ownding.com/2025/07/30/%E4%BD%BF%E7%94%A8Headscale%E6%BA%90%E7%A0%81%E8%87%AA%E8%A1%8C%E7%BC%96%E8%AF%91%E6%89%93%E5%8C%85/

方法介绍

有两种方法能够获取到EMQX的设备是否在线:

  • 使用 EMQX 提供的 REST API,通过API获取设备状态
  • 订阅EMQX的系统主题,当设备上线、下线时通过该主题都会收到设备上下线消息

订阅系统主题

EMQ X提供上下线状态主题:
上线主题:$SYS/brokers/<node>/clients/<clientid>/connected

下线主题:$SYS/brokers/<node>/clients/<clientid>/disconnected

1)其中 <node><clientid> 分别指定具体节点名、设备ClientID

2)支持 +#通配符

通配符模式主题: $SYS/brokers/+/clients/+/+

示例:

  • 1、配置ACL规则,可使用ip也可以用username,使得某个用户或IP能够订阅系统主题。

ACL

ACL1

  • 2、订阅主题。 我使用的是MQTTX工具进行订阅模拟。

sub

说明

本文说明在 Linux 系统下使用 tailscale 客户端 登入 http 协议的 headscale
Tailscale 客户端默认使用 https 协议。

  • Ubuntu 24.04 LTS
  • headscale v0.26.1
  • tailscale 客户端版本 1.86.2

tailscale客户端安装

1
2
3
4
5
6
7
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null

curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list

sudo apt-get update

sudo apt-get install tailscale

问题

linux(ubuntu 24.04)系统中安装了 tailscale 客户端,但是程序启动后,再登入自定的 headscale 服务端(如果是 http,没有ssl证书)会一直卡在登入环节。

Tailscale 客户端登入信息如下:

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
Aug 06 08:00:42 ding tailscaled[828]: Received error: register request: Post "https://124.232.182.156:8081/machine/register": connection attempts aborted by context: context deadline exceeded 
Aug 06 08:00:43 ding tailscaled[828]: control: doLogin(regen=false, hasUrl=false)
Aug 06 08:00:43 ding tailscaled[828]: control: control server key from http://124.232.182.156:8081: ts2021=[QNt10], legacy=
Aug 06 08:00:43 ding tailscaled[828]: control: RegisterReq: onode= node=[V20sD] fup=false nks=false
Aug 06 08:00:43 ding tailscaled[828]: control: controlhttp: forcing port 443 dial due to recent noise dial
Aug 06 08:00:53 ding tailscaled[828]: Received error: register request: Post "https://124.232.182.156:8081/machine/register": connection attempts aborted by context: context deadline exceeded
Aug 06 08:00:54 ding tailscaled[828]: control: doLogin(regen=false, hasUrl=false)
Aug 06 08:00:54 ding tailscaled[828]: control: control server key from http://124.232.182.156:8081: ts2021=[QNt10], legacy=
Aug 06 08:00:54 ding tailscaled[828]: control: RegisterReq: onode= node=[V20sD] fup=false nks=false
Aug 06 08:00:54 ding tailscaled[828]: control: controlhttp: forcing port 443 dial due to recent noise dial
Aug 06 08:01:04 ding tailscaled[828]: Received error: register request: Post "https://124.232.182.156:8081/machine/register": connection attempts aborted by context: context deadline exceeded
Aug 06 08:01:05 ding tailscaled[828]: control: doLogin(regen=false, hasUrl=false)
Aug 06 08:01:05 ding tailscaled[828]: control: control server key from http://124.232.182.156:8081: ts2021=[QNt10], legacy=
Aug 06 08:01:05 ding tailscaled[828]: control: RegisterReq: onode= node=[V20sD] fup=false nks=false
Aug 06 08:01:05 ding tailscaled[828]: control: controlhttp: forcing port 443 dial due to recent noise dial
Aug 06 08:01:15 ding tailscaled[828]: Received error: register request: Post "https://124.232.182.156:8081/machine/register": connection attempts aborted by context: context deadline exceeded
Aug 06 08:01:16 ding tailscaled[828]: control: doLogin(regen=false, hasUrl=false)
Aug 06 08:01:16 ding tailscaled[828]: control: control server key from http://124.232.182.156:8081: ts2021=[QNt10], legacy=
Aug 06 08:01:16 ding tailscaled[828]: control: RegisterReq: onode= node=[V20sD] fup=false nks=false
Aug 06 08:01:16 ding tailscaled[828]: control: controlhttp: forcing port 443 dial due to recent noise dial
Aug 06 08:01:24 ding kernel: workqueue: drain_vmap_area_work hogged CPU for >10000us 4 times, consider switching to WQ_UNBOUND
Aug 06 08:01:26 ding tailscaled[828]: Received error: register request: Post "https://124.232.182.156:8081/machine/register": connection attempts aborted by context: context deadline exceeded
Aug 06 08:01:28 ding tailscaled[828]: control: doLogin(regen=false, hasUrl=false)
Aug 06 08:01:28 ding tailscaled[828]: control: control server key from http://124.232.182.156:8081: ts2021=[QNt10], legacy=
Aug 06 08:01:28 ding tailscaled[828]: control: RegisterReq: onode= node=[V20sD] fup=false nks=false
Aug 06 08:01:28 ding tailscaled[828]: control: controlhttp: forcing port 443 dial due to recent noise dial
Aug 06 08:01:38 ding tailscaled[828]: Received error: register request: Post "https://124.232.182.156:8081/machine/register": connection attempts aborted by context: context deadline exceeded
Aug 06 08:01:40 ding tailscaled[828]: control: doLogin(regen=false, hasUrl=false)
Aug 06 08:01:40 ding tailscaled[828]: control: control server key from http://124.232.182.156:8081: ts2021=[QNt10], legacy=
Aug 06 08:01:40 ding tailscaled[828]: control: RegisterReq: onode= node=[V20sD] fup=false nks=false
Aug 06 08:01:40 ding tailscaled[828]: control: controlhttp: forcing port 443 dial due to recent noise dial
Aug 06 08:01:50 ding tailscaled[828]: Received error: register request: Post "https://124.232.182.156:8081/machine/register": connection attempts aborted by context: context deadline exceeded
Aug 06 08:01:52 ding tailscaled[828]: control: doLogin(regen=false, hasUrl=false)
Aug 06 08:01:52 ding tailscaled[828]: control: control server key from http://124.232.182.156:8081: ts2021=[QNt10], legacy=
Aug 06 08:01:52 ding tailscaled[828]: control: RegisterReq: onode= node=[V20sD] fup=false nks=false
Aug 06 08:01:52 ding tailscaled[828]: control: controlhttp: forcing port 443 dial due to recent noise dial

可以看到登入日志中出现了 https://124.232.182.156:8081tailscale 客户端会访问 https 的服务器,显然是无法访问成功的,因为服务器就没有配置 ssl

解决

删除 /var/lib/tailscale/tailscaled.state 文件,然后重新登入就可以了。

介绍

最近在做一个Headscale自动批准路由的功能,希望设备宣告路由后服务器端就能自动批准路由。经常一番查找,发现headscaleacl中支持autoApprovers,即当你配置你相关的acl时可以自动批准路由。
不过这还不能满足要求,因为我发现它不支持通配符,为了安全考虑只能每个路由配置一次。
本文会介绍如何在 headscale 源码中修改相关代码,让autoApprovers支持通配符路由批准设置(请确认你确实需要这个功能,可能带来安全问题)。

headscale 中的 autoApprovers 使用

具体配置及使用可参照官网链接:https://headscale.net/development/ref/routes/#automatically-approve-routes-of-a-subnet-router

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 示例配置
{
"tagOwners": {
"tag:router": ["alice@"]
},
"autoApprovers": {
"routes": {
"192.168.0.0/24": ["tag:router"]
}
},
"acls": [
// more rules
]
}

示例配置中 192.168.0.0/24tag:router 均会自动批准。

但是有一些设备或应用场景中希望所有的子路由均自动批准,那么现有的程序或 acl 配置无法满足要求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 我期望的配置,但是原版程序中无法实现
{
"tagOwners": {
"tag:router": ["alice@"]
},
"autoApprovers": {
"routes": {
"*": ["*"]
}
},
"acls": [
// more rules
]
}

源码修改

为了实现自动批准所有路由的功能,需要修改源码。
本文只介绍autoApprovers源码修改的一部分内容,代码不会全部公开。

  • 软件版本:v0.26.1

修改源码涉及的文件有 hscontrol/policy/v2/types.gohscontrol/policy/v2/policy.gohscontrol/policy/v1/acls_types.go 等。

其中 acls_types.go 中有 func (autoApprovers *AutoApprovers) GetRouteApprovers 函数,修改的示例代码:

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
// GetRouteApprovers returns the list of autoApproving users, groups or tags for a given IPPrefix.
func (autoApprovers *AutoApprovers) GetRouteApprovers(
prefix netip.Prefix,
) ([]string, error) {
if prefix.Bits() == 0 {
return autoApprovers.ExitNode, nil // 0.0.0.0/0, ::/0 or equivalent
}

approverAliases := make([]string, 0)

// Check for wildcard route approval first
// Wildcard "*" approves all non-exit routes
if wildcardApprovers, exists := autoApprovers.Routes["*"]; exists {
approverAliases = append(approverAliases, wildcardApprovers...)
}

for autoApprovedPrefix, autoApproverAliases := range autoApprovers.Routes {
// Skip wildcard entry as it's already handled above
if autoApprovedPrefix == "*" {
continue
}

autoApprovedPrefix, err := netip.ParsePrefix(autoApprovedPrefix)
if err != nil {
return nil, err
}

if prefix.Bits() >= autoApprovedPrefix.Bits() &&
autoApprovedPrefix.Contains(prefix.Masked().Addr()) {
approverAliases = append(approverAliases, autoApproverAliases...)
}
}

return approverAliases, nil
}

以上只是示例,具体修改还需要结合版本及需求进行。

说明

本文介绍如何自行打包编译 Headscale 源码,以及在编译源码的过程中可能出现的问题以及解决方法。

  • 操作系统: Windows 11 家庭中文版;WSL 2 Ubuntu24.04 LTS 系统。 本次编译源码使用了 wsl 2安装的 Ubutnu 24系统。
  • 源码目录: C:\Users\XXX\Documents\develop\0me\headscale 。 源码放在 Winodws系统中。
  • 源码下载: https://github.com/juanfont/headscale
  • 程序版本: v0.26.1

环境准备

代码下载

推荐使用 git 下载,而不是 下载 压缩包的源码。我使用压缩包的源码进行编译的时候会出现很多错误,最后是通过go build -o headscale ./cmd/headscale 编译了一个可执行文件,但是无法通过 make build 编译。

使用下面的命令下载源码:

1
2
3
4
git clone https://github.com/juanfont/headscale.git

# 使用 v0.26.1 的 tag
git checkout -b release-v0.26.1 v0.26.1

然后复制根目录下的 config-example.yaml 重命名为 config.yaml

WSL

开启 WSL,并且安装 Ubuntu系统。

在当前计算机用户目录下,比如 C:\Users\XXX ,创建一个 .wslconfig 的文件,里面填入:

1
2
3
4
5
6
7
8
9
[wsl2]
nestedVirtualization=true
ipv6=true
[experimental]
autoMemoryReclaim=gradual # gradual | dropcache | disabled
networkingMode=mirrored
dnsTunneling=true
firewall=true
autoProxy=true

然后重启 wsl,这样 Ubuntu 系统就可以使用你本机的VPN网络了。 因为编译时需要下载一堆东西,没有外网VPN无法下载成功。

Nix安装

使用 root 账号安装 nixMulti-user 版本。

1
sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --daemon

参考链接:https://nixos.org/download/

编译

编译时请从 root 账号切换到普通账号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 进入目录。windows电脑的目录在 wsl系统中 /mnt/c/ 目录下
cd /mnt/c/Users/XXX/Documents/develop/0me/headscale

# 准备环境。
nix develop

make generate
make test
make build

# 查看结果
ls -la result/

# 检查版本
cd ./result/bin
./headscale version # 会输出版本号

环境准备需要挺久,耐心等待。

make build 执行成功后,目录中会出现一个 result 目录(在windows系统中查看就是一个 0kb 大小的文件,实际在linux中是一个目录。

r
wr

运行

在开发机器上编译的 headscale 文件可直接运行,下面介绍将 headscale 文件复制到其它服务器上运行。

  • 运行服务器: Ubuntu 22.04 Server
1
2
3
4
5
6
7
8
9
10
# 检查文件 使用 ldd 或 file 命令
ubuntu@VM-16-7-ubuntu:~/doc/headscale$ ldd headscale
linux-vdso.so.1 (0x00007ffc991a1000)
libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x000079e2a91e5000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x000079e2a91e0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000079e2a8e00000)
/nix/store/vbrdc5wgzn0w1zdp10xd2favkjn5fk7y-glibc-2.40-66/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x000079e2a9201000)
ubuntu@VM-16-7-ubuntu:~/doc/headscale$ file headscale
headscale: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /nix/store/vbrdc5wgzn0w1zdp10xd2favkjn5fk7y-glibc-2.40-66/lib/ld-linux-x86-64.so.2, stripped
ubuntu@VM-16-7-ubuntu:~/doc/headscale$

从上面控制台输出我们可以发现,headscale 运行时需要 /nix/store/vbrdc5wgzn0w1zdp10xd2favkjn5fk7y-glibc-2.40-66/lib/ld-linux-x86-64.so.2 ,该文件其实是链接的 /lib64/ld-linux-x86-64.so.2 ,要想 程序正常运行,需要手动创建 /nix/store/vbrdc5wgzn0w1zdp10xd2favkjn5fk7y-glibc-2.40-66/lib/ 目录,并且将 ld-linux-x86-64.so.2 文件复制到刚创建的文件夹中。

此外,需要 config.yaml 文件,以及创建 /root/.headscale//varlib/headscale/ 并且需要给这些目录赋予权限。

1
2
3
4
5
6
7
8
9
10
11
12
# 启动命令 headscale serve

ubuntu@VM-16-7-ubuntu:~/doc/headscale$ sudo ./headscale serve
2025-08-05T17:20:01+08:00 INF Opening database database=sqlite3 path=/var/lib/headscale/db.sqlite
2025-08-05T17:20:01+08:00 INF Using policy manager version: 2
2025-08-05T17:20:01+08:00 INF Starting Headscale commit=6b6daf389bd11624c4036de525740a0568d5f72f-dirty version=6b6daf3-dirty
2025-08-05T17:20:01+08:00 INF Clients with a lower minimum version will be rejected minimum_version=v1.62.0
2025-08-05T17:20:01+08:00 INF github.com/juanfont/headscale/hscontrol/derp/server/derp_server.go:106 > DERP region: {RegionID:999 RegionCode:headscale RegionName:Headscale Embedded DERP Latitude:0 Longitude:0 Avoid:false NoMeasureNoHome:false Nodes:[0xc00051a090]}
2025-08-05T17:20:01+08:00 INF github.com/juanfont/headscale/hscontrol/derp/server/derp_server.go:107 > DERP Nodes[0]: &{Name:999 RegionID:999 HostName:124.232.181.156 CertName: IPv4:124.232.181.156 IPv6:2406:da18:d4c:c000:8d2b:1775:f73f:7c2f STUNPort:3478 STUNOnly:false DERPPort:8081 InsecureForTests:false STUNTestIP: CanPort80:false}
2025-08-05T17:20:01+08:00 INF STUN server started at [::]:3478
2025-08-05T17:20:01+08:00 INF listening and serving HTTP on: 0.0.0.0:8080
2025-08-05T17:20:01+08:00 INF listening and serving debug and metrics on: 0.0.0.0:9090

打包成镜像

如果你想将源码打包成 docker 镜像,可以参考下面的链接:

https://ownding.com/2025/08/08/headscale%E7%B3%BB%E5%88%97%EF%BC%9A%E5%A6%82%E4%BD%95%E5%B0%86%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E7%9A%84headscale%E6%89%93%E5%8C%85%E6%88%90docker%E9%95%9C%E5%83%8F/

编译异常汇总

以下编译的异常情况都是我使用 压缩的源代码编译出现的。使用 git 版本的源码编译没有如下问题。

问题1

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
# 按照官方的编译步骤,使用 nix 编译。 make test 编译成功,但是 make build 出现如下错误:
djc@jetron-djc:/mnt/c/Users/DJC/Documents/develop/0me/headscale-0.26.1$ make build
nix build
error:
while calling the 'derivationStrict' builtin
at <nix/derivation-internal.nix>:37:12:
36|
37| strict = derivationStrict drvAttrs;
| ^
38|

while evaluating the derivation attribute 'name'
at /nix/store/qmm7hgw60vp7vj9lma95hl329d0j3n6n-source/pkgs/stdenv/generic/make-derivation.nix:438:13:
437| // (optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
438| name =
| ^
439| let

(stack trace truncated; use '--show-trace' to show the full, detailed trace)

error: attribute 'dirtyShortRev' missing
at /nix/store/wrx2gzxp6f5sdha4kswnpn6j8sqmfbnk-source/flake.nix:15:41:
14| }: let
15| headscaleVersion = self.shortRev or self.dirtyShortRev;
| ^
16| commitHash = self.rev or self.dirtyRev;
make: *** [Makefile:20: build] Error 1
  • 解决方法:

编辑 flake.nix 文件,

headscaleVersion = self.shortRev or self.dirtyShortRev; 修改成:

1
2
3
4
5
headscaleVersion = if self ? shortRev 
then self.shortRev
else if self ? dirtyShortRev
then self.dirtyShortRev
else "v0.26.1";

问题2

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
djc@jetron-djc:/mnt/c/Users/DJC/Documents/develop/0me/headscale-0.26.1$ make build
nix build
warning: Git tree '/mnt/c/Users/DJC/Documents/develop/0me/headscale-0.26.1' is dirty
error: Cannot build '/nix/store/x0151wjd71c3icbvqrjrhmah4039cxrc-headscale-1d8abba-dirty.drv'.
Reason: builder failed with exit code 1.
Output paths:
/nix/store/w1kqv2j36dsixf5pm4v5lwf2wiwkrkhz-headscale-1d8abba-dirty
Last 25 log lines:
> Running phase: buildPhase
> Building subPackage ./cmd/headscale
> buildPhase completed in 52 seconds
> Running phase: checkPhase
>
> ----------------------------------------------------------------------
> FAIL: headscale_test.go:29: Suite.TestConfigFileLoading
>
> headscale_test.go:54:
> c.Assert(err, check.IsNil)
> ... value *fmt.wrapError = &fmt.wrapError{msg:"fatal error reading config file: open /build/headscale246087243/config.yaml: no such file or directory", err:(*fs.PathError)(0xc0003dfcb0)} ("fatal error reading config file: open /build/headscale246087243/config.yaml: no such file or directory")
>
>
> ----------------------------------------------------------------------
> FAIL: headscale_test.go:73: Suite.TestConfigLoading
>
> headscale_test.go:96:
> c.Assert(err, check.IsNil)
> ... value *fmt.wrapError = &fmt.wrapError{msg:"fatal error reading config file: Config File \"config\" Not Found in \"[/build/headscale4241716010]\"", err:viper.ConfigFileNotFoundError{name:"config", locations:"[/build/headscale4241716010]"}} ("fatal error reading config file: Config File \"config\" Not Found in \"[/build/headscale4241716010]\"")
>
> OOPS: 0 passed, 2 FAILED
> --- FAIL: Test (0.00s)
> FAIL
> FAIL github.com/juanfont/headscale/cmd/headscale 0.020s
> FAIL
For full logs, run:
nix log /nix/store/x0151wjd71c3icbvqrjrhmah4039cxrc-headscale-1d8abba-dirty.drv
make: *** [Makefile:20: build] Error 1

这个错误是因为测试阶段失败导致构建中断。测试失败的原因是找不到配置文件。

直接构建二进制文件:

1
2
3
cp config-example.yaml config.yaml
go mod tidy
go build -o headscale ./cmd/headscale

编译成功后,在项目目录下有个 headscale 文件,大概80多M。

问题3

1
2
root@jetron-djc:~# nix develop
error: experimental Nix feature 'nix-command' is disabled; add '--extra-experimental-features nix-command' to enable it

这个错误是因为 Nix 的 nix-command 功能是实验性的,默认被禁用了。

1
2
# 编辑配置文件
echo 'experimental-features = nix-command flakes' >> /etc/nix/nix.conf

使用 root 用户执行上面的命令即可。

问题4

1
2
3
root@jetron-djc:~# nix develop
path '/root' does not contain a 'flake.nix', searching up
error: could not find a flake.nix file

这个错误是因为 nix develop 命令需要在包含 flake.nix 文件的目录中运行,请 cd 到你项目的目录执行 nix develop

问题5

1
2
3
4
=== Failed
=== FAIL: hscontrol/db TestConstraints/no-duplicate-username-if-no-oidc-postgres (0.05s)
db_test.go:404: start postgres: initdb: initdb: error: cannot be run as root
initdb: hint: Please log in (using, e.g., "su") as the (unprivileged) user that will own the server process.

这个错误是因为 PostgreSQLinitdb 命令不能以 root 用户身份运行,这是出于安全考虑。测试代码试图以 root 用户初始化数据库,但被拒绝了。请使用普通用户 执行make build 命令。

手动安装相关依赖

1
2
3
4
5
6
# 安装 Buf
go install github.com/bufbuild/buf/cmd/buf@v1.55.1

# 安装 Protobuf
sudo apt update
sudo apt install protobuf-compiler

说明

本文介绍 使用EMQX作为 MQTT Server时,如何连接多个数据源(认证链)进行设备认证。

为了使用方便,均使用 docker 启动 EMQX ,并且在启动时配置参数自动进行数据连接。

介绍了 EMQX 中各种环境变量在 docker compose 中的写法。

MySQL认证

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
version: '3'
services:
emqx:
image: emqx/emqx:4.3.5
container_name: docker_emqx
ports:
- "18083:18083"
- "1883:1883"
- "4369:4369"
- "8883:8883"
- "8085:8084"
- "8081:8081"
- "8083:8083"
volumes:
- /home/yxin/Documents/emqx/emqx-cert:/opt/emqx/etc/certs
- /home/yxin/Documents/emqx/emqx.conf:/opt/emqx/etc/emqx.conf
restart: always
environment:
- "EMQX_ALLOW_ANONYMOUS=false"
- "EMQX_ACL_NOMATCH=deny"
- "EMQX_AUTH__MYSQL__SERVER=172.16.33.51:3306"
- "EMQX_AUTH__MYSQL__POOL=8"
- "EMQX_AUTH__MYSQL__USERNAME=root"
- "EMQX_AUTH__MYSQL__PASSWORD=MeRootSSSS"
- "EMQX_AUTH__MYSQL__DATABASE=mlic"
- "EMQX_AUTH__MYSQL__AUTH__MYSQL__QUERY_TIMEOUT=5"
- "EMQX_AUTH__MYSQL__AUTH_QUERY=select password from mqtt_user where username = '%u' union all select password from mqtt_user2 where username = '%u' limit 1"
- "EMQX_LOADED_PLUGINS=emqx_recon | emqx_retainer | emqx_rule_engine | emqx_management | emqx_dashboard | emqx_auth_mysql"

其中

1
select password from mqtt_user where username = '%u' union all select password from mqtt_user2 where username = '%u' limit 1"

经测试可用,可在多表中进行设备认证。

PostgreSQL认证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: '3'
services:
emqx:
image: emqx/emqx:4.4.15
container_name: docker_emqx
ports:
- "18083:18083"
- "1888:1883"
- "4369:4369"
- "8883:8883"
restart: always
environment:
- "EMQX_ALLOW_ANONYMOUS=false"
- "EMQX_ACL_NOMATCH=deny"
- "EMQX_AUTH__PGSQL__SERVER=172.16.33.58:5432"
- "EMQX_AUTH__PGSQL__POOL=8"
- "EMQX_AUTH__PGSQL__USERNAME=postgres"
- "EMQX_AUTH__PGSQL__PASSWORD=PgSQLmmmmmm"
- "EMQX_AUTH__PGSQL__DATABASE=timescale"
# - "EMQX_AUTH__PGSQL__AUTH__PGSQL__QUERY_TIMEOUT=5"
- "EMQX_LOADED_PLUGINS=emqx_recon | emqx_retainer | emqx_rule_engine | emqx_management | emqx_dashboard | emqx_auth_pgsql"

认证链

可同时使用 MySQL以及Postgresql认证。其中有一个认证通过即认证通过,不过如果两个数据库中有相同用户名,密码不同,当一个认证失败后就会返回认证失败。

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
version: '3'
services:
emqx:
image: emqx/emqx:4.4.15
container_name: docker_emqx_auth
ports:
- "18085:18083"
- "1889:1883"
# - "4369:4369"
# - "8883:8883"
restart: always
# 使用host模式会导致emqx启动失败
# network_mode: bridge
# external_links:
# - mysql
environment:
- "EMQX_ALLOW_ANONYMOUS=false"
- "EMQX_ACL_NOMATCH=deny"
- "EMQX_AUTH__PGSQL__SERVER=172.16.33.58:5432"
- "EMQX_AUTH__PGSQL__POOL=8"
- "EMQX_AUTH__PGSQL__USERNAME=postgres"
- "EMQX_AUTH__PGSQL__PASSWORD=JDDDDDDDDt"
- "EMQX_AUTH__PGSQL__DATABASE=timescale"
# - "EMQX_AUTH__PGSQL__AUTH__PGSQL__QUERY_TIMEOUT=5"
- "EMQX_AUTH__MYSQL__SERVER=172.16.33.62:3306"
- "EMQX_AUTH__MYSQL__POOL=8"
- "EMQX_AUTH__MYSQL__USERNAME=root"
- "EMQX_AUTH__MYSQL__PASSWORD=MVVVVVVVVne"
- "EMQX_AUTH__MYSQL__DATABASE=iform"
- "EMQX_AUTH__MYSQL__AUTH__MYSQL__QUERY_TIMEOUT=5"
- "EMQX_LOADED_PLUGINS=emqx_recon | emqx_retainer | emqx_rule_engine | emqx_management | emqx_dashboard | emqx_auth_pgsql | emqx_auth_mysql"
# networks:
# default:
# external:
# name: net-5g

Headscale使用自定义证书

在一些嵌入式设备中没有安装根证书,并且更新根证书比较麻烦,此时可以使用长时间的自定义证书。

生成自定义证书(特别是自签名证书)虽然技术上可行,但需要注意安全性和信任问题。自签名证书不会被浏览器或操作系统信任,会导致安全警告。以下是生成30年有效期证书的步骤和注意事项:


1. 生成证书的步骤

使用 OpenSSL 工具生成自签名证书(支持 SAN 扩展,兼容现代浏览器):

步骤 1:安装 OpenSSL

1
2
3
4
5
# Ubuntu/Debian
sudo apt-get install openssl

# CentOS/RHEL
sudo yum install openssl

步骤 2:创建配置文件 openssl.cnf

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
[ req ]
default_bits = 4096
default_keyfile = example.com.key
distinguished_name = req_distinguished_name
req_extensions = req_ext
x509_extensions = v3_ca
prompt = no

[ req_distinguished_name ]
C = CN # 国家代码
ST = Shanghai # 省份
L = Shanghai # 城市
O = example.com Inc # 组织名称
OU = IT # 组织单位
CN = www.example.com

[ req_ext ]
subjectAltName = @alt_names

[ v3_ca ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = www.example.com
DNS.2 = example.com # 可选:添加其他域名

步骤 3:生成私钥和证书

1
2
openssl req -x509 -new -nodes -keyout example.com.key -sha256 \
-days 10950 -out example.com.crt -config openssl.cnf
  • -days 10950:30年(30×365)
  • 输出文件:example.com.key(私钥)、example.com.crt(证书)

3. 验证证书

1
openssl x509 -in example.com.crt -text -noout

检查有效期(Validity)和域名(Subject Alternative Name)。


4. 部署证书

  • Web 服务器:将 example.com.keyexample.com.crt 配置到 Nginx/Apache。
  • 强制 HTTPS:确保服务器配置了 HTTPS 重定向。

5. 客户端信任证书

Headscale 使用自定义证书时,Tailscale 连接登入会出现如下错误:Received error: fetch control key: Get "https://www.example.com/key?v=116": x509: certificate signed by unknown authority。这是因为Tailscale 客户端默认要求服务器证书由受信任的证书颁发机构(CA)签发,例如 Let’s Encrypt。如果使用自签名证书或非 Let’s Encrypt 的证书,客户端会因无法验证证书链而拒绝连接。

步骤 1:确认证书安装位置

  1. 以管理员身份运行证书管理器

    • Win + R,输入 mmc,回车。
    • 点击 文件 → 添加/删除管理单元
    • 添加 证书 管理单元,选择 计算机账户 → 本地计算机
  2. 检查证书是否已安装到 受信任的根证书颁发机构

    • 在左侧导航栏展开 证书(本地计算机)受信任的根证书颁发机构证书
    • 确认列表中存在 www.example.com 的证书(颁发者和使用者均为 example.com Inc)。
  3. 若未找到证书,重新安装

    • 右键 证书 → 所有任务 → 导入。
    • 使用向导导入 example.com.crt,**在向导最后一步选择存储位置为 受信任的根证书颁发机构**。

注意事项

  1. 证书安装位置错误
    Windows 系统有多个证书存储区(如 受信任的根证书颁发机构本地计算机当前用户)。

    • 必须 将自签名证书安装到 受信任的根证书颁发机构(Trusted Root Certification Authorities)
    • 若安装到其他位置(如 个人证书当前用户 存储),系统级服务(如 Tailscale 客户端)将无法信任该证书。
  2. 证书未正确应用于系统级
    Windows 的证书管理分为 本地计算机当前用户

    • 如果以普通用户身份安装证书,仅当前用户有效,系统服务(如 Tailscale)可能无法访问。
    • 需要以管理员权限运行证书管理工具,将证书安装到 本地计算机受信任的根证书颁发机构
  3. 证书文件内容问题

    • 证书文件可能包含多个证书(如中间证书链),导致解析失败。
    • 证书文件可能不是 PEM 格式(需确保内容以 -----BEGIN CERTIFICATE----- 开头)。

介绍

介绍如何使用 GOWVP 以及 EasyGBD 进行 GB28181服务器的搭建和测试。

由于要将本地萤石NVR的视频信息推送到云端服务器,再查看了该NVR支持的协议后,决定采用GB28181。为了省钱,在网上找了很多开源的GB28181服务器以及模拟客户端,比如 wvp-gb28181-pro 等,但是折腾半天就是看不到画面。没办法只好换个软件,最后选定 GOWVPEasyGBDGOWVP 是服务端,EasyGBD模拟的客户端。

安装

EasyGBD

下载地址:https://github.com/EasyDarwin/EasyGBD

下载后,win/bin 目录下的 EasyGBD_Demo.exe 可直接运行使用。

EasyGBD

GOWVP

推荐使用 dokcer 进行安装,比较方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
services:
gowvp:
# 如果拉不到 docker hub 镜像,也可以尝试
# registry.cn-shanghai.aliyuncs.com/ixugo/homenvr:latest
image: registry.cn-shanghai.aliyuncs.com/ixugo/homenvr:latest
# linux 解开下行注释,并将 ports 全部注释
# network_mode: host
ports:
# gb28181
- 15123:15123 # 管理平台 http 端口
- 15060:15060 # gb28181 sip tcp 端口
- 15060:15060/udp # gb28181 sip udp 端口
# zlm
- 1935:1935 # rtmp
- 554:554 # rtsp
- 8080:80 # http
- 8443:443 # https
- 10000:10000
- 8000:8000/udp
- 9000:9000/udp
- 20000-20100:20000-20100 # gb28181 收流端口
- 20000-20100:20000-20100/udp # gb28181 收流端口udp
volumes:
- ./data:/opt/media/bin/configs

使用 docker-compose 启动程序后,会在 当前 docker-compose.yaml 文件夹中自动生成 data 文件夹,里面放着zlmeidakit等配置文件。

文件夹

然后进入到 data 目录,找到 zlm.ini ,修改里面 rtp_proxy.port_range20000-20100 ,跟 docker-compose.yaml 里暴露的端口一致。

端口

注意 如果是内网安装的话,为了测试方便可以把防火墙关掉。 如果是公网部署,那么需要开放 20000-20100/udp20000-20100/tcp15060/udp/tcp 方便设备注册推流。

设置

当程序启动后,访问服务器 15123 端口,登入到系统。

ZLMediakit 设置

登入界面后,需要查看 zlmediakit 是否正常,如果是绿色闪烁圆点,那么表示系统正常;如果是红色圆点表示不正常。

同时需要修改 zlmediakit 的配置,点击右上角齿轮图表,修改 国标收流默认地址 ,同时填上 secret keysecret keydata 目录下 zlm.ini 文件里。

setting

setting

设置完成后,重启gowvp容器即可。

连接测试

1、打开 EasyGBD_Demo.exe 程序

2、访问系统,国标通道–接入信息 查看 国标ID、国标域、端口号、密码等。

gbsetting

3、修改 EasyGBD_Demo.exeIP,点击 启动。 注册成功后,就可以在系统界面看到注册的客户端信息。

注册

4、点击播放会推送视频

播放

EasyGBD_Demo.exe 界面会显示 推送关键帧

推送

其中,gowvp系统播放界面中有 rtmprtsp等地址,可以使用vlc播放器进行串流播放。

注意 客户端的对应端口需要开通,比如 EasyGBD_Demo.exe 客户端是 15090,这个端口要在防火墙开放,不然可能导致视频无法播放。

问题

错误描述:在使用 Kubernetes(k8s) 部署mysql有状态服务时,报 : changing ownership of "var/lib/mysql" Operation not permitted

解决方法

1、在nfs服务器,修改 vi /etc/exports ,添加 no_root_squash 参数

示例

2、重启 nfs 服务, service nfs restart

背景

最近在开发一个 Headscale 控制管理平台用来管理用户创建、设备管理等等。但是当我试用了 Headscale 官网推荐的几个 UI 程序后,发现它们离我预期的功能还相差很多。我试用的几个 Headscale UI 程序,都使用 REST APIHeadscale 进行通信,但是我遇到一个问题,在高版本 Headscale(0.25.1及以上) 中,REST API 不能创建带 NamespacedisplayName 的用户。

所以我就通过搜索引擎查找关于 Headscale REST API 创建 带租户的用户名,发现了以下两个链接:

知道, Headscale 提供 gRPC API 提供 用户 displayName 创建。不过互联网上关于 Headscale gRPC API 的资料少之又少,跟别提 demo 程序了。

一番检索之后,我发现只能自己来完成这项工作了,并且在程序开发完成后,将 Headscale 一部分管理控制功能开源出来。

开发

知道使用 gRPC API 可以创建带 displayName 的用户后,后面的开发就简单多了,是的,全部交给 AI 来完成。

不过 AI 的知识库没有那么多新内容,在处理 gRPC 返回格式时一直出现错误。此时我们需要把 HeadscaleGithub 相关链接告诉它,并且提供 gRPC 返回的数据格式,AI 就顺利的完成了整个代码编写。 后续我自己进行相关测试即可。

开源

由于关于 Headscale gRPC API 功能的说明文档以及示例代码很少,所以我就准备将这部分开源出来。

开源的程序有 两套,一套是基于 headscale-admin 添加的 gRPC 功能,这是一个纯 NodeJS 项目;另一套是 Java 程序,纯后端,没有前端配套界面。

0%