Logger som ingen ser på har liten verdi. Frem til nå har jeg bare komprimert og arkivert loggene fra denne webserveren, og nå fant jeg ut at det er på tide å bruke dem til noe. Målet var å sette opp et system som automatisk leser inn logfilene fra webserveren min, og gjøre dem søkbare for i et grafisk verktøy med mulighet for å visualisere dataene.

Mitt oppsett: ELK-stack
Mitt oppsett: ELK-stack

Steg 1: Jeg startet først med å eksperimentere litt på en lokal virtuell maskin som jeg bruker for å utvikle og teste ny kode. Etter mye prøving og feiling fikk jeg installert og konfigurert ElasticSearch (database) og Logstash (innsamler og konvertering) slik at jeg fikk opp loggene for webserveren på utviklermaskinen (Apache) i Kibana. Kibana er et grafisk webgrensesnitt for visualisering av slike (og andre) typer logger. Disse tre er kjent som "ELK". I denne konfigurasjonen var det Logstash som leste /var/log/apache/access.log direkte. Som standard bruker Logstash gjeldende tidspunkt, og ikke loggfilens tidspunkt når den setter @timestamp feltet som Kibana grafer opp basert på. Dette måtte derfor overstyres i Logstash sin konfigurasjonsfil. Logstash støtter også funksjonalitet for å slå opp lokasjon, basert på IP. Kibana måtte så konfigureres for å "forstå" koordinatene riktig.

# Logstash konfigurasjon: For å lese inn fra fil, og tvinge lesing fra starten
input {
file {
path => "/var/log/apache2/access.log"
start_position => "beginning"
sincedb_path => "/dev/null"
}
}

# Logstash konfigurasjon: For å få riktig tid (tid fra log)
filter {
...
date {
locale => "en"
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
timezone => "Europe/Oslo"
}
...
}

# Logstash konfigurasjon: For GeoIP
filter {
...
geoip {
source => "clientip"
target => "geoip"
database => "/path/to/GeoLiteCity.dat"
add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}" ]
}
mutate {
convert => [ "[geoip][coordinates]", "float"]
}
...
}

# Laste ned GeoLiteCity
curl -O "http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz"
gunzip GeoLiteCity.dat.gz

# Oppsett av ElasticSearch for at GeoIP data skal virke (må gjøres før data importeres)
curl -O https://gist.githubusercontent.com/thisismitch/3429023e8438cc25b86c/raw/d8c479e2a1adcea8b1fe86570e42abab0f10f364/filebeat-index-template.json
curl -XPUT 'http://localhost:9200/_template/filebeat?pretty' -d@filebeat-index-template.json # merk at denne kun gjelder for "logstash-*"
# Noen andre maler
curl -L -O https://download.elastic.co/beats/dashboards/beats-dashboards-1.1.0.zip
unzip beats-dashboards-*.zip
cd beats-dashboards-* && ./load.sh

# Konfigurasjonsendring i ElasticSearch (./config/elasticsearch.yml)
node.name: NAVN
path.data: /new/path/
path.logs: /new/path/

Steg 2: Logger tar mye plass, og for å unngå at de fyller opp harddisken, er det vanlig å ha roterende logger. På min server er det satt opp en rotasjon på 7 dager, der den 8. dagen overskriver loggen fra dag 1 osv. Siden jeg ønsker å ta vare på loggene, har jeg et lite script som kjører en gang hver dag. Dette scriptet tar gårsdagens loggfil, komprimerer den, og lagrer den i en struktur basert på /år/måned/ med en komprimert fil per dag. Jeg måtte derfor lage et lite script som tar alle disse filene, pakker dem opp og samler alt innholdet i en stor tekstfil (ArkivApache). I motsetning til steg 1, der jeg satt Logstash til å overvåke og lese inn endringer fortløpende fra en fil, kan logstash også konfigureres til å ta imot data via stdin (standard input). Metoden for å importere "gamle" data blir da å kjøre "logstash < ArkivApache".

Steg 3: Utfordringen i live-miljøet mitt (andynor.net) er at jeg bruker såkalt "shared hosting" der jeg deler en Linux-server med flere andre. Jeg har derfor ikke root-tilgang, og kan ikke installere programvare via pakkebehandlere som "apt-get". Løsningen ble derfor å kjøpe en server der jeg har full tilgang. Jeg gikk for en pakke med 2GB ram, to CPU-kjerner og 48GB SSD fra Linode til 20$ i måneden. Denne satt jeg opp med en Linuxdistro kalt Debian. Når man setter opp en maskin med en offentlig IP-adresse, må den selvsagt beskyttes. Etter at det var gjort, gjorde jeg noen forsøk med en lettvekts virtualiseringsteknologi kalt Docker, men gav til slutt opp. Det er spennende teknologi, men jeg støtte på en del unødvendige utfordringer. Jeg installerte så Logstash, ElasticSearch og Kibana på denne nye maskinen, åpnet opp nødvendige porter i brannmuren og plasserte Kibana bak en Nginx-webserver med enkel autentisering.

