feat: operator trust root: passkey-signed operator set, signed join codes, nodes verify and pin the chain at enrollment [CI SKIP]

This commit is contained in:
justme committed 2026-10-07 23:19:59 +08:00
1 parent 321cacac32
commit fc2bbdcc6d
35 files changed
+1374 -40

No files matched your search

@@ -20,9 +20,42 @@ public static class FleetEnrollmentEndpoints
.WithTags("agent")
.WithSummary("Enroll a node with a join token")
.DisableAntiforgery();
// 注册之前取信任链:节点先确认这个面板的操作员集合和委托确实是它固定的创世签出来的,再把自己的密钥交出去
app.MapPost(path + "/trust", HandleTrustAsync)
.WithName("fleet_enroll_trust")
.WithTags("agent")
.WithSummary("The operator set chain and the grant of a join token, for the node to verify before it enrolls")
.DisableAntiforgery();
return app;
}
private static async Task<IResult> HandleTrustAsync(HttpContext http, FleetEnrollmentService service)
{
TrustRequest? request;
try
{
request = await JsonSerializer.DeserializeAsync(http.Request.Body, EnrollmentJsonContext.Default.TrustRequest, http.RequestAborted);
}
catch (JsonException)
{
request = null;
}
if (request is null || string.IsNullOrEmpty(request.JoinToken))
{
return Problem(StatusCodes.Status400BadRequest, FleetErrors.InvalidParams, "join_token is required");
}
Result<TrustBundle> result = await service.GetTrustAsync(request.JoinToken, http.RequestAborted);
if (!result.TryGet(out TrustBundle? bundle, out FleetError? error))
{
return Problem(error.Code == FleetErrors.JoinTokenExpired ? StatusCodes.Status410Gone : StatusCodes.Status401Unauthorized, error.Code, error.Message);
}
http.Response.Headers.CacheControl = "no-store";
return Results.Json(bundle, EnrollmentJsonContext.Default.TrustBundle);
}
private static async Task<IResult> HandleAsync(HttpContext http, FleetEnrollmentService service)
{
EnrollRequest? request;