Skip to content

Commit

Permalink
Include disk encryption stats only if setting is enabled for Linux ho…
Browse files Browse the repository at this point in the history
…st (#24457)

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

<img width="2555" alt="Screenshot 2024-12-05 at 10 10 48 PM"
src="https://github.com/user-attachments/assets/e3852b7f-51ae-4e87-bceb-476ccdba2459">


- [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 <[email protected]>
  • Loading branch information
jacobshandling and Jacob Shandling authored Dec 17, 2024
1 parent 885e1d5 commit af12ba1
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Updated the get host endpoint to include disk encryption stats for a linux host only if the setting is enabled
2 changes: 1 addition & 1 deletion server/fleet/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:"-"`
Expand Down
24 changes: 16 additions & 8 deletions server/service/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
}
}
}

Expand Down
22 changes: 21 additions & 1 deletion server/service/hosts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, ""},
}

Expand All @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
38 changes: 31 additions & 7 deletions server/service/integration_enterprise_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down

0 comments on commit af12ba1

Please sign in to comment.