diff --git a/server/fleet/capabilities.go b/server/fleet/capabilities.go index 44ce9c2a46..2d345d2595 100644 --- a/server/fleet/capabilities.go +++ b/server/fleet/capabilities.go @@ -94,6 +94,9 @@ const ( // CapabilityMacOSWebSetupExperience denotes the ability of the server to support // a web-based setup experience UI for macOS devices CapabilityMacOSWebSetupExperience Capability = "macos_web_setup_experience" + // CapabilityEndUserAuth denotes the ability of the client to authenticate + // the end user against the Fleet server (e.g. SSO) before enrolling + CapabilityEndUserAuth Capability = "end_user_auth" ) func GetServerOrbitCapabilities() CapabilityMap { @@ -121,6 +124,7 @@ func GetOrbitClientCapabilities() CapabilityMap { return CapabilityMap{ CapabilityEscrowBuddy: {}, CapabilitySetupExperience: {}, + CapabilityEndUserAuth: {}, } } diff --git a/server/service/endpoint_utils.go b/server/service/endpoint_utils.go index a7e91f025f..bc225dff61 100644 --- a/server/service/endpoint_utils.go +++ b/server/service/endpoint_utils.go @@ -139,6 +139,26 @@ func newNoAuthEndpointer(svc fleet.Service, opts []kithttp.ServerOption, r *mux. } } +func newOrbitNoAuthEndpointer(svc fleet.Service, opts []kithttp.ServerOption, r *mux.Router, + versions ...string, +) *eu.CommonEndpointer[eu.HandlerFunc] { + // Add the capabilities reported by Orbit to the request context + opts = append(opts, capabilitiesContextFunc()) + + return &eu.CommonEndpointer[eu.HandlerFunc]{ + EP: &endpointer{ + svc: svc, + }, + MakeDecoderFn: makeDecoder, + EncodeFn: encodeResponse, + Opts: opts, + AuthFunc: auth.UnauthenticatedRequest, + FleetService: svc, + Router: r, + Versions: versions, + } +} + func badRequest(msg string) error { return &fleet.BadRequestError{Message: msg} } diff --git a/server/service/handler.go b/server/service/handler.go index 9bd7b98b59..0bc17eb064 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -987,7 +987,9 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC // This endpoint is unauthenticated and is used by to retrieve the MDM enrollment Terms of Use neWindowsMDM.GET(microsoft_mdm.MDE2TOSPath, mdmMicrosoftTOSEndpoint, MDMWebContainer{}) - ne.POST("/api/fleet/orbit/enroll", enrollOrbitEndpoint, contract.EnrollOrbitRequest{}) + // These endpoints are unauthenticated and made from orbit, and add the orbit capabilities header. + neOrbit := newOrbitNoAuthEndpointer(svc, opts, r, apiVersions...) + neOrbit.POST("/api/fleet/orbit/enroll", enrollOrbitEndpoint, contract.EnrollOrbitRequest{}) ne.GET("/api/_version_/fleet/software/titles/{title_id:[0-9]+}/in_house_app", getInHouseAppPackageEndpoint, getInHouseAppPackageRequest{}) ne.GET("/api/_version_/fleet/software/titles/{title_id:[0-9]+}/in_house_app/manifest", getInHouseAppManifestEndpoint, getInHouseAppManifestRequest{}) diff --git a/server/service/orbit.go b/server/service/orbit.go index add62446a2..ba60bd37c7 100644 --- a/server/service/orbit.go +++ b/server/service/orbit.go @@ -197,7 +197,17 @@ func (svc *Service) EnrollOrbit(ctx context.Context, hostInfo fleet.OrbitHostInf return "", fleet.OrbitError{Message: "failed to get IdP account: " + err.Error()} } if idpAccount == nil { - return "", fleet.NewOrbitIDPAuthRequiredError() + // If the Orbit client doesn't support end user auth, complain loudly and let the host enroll. + mp, ok := capabilities.FromContext(ctx) + //nolint:gocritic // ignore ifElseChain + if !ok { + level.Error(svc.logger).Log("msg", "!!! ERR_ALLOWING_UNAUTHENTICATED: host is not authenticated, but fleet could not determine whether orbit supports end-user authentication. proceeding with enrollment. !!! ", "host_uuid", hostInfo.HardwareUUID) + } else if !mp.Has(fleet.CapabilityEndUserAuth) { + level.Error(svc.logger).Log("msg", "!!! ERR_ALLOWING_UNAUTHENTICATED: host is not authenticated, but connected with an orbit version that does not support end user authentication. proceeding with enrollment. !!! ", "host_uuid", hostInfo.HardwareUUID) + } else { + // Otherwise report the unauthenticated host and let Orbit handle it (e.g. by prompting the user to authenticate). + return "", fleet.NewOrbitIDPAuthRequiredError() + } } }