@@ -3,10 +3,10 @@ package psalabelsyncer
3
3
import (
4
4
"fmt"
5
5
"strings"
6
+ "sync"
6
7
7
8
corev1 "k8s.io/api/core/v1"
8
9
rbacv1 "k8s.io/api/rbac/v1"
9
- "k8s.io/apimachinery/pkg/api/errors"
10
10
"k8s.io/apimachinery/pkg/labels"
11
11
"k8s.io/apimachinery/pkg/util/sets"
12
12
"k8s.io/apiserver/pkg/authentication/serviceaccount"
@@ -15,6 +15,7 @@ import (
15
15
rbacv1informers "k8s.io/client-go/informers/rbac/v1"
16
16
rbacv1listers "k8s.io/client-go/listers/rbac/v1"
17
17
"k8s.io/client-go/tools/cache"
18
+ "k8s.io/klog/v2"
18
19
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
19
20
20
21
securityv1 "github.com/openshift/api/security/v1"
@@ -34,6 +35,9 @@ type SAToSCCCache struct {
34
35
35
36
rolesSynced cache.InformerSynced
36
37
roleBindingsSynced cache.InformerSynced
38
+
39
+ usefulRolesLock sync.RWMutex
40
+ usefulRoles map [string ][]string
37
41
}
38
42
39
43
// role and clusterrolebinding object for generic handling, assumes one and
@@ -54,7 +58,7 @@ func newRoleBindingObj(obj interface{}) (*roleBindingObj, error) {
54
58
}, nil
55
59
}
56
60
57
- return nil , fmt .Errorf ("the object is neither a RoleBinding, nor a ClusterRoleBinding: %v " , obj )
61
+ return nil , fmt .Errorf ("the object is neither a RoleBinding, nor a ClusterRoleBinding: %T " , obj )
58
62
}
59
63
60
64
func (r * roleBindingObj ) RoleRef () rbacv1.RoleRef {
@@ -78,6 +82,57 @@ func (r *roleBindingObj) AppliesToNS(ns string) bool {
78
82
return ns == r .roleBinding .Namespace
79
83
}
80
84
85
+ func (r * roleBindingObj ) Namespace () string {
86
+ if r .clusterRoleBinding != nil {
87
+ return ""
88
+ }
89
+ return r .roleBinding .Namespace
90
+ }
91
+
92
+ // roleObj helps to handle roles and clusterroles in a generic manner
93
+ type roleObj struct {
94
+ role * rbacv1.Role
95
+ clusterRole * rbacv1.ClusterRole
96
+ }
97
+
98
+ func newRoleObj (obj interface {}) (* roleObj , error ) {
99
+ switch r := obj .(type ) {
100
+ case * rbacv1.ClusterRole :
101
+ return & roleObj {
102
+ clusterRole : r ,
103
+ }, nil
104
+ case * rbacv1.Role :
105
+ return & roleObj {
106
+ role : r ,
107
+ }, nil
108
+ case * roleObj :
109
+ return r , nil
110
+ default :
111
+ return nil , fmt .Errorf ("the object is neither a Role, nor a ClusterRole: %T" , obj )
112
+ }
113
+ }
114
+
115
+ func (r * roleObj ) Rules () []rbacv1.PolicyRule {
116
+ if role := r .clusterRole ; role != nil {
117
+ return role .Rules
118
+ }
119
+ return r .role .Rules
120
+ }
121
+
122
+ func (r * roleObj ) Name () string {
123
+ if role := r .clusterRole ; role != nil {
124
+ return role .Name
125
+ }
126
+ return r .role .Name
127
+ }
128
+
129
+ func (r * roleObj ) Namespace () string {
130
+ if role := r .clusterRole ; role != nil {
131
+ return role .Namespace
132
+ }
133
+ return r .role .Namespace
134
+ }
135
+
81
136
func BySAIndexKeys (obj interface {}) ([]string , error ) {
82
137
roleBinding , err := newRoleBindingObj (obj )
83
138
if err != nil {
@@ -111,6 +166,8 @@ func NewSAToSCCCache(rbacInformers rbacv1informers.Interface, sccInfomer securit
111
166
// TODO: do I need these?
112
167
rolesSynced : rbacInformers .Roles ().Informer ().HasSynced ,
113
168
roleBindingsSynced : rbacInformers .RoleBindings ().Informer ().HasSynced ,
169
+
170
+ usefulRoles : make (map [string ][]string ),
114
171
}
115
172
}
116
173
@@ -163,41 +220,124 @@ func (c *SAToSCCCache) SCCsFor(serviceAccount *corev1.ServiceAccount) (sets.Stri
163
220
return nil , err
164
221
}
165
222
223
+ roleCachedKey := fmt .Sprintf ("/%s" , rb .RoleRef ().Name )
224
+ if rb .RoleRef ().Kind == "Role" {
225
+ roleCachedKey = rb .Namespace () + roleCachedKey
226
+ }
227
+
228
+ c .usefulRolesLock .RLock ()
229
+ // this role does not have SCC-related rules
230
+ cachedAllowedSCCs := c .usefulRoles [roleCachedKey ]
231
+ if len (cachedAllowedSCCs ) == 0 {
232
+ continue
233
+ }
234
+ c .usefulRolesLock .RUnlock ()
235
+
166
236
// we particularly care only about Roles in the SA NS
167
237
if roleRef := rb .RoleRef (); rb .AppliesToNS (serviceAccount .Namespace ) && roleRef .APIGroup == rbacv1 .GroupName {
168
- switch roleRef .Kind {
169
- case "Role" :
170
- r , err := c .roleLister .Roles (serviceAccount .Namespace ).Get (roleRef .Name )
171
- if err != nil {
172
- if errors .IsNotFound (err ) {
173
- continue
174
- }
175
- // TODO: maybe just ignore and log?
176
- return nil , err
177
- }
178
- allowedSCCs .Insert (SCCsAllowedByPolicyRules (serviceAccount .Namespace , realSAUserInfo , sccs , r .Rules )... )
179
-
180
- case "ClusterRole" :
181
- r , err := c .clusterRoleLister .Get (roleRef .Name )
182
- if err != nil {
183
- if errors .IsNotFound (err ) {
184
- continue
185
- }
186
- // TODO: maybe just ignore and log?
187
- return nil , err
188
- }
189
- allowedSCCs .Insert (SCCsAllowedByPolicyRules (serviceAccount .Namespace , realSAUserInfo , sccs , r .Rules )... )
190
-
191
- default :
192
- // ignore invalid role references
193
- continue
194
- }
238
+ allowedSCCs .Insert (cachedAllowedSCCs ... )
195
239
}
196
240
}
197
241
198
242
return allowedSCCs , nil
199
243
}
200
244
245
+ func (c * SAToSCCCache ) GetRoleFromRoleRef (ns string , roleRef rbacv1.RoleRef ) (* roleObj , error ) {
246
+ var role interface {}
247
+ var err error
248
+ switch kind := roleRef .Kind ; kind {
249
+ case "Role" :
250
+ role , err = c .roleLister .Roles (ns ).Get (roleRef .Name )
251
+ case "ClusterRole" :
252
+ role , err = c .clusterRoleLister .Get (roleRef .Name )
253
+ default :
254
+ return nil , fmt .Errorf ("unknown kind in roleRef: %s" , kind )
255
+ }
256
+ if err != nil {
257
+ return nil , err
258
+ }
259
+
260
+ return newRoleObj (role )
261
+ }
262
+
263
+ func (c * SAToSCCCache ) IsRoleBindingRelevant (obj interface {}) bool {
264
+ rb , err := newRoleBindingObj (obj )
265
+ if err != nil {
266
+ klog .Warningf ("unexpected error, this may be a bug: %v" , err )
267
+ return false
268
+ }
269
+
270
+ role , err := c .GetRoleFromRoleRef (rb .Namespace (), rb .RoleRef ())
271
+ if err != nil {
272
+ klog .Infof ("failed to retrieve a role for a rolebinding ref: %v" , err )
273
+ return false
274
+ }
275
+
276
+ // TODO: actually cache the relevant rolebindings and relevant roles
277
+ // or maybe only the roles and update cached roles on a role update?
278
+ return c .IsRoleInvolvesSCCs (role , false )
279
+ }
280
+
281
+ func (c * SAToSCCCache ) IsRoleInvolvesSCCs (obj interface {}, isRoleUpdate bool ) bool {
282
+ role , err := newRoleObj (obj )
283
+ if err != nil {
284
+ klog .Warningf ("unexpected error, this may be a bug: %v" , err )
285
+ return false
286
+ }
287
+
288
+ sccs , err := c .sccLister .List (labels .Everything ()) // TODO: this should probably requeue, right?
289
+ if err != nil {
290
+ klog .Warning ("failed to list SCCs: %v" , err )
291
+ return false
292
+ }
293
+
294
+ if isRoleUpdate {
295
+ c .SyncRoleCache (role .Namespace (), role .Name (), role .Rules (), sccs )
296
+ }
297
+
298
+ c .usefulRolesLock .RLock ()
299
+ defer c .usefulRolesLock .RUnlock ()
300
+ return len (c .usefulRoles [fmt .Sprintf ("%s/%s" , role .Namespace (), role .Name ())]) != 0
301
+ }
302
+
303
+ func (c * SAToSCCCache ) InitializeRoleCache () error {
304
+ roles , err := c .roleLister .List (labels .Everything ())
305
+ if err != nil {
306
+ return fmt .Errorf ("failed to initialize role cache: %w" , err )
307
+ }
308
+
309
+ clusterRoles , err := c .clusterRoleLister .List (labels .Everything ())
310
+ if err != nil {
311
+ return fmt .Errorf ("failed to initialize role cache: %w" , err )
312
+ }
313
+
314
+ sccs , err := c .sccLister .List (labels .Everything ())
315
+ if err != nil {
316
+ return fmt .Errorf ("failed to initialize role cache: %w" , err )
317
+ }
318
+
319
+ for _ , r := range roles {
320
+ c .SyncRoleCache (r .Namespace , r .Name , r .Rules , sccs )
321
+ }
322
+
323
+ for _ , r := range clusterRoles {
324
+ c .SyncRoleCache (r .Namespace , r .Name , r .Rules , sccs )
325
+ }
326
+
327
+ return nil
328
+ }
329
+
330
+ func (c * SAToSCCCache ) SyncRoleCache (roleNS , roleName string , rules []rbacv1.PolicyRule , sccs []* securityv1.SecurityContextConstraints ) {
331
+ dummyUserInfo := & user.DefaultInfo {
332
+ Name : "dummyUser" ,
333
+ }
334
+ if allowedSCCs := SCCsAllowedByPolicyRules ("" , dummyUserInfo , sccs , rules ); len (allowedSCCs ) > 0 {
335
+ c .usefulRolesLock .Lock ()
336
+ c .usefulRoles [fmt .Sprintf ("%s/%s" , roleNS , roleName )] = allowedSCCs
337
+ c .usefulRolesLock .Unlock ()
338
+ }
339
+ }
340
+
201
341
func SCCsAllowedByPolicyRules (nsName string , saUserInfo user.Info , sccs []* securityv1.SecurityContextConstraints , rules []rbacv1.PolicyRule ) []string {
202
342
ar := authorizer.AttributesRecord {
203
343
User : saUserInfo ,
0 commit comments