@@ -140,43 +140,56 @@ func (clusterRoleEvaluator) Handles(scope string) bool {
140
140
}
141
141
142
142
func (e clusterRoleEvaluator ) Validate (scope string ) error {
143
- _ , _ , err := e .getRoleNamespace (scope )
143
+ _ , _ , _ , err := e .parseScope (scope )
144
144
return err
145
145
}
146
146
147
- func (e clusterRoleEvaluator ) getRoleNamespace (scope string ) (string , string , error ) {
147
+ // parseScope parses the requested scope, determining the requested role name, namespace, and if
148
+ // access to escalating objects is required. It will return an error if it doesn't parse cleanly
149
+ func (e clusterRoleEvaluator ) parseScope (scope string ) (string /*role name*/ , string /*namespace*/ , bool /*escalating*/ , error ) {
148
150
if ! e .Handles (scope ) {
149
- return "" , "" , fmt .Errorf ("bad format for scope %v" , scope )
151
+ return "" , "" , false , fmt .Errorf ("bad format for scope %v" , scope )
150
152
}
153
+ escalating := false
154
+ if strings .HasSuffix (scope , ":!" ) {
155
+ escalating = true
156
+ // clip that last segment before parsing the rest
157
+ scope = scope [:strings .LastIndex (scope , ":" )]
158
+ }
159
+
151
160
tokens := strings .SplitN (scope , ":" , 2 )
152
161
if len (tokens ) != 2 {
153
- return "" , "" , fmt .Errorf ("bad format for scope %v" , scope )
162
+ return "" , "" , false , fmt .Errorf ("bad format for scope %v" , scope )
154
163
}
155
164
156
165
// namespaces can't have colons, but roles can. pick last.
157
166
lastColonIndex := strings .LastIndex (tokens [1 ], ":" )
158
167
if lastColonIndex <= 0 || lastColonIndex == (len (tokens [1 ])- 1 ) {
159
- return "" , "" , fmt .Errorf ("bad format for scope %v" , scope )
168
+ return "" , "" , false , fmt .Errorf ("bad format for scope %v" , scope )
160
169
}
161
170
162
- return tokens [1 ][0 :lastColonIndex ], tokens [1 ][lastColonIndex + 1 :], nil
171
+ return tokens [1 ][0 :lastColonIndex ], tokens [1 ][lastColonIndex + 1 :], escalating , nil
163
172
}
164
173
165
174
func (e clusterRoleEvaluator ) Describe (scope string ) string {
166
- roleName , scopeNamespace , err := e .getRoleNamespace (scope )
175
+ roleName , scopeNamespace , escalating , err := e .parseScope (scope )
167
176
if err != nil {
168
177
return err .Error ()
169
178
}
179
+ escalatingPhrase := "including any escalating resources like secrets"
180
+ if ! escalating {
181
+ escalatingPhrase = "excluding any escalating resources like secrets"
182
+ }
170
183
171
184
if scopeNamespace == authorizationapi .ScopesAllNamespaces {
172
- return roleName + " access in all projects"
185
+ return roleName + " access in all projects, " + escalatingPhrase
173
186
}
174
187
175
- return roleName + " access in the " + scopeNamespace + " project"
188
+ return roleName + " access in the " + scopeNamespace + " project, " + escalatingPhrase
176
189
}
177
190
178
191
func (e clusterRoleEvaluator ) ResolveRules (scope , namespace string , clusterPolicyGetter rulevalidation.ClusterPolicyGetter ) ([]authorizationapi.PolicyRule , error ) {
179
- roleName , scopeNamespace , err := e .getRoleNamespace (scope )
192
+ roleName , scopeNamespace , escalating , err := e .parseScope (scope )
180
193
if err != nil {
181
194
return nil , err
182
195
}
@@ -198,21 +211,25 @@ func (e clusterRoleEvaluator) ResolveRules(scope, namespace string, clusterPolic
198
211
199
212
rules := []authorizationapi.PolicyRule {}
200
213
for _ , rule := range role .Rules {
214
+ if escalating {
215
+ rules = append (rules , rule )
216
+ continue
217
+ }
218
+
201
219
// rules with unbounded access shouldn't be allowed in scopes.
202
220
if rule .Verbs .Has (authorizationapi .VerbAll ) || rule .Resources .Has (authorizationapi .ResourceAll ) || getAPIGroupSet (rule ).Has (authorizationapi .APIGroupAll ) {
203
221
continue
204
222
}
205
-
206
223
// rules that allow escalating resource access should be cleaned.
207
224
safeRule := removeEscalatingResources (rule )
208
-
209
225
rules = append (rules , safeRule )
210
226
}
211
227
212
228
return rules , nil
213
229
}
214
230
215
- // removeEscalatingResources has coarse logic for now. It is possible to rewrite one rule into many for the finest grain control
231
+ // removeEscalatingResources inspects a PolicyRule and removes any references to escalating resources.
232
+ // It has coarse logic for now. It is possible to rewrite one rule into many for the finest grain control
216
233
// but removing the entire matching resource regardless of verb or secondary group is cheaper, easier, and errs on the side removing
217
234
// too much, not too little
218
235
func removeEscalatingResources (in authorizationapi.PolicyRule ) authorizationapi.PolicyRule {
0 commit comments