diff --git a/api/types/constants.go b/api/types/constants.go index 453129f2b54..72a2c5da48a 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -424,12 +424,8 @@ const ( // RequestableResourceKinds lists all Teleport resource kinds users can request access to. var RequestableResourceKinds = []string{ KindNode, - KindKubeService, KindKubernetesCluster, - KindDatabaseServer, KindDatabase, - KindAppServer, KindApp, - KindWindowsDesktopService, KindWindowsDesktop, } diff --git a/lib/services/access_checker.go b/lib/services/access_checker.go index 5f875642e04..56a0cfe11a4 100644 --- a/lib/services/access_checker.go +++ b/lib/services/access_checker.go @@ -219,16 +219,35 @@ func (a *accessChecker) checkAllowedResources(r AccessCheckable) error { // resources, only role-based access control is used return nil } + + // Note: logging in this function only happens in debug mode. This is because + // adding logging to this function (which is called on every resource returned + // by the backend) can slow down this function by 50x for large clusters! + isDebugEnabled, debugf := rbacDebugLogger() + for _, resourceID := range a.info.AllowedResourceIDs { if resourceID.ClusterName == a.localCluster && resourceID.Kind == r.GetKind() && resourceID.Name == r.GetName() { - // allowed to access this resource + // Allowed to access this resource by resource ID, move on to role checks. + if isDebugEnabled { + debugf("Matched allowed resource ID %q", types.ResourceIDToString(resourceID)) + } return nil } } - return trace.AccessDenied("access to %s:%s is not allowed. Allowed resources: %v", - r.GetKind(), r.GetName(), a.info.AllowedResourceIDs) + + if isDebugEnabled { + allowedResources, err := types.ResourceIDsToString(a.info.AllowedResourceIDs) + if err != nil { + return trace.Wrap(err) + } + err = trace.AccessDenied("access to %v denied, %q not in allowed resource IDs %s", + r.GetKind(), r.GetName(), allowedResources) + debugf("Access denied: %v", err) + return err + } + return trace.AccessDenied("access to %v denied, not in allowed resource IDs", r.GetKind()) } // CheckAccess checks if the identity for this AccessChecker has access to the diff --git a/tool/tsh/access_request.go b/tool/tsh/access_request.go index 5c584768442..b099fad87cb 100644 --- a/tool/tsh/access_request.go +++ b/tool/tsh/access_request.go @@ -27,6 +27,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client" "github.com/gravitational/teleport/api/client/proto" + apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/asciitable" "github.com/gravitational/teleport/lib/auth" @@ -377,17 +378,27 @@ func onRequestSearch(cf *CLIConf) error { clusterName := clusterNameResource.GetClusterName() req := proto.ListResourcesRequest{ - ResourceType: cf.ResourceKind, + ResourceType: searchKindFixup(cf.ResourceKind), Labels: tc.Labels, PredicateExpression: cf.PredicateExpression, SearchKeywords: tc.SearchKeywords, UseSearchAsRoles: true, } - resources, err := client.GetResourcesWithFilters(cf.Context, authClient, req) + + results, err := client.GetResourcesWithFilters(cf.Context, authClient, req) if err != nil { return trace.Wrap(err) } + var resources types.ResourcesWithLabels + for _, result := range results { + fixedResources, err := resultKindFixup(result, cf.ResourceKind) + if err != nil { + return trace.Wrap(err) + } + resources = append(resources, fixedResources...) + } + rows := [][]string{} var resourceIDs []string for _, resource := range resources { @@ -425,3 +436,44 @@ To request access to these resources, run return nil } + +func searchKindFixup(kind string) string { + // Some resource kinds don't support search directly, run the search on the + // parent kind instead. + switch kind { + case types.KindApp: + return types.KindAppServer + case types.KindDatabase: + return types.KindDatabaseServer + case types.KindKubernetesCluster: + return types.KindKubeService + default: + return kind + } +} + +func resultKindFixup(resource types.ResourceWithLabels, hint string) (types.ResourcesWithLabels, error) { + // The inverse of searchKindFixup, after the search map the result back to + // the kind we really want. + switch r := resource.(type) { + case types.AppServer: + return types.ResourcesWithLabels{r.GetApp()}, nil + case types.DatabaseServer: + return types.ResourcesWithLabels{r.GetDatabase()}, nil + case types.Server: + if hint == types.KindKubernetesCluster { + kubeClusters := r.GetKubernetesClusters() + resources := make(types.ResourcesWithLabels, 0, len(kubeClusters)) + for _, kubeCluster := range kubeClusters { + resource, err := types.NewKubernetesClusterV3FromLegacyCluster(apidefaults.Namespace, kubeCluster) + if err != nil { + return nil, trace.Wrap(err) + } + resources = append(resources, resource) + } + return resources, nil + } + default: + } + return types.ResourcesWithLabels{resource}, nil +}