Experimental fleet server config for custom updates & disk encryption settings (#34598)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33316

Merges in changes made in this community PR:
https://github.com/fleetdm/fleet/pull/33665

Adds support for Windows and tests, also blocks the feature on fleet
free

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] 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.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)

- [x] QA'd all new/changed functionality manually

---------

Co-authored-by: Wesley Whetstone <wesw@stripe.com>
Co-authored-by: Wesley Whetstone <jckwhet@gmail.com>
This commit is contained in:
Jordan Montgomery
2025-10-22 13:51:10 -04:00
committed by GitHub
co-authored by Wesley Whetstone Wesley Whetstone
parent 786c0ced44
commit 7593d102fb
11 changed files with 195 additions and 53 deletions
@@ -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
+5
View File
@@ -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")
+28 -25
View File
@@ -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"),
+6 -4
View File
@@ -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 cant include OS updates settings. To control these settings, go to OS updates.")
if !allowCustomOSUpdatesAndFileVault {
return NewInvalidArgumentError(r.Type, "Declaration profile cant include OS updates settings. To control these settings, go to OS updates.")
}
}
if _, forbidden := ForbiddenDeclTypes[r.Type]; forbidden {
+81 -3
View File
@@ -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 cant 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" {
+6 -3
View File
@@ -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)
}
+50 -4
View File
@@ -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(`<Replace><Target><LocURI>Custom/URI</LocURI></Target></Replace>`),
},
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(`<Replace><Target><LocURI>/Vendor/MSFT/Policy/Config/Update/something</LocURI></Target></Replace>`),
},
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(`<Replace><Target><LocURI>/Vendor/MSFT/Policy/Config/Update/something</LocURI></Target></Replace>`),
},
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(`<Replace><Target><LocURI>/Vendor/MSFT/BitLocker/something</LocURI></Target></Replace>`),
},
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(`<Replace><Target><LocURI>/Vendor/MSFT/BitLocker/something</LocURI></Target></Replace>`),
},
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 {
@@ -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:
+1 -1
View File
@@ -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())
}
}
+4 -4
View File
@@ -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()))
}
+8 -6
View File
@@ -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.`