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

Extract License service from CCR and Watcher into license_api_guard plugin in x-pack #95973

Merged
merged 20 commits into from
Apr 15, 2021

Conversation

cjcenizal
Copy link
Contributor

@cjcenizal cjcenizal commented Mar 31, 2021

Release note

Cross-cluster Replication and Watcher used to log misleading warnings about the basic license on Kibana startup. These warnings have been removed.

Summary

Fixes #95032

When you're on the Basic license, Watcher and CCR log disconcerting license failure messages to the command line on startup. These messages are phrased in a way that makes users think something is wrong, when in reality nothing is wrong. This PR makes two fundamental changes:

  • In terms of behavior, license failure messages are only logged when a consumer attempts to access a disallowed endpoint. Because the UI doesn't display apps that aren't covered by the user's license, such messages will no longer be logged on startup.
  • In terms of architecture, the error messages we log are now localized within our internal license service instead of consumed from the core license service. In the long-term, they'll be removed from the core license service and plugins will be expected to follow this pattern and be responsible for their own license-failure UX.

I also refactored our internal license service to leverage the types exposed by the core license service.

Here's how license failures are now surfaced to the user in the UI and CLI for CCR:

image

image

image

image

image

Watcher is not quite as helpful but we can address that separately:

image

image

Steps to test

  • Edit the license.ts service to fake the license status.
  • Load Watcher and CCR to send a request to the API and see how the response is surfaced in the UI and the Kibana server logs.

Checklist

@cjcenizal cjcenizal added bug Fixes for quality problems that affect the customer experience Feature:Watcher Feature:CCR and Remote Clusters v8.0.0 Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more release_note:skip Skip the PR/issue when compiling release notes v7.13.0 v7.12.1 labels Mar 31, 2021
@cjcenizal cjcenizal requested a review from a team as a code owner March 31, 2021 19:26
@elasticmachine
Copy link
Contributor

Pinging @elastic/es-ui (Team:Elasticsearch UI)

Copy link
Contributor

@alisonelizabeth alisonelizabeth left a comment

Choose a reason for hiding this comment

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

Thanks for working on this @cjcenizal! I’m happy with this approach and also verified the changes locally.

Next steps: If reviewers approve of this direction, I'll extract this approach into a reusable service and then apply it to similar code in Watcher.

👍 I can re-review once this is complete. Can this service be used more broadly across our other plugins as well (perhaps addressed in a separate PR)?

