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
7 changes: 7 additions & 0 deletions api/types/system_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ const (
RoleDiscovery SystemRole = "Discovery"
// RoleOkta is a role for Okta nodes in the cluster
RoleOkta SystemRole = "Okta"
// RoleMDM is the role for MDM services in the cluster.
// An MDM service, like Jamf Service, has the powers to manage the cluster's
// device inventory.
// Device Trust requires Teleport Enteprise.
RoleMDM SystemRole = "MDM"
)

// roleMappings maps a set of allowed lowercase system role names
Expand All @@ -97,6 +102,7 @@ var roleMappings = map[string]SystemRole{
"instance": RoleInstance,
"discovery": RoleDiscovery,
"okta": RoleOkta,
"mdm": RoleMDM,
}

// localServiceMappings is the subset of role mappings which happen to be true
Expand All @@ -112,6 +118,7 @@ var localServiceMappings = map[SystemRole]struct{}{
RoleWindowsDesktop: {},
RoleDiscovery: {},
RoleOkta: {},
RoleMDM: {},
}

// LocalServiceMappings returns the subset of role mappings which happen
Expand Down
4 changes: 3 additions & 1 deletion lib/auth/auth_with_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3827,7 +3827,9 @@ func TestLocalServiceRolesHavePermissionsForUploaderService(t *testing.T) {
require.NoError(t, err)

for _, role := range types.LocalServiceMappings() {
if role == types.RoleAuth {
// RoleMDM services don't create events by themselves, instead they rely on
// Auth to issue events.
if role == types.RoleAuth || role == types.RoleMDM {
continue
}
t.Run(role.String(), func(t *testing.T) {
Expand Down
11 changes: 11 additions & 0 deletions lib/authz/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,17 @@ func definitionForBuiltinRole(clusterName string, recConfig types.SessionRecordi
},
},
})
case types.RoleMDM:
return services.RoleFromSpec(
role.String(),
types.RoleSpecV6{
Allow: types.RoleConditions{
Namespaces: []string{types.Wildcard},
Rules: []types.Rule{
types.NewRule(types.KindDevice, services.RW()),
},
},
})
}

return nil, trace.NotFound("builtin role %q is not recognized", role.String())
Expand Down
30 changes: 30 additions & 0 deletions lib/authz/permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,36 @@ func TestAuthorizeWithVerbs(t *testing.T) {
}
}

func TestRoleSetForBuiltinRoles(t *testing.T) {
tests := []struct {
name string
clusterName string
recConfig types.SessionRecordingConfig
roles []types.SystemRole
assertRoleSet func(t *testing.T, rs services.RoleSet)
}{
{
name: "RoleMDM is mapped",
clusterName: clusterName,
roles: []types.SystemRole{types.RoleMDM},
assertRoleSet: func(t *testing.T, rs services.RoleSet) {
for i, r := range rs {
assert.NotEmpty(t, r.GetNamespaces(types.Allow), "RoleSetForBuiltinRoles: rs[%v]: role has no namespaces", i)
assert.NotEmpty(t, r.GetRules(types.Allow), "RoleSetForBuiltinRoles: rs[%v]: role has no rules", i)
}
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
rs, err := RoleSetForBuiltinRoles(test.clusterName, test.recConfig, test.roles...)
require.NoError(t, err, "RoleSetForBuiltinRoles failed")
assert.NotEmpty(t, rs, "RoleSetForBuiltinRoles returned a nil RoleSet")
test.assertRoleSet(t, rs)
})
}
}

// fakeCtxUser is used for auth.Context tests.
type fakeCtxUser struct {
types.User
Expand Down
20 changes: 20 additions & 0 deletions tool/tctl/common/token_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"os"
"sort"
"strings"
"text/template"
"time"

"github.com/ghodss/yaml"
Expand All @@ -42,6 +43,19 @@ import (
"github.com/gravitational/teleport/lib/utils"
)

var mdmTokenAddTemplate = template.Must(
template.New("mdmTokenAdd").Parse(`The invite token: {{.token}}
This token will expire in {{.minutes}} minutes.

Use this token to add an MDM service to Teleport.

> teleport start \
--token={{.token}} \{{range .ca_pins}}
--ca-pin={{.}} \{{end}}
-c=/path/to/teleport.yaml

`))

// TokensCommand implements `tctl tokens` group of commands
type TokensCommand struct {
config *servicecfg.Config
Expand Down Expand Up @@ -297,6 +311,12 @@ func (c *TokensCommand) Add(ctx context.Context, client auth.ClientI) error {
"token": token,
"minutes": c.ttl.Minutes(),
})
case roles.Include(types.RoleMDM):
return mdmTokenAddTemplate.Execute(c.stdout, map[string]interface{}{
"token": token,
"minutes": c.ttl.Minutes(),
"ca_pins": caPins,
})
default:
authServer := authServers[0].GetAddr()

Expand Down