From 40b7f4d035812bc94352ee108c4ea204c96e70e7 Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky Date: Wed, 22 Jan 2025 14:54:40 -0600 Subject: [PATCH] Disk encryption keys are now archived when created/updated (#25638) For #25609 Manual QA in progress. Putting this "In Review" since it is a P1. Video explaining the PR: https://youtu.be/bUwIdjBLqiM - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. - [x] If database migrations are included, checked table schema to confirm autoupdate - For database migrations: - [x] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [x] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. - [x] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). - [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 --- changes/25609-archive-encryption-keys | 1 + server/datastore/mysql/apple_mdm.go | 38 --- server/datastore/mysql/apple_mdm_test.go | 19 +- server/datastore/mysql/disk_encryption.go | 305 ++++++++++++++++++ .../datastore/mysql/disk_encryption_test.go | 35 ++ server/datastore/mysql/hosts.go | 141 -------- server/datastore/mysql/hosts_test.go | 95 ++++-- server/datastore/mysql/labels_test.go | 6 +- server/datastore/mysql/linux_mdm_test.go | 10 +- server/datastore/mysql/microsoft_mdm.go | 2 +- server/datastore/mysql/microsoft_mdm_test.go | 12 +- ...094045_AddHostDiskEncryptionKeysArchive.go | 68 ++++ ...5_AddHostDiskEncryptionKeysArchive_test.go | 60 ++++ server/datastore/mysql/schema.sql | 26 +- server/fleet/datastore.go | 4 +- server/mock/datastore_mock.go | 12 +- server/service/integration_mdm_test.go | 14 +- server/service/orbit.go | 4 +- server/service/orbit_test.go | 5 +- server/service/osquery_utils/queries.go | 4 +- server/service/osquery_utils/queries_test.go | 5 +- 21 files changed, 599 insertions(+), 267 deletions(-) create mode 100644 changes/25609-archive-encryption-keys create mode 100644 server/datastore/mysql/disk_encryption.go create mode 100644 server/datastore/mysql/disk_encryption_test.go create mode 100644 server/datastore/mysql/migrations/tables/20250121094045_AddHostDiskEncryptionKeysArchive.go create mode 100644 server/datastore/mysql/migrations/tables/20250121094045_AddHostDiskEncryptionKeysArchive_test.go diff --git a/changes/25609-archive-encryption-keys b/changes/25609-archive-encryption-keys new file mode 100644 index 000000000000..a3848afbd53a --- /dev/null +++ b/changes/25609-archive-encryption-keys @@ -0,0 +1 @@ +Disk encryption keys are now archived when they are created or updated. They are never fully deleted from the database. diff --git a/server/datastore/mysql/apple_mdm.go b/server/datastore/mysql/apple_mdm.go index 8991eac117f1..601b75fa70be 100644 --- a/server/datastore/mysql/apple_mdm.go +++ b/server/datastore/mysql/apple_mdm.go @@ -3551,27 +3551,6 @@ func (ds *Datastore) CleanupUnusedBootstrapPackages(ctx context.Context, pkgStor return ctxerr.Wrap(ctx, err, "cleanup unused bootstrap packages") } -func (ds *Datastore) CleanupDiskEncryptionKeysOnTeamChange(ctx context.Context, hostIDs []uint, newTeamID *uint) error { - return ds.withTx(ctx, func(tx sqlx.ExtContext) error { - return cleanupDiskEncryptionKeysOnTeamChangeDB(ctx, tx, hostIDs, newTeamID) - }) -} - -func cleanupDiskEncryptionKeysOnTeamChangeDB(ctx context.Context, tx sqlx.ExtContext, hostIDs []uint, newTeamID *uint) error { - _, err := getMDMAppleConfigProfileByTeamAndIdentifierDB(ctx, tx, newTeamID, mobileconfig.FleetFileVaultPayloadIdentifier) - if err != nil { - if fleet.IsNotFound(err) { - // the new team does not have a filevault profile so we need to delete the existing ones - if err := bulkDeleteHostDiskEncryptionKeysDB(ctx, tx, hostIDs); err != nil { - return ctxerr.Wrap(ctx, err, "reconcile filevault profiles on team change bulk delete host disk encryption keys") - } - } else { - return ctxerr.Wrap(ctx, err, "reconcile filevault profiles on team change get profile") - } - } - return nil -} - func getMDMAppleConfigProfileByTeamAndIdentifierDB(ctx context.Context, tx sqlx.QueryerContext, teamID *uint, profileIdentifier string) (*fleet.MDMAppleConfigProfile, error) { if teamID == nil { teamID = ptr.Uint(0) @@ -3603,23 +3582,6 @@ WHERE return &profile, nil } -func bulkDeleteHostDiskEncryptionKeysDB(ctx context.Context, tx sqlx.ExtContext, hostIDs []uint) error { - if len(hostIDs) == 0 { - return nil - } - - query, args, err := sqlx.In( - "DELETE FROM host_disk_encryption_keys WHERE host_id IN (?)", - hostIDs, - ) - if err != nil { - return ctxerr.Wrap(ctx, err, "building query") - } - - _, err = tx.ExecContext(ctx, query, args...) - return err -} - func (ds *Datastore) SetOrUpdateMDMAppleSetupAssistant(ctx context.Context, asst *fleet.MDMAppleSetupAssistant) (*fleet.MDMAppleSetupAssistant, error) { const stmt = ` INSERT INTO diff --git a/server/datastore/mysql/apple_mdm_test.go b/server/datastore/mysql/apple_mdm_test.go index 84a284ac466a..a67e73908df8 100644 --- a/server/datastore/mysql/apple_mdm_test.go +++ b/server/datastore/mysql/apple_mdm_test.go @@ -984,7 +984,7 @@ func testUpdateHostTablesOnMDMUnenroll(t *testing.T, ds *Datastore) { var hostID uint err = sqlx.GetContext(context.Background(), ds.reader(context.Background()), &hostID, `SELECT id FROM hosts WHERE uuid = ?`, testUUID) require.NoError(t, err) - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, hostID, "asdf", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, &fleet.Host{ID: hostID}, "asdf", "", nil) require.NoError(t, err) key, err := ds.GetHostDiskEncryptionKey(ctx, hostID) @@ -1999,7 +1999,7 @@ func testAggregateMacOSSettingsStatusWithFileVault(t *testing.T, ds *Datastore) require.Equal(t, uint(0), res.Verifying) require.Equal(t, uint(0), res.Verified) - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0].ID, "foo", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0], "foo", "", nil) require.NoError(t, err) res, err = ds.GetMDMAppleProfilesSummary(ctx, nil) require.NoError(t, err) @@ -2041,7 +2041,7 @@ func testAggregateMacOSSettingsStatusWithFileVault(t *testing.T, ds *Datastore) require.Equal(t, uint(0), res.Verifying) require.Equal(t, uint(1), res.Verified) // hosts[0] now has filevault fully enforced and verified - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[1].ID, "bar", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[1], "bar", "", nil) require.NoError(t, err) err = ds.SetHostsDiskEncryptionKeyStatus(ctx, []uint{hosts[1].ID}, false, time.Now().Add(1*time.Hour)) require.NoError(t, err) @@ -2107,7 +2107,7 @@ func testAggregateMacOSSettingsStatusWithFileVault(t *testing.T, ds *Datastore) require.Equal(t, uint(0), res.Verifying) require.Equal(t, uint(0), res.Verified) - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[9].ID, "baz", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[9], "baz", "", nil) require.NoError(t, err) err = ds.SetHostsDiskEncryptionKeyStatus(ctx, []uint{hosts[9].ID}, true, time.Now().Add(1*time.Hour)) require.NoError(t, err) @@ -2652,10 +2652,11 @@ func testDeleteMDMAppleProfilesForHost(t *testing.T, ds *Datastore) { require.Nil(t, gotProfs) } -func createDiskEncryptionRecord(ctx context.Context, ds *Datastore, t *testing.T, hostId uint, key string, decryptable bool, threshold time.Time) { - err := ds.SetOrUpdateHostDiskEncryptionKey(ctx, hostId, key, "", nil) +func createDiskEncryptionRecord(ctx context.Context, ds *Datastore, t *testing.T, host *fleet.Host, key string, decryptable bool, + threshold time.Time) { + err := ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, key, "", nil) require.NoError(t, err) - err = ds.SetHostsDiskEncryptionKeyStatus(ctx, []uint{hostId}, decryptable, threshold) + err = ds.SetHostsDiskEncryptionKeyStatus(ctx, []uint{host.ID}, decryptable, threshold) require.NoError(t, err) } @@ -2685,7 +2686,7 @@ func TestMDMAppleFileVaultSummary(t *testing.T) { ctx, ds, t, ) oneMinuteAfterThreshold := time.Now().Add(+1 * time.Minute) - createDiskEncryptionRecord(ctx, ds, t, verifyingHost.ID, "key-1", true, oneMinuteAfterThreshold) + createDiskEncryptionRecord(ctx, ds, t, verifyingHost, "key-1", true, oneMinuteAfterThreshold) fvProfileSummary, err := ds.GetMDMAppleFileVaultSummary(ctx, nil) require.NoError(t, err) @@ -2871,7 +2872,7 @@ func TestMDMAppleFileVaultSummary(t *testing.T) { require.NoError(t, err) upsertHostCPs([]*fleet.Host{verifyingTeam1Host}, []*fleet.MDMAppleConfigProfile{team1FVProfile}, fleet.MDMOperationTypeInstall, &fleet.MDMDeliveryVerifying, ctx, ds, t) - createDiskEncryptionRecord(ctx, ds, t, verifyingTeam1Host.ID, "key-2", true, oneMinuteAfterThreshold) + createDiskEncryptionRecord(ctx, ds, t, verifyingTeam1Host, "key-2", true, oneMinuteAfterThreshold) fvProfileSummary, err = ds.GetMDMAppleFileVaultSummary(ctx, &tm.ID) require.NoError(t, err) diff --git a/server/datastore/mysql/disk_encryption.go b/server/datastore/mysql/disk_encryption.go new file mode 100644 index 000000000000..96f278a68ab9 --- /dev/null +++ b/server/datastore/mysql/disk_encryption.go @@ -0,0 +1,305 @@ +package mysql + +import ( + "context" + "database/sql" + "errors" + "fmt" + "time" + + "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" + "github.com/fleetdm/fleet/v4/server/fleet" + "github.com/fleetdm/fleet/v4/server/mdm/apple/mobileconfig" + "github.com/go-kit/log/level" + "github.com/go-sql-driver/mysql" + "github.com/jmoiron/sqlx" +) + +type encryptionKey struct { + Base string `db:"base64_encrypted"` + Salt string `db:"base64_encrypted_salt"` + KeySlot *uint + CreatedAt time.Time + NotFound bool +} + +func (ds *Datastore) SetOrUpdateHostDiskEncryptionKey(ctx context.Context, host *fleet.Host, encryptedBase64Key, clientError string, + decryptable *bool) error { + + existingKey, err := ds.getExistingHostDiskEncryptionKey(ctx, host) + if err != nil { + return ctxerr.Wrap(ctx, err, "getting existing key, if present") + } + + // We use the same timestamp for base and archive tables so that it can be used as an additional debug tool if needed. + var incomingKey = encryptionKey{Base: encryptedBase64Key, CreatedAt: time.Now().UTC()} + err = ds.archiveHostDiskEncryptionKey(ctx, host, incomingKey, existingKey) + if err != nil { + return ctxerr.Wrap(ctx, err, "archiving key") + } + + if existingKey.NotFound { + _, err = ds.writer(ctx).ExecContext(ctx, ` +INSERT INTO host_disk_encryption_keys + (host_id, base64_encrypted, client_error, decryptable, created_at) +VALUES + (?, ?, ?, ?, ?)`, host.ID, incomingKey.Base, clientError, decryptable, incomingKey.CreatedAt) + if err == nil { + return nil + } + var mysqlErr *mysql.MySQLError + switch { + case errors.As(err, &mysqlErr) && mysqlErr.Number == 1062: + level.Error(ds.logger).Log("msg", "Primary key already exists in host_disk_encryption_keys. Falling back to update", "host_id", + host) + // This should never happen unless there is a bug in the code or an infra issue (like huge replication lag). + default: + return ctxerr.Wrap(ctx, err, "inserting key") + } + } + + _, err = ds.writer(ctx).ExecContext(ctx, ` +UPDATE host_disk_encryption_keys SET + /* if the key has changed, set decrypted to its initial value so it can be calculated again if necessary (if null) */ + decryptable = IF( + base64_encrypted = ? AND base64_encrypted != '', + decryptable, + ? + ), + base64_encrypted = ?, + client_error = ? +WHERE host_id = ? +`, incomingKey.Base, decryptable, incomingKey.Base, clientError, host.ID) + if err != nil { + return ctxerr.Wrap(ctx, err, "updating key") + } + return nil +} + +func (ds *Datastore) getExistingHostDiskEncryptionKey(ctx context.Context, host *fleet.Host) (encryptionKey, error) { + getExistingKeyStmt := `SELECT base64_encrypted, base64_encrypted_salt FROM host_disk_encryption_keys WHERE host_id = ?` + var existingKey encryptionKey + err := sqlx.GetContext(ctx, ds.reader(ctx), &existingKey, getExistingKeyStmt, host.ID) + switch { + case errors.Is(err, sql.ErrNoRows): + // no existing key, proceed to insert + existingKey.NotFound = true + case err != nil: + return encryptionKey{}, ctxerr.Wrap(ctx, err, "getting existing key") + } + return existingKey, nil +} + +func (ds *Datastore) archiveHostDiskEncryptionKey(ctx context.Context, host *fleet.Host, incomingKey encryptionKey, + existingKey encryptionKey) error { + // We archive only valid and different keys to reduce noise. + if (incomingKey.Base != "" && existingKey.Base != incomingKey.Base) || + (incomingKey.Salt != "" && existingKey.Salt != incomingKey.Salt) { + const insertKeyIntoArchiveStmt = ` +INSERT INTO host_disk_encryption_keys_archive (host_id, hardware_serial, base64_encrypted, base64_encrypted_salt, key_slot, created_at) +VALUES (?, ?, ?, ?, ?, ?)` + _, err := ds.writer(ctx).ExecContext(ctx, insertKeyIntoArchiveStmt, host.ID, host.HardwareSerial, incomingKey.Base, + incomingKey.Salt, + incomingKey.KeySlot, incomingKey.CreatedAt) + if err != nil { + return ctxerr.Wrap(ctx, err, "inserting key into archive") + } + } + return nil +} + +func (ds *Datastore) SaveLUKSData(ctx context.Context, host *fleet.Host, encryptedBase64Passphrase string, encryptedBase64Salt string, + keySlot uint) error { + if encryptedBase64Passphrase == "" || encryptedBase64Salt == "" { // should have been caught at service level + return errors.New("passphrase and salt must be set") + } + + existingKey, err := ds.getExistingHostDiskEncryptionKey(ctx, host) + if err != nil { + return ctxerr.Wrap(ctx, err, "getting existing LUKS key, if present") + } + + // We use the same timestamp for base and archive tables so that it can be used as an additional debug tool if needed. + var incomingKey = encryptionKey{Base: encryptedBase64Passphrase, Salt: encryptedBase64Salt, KeySlot: &keySlot, + CreatedAt: time.Now().UTC()} + err = ds.archiveHostDiskEncryptionKey(ctx, host, incomingKey, existingKey) + if err != nil { + return ctxerr.Wrap(ctx, err, "archiving LUKS key") + } + + if existingKey.NotFound { + _, err = ds.writer(ctx).ExecContext(ctx, ` +INSERT INTO host_disk_encryption_keys + (host_id, base64_encrypted, base64_encrypted_salt, key_slot, decryptable, created_at) +VALUES + (?, ?, ?, ?, TRUE, ?)`, host.ID, incomingKey.Base, incomingKey.Salt, incomingKey.KeySlot, incomingKey.CreatedAt) + if err == nil { + return nil + } + var mysqlErr *mysql.MySQLError + switch { + case errors.As(err, &mysqlErr) && mysqlErr.Number == 1062: + level.Error(ds.logger).Log("msg", "Primary key already exists in LUKS host_disk_encryption_keys. Falling back to update", + "host_id", + host) + // This should never happen unless there is a bug in the code or an infra issue (like huge replication lag). + default: + return ctxerr.Wrap(ctx, err, "inserting LUKS key") + } + } + + _, err = ds.writer(ctx).ExecContext(ctx, ` +UPDATE host_disk_encryption_keys SET + /* if the key has changed, set decrypted to its initial value so it can be calculated again if necessary (if null) */ + decryptable = TRUE, + base64_encrypted = ?, + base64_encrypted_salt = ?, + key_slot = ?, + client_error = '' +WHERE host_id = ? +`, incomingKey.Base, incomingKey.Salt, incomingKey.KeySlot, host.ID) + if err != nil { + return ctxerr.Wrap(ctx, err, "updating LUKS key") + } + return nil + +} + +func (ds *Datastore) IsHostPendingEscrow(ctx context.Context, hostID uint) bool { + var pendingEscrowCount uint + _ = sqlx.GetContext(ctx, ds.reader(ctx), &pendingEscrowCount, ` + SELECT COUNT(*) FROM host_disk_encryption_keys WHERE host_id = ? AND reset_requested = TRUE`, hostID) + return pendingEscrowCount > 0 +} + +func (ds *Datastore) ClearPendingEscrow(ctx context.Context, hostID uint) error { + _, err := ds.writer(ctx).ExecContext(ctx, `UPDATE host_disk_encryption_keys SET reset_requested = FALSE WHERE host_id = ?`, hostID) + return err +} + +func (ds *Datastore) ReportEscrowError(ctx context.Context, hostID uint, errorMessage string) error { + _, err := ds.writer(ctx).ExecContext(ctx, ` +INSERT INTO host_disk_encryption_keys + (host_id, base64_encrypted, client_error) VALUES (?, '', ?) ON DUPLICATE KEY UPDATE client_error = VALUES(client_error) +`, hostID, errorMessage) + return err +} + +func (ds *Datastore) QueueEscrow(ctx context.Context, hostID uint) error { + _, err := ds.writer(ctx).ExecContext(ctx, ` +INSERT INTO host_disk_encryption_keys + (host_id, base64_encrypted, reset_requested) VALUES (?, '', TRUE) ON DUPLICATE KEY UPDATE reset_requested = TRUE +`, hostID) + return err +} + +func (ds *Datastore) AssertHasNoEncryptionKeyStored(ctx context.Context, hostID uint) error { + var hasKeyCount uint + err := sqlx.GetContext(ctx, ds.reader(ctx), &hasKeyCount, ` + SELECT COUNT(*) FROM host_disk_encryption_keys WHERE host_id = ? AND base64_encrypted != ''`, hostID) + if hasKeyCount > 0 { + return &fleet.BadRequestError{Message: "Key has already been escrowed for this host"} + } + + return err +} + +func (ds *Datastore) GetUnverifiedDiskEncryptionKeys(ctx context.Context) ([]fleet.HostDiskEncryptionKey, error) { + // NOTE(mna): currently we only verify encryption keys for macOS, + // Windows/bitlocker uses a different approach where orbit sends the + // encryption key and we encrypt it server-side with the WSTEP certificate, + // so it is always decryptable once received. + // + // To avoid sending Windows-related keys to verify as part of this call, we + // only return rows that have a non-empty encryption key (for Windows, the + // key is blanked if an error occurred trying to retrieve it on the host). + var keys []fleet.HostDiskEncryptionKey + err := sqlx.SelectContext(ctx, ds.reader(ctx), &keys, ` + SELECT + base64_encrypted, + host_id, + updated_at + FROM + host_disk_encryption_keys + WHERE + decryptable IS NULL AND + base64_encrypted != '' + `) + return keys, err +} + +func (ds *Datastore) SetHostsDiskEncryptionKeyStatus( + ctx context.Context, + hostIDs []uint, + decryptable bool, + threshold time.Time, +) error { + if len(hostIDs) == 0 { + return nil + } + + query, args, err := sqlx.In( + "UPDATE host_disk_encryption_keys SET decryptable = ? WHERE host_id IN (?) AND updated_at <= ?", + decryptable, hostIDs, threshold, + ) + if err != nil { + return err + } + _, err = ds.writer(ctx).ExecContext(ctx, query, args...) + return err +} + +func (ds *Datastore) GetHostDiskEncryptionKey(ctx context.Context, hostID uint) (*fleet.HostDiskEncryptionKey, error) { + var key fleet.HostDiskEncryptionKey + err := sqlx.GetContext(ctx, ds.reader(ctx), &key, ` + SELECT + host_id, base64_encrypted, decryptable, updated_at, client_error + FROM + host_disk_encryption_keys + WHERE host_id = ?`, hostID) + if err != nil { + if err == sql.ErrNoRows { + msg := fmt.Sprintf("for host %d", hostID) + return nil, ctxerr.Wrap(ctx, notFound("HostDiskEncryptionKey").WithMessage(msg)) + } + return nil, ctxerr.Wrapf(ctx, err, "getting data from host_disk_encryption_keys for host_id %d", hostID) + } + return &key, nil +} + +func (ds *Datastore) CleanupDiskEncryptionKeysOnTeamChange(ctx context.Context, hostIDs []uint, newTeamID *uint) error { + return ds.withTx(ctx, func(tx sqlx.ExtContext) error { + return cleanupDiskEncryptionKeysOnTeamChangeDB(ctx, tx, hostIDs, newTeamID) + }) +} + +func cleanupDiskEncryptionKeysOnTeamChangeDB(ctx context.Context, tx sqlx.ExtContext, hostIDs []uint, newTeamID *uint) error { + // We are using Apple's encryption profile to determine if any hosts, including Windows and Linux, are encrypted. + // This is a safe assumption since encryption is enabled for the whole team. + _, err := getMDMAppleConfigProfileByTeamAndIdentifierDB(ctx, tx, newTeamID, mobileconfig.FleetFileVaultPayloadIdentifier) + if err != nil { + if fleet.IsNotFound(err) { + // the new team does not have a filevault profile so we need to delete the existing ones + if err := bulkDeleteHostDiskEncryptionKeysDB(ctx, tx, hostIDs); err != nil { + return ctxerr.Wrap(ctx, err, "reconcile filevault profiles on team change bulk delete host disk encryption keys") + } + } else { + return ctxerr.Wrap(ctx, err, "reconcile filevault profiles on team change get profile") + } + } + return nil +} + +func bulkDeleteHostDiskEncryptionKeysDB(ctx context.Context, tx sqlx.ExtContext, hostIDs []uint) error { + if len(hostIDs) == 0 { + return nil + } + + deleteStmt, deleteArgs, err := sqlx.In("DELETE FROM host_disk_encryption_keys WHERE host_id IN (?)", hostIDs) + if err != nil { + return ctxerr.Wrap(ctx, err, "building query") + } + + _, err = tx.ExecContext(ctx, deleteStmt, deleteArgs...) + return err +} diff --git a/server/datastore/mysql/disk_encryption_test.go b/server/datastore/mysql/disk_encryption_test.go new file mode 100644 index 000000000000..a55c2f0a0bf3 --- /dev/null +++ b/server/datastore/mysql/disk_encryption_test.go @@ -0,0 +1,35 @@ +package mysql + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDiskEncryption(t *testing.T) { + ds := CreateMySQLDS(t) + + cases := []struct { + name string + fn func(t *testing.T, ds *Datastore) + }{ + {"TestCleanupDiskEncryptionKeysOnTeamChange", testCleanupDiskEncryptionKeysOnTeamChange}, + } + + for _, c := range cases { + t.Helper() + t.Run(c.name, func(t *testing.T) { + defer TruncateTables(t, ds) + + c.fn(t, ds) + }) + } +} + +func testCleanupDiskEncryptionKeysOnTeamChange(t *testing.T, ds *Datastore) { + ctx := context.Background() + + // No-op test + assert.NoError(t, ds.CleanupDiskEncryptionKeysOnTeamChange(ctx, []uint{1, 2, 3}, nil)) +} diff --git a/server/datastore/mysql/hosts.go b/server/datastore/mysql/hosts.go index 9e972faa3d5a..4ddb9ec7e06b 100644 --- a/server/datastore/mysql/hosts.go +++ b/server/datastore/mysql/hosts.go @@ -3826,147 +3826,6 @@ func (ds *Datastore) SetOrUpdateHostDisksEncryption(ctx context.Context, hostID ) } -func (ds *Datastore) SetOrUpdateHostDiskEncryptionKey(ctx context.Context, hostID uint, encryptedBase64Key, clientError string, decryptable *bool) error { - _, err := ds.writer(ctx).ExecContext(ctx, ` -INSERT INTO host_disk_encryption_keys - (host_id, base64_encrypted, client_error, decryptable) -VALUES - (?, ?, ?, ?) -ON DUPLICATE KEY UPDATE - /* if the key has changed, set decrypted to its initial value so it can be calculated again if necessary (if null) */ - decryptable = IF( - base64_encrypted = VALUES(base64_encrypted) AND base64_encrypted != '', - decryptable, - VALUES(decryptable) - ), - base64_encrypted = VALUES(base64_encrypted), - client_error = VALUES(client_error) -`, hostID, encryptedBase64Key, clientError, decryptable) - return err -} - -func (ds *Datastore) SaveLUKSData(ctx context.Context, hostID uint, encryptedBase64Passphrase string, encryptedBase64Salt string, keySlot uint) error { - if encryptedBase64Passphrase == "" || encryptedBase64Salt == "" { // should have been caught at service level - return errors.New("passphrase and salt must be set") - } - - _, err := ds.writer(ctx).ExecContext(ctx, ` -INSERT INTO host_disk_encryption_keys - (host_id, base64_encrypted, base64_encrypted_salt, key_slot, client_error, decryptable) -VALUES - (?, ?, ?, ?, '', TRUE) -ON DUPLICATE KEY UPDATE - decryptable = TRUE, - base64_encrypted = VALUES(base64_encrypted), - base64_encrypted_salt = VALUES(base64_encrypted_salt), - key_slot = VALUES(key_slot), - client_error = '' -`, hostID, encryptedBase64Passphrase, encryptedBase64Salt, keySlot) - return err -} - -func (ds *Datastore) IsHostPendingEscrow(ctx context.Context, hostID uint) bool { - var pendingEscrowCount uint - _ = sqlx.GetContext(ctx, ds.reader(ctx), &pendingEscrowCount, ` - SELECT COUNT(*) FROM host_disk_encryption_keys WHERE host_id = ? AND reset_requested = TRUE`, hostID) - return pendingEscrowCount > 0 -} - -func (ds *Datastore) ClearPendingEscrow(ctx context.Context, hostID uint) error { - _, err := ds.writer(ctx).ExecContext(ctx, `UPDATE host_disk_encryption_keys SET reset_requested = FALSE WHERE host_id = ?`, hostID) - return err -} - -func (ds *Datastore) ReportEscrowError(ctx context.Context, hostID uint, errorMessage string) error { - _, err := ds.writer(ctx).ExecContext(ctx, ` -INSERT INTO host_disk_encryption_keys - (host_id, base64_encrypted, client_error) VALUES (?, '', ?) ON DUPLICATE KEY UPDATE client_error = VALUES(client_error) -`, hostID, errorMessage) - return err -} - -func (ds *Datastore) QueueEscrow(ctx context.Context, hostID uint) error { - _, err := ds.writer(ctx).ExecContext(ctx, ` -INSERT INTO host_disk_encryption_keys - (host_id, base64_encrypted, reset_requested) VALUES (?, '', TRUE) ON DUPLICATE KEY UPDATE reset_requested = TRUE -`, hostID) - return err -} - -func (ds *Datastore) AssertHasNoEncryptionKeyStored(ctx context.Context, hostID uint) error { - var hasKeyCount uint - err := sqlx.GetContext(ctx, ds.reader(ctx), &hasKeyCount, ` - SELECT COUNT(*) FROM host_disk_encryption_keys WHERE host_id = ? AND base64_encrypted != ''`, hostID) - if hasKeyCount > 0 { - return &fleet.BadRequestError{Message: "Key has already been escrowed for this host"} - } - - return err -} - -func (ds *Datastore) GetUnverifiedDiskEncryptionKeys(ctx context.Context) ([]fleet.HostDiskEncryptionKey, error) { - // NOTE(mna): currently we only verify encryption keys for macOS, - // Windows/bitlocker uses a different approach where orbit sends the - // encryption key and we encrypt it server-side with the WSTEP certificate, - // so it is always decryptable once received. - // - // To avoid sending Windows-related keys to verify as part of this call, we - // only return rows that have a non-empty encryption key (for Windows, the - // key is blanked if an error occurred trying to retrieve it on the host). - var keys []fleet.HostDiskEncryptionKey - err := sqlx.SelectContext(ctx, ds.reader(ctx), &keys, ` - SELECT - base64_encrypted, - host_id, - updated_at - FROM - host_disk_encryption_keys - WHERE - decryptable IS NULL AND - base64_encrypted != '' - `) - return keys, err -} - -func (ds *Datastore) SetHostsDiskEncryptionKeyStatus( - ctx context.Context, - hostIDs []uint, - decryptable bool, - threshold time.Time, -) error { - if len(hostIDs) == 0 { - return nil - } - - query, args, err := sqlx.In( - "UPDATE host_disk_encryption_keys SET decryptable = ? WHERE host_id IN (?) AND updated_at <= ?", - decryptable, hostIDs, threshold, - ) - if err != nil { - return err - } - _, err = ds.writer(ctx).ExecContext(ctx, query, args...) - return err -} - -func (ds *Datastore) GetHostDiskEncryptionKey(ctx context.Context, hostID uint) (*fleet.HostDiskEncryptionKey, error) { - var key fleet.HostDiskEncryptionKey - err := sqlx.GetContext(ctx, ds.reader(ctx), &key, ` - SELECT - host_id, base64_encrypted, decryptable, updated_at, client_error - FROM - host_disk_encryption_keys - WHERE host_id = ?`, hostID) - if err != nil { - if err == sql.ErrNoRows { - msg := fmt.Sprintf("for host %d", hostID) - return nil, ctxerr.Wrap(ctx, notFound("HostDiskEncryptionKey").WithMessage(msg)) - } - return nil, ctxerr.Wrapf(ctx, err, "getting data from host_disk_encryption_keys for host_id %d", hostID) - } - return &key, nil -} - func (ds *Datastore) SetOrUpdateHostOrbitInfo( ctx context.Context, hostID uint, version string, desktopVersion sql.NullString, scriptsEnabled sql.NullBool, ) error { diff --git a/server/datastore/mysql/hosts_test.go b/server/datastore/mysql/hosts_test.go index 7b4319c5b0cc..f08c37bf5489 100644 --- a/server/datastore/mysql/hosts_test.go +++ b/server/datastore/mysql/hosts_test.go @@ -907,8 +907,8 @@ func testHostListOptionsTeamFilter(t *testing.T, ds *Datastore) { listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{OSSettingsFilter: fleet.OSSettingsVerifying}, 1) listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{TeamFilter: teamIDFilterZero, OSSettingsFilter: fleet.OSSettingsPending}, 5) // pending supported linux hosts - require.NoError(t, ds.SaveLUKSData(context.Background(), hosts[1].ID, "key1", "morton", 1)) // set host 1 to verified - require.NoError(t, ds.ReportEscrowError(context.Background(), hosts[2].ID, "error")) // set host 2 to failed + require.NoError(t, ds.SaveLUKSData(context.Background(), hosts[1], "key1", "morton", 1)) // set host 1 to verified + require.NoError(t, ds.ReportEscrowError(context.Background(), hosts[2].ID, "error")) // set host 2 to failed listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{TeamFilter: teamIDFilterZero, OSSettingsFilter: fleet.OSSettingsVerified}, 1) // hosts[1] listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{TeamFilter: teamIDFilterZero, OSSettingsFilter: fleet.OSSettingsFailed}, 1) // hosts[2] @@ -941,8 +941,8 @@ func testHostListOptionsTeamFilter(t *testing.T, ds *Datastore) { require.NoError(t, ds.AddHostsToTeam(context.Background(), &team1.ID, []uint{hosts[1].ID, hosts[2].ID, hosts[3].ID, hosts[4].ID, hosts[5].ID})) listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{TeamFilter: &team1.ID, OSSettingsFilter: fleet.OSSettingsPending}, 5) // pending supported linux hosts - require.NoError(t, ds.SaveLUKSData(context.Background(), hosts[1].ID, "key1", "mutton", 2)) // set host 1 to verified - require.NoError(t, ds.ReportEscrowError(context.Background(), hosts[2].ID, "error")) // set host 2 to failed + require.NoError(t, ds.SaveLUKSData(context.Background(), hosts[1], "key1", "mutton", 2)) // set host 1 to verified + require.NoError(t, ds.ReportEscrowError(context.Background(), hosts[2].ID, "error")) // set host 2 to failed listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{TeamFilter: &team1.ID, OSSettingsFilter: fleet.OSSettingsVerified}, 1) // hosts[1] listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{TeamFilter: &team1.ID, OSSettingsFilter: fleet.OSSettingsFailed}, 1) // hosts[2] @@ -3409,7 +3409,7 @@ func testHostsListMacOSSettingsDiskEncryptionStatus(t *testing.T, ds *Datastore) upsertHostCPs([]*fleet.Host{hosts[0], hosts[1]}, []*fleet.MDMAppleConfigProfile{noTeamFVProfile}, fleet.MDMOperationTypeInstall, &fleet.MDMDeliveryVerifying, ctx, ds, t) oneMinuteAfterThreshold := time.Now().Add(+1 * time.Minute) // host 0 needs to finish key rotation (action required), host 1 has finished key rotation but profile is verifying - createDiskEncryptionRecord(ctx, ds, t, hosts[1].ID, "key-1", true, oneMinuteAfterThreshold) + createDiskEncryptionRecord(ctx, ds, t, hosts[1], "key-1", true, oneMinuteAfterThreshold) listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{MacOSSettingsDiskEncryptionFilter: fleet.DiskEncryptionVerifying}, 1) listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{MacOSSettingsDiskEncryptionFilter: fleet.DiskEncryptionVerified}, 0) @@ -3429,7 +3429,7 @@ func testHostsListMacOSSettingsDiskEncryptionStatus(t *testing.T, ds *Datastore) listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{MacOSSettingsDiskEncryptionFilter: fleet.DiskEncryptionRemovingEnforcement}, 0) // simulate osquery ping from host 0 with unverified key after key rotation; should switch host to verifying - require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0].ID, "key-1", "", nil)) + require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0], "key-1", "", nil)) listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{MacOSSettingsDiskEncryptionFilter: fleet.DiskEncryptionVerifying}, 2) listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{MacOSSettingsDiskEncryptionFilter: fleet.DiskEncryptionVerified}, 0) @@ -6935,7 +6935,7 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) { ) require.NoError(t, err) // set an encryption key - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host.ID, "TESTKEY", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host, "TESTKEY", "", nil) require.NoError(t, err) // set an mdm profile prof, err := ds.NewMDMAppleConfigProfile(context.Background(), *configProfileForTest(t, "N1", "I1", "U1")) @@ -8001,7 +8001,7 @@ func testHostsLoadHostByOrbitNodeKey(t *testing.T, ds *Datastore) { // fill in disk encryption information require.NoError(t, ds.SetOrUpdateHostDisksEncryption(context.Background(), hFleet.ID, true)) - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, hFleet.ID, "test-key", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, hFleet, "test-key", "", nil) require.NoError(t, err) err = ds.SetHostsDiskEncryptionKeyStatus(ctx, []uint{hFleet.ID}, true, time.Now()) require.NoError(t, err) @@ -8033,6 +8033,14 @@ func checkEncryptionKeyStatus(t *testing.T, ds *Datastore, hostID uint, expected require.NoError(t, err) require.Equal(t, expectedKey, got.Base64Encrypted) require.Equal(t, expectedDecryptable, got.Decryptable) + if expectedKey != "" { + var archiveKey string + ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error { + return sqlx.GetContext(context.Background(), q, &archiveKey, + `SELECT base64_encrypted FROM host_disk_encryption_keys_archive WHERE host_id = ? ORDER BY created_at DESC LIMIT 1`, hostID) + }) + assert.Equal(t, expectedKey, archiveKey) + } } func testLUKSDatastoreFunctions(t *testing.T, ds *Datastore) { @@ -8105,29 +8113,46 @@ func testLUKSDatastoreFunctions(t *testing.T, ds *Datastore) { require.NoError(t, ds.AssertHasNoEncryptionKeyStored(ctx, host3.ID)) // no change when blank key or salt attempted to save - err = ds.SaveLUKSData(ctx, host1.ID, "", "", 0) + err = ds.SaveLUKSData(ctx, host1, "", "", 0) require.Error(t, err) require.NoError(t, ds.AssertHasNoEncryptionKeyStored(ctx, host1.ID)) - err = ds.SaveLUKSData(ctx, host1.ID, "foo", "", 0) + err = ds.SaveLUKSData(ctx, host1, "foo", "", 0) require.Error(t, err) require.NoError(t, ds.AssertHasNoEncryptionKeyStored(ctx, host1.ID)) // persists with passphrase and salt set - err = ds.SaveLUKSData(ctx, host2.ID, "bazqux", "fuzzmuffin", 0) + err = ds.SaveLUKSData(ctx, host2, "bazqux", "fuzzmuffin", 0) require.NoError(t, err) require.NoError(t, ds.AssertHasNoEncryptionKeyStored(ctx, host1.ID)) require.Error(t, ds.AssertHasNoEncryptionKeyStored(ctx, host2.ID)) - key, err := ds.GetHostDiskEncryptionKey(ctx, host2.ID) - require.NoError(t, err) - require.Equal(t, "bazqux", key.Base64Encrypted) + checkLUKSEncryptionKey(t, ds, host2.ID, "bazqux", "fuzzmuffin") // persists when host hasn't had anything queued - err = ds.SaveLUKSData(ctx, host3.ID, "newstuff", "fuzzball", 1) + err = ds.SaveLUKSData(ctx, host3, "newstuff", "fuzzball", 1) require.NoError(t, err) require.Error(t, ds.AssertHasNoEncryptionKeyStored(ctx, host3.ID)) - key, err = ds.GetHostDiskEncryptionKey(ctx, host3.ID) + checkLUKSEncryptionKey(t, ds, host3.ID, "newstuff", "fuzzball") +} + +func checkLUKSEncryptionKey(t *testing.T, ds *Datastore, hostID uint, expectedKey string, expectedSalt string) { + got, err := ds.GetHostDiskEncryptionKey(context.Background(), hostID) require.NoError(t, err) - require.Equal(t, "newstuff", key.Base64Encrypted) + require.Equal(t, expectedKey, got.Base64Encrypted) + if expectedKey != "" { + var archiveKey string + ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error { + return sqlx.GetContext(context.Background(), q, &archiveKey, + `SELECT base64_encrypted FROM host_disk_encryption_keys_archive WHERE host_id = ? ORDER BY created_at DESC LIMIT 1`, hostID) + }) + assert.Equal(t, expectedKey, archiveKey) + var archiveSalt string + ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error { + return sqlx.GetContext(context.Background(), q, &archiveSalt, + `SELECT base64_encrypted_salt FROM host_disk_encryption_keys_archive WHERE host_id = ? ORDER BY created_at DESC LIMIT 1`, + hostID) + }) + assert.Equal(t, expectedSalt, archiveSalt) + } } func testHostsSetOrUpdateHostDisksEncryptionKey(t *testing.T, ds *Datastore) { @@ -8171,10 +8196,10 @@ func testHostsSetOrUpdateHostDisksEncryptionKey(t *testing.T, ds *Datastore) { }) require.NoError(t, err) - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host.ID, "AAA", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host, "AAA", "", nil) require.NoError(t, err) - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host2.ID, "BBB", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host2, "BBB", "", nil) require.NoError(t, err) h, err := ds.Host(context.Background(), host.ID) @@ -8185,7 +8210,7 @@ func testHostsSetOrUpdateHostDisksEncryptionKey(t *testing.T, ds *Datastore) { require.NoError(t, err) checkEncryptionKeyStatus(t, ds, h.ID, "BBB", nil) - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host2.ID, "CCC", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host2, "CCC", "", nil) require.NoError(t, err) h, err = ds.Host(context.Background(), host2.ID) @@ -8199,47 +8224,47 @@ func testHostsSetOrUpdateHostDisksEncryptionKey(t *testing.T, ds *Datastore) { checkEncryptionKeyStatus(t, ds, host.ID, "AAA", ptr.Bool(true)) // same key doesn't change encryption status - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host.ID, "AAA", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host, "AAA", "", nil) require.NoError(t, err) checkEncryptionKeyStatus(t, ds, host.ID, "AAA", ptr.Bool(true)) // different key resets encryption status - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host.ID, "XZY", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host, "XZY", "", nil) require.NoError(t, err) checkEncryptionKeyStatus(t, ds, host.ID, "XZY", nil) // set the key with an initial decrypted status of true - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3.ID, "abc", "", ptr.Bool(true)) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3, "abc", "", ptr.Bool(true)) require.NoError(t, err) checkEncryptionKeyStatus(t, ds, host3.ID, "abc", ptr.Bool(true)) // same key, provided decrypted status is ignored (stored one is kept) - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3.ID, "abc", "", ptr.Bool(false)) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3, "abc", "", ptr.Bool(false)) require.NoError(t, err) checkEncryptionKeyStatus(t, ds, host3.ID, "abc", ptr.Bool(true)) // client error, key is removed and decrypted status is nulled - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3.ID, "", "fail", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3, "", "fail", nil) require.NoError(t, err) checkEncryptionKeyStatus(t, ds, host3.ID, "", nil) // new key, provided decrypted status is applied - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3.ID, "def", "", ptr.Bool(true)) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3, "def", "", ptr.Bool(true)) require.NoError(t, err) checkEncryptionKeyStatus(t, ds, host3.ID, "def", ptr.Bool(true)) // different key, provided decrypted status is applied - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3.ID, "ghi", "", ptr.Bool(false)) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3, "ghi", "", ptr.Bool(false)) require.NoError(t, err) checkEncryptionKeyStatus(t, ds, host3.ID, "ghi", ptr.Bool(false)) // set an empty key (backfill for issue #15068) - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3.ID, "", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3, "", "", nil) require.NoError(t, err) checkEncryptionKeyStatus(t, ds, host3.ID, "", nil) // setting the decryptable value works even if the key is still empty - err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3.ID, "", "", ptr.Bool(false)) + err = ds.SetOrUpdateHostDiskEncryptionKey(context.Background(), host3, "", "", ptr.Bool(false)) require.NoError(t, err) checkEncryptionKeyStatus(t, ds, host3.ID, "", ptr.Bool(false)) } @@ -8259,7 +8284,7 @@ func testHostsSetDiskEncryptionKeyStatus(t *testing.T, ds *Datastore) { PrimaryMac: "30-65-EC-6F-C4-58", }) require.NoError(t, err) - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, "TESTKEY", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, "TESTKEY", "", nil) require.NoError(t, err) host2, err := ds.NewHost(context.Background(), &fleet.Host{ @@ -8276,7 +8301,7 @@ func testHostsSetDiskEncryptionKeyStatus(t *testing.T, ds *Datastore) { }) require.NoError(t, err) - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host2.ID, "TESTKEY", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host2, "TESTKEY", "", nil) require.NoError(t, err) threshold := time.Now().Add(time.Hour) @@ -8340,9 +8365,9 @@ func testHostsGetUnverifiedDiskEncryptionKeys(t *testing.T, ds *Datastore) { }) require.NoError(t, err) - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, "TESTKEY", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, "TESTKEY", "", nil) require.NoError(t, err) - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host2.ID, "TESTKEY", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host2, "TESTKEY", "", nil) require.NoError(t, err) keys, err := ds.GetUnverifiedDiskEncryptionKeys(ctx) @@ -8365,7 +8390,7 @@ func testHostsGetUnverifiedDiskEncryptionKeys(t *testing.T, ds *Datastore) { // update key of host 1 to empty with a client error, should not be reported // by GetUnverifiedDiskEncryptionKeys - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, "", "failed", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, "", "failed", nil) require.NoError(t, err) keys, err = ds.GetUnverifiedDiskEncryptionKeys(ctx) @@ -8794,7 +8819,7 @@ func testHostsEncryptionKeyRawDecryption(t *testing.T, ds *Datastore) { require.Equal(t, -1, *got.MDM.TestGetRawDecryptable()) // create the encryption key row, but unknown decryptable - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, "abc", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, "abc", "", nil) require.NoError(t, err) got, err = ds.Host(ctx, host.ID) diff --git a/server/datastore/mysql/labels_test.go b/server/datastore/mysql/labels_test.go index 3095b1c05790..6aa9250b5478 100644 --- a/server/datastore/mysql/labels_test.go +++ b/server/datastore/mysql/labels_test.go @@ -1240,8 +1240,8 @@ func testListHostsInLabelDiskEncryptionStatus(t *testing.T, ds *Datastore) { // verifying status upsertHostCPs([]*fleet.Host{hosts[0], hosts[1]}, []*fleet.MDMAppleConfigProfile{noTeamFVProfile}, fleet.MDMOperationTypeInstall, &fleet.MDMDeliveryVerifying, ctx, ds, t) oneMinuteAfterThreshold := time.Now().Add(+1 * time.Minute) - createDiskEncryptionRecord(ctx, ds, t, hosts[0].ID, "key-1", true, oneMinuteAfterThreshold) - createDiskEncryptionRecord(ctx, ds, t, hosts[1].ID, "key-1", true, oneMinuteAfterThreshold) + createDiskEncryptionRecord(ctx, ds, t, hosts[0], "key-1", true, oneMinuteAfterThreshold) + createDiskEncryptionRecord(ctx, ds, t, hosts[1], "key-1", true, oneMinuteAfterThreshold) listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{MacOSSettingsDiskEncryptionFilter: fleet.DiskEncryptionVerifying}, 2) listHostsCheckCount(t, ds, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{MacOSSettingsDiskEncryptionFilter: fleet.DiskEncryptionVerified}, 0) @@ -1595,7 +1595,7 @@ func testLabelsListHostsInLabelOSSettings(t *testing.T, db *Datastore) { require.NoError(t, db.SetOrUpdateMDMData(context.Background(), h.ID, false, true, "https://example.com", false, fleet.WellKnownMDMFleet, "")) } // add disk encryption key for h1 - require.NoError(t, db.SetOrUpdateHostDiskEncryptionKey(context.Background(), h1.ID, "test-key", "", ptr.Bool(true))) + require.NoError(t, db.SetOrUpdateHostDiskEncryptionKey(context.Background(), h1, "test-key", "", ptr.Bool(true))) // add disk encryption for h1 require.NoError(t, db.SetOrUpdateHostDisksEncryption(context.Background(), h1.ID, true)) diff --git a/server/datastore/mysql/linux_mdm_test.go b/server/datastore/mysql/linux_mdm_test.go index d0cecb405bea..8cc93f275e33 100644 --- a/server/datastore/mysql/linux_mdm_test.go +++ b/server/datastore/mysql/linux_mdm_test.go @@ -51,13 +51,13 @@ func TestLinuxDiskEncryptionSummary(t *testing.T) { // Add disk encryption keys // ubuntu - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, ubuntuHosts[0].ID, "base64_encrypted", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, ubuntuHosts[0], "base64_encrypted", "", nil) require.NoError(t, err) // fedora - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, fedoraHosts[0].ID, "base64_encrypted", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, fedoraHosts[0], "base64_encrypted", "", nil) require.NoError(t, err) // macos - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, macosHosts[0].ID, "base64_encrypted", "", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, macosHosts[0], "base64_encrypted", "", nil) require.NoError(t, err) summary, err = ds.GetLinuxDiskEncryptionSummary(ctx, nil) @@ -68,7 +68,7 @@ func TestLinuxDiskEncryptionSummary(t *testing.T) { require.Equal(t, uint(0), summary.Failed) // update ubuntu with key and client error - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, ubuntuHosts[0].ID, "base64_encrypted", "client error", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, ubuntuHosts[0], "base64_encrypted", "client error", nil) require.NoError(t, err) summary, err = ds.GetLinuxDiskEncryptionSummary(ctx, nil) @@ -79,7 +79,7 @@ func TestLinuxDiskEncryptionSummary(t *testing.T) { require.Equal(t, uint(1), summary.Failed) // add ubuntu with no key and client error - err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, ubuntuHosts[1].ID, "", "client error", nil) + err = ds.SetOrUpdateHostDiskEncryptionKey(ctx, ubuntuHosts[1], "", "client error", nil) require.NoError(t, err) summary, err = ds.GetLinuxDiskEncryptionSummary(ctx, nil) diff --git a/server/datastore/mysql/microsoft_mdm.go b/server/datastore/mysql/microsoft_mdm.go index bdff02165402..74c46d1243e7 100644 --- a/server/datastore/mysql/microsoft_mdm.go +++ b/server/datastore/mysql/microsoft_mdm.go @@ -512,7 +512,7 @@ func (ds *Datastore) whereBitLockerStatus(status fleet.DiskEncryptionStatus) str whereEncrypted = `(hd.encrypted IS NOT NULL AND hd.encrypted = 1)` whereHostDisksUpdated = `(hd.updated_at IS NOT NULL AND hdek.updated_at IS NOT NULL AND hd.updated_at >= hdek.updated_at)` whereClientError = `(hdek.client_error IS NOT NULL AND hdek.client_error != '')` - withinGracePeriod = `(hdek.updated_at IS NOT NULL AND hdek.updated_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR))` + withinGracePeriod = `(hdek.updated_at IS NOT NULL AND hdek.updated_at >= DATE_SUB(NOW(6), INTERVAL 1 HOUR))` ) // TODO: what if windows sends us a key for an already encrypted volumne? could it get stuck diff --git a/server/datastore/mysql/microsoft_mdm_test.go b/server/datastore/mysql/microsoft_mdm_test.go index 9771fdff505f..6bad061f14cd 100644 --- a/server/datastore/mysql/microsoft_mdm_test.go +++ b/server/datastore/mysql/microsoft_mdm_test.go @@ -334,7 +334,7 @@ func testMDMWindowsDiskEncryption(t *testing.T, ds *Datastore) { fleet.DiskEncryptionEnforcing: []uint{hosts[0].ID, hosts[1].ID, hosts[2].ID, hosts[3].ID, hosts[4].ID}, }) - require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0].ID, "test-key", "", ptr.Bool(true))) + require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0], "test-key", "", ptr.Bool(true))) checkExpected(t, nil, hostIDsByDEStatus{ // status is still pending because hosts_disks hasn't been updated yet fleet.DiskEncryptionEnforcing: []uint{hosts[0].ID, hosts[1].ID, hosts[2].ID, hosts[3].ID, hosts[4].ID}, @@ -438,7 +438,7 @@ func testMDMWindowsDiskEncryption(t *testing.T, ds *Datastore) { }) // ensure hosts[0] is set to verified for the rest of the tests - require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0].ID, "test-key", "", ptr.Bool(true))) + require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0], "test-key", "", ptr.Bool(true))) require.NoError(t, ds.SetOrUpdateHostDisksEncryption(ctx, hosts[0].ID, true)) checkExpected(t, nil, hostIDsByDEStatus{ fleet.DiskEncryptionVerified: []uint{hosts[0].ID}, @@ -447,7 +447,7 @@ func testMDMWindowsDiskEncryption(t *testing.T, ds *Datastore) { t.Run("BitLocker failed status", func(t *testing.T) { // set hosts[1] to failed - require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[1].ID, "", "test-error", ptr.Bool(false))) + require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[1], "", "test-error", ptr.Bool(false))) expected := hostIDsByDEStatus{ fleet.DiskEncryptionVerified: []uint{hosts[0].ID}, @@ -767,7 +767,7 @@ func testMDMWindowsProfilesSummary(t *testing.T, ds *Datastore) { checkExpected(t, nil, expected) require.NoError(t, ds.SetOrUpdateHostDisksEncryption(ctx, hosts[0].ID, true)) - require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0].ID, "test-key", "", ptr.Bool(true))) + require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0], "test-key", "", ptr.Bool(true))) // simulate bitlocker verifying status by ensuring host_disks updated at timestamp is before host_disk_encryption_key updateHostDisks(t, hosts[0].ID, true, time.Now().Add(-10*time.Minute)) // status for hosts[0] now verifying because bitlocker status is verifying and host[0] has @@ -819,7 +819,7 @@ func testMDMWindowsProfilesSummary(t *testing.T, ds *Datastore) { // all hosts are pending because no profiles and disk encryption is enabled checkExpected(t, nil, expected) - require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0].ID, "test-key", "", ptr.Bool(true))) + require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0], "test-key", "", ptr.Bool(true))) // status is still pending because hosts_disks hasn't been updated yet checkExpected(t, nil, expected) @@ -870,7 +870,7 @@ func testMDMWindowsProfilesSummary(t *testing.T, ds *Datastore) { // all hosts are pending because no profiles and disk encryption is enabled checkExpected(t, nil, expected) - require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0].ID, "", "some-bitlocker-error", nil)) + require.NoError(t, ds.SetOrUpdateHostDiskEncryptionKey(ctx, hosts[0], "", "some-bitlocker-error", nil)) // status for hosts[0] now failed because any failed status takes precedence expected = hostIDsByProfileStatus{ fleet.MDMDeliveryFailed: []uint{hosts[0].ID}, diff --git a/server/datastore/mysql/migrations/tables/20250121094045_AddHostDiskEncryptionKeysArchive.go b/server/datastore/mysql/migrations/tables/20250121094045_AddHostDiskEncryptionKeysArchive.go new file mode 100644 index 000000000000..dc35ade9f0cb --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20250121094045_AddHostDiskEncryptionKeysArchive.go @@ -0,0 +1,68 @@ +package tables + +import ( + "database/sql" + "fmt" +) + +func init() { + MigrationClient.AddMigration(Up_20250121094045, Down_20250121094045) +} + +func Up_20250121094045(tx *sql.Tx) error { + _, err := tx.Exec(`ALTER TABLE host_disks + MODIFY COLUMN created_at TIMESTAMP(6) NOT NULL DEFAULT NOW(6), + MODIFY COLUMN updated_at TIMESTAMP(6) NULL DEFAULT NOW(6) ON UPDATE NOW(6)`) + if err != nil { + return fmt.Errorf("failed to alter host_disks table: %w", err) + } + + _, err = tx.Exec(`ALTER TABLE host_disk_encryption_keys + MODIFY COLUMN created_at TIMESTAMP(6) NOT NULL DEFAULT NOW(6), + MODIFY COLUMN updated_at TIMESTAMP(6) NULL DEFAULT NOW(6) ON UPDATE NOW(6)`) + if err != nil { + return fmt.Errorf("failed to alter host_disk_encryption_keys table: %w", err) + } + + stmt := ` +CREATE TABLE IF NOT EXISTS host_disk_encryption_keys_archive ( + -- Since we may never delete rows from this table, we use a large PRIMARY KEY + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + host_id int unsigned NOT NULL, + hardware_serial VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + base64_encrypted text COLLATE utf8mb4_unicode_ci NOT NULL, + base64_encrypted_salt varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + key_slot tinyint unsigned DEFAULT NULL, + created_at TIMESTAMP(6) NOT NULL DEFAULT NOW(6), + KEY idx_host_disk_encryption_keys_archive_host_created_at (host_id, created_at DESC) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci` + if _, err := tx.Exec(stmt); err != nil { + return fmt.Errorf("failed to create host_disk_encryption_keys_archive: %w", err) + } + + // Copy all existing rows from host_disk_encryption_keys to host_disk_encryption_keys_archive + const copyKeysToArchiveQuery = ` +INSERT INTO host_disk_encryption_keys_archive (host_id, base64_encrypted, base64_encrypted_salt, key_slot, created_at) +SELECT host_id, base64_encrypted, base64_encrypted_salt, key_slot, created_at +FROM host_disk_encryption_keys` + _, err = tx.Exec(copyKeysToArchiveQuery) + if err != nil { + return fmt.Errorf("failed to copy existing rows to host_disk_encryption_keys_archive: %w", err) + } + + // Update the hardware_serial column to match the host table + const updateHardwareSerialQuery = ` +UPDATE host_disk_encryption_keys_archive +JOIN hosts ON host_disk_encryption_keys_archive.host_id = hosts.id +SET host_disk_encryption_keys_archive.hardware_serial = hosts.hardware_serial` + _, err = tx.Exec(updateHardwareSerialQuery) + if err != nil { + return fmt.Errorf("failed to update host_disk_encryption_keys_archive.hardware_serial: %w", err) + } + + return nil +} + +func Down_20250121094045(_ *sql.Tx) error { + return nil +} diff --git a/server/datastore/mysql/migrations/tables/20250121094045_AddHostDiskEncryptionKeysArchive_test.go b/server/datastore/mysql/migrations/tables/20250121094045_AddHostDiskEncryptionKeysArchive_test.go new file mode 100644 index 000000000000..5a92bbdd1c85 --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20250121094045_AddHostDiskEncryptionKeysArchive_test.go @@ -0,0 +1,60 @@ +package tables + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestUp_20250121094045(t *testing.T) { + db := applyUpToPrev(t) + + // Set up: 2 hosts and 3 keys + i := uint(1) + newHost := func(platform string) uint { + id := fmt.Sprintf("%d", i) + i++ + hostID := uint(execNoErrLastID(t, db, //nolint:gosec // dismiss G115 + `INSERT INTO hosts (hardware_serial, osquery_host_id, node_key, uuid, platform) VALUES (?, ?, ?, ?, ?)`, + id, id, id, id, platform, + )) + return hostID + } + ubuntuHostID := newHost("ubuntu") + macOSHostID := newHost("darwin") + + hostIDs := []uint{ubuntuHostID, macOSHostID, 9999} + for _, hostID := range hostIDs { + execNoErr(t, db, + `INSERT INTO host_disk_encryption_keys (host_id, base64_encrypted, base64_encrypted_salt, key_slot) VALUES (?, ?, ?, ?)`, + hostID, fmt.Sprintf("encrypted-%d", hostID), "salt", 1, + ) + } + timeBeforeMigration := time.Now() + + // Apply current migration. + applyNext(t, db) + + type archiveKey struct { + HostID uint `db:"host_id"` + HardwareSerial string `db:"hardware_serial"` + Base64Encrypted string `db:"base64_encrypted"` + CreatedAt time.Time `db:"created_at"` + } + var keys []archiveKey + require.NoError(t, + db.Select(&keys, + `SELECT host_id, hardware_serial, base64_encrypted, created_at FROM host_disk_encryption_keys_archive ORDER BY host_id ASC`)) + require.Len(t, keys, 3) + for i := range 3 { + require.Equal(t, hostIDs[i], keys[i].HostID) + require.Equal(t, fmt.Sprintf("encrypted-%d", hostIDs[i]), keys[i].Base64Encrypted) + require.GreaterOrEqual(t, keys[i].CreatedAt.Unix(), timeBeforeMigration.Unix()) + } + require.Equal(t, "1", keys[0].HardwareSerial) + require.Equal(t, "2", keys[1].HardwareSerial) + require.Equal(t, "", keys[2].HardwareSerial) + +} diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql index 4588000e1f9d..9ce4cffc5225 100644 --- a/server/datastore/mysql/schema.sql +++ b/server/datastore/mysql/schema.sql @@ -306,8 +306,8 @@ CREATE TABLE `host_disk_encryption_keys` ( `base64_encrypted_salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `key_slot` tinyint unsigned DEFAULT NULL, `decryptable` tinyint(1) DEFAULT NULL, - `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` timestamp(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `reset_requested` tinyint(1) NOT NULL DEFAULT '0', `client_error` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', PRIMARY KEY (`host_id`), @@ -316,12 +316,26 @@ CREATE TABLE `host_disk_encryption_keys` ( /*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `host_disk_encryption_keys_archive` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `host_id` int unsigned NOT NULL, + `hardware_serial` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `base64_encrypted` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `base64_encrypted_salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `key_slot` tinyint unsigned DEFAULT NULL, + `created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`), + KEY `idx_host_disk_encryption_keys_archive_host_created_at` (`host_id`,`created_at` DESC) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `host_disks` ( `host_id` int unsigned NOT NULL, `gigs_disk_space_available` decimal(10,2) NOT NULL DEFAULT '0.00', `percent_disk_space_available` decimal(10,2) NOT NULL DEFAULT '0.00', - `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` timestamp(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `encrypted` tinyint(1) DEFAULT NULL, `gigs_total_disk_space` decimal(10,2) NOT NULL DEFAULT '0.00', PRIMARY KEY (`host_id`), @@ -1111,9 +1125,9 @@ CREATE TABLE `migration_status_tables` ( `is_applied` tinyint(1) NOT NULL, `tstamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) -) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=346 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=347 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241017163402,1,'2020-01-01 01:01:01'),(322,20241021224359,1,'2020-01-01 01:01:01'),(323,20241022140321,1,'2020-01-01 01:01:01'),(324,20241025111236,1,'2020-01-01 01:01:01'),(325,20241025112748,1,'2020-01-01 01:01:01'),(326,20241025141855,1,'2020-01-01 01:01:01'),(327,20241110152839,1,'2020-01-01 01:01:01'),(328,20241110152840,1,'2020-01-01 01:01:01'),(329,20241110152841,1,'2020-01-01 01:01:01'),(330,20241116233322,1,'2020-01-01 01:01:01'),(331,20241122171434,1,'2020-01-01 01:01:01'),(332,20241125150614,1,'2020-01-01 01:01:01'),(333,20241203125346,1,'2020-01-01 01:01:01'),(334,20241203130032,1,'2020-01-01 01:01:01'),(335,20241205122800,1,'2020-01-01 01:01:01'),(336,20241209164540,1,'2020-01-01 01:01:01'),(337,20241210140021,1,'2020-01-01 01:01:01'),(338,20241219180042,1,'2020-01-01 01:01:01'),(339,20241220100000,1,'2020-01-01 01:01:01'),(340,20241220114903,1,'2020-01-01 01:01:01'),(341,20241220114904,1,'2020-01-01 01:01:01'),(342,20241224000000,1,'2020-01-01 01:01:01'),(343,20241230000000,1,'2020-01-01 01:01:01'),(344,20241231112624,1,'2020-01-01 01:01:01'),(345,20250102121439,1,'2020-01-01 01:01:01'); +INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241017163402,1,'2020-01-01 01:01:01'),(322,20241021224359,1,'2020-01-01 01:01:01'),(323,20241022140321,1,'2020-01-01 01:01:01'),(324,20241025111236,1,'2020-01-01 01:01:01'),(325,20241025112748,1,'2020-01-01 01:01:01'),(326,20241025141855,1,'2020-01-01 01:01:01'),(327,20241110152839,1,'2020-01-01 01:01:01'),(328,20241110152840,1,'2020-01-01 01:01:01'),(329,20241110152841,1,'2020-01-01 01:01:01'),(330,20241116233322,1,'2020-01-01 01:01:01'),(331,20241122171434,1,'2020-01-01 01:01:01'),(332,20241125150614,1,'2020-01-01 01:01:01'),(333,20241203125346,1,'2020-01-01 01:01:01'),(334,20241203130032,1,'2020-01-01 01:01:01'),(335,20241205122800,1,'2020-01-01 01:01:01'),(336,20241209164540,1,'2020-01-01 01:01:01'),(337,20241210140021,1,'2020-01-01 01:01:01'),(338,20241219180042,1,'2020-01-01 01:01:01'),(339,20241220100000,1,'2020-01-01 01:01:01'),(340,20241220114903,1,'2020-01-01 01:01:01'),(341,20241220114904,1,'2020-01-01 01:01:01'),(342,20241224000000,1,'2020-01-01 01:01:01'),(343,20241230000000,1,'2020-01-01 01:01:01'),(344,20241231112624,1,'2020-01-01 01:01:01'),(345,20250102121439,1,'2020-01-01 01:01:01'),(346,20250121094045,1,'2020-01-01 01:01:01'); /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `mobile_device_management_solutions` ( diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index 84b468dce283..1c1c745ad1a3 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -916,10 +916,10 @@ type Datastore interface { SetOrUpdateHostDisksEncryption(ctx context.Context, hostID uint, encrypted bool) error // SetOrUpdateHostDiskEncryptionKey sets the base64, encrypted key for // a host - SetOrUpdateHostDiskEncryptionKey(ctx context.Context, hostID uint, encryptedBase64Key, clientError string, decryptable *bool) error + SetOrUpdateHostDiskEncryptionKey(ctx context.Context, host *Host, encryptedBase64Key, clientError string, decryptable *bool) error // SaveLUKSData sets base64'd encrypted LUKS passphrase, key slot, and salt data for a host that has successfully // escrowed LUKS data - SaveLUKSData(ctx context.Context, hostID uint, encryptedBase64Passphrase string, encryptedBase64Salt string, keySlot uint) error + SaveLUKSData(ctx context.Context, host *Host, encryptedBase64Passphrase string, encryptedBase64Salt string, keySlot uint) error // GetUnverifiedDiskEncryptionKeys returns all the encryption keys that // are collected but their decryptable status is not known yet (ie: diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index e7eaf0efb002..a31229c1ee78 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -647,9 +647,9 @@ type GetConfigEnableDiskEncryptionFunc func(ctx context.Context, teamID *uint) ( type SetOrUpdateHostDisksEncryptionFunc func(ctx context.Context, hostID uint, encrypted bool) error -type SetOrUpdateHostDiskEncryptionKeyFunc func(ctx context.Context, hostID uint, encryptedBase64Key string, clientError string, decryptable *bool) error +type SetOrUpdateHostDiskEncryptionKeyFunc func(ctx context.Context, host *fleet.Host, encryptedBase64Key string, clientError string, decryptable *bool) error -type SaveLUKSDataFunc func(ctx context.Context, hostID uint, encryptedBase64Passphrase string, encryptedBase64Salt string, keySlot uint) error +type SaveLUKSDataFunc func(ctx context.Context, host *fleet.Host, encryptedBase64Passphrase string, encryptedBase64Salt string, keySlot uint) error type GetUnverifiedDiskEncryptionKeysFunc func(ctx context.Context) ([]fleet.HostDiskEncryptionKey, error) @@ -5146,18 +5146,18 @@ func (s *DataStore) SetOrUpdateHostDisksEncryption(ctx context.Context, hostID u return s.SetOrUpdateHostDisksEncryptionFunc(ctx, hostID, encrypted) } -func (s *DataStore) SetOrUpdateHostDiskEncryptionKey(ctx context.Context, hostID uint, encryptedBase64Key string, clientError string, decryptable *bool) error { +func (s *DataStore) SetOrUpdateHostDiskEncryptionKey(ctx context.Context, host *fleet.Host, encryptedBase64Key string, clientError string, decryptable *bool) error { s.mu.Lock() s.SetOrUpdateHostDiskEncryptionKeyFuncInvoked = true s.mu.Unlock() - return s.SetOrUpdateHostDiskEncryptionKeyFunc(ctx, hostID, encryptedBase64Key, clientError, decryptable) + return s.SetOrUpdateHostDiskEncryptionKeyFunc(ctx, host, encryptedBase64Key, clientError, decryptable) } -func (s *DataStore) SaveLUKSData(ctx context.Context, hostID uint, encryptedBase64Passphrase string, encryptedBase64Salt string, keySlot uint) error { +func (s *DataStore) SaveLUKSData(ctx context.Context, host *fleet.Host, encryptedBase64Passphrase string, encryptedBase64Salt string, keySlot uint) error { s.mu.Lock() s.SaveLUKSDataFuncInvoked = true s.mu.Unlock() - return s.SaveLUKSDataFunc(ctx, hostID, encryptedBase64Passphrase, encryptedBase64Salt, keySlot) + return s.SaveLUKSDataFunc(ctx, host, encryptedBase64Passphrase, encryptedBase64Salt, keySlot) } func (s *DataStore) GetUnverifiedDiskEncryptionKeys(ctx context.Context) ([]fleet.HostDiskEncryptionKey, error) { diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index a24fe947bcf2..532428bb692f 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -1763,7 +1763,7 @@ func (s *integrationMDMTestSuite) TestEscrowBuddyBackwardsCompat() { assert.True(t, acResp.MDM.EnableDiskEncryption.Value) // set the status as non-decryptable so a notification should be sent - err := s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, "", "", ptr.Bool(false)) + err := s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, "", "", ptr.Bool(false)) require.NoError(t, err) // notification is false because the escrow buddy capability is not set @@ -1909,7 +1909,7 @@ func (s *integrationMDMTestSuite) TestMDMAppleHostDiskEncryption() { require.NoError(t, err) base64EncryptedKey := base64.StdEncoding.EncodeToString(encryptedKey) - err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, base64EncryptedKey, "", nil) + err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, base64EncryptedKey, "", nil) require.NoError(t, err) // get that host - it has an encryption key with unknown decryptability, so @@ -2123,7 +2123,7 @@ func (s *integrationMDMTestSuite) TestWindowsMDMGetEncryptionKey() { encryptedKey, err := microsoft_mdm.Encrypt(recoveryKey, cert.Leaf) require.NoError(t, err) - err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, encryptedKey, "", ptr.Bool(true)) + err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, encryptedKey, "", ptr.Bool(true)) require.NoError(t, err) resp = getHostEncryptionKeyResponse{} @@ -2134,7 +2134,7 @@ func (s *integrationMDMTestSuite) TestWindowsMDMGetEncryptionKey() { fmt.Sprintf(`{"host_display_name": "%s", "host_id": %d}`, host.DisplayName(), host.ID), 0) // update the key to blank with a client error - err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, "", "failed", nil) + err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, "", "failed", nil) require.NoError(t, err) resp = getHostEncryptionKeyResponse{} @@ -2310,7 +2310,7 @@ func (s *integrationMDMTestSuite) TestMDMAppleDiskEncryptionAggregate() { }) require.NoError(t, err) oneMinuteAfterThreshold := time.Now().Add(+1 * time.Minute) - err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, "test-key", "", nil) + err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, "test-key", "", nil) require.NoError(t, err) err = s.ds.SetHostsDiskEncryptionKeyStatus(ctx, []uint{host.ID}, decryptable, oneMinuteAfterThreshold) require.NoError(t, err) @@ -7557,7 +7557,7 @@ func (s *integrationMDMTestSuite) TestBitLockerEnforcementNotifications() { checkNotification(true) // host has disk encryption on, we don't know if the key is decriptable. Gets the notification - err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, windowsHost.ID, "test-key", "", nil) + err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, windowsHost, "test-key", "", nil) require.NoError(t, err) checkNotification(true) @@ -7605,7 +7605,7 @@ func (s *integrationMDMTestSuite) TestBitLockerEnforcementNotifications() { checkNotification(true) // host has disk encryption on, we don't know if the key is decriptable. Gets the notification - err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, windowsHost.ID, "test-key", "", nil) + err = s.ds.SetOrUpdateHostDiskEncryptionKey(ctx, windowsHost, "test-key", "", nil) require.NoError(t, err) checkNotification(true) diff --git a/server/service/orbit.go b/server/service/orbit.go index f3bbaabb16c3..923320b27920 100644 --- a/server/service/orbit.go +++ b/server/service/orbit.go @@ -1047,7 +1047,7 @@ func (svc *Service) SetOrUpdateDiskEncryptionKey(ctx context.Context, encryption decryptable = ptr.Bool(true) } - if err := svc.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, encryptedEncryptionKey, clientError, decryptable); err != nil { + if err := svc.ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, encryptedEncryptionKey, clientError, decryptable); err != nil { return ctxerr.Wrap(ctx, err, "set or update disk encryption key") } @@ -1110,7 +1110,7 @@ func (svc *Service) EscrowLUKSData(ctx context.Context, passphrase string, salt return err } - return svc.ds.SaveLUKSData(ctx, host.ID, encryptedPassphrase, encryptedSalt, validatedKeySlot) + return svc.ds.SaveLUKSData(ctx, host, encryptedPassphrase, encryptedSalt, validatedKeySlot) } func (svc *Service) validateAndEncrypt(ctx context.Context, passphrase string, salt string, keySlot *uint) (encryptedPassphrase string, encryptedSalt string, validatedKeySlot uint, err error) { diff --git a/server/service/orbit_test.go b/server/service/orbit_test.go index 135a70b06c4f..6ebe2cc6ebf3 100644 --- a/server/service/orbit_test.go +++ b/server/service/orbit_test.go @@ -194,8 +194,9 @@ func TestOrbitLUKSDataSave(t *testing.T) { ds.ReportEscrowErrorFuncInvoked = false passphrase, salt := "foo", "" var keySlot *uint - ds.SaveLUKSDataFunc = func(ctx context.Context, hostID uint, encryptedBase64Passphrase string, encryptedBase64Salt string, keySlotToPersist uint) error { - require.Equal(t, host.ID, hostID) + ds.SaveLUKSDataFunc = func(ctx context.Context, incomingHost *fleet.Host, encryptedBase64Passphrase string, + encryptedBase64Salt string, keySlotToPersist uint) error { + require.Equal(t, host.ID, incomingHost.ID) key := config.TestConfig().Server.PrivateKey decryptedPassphrase, err := mdm.DecodeAndDecrypt(encryptedBase64Passphrase, key) diff --git a/server/service/osquery_utils/queries.go b/server/service/osquery_utils/queries.go index 35a5fb748bd1..7ec132f8add6 100644 --- a/server/service/osquery_utils/queries.go +++ b/server/service/osquery_utils/queries.go @@ -2068,7 +2068,7 @@ func directIngestDiskEncryptionKeyFileDarwin( if base64Key == "" { decryptable = ptr.Bool(false) } - return ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, base64Key, "", decryptable) + return ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, base64Key, "", decryptable) } // directIngestDiskEncryptionKeyFileLinesDarwin ingests the FileVault key from the `file_lines` @@ -2127,7 +2127,7 @@ func directIngestDiskEncryptionKeyFileLinesDarwin( decryptable = ptr.Bool(false) } - return ds.SetOrUpdateHostDiskEncryptionKey(ctx, host.ID, base64Key, "", decryptable) + return ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, base64Key, "", decryptable) } func directIngestMacOSProfiles( diff --git a/server/service/osquery_utils/queries_test.go b/server/service/osquery_utils/queries_test.go index 57ba06d58c65..a71b1deb806d 100644 --- a/server/service/osquery_utils/queries_test.go +++ b/server/service/osquery_utils/queries_test.go @@ -1529,11 +1529,12 @@ func TestDirectIngestDiskEncryptionKeyDarwin(t *testing.T) { } } - ds.SetOrUpdateHostDiskEncryptionKeyFunc = func(ctx context.Context, hostID uint, encryptedBase64Key, clientError string, decryptable *bool) error { + ds.SetOrUpdateHostDiskEncryptionKeyFunc = func(ctx context.Context, incomingHost *fleet.Host, encryptedBase64Key, clientError string, + decryptable *bool) error { if base64.StdEncoding.EncodeToString([]byte(wantKey)) != encryptedBase64Key { return errors.New("key mismatch") } - if host.ID != hostID { + if host.ID != incomingHost.ID { return errors.New("host ID mismatch") } if encryptedBase64Key == "" && (decryptable == nil || *decryptable == true) {