1
16
17 package bootstrappolicy_test
18
19 import (
20 "os"
21 "path/filepath"
22 "reflect"
23 "testing"
24
25 "github.com/google/go-cmp/cmp"
26 "sigs.k8s.io/yaml"
27
28 v1 "k8s.io/api/core/v1"
29 rbacv1 "k8s.io/api/rbac/v1"
30 "k8s.io/apimachinery/pkg/api/meta"
31 "k8s.io/apimachinery/pkg/runtime"
32 "k8s.io/apimachinery/pkg/util/sets"
33 "k8s.io/component-helpers/auth/rbac/validation"
34 "k8s.io/kubernetes/pkg/api/legacyscheme"
35 api "k8s.io/kubernetes/pkg/apis/core"
36 _ "k8s.io/kubernetes/pkg/apis/core/install"
37 _ "k8s.io/kubernetes/pkg/apis/rbac/install"
38 rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
39 "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
40 )
41
42
43
44 type semanticRoles struct {
45 admin *rbacv1.ClusterRole
46 edit *rbacv1.ClusterRole
47 view *rbacv1.ClusterRole
48 }
49
50 func getSemanticRoles(roles []rbacv1.ClusterRole) semanticRoles {
51 ret := semanticRoles{}
52 for i := range roles {
53 role := roles[i]
54 switch role.Name {
55 case "system:aggregate-to-admin":
56 ret.admin = &role
57 case "system:aggregate-to-edit":
58 ret.edit = &role
59 case "system:aggregate-to-view":
60 ret.view = &role
61 }
62 }
63 return ret
64 }
65
66
67
68 var viewEscalatingNamespaceResources = []rbacv1.PolicyRule{
69 rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/attach").RuleOrDie(),
70 rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/proxy").RuleOrDie(),
71 rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/exec").RuleOrDie(),
72 rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("pods/portforward").RuleOrDie(),
73 rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("secrets").RuleOrDie(),
74 rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("").Resources("services/proxy").RuleOrDie(),
75 }
76
77
78
79 var ungettableResources = []rbacv1.PolicyRule{
80 rbacv1helpers.NewRule(bootstrappolicy.Read...).Groups("apps", "extensions").Resources("deployments/rollback").RuleOrDie(),
81 }
82
83 func TestEditViewRelationship(t *testing.T) {
84 readVerbs := sets.NewString(bootstrappolicy.Read...)
85 semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
86
87
88 for i := range semanticRoles.edit.Rules {
89 rule := semanticRoles.edit.Rules[i]
90 remainingVerbs := []string{}
91 for _, verb := range rule.Verbs {
92 if readVerbs.Has(verb) {
93 remainingVerbs = append(remainingVerbs, verb)
94 }
95 }
96 rule.Verbs = remainingVerbs
97 semanticRoles.edit.Rules[i] = rule
98 }
99
100
101 for _, rule := range viewEscalatingNamespaceResources {
102 if covers, _ := validation.Covers(semanticRoles.view.Rules, []rbacv1.PolicyRule{rule}); covers {
103 t.Errorf("view has extra powers: %#v", rule)
104 }
105 }
106 semanticRoles.view.Rules = append(semanticRoles.view.Rules, viewEscalatingNamespaceResources...)
107
108
109 for _, rule := range ungettableResources {
110 if covers, _ := validation.Covers(semanticRoles.view.Rules, []rbacv1.PolicyRule{rule}); covers {
111 t.Errorf("view has ungettable resource: %#v", rule)
112 }
113 }
114 semanticRoles.view.Rules = append(semanticRoles.view.Rules, ungettableResources...)
115 }
116
117 func TestBootstrapNamespaceRoles(t *testing.T) {
118 list := &api.List{}
119 names := sets.NewString()
120 roles := map[string]runtime.Object{}
121
122 namespaceRoles := bootstrappolicy.NamespaceRoles()
123 for _, namespace := range sets.StringKeySet(namespaceRoles).List() {
124 bootstrapRoles := namespaceRoles[namespace]
125 for i := range bootstrapRoles {
126 role := bootstrapRoles[i]
127 names.Insert(role.Name)
128 roles[role.Name] = &role
129 }
130
131 for _, name := range names.List() {
132 list.Items = append(list.Items, roles[name])
133 }
134 }
135
136 testObjects(t, list, "namespace-roles.yaml")
137 }
138
139 func TestBootstrapNamespaceRoleBindings(t *testing.T) {
140 list := &api.List{}
141 names := sets.NewString()
142 roleBindings := map[string]runtime.Object{}
143
144 namespaceRoleBindings := bootstrappolicy.NamespaceRoleBindings()
145 for _, namespace := range sets.StringKeySet(namespaceRoleBindings).List() {
146 bootstrapRoleBindings := namespaceRoleBindings[namespace]
147 for i := range bootstrapRoleBindings {
148 roleBinding := bootstrapRoleBindings[i]
149 names.Insert(roleBinding.Name)
150 roleBindings[roleBinding.Name] = &roleBinding
151 }
152
153 for _, name := range names.List() {
154 list.Items = append(list.Items, roleBindings[name])
155 }
156 }
157
158 testObjects(t, list, "namespace-role-bindings.yaml")
159 }
160
161 func TestBootstrapClusterRoles(t *testing.T) {
162 list := &api.List{}
163 names := sets.NewString()
164 roles := map[string]runtime.Object{}
165 bootstrapRoles := bootstrappolicy.ClusterRoles()
166 for i := range bootstrapRoles {
167 role := bootstrapRoles[i]
168 names.Insert(role.Name)
169 roles[role.Name] = &role
170 }
171 for _, name := range names.List() {
172 list.Items = append(list.Items, roles[name])
173 }
174 testObjects(t, list, "cluster-roles.yaml")
175 }
176
177 func TestBootstrapClusterRoleBindings(t *testing.T) {
178 list := &api.List{}
179 names := sets.NewString()
180 roleBindings := map[string]runtime.Object{}
181 bootstrapRoleBindings := bootstrappolicy.ClusterRoleBindings()
182 for i := range bootstrapRoleBindings {
183 role := bootstrapRoleBindings[i]
184 names.Insert(role.Name)
185 roleBindings[role.Name] = &role
186 }
187 for _, name := range names.List() {
188 list.Items = append(list.Items, roleBindings[name])
189 }
190 testObjects(t, list, "cluster-role-bindings.yaml")
191 }
192
193 func TestBootstrapControllerRoles(t *testing.T) {
194 list := &api.List{}
195 names := sets.NewString()
196 roles := map[string]runtime.Object{}
197 bootstrapRoles := bootstrappolicy.ControllerRoles()
198 for i := range bootstrapRoles {
199 role := bootstrapRoles[i]
200 names.Insert(role.Name)
201 roles[role.Name] = &role
202 }
203 for _, name := range names.List() {
204 list.Items = append(list.Items, roles[name])
205 }
206 testObjects(t, list, "controller-roles.yaml")
207 }
208
209 func TestBootstrapControllerRoleBindings(t *testing.T) {
210 list := &api.List{}
211 names := sets.NewString()
212 roleBindings := map[string]runtime.Object{}
213 bootstrapRoleBindings := bootstrappolicy.ControllerRoleBindings()
214 for i := range bootstrapRoleBindings {
215 roleBinding := bootstrapRoleBindings[i]
216 names.Insert(roleBinding.Name)
217 roleBindings[roleBinding.Name] = &roleBinding
218 }
219 for _, name := range names.List() {
220 list.Items = append(list.Items, roleBindings[name])
221 }
222 testObjects(t, list, "controller-role-bindings.yaml")
223 }
224
225 func testObjects(t *testing.T, list *api.List, fixtureFilename string) {
226 filename := filepath.Join("testdata", fixtureFilename)
227 expectedYAML, err := os.ReadFile(filename)
228 if err != nil {
229 t.Fatal(err)
230 }
231
232 if err := runtime.EncodeList(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, rbacv1.SchemeGroupVersion), list.Items); err != nil {
233 t.Fatal(err)
234 }
235
236 jsonData, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, rbacv1.SchemeGroupVersion), list)
237 if err != nil {
238 t.Fatal(err)
239 }
240 yamlData, err := yaml.JSONToYAML(jsonData)
241 if err != nil {
242 t.Fatal(err)
243 }
244 if string(yamlData) != string(expectedYAML) {
245 t.Errorf("Bootstrap policy data does not match the test fixture in %s", filename)
246
247 const updateEnvVar = "UPDATE_BOOTSTRAP_POLICY_FIXTURE_DATA"
248 if os.Getenv(updateEnvVar) == "true" {
249 if err := os.WriteFile(filename, []byte(yamlData), os.FileMode(0755)); err == nil {
250 t.Logf("Updated data in %s", filename)
251 t.Logf("Verify the diff, commit changes, and rerun the tests")
252 } else {
253 t.Logf("Could not update data in %s: %v", filename, err)
254 }
255 } else {
256 t.Logf("Diff between bootstrap data and fixture data in %s:\n-------------\n%s", filename, cmp.Diff(string(yamlData), string(expectedYAML)))
257 t.Logf("If the change is expected, re-run with %s=true to update the fixtures", updateEnvVar)
258 }
259 }
260 }
261
262 func TestClusterRoleLabel(t *testing.T) {
263 roles := bootstrappolicy.ClusterRoles()
264 for i := range roles {
265 role := roles[i]
266 accessor, err := meta.Accessor(&role)
267 if err != nil {
268 t.Fatalf("unexpected error: %v", err)
269 }
270
271 if accessor.GetLabels()["kubernetes.io/bootstrapping"] != "rbac-defaults" {
272 t.Errorf("ClusterRole: %s GetLabels() = %s, want %s", accessor.GetName(), accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"})
273 }
274 }
275
276 rolebindings := bootstrappolicy.ClusterRoleBindings()
277 for i := range rolebindings {
278 rolebinding := rolebindings[i]
279 accessor, err := meta.Accessor(&rolebinding)
280 if err != nil {
281 t.Fatalf("unexpected error: %v", err)
282 }
283 if got, want := accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"}; !reflect.DeepEqual(got, want) {
284 t.Errorf("ClusterRoleBinding: %s GetLabels() = %s, want %s", accessor.GetName(), got, want)
285 }
286 }
287 }
288
View as plain text