Skip to content

Commit

Permalink
feat: add auth check when listing kinds (#325)
Browse files Browse the repository at this point in the history
Signed-off-by: Charles-Edouard Brétéché <[email protected]>
  • Loading branch information
eddycharly authored May 30, 2023
1 parent ebc205a commit 12e67ed
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 7 deletions.
34 changes: 34 additions & 0 deletions backend/pkg/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package auth

import (
"context"

authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
)

// AuthResult contains authorization check result
type AuthResult struct {
Allowed bool
Reason string
EvaluationError string
}

// AuthChecker provides utility to check authorization
type AuthChecker interface {
// Check checks if the caller can perform an operation
Check(ctx context.Context, group, version, resource, subresource, namespace, verb string) (*AuthResult, error)
}

func NewSelfChecker(client authorizationv1client.SelfSubjectAccessReviewInterface) AuthChecker {
return self{
client: client,
}
}

func NewSubjectChecker(client authorizationv1client.SubjectAccessReviewInterface, user string, groups []string) AuthChecker {
return subject{
client: client,
user: user,
groups: groups,
}
}
18 changes: 18 additions & 0 deletions backend/pkg/auth/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package auth

import (
"context"
)

func Check(ctx context.Context, checker AuthChecker, group, version, resource, subresource, namespace string, verbs ...string) (bool, error) {
for _, verb := range verbs {
result, err := checker.Check(ctx, group, version, resource, subresource, namespace, verb)
if err != nil {
return false, err
}
if !result.Allowed {
return false, nil
}
}
return true, nil
}
37 changes: 37 additions & 0 deletions backend/pkg/auth/self.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package auth

import (
"context"

authorizationv1 "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
)

type self struct {
client authorizationv1client.SelfSubjectAccessReviewInterface
}

func (c self) Check(ctx context.Context, group, version, resource, subresource, namespace, verb string) (*AuthResult, error) {
review := &authorizationv1.SelfSubjectAccessReview{
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Group: group,
Version: version,
Resource: resource,
Subresource: subresource,
Namespace: namespace,
Verb: verb,
},
},
}
resp, err := c.client.Create(ctx, review, metav1.CreateOptions{})
if err != nil {
return nil, err
}
return &AuthResult{
Allowed: resp.Status.Allowed,
Reason: resp.Status.Reason,
EvaluationError: resp.Status.EvaluationError,
}, nil
}
41 changes: 41 additions & 0 deletions backend/pkg/auth/subject.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package auth

import (
"context"

authorizationv1 "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
)

type subject struct {
client authorizationv1client.SubjectAccessReviewInterface
user string
groups []string
}

func (c subject) Check(ctx context.Context, group, version, resource, subresource, namespace, verb string) (*AuthResult, error) {
review := &authorizationv1.SubjectAccessReview{
Spec: authorizationv1.SubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Group: group,
Version: version,
Resource: resource,
Subresource: subresource,
Namespace: namespace,
Verb: verb,
},
User: c.user,
Groups: c.groups,
},
}
resp, err := c.client.Create(ctx, review, metav1.CreateOptions{})
if err != nil {
return nil, err
}
return &AuthResult{
Allowed: resp.Status.Allowed,
Reason: resp.Status.Reason,
EvaluationError: resp.Status.EvaluationError,
}, nil
}
21 changes: 15 additions & 6 deletions backend/pkg/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"

"github.com/kyverno/playground/backend/pkg/auth"
)

type SearchResult struct {
Expand Down Expand Up @@ -65,10 +67,11 @@ func New(restConfig *rest.Config) (Cluster, error) {
return cluster{kubeClient, kyvernoClient, NewWrapper(dClient)}, nil
}

func (c cluster) Kinds(_ context.Context, excludeGroups ...string) ([]Resource, error) {
func (c cluster) Kinds(ctx context.Context, excludeGroups ...string) ([]Resource, error) {
excluded := sets.New(excludeGroups...)
disco := c.kubeClient.Discovery()
_, resources, err := disco.ServerGroupsAndResources()
checker := auth.NewSelfChecker(c.kubeClient.AuthorizationV1().SelfSubjectAccessReviews())
var kinds []Resource
for _, group := range resources {
gv, err := schema.ParseGroupVersion(group.GroupVersion)
Expand All @@ -84,11 +87,17 @@ func (c cluster) Kinds(_ context.Context, excludeGroups ...string) ([]Resource,
}
verbs := sets.New(resource.Verbs...)
if verbs.Has("get") && verbs.Has("list") {
kinds = append(kinds, Resource{
APIVersion: group.GroupVersion,
Kind: resource.Kind,
ClusterScoped: !resource.Namespaced,
})
allowed, err := auth.Check(ctx, checker, gv.Group, gv.Version, resource.Name, "", "", "get", "list")
if err != nil {
continue
}
if allowed {
kinds = append(kinds, Resource{
APIVersion: group.GroupVersion,
Kind: resource.Kind,
ClusterScoped: !resource.Namespaced,
})
}
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion backend/pkg/utils/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,11 @@ import (
func RestConfig(overrides clientcmd.ConfigOverrides) (*rest.Config, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &overrides)
return kubeConfig.ClientConfig()
config, err := kubeConfig.ClientConfig()
if err != nil {
return nil, err
}
config.QPS = 300
config.Burst = 300
return config, nil
}

0 comments on commit 12e67ed

Please sign in to comment.