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

Updatable API keys - noop check #88346

Merged
merged 247 commits into from
Jul 12, 2022
Merged

Conversation

n1v0lg
Copy link
Contributor

@n1v0lg n1v0lg commented Jul 7, 2022

This PR adds a noop check for API key updates. If we detect a noop
update, i.e., an update that does not result in any changes to the
existing doc, we skip the index step and return updated = false in
the response.

This PR also extends test coverage around various corner cases.

@n1v0lg n1v0lg added :Security/Security Security issues without another label and removed :Security/Authentication Logging in, Usernames/passwords, Realms (Native/LDAP/AD/SAML/PKI/etc) labels Jul 11, 2022
for (final String clusterPrivilege : clusterPrivileges) {
putRoleRequest.cluster(clusterPrivilege);
}
putRoleRequest.cluster(clusterPrivileges);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fix test to use all cluster privs, not just last.

@n1v0lg
Copy link
Contributor Author

n1v0lg commented Jul 11, 2022

@elasticmachine run elasticsearch-ci/part-2-fips plz. Unrelated failure.

@n1v0lg n1v0lg marked this pull request as ready for review July 11, 2022 11:20
@n1v0lg n1v0lg requested a review from ywangd July 11, 2022 11:21
Copy link
Member

@ywangd ywangd left a comment

Choose a reason for hiding this comment

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

I have a few comments.

I appreciate your exhaustive work for the isNoop method.

Comment on lines 568 to 569
assert userRoles != null;
final List<RoleDescriptor> currentLimitedByRoleDescriptorRoles = parseRoleDescriptorsBytes(
Copy link
Member

Choose a reason for hiding this comment

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

Nit: The name currentLimitedByRoleDescriptorRoles is a bit awkward. It should just be currentLimitedByRoleDescriptors. Also, userRoles really should be userRoleDescriptors since the code has distinct concepts for role, role name and role descriptor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh whoops currentLimitedByRoleDescriptorRoles is definitely a copy&mishap. Thanks for catching 👍

Also, userRoles really should be userRoleDescriptors since the code has distinct concepts for role, role name and role descriptor.

I'm good with that, I went with userRoles because that's what it was called before the work on updatable API keys elsewhere in this class.

Copy link
Member

Choose a reason for hiding this comment

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

I went with userRoles because that's what it was called before the work on updatable API keys elsewhere in this class.

I figured. I wasn't a fan of this name in other places as well. But we never bothered to change them ...

Comment on lines 493 to 498
return builder.endObject();
return new ApiKeyDocBuilderWithNoopFlag(
builder.endObject(),
isNoop(currentApiKeyDoc, targetDocVersion, authentication, request, userRoles)
);
Copy link
Member

Choose a reason for hiding this comment

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

This works. But I think it can be simplified a bit if:

  1. Perform isNoop check in the beginning of the method. So we can avoid creating and preparing the builder all together if no change.
  2. Remove the ApiKeyDocBuilderWithNoopFlag record type and just return null if there is no change or the actual builder if there are any changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Performing the noop check at the beginning of the method is how I had this structured initially, so I'm happy to switch it back to that. My thinking around always building was that it makes the method more generic and gives it simpler semantics: it creates the object and tells the caller if it's a noop -- then it's up to the caller to decide what to do with it.

This is just to give context. I'm good with your suggestion 👍

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the context. It helps. I think it will at least partially ease your concern if we rename this method to maybeBuildUpdatedDocument to indicate caller needs to perform null check.

Comment on lines 1842 to 1856
// Role descriptor corresponding to SecuritySettingsSource.TEST_ROLE_YML to ensure that these stay the same and
// don't cause an update
Set.of(
new RoleDescriptor(
TEST_ROLE,
new String[] { "ALL" },
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder()
.indices("*")
.allowRestrictedIndices(true)
.privileges("ALL")
.build() },
null
)
),
Copy link
Member

Choose a reason for hiding this comment

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

For this assumption to uphold for future changes, it would be better if we check that it indeed results into a noop update before this check. Otherwise, any future change to the test role will essentially make this test not doing anything.

