5b24eca0bc
* add support for self-signed certs in es and kibana * allow Kibana to auth against any providerType * fix export-rules command * fix kibana upload-rule command * fix view-rule command * fix validate-rule command * fix search-rules command * fix dev kibana-diff command * fix dev package-stats command * fix dev search-rule-prs command * fix dev deprecate-rule command * replace toml with pytoml to fix import-rules command * use no_verify in get_kibana_client * use Path for rule-file type in view-rule * update schemas to resolve additionalProperties type bug * fix missing unique_fields in package rule filter * fix github pr loader * Load gh rules as TOMLRule instead of dict * remove unnecessary version insertion
105 lines
3.6 KiB
Python
105 lines
3.6 KiB
Python
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
# or more contributor license agreements. Licensed under the Elastic License
|
|
# 2.0; you may not use this file except in compliance with the Elastic License
|
|
# 2.0.
|
|
|
|
"""Kibana cli commands."""
|
|
import uuid
|
|
|
|
import click
|
|
|
|
import kql
|
|
from kibana import Signal, RuleResource
|
|
from .cli_utils import multi_collection
|
|
from .main import root
|
|
from .misc import add_params, client_error, kibana_options, get_kibana_client
|
|
from .schemas import downgrade
|
|
from .utils import format_command_options
|
|
|
|
|
|
@root.group('kibana')
|
|
@add_params(*kibana_options)
|
|
@click.pass_context
|
|
def kibana_group(ctx: click.Context, **kibana_kwargs):
|
|
"""Commands for integrating with Kibana."""
|
|
ctx.ensure_object(dict)
|
|
|
|
# only initialize an kibana client if the subcommand is invoked without help (hacky)
|
|
if click.get_os_args()[-1] in ctx.help_option_names:
|
|
click.echo('Kibana client:')
|
|
click.echo(format_command_options(ctx))
|
|
|
|
else:
|
|
ctx.obj['kibana'] = get_kibana_client(**kibana_kwargs)
|
|
|
|
|
|
@kibana_group.command("upload-rule")
|
|
@multi_collection
|
|
@click.option('--replace-id', '-r', is_flag=True, help='Replace rule IDs with new IDs before export')
|
|
@click.pass_context
|
|
def upload_rule(ctx, rules, replace_id):
|
|
"""Upload a list of rule .toml files to Kibana."""
|
|
kibana = ctx.obj['kibana']
|
|
api_payloads = []
|
|
|
|
for rule in rules:
|
|
try:
|
|
payload = rule.contents.to_api_format()
|
|
payload.setdefault("meta", {}).update(rule.contents.metadata.to_dict())
|
|
|
|
if replace_id:
|
|
payload["rule_id"] = str(uuid.uuid4())
|
|
|
|
payload = downgrade(payload, target_version=kibana.version)
|
|
|
|
except ValueError as e:
|
|
client_error(f'{e} in version:{kibana.version}, for rule: {rule.name}', e, ctx=ctx)
|
|
|
|
rule = RuleResource(payload)
|
|
api_payloads.append(rule)
|
|
|
|
with kibana:
|
|
results = RuleResource.bulk_create(api_payloads)
|
|
|
|
success = []
|
|
errors = []
|
|
for result in results:
|
|
if 'error' in result:
|
|
errors.append(f'{result["rule_id"]} - {result["error"]["message"]}')
|
|
else:
|
|
success.append(result['rule_id'])
|
|
|
|
if success:
|
|
click.echo('Successful uploads:\n - ' + '\n - '.join(success))
|
|
if errors:
|
|
click.echo('Failed uploads:\n - ' + '\n - '.join(errors))
|
|
|
|
return results
|
|
|
|
|
|
@kibana_group.command('search-alerts')
|
|
@click.argument('query', required=False)
|
|
@click.option('--date-range', '-d', type=(str, str), default=('now-7d', 'now'), help='Date range to scope search')
|
|
@click.option('--columns', '-c', multiple=True, help='Columns to display in table')
|
|
@click.option('--extend', '-e', is_flag=True, help='If columns are specified, extend the original columns')
|
|
@click.pass_context
|
|
def search_alerts(ctx, query, date_range, columns, extend):
|
|
"""Search detection engine alerts with KQL."""
|
|
from eql.table import Table
|
|
from .eswrap import MATCH_ALL, add_range_to_dsl
|
|
|
|
kibana = ctx.obj['kibana']
|
|
start_time, end_time = date_range
|
|
kql_query = kql.to_dsl(query) if query else MATCH_ALL
|
|
add_range_to_dsl(kql_query['bool'].setdefault('filter', []), start_time, end_time)
|
|
|
|
with kibana:
|
|
alerts = [a['_source'] for a in Signal.search({'query': kql_query})['hits']['hits']]
|
|
|
|
table_columns = ['host.hostname', 'signal.rule.name', 'signal.status', 'signal.original_time']
|
|
if columns:
|
|
columns = list(columns)
|
|
table_columns = table_columns + columns if extend else columns
|
|
click.echo(Table.from_list(table_columns, alerts))
|
|
return alerts
|