Elasticsearch najczęściej wykorzystywany jest do agregowania logów i monitorowania elementów systemu. Umożliwia również alertowanie, ale jest to funkcjonalność dostępna od wersji Gold, czyli trzeba za nią zapłacić ?. Są jednak darmowe rozwiązania. W tym przypadku przyjrzymy się rozwiązaniu Praeco (z łac. obwoływacz, herold) opartym o ElastAlert.
Praeco
ElastAlert jest to rozwiązanie do powiadamiania o anomaliach, skokach lub innych wzorcach w Elasticsearch. Samo w sobie nie posiada GUI.
Praeco jest to nakładka graficzna na ElastAlert, wykorzystująca jego API. Pozwala w prosty sposób wyklikać reguły i kierunki (Slack, Email, Http, Telegram, JIRA), na które ma być wysłane powiadomienie.
Środowisko
Tym razem będę opierał się na „stacjonarnym” zabezpieczonym klastrze Elasticsearch w wersji 7.6, ale jeśli potrzebujesz szybko postawić ES na komputerze, skorzystaj z Dockera.
W przypadku Praeco użyjemy ichniego repozytorium z docker-compose.yml. Do sprawdzenia, czy alertowanie faktycznie działa użyjemy Logstasha i powiadomień HTTP.
git clone https://github.com/ServerCentral/praeco.git
Konfiguracja
Praeco & ElastAlert
W sklonowanym repo mamy docker-compose.yml i configi. Oto zmiany które wprowadziłem.
version: '3' services: elastalert: image: 'servercentral/elastalert' ports: - '3030:3030' - '3333:3333' volumes: - ./config/elastalert.yaml:/opt/elastalert/config.yaml - ./config/api.config.json:/opt/elastalert-server/config/config.json - ./rules:/opt/elastalert/rules - ./rule_templates:/opt/elastalert/rule_templates - /jakas/sciezka/ca.pem:/ca.pem extra_hosts: - 'hadoop01:10.7.35.110' # - 'elasticsearch:${PRAECO_ELASTICSEARCH}' webapp: image: 'servercentral/praeco' ports: - '5602:8080' volumes: - ./public/praeco.config.json:/var/www/html/praeco.config.json - ./nginx_config/nginx.conf:/etc/nginx/nginx.conf - ./nginx_config/default.conf:/etc/nginx/conf.d/default.conf
docker-compose.yml – kolejno:
– 16- CA, by elastalert ufał że mój ES jest moim ES
– 19 – mój cooridination node znajduje się na pod adresem hadoop01 i odpowiednim IP.
– 26 – Port 8080 zajęty był przez Ambari, więc użyłem 5602.
{ "appName": "elastalert-server", "port": 3030, "wsport": 3333, "elastalertPath": "/opt/elastalert", "verbose": false, "es_debug": false, "debug": false, "rulesPath": { "relative": true, "path": "/rules" }, "templatesPath": { "relative": true, "path": "/rule_templates" }, "es_host": "hadoop01", "es_port": 9200, "es_username": "elastic", "es_password": "Asdf1!", "es_ssl": true, "writeback_index": "praeco_elastalert_status" }
config/api.config.json – 17-21 – namiary na ES
es_host: hadoop01 es_port: 9200 rules_folder: rules run_every: seconds: 60 buffer_time: minutes: 1 use_ssl: True ca_certs: "/ca.pem" es_username: elastic es_password: Asdf1! writeback_index: praeco_elastalert_status alert_time_limit: days: 2 skip_invalid: True
config/elastalert.json – kolejno:
– 1 – namiary na ES
– 14-17 – wskazanie zaufanego CA, użytkownik i hasło (na produkcji utworzyłbym osobne konto z odpowiednimi uprawnieniami)
Logstash
Pipeline do wrzucania logów z HTTP do Elasticsearch
input { http { host => "hadoop01" port => 7070 } } output { stdout{} elasticsearch { hosts => ["hadoop01"] index => "logstash-test-%{+YYYY.MM.dd}" ssl => true user => logstash_internal password => "Asdf1!" cacert => "/jakas/sciezka/ca.pem" } }
Pipeline do odebrania notyfikacji HTTP z Praeco/ElastAlert
input { http { host => "hadoop01" port => 7077 } } output { stdout{} }
Aby odpalić dwa pipeline jednocześnie, trzeba utworzyć plik YAML który je wskaże. Jeśli nazwiesz go pipelines.yml, wystarczy wskazać folder.
- pipeline.id: ingest-http path.config: "config/pipelines/pipeline.conf" - pipeline.id: output-http path.config: "config/pipelines/pipeline2.conf"
bin/logstash --path.settings sciezka/do/folderu/
Reguła w Praeco
Celem jest sprawdzenie ” czy to działa”, więc reguła będzie prosta. Jeśli w ciągu ostatnich 5 minut będzie więcej niż 40 dokumentów, strzelaj z HTTP.


Akcja
Strzelając POST-em pod hadoop01:7070 na konsoli widzimy log
{ "@version" => "1", "message" => "Wiaderko :-)", "host" => "10.100.64.21", "@timestamp" => 2020-03-08T19:04:29.389Z, "headers" => { "connection" => "keep-alive", "content_type" => "application/json", "http_version" => "HTTP/1.1", "request_path" => "/", "postman_token" => "3416b074-ecf9-4de2-a441-5b7a38bada46", "http_accept" => "*/*", "accept_encoding" => "gzip, deflate, br", "request_method" => "POST", "cache_control" => "no-cache", "http_host" => "hadoop01:7070", "content_length" => "29", "http_user_agent" => "PostmanRuntime/7.22.0" } }
Reakcja
po N-tym strzale widać na konsoli
{ "message" => "Wiaderko :-)", "host" => "172.18.0.2", "headers" => { "http_user_agent" => "python-requests/2.22.0", "http_accept" => "application/json;charset=utf-8", "accept_encoding" => "gzip, deflate", "connection" => "keep-alive", "request_method" => "POST", "http_host" => "hadoop01:7077", "content_type" => "application/json", "http_version" => "HTTP/1.1", "content_length" => "628", "request_path" => "/" }, "@timestamp" => 2020-03-08T19:06:57.251Z, "_id" => "5oyLu3ABCzENcYtTDZXS", "@version" => "1", "num_matches" => 1, "num_hits" => 72, "_index" => "logstash-test-2020.03.08", "_type" => "_doc" }
Po http_user_agent, widać że dostaliśmy POST od ElastAlert (napisany jest w pythonie). num_hits pokrwa nam się z tym co widać w GUI. Niestety w body nie ma nazwy reguły.

Podsumowanie
