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:
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user