mirror of
https://github.com/fleetdm/fleet
synced 2026-05-22 16:39:01 +00:00
Remote Wipe: implement transition of "wiped" back to "unlocked" after re-enrollment (#17217)
This commit is contained in:
parent
b692d7fa05
commit
1710e1c8ef
4 changed files with 75 additions and 2 deletions
|
|
@ -114,6 +114,8 @@ func (svc *Service) LockHost(ctx context.Context, hostID uint) error {
|
|||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host has pending unlock request. Host cannot be locked again until unlock is complete."))
|
||||
case lockWipe.IsPendingWipe():
|
||||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host has pending wipe request. Cannot process lock requests once host is wiped."))
|
||||
case lockWipe.IsWiped():
|
||||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host is wiped. Cannot process lock requests once host is wiped."))
|
||||
case lockWipe.IsLocked():
|
||||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host is already locked.").WithStatus(http.StatusConflict))
|
||||
}
|
||||
|
|
@ -186,6 +188,8 @@ func (svc *Service) UnlockHost(ctx context.Context, hostID uint) (string, error)
|
|||
return "", ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host has pending unlock request. The host will unlock when it comes online."))
|
||||
case lockWipe.IsPendingWipe():
|
||||
return "", ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host has pending wipe request. Cannot process unlock requests once host is wiped."))
|
||||
case lockWipe.IsWiped():
|
||||
return "", ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host is wiped. Cannot process unlock requests once host is wiped."))
|
||||
case lockWipe.IsUnlocked():
|
||||
return "", ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host is already unlocked.").WithStatus(http.StatusConflict))
|
||||
}
|
||||
|
|
@ -275,6 +279,8 @@ func (svc *Service) WipeHost(ctx context.Context, hostID uint) error {
|
|||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host has pending unlock request. Host cannot be wiped until unlock is complete."))
|
||||
case lockWipe.IsPendingWipe():
|
||||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host has pending wipe request. The host will be wiped when it comes online."))
|
||||
case lockWipe.IsLocked():
|
||||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host is locked. Host cannot be wiped when it is locked."))
|
||||
case lockWipe.IsWiped():
|
||||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("host_id", "Host is already wiped.").WithStatus(http.StatusConflict))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -653,6 +653,11 @@ func updateMDMAppleHostDB(
|
|||
return ctxerr.Wrap(ctx, err, "update mdm apple host")
|
||||
}
|
||||
|
||||
// clear any host_mdm_actions following re-enrollment here
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM host_mdm_actions WHERE host_id = ?`, hostID); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "error clearing mdm apple host_mdm_actions")
|
||||
}
|
||||
|
||||
if err := upsertMDMAppleHostMDMInfoDB(ctx, tx, appCfg.ServerSettings, false, hostID); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "ingest mdm apple host upsert MDM info")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1506,9 +1506,9 @@ func filterHostsByVulnerability(sqlstmt string, opt fleet.HostListOptions, param
|
|||
SELECT hs.host_id FROM host_software hs
|
||||
JOIN software_cve sc ON sc.software_id = hs.software_id
|
||||
WHERE sc.cve = ?
|
||||
|
||||
|
||||
UNION
|
||||
|
||||
|
||||
SELECT hos.host_id FROM host_operating_system hos
|
||||
JOIN operating_system_vulnerabilities osv ON osv.operating_system_id = hos.os_id
|
||||
WHERE osv.cve = ?)`
|
||||
|
|
@ -1778,6 +1778,11 @@ func (ds *Datastore) EnrollOrbit(ctx context.Context, isMDMEnabled bool, hostInf
|
|||
}
|
||||
host.ID = hostID
|
||||
|
||||
// clear any host_mdm_actions following re-enrollment here
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM host_mdm_actions WHERE host_id = ?`, hostID); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "orbit enroll error clearing host_mdm_actions")
|
||||
}
|
||||
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
zeroTime := time.Unix(0, 0).Add(24 * time.Hour)
|
||||
// Create new host record. We always create newly enrolled hosts with refetch_requested = true
|
||||
|
|
@ -1900,6 +1905,11 @@ func (ds *Datastore) EnrollHost(ctx context.Context, isMDMEnabled bool, osqueryH
|
|||
return ctxerr.Wrap(ctx, err, "cleanup policy membership on re-enroll")
|
||||
}
|
||||
|
||||
// clear any host_mdm_actions following re-enrollment here
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM host_mdm_actions WHERE host_id = ?`, matchedID); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "error clearing host_mdm_actions")
|
||||
}
|
||||
|
||||
// Update existing host record
|
||||
sqlUpdate := `
|
||||
UPDATE hosts
|
||||
|
|
|
|||
|
|
@ -11067,6 +11067,10 @@ func (s *integrationMDMTestSuite) TestLockUnlockWipeWindowsLinux() {
|
|||
|
||||
// try to lock the host again
|
||||
s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/lock", host.ID), nil, http.StatusConflict)
|
||||
// try to wipe a locked host
|
||||
res = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/wipe", host.ID), nil, http.StatusUnprocessableEntity)
|
||||
errMsg = extractServerErrorText(res.Body)
|
||||
require.Contains(t, errMsg, "Host cannot be wiped when it is locked.")
|
||||
|
||||
// unlock the host
|
||||
s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/unlock", host.ID), nil, http.StatusNoContent)
|
||||
|
|
@ -11176,10 +11180,35 @@ func (s *integrationMDMTestSuite) TestLockUnlockWipeWindowsLinux() {
|
|||
require.NotNil(t, getHostResp.Host.MDM.PendingAction)
|
||||
require.Equal(t, "", *getHostResp.Host.MDM.PendingAction)
|
||||
|
||||
// try to lock/unlock the host fails
|
||||
res = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/lock", host.ID), nil, http.StatusUnprocessableEntity)
|
||||
errMsg = extractServerErrorText(res.Body)
|
||||
require.Contains(t, errMsg, "Cannot process lock requests once host is wiped.")
|
||||
res = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/unlock", host.ID), nil, http.StatusUnprocessableEntity)
|
||||
errMsg = extractServerErrorText(res.Body)
|
||||
require.Contains(t, errMsg, "Cannot process unlock requests once host is wiped.")
|
||||
|
||||
// try to wipe the host again, conflict (already wiped)
|
||||
s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/wipe", host.ID), nil, http.StatusConflict)
|
||||
// no activity created
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeWipedHost{}.ActivityName(), fmt.Sprintf(`{"host_id": %d, "host_display_name": %q}`, host.ID, host.DisplayName()), wipeActID)
|
||||
|
||||
// re-enroll the host, simulating that another user received the wiped host
|
||||
newOrbitKey := uuid.New().String()
|
||||
newHost, err := s.ds.EnrollOrbit(ctx, true, fleet.OrbitHostInfo{
|
||||
HardwareUUID: *host.OsqueryHostID,
|
||||
HardwareSerial: host.HardwareSerial,
|
||||
}, newOrbitKey, nil)
|
||||
require.NoError(t, err)
|
||||
// it re-enrolled using the same host record
|
||||
require.Equal(t, host.ID, newHost.ID)
|
||||
|
||||
// refresh the host's status, it is back to unlocked
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", host.ID), nil, http.StatusOK, &getHostResp)
|
||||
require.NotNil(t, getHostResp.Host.MDM.DeviceStatus)
|
||||
require.Equal(t, "unlocked", *getHostResp.Host.MDM.DeviceStatus)
|
||||
require.NotNil(t, getHostResp.Host.MDM.PendingAction)
|
||||
require.Equal(t, "", *getHostResp.Host.MDM.PendingAction)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -11232,6 +11261,10 @@ func (s *integrationMDMTestSuite) TestLockUnlockWipeMacOS() {
|
|||
|
||||
// try to lock the host again
|
||||
s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/lock", host.ID), nil, http.StatusConflict)
|
||||
// try to wipe a locked host
|
||||
res = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/wipe", host.ID), nil, http.StatusUnprocessableEntity)
|
||||
errMsg = extractServerErrorText(res.Body)
|
||||
require.Contains(t, errMsg, "Host cannot be wiped when it is locked.")
|
||||
|
||||
// unlock the host
|
||||
unlockResp = unlockHostResponse{}
|
||||
|
|
@ -11304,10 +11337,29 @@ func (s *integrationMDMTestSuite) TestLockUnlockWipeMacOS() {
|
|||
require.NotNil(t, getHostResp.Host.MDM.PendingAction)
|
||||
require.Equal(t, "", *getHostResp.Host.MDM.PendingAction)
|
||||
|
||||
// try to lock/unlock the host fails
|
||||
res = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/lock", host.ID), nil, http.StatusUnprocessableEntity)
|
||||
errMsg = extractServerErrorText(res.Body)
|
||||
require.Contains(t, errMsg, "Cannot process lock requests once host is wiped.")
|
||||
res = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/unlock", host.ID), nil, http.StatusUnprocessableEntity)
|
||||
errMsg = extractServerErrorText(res.Body)
|
||||
require.Contains(t, errMsg, "Cannot process unlock requests once host is wiped.")
|
||||
|
||||
// try to wipe the host again, conflict (already wiped)
|
||||
s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/wipe", host.ID), nil, http.StatusConflict)
|
||||
// no activity created
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeWipedHost{}.ActivityName(), fmt.Sprintf(`{"host_id": %d, "host_display_name": %q}`, host.ID, host.DisplayName()), wipeActID)
|
||||
|
||||
// re-enroll the host, simulating that another user received the wiped host
|
||||
err = mdmClient.Enroll()
|
||||
require.NoError(t, err)
|
||||
|
||||
// refresh the host's status, it is back to unlocked
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", host.ID), nil, http.StatusOK, &getHostResp)
|
||||
require.NotNil(t, getHostResp.Host.MDM.DeviceStatus)
|
||||
require.Equal(t, "unlocked", *getHostResp.Host.MDM.DeviceStatus)
|
||||
require.NotNil(t, getHostResp.Host.MDM.PendingAction)
|
||||
require.Equal(t, "", *getHostResp.Host.MDM.PendingAction)
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestZCustomConfigurationWebURL() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue