Skip to content
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

Superuser fastpath for indexAccessControl #78498

Merged

Conversation

ywangd
Copy link
Member

@ywangd ywangd commented Sep 30, 2021

This PR adds a fast path for loading authorizedIndices and computing
indicesAccessControl if the role has all access to all indices.

A role is considered to have all access to all indices if any of its
IndicesPermission#Group satisfy the following criteria:

  1. Any of the index patterns is a simple match-all wildcard, i.e. "*"
  2. It allows access to restricted indices
  3. It grants the "all" index privilege
  4. It has no DLS or FLS

An example of such role is the builtin superuser role.

Note the fastpath does not apply to roles that have "effective" but not
direct "all access of all indices". For example, if the "effective"
access is achieved by combining multiple Groups belong to the role, or
combining multiple index patterns within a single Group, the fastpath
will not apply.

This fast path is provided so that we have a reference baseline for
authorization related performance which is useful for both production
and troubleshooting.

This PR adds a fast path for loading authorizedIndices and computing
indicesAccessControl if the role has all access to all indices.

A role is considered to have all access to all indices if any of its
IndicesPermission#Group satisfy the following criteria:
1. Any of the index patterns is a simple match-all wildcard, i.e. "*"
2. It allows access to restricted indices
3. It grants the "all" index privilege
4. It has no DLS or FLS

An example of such role is the builtin superuser role.

Note the fastpath does not apply to roles that have "effective" but not
direct "all access of all indices". For example, if the "effective"
access is achieved by combining multiple Groups belong to the role, or
combining multiple index patterns within a single Group. This fast path
is provided so that we have a reference baseline for authorization
related performance which is useful for both production use and
troubleshooting.
@ywangd ywangd added >enhancement :Security/Authorization Roles, Privileges, DLS/FLS, RBAC/ABAC v8.0.0 v7.16.0 labels Sep 30, 2021
@elasticmachine elasticmachine added the Team:Security Meta label for security team label Sep 30, 2021
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-security (Team:Security)

@ywangd
Copy link
Member Author

ywangd commented Oct 1, 2021

@elasticmachine update branch

@@ -118,6 +118,10 @@ private StringMatcher indexMatcher(Collection<String> ordinaryIndices, Collectio
return groups;
}

