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

Convert remote license checker to use LicensedFeature #79876

Merged
merged 20 commits into from
Oct 28, 2021

Conversation

rjernst
Copy link
Member

@rjernst rjernst commented Oct 26, 2021

The RemoteClusterLicenseChecker pulls the license level of a remote
cluster and checks it allows a local feature to communicate with that
cluster. It does the check with a lambda, but these methods could be out
of sync with the actual licensed feature. This commit converts the
remote license checker to take in the feature object that should be
checked.

The RemoteClusterLicenseChecker pulls the license level of a remote
cluster and checks it allows a local feature to communicate with that
cluster. It does the check with a lambda, but these methods could be out
of sync with the actual licensed feature. This commit converts the
remote license checker to take in the feature object that should be
checked.
@rjernst rjernst added >refactoring :Security/License License functionality for commercial features v8.0.0 labels Oct 26, 2021
@rjernst rjernst requested a review from ywangd October 26, 2021 21:51
@elasticmachine elasticmachine added the Team:Security Meta label for security team label Oct 26, 2021
@elasticmachine
Copy link
Collaborator

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

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 question for whether null is equivalent to "alway-true" license checker.

Comment on lines 132 to 133
* Constructs a remote cluster license checker with the specified license predicate for checking license compatibility. The predicate
* does not need to check for the active license state as this is handled by the remote cluster license checker.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* Constructs a remote cluster license checker with the specified license predicate for checking license compatibility. The predicate
* does not need to check for the active license state as this is handled by the remote cluster license checker.
* Constructs a remote cluster license checker with the specified licensed feature for checking license compatibility. The feature
* does not need to check for the active license state as this is handled by the remote cluster license checker.

