fleet/server/service/linux_mdm.go
Scott Gress 7a8f18cc8f
Implement BitLocker "action required" status (#31451)
for #31182 

# Details

This PR implements the "Action Required" state for Windows host disk
encryption. This includes updates to reporting for:

* disk encryption summary (`GET /fleet/disk_encryption`)
* config profiles summary (`GET /configuration_profiles/summary`)
* config profile status ( `GET
/configuration_profiles/{profile_uuid}/status`)

For disk encryption summary, the statuses are now determined according
to [the rules in the
Figma](https://www.figma.com/design/XbhlPuEJxQtOgTZW9EOJZp/-28133-Enforce-BitLocker-PIN?node-id=5484-928&t=JB13g8zQ2QDVEmPB-0).
TL;DR if the criteria for "verified" or "verifying" are set, but a
required PIN is not set, we report a host as "action required".

For profiles, I followed what seems to be the existing pattern and set
the profile status to "pending" if the disk encryption status is "action
required". This is what we do for hosts with the "enforcing" or
"removing enforcement" statuses.

A lot of the changes in these files are due to the creation of the
`fleet.DiskEncryptionConfig` struct to hold info about disk encryption
config, and passing variables of that type to various functions instead
of passing a `bool` to indicate whether encryption is enabled. Other
than that, the functional changes are constrained to a few files.

> Note: to get the "require bitlocker pin" UI, compile the front end
with:
```
SHOW_BITLOCKER_PIN_OPTION=true NODE_ENV=development yarn run webpack --progress --watch
```

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
Changelog will be added when feature is complete.

- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [X] Added/updated automated tests
- [X] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)

- [ ] QA'd all new/changed functionality manually
Could use some help testing this end-to-end. I was able to test the
banners showing up correctly, but testing the Disk Encryption table
requires some Windows-MDM-fu (I just get all zeroes).

## Database migrations

- [X] Checked table schema to confirm autoupdate
- [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`).
2025-08-05 11:23:27 -05:00

70 lines
1.8 KiB
Go

package service
import (
"context"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
)
func (svc *Service) LinuxHostDiskEncryptionStatus(ctx context.Context, host fleet.Host) (fleet.HostMDMDiskEncryption, error) {
if !host.IsLUKSSupported() {
return fleet.HostMDMDiskEncryption{}, nil
}
actionRequired := fleet.DiskEncryptionActionRequired
verified := fleet.DiskEncryptionVerified
failed := fleet.DiskEncryptionFailed
key, err := svc.ds.GetHostDiskEncryptionKey(ctx, host.ID)
if err != nil {
if fleet.IsNotFound(err) {
return fleet.HostMDMDiskEncryption{
Status: &actionRequired,
}, nil
}
return fleet.HostMDMDiskEncryption{}, err
}
if key.ClientError != "" {
return fleet.HostMDMDiskEncryption{
Status: &failed,
Detail: key.ClientError,
}, nil
}
if key.Base64Encrypted == "" {
return fleet.HostMDMDiskEncryption{
Status: &actionRequired,
}, nil
}
return fleet.HostMDMDiskEncryption{
Status: &verified,
}, nil
}
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)
}
// Linux doesn't have configuration profiles, so if we aren't enforcing disk encryption we have nothing to report
diskEncryptionConfig, err := svc.ds.GetConfigEnableDiskEncryption(ctx, teamId)
if err != nil {
return summary, ctxerr.Wrap(ctx, err)
} else if !diskEncryptionConfig.Enabled {
return summary, nil
}
counts, err := svc.ds.GetLinuxDiskEncryptionSummary(ctx, teamId)
if err != nil {
return summary, ctxerr.Wrap(ctx, err)
}
return fleet.MDMProfilesSummary{
Verified: counts.Verified,
Pending: counts.ActionRequired,
Failed: counts.Failed,
}, nil
}