From f00aecb382b34b3d9b76a8772ccc2afb796f698c Mon Sep 17 00:00:00 2001 From: Sarah Gillespie <73313222+gillespi314@users.noreply.github.com> Date: Wed, 2 Jul 2025 13:47:42 -0500 Subject: [PATCH] Fallback to ConfigurationURL when ConfigurationWebURL is not set in macOS MDM enrollment profile (#30462) --- orbit/changes/30372-darwin-profiles-parser | 2 + orbit/pkg/profiles/profiles_darwin.go | 25 +++-- orbit/pkg/profiles/profiles_darwin_test.go | 116 +++++++++++++++------ 3 files changed, 105 insertions(+), 38 deletions(-) create mode 100644 orbit/changes/30372-darwin-profiles-parser diff --git a/orbit/changes/30372-darwin-profiles-parser b/orbit/changes/30372-darwin-profiles-parser new file mode 100644 index 0000000000..a7f880402f --- /dev/null +++ b/orbit/changes/30372-darwin-profiles-parser @@ -0,0 +1,2 @@ +- Fixed issue with macOS MDM migration where fleetd did not fallback to parsing the + `ConfigurationURL` when `ConfigurationWebURL` was not set in the MDM enrollment profile. diff --git a/orbit/pkg/profiles/profiles_darwin.go b/orbit/pkg/profiles/profiles_darwin.go index d0efd07504..8cdd08d823 100644 --- a/orbit/pkg/profiles/profiles_darwin.go +++ b/orbit/pkg/profiles/profiles_darwin.go @@ -215,29 +215,40 @@ func CheckAssignedEnrollmentProfile(expectedURL string) error { return errors.New("parsing profiles output: received null device enrollment configuration") } + var configURL, configWebURL string var assignedURL string for _, line := range lines { // Note the output may contain both ConfigurationURL and ConfigurationWebURL but we check only // the latter for backwards compatibility. // See https://github.com/fleetdm/fleet/blob/963b2438537de14e7e16f1f18857ed8a66d51bfc/server/mdm/apple/apple_mdm.go#L195 - v, ok := parseEnrollmentProfileValue(line, "ConfigurationWebURL") - if ok { - assignedURL = v + if v, ok := parseEnrollmentProfileValue(line, "ConfigurationURL"); ok { + configURL = v + continue + } + if v, ok := parseEnrollmentProfileValue(line, "ConfigurationWebURL"); ok { + configWebURL = v break } } - if assignedURL == "" { - return errors.New("parsing profiles output: missing or empty configuration web url") + switch { + case configURL == "" && configWebURL == "": + return errors.New("parsing profiles output: missing both configuration web url and configuration url") + case configWebURL != "": + // Always prefer web URL for consistency. + assignedURL = configWebURL + default: + // Fallback to configuration URL. + assignedURL = configURL } assigned, err := url.Parse(assignedURL) if err != nil { - return fmt.Errorf("parsing profiles output: unable to parse configuration web url: %w", err) + return fmt.Errorf("parsing profiles output: unable to parse server url: %w", err) } if !strings.EqualFold(assigned.Hostname(), expected.Hostname()) { - return fmt.Errorf(`matching configuration web url: expected '%s' but found '%s'`, expected.Hostname(), assigned.Hostname()) + return fmt.Errorf(`matching server url: expected '%s' but found '%s'`, expected.Hostname(), assigned.Hostname()) } return nil diff --git a/orbit/pkg/profiles/profiles_darwin_test.go b/orbit/pkg/profiles/profiles_darwin_test.go index 9d02e97afd..1f4cf91a55 100644 --- a/orbit/pkg/profiles/profiles_darwin_test.go +++ b/orbit/pkg/profiles/profiles_darwin_test.go @@ -333,35 +333,31 @@ func TestCheckAssignedEnrollmentProfile(t *testing.T) { name string cmdOut *string cmdErr error - wantOut bool wantErr error }{ { - "command error", - nil, - errors.New("some command error"), - false, - errors.New("some command error"), + name: "command error", + cmdOut: nil, + cmdErr: errors.New("some command error"), + wantErr: errors.New("some command error"), }, { - "empty output", - ptr.String(""), - nil, - false, - errors.New("parsing profiles output: expected at least 2 lines but got 1"), + name: "empty output", + cmdOut: ptr.String(""), + cmdErr: nil, + wantErr: errors.New("parsing profiles output: expected at least 2 lines but got 1"), }, { - "null profile", - ptr.String(`Device Enrollment configuration: + name: "null profile", + cmdOut: ptr.String(`Device Enrollment configuration: (null) `), - nil, - false, - errors.New("parsing profiles output: received null device enrollment configuration"), + cmdErr: nil, + wantErr: errors.New("parsing profiles output: received null device enrollment configuration"), }, { - "mismatch profile", - ptr.String(`Device Enrollment configuration: + name: "mismatch profile", + cmdOut: ptr.String(`Device Enrollment configuration: { AllowPairing = 1; AutoAdvanceSetup = 0; @@ -371,13 +367,12 @@ func TestCheckAssignedEnrollmentProfile(t *testing.T) { ... } `), - nil, - false, - errors.New(`configuration web url: expected 'valid.com' but found 'test.example.com'`), + cmdErr: nil, + wantErr: errors.New(`server url: expected 'valid.com' but found 'test.example.com'`), }, { - "match profile", - ptr.String(`Device Enrollment configuration: + name: "match profile", + cmdOut: ptr.String(`Device Enrollment configuration: { AllowPairing = 1; AutoAdvanceSetup = 0; @@ -387,13 +382,12 @@ func TestCheckAssignedEnrollmentProfile(t *testing.T) { ... } `), - nil, - false, - nil, + cmdErr: nil, + wantErr: nil, }, { - "mixed case match", - ptr.String(`Device Enrollment configuration: + name: "mixed case match configuration web URL", + cmdOut: ptr.String(`Device Enrollment configuration: { AllowPairing = 1; AutoAdvanceSetup = 0; @@ -403,9 +397,69 @@ func TestCheckAssignedEnrollmentProfile(t *testing.T) { ... } `), - nil, - false, - nil, + cmdErr: nil, + wantErr: nil, + }, + { + name: "mixed case match configuration URL but wrong configuration web URL", + cmdOut: ptr.String(`Device Enrollment configuration: +{ + AllowPairing = 1; + AutoAdvanceSetup = 0; + AwaitDeviceConfigured = 0; + ConfigurationURL = "https://vaLiD.com?tOken=1234"; + ConfigurationWebURL = "https://test.ExaMplE.com/mdm/apple/enroll?token=1234"; + ... +} + `), + cmdErr: nil, + wantErr: errors.New(`server url: expected 'valid.com' but found 'test.ExaMplE.com'`), + }, + { + name: "match configuration URL and empty configuration web URL", + cmdOut: ptr.String(`Device Enrollment configuration: +{ + AllowPairing = 1; + AutoAdvanceSetup = 0; + AwaitDeviceConfigured = 0; + ConfigurationURL = "https://valid.com?token=1234"; + ConfigurationWebURL = ""; + ... +} + `), + cmdErr: nil, + wantErr: nil, + }, + { + name: "mixed case match configuration web URL and empty configuration URL", + cmdOut: ptr.String(`Device Enrollment configuration: +{ + AllowPairing = 1; + AutoAdvanceSetup = 0; + AwaitDeviceConfigured = 0; + ConfigurationURL = ""; + ConfigurationWebURL = "https://vaLiD.com?tOken=1234"; + ... +} + `), + cmdErr: nil, + wantErr: nil, + }, + + { + name: "unparseable URL", + cmdOut: ptr.String(`Device Enrollment configuration: +{ + AllowPairing = 1; + AutoAdvanceSetup = 0; + AwaitDeviceConfigured = 0; + ConfigurationURL = "://invalid-url"; + ConfigurationWebURL = ""; + ... +} + `), + cmdErr: nil, + wantErr: errors.New("parsing profiles output: unable to parse server url"), }, }