Сбор журналов Apache в Elasticsearch

В этой публикации я покажу пример того, как можно настроить сбор журналов Apache в Elasticsearch через Filebeat. Примеры развертывания кластера Elasticsearch и хоста с Kibana уже есть в моём блоге. Можете использовать их в качестве опорных руководств по развертывания соответствующих компонентов.

Схема решения

Логическая схеме решения приведена ниже.

Схема ниже отображает физическую топологию решения.

Подготовка сервера Apache

Сначала подготовим сервер Apache:

1. Самый первый шаг – установка Filebeat.

2. Активируйте модуль для nginx:

sudo filebeat modules enable apache
[root@tst05 roman]# sudo filebeat modules enable apache
Enabled apache
[root@tst05 roman]#

3. Откройте на редактирование конфигурационный файл модуля для Apache:

sudo nano /etc/filebeat/modules.d/apache.yml

4. Отредактируйте конфигурационный файл модуля и укажите перечень журналов доступа или ошибок, данные из которых нужно передать:

# Module: apache
# Docs: https://www.elastic.co/guide/en/beats/filebeat/8.13/filebeat-module-apache.html

- module: apache
  # Access logs
  access:
    enabled: true 

    # Set custom paths for the log files. If left empty,
    # Filebeat will choose the paths depending on your OS.
    var.paths: ["/var/log/httpd/access*"]

  # Error logs
  error:
    enabled: true 

    # Set custom paths for the log files. If left empty,
    # Filebeat will choose the paths depending on your OS.
    var.paths: ["/var/log/httpd/error*"]

5. Теперь скорректируем конфигурационный файл Filebeat, чтобы он отправлял данные на наш узел Logstash.

sudo nano /etc/filebeat/filebeat.yml

6. Содержимое моего конфигурационного файла:

# ============================== Filebeat modules ==============================

filebeat.config.modules:
  # Glob pattern for configuration loading
  path: ${path.config}/modules.d/*.yml

  # Set to true to enable config reloading
  reload.enabled: true

  # Period on which files under path should be checked for changes
  reload.period: 30s

# ======================= Elasticsearch template setting =======================

setup.template.settings:
  index.number_of_shards: 1
  #index.codec: best_compression
  #_source.enabled: false


# ================================== General ===================================
tags: ["web", "apache"]

# ================================== Outputs ===================================

# Configure what output to use when sending the data collected by the beat.

# ------------------------------ Logstash Output -------------------------------
output.logstash:
  # The Logstash hosts
  hosts: ["192.168.56.36:5044"]

# ================================= Processors =================================
processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
  - add_cloud_metadata: ~
  - add_docker_metadata: ~
  - add_kubernetes_metadata: ~

7. Перезапустите сервис Filebeat:

sudo systemctl restart filebeat.service

Подготовка Elasticsearch

Выполним подготовку на стороне Elasticsearch.

1. Сначала я создам отдельную роль через Dev Tools для работы с индексами веб серверов:

https://192.168.56.36:5601/app/dev_tools

2. Для этого я выполню следующий запрос:

POST _security/role/logstash_weblogs
{
  "cluster": ["manage_index_templates", "monitor", "manage_ilm"], 
  "indices": [
    {
      "names": [ "weblogs-*" ], 
      "privileges": ["write","create","create_index","manage","manage_ilm"]  
    }
  ]
}

3. Теперь я создам отдельного пользователя:

POST _security/user/logstash_web
{
  "password" : "Qwerty123",
  "roles" : [ "logstash_weblogs"],
  "full_name" : "Logstash User for store weblogs"
}

Предварительная подготовка на стороне Elasticsearch завершена.

Подготовка Logstash

Теперь подготовим сервер Logstash.

1. Сначала загрузим базу с геолокациями:

sudo cd /etc/logstash/
sudo wget https://git.io/GeoLite2-City.mmdb

2. Теперь добавим конфигурационный файл для сбора журналов с веб серверов:

sudo nano /etc/logstash/conf.d/web.conf

3. Пример моего конфигурационного файла:

input {
    beats {
	port => 5044
    }
}

filter {
 grok {
   match => [ "message" , "%{COMBINEDAPACHELOG}+%{GREEDYDATA:extra_fields}"]
#   match => [ "message" , "%{HTTPD_COMMONLOG}+%{QS:referrer}+%{QS:client_agent}+%{GREEDYDATA:extra_fields}"]
   overwrite => [ "message" ]
 }
 mutate {
   convert => ["response", "integer"]
   convert => ["bytes", "integer"]
   convert => ["responsetime", "float"]
 }
 geoip {
   source => "[source][address]"
   target => "geoip"
   database => "/etc/logstash/GeoLite2-City.mmdb"
   add_tag => [ "geoip" ]
 }
 date {
   match => [ "timestamp" , "dd/MMM/YYYY:HH:mm:ss Z" ]
   remove_field => [ "timestamp" ]
 }
mutate { remove_field => [ "message"] }
}

output {
 elasticsearch {
   hosts => ["https://192.168.56.40:9200","https://192.168.56.37:9200"]
   user => logstash_web
   password => Qwerty123
   ssl => true
   cacert => "/etc/logstash/http_ca.crt"
   index => "weblogs-%{+YYYY.MM.dd}"
 }
}

Формат weblogs-%{+YYYY.MM.dd} будет создавать новый индекс для каждого нового дня, что является вполне распространенной сегментацией.

4. Сохраняем внесенные изменения.

5. Перезапускаем сервис Logstash.

sudo systemctl restart logstash.service

Проверка

Теперь проверим наш индекс в Elasticsearch.

Предварительно я попробую обратиться к веб сервер Apache с другого хоста:

curl 192.168.56.93 | head -10

Теперь через Dev Tools я запрошу данные из индекса:

GET weblogs-*/_search
{
  "query": {
    "match_all": {}
  },
  "track_total_hits": true,
  "size": 1, 
  "sort": [
    {
      "@timestamp": {
        "order": "desc"
      }
    }
  ]
}

