-
Notifications
You must be signed in to change notification settings - Fork 294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Subset of permissions check on creation #5012
Subset of permissions check on creation #5012
Conversation
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
…rmissions Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
… write test Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## feature/api-tokens #5012 +/- ##
======================================================
- Coverage 71.46% 71.15% -0.31%
======================================================
Files 334 354 +20
Lines 22552 23275 +723
Branches 3590 3680 +90
======================================================
+ Hits 16117 16562 +445
- Misses 4642 4881 +239
- Partials 1793 1832 +39
|
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
…curity into subset Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/action/apitokens/ApiTokenAction.java
Outdated
Show resolved
Hide resolved
List<String> roleIndexPerms = indexPermission.getAllowed_actions(); | ||
|
||
// Check if this role's pattern covers the requested pattern | ||
if (WildcardMatcher.from(rolePatterns).test(requestedPattern)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assume that I have a role with the index_pattern
/index_[0-9]+/
. How can a requestedPattern
look like that replicates this pattern?
At the moment, I have doubts that simple pattern matching can be used to support the full pattern semantics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also does not cover alias semantics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assume that I have a role with the index_pattern /index_[0-9]+/. How can a requestedPattern look like that replicates this pattern?
Do role definitions support this? I have only seen this in a context of masked fields
Edit: Found an example: https://github.com/opensearch-project/security/blob/main/src/test/resources/roles.yml#L20-L33
Edit2: Is that the purpose of the renderedMatcher?
Looks like this is handled by WildcardMatcher itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added alias support in this commit: bf31791. I also shimmed in a way to not pass in a privilegesEvaluationContext into the indexpattern class, as long as you pass in the used components out of the context. What do y'all think of this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also does not cover alias semantics.
How would this not cover aliases? i.e. if a user has a role mapping with read access to logs
and requests a token with read access to logs
(assuming logs
is an alias) won't this work?
Agree on regexp support here, @derek-ho can we throw an IllegalArgumentException if the requested permission is for a regexp?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it doesn't cover alias support for underlying indices, i.e. Users with access to logs
alias would not be able to request api tokens for a subset of any of the underlying indices, even when they have permission to do so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the regexp issue:
I think, one of the fundamental issues here is the following:
Normal users generally do not have access to the roles.yml
configuration. Thus, they do not know how they gain access to the indices - they just know that they have access to the indices they can observe.
Thus, they need to resort to trial-and-error process to see whether they can gain API tokens to an index or whether the roles.yml definition uses a construct that is not supported.
Obviously, this is sub-optimal from a UX point of view.
Ways around that issue - just from the top of my head:
- Just support API token creation for admin users only. These have full access anyway, and all the checks for intersection become unnecessary (as described in Subset of permissions check on creation #5012 (comment)).
- Just support API token creation for concrete index names, not for patterns. Then you do not have to match regexes against regexes.
- Move the process of determining the intersection of requested privileges and available privileges from the token creation to the privilege evaluation phase. Thus, one would change the check "does roles.yml provide privileges" into "does roles.yml provide privileges AND does the api token request the privileges". This would also solve - to a certain extent - the issue of changes to roles.yml.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Obviously, this is sub-optimal from a UX point of view.
Can't this be handled another way though? i.e. prevent using tokens entirely if any role uses regex in index_permissions
or skip the subset check for any role using regexes
?
One other way this could be handled is creating an API that a user can call that helps them comprehend what actions and index patterns they are able to select. This API could also be used to power UX for the feature.
Just support API token creation for concrete index names, not for patterns. Then you do not have to match regexes against regexes.
What about glob-like patterns?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about glob-like patterns?
It is quite a brain-twister to think about matching patterns against patterns 😅 .. but I thought this through again, and there might be another issue.
Consider:
-
I have in roles.yml privileges for the pattern
index_1?
. This gives me privileges onindex_10
,index_11
, etc., but not onindex_1
. -
I now try to request an API token for the index pattern
index_1*
. The patternindex_1?
will positively matchindex_1*
. The*
character will be bound to the?
. However, the patternindex_1*
is broader thanindex_1?
. It will also matchindex_1
, which was not matched byindex_1?
. Thus, we would have a privilege escalation vulnerability.
Of course, this might be quite a theoretical issue where its real world applicability is questionable. Still, IMHO, security software should be also safe of such scenarios.
Thus, I would strongly recommend against matching any patterns against patterns. It is just too fragile.
I would still recommend the approach sketched out above:
Move the process of determining the intersection of requested privileges and available privileges from the token creation to the privilege evaluation phase. Thus, one would change the check "does roles.yml provide privileges" into "does roles.yml provide privileges AND does the api token request the privileges". This would also solve - to a certain extent - the issue of changes to roles.yml.
return DynamicConfigFactory.addStatics(loaded); | ||
} | ||
|
||
protected void authorizeSecurityAccess(RestRequest request) throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about having a dedicated transport action backing the REST action? Then, one would get access control out of the box without needing explicit checks within the action code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with this though it depends on the action.
If the security admin is going to a page in the security dashboards plugin to manage the existing tokens (listing them and performing actions such as revoke) then these should follow the authorization model of other security APIs.
If regular users are allowed to request a token (with a subset of their own permissions) then that could be authorized like other cluster actions in OpenSearch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think (for now) my operating assumption would be that this is an admin-level action to issue api tokens to connect with other services programmatically. Since API tokens (currently) do not follow the new optimized privileges evaluation, and thus have higher performance penalty than other means of authc/z, and thus we should restrict this as much as possible. However, if there is community feedback/requests for this feature, then we can consider it at a later time. Since we want to authorize these APIs like the others, I added this logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, it is a bit difficult for me to review this without a proper scope definition. During review, I learned from you about a couple of assumptions and assumed limitations (such as not using the optimized privilege evaluation code paths, not supporting regexes, etc). IMHO, there should be some definitions in the issue #1504 what's the initial goal to be achieved and what not. That will be then an information to guide the code review process. Otherwise, this review process will get quite inefficient.
If the assumption is indeed that issuing tokens is an admin-level action, I do wonder why we need the matching to existing privileges at all. Won't an admin-level action just be similar to granting new privileges?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If its rolled out to admin users initially at first than I agree, but if the longer term plan is for other users to be able to request tokens then the logic to determine whether the requested permissions are a subset of the user's permissions would be needed. I was thinking to keep the logic simple and only consider: 1) cluster_permissions
and 2) index_permissions
. For index permissions, would it be sufficient to limit the checks to concrete indices and glob-like patterns and prohibit regexes?
In practice, I have not come across many roles definitions where cluster administrators use regex when defining the index patterns in a role. I'm sure its used, but not prevalent from my experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider following a similar model to AbstractApiAction here?
I know this action is not a subset of AbstractApiAction (since it doesn't alter anything in the security index), but maybe it can be modeled similarly? Part of that block also registers an updateHandler to synchronize across the cluster.
Signed-off-by: Derek Ho <dxho@amazon.com>
if (indexPattern.matches( | ||
requestedPattern, | ||
indexNameExpressionResolver, | ||
(String template) -> WildcardMatcher.NONE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit confused about returning WildcardMatcher.NONE
here. Why do we do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since I changed the index pattern class to take in a matcher generator, here I need to pass in a function to generate a wildcardmatcher (as I don't have a PrivilegesEvaluationContext to pass in). In the Index pattern class, the wildcardmatcher is used to evaluate pattern templates/user attributes, which we don't plan to support for API tokens, and thus I passed in a WildCardMatcher that doesn't match anything to not support that use case.
Signed-off-by: Derek Ho <dxho@amazon.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good to me. Left a comment about try to unify endpoint authorization of these new endpoints and existing endpoints that subclass AbstractApiAction.
ConfigConstants.OPENSEARCH_API_TOKENS_INDEX, | ||
"Security API token index" | ||
); | ||
return List.of(systemIndexDescriptor, apiTokenSystemIndexDescriptor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we can start creating these inline.
return DynamicConfigFactory.addStatics(loaded); | ||
} | ||
|
||
protected void authorizeSecurityAccess(RestRequest request) throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider following a similar model to AbstractApiAction here?
I know this action is not a subset of AbstractApiAction (since it doesn't alter anything in the security index), but maybe it can be modeled similarly? Part of that block also registers an updateHandler to synchronize across the cluster.
public boolean matches(String index, PrivilegesEvaluationContext context, Map<String, IndexAbstraction> indexMetadata) | ||
throws PrivilegesEvaluationException { | ||
@FunctionalInterface | ||
public interface MatcherGenerator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This refactor looks ok, but may be unnecessary now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
a8b4ac1
into
opensearch-project:feature/api-tokens
Description
This PR adds permission checks around API token creation, and cleans up misc. items from the feature branch, including authorizing API token endpoint properly and stashing the context in order to perform the API token refresh action without explicit permission to do so. This PR is dependent on #4992 to be merged first, and this PR only contains the following changes:
https://github.com/opensearch-project/security/pull/5012/files/aa506e78eb5699d2580e28d4bb0f0060971449e1..e13c055e063ae3e5ee40386a53155e5c65893b58
Issues Resolved
[List any issues this PR will resolve]
Is this a backport? If so, please add backport PR # and/or commits #, and remove
backport-failed
label from the original PR.Do these changes introduce new permission(s) to be displayed in the static dropdown on the front-end? If so, please open a draft PR in the security dashboards plugin and link the draft PR here
Testing
[Please provide details of testing done: unit testing, integration testing and manual testing]
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.