Главная

Friday, 28 January 2022

Полигон ELK.

Всем привет.

Использование rsyslog для хранения log-сообщений сетевого оборудования, и loganalyzer для их отображения, обозначило необходимость поиска, соответствующей современным требованиям, альтернативы. В результате анализа различных решений был сделан выбор в пользу open-source компонентов стека ELK. Был ли этот выбор полностью осознанным - скорее нет, ибо популярность ELK стека бьет все рекорды. Поэтому не пощупать его своими руками было бы неразумно. Тем более у меня была давняя задача разобраться с IDS SELKS изнутри, где ELK, как вы заметили, является основой.

Итак, основными элементами ELK являются: Elasticsearch (search engine – для хранения и быстрого поиска структурированных данных), Logstash (collector – для приема в различном формате данных, их фильтрации и преобразования, и последующей отправки в различные базы данных) и Kibana (инструмент для визуализации), которые и составляют акроним ELK. Это в классическом варианте исходя из названия, но в последнее время сюда активно подмешивают Filebeat, который идет как в в паре с Logstash, так и вместо него.

Подробное описание работы этих компонентов не планировалось в рамках данной статьи. Интересующая информация по стеку очень хорошо представлена на официальном сайте, в т.ч. в виде подробной документации, а также в огромном количестве статей и публикаций. Именно по ним я поднимал весь стек пошагово. За основу была взята подробная инструкция здесь, ему помагал материал отсюда и отсюда. Каждый автор решал свою практичечкую задачу поэтому мне пришлось компилировать нужное по ходу. Обычно ELK разворачивают на одном сервере  (как с SELKS), но я решил что это будет слишком просто, поэтому  в моем полигоне Elasticsearch один хост, Kibana второй хост, и Logstash+FileBeat третий. До кластера руки не дошли, но я уверен, что это будет создать не так уж и трудно.

В ходе работ было проведено: 

  • установка Elasticsearch
  • установка Kibana
  • установка Logstash
  • установка Filebeat для отправки логов в Logstash
  • установка и настройка Winlogbeat
  • установка плагина Filebeat для отправки netflow Cisco
  • настройка безопасности и авторизация в Kibana (несколько вариантов)
  • проксирование подключений к Kibana через Nginx
  • автоматическая очистка индексов в elasticsearch.


А теперь мои шаги:

1) Проверить для всех хостов стека с Ubuntu 

sudo timedatectl set-timezone Europe/Kiev

sudo apt-get install --reinstall systemd

2) Установить для всех хостов ELK стека

sudo apt-get install apt-transport-https

sudo wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a  /etc/apt/sources.list.d/elastic-7.x.list

# Также ставим JDK for Elasticsearch (Host0) & Logstash (Host1)

sudo apt install default-jdk -y

java -version

3) Настройка Elasticsearch (Host0, IP 192.168.1.11, port 9200,9300) 

- /etc/elasticsearch/elasticsearch.yml:

network.host: 0.0.0.0 (or 192.168.1.11)

http.port: 9200

transport.host: localhost

transport.tcp.port: 9300


# проверка статуса сервиса

sudo service elasticsearch status

# проверка web-отклика elasticsearch:

curl localhost:9200


Список актуальных индексов можно получить при помощи запроса:

curl 'localhost:9200/_cat/indices?v'


Для постоянной чистки индексов лучше использовать elasticsearch-curator:

wget https://packages.elastic.co/curator/5/debian/pool/main/e/elasticsearch-curator/elasticsearch-curator_5.7.5_amd64.deb

sudo dpkg -i elasticsearch-curator_5.7.5_amd64.deb

# /etc/curator/action.yml

actions:

  1:

    action: close

    description: >-

      Close indices older than 60 days (based on index name).

    options:

      ignore_empty_list: True

      delete_aliases: False

      disable_action: False

    filters:

    - filtertype: pattern

      kind: prefix

      value: nginx-

    - filtertype: age

      source: name

      direction: older

      timestring: '%Y.%m.%d'

      unit: days

      unit_count: 60


  2:

    action: delete_indices

    description: >-

      Delete indices older than 90 days (based on index name).

    options:

      ignore_empty_list: True

      disable_action: False

    filters:

    - filtertype: pattern

      kind: prefix

      value: nginx-

    - filtertype: age

      source: name

      direction: older

      timestring: '%Y.%m.%d'

      unit: days

      unit_count: 90


# Test dry-run:

sudo curator --config /etc/curator/config.yml --dry-run  /etc/curator/action.yml


# /etc/curator/delete_indices.yml

