From 8ce3e8936002a4ab79b2414d9e6a8bee2a92180c Mon Sep 17 00:00:00 2001 From: nsano-rururu Date: Tue, 23 Nov 2021 22:56:31 +0900 Subject: [PATCH 1/2] Add support for basic auth to alertmanager --- docs/source/ruletypes.rst | 4 ++ elastalert/alerters/alertmanager.py | 8 +++- elastalert/schema.yaml | 2 + tests/alerters/alertmanager_test.py | 72 +++++++++++++++++++++++++++-- 4 files changed, 81 insertions(+), 5 deletions(-) diff --git a/docs/source/ruletypes.rst b/docs/source/ruletypes.rst index 5caf8b07..9804d747 100644 --- a/docs/source/ruletypes.rst +++ b/docs/source/ruletypes.rst @@ -1731,6 +1731,10 @@ Optional: ``alertmanager_timeout``: You can specify a timeout value, in seconds, for making communicating with Alertmanager. The default is 10. If a timeout occurs, the alert will be retried next time ElastAlert 2 cycles. +``alertmanager_basic_auth_login``: Basic authentication username. + +``alertmanager_basic_auth_password``: Basic authentication password. + Example usage:: alert: diff --git a/elastalert/alerters/alertmanager.py b/elastalert/alerters/alertmanager.py index 573e4a72..70eb1598 100644 --- a/elastalert/alerters/alertmanager.py +++ b/elastalert/alerters/alertmanager.py @@ -3,6 +3,7 @@ import requests from requests import RequestException +from requests.auth import HTTPBasicAuth from elastalert.alerts import Alerter, DateTimeEncoder from elastalert.util import EAException, elastalert_logger, lookup_es_key @@ -27,6 +28,9 @@ def __init__(self, rule): self.ca_certs = self.rule.get('alertmanager_ca_certs') self.ignore_ssl_errors = self.rule.get('alertmanager_ignore_ssl_errors', False) self.timeout = self.rule.get('alertmanager_timeout', 10) + self.alertmanager_basic_auth_login = self.rule.get('alertmanager_basic_auth_login', None) + self.alertmanager_basic_auth_password = self.rule.get('alertmanager_basic_auth_password', None) + @staticmethod def _json_or_string(obj): @@ -38,6 +42,7 @@ def _json_or_string(obj): def alert(self, matches): headers = {'content-type': 'application/json'} proxies = {'https': self.proxies} if self.proxies else None + auth = HTTPBasicAuth(self.alertmanager_basic_auth_login, self.alertmanager_basic_auth_password) if self.alertmanager_basic_auth_login else None self.labels.update({ label: self._json_or_string(lookup_es_key(matches[0], term)) @@ -70,7 +75,8 @@ def alert(self, matches): headers=headers, verify=verify, proxies=proxies, - timeout=self.timeout + timeout=self.timeout, + auth=auth ) warnings.resetwarnings() diff --git a/elastalert/schema.yaml b/elastalert/schema.yaml index eeac1c69..f834ae2c 100644 --- a/elastalert/schema.yaml +++ b/elastalert/schema.yaml @@ -307,6 +307,8 @@ properties: alertmanager_ca_certs: {type: boolean} alertmanager_ignore_ssl_errors: {type: boolean} alertmanager_timeout: {type: integer} + alertmanager_basic_auth_login: {type: string} + alertmanager_basic_auth_password: {type: string} alertmanager_labels: type: object minProperties: 1 diff --git a/tests/alerters/alertmanager_test.py b/tests/alerters/alertmanager_test.py index f3ec3cd3..5c831413 100644 --- a/tests/alerters/alertmanager_test.py +++ b/tests/alerters/alertmanager_test.py @@ -5,6 +5,7 @@ from unittest import mock from requests import RequestException +from requests.auth import HTTPBasicAuth from elastalert.alerters.alertmanager import AlertmanagerAlerter from elastalert.loaders import FileRulesLoader @@ -63,7 +64,8 @@ def test_alertmanager(caplog): headers={'content-type': 'application/json'}, proxies=None, verify=True, - timeout=10 + timeout=10, + auth=None ) assert expected_data == json.loads(mock_post_request.call_args_list[0][1]['data']) assert ('elastalert', logging.INFO, "Alert sent to Alertmanager") == caplog.record_tuples[0] @@ -121,7 +123,8 @@ def test_alertmanager_porxy(): headers={'content-type': 'application/json'}, proxies={'https': 'http://proxy.url'}, verify=True, - timeout=10 + timeout=10, + auth=None ) assert expected_data == json.loads(mock_post_request.call_args_list[0][1]['data']) @@ -178,7 +181,8 @@ def test_alertmanager_timeout(): headers={'content-type': 'application/json'}, proxies=None, verify=True, - timeout=20 + timeout=20, + auth=None ) assert expected_data == json.loads(mock_post_request.call_args_list[0][1]['data']) @@ -251,7 +255,8 @@ def test_alertmanager_ca_certs(ca_certs, ignore_ssl_errors, excpet_verify): headers={'content-type': 'application/json'}, proxies=None, verify=excpet_verify, - timeout=10 + timeout=10, + auth=None ) assert expected_data == json.loads(mock_post_request.call_args_list[0][1]['data']) @@ -322,3 +327,62 @@ def test_alertmanager_required_error(alertmanager_hosts, expected_data): except Exception as ea: print('ea %s' % str(ea)) assert expected_data in str(ea) + + +def test_alertmanager_basic_auth(): + rule = { + 'name': 'Test Alertmanager Rule', + 'type': 'any', + 'alertmanager_hosts': ['http://alertmanager:9093'], + 'alertmanager_alertname': 'Title', + 'alertmanager_annotations': {'severity': 'error'}, + 'alertmanager_labels': {'source': 'elastalert'}, + 'alertmanager_fields': {'msg': 'message', 'log': '@log_name'}, + 'alertmanager_basic_auth_login': 'user', + 'alertmanager_basic_auth_password': 'password', + 'alert_subject_args': ['message', '@log_name'], + 'alert': [] + } + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = AlertmanagerAlerter(rule) + match = { + '@timestamp': '2021-01-01T00:00:00', + 'somefield': 'foobarbaz', + 'message': 'Quit 123', + '@log_name': 'mysqld.general' + } + with mock.patch('requests.post') as mock_post_request: + alert.alert([match]) + + expected_data = [ + { + 'annotations': + { + 'severity': 'error', + 'summary': 'Test Alertmanager Rule', + 'description': 'Test Alertmanager Rule\n\n' + + '@log_name: mysqld.general\n' + + '@timestamp: 2021-01-01T00:00:00\n' + + 'message: Quit 123\nsomefield: foobarbaz\n' + }, + 'labels': { + 'source': 'elastalert', + 'msg': 'Quit 123', + 'log': 'mysqld.general', + 'alertname': 'Title', + 'elastalert_rule': 'Test Alertmanager Rule' + } + } + ] + + mock_post_request.assert_called_once_with( + 'http://alertmanager:9093/api/v1/alerts', + data=mock.ANY, + headers={'content-type': 'application/json'}, + proxies=None, + verify=True, + timeout=10, + auth=HTTPBasicAuth('user', 'password') + ) + assert expected_data == json.loads(mock_post_request.call_args_list[0][1]['data']) From ddb171f43166b7be8b775787489fe056079ff83b Mon Sep 17 00:00:00 2001 From: nsano-rururu Date: Tue, 23 Nov 2021 22:59:55 +0900 Subject: [PATCH 2/2] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f1d499b..eeb87319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ## New features - Add metric_agg_script to MetricAggregationRule [#558](https://github.com/jertel/elastalert2/pull/558) - @dequis +- [Alertmanager]Add support for basic authentication - [#575](https://github.com/jertel/elastalert2/pull/575) - @nsano-rururu ## Other changes - sphinx 4.2.0 to 4.3.0 and tzlocal==2.1 - [#561](https://github.com/jertel/elastalert2/pull/561) - @nsano-rururu