From f71089d07b2a004b80b6f06457aac67b5875411c Mon Sep 17 00:00:00 2001 From: Fabian Kramm Date: Fri, 3 Nov 2023 20:54:59 +0100 Subject: [PATCH] feat: add azure.skip-authz-namespace Signed-off-by: Fabian Kramm --- authz/providers/azure/options/options.go | 3 +++ .../azure/rbac/checkaccessreqhelper.go | 8 ++++++-- .../azure/rbac/checkaccessreqhelper_test.go | 20 +++++++++++++++---- authz/providers/azure/rbac/rbac.go | 12 +++++++---- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/authz/providers/azure/options/options.go b/authz/providers/azure/options/options.go index 247179e1b..66d3448ed 100644 --- a/authz/providers/azure/options/options.go +++ b/authz/providers/azure/options/options.go @@ -44,6 +44,7 @@ type Options struct { ARMCallLimit int SkipAuthzCheck []string SkipAuthzForNonAADUsers bool + SkipAuthzNamespace bool AllowNonResDiscoveryPathAccess bool UseNamespaceResourceScopeFormat bool DiscoverResources bool @@ -56,6 +57,7 @@ func NewOptions() Options { ARMCallLimit: defaultArmCallLimit, SkipAuthzCheck: []string{""}, SkipAuthzForNonAADUsers: true, + SkipAuthzNamespace: false, AllowNonResDiscoveryPathAccess: true, UseNamespaceResourceScopeFormat: false, DiscoverResources: false, @@ -70,6 +72,7 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) { fs.IntVar(&o.ARMCallLimit, "azure.arm-call-limit", o.ARMCallLimit, "No of calls before which webhook switch to new ARM instance to avoid throttling") fs.StringSliceVar(&o.SkipAuthzCheck, "azure.skip-authz-check", o.SkipAuthzCheck, "name of usernames/email for which authz check will be skipped") fs.BoolVar(&o.SkipAuthzForNonAADUsers, "azure.skip-authz-for-non-aad-users", o.SkipAuthzForNonAADUsers, "skip authz for non AAD users") + fs.BoolVar(&o.SkipAuthzNamespace, "azure.skip-authz-namespace", o.SkipAuthzNamespace, "skip authz namespace information") fs.BoolVar(&o.AllowNonResDiscoveryPathAccess, "azure.allow-nonres-discovery-path-access", o.AllowNonResDiscoveryPathAccess, "allow access on Non Resource paths required for discovery, setting it false will require explicit non resource path role assignment for all users in Azure RBAC") fs.BoolVar(&o.UseNamespaceResourceScopeFormat, "azure.use-ns-resource-scope-format", o.UseNamespaceResourceScopeFormat, "use namespace as resource scope format for making rbac checkaccess calls at namespace scope") fs.StringVar(&o.KubeConfigFile, "azure.kubeconfig-file", "", "path to the kubeconfig of cluster.") diff --git a/authz/providers/azure/rbac/checkaccessreqhelper.go b/authz/providers/azure/rbac/checkaccessreqhelper.go index 8ef5c0312..ab6ba9745 100644 --- a/authz/providers/azure/rbac/checkaccessreqhelper.go +++ b/authz/providers/azure/rbac/checkaccessreqhelper.go @@ -447,7 +447,7 @@ func getResultCacheKey(subRevReq *authzv1.SubjectAccessReviewSpec) string { return cacheKey } -func prepareCheckAccessRequestBody(req *authzv1.SubjectAccessReviewSpec, clusterType string, resourceId string, useNamespaceResourceScopeFormat bool) ([]*CheckAccessRequest, error) { +func prepareCheckAccessRequestBody(req *authzv1.SubjectAccessReviewSpec, clusterType string, resourceId string, skipNamespace, useNamespaceResourceScopeFormat bool) ([]*CheckAccessRequest, error) { /* This is how sample SubjectAccessReview request will look like { "kind": "SubjectAccessReview", @@ -522,7 +522,11 @@ func prepareCheckAccessRequestBody(req *authzv1.SubjectAccessReviewSpec, cluster checkaccessreq.Subject.Attributes.Groups = groups checkaccessreq.Subject.Attributes.ObjectId = userOid checkaccessreq.Actions = actions[i:j] - checkaccessreq.Resource.Id = getScope(resourceId, req.ResourceAttributes, useNamespaceResourceScopeFormat) + if skipNamespace { + checkaccessreq.Resource.Id = resourceId + } else { + checkaccessreq.Resource.Id = getScope(resourceId, req.ResourceAttributes, useNamespaceResourceScopeFormat) + } checkAccessReqs = append(checkAccessReqs, &checkaccessreq) } diff --git a/authz/providers/azure/rbac/checkaccessreqhelper_test.go b/authz/providers/azure/rbac/checkaccessreqhelper_test.go index 38ee4f531..c01ffb522 100644 --- a/authz/providers/azure/rbac/checkaccessreqhelper_test.go +++ b/authz/providers/azure/rbac/checkaccessreqhelper_test.go @@ -574,7 +574,7 @@ func Test_prepareCheckAccessRequestBody(t *testing.T) { createOperationsMap(clusterType) wantErr := errors.New("oid info not sent from authenticatoin module") - got, gotErr := prepareCheckAccessRequestBody(req, clusterType, resourceId, false) + got, gotErr := prepareCheckAccessRequestBody(req, clusterType, resourceId, false, false) if got != nil && gotErr != wantErr { t.Errorf("Want:%v WantErr:%v, got:%v, gotErr:%v", nil, wantErr, got, gotErr) @@ -584,7 +584,7 @@ func Test_prepareCheckAccessRequestBody(t *testing.T) { clusterType = "arc" wantErr = errors.New("oid info sent from authenticatoin module is not valid") - got, gotErr = prepareCheckAccessRequestBody(req, clusterType, resourceId, false) + got, gotErr = prepareCheckAccessRequestBody(req, clusterType, resourceId, false, false) if got != nil && gotErr != wantErr { t.Errorf("Want:%v WantErr:%v, got:%v, gotErr:%v", nil, wantErr, got, gotErr) @@ -600,7 +600,7 @@ func Test_prepareCheckAccessRequestBodyWithNamespace(t *testing.T) { // testing with new ns scope format var want string = "resourceId/providers/Microsoft.KubernetesConfiguration/namespaces/dev" - got, gotErr := prepareCheckAccessRequestBody(req, clusterType, resourceId, true) + got, gotErr := prepareCheckAccessRequestBody(req, clusterType, resourceId, false, true) if got == nil { t.Errorf("Want: not nil Got: nil, gotErr:%v", gotErr) @@ -613,7 +613,19 @@ func Test_prepareCheckAccessRequestBodyWithNamespace(t *testing.T) { // testing with the old namespace format want = "resourceId/namespaces/dev" - got, gotErr = prepareCheckAccessRequestBody(req, clusterType, resourceId, false) + got, gotErr = prepareCheckAccessRequestBody(req, clusterType, resourceId, false, false) + if got == nil { + t.Errorf("Want: not nil Got: nil, gotErr:%v", gotErr) + } + + if got != nil && got[0].Resource.Id != want { + t.Errorf("Want:%v, got:%v", want, got) + } + + // testing without namespace + want = "resourceId" + + got, gotErr = prepareCheckAccessRequestBody(req, clusterType, resourceId, true, false) if got == nil { t.Errorf("Want: not nil Got: nil, gotErr:%v", gotErr) } diff --git a/authz/providers/azure/rbac/rbac.go b/authz/providers/azure/rbac/rbac.go index 8f771f625..db310f37d 100644 --- a/authz/providers/azure/rbac/rbac.go +++ b/authz/providers/azure/rbac/rbac.go @@ -84,6 +84,7 @@ type AccessInfo struct { armCallLimit int skipCheck map[string]void skipAuthzForNonAADUsers bool + skipAuthzNamespace bool allowNonResDiscoveryPathAccess bool useNamespaceResourceScopeFormat bool lock sync.RWMutex @@ -167,6 +168,7 @@ func newAccessInfo(tokenProvider graph.TokenProvider, rbacURL *url.URL, opts aut azureResourceId: opts.ResourceId, armCallLimit: opts.ARMCallLimit, skipAuthzForNonAADUsers: opts.SkipAuthzForNonAADUsers, + skipAuthzNamespace: opts.SkipAuthzNamespace, allowNonResDiscoveryPathAccess: opts.AllowNonResDiscoveryPathAccess, useNamespaceResourceScopeFormat: opts.UseNamespaceResourceScopeFormat, } @@ -293,7 +295,7 @@ func (a *AccessInfo) setReqHeaders(req *http.Request) { } func (a *AccessInfo) CheckAccess(request *authzv1.SubjectAccessReviewSpec) (*authzv1.SubjectAccessReviewStatus, error) { - checkAccessBodies, err := prepareCheckAccessRequestBody(request, a.clusterType, a.azureResourceId, a.useNamespaceResourceScopeFormat) + checkAccessBodies, err := prepareCheckAccessRequestBody(request, a.clusterType, a.azureResourceId, a.skipAuthzNamespace, a.useNamespaceResourceScopeFormat) if err != nil { return nil, errors.Wrap(err, "error in preparing check access request") } @@ -301,9 +303,11 @@ func (a *AccessInfo) CheckAccess(request *authzv1.SubjectAccessReviewSpec) (*aut checkAccessURL := *a.apiURL // Append the path for azure cluster resource id checkAccessURL.Path = path.Join(checkAccessURL.Path, a.azureResourceId) - exist, nameSpaceString := getNameSpaceScope(request, a.useNamespaceResourceScopeFormat) - if exist { - checkAccessURL.Path = path.Join(checkAccessURL.Path, nameSpaceString) + if !a.skipAuthzNamespace { + exist, nameSpaceString := getNameSpaceScope(request, a.useNamespaceResourceScopeFormat) + if exist { + checkAccessURL.Path = path.Join(checkAccessURL.Path, nameSpaceString) + } } checkAccessURL.Path = path.Join(checkAccessURL.Path, checkAccessPath)