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/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,13 @@ func (a *AppV3) CheckAndSetDefaults() error {
}
}

// Set an "app-sub-kind" label can be used for RBAC.
if a.SubKind != "" {
if a.Metadata.Labels == nil {
a.Metadata.Labels = make(map[string]string)
}
a.Metadata.Labels[AppSubKindLabel] = a.SubKind
}
return nil
}

Expand Down
2 changes: 2 additions & 0 deletions api/types/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ func TestNewAppV3(t *testing.T) {
Metadata: Metadata{
Name: "mcp-everything",
Namespace: "default",
Labels: map[string]string{AppSubKindLabel: "mcp"},
},
Spec: AppSpecV3{
URI: "mcp+stdio://",
Expand Down Expand Up @@ -678,6 +679,7 @@ func TestNewAppV3(t *testing.T) {
Namespace: "default",
Labels: map[string]string{
TeleportInternalResourceType: DemoResource,
AppSubKindLabel: "mcp",
},
},
Spec: AppSpecV3{
Expand Down
3 changes: 3 additions & 0 deletions api/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,9 @@ const (

// GitHubOrgLabel is the label for GitHub organization.
GitHubOrgLabel = TeleportInternalLabelPrefix + "github-org"

// AppSubKindLabel is the label that has the same value of "app.sub_kind".
AppSubKindLabel = TeleportInternalLabelPrefix + "app-sub-kind"
)

const (
Expand Down
4 changes: 4 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,10 @@ const (
// PresetListAccessRequestResourcesRoleName is a name of a preset role that
// includes permissions to read access request resources.
PresetListAccessRequestResourcesRoleName = "list-access-request-resources"

// PresetMCPUserRoleName is a name of a preset role that allows
// accessing MCP servers.
PresetMCPUserRoleName = "mcp-user"
)

var PresetRoles = []string{PresetEditorRoleName, PresetAccessRoleName, PresetAuditorRoleName}
Expand Down
1 change: 1 addition & 0 deletions lib/auth/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,7 @@ func GetPresetRoles() []types.Role {
services.NewPresetWildcardWorkloadIdentityIssuerRole(),
services.NewPresetAccessPluginRole(),
services.NewPresetListAccessRequestResourcesRole(),
services.NewPresetMCPUserRole(),
}

// Certain `New$FooRole()` functions will return a nil role if the
Expand Down
1 change: 1 addition & 0 deletions lib/auth/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,7 @@ func TestPresets(t *testing.T) {
teleport.PresetWildcardWorkloadIdentityIssuerRoleName,
teleport.PresetAccessPluginRoleName,
teleport.PresetListAccessRequestResourcesRoleName,
teleport.PresetMCPUserRoleName,
}

t.Run("EmptyCluster", func(t *testing.T) {
Expand Down
35 changes: 32 additions & 3 deletions lib/services/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,34 @@ func NewPresetTerraformProviderRole() types.Role {
return role
}

// NewPresetMCPUserRole returns a new pre-defined role for accessing MCP
// servers.
func NewPresetMCPUserRole() types.Role {
role := &types.RoleV6{
Kind: types.KindRole,
Version: types.V8,
Metadata: types.Metadata{
Name: teleport.PresetMCPUserRoleName,
Namespace: apidefaults.Namespace,
Description: "Access to MCP servers",
Labels: map[string]string{
types.TeleportInternalResourceType: types.PresetResource,
},
},
Spec: types.RoleSpecV6{
Allow: types.RoleConditions{
AppLabels: map[string]apiutils.Strings{
types.AppSubKindLabel: []string{types.SubKindMCP},
},
MCP: &types.MCPPermissions{
Tools: []string{types.Wildcard},
},
},
},
}
return role
}

// NewPresetHealthCheckConfig returns a preset default health_check_config that
// enables health checks for all resources.
func NewPresetHealthCheckConfig() *healthcheckconfigv1.HealthCheckConfig {
Expand Down Expand Up @@ -873,8 +901,9 @@ func bootstrapRoleMetadataLabels() map[string]map[string]string {
teleport.SystemIdentityCenterAccessRoleName: {
types.TeleportInternalResourceType: types.SystemResource,
},
// Group access, reviewer and requester are intentionally not added here as there may be
// existing customer defined roles that have these labels.
// These roles are intentionally not added here as there may be existing
// customer defined roles that have these labels:
// group-access, reviewer, requester, mcp-user
}
}

Expand Down Expand Up @@ -1036,7 +1065,7 @@ func AddRoleDefaults(ctx context.Context, role types.Role) (types.Role, error) {
// Check if the role has a TeleportInternalResourceType attached. We do this after setting the role metadata
// labels because we set the role metadata labels for roles that have been well established (access,
// editor, auditor) that may not already have this label set, but we don't set it for newer roles
// (group-access, reviewer, requester) that may have customer definitions.
// (group-access, reviewer, requester, mcp-user) that may have customer definitions.
resourceType := labels[types.TeleportInternalResourceType]
if resourceType != types.PresetResource && resourceType != types.SystemResource {
return nil, trace.AlreadyExists("not modifying user created role")
Expand Down
13 changes: 2 additions & 11 deletions lib/srv/mcp/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,8 @@ func withRole(role types.Role) setupTestContextOptionFunc {
// tools.
func withAdminRole(t *testing.T) setupTestContextOptionFunc {
t.Helper()
role, err := types.NewRole("admin", types.RoleSpecV6{
Allow: types.RoleConditions{
AppLabels: map[string]apiutils.Strings{
types.Wildcard: {types.Wildcard},
},
MCP: &types.MCPPermissions{
Tools: []string{types.Wildcard},
},
},
})
require.NoError(t, err)
role := services.NewPresetMCPUserRole()
require.NoError(t, services.CheckAndSetDefaults(role))
return withRole(role)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"name": "allow-read",
"description": "description",
"labels": {
"env": "dev"
"env": "dev",
"teleport.internal/app-sub-kind": "mcp"
}
},
"spec": {
Expand Down Expand Up @@ -39,7 +40,8 @@
"name": "deny-write",
"description": "description",
"labels": {
"env": "dev"
"env": "dev",
"teleport.internal/app-sub-kind": "mcp"
}
},
"spec": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
description: description
labels:
env: dev
teleport.internal/app-sub-kind: mcp
name: allow-read
permissions:
mcp:
Expand All @@ -24,6 +25,7 @@
description: description
labels:
env: dev
teleport.internal/app-sub-kind: mcp
name: deny-write
permissions:
mcp:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Name Description Type Labels Command Args Allowed Tools
---------- ----------- ----- ------- ------- ---- ----------------------
allow-read description stdio env=dev test arg [read_*]
deny-write description stdio env=dev test arg [*], except: [write_*]
Name Description Type Labels Command Args Allowed Tools
---------- ----------- ----- ------------------------------------------ ------- ---- ----------------------
allow-read description stdio env=dev,teleport.internal/app-sub-kind=mcp test arg [read_*]
deny-write description stdio env=dev,teleport.internal/app-sub-kind=mcp test arg [*], except: [write_*]

Loading