proper fix for renew enrollment profile requests (#19909)

# Checklist for submitter

- [x] Added/updated tests
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Roberto Dip
2024-06-20 16:35:23 -03:00
committed by GitHub
parent c7dfaf45f7
commit 03dd4721b6
2 changed files with 148 additions and 8 deletions
-8
View File
@@ -708,14 +708,6 @@ func (h *Host) IsEligibleForDEPMigration(isConnectedToFleetMDM bool) bool {
func (h *Host) NeedsDEPEnrollment(isConnectedToFleetMDM bool) bool {
return h.MDMInfo != nil &&
!h.MDMInfo.Enrolled &&
// as a special case for migration with user interaction, we
// also check the information stored in host_mdm, and assume
// the host needs migration if it's not Fleet
//
// this is because we can't always rely on nano setting
// `nano_enrollment.active = 1` since sometimes Fleet won't get
// the checkout message from the host.
(!isConnectedToFleetMDM || h.MDMInfo.Name != WellKnownMDMFleet) &&
h.IsDEPAssignedToFleet()
}
+148
View File
@@ -8927,3 +8927,151 @@ func (s *integrationMDMTestSuite) uploadABMToken(encryptedToken []byte, expected
assert.Contains(t, errMsg, wantErr)
}
}
func (s *integrationMDMTestSuite) TestSilentMigrationGotchas() {
t := s.T()
ctx := context.Background()
host := createOrbitEnrolledHost(t, "darwin", "h1", s.ds)
// set the host as enrolled in a third-party MDM
err := s.ds.SetOrUpdateMDMData(ctx, host.ID, true, true, "https://foo.com", false, fleet.WellKnownMDMSimpleMDM, "")
require.NoError(t, err)
var hostResp getHostResponse
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", host.ID), nil, http.StatusOK, &hostResp)
require.NotNil(t, hostResp.Host)
require.NotNil(t, hostResp.Host.MDM.ConnectedToFleet)
require.False(t, *hostResp.Host.MDM.ConnectedToFleet)
// simulate that the device is assigned to Fleet in ABM
s.mockDEPResponse(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
switch r.URL.Path {
case "/session":
_, _ = w.Write([]byte(`{"auth_session_token": "xyz"}`))
case "/profile":
encoder := json.NewEncoder(w)
err := encoder.Encode(godep.ProfileResponse{ProfileUUID: "abc"})
require.NoError(t, err)
case "/server/devices", "/devices/sync":
encoder := json.NewEncoder(w)
err := encoder.Encode(godep.DeviceResponse{
Devices: []godep.Device{
{
SerialNumber: host.HardwareSerial,
Model: "Mac Mini",
OS: "osx",
OpType: "added",
},
},
})
require.NoError(t, err)
case "/profile/devices":
b, err := io.ReadAll(r.Body)
require.NoError(t, err)
var prof profileAssignmentReq
require.NoError(t, json.Unmarshal(b, &prof))
var resp godep.ProfileResponse
resp.ProfileUUID = prof.ProfileUUID
resp.Devices = map[string]string{
prof.Devices[0]: string(fleet.DEPAssignProfileResponseSuccess),
}
encoder := json.NewEncoder(w)
err = encoder.Encode(resp)
require.NoError(t, err)
}
}))
s.runDEPSchedule()
// enable migrations
acResp := appConfigResponse{}
s.DoJSON("PATCH", "/api/v1/fleet/config", json.RawMessage(`{
"mdm": { "macos_migration": { "enable": true, "mode": "voluntary", "webhook_url": "https://example.com" } }
}`), http.StatusOK, &acResp)
// orbit config asks for a migration but not to renew enrollment profile
resp := orbitGetConfigResponse{}
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *host.OrbitNodeKey)), http.StatusOK, &resp)
require.False(t, resp.Notifications.RenewEnrollmentProfile)
require.True(t, resp.Notifications.NeedsMDMMigration)
// simulate that's actually enrolled to Fleet under the hood
mdmDevice := mdmtest.NewTestMDMClientAppleDirect(mdmtest.AppleEnrollInfo{
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)
// host response says that's connected to Fleet
hostResp = getHostResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", host.ID), nil, http.StatusOK, &hostResp)
require.NotNil(t, hostResp.Host)
require.NotNil(t, hostResp.Host.MDM.ConnectedToFleet)
require.False(t, *hostResp.Host.MDM.ConnectedToFleet)
// orbit config asks for a migration because user migrations are enabled, but no ask to renew the enrollment profile.
resp = orbitGetConfigResponse{}
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *host.OrbitNodeKey)), http.StatusOK, &resp)
require.False(t, resp.Notifications.RenewEnrollmentProfile)
require.True(t, resp.Notifications.NeedsMDMMigration)
// set an enroll secret so the fleetd profile is delivered
var applyResp applyEnrollSecretSpecResponse
s.DoJSON("POST", "/api/latest/fleet/spec/enroll_secret", applyEnrollSecretSpecRequest{
Spec: &fleet.EnrollSecretSpec{
Secrets: []*fleet.EnrollSecret{{Secret: t.Name()}},
},
}, http.StatusOK, &applyResp)
// trigger the profile cron
s.awaitTriggerProfileSchedule(t)
installs := [][]byte{}
cmd, err := mdmDevice.Idle()
require.NoError(t, err)
for cmd != nil {
require.Equal(t, "InstallProfile", cmd.Command.RequestType)
installs = append(installs, cmd.Raw)
cmd, err = mdmDevice.Acknowledge(cmd.CommandUUID)
require.NoError(t, err)
}
require.Len(t, installs, 2)
// trigger the scep renewals cron
cert, key, err := generateCertWithAPNsTopic()
require.NoError(t, err)
fleetCfg := config.TestConfig()
config.SetTestMDMConfig(s.T(), &fleetCfg, cert, key, "")
logger := kitlog.NewJSONLogger(os.Stdout)
err = RenewSCEPCertificates(ctx, logger, s.ds, &fleetCfg, s.mdmCommander)
require.NoError(t, err)
// no new commands were enqueued
cmd, err = mdmDevice.Idle()
require.NoError(t, err)
require.Nil(t, cmd)
// set the host as completely unenrolled
err = s.ds.SetOrUpdateMDMData(ctx, host.ID, false, false, "", false, "", "")
require.NoError(t, err)
// orbit config asks to renew the enrollment profile, migration is not needed anymore so it's false
resp = orbitGetConfigResponse{}
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *host.OrbitNodeKey)), http.StatusOK, &resp)
require.True(t, resp.Notifications.RenewEnrollmentProfile)
require.False(t, resp.Notifications.NeedsMDMMigration)
// with migrations disabled, it still asks to renew the enrollment profile
acResp = appConfigResponse{}
s.DoJSON("PATCH", "/api/v1/fleet/config", json.RawMessage(`{
"mdm": { "macos_migration": { "enable": false } }
}`), http.StatusOK, &acResp)
resp = orbitGetConfigResponse{}
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *host.OrbitNodeKey)), http.StatusOK, &resp)
require.True(t, resp.Notifications.RenewEnrollmentProfile)
require.False(t, resp.Notifications.NeedsMDMMigration)
}