mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
463 lines
9.1 KiB
Markdown
463 lines
9.1 KiB
Markdown
---
|
|||
|
|
name: elk-stack
|
||
|
|
description: Deploy and manage the ELK Stack (Elasticsearch, Logstash, Kibana) for log aggregation and analysis. Configure log pipelines, create visualizations, and implement log-based monitoring. Use when centralizing logs, implementing search functionality, or building log analytics platforms.
|
||
|
|
license: MIT
|
||
|
|
metadata:
|
||
|
|
author: devops-skills
|
||
|
|
version: "1.0"
|
||
|
|
---
|
||
|
|
|
||
|
|
# ELK Stack
|
||
|
|
|
||
|
|
Centralize and analyze logs with Elasticsearch, Logstash, and Kibana.
|
||
|
|
|
||
|
|
## When to Use This Skill
|
||
|
|
|
||
|
|
Use this skill when:
|
||
|
|
- Centralizing logs from multiple sources
|
||
|
|
- Building log search and analytics platforms
|
||
|
|
- Creating log-based dashboards and alerts
|
||
|
|
- Implementing full-text search for logs
|
||
|
|
- Processing and transforming log data
|
||
|
|
|
||
|
|
## Prerequisites
|
||
|
|
|
||
|
|
- Docker or server infrastructure
|
||
|
|
- Sufficient disk space for log storage
|
||
|
|
- Network access from log sources
|
||
|
|
|
||
|
|
## Docker Deployment
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# docker-compose.yml
|
||
|
|
version: '3.8'
|
||
|
|
|
||
|
|
services:
|
||
|
|
elasticsearch:
|
||
|
|
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
|
||
|
|
environment:
|
||
|
|
- discovery.type=single-node
|
||
|
|
- xpack.security.enabled=false
|
||
|
|
- "ES_JAVA_OPTS=-Xms1g -Xmx1g"
|
||
|
|
ports:
|
||
|
|
- "9200:9200"
|
||
|
|
volumes:
|
||
|
|
- elasticsearch-data:/usr/share/elasticsearch/data
|
||
|
|
|
||
|
|
logstash:
|
||
|
|
image: docker.elastic.co/logstash/logstash:8.11.0
|
||
|
|
volumes:
|
||
|
|
- ./logstash/pipeline:/usr/share/logstash/pipeline
|
||
|
|
- ./logstash/config:/usr/share/logstash/config
|
||
|
|
ports:
|
||
|
|
- "5044:5044"
|
||
|
|
- "5000:5000"
|
||
|
|
depends_on:
|
||
|
|
- elasticsearch
|
||
|
|
|
||
|
|
kibana:
|
||
|
|
image: docker.elastic.co/kibana/kibana:8.11.0
|
||
|
|
ports:
|
||
|
|
- "5601:5601"
|
||
|
|
environment:
|
||
|
|
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
|
||
|
|
depends_on:
|
||
|
|
- elasticsearch
|
||
|
|
|
||
|
|
filebeat:
|
||
|
|
image: docker.elastic.co/beats/filebeat:8.11.0
|
||
|
|
user: root
|
||
|
|
volumes:
|
||
|
|
- ./filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
|
||
|
|
- /var/lib/docker/containers:/var/lib/docker/containers:ro
|
||
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||
|
|
depends_on:
|
||
|
|
- logstash
|
||
|
|
|
||
|
|
volumes:
|
||
|
|
elasticsearch-data:
|
||
|
|
```
|
||
|
|
|
||
|
|
## Elasticsearch Configuration
|
||
|
|
|
||
|
|
### Index Templates
|
||
|
|
|
||
|
|
```json
|
||
|
|
PUT _index_template/logs-template
|
||
|
|
{
|
||
|
|
"index_patterns": ["logs-*"],
|
||
|
|
"template": {
|
||
|
|
"settings": {
|
||
|
|
"number_of_shards": 1,
|
||
|
|
"number_of_replicas": 1,
|
||
|
|
"index.lifecycle.name": "logs-policy"
|
||
|
|
},
|
||
|
|
"mappings": {
|
||
|
|
"properties": {
|
||
|
|
"@timestamp": { "type": "date" },
|
||
|
|
"message": { "type": "text" },
|
||
|
|
"level": { "type": "keyword" },
|
||
|
|
"service": { "type": "keyword" },
|
||
|
|
"host": { "type": "keyword" },
|
||
|
|
"trace_id": { "type": "keyword" }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Index Lifecycle Management
|
||
|
|
|
||
|
|
```json
|
||
|
|
PUT _ilm/policy/logs-policy
|
||
|
|
{
|
||
|
|
"policy": {
|
||
|
|
"phases": {
|
||
|
|
"hot": {
|
||
|
|
"min_age": "0ms",
|
||
|
|
"actions": {
|
||
|
|
"rollover": {
|
||
|
|
"max_size": "50GB",
|
||
|
|
"max_age": "1d"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"warm": {
|
||
|
|
"min_age": "7d",
|
||
|
|
"actions": {
|
||
|
|
"shrink": { "number_of_shards": 1 },
|
||
|
|
"forcemerge": { "max_num_segments": 1 }
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"cold": {
|
||
|
|
"min_age": "30d",
|
||
|
|
"actions": {
|
||
|
|
"freeze": {}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"delete": {
|
||
|
|
"min_age": "90d",
|
||
|
|
"actions": {
|
||
|
|
"delete": {}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Logstash Pipeline
|
||
|
|
|
||
|
|
### Basic Pipeline
|
||
|
|
|
||
|
|
```ruby
|
||
|
|
# logstash/pipeline/main.conf
|
||
|
|
input {
|
||
|
|
beats {
|
||
|
|
port => 5044
|
||
|
|
}
|
||
|
|
|
||
|
|
tcp {
|
||
|
|
port => 5000
|
||
|
|
codec => json_lines
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
filter {
|
||
|
|
# Parse JSON logs
|
||
|
|
if [message] =~ /^\{/ {
|
||
|
|
json {
|
||
|
|
source => "message"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Parse timestamp
|
||
|
|
date {
|
||
|
|
match => ["timestamp", "ISO8601", "yyyy-MM-dd HH:mm:ss"]
|
||
|
|
target => "@timestamp"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Add environment tag
|
||
|
|
mutate {
|
||
|
|
add_field => { "environment" => "production" }
|
||
|
|
}
|
||
|
|
|
||
|
|
# Grok pattern for nginx logs
|
||
|
|
if [type] == "nginx" {
|
||
|
|
grok {
|
||
|
|
match => {
|
||
|
|
"message" => '%{IPORHOST:client_ip} - %{USER:user} \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{URIPATHPARAM:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status} %{NUMBER:bytes}'
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
output {
|
||
|
|
elasticsearch {
|
||
|
|
hosts => ["elasticsearch:9200"]
|
||
|
|
index => "logs-%{+YYYY.MM.dd}"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Advanced Filtering
|
||
|
|
|
||
|
|
```ruby
|
||
|
|
filter {
|
||
|
|
# Parse application logs
|
||
|
|
grok {
|
||
|
|
match => {
|
||
|
|
"message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} \[%{DATA:service}\] %{GREEDYDATA:log_message}"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Extract trace ID from message
|
||
|
|
if [log_message] =~ /trace_id=/ {
|
||
|
|
grok {
|
||
|
|
match => { "log_message" => "trace_id=%{UUID:trace_id}" }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# GeoIP lookup
|
||
|
|
if [client_ip] {
|
||
|
|
geoip {
|
||
|
|
source => "client_ip"
|
||
|
|
target => "geoip"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Drop debug logs in production
|
||
|
|
if [level] == "DEBUG" and [environment] == "production" {
|
||
|
|
drop {}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Enrich with lookup
|
||
|
|
translate {
|
||
|
|
field => "status"
|
||
|
|
destination => "status_description"
|
||
|
|
dictionary => {
|
||
|
|
"200" => "OK"
|
||
|
|
"404" => "Not Found"
|
||
|
|
"500" => "Internal Server Error"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Filebeat Configuration
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# filebeat/filebeat.yml
|
||
|
|
filebeat.inputs:
|
||
|
|
- type: container
|
||
|
|
paths:
|
||
|
|
- '/var/lib/docker/containers/*/*.log'
|
||
|
|
processors:
|
||
|
|
- add_docker_metadata:
|
||
|
|
host: "unix:///var/run/docker.sock"
|
||
|
|
|
||
|
|
- type: log
|
||
|
|
enabled: true
|
||
|
|
paths:
|
||
|
|
- /var/log/nginx/*.log
|
||
|
|
tags: ["nginx"]
|
||
|
|
fields:
|
||
|
|
type: nginx
|
||
|
|
|
||
|
|
output.logstash:
|
||
|
|
hosts: ["logstash:5044"]
|
||
|
|
|
||
|
|
logging.level: info
|
||
|
|
logging.to_files: true
|
||
|
|
logging.files:
|
||
|
|
path: /var/log/filebeat
|
||
|
|
name: filebeat
|
||
|
|
keepfiles: 7
|
||
|
|
```
|
||
|
|
|
||
|
|
## Elasticsearch Queries
|
||
|
|
|
||
|
|
### Basic Queries
|
||
|
|
|
||
|
|
```json
|
||
|
|
// Search all logs
|
||
|
|
GET logs-*/_search
|
||
|
|
{
|
||
|
|
"query": {
|
||
|
|
"match_all": {}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Search by keyword
|
||
|
|
GET logs-*/_search
|
||
|
|
{
|
||
|
|
"query": {
|
||
|
|
"match": {
|
||
|
|
"message": "error"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Filter by field
|
||
|
|
GET logs-*/_search
|
||
|
|
{
|
||
|
|
"query": {
|
||
|
|
"bool": {
|
||
|
|
"must": [
|
||
|
|
{ "match": { "level": "ERROR" } },
|
||
|
|
{ "range": { "@timestamp": { "gte": "now-1h" } } }
|
||
|
|
],
|
||
|
|
"filter": [
|
||
|
|
{ "term": { "service": "api-gateway" } }
|
||
|
|
]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Aggregations
|
||
|
|
|
||
|
|
```json
|
||
|
|
// Count by log level
|
||
|
|
GET logs-*/_search
|
||
|
|
{
|
||
|
|
"size": 0,
|
||
|
|
"aggs": {
|
||
|
|
"log_levels": {
|
||
|
|
"terms": { "field": "level" }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Error rate over time
|
||
|
|
GET logs-*/_search
|
||
|
|
{
|
||
|
|
"size": 0,
|
||
|
|
"aggs": {
|
||
|
|
"errors_over_time": {
|
||
|
|
"date_histogram": {
|
||
|
|
"field": "@timestamp",
|
||
|
|
"fixed_interval": "5m"
|
||
|
|
},
|
||
|
|
"aggs": {
|
||
|
|
"error_count": {
|
||
|
|
"filter": { "term": { "level": "ERROR" } }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Kibana Setup
|
||
|
|
|
||
|
|
### Index Patterns
|
||
|
|
|
||
|
|
1. Go to Stack Management → Index Patterns
|
||
|
|
2. Create pattern: `logs-*`
|
||
|
|
3. Set time field: `@timestamp`
|
||
|
|
|
||
|
|
### Saved Searches
|
||
|
|
|
||
|
|
Create saved searches for common queries:
|
||
|
|
- `level:ERROR` - All errors
|
||
|
|
- `service:api-gateway AND level:ERROR` - API gateway errors
|
||
|
|
- `response_time:>1000` - Slow requests
|
||
|
|
|
||
|
|
### Visualizations
|
||
|
|
|
||
|
|
Common visualization types:
|
||
|
|
- **Line Chart**: Error rate over time
|
||
|
|
- **Pie Chart**: Distribution by log level
|
||
|
|
- **Data Table**: Top error messages
|
||
|
|
- **Metric**: Total error count
|
||
|
|
|
||
|
|
### Dashboard Example
|
||
|
|
|
||
|
|
Create dashboard with:
|
||
|
|
1. Total log count (Metric)
|
||
|
|
2. Error rate trend (Line chart)
|
||
|
|
3. Logs by service (Pie chart)
|
||
|
|
4. Recent errors (Data table)
|
||
|
|
5. Log stream (Discover panel)
|
||
|
|
|
||
|
|
## Alerting
|
||
|
|
|
||
|
|
### Watcher (X-Pack)
|
||
|
|
|
||
|
|
```json
|
||
|
|
PUT _watcher/watch/error_alert
|
||
|
|
{
|
||
|
|
"trigger": {
|
||
|
|
"schedule": { "interval": "5m" }
|
||
|
|
},
|
||
|
|
"input": {
|
||
|
|
"search": {
|
||
|
|
"request": {
|
||
|
|
"indices": ["logs-*"],
|
||
|
|
"body": {
|
||
|
|
"query": {
|
||
|
|
"bool": {
|
||
|
|
"must": [
|
||
|
|
{ "match": { "level": "ERROR" } },
|
||
|
|
{ "range": { "@timestamp": { "gte": "now-5m" } } }
|
||
|
|
]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"condition": {
|
||
|
|
"compare": { "ctx.payload.hits.total.value": { "gt": 100 } }
|
||
|
|
},
|
||
|
|
"actions": {
|
||
|
|
"notify_slack": {
|
||
|
|
"webhook": {
|
||
|
|
"scheme": "https",
|
||
|
|
"host": "hooks.slack.com",
|
||
|
|
"port": 443,
|
||
|
|
"method": "post",
|
||
|
|
"path": "/services/xxx",
|
||
|
|
"body": "{\"text\": \"High error rate detected: {{ctx.payload.hits.total.value}} errors in last 5 minutes\"}"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Common Issues
|
||
|
|
|
||
|
|
### Issue: High Disk Usage
|
||
|
|
**Problem**: Elasticsearch consuming too much disk
|
||
|
|
**Solution**: Implement ILM policies, reduce retention
|
||
|
|
|
||
|
|
### Issue: Slow Searches
|
||
|
|
**Problem**: Queries taking too long
|
||
|
|
**Solution**: Optimize index settings, add more shards, use filters
|
||
|
|
|
||
|
|
### Issue: Log Parsing Failures
|
||
|
|
**Problem**: Logs not parsed correctly
|
||
|
|
**Solution**: Test grok patterns, check for log format changes
|
||
|
|
|
||
|
|
### Issue: Memory Pressure
|
||
|
|
**Problem**: Elasticsearch OOM errors
|
||
|
|
**Solution**: Increase heap size (max 50% of RAM), limit field data
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
- Implement index lifecycle management
|
||
|
|
- Use index templates for consistent mappings
|
||
|
|
- Parse logs at ingestion time
|
||
|
|
- Limit stored fields to reduce storage
|
||
|
|
- Use data streams for time-series data
|
||
|
|
- Monitor cluster health
|
||
|
|
- Implement proper security (X-Pack)
|
||
|
|
- Regular index maintenance
|
||
|
|
|
||
|
|
## Related Skills
|
||
|
|
|
||
|
|
- [loki-logging](../loki-logging/) - Alternative logging stack
|
||
|
|
- [prometheus-grafana](../prometheus-grafana/) - Metrics monitoring
|
||
|
|
- [audit-logging](../../../compliance/auditing/audit-logging/) - Compliance logging
|