mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 08:58:41 +00:00
Clear host_mdm table row when existing Windows hosts enroll as a different OS (#28463)
For https://github.com/fleetdm/fleet/issues/27501 . We wanted the fix to be as simple and targeted as possible so I made it only happen when an existing Windows host enrolls as a different OS. # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/Committing-Changes.md#changes-files) for more information. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [x] Added/updated automated tests - [x] A detailed QA plan exists on the associated ticket (if it isn't there, work with the product group's QA engineer to add it) - [x] Manual QA for all new/changed functionality
This commit is contained in:
parent
1df3270545
commit
97d261968b
3 changed files with 99 additions and 2 deletions
1
changes/27501-clear-windows-mdm-info-on-linux-enroll
Normal file
1
changes/27501-clear-windows-mdm-info-on-linux-enroll
Normal file
|
|
@ -0,0 +1 @@
|
|||
Windows hosts which re-enroll as another OS such as Linux will no longer show Windows MDM information on the hosts page unless they later re-enroll as a Windows host again(i.e. when dual booting).
|
||||
|
|
@ -1859,6 +1859,8 @@ type enrolledHostInfo struct {
|
|||
// NodeKeySet indicates whether `node_key` is set (NOT NULL) for a osquery host
|
||||
// or if `orbit_node_key` is set (NOT NULL) for a orbit host.
|
||||
NodeKeySet bool
|
||||
// Platform is the OS of the host.
|
||||
Platform string
|
||||
}
|
||||
|
||||
// Attempts to find the matching host ID by osqueryID, host UUID or serial
|
||||
|
|
@ -1887,6 +1889,7 @@ func matchHostDuringEnrollment(
|
|||
LastEnrolledAt time.Time `db:"last_enrolled_at"`
|
||||
NodeKeySet bool `db:"node_key_set"`
|
||||
Priority int
|
||||
Platform string `db:"platform"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -1902,7 +1905,7 @@ func matchHostDuringEnrollment(
|
|||
}
|
||||
|
||||
if osqueryID != "" || uuid != "" {
|
||||
_, _ = query.WriteString(fmt.Sprintf(`(SELECT id, last_enrolled_at, %s IS NOT NULL AS node_key_set, 1 priority FROM hosts WHERE osquery_host_id = ?)`, nodeKeyColumn))
|
||||
_, _ = query.WriteString(fmt.Sprintf(`(SELECT id, last_enrolled_at, %s IS NOT NULL AS node_key_set, 1 priority, platform FROM hosts WHERE osquery_host_id = ?)`, nodeKeyColumn))
|
||||
osqueryHostID := osqueryID
|
||||
if osqueryID == "" {
|
||||
// special-case, if there's no osquery identifier, use the uuid
|
||||
|
|
@ -1918,7 +1921,7 @@ func matchHostDuringEnrollment(
|
|||
if query.Len() > 0 {
|
||||
_, _ = query.WriteString(" UNION ")
|
||||
}
|
||||
_, _ = query.WriteString(fmt.Sprintf(`(SELECT id, last_enrolled_at, %s IS NOT NULL AS node_key_set, 2 priority FROM hosts WHERE hardware_serial = ? AND (platform = 'darwin' OR platform = 'ios' OR platform = 'ipados') ORDER BY id LIMIT 1)`, nodeKeyColumn))
|
||||
_, _ = query.WriteString(fmt.Sprintf(`(SELECT id, last_enrolled_at, %s IS NOT NULL AS node_key_set, 2 priority, platform FROM hosts WHERE hardware_serial = ? AND (platform = 'darwin' OR platform = 'ios' OR platform = 'ipados') ORDER BY id LIMIT 1)`, nodeKeyColumn))
|
||||
args = append(args, serial)
|
||||
}
|
||||
|
||||
|
|
@ -1936,6 +1939,7 @@ func matchHostDuringEnrollment(
|
|||
ID: rows[0].ID,
|
||||
LastEnrolledAt: rows[0].LastEnrolledAt,
|
||||
NodeKeySet: rows[0].NodeKeySet,
|
||||
Platform: rows[0].Platform,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -2015,6 +2019,14 @@ func (ds *Datastore) EnrollOrbit(ctx context.Context, isMDMEnabled bool, hostInf
|
|||
return ctxerr.Wrap(ctx, err, "orbit enroll error clearing host_mdm_actions")
|
||||
}
|
||||
|
||||
// Clear host_mdm table row for this host if switching from Windows to non-Windows(e.g. Ubuntu) platform
|
||||
// so that hosts that get re-imaged with other OS don't show erroneous MDM status
|
||||
if enrolledHostInfo.Platform == "windows" && hostInfo.Platform != "" && hostInfo.Platform != "windows" {
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM host_mdm WHERE host_id = ?`, enrolledHostInfo.ID); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "orbit enroll error clearing host_mdm entry on platform change re-enrollment")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -8848,6 +8848,90 @@ func testHostsEnrollOrbit(t *testing.T, ds *Datastore) {
|
|||
scenarioD(platform)
|
||||
})
|
||||
}
|
||||
|
||||
// Scenario E:
|
||||
// - Fleet with MDM enabled.
|
||||
// - two hosts with the same hardware identifiers (e.g. two cloned VMs) but platform may be different
|
||||
// - fleetd running with host identifier set to uuid (default).
|
||||
// - orbit enrolls first, then osquery
|
||||
// - host_mdm entry exists after first host enrolls
|
||||
// Expected output: The two fleetd instances should be enrolled as one host and if the first host was
|
||||
// platform="windows" and the second host was not, the host_mdm entry should be removed
|
||||
scenarioE := func(fromPlatform, toPlatform string) {
|
||||
dupUUID := uuid.New().String()
|
||||
dupHWSerial := uuid.New().String()
|
||||
|
||||
h1Orbit, err := ds.EnrollOrbit(ctx, false, fleet.OrbitHostInfo{
|
||||
HardwareUUID: dupUUID,
|
||||
HardwareSerial: dupHWSerial,
|
||||
Platform: fromPlatform,
|
||||
}, uuid.New().String(), nil)
|
||||
require.NoError(t, err)
|
||||
h1OrbitFetched, err := ds.Host(ctx, h1Orbit.ID)
|
||||
require.NoError(t, err)
|
||||
time.Sleep(1 * time.Second) // to test the update of last_enrolled_at
|
||||
h1Osquery, err := ds.EnrollHost(ctx, false, dupUUID, dupUUID, dupHWSerial, uuid.New().String(), nil, 0)
|
||||
require.NoError(t, err)
|
||||
h1OsqueryFetched, err := ds.Host(ctx, h1Osquery.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, h1OrbitFetched.LastEnrolledAt, h1OsqueryFetched.LastEnrolledAt)
|
||||
require.Equal(t, h1Orbit.ID, h1Osquery.ID)
|
||||
time.Sleep(1 * time.Second) // to test the update of last_enrolled_at
|
||||
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `INSERT INTO host_mdm(host_id, enrolled, server_url, installed_from_dep, mdm_id, is_server)
|
||||
VALUES(?, 1, 'https://example.com/mdm', 0, ?, 0)`, h1Orbit.ID, h1Orbit.ID+100)
|
||||
return err
|
||||
})
|
||||
h1WithMdmFetched, err := ds.Host(ctx, h1Orbit.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, h1WithMdmFetched.MDM.ServerURL)
|
||||
require.NotNil(t, h1WithMdmFetched.MDM.EnrollmentStatus)
|
||||
|
||||
h2Orbit, err := ds.EnrollOrbit(ctx, false, fleet.OrbitHostInfo{
|
||||
HardwareUUID: dupUUID,
|
||||
HardwareSerial: dupHWSerial,
|
||||
Platform: toPlatform,
|
||||
}, uuid.New().String(), nil)
|
||||
require.NoError(t, err)
|
||||
h2OrbitFetched, err := ds.Host(ctx, h2Orbit.ID)
|
||||
require.NoError(t, err)
|
||||
// orbit should not update last_enrolled_at if re-enrolling (because last_enrolled_at
|
||||
// is to be set by osquery only).
|
||||
require.Equal(t, h1OsqueryFetched.LastEnrolledAt, h2OrbitFetched.LastEnrolledAt)
|
||||
time.Sleep(1 * time.Second) // to test the update of last_enrolled_at
|
||||
h2Osquery, err := ds.EnrollHost(ctx, false, dupUUID, dupUUID, dupHWSerial, uuid.New().String(), nil, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, h2Orbit.ID, h2Osquery.ID)
|
||||
h2OsqueryFetched, err := ds.Host(ctx, h2Osquery.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, h2OrbitFetched.LastEnrolledAt, h2OsqueryFetched.LastEnrolledAt)
|
||||
|
||||
// the hosts compete for the host entry (all have same row id)
|
||||
require.Equal(t, h1Orbit.ID, h2Orbit.ID)
|
||||
require.Equal(t, h1Orbit.ID, h1Osquery.ID)
|
||||
require.Equal(t, h2Orbit.ID, h2Osquery.ID)
|
||||
|
||||
if fromPlatform == "windows" && toPlatform != "windows" {
|
||||
assert.Nil(t, h2OrbitFetched.MDM.EnrollmentStatus)
|
||||
assert.Nil(t, h2OrbitFetched.MDM.ServerURL)
|
||||
} else {
|
||||
require.NotNil(t, h2OrbitFetched.MDM.EnrollmentStatus)
|
||||
assert.Equal(t, *h1WithMdmFetched.MDM.EnrollmentStatus, *h2OrbitFetched.MDM.EnrollmentStatus)
|
||||
require.NotNil(t, h2OrbitFetched.MDM.ServerURL)
|
||||
assert.Equal(t, *h1WithMdmFetched.MDM.ServerURL, *h2OrbitFetched.MDM.ServerURL)
|
||||
}
|
||||
}
|
||||
for _, fromPlatform := range []string{"ubuntu", "windows", "darwin"} {
|
||||
for _, toPlatform := range []string{"ubuntu", "windows", "darwin"} {
|
||||
fromPlatform := fromPlatform
|
||||
toPlatform := toPlatform
|
||||
t.Run("scenarioE_from_"+fromPlatform+"_to_"+toPlatform, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
scenarioE(fromPlatform, toPlatform)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testHostsEnrollUpdatesMissingInfo(t *testing.T, ds *Datastore) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue