Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/36188-back-end-add-update-new-hosts-config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Updated backend and GitOps to handle AppleOSUpdateSettings.UpdateNewHosts
4 changes: 4 additions & 0 deletions cmd/fleetctl/fleetctl/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,15 @@ spec:
macos_updates:
minimum_version: 12.3.1
deadline: 2011-03-01
update_new_hosts: true
`)

newAgentOpts := json.RawMessage(`{"config":{"views":{"foo":"bar"}}}`)
newMDMSettings := fleet.TeamMDM{
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.3.1"),
Deadline: optjson.SetString("2011-03-01"),
UpdateNewHosts: optjson.SetBool(true),
},
MacOSSetup: fleet.MacOSSetup{
EnableReleaseDeviceManually: optjson.SetBool(false),
Expand Down Expand Up @@ -282,6 +284,7 @@ spec:
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.3.1"),
Deadline: optjson.SetString("2011-03-01"),
UpdateNewHosts: optjson.SetBool(true),
},
WindowsUpdates: fleet.WindowsUpdates{
DeadlineDays: optjson.SetInt(5),
Expand Down Expand Up @@ -310,6 +313,7 @@ spec:
MacOSUpdates: fleet.AppleOSUpdateSettings{
MinimumVersion: optjson.SetString("12.3.1"),
Deadline: optjson.SetString("2011-03-01"),
UpdateNewHosts: optjson.SetBool(true),
},
WindowsUpdates: fleet.WindowsUpdates{
DeadlineDays: optjson.SetInt(5),
Expand Down
24 changes: 24 additions & 0 deletions cmd/fleetctl/fleetctl/gitops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,17 @@ func TestGitOpsBasicGlobalPremium(t *testing.T) {
_, err = tmpFile.WriteString(
`
controls:
macos_updates:
deadline: "2024-03-03"
minimum_version: "18.0"
ios_updates:
deadline: "2022-02-02"
minimum_version: "17.6"
update_new_hosts: true
ipados_updates:
deadline: "2023-03-03"
minimum_version: "18.0"
update_new_hosts: false
enable_disk_encryption: true
windows_require_bitlocker_pin: true
queries:
Expand Down Expand Up @@ -474,6 +479,16 @@ software:
// Check MDM settings
require.True(t, savedAppConfig.MDM.EnableDiskEncryption.Value)
require.True(t, savedAppConfig.MDM.RequireBitLockerPIN.Value)
require.Equal(t, "18.0", savedAppConfig.MDM.MacOSUpdates.MinimumVersion.Value)
require.Equal(t, "2024-03-03", savedAppConfig.MDM.MacOSUpdates.Deadline.Value)
// To keep things backwards compatible if MinimumVersion & Deadline are set, then UpdateNewHosts should be set to true
require.Equal(t, optjson.SetBool(true), savedAppConfig.MDM.MacOSUpdates.UpdateNewHosts)
require.Equal(t, "2022-02-02", savedAppConfig.MDM.IOSUpdates.Deadline.Value)
require.Equal(t, "17.6", savedAppConfig.MDM.IOSUpdates.MinimumVersion.Value)
require.Equal(t, optjson.Bool{}, savedAppConfig.MDM.IOSUpdates.UpdateNewHosts)
require.Equal(t, "2023-03-03", savedAppConfig.MDM.IPadOSUpdates.Deadline.Value)
require.Equal(t, "18.0", savedAppConfig.MDM.IPadOSUpdates.MinimumVersion.Value)
require.Equal(t, optjson.Bool{}, savedAppConfig.MDM.IPadOSUpdates.UpdateNewHosts)

// Check certificate authorities
assert.True(t, ds.GetGroupedCertificateAuthoritiesFuncInvoked)
Expand Down Expand Up @@ -732,6 +747,9 @@ func TestGitOpsBasicTeam(t *testing.T) {
_, err = tmpFile.WriteString(
`
controls:
macos_updates:
deadline: "2025-10-10"
minimum_version: "18.0"
ios_updates:
deadline: "2024-10-10"
minimum_version: "18.0"
Expand Down Expand Up @@ -788,6 +806,11 @@ software:
assert.Empty(t, enrolledTeamSecrets)
assert.True(t, savedTeam.Config.Features.EnableSoftwareInventory)

assert.Equal(t, "2025-10-10", savedTeam.Config.MDM.MacOSUpdates.Deadline.Value)
assert.Equal(t, "18.0", savedTeam.Config.MDM.MacOSUpdates.MinimumVersion.Value)
// To keep things backwards compatible if MinimumVersion & Deadline are set, then UpdateNewHosts should be set to true
assert.Equal(t, optjson.SetBool(true), savedTeam.Config.MDM.MacOSUpdates.UpdateNewHosts)

// The previous run created the team, so let's rerun with an existing team
_ = RunAppForTest(t, []string{"gitops", "-f", tmpFile.Name()})
assert.Empty(t, enrolledTeamSecrets)
Expand Down Expand Up @@ -1458,6 +1481,7 @@ software:
assert.Empty(t, savedTeam.Config.MDM.WindowsSettings.CustomSettings.Value)
assert.Empty(t, savedTeam.Config.MDM.MacOSUpdates.Deadline.Value)
assert.Empty(t, savedTeam.Config.MDM.MacOSUpdates.MinimumVersion.Value)
assert.False(t, savedTeam.Config.MDM.MacOSUpdates.UpdateNewHosts.Value)
assert.Empty(t, savedTeam.Config.MDM.MacOSSetup.BootstrapPackage.Value)
assert.False(t, savedTeam.Config.MDM.EnableDiskEncryption)
assert.Equal(t, filepath.Base(tmpFile.Name()), *savedTeam.Filename)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@
"enabled_and_configured": true,
"macos_updates": {
"minimum_version": "15.1",
"deadline": "2024-12-31"
"deadline": "2024-12-31",
"update_new_hosts": true
},
"ios_updates": {
"minimum_version": "18.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ android_settings:
macos_updates:
minimum_version: "15.1"
deadline: "2024-12-31"
update_new_hosts:
update_new_hosts: true
ios_updates:
minimum_version: "18.1"
deadline: "2025-12-31"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@
"enable_disk_encryption": true,
"macos_updates": {
"minimum_version": "95.1",
"deadline": "2020-12-31"
"deadline": "2020-12-31",
"update_new_hosts": true
},
"ios_updates": {
"minimum_version": "98.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ controls:
macos_updates:
deadline: "2024-12-31"
minimum_version: "15.1"
update_new_hosts:
update_new_hosts: true
scripts:
- path: ../lib/no-team/scripts/Script Z.ps1
windows_enabled_and_configured: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ controls:
macos_updates:
deadline: "2020-12-31"
minimum_version: "95.1"
update_new_hosts:
update_new_hosts: true
scripts:
- path: "../lib/team-a-👍/scripts/Script B.ps1"
windows_enabled_and_configured: true
Expand Down
27 changes: 26 additions & 1 deletion ee/server/service/teams.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T

var (
macOSMinVersionUpdated bool
updateNewHostsChanged bool
iOSMinVersionUpdated bool
iPadOSMinVersionUpdated bool
windowsUpdatesUpdated bool
Expand All @@ -182,16 +183,18 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
if err := payload.MDM.MacOSUpdates.Validate(); err != nil {
return nil, fleet.NewInvalidArgumentError("macos_updates", err.Error())
}
if payload.MDM.MacOSUpdates.MinimumVersion.Set || payload.MDM.MacOSUpdates.Deadline.Set {
if payload.MDM.MacOSUpdates.MinimumVersion.Set || payload.MDM.MacOSUpdates.Deadline.Set || payload.MDM.MacOSUpdates.UpdateNewHosts.Set {
macOSMinVersionUpdated = team.Config.MDM.MacOSUpdates.MinimumVersion.Value != payload.MDM.MacOSUpdates.MinimumVersion.Value ||
team.Config.MDM.MacOSUpdates.Deadline.Value != payload.MDM.MacOSUpdates.Deadline.Value
updateNewHostsChanged = team.Config.MDM.MacOSUpdates.UpdateNewHosts.Value != payload.MDM.MacOSUpdates.UpdateNewHosts.Value
team.Config.MDM.MacOSUpdates = *payload.MDM.MacOSUpdates
}
}
if payload.MDM.IOSUpdates != nil {
if err := payload.MDM.IOSUpdates.Validate(); err != nil {
return nil, fleet.NewInvalidArgumentError("ios_updates", err.Error())
}

if payload.MDM.IOSUpdates.MinimumVersion.Set || payload.MDM.IOSUpdates.Deadline.Set {
iOSMinVersionUpdated = team.Config.MDM.IOSUpdates.MinimumVersion.Value != payload.MDM.IOSUpdates.MinimumVersion.Value ||
team.Config.MDM.IOSUpdates.Deadline.Value != payload.MDM.IOSUpdates.Deadline.Value
Expand Down Expand Up @@ -376,6 +379,28 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
}
}

if updateNewHostsChanged {
var activity fleet.ActivityDetails
activity = fleet.ActivityTypeEnabledMacosUpdateNewHosts{
TeamID: &team.ID,
TeamName: &team.Name,
}
if payload.MDM != nil && payload.MDM.MacOSUpdates != nil && !payload.MDM.MacOSUpdates.UpdateNewHosts.Value {
activity = fleet.ActivityTypeDisabledMacosUpdateNewHosts{
TeamID: &team.ID,
TeamName: &team.Name,
}
}

if err := svc.NewActivity(
ctx,
authz.UserFromContext(ctx),
activity,
); err != nil {
return nil, ctxerr.Wrap(ctx, err, "create activity for team macOS update new hosts edited")
}
}

if windowsUpdatesUpdated {
var deadline, grace *int
if team.Config.MDM.WindowsUpdates.DeadlineDays.Valid {
Expand Down
14 changes: 7 additions & 7 deletions server/datastore/mysql/apple_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -6782,7 +6782,7 @@ WHERE (
return nil
}

func (ds *Datastore) GetMDMAppleOSUpdatesSettingsByHostSerial(ctx context.Context, serial string) (*fleet.AppleOSUpdateSettings, error) {
func (ds *Datastore) GetMDMAppleOSUpdatesSettingsByHostSerial(ctx context.Context, serial string) (string, *fleet.AppleOSUpdateSettings, error) {
stmt := `
SELECT
team_id, platform
Expand All @@ -6799,15 +6799,15 @@ LIMIT 1`
Platform string `db:"platform"`
}
if err := sqlx.GetContext(ctx, ds.reader(ctx), &dest, stmt, serial); err != nil {
return nil, ctxerr.Wrap(ctx, err, "getting team id for host")
return "", nil, ctxerr.Wrap(ctx, err, "getting team id for host")
}

var settings fleet.AppleOSUpdateSettings
if dest.TeamID == nil {
// use the global settings
ac, err := ds.AppConfig(ctx)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "getting app config for os update settings")
return "", nil, ctxerr.Wrap(ctx, err, "getting app config for os update settings")
}
switch dest.Platform {
case "ios":
Expand All @@ -6817,13 +6817,13 @@ LIMIT 1`
case "darwin":
settings = ac.MDM.MacOSUpdates
default:
return nil, ctxerr.New(ctx, fmt.Sprintf("unsupported platform %s", dest.Platform))
return "", nil, ctxerr.New(ctx, fmt.Sprintf("unsupported platform %s", dest.Platform))
}
} else {
// use the team settings
tm, err := ds.TeamLite(ctx, *dest.TeamID)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "getting team os update settings")
return "", nil, ctxerr.Wrap(ctx, err, "getting team os update settings")
}
switch dest.Platform {
case "ios":
Expand All @@ -6833,11 +6833,11 @@ LIMIT 1`
case "darwin":
settings = tm.Config.MDM.MacOSUpdates
default:
return nil, ctxerr.New(ctx, fmt.Sprintf("unsupported platform %s", dest.Platform))
return "", nil, ctxerr.New(ctx, fmt.Sprintf("unsupported platform %s", dest.Platform))
}
}

return &settings, nil
return dest.Platform, &settings, nil
}

// ClearMDMUpcomingActivitiesDB clears the upcoming activities of the host that
Expand Down
11 changes: 8 additions & 3 deletions server/datastore/mysql/apple_mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8011,9 +8011,14 @@ func TestGetMDMAppleOSUpdatesSettingsByHostSerial(t *testing.T) {

checkDevice := func(t *testing.T, teamID uint, key string, wantVersion string) {
checkExpectedVersion(t, getConfigSettings(teamID, key), wantVersion)
gotSettings, err := ds.GetMDMAppleOSUpdatesSettingsByHostSerial(context.Background(), devicesByKey[key].SerialNumber)
platform, gotSettings, err := ds.GetMDMAppleOSUpdatesSettingsByHostSerial(context.Background(), devicesByKey[key].SerialNumber)
require.NoError(t, err)
checkExpectedVersion(t, gotSettings, wantVersion)
if key == "macos" {
require.Equal(t, "darwin", platform)
} else {
require.Equal(t, platform, key)
}
}

// empty global settings to start
Expand Down Expand Up @@ -8110,15 +8115,15 @@ func TestGetMDMAppleOSUpdatesSettingsByHostSerial(t *testing.T) {
})

// non-DEP host should return not found
_, err = ds.GetMDMAppleOSUpdatesSettingsByHostSerial(context.Background(), "non-dep-serial")
_, _, err = ds.GetMDMAppleOSUpdatesSettingsByHostSerial(context.Background(), "non-dep-serial")
require.ErrorIs(t, err, sql.ErrNoRows)

// deleted DEP host should return not found
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(context.Background(), "UPDATE host_dep_assignments SET deleted_at = NOW() WHERE host_id = ?", hostIDsByKey["macos"])
return err
})
_, err = ds.GetMDMAppleOSUpdatesSettingsByHostSerial(context.Background(), devicesByKey["macos"].SerialNumber)
_, _, err = ds.GetMDMAppleOSUpdatesSettingsByHostSerial(context.Background(), devicesByKey["macos"].SerialNumber)
require.ErrorIs(t, err, sql.ErrNoRows)
}

Expand Down
4 changes: 2 additions & 2 deletions server/fleet/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -1566,8 +1566,8 @@ type Datastore interface {
MDMAppleSetPendingDeclarationsAs(ctx context.Context, hostUUID string, status *MDMDeliveryStatus, detail string) error
MDMAppleSetRemoveDeclarationsAsPending(ctx context.Context, hostUUID string, declarationUUIDs []string) error
// GetMDMAppleOSUpdatesSettingsByHostSerial returns applicable Apple OS update settings (if any)
// for the host with the given serial number. The host must be DEP assigned to Fleet.
GetMDMAppleOSUpdatesSettingsByHostSerial(ctx context.Context, hostSerial string) (*AppleOSUpdateSettings, error)
// for the host with the given serial number alongside the host's platform. The host must be DEP assigned to Fleet.
GetMDMAppleOSUpdatesSettingsByHostSerial(ctx context.Context, hostSerial string) (string, *AppleOSUpdateSettings, error)

// InsertMDMConfigAssets inserts MDM related config assets, such as SCEP and APNS certs and keys.
// tx is optional and can be used to pass an existing transaction.
Expand Down
4 changes: 2 additions & 2 deletions server/mock/datastore_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ type MDMAppleSetPendingDeclarationsAsFunc func(ctx context.Context, hostUUID str

type MDMAppleSetRemoveDeclarationsAsPendingFunc func(ctx context.Context, hostUUID string, declarationUUIDs []string) error

type GetMDMAppleOSUpdatesSettingsByHostSerialFunc func(ctx context.Context, hostSerial string) (*fleet.AppleOSUpdateSettings, error)
type GetMDMAppleOSUpdatesSettingsByHostSerialFunc func(ctx context.Context, hostSerial string) (string, *fleet.AppleOSUpdateSettings, error)

type InsertMDMConfigAssetsFunc func(ctx context.Context, assets []fleet.MDMConfigAsset, tx sqlx.ExtContext) error

Expand Down Expand Up @@ -7739,7 +7739,7 @@ func (s *DataStore) MDMAppleSetRemoveDeclarationsAsPending(ctx context.Context,
return s.MDMAppleSetRemoveDeclarationsAsPendingFunc(ctx, hostUUID, declarationUUIDs)
}

func (s *DataStore) GetMDMAppleOSUpdatesSettingsByHostSerial(ctx context.Context, hostSerial string) (*fleet.AppleOSUpdateSettings, error) {
func (s *DataStore) GetMDMAppleOSUpdatesSettingsByHostSerial(ctx context.Context, hostSerial string) (string, *fleet.AppleOSUpdateSettings, error) {
s.mu.Lock()
s.GetMDMAppleOSUpdatesSettingsByHostSerialFuncInvoked = true
s.mu.Unlock()
Expand Down
15 changes: 15 additions & 0 deletions server/service/appconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,10 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
return nil, ctxerr.Wrap(ctx, err)
}

// AppleOSUpdateSettings.UpdateNewHosts only applies to macOS ... so just ignore w/e posted for iOS/iPadOS
appConfig.MDM.IOSUpdates.UpdateNewHosts = optjson.Bool{}
appConfig.MDM.IPadOSUpdates.UpdateNewHosts = optjson.Bool{}

// if turning off Windows MDM and Windows Migration is not explicitly set to
// on in the same update, set it to off (otherwise, if it is explicitly set
// to true, return an error that it can't be done when MDM is off, this is
Expand Down Expand Up @@ -1131,6 +1135,17 @@ func (svc *Service) processAppleOSUpdateSettings(
return ctxerr.Wrap(ctx, err, "create activity for app config apple min version modification")
}
}

if oldOSUpdateSettings.UpdateNewHosts.Value != newOSUpdateSettings.UpdateNewHosts.Value && appleDevice == fleet.MacOS {
var activity fleet.ActivityDetails
activity = fleet.ActivityTypeEnabledMacosUpdateNewHosts{}
if !newOSUpdateSettings.UpdateNewHosts.Value {
activity = fleet.ActivityTypeDisabledMacosUpdateNewHosts{}
}
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), activity); err != nil {
return ctxerr.Wrap(ctx, err, "create activity for app config apple min version modification")
}
}
return nil
}

Expand Down
Loading
Loading