# Nginx konfigurasjon (/etc/nginx/sites-available med symlink til sites-enabled)
server {
listen 80;
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;
}
}

Steg 4: Nå ligger den såkalte "ELK" programvaren, og loggfilene på to forskjellige maskiner, og jeg trengte derfor en måte å få oversendt dem på. Jeg installerte Filebeat på webserveren, og satte opp en forbindelse mot Logstash på Linode-serveren. Måtte også åpne opp porten i brannmuren/iptables. Her benyttes sertifikater for å kryptere forbindelsen. I tillegg trengte jeg en enkel måte å sørge for at programmene alltid kjører. For det benyttet jeg Cron og noen enkle bash-script.

# Konfigurasjon av Logstash
input {
...
beats {
type => "apache"
port => 5044
ssl => true
ssl_certificate => "/path/to/logstash-forwarder.crt"
ssl_key => "/path/to/logstash-forwarder.key"
}
...
}

# Laste ned og konfigurasjon av filebeat
wget https://download.elastic.co/beats/filebeat/filebeat-1.1.2-x86_64.tar.gz # https://www.elastic.co/downloads/beats/filebeat
# filebeat.yml
filebeat:
prospectors:
-
paths:
- /path/to/access.log
input_type: log
document_type: apache
output:
logstash:
hosts: ["IP:5044"]
bulk_max_size: 1024
tls:
certificate_authorities: ["/path/to/logstash-forwarder.crt"]
logging:
to_files: true
files:
path: /home/andynor/serverapps/logs/
name: mybeat
rotateeverybytes: 10485760 # = 10MB
keepfiles: 7

# Lage sertifikater som lar filebeat snakke med logstash
# Min linode-server har ikke et domene (enda), så jeg måtte gjøre denne endringen
sudo nano /etc/ssl/openssl.cnf
# Legge til under "[ v3_ca ]" linjen: subjectAltName = IP: logstash_server_private_ip
sudo openssl req -config /etc/ssl/openssl.cnf -x509 -days 3650 -batch -nodes -newkey rsa:2048 -keyout logstash-forwarder.key -out logstash-forwarder.crt
# Offentlig nøkkel kan så kopieres til filebeat-klientene med scp
scp FIL USER@SERVER:PATH


crontab -l # edit cron jobs. -e for edit
*/3 * * * * /apps/elk/process_ctrl # hvert 3. minutt, kjør dette bash-scriptet
# Eksempel på process_ctrl
#!/bin/bash
OUTPUT="$(ps ux | grep elasticsearch | wc -l)"
if [[ ${OUTPUT} -lt 2 ]] ; then
nohup /apps/elk/bin/elasticsearch-2.2.1/bin/elasticsearch &
sleep 30
fi

Steg 5: Oppsett av Kibana dashboard er der moroa begynner. En Apache-log inneholder følgende informasjon:

  • timestamp (tidspunkt for hendelse)
  • clientip (som kan "oversettes" til en omtrentlig lokasjon basert på fordeling av IP-ranges)
  • verb (post, get osv)
  • request (URL som etterspørres)
  • referrer (URL nettleseren kom fra - ofte ikke i bruk)
  • agent (Navn på nettleser som ber om innhold)
  • auth (autentisering - hvis bruker er autentisert med webservers innebygde auth-mekanisme)
  • response (statuskode, normalt 200 OK)
  • bytes (antall byte som ble sendt til klienten)

Basert på dette er det masse spennende som kan grafes opp. F.eks:

  • Heatmap-kart over lokasjoner
  • Tid på x-aksen og (antall) IP-adresser på y-aksen, stacked
  • Antall unike IP-adresser
  • Mest etterspurte requests
  • Tid på x-aksen og (antall) response på y-aksen

Første versjon av mitt dashboard.
Første versjon av mitt dashboard.

Kilder:
hackzine.org: Importing Apache logs in ElasticSearch
linode.com: Visualizing Apache Webserver Logs in the ELK Stack on Debian 8
michael.bouvy.net: Collect & visualize your logs with Logstash Elasticsearch & Redis