From ad309991512a12d1d5626d92115dcfa7f7c92707 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Fri, 22 Nov 2024 20:25:55 -0600 Subject: [PATCH] Use MDM config profiles return type, gate Linux disk encryption visibility in profiles stats by whether disk encryption is enabled --- server/datastore/mysql/app_configs.go | 2 +- server/datastore/mysql/app_configs_test.go | 6 +++--- server/datastore/mysql/hosts.go | 2 +- server/datastore/mysql/labels.go | 2 +- server/datastore/mysql/microsoft_mdm.go | 6 +++--- server/fleet/datastore.go | 1 + server/fleet/service.go | 9 ++++---- server/mock/datastore_mock.go | 12 +++++++++++ server/service/linux_mdm.go | 24 ++++++++++++++-------- server/service/mdm.go | 20 ++++++------------ 10 files changed, 48 insertions(+), 36 deletions(-) diff --git a/server/datastore/mysql/app_configs.go b/server/datastore/mysql/app_configs.go index 8f8d708eb908..5de25d4d0006 100644 --- a/server/datastore/mysql/app_configs.go +++ b/server/datastore/mysql/app_configs.go @@ -274,7 +274,7 @@ func (ds *Datastore) AggregateEnrollSecretPerTeam(ctx context.Context) ([]*fleet return secrets, nil } -func (ds *Datastore) getConfigEnableDiskEncryption(ctx context.Context, teamID *uint) (bool, error) { +func (ds *Datastore) GetConfigEnableDiskEncryption(ctx context.Context, teamID *uint) (bool, error) { if teamID != nil && *teamID > 0 { tc, err := ds.TeamMDMConfig(ctx, *teamID) if err != nil { diff --git a/server/datastore/mysql/app_configs_test.go b/server/datastore/mysql/app_configs_test.go index dc0d4b1c9d9d..46df41010be8 100644 --- a/server/datastore/mysql/app_configs_test.go +++ b/server/datastore/mysql/app_configs_test.go @@ -449,7 +449,7 @@ func testGetConfigEnableDiskEncryption(t *testing.T, ds *Datastore) { require.NoError(t, err) require.False(t, ac.MDM.EnableDiskEncryption.Value) - enabled, err := ds.getConfigEnableDiskEncryption(ctx, nil) + enabled, err := ds.GetConfigEnableDiskEncryption(ctx, nil) require.NoError(t, err) require.False(t, enabled) @@ -461,7 +461,7 @@ func testGetConfigEnableDiskEncryption(t *testing.T, ds *Datastore) { require.NoError(t, err) require.True(t, ac.MDM.EnableDiskEncryption.Value) - enabled, err = ds.getConfigEnableDiskEncryption(ctx, nil) + enabled, err = ds.GetConfigEnableDiskEncryption(ctx, nil) require.NoError(t, err) require.True(t, enabled) @@ -474,7 +474,7 @@ func testGetConfigEnableDiskEncryption(t *testing.T, ds *Datastore) { require.NotNil(t, tm) require.False(t, tm.Config.MDM.EnableDiskEncryption) - enabled, err = ds.getConfigEnableDiskEncryption(ctx, &team1.ID) + enabled, err = ds.GetConfigEnableDiskEncryption(ctx, &team1.ID) require.NoError(t, err) require.False(t, enabled) diff --git a/server/datastore/mysql/hosts.go b/server/datastore/mysql/hosts.go index b2f7f3a650ed..7bb7f11f7ca0 100644 --- a/server/datastore/mysql/hosts.go +++ b/server/datastore/mysql/hosts.go @@ -1218,7 +1218,7 @@ func (ds *Datastore) applyHostFilters( return "", nil, ctxerr.Wrap(ctx, err, "building query to filter macOS settings status") } sqlStmt, whereParams = filterHostsByMacOSDiskEncryptionStatus(sqlStmt, opt, whereParams) - if enableDiskEncryption, err := ds.getConfigEnableDiskEncryption(ctx, opt.TeamFilter); err != nil { + if enableDiskEncryption, err := ds.GetConfigEnableDiskEncryption(ctx, opt.TeamFilter); err != nil { if errors.Is(err, sql.ErrNoRows) { return "", nil, ctxerr.Wrap( ctx, &fleet.BadRequestError{ diff --git a/server/datastore/mysql/labels.go b/server/datastore/mysql/labels.go index 21c4e0eb9402..4111c81b6ee0 100644 --- a/server/datastore/mysql/labels.go +++ b/server/datastore/mysql/labels.go @@ -662,7 +662,7 @@ func (ds *Datastore) applyHostLabelFilters(ctx context.Context, filter fleet.Tea } query, whereParams = filterHostsByMacOSDiskEncryptionStatus(query, opt, whereParams) query, whereParams = filterHostsByMDMBootstrapPackageStatus(query, opt, whereParams) - if enableDiskEncryption, err := ds.getConfigEnableDiskEncryption(ctx, opt.TeamFilter); err != nil { + if enableDiskEncryption, err := ds.GetConfigEnableDiskEncryption(ctx, opt.TeamFilter); err != nil { return "", nil, err } else if opt.OSSettingsFilter.IsValid() { query, whereParams, err = ds.filterHostsByOSSettingsStatus(query, opt, whereParams, enableDiskEncryption) diff --git a/server/datastore/mysql/microsoft_mdm.go b/server/datastore/mysql/microsoft_mdm.go index 9e773afddb06..e846ef8af79e 100644 --- a/server/datastore/mysql/microsoft_mdm.go +++ b/server/datastore/mysql/microsoft_mdm.go @@ -585,7 +585,7 @@ AND ( } func (ds *Datastore) GetMDMWindowsBitLockerSummary(ctx context.Context, teamID *uint) (*fleet.MDMWindowsBitLockerSummary, error) { - enabled, err := ds.getConfigEnableDiskEncryption(ctx, teamID) + enabled, err := ds.GetConfigEnableDiskEncryption(ctx, teamID) if err != nil { return nil, err } @@ -655,7 +655,7 @@ func (ds *Datastore) GetMDMWindowsBitLockerStatus(ctx context.Context, host *fle return nil, nil } - enabled, err := ds.getConfigEnableDiskEncryption(ctx, host.TeamID) + enabled, err := ds.GetConfigEnableDiskEncryption(ctx, host.TeamID) if err != nil { return nil, err } @@ -887,7 +887,7 @@ func subqueryHostsMDMWindowsOSSettingsStatusVerified() (string, []interface{}, e } func (ds *Datastore) GetMDMWindowsProfilesSummary(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error) { - includeBitLocker, err := ds.getConfigEnableDiskEncryption(ctx, teamID) + includeBitLocker, err := ds.GetConfigEnableDiskEncryption(ctx, teamID) if err != nil { return nil, err } diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index 6dfaae64b248..f0e2d99871c0 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -899,6 +899,7 @@ type Datastore interface { GetHostEmails(ctx context.Context, hostUUID string, source string) ([]string, error) SetOrUpdateHostDisksSpace(ctx context.Context, hostID uint, gigsAvailable, percentAvailable, gigsTotal float64) error + GetConfigEnableDiskEncryption(ctx context.Context, teamID *uint) (bool, error) SetOrUpdateHostDisksEncryption(ctx context.Context, hostID uint, encrypted bool) error // SetOrUpdateHostDiskEncryptionKey sets the base64, encrypted key for // a host diff --git a/server/fleet/service.go b/server/fleet/service.go index cb6344b79e62..7e9f7c973cbb 100644 --- a/server/fleet/service.go +++ b/server/fleet/service.go @@ -1059,11 +1059,10 @@ type Service interface { // Returns empty status if the host is not a supported Linux host LinuxHostDiskEncryptionStatus(ctx context.Context, host Host) (HostMDMDiskEncryption, error) - // GetLinuxDiskEncryptionSummary summarizes the current state of disk encryption for - // supported Linux hosts in the specified team (or, if no team is specified, each host that is not assigned to any team). - - // TODO - custom return type, or just use relevant fields? - GetLinuxDiskEncryptionSummary(ctx context.Context, teamId *uint) (MDMLinuxDiskEncryptionSummary, error) + // GetMDMLinuxProfilesSummary summarizes the current status of Linux disk encryption for + // the provided team (or hosts without a team if teamId is nil), or returns zeroes if disk + // encryption is not enforced on the selected team + GetMDMLinuxProfilesSummary(ctx context.Context, teamId *uint) (MDMProfilesSummary, error) /////////////////////////////////////////////////////////////////////////////// // Common MDM diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index 6d8fbd885676..1dfb9436612b 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -635,6 +635,8 @@ type GetHostEmailsFunc func(ctx context.Context, hostUUID string, source string) type SetOrUpdateHostDisksSpaceFunc func(ctx context.Context, hostID uint, gigsAvailable float64, percentAvailable float64, gigsTotal float64) error +type GetConfigEnableDiskEncryptionFunc func(ctx context.Context, teamID *uint) (bool, error) + 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 @@ -2087,6 +2089,9 @@ type DataStore struct { SetOrUpdateHostDisksSpaceFunc SetOrUpdateHostDisksSpaceFunc SetOrUpdateHostDisksSpaceFuncInvoked bool + GetConfigEnableDiskEncryptionFunc GetConfigEnableDiskEncryptionFunc + GetConfigEnableDiskEncryptionFuncInvoked bool + SetOrUpdateHostDisksEncryptionFunc SetOrUpdateHostDisksEncryptionFunc SetOrUpdateHostDisksEncryptionFuncInvoked bool @@ -5034,6 +5039,13 @@ func (s *DataStore) SetOrUpdateHostDisksSpace(ctx context.Context, hostID uint, return s.SetOrUpdateHostDisksSpaceFunc(ctx, hostID, gigsAvailable, percentAvailable, gigsTotal) } +func (s *DataStore) GetConfigEnableDiskEncryption(ctx context.Context, teamID *uint) (bool, error) { + s.mu.Lock() + s.GetConfigEnableDiskEncryptionFuncInvoked = true + s.mu.Unlock() + return s.GetConfigEnableDiskEncryptionFunc(ctx, teamID) +} + func (s *DataStore) SetOrUpdateHostDisksEncryption(ctx context.Context, hostID uint, encrypted bool) error { s.mu.Lock() s.SetOrUpdateHostDisksEncryptionFuncInvoked = true diff --git a/server/service/linux_mdm.go b/server/service/linux_mdm.go index 0a439b7856d4..815e2bf04d7f 100644 --- a/server/service/linux_mdm.go +++ b/server/service/linux_mdm.go @@ -44,19 +44,27 @@ func (svc *Service) LinuxHostDiskEncryptionStatus(ctx context.Context, host flee }, nil } -func (svc *Service) GetLinuxDiskEncryptionSummary(ctx context.Context, teamId *uint) (fleet.MDMLinuxDiskEncryptionSummary, error) { - if err := svc.authz.Authorize(ctx, fleet.MDMConfigProfileAuthz{TeamID: teamId}, fleet.ActionRead); err != nil { - return fleet.MDMLinuxDiskEncryptionSummary{}, ctxerr.Wrap(ctx, err) +func (svc *Service) GetMDMLinuxProfilesSummary(ctx context.Context, teamId *uint) (summary fleet.MDMProfilesSummary, err error) { + if err = svc.authz.Authorize(ctx, fleet.MDMConfigProfileAuthz{TeamID: teamId}, fleet.ActionRead); err != nil { + return summary, ctxerr.Wrap(ctx, err) } - if svc.config.Server.PrivateKey == "" { - return fleet.MDMLinuxDiskEncryptionSummary{}, ctxerr.New(ctx, "Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key") + // Linux doesn't have configuration profiles, so if we aren't enforcing disk encryption we have nothing to report + includeDiskEncryptionStats, err := svc.ds.GetConfigEnableDiskEncryption(ctx, teamId) + if err != nil { + return summary, ctxerr.Wrap(ctx, err) + } else if !includeDiskEncryptionStats { + return summary, nil } - ps, err := svc.ds.GetLinuxDiskEncryptionSummary(ctx, teamId) + counts, err := svc.ds.GetLinuxDiskEncryptionSummary(ctx, teamId) if err != nil { - return fleet.MDMLinuxDiskEncryptionSummary{}, ctxerr.Wrap(ctx, err) + return summary, ctxerr.Wrap(ctx, err) } - return ps, nil + return fleet.MDMProfilesSummary{ + Verified: counts.Verified, + Pending: counts.ActionRequired, + Failed: counts.Failed, + }, nil } diff --git a/server/service/mdm.go b/server/service/mdm.go index a23f8cccfa13..576f57c2da33 100644 --- a/server/service/mdm.go +++ b/server/service/mdm.go @@ -936,23 +936,15 @@ func getMDMProfilesSummaryEndpoint(ctx context.Context, request interface{}, svc return &getMDMProfilesSummaryResponse{Err: err}, nil } - var lx fleet.MDMLinuxDiskEncryptionSummary - // since this endpoint is available for Free users as well, check license here to include Linux - // disk encryption counts which is a premium feature. Similar to - // `ds.GetMDMWindowsProfilesSummary`'s `includeBitLocker` check, except that Linux hosts don't - // have any non-premium data to contribute here, so we can check higher up. - license, _ := license.FromContext(ctx) - if license.IsPremium() { - lx, err = svc.GetLinuxDiskEncryptionSummary(ctx, req.TeamID) - if err != nil { - return &getMDMProfilesSummaryResponse{Err: err}, nil - } + ls, err := svc.GetMDMLinuxProfilesSummary(ctx, req.TeamID) + if err != nil { + return &getMDMProfilesSummaryResponse{Err: err}, nil } - res.Verified = as.Verified + ws.Verified + lx.Verified + res.Verified = as.Verified + ws.Verified + ls.Verified res.Verifying = as.Verifying + ws.Verifying - res.Failed = as.Failed + ws.Failed + lx.Failed - res.Pending = as.Pending + ws.Pending + lx.ActionRequired + res.Failed = as.Failed + ws.Failed + ls.Failed + res.Pending = as.Pending + ws.Pending + ls.Pending return &res, nil }