mirror of
https://github.com/rancher/steve.git
synced 2025-09-17 15:58:41 +00:00
Fix namespace access control in steve (#568)
* Fix adding namespace resource access * Add tests for addResourceAccess func
This commit is contained in:
committed by
GitHub
parent
e1061a86cd
commit
5ae8585e5d
@@ -98,14 +98,31 @@ func addResourceAccess(accessSet *AccessSet, namespace string, rule rbacv1.Polic
|
|||||||
}
|
}
|
||||||
for _, resourceName := range names {
|
for _, resourceName := range names {
|
||||||
for _, verb := range rule.Verbs {
|
for _, verb := range rule.Verbs {
|
||||||
|
access := Access{
|
||||||
|
Namespace: namespace,
|
||||||
|
ResourceName: resourceName,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first condition namespace != All is to determine if it is a RoleBinding.
|
||||||
|
// The second and third conditions are to check if the resource is for "namespaces" in core group.
|
||||||
|
// In kubernetes, rule are valid if they satisfy the following
|
||||||
|
// - Should be `namespaces` GR
|
||||||
|
// - From RoleBindings in `namespace`
|
||||||
|
// - From Rule with ResourceName `*`` or the `namespace` itself.
|
||||||
|
// Ref: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go#L194
|
||||||
|
// If the ResourceName is `All` || namespace itself then only the current namespace is considered as Resourcename
|
||||||
|
// In the case of Rolebinding for the resource "namespaces" in core group, access.Namespace
|
||||||
|
// is set to All since namespace on the resource "namespaces" is not valid.
|
||||||
|
if namespace != All && resource == "namespaces" && group == "" && (resourceName == All || resourceName == namespace) {
|
||||||
|
access.Namespace = All
|
||||||
|
access.ResourceName = namespace
|
||||||
|
}
|
||||||
|
|
||||||
accessSet.Add(verb,
|
accessSet.Add(verb,
|
||||||
schema.GroupResource{
|
schema.GroupResource{
|
||||||
Group: group,
|
Group: group,
|
||||||
Resource: resource,
|
Resource: resource,
|
||||||
}, Access{
|
}, access)
|
||||||
Namespace: namespace,
|
|
||||||
ResourceName: resourceName,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
package accesscontrol
|
package accesscontrol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_policyRuleIndex_roleBindingBySubject(t *testing.T) {
|
func Test_policyRuleIndex_roleBindingBySubject(t *testing.T) {
|
||||||
@@ -236,3 +238,151 @@ func makeCRB(name string, roleRef rbacv1.RoleRef, subjects []rbacv1.Subject) *rb
|
|||||||
Subjects: subjects,
|
Subjects: subjects,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_addResourceAccess(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
namespace string
|
||||||
|
rule rbacv1.PolicyRule
|
||||||
|
want AccessSet
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "RoleBinding namespaces resource with empty names",
|
||||||
|
namespace: "test-ns",
|
||||||
|
rule: rbacv1.PolicyRule{
|
||||||
|
APIGroups: []string{""},
|
||||||
|
Resources: []string{"namespaces", "deployments"},
|
||||||
|
ResourceNames: []string{},
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
},
|
||||||
|
want: AccessSet{
|
||||||
|
set: map[key]resourceAccessSet{
|
||||||
|
{
|
||||||
|
verb: "get", gr: schema.GroupResource{Group: "", Resource: "namespaces"}}: {
|
||||||
|
Access{Namespace: "*", ResourceName: "test-ns"}: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
verb: "get", gr: schema.GroupResource{Group: "", Resource: "deployments"}}: {
|
||||||
|
Access{Namespace: "test-ns", ResourceName: "*"}: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ClusterRoleBinding namespaces resource with empty names",
|
||||||
|
namespace: "*",
|
||||||
|
rule: rbacv1.PolicyRule{
|
||||||
|
APIGroups: []string{""},
|
||||||
|
Resources: []string{"namespaces", "deployments"},
|
||||||
|
ResourceNames: []string{},
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
},
|
||||||
|
want: AccessSet{
|
||||||
|
set: map[key]resourceAccessSet{
|
||||||
|
{verb: "get", gr: schema.GroupResource{Group: "", Resource: "namespaces"}}: {
|
||||||
|
Access{Namespace: "*", ResourceName: "*"}: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
verb: "get", gr: schema.GroupResource{Group: "", Resource: "deployments"}}: {
|
||||||
|
Access{Namespace: "*", ResourceName: "*"}: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RoleBinding namespaces resource with specific names",
|
||||||
|
namespace: "test-ns",
|
||||||
|
rule: rbacv1.PolicyRule{
|
||||||
|
APIGroups: []string{""},
|
||||||
|
Resources: []string{"namespaces"},
|
||||||
|
ResourceNames: []string{"specific-ns"},
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
},
|
||||||
|
want: AccessSet{
|
||||||
|
set: map[key]resourceAccessSet{
|
||||||
|
{verb: "get", gr: schema.GroupResource{Group: "", Resource: "namespaces"}}: {
|
||||||
|
Access{Namespace: "test-ns", ResourceName: "specific-ns"}: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RoleBinding namespaces resource with its own namespace",
|
||||||
|
namespace: "test-ns",
|
||||||
|
rule: rbacv1.PolicyRule{
|
||||||
|
APIGroups: []string{""},
|
||||||
|
Resources: []string{"namespaces"},
|
||||||
|
ResourceNames: []string{"test-ns"},
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
},
|
||||||
|
want: AccessSet{
|
||||||
|
set: map[key]resourceAccessSet{
|
||||||
|
{verb: "get", gr: schema.GroupResource{Group: "", Resource: "namespaces"}}: {
|
||||||
|
Access{Namespace: "*", ResourceName: "test-ns"}: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RoleBinding other resource with empty names",
|
||||||
|
namespace: "test-ns",
|
||||||
|
rule: rbacv1.PolicyRule{
|
||||||
|
APIGroups: []string{"apps"},
|
||||||
|
Resources: []string{"deployments"},
|
||||||
|
ResourceNames: []string{},
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
},
|
||||||
|
want: AccessSet{
|
||||||
|
set: map[key]resourceAccessSet{
|
||||||
|
{verb: "get", gr: schema.GroupResource{Group: "apps", Resource: "deployments"}}: {
|
||||||
|
Access{Namespace: "test-ns", ResourceName: "*"}: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ClusterRoleBinding other resource with empty names",
|
||||||
|
namespace: "*",
|
||||||
|
rule: rbacv1.PolicyRule{
|
||||||
|
APIGroups: []string{"apps"},
|
||||||
|
Resources: []string{"deployments"},
|
||||||
|
ResourceNames: []string{},
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
},
|
||||||
|
want: AccessSet{
|
||||||
|
set: map[key]resourceAccessSet{
|
||||||
|
{verb: "get", gr: schema.GroupResource{Group: "apps", Resource: "deployments"}}: {
|
||||||
|
Access{Namespace: "*", ResourceName: "*"}: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RoleBinding other resource with specific names",
|
||||||
|
namespace: "test-ns",
|
||||||
|
rule: rbacv1.PolicyRule{
|
||||||
|
APIGroups: []string{"apps"},
|
||||||
|
Resources: []string{"deployments"},
|
||||||
|
ResourceNames: []string{"my-deploy"},
|
||||||
|
Verbs: []string{"get"},
|
||||||
|
},
|
||||||
|
want: AccessSet{
|
||||||
|
set: map[key]resourceAccessSet{
|
||||||
|
{verb: "get", gr: schema.GroupResource{Group: "apps", Resource: "deployments"}}: {
|
||||||
|
Access{Namespace: "test-ns", ResourceName: "my-deploy"}: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
accessSet := &AccessSet{}
|
||||||
|
addResourceAccess(accessSet, tt.namespace, tt.rule)
|
||||||
|
if !reflect.DeepEqual(*accessSet, tt.want) {
|
||||||
|
t.Errorf("addResourceAccess() got = %v, want %v", *accessSet, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user