actions:

  1:

    action: delete_indices

    description: >-

      Delete indices older than 90 days (based on index name), for logstash

      and filebeat prefixed indices.

    options:

      ignore_empty_list: True

      timeout_override:

      continue_if_exception: False

      disable_action: False

    filters:

    - filtertype: pattern

      kind: regex

      value: '^(logstash-|filebeat-|nginx-).*$'

      exclude:

    - filtertype: age

      source: name

      direction: older

      timestring: '%Y.%m.%d'

      unit: days

      unit_count: 90

      exclude:


# Test dry-run:

sudo curator --config /etc/curator/config.yml --dry-run  /etc/curator/delete_indices.yml

Очистка индексов будет выполняться каждый день в 4 утра:

crontab -e 0 4 * * * /usr/bin/curator --config /etc/curator/config.yml /etc/curator/action.yml

Хотя в последних версиях Elastic есть настройка автоматической чистки индекса штатными средствами, без curator-а.


4) Настройка Filebeat (Host1, IP 192.168.1.12, port 5044)

- /etc/filebeat/filebeat.yml:

# что именно мониторим:

filebeats.inputs:

- type: filestream

enabled: true

         paths:

           - /var/log/nginx/*.log

           - /var/log/*.log


# Можно настроить передачу напрямую в Elasticsearch или Logstash.

# Можно настроить передачу напрямую в Kibana через API. Нам это понадобится для отрисовывания графиков netflow (п.12).

setup.kibana:

  host: "192.168.1.12:5601"

# Мы передаем данные локально в logstash:

output.logstash:

  hosts: ['localhost:5044']


# Проверяем корректность конфига:

sudo filebeat test config -e

# Если сервис filebeat не запускается нужно проверить файл:

- /etc/systemd/system/filebeat.service:

[Unit]

Description=Filebeat sends log files to Logstash or directly to Elasticsearch.

Documentation=https://www.elastic.co/products/beats/filebeat

Wants=network-online.target

After=network-online.target

[Service]

ExecStart=/usr/share/filebeat/bin/filebeat --path.config "/etc/filebeat"

Restart=always

[Install]

WantedBy=multi-user.target

У меня тут что-то не сошлось и мне его пришлось создавать руками.

:проверка статуса

sudo systemctl status filebeat

:проверка журнала

sudo tail -f /usr/share/filebeat/bin/logs/filebeat

Модули filebeat работают только в том случае, если вы отправляете данные напрямую в Elasticsearch. На него вы тоже ставите соответствующий плагин и получаете отформатированные данные с помощью elastic ingest. Но у нас работает промежуточное звено Logstash, который принимает данные. С ним плагины filebeat не работают, поэтому приходится отдельно в logstash парсить данные. Это не очень сложно, но тем не менее. Как мне обьяснили это плата за удобства, которые дает logstash. Если у вас много разрозненных данных, то отправлять их напрямую в Elasticsearch не так удобно, как с использованием предобработки в Logstash.


5) Настройка Logstash  (Host1, IP 192.168.1.12, port 1514) 

- /etc/logstash/logstash.yml

All params have been commented.

# Прием логов с Filebeat:

/etc/logstash/conf.d/input.conf

input {

  beats {

    port => 5044

    client_inactivity_timeout => "1200"

# SSL отключен потому как Logstash и Filebeat общаются на одном Host1

    ssl => false

    ssl_certificate => "/etc/ssl/logstash/logstash.crt"

    ssl_key => "/etc/ssl/logstash/logstash.key"

  }


  file {

    path => "/var/log/nginx/*.log"

  }

}


# Фильтрация логов c nginx (его мы будет мониторить для теста всего стека):

/etc/logstash/conf.d/nginx-error.conf

filter {

    if [type] == "nginx_error" {

      grok {

        match => { "message" => "(?<timestamp>%{YEAR}[./] %{MONTHNUM}[./]%{MONTHDAY} %{TIME}) \[%{LOGLEVEL:severity}\] %{POSINT:pid}#%{NUMBER}: %{GREEDYDATA:errormessage},

\ client: %{IP:client}, server: \$domain, request: \"%{WORD:method} %{URIPATH:path}\"" }

      }

    }

    else if [type] == "nginx_access" {

      grok {

        match => { "message" => "%{IPORHOST:remote_ip} - %{DATA:user} \[%{HTTPDATE:access_time}\] \"%{WORD:http_method} %{DATA:url} HTTP/%{NUMBER:http_version}\" %{NUMBER:response_code} %{NUMBER:body_sent_bytes} \"%{DATA:referrer}\" \"%{DATA:agent}\"" }

    }

  }

    date {

          match => [ "timestamp" , "dd/MMM/YYYY:HH:mm:ss Z" ]

  }

    geoip {

           source => "remote_ip"

           target => "geoip"

           add_tag => [ "nginx-geoip" ]

  }

}


# Передача логов на Elasticsearch:

/etc/logstash/conf.d/output.conf

output {

    if [type] == "nginx_access" {

        elasticsearch {

            hosts     => "192.168.1.11:9200"

            index     => "nginx-%{+YYYY.MM.dd}"

        }

    }

    else if [type] == "nginx_error" {

        elasticsearch {

            hosts     => "192.168.1.11:9200"

            index     => "nginx-%{+YYYY.MM.dd}"

        }

    }

    else if "winsrv" in [tags] {

        elasticsearch {

            hosts     => "192.168.1.11:9200"

            index     => "winsrv-%{+YYYY.MM.dd}"

        }

    }

    else {

        elasticsearch {

            hosts     => "192.168.1.11:9200"

            index     => "logstash_msg"

        }

    }

#stdout { codec => rubydebug }

}


:проверка статуса

sudo systemctl status logstash.service


:проверка журнала

sudo tail -f /var/log/logstash/logstash-plain.log


6) Настройка NGINX  (Host1, IP 192.168.1.11, port 80,443) для теста 

sudo apt install nginx -y

sudo nginx -t


7) Настройка Kibana (Host2, IP 192.168.1.10, port 5601) 

- /etc/kibana/kibana.yml

server.host: 192.168.1.10

server.port: 5601

...

Пс: создание SSL-сертификатов в п.10.

# включаем доступ к frontend Kibana по https:

server.ssl.enabled: true

server.ssl.certificate: /home/vagrant/Kibana.crt

server.ssl.key: /home/vagrant/rootCA.key


# включаем доступ к frontend Kibana через Nginx:

Создаем файл для пользователя и генерируем пароль:

# htpasswd -c /etc/nginx/htpasswd.users kibanauser

-OR-

# echo "kibanauser:`openssl passwd -apr1`" | sudo tee -a /etc/nginx/htpasswd.users


# Создаем файл с виртуальным сайтом для веб-сервера Nginx: 

sudo nano /etc/nginx/sites-available/elk


# Проверяем в kibana.yml:

server.host: localhost


# доступ по http Nginx:

- /etc/nginx/sites-available/elk

server {

listen 80;


server_name 192.168.1.10;

auth_basic "Restricted Access";

auth_basic_user_file /etc/nginx/htpasswd.users;


location / {

proxy_pass http://localhost:5601/;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection "upgrade";

proxy_set_header Host $host;

proxy_cache_bypass $http_upgrade;

}

}


-OR-

# досту по https Nginx:

server {

listen 443 ssl;


server_name 192.168.1.10;

# by password

auth_basic "Restricted Access";

auth_basic_user_file /etc/nginx/htpasswd.users;

# by IP

        allow 192.168.1.0/24;

        deny all;

# by SSL

ssl_certificate /home/vagrant/Kibana.crt;

ssl_certificate_key /home/vagrant/rootCA.key;

        ssl_ciphers EECDH:+AES256:-3DES:RSA+AES:RSA+3DES:!NULL:!RC4:!RSA+3DES;

        ssl_prefer_server_ciphers on;

        ssl_protocols TLSv1.1 TLSv1.2;


        add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";

        add_header X-XSS-Protection "1; mode=block";

        add_header X-Frame-Options "SAMEORIGIN";

        add_header X-Content-Type-Options nosniff;


location / {

proxy_pass https://localhost:5601/;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection 'upgrade';

proxy_set_header Host $host;

proxy_cache_bypass $http_upgrade;

                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                proxy_set_header X-Real-IP $remote_addr;

}

}


sudo ln -s /etc/nginx/sites-available/elk /etc/nginx/sites-enabled/

:start

sudo service kibana start

:проверка статуса

sudo systemctl status kibana

sudo service kibana status

:проверка журнала

sudo tail -f /var/log/kibana/kibana.log


Тоже самое сделать и для Nginx.


8) Включаем Elastic security.

По умолчанию ELK беззащитен, поэтому добавим базовую защиту.

Закрываем в /etc/nginx/sites-available/elk две строки закомментируем иначе будет масло масленное:

# auth_basic "Restricted Access";

# auth_basic_user_file /etc/nginx/htpasswd.users;


Генерируем пароли от Elastic-a:

# /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto


Changed password for user apm_system

PASSWORD apm_system = iBRUvfTvOWOrxCrVd2K5

Changed password for user kibana_system

PASSWORD kibana_system = 0eRyH6kzsmyC7tvc5PBK

Changed password for user kibana

PASSWORD kibana = 0eRyH6kzsmyC7tvc5PBK

Changed password for user logstash_system

PASSWORD logstash_system = K1k6Vuh9zqxzkb4YILW5

Changed password for user beats_system

PASSWORD beats_system = gM0AUnS062SA71VQjlyb

Changed password for user remote_monitoring_user

PASSWORD remote_monitoring_user = GPnQJnrhKUpEmVlqJoXA

Changed password for user elastic (Web Kibana)

PASSWORD elastic = VouGayh9oNHrjtthnI9k

Эти пароли надо запомнить, ибо в явном виде они более нигде не видны.

Некоторые из них можно будет позже изменить в фронтенде Kibana.

Далее в Kibana заходим пользователем elastic!



9) Elastic security update - выполним необходимые изменения ибо теперь Elastic без пароля не пускает никого.

# для Kibana в kibana.yml добавляем:

xpack.security.enabled: true

elasticsearch.username: "kibana_system"

elasticsearch.password: "0eRyH6kzsmyC7tvc5PBK"


Для приема данных из Logstash не стоит использовать встроенного logstash_system, а лучше создать нового Logstash_index.

# для Logstash в output.yml в каждую секцию добавляем:

user     => "Logstash_index"

password => "zkb4YILW5"


Для входа в фронтенд Kibana также следует вместо встроенного elastic-а создать нового типа  kibana_admin:

POST /_security/user/kibana_admin

{

  "password" : "123456",

  "roles" : [ "kibana_admin" ],

  "full_name" : "Kibana Administrator",

  "email" : "kibana.administrator@example.local"

}

и после добавить ему необходимые роли, чтобы он видел индексы в Discovery и пользоваться далее уже им.

Также настраиваем параметры пользовательских сессий Kibana:

- закрываем неактивные сессии:

xpack.security.session.idleTimeout: "30m"

- устанавливаем максимальную продолжительность одной сессии:

xpack.security.session.lifespan: "1d"

- настраиваем интервал принудительной очистки данных о неактивных или просроченных сессиях из сессионного индекса:

xpack.security.session.cleanupInterval: "8h"

Для всех параметров формат времени может быть следующим: ms | s | m | h | d | w | M | Y

Данные о сессии удаляются после того, как пользователь осуществляет закрытие сессии. Если не настроить закрытие неактивных сессий или не ограничить максимальную длительность сессии, то информация об этих сессиях будет накапливаться в сессионном индексе, и Kibana не сможет автоматически удалить данные.


10) SSL  - то что надо было сделать с самого начала.

Классификация SSL-файлов:

.pem, .crt, .cer - готовый, подписанный центром сертификации сертификат, расширения разные, но означают одно и то же. Если совсем просто, то сертификат, это подписанный открытый ключ, плюс немного информации о вашей организаци;

.key - закрытый или открытый ключ;

.csr - запрос на подпись сертификата, в этом файле хранится ваш открытый ключ плюс информация, об организации и домене, которую вы указали.

Для генерации мы используем OpenSSL на примере Host1.

1. Создаем закрытый "корневой" ключ:

openssl genrsa -out LogstashCA.key

2. Создаем корневой сертификат с использованием сгенерированого ключа:

openssl req -x509 -new -nodes -key LogstashCA.key -sha256 -days 1024 -out Logstash.pem

3. Создаем запрос на сертификат (CSR):

CSR - обычно это та информация, которая отправляется в УЦ, но в нашем случае мы подписываем сертификат самостоятельно.

openssl req -new -key LogstashCA.key -out Logstash.csr

4. Выпускаем сертификат:

openssl x509 -req -in Logstash.csr -CA Logstash.pem -CAkey LogstashCA.key -CAcreateserial -out Logstash.crt -days 365 -sha256

Копируем файлы в соот папки:

ssl_certificate => "/etc/ssl/logstash/logstash.crt"

ssl_key => "/etc/ssl/logstash/logstash.key"


11) Подключаем плагин Netflow для filebeat.

За подробностями идете сюда.

filebeat modules enable netflow

# cat /etc/filebeat/modules.d/netflow.yml

- module: netflow

  log:

    enabled: true

    var:

      netflow_host: 192.168.100.1

      netflow_port: 9996


systemctl stop filebeat

systemctl start filebeat


# и для Cisco router включаем netflow на передачу:

router(conf-if)#ip route-cache flow

router(conf-if)#exit

router(config)#ip flow-export destination 192.168.1.12 9996

router(config)#ip flow-export version 5

Вот на этом вроде бы все. На самом деле это только начало, впереди еще изучение всего фронтенда Kibana, и также фильтрации сообщений через Grok. Плюс ко всему надо будет вернуться к SELKS. Скучно точно не будет! 


No comments:

Post a Comment

А что вы думаете по этому поводу?