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

[alerting] Adds Action Type configuration support and whitelisting #44483

Merged
merged 23 commits into from
Sep 6, 2019

Conversation

gmmorris
Copy link
Contributor

@gmmorris gmmorris commented Aug 30, 2019

Summary

Refactor Action Types registration.
Adds whitelisting configuration for the builtin webhooks action.
Refers to: https://github.com/elastic/kibana-team/issues/136

Checklist

Use strikethroughs to remove checklist items you don't feel are applicable to this PR.

For maintainers

@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-stack-services

@gmmorris gmmorris changed the title [alerting] refactor action types [alerting] Action Type configuration and whitelisting Aug 30, 2019
@gmmorris gmmorris changed the title [alerting] Action Type configuration and whitelisting [alerting] Adds Action Type configuration support and whitelisting Aug 30, 2019
@gmmorris
Copy link
Contributor Author

gmmorris commented Aug 30, 2019

I'll we adding documentation for:

  1. The API change around ActionType usage
  2. The additional configuration needed for whitelisting.\

Just waiting to confirm the team is comfortable with the changes....

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Member

@pmuellr pmuellr left a comment

Choose a reason for hiding this comment

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

This is looking great! I'll re-review when you feel like it's done.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💔 Build Failed

@gmmorris gmmorris marked this pull request as ready for review September 2, 2019 13:17
@gmmorris gmmorris requested a review from a team September 2, 2019 13:17
@gmmorris gmmorris added the release_note:skip Skip the PR/issue when compiling release notes label Sep 2, 2019
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@bmcconaghy bmcconaghy left a comment

Choose a reason for hiding this comment

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

The implementation of isWhitelistedHost seems convoluted to me. I expected it to either return a boolean (based on * || Array.includes) or to throw if those conditions are not met. Returning an error seems like an odd choice to me, definitely don't expect errors to get passed around, only thrown when they are created.

}

