В этой публикации я покажу пример того, как можно настроить сбор журналов 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