From b57681f2ad157ff526fd1bf3dc8556cd3f76ac58 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Thu, 22 Jan 2026 11:11:07 +0100 Subject: [PATCH] Provide reason field for retrieving VPN auth key. --- cmd/metal-api/internal/service/v1/vpn.go | 1 + cmd/metal-api/internal/service/vpn-service.go | 8 ++++++++ cmd/metal-api/main.go | 2 +- spec/metal-api.json | 7 ++++++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/cmd/metal-api/internal/service/v1/vpn.go b/cmd/metal-api/internal/service/v1/vpn.go index d89b1d18f..1a6472b6b 100644 --- a/cmd/metal-api/internal/service/v1/vpn.go +++ b/cmd/metal-api/internal/service/v1/vpn.go @@ -11,4 +11,5 @@ type VPNRequest struct { Pid string `json:"pid" description:"project ID"` Ephemeral bool `json:"ephemeral" description:"specifies if auth key should be ephemeral"` Expiration *time.Duration `json:"expiration" description:"expiration time" optional:"true"` + Reason string `json:"reason" description:"reason why the consolepassword is requested, typically a incident number with short description"` } diff --git a/cmd/metal-api/internal/service/vpn-service.go b/cmd/metal-api/internal/service/vpn-service.go index a1584f7c3..ce2dc10d5 100644 --- a/cmd/metal-api/internal/service/vpn-service.go +++ b/cmd/metal-api/internal/service/vpn-service.go @@ -23,18 +23,21 @@ import ( type vpnResource struct { webResource headscaleClient *headscale.HeadscaleClient + reasonMinLength uint } // NewVPN returns a webservice for VPN specific endpoints. func NewVPN( log *slog.Logger, headscaleClient *headscale.HeadscaleClient, + reasonMinLength uint, ) *restful.WebService { r := vpnResource{ webResource: webResource{ log: log, }, headscaleClient: headscaleClient, + reasonMinLength: reasonMinLength, } return r.webService() @@ -74,6 +77,11 @@ func (r *vpnResource) getVPNAuthKey(request *restful.Request, response *restful. return } + if uint(len(requestPayload.Reason)) < r.reasonMinLength { + r.sendError(request, response, httperrors.BadRequest(fmt.Errorf("reason must be at least %d characters long", r.reasonMinLength))) + return + } + pid := requestPayload.Pid if ok := r.headscaleClient.UserExists(request.Request.Context(), pid); !ok { r.sendError( diff --git a/cmd/metal-api/main.go b/cmd/metal-api/main.go index 13c2442e3..94856043b 100644 --- a/cmd/metal-api/main.go +++ b/cmd/metal-api/main.go @@ -778,7 +778,7 @@ func initRestServices(searchAuditBackend auditing.Auditing, allAuditBackends []a restful.DefaultContainer.Add(service.NewFilesystemLayout(logger.WithGroup("filesystem-layout-service"), ds)) restful.DefaultContainer.Add(service.NewSwitch(logger.WithGroup("switch-service"), ds)) restful.DefaultContainer.Add(healthService) - restful.DefaultContainer.Add(service.NewVPN(logger.WithGroup("vpn-service"), headscaleClient)) + restful.DefaultContainer.Add(service.NewVPN(logger.WithGroup("vpn-service"), headscaleClient, reasonMinLength)) restful.DefaultContainer.Add(rest.NewVersion(moduleName, &rest.VersionOpts{ BasePath: service.BasePath, MinClientVersion: minClientVersion.Original(), diff --git a/spec/metal-api.json b/spec/metal-api.json index 480ac1baa..d073d62d0 100644 --- a/spec/metal-api.json +++ b/spec/metal-api.json @@ -5891,11 +5891,16 @@ "pid": { "description": "project ID", "type": "string" + }, + "reason": { + "description": "reason why the consolepassword is requested, typically a incident number with short description", + "type": "string" } }, "required": [ "ephemeral", - "pid" + "pid", + "reason" ] }, "v1.VPNResponse": {