From ebf872c9ba6be83be1f2a0a03ae6e67dd99935ae Mon Sep 17 00:00:00 2001 From: Joel Takvorian Date: Wed, 10 Jan 2024 08:53:16 +0100 Subject: [PATCH] Allow skipping per-query namespace inference via config This is a way to open the current behaviour of Loki "meta-data" queries to other use cases where the list of namespaces cannot be inferred from queries. NetObserv will use this flag because its queries are not namespaced. This allows to effectively use the fine-grained SAR feature with netobserv. Related JIRA: NETOBSERV-1324 --- internal/authorizer/authorizer.go | 10 +++++----- internal/authorizer/cache.go | 4 ++-- internal/config/config.go | 18 ++++++++++-------- internal/handler/handler.go | 3 ++- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/internal/authorizer/authorizer.go b/internal/authorizer/authorizer.go index 81f52e1..d7e3ce2 100644 --- a/internal/authorizer/authorizer.go +++ b/internal/authorizer/authorizer.go @@ -53,7 +53,7 @@ func (a *Authorizer) Authorize( token, user string, groups []string, verb, resource, resourceName, apiGroup string, - namespaces []string, metadataOnly bool, + namespaces []string, allowSkipNamespaceInference bool, ) (types.DataResponseV1, error) { switch verb { case CreateVerb, GetVerb: @@ -61,7 +61,7 @@ func (a *Authorizer) Authorize( return types.DataResponseV1{}, &StatusCodeError{fmt.Errorf("unexpected verb: %s", verb), http.StatusBadRequest} } - cacheKey := generateCacheKey(token, user, groups, verb, resource, resourceName, apiGroup, namespaces, metadataOnly, a.matcher) + cacheKey := generateCacheKey(token, user, groups, verb, resource, resourceName, apiGroup, namespaces, allowSkipNamespaceInference, a.matcher) level.Debug(a.logger).Log("msg", "looking up in cache", "cachekey", cacheKey) //nolint:errcheck res, ok, err := a.cache.Get(cacheKey) @@ -75,7 +75,7 @@ func (a *Authorizer) Authorize( return res, nil } - res, err = a.authorizeInner(user, groups, verb, resource, resourceName, apiGroup, namespaces, metadataOnly) + res, err = a.authorizeInner(user, groups, verb, resource, resourceName, apiGroup, namespaces, allowSkipNamespaceInference) if err != nil { return types.DataResponseV1{}, err } @@ -88,7 +88,7 @@ func (a *Authorizer) Authorize( return res, nil } -func (a *Authorizer) authorizeInner(user string, groups []string, verb, resource, resourceName, apiGroup string, namespaces []string, metadataOnly bool) (types.DataResponseV1, error) { +func (a *Authorizer) authorizeInner(user string, groups []string, verb, resource, resourceName, apiGroup string, namespaces []string, allowSkipNamespaceInference bool) (types.DataResponseV1, error) { // check if user has cluster-wide access clusterAllow, err := a.client.AccessReview(user, groups, verb, resource, resourceName, apiGroup, "") if err != nil { @@ -113,7 +113,7 @@ func (a *Authorizer) authorizeInner(user string, groups []string, verb, resource return a.authorizeClusterWide(namespaces) } - if metadataOnly && len(namespaces) == 0 { + if allowSkipNamespaceInference && len(namespaces) == 0 { // Only a metadata request and no namespaces provided -> populate with API list nsList, err := a.client.ListNamespaces() if err != nil { diff --git a/internal/authorizer/cache.go b/internal/authorizer/cache.go index bf3bf72..2442444 100644 --- a/internal/authorizer/cache.go +++ b/internal/authorizer/cache.go @@ -13,13 +13,13 @@ import ( func generateCacheKey( token, user string, groups []string, verb, resource, resourceName, apiGroup string, namespaces []string, - metadataOnly bool, matcher *config.Matcher, + allowSkipNamespaceInference bool, matcher *config.Matcher, ) string { userHash := hashUserinfo(token, user, groups) matcherHash := hashMatcher(matcher) return strings.Join([]string{ - verb, fmt.Sprintf("%v", metadataOnly), + verb, fmt.Sprintf("%v", allowSkipNamespaceInference), apiGroup, resourceName, resource, strings.Join(namespaces, ":"), userHash, matcherHash, }, ",") diff --git a/internal/config/config.go b/internal/config/config.go index 7343f41..2889702 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -31,14 +31,15 @@ type Config struct { } type OPAConfig struct { - Pkg string - Rule string - Matcher string - MatcherOp string - MatcherSkipTenants string - MatcherAdminGroups string - SSAR bool - ViaQToOTELMigration bool + Pkg string + Rule string + Matcher string + MatcherOp string + MatcherSkipTenants string + MatcherAdminGroups string + SSAR bool + ViaQToOTELMigration bool + AllowSkipNamespaceInference bool } type ServerConfig struct { @@ -112,6 +113,7 @@ func ParseFlags() (*Config, error) { flag.StringVar(&cfg.Opa.MatcherAdminGroups, "opa.admin-groups", "", "Groups which should be treated as admins and cause the matcher to be omitted.") flag.BoolVar(&cfg.Opa.SSAR, "opa.ssar", false, "Use SelftSubjectAccessReview instead of SubjectAccessReview.") flag.BoolVar(&cfg.Opa.ViaQToOTELMigration, "opa.viaq-to-otel-migration", false, "Enable the ViaQ to OTel migration.") + flag.BoolVar(&cfg.Opa.AllowSkipNamespaceInference, "opa.skip-namespace-inference", false, "Set true when namespaces cannot be inferred from query. This results in doing SARs for each user accessible namespace.") // Memcached flags flag.StringSliceVar(&cfg.Memcached.Servers, "memcached", nil, "One or more Memcached server addresses.") diff --git a/internal/handler/handler.go b/internal/handler/handler.go index ceceaf3..07732ee 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -150,8 +150,9 @@ func New(l log.Logger, c cache.Cacher, wt transport.WrapperFunc, cfg *config.Con } a := authorizer.New(oc, l, c, matcherForRequest) + allowSkipNamespaceInference := cfg.Opa.AllowSkipNamespaceInference || extras.MetadataOnly - res, err := a.Authorize(token, req.Input.Subject, req.Input.Groups, verb, req.Input.Tenant, req.Input.Resource, apiGroup, namespaces.UnsortedList(), extras.MetadataOnly) + res, err := a.Authorize(token, req.Input.Subject, req.Input.Groups, verb, req.Input.Tenant, req.Input.Resource, apiGroup, namespaces.UnsortedList(), allowSkipNamespaceInference) if err != nil { statusCode := http.StatusInternalServerError //nolint:errorlint