ELK Stack ile Compliance ve Denetim Log Yönetimi
Compliance denetimlerinde en çok zaman kaybettiğim anların birinde, bir güvenlik denetçisi benden “son 90 günlük tüm yetkisiz erişim denemelerini” istedi. O an elimde dağınık log dosyaları vardı, 47 farklı sunucuya dağılmış, farklı formatlarda, kısmen rotate edilmiş. Üç gün sonra ancak sunabilmiştim raporu. İşte o günden sonra ELK Stack üzerinde ciddi bir compliance altyapısı kurmak benim için zorunluluk haline geldi.
Compliance Log Yönetimi Neden Özeldir?
Sıradan operasyonel log yönetiminden farklı olarak compliance logları için bazı kritik gereksinimler var. Değiştirilemezlik, uzun süreli saklama, denetlenebilir erişim ve raporlanabilirlik bunların başında geliyor. PCI-DSS, ISO 27001, KVKK ve BDDK gibi regülasyonlar genellikle logların en az 1 yıl, bazı durumlarda 5 yıl saklanmasını şart koşuyor.
ELK Stack bu gereksinimler için iyi bir zemin sunuyor ancak kutudan çıktığı haliyle compliance-ready değil. Doğru konfigürasyon, doğru veri modeli ve doğru retention politikaları olmadan sadece pahalı bir log deposu kalırsınız.
Temel Mimari Tasarım
Compliance odaklı bir ELK kurulumunda şu bileşenler olmalı:
- Beats/Logstash: Veri toplama katmanı
- Elasticsearch: Depolama ve arama
- Kibana: Görselleştirme ve raporlama
- ILM (Index Lifecycle Management): Otomatik log yaşam döngüsü
- Audit logging: ELK’nin kendisinin denetim logları
- Frozen/Searchable snapshot: Uzun vadeli arşivleme
Kritik bir tasarım kararı: compliance loglarını operasyonel loglardan ayırın. Aynı cluster’ı kullansanız bile ayrı index’ler, ayrı ILM politikaları ve ayrı erişim kontrolleri şart.
Logstash Pipeline Konfigürasyonu
Compliance logları için özel bir Logstash pipeline kuruyoruz. Burada önemli olan her log kaydının kim tarafından, ne zaman, hangi sistemden geldiğini kesin olarak takip etmek.
# /etc/logstash/conf.d/compliance-pipeline.conf
input {
beats {
port => 5044
ssl => true
ssl_certificate => "/etc/logstash/certs/logstash.crt"
ssl_key => "/etc/logstash/certs/logstash.key"
ssl_certificate_authorities => ["/etc/logstash/certs/ca.crt"]
ssl_verify_mode => "force_peer"
}
syslog {
port => 5514
type => "syslog_compliance"
}
}
filter {
# Kaynak doğrulama
if ![agent][hostname] {
drop { }
}
# Timestamp normalizasyonu - compliance için kritik
date {
match => [ "timestamp", "ISO8601", "MMM dd HH:mm:ss", "MMM d HH:mm:ss" ]
timezone => "Europe/Istanbul"
target => "@timestamp"
}
# Zorunlu compliance alanları
mutate {
add_field => {
"compliance.ingestion_time" => "%{+ISO8601}"
"compliance.pipeline_version" => "2.1"
"compliance.immutable" => "true"
}
}
# Authentication logları için özel parsing
if [type] == "auth" or [fields][log_type] == "authentication" {
grok {
match => {
"message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{HOSTNAME:hostname} %{WORD:process}(?:[%{POSINT:pid}])?: %{GREEDYDATA:auth_message}"
}
}
if "Failed password" in [auth_message] {
mutate {
add_field => { "security.event_type" => "failed_authentication" }
add_tag => [ "security_event", "failed_auth" ]
}
}
if "Accepted" in [auth_message] {
mutate {
add_field => { "security.event_type" => "successful_authentication" }
add_tag => [ "security_event", "successful_auth" ]
}
}
}
}
output {
if "security_event" in [tags] {
elasticsearch {
hosts => ["https://elk-node1:9200", "https://elk-node2:9200"]
index => "compliance-security-%{+YYYY.MM}"
user => "logstash_compliance_writer"
password => "${LOGSTASH_COMPLIANCE_PASS}"
ssl => true
cacert => "/etc/logstash/certs/ca.crt"
ilm_enabled => true
ilm_rollover_alias => "compliance-security"
ilm_policy => "compliance-retention-policy"
}
}
elasticsearch {
hosts => ["https://elk-node1:9200", "https://elk-node2:9200"]
index => "compliance-audit-%{+YYYY.MM}"
user => "logstash_compliance_writer"
password => "${LOGSTASH_COMPLIANCE_PASS}"
ssl => true
cacert => "/etc/logstash/certs/ca.crt"
ilm_enabled => true
ilm_rollover_alias => "compliance-audit"
ilm_policy => "compliance-retention-policy"
}
}
Index Lifecycle Management Politikası
Bu kısım compliance’ın omurgası. Log verilerinin ne kadar süre hot storage’da, ne kadar warm’da, ne kadar cold’da kalacağını ve ne zaman silineceğini tanımlıyoruz.
# ILM politikasını Elasticsearch API'si ile oluştur
curl -X PUT "https://localhost:9200/_ilm/policy/compliance-retention-policy"
-u elastic:${ELASTIC_PASS}
-H "Content-Type: application/json"
-d '{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_primary_shard_size": "50gb",
"max_age": "30d"
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
},
"set_priority": {
"priority": 50
},
"readonly": {}
}
},
"cold": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "compliance-archive-s3"
},
"set_priority": {
"priority": 0
}
}
},
"frozen": {
"min_age": "365d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "compliance-archive-s3",
"force_merge_index": true
}
}
},
"delete": {
"min_age": "1825d",
"actions": {
"delete": {}
}
}
}
}
}'
Bu politikada loglar 5 yıl sonra siliniyor. KVKK ve bankacılık regülasyonları için yeterli. delete phase’ini kaldırırsanız loglar sonsuza kadar saklanır, ama bu genellikle pratik değil.
Elasticsearch Index Template ve Mapping
Compliance index’leri için sıkı bir mapping tanımlamak şart. Özellikle _source alanını disable etmeyin, denetçiler ham veriyi görmek isteyebilir.
curl -X PUT "https://localhost:9200/_index_template/compliance-template"
-u elastic:${ELASTIC_PASS}
-H "Content-Type: application/json"
-d '{
"index_patterns": ["compliance-*"],
"priority": 200,
"template": {
"settings": {
"number_of_shards": 2,
"number_of_replicas": 1,
"index.lifecycle.name": "compliance-retention-policy",
"index.lifecycle.rollover_alias": "compliance-audit",
"index.codec": "best_compression",
"index.mapping.total_fields.limit": 2000
},
"mappings": {
"dynamic": "strict",
"_source": {
"enabled": true
},
"properties": {
"@timestamp": { "type": "date" },
"compliance": {
"properties": {
"ingestion_time": { "type": "date" },
"pipeline_version": { "type": "keyword" },
"immutable": { "type": "keyword" }
}
},
"security": {
"properties": {
"event_type": { "type": "keyword" },
"user": { "type": "keyword" },
"source_ip": { "type": "ip" },
"action": { "type": "keyword" },
"result": { "type": "keyword" }
}
},
"host": {
"properties": {
"name": { "type": "keyword" },
"ip": { "type": "ip" }
}
},
"message": { "type": "text", "index": false }
}
}
}
}'
"dynamic": "strict" ayarına dikkat edin. Tanımlanmamış alanlar gelirse Elasticsearch hata verir. Bu compliance açısından iyi bir şey, beklenmedik veri formatlarını engeller.
Role-Based Access Control
Compliance loglarına kimin erişebildiği de denetim kapsamında. Denetçiler okuma yetkisine sahip olmalı ama yazma veya silme yetkisi hiç kimseye olmamalı.
# Denetçi rolü oluştur
curl -X PUT "https://localhost:9200/_security/role/compliance_auditor"
-u elastic:${ELASTIC_PASS}
-H "Content-Type: application/json"
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["compliance-*"],
"privileges": ["read", "view_index_metadata"],
"field_security": {
"grant": ["*"],
"except": []
}
}
],
"applications": [
{
"application": "kibana-.kibana",
"privileges": ["feature_discover.read", "feature_dashboard.read"],
"resources": ["*"]
}
]
}'
# Log yazma rolü - sadece Logstash için
curl -X PUT "https://localhost:9200/_security/role/compliance_writer"
-u elastic:${ELASTIC_PASS}
-H "Content-Type: application/json"
-d '{
"cluster": ["monitor", "manage_index_templates", "manage_ilm"],
"indices": [
{
"names": ["compliance-*"],
"privileges": ["create_index", "index", "create"]
}
]
}'
Kimse compliance indexlerinde delete yetkisine sahip olmasın. Bunu enforce etmek için Elasticsearch’ün Audit Logging özelliğini de açmanız gerekiyor.
Elasticsearch Audit Logging Konfigürasyonu
ELK Stack’in kendisinin de denetim logu üretmesi gerekiyor. Yani “kim ne zaman hangi log kaydına baktı” sorusunun cevabı olmalı.
# elasticsearch.yml içine ekle
cat >> /etc/elasticsearch/elasticsearch.yml << 'EOF'
xpack.security.audit.enabled: true
xpack.security.audit.logfile.events.include:
- authentication_success
- authentication_failed
- access_denied
- anonymous_access_denied
- connection_denied
- tampered_request
- run_as_denied
- run_as_granted
- security_config_change
xpack.security.audit.logfile.events.exclude:
- access_granted
xpack.security.audit.logfile.emit_node_name: true
xpack.security.audit.logfile.emit_node_host_address: true
EOF
systemctl restart elasticsearch
Bu konfigürasyonla Elasticsearch kendi erişimlerini /var/log/elasticsearch/ altında ayrı bir dosyaya yazacak. Bu dosyaları da aynı ELK pipeline’ına besleyin, ancak ayrı bir index’e.
Otomatik Compliance Raporu Oluşturma
Denetimler genellikle düzenli aralıklarla gerçekleşiyor. Bu script haftalık compliance özeti çıkartıyor:
#!/bin/bash
# /opt/compliance/generate_weekly_report.sh
ELASTIC_URL="https://localhost:9200"
ELASTIC_USER="compliance_reporter"
ELASTIC_PASS="${ELASTIC_REPORTER_PASS}"
REPORT_DATE=$(date +%Y-%m-%d)
WEEK_AGO=$(date -d '7 days ago' +%Y-%m-%dT%H:%M:%S)
echo "Compliance Haftalik Raporu - ${REPORT_DATE}" > /opt/compliance/reports/weekly-${REPORT_DATE}.txt
echo "============================================" >> /opt/compliance/reports/weekly-${REPORT_DATE}.txt
# Basarisiz kimlik dogrulama sayisi
FAILED_AUTH=$(curl -s -u "${ELASTIC_USER}:${ELASTIC_PASS}"
"${ELASTIC_URL}/compliance-security-*/_count"
-H "Content-Type: application/json"
-d "{
"query": {
"bool": {
"must": [
{ "term": { "security.event_type": "failed_authentication" } },
{ "range": { "@timestamp": { "gte": "${WEEK_AGO}" } } }
]
}
}
}" | jq -r '.count')
echo "Basarisiz Kimlik Dogrulama: ${FAILED_AUTH}" >> /opt/compliance/reports/weekly-${REPORT_DATE}.txt
# En cok basarisiz giris denemesi yapilan IP'ler
echo -e "nEn Cok Saldirganlarin IP Adresleri:" >> /opt/compliance/reports/weekly-${REPORT_DATE}.txt
curl -s -u "${ELASTIC_USER}:${ELASTIC_PASS}"
"${ELASTIC_URL}/compliance-security-*/_search"
-H "Content-Type: application/json"
-d "{
"size": 0,
"query": {
"bool": {
"must": [
{ "term": { "security.event_type": "failed_authentication" } },
{ "range": { "@timestamp": { "gte": "${WEEK_AGO}" } } }
]
}
},
"aggs": {
"top_ips": {
"terms": {
"field": "security.source_ip",
"size": 10
}
}
}
}" | jq -r '.aggregations.top_ips.buckets[] | "(.key): (.doc_count) deneme"'
>> /opt/compliance/reports/weekly-${REPORT_DATE}.txt
echo "Rapor olusturuldu: /opt/compliance/reports/weekly-${REPORT_DATE}.txt"
# Raporu mail ile gonder
if command -v mail &> /dev/null; then
mail -s "Haftalik Compliance Raporu - ${REPORT_DATE}"
[email protected] < /opt/compliance/reports/weekly-${REPORT_DATE}.txt
fi
# Script'i haftalik calistir
chmod +x /opt/compliance/generate_weekly_report.sh
cat > /etc/cron.d/compliance-report << 'EOF'
0 6 * * 1 root /opt/compliance/generate_weekly_report.sh >> /var/log/compliance-report.log 2>&1
EOF
Snapshot ve Arşivleme Stratejisi
Uzun vadeli saklama için S3 veya S3-compatible bir depo şart. Minio ile on-premise da çalışabilirsiniz.
# S3 snapshot repository kaydet
curl -X PUT "https://localhost:9200/_snapshot/compliance-archive-s3"
-u elastic:${ELASTIC_PASS}
-H "Content-Type: application/json"
-d '{
"type": "s3",
"settings": {
"bucket": "elk-compliance-archive",
"region": "eu-central-1",
"base_path": "snapshots",
"compress": true,
"server_side_encryption": true,
"storage_class": "STANDARD_IA",
"max_snapshot_bytes_per_sec": "40mb",
"max_restore_bytes_per_sec": "40mb"
}
}'
# Otomatik snapshot politikasi
curl -X PUT "https://localhost:9200/_slm/policy/compliance-daily-snapshot"
-u elastic:${ELASTIC_PASS}
-H "Content-Type: application/json"
-d '{
"schedule": "0 30 2 * * ?",
"name": "<compliance-snap-{now/d}>",
"repository": "compliance-archive-s3",
"config": {
"indices": ["compliance-*"],
"include_global_state": false,
"metadata": {
"policy": "compliance-daily-snapshot",
"created_by": "slm"
}
},
"retention": {
"expire_after": "1825d",
"min_count": 5,
"max_count": 1825
}
}'
Filebeat ile Kritik Sistem Loglarını Toplama
Sunucularda Filebeat konfigürasyonu compliance perspektifinden özelleştirilmeli:
# /etc/filebeat/filebeat.yml - compliance odakli konfig
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/auth.log
- /var/log/secure
fields:
log_type: authentication
compliance_scope: "pci-dss,iso27001"
data_classification: "confidential"
fields_under_root: false
scan_frequency: 10s
close_inactive: 5m
- type: log
enabled: true
paths:
- /var/log/audit/audit.log
fields:
log_type: system_audit
compliance_scope: "pci-dss"
fields_under_root: false
- type: log
enabled: true
paths:
- /var/log/sudo.log
- /var/log/sulog
fields:
log_type: privilege_escalation
compliance_scope: "iso27001,kvkk"
fields_under_root: false
processors:
- add_host_metadata:
when.not.contains.tags: forwarded
- add_cloud_metadata: ~
- fingerprint:
fields: ["message", "@timestamp", "host.name"]
target_field: "_id"
method: "SHA-256"
output.logstash:
hosts: ["logstash.internal:5044"]
ssl.certificate_authorities: ["/etc/filebeat/certs/ca.crt"]
ssl.certificate: "/etc/filebeat/certs/filebeat.crt"
ssl.key: "/etc/filebeat/certs/filebeat.key"
logging.level: warning
logging.to_files: true
logging.files:
path: /var/log/filebeat
name: filebeat
keepfiles: 7
permissions: 0640
fingerprint processor önemli bir nokta burada. Her log kaydı için SHA-256 hash üretip Elasticsearch’ün doküman ID’si olarak kullanıyoruz. Bu sayede aynı log iki kez geldiğinde duplicate oluşmuyor ve log kaydının değiştirilmediğini hash ile doğrulayabiliyorsunuz.
Pratik Denetim Senaryoları
Gerçek bir denetimde denetçiler şu soruları soruyor, ELK’den nasıl cevaplanıyor bakalım:
“15 Mart 2024 tarihinde saat 03:00-05:00 arasında veritabanı sunucusuna kim erişti?”
Kibana Discover ekranında şu KQL sorgusu yeterli:
host.name: "db-server-01" AND @timestamp >= "2024-03-15T03:00:00" AND @timestamp <= "2024-03-15T05:00:00" AND security.event_type: "successful_authentication"
“Son 30 günde root yetkisiyle çalıştırılan komutlar neler?”
fields.log_type: "privilege_escalation" AND message: "sudo" AND @timestamp >= now-30d
Bu sorguların sonuçlarını Kibana’dan CSV olarak export edip denetçiye verebilirsiniz. Bu işlemin kendisi de audit log’a düşüyor çünkü.
Log Bütünlüğünü Doğrulama
Compliance açısından en sık sorulan sorulardan biri: “Bu loglar değiştirilmedi mi?” Bunu kanıtlamak için periyodik hash doğrulama yapıyoruz:
#!/bin/bash
# /opt/compliance/verify_integrity.sh
# Her gece index bütünlüğünü kontrol eder
ELASTIC_URL="https://localhost:9200"
DATE_PATTERN=$(date -d 'yesterday' +%Y.%m)
INDEX_NAME="compliance-audit-${DATE_PATTERN}"
# Index istatistiklerini cek
STATS=$(curl -s -u "elastic:${ELASTIC_PASS}"
"${ELASTIC_URL}/${INDEX_NAME}/_stats/docs,store")
DOC_COUNT=$(echo $STATS | jq -r ".indices["${INDEX_NAME}"].total.docs.count")
STORE_SIZE=$(echo $STATS | jq -r ".indices["${INDEX_NAME}"].total.store.size_in_bytes")
# Hash olustur
INTEGRITY_HASH=$(echo "${INDEX_NAME}:${DOC_COUNT}:${STORE_SIZE}" | sha256sum | awk '{print $1}')
# Sonucu kaydet
echo "$(date +%Y-%m-%dT%H:%M:%S) | ${INDEX_NAME} | docs:${DOC_COUNT} | size:${STORE_SIZE} | hash:${INTEGRITY_HASH}"
>> /opt/compliance/integrity-records.log
echo "Butunluk kontrolu tamamlandi: ${INDEX_NAME}"
echo "Hash: ${INTEGRITY_HASH}"
Bu script her gün çalışır ve index’in doküman sayısı ile boyutunun hash’ini kaydeder. Bir değişiklik olursa hash eşleşmez ve alarm tetiklenir.
Kibana Dashboard’larında Compliance Görünümü
Compliance dashboard’ları operasyonel dashboard’lardan farklı olmalı. Denetçiler için sade, anlaşılır ve export edilebilir görseller gerekiyor. Kibana’da şu panelleri mutlaka oluşturun:
- Günlük başarısız kimlik doğrulama trendi: Bar chart, 90 günlük pencere
- Privileged access haritası: Hangi kullanıcı hangi sunucuya erişmiş
- Coğrafi erişim haritası: Türkiye dışından gelen erişimler kırmızıyla
- Log kaynak durumu: Hangi sunuculardan log geliyor, hangileri sessiz
- Retention durumu: Index’lerin hangi lifecycle phase’inde olduğu
Dashboard’ları PDF olarak export eden özelliği Kibana Reporting ile otomatize edin. Aylık PDF raporları denetçilere mail ile göndermek ciddi zaman kazandırıyor.
Sonuç
ELK Stack ile compliance log yönetimi kurulumu bir kez yapıp unutulacak bir iş değil. Regülasyonlar değişiyor, yeni sistemler ekleniyor, veri hacimleri büyüyor. Bu yüzden altyapıyı baştan esnek tasarlamak önemli.
Özellikle vurgulayacağım birkaç nokta var: Önce compliance index’lerini operasyonel index’lerden ayırın, bunu sonradan yapmak acı verici. ILM politikalarını regülasyon gereksinimlerine göre yazın, varsayılan değerleri kullanmayın. Elasticsearch’ün kendi audit logging’ini mutlaka açın, sistemi kimin kullandığı bilgisi en az sistem logları kadar önemli. Son olarak, sadece log toplamak yetmez; periyodik olarak sorgu yapın, rapor üretin, denetim senaryolarını test edin. Gerçek denetim geldiğinde “sistem çalışıyor mu” değil, “sistem ne kadar iyi çalışıyor” sorusu sorulacak.