public boolean isTotal() {
return Arrays.stream(groups).anyMatch(Group::isTotal);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can make this stricter to require the groups to have only one member, which is the case for superuser role and the API keys created by it.

Copy link
Contributor

@albertzaharovits albertzaharovits Oct 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the condition is OK like this.

But this being a public method irks me a little bit.
I think we can avoid it by including this check at the beginning of the authorize method.
We get one less method to worry about for the caller, especially since the isTotal is fuzzy (isTotal can be false but the permission to actually grant all indices).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we also avoid the companion isTotal public method on the Role and LimitedRole (hopefully).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated as suggested.

@ywangd ywangd changed the title Superuser fastpath for index authorization Superuser fastpath for indexAccessControl Oct 11, 2021

@Override
public IndexAccessControl getIndexPermissions(String index) {
return ALLOW_ALL_INDEX_ACCESS_CONTROL;
Copy link
Contributor

@albertzaharovits albertzaharovits Oct 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it that we now return an IndexAccessControl instance instead of null, for the "allow all" case.

return new IndexAuthorizationResult(true, accessControl);
// Fast path if the role can access all indices, e.g. superuser
if (role.allowAllIndices()) {
return new IndexAuthorizationResult(true, IndicesAccessControl.allowAll());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion from the other comments is to push the fast-path one level down in the role.authorize call, for slight reasons of interface clarity, but I also prefer it because at this level we avoid introducing a bypass for such an important operation as authorization (I feel that the bypass inside the authorize method is more tolerable mentally).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++

Copy link
Contributor

@albertzaharovits albertzaharovits left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had more of a style suggestion, in order to limit the portion of the code that has to "know" of the bypass. I hope you find it useful.

I think I would've implemented it as a bypass for the static superuser role only, but I now believe being more generic and inspecting the index permissions is superior, although slightly harder to explain in words.

return limitedByIndicesAccessControl;
} else if (limitedByIndicesAccessControl instanceof AllowAllIndicesAccessControl) {
return this;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to have 3 different (overlapping) methods for deciding whether an IndicesAccessControl object is the allow all object.

  1. instanceof AllowAllIndicesAccessControl
  2. == AllowAllIndicesAccessControl.ALLOW_ALL_INDICES_ACCESS_CONTROL
  3. isAllowAll() (implemented via method 2)

Can we standardise?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the 3rd method (it is actually not used ...) and thus remove 2 as well. Option 1 is the standard now.

private static class AllowAllIndicesAccessControl extends IndicesAccessControl {

private static final IndicesAccessControl ALLOW_ALL_INDICES_ACCESS_CONTROL = new AllowAllIndicesAccessControl();
private static final IndexAccessControl ALLOW_ALL_INDEX_ACCESS_CONTROL = new IndexAccessControl(true, null, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this a non-static field?

Since ALLOW_ALL_INDICES_ACCESS_CONTROL is a singleton, having a non-static field for ALLOW_ALL_INDEX_ACCESS_CONTROL makes no difference in terms on memory usage or execution time.
However, it makes the code much cleaner because then we can have 1 static field for the singleton INSTANCE which is the one you want to use when you need an instance of AllowAllIndicesAccessControl and the other field becomes a member field and there's no chance of ambiguity because it's clearly an internal implementation detail.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. updated as suggested.

return new IndexAuthorizationResult(true, accessControl);
// Fast path if the role can access all indices, e.g. superuser
if (role.allowAllIndices()) {
return new IndexAuthorizationResult(true, IndicesAccessControl.allowAll());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++

@tvernum
Copy link
Contributor

tvernum commented Oct 15, 2021

I don't love the idea of optimizing for superuser because we want to discourage people from using superuser for anything other than maintenance.
However, if we follow Albert's suggestions and push the optimizations into Role.authorize then (in the future) we can look at how to adapt it for other more reasonable use cases (for example a user with monitor over *)

@ywangd
Copy link
Member Author

ywangd commented Oct 15, 2021

@albertzaharovits @tvernum

Thanks for looking into this PR. Sorry there was some messiness. I agree on the main suggestion which is to push down the short circuit logic to Role and IndicesPermission. I have updated the code accordingly so that all special handling is inside IndicesPermission.

However, if we follow Albert's suggestions and push the optimizations into Role.authorize then (in the future) we can look at how to adapt it for other more reasonable use cases (for example a user with monitor over *)

This is a good point. Being able to short circuit for more scenarios will definitely be a bonus.

@ywangd ywangd requested a review from tvernum October 15, 2021 05:24

private static final IndicesAccessControl INSTANCE = new AllowAllIndicesAccessControl();

private final IndexAccessControl ALLOW_ALL_INDEX_ACCESS_CONTROL = new IndexAccessControl(true, null, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Since this is no longer static it shouldn't have an ALL_CAPS_NAME either.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -336,7 +336,7 @@ private void authorizeAction(final RequestInfo requestInfo, final String request
if (ClusterPrivilegeResolver.isClusterAction(action)) {
final ActionListener<AuthorizationResult> clusterAuthzListener =
wrapPreservingContext(new AuthorizationResultListener<>(result -> {
threadContext.putTransient(INDICES_PERMISSIONS_KEY, IndicesAccessControl.ALLOW_ALL);
threadContext.putTransient(INDICES_PERMISSIONS_KEY, IndicesAccessControl.allowAll());
listener.onResponse(null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how GH throws in a main source code change in between test ones, to keep us on our toes about tests.

Copy link
Contributor

@albertzaharovits albertzaharovits left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thanks Yang!

@ywangd ywangd added the auto-backport-and-merge Automatically create backport pull requests and merge when ready label Oct 18, 2021
@ywangd ywangd merged commit 5ae1518 into elastic:master Oct 18, 2021
@elasticsearchmachine
Copy link
Collaborator

💔 Backport failed

Status Branch Result
7.x Commit could not be cherrypicked due to conflicts

You can use sqren/backport to manually backport by running backport --upstream elastic/elasticsearch --pr 78498

ywangd added a commit to ywangd/elasticsearch that referenced this pull request Oct 18, 2021
This PR adds a fast path for computing indicesAccessControl if
the role has all access to all indices.

A role is considered to have all access to all indices if any of its
IndicesPermission#Group satisfy the following criteria:

1. Any of the index patterns is a simple match-all wildcard, i.e. "*"
2. It allows access to restricted indices
3. It grants the "all" index privilege
4. It has no DLS or FLS

An example of such role is the builtin superuser role.

Note the fastpath does not apply to roles that have "effective" but not
direct "all access of all indices". For example, if the "effective"
access is achieved by combining multiple Groups belong to the role, or
combining multiple index patterns within a single Group. This fast path
is provided so that we have a reference baseline for authorization
related performance which is useful for both production use and
troubleshooting.
elasticsearchmachine pushed a commit that referenced this pull request Oct 18, 2021
This PR adds a fast path for computing indicesAccessControl if
the role has all access to all indices.

A role is considered to have all access to all indices if any of its
IndicesPermission#Group satisfy the following criteria:

1. Any of the index patterns is a simple match-all wildcard, i.e. "*"
2. It allows access to restricted indices
3. It grants the "all" index privilege
4. It has no DLS or FLS

An example of such role is the builtin superuser role.

Note the fastpath does not apply to roles that have "effective" but not
direct "all access of all indices". For example, if the "effective"
access is achieved by combining multiple Groups belong to the role, or
combining multiple index patterns within a single Group. This fast path
is provided so that we have a reference baseline for authorization
related performance which is useful for both production use and
troubleshooting.
weizijun added a commit to weizijun/elasticsearch that referenced this pull request Oct 18, 2021
* upstream/master:
  Changing test keytab to use aes256-cts-hmac-sha1-96 instead of des3-cbc-sha1-kd (elastic#78703)
  Add support for configuring HNSW parameters (elastic#79193)
  Deprecate resolution loss on date field (elastic#78921)
  Add Optional to Configure bind user (elastic#78303)
  Adapt BWC after backporting elastic#78765 (elastic#79350)
  [DOCS] Add deprecation notice for reset password tool (elastic#78793)
  added test for flattened type in top_metrics.yml (elastic#78960)
  [DOCS] Fixes indentation issue in GET trained models API docs. (elastic#79347)
  Fix parsing of PBES2 encrypted PKCS#8 keys (elastic#78904)
  Mute testReindex (elastic#79343)
  Node level can match action (elastic#78765)
  Fix duplicate license header in source files (elastic#79236)
  AllowAll for indicesAccessControl (elastic#78498)
  Better logging and internal user handling for operator privileges (elastic#79331)

# Conflicts:
#	server/src/main/java/org/elasticsearch/index/mapper/MappingParser.java
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-backport-and-merge Automatically create backport pull requests and merge when ready >enhancement :Security/Authorization Roles, Privileges, DLS/FLS, RBAC/ABAC Team:Security Meta label for security team v7.16.0 v8.0.0-beta1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants