From 989764969604a87128cf8b3fc9e14e4bc175f908 Mon Sep 17 00:00:00 2001 From: Sarah Gillespie <73313222+gillespi314@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:56:25 -0500 Subject: [PATCH] Update osquery-perf with DDM functionality (#18273) --- cmd/osquery-perf/agent.go | 210 ++++++++++++++++++++++++++++++++++---- server/fleet/apple_mdm.go | 20 ++++ 2 files changed, 210 insertions(+), 20 deletions(-) diff --git a/cmd/osquery-perf/agent.go b/cmd/osquery-perf/agent.go index a1aea3b1bd..6a7af0964c 100644 --- a/cmd/osquery-perf/agent.go +++ b/cmd/osquery-perf/agent.go @@ -29,6 +29,7 @@ import ( "github.com/fleetdm/fleet/v4/server/fleet" apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple" "github.com/fleetdm/fleet/v4/server/mdm/microsoft/syncml" + "github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm" "github.com/fleetdm/fleet/v4/server/ptr" "github.com/fleetdm/fleet/v4/server/service" "github.com/google/uuid" @@ -135,25 +136,33 @@ func init() { } type Stats struct { - startTime time.Time - errors int - osqueryEnrollments int - orbitEnrollments int - mdmEnrollments int - mdmSessions int - distributedWrites int - mdmCommandsReceived int - distributedReads int - configRequests int - configErrors int - resultLogRequests int - orbitErrors int - mdmErrors int - desktopErrors int - distributedReadErrors int - distributedWriteErrors int - resultLogErrors int - bufferedLogs int + startTime time.Time + errors int + osqueryEnrollments int + orbitEnrollments int + mdmEnrollments int + mdmSessions int + distributedWrites int + mdmCommandsReceived int + distributedReads int + configRequests int + configErrors int + resultLogRequests int + orbitErrors int + mdmErrors int + ddmDeclarationItemsErrors int + ddmConfigurationErrors int + ddmActivationErrors int + ddmStatusErrors int + ddmDeclarationItemsSuccess int + ddmConfigurationSuccess int + ddmActivationSuccess int + ddmStatusSuccess int + desktopErrors int + distributedReadErrors int + distributedWriteErrors int + resultLogErrors int + bufferedLogs int l sync.Mutex } @@ -236,6 +245,54 @@ func (s *Stats) IncrementMDMErrors() { s.mdmErrors++ } +func (s *Stats) IncrementDDMDeclarationItemsErrors() { + s.l.Lock() + defer s.l.Unlock() + s.ddmDeclarationItemsErrors++ +} + +func (s *Stats) IncrementDDMConfigurationErrors() { + s.l.Lock() + defer s.l.Unlock() + s.ddmConfigurationErrors++ +} + +func (s *Stats) IncrementDDMActivationErrors() { + s.l.Lock() + defer s.l.Unlock() + s.ddmActivationErrors++ +} + +func (s *Stats) IncrementDDMStatusErrors() { + s.l.Lock() + defer s.l.Unlock() + s.ddmStatusErrors++ +} + +func (s *Stats) IncrementDDMDeclarationItemsSuccess() { + s.l.Lock() + defer s.l.Unlock() + s.ddmDeclarationItemsSuccess++ +} + +func (s *Stats) IncrementDDMConfigurationSuccess() { + s.l.Lock() + defer s.l.Unlock() + s.ddmConfigurationSuccess++ +} + +func (s *Stats) IncrementDDMActivationSuccess() { + s.l.Lock() + defer s.l.Unlock() + s.ddmActivationSuccess++ +} + +func (s *Stats) IncrementDDMStatusSuccess() { + s.l.Lock() + defer s.l.Unlock() + s.ddmStatusSuccess++ +} + func (s *Stats) IncrementDesktopErrors() { s.l.Lock() defer s.l.Unlock() @@ -274,7 +331,7 @@ func (s *Stats) Log() { defer s.l.Unlock() log.Printf( - "uptime: %s, error rate: %.2f, osquery enrolls: %d, orbit enrolls: %d, mdm enrolls: %d, distributed/reads: %d, distributed/writes: %d, config requests: %d, result log requests: %d, mdm sessions initiated: %d, mdm commands received: %d, config errors: %d, distributed/read errors: %d, distributed/write errors: %d, log result errors: %d, orbit errors: %d, desktop errors: %d, mdm errors: %d, buffered logs: %d", + "uptime: %s, error rate: %.2f, osquery enrolls: %d, orbit enrolls: %d, mdm enrolls: %d, distributed/reads: %d, distributed/writes: %d, config requests: %d, result log requests: %d, mdm sessions initiated: %d, mdm commands received: %d, config errors: %d, distributed/read errors: %d, distributed/write errors: %d, log result errors: %d, orbit errors: %d, desktop errors: %d, mdm errors: %d, ddm declaration items success: %d, ddm declaration items errors: %d, ddm activation success: %d, ddm activation errors: %d, ddm configuration success: %d, ddm configuration errors: %d, ddm status success: %d, ddm status errors: %d, buffered logs: %d", time.Since(s.startTime).Round(time.Second), float64(s.errors)/float64(s.osqueryEnrollments), s.osqueryEnrollments, @@ -293,6 +350,14 @@ func (s *Stats) Log() { s.orbitErrors, s.desktopErrors, s.mdmErrors, + s.ddmDeclarationItemsSuccess, + s.ddmDeclarationItemsErrors, + s.ddmActivationSuccess, + s.ddmActivationErrors, + s.ddmConfigurationSuccess, + s.ddmConfigurationErrors, + s.ddmStatusSuccess, + s.ddmStatusErrors, s.bufferedLogs, ) } @@ -943,10 +1008,115 @@ func (a *agent) runMacosMDMLoop() { a.stats.IncrementMDMErrors() break INNER_FOR_LOOP } + if mdmCommandPayload != nil && mdmCommandPayload.Command.RequestType == "DeclarativeManagement" { + a.doDeclarativeManagement(mdmCommandPayload) + } } } } +func (a *agent) doDeclarativeManagement(cmd *mdm.Command) { + // defer log.Printf("Exiting DeclarativeManagement for command %s", cmd.CommandUUID) + + // get declaration-items endpoint + r, err := a.macMDMClient.DeclarativeManagement("declaration-items") + if err != nil { + log.Printf("DDM %s declaration-items request failed: %s", cmd.CommandUUID, err) + a.stats.IncrementDDMDeclarationItemsErrors() + return + } + body, err := io.ReadAll(r.Body) + if err != nil { + log.Printf("DDM %s declaration-items read body failed: %s", cmd.CommandUUID, err) + a.stats.IncrementDDMDeclarationItemsErrors() + return + } + var items fleet.MDMAppleDDMDeclarationItemsResponse + err = json.Unmarshal(body, &items) + if err != nil { + log.Printf("DDM %s declaration-items unmarshal failed: %s", cmd.CommandUUID, err) + a.stats.IncrementDDMDeclarationItemsErrors() + return + } + a.stats.IncrementDDMDeclarationItemsSuccess() + + // get declaration/configuration/:identifer endpoint + for _, d := range items.Declarations.Configurations { + path := fmt.Sprintf("declaration/%s/%s", "configuration", d.Identifier) + r, err := a.macMDMClient.DeclarativeManagement(path) + if err != nil { + log.Printf("DDM %s request failed: %s", path, err) + a.stats.IncrementDDMConfigurationErrors() + return + } + body, err := io.ReadAll(r.Body) + if err != nil { + log.Printf("DDM %s read body failed: %s", path, err) + a.stats.IncrementDDMConfigurationErrors() + return + } + var decl fleet.MDMAppleDeclaration + err = json.Unmarshal(body, &decl) + if err != nil { + log.Printf("DDM %s unmarshal failed: %s", path, err) + a.stats.IncrementDDMConfigurationErrors() + return + } + } + a.stats.IncrementDDMConfigurationSuccess() + + // get declaration/activation/:identifer endpoint + for _, d := range items.Declarations.Activations { + path := fmt.Sprintf("declaration/%s/%s", "activation", d.Identifier) + r, err := a.macMDMClient.DeclarativeManagement(path) + if err != nil { + log.Printf("DDM %s request failed: %s", path, err) + a.stats.IncrementDDMActivationErrors() + return + } + body, err := io.ReadAll(r.Body) + if err != nil { + log.Printf("DDM %s read body failed: %s", path, err) + a.stats.IncrementDDMActivationErrors() + return + } + var act fleet.MDMAppleDDMActivation + err = json.Unmarshal(body, &act) + if err != nil { + log.Printf("DDM %s unmarshal failed: %s", path, err) + a.stats.IncrementDDMActivationErrors() + return + } + } + a.stats.IncrementDDMActivationSuccess() + + // sent status report + for _, d := range items.Declarations.Configurations { + report := fleet.MDMAppleDDMStatusReport{} + report.StatusItems.Management.Declarations.Configurations = []fleet.MDMAppleDDMStatusDeclaration{ + {Active: true, Valid: fleet.MDMAppleDeclarationValid, Identifier: d.Identifier, ServerToken: d.ServerToken}, + } + r, err := a.macMDMClient.DeclarativeManagement("status", report) + if err != nil { + log.Printf("DDM %s status request failed: %s", d.Identifier, err) + a.stats.IncrementDDMStatusErrors() + return + } + + // Apple's documentation has some conflicting information about the expected status here so we'll + // just check for both. + // + // https://developer.apple.com/documentation/devicemanagement/get_the_device_status#response-codes + // https://developer.apple.com/documentation/devicemanagement/statusreport#discussion + if r.StatusCode != http.StatusOK && r.StatusCode != http.StatusNoContent { + log.Printf("DDM %s status response unexpected: %d", d.Identifier, r.StatusCode) + a.stats.IncrementDDMStatusErrors() + return + } + } + a.stats.IncrementDDMStatusSuccess() +} + func (a *agent) runWindowsMDMLoop() { mdmCheckInTicker := time.Tick(a.MDMCheckInInterval) diff --git a/server/fleet/apple_mdm.go b/server/fleet/apple_mdm.go index 1ee60cb3b9..a646bddcad 100644 --- a/server/fleet/apple_mdm.go +++ b/server/fleet/apple_mdm.go @@ -819,3 +819,23 @@ type MDMAppleDDMStatusErrorReason struct { // error. Details map[string]any `json:"Details"` } + +// MDMAppleDDMActivationPayload represents the payload of an activation declaration. +// +// https://developer.apple.com/documentation/devicemanagement/activationsimple +type MDMAppleDDMActivationPayload struct { + Predicate string `json:"Predicate"` + StandardConfigurations []string `json:"StandardConfigurations"` +} + +// MDMAppleDDMActivation represents the declaration of an activation. It combines the base +// declaation with the activation payload. +// +// https://developer.apple.com/documentation/devicemanagement/declarationbase +// https://developer.apple.com/documentation/devicemanagement/activationsimple +type MDMAppleDDMActivation struct { + Identifier string `json:"Identifier"` + Payload MDMAppleDDMActivationPayload `json:"Payload"` + ServerToken string `json:"ServerToken"` + Type string `json:"Type"` // "com.apple.activation.simple" +}