Ignore errors caused by unkonwn scopes where it makes sense to do so.#20911
Conversation
ce9b412 to
cc8d9e7
Compare
| func (userEvaluator) Handles(scope string) bool { | ||
| return strings.HasPrefix(scope, UserIndicator) | ||
| switch scope { | ||
| case UserFull, UserInfo, UserAccessCheck, UserListScopedProjects, UserListAllProjects: |
There was a problem hiding this comment.
Probably just easier to check Validate(scope) == nil since you are basically binding their logic.
| } | ||
| } | ||
|
|
||
| if !found { |
There was a problem hiding this comment.
Hmm let us add something like ignoreUnhandledScopes as a bool param and let the caller tell us this is ok to ignore.
There was a problem hiding this comment.
all callers will tell us that though ... can do it, but sounds a bit redundant
There was a problem hiding this comment.
Seems bad to just drop it. I keep wondering if it would just be better to let the callers ignore all errors. Sigh, I cannot tell what would be best here.
There was a problem hiding this comment.
Ok rebased to what it looks like with the boolean ...
|
@openshift/sig-security |
ca267a1 to
ac4ef34
Compare
ac4ef34 to
dd48375
Compare
dd48375 to
3dda4db
Compare
| type userEvaluator struct{} | ||
|
|
||
| func (userEvaluator) Handles(scope string) bool { | ||
| return strings.HasPrefix(scope, UserIndicator) |
There was a problem hiding this comment.
I was expecting something like return e.Validate(scope) == nil since that forces the logic into one place.
There was a problem hiding this comment.
it's the same just makes more semantic sense inside Validate() to check for Handles() rather than in Handles() to call Validate()
|
/retest |
|
Will give David/others a chance to look. |
|
/lgtm |
|
If I were trying to consume a different kind of scope from a different provider, I would expect them to sanitize the scopes to indicate which ones they expect our authorizer to enforce and provide a list in a different user.Extra key for all their scopes. That would clearly delineate responsibility of enforcement instead of ignoring. I accept that this may work, but it does seem to increase the risk of bugs. If you decide to go with this anyway, you need tests ensuring that properly fail closed when scopes are presented and none are recognized. |
| // ScopesToVisibleNamespaces returns a list of namespaces that the provided scopes have "get" access to. | ||
| // This exists only to support efficiently list/watch of projects (ACLed namespaces) | ||
| func ScopesToVisibleNamespaces(scopes []string, clusterRoleGetter rbaclisters.ClusterRoleLister) (sets.String, error) { | ||
| func ScopesToVisibleNamespaces(scopes []string, clusterRoleGetter rbaclisters.ClusterRoleLister, ignoreUnhandledScopes bool) (sets.String, error) { |
There was a problem hiding this comment.
It appears that every caller is setting this to true. Why have the option?
There was a problem hiding this comment.
I did not want to blanket remove the "unhandled scope" errors...
|
If I were trying to consume a different kind of scope from a
different provider, I would expect them to sanitize the scopes to
indicate which ones they expect *our* authorizer to enforce and
provide a list in a different user.Extra key for all their scopes.
This is not how an external auth server works, it is safer and more
secure for us to properly handle extra scopes than depend on them
filtering stuff.
That would clearly delineate responsibility of enforcement instead
of ignoring.
We are responsible for enforcing, and we enforce only scopes we
understand, the rest we do not care for. There is no risk in ignoring
scopes we do not understand as individual scopes allow things they do
not restrict, so we fail safe anyway.
It *also* allows us to upgrade servers where we need to add a new scope
without causing old servers to completely fail all requests, and I
think this alone is a big plus.
I accept that this may work, but it does seem to increase the risk of
bugs. If you decide to go with this anyway, you need tests ensuring
that properly fail closed when scopes are presented and none are
recognized.
We have unit tests that show that unknown scopes return an error, and
the 2 calls that we depend on are just fine ignoring unknown scopes as
there is no way they are going to grant any more access than they do
now.
Unfortunately creating also an integration test with our own oauth
server and unknown scopes is not possible, but we'll definitely test as
soon as we have integration tests with Keycloak
|
|
Truns out we have a way to test unknown scopes by using the webhook authentication interface, so I'm going to add to the webhook integration test as well |
|
/hold |
0f000b8 to
3ea2d37
Compare
enj
left a comment
There was a problem hiding this comment.
Some important role: bits, otherwise minor nits.
test/integration/scopes_test.go
Outdated
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
|
|
||
| _, haroldClientConfig, err := testutil.GetClientForUser(clusterAdminClientConfig, "harold") |
There was a problem hiding this comment.
CreateNewProject already gives you a haroldClientConfig
test/integration/scopes_test.go
Outdated
| Scopes: []string{"user:info", "user:garbage", "garbage"}, | ||
| }, | ||
| } | ||
| rulesReviewWithGarbageObj, err := authzv1client.SelfSubjectRulesReviews(projectName).Create(rulesReviewWithGarbage) |
test/integration/scopes_test.go
Outdated
| } | ||
| rulesReviewOnlyGarbageObj, err := authzv1client.SelfSubjectRulesReviews(projectName).Create(rulesReviewOnlyGarbage) | ||
|
|
||
| //Check same rules, first we need to convert SSRR result from: |
There was a problem hiding this comment.
Can you move the conversion to its own function, it is pretty awful.
test/integration/scopes_test.go
Outdated
| // Test with valid scope as baseline | ||
| rulesReview := &authorizationv1.SelfSubjectRulesReview{ | ||
| Spec: authorizationv1.SelfSubjectRulesReviewSpec{ | ||
| Scopes: []string{"user:info"}, |
There was a problem hiding this comment.
Add a valid role: based scope for admin in the current namespace.
test/integration/scopes_test.go
Outdated
| // Try adding garbage scope and check we have the same perms as baseline | ||
| rulesReviewWithGarbage := &authorizationv1.SelfSubjectRulesReview{ | ||
| Spec: authorizationv1.SelfSubjectRulesReviewSpec{ | ||
| Scopes: []string{"user:info", "user:garbage", "garbage"}, |
There was a problem hiding this comment.
Also the same valid role: from above and also add garbage role:
test/integration/scopes_test.go
Outdated
| t.Fatalf("unexpected error getting user harold client config: %v", err) | ||
| } | ||
|
|
||
| authzv1client, err := authorizationv1typedclient.NewForConfig(haroldClientConfig) |
There was a problem hiding this comment.
Is there a NewForConfigOrDie to remove some more boilerplate?
test/integration/scopes_test.go
Outdated
| Scopes: []string{"user:garbage", "garbage"}, | ||
| }, | ||
| } | ||
| rulesReviewOnlyGarbageObj, err := authzv1client.SelfSubjectRulesReviews(projectName).Create(rulesReviewOnlyGarbage) |
test/integration/scopes_test.go
Outdated
| // Make sure no rules (beyond baseline) when only garbage scopes are present | ||
| rulesReviewOnlyGarbage := &authorizationv1.SelfSubjectRulesReview{ | ||
| Spec: authorizationv1.SelfSubjectRulesReviewSpec{ | ||
| Scopes: []string{"user:garbage", "garbage"}, |
test/integration/scopes_test.go
Outdated
| // semantically identical | ||
| baselineRules := []rbacv1.PolicyRule{authorizationapi.DiscoveryRule} | ||
|
|
||
| covers, unexpectedRules := rbacvalidation.Covers(baselineRules, rbacv1Rules) |
There was a problem hiding this comment.
An assertEqual helper function would be nice.
599453b to
6f664ef
Compare
|
Ok all the requests should have been handled now, and I have tests that exercise both ScopesToRules and ScopesToVisibleNamespaces |
22fe81e to
e19ea8d
Compare
test/integration/scopes_test.go
Outdated
| if err != nil { | ||
| return false, err | ||
| } | ||
| if len(projects.Items) > 0 { |
There was a problem hiding this comment.
return len(projects.Items) > 0, nil
test/integration/scopes_test.go
Outdated
| projectClient := projectclient.NewForConfigOrDie(&impersonatingConfig) | ||
|
|
||
| var projects *projectapiv1.ProjectList | ||
| err = wait.Poll(100*time.Millisecond, 10*time.Second, |
There was a problem hiding this comment.
Probably want 30 seconds, CI can be slow and it is not worth the flake.
test/integration/scopes_test.go
Outdated
| &badScopesUserInfo, *clusterAdminClientConfig) | ||
| badScopesProjectClient := projectclient.NewForConfigOrDie(&badScopesImpersonatingConfig) | ||
| projects, err = badScopesProjectClient.ProjectV1().Projects().List(metav1.ListOptions{}) | ||
| if !kapierrors.IsForbidden(err) { |
There was a problem hiding this comment.
Add a check to fail on nil error and then change this to if !kapierrors.IsForbidden(err) || !strings.Contains(err.Error(), "prevent this action, additionally the following non-fatal errors were reported"
Forbidden is the default fall through case so let us make sure the scope message is there.
Added integration tests Signed-off-by: Simo Sorce <simo@redhat.com>
e19ea8d to
0f2438c
Compare
|
Ok addressed the last nits too. |
|
/lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: enj, mrogers950, simo5 The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
/retest Please review the full test history for this PR and help us cut down flakes. |
This allows us to interop with an OIDC/OAuth server that sends us tokens with additional scopes used for other applications than Openshift