Skip to content

Commit

Permalink
Merge pull request #254 from jetstack/ClusterRole_Binding
Browse files Browse the repository at this point in the history
Cluster role binding
  • Loading branch information
Weeblin authored Aug 12, 2021
2 parents 1433408 + 38da52c commit 1b64657
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 106 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ require (
github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect
github.com/kylelemons/godebug v1.1.0
github.com/leodido/go-urn v1.2.0 // indirect
github.com/maxatome/go-testdeep v1.9.2 // indirect
github.com/pkg/errors v0.9.1
github.com/pmylund/go-cache v2.1.0+incompatible
github.com/sirupsen/logrus v1.7.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,8 @@ github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxatome/go-testdeep v1.9.2 h1:5D7u/JkeG0A/HDTbZ/CFFmpYZPeWN+uGQ1IbsEHYlCo=
github.com/maxatome/go-testdeep v1.9.2/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=
Expand Down
122 changes: 81 additions & 41 deletions pkg/permissions/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,74 +2,114 @@ package permissions

import (
"fmt"
"strings"

"github.com/jetstack/preflight/pkg/agent"
"github.com/jetstack/preflight/pkg/datagatherer/k8s"
rbac "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func Generate(dataGatherers []agent.DataGatherer) string {
var accumulator string = ""

for _, g := range dataGatherers {
if g.Kind != "k8s-dynamic" {
continue
}

genericConfig := g.Config
dyConfig := genericConfig.(*k8s.ConfigDynamic)

metaName := fmt.Sprint(dyConfig.GroupVersionResource.Resource)

accumulator = fmt.Sprintf(`%s
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: jetstack-secure-agent-%s-reader
rules:
- apiGroups: ["%s"]
resources: ["%s"]
verbs: ["get", "list", "watch"]
---`, accumulator, metaName, dyConfig.GroupVersionResource.Group, dyConfig.GroupVersionResource.Resource)
}

s := strings.TrimPrefix(accumulator, "\n")
ss := strings.TrimSuffix(s, "---")
return strings.TrimSuffix(ss, "\n")
// AgentRBACManifests is a wrapper around the various RBAC structs needed to grant the agent fine-grained permissions as per its dg configs
type AgentRBACManifests struct {
// ClusterRoles is a list of roles for resources the agent will collect
ClusterRoles []rbac.ClusterRole
// ClusterRoleBindings is a list of crbs for resources which have no include/exclude ns configured
ClusterRoleBindings []rbac.ClusterRoleBinding
// RoleBindings is a list of namespaced bindings to grant permissions when include/exclude ns set
RoleBindings []rbac.RoleBinding
}

func GenerateRoles(dataGatherer []agent.DataGatherer) []rbac.ClusterRole {
out := []rbac.ClusterRole{}
const agentNamespace = "jetstack-secure"
const agentSubjectName = "agent"

for _, g := range dataGatherer {
if g.Kind != "k8s-dynamic" {
func GenerateAgentRBACManifests(dataGatherers []agent.DataGatherer) AgentRBACManifests {
// create a new AgentRBACManifest struct
var AgentRBACManifests AgentRBACManifests

for _, dg := range dataGatherers {
if dg.Kind != "k8s-dynamic" {
continue
}

genericConfig := g.Config
dyConfig := genericConfig.(*k8s.ConfigDynamic)

metaName := dyConfig.GroupVersionResource.Resource
dyConfig := dg.Config.(*k8s.ConfigDynamic)
metadataName := fmt.Sprintf("%s-agent-%s-reader", agentNamespace, dyConfig.GroupVersionResource.Resource)

out = append(out, rbac.ClusterRole{
AgentRBACManifests.ClusterRoles = append(AgentRBACManifests.ClusterRoles, rbac.ClusterRole{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRole",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("jetstack-secure-agent-%s-reader", metaName),
Name: metadataName,
},
Rules: []rbac.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{dyConfig.GroupVersionResource.Group},
Resources: []string{metaName},
Resources: []string{dyConfig.GroupVersionResource.Resource},
},
},
})

// if dyConfig.IncludeNamespaces has more than 0 items in it
// then, for each namespace create a rbac.RoleBinding in that namespace
if len(dyConfig.IncludeNamespaces) != 0 {
for _, ns := range dyConfig.IncludeNamespaces {
AgentRBACManifests.RoleBindings = append(AgentRBACManifests.RoleBindings, rbac.RoleBinding{
TypeMeta: metav1.TypeMeta{
Kind: "RoleBinding",
APIVersion: "rbac.authorization.k8s.io/v1",
},

ObjectMeta: metav1.ObjectMeta{
Name: metadataName,
Namespace: ns,
},

Subjects: []rbac.Subject{
{
Kind: "ServiceAccount",
Name: agentSubjectName,
Namespace: agentNamespace,
},
},

RoleRef: rbac.RoleRef{
Kind: "ClusterRole",
Name: metadataName,
APIGroup: "rbac.authorization.k8s.io",
},
})
}
} else {
// only do this if the dg does not have IncludeNamespaces set
AgentRBACManifests.ClusterRoleBindings = append(AgentRBACManifests.ClusterRoleBindings, rbac.ClusterRoleBinding{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRoleBinding",
APIVersion: "rbac.authorization.k8s.io/v1",
},

ObjectMeta: metav1.ObjectMeta{
Name: metadataName,
},

Subjects: []rbac.Subject{
{
Kind: "ServiceAccount",
Name: agentSubjectName,
Namespace: agentNamespace,
},
},

RoleRef: rbac.RoleRef{
Kind: "ClusterRole",
Name: metadataName,
APIGroup: "rbac.authorization.k8s.io",
},
})
}

}
return out

return AgentRBACManifests
}
179 changes: 114 additions & 65 deletions pkg/permissions/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,22 @@ package permissions
import (
"testing"

"github.com/d4l3k/messagediff"
"github.com/jetstack/preflight/pkg/agent"
"github.com/jetstack/preflight/pkg/datagatherer/k8s"
"github.com/maxatome/go-testdeep/td"
rbac "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func TestGenerateRBAC(t *testing.T) {
// Use these test cases to check if Generate function is correct
func TestGenerateAgentRBACManifests(t *testing.T) {
testCases := []struct {
// expectedClusterRoles is the collection of ClusterRole
expectedClusterRoles []rbac.ClusterRole
dataGatherers []agent.DataGatherer
description string
description string
dataGatherers []agent.DataGatherer
expectedAgentRBACManifests AgentRBACManifests
}{
{
description: "Generate RBAC struct for pods datagatherer",
description: "Generate ClusterRole and ClusterRoleBinding for simple pod dg use case",
dataGatherers: []agent.DataGatherer{
{
Name: "k8s/pods",
Expand All @@ -32,87 +30,138 @@ func TestGenerateRBAC(t *testing.T) {
},
},
},
{
Name: "k8s/secrets",
Kind: "k8s-dynamic",
Config: &k8s.ConfigDynamic{
GroupVersionResource: schema.GroupVersionResource{
Version: "v1",
Resource: "secrets",
},
expectedAgentRBACManifests: AgentRBACManifests{
ClusterRoles: []rbac.ClusterRole{
{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRole",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "jetstack-secure-agent-pods-reader",
},
Rules: []rbac.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{""},
Resources: []string{"pods"},
},
},
},
},
ClusterRoleBindings: []rbac.ClusterRoleBinding{
{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRoleBinding",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "jetstack-secure-agent-pods-reader",
},
Subjects: []rbac.Subject{
{
Kind: "ServiceAccount",
Name: "agent",
Namespace: "jetstack-secure",
},
},
RoleRef: rbac.RoleRef{
Kind: "ClusterRole",
Name: "jetstack-secure-agent-pods-reader",
APIGroup: "rbac.authorization.k8s.io",
},
},
},
},
},
{
description: "Generate RBAC config for simple pod dg use case where only two namespace are included",
dataGatherers: []agent.DataGatherer{
{
Name: "k8s/awspcaissuer",
Name: "k8s/pods",
Kind: "k8s-dynamic",
Config: &k8s.ConfigDynamic{
GroupVersionResource: schema.GroupVersionResource{
Group: "awspca.cert-manager.io",
Version: "v1",
Resource: "awspcaissuers",
Resource: "pods",
},
IncludeNamespaces: []string{"example", "foobar"},
},
},
},
expectedClusterRoles: []rbac.ClusterRole{
{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRole",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "jetstack-secure-agent-pods-reader",
},
Rules: []rbac.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{""},
Resources: []string{"pods"},
expectedAgentRBACManifests: AgentRBACManifests{
ClusterRoles: []rbac.ClusterRole{
{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRole",
APIVersion: "rbac.authorization.k8s.io/v1",
},
},
},
{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRole",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "jetstack-secure-agent-secrets-reader",
},
Rules: []rbac.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{""},
Resources: []string{"secrets"},
ObjectMeta: metav1.ObjectMeta{
Name: "jetstack-secure-agent-pods-reader",
},
Rules: []rbac.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{""},
Resources: []string{"pods"},
},
},
},
},
{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRole",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "jetstack-secure-agent-awspcaissuers-reader",
RoleBindings: []rbac.RoleBinding{
{
TypeMeta: metav1.TypeMeta{
Kind: "RoleBinding",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "jetstack-secure-agent-pods-reader",
Namespace: "example",
},
Subjects: []rbac.Subject{
{
Kind: "ServiceAccount",
Name: "agent",
Namespace: "jetstack-secure",
},
},
RoleRef: rbac.RoleRef{
Kind: "ClusterRole",
Name: "jetstack-secure-agent-pods-reader",
APIGroup: "rbac.authorization.k8s.io",
},
},
Rules: []rbac.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{"awspca.cert-manager.io"},
Resources: []string{"awspcaissuers"},
{
TypeMeta: metav1.TypeMeta{
Kind: "RoleBinding",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "jetstack-secure-agent-pods-reader",
Namespace: "foobar",
},
Subjects: []rbac.Subject{
{
Kind: "ServiceAccount",
Name: "agent",
Namespace: "jetstack-secure",
},
},
RoleRef: rbac.RoleRef{
Kind: "ClusterRole",
Name: "jetstack-secure-agent-pods-reader",
APIGroup: "rbac.authorization.k8s.io",
},
},
},
},
},
// Try adding more test cases
}

for _, input := range testCases {
got := GenerateRoles(input.dataGatherers)
if diff, equal := messagediff.PrettyDiff(input.expectedClusterRoles, got); !equal {
t.Errorf("%s:\n%s", input.description, diff)
t.Fatalf("unexpected difference in RBAC cluster role: \ngot \n%v\nwant\n%v", got, input.expectedClusterRoles)
}
got := GenerateAgentRBACManifests(input.dataGatherers)

td.Cmp(t, input.expectedAgentRBACManifests, got)
}
}

0 comments on commit 1b64657

Please sign in to comment.