Skip to content

Commit 1b64657

Browse files
authored
Merge pull request #254 from jetstack/ClusterRole_Binding
Cluster role binding
2 parents 1433408 + 38da52c commit 1b64657

File tree

4 files changed

+198
-106
lines changed

4 files changed

+198
-106
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ require (
2121
github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect
2222
github.com/kylelemons/godebug v1.1.0
2323
github.com/leodido/go-urn v1.2.0 // indirect
24+
github.com/maxatome/go-testdeep v1.9.2 // indirect
2425
github.com/pkg/errors v0.9.1
2526
github.com/pmylund/go-cache v2.1.0+incompatible
2627
github.com/sirupsen/logrus v1.7.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,8 @@ github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO
664664
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
665665
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
666666
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
667+
github.com/maxatome/go-testdeep v1.9.2 h1:5D7u/JkeG0A/HDTbZ/CFFmpYZPeWN+uGQ1IbsEHYlCo=
668+
github.com/maxatome/go-testdeep v1.9.2/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70=
667669
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
668670
github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
669671
github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=

pkg/permissions/generate.go

Lines changed: 81 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,114 @@ package permissions
22

33
import (
44
"fmt"
5-
"strings"
65

76
"github.com/jetstack/preflight/pkg/agent"
87
"github.com/jetstack/preflight/pkg/datagatherer/k8s"
98
rbac "k8s.io/api/rbac/v1"
109
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1110
)
1211

13-
func Generate(dataGatherers []agent.DataGatherer) string {
14-
var accumulator string = ""
15-
16-
for _, g := range dataGatherers {
17-
if g.Kind != "k8s-dynamic" {
18-
continue
19-
}
20-
21-
genericConfig := g.Config
22-
dyConfig := genericConfig.(*k8s.ConfigDynamic)
23-
24-
metaName := fmt.Sprint(dyConfig.GroupVersionResource.Resource)
25-
26-
accumulator = fmt.Sprintf(`%s
27-
apiVersion: rbac.authorization.k8s.io/v1
28-
kind: ClusterRole
29-
metadata:
30-
name: jetstack-secure-agent-%s-reader
31-
rules:
32-
- apiGroups: ["%s"]
33-
resources: ["%s"]
34-
verbs: ["get", "list", "watch"]
35-
---`, accumulator, metaName, dyConfig.GroupVersionResource.Group, dyConfig.GroupVersionResource.Resource)
36-
}
37-
38-
s := strings.TrimPrefix(accumulator, "\n")
39-
ss := strings.TrimSuffix(s, "---")
40-
return strings.TrimSuffix(ss, "\n")
12+
// AgentRBACManifests is a wrapper around the various RBAC structs needed to grant the agent fine-grained permissions as per its dg configs
13+
type AgentRBACManifests struct {
14+
// ClusterRoles is a list of roles for resources the agent will collect
15+
ClusterRoles []rbac.ClusterRole
16+
// ClusterRoleBindings is a list of crbs for resources which have no include/exclude ns configured
17+
ClusterRoleBindings []rbac.ClusterRoleBinding
18+
// RoleBindings is a list of namespaced bindings to grant permissions when include/exclude ns set
19+
RoleBindings []rbac.RoleBinding
4120
}
4221

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

46-
for _, g := range dataGatherer {
47-
if g.Kind != "k8s-dynamic" {
25+
func GenerateAgentRBACManifests(dataGatherers []agent.DataGatherer) AgentRBACManifests {
26+
// create a new AgentRBACManifest struct
27+
var AgentRBACManifests AgentRBACManifests
28+
29+
for _, dg := range dataGatherers {
30+
if dg.Kind != "k8s-dynamic" {
4831
continue
4932
}
5033

51-
genericConfig := g.Config
52-
dyConfig := genericConfig.(*k8s.ConfigDynamic)
53-
54-
metaName := dyConfig.GroupVersionResource.Resource
34+
dyConfig := dg.Config.(*k8s.ConfigDynamic)
35+
metadataName := fmt.Sprintf("%s-agent-%s-reader", agentNamespace, dyConfig.GroupVersionResource.Resource)
5536

56-
out = append(out, rbac.ClusterRole{
37+
AgentRBACManifests.ClusterRoles = append(AgentRBACManifests.ClusterRoles, rbac.ClusterRole{
5738
TypeMeta: metav1.TypeMeta{
5839
Kind: "ClusterRole",
5940
APIVersion: "rbac.authorization.k8s.io/v1",
6041
},
6142
ObjectMeta: metav1.ObjectMeta{
62-
Name: fmt.Sprintf("jetstack-secure-agent-%s-reader", metaName),
43+
Name: metadataName,
6344
},
6445
Rules: []rbac.PolicyRule{
6546
{
6647
Verbs: []string{"get", "list", "watch"},
6748
APIGroups: []string{dyConfig.GroupVersionResource.Group},
68-
Resources: []string{metaName},
49+
Resources: []string{dyConfig.GroupVersionResource.Resource},
6950
},
7051
},
7152
})
7253

54+
// if dyConfig.IncludeNamespaces has more than 0 items in it
55+
// then, for each namespace create a rbac.RoleBinding in that namespace
56+
if len(dyConfig.IncludeNamespaces) != 0 {
57+
for _, ns := range dyConfig.IncludeNamespaces {
58+
AgentRBACManifests.RoleBindings = append(AgentRBACManifests.RoleBindings, rbac.RoleBinding{
59+
TypeMeta: metav1.TypeMeta{
60+
Kind: "RoleBinding",
61+
APIVersion: "rbac.authorization.k8s.io/v1",
62+
},
63+
64+
ObjectMeta: metav1.ObjectMeta{
65+
Name: metadataName,
66+
Namespace: ns,
67+
},
68+
69+
Subjects: []rbac.Subject{
70+
{
71+
Kind: "ServiceAccount",
72+
Name: agentSubjectName,
73+
Namespace: agentNamespace,
74+
},
75+
},
76+
77+
RoleRef: rbac.RoleRef{
78+
Kind: "ClusterRole",
79+
Name: metadataName,
80+
APIGroup: "rbac.authorization.k8s.io",
81+
},
82+
})
83+
}
84+
} else {
85+
// only do this if the dg does not have IncludeNamespaces set
86+
AgentRBACManifests.ClusterRoleBindings = append(AgentRBACManifests.ClusterRoleBindings, rbac.ClusterRoleBinding{
87+
TypeMeta: metav1.TypeMeta{
88+
Kind: "ClusterRoleBinding",
89+
APIVersion: "rbac.authorization.k8s.io/v1",
90+
},
91+
92+
ObjectMeta: metav1.ObjectMeta{
93+
Name: metadataName,
94+
},
95+
96+
Subjects: []rbac.Subject{
97+
{
98+
Kind: "ServiceAccount",
99+
Name: agentSubjectName,
100+
Namespace: agentNamespace,
101+
},
102+
},
103+
104+
RoleRef: rbac.RoleRef{
105+
Kind: "ClusterRole",
106+
Name: metadataName,
107+
APIGroup: "rbac.authorization.k8s.io",
108+
},
109+
})
110+
}
111+
73112
}
74-
return out
113+
114+
return AgentRBACManifests
75115
}

pkg/permissions/generate_test.go

Lines changed: 114 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,22 @@ package permissions
33
import (
44
"testing"
55

6-
"github.com/d4l3k/messagediff"
76
"github.com/jetstack/preflight/pkg/agent"
87
"github.com/jetstack/preflight/pkg/datagatherer/k8s"
8+
"github.com/maxatome/go-testdeep/td"
99
rbac "k8s.io/api/rbac/v1"
1010
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1111
"k8s.io/apimachinery/pkg/runtime/schema"
1212
)
1313

14-
func TestGenerateRBAC(t *testing.T) {
15-
// Use these test cases to check if Generate function is correct
14+
func TestGenerateAgentRBACManifests(t *testing.T) {
1615
testCases := []struct {
17-
// expectedClusterRoles is the collection of ClusterRole
18-
expectedClusterRoles []rbac.ClusterRole
19-
dataGatherers []agent.DataGatherer
20-
description string
16+
description string
17+
dataGatherers []agent.DataGatherer
18+
expectedAgentRBACManifests AgentRBACManifests
2119
}{
2220
{
23-
description: "Generate RBAC struct for pods datagatherer",
21+
description: "Generate ClusterRole and ClusterRoleBinding for simple pod dg use case",
2422
dataGatherers: []agent.DataGatherer{
2523
{
2624
Name: "k8s/pods",
@@ -32,87 +30,138 @@ func TestGenerateRBAC(t *testing.T) {
3230
},
3331
},
3432
},
35-
{
36-
Name: "k8s/secrets",
37-
Kind: "k8s-dynamic",
38-
Config: &k8s.ConfigDynamic{
39-
GroupVersionResource: schema.GroupVersionResource{
40-
Version: "v1",
41-
Resource: "secrets",
33+
},
34+
expectedAgentRBACManifests: AgentRBACManifests{
35+
ClusterRoles: []rbac.ClusterRole{
36+
{
37+
TypeMeta: metav1.TypeMeta{
38+
Kind: "ClusterRole",
39+
APIVersion: "rbac.authorization.k8s.io/v1",
40+
},
41+
ObjectMeta: metav1.ObjectMeta{
42+
Name: "jetstack-secure-agent-pods-reader",
43+
},
44+
Rules: []rbac.PolicyRule{
45+
{
46+
Verbs: []string{"get", "list", "watch"},
47+
APIGroups: []string{""},
48+
Resources: []string{"pods"},
49+
},
50+
},
51+
},
52+
},
53+
ClusterRoleBindings: []rbac.ClusterRoleBinding{
54+
{
55+
TypeMeta: metav1.TypeMeta{
56+
Kind: "ClusterRoleBinding",
57+
APIVersion: "rbac.authorization.k8s.io/v1",
58+
},
59+
ObjectMeta: metav1.ObjectMeta{
60+
Name: "jetstack-secure-agent-pods-reader",
61+
},
62+
Subjects: []rbac.Subject{
63+
{
64+
Kind: "ServiceAccount",
65+
Name: "agent",
66+
Namespace: "jetstack-secure",
67+
},
68+
},
69+
RoleRef: rbac.RoleRef{
70+
Kind: "ClusterRole",
71+
Name: "jetstack-secure-agent-pods-reader",
72+
APIGroup: "rbac.authorization.k8s.io",
4273
},
4374
},
4475
},
76+
},
77+
},
78+
{
79+
description: "Generate RBAC config for simple pod dg use case where only two namespace are included",
80+
dataGatherers: []agent.DataGatherer{
4581
{
46-
Name: "k8s/awspcaissuer",
82+
Name: "k8s/pods",
4783
Kind: "k8s-dynamic",
4884
Config: &k8s.ConfigDynamic{
4985
GroupVersionResource: schema.GroupVersionResource{
50-
Group: "awspca.cert-manager.io",
5186
Version: "v1",
52-
Resource: "awspcaissuers",
87+
Resource: "pods",
5388
},
89+
IncludeNamespaces: []string{"example", "foobar"},
5490
},
5591
},
5692
},
57-
expectedClusterRoles: []rbac.ClusterRole{
58-
{
59-
TypeMeta: metav1.TypeMeta{
60-
Kind: "ClusterRole",
61-
APIVersion: "rbac.authorization.k8s.io/v1",
62-
},
63-
ObjectMeta: metav1.ObjectMeta{
64-
Name: "jetstack-secure-agent-pods-reader",
65-
},
66-
Rules: []rbac.PolicyRule{
67-
{
68-
Verbs: []string{"get", "list", "watch"},
69-
APIGroups: []string{""},
70-
Resources: []string{"pods"},
93+
expectedAgentRBACManifests: AgentRBACManifests{
94+
ClusterRoles: []rbac.ClusterRole{
95+
{
96+
TypeMeta: metav1.TypeMeta{
97+
Kind: "ClusterRole",
98+
APIVersion: "rbac.authorization.k8s.io/v1",
7199
},
72-
},
73-
},
74-
{
75-
TypeMeta: metav1.TypeMeta{
76-
Kind: "ClusterRole",
77-
APIVersion: "rbac.authorization.k8s.io/v1",
78-
},
79-
ObjectMeta: metav1.ObjectMeta{
80-
Name: "jetstack-secure-agent-secrets-reader",
81-
},
82-
Rules: []rbac.PolicyRule{
83-
{
84-
Verbs: []string{"get", "list", "watch"},
85-
APIGroups: []string{""},
86-
Resources: []string{"secrets"},
100+
ObjectMeta: metav1.ObjectMeta{
101+
Name: "jetstack-secure-agent-pods-reader",
102+
},
103+
Rules: []rbac.PolicyRule{
104+
{
105+
Verbs: []string{"get", "list", "watch"},
106+
APIGroups: []string{""},
107+
Resources: []string{"pods"},
108+
},
87109
},
88110
},
89111
},
90-
{
91-
TypeMeta: metav1.TypeMeta{
92-
Kind: "ClusterRole",
93-
APIVersion: "rbac.authorization.k8s.io/v1",
94-
},
95-
ObjectMeta: metav1.ObjectMeta{
96-
Name: "jetstack-secure-agent-awspcaissuers-reader",
112+
RoleBindings: []rbac.RoleBinding{
113+
{
114+
TypeMeta: metav1.TypeMeta{
115+
Kind: "RoleBinding",
116+
APIVersion: "rbac.authorization.k8s.io/v1",
117+
},
118+
ObjectMeta: metav1.ObjectMeta{
119+
Name: "jetstack-secure-agent-pods-reader",
120+
Namespace: "example",
121+
},
122+
Subjects: []rbac.Subject{
123+
{
124+
Kind: "ServiceAccount",
125+
Name: "agent",
126+
Namespace: "jetstack-secure",
127+
},
128+
},
129+
RoleRef: rbac.RoleRef{
130+
Kind: "ClusterRole",
131+
Name: "jetstack-secure-agent-pods-reader",
132+
APIGroup: "rbac.authorization.k8s.io",
133+
},
97134
},
98-
Rules: []rbac.PolicyRule{
99-
{
100-
Verbs: []string{"get", "list", "watch"},
101-
APIGroups: []string{"awspca.cert-manager.io"},
102-
Resources: []string{"awspcaissuers"},
135+
{
136+
TypeMeta: metav1.TypeMeta{
137+
Kind: "RoleBinding",
138+
APIVersion: "rbac.authorization.k8s.io/v1",
139+
},
140+
ObjectMeta: metav1.ObjectMeta{
141+
Name: "jetstack-secure-agent-pods-reader",
142+
Namespace: "foobar",
143+
},
144+
Subjects: []rbac.Subject{
145+
{
146+
Kind: "ServiceAccount",
147+
Name: "agent",
148+
Namespace: "jetstack-secure",
149+
},
150+
},
151+
RoleRef: rbac.RoleRef{
152+
Kind: "ClusterRole",
153+
Name: "jetstack-secure-agent-pods-reader",
154+
APIGroup: "rbac.authorization.k8s.io",
103155
},
104156
},
105157
},
106158
},
107159
},
108-
// Try adding more test cases
109160
}
110161

111162
for _, input := range testCases {
112-
got := GenerateRoles(input.dataGatherers)
113-
if diff, equal := messagediff.PrettyDiff(input.expectedClusterRoles, got); !equal {
114-
t.Errorf("%s:\n%s", input.description, diff)
115-
t.Fatalf("unexpected difference in RBAC cluster role: \ngot \n%v\nwant\n%v", got, input.expectedClusterRoles)
116-
}
163+
got := GenerateAgentRBACManifests(input.dataGatherers)
164+
165+
td.Cmp(t, input.expectedAgentRBACManifests, got)
117166
}
118167
}

0 commit comments

Comments
 (0)