Skip to content

Commit 361e97a

Browse files
[FR] Add API auth to Kibana module (elastic#3815)
* [FR] Add API auth to Kibana module * update make file to properly install all deps * Bump Kibana Version --------- Co-authored-by: brokensound77 <brokensound77@users.noreply.github.com> Co-authored-by: eric-forte-elastic <eric.forte@elastic.co> Co-authored-by: Eric Forte <119343520+eric-forte-elastic@users.noreply.github.com>
1 parent 44658ea commit 361e97a

File tree

5 files changed

+31
-10
lines changed

5 files changed

+31
-10
lines changed

CLI.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Currently supported arguments:
2727
* cloud_id
2828
* *_username (kibana and es)
2929
* *_password (kibana and es)
30+
* api_key
3031

3132
#### Using environment variables
3233

Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ clean:
2323
deps: $(VENV)
2424
@echo "Installing all dependencies..."
2525
$(PIP) install .[dev]
26+
$(PIP) install lib/kibana
27+
$(PIP) install lib/kql
2628

2729
.PHONY: pytest
2830
pytest: $(VENV) deps
@@ -42,12 +44,12 @@ lint: $(VENV) deps
4244
test: $(VENV) lint pytest
4345

4446
.PHONY: test-cli
45-
test-cli: $(VENV)
47+
test-cli: $(VENV) deps
4648
@echo "Executing test_cli script..."
4749
@./detection_rules/etc/test_cli.bash
4850

4951
.PHONY: test-remote-cli
50-
test-remote-cli: $(VENV)
52+
test-remote-cli: $(VENV) deps
5153
@echo "Executing test_remote_cli script..."
5254
@./detection_rules/etc/test_remote_cli.bash
5355

detection_rules/misc.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -297,22 +297,27 @@ def getdefault(name):
297297
return lambda: os.environ.get(envvar, config.get(name))
298298

299299

300-
def get_elasticsearch_client(cloud_id=None, elasticsearch_url=None, es_user=None, es_password=None, ctx=None, **kwargs):
300+
def get_elasticsearch_client(cloud_id: str = None, elasticsearch_url: str = None, es_user: str = None,
301+
es_password: str = None, ctx: click.Context = None, api_key: str = None, **kwargs):
301302
"""Get an authenticated elasticsearch client."""
302303
from elasticsearch import AuthenticationException, Elasticsearch
303304

304305
if not (cloud_id or elasticsearch_url):
305306
client_error("Missing required --cloud-id or --elasticsearch-url")
306307

307308
# don't prompt for these until there's a cloud id or elasticsearch URL
308-
es_user = es_user or click.prompt("es_user")
309-
es_password = es_password or click.prompt("es_password", hide_input=True)
309+
basic_auth: (str, str) | None = None
310+
if not api_key:
311+
es_user = es_user or click.prompt("es_user")
312+
es_password = es_password or click.prompt("es_password", hide_input=True)
313+
basic_auth = (es_user, es_password)
314+
310315
hosts = [elasticsearch_url] if elasticsearch_url else None
311316
timeout = kwargs.pop('timeout', 60)
312317
kwargs['verify_certs'] = not kwargs.pop('ignore_ssl_errors', False)
313318

314319
try:
315-
client = Elasticsearch(hosts=hosts, cloud_id=cloud_id, http_auth=(es_user, es_password), timeout=timeout,
320+
client = Elasticsearch(hosts=hosts, cloud_id=cloud_id, http_auth=basic_auth, timeout=timeout, api_key=api_key,
316321
**kwargs)
317322
# force login to test auth
318323
client.info()
@@ -322,16 +327,17 @@ def get_elasticsearch_client(cloud_id=None, elasticsearch_url=None, es_user=None
322327
client_error(error_msg, e, ctx=ctx, err=True)
323328

324329

325-
def get_kibana_client(cloud_id, kibana_url, kibana_user, kibana_password, kibana_cookie, space, ignore_ssl_errors,
326-
provider_type, provider_name, **kwargs):
330+
def get_kibana_client(cloud_id: str, kibana_url: str, kibana_user: str, kibana_password: str, kibana_cookie: str,
331+
space: str, ignore_ssl_errors: bool, provider_type: str, provider_name: str, api_key: str,
332+
**kwargs):
327333
"""Get an authenticated Kibana client."""
328334
from requests import HTTPError
329335
from kibana import Kibana
330336

331337
if not (cloud_id or kibana_url):
332338
client_error("Missing required --cloud-id or --kibana-url")
333339

334-
if not kibana_cookie:
340+
if not (kibana_cookie or api_key):
335341
# don't prompt for these until there's a cloud id or Kibana URL
336342
kibana_user = kibana_user or click.prompt("kibana_user")
337343
kibana_password = kibana_password or click.prompt("kibana_password", hide_input=True)
@@ -342,6 +348,9 @@ def get_kibana_client(cloud_id, kibana_url, kibana_user, kibana_password, kibana
342348
if kibana_cookie:
343349
kibana.add_cookie(kibana_cookie)
344350
return kibana
351+
elif api_key:
352+
kibana.add_api_key(api_key)
353+
return kibana
345354

346355
try:
347356
kibana.login(kibana_user, kibana_password, provider_type=provider_type, provider_name=provider_name)
@@ -359,6 +368,7 @@ def get_kibana_client(cloud_id, kibana_url, kibana_user, kibana_password, kibana
359368
'kibana': {
360369
'cloud_id': click.Option(['--cloud-id'], default=getdefault('cloud_id'),
361370
help="ID of the cloud instance."),
371+
'api_key': click.Option(['--api-key'], default=getdefault('api_key')),
362372
'kibana_cookie': click.Option(['--kibana-cookie', '-kc'], default=getdefault('kibana_cookie'),
363373
help='Cookie from an authed session'),
364374
'kibana_password': click.Option(['--kibana-password', '-kp'], default=getdefault('kibana_password')),
@@ -373,6 +383,7 @@ def get_kibana_client(cloud_id, kibana_url, kibana_user, kibana_password, kibana
373383
},
374384
'elasticsearch': {
375385
'cloud_id': click.Option(['--cloud-id'], default=getdefault("cloud_id")),
386+
'api_key': click.Option(['--api-key'], default=getdefault('api_key')),
376387
'elasticsearch_url': click.Option(['--elasticsearch-url'], default=getdefault("elasticsearch_url")),
377388
'es_user': click.Option(['--es-user', '-eu'], default=getdefault("es_user")),
378389
'es_password': click.Option(['--es-password', '-ep'], default=getdefault("es_password")),

lib/kibana/kibana/connector.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ def add_cookie(self, cookie):
194194
self.status = self.get('/api/status')
195195
self.authenticated = True
196196

197+
def add_api_key(self, api_key: str) -> bool:
198+
"""Add an API key to be used for auth."""
199+
self.session.headers['Authorization'] = f'ApiKey {api_key}'
200+
self.status = self.get('/api/status')
201+
self.authenticated = True
202+
return bool(self.status)
203+
197204
def logout(self):
198205
"""Quit the current session."""
199206
try:

lib/kibana/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "detection-rules-kibana"
3-
version = "0.2.1"
3+
version = "0.3.0"
44
description = "Kibana API utilities for Elastic Detection Rules"
55
license = {text = "Elastic License v2"}
66
keywords = ["Elastic", "Kibana", "Detection Rules", "Security", "Elasticsearch"]

0 commit comments

Comments
 (0)