Результат запроса:

{
  "took": 34,
  "timed_out": false,
  "_shards": {
    "total": 2,
    "successful": 2,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 94,
      "relation": "eq"
    },
    "max_score": null,
    "hits": [
      {
        "_index": "weblogs-2024.04.21",
        "_id": "f33BAI8BUYJcP_C-DjYN",
        "_score": null,
        "_source": {
          "tags": [
            "web",
            "apache",
            "beats_input_codec_plain_applied",
            "geoip"
          ],
          "http": {
            "version": "1.1",
            "response": {
              "status_code": 403,
              "body": {
                "bytes": 7620
              }
            },
            "request": {
              "method": "GET"
            }
          },
          "fileset": {
            "name": "access"
          },
          "service": {
            "type": "apache"
          },
          "user_agent": {
            "original": "curl/7.81.0"
          },
          "host": {
            "os": {
              "version": "9.3 (Blue Onyx)",
              "family": "redhat",
              "kernel": "5.14.0-362.8.1.el9_3.x86_64",
              "name": "Rocky Linux",
              "type": "linux",
              "codename": "Blue Onyx",
              "platform": "rocky"
            },
            "name": "tst05",
            "mac": [
              "CE-5B-98-9F-76-04"
            ],
            "hostname": "tst05",
            "id": "b6f78ca430644c6cb99b8f5565df46f6",
            "ip": [
              "192.168.56.93",
              "fe80::cc5b:98ff:fe9f:7604"
            ],
            "containerized": false,
            "architecture": "x86_64"
          },
          "ecs": {
            "version": "1.12.0"
          },
          "@version": "1",
          "input": {
            "type": "log"
          },
          "@timestamp": "2024-04-21T13:02:58.000Z",
          "url": {
            "original": "/"
          },
          "geoip": {
            "geo": {
              "location": {
                "lon": 37.6068,
                "lat": 55.7386
              },
              "continent_code": "EU",
              "country_iso_code": "RU",
              "timezone": "Europe/Moscow",
              "country_name": "Russia"
            },
            "ip": "31.43.195.167"
          },
          "agent": {
            "id": "6c958160-4aa0-4b57-8299-3fe0fe78a826",
            "version": "8.13.2",
            "ephemeral_id": "7fb20f07-22dc-4387-9481-ca7eca477bce",
            "name": "tst05",
            "type": "filebeat"
          },
          "log": {
            "offset": 1808,
            "file": {
              "path": "/var/log/httpd/access_log"
            }
          },
          "event": {
            "original": "31.43.195.167 - - [21/Apr/2024:20:02:58 +0700] \"GET / HTTP/1.1\" 403 7620 \"-\" \"curl/7.81.0\"",
            "module": "apache",
            "dataset": "apache.access"
          },
          "source": {
            "address": "31.43.195.167"
          }
        },
        "sort": [
          1713704578000
        ]
      }
    ]
  }
}

Именно в таком виде данные и будут храниться в наших индексах.

Если у вас есть желание поделиться своими тонкостями настройки сбора журналов с веб серверов, то пишите в комментариях. Внесем общий вклад в развитие ИТ сообщества и поможем нашим с вами коллегам.

Если что-то не работает

Есть несколько журналов, которые нужно проанализировать в таком случае:

1. Журнал Filebeat на сервере с nginx:

sudo journalctl -ru filebeat.service

2. Журнал Logstash на сервере Logstash:

sudo journalctl -ru logstash.service

Также в конфигурационный файл Logstash вы можете добавить вывод на консоль:

stdout { codec => rubydebug }

Пример вывода в журналах:

3. Журналы Elasticearch на узлах:

sudo journalctl -ru elasticsearch.service

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *