From af12ba144a933b7490aaebc50d25715610e2daa7 Mon Sep 17 00:00:00 2001 From: jacobshandling <61553566+jacobshandling@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:43:35 -0800 Subject: [PATCH] Include disk encryption stats only if setting is enabled for Linux host (#24457) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Addresses #24456 - host detail response (for Host details page and My device page) excludes `mdm.os_settings` field if disk encryption isn't enabled for the host - confirmed it is still included when setting is enabled - confirmed expected banner is still shown when setting enabled Screenshot 2024-12-05 at 10 10 48 PM - [x] Changes file added for user-visible changes in `changes/`, - [x] Added/updated tests - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling --- ...de-linux-encryption-data-only-when-enabled | 1 + server/fleet/hosts.go | 2 +- server/service/hosts.go | 24 ++++++++---- server/service/hosts_test.go | 22 ++++++++++- server/service/integration_enterprise_test.go | 38 +++++++++++++++---- 5 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 changes/24456-include-linux-encryption-data-only-when-enabled diff --git a/changes/24456-include-linux-encryption-data-only-when-enabled b/changes/24456-include-linux-encryption-data-only-when-enabled new file mode 100644 index 000000000000..cefe63053829 --- /dev/null +++ b/changes/24456-include-linux-encryption-data-only-when-enabled @@ -0,0 +1 @@ +- Updated the get host endpoint to include disk encryption stats for a linux host only if the setting is enabled diff --git a/server/fleet/hosts.go b/server/fleet/hosts.go index d55e4ff3ceb0..2ed23a10cebc 100644 --- a/server/fleet/hosts.go +++ b/server/fleet/hosts.go @@ -437,7 +437,7 @@ type MDMHostData struct { rawDecryptable *int // OSSettings contains information related to operating systems settings that are managed for - // MDM-enrolled hosts. + // MDM-enrolled hosts and/or Linux hosts with disk encryption enabled, which don't require MDM. // // Note: Additional information for macOS hosts is currently stored in MacOSSettings. OSSettings *HostMDMOSSettings `json:"os_settings,omitempty" db:"-" csv:"-"` diff --git a/server/service/hosts.go b/server/service/hosts.go index 4af625c2029f..782250b58bb2 100644 --- a/server/service/hosts.go +++ b/server/service/hosts.go @@ -1169,7 +1169,7 @@ func (svc *Service) getHostDetails(ctx context.Context, host *fleet.Host, opts f } // If Fleet MDM is enabled and configured, we want to include MDM profiles, - // disk encryption status, and macOS setup details. + // disk encryption status, and macOS setup details for non-linux hosts. ac, err := svc.ds.AppConfig(ctx) if err != nil { return nil, ctxerr.Wrap(ctx, err, "get app config for host mdm details") @@ -1243,16 +1243,24 @@ func (svc *Service) getHostDetails(ctx context.Context, host *fleet.Host, opts f host.MDM.Profiles = &profiles if host.IsLUKSSupported() { - status, err := svc.LinuxHostDiskEncryptionStatus(ctx, *host) + // since Linux hosts don't require MDM to be enabled & configured, explicitly check that disk encryption is + // enabled for the host's team + eDE, err := svc.ds.GetConfigEnableDiskEncryption(ctx, host.TeamID) if err != nil { - return nil, ctxerr.Wrap(ctx, err, "get host disk encryption status") - } - host.MDM.OSSettings = &fleet.HostMDMOSSettings{ - DiskEncryption: status, + return nil, ctxerr.Wrap(ctx, err, "get host disk encryption enabled setting") } + if eDE { + status, err := svc.LinuxHostDiskEncryptionStatus(ctx, *host) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "get host disk encryption status") + } + host.MDM.OSSettings = &fleet.HostMDMOSSettings{ + DiskEncryption: status, + } - if status.Status != nil && *status.Status == fleet.DiskEncryptionVerified { - host.MDM.EncryptionKeyAvailable = true + if status.Status != nil && *status.Status == fleet.DiskEncryptionVerified { + host.MDM.EncryptionKeyAvailable = true + } } } diff --git a/server/service/hosts_test.go b/server/service/hosts_test.go index 035a55248672..21f6ee3d5908 100644 --- a/server/service/hosts_test.go +++ b/server/service/hosts_test.go @@ -413,7 +413,9 @@ func TestHostDetailsOSSettings(t *testing.T) { cases := []testCase{ {"windows", &fleet.Host{ID: 42, Platform: "windows"}, fleet.TierPremium, fleet.DiskEncryptionEnforcing}, {"darwin", &fleet.Host{ID: 42, Platform: "darwin"}, fleet.TierPremium, ""}, - {"ubuntu", &fleet.Host{ID: 42, Platform: "ubuntu"}, fleet.TierPremium, ""}, + // TeamID necessary to check whether disk encryption is enabled for Linux hosts, in lieu of + // MDM-related logic which doesn't apply to Linux hosts + {"ubuntu", &fleet.Host{ID: 42, Platform: "ubuntu", TeamID: ptr.Uint(1)}, fleet.TierPremium, ""}, {"not premium", &fleet.Host{ID: 42, Platform: "windows"}, fleet.TierFree, ""}, } @@ -423,6 +425,7 @@ func TestHostDetailsOSSettings(t *testing.T) { ds.GetHostMDMAppleProfilesFuncInvoked = false ds.GetHostMDMWindowsProfilesFuncInvoked = false ds.GetHostMDMFuncInvoked = false + ds.GetConfigEnableDiskEncryptionFuncInvoked = false ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) { return &fleet.AppConfig{MDM: fleet.MDM{EnabledAndConfigured: true, WindowsEnabledAndConfigured: true}}, nil @@ -443,6 +446,10 @@ func TestHostDetailsOSSettings(t *testing.T) { hmdm := fleet.HostMDM{Enrolled: true, IsServer: false} return &hmdm, nil } + ds.GetConfigEnableDiskEncryptionFunc = func(ctx context.Context, teamID *uint) (bool, error) { + // testing API response when not enabled + return false, nil + } } for _, c := range cases { @@ -475,6 +482,19 @@ func TestHostDetailsOSSettings(t *testing.T) { require.False(t, ds.GetMDMWindowsBitLockerStatusFuncInvoked) require.Nil(t, hostDetail.MDM.OSSettings.DiskEncryption.Status) } + case "ubuntu": + require.False(t, ds.GetHostMDMAppleProfilesFuncInvoked) + require.False(t, ds.GetMDMWindowsBitLockerStatusFuncInvoked) + // service should call this function to check whether disk encryption is enabled for a Linux host + require.True(t, ds.GetConfigEnableDiskEncryptionFuncInvoked) + + // `hostDetail.MDM.OSSettings` and `hostDetail.MDM.OSSettings.DiskEncryption` will actually not + // be `nil` here due to the way those fields are initialized by `svc.ds.Host`, so we can't + // expect them to be `nil` in these tests. However, since the relevant struct tags are set to + // `omitempty`, the resulting API response WILL omit these fields/subfields when empty, + // which is confirmed at the integration layer. + require.Nil(t, hostDetail.MDM.OSSettings.DiskEncryption.Status) + case "darwin": require.True(t, ds.GetHostMDMAppleProfilesFuncInvoked) require.False(t, ds.GetMDMWindowsBitLockerStatusFuncInvoked) diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index 47ac9176847d..9b7e5b693c4a 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -2937,9 +2937,38 @@ func (s *integrationEnterpriseTestSuite) TestLinuxDiskEncryption() { s.DoJSON("GET", "/api/latest/fleet/configuration_profiles/summary", getMDMProfilesSummaryRequest{}, http.StatusOK, &profileSummary) require.Equal(t, fleet.MDMProfilesSummary{}, profileSummary.MDMProfilesSummary) + // should be nil before disk encryption is turned on + // from host details + getHostResp := getHostResponse{} + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", noTeamHost.ID), nil, http.StatusOK, &getHostResp) + require.Nil(t, getHostResp.Host.MDM.OSSettings) + + // and my device + deviceToken := "for_sure_secure" + createDeviceTokenForHost(t, s.ds, noTeamHost.ID, deviceToken) + + getDeviceHostResp := getDeviceHostResponse{} + res := s.DoRawNoAuth("GET", "/api/latest/fleet/device/"+deviceToken, nil, http.StatusOK) + err = json.NewDecoder(res.Body).Decode(&getDeviceHostResp) + require.NoError(t, err) + require.Nil(t, getHostResp.Host.MDM.OSSettings) + // turn on disk encryption enforcement s.Do("POST", "/api/latest/fleet/disk_encryption", updateDiskEncryptionRequest{EnableDiskEncryption: true}, http.StatusNoContent) + // should be populated after disk encryption is turned on + // from host details + getHostResp = getHostResponse{} + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", noTeamHost.ID), nil, http.StatusOK, &getHostResp) + require.NotNil(t, getHostResp.Host.MDM.OSSettings) + + // and my device + getDeviceHostResp = getDeviceHostResponse{} + res = s.DoRawNoAuth("GET", "/api/latest/fleet/device/"+deviceToken, nil, http.StatusOK) + err = json.NewDecoder(res.Body).Decode(&getDeviceHostResp) + require.NoError(t, err) + require.NotNil(t, getHostResp.Host.MDM.OSSettings) + // should show the Linux host as pending s.DoJSON("GET", "/api/latest/fleet/configuration_profiles/summary", getMDMProfilesSummaryRequest{}, http.StatusOK, &profileSummary) require.Equal(t, fleet.MDMProfilesSummary{Pending: 1}, profileSummary.MDMProfilesSummary) @@ -2952,18 +2981,13 @@ func (s *integrationEnterpriseTestSuite) TestLinuxDiskEncryption() { require.Equal(t, fleet.MDMDiskEncryptionSummary{ActionRequired: fleet.MDMPlatformsCounts{Linux: 1}}, *summary.MDMDiskEncryptionSummary) // trigger escrow process from device - token := "much_valid" - mysql.ExecAdhocSQL(t, s.ds, func(db sqlx.ExtContext) error { - _, err := db.ExecContext(context.Background(), `INSERT INTO host_device_auth (host_id, token) VALUES (?, ?)`, noTeamHost.ID, token) - return err - }) // should fail because default Orbit version is too old - res := s.DoRawNoAuth("POST", fmt.Sprintf("/api/latest/fleet/device/%s/mdm/linux/trigger_escrow", token), nil, http.StatusBadRequest) + res = s.DoRawNoAuth("POST", fmt.Sprintf("/api/latest/fleet/device/%s/mdm/linux/trigger_escrow", deviceToken), nil, http.StatusBadRequest) res.Body.Close() // should succeed now that Orbit version isn't too old require.NoError(t, s.ds.SetOrUpdateHostOrbitInfo(context.Background(), noTeamHost.ID, fleet.MinOrbitLUKSVersion, sql.NullString{}, sql.NullBool{})) - res = s.DoRawNoAuth("POST", fmt.Sprintf("/api/latest/fleet/device/%s/mdm/linux/trigger_escrow", token), nil, http.StatusNoContent) + res = s.DoRawNoAuth("POST", fmt.Sprintf("/api/latest/fleet/device/%s/mdm/linux/trigger_escrow", deviceToken), nil, http.StatusNoContent) res.Body.Close() // confirm that Orbit endpoint shows notification flag