{ licensing, logger }: { licensing: LicensingPluginSetup; logger: Logger }
) {
this.logger = logger;

licensing.license$.subscribe((license) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

not directly related to this PR - but I noticed while looking at the code in my IDE that LicensingPluginSetup.license$ is deprecated, and we should be using license$ provided by the start contract.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! Done.

@cjcenizal cjcenizal force-pushed the bug/ccr-watcher-license-messages branch from 361d501 to 4ba7a29 Compare April 2, 2021 22:09
@cjcenizal cjcenizal force-pushed the bug/ccr-watcher-license-messages branch from 5e573a1 to 87c0a21 Compare April 2, 2021 23:14
RequestHandlerContext,
} from 'src/core/server';

/* eslint-disable @kbn/eslint/no-restricted-paths */
Copy link
Contributor Author

@cjcenizal cjcenizal Apr 2, 2021

Choose a reason for hiding this comment

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

@elastic/kibana-core Do we still need this linting rule to disallow importing x-pack modules into non-x-pack?

Copy link
Contributor

Choose a reason for hiding this comment

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

For concrete imports, we definitely do, as it would plainly breaks on OSS distrib.

For types, it's not a 'technical' requirement, but until the basic plugins are moved out of xpack, we should stick with that rule.

Copy link
Contributor

Choose a reason for hiding this comment

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

AFAIK we don't release OSS distributable starting from v7.11 https://www.elastic.co/downloads/past-releases#kibana-oss

But I'd rather plugins didn't import from x-pack. Note that the code in src/ and x-pack have different licenses, so it's easy to leak commercial code outside by importing runtime code accidentally.
Regarding the current use case: why licensing code lives in src/ at all? Licensing compatible types can be declared in the current file, but maybe an x-pack plugin is a better place?

@cjcenizal cjcenizal force-pushed the bug/ccr-watcher-license-messages branch from 937ed8a to 3c6acd7 Compare April 5, 2021 18:35
@cjcenizal cjcenizal force-pushed the bug/ccr-watcher-license-messages branch from 3c6acd7 to 7c34572 Compare April 5, 2021 19:31
@elastic elastic deleted a comment from kibanamachine Apr 5, 2021
start({ pluginId, minimumLicenseType, licensing }: StartSettings) {
licensing.license$.subscribe((license) => {
this.licenseType = license.type;
this.licenseCheckState = license.check(pluginId, minimumLicenseType!).state;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

To testers: you can hardcode a value here, e.g. 'invalid', to test the behavior under various conditions.

@cjcenizal
Copy link
Contributor Author

@alisonelizabeth @jloleysens This is ready for your 👀 !

Copy link
Contributor

@pgayvallet pgayvallet left a comment

Choose a reason for hiding this comment

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

I know this may change soon, but I'm not at ease with the fact a xpack-dependent utility class importing types from licensing is exposed from an OSS plugin.

@mshustov wdyt?

RequestHandlerContext,
} from 'src/core/server';

/* eslint-disable @kbn/eslint/no-restricted-paths */
Copy link
Contributor

Choose a reason for hiding this comment

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

For concrete imports, we definitely do, as it would plainly breaks on OSS distrib.

For types, it's not a 'technical' requirement, but until the basic plugins are moved out of xpack, we should stick with that rule.

@cjcenizal
Copy link
Contributor Author

@alisonelizabeth @jloleysens @pgayvallet @mshustov Thanks for your reviews! I've addressed your feedback. I agree that we should sequester x-pack related code together until we make a formal commitment to move this code into src, so I've moved the License module into a new x-pack plugin called license_api_guard. At some point in the future I think we could discuss either moving it into the licensing plugin if we think non-ESUI plugins would benefit from it, or into an x-pack-specific version of es_ui_shared if not.

@alisonelizabeth To your question about using this in our other plugins -- I just checked and I believe all of our other plugins are licensed under Basic, so there's no need to guard their API routes this way.

Reviewers, would you mind taking another look at sharing your thoughts? Thanks!

import { of } from 'rxjs';
import type { KibanaRequest, RequestHandlerContext } from 'src/core/server';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { httpServerMock } from 'src/core/server/http/http_server.mocks';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mshustov Do you think we could update src/core/server to export these helpers publicly?

Copy link
Contributor

Choose a reason for hiding this comment

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

They are available from src/core/server/mocks

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, fixed in #97334

Copy link
Contributor

@mshustov mshustov left a comment

Choose a reason for hiding this comment

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

LGTM!

At some point in the future I think we could discuss either moving it into the licensing plugin if we think non-ESUI plugins would benefit from it, or into an x-pack-specific version of es_ui_shared if not.

We are planning to add something similar #56926

}

start({ pluginId, minimumLicenseType, licensing }: StartSettings) {
licensing.license$.subscribe((license: ILicense) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

shouldn't we unsubscribe on the stop lifecycle?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mshustov I was under the impression that stop is never called in server-side plugins -- plugins just "live" until the Kibana server process is killed. Is that not the case?

Copy link
Contributor

@mshustov mshustov Apr 16, 2021

Choose a reason for hiding this comment

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

plugins just "live" until the Kibana server process is killed.

@cjcenizal Nope. It's always called during the normal teardown process (unless Kibana crashes).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When does teardown occur? How can I trigger it?

const licenseErrorMessage = this.getLicenseErrorMessage(this.licenseCheckState);
this.logger?.warn(licenseErrorMessage);

return response.customError({
Copy link
Contributor

Choose a reason for hiding this comment

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

why not response.forbidden?

        return response.forbidden({
          body: {
            message: licenseErrorMessage,
          },
        });

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion!

Copy link
Contributor

@alisonelizabeth alisonelizabeth left a comment

Choose a reason for hiding this comment

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

Changes LGTM. Thanks @cjcenizal!

@alisonelizabeth To your question about using this in our other plugins -- I just checked and I believe all of our other plugins are licensed under Basic, so there's no need to guard their API routes this way.

I think most - if not all - of our plugins have a similar license check (for example, Ingest Node Pipelines, Index Management and ILM). Can we remove this guard now?

},
].forEach(({ licenseState, expectedMessage }) => {
describe(`${licenseState} license`, () => {
it('replies with 403 and message and logs the message', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
it('replies with 403 and message and logs the message', () => {
it('replies with 403 status and logs the message', () => {

x-pack/plugins/license_api_guard/server/license.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@jloleysens jloleysens left a comment

Choose a reason for hiding this comment

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

Thanks for addressing that feedback @cjcenizal. Happy with the current changes!

@cjcenizal
Copy link
Contributor Author

@alisonelizabeth Great call, I created #97314 to track this work. @mshustov I'm going to go ahead and merge once CI is green. We can follow up on your comment about unsubscribing from the license service in another PR if this needs to be addressed.

@cjcenizal cjcenizal merged commit 3521441 into elastic:master Apr 15, 2021
@cjcenizal cjcenizal deleted the bug/ccr-watcher-license-messages branch April 15, 2021 21:54
@cjcenizal cjcenizal changed the title Restrict CCR license-checking to API requests and localize error messages Extract License service from CCR and Watcher into license_api_guard plugin in x-pack Apr 15, 2021
@cjcenizal cjcenizal added v7.12.2 and removed v7.12.1 labels Apr 15, 2021
@cjcenizal cjcenizal added release_note:fix and removed v7.12.2 release_note:skip Skip the PR/issue when compiling release notes labels Apr 15, 2021
@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

✅ unchanged

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cjcenizal added a commit that referenced this pull request Apr 16, 2021
…lugin in x-pack (#95973) (#97332)

* Localize error messages.
# Conflicts:
#	.github/CODEOWNERS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Fixes for quality problems that affect the customer experience Feature:CCR and Remote Clusters Feature:Watcher release_note:fix Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more v7.13.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Misleading license log messages with a fresh Basic installation
7 participants