Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue related to verification of Windows disk encryption #25875

Merged
merged 3 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changes/25273-hde-windows-verifying
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Fixed issue where Windows disk encryption where status updates from "Verifying" to "Verified" were
sometimes stuck in the "Verifying" state.
2 changes: 1 addition & 1 deletion server/datastore/mysql/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -3829,7 +3829,7 @@ func (ds *Datastore) SetOrUpdateHostDisksSpace(ctx context.Context, hostID uint,
func (ds *Datastore) SetOrUpdateHostDisksEncryption(ctx context.Context, hostID uint, encrypted bool) error {
return ds.updateOrInsert(
ctx,
`UPDATE host_disks SET encrypted = ? WHERE host_id = ?`,
`UPDATE host_disks SET encrypted = ?, updated_at = CURRENT_TIMESTAMP(6) WHERE host_id = ?`,
`INSERT INTO host_disks (encrypted, host_id) VALUES (?, ?)`,
encrypted, hostID,
)
Expand Down
80 changes: 78 additions & 2 deletions server/datastore/mysql/microsoft_mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,52 @@ func testMDMWindowsDiskEncryption(t *testing.T, ds *Datastore) {
// Check that filtered lists do include macOS hosts
checkListHostsFilterDiskEncryption(t, nil, fleet.DiskEncryptionFailed, []uint{hosts[1].ID, hosts[5].ID})
checkListHostsFilterOSSettings(t, nil, fleet.OSSettingsFailed, []uint{hosts[1].ID, hosts[5].ID})

// delete the macOS host profile
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_apple_profiles WHERE host_uuid = ? AND profile_identifier = ?`, hosts[5].UUID, mobileconfig.FleetFileVaultPayloadIdentifier)
return err
})
})

t.Run("BitLocker host disks must update to transition from Verifying to Verified", func(t *testing.T) {
// we'll use hosts[4] as the target for this test
targetHost := hosts[4]

// confirm our initial state is as expected from previous tests
// hosts[2] is was transferred to a team and is not counted
// hosts[3] is a Windows server and is not counted
checkExpected(t, nil, hostIDsByDEStatus{
fleet.DiskEncryptionVerified: []uint{hosts[0].ID},
fleet.DiskEncryptionFailed: []uint{hosts[1].ID},
fleet.DiskEncryptionEnforcing: []uint{targetHost.ID}, // targetHost is initially enforcing
})

// simulate targetHost previously reported encrypted for disk encryption detail query
// results
require.NoError(t, ds.SetOrUpdateHostDisksEncryption(ctx, targetHost.ID, true))
// manualy update host_disks for targetHost to encrypted and ensure updated_at
// timestamp is in the past
updateHostDisks(t, targetHost.ID, true, time.Now().Add(-3*time.Hour))

// simulate targetHost reporting disk encryption key
require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, targetHost, "test-key", "", ptr.Bool(true)))

// check that targetHost is now counted as verifying (not verified because host_disks still needs to be updated)
checkExpected(t, nil, hostIDsByDEStatus{
fleet.DiskEncryptionVerified: []uint{hosts[0].ID},
fleet.DiskEncryptionFailed: []uint{hosts[1].ID},
fleet.DiskEncryptionVerifying: []uint{targetHost.ID},
})

// simulate targetHost reporting detail query results for disk encryption
require.NoError(t, ds.SetOrUpdateHostDisksEncryption(ctx, targetHost.ID, true))
// status for targetHost now verified because SetOrUpdateHostDisksEncryption always sets host_disks.updated_at
// to the current timestamp even if the `encrypted` value hasn't changed
checkExpected(t, nil, hostIDsByDEStatus{
fleet.DiskEncryptionVerified: []uint{hosts[0].ID, targetHost.ID},
fleet.DiskEncryptionFailed: []uint{hosts[1].ID},
})
})
})
}
Expand Down Expand Up @@ -863,6 +909,36 @@ func testMDMWindowsProfilesSummary(t *testing.T, ds *Datastore) {
cleanupTables(t)
})

t.Run("BitLocker host disks must update to transition from Verifying to Verified", func(t *testing.T) {
// all hosts are pending because no profiles and disk encryption is enabled
checkExpected(t, nil, hostIDsByProfileStatus{
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[1].ID, hosts[2].ID, hosts[3].ID, hosts[4].ID},
})

// simulate host already has encrypted disks
require.NoError(t, ds.SetOrUpdateHostDisksEncryption(ctx, hosts[0].ID, true))
// manualy update host_disks for hosts[0] to encrypted and ensure updated_at
// timestamp is in the past
updateHostDisks(t, hosts[0].ID, true, time.Now().Add(-2*time.Hour))

require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0], "test-key", "", ptr.Bool(true)))
// status is verifying because hosts_disks hasn't been updated again
checkExpected(t, nil, hostIDsByProfileStatus{
fleet.MDMDeliveryVerifying: []uint{hosts[0].ID},
fleet.MDMDeliveryPending: []uint{hosts[1].ID, hosts[2].ID, hosts[3].ID, hosts[4].ID},
})

require.NoError(t, ds.SetOrUpdateHostDisksEncryption(ctx, hosts[0].ID, true))
// status for hosts[0] now verified because SetOrUpdateHostDisksEncryption always sets host_disks.updated_at
// to the current timestamp even if the `encrypted` value hasn't changed
checkExpected(t, nil, hostIDsByProfileStatus{
fleet.MDMDeliveryVerified: []uint{hosts[0].ID},
fleet.MDMDeliveryPending: []uint{hosts[1].ID, hosts[2].ID, hosts[3].ID, hosts[4].ID},
})

cleanupTables(t)
})

t.Run("bitlocker failed", func(t *testing.T) {
expected := hostIDsByProfileStatus{
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[1].ID, hosts[2].ID, hosts[3].ID, hosts[4].ID},
Expand Down Expand Up @@ -2460,11 +2536,11 @@ VALUES (?, 'pending', 'install', ?, 'disable-onedrive', ?)`, enrolledDevice2.Hos
atomicCommandUUID)
})
assert.Empty(t, count, "All devices have responded, so the command should be completely removed from the queue")

}

func createResponseAsEnrichedSyncML(t *testing.T, enrolledDevice *fleet.MDMWindowsEnrolledDevice, atomicCommandUUID string,
replaceCommandUUID string) fleet.EnrichedSyncML {
replaceCommandUUID string,
) fleet.EnrichedSyncML {
rawResponse := fmt.Sprintf(`
<SyncML
xmlns="SYNCML:SYNCML1.2">
Expand Down
Loading