export interface ActionsConfigurationUtilities {
isWhitelistedHostname: (hostname: string) => string | NotWhitelistedError;
Copy link
Contributor

Choose a reason for hiding this comment

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

I find the name and semantics of this to be strange. I expect is... methods to return booleans.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair, I could rename it to ensureIsWhitelisted... or something like that.

return tryCatch(() => new URL(uri))
.map(url => url.hostname)
.map(hostname => {
const result = isWhitelistedHostname(hostname);
Copy link
Contributor

Choose a reason for hiding this comment

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

why doesn't this just throw instead of returning an error and then throwing based on instanceof? Seems overcomplicated to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can't typecheck thrown errors in Typescript, so the general recommendation there seems to be to return errors.
I agree it's over complicated- that's why the Result type is a much more elegant solution, but there were concerns about Result type being used on external APIs.

@gmmorris
Copy link
Contributor Author

gmmorris commented Sep 4, 2019

The implementation of isWhitelistedHost seems convoluted to me. I expected it to either return a boolean (based on * || Array.includes) or to throw if those conditions are not met. Returning an error seems like an odd choice to me, definitely don't expect errors to get passed around, only thrown when they are created.

It did originally return a boolean, but following feedback from Patrick we decided the function would return the messaging that made it . clear what the source of the problem was (value not being in the whitelist) so that you don't have top repeat that in each action that uses the utility.

I also agree, returning Errors is weird in JS land, which is why the Result type is a nice abstraction for this, but as long as there's unease about using TS types on APIs like this, we're low on options here.

We can either:

  1. Throw, which means you can't type check that it's handled by the developer.
  2. Only return a value if it is an error (like validators) which is hard to compose, is implicit, and is error prone.
  3. Return boolean, and handle the messaging independently, which repeats code.

To me, the ideal is option #4, where we use TS on our APIs, but sounds like we're not quite ready for that yet.

@bmcconaghy
Copy link
Contributor

I'm OK with the approach here so long as we change the name of the method to check....

@gmmorris
Copy link
Contributor Author

gmmorris commented Sep 4, 2019

I'm OK with the approach here so long as we change the name of the method to check....

Cool, thanks, I'll rename to make it clearer.

Copy link
Member

@pmuellr pmuellr left a comment

Choose a reason for hiding this comment

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

Made a bunch of comments; most concerned with the validator function shapes here - eg, isWhitelistedHostname: (hostname: string) => string | NotWhitelistedError - it doesn't seem very idiomatic to me. I suppose I could be convinced, but ... it will be tough heh.

}

const whitelistingErrorMessage = 'target url not in whitelist';
const doesValueWhitelistAnyHostname = (whitelistedHostname: string): boolean =>
Copy link
Member

Choose a reason for hiding this comment

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

this starts to read as a valuable piece of function, but there's no verbs, or something. Would something like isHostnameWildcard() work instead? Or is this some kind of functional pattern I'm not familiar with?

I also noticed you used const <functionName> = <lambda> as is the style for a lot of code these days, but I think we've generally just used functions in this area of Kibana at least; ie, function doesValueWhitelistAnyHostname(...) {...} instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Regarding doesValueWhitelistAnyHostname it's not about FP, it's about communicating intent.
Implementation is easier to communicate (they read the code) but intent is much harder (read the code, and the surrounding code, and how they fit together). I find it helpful to communicate intent in the name of functions rather than implementation.

Hope that helps

const log = (level: string, msg: string) =>
execOptions.services.log([level, 'actions', 'webhook'], msg);

const id = execOptions.id;
const { method, url, headers = {} } = execOptions.config as ActionTypeConfigType;
const { user: username, password } = execOptions.secrets as ActionTypeSecretsType;
const { body: data } = execOptions.params as ActionParamsType;
const whitelistValidation = configurationUtilities.isWhitelistedUri(url);
Copy link
Member

Choose a reason for hiding this comment

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

I thought we re-ran the validators (all of them?) before invoking the executor? Yeah, I think so, so this explicit check shouldn't be needed, and I think you'd never get into this method, and have the subsequent code run.

try {
validatedParams = validateParams(actionType, params);
validatedConfig = validateConfig(actionType, config);
validatedSecrets = validateSecrets(actionType, secrets);
} catch (err) {
return { status: 'error', message: err.message, retry: false };
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, I've double checked and this is true, but somewhat implicit as it requires prior knowledge of the wiring.
I've removed the redundant validation, but feel this is something worth revisiting in the future... perhaps there's a better way to model this so it's clearer.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@bmcconaghy bmcconaghy left a comment

Choose a reason for hiding this comment

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

Code LGTM

Copy link
Member

@pmuellr pmuellr left a comment

Choose a reason for hiding this comment

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

Fantastic!

I made a few comments for things I think need to be fixed, but can be in a subsequent PR; let's get this sucker merged!

@@ -27,6 +27,13 @@ export function actions(kibana: any) {
return Joi.object()
.keys({
enabled: Joi.boolean().default(false),
whitelistedHosts: Joi.alternatives()
Copy link
Member

Choose a reason for hiding this comment

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

I think alternatives was here when the property was string | string[]? So, I think we can remove the alternatives() and try() wrappers, just make it a Joi.array()? Or maybe there's some joi sneakiness I don't yet understand :-)

It clearly works now, so fine with shipping this way, create an issue / project card if it should be fixed later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, that's not needed anymore, I'll fix in a small PR next

@@ -19,6 +20,12 @@ import { ActionParamsType, ActionTypeConfigType } from './es_index';

const ACTION_TYPE_ID = '.index';
const NO_OP_FN = () => {};
const MOCK_KIBANA_CONFIG_UTILS: ActionsConfigurationUtilities = {
Copy link
Member

Choose a reason for hiding this comment

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

fyi: more bits to add to the theoretical "testing rig for actions" :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed

configurationUtilities.ensureWhitelistedUri(configObject.url);
} catch (whitelistError) {
return i18n.translate('xpack.actions.builtin.webhook.webhookConfigurationError', {
defaultMessage: 'error configuring webhook action: {message}',
Copy link
Member

Choose a reason for hiding this comment

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

I think we still need the actionId in here - unless it's somehow included in some other way (not obvious to me that it is).

If we need to get it added, can be done in a subsequent PR; create an issue | project card for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, we don't have access to the ID at this point (in create it happens before an ID is generated, and in update/execute it's not provided), so this will have to be a separate PR.

@gmmorris gmmorris merged commit 0d1f3cf into elastic:master Sep 6, 2019
gmmorris added a commit to gmmorris/kibana that referenced this pull request Sep 6, 2019
…lastic#44483)

A wide refactor of Action Types and their configuration:
- Action Types are now initialised with a their associated configuration based on `kibana.yml`
- You may now whitelist certain hostnames to allow built-in actions, such as _Webhooks_, to communicate with the outside world
- The _Webhooks_ Built-in action is now available, if you have `alerting` and `actions` enabled
gmmorris added a commit that referenced this pull request Sep 6, 2019
…44483) (#44969)

A wide refactor of Action Types and their configuration:
- Action Types are now initialised with a their associated configuration based on `kibana.yml`
- You may now whitelist certain hostnames to allow built-in actions, such as _Webhooks_, to communicate with the outside world
- The _Webhooks_ Built-in action is now available, if you have `alerting` and `actions` enabled
jloleysens added a commit to jloleysens/kibana that referenced this pull request Sep 6, 2019
…ete-for-distance_feature

* 'master' of github.com:elastic/kibana: (89 commits)
  Replace TSVB timeseries charts with elastic-charts (elastic#33558)
  [TSVB][Top N aggregation] Unable to deal with negative values (elastic#43581)
  [alerting] Adds Action Type configuration support and whitelisting (elastic#44483)
  FTR: fix WebDriver Actions calls (elastic#44605)
  [Code] add NodeRepositoriesService to watch new repositories on local node (elastic#44677)
  [skip-ci][Maps] Improve Maps intro page (elastic#44721)
  [Maps] Update titles and descriptions for data sources (elastic#44833)
  Types + Extract Integration Util (elastic#44433)
  Downgrade log level from info to debug for cases when we cannot handle authentication attempt. (elastic#44933)
  [Reporting] Remove Chome stdout/stderr observables, Add Browser Logger observable (elastic#44359)
  Update Jest script to output coverage (elastic#44447)
  [ftr] support --kibana-install-dir flag (elastic#44552)
  [WATCHER] Allow user to set a threshold value of 0 (elastic#44810)
  Remove injectI18n in dashboard plugin. (elastic#44580)
  [Graph] Save modal (elastic#44261)
  Use external script for the OIDC Implicit flow handler page. (elastic#44866)
  disable router prefixing with pluginId (elastic#44855)
  [SIEM] Fix bug on url + inspect functionality on hosts/hostDetails page (elastic#44671)
  [ML] File data viz limiting uploaded doc chunk size (elastic#44768)
  [code] Append go env variable 'GOCACHE' to go lsp spawn command. (elastic#44864)
  ...
jloleysens added a commit to jloleysens/kibana that referenced this pull request Sep 6, 2019
…plate

* 'master' of github.com:elastic/kibana: (91 commits)
  [APM] Make number of x ticks responsive to the plot width (elastic#44870)
  [ML] Single metric viewer: Fix top nav refresh behaviour. (elastic#44860)
  Replace TSVB timeseries charts with elastic-charts (elastic#33558)
  [TSVB][Top N aggregation] Unable to deal with negative values (elastic#43581)
  [alerting] Adds Action Type configuration support and whitelisting (elastic#44483)
  FTR: fix WebDriver Actions calls (elastic#44605)
  [Code] add NodeRepositoriesService to watch new repositories on local node (elastic#44677)
  [skip-ci][Maps] Improve Maps intro page (elastic#44721)
  [Maps] Update titles and descriptions for data sources (elastic#44833)
  Types + Extract Integration Util (elastic#44433)
  Downgrade log level from info to debug for cases when we cannot handle authentication attempt. (elastic#44933)
  [Reporting] Remove Chome stdout/stderr observables, Add Browser Logger observable (elastic#44359)
  Update Jest script to output coverage (elastic#44447)
  [ftr] support --kibana-install-dir flag (elastic#44552)
  [WATCHER] Allow user to set a threshold value of 0 (elastic#44810)
  Remove injectI18n in dashboard plugin. (elastic#44580)
  [Graph] Save modal (elastic#44261)
  Use external script for the OIDC Implicit flow handler page. (elastic#44866)
  disable router prefixing with pluginId (elastic#44855)
  [SIEM] Fix bug on url + inspect functionality on hosts/hostDetails page (elastic#44671)
  ...
@gmmorris gmmorris deleted the actions/refactor-action-types branch November 21, 2019 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants