Implement BitLocker "action required" status#31451
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
| type DiskEncryptionConfig struct { | ||
| // Enabled indicates if disk encryption is enabled. | ||
| Enabled bool | ||
| // BitLockerPINRequired indicates if a PIN is required for BitLocker disk encryption. | ||
| BitLockerPINRequired bool | ||
| } | ||
|
|
There was a problem hiding this comment.
Previously we were passing a bool to various functions to indicate whether disk encryption was enabled. Since we now have two flags to be concerned with ("enabled" and "pin required"), this PR introduces a new struct to pass in place of the bool. Most of the file changes in the PR are updating this usage.
| CASE WHEN (%s) THEN | ||
| 'bitlocker_pending' | ||
| WHEN (%s) THEN |
There was a problem hiding this comment.
This is in the code which allows filtering hosts by profile status. In this branch of the logic, we haven't determined profile status by means of the host_mdm_windows_profiles table, so we're going completely off of bitlocker status, which we'll transform by removing the bitlocker_ prefix here.
The new case here which checks for the "action required" disk encryption state and, if found, returns the profile status as bitlocker_pending (which will be transformed to pending).
| WHEN 'bitlocker_pending' THEN | ||
| WHEN 'bitlocker_pending' THEN | ||
| 'pending' | ||
| WHEN 'bitlocker_action_required' THEN | ||
| 'pending' | ||
| ELSE | ||
| 'verifying' | ||
| END) | ||
| WHEN 'profiles_verified' THEN ( | ||
| CASE (%s) | ||
| WHEN 'bitlocker_failed' THEN | ||
| 'failed' | ||
| WHEN 'bitlocker_pending' THEN | ||
| WHEN 'bitlocker_pending' THEN | ||
| 'pending' | ||
| WHEN 'bitlocker_action_required' THEN | ||
| 'pending' |
There was a problem hiding this comment.
This part of the profile status filtering SQL utilizes the host_mdm_windows_profiles to get a status, and then modifies it depending on the bitlocker status. Again we're setting the status to "pending" if bitlocker action is required.
| whereBitLockerPINSet := `TRUE` | ||
| if bitLockerPINRequired { | ||
| whereBitLockerPINSet = `(hd.tpm_pin_set = true)` | ||
| } |
There was a problem hiding this comment.
This is in the helper method which provides SQL for checking a host's BitLocker status. We set up a WHERE clause to check whether a host has their TPM PIN set, defaulting to TRUE if PIN is not required.
| AND ` + whereHostDisksUpdated | ||
| AND ` + whereHostDisksUpdated + ` | ||
| AND ` + whereBitLockerPINSet |
There was a problem hiding this comment.
Update the "verified" state SQL to include the PIN check.
| ) | ||
| AND ` + whereBitLockerPINSet |
There was a problem hiding this comment.
Update the "verifying" state SQL to include the PIN check.
| case fleet.DiskEncryptionActionRequired: | ||
| // Action required means we _would_ be in verified / verifying, | ||
| // but we require a PIN to be set and it's not. | ||
| return whereNotServer + ` | ||
| AND NOT ` + whereClientError + ` | ||
| AND ` + whereKeyAvailable + ` | ||
| AND ( | ||
| ` + whereEncrypted + ` | ||
| OR (NOT ` + whereEncrypted + ` AND ` + whereHostDisksUpdated + ` AND ` + withinGracePeriod + `) | ||
| ) | ||
| AND NOT ` + whereBitLockerPINSet |
There was a problem hiding this comment.
Add the new "action required" state, which is basically the disjunction of the "verifying" and "verified" states, with the additional "PIN not set" check.
| 0 AS action_required, | ||
| COUNT(if((%s), 1, NULL)) AS action_required, |
There was a problem hiding this comment.
Implement the "action required" state in the SQL that gets the summary of windows host disk encryption states
| res.Pending = c.Count | ||
| res.Pending += c.Count | ||
| case "verifying": | ||
| res.Verifying = c.Count | ||
| case "verified": | ||
| res.Verified = c.Count | ||
| case "action_required": | ||
| res.Pending += c.Count |
There was a problem hiding this comment.
For the profiles summary, count both "pending" and "action_required" BitLocker status as "pending" for the sake of the profiles.
| cleanupHostProfiles(t) | ||
| }) | ||
|
|
||
| t.Run("BitLocker profile status with PIN required", func(t *testing.T) { |
There was a problem hiding this comment.
These tests are very inter-related. I tried to make them more table-based (I really did, @ksykulev!) but somehow starting from scratch caused unexpected issues as well, perhaps due to mdm side-effects I don't fully grok. The best I could do is create a mini setup and teardown in this test -- it turns on PIN requirement at the beginning, and removes it at the end. It also reverts the change made to the host before returning.
There was a problem hiding this comment.
I think changing existing tests is going to be fairly arduous. Especially in some of the extra complicated situations. I have begun pulling out any new tests into new methods in the new paradigm - as long as the setup isn't wildly complicated.
Not the
|
ah interesting -- ngl I don't know exactly how that works, will investigate |
|
@juan-fdz-hawa ok I see it when I turn Windows MDM on in Settings -> Integrations -> Mobile Device Management, and also have Disk Encryption on in Controls -> OS Settings (for the team that the host is on):
The actual verification is based on Fleet getting the disk encryption key. I haven't had any luck getting that to happen between my local Fleet and my VM though. |
The encryption process is a little bit slow on Windows because the whole driver needs to be encrypted and then decrypted. The Screencast.from.2025-08-04.13-24-54.webmIf you are having problems with your local env setup, I'm happy to approve this and address the issue in another PR to keep the ball rolling. |
|
Ok that's wacky, let me see if I can get mine to "verified" and I'll see if I can repro. |
| else if (!operationType && isMdmProfileStatus(status)) { | ||
| else if ( | ||
| !operationType && | ||
| status !== "success" && | ||
| status !== "acknowledged" | ||
| ) { |
There was a problem hiding this comment.
I'm sure there's a more Typescript-y way to do this but I could not figure it out and Claude kept making a mess of things, so this will do for now.
| const convertWinDiskEncryptionStatusToSettingStatus = ( | ||
| diskEncryptionStatus: WindowsDiskEncryptionStatus | ||
| ): MdmProfileStatus => { | ||
| ): MdmProfileStatus | "action_required" => { |
There was a problem hiding this comment.
we do want to show "action required" for windows disk encryption status
| export const isMdmProfileStatus = ( | ||
| status: string | ||
| ): status is MdmProfileStatus => { | ||
| return status !== "action_required"; | ||
| }; | ||
|
|
There was a problem hiding this comment.
This was only used to filter out action_required which we no longer want to do
| @@ -190,7 +190,7 @@ export type DiskEncryptionStatus = | |||
| values. In the future we may add more. */ | |||
There was a problem hiding this comment.
In a follow up it'd make sense to update / remove this comment
for #31196 # 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. Will add changelog when feature is complete. ## Testing > Note - to enable the "require bitlocker pin" ui, compile front end with: ``` SHOW_BITLOCKER_PIN_OPTION=true NODE_ENV=development yarn run webpack --progress --watch ``` - [X] Added/updated automated tests - [X] QA'd all new/changed functionality manually I had to add/remove the `tpm_pin_set` flag in the db manually, but by doing that I was able to get the banners to appear, when running #31451. <img width="1158" height="128" alt="image" src="https://github.com/user-attachments/assets/f3e4dc62-5e1f-410a-a684-11aee3e29f50" /> --- <img width="833" height="375" alt="image" src="https://github.com/user-attachments/assets/b178f203-1399-4dd1-b743-558bd77b175b" /> --- <img width="1160" height="199" alt="image" src="https://github.com/user-attachments/assets/cf86380c-d84c-47fd-b62f-349f0a4bb718" /> --------- Co-authored-by: Gabriel Hernandez <ghernandez345@gmail.com>



for #31182
Details
This PR implements the "Action Required" state for Windows host disk encryption. This includes updates to reporting for:
GET /fleet/disk_encryption)GET /configuration_profiles/summary)GET /configuration_profiles/{profile_uuid}/status)For disk encryption summary, the statuses are now determined according to the rules in the Figma. 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.DiskEncryptionConfigstruct to hold info about disk encryption config, and passing variables of that type to various functions instead of passing aboolto indicate whether encryption is enabled. Other than that, the functional changes are constrained to a few files.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/oree/fleetd-chrome/changes.See Changes files for more information.
Changelog will be added when feature is complete.
Input data is properly validated,
SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements)Testing
Added/updated automated tests
Where appropriate, automated tests simulate multiple hosts and test for host isolation (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
COLLATE utf8mb4_unicode_ci).