final List<RoleDescriptor> currentLimitedByRoleDescriptorRoles = parseRoleDescriptorsBytes(
request.getId(),
apiKeyDoc.limitedByRoleDescriptorsBytes,
RoleReference.ApiKeyRoleType.LIMITED_BY
Copy link
Member

@ywangd ywangd Jul 12, 2022

Choose a reason for hiding this comment

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

This might be OK though the behaviour is not perfect.

The method parseRoleDescriptorsBytes replaces old superuser role definition (7.x) with the new one (8.0). This means it can return a role descriptor that is different from what is actually stored. If we only test against the returned role descriptor, we may miss an update because the equality does not apply to the actual stored descriptor.

In practice this can only happen in a mix version cluster (8.4 + 7.17). If update skips for a superuser limitedByRoleDescriptor. It may not be too big of a deal especially since the legacy descriptor will be handled correctly when loaded (at authentication time).

Alternatively, we can check whether the current doc has version 7.x and the returned a descriptor is a superuser. If answers to both are true, it is not a noop (because we will be updating the legacy superuser descriptor to the new one). This would also means after update, the key will lose the legacy superuser behaviour even when it is used on a 7.17 node. But that is probably OK (and even desired due to consistency). But this is still not always correct because if the key is updated for a second time, the check would suggest an update is needed but it is not true (because it was updated already in the first update).

It is definitely an edge case. I don't really have a strong opinion. But at least it worths some code level comment. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed this on Slack:

For this PR, we will simply document the behavior as a comment. We will tweak the behavior in a follow up PR to call parseRoleDescriptorsBytes such that it does not replace superuser role descriptors, effectively allowing legacy descriptors to be updated to 8.x descriptors.

@n1v0lg n1v0lg requested a review from ywangd July 12, 2022 09:51
@n1v0lg
Copy link
Contributor Author

n1v0lg commented Jul 12, 2022

@elasticmachine run elasticsearch-ci/part-2

Copy link
Member

@ywangd ywangd left a comment

Choose a reason for hiding this comment

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

LGTM

@n1v0lg n1v0lg merged commit 28048a5 into elastic:master Jul 12, 2022
@n1v0lg n1v0lg deleted the update-api-keys-noop-check branch July 12, 2022 13:34
weizijun added a commit to weizijun/elasticsearch that referenced this pull request Jul 13, 2022
* upstream/master: (38 commits)
  Simplify map copying (elastic#88432)
  Make DiffableUtils.diff implementation agnostic (elastic#88403)
  Ingest: Start separating Metadata from IngestSourceAndMetadata (elastic#88401)
  Move runtime fields base scripts out of scripting fields api package. (elastic#88488)
  Enable TRACE Logging for test and increase timeout (elastic#88477)
  Mute ReactiveStorageIT#testScaleDuringSplitOrClone (elastic#88480)
  Track the count of failed invocations since last successful policy snapshot (elastic#88398)
  Avoid noisy exceptions on data nodes when aborting snapshots (elastic#88476)
  Fix ReactiveStorageDeciderServiceTests testNodeSizeForDataBelowLowWatermark (elastic#88452)
  INFO logging of snapshot restore and completion (elastic#88257)
  unmute test (elastic#88454)
  Updatable API keys - noop check (elastic#88346)
  Corrected an incomplete sentence. (elastic#86542)
  Use consistent shard map type in IndexService (elastic#88465)
  Stop registering TestGeoShapeFieldMapperPlugin in ESIntegTestCase (elastic#88460)
  TSDB: RollupShardIndexer logging improvements (elastic#88416)
  Audit API key ID when create or grant API keys (elastic#88456)
  Bound random negative size test in SearchSourceBuilderTests#testNegativeSizeErrors (elastic#88457)
  Updatable API keys - logging audit trail event (elastic#88276)
  Polish reworked LoggedExec task (elastic#88424)
  ...

# Conflicts:
#	x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/v2/RollupShardIndexer.java
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>enhancement :Security/Security Security issues without another label Team:Clients Meta label for clients team Team:Security Meta label for security team v8.4.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants