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
