Skip to content

Commit

Permalink
adding --gke flag, some related cleanup and restructuring
Browse files Browse the repository at this point in the history
  • Loading branch information
robscott committed Sep 26, 2018
1 parent c4bdc2b commit ba8db1f
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 106 deletions.
4 changes: 3 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
)

var output string
var gke bool

var rootCmd = &cobra.Command{
Use: "rbac-lookup [subject query]",
Expand All @@ -34,12 +35,13 @@ var rootCmd = &cobra.Command{
fmt.Printf("Error parsing flags: %v", err)
}

lookup.List(args, output)
lookup.List(args, output, gke)
},
}

func init() {
rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "output format (normal,wide)")
rootCmd.PersistentFlags().BoolVar(&gke, "gke", false, "enable GKE integration")
}

// Execute is the primary entrypoint for this CLI
Expand Down
1 change: 1 addition & 0 deletions lookup/gke_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package lookup

var gkeIamScope = "project-wide"
var gkeIamRoles = map[string]simpleRole{
"roles/container.clusterAdmin": {
Kind: "IAM",
Expand Down
26 changes: 16 additions & 10 deletions lookup/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,13 @@ type clusterInfo struct {
}

// List outputs rbac bindings where subject names match given string
func List(args []string, outputFormat string) {
func List(args []string, outputFormat string, enableGke bool) {
kubeconfig := getKubeConfig()
clientset, err := getClientSet(kubeconfig)
if err != nil {
panic(err.Error())
}

ci := getClusterInfo(kubeconfig)

filter := ""
if len(args) > 0 {
filter = args[0]
Expand All @@ -52,10 +50,14 @@ func List(args []string, outputFormat string) {
l := lister{
filter: filter,
clientset: clientset,
gkeProjectName: ci.GkeProjectName,
rbacSubjectsByScope: make(map[string]rbacSubject),
}

if enableGke {
ci := getClusterInfo(kubeconfig)
l.gkeProjectName = ci.GkeProjectName
}

loadErr := l.loadAll()
if loadErr != nil {
fmt.Printf("Error loading RBAC: %v\n", loadErr)
Expand Down Expand Up @@ -90,12 +92,16 @@ func getClusterInfo(kubeconfig string) *clusterInfo {
if err != nil {
panic(err.Error())
}
s := strings.Split(c.CurrentContext, "_")
if s[0] == "gke" {
return &clusterInfo{
ClusterName: s[3],
GkeZone: s[2],
GkeProjectName: s[1],

currentContext := c.Contexts[c.CurrentContext]
if currentContext.Cluster != "" {
s := strings.Split(currentContext.Cluster, "_")
if s[0] == "gke" {
return &clusterInfo{
ClusterName: s[3],
GkeZone: s[2],
GkeProjectName: s[1],
}
}
}
return &clusterInfo{}
Expand Down
155 changes: 60 additions & 95 deletions lookup/lister.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (
"golang.org/x/oauth2/google"
"google.golang.org/api/cloudresourcemanager/v1"

rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"k8s.io/client-go/kubernetes"
Expand All @@ -34,22 +33,6 @@ import (
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
)

type rbacSubject struct {
Kind string
RolesByScope map[string][]simpleRole
}

type simpleRole struct {
Kind string
Name string
Source simpleRoleSource
}

type simpleRoleSource struct {
Kind string
Name string
}

type lister struct {
clientset kubernetes.Interface
filter string
Expand All @@ -70,15 +53,53 @@ func (l *lister) loadAll() error {
return crbErr
}

gkeErr := l.loadGkeRoleBindings()
if l.gkeProjectName != "" {
gkeErr := l.loadGkeRoleBindings()

if gkeErr != nil {
return gkeErr
if gkeErr != nil {
return gkeErr
}
}

return nil
}

func (l *lister) printRbacBindings(outputFormat string) {
if len(l.rbacSubjectsByScope) < 1 {
fmt.Println("No RBAC Bindings found")
return
}

names := make([]string, 0, len(l.rbacSubjectsByScope))
for name := range l.rbacSubjectsByScope {
names = append(names, name)
}
sort.Strings(names)

w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 2, ' ', 0)

if outputFormat == "wide" {
fmt.Fprintln(w, "SUBJECT\t SCOPE\t ROLE\t SOURCE")
} else {
fmt.Fprintln(w, "SUBJECT\t SCOPE\t ROLE")
}

for _, subjectName := range names {
rbacSubject := l.rbacSubjectsByScope[subjectName]
for scope, simpleRoles := range rbacSubject.RolesByScope {
for _, simpleRole := range simpleRoles {
if outputFormat == "wide" {
fmt.Fprintf(w, "%s/%s \t %s\t %s/%s\t %s/%s\n", rbacSubject.Kind, subjectName, scope, simpleRole.Kind, simpleRole.Name, simpleRole.Source.Kind, simpleRole.Source.Name)
} else {
fmt.Fprintf(w, "%s \t %s\t %s/%s\n", subjectName, scope, simpleRole.Kind, simpleRole.Name)
}
}
}
}
w.Flush()
}

func (l *lister) loadRoleBindings() error {
roleBindings, err := l.clientset.RbacV1().RoleBindings("").List(metav1.ListOptions{})

Expand Down Expand Up @@ -133,61 +154,28 @@ func (l *lister) loadClusterRoleBindings() error {
return nil
}

func (l *lister) printRbacBindings(outputFormat string) {
if len(l.rbacSubjectsByScope) < 1 {
fmt.Println("No RBAC Bindings found")
return
}

names := make([]string, 0, len(l.rbacSubjectsByScope))
for name := range l.rbacSubjectsByScope {
names = append(names, name)
}
sort.Strings(names)

w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 2, ' ', 0)

if outputFormat == "wide" {
fmt.Fprintln(w, "SUBJECT\t SCOPE\t ROLE\t SOURCE")
} else {
fmt.Fprintln(w, "SUBJECT\t SCOPE\t ROLE")
}
func (l *lister) loadGkeIamPolicy(policy *cloudresourcemanager.Policy) {
for _, binding := range policy.Bindings {
if sr, ok := gkeIamRoles[binding.Role]; ok {
for _, member := range binding.Members {
s := strings.Split(member, ":")
memberKind := strings.Title(s[0])
memberName := s[1]
if l.filter == "" || strings.Contains(memberName, l.filter) {
rbacSubj, exist := l.rbacSubjectsByScope[memberName]
if !exist {
rbacSubj = rbacSubject{
Kind: memberKind,
RolesByScope: make(map[string][]simpleRole),
}
}

for _, subjectName := range names {
rbacSubject := l.rbacSubjectsByScope[subjectName]
for scope, simpleRoles := range rbacSubject.RolesByScope {
for _, simpleRole := range simpleRoles {
if outputFormat == "wide" {
fmt.Fprintf(w, "%s/%s \t %s\t %s/%s\t %s/%s\n", rbacSubject.Kind, subjectName, scope, simpleRole.Kind, simpleRole.Name, simpleRole.Source.Kind, simpleRole.Source.Name)
} else {
fmt.Fprintf(w, "%s \t %s\t %s/%s\n", subjectName, scope, simpleRole.Kind, simpleRole.Name)
rbacSubj.RolesByScope[gkeIamScope] = append(rbacSubj.RolesByScope[gkeIamScope], sr)
l.rbacSubjectsByScope[memberName] = rbacSubj
}
}
}
}
w.Flush()
}

func (rbacSubj *rbacSubject) addClusterRoleBinding(clusterRoleBinding *rbacv1.ClusterRoleBinding) {
simpleRole := simpleRole{
Name: clusterRoleBinding.RoleRef.Name,
Source: simpleRoleSource{Name: clusterRoleBinding.Name, Kind: "ClusterRoleBinding"},
}

simpleRole.Kind = clusterRoleBinding.RoleRef.Kind
scope := "cluster-wide"
rbacSubj.RolesByScope[scope] = append(rbacSubj.RolesByScope[scope], simpleRole)
}

func (rbacSubj *rbacSubject) addRoleBinding(roleBinding *rbacv1.RoleBinding) {
simpleRole := simpleRole{
Name: roleBinding.RoleRef.Name,
Source: simpleRoleSource{Name: roleBinding.Name, Kind: "RoleBinding"},
}

simpleRole.Kind = roleBinding.RoleRef.Kind
rbacSubj.RolesByScope[roleBinding.Namespace] = append(rbacSubj.RolesByScope[roleBinding.Namespace], simpleRole)
}

func (l *lister) loadGkeRoleBindings() error {
Expand All @@ -204,37 +192,14 @@ func (l *lister) loadGkeRoleBindings() error {
}

resource := l.gkeProjectName

ipr := &cloudresourcemanager.GetIamPolicyRequest{}

resp, err := crmService.Projects.GetIamPolicy(resource, ipr).Context(ctx).Do()
policy, err := crmService.Projects.GetIamPolicy(resource, ipr).Context(ctx).Do()
if err != nil {
return err
}

scope := "project-wide"

for _, binding := range resp.Bindings {
if sr, ok := gkeIamRoles[binding.Role]; ok {
for _, member := range binding.Members {
s := strings.Split(member, ":")
memberKind := strings.Title(s[0])
memberName := s[1]
if l.filter == "" || strings.Contains(memberName, l.filter) {
rbacSubj, exist := l.rbacSubjectsByScope[memberName]
if !exist {
rbacSubj = rbacSubject{
Kind: memberKind,
RolesByScope: make(map[string][]simpleRole),
}
}

rbacSubj.RolesByScope[scope] = append(rbacSubj.RolesByScope[scope], sr)
l.rbacSubjectsByScope[memberName] = rbacSubj
}
}
}
}
l.loadGkeIamPolicy(policy)

return nil
}
56 changes: 56 additions & 0 deletions lookup/rbac_subject.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2018 ReactiveOps
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lookup

import (
rbacv1 "k8s.io/api/rbac/v1"
)

type rbacSubject struct {
Kind string
RolesByScope map[string][]simpleRole
}

type simpleRole struct {
Kind string
Name string
Source simpleRoleSource
}

type simpleRoleSource struct {
Kind string
Name string
}

func (rbacSubj *rbacSubject) addRoleBinding(roleBinding *rbacv1.RoleBinding) {
simpleRole := simpleRole{
Name: roleBinding.RoleRef.Name,
Source: simpleRoleSource{Name: roleBinding.Name, Kind: "RoleBinding"},
}

simpleRole.Kind = roleBinding.RoleRef.Kind
rbacSubj.RolesByScope[roleBinding.Namespace] = append(rbacSubj.RolesByScope[roleBinding.Namespace], simpleRole)
}

func (rbacSubj *rbacSubject) addClusterRoleBinding(clusterRoleBinding *rbacv1.ClusterRoleBinding) {
simpleRole := simpleRole{
Name: clusterRoleBinding.RoleRef.Name,
Source: simpleRoleSource{Name: clusterRoleBinding.Name, Kind: "ClusterRoleBinding"},
}

simpleRole.Kind = clusterRoleBinding.RoleRef.Kind
scope := "cluster-wide"
rbacSubj.RolesByScope[scope] = append(rbacSubj.RolesByScope[scope], simpleRole)
}

0 comments on commit ba8db1f

Please sign in to comment.