Skip to content

Commit d0d0b9f

Browse files
authored
Merge pull request #258 from jetstack/250_Command
Add the rbac generation command `go run main.go agent rbac -c agent.yaml` to CLI for easy generation of rbac manifests. Related #250
2 parents 1b64657 + 9858f68 commit d0d0b9f

File tree

4 files changed

+271
-2
lines changed

4 files changed

+271
-2
lines changed

cmd/agent.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package cmd
22

33
import (
44
"fmt"
5+
"io/ioutil"
6+
"log"
57
"time"
68

79
"github.com/jetstack/preflight/pkg/agent"
10+
"github.com/jetstack/preflight/pkg/permissions"
811
"github.com/spf13/cobra"
912
)
1013

@@ -27,9 +30,30 @@ var agentInfoCmd = &cobra.Command{
2730
},
2831
}
2932

33+
var agentRBACCmd = &cobra.Command{
34+
Use: "rbac",
35+
Short: "print the agent's minimal RBAC manifest",
36+
Long: `Print RBAC string by reading GVRs`,
37+
Run: func(cmd *cobra.Command, args []string) {
38+
39+
b, err := ioutil.ReadFile(agent.ConfigFilePath)
40+
if err != nil {
41+
log.Fatalf("Failed to read config file: %s", err)
42+
}
43+
config, err := agent.ParseConfig(b)
44+
if err != nil {
45+
log.Fatalf("Failed to parse config file: %s", err)
46+
}
47+
48+
out := permissions.GenerateFullManifest(config.DataGatherers)
49+
fmt.Print(out)
50+
},
51+
}
52+
3053
func init() {
3154
rootCmd.AddCommand(agentCmd)
3255
agentCmd.AddCommand(agentInfoCmd)
56+
agentCmd.AddCommand(agentRBACCmd)
3357
agentCmd.PersistentFlags().StringVarP(
3458
&agent.ConfigFilePath,
3559
"agent-config-file",

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/aws/aws-sdk-go v1.36.19
1313
github.com/cenkalti/backoff v2.2.1+incompatible
1414
github.com/d4l3k/messagediff v1.2.1
15-
github.com/fatih/color v1.12.0 // indirect
15+
github.com/fatih/color v1.12.0
1616
github.com/go-playground/universal-translator v0.17.0 // indirect
1717
github.com/hashicorp/go-multierror v1.1.0
1818
github.com/jetstack/version-checker v0.2.2-0.20201118163251-4bab9ef088ef
@@ -21,7 +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
24+
github.com/maxatome/go-testdeep v1.9.2
2525
github.com/pkg/errors v0.9.1
2626
github.com/pmylund/go-cache v2.1.0+incompatible
2727
github.com/sirupsen/logrus v1.7.0
@@ -39,6 +39,7 @@ require (
3939
k8s.io/apimachinery v0.20.1
4040
k8s.io/client-go v11.0.0+incompatible
4141
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
42+
sigs.k8s.io/yaml v1.2.0
4243
)
4344

4445
replace k8s.io/client-go => k8s.io/client-go v0.20.1

pkg/permissions/generate.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package permissions
22

33
import (
44
"fmt"
5+
"strings"
56

67
"github.com/jetstack/preflight/pkg/agent"
78
"github.com/jetstack/preflight/pkg/datagatherer/k8s"
89
rbac "k8s.io/api/rbac/v1"
910
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"sigs.k8s.io/yaml"
1012
)
1113

1214
// AgentRBACManifests is a wrapper around the various RBAC structs needed to grant the agent fine-grained permissions as per its dg configs
@@ -113,3 +115,64 @@ func GenerateAgentRBACManifests(dataGatherers []agent.DataGatherer) AgentRBACMan
113115

114116
return AgentRBACManifests
115117
}
118+
119+
func createClusterRoleString(clusterRoles []rbac.ClusterRole) string {
120+
var builder strings.Builder
121+
for _, cb := range clusterRoles {
122+
data, err := yaml.Marshal(cb)
123+
if err != nil {
124+
fmt.Print("Cluster Role fails to marshal")
125+
}
126+
127+
builder.WriteString("\n")
128+
builder.Write(data)
129+
builder.WriteString("---")
130+
}
131+
132+
return builder.String()
133+
}
134+
func createRoleBindingString(roleBindings []rbac.RoleBinding) string {
135+
var builder strings.Builder
136+
for _, cb := range roleBindings {
137+
data, err := yaml.Marshal(cb)
138+
if err != nil {
139+
fmt.Print("Role Binding fails to marshal")
140+
}
141+
142+
builder.WriteString("\n")
143+
builder.Write(data)
144+
builder.WriteString("---")
145+
}
146+
147+
return builder.String()
148+
}
149+
func createClusterRoleBindingString(clusterRoleBindings []rbac.ClusterRoleBinding) string {
150+
var builder strings.Builder
151+
for _, cb := range clusterRoleBindings {
152+
data, err := yaml.Marshal(cb)
153+
if err != nil {
154+
fmt.Print("Cluster Role Binding fails to marshal")
155+
}
156+
157+
builder.WriteString("\n")
158+
builder.Write(data)
159+
builder.WriteString("---")
160+
}
161+
162+
return builder.String()
163+
}
164+
165+
func GenerateFullManifest(dataGatherers []agent.DataGatherer) string {
166+
agentRBACManifestsStruct := GenerateAgentRBACManifests(dataGatherers)
167+
agentCLR := createClusterRoleString(agentRBACManifestsStruct.ClusterRoles)
168+
agentCLRB := createClusterRoleBindingString(agentRBACManifestsStruct.ClusterRoleBindings)
169+
agentRB := createRoleBindingString(agentRBACManifestsStruct.RoleBindings)
170+
171+
out := fmt.Sprintf(`%s%s%s`, agentCLR, agentCLRB, agentRB)
172+
out = strings.TrimPrefix(out, "\n")
173+
out = strings.TrimSpace(out)
174+
out = strings.ReplaceAll(out, "\n creationTimestamp: null", "")
175+
176+
return out
177+
178+
}

pkg/permissions/generate_test.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,187 @@ import (
1111
"k8s.io/apimachinery/pkg/runtime/schema"
1212
)
1313

14+
func TestGenerateAgentRBACManifestsString(t *testing.T) {
15+
testCases := []struct {
16+
description string
17+
dataGatherers []agent.DataGatherer
18+
expectedRBACManifests string
19+
}{
20+
{
21+
description: "Generate ClusterRole and ClusterRoleBinding for simple pod dg use case",
22+
dataGatherers: []agent.DataGatherer{
23+
{
24+
Name: "k8s/pods",
25+
Kind: "k8s-dynamic",
26+
Config: &k8s.ConfigDynamic{
27+
GroupVersionResource: schema.GroupVersionResource{
28+
Version: "v1",
29+
Resource: "pods",
30+
},
31+
},
32+
},
33+
},
34+
expectedRBACManifests: `apiVersion: rbac.authorization.k8s.io/v1
35+
kind: ClusterRole
36+
metadata:
37+
name: jetstack-secure-agent-pods-reader
38+
rules:
39+
- apiGroups:
40+
- ""
41+
resources:
42+
- pods
43+
verbs:
44+
- get
45+
- list
46+
- watch
47+
---
48+
apiVersion: rbac.authorization.k8s.io/v1
49+
kind: ClusterRoleBinding
50+
metadata:
51+
name: jetstack-secure-agent-pods-reader
52+
roleRef:
53+
apiGroup: rbac.authorization.k8s.io
54+
kind: ClusterRole
55+
name: jetstack-secure-agent-pods-reader
56+
subjects:
57+
- kind: ServiceAccount
58+
name: agent
59+
namespace: jetstack-secure
60+
---`,
61+
},
62+
{
63+
description: "Generate ClusterRole and RoleBinding for simple pod dg with include namespace \"foobar\"",
64+
dataGatherers: []agent.DataGatherer{
65+
{
66+
Name: "k8s/pods",
67+
Kind: "k8s-dynamic",
68+
Config: &k8s.ConfigDynamic{
69+
IncludeNamespaces: []string{"foobar"},
70+
GroupVersionResource: schema.GroupVersionResource{
71+
Version: "v1",
72+
Resource: "pods",
73+
},
74+
},
75+
},
76+
},
77+
expectedRBACManifests: `apiVersion: rbac.authorization.k8s.io/v1
78+
kind: ClusterRole
79+
metadata:
80+
name: jetstack-secure-agent-pods-reader
81+
rules:
82+
- apiGroups:
83+
- ""
84+
resources:
85+
- pods
86+
verbs:
87+
- get
88+
- list
89+
- watch
90+
---
91+
apiVersion: rbac.authorization.k8s.io/v1
92+
kind: RoleBinding
93+
metadata:
94+
name: jetstack-secure-agent-pods-reader
95+
namespace: foobar
96+
roleRef:
97+
apiGroup: rbac.authorization.k8s.io
98+
kind: ClusterRole
99+
name: jetstack-secure-agent-pods-reader
100+
subjects:
101+
- kind: ServiceAccount
102+
name: agent
103+
namespace: jetstack-secure
104+
---`,
105+
},
106+
{
107+
description: "Generate multiple ClusterRoles and ClusterRoleBindings for simple pod and nodes dg use case",
108+
dataGatherers: []agent.DataGatherer{
109+
{
110+
Name: "k8s/pods",
111+
Kind: "k8s-dynamic",
112+
Config: &k8s.ConfigDynamic{
113+
GroupVersionResource: schema.GroupVersionResource{
114+
Version: "v1",
115+
Resource: "pods",
116+
},
117+
},
118+
},
119+
{
120+
Name: "k8s/nodes",
121+
Kind: "k8s-dynamic",
122+
Config: &k8s.ConfigDynamic{
123+
GroupVersionResource: schema.GroupVersionResource{
124+
Version: "v1",
125+
Resource: "nodes",
126+
},
127+
},
128+
},
129+
},
130+
expectedRBACManifests: `apiVersion: rbac.authorization.k8s.io/v1
131+
kind: ClusterRole
132+
metadata:
133+
name: jetstack-secure-agent-pods-reader
134+
rules:
135+
- apiGroups:
136+
- ""
137+
resources:
138+
- pods
139+
verbs:
140+
- get
141+
- list
142+
- watch
143+
---
144+
apiVersion: rbac.authorization.k8s.io/v1
145+
kind: ClusterRole
146+
metadata:
147+
name: jetstack-secure-agent-nodes-reader
148+
rules:
149+
- apiGroups:
150+
- ""
151+
resources:
152+
- nodes
153+
verbs:
154+
- get
155+
- list
156+
- watch
157+
---
158+
apiVersion: rbac.authorization.k8s.io/v1
159+
kind: ClusterRoleBinding
160+
metadata:
161+
name: jetstack-secure-agent-pods-reader
162+
roleRef:
163+
apiGroup: rbac.authorization.k8s.io
164+
kind: ClusterRole
165+
name: jetstack-secure-agent-pods-reader
166+
subjects:
167+
- kind: ServiceAccount
168+
name: agent
169+
namespace: jetstack-secure
170+
---
171+
apiVersion: rbac.authorization.k8s.io/v1
172+
kind: ClusterRoleBinding
173+
metadata:
174+
name: jetstack-secure-agent-nodes-reader
175+
roleRef:
176+
apiGroup: rbac.authorization.k8s.io
177+
kind: ClusterRole
178+
name: jetstack-secure-agent-nodes-reader
179+
subjects:
180+
- kind: ServiceAccount
181+
name: agent
182+
namespace: jetstack-secure
183+
---`,
184+
},
185+
}
186+
187+
for _, input := range testCases {
188+
got := GenerateFullManifest(input.dataGatherers)
189+
if input.expectedRBACManifests != got {
190+
t.Errorf("value mismatch, \n**********expected:******************************\n%s\n**********got:******************************\n%s", input.expectedRBACManifests, got)
191+
}
192+
}
193+
}
194+
14195
func TestGenerateAgentRBACManifests(t *testing.T) {
15196
testCases := []struct {
16197
description string

0 commit comments

Comments
 (0)