diff --git a/constants.go b/constants.go index 8e63416b46f47..5794d971178b4 100644 --- a/constants.go +++ b/constants.go @@ -738,6 +738,14 @@ const ( // credentials using any workload_identity resource. This exists to simplify // Day 0 UX experience with workload identity. PresetWildcardWorkloadIdentityIssuerRoleName = "wildcard-workload-identity-issuer" + + // PresetAccessPluginRoleName is a name of a preset role that includes + // permissions required by self-hosted access request plugin. + PresetAccessPluginRoleName = "access-plugin" + + // PresetListAccessRequestResourcesRoleName is a name of a preset role that + // includes permissions to read access request resources. + PresetListAccessRequestResourcesRoleName = "list-access-request-resources" ) var PresetRoles = []string{PresetEditorRoleName, PresetAccessRoleName, PresetAuditorRoleName} diff --git a/docs/pages/includes/preset-roles-table.mdx b/docs/pages/includes/preset-roles-table.mdx index c3a2e1a9d829c..5b80035ebd678 100644 --- a/docs/pages/includes/preset-roles-table.mdx +++ b/docs/pages/includes/preset-roles-table.mdx @@ -3,11 +3,13 @@ | `access`| Allows access to cluster resources. | | | `editor` | Allows editing of cluster configuration settings. | | | `auditor`| Allows reading cluster events, audit logs, and playing back session records. | | +| `access-plugin` | Enables self-hosted access request plugin features. | | +| `list-access-request-resources` | Allows reading access request resources. | | | `requester`| Allows a user to create Access Requests. | ✔ | | `reviewer`| Allows review of Access Requests. | ✔ | | `group-access`| Allows access to all user groups. | ✔ | | `device-admin`| Used to manage trusted devices. | ✔ | | `device-enroll`| Used to grant device enrollment powers to users. | ✔ | | `require-trusted-device`| Requires trusted device access to resources. | ✔ | -| `terraform-provider`| Allows the Teleport Terraform provider to configure all of its supported Teleport resources. | ✔ | +| `terraform-provider`| Allows the Teleport Terraform provider to configure all of its supported Teleport resources. | | diff --git a/lib/auth/init.go b/lib/auth/init.go index dd6b567d0e7a9..0d40f1eb067f0 100644 --- a/lib/auth/init.go +++ b/lib/auth/init.go @@ -1225,6 +1225,8 @@ func GetPresetRoles() []types.Role { services.NewPresetTerraformProviderRole(), services.NewSystemIdentityCenterAccessRole(), services.NewPresetWildcardWorkloadIdentityIssuerRole(), + services.NewPresetAccessPluginRole(), + services.NewPresetListAccessRequestResourcesRole(), } // Certain `New$FooRole()` functions will return a nil role if the diff --git a/lib/auth/init_test.go b/lib/auth/init_test.go index d3b1b8f0e28c8..cc4aaaddfedff 100644 --- a/lib/auth/init_test.go +++ b/lib/auth/init_test.go @@ -940,6 +940,8 @@ func TestPresets(t *testing.T) { teleport.PresetAuditorRoleName, teleport.PresetTerraformProviderRoleName, teleport.PresetWildcardWorkloadIdentityIssuerRoleName, + teleport.PresetAccessPluginRoleName, + teleport.PresetListAccessRequestResourcesRoleName, } t.Run("EmptyCluster", func(t *testing.T) { diff --git a/lib/services/presets.go b/lib/services/presets.go index 87a655f63f261..ca114585d1ec0 100644 --- a/lib/services/presets.go +++ b/lib/services/presets.go @@ -584,6 +584,76 @@ func NewPresetWildcardWorkloadIdentityIssuerRole() types.Role { return role } +// NewPresetAccessPluginRole returns a new pre-defined role for self-hosted +// access request plugins. +func NewPresetAccessPluginRole() types.Role { + role := &types.RoleV6{ + Kind: types.KindRole, + Version: types.V7, + Metadata: types.Metadata{ + Name: teleport.PresetAccessPluginRoleName, + Namespace: apidefaults.Namespace, + Description: "Default access plugin role", + Labels: map[string]string{ + types.TeleportInternalResourceType: types.PresetResource, + }, + }, + Spec: types.RoleSpecV6{ + Allow: types.RoleConditions{ + Rules: []types.Rule{ + types.NewRule(types.KindAccessRequest, RO()), + types.NewRule(types.KindAccessPluginData, RW()), + types.NewRule(types.KindAccessMonitoringRule, RO()), + types.NewRule(types.KindAccessList, RO()), + types.NewRule(types.KindRole, RO()), + types.NewRule(types.KindUser, RO()), + }, + ReviewRequests: &types.AccessReviewConditions{ + PreviewAsRoles: []string{ + teleport.PresetListAccessRequestResourcesRoleName, + }, + }, + }, + }, + } + return role +} + +// NewPresetListAccessRequestResourcesRole returns a new pre-defined role that +// allows reading access request resources. +func NewPresetListAccessRequestResourcesRole() types.Role { + role := &types.RoleV6{ + Kind: types.KindRole, + Version: types.V7, + Metadata: types.Metadata{ + Name: teleport.PresetListAccessRequestResourcesRoleName, + Namespace: apidefaults.Namespace, + Description: "Default list access request resources role", + Labels: map[string]string{ + types.TeleportInternalResourceType: types.PresetResource, + }, + }, + Spec: types.RoleSpecV6{ + Allow: types.RoleConditions{ + Rules: []types.Rule{ + types.NewRule(types.KindNode, RO()), + types.NewRule(types.KindApp, RO()), + types.NewRule(types.KindDatabase, RO()), + types.NewRule(types.KindKubernetesCluster, RO()), + }, + // To enable all access plugin features, the role requires read + // access to all of the following resources. + AppLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + DatabaseLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + GroupLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + KubernetesLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + NodeLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + }, + }, + } + return role +} + // SystemOktaAccessRoleName is the name of the system role that allows // access to Okta resources. This will be used by the Okta requester role to // search for Okta resources. @@ -770,10 +840,12 @@ func bootstrapRoleMetadataLabels() map[string]map[string]string { } var defaultAllowRulesMap = map[string][]types.Rule{ - teleport.PresetAuditorRoleName: NewPresetAuditorRole().GetRules(types.Allow), - teleport.PresetEditorRoleName: NewPresetEditorRole().GetRules(types.Allow), - teleport.PresetAccessRoleName: NewPresetAccessRole().GetRules(types.Allow), - teleport.PresetTerraformProviderRoleName: NewPresetTerraformProviderRole().GetRules(types.Allow), + teleport.PresetAuditorRoleName: NewPresetAuditorRole().GetRules(types.Allow), + teleport.PresetEditorRoleName: NewPresetEditorRole().GetRules(types.Allow), + teleport.PresetAccessRoleName: NewPresetAccessRole().GetRules(types.Allow), + teleport.PresetTerraformProviderRoleName: NewPresetTerraformProviderRole().GetRules(types.Allow), + teleport.PresetAccessPluginRoleName: NewPresetAccessPluginRole().GetRules(types.Allow), + teleport.PresetListAccessRequestResourcesRoleName: NewPresetListAccessRequestResourcesRole().GetRules(types.Allow), } // defaultAllowRules has the Allow rules that should be set as default when @@ -804,6 +876,13 @@ func defaultAllowLabels(enterprise bool) map[string]types.RoleConditions { NodeLabels: wildcardLabels, WindowsDesktopLabels: wildcardLabels, }, + teleport.PresetListAccessRequestResourcesRoleName: { + AppLabels: wildcardLabels, + DatabaseLabels: wildcardLabels, + GroupLabels: wildcardLabels, + KubernetesLabels: wildcardLabels, + NodeLabels: wildcardLabels, + }, } if enterprise { @@ -954,6 +1033,7 @@ func AddRoleDefaults(role types.Role) (types.Role, error) { types.KindNode, types.KindUserGroup, types.KindWindowsDesktop, + types.KindKubernetesCluster, } { var labels types.Labels switch kind { @@ -969,6 +1049,8 @@ func AddRoleDefaults(role types.Role) (types.Role, error) { labels = defaultLabels.GroupLabels case types.KindWindowsDesktop: labels = defaultLabels.WindowsDesktopLabels + case types.KindKubernetesCluster: + labels = defaultLabels.KubernetesLabels } labelsUpdated, err := updateAllowLabels(role, kind, labels) if err != nil { diff --git a/lib/services/presets_test.go b/lib/services/presets_test.go index fd65c4bce12a8..324ee35ac3741 100644 --- a/lib/services/presets_test.go +++ b/lib/services/presets_test.go @@ -754,6 +754,90 @@ func TestAddRoleDefaults(t *testing.T) { }, }, }, + { + name: "access-plugin (default roles match preset rules)", + role: &types.RoleV6{ + Kind: types.KindRole, + Version: types.V7, + Metadata: types.Metadata{ + Name: teleport.PresetAccessPluginRoleName, + Namespace: apidefaults.Namespace, + Description: "Default access plugin role", + Labels: map[string]string{ + types.TeleportInternalResourceType: types.PresetResource, + }, + }, + }, + expectedErr: require.NoError, + expected: &types.RoleV6{ + Kind: types.KindRole, + Version: types.V7, + Metadata: types.Metadata{ + Name: teleport.PresetAccessPluginRoleName, + Namespace: apidefaults.Namespace, + Description: "Default access plugin role", + Labels: map[string]string{ + types.TeleportInternalResourceType: types.PresetResource, + }, + }, + Spec: types.RoleSpecV6{ + Allow: types.RoleConditions{ + Rules: []types.Rule{ + // The missing resources got added as individual rules + types.NewRule(types.KindAccessRequest, RO()), + types.NewRule(types.KindAccessPluginData, RW()), + types.NewRule(types.KindAccessMonitoringRule, RO()), + types.NewRule(types.KindAccessList, RO()), + types.NewRule(types.KindRole, RO()), + types.NewRule(types.KindUser, RO()), + }, + }, + }, + }, + }, + { + name: "list-access-request-resources (default roles match preset rules)", + role: &types.RoleV6{ + Kind: types.KindRole, + Version: types.V7, + Metadata: types.Metadata{ + Name: teleport.PresetListAccessRequestResourcesRoleName, + Namespace: apidefaults.Namespace, + Description: "Default list access request resources role", + Labels: map[string]string{ + types.TeleportInternalResourceType: types.PresetResource, + }, + }, + }, + expectedErr: require.NoError, + expected: &types.RoleV6{ + Kind: types.KindRole, + Version: types.V7, + Metadata: types.Metadata{ + Name: teleport.PresetListAccessRequestResourcesRoleName, + Namespace: apidefaults.Namespace, + Description: "Default list access request resources role", + Labels: map[string]string{ + types.TeleportInternalResourceType: types.PresetResource, + }, + }, + Spec: types.RoleSpecV6{ + Allow: types.RoleConditions{ + Rules: []types.Rule{ + types.NewRule(types.KindNode, RO()), + types.NewRule(types.KindApp, RO()), + types.NewRule(types.KindDatabase, RO()), + types.NewRule(types.KindKubernetesCluster, RO()), + }, + AppLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + DatabaseLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + GroupLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + KubernetesLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + NodeLabels: types.Labels{types.Wildcard: []string{types.Wildcard}}, + }, + }, + }, + }, } for _, test := range tests {