diff --git a/changes/33665-custom-os-updates-and-filevault b/changes/33665-custom-os-updates-and-filevault new file mode 100644 index 0000000000..c52ce4c9c7 --- /dev/null +++ b/changes/33665-custom-os-updates-and-filevault @@ -0,0 +1 @@ +* Added experimental FLEET_MDM_ENABLE_CUSTOM_OS_UPDATES_AND_FILEVAULT configuration to allow deploying custom OS settings including Filevault payloads and macOS and Windows update settings diff --git a/cmd/fleet/serve.go b/cmd/fleet/serve.go index 17a93adc46..9db37922b9 100644 --- a/cmd/fleet/serve.go +++ b/cmd/fleet/serve.go @@ -471,6 +471,11 @@ the way that the Fleet server works. } } + if config.MDM.EnableCustomOSUpdatesAndFileVault && !license.IsPremium() { + config.MDM.EnableCustomOSUpdatesAndFileVault = false + level.Warn(logger).Log("msg", "Disabling custom OS updates and FileVault management because Fleet Premium license is not present") + } + mdmStorage, err := mds.NewMDMAppleMDMStorage() if err != nil { initFatal(err, "initialize mdm apple MySQL storage") diff --git a/server/config/config.go b/server/config/config.go index f93afa531a..b444e976d4 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -737,7 +737,8 @@ type MDMConfig struct { microsoftWSTEPCertPEM []byte microsoftWSTEPKeyPEM []byte - SSORateLimitPerMinute int `yaml:"sso_rate_limit_per_minute"` + SSORateLimitPerMinute int `yaml:"sso_rate_limit_per_minute"` + EnableCustomOSUpdatesAndFileVault bool `yaml:"enable_custom_os_updates_and_filevault"` } type CalendarConfig struct { @@ -1473,6 +1474,7 @@ func (man Manager) addConfigs() { man.addConfigString("mdm.windows_wstep_identity_cert_bytes", "", "Microsoft WSTEP PEM-encoded certificate bytes") man.addConfigString("mdm.windows_wstep_identity_key_bytes", "", "Microsoft WSTEP PEM-encoded private key bytes") man.addConfigInt("mdm.sso_rate_limit_per_minute", 0, "Number of allowed requests per minute to MDM SSO endpoints (default is sharing login rate limit bucket)") + man.addConfigBool("mdm.enable_custom_os_updates_and_filevault", false, "Experimental feature: allows usage of specific Apple MDM profiles for OS updates and FileVault") // Calendar integration man.addConfigDuration( @@ -1741,30 +1743,31 @@ func (man Manager) LoadConfig() FleetConfig { }, }, MDM: MDMConfig{ - AppleAPNsCert: man.getConfigString("mdm.apple_apns_cert"), - AppleAPNsCertBytes: man.getConfigString("mdm.apple_apns_cert_bytes"), - AppleAPNsKey: man.getConfigString("mdm.apple_apns_key"), - AppleAPNsKeyBytes: man.getConfigString("mdm.apple_apns_key_bytes"), - AppleSCEPCert: man.getConfigString("mdm.apple_scep_cert"), - AppleSCEPCertBytes: man.getConfigString("mdm.apple_scep_cert_bytes"), - AppleSCEPKey: man.getConfigString("mdm.apple_scep_key"), - AppleSCEPKeyBytes: man.getConfigString("mdm.apple_scep_key_bytes"), - AppleBMServerToken: man.getConfigString("mdm.apple_bm_server_token"), - AppleBMServerTokenBytes: man.getConfigString("mdm.apple_bm_server_token_bytes"), - AppleBMCert: man.getConfigString("mdm.apple_bm_cert"), - AppleBMCertBytes: man.getConfigString("mdm.apple_bm_cert_bytes"), - AppleBMKey: man.getConfigString("mdm.apple_bm_key"), - AppleBMKeyBytes: man.getConfigString("mdm.apple_bm_key_bytes"), - AppleEnable: man.getConfigBool("mdm.apple_enable"), - AppleSCEPSignerValidityDays: man.getConfigInt("mdm.apple_scep_signer_validity_days"), - AppleSCEPSignerAllowRenewalDays: man.getConfigInt("mdm.apple_scep_signer_allow_renewal_days"), - AppleSCEPChallenge: man.getConfigString("mdm.apple_scep_challenge"), - AppleDEPSyncPeriodicity: man.getConfigDuration("mdm.apple_dep_sync_periodicity"), - WindowsWSTEPIdentityCert: man.getConfigString("mdm.windows_wstep_identity_cert"), - WindowsWSTEPIdentityKey: man.getConfigString("mdm.windows_wstep_identity_key"), - WindowsWSTEPIdentityCertBytes: man.getConfigString("mdm.windows_wstep_identity_cert_bytes"), - WindowsWSTEPIdentityKeyBytes: man.getConfigString("mdm.windows_wstep_identity_key_bytes"), - SSORateLimitPerMinute: man.getConfigInt("mdm.sso_rate_limit_per_minute"), + AppleAPNsCert: man.getConfigString("mdm.apple_apns_cert"), + AppleAPNsCertBytes: man.getConfigString("mdm.apple_apns_cert_bytes"), + AppleAPNsKey: man.getConfigString("mdm.apple_apns_key"), + AppleAPNsKeyBytes: man.getConfigString("mdm.apple_apns_key_bytes"), + AppleSCEPCert: man.getConfigString("mdm.apple_scep_cert"), + AppleSCEPCertBytes: man.getConfigString("mdm.apple_scep_cert_bytes"), + AppleSCEPKey: man.getConfigString("mdm.apple_scep_key"), + AppleSCEPKeyBytes: man.getConfigString("mdm.apple_scep_key_bytes"), + AppleBMServerToken: man.getConfigString("mdm.apple_bm_server_token"), + AppleBMServerTokenBytes: man.getConfigString("mdm.apple_bm_server_token_bytes"), + AppleBMCert: man.getConfigString("mdm.apple_bm_cert"), + AppleBMCertBytes: man.getConfigString("mdm.apple_bm_cert_bytes"), + AppleBMKey: man.getConfigString("mdm.apple_bm_key"), + AppleBMKeyBytes: man.getConfigString("mdm.apple_bm_key_bytes"), + AppleEnable: man.getConfigBool("mdm.apple_enable"), + AppleSCEPSignerValidityDays: man.getConfigInt("mdm.apple_scep_signer_validity_days"), + AppleSCEPSignerAllowRenewalDays: man.getConfigInt("mdm.apple_scep_signer_allow_renewal_days"), + AppleSCEPChallenge: man.getConfigString("mdm.apple_scep_challenge"), + AppleDEPSyncPeriodicity: man.getConfigDuration("mdm.apple_dep_sync_periodicity"), + WindowsWSTEPIdentityCert: man.getConfigString("mdm.windows_wstep_identity_cert"), + WindowsWSTEPIdentityKey: man.getConfigString("mdm.windows_wstep_identity_key"), + WindowsWSTEPIdentityCertBytes: man.getConfigString("mdm.windows_wstep_identity_cert_bytes"), + WindowsWSTEPIdentityKeyBytes: man.getConfigString("mdm.windows_wstep_identity_key_bytes"), + SSORateLimitPerMinute: man.getConfigInt("mdm.sso_rate_limit_per_minute"), + EnableCustomOSUpdatesAndFileVault: man.getConfigBool("mdm.enable_custom_os_updates_and_filevault"), }, Calendar: CalendarConfig{ Periodicity: man.getConfigDuration("calendar.periodicity"), diff --git a/server/fleet/apple_mdm.go b/server/fleet/apple_mdm.go index 437e5027b1..f23f299642 100644 --- a/server/fleet/apple_mdm.go +++ b/server/fleet/apple_mdm.go @@ -281,7 +281,7 @@ func ValidateNoSecretsInProfileName(xmlContent []byte) error { return nil } -func (cp MDMAppleConfigProfile) ValidateUserProvided() error { +func (cp MDMAppleConfigProfile) ValidateUserProvided(allowCustomOSUpdatesAndFileVault bool) error { // first screen the top-level object for reserved identifiers and names if _, ok := mobileconfig.FleetPayloadIdentifiers()[cp.Identifier]; ok { return fmt.Errorf("payload identifier %s is not allowed", cp.Identifier) @@ -292,7 +292,7 @@ func (cp MDMAppleConfigProfile) ValidateUserProvided() error { } // then screen the payload content for reserved identifiers, names, and types - return cp.Mobileconfig.ScreenPayloads() + return cp.Mobileconfig.ScreenPayloads(allowCustomOSUpdatesAndFileVault) } // HostMDMAppleProfile represents the status of an Apple MDM profile in a host. @@ -715,12 +715,14 @@ var ForbiddenDeclTypes = map[string]struct{}{ "com.apple.configuration.watch.enrollment": {}, } -func (r *MDMAppleRawDeclaration) ValidateUserProvided() error { +func (r *MDMAppleRawDeclaration) ValidateUserProvided(allowCustomOSUpdatesAndFileVault bool) error { var err error // Check against types we don't allow if r.Type == `com.apple.configuration.softwareupdate.enforcement.specific` { - return NewInvalidArgumentError(r.Type, "Declaration profile can’t include OS updates settings. To control these settings, go to OS updates.") + if !allowCustomOSUpdatesAndFileVault { + return NewInvalidArgumentError(r.Type, "Declaration profile can’t include OS updates settings. To control these settings, go to OS updates.") + } } if _, forbidden := ForbiddenDeclTypes[r.Type]; forbidden { diff --git a/server/fleet/apple_mdm_test.go b/server/fleet/apple_mdm_test.go index ac0be40bbe..40fbed8aad 100644 --- a/server/fleet/apple_mdm_test.go +++ b/server/fleet/apple_mdm_test.go @@ -156,7 +156,8 @@ func TestMDMAppleConfigProfileScreenPayloadContent(t *testing.T) { require.Equal(t, "ValidName", parsed.Name) require.Equal(t, "ValidIdentifier", parsed.Identifier) - err = parsed.ValidateUserProvided() + // Test with allowCustomOSUpdatesAndFileVault = false (default behavior) + err = parsed.ValidateUserProvided(false) for _, pt := range c.shouldFail { require.Error(t, err) require.ErrorContains(t, err, pt) @@ -168,6 +169,83 @@ func TestMDMAppleConfigProfileScreenPayloadContent(t *testing.T) { } } +func TestMDMAppleConfigProfileAllowCustomOSUpdatesAndFileVault(t *testing.T) { + cases := []struct { + testName string + payloadTypes []string + }{ + { + testName: "FileVault2Allowed", + payloadTypes: []string{"com.apple.MCX.FileVault2"}, + }, + { + testName: "FDERecoveryKeyEscrowAllowed", + payloadTypes: []string{"com.apple.security.FDERecoveryKeyEscrow"}, + }, + { + testName: "AllFileVaultTypesAllowed", + payloadTypes: []string{"com.apple.security.FDERecoveryKeyEscrow", "com.apple.MCX.FileVault2"}, + }, + { + testName: "FileVaultMixedWithOtherPayloadTypes", + payloadTypes: []string{"com.apple.MCX.FileVault2", "com.apple.security.firewall", "com.apple.security.FDERecoveryKeyEscrow"}, + }, + } + + for _, c := range cases { + t.Run(c.testName, func(t *testing.T) { + mc := MobileconfigForTest("ValidName", "ValidIdentifier", uuid.NewString(), mcPayloadContentForTest(c.payloadTypes)) + parsed, err := NewMDMAppleConfigProfile(mc, nil) + require.NoError(t, err) + require.Equal(t, "ValidName", parsed.Name) + require.Equal(t, "ValidIdentifier", parsed.Identifier) + + // When allowCustomOSUpdatesAndFileVault = true, these profiles should be allowed + err = parsed.ValidateUserProvided(true) + require.NoError(t, err) + }) + } +} + +func TestMDMAppleDeclarationAllowCustomOSUpdatesAndFileVault(t *testing.T) { + t.Run("OSUpdateDeclarationBlockedByDefault", func(t *testing.T) { + decl := &MDMAppleRawDeclaration{ + Type: "com.apple.configuration.softwareupdate.enforcement.specific", + Identifier: "test-os-update", + } + + // Should fail when allowCustomOSUpdatesAndFileVault = false + err := decl.ValidateUserProvided(false) + require.Error(t, err) + require.ErrorContains(t, err, "Declaration profile can’t include OS updates settings") + }) + + t.Run("OSUpdateDeclarationAllowedWhenFlagEnabled", func(t *testing.T) { + decl := &MDMAppleRawDeclaration{ + Type: "com.apple.configuration.softwareupdate.enforcement.specific", + Identifier: "test-os-update", + } + + // Should succeed when allowCustomOSUpdatesAndFileVault = true + err := decl.ValidateUserProvided(true) + require.NoError(t, err) + }) + + t.Run("OtherDeclarationsUnaffected", func(t *testing.T) { + decl := &MDMAppleRawDeclaration{ + Type: "com.apple.configuration.passcode.settings", + Identifier: "test-passcode", + } + + // Should succeed regardless of flag + err := decl.ValidateUserProvided(false) + require.NoError(t, err) + + err = decl.ValidateUserProvided(true) + require.NoError(t, err) + }) +} + func TestMDMAppleConfigProfileScreenPayloadIdentifiers(t *testing.T) { cases := []struct { testName string @@ -214,7 +292,7 @@ func TestMDMAppleConfigProfileScreenPayloadIdentifiers(t *testing.T) { require.Equal(t, "ValidName", parsed.Name) require.Equal(t, "ValidIdentifier", parsed.Identifier) - err = parsed.ValidateUserProvided() + err = parsed.ValidateUserProvided(false) for _, pt := range c.shouldFail { require.Error(t, err) require.ErrorContains(t, err, pt) @@ -260,7 +338,7 @@ func TestMDMAppleConfigProfileScreenReservedNames(t *testing.T) { require.Equal(t, c.toplevelName, parsed.Name) require.Equal(t, "ValidIdentifier", parsed.Identifier) - err = parsed.ValidateUserProvided() + err = parsed.ValidateUserProvided(false) if c.shouldFail { require.Error(t, err) if c.toplevelName == "unreserved name" { diff --git a/server/fleet/windows_mdm.go b/server/fleet/windows_mdm.go index d28dd50822..9061c436a2 100644 --- a/server/fleet/windows_mdm.go +++ b/server/fleet/windows_mdm.go @@ -59,7 +59,7 @@ type MDMWindowsConfigProfile struct { // // [1]: http://www.w3.org/TR/2006/REC-xml-20060816 // [2]: https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-MDM/%5bMS-MDM%5d.pdf -func (m *MDMWindowsConfigProfile) ValidateUserProvided() error { +func (m *MDMWindowsConfigProfile) ValidateUserProvided(enableCustomOSUpdates bool) error { if len(bytes.TrimSpace(m.SyncML)) == 0 { return errors.New("The file should include valid XML.") } @@ -137,7 +137,7 @@ func (m *MDMWindowsConfigProfile) ValidateUserProvided() error { case xml.CharData: if inLocURI { - if err := validateFleetProvidedLocURI(string(t)); err != nil { + if err := validateFleetProvidedLocURI(string(t), enableCustomOSUpdates); err != nil { return err } } @@ -152,10 +152,13 @@ var fleetProvidedLocURIValidationMap = map[string][]string{ syncml.FleetOSUpdateTargetLocURI: {"Windows updates", "mdm.windows_updates"}, } -func validateFleetProvidedLocURI(locURI string) error { +func validateFleetProvidedLocURI(locURI string, enableCustomOSUpdates bool) error { sanitizedLocURI := strings.TrimSpace(locURI) for fleetLocURI, errHints := range fleetProvidedLocURIValidationMap { if strings.Contains(sanitizedLocURI, fleetLocURI) { + if fleetLocURI == syncml.FleetOSUpdateTargetLocURI && enableCustomOSUpdates { + continue + } if fleetLocURI == syncml.FleetBitLockerTargetLocURI { return errors.New(syncml.DiskEncryptionProfileRestrictionErrMsg) } diff --git a/server/fleet/windows_mdm_test.go b/server/fleet/windows_mdm_test.go index d756391f7e..694e690d08 100644 --- a/server/fleet/windows_mdm_test.go +++ b/server/fleet/windows_mdm_test.go @@ -10,9 +10,10 @@ import ( func TestValidateUserProvided(t *testing.T) { tests := []struct { - name string - profile MDMWindowsConfigProfile - wantErr string + name string + profile MDMWindowsConfigProfile + allowCustomOSUpdates bool + wantErr string }{ { name: "Valid XML with Replace", @@ -407,6 +408,51 @@ func TestValidateUserProvided(t *testing.T) { }, wantErr: `Profile name "Windows OS Updates" is not allowed`, }, + { + name: "Valid XML with reserved name but experimental allow custom OS updates flag enabled is still not allowed", + profile: MDMWindowsConfigProfile{ + Name: mdm.FleetWindowsOSUpdatesProfileName, + SyncML: []byte(`Custom/URI`), + }, + allowCustomOSUpdates: true, + wantErr: `Profile name "Windows OS Updates" is not allowed`, + }, + { + name: "Valid XML with Windows Update LocURI without experimental allow custom OS updates flag enabled is blocked", + profile: MDMWindowsConfigProfile{ + Name: "FleetieUpdater", + SyncML: []byte(`/Vendor/MSFT/Policy/Config/Update/something`), + }, + allowCustomOSUpdates: false, + wantErr: "Custom configuration profiles can't include Windows updates settings. To control these settings, use the mdm.windows_updates option.", + }, + { + name: "Valid XML with Windows Update LocURI but experimental allow custom OS updates flag enabled is allowed", + profile: MDMWindowsConfigProfile{ + Name: "FleetieUpdater", + SyncML: []byte(`/Vendor/MSFT/Policy/Config/Update/something`), + }, + allowCustomOSUpdates: true, + wantErr: "", + }, + { + name: "Valid XML with Bitlocker LocURI without experimental allow custom OS updates flag enabled is blocked", + profile: MDMWindowsConfigProfile{ + Name: "FleetieUpdater", + SyncML: []byte(`/Vendor/MSFT/BitLocker/something`), + }, + allowCustomOSUpdates: false, + wantErr: "Couldn't add. The configuration profile can't include BitLocker settings.", + }, + { + name: "Valid XML with Bitlocker LocURI without experimental allow custom OS updates flag enabled is blocked", + profile: MDMWindowsConfigProfile{ + Name: "FleetieUpdater", + SyncML: []byte(`/Vendor/MSFT/BitLocker/something`), + }, + allowCustomOSUpdates: true, + wantErr: "Couldn't add. The configuration profile can't include BitLocker settings.", + }, { name: "XML with top level comment", profile: MDMWindowsConfigProfile{ @@ -554,7 +600,7 @@ func TestValidateUserProvided(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := tt.profile.ValidateUserProvided() + err := tt.profile.ValidateUserProvided(tt.allowCustomOSUpdates) if tt.wantErr != "" { require.ErrorContains(t, err, tt.wantErr) } else { diff --git a/server/mdm/apple/mobileconfig/mobileconfig.go b/server/mdm/apple/mobileconfig/mobileconfig.go index 0dd88d48b3..73619c1826 100644 --- a/server/mdm/apple/mobileconfig/mobileconfig.go +++ b/server/mdm/apple/mobileconfig/mobileconfig.go @@ -208,7 +208,7 @@ func (mc Mobileconfig) payloadSummary() ([]payloadSummary, error) { return result, nil } -func (mc *Mobileconfig) ScreenPayloads() error { +func (mc *Mobileconfig) ScreenPayloads(allowCustomOSUpdatesAndFileVault bool) error { pct, err := mc.payloadSummary() if err != nil { // don't error if there's nothing for us to screen. @@ -240,13 +240,15 @@ func (mc *Mobileconfig) ScreenPayloads() error { for _, t := range screenedTypes { switch t { case FleetFileVaultPayloadType, FleetRecoveryKeyEscrowPayloadType: - return errors.New(DiskEncryptionProfileRestrictionErrMsg) + if !allowCustomOSUpdatesAndFileVault { + return errors.New(DiskEncryptionProfileRestrictionErrMsg) + } case FleetCustomSettingsPayloadType: contains, err := ContainsFDEFileVaultOptionsPayload(*mc) if err != nil { return fmt.Errorf("checking for FDEVileVaultOptions payload: %w", err) } - if contains { + if contains && !allowCustomOSUpdatesAndFileVault { return errors.New(DiskEncryptionProfileRestrictionErrMsg) } default: diff --git a/server/mdm/apple/profile_matcher.go b/server/mdm/apple/profile_matcher.go index 4092edc9a4..c91eee477d 100644 --- a/server/mdm/apple/profile_matcher.go +++ b/server/mdm/apple/profile_matcher.go @@ -54,7 +54,7 @@ func (p *profileMatcher) PreassignProfile(ctx context.Context, payload fleet.MDM // team ID is not relevant at this stage, this is just for validation if cp, err := fleet.NewMDMAppleConfigProfile(payload.Profile, nil); err != nil { invArg.Append("profile", err.Error()) - } else if err := cp.ValidateUserProvided(); err != nil { + } else if err := cp.ValidateUserProvided(false); err != nil { invArg.Append("profile", err.Error()) } } diff --git a/server/service/apple_mdm.go b/server/service/apple_mdm.go index 5b8b41b5ae..0f9ca22fd4 100644 --- a/server/service/apple_mdm.go +++ b/server/service/apple_mdm.go @@ -428,7 +428,7 @@ func (svc *Service) NewMDMAppleConfigProfile(ctx context.Context, teamID uint, d }) } - if err := cp.ValidateUserProvided(); err != nil { + if err := cp.ValidateUserProvided(svc.config.MDM.EnableCustomOSUpdatesAndFileVault); err != nil { if strings.Contains(err.Error(), mobileconfig.DiskEncryptionProfileRestrictionErrMsg) { return nil, ctxerr.Wrap(ctx, &fleet.BadRequestError{Message: err.Error() + ` To control these settings use disk encryption endpoint.`}) } @@ -1052,7 +1052,7 @@ func (svc *Service) NewMDMAppleDeclaration(ctx context.Context, teamID uint, dat } // After validation, we should no longer need to keep the expanded secrets. - if err := rawDecl.ValidateUserProvided(); err != nil { + if err := rawDecl.ValidateUserProvided(svc.config.MDM.EnableCustomOSUpdatesAndFileVault); err != nil { return nil, err } @@ -1443,7 +1443,7 @@ func (svc *Service) DeleteMDMAppleDeclaration(ctx context.Context, declUUID stri if err := json.Unmarshal(decl.RawJSON, &d); err != nil { return ctxerr.Wrap(ctx, err, "unmarshalling declaration") } - if err := d.ValidateUserProvided(); err != nil { + if err := d.ValidateUserProvided(svc.config.MDM.EnableCustomOSUpdatesAndFileVault); err != nil { return ctxerr.Wrap(ctx, &fleet.BadRequestError{Message: err.Error()}) } } @@ -2745,7 +2745,7 @@ func (svc *Service) BatchSetMDMAppleProfiles(ctx context.Context, tmID *uint, tm "invalid mobileconfig profile") } - if err := mdmProf.ValidateUserProvided(); err != nil { + if err := mdmProf.ValidateUserProvided(svc.config.MDM.EnableCustomOSUpdatesAndFileVault); err != nil { return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError(fmt.Sprintf("profiles[%d]", i), err.Error())) } diff --git a/server/service/mdm.go b/server/service/mdm.go index 52f9943035..ee00d096c3 100644 --- a/server/service/mdm.go +++ b/server/service/mdm.go @@ -1709,7 +1709,7 @@ func (svc *Service) NewMDMWindowsConfigProfile(ctx context.Context, teamID uint, Name: profileName, SyncML: data, } - if err := cp.ValidateUserProvided(); err != nil { + if err := cp.ValidateUserProvided(svc.config.MDM.EnableCustomOSUpdatesAndFileVault); err != nil { msg := err.Error() if strings.Contains(msg, syncml.DiskEncryptionProfileRestrictionErrMsg) { return nil, ctxerr.Wrap(ctx, @@ -2044,12 +2044,12 @@ func (svc *Service) BatchSetMDMProfiles( return ctxerr.Wrap(ctx, err, "validating profiles") } - appleProfiles, appleDecls, err := getAppleProfiles(ctx, tmID, appCfg, profilesWithSecrets, labelMap) + appleProfiles, appleDecls, err := getAppleProfiles(ctx, tmID, appCfg, profilesWithSecrets, labelMap, svc.config.MDM.EnableCustomOSUpdatesAndFileVault) if err != nil { return ctxerr.Wrap(ctx, err, "validating macOS profiles") } - windowsProfiles, err := getWindowsProfiles(ctx, tmID, appCfg, profilesWithSecrets, labelMap) + windowsProfiles, err := getWindowsProfiles(ctx, tmID, appCfg, profilesWithSecrets, labelMap, svc.config.MDM.EnableCustomOSUpdatesAndFileVault) if err != nil { return ctxerr.Wrap(ctx, err, "validating Windows profiles") } @@ -2330,6 +2330,7 @@ func getAppleProfiles( appCfg *fleet.AppConfig, profiles map[int]fleet.MDMProfileBatchPayload, labelMap map[string]fleet.ConfigurationProfileLabel, + allowCustomOSUpdatesAndFileVault bool, ) (map[int]*fleet.MDMAppleConfigProfile, map[int]*fleet.MDMAppleDeclaration, error) { // any duplicate identifier or name in the provided set results in an error profs := make(map[int]*fleet.MDMAppleConfigProfile, len(profiles)) @@ -2350,7 +2351,7 @@ func getAppleProfiles( return nil, nil, err } - if err := rawDecl.ValidateUserProvided(); err != nil { + if err := rawDecl.ValidateUserProvided(allowCustomOSUpdatesAndFileVault); err != nil { return nil, nil, err } @@ -2448,7 +2449,7 @@ func getAppleProfiles( } } - if err := mdmProf.ValidateUserProvided(); err != nil { + if err := mdmProf.ValidateUserProvided(allowCustomOSUpdatesAndFileVault); err != nil { var iae *fleet.InvalidArgumentError if strings.Contains(err.Error(), mobileconfig.DiskEncryptionProfileRestrictionErrMsg) { iae = fleet.NewInvalidArgumentError(prof.Name, @@ -2505,6 +2506,7 @@ func getWindowsProfiles( appCfg *fleet.AppConfig, profiles map[int]fleet.MDMProfileBatchPayload, labelMap map[string]fleet.ConfigurationProfileLabel, + enableCustomOSUpdatesAndFileVault bool, ) (map[int]*fleet.MDMWindowsConfigProfile, error) { profs := make(map[int]*fleet.MDMWindowsConfigProfile, len(profiles)) @@ -2548,7 +2550,7 @@ func getWindowsProfiles( } } - if err := mdmProf.ValidateUserProvided(); err != nil { + if err := mdmProf.ValidateUserProvided(enableCustomOSUpdatesAndFileVault); err != nil { msg := err.Error() if strings.Contains(msg, syncml.DiskEncryptionProfileRestrictionErrMsg) { msg += ` To control disk encryption use config API endpoint or add "enable_disk_encryption" to your YAML file.`