Immediately ask for a host refetch when a host re-enrolls and reuses an existing host row (#29081)

This commit is contained in:
Martin Angers 2025-05-14 09:38:53 -04:00 committed by GitHub
parent 7c88ed8a15
commit 7f5fc14f59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 99 additions and 6 deletions

View file

@ -0,0 +1 @@
* Fixed a bug where a host that was wiped and re-enrolled without deleting the corresponding host row in Fleet had its old Google Chrome profiles (and other osquery-based data) showing for about an hour.

View file

@ -1987,6 +1987,7 @@ func (ds *Datastore) EnrollOrbit(ctx context.Context, isMDMEnabled bool, hostInf
"host_id", enrolledHostInfo.ID,
)
}
refetchRequested := fleet.PlatformSupportsOsquery(enrolledHostInfo.Platform)
sqlUpdate := `
UPDATE
@ -1996,9 +1997,10 @@ func (ds *Datastore) EnrollOrbit(ctx context.Context, isMDMEnabled bool, hostInf
uuid = COALESCE(NULLIF(uuid, ''), ?),
osquery_host_id = COALESCE(NULLIF(osquery_host_id, ''), ?),
hardware_serial = COALESCE(NULLIF(hardware_serial, ''), ?),
computer_name = COALESCE(NULLIF(computer_name, ''), ?),
hardware_model = COALESCE(NULLIF(hardware_model, ''), ?),
team_id = ?
computer_name = COALESCE(NULLIF(computer_name, ''), ?),
hardware_model = COALESCE(NULLIF(hardware_model, ''), ?),
team_id = ?,
refetch_requested = ?
WHERE id = ?`
_, err := tx.ExecContext(ctx, sqlUpdate,
orbitNodeKey,
@ -2008,6 +2010,7 @@ func (ds *Datastore) EnrollOrbit(ctx context.Context, isMDMEnabled bool, hostInf
hostInfo.ComputerName,
hostInfo.HardwareModel,
teamID,
refetchRequested,
enrolledHostInfo.ID,
)
if err != nil {
@ -2171,6 +2174,8 @@ func (ds *Datastore) EnrollHost(ctx context.Context, isMDMEnabled bool, osqueryH
return ctxerr.Wrap(ctx, err, "error clearing host_mdm_actions")
}
refetchRequested := fleet.PlatformSupportsOsquery(enrolledHostInfo.Platform)
// Update existing host record
sqlUpdate := `
UPDATE hosts
@ -2179,10 +2184,11 @@ func (ds *Datastore) EnrollHost(ctx context.Context, isMDMEnabled bool, osqueryH
last_enrolled_at = NOW(),
osquery_host_id = ?,
uuid = COALESCE(NULLIF(uuid, ''), ?),
hardware_serial = COALESCE(NULLIF(hardware_serial, ''), ?)
hardware_serial = COALESCE(NULLIF(hardware_serial, ''), ?),
refetch_requested = ?
WHERE id = ?
`
_, err := tx.ExecContext(ctx, sqlUpdate, nodeKey, teamID, osqueryHostID, hardwareUUID, hardwareSerial, enrolledHostInfo.ID)
_, err := tx.ExecContext(ctx, sqlUpdate, nodeKey, teamID, osqueryHostID, hardwareUUID, hardwareSerial, refetchRequested, enrolledHostInfo.ID)
if err != nil {
return ctxerr.Wrap(ctx, err, "update host")
}

View file

@ -862,7 +862,12 @@ func (h *Host) FleetPlatform() string {
// SupportsOsquery returns whether the device runs osquery.
func (h *Host) SupportsOsquery() bool {
return h.Platform != "ios" && h.Platform != "ipados" && h.Platform != "android"
return PlatformSupportsOsquery(h.Platform)
}
// PlatformSupportsOsquery returns whether osquery is supported on this platform.
func PlatformSupportsOsquery(platform string) bool {
return platform != "ios" && platform != "ipados" && platform != "android"
}
// HostLinuxOSs are the possible linux values for Host.Platform.

View file

@ -13260,3 +13260,84 @@ func (s *integrationTestSuite) TestHostCertificates() {
})
}
}
func (s *integrationTestSuite) TestHostReenrollWithSameHostRowRefetchOsquery() {
t := s.T()
// create a mac, linux and windows host
host1 := createOrbitEnrolledHost(t, "darwin", "host1", s.ds)
host2 := createOrbitEnrolledHost(t, "linux", "host2", s.ds)
host3 := createOrbitEnrolledHost(t, "windows", "host3", s.ds)
// set a chrome profile for each host
for i, h := range []*fleet.Host{host1, host2, host3} {
distributedReq := submitDistributedQueryResultsRequestShim{
NodeKey: *h.NodeKey,
Results: map[string]json.RawMessage{
hostDetailQueryPrefix + "google_chrome_profiles": json.RawMessage(fmt.Sprintf(
`[{"email": "%s"}]`, fmt.Sprintf("user%d@example.com", i))),
},
Statuses: map[string]interface{}{
hostDistributedQueryPrefix + "google_chrome_profiles": 0,
},
Messages: map[string]string{},
Stats: map[string]*fleet.Stats{},
}
distributedResp := submitDistributedQueryResultsResponse{}
s.DoJSON("POST", "/api/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
}
oldHosts := make([]fleet.Host, 3)
for i, h := range []*fleet.Host{host1, host2, host3} {
var hostResponse getHostResponse
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", h.ID), nil, http.StatusOK, &hostResponse)
require.False(t, hostResponse.Host.RefetchRequested)
require.Len(t, hostResponse.Host.EndUsers, 1)
require.Len(t, hostResponse.Host.EndUsers[0].OtherEmails, 1)
require.Equal(t, hostResponse.Host.EndUsers[0].OtherEmails[0].Source, "google_chrome_profiles")
oldHosts[i] = hostResponse.Host.Host
}
// do an orbit re-enrollment of the hosts, should set refetch requested
orbitKey := setOrbitEnrollment(t, host1, s.ds)
host1.OrbitNodeKey = &orbitKey
orbitKey = setOrbitEnrollment(t, host2, s.ds)
host2.OrbitNodeKey = &orbitKey
orbitKey = setOrbitEnrollment(t, host3, s.ds)
host3.OrbitNodeKey = &orbitKey
for i, h := range []*fleet.Host{host1, host2, host3} {
var hostResponse getHostResponse
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", h.ID), nil, http.StatusOK, &hostResponse)
require.True(t, hostResponse.Host.RefetchRequested)
require.Len(t, hostResponse.Host.EndUsers, 1)
require.Len(t, hostResponse.Host.EndUsers[0].OtherEmails, 1)
require.Equal(t, hostResponse.Host.EndUsers[0].OtherEmails[0].Source, "google_chrome_profiles")
require.Equal(t, oldHosts[i].ID, h.ID)
}
// send a response for the refetch request
for _, h := range []*fleet.Host{host1, host2, host3} {
distributedReq := submitDistributedQueryResultsRequestShim{
NodeKey: *h.NodeKey,
Results: map[string]json.RawMessage{
hostDetailQueryPrefix + "google_chrome_profiles": json.RawMessage(`[]`),
},
Statuses: map[string]interface{}{
hostDistributedQueryPrefix + "google_chrome_profiles": 0,
},
Messages: map[string]string{},
Stats: map[string]*fleet.Stats{},
}
distributedResp := submitDistributedQueryResultsResponse{}
s.DoJSON("POST", "/api/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
}
for i, h := range []*fleet.Host{host1, host2, host3} {
var hostResponse getHostResponse
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", h.ID), nil, http.StatusOK, &hostResponse)
require.False(t, hostResponse.Host.RefetchRequested)
require.Len(t, hostResponse.Host.EndUsers, 0)
require.Equal(t, oldHosts[i].ID, h.ID)
}
}