diff --git a/orbit/changes/11692-profile-config-read b/orbit/changes/11692-profile-config-read
new file mode 100644
index 0000000000..28fa771571
--- /dev/null
+++ b/orbit/changes/11692-profile-config-read
@@ -0,0 +1 @@
+* Improve the logic to read enroll secrets from macOS configuration profiles to be compatible with different MDM providers.
diff --git a/orbit/pkg/profiles/profiles_darwin.go b/orbit/pkg/profiles/profiles_darwin.go
index 700778719d..feba9230d7 100644
--- a/orbit/pkg/profiles/profiles_darwin.go
+++ b/orbit/pkg/profiles/profiles_darwin.go
@@ -4,66 +4,49 @@ package profiles
import (
"bytes"
+ "encoding/json"
"fmt"
"os/exec"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm/apple/mobileconfig"
- "github.com/groob/plist"
)
-type profileItem struct {
- PayloadContent fleet.MDMAppleFleetdConfig
- PayloadType string
-}
-
-type profilePayload struct {
- ProfileIdentifier string
- ProfileItems []profileItem
-}
-
-type profilesOutput struct {
- ComputerLevel []profilePayload `plist:"_computerlevel"`
-}
-
-// GetFleetdConfig searches and parses a device level configuration profile
-// with Fleet's payload identifier.
+// GetFleetdConfig reads a system level setting set with Fleet's payload identifier.
func GetFleetdConfig() (*fleet.MDMAppleFleetdConfig, error) {
- p, err := getProfile(mobileconfig.FleetdConfigPayloadIdentifier)
- if err != nil {
- return nil, err
- }
+ readFleetdConfigAppleScript := fmt.Sprintf(`
+ const config = $.NSUserDefaults.alloc.initWithSuiteName("%s");
+ const enrollSecret = config.objectForKey("EnrollSecret");
+ const fleetURL = config.objectForKey("FleetURL");
+ JSON.stringify({
+ EnrollSecret: ObjC.deepUnwrap(enrollSecret),
+ FleetURL: ObjC.deepUnwrap(fleetURL),
+ });
+ `, mobileconfig.FleetdConfigPayloadIdentifier)
- return &p.ProfileItems[0].PayloadContent, nil
-}
-
-func getProfile(identifier string) (*profilePayload, error) {
- outBuf, err := execProfileCmd()
+ outBuf, err := execScript(readFleetdConfigAppleScript)
if err != nil {
return nil, fmt.Errorf("get profile: %w", err)
}
- var profiles profilesOutput
- if err := plist.Unmarshal(outBuf.Bytes(), &profiles); err != nil {
- return nil, fmt.Errorf("get profile: %w", err)
+ var cfg fleet.MDMAppleFleetdConfig
+ if err = json.Unmarshal(outBuf.Bytes(), &cfg); err != nil {
+ return nil, fmt.Errorf("unmarshaling configuration: %w", err)
}
- for _, profile := range profiles.ComputerLevel {
- if profile.ProfileIdentifier == identifier {
- return &profile, nil
- }
+ if cfg.EnrollSecret == "" || cfg.FleetURL == "" {
+ return nil, ErrNotFound
}
- return nil, ErrNotFound
+ return &cfg, err
}
-// execProfileCmd is declared as a variable so it can be overwritten by tests.
-var execProfileCmd = func() (*bytes.Buffer, error) {
+// execScript is declared as a variable so it can be overwritten by tests.
+var execScript = func(script string) (*bytes.Buffer, error) {
var outBuf bytes.Buffer
- cmd := exec.Command("/usr/bin/profiles", "list", "-o", "stdout-xml")
+ cmd := exec.Command("osascript", "-l", "JavaScript", "-e", script)
cmd.Stdout = &outBuf
cmd.Stderr = &outBuf
-
if err := cmd.Run(); err != nil {
return nil, err
}
diff --git a/orbit/pkg/profiles/profiles_darwin_test.go b/orbit/pkg/profiles/profiles_darwin_test.go
index 1a657b5a61..da53214106 100644
--- a/orbit/pkg/profiles/profiles_darwin_test.go
+++ b/orbit/pkg/profiles/profiles_darwin_test.go
@@ -5,7 +5,6 @@ package profiles
import (
"bytes"
"errors"
- "io"
"testing"
"github.com/fleetdm/fleet/v4/server/fleet"
@@ -19,18 +18,38 @@ func TestGetFleetdConfig(t *testing.T) {
cmdOut *string
cmdErr error
wantOut *fleet.MDMAppleFleetdConfig
- wantErr error
+ wantErr string
}{
- {nil, testErr, nil, testErr},
- {ptr.String("invalid-xml"), nil, nil, io.EOF},
- {&emptyOutput, nil, nil, ErrNotFound},
- {&withFleetdConfig, nil, &fleet.MDMAppleFleetdConfig{EnrollSecret: "ENROLL_SECRET", FleetURL: "https://test.example.com"}, nil},
+ {nil, testErr, nil, testErr.Error()},
+ {ptr.String("invalid-json"), nil, nil, "unmarshaling configuration"},
+ {ptr.String("{}"), nil, nil, ErrNotFound.Error()},
+ {
+ ptr.String(`{"EnrollSecret": "ENROLL_SECRET", "FleetURL": "https://test.example.com"}`),
+ nil,
+ &fleet.MDMAppleFleetdConfig{
+ EnrollSecret: "ENROLL_SECRET",
+ FleetURL: "https://test.example.com",
+ },
+ "",
+ },
+ {
+ ptr.String(`{"EnrollSecret": "ENROLL_SECRET", "FleetURL": ""}`),
+ nil,
+ nil,
+ ErrNotFound.Error(),
+ },
+ {
+ ptr.String(`{"EnrollSecret": "", "FleetURL": "https://test.example.com"}`),
+ nil,
+ nil,
+ ErrNotFound.Error(),
+ },
}
- origExecProfileCmd := execProfileCmd
- t.Cleanup(func() { execProfileCmd = origExecProfileCmd })
+ origExecScript := execScript
+ t.Cleanup(func() { execScript = origExecScript })
for _, c := range cases {
- execProfileCmd = func() (*bytes.Buffer, error) {
+ execScript = func(script string) (*bytes.Buffer, error) {
if c.cmdOut == nil {
return nil, c.cmdErr
}
@@ -41,69 +60,12 @@ func TestGetFleetdConfig(t *testing.T) {
}
out, err := GetFleetdConfig()
- require.ErrorIs(t, err, c.wantErr)
+ if c.wantErr != "" {
+ require.ErrorContains(t, err, c.wantErr)
+ } else {
+ require.NoError(t, err)
+ }
require.Equal(t, c.wantOut, out)
}
}
-
-var (
- emptyOutput = `
-
-
-
-`
-
- withFleetdConfig = `
-
-
-
-
- _computerlevel
-
-
- ProfileDescription
- test descripiton
- ProfileDisplayName
- test name
- ProfileIdentifier
- com.fleetdm.fleetd.config
- ProfileInstallDate
- 2023-02-27 18:55:07 +0000
- ProfileItems
-
-
- PayloadContent
-
- EnrollSecret
- ENROLL_SECRET
- FleetURL
- https://test.example.com
-
- PayloadDescription
- test description
- PayloadDisplayName
- test name
- PayloadIdentifier
- com.fleetdm.fleetd.config
- PayloadType
- com.fleetdm.fleetd
- PayloadUUID
- 0C6AFB45-01B6-4E19-944A-123CD16381C7
- PayloadVersion
- 1
-
-
- ProfileRemovalDisallowed
- true
- ProfileType
- Configuration
- ProfileUUID
- 8D0F62E6-E24F-4B2F-AFA8-CAC1F07F4FDC
- ProfileVersion
- 1
-
-
-
-`
-)