Skip to content

Commit

Permalink
Added Show Command (#58)
Browse files Browse the repository at this point in the history
Generate ClusterRole with all available permissions from the target cluster
  • Loading branch information
gadinaor-r7 authored Sep 30, 2022
1 parent 5fb8384 commit ba68333
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 37 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Available Commands:
help Help about any command
lookup RBAC Lookup by subject (user/group/serviceaccount) name
policy-rules RBAC List Policy Rules For subject (user/group/serviceaccount) name
show Generate ClusterRole with all available permissions from the target cluster
version Print rbac-tool version
visualize A RBAC visualizer
who-can Shows which subjects have RBAC permissions to perform an action
Expand All @@ -77,6 +78,7 @@ Use "rbac-tool [command] --help" for more information about a command.
- [The `rbac-tool policy-rules` command](#rbac-tool-policy-rules)
- [The `rbac-tool auditgen` command](#rbac-tool-auditgen)
- [The `rbac-tool gen` command](#rbac-tool-gen)
- [The `rbac-tool show` command](#rbac-tool-show)
- [Command Line Reference](#command-line-reference)
- [Contributing](#contributing)

Expand Down Expand Up @@ -112,6 +114,21 @@ rbac-tool viz --cluster-context myctx
rbac-tool viz --outformat dot --exclude-namespaces=soemns && cat rbac.dot | dot -Tpng > rbac.png && google-chrome rbac.png
```


# `rbac-tool show`

Generate sample ClusterRole with all available permissions from the target cluster.

rbac-tool read from the Kubernetes discovery API the available API Groups and resources,
and based on the command line options, generate an explicit ClusterRole with available resource permissions.
Examples:

```shell script
# Generate a ClusterRole with all the available permissions for core and apps api groups
rbac-tool show --for-groups=,apps
```


# `rbac-tool analysis`

Analyze RBAC permissions and highlight overly permissive principals, risky permissions.
Expand Down
83 changes: 46 additions & 37 deletions cmd/generate_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,47 +50,22 @@ rbac-tool gen --generated-type=ClusterRole --deny-resources=secrets., --allowed-
`,
Hidden: false,
RunE: func(c *cobra.Command, args []string) error {

computedPolicyRules, err := generateRules(clusterContext, sets.NewString(denyResources...), sets.NewString(allowedGroups...), sets.NewString(allowedVerb...))
kubeClient, err := kube.NewClient(clusterContext)
if err != nil {
return err
return fmt.Errorf("Failed to create kubernetes client - %v", err)
}

var obj runtime.Object

if generateKind == "ClusterRole" {
obj = &rbacv1.ClusterRole{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRole",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "custom-cluster-role",
},
Rules: computedPolicyRules,
}
} else {
obj = &rbacv1.Role{
TypeMeta: metav1.TypeMeta{
Kind: "Role",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "custom-role",
Namespace: "mynamespace",
},
Rules: computedPolicyRules,
}
computedPolicyRules, err := generateRules(kubeClient.ServerPreferredResources, sets.NewString(denyResources...), sets.NewString(allowedGroups...), sets.NewString(allowedVerb...))
if err != nil {
return err
}

serializer := k8sJson.NewSerializerWithOptions(k8sJson.DefaultMetaFactory, nil, nil, k8sJson.SerializerOptions{Yaml: true, Pretty: true, Strict: true})
var writer = bytes.NewBufferString("")
err = serializer.Encode(obj, writer)
obj, err := generateRole(generateKind, computedPolicyRules)
if err != nil {
return err
}

println(writer.String())
println(obj)

return nil
},
Expand All @@ -108,18 +83,52 @@ rbac-tool gen --generated-type=ClusterRole --deny-resources=secrets., --allowed-
return cmd
}

func generateRules(clusterContext string, denyResources sets.String, includeGroups sets.String, allowedVerbs sets.String) ([]rbacv1.PolicyRule, error) {
errs := []error{}
kubeClient, err := kube.NewClient(clusterContext)
func generateRole(generateKind string, rules []rbacv1.PolicyRule) (string, error) {
var obj runtime.Object

if generateKind == "ClusterRole" {
obj = &rbacv1.ClusterRole{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRole",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "custom-cluster-role",
},
Rules: rules,
}
} else {
obj = &rbacv1.Role{
TypeMeta: metav1.TypeMeta{
Kind: "Role",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "custom-role",
Namespace: "mynamespace",
},
Rules: rules,
}
}

serializer := k8sJson.NewSerializerWithOptions(k8sJson.DefaultMetaFactory, nil, nil, k8sJson.SerializerOptions{Yaml: true, Pretty: true, Strict: true})
var writer = bytes.NewBufferString("")
err := serializer.Encode(obj, writer)
if err != nil {
return nil, fmt.Errorf("Failed to create kubernetes client - %v", err)
return "", err
}

return writer.String(), nil
}

func generateRules(apiresourceList []*metav1.APIResourceList, denyResources sets.String, includeGroups sets.String, allowedVerbs sets.String) ([]rbacv1.PolicyRule, error) {
errs := []error{}

computedPolicyRules := make([]rbacv1.PolicyRule, 0)

//processedGroups := sets.NewString()

for _, apiGroup := range kubeClient.ServerPreferredResources {
for _, apiGroup := range apiresourceList {

// rbac rules only look at API group names, not name + version
gv, err := schema.ParseGroupVersion(apiGroup.GroupVersion)
Expand Down
148 changes: 148 additions & 0 deletions cmd/show_permissions_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package cmd

import (
"fmt"
"strings"

"github.com/spf13/cobra"

rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"

"github.com/alcideio/rbac-tool/pkg/kube"
)

func NewCommandGenerateShowPermissions() *cobra.Command {

clusterContext := ""
generateKind := "ClusterRole"
forGroups := []string{"*"}
withVerb := []string{"*"}

// Support overrides
cmd := &cobra.Command{
Use: "show",
Short: "Generate ClusterRole with all available permissions from the target cluster",
Long: `
Generate sample ClusterRole with all available permissions from the target cluster.
rbac-tool read from the Kubernetes discovery API the available API Groups and resources,
and based on the command line options, generate an explicit ClusterRole with available resource permissions.
Examples:
# Generate a ClusterRole with all the available permissions for core and apps api groups
rbac-tool show --for-groups=,apps
`,
Hidden: false,
RunE: func(c *cobra.Command, args []string) error {
kubeClient, err := kube.NewClient(clusterContext)
if err != nil {
return fmt.Errorf("Failed to create kubernetes client - %v", err)
}

_, allResources, err := kubeClient.Client.Discovery().ServerGroupsAndResources()
if err != nil {
return fmt.Errorf("Failed to create kubernetes client - %v", err)
}

//println(pretty.Sprint(allResources))
//
//if true {
// return nil
//}

computedPolicyRules, err := generateRulesWithSubResources(allResources, sets.NewString(), sets.NewString(forGroups...), sets.NewString(withVerb...))
if err != nil {
return err
}

obj, err := generateRole(generateKind, computedPolicyRules)
if err != nil {
return err
}

println(obj)

return nil
},
}

flags := cmd.Flags()

flags.StringVarP(&clusterContext, "cluster-context", "c", "", "Cluster.use 'kubectl config get-contexts' to list available contexts")
flags.StringSliceVar(&forGroups, "for-groups", []string{"*"}, "Comma separated list of API groups we would like to show the permissions")
flags.StringSliceVar(&withVerb, "with-verbs", []string{"*"}, "Comma separated list of verbs to include. To include all use '*'")

return cmd
}

func generateRulesWithSubResources(apiresourceList []*metav1.APIResourceList, denyResources sets.String, includeGroups sets.String, allowedVerbs sets.String) ([]rbacv1.PolicyRule, error) {
errs := []error{}

computedPolicyRules := make([]rbacv1.PolicyRule, 0)

//processedGroups := sets.NewString()

for _, apiGroup := range apiresourceList {

// rbac rules only look at API group names, not name + version
gv, err := schema.ParseGroupVersion(apiGroup.GroupVersion)
if err != nil {
errs = append(errs, err)
continue
}

//Skip the API Groups for specific
if !includeGroups.Has(gv.Group) && !includeGroups.Has(rbacv1.APIGroupAll) {
continue
}

//Skip API Group versions (RBAC ignore API version)
//if processedGroups.Has(gv.Group) {
// continue
//}

//Skip API Group entirely if *.APIGroup was specified
if denyResources.Has(fmt.Sprintf("*.%v", strings.ToLower(gv.Group))) {
continue
}

//processedGroups.Insert(gv.Group)

for _, kind := range apiGroup.APIResources {

if denyResources.Has(fmt.Sprintf("%v.%v", strings.ToLower(kind.Name), strings.ToLower(gv.Group))) {
continue
}

var newPolicyRule *rbacv1.PolicyRule
var uniqueVerbs sets.String

uniqueVerbs = sets.NewString()
for _, verb := range kind.Verbs {
if allowedVerbs.Has(verb) || allowedVerbs.Has(rbacv1.VerbAll) {
uniqueVerbs.Insert(verb)
}
}

if uniqueVerbs.Len() > 0 {
newPolicyRule = &rbacv1.PolicyRule{
APIGroups: []string{gv.Group},
Verbs: uniqueVerbs.List(),
Resources: []string{kind.Name},
}

computedPolicyRules = append(computedPolicyRules, *newPolicyRule)
}

}
}

return computedPolicyRules, errors.NewAggregate(errs)
}
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func RbacGenCmd() *cobra.Command {
cmd.NewCommandAuditGen(),
cmd.NewCommandWhoCan(),
cmd.NewCommandAnalysis(),
cmd.NewCommandGenerateShowPermissions(),
}

flags := rootCmd.PersistentFlags()
Expand Down
6 changes: 6 additions & 0 deletions notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ rbac-tool auditgen -f testdata | rbac-tool viz -f -

# Generate a `ClusterRole` policy that allows to read everything **except** *secrets* and *services*
rbac-tool gen --deny-resources=secrets.,services. --allowed-verbs=get,list

# Generate a ClusterRole with all the available permissions for core and apps api groups
rbac-tool show --for-groups=,apps
```

## kubectl rbac-tool ...
Expand All @@ -64,4 +67,7 @@ kubectl rbac-tool gen --deny-resources=secrets.,services. --allowed-verbs=get,li
# Analyze cluster RBAC permissions to identify overly permissive roles and principals
kubectl rbac-tool analysis -o table

# Generate a ClusterRole with all the available permissions for core and apps api groups
kubectl rbac-tool show --for-groups=,apps

```

0 comments on commit ba68333

Please sign in to comment.