add mdm root key and macos_updates to app and team configs (#9442)
Related to https://github.com/fleetdm/fleet/issues/9345, https://github.com/fleetdm/fleet/issues/9358 and https://github.com/fleetdm/fleet/issues/9346 this adds: 1. The ability to configure `mdm.macos_updates` via `PATCH /config` and `PATCH /teams/{id}` 3. The ability to configure `mdm.macos_updates` by using `fleetctl apply -f` for teams and global config.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- Allow to configure a minimum macOS version and a deadline for hosts enrolled into Fleet's MDM.
|
||||
+212
-4
@@ -5,7 +5,6 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -76,7 +75,7 @@ func TestApplyUserRoles(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
tmpFile, err := ioutil.TempFile(os.TempDir(), "*.yml")
|
||||
tmpFile, err := os.CreateTemp(os.TempDir(), "*.yml")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
@@ -168,13 +167,25 @@ spec:
|
||||
name: team1
|
||||
secrets:
|
||||
- secret: AAA
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: 12.3.1
|
||||
deadline: 2011-03-01
|
||||
`)
|
||||
|
||||
newAgentOpts := json.RawMessage(`{"config":{"views":{"foo":"bar"}}}`)
|
||||
newMDMSettings := fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "12.3.1",
|
||||
Deadline: "2011-03-01",
|
||||
},
|
||||
}
|
||||
require.Equal(t, "[+] applied 2 teams\n", runAppForTest(t, []string{"apply", "-f", filename}))
|
||||
assert.JSONEq(t, string(agentOpts), string(*teamsByName["team2"].Config.AgentOptions))
|
||||
assert.JSONEq(t, string(newAgentOpts), string(*teamsByName["team1"].Config.AgentOptions))
|
||||
assert.Equal(t, []*fleet.EnrollSecret{{Secret: "AAA"}}, enrolledSecretsCalled[uint(42)])
|
||||
assert.Equal(t, fleet.TeamMDM{}, teamsByName["team2"].Config.MDM)
|
||||
assert.Equal(t, newMDMSettings, teamsByName["team1"].Config.MDM)
|
||||
assert.True(t, ds.ApplyEnrollSecretsFuncInvoked)
|
||||
ds.ApplyEnrollSecretsFuncInvoked = false
|
||||
|
||||
@@ -191,6 +202,7 @@ spec:
|
||||
assert.False(t, ds.ApplyEnrollSecretsFuncInvoked)
|
||||
// agent options not provided, so left unchanged
|
||||
assert.JSONEq(t, string(newAgentOpts), string(*teamsByName["team1"].Config.AgentOptions))
|
||||
assert.Equal(t, fleet.TeamMDM{}, teamsByName["team1"].Config.MDM)
|
||||
|
||||
filename = writeTmpYml(t, `
|
||||
apiVersion: v1
|
||||
@@ -202,13 +214,24 @@ spec:
|
||||
views:
|
||||
foo: qux
|
||||
name: team1
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: 10.10.10
|
||||
deadline: 1992-03-01
|
||||
secrets:
|
||||
- secret: BBB
|
||||
`)
|
||||
|
||||
newMDMSettings = fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "10.10.10",
|
||||
Deadline: "1992-03-01",
|
||||
},
|
||||
}
|
||||
newAgentOpts = json.RawMessage(`{"config":{"views":{"foo":"qux"}}}`)
|
||||
require.Equal(t, "[+] applied 1 teams\n", runAppForTest(t, []string{"apply", "-f", filename}))
|
||||
assert.JSONEq(t, string(newAgentOpts), string(*teamsByName["team1"].Config.AgentOptions))
|
||||
assert.Equal(t, newMDMSettings, teamsByName["team1"].Config.MDM)
|
||||
assert.Equal(t, []*fleet.EnrollSecret{{Secret: "BBB"}}, enrolledSecretsCalled[uint(42)])
|
||||
assert.True(t, ds.ApplyEnrollSecretsFuncInvoked)
|
||||
|
||||
@@ -227,7 +250,7 @@ spec:
|
||||
}
|
||||
|
||||
func writeTmpYml(t *testing.T, contents string) string {
|
||||
tmpFile, err := ioutil.TempFile(t.TempDir(), "*.yml")
|
||||
tmpFile, err := os.CreateTemp(t.TempDir(), "*.yml")
|
||||
require.NoError(t, err)
|
||||
_, err = tmpFile.WriteString(contents)
|
||||
require.NoError(t, err)
|
||||
@@ -235,7 +258,8 @@ func writeTmpYml(t *testing.T, contents string) string {
|
||||
}
|
||||
|
||||
func TestApplyAppConfig(t *testing.T) {
|
||||
_, ds := runServerWithMockedDS(t)
|
||||
license := &fleet.LicenseInfo{Tier: fleet.TierPremium, Expiration: time.Now().Add(24 * time.Hour)}
|
||||
_, ds := runServerWithMockedDS(t, &service.TestServerOpts{License: license})
|
||||
|
||||
ds.ListUsersFunc = func(ctx context.Context, opt fleet.UserListOptions) ([]*fleet.User, error) {
|
||||
return userRoleSpecList, nil
|
||||
@@ -251,6 +275,9 @@ func TestApplyAppConfig(t *testing.T) {
|
||||
}
|
||||
return userRoleSpecList[1], nil
|
||||
}
|
||||
ds.TeamByNameFunc = func(ctx context.Context, name string) (*fleet.Team, error) {
|
||||
return &fleet.Team{ID: 123}, nil
|
||||
}
|
||||
|
||||
defaultAgentOpts := json.RawMessage(`{"config":{"foo":"bar"}}`)
|
||||
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
|
||||
@@ -274,12 +301,26 @@ spec:
|
||||
features:
|
||||
enable_host_users: false
|
||||
enable_software_inventory: false
|
||||
mdm:
|
||||
apple_bm_default_team: "team1"
|
||||
macos_updates:
|
||||
minimum_version: 12.1.1
|
||||
deadline: 2011-02-01
|
||||
`)
|
||||
|
||||
newMDMSettings := fleet.MDM{
|
||||
AppleBMDefaultTeam: "team1",
|
||||
AppleBMTermsExpired: false,
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "12.1.1",
|
||||
Deadline: "2011-02-01",
|
||||
},
|
||||
}
|
||||
assert.Equal(t, "[+] applied fleet config\n", runAppForTest(t, []string{"apply", "-f", name}))
|
||||
require.NotNil(t, savedAppConfig)
|
||||
assert.False(t, savedAppConfig.Features.EnableHostUsers)
|
||||
assert.False(t, savedAppConfig.Features.EnableSoftwareInventory)
|
||||
assert.Equal(t, newMDMSettings, savedAppConfig.MDM)
|
||||
// agent options were not modified, since they were not provided
|
||||
assert.Equal(t, string(defaultAgentOpts), string(*savedAppConfig.AgentOptions))
|
||||
|
||||
@@ -291,6 +332,8 @@ spec:
|
||||
enable_host_users: true
|
||||
enable_software_inventory: true
|
||||
agent_options:
|
||||
mdm:
|
||||
macos_updates:
|
||||
`)
|
||||
|
||||
assert.Equal(t, "[+] applied fleet config\n", runAppForTest(t, []string{"apply", "-f", name}))
|
||||
@@ -299,6 +342,7 @@ spec:
|
||||
assert.True(t, savedAppConfig.Features.EnableSoftwareInventory)
|
||||
// agent options were cleared, provided but empty
|
||||
assert.Nil(t, savedAppConfig.AgentOptions)
|
||||
assert.Equal(t, newMDMSettings, savedAppConfig.MDM)
|
||||
}
|
||||
|
||||
func TestApplyAppConfigDryRunIssue(t *testing.T) {
|
||||
@@ -1213,6 +1257,94 @@ spec:
|
||||
[+] would've applied fleet config
|
||||
[+] would've applied 1 teams`,
|
||||
},
|
||||
{
|
||||
desc: "macos_updates deadline set but minimum_version empty",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: team
|
||||
spec:
|
||||
team:
|
||||
name: team1
|
||||
mdm:
|
||||
macos_updates:
|
||||
deadline: 2022-01-04
|
||||
`,
|
||||
wantErr: `422 Validation Failed: minimum_version is required when deadline is provided`,
|
||||
},
|
||||
{
|
||||
desc: "macos_updates minimum_version set but deadline empty",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: team
|
||||
spec:
|
||||
team:
|
||||
name: team1
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2"
|
||||
`,
|
||||
wantErr: `422 Validation Failed: deadline is required when minimum_version is provided`,
|
||||
},
|
||||
{
|
||||
desc: "macos_updates.minimum_version with build version",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: team
|
||||
spec:
|
||||
team:
|
||||
name: team1
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2 (ABCD)"
|
||||
deadline: 1892-01-01
|
||||
`,
|
||||
wantErr: `422 Validation Failed: minimum_version accepts version numbers only. (E.g., "13.0.1.") NOT "Ventura 13" or "13.0.1 (22A400)"`,
|
||||
},
|
||||
{
|
||||
desc: "macos_updates.deadline with timestamp",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: team
|
||||
spec:
|
||||
team:
|
||||
name: team1
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2"
|
||||
deadline: "1892-01-01T00:00:00Z"
|
||||
`,
|
||||
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
|
||||
},
|
||||
{
|
||||
desc: "macos_updates.deadline with invalid date",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: team
|
||||
spec:
|
||||
team:
|
||||
name: team1
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2"
|
||||
deadline: "18-01-01"
|
||||
`,
|
||||
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
|
||||
},
|
||||
{
|
||||
desc: "macos_updates.deadline with incomplete date",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: team
|
||||
spec:
|
||||
team:
|
||||
name: team1
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2"
|
||||
deadline: "2022-01"
|
||||
`,
|
||||
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
|
||||
},
|
||||
{
|
||||
desc: "missing required sso entity_id",
|
||||
spec: `
|
||||
@@ -1340,6 +1472,82 @@ spec:
|
||||
`,
|
||||
wantOutput: `[+] applied fleet config`,
|
||||
},
|
||||
{
|
||||
desc: "app config macos_updates deadline set but minimum_version empty",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
mdm:
|
||||
macos_updates:
|
||||
deadline: 2022-01-04
|
||||
`,
|
||||
wantErr: `422 Validation Failed: minimum_version is required when deadline is provided`,
|
||||
},
|
||||
{
|
||||
desc: "app config macos_updates minimum_version set but deadline empty",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2"
|
||||
`,
|
||||
wantErr: `422 Validation Failed: deadline is required when minimum_version is provided`,
|
||||
},
|
||||
{
|
||||
desc: "app config macos_updates.minimum_version with build version",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2 (ABCD)"
|
||||
deadline: 1892-01-01
|
||||
`,
|
||||
wantErr: `422 Validation Failed: minimum_version accepts version numbers only. (E.g., "13.0.1.") NOT "Ventura 13" or "13.0.1 (22A400)"`,
|
||||
},
|
||||
{
|
||||
desc: "app config macos_updates.deadline with timestamp",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2"
|
||||
deadline: "1892-01-01T00:00:00Z"
|
||||
`,
|
||||
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
|
||||
},
|
||||
{
|
||||
desc: "app config macos_updates.deadline with invalid date",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2"
|
||||
deadline: "18-01-01"
|
||||
`,
|
||||
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
|
||||
},
|
||||
{
|
||||
desc: "app config macos_updates.deadline with incomplete date",
|
||||
spec: `
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.2"
|
||||
deadline: "2022-01"
|
||||
`,
|
||||
wantErr: `422 Validation Failed: deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`,
|
||||
},
|
||||
}
|
||||
// NOTE: Integrations required fields are not tested (Jira/Zendesk) because
|
||||
// they require a complex setup to mock the client that would communicate
|
||||
|
||||
@@ -148,6 +148,12 @@ func TestGetTeams(t *testing.T) {
|
||||
Features: fleet.Features{
|
||||
AdditionalQueries: &additionalQueries,
|
||||
},
|
||||
MDM: fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "12.3.1",
|
||||
Deadline: "2021-12-14",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
@@ -176,6 +182,10 @@ spec:
|
||||
integrations:
|
||||
jira: null
|
||||
zendesk: null
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: ""
|
||||
deadline: ""
|
||||
name: team1
|
||||
user_count: 99
|
||||
webhook_settings:
|
||||
@@ -208,6 +218,10 @@ spec:
|
||||
integrations:
|
||||
jira: null
|
||||
zendesk: null
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.3.1"
|
||||
deadline: "2021-12-14"
|
||||
name: team2
|
||||
user_count: 87
|
||||
webhook_settings:
|
||||
@@ -217,8 +231,8 @@ spec:
|
||||
host_batch_size: 0
|
||||
policy_ids: null
|
||||
`
|
||||
expectedJson := `{"kind":"team","apiVersion":"v1","spec":{"team":{"id":42,"created_at":"1999-03-10T02:45:06.371Z","name":"team1","description":"team1 description","webhook_settings":{"failing_policies_webhook":{"enable_failing_policies_webhook":false,"destination_url":"","policy_ids":null,"host_batch_size":0}},"integrations":{"jira":null,"zendesk":null},"features":{"enable_host_users":true,"enable_software_inventory":true},"user_count":99,"host_count":0}}}
|
||||
{"kind":"team","apiVersion":"v1","spec":{"team":{"id":43,"created_at":"1999-03-10T02:45:06.371Z","name":"team2","description":"team2 description","agent_options":{"config":{"foo":"bar"},"overrides":{"platforms":{"darwin":{"foo":"override"}}}},"webhook_settings":{"failing_policies_webhook":{"enable_failing_policies_webhook":false,"destination_url":"","policy_ids":null,"host_batch_size":0}},"integrations":{"jira":null,"zendesk":null},"features":{"enable_host_users":false,"enable_software_inventory":false,"additional_queries":{"foo":"bar"}},"user_count":87,"host_count":0}}}
|
||||
expectedJson := `{"kind":"team","apiVersion":"v1","spec":{"team":{"id":42,"created_at":"1999-03-10T02:45:06.371Z","name":"team1","description":"team1 description","webhook_settings":{"failing_policies_webhook":{"enable_failing_policies_webhook":false,"destination_url":"","policy_ids":null,"host_batch_size":0}},"integrations":{"jira":null,"zendesk":null},"features":{"enable_host_users":true,"enable_software_inventory":true},"mdm":{"macos_updates":{"minimum_version":"","deadline":""}},"user_count":99,"host_count":0}}}
|
||||
{"kind":"team","apiVersion":"v1","spec":{"team":{"id":43,"created_at":"1999-03-10T02:45:06.371Z","name":"team2","description":"team2 description","agent_options":{"config":{"foo":"bar"},"overrides":{"platforms":{"darwin":{"foo":"override"}}}},"webhook_settings":{"failing_policies_webhook":{"enable_failing_policies_webhook":false,"destination_url":"","policy_ids":null,"host_batch_size":0}},"integrations":{"jira":null,"zendesk":null},"features":{"enable_host_users":false,"enable_software_inventory":false,"additional_queries":{"foo":"bar"}},"mdm":{"macos_updates":{"minimum_version":"12.3.1","deadline":"2021-12-14"}},"user_count":87,"host_count":0}}}
|
||||
`
|
||||
if tt.shouldHaveExpiredBanner {
|
||||
expectedJson = expiredBanner.String() + expectedJson
|
||||
@@ -226,8 +240,8 @@ spec:
|
||||
expectedText = expiredBanner.String() + expectedText
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedText, runAppForTest(t, []string{"get", "teams"}))
|
||||
assert.Equal(t, expectedYaml, runAppForTest(t, []string{"get", "teams", "--yaml"}))
|
||||
assert.YAMLEq(t, expectedText, runAppForTest(t, []string{"get", "teams"}))
|
||||
assert.YAMLEq(t, expectedYaml, runAppForTest(t, []string{"get", "teams", "--yaml"}))
|
||||
assert.Equal(t, expectedJson, runAppForTest(t, []string{"get", "teams", "--json"}))
|
||||
})
|
||||
}
|
||||
@@ -476,6 +490,9 @@ spec:
|
||||
mdm:
|
||||
apple_bm_terms_expired: false
|
||||
apple_bm_default_team: ""
|
||||
macos_updates:
|
||||
minimum_version: ""
|
||||
deadline: ""
|
||||
org_info:
|
||||
org_logo_url: ""
|
||||
org_name: ""
|
||||
@@ -562,6 +579,12 @@ spec:
|
||||
"enable_host_users": true,
|
||||
"enable_software_inventory": false
|
||||
},
|
||||
"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
}
|
||||
},
|
||||
"sso_settings": {
|
||||
"entity_id": "",
|
||||
"issuer_uri": "",
|
||||
@@ -596,7 +619,14 @@ spec:
|
||||
"interval": "0s"
|
||||
},
|
||||
"integrations": { "jira": null, "zendesk": null },
|
||||
"mdm": { "apple_bm_terms_expired": false, "apple_bm_default_team": "" }
|
||||
"mdm": {
|
||||
"apple_bm_terms_expired": false,
|
||||
"apple_bm_default_team": "",
|
||||
"macos_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@@ -625,6 +655,9 @@ spec:
|
||||
mdm:
|
||||
apple_bm_default_team: ""
|
||||
apple_bm_terms_expired: false
|
||||
macos_updates:
|
||||
minimum_version: ""
|
||||
deadline: ""
|
||||
license:
|
||||
expiration: "0001-01-01T00:00:00Z"
|
||||
tier: free
|
||||
@@ -757,6 +790,12 @@ spec:
|
||||
"enable_host_users": true,
|
||||
"enable_software_inventory": false
|
||||
},
|
||||
"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
}
|
||||
},
|
||||
"sso_settings": {
|
||||
"enable_jit_provisioning": false,
|
||||
"entity_id": "",
|
||||
@@ -798,10 +837,14 @@ spec:
|
||||
"jira": null,
|
||||
"zendesk": null
|
||||
},
|
||||
"mdm": {
|
||||
"mdm": {
|
||||
"apple_bm_default_team": "",
|
||||
"apple_bm_terms_expired": false
|
||||
},
|
||||
"apple_bm_terms_expired": false,
|
||||
"macos_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
}
|
||||
},
|
||||
"update_interval": {
|
||||
"osquery_detail": "1h0m0s",
|
||||
"osquery_policy": "1h0m0s"
|
||||
|
||||
@@ -838,7 +838,11 @@ None.
|
||||
},
|
||||
"mdm": {
|
||||
"apple_bm_default_team": "",
|
||||
"apple_bm_terms_expired": false
|
||||
"apple_bm_terms_expired": false,
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.3.1",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
},
|
||||
"agent_options": {
|
||||
"spec": {
|
||||
@@ -914,7 +918,11 @@ None.
|
||||
"jira": null
|
||||
},
|
||||
"mdm": {
|
||||
"apple_bm_default_team": ""
|
||||
"apple_bm_default_team": "",
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.3.1",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"debug": false,
|
||||
@@ -1013,7 +1021,9 @@ Modifies the Fleet's configuration with the supplied information.
|
||||
| email | string | body | _integrations.zendesk[] settings_. The Zendesk user email to use for this Zendesk integration. |
|
||||
| api_token | string | body | _integrations.zendesk[] settings_. The Zendesk API token to use for this Zendesk integration. |
|
||||
| group_id | integer | body | _integrations.zendesk[] settings_. The Zendesk group id to use for this integration. Zendesk tickets will be created in this group. |
|
||||
| apple_bm_default_team | string | body | _mdm settings_. The default team to use with Apple Business Manager. |
|
||||
| apple_bm_default_team | string | body | _mdm settings_. The default team to use with Apple Business Manager. **Requires Fleet Premium license** |
|
||||
| minimum_version | string | body | _mdm.macos_updates settings_. Hosts that belong to no team and are enrolled into Fleet's MDM will be nudged until their macOS is at or above this version. **Requires Fleet Premium license** |
|
||||
| deadline | string | body | _mdm.macos_updates settings_. Hosts that belong to no team and are enrolled into Fleet's MDM won't be able to dismiss the Nudge window once this deadline is past. **Requires Fleet Premium license** |
|
||||
| additional_queries | boolean | body | Whether or not additional queries are enabled on hosts. |
|
||||
| force | bool | query | Force apply the agent options even if there are validation errors. |
|
||||
| dry_run | bool | query | Validate the configuration and return any validation errors, but do not apply the changes. |
|
||||
@@ -1089,7 +1099,11 @@ Modifies the Fleet's configuration with the supplied information.
|
||||
},
|
||||
"mdm": {
|
||||
"apple_bm_default_team": "",
|
||||
"apple_bm_terms_expired": false
|
||||
"apple_bm_terms_expired": false,
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.3.1",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
},
|
||||
"agent_options": {
|
||||
"config": {
|
||||
@@ -1146,7 +1160,11 @@ Modifies the Fleet's configuration with the supplied information.
|
||||
]
|
||||
},
|
||||
"mdm": {
|
||||
"apple_bm_default_team": ""
|
||||
"apple_bm_default_team": "",
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.3.1",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"debug": false,
|
||||
@@ -5420,27 +5438,32 @@ _Available in Fleet Premium_
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | In | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| id | integer | path | **Required.** The desired team's ID. |
|
||||
| name | string | body | The team's name. |
|
||||
| host_ids | list | body | A list of hosts that belong to the team. |
|
||||
| user_ids | list | body | A list of users that are members of the team. |
|
||||
| webhook_settings | object | body | Webhook settings contains for the team. |
|
||||
| failing_policies_webhook | object | body | Failing policies webhook settings. |
|
||||
| enable_failing_policies_webhook | boolean | body | Whether or not the failing policies webhook is enabled. |
|
||||
| destination_url | string | body | The URL to deliver the webhook requests to. |
|
||||
| policy_ids | array | body | List of policy IDs to enable failing policies webhook. |
|
||||
| host_batch_size | integer | body | Maximum number of hosts to batch on failing policy webhook requests. The default, 0, means no batching (all hosts failing a policy are sent on one request). |
|
||||
| integrations | object | body | Integrations settings for the team. Note that integrations referenced here must already exist globally, created by a call to [Modify configuration](#modify-configuration). |
|
||||
| jira | array | body | Jira integrations configuration. |
|
||||
| url | string | body | The URL of the Jira server to use. |
|
||||
| project_key | string | body | The project key of the Jira integration to use. Jira tickets will be created in this project. |
|
||||
| enable_failing_policies | boolean | body | Whether or not that Jira integration is enabled for failing policies. Only one failing policy automation can be enabled at a given time (enable_failing_policies_webhook and enable_failing_policies). |
|
||||
| zendesk | array | body | Zendesk integrations configuration. |
|
||||
| url | string | body | The URL of the Zendesk server to use. |
|
||||
| group_id | integer | body | The Zendesk group id to use. Zendesk tickets will be created in this group. |
|
||||
| Name | Type | In | Description |
|
||||
| ------------------------------------------------------- | ------- | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| id | integer | path | **Required.** The desired team's ID. |
|
||||
| name | string | body | The team's name. |
|
||||
| host_ids | list | body | A list of hosts that belong to the team. |
|
||||
| user_ids | list | body | A list of users that are members of the team. |
|
||||
| webhook_settings | object | body | Webhook settings contains for the team. |
|
||||
| failing_policies_webhook | object | body | Failing policies webhook settings. |
|
||||
| enable_failing_policies_webhook | boolean | body | Whether or not the failing policies webhook is enabled. |
|
||||
| destination_url | string | body | The URL to deliver the webhook requests to. |
|
||||
| policy_ids | array | body | List of policy IDs to enable failing policies webhook. |
|
||||
| host_batch_size | integer | body | Maximum number of hosts to batch on failing policy webhook requests. The default, 0, means no batching (all hosts failing a policy are sent on one request). |
|
||||
| integrations | object | body | Integrations settings for the team. Note that integrations referenced here must already exist globally, created by a call to [Modify configuration](#modify-configuration). |
|
||||
| jira | array | body | Jira integrations configuration. |
|
||||
| url | string | body | The URL of the Jira server to use. |
|
||||
| project_key | string | body | The project key of the Jira integration to use. Jira tickets will be created in this project. |
|
||||
| enable_failing_policies | boolean | body | Whether or not that Jira integration is enabled for failing policies. Only one failing policy automation can be enabled at a given time (enable_failing_policies_webhook and enable_failing_policies). |
|
||||
| zendesk | array | body | Zendesk integrations configuration. |
|
||||
| url | string | body | The URL of the Zendesk server to use. |
|
||||
| group_id | integer | body | The Zendesk group id to use. Zendesk tickets will be created in this group. |
|
||||
| enable_failing_policies | boolean | body | Whether or not that Zendesk integration is enabled for failing policies. Only one failing policy automation can be enabled at a given time (enable_failing_policies_webhook and enable_failing_policies). |
|
||||
| mdm | object | body | MDM settings for the team. |
|
||||
| macos_updates | object | body | MacOS updates settings. |
|
||||
| minimum_version | string | body | Hosts that belong to this team and are enrolled into Fleet's MDM will be nudged until their macOS is at or above this version. |
|
||||
| deadline | string | body | Hosts that belong to this team and are enrolled into Fleet's MDM won't be able to dismiss the Nudge window once this deadline is past. |
|
||||
|
||||
|
||||
#### Example (add users to a team)
|
||||
|
||||
@@ -5493,7 +5516,13 @@ _Available in Fleet Premium_
|
||||
"policy_ids": null,
|
||||
"host_batch_size": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.3.1",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -133,6 +133,10 @@ spec:
|
||||
secrets:
|
||||
- secret: RzTlxPvugG4o4O5IKS/HqEDJUmI1hwBoffff
|
||||
- secret: JZ/C/Z7ucq22dt/zjx2kEuDBN0iLjqfz
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: 12.3.1
|
||||
deadline: 2022-01-04
|
||||
```
|
||||
|
||||
### Team settings
|
||||
@@ -168,6 +172,24 @@ The `secrets` section provides the list of enroll secrets that will be valid for
|
||||
- secret: JZ/C/Z7ucq22dt/zjx2kEuDBN0iLjqfz
|
||||
```
|
||||
|
||||
#### Mobile device management (MDM) settings
|
||||
|
||||
> MDM features are not ready for production and are currently in development. These features are disabled by default.
|
||||
|
||||
The `mdm` section of the configuration YAML lets you control MDM settings for the team in Fleet.
|
||||
|
||||
The documentation for this section is identical to the [MDM settings](#mobile-device-management-mdm-settings) documentation for the organization settings, except that the YAML section where it is set must be as follows. (Note the `kind: team` key and the location of the `mdm` key under `team` must have a `name` key to identify the team to configure.)
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: team
|
||||
spec:
|
||||
team:
|
||||
name: Client Platform Engineering
|
||||
mdm:
|
||||
# the team-specific mdm options go here
|
||||
```
|
||||
|
||||
## Organization settings
|
||||
|
||||
The `config` YAML file controls Fleet's organization settings.
|
||||
@@ -264,6 +286,9 @@ spec:
|
||||
host_batch_size: 0
|
||||
mdm:
|
||||
apple_bm_default_team: ""
|
||||
macos_updates:
|
||||
minimum_version: ""
|
||||
deadline: ""
|
||||
```
|
||||
|
||||
### Settings
|
||||
@@ -1202,7 +1227,41 @@ Set name of default team to use with Apple Business Manager.
|
||||
- Config file format:
|
||||
```yaml
|
||||
mdm:
|
||||
team: "Workstations"
|
||||
apple_bm_default_team: "Workstations"
|
||||
```
|
||||
|
||||
##### mdm.macos_updates
|
||||
|
||||
**Applies only to Fleet Premium**.
|
||||
|
||||
The following options allow to configure the behavior of Nudge for macOS hosts that belong to no team and are enrolled into Fleet's MDM.
|
||||
|
||||
##### mdm.macos_updates.minimum_version
|
||||
|
||||
Hosts that belong to no team and are enrolled into Fleet's MDM will be nudged until their macOS is at or above this version.
|
||||
|
||||
Requires `mdm.macos_updates.deadline` to be set.
|
||||
|
||||
- Default value: ""
|
||||
- Config file format:
|
||||
```yaml
|
||||
mdm:
|
||||
macos_updates:
|
||||
minimum_version: "12.1.1"
|
||||
```
|
||||
|
||||
##### mdm.macos_updates.deadline
|
||||
|
||||
A deadline in the form `YYYY-MM-DD`. Hosts that belong to no team and are enrolled into Fleet's MDM won't be able to dismiss the Nudge window once this deadline is past.
|
||||
|
||||
Requires `mdm.macos_updates.minimum_version` to be set.
|
||||
|
||||
- Default value: ""
|
||||
- Config file format:
|
||||
```yaml
|
||||
mdm:
|
||||
macos_updates:
|
||||
deadline: "2022-01-01"
|
||||
```
|
||||
|
||||
#### Advanced configuration
|
||||
|
||||
@@ -103,6 +103,13 @@ func (svc *Service) ModifyTeam(ctx context.Context, teamID uint, payload fleet.T
|
||||
team.Config.WebhookSettings = *payload.WebhookSettings
|
||||
}
|
||||
|
||||
if payload.MDM != nil {
|
||||
if err := payload.MDM.MacOSUpdates.Validate(); err != nil {
|
||||
return nil, fleet.NewInvalidArgumentError("macos_updates", err.Error())
|
||||
}
|
||||
team.Config.MDM = *payload.MDM
|
||||
}
|
||||
|
||||
if payload.Integrations != nil {
|
||||
// the team integrations must reference an existing global config integration.
|
||||
appCfg, err := svc.ds.AppConfig(ctx)
|
||||
@@ -458,6 +465,9 @@ func (svc *Service) ApplyTeamSpecs(ctx context.Context, specs []*fleet.TeamSpec,
|
||||
if len(spec.Secrets) > fleet.MaxEnrollSecretsCount {
|
||||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("secrets", "too many secrets"), "validate secrets")
|
||||
}
|
||||
if err := spec.MDM.MacOSUpdates.Validate(); err != nil {
|
||||
return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("macos_updates", err.Error()))
|
||||
}
|
||||
|
||||
if applyOpts.DryRun {
|
||||
continue
|
||||
@@ -521,6 +531,7 @@ func (svc Service) createTeamFromSpec(ctx context.Context, spec *fleet.TeamSpec,
|
||||
Config: fleet.TeamConfig{
|
||||
AgentOptions: agentOptions,
|
||||
Features: features,
|
||||
MDM: spec.MDM,
|
||||
},
|
||||
Secrets: secrets,
|
||||
})
|
||||
@@ -546,6 +557,7 @@ func (svc Service) editTeamFromSpec(ctx context.Context, team *fleet.Team, spec
|
||||
return err
|
||||
}
|
||||
team.Config.Features = features
|
||||
team.Config.MDM = spec.MDM
|
||||
|
||||
if len(secrets) > 0 {
|
||||
team.Secrets = secrets
|
||||
|
||||
@@ -38,7 +38,7 @@ CREATE TABLE `app_config_json` (
|
||||
UNIQUE KEY `id` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false}, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"org_logo_url\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"deferred_save_host\": false, \"live_query_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
|
||||
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_updates\": {\"deadline\": \"\", \"minimum_version\": \"\"}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false}, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"org_logo_url\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"deferred_save_host\": false, \"live_query_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `carve_blocks` (
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
@@ -111,12 +112,52 @@ type MDM struct {
|
||||
// API.
|
||||
AppleBMTermsExpired bool `json:"apple_bm_terms_expired"`
|
||||
|
||||
MacOSUpdates MacOSUpdates `json:"macos_updates"`
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// WARNING: If you add to this struct make sure it's taken into
|
||||
// account in the AppConfig Clone implementation!
|
||||
/////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
// versionStringRegex is used to validate that a version string is in the x.y.z
|
||||
// format only (no prerelease or build metadata).
|
||||
var versionStringRegex = regexp.MustCompile(`^\d+(\.\d+)?(\.\d+)?$`)
|
||||
|
||||
// MacOSUpdates is part of AppConfig and defines the macOS update settings.
|
||||
type MacOSUpdates struct {
|
||||
// MinimumVerssion is the required minimum operating system version.
|
||||
MinimumVersion string `json:"minimum_version"`
|
||||
// Deadline the required installation date for Nudge to enforce the required
|
||||
// operating system version.
|
||||
Deadline string `json:"deadline"`
|
||||
}
|
||||
|
||||
func (m MacOSUpdates) Validate() error {
|
||||
// if no settings are provided it's okay to skip further validation
|
||||
if m.MinimumVersion == "" && m.Deadline == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.MinimumVersion != "" && m.Deadline == "" {
|
||||
return errors.New("deadline is required when minimum_version is provided")
|
||||
}
|
||||
|
||||
if m.Deadline != "" && m.MinimumVersion == "" {
|
||||
return errors.New("minimum_version is required when deadline is provided")
|
||||
}
|
||||
|
||||
if !versionStringRegex.MatchString(m.MinimumVersion) {
|
||||
return errors.New(`minimum_version accepts version numbers only. (E.g., "13.0.1.") NOT "Ventura 13" or "13.0.1 (22A400)"`)
|
||||
}
|
||||
|
||||
if _, err := time.Parse("2006-01-02", m.Deadline); err != nil {
|
||||
return errors.New(`deadline accepts YYYY-MM-DD format only (E.g., "2023-06-01.")`)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AppConfig holds server configuration that can be changed via the API.
|
||||
//
|
||||
// Note: management of deprecated fields is done on JSON-marshalling and uses
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMacOSUpdatesValidate(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
m MacOSUpdates
|
||||
}{
|
||||
{"empty", MacOSUpdates{}},
|
||||
{
|
||||
"with full version",
|
||||
MacOSUpdates{
|
||||
MinimumVersion: "10.15.0",
|
||||
Deadline: "2020-01-01",
|
||||
},
|
||||
},
|
||||
{
|
||||
"without patch version",
|
||||
MacOSUpdates{
|
||||
MinimumVersion: "10.15",
|
||||
Deadline: "2020-01-01",
|
||||
},
|
||||
},
|
||||
{
|
||||
"only major version",
|
||||
MacOSUpdates{
|
||||
MinimumVersion: "10",
|
||||
Deadline: "2020-01-01",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require.NoError(t, tc.m.Validate())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid deadline", func(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
m MacOSUpdates
|
||||
}{
|
||||
{
|
||||
"version but no deadline",
|
||||
MacOSUpdates{
|
||||
MinimumVersion: "10.15.0",
|
||||
Deadline: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
"deadline with timestamp",
|
||||
MacOSUpdates{
|
||||
MinimumVersion: "10.15.0",
|
||||
Deadline: "2020-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
{
|
||||
"incomplete date",
|
||||
MacOSUpdates{
|
||||
MinimumVersion: "10.15.0",
|
||||
Deadline: "2020-01",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require.Error(t, tc.m.Validate())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid version", func(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
m MacOSUpdates
|
||||
}{
|
||||
{
|
||||
"deadline but no version",
|
||||
MacOSUpdates{
|
||||
MinimumVersion: "",
|
||||
Deadline: "2020-01-01",
|
||||
},
|
||||
},
|
||||
{
|
||||
"version with build info",
|
||||
MacOSUpdates{
|
||||
MinimumVersion: "10.15.0 (19A583)",
|
||||
Deadline: "2020-01-01",
|
||||
},
|
||||
},
|
||||
{
|
||||
"version with patch info",
|
||||
MacOSUpdates{
|
||||
MinimumVersion: "10.15.0-patch1",
|
||||
Deadline: "2020-01-01",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require.Error(t, tc.m.Validate())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -19,6 +19,7 @@ type TeamPayload struct {
|
||||
Secrets []*EnrollSecret `json:"secrets"`
|
||||
WebhookSettings *TeamWebhookSettings `json:"webhook_settings"`
|
||||
Integrations *TeamIntegrations `json:"integrations"`
|
||||
MDM *TeamMDM `json:"mdm"`
|
||||
// Note AgentOptions must be set by a separate endpoint.
|
||||
}
|
||||
|
||||
@@ -123,12 +124,17 @@ type TeamConfig struct {
|
||||
WebhookSettings TeamWebhookSettings `json:"webhook_settings"`
|
||||
Integrations TeamIntegrations `json:"integrations"`
|
||||
Features Features `json:"features"`
|
||||
MDM TeamMDM `json:"mdm"`
|
||||
}
|
||||
|
||||
type TeamWebhookSettings struct {
|
||||
FailingPoliciesWebhook FailingPoliciesWebhookSettings `json:"failing_policies_webhook"`
|
||||
}
|
||||
|
||||
type TeamMDM struct {
|
||||
MacOSUpdates MacOSUpdates `json:"macos_updates"`
|
||||
}
|
||||
|
||||
// Scan implements the sql.Scanner interface
|
||||
func (t *TeamConfig) Scan(val interface{}) error {
|
||||
switch v := val.(type) {
|
||||
@@ -264,4 +270,5 @@ type TeamSpec struct {
|
||||
|
||||
Secrets []EnrollSecret `json:"secrets"`
|
||||
Features *json.RawMessage `json:"features"`
|
||||
MDM TeamMDM `json:"mdm"`
|
||||
}
|
||||
|
||||
@@ -452,6 +452,22 @@ func (svc *Service) validateMDM(
|
||||
invalid.Append("apple_bm_default_team", "team name not found")
|
||||
}
|
||||
}
|
||||
|
||||
// MacOSUpdates
|
||||
updatingVersion := mdm.MacOSUpdates.MinimumVersion != "" &&
|
||||
mdm.MacOSUpdates.MinimumVersion != oldMdm.MacOSUpdates.MinimumVersion
|
||||
updatingDeadline := mdm.MacOSUpdates.Deadline != "" &&
|
||||
mdm.MacOSUpdates.Deadline != oldMdm.MacOSUpdates.Deadline
|
||||
|
||||
if updatingVersion || updatingDeadline {
|
||||
if !license.IsPremium() {
|
||||
invalid.Append("macos_updates.minimum_version", ErrMissingLicense.Error())
|
||||
return
|
||||
}
|
||||
if err := mdm.MacOSUpdates.Validate(); err != nil {
|
||||
invalid.Append("macos_updates", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateSSOSettings(p fleet.AppConfig, existing *fleet.AppConfig, invalid *fleet.InvalidArgumentError, license *fleet.LicenseInfo) {
|
||||
|
||||
@@ -80,12 +80,27 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
// updates a team, no secret is provided so it will keep the one generated
|
||||
// automatically when the team was created.
|
||||
agentOpts := json.RawMessage(`{"config": {"views": {"foo": "bar"}}, "overrides": {"platforms": {"darwin": {"views": {"bar": "qux"}}}}}`)
|
||||
mdm := fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "10.15.0",
|
||||
Deadline: "2021-01-01",
|
||||
},
|
||||
}
|
||||
features := json.RawMessage(`{
|
||||
"enable_host_users": false,
|
||||
"enable_software_inventory": false,
|
||||
"additional_queries": {"foo": "bar"}
|
||||
}`)
|
||||
teamSpecs := applyTeamSpecsRequest{Specs: []*fleet.TeamSpec{{Name: teamName, AgentOptions: agentOpts, Features: &features}}}
|
||||
teamSpecs := applyTeamSpecsRequest{
|
||||
Specs: []*fleet.TeamSpec{
|
||||
{
|
||||
Name: teamName,
|
||||
AgentOptions: agentOpts,
|
||||
Features: &features,
|
||||
MDM: mdm,
|
||||
},
|
||||
},
|
||||
}
|
||||
s.Do("POST", "/api/latest/fleet/spec/teams", teamSpecs, http.StatusOK)
|
||||
|
||||
team, err := s.ds.TeamByName(context.Background(), teamName)
|
||||
@@ -97,6 +112,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
EnableSoftwareInventory: false,
|
||||
AdditionalQueries: ptr.RawMessage(json.RawMessage(`{"foo": "bar"}`)),
|
||||
}, team.Config.Features)
|
||||
require.Equal(t, mdm, team.Config.MDM)
|
||||
|
||||
// an activity was created for team spec applied
|
||||
var listActivities listActivitiesResponse
|
||||
@@ -171,7 +187,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
]
|
||||
}`), http.StatusOK, "force", "true")
|
||||
|
||||
team, err = s.ds.TeamByName(context.Background(), "team_with_invalid_key")
|
||||
_, err = s.ds.TeamByName(context.Background(), "team_with_invalid_key")
|
||||
require.NoError(t, err)
|
||||
|
||||
// invalid agent options command-line flag
|
||||
@@ -1221,6 +1237,95 @@ func (s *integrationEnterpriseTestSuite) TestExternalIntegrationsTeamConfig() {
|
||||
}`), http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesConfig() {
|
||||
t := s.T()
|
||||
|
||||
// Create a team
|
||||
team := &fleet.Team{
|
||||
Name: t.Name(),
|
||||
Description: "Team description",
|
||||
Secrets: []*fleet.EnrollSecret{{Secret: "XYZ"}},
|
||||
}
|
||||
var tmResp teamResponse
|
||||
s.DoJSON("POST", "/api/latest/fleet/teams", team, http.StatusOK, &tmResp)
|
||||
require.Equal(t, team.Name, tmResp.Team.Name)
|
||||
team.ID = tmResp.Team.ID
|
||||
|
||||
// modify the team's config
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{
|
||||
MDM: &fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "10.15.0",
|
||||
Deadline: "2021-01-01",
|
||||
},
|
||||
},
|
||||
}, http.StatusOK, &tmResp)
|
||||
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion)
|
||||
require.Equal(t, "2021-01-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline)
|
||||
|
||||
// only update the deadline
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{
|
||||
MDM: &fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "10.15.0",
|
||||
Deadline: "2025-10-01",
|
||||
},
|
||||
},
|
||||
}, http.StatusOK, &tmResp)
|
||||
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion)
|
||||
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline)
|
||||
|
||||
// sending a nil MacOSUpdate config doesn't modify anything
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{MDM: nil}, http.StatusOK, &tmResp)
|
||||
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion)
|
||||
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline)
|
||||
|
||||
// sending an empty MacOSUpdate empties both fields
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{MDM: &fleet.TeamMDM{MacOSUpdates: fleet.MacOSUpdates{}}}, http.StatusOK, &tmResp)
|
||||
require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion)
|
||||
require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.Deadline)
|
||||
|
||||
// error checks:
|
||||
|
||||
// try to set an invalid deadline
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{
|
||||
MDM: &fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "10.15.0",
|
||||
Deadline: "2021-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
|
||||
// try to set an invalid minimum version
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{
|
||||
MDM: &fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "10.15.0 (19A583)",
|
||||
Deadline: "2021-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
|
||||
// try to set a deadline but not a minimum version
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{
|
||||
MDM: &fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
Deadline: "2021-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
|
||||
// try to set a minimum version but not a deadline
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{
|
||||
MDM: &fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MinimumVersion: "10.15.0 (19A583)",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestListDevicePolicies() {
|
||||
t := s.T()
|
||||
|
||||
@@ -1419,6 +1524,78 @@ func (s *integrationEnterpriseTestSuite) TestDefaultAppleBMTeam() {
|
||||
require.Equal(t, tm.Name, acResp.MDM.AppleBMDefaultTeam)
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
t := s.T()
|
||||
|
||||
checkInvalidConfig := func(config string) {
|
||||
// try to set an invalid config
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(config), http.StatusUnprocessableEntity, &acResp)
|
||||
|
||||
// get the appconfig, nothing changed
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
|
||||
require.Equal(t, fleet.MacOSUpdates{}, acResp.MDM.MacOSUpdates)
|
||||
}
|
||||
|
||||
// missing minimum_version
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"macos_updates": {
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// missing deadline
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.1.1"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// invalid deadline
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.1.1",
|
||||
"deadline": "2022"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// deadline includes timestamp
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.1.1",
|
||||
"deadline": "2022-01-01T00:00:00Z"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// minimum_version includes build info
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.1.1 (ABCD)",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// valid config
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
|
||||
"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.3.1",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
}
|
||||
}`), http.StatusOK, &acResp)
|
||||
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion)
|
||||
require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline)
|
||||
|
||||
// get the appconfig
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
|
||||
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion)
|
||||
require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline)
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestSSOJITProvisioning() {
|
||||
t := s.T()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user