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
- [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