headscale自动批准路由以及如何修改源码支持通配符批准路由

介绍

最近在做一个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
}

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