Merge pull request #53722 from deads2k/rbac-01-allow-star

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

allow */subresource in rbac policy rules

xref #29698
xref #38756
xref #49504
xref #38810

Allow `*/subresource` format in RBAC policy rules to support polymorphic subresources like `*/scale` for HPA.

@DirectXMan12 fyi

```release-note
RBAC PolicyRules now allow resource=`*/<subresource>` to cover `any-resource/<subresource>`.   For example, `*/scale` covers `replicationcontroller/scale`.
```
This commit is contained in:
Kubernetes Submit Queue
2017-10-18 14:02:05 -07:00
committed by GitHub
26 changed files with 205 additions and 49 deletions

View File

@@ -55,14 +55,29 @@ func APIGroupMatches(rule *PolicyRule, requestedGroup string) bool {
return false
}
func ResourceMatches(rule *PolicyRule, requestedResource string) bool {
func ResourceMatches(rule *PolicyRule, combinedRequestedResource, requestedSubresource string) bool {
for _, ruleResource := range rule.Resources {
// if everything is allowed, we match
if ruleResource == ResourceAll {
return true
}
if ruleResource == requestedResource {
// if we have an exact match, we match
if ruleResource == combinedRequestedResource {
return true
}
// We can also match a */subresource.
// if there isn't a subresource, then continue
if len(requestedSubresource) == 0 {
continue
}
// if the rule isn't in the format */subresource, then we don't match, continue
if len(ruleResource) == len(requestedSubresource)+2 &&
strings.HasPrefix(ruleResource, "*/") &&
strings.HasSuffix(ruleResource, requestedSubresource) {
return true
}
}
return false

View File

@@ -70,3 +70,108 @@ func TestHelpersRoundTrip(t *testing.T) {
}
}
}
func TestResourceMatches(t *testing.T) {
tests := []struct {
name string
ruleResources []string
combinedRequestedResource string
requestedSubresource string
expected bool
}{
{
name: "all matches 01",
ruleResources: []string{"*"},
combinedRequestedResource: "foo",
expected: true,
},
{
name: "checks all rules",
ruleResources: []string{"doesn't match", "*"},
combinedRequestedResource: "foo",
expected: true,
},
{
name: "matches exact rule",
ruleResources: []string{"foo/bar"},
combinedRequestedResource: "foo/bar",
requestedSubresource: "bar",
expected: true,
},
{
name: "matches exact rule 02",
ruleResources: []string{"foo/bar"},
combinedRequestedResource: "foo",
expected: false,
},
{
name: "matches subresource",
ruleResources: []string{"*/scale"},
combinedRequestedResource: "foo/scale",
requestedSubresource: "scale",
expected: true,
},
{
name: "doesn't match partial subresource hit",
ruleResources: []string{"foo/bar", "*/other"},
combinedRequestedResource: "foo/other/segment",
requestedSubresource: "other/segment",
expected: false,
},
{
name: "matches subresource with multiple slashes",
ruleResources: []string{"*/other/segment"},
combinedRequestedResource: "foo/other/segment",
requestedSubresource: "other/segment",
expected: true,
},
{
name: "doesn't fail on empty",
ruleResources: []string{""},
combinedRequestedResource: "foo/other/segment",
requestedSubresource: "other/segment",
expected: false,
},
{
name: "doesn't fail on slash",
ruleResources: []string{"/"},
combinedRequestedResource: "foo/other/segment",
requestedSubresource: "other/segment",
expected: false,
},
{
name: "doesn't fail on missing subresource",
ruleResources: []string{"*/"},
combinedRequestedResource: "foo/other/segment",
requestedSubresource: "other/segment",
expected: false,
},
{
name: "doesn't match on not star",
ruleResources: []string{"*something/other/segment"},
combinedRequestedResource: "foo/other/segment",
requestedSubresource: "other/segment",
expected: false,
},
{
name: "doesn't match on something else",
ruleResources: []string{"something/other/segment"},
combinedRequestedResource: "foo/other/segment",
requestedSubresource: "other/segment",
expected: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
rule := &rbac.PolicyRule{
Resources: tc.ruleResources,
}
actual := rbac.ResourceMatches(rule, tc.combinedRequestedResource, tc.requestedSubresource)
if tc.expected != actual {
t.Errorf("expected %v, got %v", tc.expected, actual)
}
})
}
}

View File

@@ -48,7 +48,8 @@ type PolicyRule struct {
// APIGroups is the name of the APIGroup that contains the resources.
// If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed.
APIGroups []string
// Resources is a list of resources this rule applies to. ResourceAll represents all resources.
// Resources is a list of resources this rule applies to. '*' represents all resources in the specified apiGroups.
// '*/foo' represents the subresource 'foo' for all resources in the specified apiGroups.
Resources []string
// ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.
ResourceNames []string