Skip to content

Implement BitLocker "action required" status#31451

Merged
sgress454 merged 15 commits into
mainfrom
sgress454/31182-implement-bitlocker-action-required
Aug 5, 2025
Merged

Implement BitLocker "action required" status#31451
sgress454 merged 15 commits into
mainfrom
sgress454/31182-implement-bitlocker-action-required

Conversation

@sgress454
Copy link
Copy Markdown
Contributor

@sgress454 sgress454 commented Jul 31, 2025

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

  • Checked table schema to confirm autoupdate
  • Checked schema for all modified table for columns that will auto-update timestamps during migration.
  • Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects.
  • Ensured the correct collation is explicitly set for character columns (COLLATE utf8mb4_unicode_ci).

@sgress454 sgress454 requested a review from a team as a code owner July 31, 2025 14:34
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 31, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sgress454/31182-implement-bitlocker-action-required

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Comment thread server/fleet/app.go
Comment on lines +233 to +239
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
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 1540 to +1542
CASE WHEN (%s) THEN
'bitlocker_pending'
WHEN (%s) THEN
Copy link
Copy Markdown
Contributor Author

@sgress454 sgress454 Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Comment on lines -1572 to +1590
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'
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread server/datastore/mysql/mdm.go Outdated
Comment on lines +544 to +547
whereBitLockerPINSet := `TRUE`
if bitLockerPINRequired {
whereBitLockerPINSet = `(hd.tpm_pin_set = true)`
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines -556 to +561
AND ` + whereHostDisksUpdated
AND ` + whereHostDisksUpdated + `
AND ` + whereBitLockerPINSet
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the "verified" state SQL to include the PIN check.

Comment on lines +573 to +574
)
AND ` + whereBitLockerPINSet
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the "verifying" state SQL to include the PIN check.

Comment on lines +576 to +586
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
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the new "action required" state, which is basically the disjunction of the "verifying" and "verified" states, with the additional "PIN not set" check.

Comment on lines -608 to +626
0 AS action_required,
COUNT(if((%s), 1, NULL)) AS action_required,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the "action required" state in the SQL that gets the summary of windows host disk encryption states

Comment on lines -964 to +993
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
Copy link
Copy Markdown
Contributor Author

@sgress454 sgress454 Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the profiles summary, count both "pending" and "action_required" BitLocker status as "pending" for the sake of the profiles.

Comment thread server/datastore/mysql/microsoft_mdm.go Outdated
@sgress454 sgress454 changed the title Sgress454/31182 implement bitlocker action required Implement BitLocker "action required" status Jul 31, 2025
cleanupHostProfiles(t)
})

t.Run("BitLocker profile status with PIN required", func(t *testing.T) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@juan-fdz-hawa
Copy link
Copy Markdown
Contributor

Great work with this! Something that I noticed is that when the TPM PIN is required and is not set yet, the Pending state is not shown on the host detail page:

If you mean how "Disk Encryption" shows "On", I think this is expected -- we only show "On", "Off" or "Unknown" there. Once we're all done, if the host is not meeting the PIN requirement then we'll show a banner on the host page and My Device pages indicating that they need to take action. If you build the front end from this branch you should be able to see it.

Not the Disk Encryption card, but the OS settings one, I was expecting to see something like this:

image

@sgress454
Copy link
Copy Markdown
Contributor Author

Not the Disk Encryption card, but the OS settings one, I was expecting to see something like this:

ah interesting -- ngl I don't know exactly how that works, will investigate

@sgress454
Copy link
Copy Markdown
Contributor Author

@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):

image

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.

@juan-fdz-hawa
Copy link
Copy Markdown
Contributor

@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):

image 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 OS Settings card seem to disappear when the require PIN checkbox is set:

Screencast.from.2025-08-04.13-24-54.webm

If 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.

@sgress454
Copy link
Copy Markdown
Contributor Author

Ok that's wacky, let me see if I can get mine to "verified" and I'll see if I can repro.

Comment on lines -51 to +52
else if (!operationType && isMdmProfileStatus(status)) {
else if (
!operationType &&
status !== "success" &&
status !== "acknowledged"
) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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" => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do want to show "action required" for windows disk encryption status

Comment on lines -24 to -29
export const isMdmProfileStatus = (
status: string
): status is MdmProfileStatus => {
return status !== "action_required";
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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. */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a follow up it'd make sense to update / remove this comment

Copy link
Copy Markdown
Contributor

@jacobshandling jacobshandling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FE looks good

@sgress454 sgress454 merged commit 7a8f18c into main Aug 5, 2025
44 checks passed
@sgress454 sgress454 deleted the sgress454/31182-implement-bitlocker-action-required branch August 5, 2025 16:23
sgress454 added a commit that referenced this pull request Aug 6, 2025
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants