hscontrol, cli: add auth register and approve commands

Implement AuthRegister and AuthApprove gRPC handlers and add
corresponding CLI commands (headscale auth register, approve, reject)
for managing pending auth requests including SSH check approvals.

Updates #1850
This commit is contained in:
Kristoffer Dalby
2026-02-24 18:51:11 +00:00
parent 61a14bb0e4
commit 48cc98b787
4 changed files with 153 additions and 4 deletions

View File

@@ -856,4 +856,59 @@ func (api headscaleV1APIServer) Health(
return response, healthErr
}
func (api headscaleV1APIServer) AuthRegister(
ctx context.Context,
request *v1.AuthRegisterRequest,
) (*v1.AuthRegisterResponse, error) {
resp, err := api.RegisterNode(ctx, &v1.RegisterNodeRequest{
Key: request.GetAuthId(),
User: request.GetUser(),
})
if err != nil {
return nil, err
}
return &v1.AuthRegisterResponse{Node: resp.GetNode()}, nil
}
func (api headscaleV1APIServer) AuthApprove(
ctx context.Context,
request *v1.AuthApproveRequest,
) (*v1.AuthApproveResponse, error) {
authID, err := types.AuthIDFromString(request.GetAuthId())
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid auth_id: %v", err)
}
authReq, ok := api.h.state.GetAuthCacheEntry(authID)
if !ok {
return nil, status.Errorf(codes.NotFound, "no pending auth session for auth_id %s", authID)
}
authReq.FinishAuth(types.AuthVerdict{})
return &v1.AuthApproveResponse{}, nil
}
func (api headscaleV1APIServer) AuthReject(
ctx context.Context,
request *v1.AuthRejectRequest,
) (*v1.AuthRejectResponse, error) {
authID, err := types.AuthIDFromString(request.GetAuthId())
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid auth_id: %v", err)
}
authReq, ok := api.h.state.GetAuthCacheEntry(authID)
if !ok {
return nil, status.Errorf(codes.NotFound, "no pending auth session for auth_id %s", authID)
}
authReq.FinishAuth(types.AuthVerdict{
Err: fmt.Errorf("auth request rejected"),
})
return &v1.AuthRejectResponse{}, nil
}
func (api headscaleV1APIServer) mustEmbedUnimplementedHeadscaleServiceServer() {}

View File

@@ -282,7 +282,7 @@ func (a *AuthProviderWeb) AuthHandler(
}
func authIDFromRequest(req *http.Request) (types.AuthID, error) {
registrationId, err := urlParam[types.AuthID](req, "auth_id")
raw, err := urlParam[string](req, "auth_id")
if err != nil {
return "", NewHTTPError(http.StatusBadRequest, "invalid registration id", fmt.Errorf("parsing auth_id from URL: %w", err))
}
@@ -290,7 +290,7 @@ func authIDFromRequest(req *http.Request) (types.AuthID, error) {
// We need to make sure we dont open for XSS style injections, if the parameter that
// is passed as a key is not parsable/validated as a NodePublic key, then fail to render
// the template and log an error.
err = registrationId.Validate()
registrationId, err := types.AuthIDFromString(raw)
if err != nil {
return "", NewHTTPError(http.StatusBadRequest, "invalid registration id", fmt.Errorf("parsing auth_id from URL: %w", err))
}