diff --git a/changes/24862-disk-encryption-error-messages b/changes/24862-disk-encryption-error-messages new file mode 100644 index 0000000000..bbfd51a443 --- /dev/null +++ b/changes/24862-disk-encryption-error-messages @@ -0,0 +1 @@ +Improved error messages when Fleet admin tries to upload a FileVault(macOS) or a BitLocker(Windows) configuration profile. diff --git a/ee/server/service/mdm_profiles.go b/ee/server/service/mdm_profiles.go index fa9e0b23cb..13a11df02f 100644 --- a/ee/server/service/mdm_profiles.go +++ b/ee/server/service/mdm_profiles.go @@ -1,6 +1,10 @@ package service -import "text/template" +import ( + "text/template" + + "github.com/fleetdm/fleet/v4/server/mdm/apple/mobileconfig" +) type fileVaultProfileOptions struct { PayloadIdentifier string @@ -22,9 +26,9 @@ var fileVaultProfileTemplate = template.Must(template.New("").Option("missingkey PayloadDisplayName FileVault 2 PayloadIdentifier - com.apple.MCX.FileVault2.3548D750-6357-4910-8DEA-D80ADCE2C787 + ` + mobileconfig.FleetFileVaultPayloadType + `.3548D750-6357-4910-8DEA-D80ADCE2C787 PayloadType - com.apple.MCX.FileVault2 + ` + mobileconfig.FleetFileVaultPayloadType + ` PayloadUUID 3548D750-6357-4910-8DEA-D80ADCE2C787 PayloadVersion @@ -44,9 +48,9 @@ var fileVaultProfileTemplate = template.Must(template.New("").Option("missingkey PayloadDisplayName FileVault Recovery Key Escrow PayloadIdentifier - com.apple.security.FDERecoveryKeyEscrow.3690D771-DCB8-4D5D-97D6-209A138DF03E + ` + mobileconfig.FleetRecoveryKeyEscrowPayloadType + `.3690D771-DCB8-4D5D-97D6-209A138DF03E PayloadType - com.apple.security.FDERecoveryKeyEscrow + ` + mobileconfig.FleetRecoveryKeyEscrowPayloadType + ` PayloadUUID 3C329F2B-3D47-4141-A2B5-5C52A2FD74F8 PayloadVersion @@ -72,9 +76,9 @@ var fileVaultProfileTemplate = template.Must(template.New("").Option("missingkey dontAllowFDEDisable PayloadIdentifier - com.apple.MCX.62024f29-105E-497A-A724-1D5BA4D9E854 + ` + mobileconfig.FleetFileVaultOptionsPayloadType + `.62024f29-105E-497A-A724-1D5BA4D9E854 PayloadType - com.apple.MCX + ` + mobileconfig.FleetFileVaultOptionsPayloadType + ` PayloadUUID 62024f29-105E-497A-A724-1D5BA4D9E854 PayloadVersion diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/helpers.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/helpers.tsx index 6c849605a7..d938d033e0 100644 --- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/helpers.tsx +++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/helpers.tsx @@ -51,7 +51,23 @@ export const getErrorMessage = (err: AxiosResponse) => { ) { return ( - {apiReason} To control these settings, go to Disk encryption. + Couldn't add. The configuration profile can't include + BitLocker settings. To control these settings, go to{" "} + Disk encryption. + + ); + } + + if ( + apiReason.includes( + "The configuration profile can't include FileVault settings." + ) + ) { + return ( + + Couldn't add. The configuration profile can't include + FileVault settings. To control these settings, go to{" "} + Disk encryption. ); } diff --git a/server/fleet/apple_mdm_test.go b/server/fleet/apple_mdm_test.go index 16155052db..56711b892d 100644 --- a/server/fleet/apple_mdm_test.go +++ b/server/fleet/apple_mdm_test.go @@ -114,17 +114,17 @@ func TestMDMAppleConfigProfileScreenPayloadContent(t *testing.T) { { testName: "AllFileVaultScreened", payloadTypes: []string{"com.apple.security.FDERecoveryKeyEscrow", "com.apple.MCX.FileVault2", "com.apple.security.FDERecoveryRedirect"}, - shouldFail: []string{"com.apple.security.FDERecoveryKeyEscrow", "com.apple.MCX.FileVault2", "com.apple.security.FDERecoveryRedirect"}, + shouldFail: []string{"The configuration profile can't include FileVault settings."}, }, { testName: "FileVault2Screened", payloadTypes: []string{"com.apple.MCX.FileVault2"}, - shouldFail: []string{"com.apple.MCX.FileVault2"}, + shouldFail: []string{"The configuration profile can't include FileVault settings."}, }, { testName: "FDERecoveryKeyEscrowScreened", payloadTypes: []string{"com.apple.security.FDERecoveryKeyEscrow"}, - shouldFail: []string{"com.apple.security.FDERecoveryKeyEscrow"}, + shouldFail: []string{"The configuration profile can't include FileVault settings."}, }, { testName: "FDERecoveryRedirectScreened", @@ -139,7 +139,7 @@ func TestMDMAppleConfigProfileScreenPayloadContent(t *testing.T) { { testName: "FileVaultMixedWithOtherPayloadTypes", payloadTypes: []string{"com.apple.MCX.FileVault2", "com.apple.security.firewall", "com.apple.security.FDERecoveryKeyEscrow", "com.apple.MCX"}, - shouldFail: []string{"com.apple.MCX.FileVault2", "com.apple.security.FDERecoveryKeyEscrow"}, + shouldFail: []string{"The configuration profile can't include FileVault settings."}, }, { testName: "NoPayloadContent", diff --git a/server/fleet/windows_mdm.go b/server/fleet/windows_mdm.go index c06e18e1fe..9e079cd09f 100644 --- a/server/fleet/windows_mdm.go +++ b/server/fleet/windows_mdm.go @@ -146,8 +146,8 @@ func (m *MDMWindowsConfigProfile) ValidateUserProvided() error { return nil } -var fleetProvidedLocURIValidationMap = map[string][2]string{ - syncml.FleetBitLockerTargetLocURI: {"BitLocker", "mdm.enable_disk_encryption"}, +var fleetProvidedLocURIValidationMap = map[string][]string{ + syncml.FleetBitLockerTargetLocURI: nil, syncml.FleetOSUpdateTargetLocURI: {"Windows updates", "mdm.windows_updates"}, } @@ -155,7 +155,14 @@ func validateFleetProvidedLocURI(locURI string) error { sanitizedLocURI := strings.TrimSpace(locURI) for fleetLocURI, errHints := range fleetProvidedLocURIValidationMap { if strings.Contains(sanitizedLocURI, fleetLocURI) { - return fmt.Errorf("Custom configuration profiles can't include %s settings. To control these settings, use the %s option.", errHints[0], errHints[1]) + if fleetLocURI == syncml.FleetBitLockerTargetLocURI { + return errors.New("The configuration profile can't include BitLocker settings. To control these settings use disk encryption endpoint.") + } + if len(errHints) == 2 { + return fmt.Errorf("Custom configuration profiles can't include %s settings. To control these settings, use the %s option.", + errHints[0], errHints[1]) + } + return fmt.Errorf("Custom configuration profiles can't include these settings. %q", errHints) } } diff --git a/server/fleet/windows_mdm_test.go b/server/fleet/windows_mdm_test.go index 93f56f8b0a..d8fae918dc 100644 --- a/server/fleet/windows_mdm_test.go +++ b/server/fleet/windows_mdm_test.go @@ -65,7 +65,7 @@ func TestValidateUserProvided(t *testing.T) { `), }, - wantErr: "Custom configuration profiles can't include BitLocker settings.", + wantErr: "The configuration profile can't include BitLocker settings.", }, { name: "Reserved LocURI with implicit ./Device prefix", @@ -78,7 +78,7 @@ func TestValidateUserProvided(t *testing.T) { `), }, - wantErr: "Custom configuration profiles can't include BitLocker settings.", + wantErr: "The configuration profile can't include BitLocker settings.", }, { name: "XML with Multiple Replace Elements", @@ -121,7 +121,7 @@ func TestValidateUserProvided(t *testing.T) { `), }, - wantErr: "Custom configuration profiles can't include BitLocker settings", + wantErr: "The configuration profile can't include BitLocker settings.", }, { name: "XML with Mixed Replace and Add", diff --git a/server/mdm/apple/mobileconfig/mobileconfig.go b/server/mdm/apple/mobileconfig/mobileconfig.go index feb718a5fe..21e1e57baf 100644 --- a/server/mdm/apple/mobileconfig/mobileconfig.go +++ b/server/mdm/apple/mobileconfig/mobileconfig.go @@ -18,7 +18,10 @@ import ( const ( // FleetFileVaultPayloadIdentifier is the value for the PayloadIdentifier // used by Fleet to configure FileVault and FileVault Escrow. - FleetFileVaultPayloadIdentifier = "com.fleetdm.fleet.mdm.filevault" + FleetFileVaultPayloadIdentifier = "com.fleetdm.fleet.mdm.filevault" + FleetFileVaultPayloadType = "com.apple.MCX.FileVault2" + FleetFileVaultOptionsPayloadType = "com.apple.MCX" + FleetRecoveryKeyEscrowPayloadType = "com.apple.security.FDERecoveryKeyEscrow" // FleetdConfigPayloadIdentifier is the value for the PayloadIdentifier used // by fleetd to read configuration values from the system. @@ -58,15 +61,12 @@ func FleetPayloadIdentifiers() map[string]struct{} { // FleetPayloadTypes returns a map of PayloadType strings // that are handled and delivered by Fleet. -// -// TODO(roperzh): when I was refactoring this, I noticed that the strings are -// not constants, we should refactor that and use the constant in the templates -// we use to generate the FileVault mobileconfig. func FleetPayloadTypes() map[string]struct{} { return map[string]struct{}{ - "com.apple.security.FDERecoveryKeyEscrow": {}, - "com.apple.MCX.FileVault2": {}, - "com.apple.security.FDERecoveryRedirect": {}, + FleetRecoveryKeyEscrowPayloadType: {}, + FleetFileVaultPayloadType: {}, + FleetFileVaultOptionsPayloadType: {}, + "com.apple.security.FDERecoveryRedirect": {}, // no longer supported in macOS 10.13 and later } } @@ -249,6 +249,13 @@ func (mc *Mobileconfig) ScreenPayloads() error { } if len(screenedTypes) > 0 { + for _, t := range screenedTypes { + switch t { + case FleetFileVaultPayloadType, FleetFileVaultOptionsPayloadType, FleetRecoveryKeyEscrowPayloadType: + return errors.New("Couldn't add. The configuration profile can't include FileVault settings. " + + "To control these settings use disk encryption endpoint.") + } + } return fmt.Errorf("unsupported PayloadType(s): %s", strings.Join(screenedTypes, ", ")) } diff --git a/server/service/apple_mdm_test.go b/server/service/apple_mdm_test.go index 152f4cffbb..96831b7274 100644 --- a/server/service/apple_mdm_test.go +++ b/server/service/apple_mdm_test.go @@ -1792,7 +1792,7 @@ func TestMDMBatchSetAppleProfiles(t *testing.T) { false, nil, nil, - [][]byte{[]byte(` + [][]byte{[]byte(fmt.Sprintf(` @@ -1806,7 +1806,7 @@ func TestMDMBatchSetAppleProfiles(t *testing.T) { PayloadIdentifier com.apple.MCX.FileVault2.A5874654-D6BA-4649-84B5-43847953B369 PayloadType - com.apple.MCX.FileVault2 + %s PayloadUUID A5874654-D6BA-4649-84B5-43847953B369 PayloadVersion @@ -1824,8 +1824,8 @@ func TestMDMBatchSetAppleProfiles(t *testing.T) { PayloadVersion 1 - `)}, - "unsupported PayloadType(s)", + `, mobileconfig.FleetFileVaultPayloadType))}, + "The configuration profile can't include FileVault settings.", }, } for name := range fleetmdm.FleetReservedProfileNames() { diff --git a/server/service/integration_mdm_profiles_test.go b/server/service/integration_mdm_profiles_test.go index fda94af6e6..a94d3ceab0 100644 --- a/server/service/integration_mdm_profiles_test.go +++ b/server/service/integration_mdm_profiles_test.go @@ -2350,7 +2350,12 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMAppleProfiles() { mobileconfigForTestWithContent("N1", "I1", "II1", p, ""), }}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID)) errMsg := extractServerErrorText(res.Body) - require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadType(s): %s", p)) + switch p { + case mobileconfig.FleetFileVaultPayloadType, mobileconfig.FleetFileVaultOptionsPayloadType, mobileconfig.FleetRecoveryKeyEscrowPayloadType: + assert.Contains(t, errMsg, "Validation Failed: Couldn't add. The configuration profile can't include FileVault settings.") + default: + assert.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadType(s): %s", p)) + } } // payloads with reserved identifiers @@ -3203,8 +3208,10 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() { assertAppleDeclaration("foo.txt", "foo-ident", 0, nil, http.StatusBadRequest, "Couldn't add profile. The file should be a .mobileconfig, XML, or JSON file.") // Windows-reserved LocURI - assertWindowsProfile("bitlocker.xml", syncml.FleetBitLockerTargetLocURI, 0, nil, http.StatusBadRequest, "Couldn't add. Custom configuration profiles can't include BitLocker settings.") - assertWindowsProfile("updates.xml", syncml.FleetOSUpdateTargetLocURI, testTeam.ID, nil, http.StatusBadRequest, "Couldn't add. Custom configuration profiles can't include Windows updates settings.") + assertWindowsProfile("bitlocker.xml", syncml.FleetBitLockerTargetLocURI, 0, nil, http.StatusBadRequest, + "Couldn't add. The configuration profile can't include BitLocker settings.") + assertWindowsProfile("updates.xml", syncml.FleetOSUpdateTargetLocURI, testTeam.ID, nil, http.StatusBadRequest, + "Couldn't add. Custom configuration profiles can't include Windows updates settings.") // Fleet-reserved profiles for name := range servermdm.FleetReservedProfileNames() { @@ -4304,7 +4311,12 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfiles() { {Name: "N4", Contents: declarationForTest("D1")}, }}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID)) errMsg := extractServerErrorText(res.Body) - require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadType(s): %s", p)) + switch p { + case mobileconfig.FleetFileVaultPayloadType, mobileconfig.FleetFileVaultOptionsPayloadType, mobileconfig.FleetRecoveryKeyEscrowPayloadType: + assert.Contains(t, errMsg, "Validation Failed: Couldn't add. The configuration profile can't include FileVault settings.") + default: + assert.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadType(s): %s", p)) + } } // payloads with reserved identifiers @@ -4354,7 +4366,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfiles() { {Name: "N3", Contents: syncMLForTest("./Foo/Bar")}, }}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID)) errMsg = extractServerErrorText(res.Body) - require.Contains(t, errMsg, "Custom configuration profiles can't include BitLocker settings. To control these settings, use the mdm.enable_disk_encryption option.") + assert.Contains(t, errMsg, "Validation Failed: The configuration profile can't include BitLocker settings.") // os updates res = s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", batchSetMDMProfilesRequest{Profiles: []fleet.MDMProfileBatchPayload{ @@ -4578,7 +4590,12 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfilesBackwardsCompat() { "N3": syncMLForTest("./Foo/Bar"), }}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID)) errMsg := extractServerErrorText(res.Body) - require.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadType(s): %s", p)) + switch p { + case mobileconfig.FleetFileVaultPayloadType, mobileconfig.FleetFileVaultOptionsPayloadType, mobileconfig.FleetRecoveryKeyEscrowPayloadType: + assert.Contains(t, errMsg, "Validation Failed: Couldn't add. The configuration profile can't include FileVault settings.") + default: + assert.Contains(t, errMsg, fmt.Sprintf("Validation Failed: unsupported PayloadType(s): %s", p)) + } } // payloads with reserved identifiers @@ -4599,7 +4616,7 @@ func (s *integrationMDMTestSuite) TestBatchSetMDMProfilesBackwardsCompat() { "N3": syncMLForTest("./Foo/Bar"), }}, http.StatusUnprocessableEntity, "team_id", fmt.Sprint(tm.ID)) errMsg := extractServerErrorText(res.Body) - require.Contains(t, errMsg, "Custom configuration profiles can't include BitLocker settings. To control these settings, use the mdm.enable_disk_encryption option.") + assert.Contains(t, errMsg, "Validation Failed: The configuration profile can't include BitLocker settings.") // os updates res = s.Do("POST", "/api/v1/fleet/mdm/profiles/batch", map[string]any{"profiles": map[string][]byte{ diff --git a/server/service/mdm_test.go b/server/service/mdm_test.go index 4402249658..4ca085b84b 100644 --- a/server/service/mdm_test.go +++ b/server/service/mdm_test.go @@ -9,6 +9,7 @@ import ( "crypto/x509/pkix" "database/sql" "errors" + "fmt" "math/big" "net/http" "net/http/httptest" @@ -17,6 +18,7 @@ import ( "testing" "time" + "github.com/fleetdm/fleet/v4/server/mdm/apple/mobileconfig" nanodep_client "github.com/fleetdm/fleet/v4/server/mdm/nanodep/client" nanodep_mock "github.com/fleetdm/fleet/v4/server/mock/nanodep" "github.com/jmoiron/sqlx" @@ -1216,7 +1218,9 @@ func TestUploadWindowsMDMConfigProfileValidations(t *testing.T) { {"duplicate profile name", 0, `duplicate`, true, "configuration profile with this name already exists"}, {"multiple Replace", 0, `ab`, true, ""}, {"Replace and non-Replace", 0, `ab`, true, "Windows configuration profiles can only have or top level elements."}, - {"BitLocker profile", 0, `./Device/Vendor/MSFT/BitLocker/AllowStandardUserEncryption`, true, "Custom configuration profiles can't include BitLocker settings."}, + {"BitLocker profile", 0, + `./Device/Vendor/MSFT/BitLocker/AllowStandardUserEncryption`, true, + "The configuration profile can't include BitLocker settings."}, {"Windows updates profile", 0, ` ./Device/Vendor/MSFT/Policy/Config/Update/ConfigureDeadlineNoAutoRebootForFeatureUpdates `, true, "Custom configuration profiles can't include Windows updates settings."}, {"unsupported Fleet variable", 0, `$FLEET_VAR_BOZO`, true, "Fleet variable"}, @@ -1228,7 +1232,9 @@ func TestUploadWindowsMDMConfigProfileValidations(t *testing.T) { {"team duplicate profile name", 1, `duplicate`, true, "configuration profile with this name already exists"}, {"team multiple Replace", 1, `ab`, true, ""}, {"team Replace and non-Replace", 1, `ab`, true, "Windows configuration profiles can only have or top level elements."}, - {"team BitLocker profile", 1, `./Device/Vendor/MSFT/BitLocker/AllowStandardUserEncryption`, true, "Custom configuration profiles can't include BitLocker settings."}, + {"team BitLocker profile", 1, + `./Device/Vendor/MSFT/BitLocker/AllowStandardUserEncryption`, true, + "The configuration profile can't include BitLocker settings."}, {"team Windows updates profile", 1, ` ./Device/Vendor/MSFT/Policy/Config/Update/ConfigureDeadlineNoAutoRebootForFeatureUpdates `, true, "Custom configuration profiles can't include Windows updates settings."}, {"invalid team", 2, ``, true, "not found"}, @@ -1541,7 +1547,7 @@ func TestMDMBatchSetProfiles(t *testing.T) { nil, []fleet.MDMProfileBatchPayload{ { - Name: "foo", Contents: []byte(` + Name: "foo", Contents: []byte(fmt.Sprintf(` @@ -1555,7 +1561,7 @@ func TestMDMBatchSetProfiles(t *testing.T) { PayloadIdentifier com.apple.MCX.FileVault2.A5874654-D6BA-4649-84B5-43847953B369 PayloadType - com.apple.MCX.FileVault2 + %s PayloadUUID A5874654-D6BA-4649-84B5-43847953B369 PayloadVersion @@ -1573,10 +1579,10 @@ func TestMDMBatchSetProfiles(t *testing.T) { PayloadVersion 1 - `), + `, mobileconfig.FleetFileVaultPayloadType)), }, }, - "unsupported PayloadType(s)", + "The configuration profile can't include FileVault settings.", }, { "unsupported Apple config profile Fleet variable",