From 3dbdbc1bcf15519422f079e63ca4931d21ff924a Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Mon, 10 Jun 2024 17:02:35 -0300 Subject: [PATCH] Add support for iOS/iPadOS to osquery-perf (#19522) #18119 - [X] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. Sample on how to simulate 50 iPads and 50 iPhones: ```sh go run ./cmd/osquery-perf -host_count 100 -os_templates iphone_14.6.tmpl:50,ipad_13.18.tmpl:50 -mdm_scep_challenge <...> ``` --- cmd/osquery-perf/agent.go | 62 ++++++++++++++++++- cmd/osquery-perf/ipad_13.18.tmpl | 0 cmd/osquery-perf/iphone_14.6.tmpl | 0 pkg/mdm/mdmtest/apple.go | 35 ++++++++++- .../service/integration_mdm_lifecycle_test.go | 8 +-- .../service/integration_mdm_profiles_test.go | 2 +- server/service/integration_mdm_test.go | 54 ++++++++-------- 7 files changed, 123 insertions(+), 38 deletions(-) create mode 100644 cmd/osquery-perf/ipad_13.18.tmpl create mode 100644 cmd/osquery-perf/iphone_14.6.tmpl diff --git a/cmd/osquery-perf/agent.go b/cmd/osquery-perf/agent.go index 6875b07150..b919a4c000 100644 --- a/cmd/osquery-perf/agent.go +++ b/cmd/osquery-perf/agent.go @@ -567,7 +567,7 @@ func newAgent( SCEPChallenge: mdmSCEPChallenge, SCEPURL: serverAddress + apple_mdm.SCEPPath, MDMURL: serverAddress + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") // Have the osquery agent match the MDM device serial number and UUID. serialNumber = macMDMClient.SerialNumber hostUUID = macMDMClient.UUID @@ -2150,6 +2150,54 @@ func (a *agent) submitLogs(results []resultLog) error { return nil } +func runAppleIDeviceMDMLoop(i int, stats *Stats, model string, serverURL string, mdmSCEPChallenge string, mdmCheckInInterval time.Duration) { + udid := mdmtest.RandUDID() + + mdmClient := mdmtest.NewTestMDMClientAppleDirect(mdmtest.AppleEnrollInfo{ + SCEPChallenge: mdmSCEPChallenge, + SCEPURL: serverURL + apple_mdm.SCEPPath, + MDMURL: serverURL + apple_mdm.MDMPath, + }, model) + mdmClient.UUID = udid + mdmClient.SerialNumber = mdmtest.RandSerialNumber() + deviceName := fmt.Sprintf("%s-%d", model, i) + productName := model + + if err := mdmClient.Enroll(); err != nil { + log.Printf("%s MDM enroll failed: %s", model, err) + stats.IncrementMDMErrors() + return + } + + stats.IncrementMDMEnrollments() + + mdmCheckInTicker := time.Tick(mdmCheckInInterval) + + for range mdmCheckInTicker { + mdmCommandPayload, err := mdmClient.Idle() + if err != nil { + log.Printf("MDM Idle request failed: %s: %s", model, err) + stats.IncrementMDMErrors() + continue + } + stats.IncrementMDMSessions() + + for mdmCommandPayload != nil { + stats.IncrementMDMCommandsReceived() + if mdmCommandPayload.Command.RequestType == "DeviceInformation" { + mdmCommandPayload, err = mdmClient.AcknowledgeDeviceInformation(udid, mdmCommandPayload.CommandUUID, deviceName, productName) + } else { + mdmCommandPayload, err = mdmClient.Acknowledge(mdmCommandPayload.CommandUUID) + } + if err != nil { + log.Printf("MDM Acknowledge request failed: %s: %s", model, err) + stats.IncrementMDMErrors() + break + } + } + } +} + // rows returns a set of rows for use in tests for query results. func rows(num int) string { b := strings.Builder{} @@ -2197,6 +2245,8 @@ func main() { "windows_11_22H2_2861.tmpl": true, "windows_11_22H2_3007.tmpl": true, "ubuntu_22.04.tmpl": true, + "iphone_14.6.tmpl": true, + "ipad_13.18.tmpl": true, } allowedTemplateNames := make([]string, 0, len(validTemplateNames)) for k := range validTemplateNames { @@ -2349,6 +2399,16 @@ func main() { tmpl = tmplss[i%len(tmplss)] } + if tmpl.Name() == "iphone_14.6.tmpl" || tmpl.Name() == "ipad_13.18.tmpl" { + model := "iPhone 14,6" + if tmpl.Name() == "ipad_13.18.tmpl" { + model = "iPad 13,18" + } + go runAppleIDeviceMDMLoop(i, stats, model, *serverURL, *mdmSCEPChallenge, *mdmCheckInInterval) + time.Sleep(sleepTime) + continue + } + a := newAgent(i+1, *serverURL, *enrollSecret, diff --git a/cmd/osquery-perf/ipad_13.18.tmpl b/cmd/osquery-perf/ipad_13.18.tmpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/osquery-perf/iphone_14.6.tmpl b/cmd/osquery-perf/iphone_14.6.tmpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pkg/mdm/mdmtest/apple.go b/pkg/mdm/mdmtest/apple.go index d4463575d4..8d18c1c2a1 100644 --- a/pkg/mdm/mdmtest/apple.go +++ b/pkg/mdm/mdmtest/apple.go @@ -137,11 +137,11 @@ func NewTestMDMClientAppleDEP(serverURL string, depURLToken string, opts ...Test // NewTestMDMClientAppleDirect will create a simulated device that will not fetch the enrollment // profile from Fleet. The enrollment information is to be provided in the enrollInfo. -func NewTestMDMClientAppleDirect(enrollInfo AppleEnrollInfo, opts ...TestMDMAppleClientOption) *TestAppleMDMClient { +func NewTestMDMClientAppleDirect(enrollInfo AppleEnrollInfo, model string, opts ...TestMDMAppleClientOption) *TestAppleMDMClient { c := TestAppleMDMClient{ UUID: strings.ToUpper(uuid.New().String()), SerialNumber: RandSerialNumber(), - Model: "MacBookPro16,1", + Model: model, EnrollInfo: enrollInfo, } @@ -392,6 +392,9 @@ func (c *TestAppleMDMClient) Authenticate() error { "EnrollmentID": "testenrollmentid-" + c.UUID, "SerialNumber": c.SerialNumber, } + if strings.HasPrefix(c.Model, "iPhone") || strings.HasPrefix(c.Model, "iPad") { + payload["ProductName"] = c.Model + } _, err := c.request("application/x-apple-aspen-mdm-checkin", payload) return err } @@ -480,6 +483,23 @@ func (c *TestAppleMDMClient) Acknowledge(cmdUUID string) (*mdm.Command, error) { return c.sendAndDecodeCommandResponse(payload) } +func (c *TestAppleMDMClient) AcknowledgeDeviceInformation(udid, cmdUUID, deviceName, productName string) (*mdm.Command, error) { + payload := map[string]any{ + "Status": "Acknowledged", + "UDID": udid, + "CommandUUID": cmdUUID, + "QueryResponses": map[string]interface{}{ + "AvailableDeviceCapacity": float64(51.53312768), + "DeviceCapacity": float64(64), + "DeviceName": deviceName, + "OSVersion": "17.5.1", + "ProductName": productName, + "WiFiMAC": "ff:ff:ff:ff:ff:ff", + }, + } + return c.sendAndDecodeCommandResponse(payload) +} + func (c *TestAppleMDMClient) GetBootstrapToken() ([]byte, error) { payload := map[string]any{ "MessageType": "GetBootstrapToken", @@ -648,7 +668,11 @@ const serialLetters = "0123456789ABCDEFGHJKMNPQRSTUVWXYZ" // RandSerialNumber returns a fake random serial number. func RandSerialNumber() string { - b := make([]byte, 12) + return randStr(12) +} + +func randStr(n int) string { + b := make([]byte, n) for i := range b { //nolint:gosec // not used for crypto, only to generate random serial for testing b[i] = serialLetters[mrand.Intn(len(serialLetters))] @@ -656,6 +680,11 @@ func RandSerialNumber() string { return string(b) } +// RandUDID returns a fake random iOS/iPadOS 17+ UDID. +func RandUDID() string { + return fmt.Sprintf("%s-%s", randStr(8), randStr(16)) +} + type scepClient interface { scepserver.Service Supports(cap string) bool diff --git a/server/service/integration_mdm_lifecycle_test.go b/server/service/integration_mdm_lifecycle_test.go index 7bd789f56d..75fad197f0 100644 --- a/server/service/integration_mdm_lifecycle_test.go +++ b/server/service/integration_mdm_lifecycle_test.go @@ -134,12 +134,10 @@ func (s *integrationMDMTestSuite) TestTurnOnLifecycleEventsApple() { dupeClient := mdmtest.NewTestMDMClientAppleDirect( mdmtest.AppleEnrollInfo{ - SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }, - ) + }, "MacBookPro16,1") dupeClient.UUID = device.UUID dupeClient.SerialNumber = device.SerialNumber dupeClient.Model = device.Model @@ -159,12 +157,10 @@ func (s *integrationMDMTestSuite) TestTurnOnLifecycleEventsApple() { dupeClient := mdmtest.NewTestMDMClientAppleDirect( mdmtest.AppleEnrollInfo{ - SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }, - ) + }, "MacBookPro16,1") dupeClient.UUID = device.UUID dupeClient.SerialNumber = device.SerialNumber dupeClient.Model = device.Model diff --git a/server/service/integration_mdm_profiles_test.go b/server/service/integration_mdm_profiles_test.go index 8a46f3ec95..6a3a28104a 100644 --- a/server/service/integration_mdm_profiles_test.go +++ b/server/service/integration_mdm_profiles_test.go @@ -1898,7 +1898,7 @@ func (s *integrationMDMTestSuite) TestHostMDMAppleProfilesStatus() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") // enroll the device with orbit var resp EnrollOrbitResponse diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index 4463ab2cda..4f95a5395d 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -425,7 +425,7 @@ func (s *integrationMDMTestSuite) TestGetBootstrapToken() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") err := mdmDevice.Enroll() require.NoError(t, err) @@ -776,13 +776,13 @@ func (s *integrationMDMTestSuite) TestAppleMDMDeviceEnrollment() { SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, } - mdmDeviceA := mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo) + mdmDeviceA := mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1") err := mdmDeviceA.Enroll() require.NoError(t, err) s.lastActivityOfTypeMatches(fleet.ActivityTypeMDMEnrolled{}.ActivityName(), fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, mdmDeviceA.SerialNumber, mdmDeviceA.Model, mdmDeviceA.SerialNumber), 0) - mdmDeviceB := mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo) + mdmDeviceB := mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1") err = mdmDeviceB.Enroll() require.NoError(t, err) s.lastActivityOfTypeMatches(fleet.ActivityTypeMDMEnrolled{}.ActivityName(), @@ -873,7 +873,7 @@ func (s *integrationMDMTestSuite) TestDeviceMultipleAuthMessages() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") err := mdmDevice.Enroll() require.NoError(t, err) @@ -1008,7 +1008,7 @@ func (s *integrationMDMTestSuite) TestMDMAppleUnenroll() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") err := mdmDevice.Enroll() require.NoError(t, err) @@ -2341,7 +2341,7 @@ func (s *integrationMDMTestSuite) TestEnqueueMDMCommand() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") err := mdmDevice.Enroll() require.NoError(t, err) @@ -2754,24 +2754,24 @@ func (s *integrationMDMTestSuite) TestBootstrapPackageStatus() { MDMURL: s.server.URL + apple_mdm.MDMPath, } noTeamDevices := []deviceWithResponse{ - {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Error", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Offline", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Offline", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Pending", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Pending", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, + {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Error", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Offline", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Offline", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Pending", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Pending", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, } teamDevices := []deviceWithResponse{ - {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Error", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Error", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Error", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Offline", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, - {"Pending", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo)}, + {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Acknowledge", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Error", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Error", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Error", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Offline", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, + {"Pending", mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "MacBookPro16,1")}, } expectedSerialsByTeamAndStatus := make(map[uint]map[fleet.MDMBootstrapPackageStatus][]string) @@ -4375,7 +4375,7 @@ func (s *integrationMDMTestSuite) TestSSO() { mdmDevice := mdmtest.NewTestMDMClientAppleDirect(mdmtest.AppleEnrollInfo{ SCEPChallenge: s.scepChallenge, - }) + }, "MacBookPro16,1") var lastSubmittedProfile *godep.Profile s.mockDEPResponse(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) @@ -5292,7 +5292,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") mdmDevice.SerialNumber = h.HardwareSerial mdmDevice.UUID = h.UUID err = mdmDevice.Enroll() @@ -5349,7 +5349,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") mdmDevice.SerialNumber = h2.HardwareSerial mdmDevice.UUID = h2.UUID err = mdmDevice.Enroll() @@ -5372,7 +5372,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") mdmDevice.SerialNumber = h3.HardwareSerial mdmDevice.UUID = h3.UUID err = mdmDevice.Enroll() @@ -5393,7 +5393,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") mdmDevice.SerialNumber = h4.HardwareSerial mdmDevice.UUID = h4.UUID err = mdmDevice.Enroll() @@ -7797,7 +7797,7 @@ func (s *integrationMDMTestSuite) TestManualEnrollmentCommands() { SCEPChallenge: s.scepChallenge, SCEPURL: s.server.URL + apple_mdm.SCEPPath, MDMURL: s.server.URL + apple_mdm.MDMPath, - }) + }, "MacBookPro16,1") err := mdmDevice.Enroll() require.NoError(t, err) s.runWorker()