Comment on lines 169 to 171
if (licenseInfo.getStatus() == LicenseStatus.ACTIVE == false
|| isAllowedByOperationMode(License.OperationMode.parse(licenseInfo.getMode()),
feature.getMinimumOperationMode()) == false) {
Copy link
Member

Choose a reason for hiding this comment

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

It's not a real issue but I feel a bit itchy that we don't check whether the feature actual needs an Active license here. It does and requiring active license is a norm going forward. But for the sake of clarity in this particular scope, I'd have something like

Suggested change
if (licenseInfo.getStatus() == LicenseStatus.ACTIVE == false
|| isAllowedByOperationMode(License.OperationMode.parse(licenseInfo.getMode()),
feature.getMinimumOperationMode()) == false) {
if ((feature.isNeedsActive() && licenseInfo.getStatus() == LicenseStatus.ACTIVE == false)
|| isAllowedByOperationMode(License.OperationMode.parse(licenseInfo.getMode()),
feature.getMinimumOperationMode()) == false) {

Comment on lines 277 to 278
if (remoteClusterLicenseInfo.licenseInfo().getStatus() != LicenseStatus.ACTIVE) {
error.append(String.format(Locale.ROOT, "the license on cluster [%s] is not active", remoteClusterLicenseInfo.clusterAlias()));
Copy link
Member

Choose a reason for hiding this comment

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

Similarly, this assumes the feature requires an active license, which is true. But the assumption is somewhat disconnected.

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've adjusted this so that if the feature is present, it checks the isNeedsActive(). It also checks if the feature is not present, to match the current behavior where an active license is still needed for the BASIC functionality.

Comment on lines 134 to 133
DiscoveryNode.isRemoteClusterClient(settings)
/* transforms are BASIC so always allowed, no need to check license */
? new RemoteClusterLicenseChecker(client, mode -> true) : null,
/* transforms are BASIC so always allowed, no need to check license */
null,
Copy link
Member

Choose a reason for hiding this comment

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

I am familiar with code for the transform feature. But it seems a null license checker would disable CCS according to the following comment:

* @param remoteClusterLicenseChecker A RemoteClusterLicenseChecker or null if CCS is disabled

So there is a difference between null and a license checker that always return true?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch, I misunderstood what null meant here. I've changed the remote checker so that null can be passed into the ctor to mean the checker is a noop.

Comment on lines 88 to 87
DiscoveryNode.isRemoteClusterClient(settings)
/* transforms are BASIC so always allowed, no need to check license */
? new RemoteClusterLicenseChecker(client, mode -> true) : null,
/* transforms are BASIC so always allowed, no need to check license */
null,
Copy link
Member

Choose a reason for hiding this comment

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

Same as above

@rjernst
Copy link
Member Author

rjernst commented Oct 27, 2021

@ywangd This is read for another review.

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 left a few more suggestions. Thanks!

Comment on lines 170 to 175
if (((feature == null || feature.isNeedsActive()) && licenseInfo.getStatus() != LicenseStatus.ACTIVE)
|| feature != null
&& isAllowedByOperationMode(
License.OperationMode.parse(licenseInfo.getMode()),
feature.getMinimumOperationMode()
) == false) {
Copy link
Member

Choose a reason for hiding this comment

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

Can we break into smaller and more readable pieces? I had to copy/paste it into an IDE to parse it.

I also think the logic is incorrect when feature !=null. When feature != null, the second half of the condition is triggered and it bypasses the active license check because it is or'd.

Copy link
Member Author

@rjernst rjernst Oct 27, 2021

Choose a reason for hiding this comment

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

The formatting got destroyed by spotless. I will try to restructure, but I think the logic is correct. This entire conditional is a short-circuit, so the first part of the OR is whether the license is active. If it is active (first part evaluates to false), then the second part is checked, which is only necessary when feature != null.

Copy link
Member

Choose a reason for hiding this comment

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

OK I think you are right. The whole thing is a negative check, i.e. it checks for violation instead of conformation. That's what got me confused initially. I think this is also a sign that the logic here is not easy to parse. I'd appreciate any improvement for readability.

? new RemoteClusterLicenseChecker(client, mode -> true)
? new RemoteClusterLicenseChecker(client, null)
Copy link
Member

Choose a reason for hiding this comment

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

Exposing a null feature to the caller does not seem to be very intuitive. I think a more descriptive approach is to have a static method that returns an instance of license checker, e.g. something like

RemoteClusterLicenseChecker.activeLicenseChecker(client)

Copy link
Member Author

Choose a reason for hiding this comment

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

Given there are only 2 callers with null, I see this as a special case not warranting another method. I'm inclined to leave this for now.

@rjernst
Copy link
Member Author

rjernst commented Oct 28, 2021

@ywangd I pushed an update which makes the logic much simpler to understand.

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

Thanks for the iterations!

Comment on lines 288 to 292
} else if (feature != null) {
assert isAllowedByOperationMode(
License.OperationMode.parse(remoteClusterLicenseInfo.licenseInfo().getMode()),
feature.getMinimumOperationMode()
) == false : "license must be incompatible to build error message";
Copy link
Member

Choose a reason for hiding this comment

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

Nit: We no longer have a else block which is for the impossible case of feature == null && licenseStatus == ACTIVE. But should we add an assertion for it?

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 will remove feature != null here. The isAllowed assertion now takes into account the null feature.

@rjernst rjernst merged commit af226be into elastic:master Oct 28, 2021
@rjernst rjernst deleted the license/remote branch October 28, 2021 01:32
rjernst added a commit to rjernst/elasticsearch that referenced this pull request Oct 28, 2021
The RemoteClusterLicenseChecker pulls the license level of a remote
cluster and checks it allows a local feature to communicate with that
cluster. It does the check with a lambda, but these methods could be out
of sync with the actual licensed feature. This commit converts the
remote license checker to take in the feature object that should be
checked.
@elasticsearchmachine
Copy link
Collaborator

💔 Backport failed

Status Branch Result
8.0
7.16 Commit could not be cherrypicked due to conflicts

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

rjernst added a commit to rjernst/elasticsearch that referenced this pull request Oct 28, 2021
The RemoteClusterLicenseChecker pulls the license level of a remote
cluster and checks it allows a local feature to communicate with that
cluster. It does the check with a lambda, but these methods could be out
of sync with the actual licensed feature. This commit converts the
remote license checker to take in the feature object that should be
checked.
elasticsearchmachine pushed a commit that referenced this pull request Oct 28, 2021
The RemoteClusterLicenseChecker pulls the license level of a remote
cluster and checks it allows a local feature to communicate with that
cluster. It does the check with a lambda, but these methods could be out
of sync with the actual licensed feature. This commit converts the
remote license checker to take in the feature object that should be
checked.
rjernst added a commit that referenced this pull request Oct 28, 2021
The RemoteClusterLicenseChecker pulls the license level of a remote
cluster and checks it allows a local feature to communicate with that
cluster. It does the check with a lambda, but these methods could be out
of sync with the actual licensed feature. This commit converts the
remote license checker to take in the feature object that should be
checked.
weizijun added a commit to weizijun/elasticsearch that referenced this pull request Oct 28, 2021
…formance

* upstream/master: (153 commits)
  [ML] update truncation default & adding field output when input is truncated (elastic#79942)
  [ML] stop using isAllowedByLicense for model license checks (elastic#79908)
  [ML] Retain built-in ML roles granting Kibana privileges (elastic#80014)
  [Transform] remove old mixed cluster BWC layers, not required for 8x (elastic#79927)
  Increase test timeout for CoordinatorTests testAllSearchesExecuted
  [Transform] add rolling upgrade tests for upgrade endpoint (elastic#79721)
  [ML] Update trained model docs for truncate parameter for bert tokenization (elastic#79652)
  `CoordinatorTests` sometimes needs three term bumps (elastic#79574)
  [ML] Account for service being triggered twice in tests (elastic#80000)
  SearchContext: remove unused variable (elastic#79917)
  Revert "Deprecate resolution loss on date field (elastic#78921)" (elastic#79914)
  Re-enable GeoIpDownloaderIT#testStartWithNoDatabases() (elastic#79907)
  Fix SnapshotBasedIndexRecoveryIT#testSeqNoBasedRecoveryIsUsedAfterPrimaryFailOver (elastic#79469)
  Fix RecoverySourceHandlerTests (elastic#79546)
  SQL: stabilize SqlSearchPageTimeoutIT (elastic#79928)
  Wait 3 seconds for the server to reload trust (elastic#79778)
  Skip automatically preserved request headers when rewriting (elastic#79973)
  Check whether stdout is a real console (elastic#79882)
  Convert remote license checker to use LicensedFeature (elastic#79876)
  Miscellaneous fixes for LDAP SDK v6 upgrade (elastic#79891)
  ...

# Conflicts:
#	libs/x-content/src/main/java/org/elasticsearch/xcontent/support/filtering/FilterPath.java
#	libs/x-content/src/test/java/org/elasticsearch/xcontent/support/filtering/FilterPathGeneratorFilteringTests.java
#	libs/x-content/src/test/java/org/elasticsearch/xcontent/support/filtering/FilterPathTests.java
@danhermann danhermann added v7.16.0 and removed v7.16.1 labels Nov 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>refactoring :Security/License License functionality for commercial features Team:Security Meta label for security team v7.16.0 v8.0.0-beta1 v8.1.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants