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


 
	