Validate Require BitLocker PIN (#32240)

For #28133

Validate that if 'Require BitLocker PIN' is enabled, disk encryption
must be enabled as well.
This commit is contained in:
Juan Fernandez
2025-08-26 13:23:24 -04:00
committed by GitHub
parent 17b5732673
commit 56c0773ff3
7 changed files with 172 additions and 21 deletions
+7
View File
@@ -1669,6 +1669,13 @@ func (svc *Service) updateTeamMDMDiskEncryption(ctx context.Context, tm *fleet.T
}
if didUpdateEncryption || didUpdateRequirePIN {
if didUpdateEncryption && !tm.Config.MDM.EnableDiskEncryption && tm.Config.MDM.RequireBitLockerPIN {
return ctxerr.New(ctx, fleet.CantDisableDiskEncryptionIfPINRequiredErrMsg)
}
if !didUpdateEncryption && !tm.Config.MDM.EnableDiskEncryption && tm.Config.MDM.RequireBitLockerPIN {
return ctxerr.New(ctx, fleet.CantEnablePINRequiredIfDiskEncryptionEnabled)
}
if _, err := svc.ds.SaveTeam(ctx, tm); err != nil {
return err
}
+83
View File
@@ -1,13 +1,96 @@
package service
import (
"context"
"strings"
"testing"
"github.com/fleetdm/fleet/v4/server/config"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mock"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/stretchr/testify/require"
)
func TestUpdateTeamMDMDiskEncryption(t *testing.T) {
testCases := []struct {
name string
mdmConfig fleet.TeamMDM
diskEncryption *bool
requireTPMPIN *bool
expectedError string
}{
{
name: "try to disable disk encryption with TPM PIN enabled",
mdmConfig: fleet.TeamMDM{
EnableDiskEncryption: true,
RequireBitLockerPIN: true,
},
diskEncryption: ptr.Bool(false),
requireTPMPIN: ptr.Bool(true),
expectedError: fleet.CantDisableDiskEncryptionIfPINRequiredErrMsg,
},
{
name: "try to enable disk encryption with TPM PIN enabled",
mdmConfig: fleet.TeamMDM{
EnableDiskEncryption: false,
RequireBitLockerPIN: false,
},
diskEncryption: ptr.Bool(false),
requireTPMPIN: ptr.Bool(true),
expectedError: fleet.CantEnablePINRequiredIfDiskEncryptionEnabled,
},
{
name: "try to disable disk encryption with TPM PIN enabled when disk encryption prev enabled",
mdmConfig: fleet.TeamMDM{
EnableDiskEncryption: true,
RequireBitLockerPIN: false,
},
diskEncryption: ptr.Bool(false),
requireTPMPIN: ptr.Bool(true),
expectedError: fleet.CantDisableDiskEncryptionIfPINRequiredErrMsg,
},
}
ds := new(mock.Store)
svc := &Service{
ds: ds,
config: config.FleetConfig{
Server: config.ServerConfig{
PrivateKey: "something",
},
},
}
ctx := context.Background()
for _, tC := range testCases {
team := fleet.Team{
Config: fleet.TeamConfig{
MDM: tC.mdmConfig,
},
}
err := svc.updateTeamMDMDiskEncryption(
ctx,
&team,
tC.diskEncryption,
tC.requireTPMPIN,
)
if tC.expectedError != "" {
require.NotNil(t, err)
require.True(
t,
strings.Contains(err.Error(), tC.expectedError),
"Expected '%s' to contain '%s'",
err.Error(), tC.expectedError)
}
}
}
func TestObfuscateSecrets(t *testing.T) {
buildTeams := func(n int) []*fleet.Team {
r := make([]*fleet.Team, 0, n)
@@ -126,10 +126,10 @@ const DiskEncryption = ({
</>
);
} else {
renderFlash(
"error",
"Could not update the disk encryption enforcement. Please try again."
);
const errorMsg =
getErrorReason(e) ??
"Could not update the disk encryption enforcement. Please try again.";
renderFlash("error", errorMsg);
}
}
};
+10 -8
View File
@@ -22,14 +22,16 @@ var (
ErrWindowsMDMNotConfigured = &WindowsMDMNotConfiguredError{}
ErrNotConfigured = &NotConfiguredError{}
MDMNotConfiguredMessage = "MDM features aren't turned on in Fleet. For more information about setting up MDM, please visit https://fleetdm.com/docs/using-fleet"
WindowsMDMNotConfiguredMessage = "Windows MDM isn't turned on. For more information about setting up MDM, please visit https://fleetdm.com/learn-more-about/windows-mdm"
AppleMDMNotConfiguredMessage = "macOS MDM isn't turned on. Visit https://fleetdm.com/docs/using-fleet to learn how to turn on MDM."
AppleABMDefaultTeamDeprecatedMessage = "mdm.apple_bm_default_team has been deprecated. Please use the new mdm.apple_business_manager key documented here: https://fleetdm.com/learn-more-about/apple-business-manager-gitops"
CantTurnOffMDMForWindowsHostsMessage = "Can't turn off MDM for Windows hosts."
CantTurnOffMDMForPersonalHostsMessage = "Couldn't turn off MDM. This command isn't available for personal hosts."
CantWipePersonalHostsMessage = "Couldn't wipe. This command isn't available for personal hosts."
CantLockPersonalHostsMessage = "Couldn't lock. This command isn't available for personal hosts."
MDMNotConfiguredMessage = "MDM features aren't turned on in Fleet. For more information about setting up MDM, please visit https://fleetdm.com/docs/using-fleet"
WindowsMDMNotConfiguredMessage = "Windows MDM isn't turned on. For more information about setting up MDM, please visit https://fleetdm.com/learn-more-about/windows-mdm"
AppleMDMNotConfiguredMessage = "macOS MDM isn't turned on. Visit https://fleetdm.com/docs/using-fleet to learn how to turn on MDM."
AppleABMDefaultTeamDeprecatedMessage = "mdm.apple_bm_default_team has been deprecated. Please use the new mdm.apple_business_manager key documented here: https://fleetdm.com/learn-more-about/apple-business-manager-gitops"
CantTurnOffMDMForWindowsHostsMessage = "Can't turn off MDM for Windows hosts."
CantTurnOffMDMForPersonalHostsMessage = "Couldn't turn off MDM. This command isn't available for personal hosts."
CantWipePersonalHostsMessage = "Couldn't wipe. This command isn't available for personal hosts."
CantLockPersonalHostsMessage = "Couldn't lock. This command isn't available for personal hosts."
CantDisableDiskEncryptionIfPINRequiredErrMsg = "Couldn't disable disk encryption, you need to disable the BitLocker PIN requirement first."
CantEnablePINRequiredIfDiskEncryptionEnabled = "Couldn't enable BitLocker PIN requirement, you must enable disk encryption first."
)
// ErrWithStatusCode is an interface for errors that should set a specific HTTP
+16
View File
@@ -1751,6 +1751,22 @@ func (svc *Service) validateMDM(
if !mdm.WindowsEnabledAndConfigured && mdm.WindowsMigrationEnabled {
invalid.Append("mdm.windows_migration_enabled", "Couldn't enable Windows MDM migration, Windows MDM is not enabled.")
}
if !mdm.EnableDiskEncryption.Value {
switch {
case !oldMdm.EnableDiskEncryption.Value && mdm.RequireBitLockerPIN.Value:
invalid.Append(
"mdm.windows_require_bitlocker_pin",
fleet.CantEnablePINRequiredIfDiskEncryptionEnabled,
)
case oldMdm.EnableDiskEncryption.Value && mdm.RequireBitLockerPIN.Value:
invalid.Append(
"mdm.enable_disk_encryption",
fleet.CantDisableDiskEncryptionIfPINRequiredErrMsg,
)
}
}
return nil
}
+40 -1
View File
@@ -901,7 +901,7 @@ func TestTransparencyURLDowngradeLicense(t *testing.T) {
require.Equal(t, "", ac.FleetDesktop.TransparencyURL)
}
func TestMDMAppleConfig(t *testing.T) {
func TestMDMConfig(t *testing.T) {
ds := new(mock.Store)
depStorage := new(nanodep_mock.Storage)
@@ -1179,6 +1179,45 @@ func TestMDMAppleConfig(t *testing.T) {
RequireBitLockerPIN: optjson.Bool{Set: true, Value: false},
},
},
{
name: "try to disable disk encryption with TPM PIN enabled",
licenseTier: "premium",
oldMDM: fleet.MDM{
EnableDiskEncryption: optjson.SetBool(true),
RequireBitLockerPIN: optjson.SetBool(true),
},
newMDM: fleet.MDM{
EnableDiskEncryption: optjson.SetBool(false),
RequireBitLockerPIN: optjson.SetBool(true),
},
expectedError: fleet.CantDisableDiskEncryptionIfPINRequiredErrMsg,
},
{
name: "try to enable disk encryption with TPM PIN enabled",
licenseTier: "premium",
oldMDM: fleet.MDM{
EnableDiskEncryption: optjson.SetBool(false),
RequireBitLockerPIN: optjson.SetBool(false),
},
newMDM: fleet.MDM{
EnableDiskEncryption: optjson.SetBool(false),
RequireBitLockerPIN: optjson.SetBool(true),
},
expectedError: fleet.CantEnablePINRequiredIfDiskEncryptionEnabled,
},
{
name: "try to disable disk encryption with TPM PIN enabled when disk encryption prev enabled",
licenseTier: "premium",
oldMDM: fleet.MDM{
EnableDiskEncryption: optjson.SetBool(true),
RequireBitLockerPIN: optjson.SetBool(false),
},
newMDM: fleet.MDM{
EnableDiskEncryption: optjson.SetBool(false),
RequireBitLockerPIN: optjson.SetBool(true),
},
expectedError: fleet.CantDisableDiskEncryptionIfPINRequiredErrMsg,
},
}
for _, tt := range testCases {
+12 -8
View File
@@ -1637,8 +1637,7 @@ func (c *Client) DoGitOps(
logf func(format string, args ...interface{}),
dryRun bool,
teamDryRunAssumptions *fleet.TeamSpecsDryRunAssumptions,
appConfig *fleet.EnrichedAppConfig,
// pass-by-ref to build lists
appConfig *fleet.EnrichedAppConfig, // pass-by-ref to build lists
teamsSoftwareInstallers map[string][]fleet.SoftwarePackageResponse,
teamsVPPApps map[string][]fleet.VPPAppResponse,
teamsScripts map[string][]fleet.ScriptResponse,
@@ -2046,17 +2045,22 @@ func (c *Client) DoGitOps(
windowsUpdates["grace_period_days"] = nil
}
}
// Put in default value for enable_disk_encryption
enableDiskEncryption := false
requireBitLockerPIN := false
if config.Controls.EnableDiskEncryption != nil {
mdmAppConfig["enable_disk_encryption"] = config.Controls.EnableDiskEncryption
} else {
mdmAppConfig["enable_disk_encryption"] = false
enableDiskEncryption = config.Controls.EnableDiskEncryption.(bool)
}
if config.Controls.RequireBitLockerPIN != nil {
mdmAppConfig["windows_require_bitlocker_pin"] = config.Controls.RequireBitLockerPIN
} else {
mdmAppConfig["windows_require_bitlocker_pin"] = false
requireBitLockerPIN = config.Controls.RequireBitLockerPIN.(bool)
}
if !enableDiskEncryption && requireBitLockerPIN {
return nil, nil, errors.New("enable_disk_encryption cannot be false if windows_require_bitlocker_pin is true")
}
mdmAppConfig["enable_disk_encryption"] = enableDiskEncryption
mdmAppConfig["windows_require_bitlocker_pin"] = requireBitLockerPIN
if config.TeamName != nil {
team["gitops_filename"] = filename