Elasticsearch Logstash Kibana Cluster

aus PUG, der Penguin User Group
Wechseln zu: Navigation, Suche

Redundanter Elasticsearch Logservercluster mit Logstash und Kibana4

Einleitung

Da ich für die Firma einen neuen Logserver benötigte, habe ich mich auf dem "Markt" umgeschaut und mich für die gewählte Kombination wie in der Überschrift entschieden. Diese Anleitung ist mehr eine Notiz für mich, damit ich es später wieder nachvollziehen kann :-)

Hardware

Als Hardware habe ich zwei Lenovo (ehemals IBM) X3250 M5 (Typ 5458) Server im Einsatz, mit einer Standardausstattung von 16GB Ram und zwei 1TB SAS Platten im Raid HW Verbund. Um einen möglichst hohen Nutzwert aus der Hardware zu ziehen, läuft auf der Hardware Proxmox 3.x als Hypervisor. Darauf wurden dann pro physischen Host zwei Debian Jessie VMs eingerichtet. In der Summe stehen damit vier virtuelle Server bereit.


Aufbau / Umsetzung

Um eine gewisse Ausfallsicherheit zu haben, sollte der Elastisearchcluster über mindestens drei Nodes verfügen. Dabei replizieren zwei Nodes die Daten untereinander, die dritte Instanz dient lediglich als "Wahlstimme, um Split-Brain Situationen zuvorzukommen. Durch die Gebäudesituation (zwei Brandabschnitte) zzgl. anderer Gegebenheiten, setze ich auf zwei Wahlstimmen, sodass letzten Endes zwei Eleasticsearchserver die Daten verwalten und zwei Instanzen lediglich als Quorum herhalten.

  • Loghost-01 - Proxmox
    • elasearch01 - VM - 192.168.23.1
    • elaquorum-01 - VM - 192.168.23.3
  • Loghost-02 - Proxmox
    • elasearch02 - VM - 192.168.23.2
    • elaquorum-02 - VM - 192.168.23.4

Die Netzwerkverbindungen werden hier zwar über zwei separate VLANs geschoben (intern / extern), aber das ist ohnehin für jede Installation anders. Wichtig an dieser Stelle ist nur, dass Elasticsearch zwar mittels Multicast seine Clustermitglieder selbst finden kann, dieses Prozedere jedoch in vielen Konstellationen nicht die richtige Wahl ist. Daher setzte ich auf Unicast, was auch von der Firewall her einfacher ist.

Da ich ebenfalls kein allzu großer Verfechter von komplexer Software bin, setzte ich statt auf Pacemaker und Co, auf ein "simples" keepalived, welches mir meine externe IP Adresse auf die jeweils verbliebenen / aktive Node umzieht.

Auf beiden Datennodes (elasearch-01/02) läuft jeweils ein Nginx, Logstash und Kibana4. Somit kann ich auch die jeweilige Node direkt ansprechen, sofern ein Zugriff auf die interne IP erlaubt ist. Die Clients die ihre Logdaten an den Logserver senden wollen, sprechen dagegen immer nur die externe IP Adresse an. Da Logstash (der die Daten annimmt) per Definition nicht als Rootuser läuft und damit auch keinen privilegierten Port (514) verwenden darf, wird per IPTables der Port 514 auf 5144 umgeleitet. Man könnte zwar z.B. auch Rsyslog einbinden, der die Daten annimmt und dann an Logstash weiterreicht (entweder via Logdatei od. via TCP/UDP/Unix Socket), aber diese Lösung hat mir einfach besser gefallen.

Der komplette Pfad sieht dann wie im folgenden aus:

Elasticsearch-Clusteraufbau.png


Die Qorumserver habe ich jetzt außen vor gelassen, da die wirklich nicht viel tun außer da zu sein.

Hinweis

Um diese Server zu installieren, nutze ich zum einen Fai für die Basisinstallation von Jessie und für die Nacharbeiten Puppet. Da es zu sehr ausschweifen würde auch noch Puppet mit zu erläutern, werde ich nur das fertige Ergebnis hier aufzeigen, jedoch nicht die Puppet Manifests etc. Was ich jedoch tun kann, hier die von mir genutzten Puppet Module auflisten:

  • arioch-keepalived
  • elasticsearch-elasticsearch
  • elasticsearch-logstash
  • evenup-kibana
  • puppetlabs-apt
  • saz-rsyslog
  • jfryman-nginx

Der Rest ergibt sich aus den Abhängigkeiten der jeweiligen Module.

Messagebox info.png

Sofern man keine Technik alla Pupper/Chef/Ansible nutzt, sollte man sich entweder ClusterSSH besorgen, oder man klont am Ende die fertige Maschine und ändert nur noch den Hostname/IP Adresse etc. allerdings ist das doch ein wenig fehleranfällig.

Hinweis

Die Anleitung hier geht ganz stupide die einzelnen Abschnitte durch. Dabei wird erst einmal keine Rücksicht darauf genommen, ob der Dienst nach der jeweiligen Installation und Konfiguration danach funktioniert. Sobald alles durchlaufen wurde, folgt ganz am Ende ein kompletter Neustart aller VMs. Damit wird dann sichergestellt, dass alle Dienste (neu-)gestartet werden. Daher erst nach dem Neustart alles prüfen, ob alle Dienste passend laufen.

Vorarbeiten

Mit zu den ersten Dingen die getan werden sollten, ist es die IP Adressen und Hostnamen in der /etc/hosts zu hinterlegen. Das erspart einem unnötige Probleme, wenn der DNS Dienst einmal versagt:

  • /etc/hosts
Ascii.png
192.168.23.1    elasearch-01.mydomain.local elasearch-01
192.168.23.2    elasearch-02.mydomain.local elasearch-02
192.168.23.3    elaquorum-01.mydomain.local elaquorum-01
192.168.23.4    elaquorum-02.mydomain.local elaquorum-02

Installation von Keepalived

Da der Zugriff auf das Webfrontend und die eingehenden Logmeldungen nur über eine "öffentliche/externe" laufen soll, muss dafür gesorgt werden, dass die IP Adresse an der aktiven Node (elasearch-01 oder elasearch-02) hochgefahren wird. Dafür eignet sich der Keepalived. Dieser Dienst kommuniziert via VRRP Protokoll und kann Aktionen bei einem Ausfall einer Node ausführen. Auf meinem System habe ich zwei Netzwerkkarten (Interfaces):

  • eth0 -> internes Interface
  • eth1 -> externes Interface

Da eth1 keine weitere aktive IP Adresse hat, sieht die /etc/network/interfaces entsprechend so aus:

  • /etc/network/interfaces
Ascii.png
auto eth1
iface eth1 inet manual
      pre-up ifconfig $IFACE up
      post-down ifconfig $IFACE down

Damit wird das Interface ohne aktive IP Adresse hochgefahren. Nun kann keepalived selbst installiert werden.

Debian-term.png
# apt-get install keepalived -y


Die Konfiguration ist in diesem Fall sehr übersichtlich:


elasearch-01

  • /etc/keepalived/keepalived.conf
Ascii.png
vrrp_instance VI_ELASEARCH {
  # Dieses Interface interessiert uns
  interface                 eth1
  state                     MASTER
  virtual_router_id         50
  priority                  101
  advert_int                1
  garp_master_delay         5

  authentication {
    auth_type PASS
    auth_pass secret
  }

  track_interface {
    eth0
  }

 # Hier ist die öffentliche / externe Adresse
  virtual_ipaddress {
    10.10.10.1/24 dev eth1
  }

# Darüber soll keepalived kommunzieren
  unicast_src_ip 192.168.23.1

# Die Nachbarn, elasearch-01 und elasearch-02
  unicast_peer {
  192.168.23.1
  192.168.23.2
  }
}


elasearch-02

  • /etc/keepalived/keepalived.conf
Ascii.png
vrrp_instance VI_ELASEARCH {
  interface                 eth1
  state                     MASTER
  virtual_router_id         50
  priority                  101
  advert_int                1
  garp_master_delay         5

  authentication {
    auth_type PASS
    auth_pass secret
  }

  track_interface {
    eth0
  }

  virtual_ipaddress {
    10.10.10.1/24 dev eth1
  }

  unicast_src_ip 192.168.23.2

  unicast_peer {
  192.168.23.1
  192.168.23.2
  }
}

Installation der Daten Nodes

Nun werden die Nodes eingerichtet, die auch Daten vorhalten und nicht zu den Qorums gehören. Für die essentiellen Programme gibt es entsprechende Debian Pakete, daher hinterlegen wir die Quellen in /etc/apt/sources.list.d/

  • /etc/apt/sources.list.d/debian_elastic.list
Ascii.png
deb http://packages.elastic.co/elasticsearch/1.7/debian stable main


  • /etc/apt/sources.list.d/debian_logstash.list
Ascii.png
deb http://packages.elastic.co/logstash/1.5/debian stable main
Debian-term.png
# wget -O - http://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add -
# apt-get update && apt-get upgrade

Elasticsearch benötigt Java, daher installieren wie die passende JRE, allerdings in der "Headless" Version, die uns einiges an Plattenplatz erspart:

Debian-term.png
# apt-get install default-jre-headless -y

Dann kommen Elasticsearch und Logstash.

Debian-term.png
# apt-get install elasticsearch logstash curl

Elasticsearch Konfiguration

Ohne jetzt näher auf die Elasticsearch Eigenschaften einzugehen (die kann man hier nachlesen), sollte man vorab wissen, dass mehrere Elasticsearch Instanzen parallel auf einem Server laufen können. Die haben dann ihre jeweils eigenen Parameter und Konfigurationen.

Die Konfigurationsdateien:

  • /etc/default/elasticsearch-* - Defaulteinstellungen der jeweiligen Elasticsearch Instanz.
  • /etc/elasticsearch/ - Hauptkonfigurationsverzeichnis


Da bei mir Puppet automatisch die passenden Init/Systemd Scripte für die jeweilige Instanz hinterlegt, habe ich nichts mehr zu tun, für die anderen sollte jedoch /etc/default/elasticsearch angepasst werden:

  • /etc/default/elasticsearch
Ascii.png
# Wo ist unser Konfigurationsverezichnis für diese Instanz mit Namen "mylog" (vorzugsweise Cluster-/Instanzname)
CONF_DIR=/etc/elasticsearch/mylog
# Die passende Datei dazu
CONF_FILE=/etc/elasticsearch/mylog/elasticsearch.yml
ES_GROUP=elasticsearch
ES_HOME=/usr/share/elasticsearch
ES_USER=elasticsearch
# Wo sollen die (Debug) Logs hin
LOG_DIR=/var/log/elasticsearch/mylog
MAX_OPEN_FILES=65535

Das Verzeichnis /etc/elasticsearch/mylog existiert natürlich noch nicht und muss erstellt werden:

Gnome-terminal.png
# mkdir -p /etc/elasticsearch/mylog /var/log/elasticsearch/mylog
# chown elasticsearch:elasticsearch -R /etc/elasticsearch/ /var/log/elasticsearch/

Da wir Elasticsearch im Cluster betreiben wollen, benötigen wir dazu eine passende Konfiguration. Die wichtigen Parameter sind:

  • Die (interne) IP Adresse unter der die Nodes miteinander kommunizieren
  • Der Clustername
  • Anzahl der Nodes im Cluster und wer von ihnen auch Daten bereitstellt (und nicht nur Quorum ist)

eleasearch-01

  • /etc/elasticsearch/mylog/elasticsearch.yml
Ascii.png
---
bootstrap:
  mlockall: true

# Clustername -> Hier mylog
cluster:
  name: mylog
discovery:
  zen:
# Es muss mindestens zwei Master Nodes geben, in einem 3 Node Cluster. Dies schützt vor Split-Brain und dient der Mehrheitsfindung \
# In diesem Setup wäre auch ein minimum_master_nodes: 3 ok, da es zwei Quorums gibt.
    minimum_master_nodes: 2
    ping:
# Multicast ausschalten, wie weisen die Nodes dediziert zu
      multicast:
        enabled: false
      timeout: 30s
# Hier Unicast aktivieren und die Mitglieder auflisten (hier Name, statt IP)
      unicast:
        hosts:
             - elasearch-01
             - elasearch-02
             - elasearch-quorum-01
             - elasearch-quorum-02
gateway:
# Wir haben in Summe 4 Nodes
  expected_nodes: 4
# Aber es reicht, wenn 3 Nodes da sind, damit der Cluster seine Arbeit aufnimmt
  recover_after_nodes: 3
  recover_after_time: 5m
http:
  host: 127.0.0.1

# Nur eine Node erhält auch eine Kopie der Daten (ein Original + eine Kopie + ein Quorum = Cluster)
index:
  number_of_replicas: 1

# Diese Node darf Master sein
node:
  master: true
# und auch Daten halten (!)
  data: true
# Der Name dieser Node
  name: elasearch-01.mydomain.local
# Die (schnell) wachsenden Datenmengen werden hier abgelegt
path:
  data: /var/lib/elasticsearch/mylog
# Die interne IP Adresse, an der die Node Daten annehmen darf/soll -> =! externe IP
transport:
  host: 192.168.23.1

eleasearch-02

Hier nun unsere zwei Datennode, mit der sich elasearch-01 synchronisiert und Daten austauscht.

  • /etc/elasticsearch/mylog/elasticsearch.yml
Ascii.png
---
bootstrap:
  mlockall: true
cluster:
  name: mylog
discovery:
  zen:
    minimum_master_nodes: 2
    ping:
      multicast:
        enabled: false
      timeout: 30s
      unicast:
        hosts:
             - elasearch-01
             - elasearch-02
             - elaquorum-01
             - elaquorum-02
gateway:
  expected_nodes: 4
  recover_after_nodes: 3
  recover_after_time: 5m
http:
  host: 127.0.0.1
index:
  number_of_replicas: 1
node:
  master: true
  data: true
  name: elasearch-02.mydomain.local
path:
  data: /var/lib/elasticsearch/mylog
transport:
  host: 192.168.23.2

Elasticsearch Quorum Nodes

Nachdem nun die Elasticsearch DATEN Nodes soweit fertig sind, verbleiben nur noch die zwei Quorum Nodes. Deren einzige Aufgabe ist es Split-Brain Situationen nicht entstehen zu lassen. Es ist zwar auch Möglich mit wirklich nur zwei Nodes zu arbeiten, aber der Autor (ich) hatte da sehr häufig Probleme, einen grünen Cluster Status zu erhalten. Da die Quorum Nodes keine Datenhaltung betreiben, benötigen die VMs nur sehr wenige Ressourcen. Als Festplattenspeicher genügen 20GByte und als Ram 512MByte bis maximal 1GByte an Arbeitsspeicher. Die Installation ist nahezu identisch und auch die Konfigurationsdatei ist fast genau gleich. Der einzige Unterschied ist "data: false" sowie die IP Adresse von transport:

elaquorum-01

  • /etc/elasticsearch/mylog/elasticsearch.yml
Ascii.png
---
bootstrap:
  mlockall: true
cluster:
  name: mylog
discovery:
  zen:
    minimum_master_nodes: 2
    ping:
      multicast:
        enabled: false
      timeout: 30s
      unicast:
        hosts:
             - elasearch-01
             - elasearch-02
             - elaquorum-01
             - elaquorum-02
gateway:
  expected_nodes: 4
  recover_after_nodes: 3
  recover_after_time: 5m
http:
  host: 127.0.0.1
index:
  number_of_replicas: 1
node:

# Ich darf Master sein ...
  master: true
# aber darf _keine_ Daten halten, da ich nur Quorum sein soll
  data: false
  name: elaquorum-01.mydomain.local
path:
  data: /var/lib/elasticsearch/mylog
transport:
  host: 192.168.23.3

elaquorum-02

Nun unsere zweite Quorum Node

  • /etc/elasticsearch/mylog/elasticsearch.yml
Ascii.png
---
bootstrap:
  mlockall: true
cluster:
  name: mylog
discovery:
  zen:
    minimum_master_nodes: 2
    ping:
      multicast:
        enabled: false
      timeout: 30s
      unicast:
        hosts:
             - elasearch-01
             - elasearch-02
             - elaquorum-01
             - elaquorum-02
gateway:
  expected_nodes: 4
  recover_after_nodes: 3
  recover_after_time: 5m
http:
  host: 127.0.0.1
index:
  number_of_replicas: 1
node:

# Ich darf Master sein ...
  master: true
# aber darf _keine_ Daten halten, da ich nur Quorum sein soll
  data: false
  name: elaquorum-02.mydomain.local
path:
  data: /var/lib/elasticsearch/mylog
transport:
  host: 192.168.23.4

Logstash Elasticsearch Daten Nodes

Nun steht die Basiskonfiguration der Elasticsearch Nodes und können Logstash einrichten. Dies findet nur auf den Datennodes statt und nicht auf den Quorum Nodes.

Die Aufgabe von Logstash besteht einfach nur darin die eingehenden Meldungen die entweder von Syslog Clients kommen (udp/tcp), oder aus Logdateien (/var/log/...) passend vorzufiltern und dann dem Elasticsearch zu übergeben.

Installation

Die Debian Quellen sollten ja bereits vorhanden sein, daher reicht:

Debian-term.png
# apt-get install logstash -y 

Konfiguration

Die Basiskonfiguration ist eigentlich sehr simpel gestrickt:


  • /etc/logstash/conf.d/logstash.conf
Ascii.png
input {

# Dieser Input wird z.B. für logstashforwder genutzt
# Beispiel SSL Kommando: openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout logstash-forwarder.key -out logstash-forwarder.crt -days 1829
	lumberjack {
		type => "logs"
			port => 5000
			ssl_certificate => "/etc/ssl/certs/logstash-forwarder.crt"
			ssl_key => "/etc/ssl/private/logstash-forwarder.key"
	}
# Hat man Rsyslog 6.4.x -> http://www.rsyslog.com/doc/v8-stable/configuration/modules/omelasticsearch.html
	tcp {
		type => syslogjson
			port => 5500
			codec => "json"
	}
# Standard TCP Syslog, allerdings unprivilegierter Port, Weiterleitung von 514 erfolgt via IPTables
	tcp {
		port => 5144
			type => "syslog"
	}
# Standard UDP Syslog, allerdings unprivilegierter Port, Weiterleitung von 514 erfolgt via IPTables
	udp {
		port => 5144
			type => "syslog"
	}
}

# An dieser Stelle übergeben wir die gefilterten Nachricht an Elasticsearch
output {
	elasticsearch {
		host => ["elasearch-01","elasearch-02"]
			cluster => "informatiklog"
			protocol => "transport"
			flush_size => 2000
	}
# Praktisch um zu sehen, warum etwas _nicht_ funktioniert.
	stdout { codec => rubydebug }
}

# ...
# .... jetzt die Filter


Konfiguration

Die Basiskonfiguration ist eigentlich sehr simpel gestrickt:


  • /etc/logstash/conf.d/logstash.conf
Ascii.png
input {

# Dieser Input wird z.B. für logstashforwder genutzt
# Beispiel SSL Kommando: openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout logstash-forwarder.key -out logstash-forwarder.crt -days 1829
	lumberjack {
		type => "logs"
			port => 5000
			ssl_certificate => "/etc/ssl/certs/logstash-forwarder.crt"
			ssl_key => "/etc/ssl/private/logstash-forwarder.key"
	}
# Hat man Rsyslog 6.4.x -> http://www.rsyslog.com/doc/v8-stable/configuration/modules/omelasticsearch.html
	tcp {
		type => syslogjson
			port => 5500
			codec => "json"
	}
# Standard TCP Syslog, allerdings unprivilegierter Port, Weiterleitung von 514 erfolgt via IPTables
	tcp {
		port => 5144
			type => "syslog"
	}
# Standard UDP Syslog, allerdings unprivilegierter Port, Weiterleitung von 514 erfolgt via IPTables
	udp {
		port => 5144
			type => "syslog"
	}
}

# An dieser Stelle übergeben wir die gefilterten Nachricht an Elasticsearch
output {
	elasticsearch {
		host => ["elasearch-01","elasearch-02"]
			cluster => "mylog"
			protocol => "transport"
			flush_size => 2000
	}
# Praktisch um zu sehen, warum etwas _nicht_ funktioniert.
	stdout { codec => rubydebug }
}

# ...
# .... jetzt die Filter
Filter

Nun kommt der für mich komplizierte Teil: die Filter. Die Filter sind der Kernprozess von Logstash und ist maßgeblich dafür verantwortlich, wie gut sich die späteren Logmeldungen im Kibana verwerten lassen.

Die Filter können in zwei Variationen hinterlegt werden:

  • Bedingung und Pattern in einem
  • Bedingung und Pattern getrennt ablegen

Für die getrennte Haltung gibt es:

  • /etc/logstash/conf.d/ -> Konfigurationsverzeichnis
  • /etc/logstash/patterns/ -> Patternverzeichnis

Da es sehr stark darauf ankommt von wem und wie die Meldungen reinkommen, ist es nicht sehr sinnvoll hier meine gesamte Filterkonfiguration einzustellen, aber zumindest die Basics sollte vorhanden sein. Die größte Schwierigkeit besteht meistens darin, die reinkommenden Daten zu normalisieren, sodass Logstash die Daten passend im JSON Format übergeben kann. Gerade mit Hinblick auf die Datumsformate kann sehr viel schief gehen, sodass man nicht das gewünschte Verhalten erreicht.

Syslog

Der wichtigste Filter dürfte Syslog sein, da auf ihm verschiedene andere aufbauen:

  • /etc/logstash/conf.d/logstash.conf
Ascii.png
#### SYSLOG
filter {
	if [type] == "syslog" {
# Parse syslog messages
		grok {
# match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" }
			match   => [ "message", "<%{POSINT:syslog_pri}>(?:%{SYSLOGTIMESTAMP:syslog_timestamp}|%{TIMESTAMP_ISO8601:syslog_timestamp8601}) (?:%{SYSLOGHOST:syslog_hostname})? %{PROG:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" ]
				add_field => [ "received_at", "%{@timestamp}" ]
				add_field => [ "received_from", "%{host}" ]
				add_tag => [ "grokked" ]
		}
		if "grokked" in [tags] {
# Convert syslog priority number into facility and severity name/number
			syslog_pri {}
# Parse timestamp
			if [syslog_timestamp8601] {
# Parse proper ISO8601 syslog timestamps
				date {
					match => [ "syslog_timestamp8601", "ISO8601" ] # RSYSLOG_ForwardFormat
				}
			}
			else {
				if [syslog_timestamp] {
# Parse traditional syslog timestamps
					date {
						match => [ "syslog_timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss" ]
					}
				}
			}
# If has a hostname, replace host
			if [received_from] {
				mutate {
					replace => [ "host", "%{syslog_hostname}" ]
				}
			}
# Replace message with syslog_message (if message is available)
			if [syslog_message] {
				mutate {
					replace => [ "message", "%{syslog_message}" ]
				}
			}
			else {
				mutate {
					replace => [ "message", "(empty message)" ]
				}
			}
# Rename some fields
			mutate {
				rename => [ "syslog_program", "program" ]
					rename => [ "syslog_pid",     "pid"     ]
					rename => [ "syslog_severity", "severity" ]
					rename => [ "syslog_facility", "category" ]
			}
# Clean up redundant parameters
			mutate {
				convert       => [ "syslog_facility_code", "integer" ]
					convert       => [ "syslog_severity_code", "integer" ]
					convert       => [ "pid",                  "integer" ]
					remove_field  => [ "syslog_timestamp", "syslog_timestamp8601",
						      "syslog_hostname", "syslog_message", "syslog_pri" ]
			}
		}
# Extract remote address from sshd log lines
		if [program] == "sshd" {
			grok {
				match => { "message" => [
					"Invalid user %{USERNAME:username} from %{IP:remote_addr}",
						"Failed %{WORD:login_method} for invalid user %{USERNAME:username} from %{IP:remote_addr} port %{POSINT:port} ssh2",
						"pam_unix(sshd:auth): authentication failure; logname= uid=%{POSINT:uid} euid=%{POSINT:euid} tty=ssh ruser= rhost=%{IPORHOST:remote_addr}(?: user=%{USERNAME:username})?",
						"PAM %{POSINT} more authentication failures; logname= uid=%{POSINT:uid} euid=%{POSINT:euid} tty=ssh ruser= rhost=%{IPORHOST:remote_addr}(?: user=%{USERNAME:username})?",
						"Did not receive identification string from %{IPORHOST:remote_addr}"
							]
				}
			}
# Filter bottom end -> do no touch

			if "_grokparsefailure" not in [tags] {
				if [remote_addr] =~ /%{HOST}/ {
					dns {
						resolve => [ "remote_addr" ]
							action  => "replace"
					}
				}
			}
		}
# Extract remote address from fail2ban log lines
		if [program] == "fail2ban.actions" {
			grok {
				match => { "message" => "WARNING \[%{WORD:service}\] %{WORD:action} %{IP:remote_addr}" }
			}
			if "_grokparsefailure" not in [tags] {
				mutate {
					lowercase => [ "action" ]
				}
			}
		}
#        # Get rid of the tags field, as it should be empty
#        mutate {
#            remove_field => [ "tags" ]
#        }

# Add visitor geolocation information if remote_addr is available
		if [remote_addr] {
			geoip {
				source => "remote_addr"
			}
		}
	}
}

Diese Zeilen würden ausreichen, um eine funktionierende Konfiguration zu erhalten. Am Ende im Quellen-Bereich finden sich noch ein paar weitere Filter für Logstash(Grok).

IPTables Umleitung

Da Logstash auf Port 5144 lauscht und der Standardsyslog Port 514 ist, leiten wir diesen per IPTables einfach um. Dazu öffnen wir die entsprechende Ports und leiten sie um:

  • Ein Beispiel:
...
-A INPUT -s 10.10.10.0/24 -p tcp -m multiport --ports 5000,514,5144 -m comment --comment "112 allow logstash and syslog tcp" -j ACCEPT
-A INPUT -s 10.10.10.0/24 -p udp -m multiport --ports 5000,514,5144 -m comment --comment "112 allow logstash and syslog udp" -j ACCEPT
...
-A PREROUTING -d 10.10.10.1/32 -p tcp -m multiport --dports 514 -m comment --comment "113 forward syslog udp to elasearch" -j DNAT --to-destination 10.10.10.1:5144
-A PREROUTING -d 10.10.10.1/32 -p udp -m multiport --dports 514 -m comment --comment "114 forward syslog udp to elasearch" -j DNAT --to-destination 10.10.10.1:5144
...

Die müssten natürlich für die eigene Umgebung angepasst werden :-)

Dialog-warning.png

Es muss natürlich dafür gesorgt werden, dass kein anderer Dienst diesen Port blockiert, also z.B. Rsyslog, Syslog oder auch syslog-ng. Diese Dienste müssen entsprechend darauf angepasst werden.

Installation von Nginx

Kibana4 bringt zwar einen eigenen Webdienst mit, allerdings sollte man nach Möglichkeit die Benutzer nicht direkt auf diesen zugreifen lassen, zumal Nginx einfach mehr Möglichkeiten der Absicherung mitbringt. Die Konfiguration hält sich sehr stark in Grenzen, da im Grunde nur eine Proxy Konfiguration erfolgt. Das Ganze lässt sich natürlich auch mit dem Apachen lösen, aber man muss ja auch mal über den Tellerrand schauen:

Debian-term.png
# apt-get install nginx apache2-utils -y
# htpasswd -c /etc/nginx/htpasswd admin
<passwort eintippen>


Die (SSL) Konfiguration ist dementsprechend sehr einfach gehalten.


  • /etc/nginx/sites-enabled/log.mydomain.local.conf
Ascii.png
server {
  listen       *:443 ssl;
  server_name  log.mydomain.local;

  ssl on;

  ssl_certificate           /etc/ssl/certs/log.mydomain.local.pem;
  ssl_certificate_key       /etc/ssl/private/log.mydomain.local.key;
  ssl_session_cache         shared:SSL:10m;
  ssl_session_timeout       5m;
  ssl_protocols             TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers               ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
  ssl_prefer_server_ciphers on;

  index  index.html index.htm index.php;

  access_log            /var/log/nginx/ssl-log.mydomain.local.access.log combined;
  error_log             /var/log/nginx/ssl-log.mydomain.local.error.log;


  location / {
   # Kibana4 lauscht auf Port 5601
    proxy_pass            http://localhost:5601;
    proxy_read_timeout    90;
    proxy_connect_timeout 90;
    proxy_redirect        off;

  }
  # Es dürfen nur Benutzer drauf, mit dem entsprechenden User:passwort in dieser Datei
  auth_basic Restricted;
  auth_basic_user_file /etc/nginx/htpasswd;
}

Die Konfiguration lässt die Möglichkeit auf, statt über die öffentliche Adresse auch gezielt die jeweilige Node anzusprechen. Man weiß ja nie.

Kibana (4)

Um nun die Logs bequem durchsuchen zu können, wird oftmals [Kibana] verwendet. Die Handhabung bzw. Installation ist recht einfach. Leider gibt es dafür aktuell noch keine Debianpakete, weshalb die Installation (und damit auch die Pflege) von Hand durchgeführt werden muss. Kibana wird in diesem Fall nach /opt installiert.

Installation

  • Archiv herunterladen und auspacken:
Gnome-terminal.png
# cd /opt
# wget https://download.elastic.co/kibana/kibana/kibana-4.1.2-linux-x64.tar.gz
# wget https://download.elastic.co/kibana/kibana/kibana-4.1.2-linux-x64.tar.gz.sha1.txt
# sha1sum -c kibana-4.1.2-linux-x64.tar.gz.sha1.txt
# tar xzf kibana-4.1.2-linux-x64.tar.gz
# ln -s kibana-4.1.2-linux-x64 kibana

Dann noch einen passenden Benutzer erzeugen, unter dem Kibana laufen soll:

Gnome-terminal.png
# useradd -r -d /opt/kibana -s /bin/sh kibana


Das war es auch fast schon. Im Verzeichnis /opt/kibana/config findet sich noch die Datei kibana.yml in der noch diverse Einstellungen vorgenommen werden können. Die Standardeinstellungen können jedoch so verbleiben (Port 5601 / elasticsearch_url), da der Zugriff über Nginx stattfindet. Da kein Init/Systemd Script mitgeliefert wird, muss eins erstellt werden:

  • /etc/systemd/system/kibana4.service
Ascii.png
[Service]
ExecStart=/opt/kibana/bin/kibana
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=kibana4
User=kibana
Group=kibana
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Und am Ende noch den Dienst aktivieren (aber noch nicht starten):

Gnome-terminal.png
# systemctl enable kibana4

Damit ist Kibana vorerst (shell- seitig) fertig konfiguriert.

Reboot

Sofern nun alles installiert und eingerichtet wurde, können nun alle VMs (elasearch-01/elasearch-02/elaquorum-01/elaquorum-02) neugestartet werden. Im Anschluss beginnt dann die Prüfung, ob die Dienste laufen.

Checkliste

Nun folgt die Checkliste :-)

  • Keepalived
    • Läuft keepalivd ?
    • systemctl status keepalived
    • Öffentliche IP Adresse (hier im Test 10.10.10.1) erreichbar?
    • Wenn nicht -> Blockiert Firewall Kommunikation ?
    • Syntax Fehler in der Konfiguration ?
  • Elasticsearch
    • Läuft der Prozess ? (ps ax | grep elastic)
    • systemctl status elasticsearch
    • Wird an den passenden Ports gelauscht (9200 (http) / 9300 (transport))
    • Ist der Clusterstatus O.K?
Gnome-terminal.png
$ curl -XGET 'http://localhost:9200/_cluster/health?pretty=true'

{
  "cluster_name" : "mylog",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 4,
  "number_of_data_nodes" : 4,
  "active_primary_shards" : 116,
  "active_shards" : 232,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0
}
    • Logs in /var/log/elasticsearch/
  • Logstash
    • Läuft der Prozess
    • systemctl status logstash
    • Wird an den Ports gelauscht (5000tcp / 5500tcp / 5144tcp/udp)
    • Ist die Syntax O.K -> /opt/logstash/bin/logstash --configtest -f /etc/logstash/conf.d/logstash.conf
    • Wird korrekt von Port 514 auf 5144 umgeleitet?
  • Kibana
    • Läuft der Prozess ?
    • systemctl status kibana4
    • Wird am Port 5601 gelauscht? (netstat -tln |grep 5601)
  • Nginx
    • Läuft der Prozess?
    • systemctl status nginx
    • Wird an den Ports 80/443 gelauscht?
    • Ist der Zugriff möglich und wird nach einem Username/Passwort gefragt?

Abschluss

Hat nun alles soweit funktioniert, sodass die Kibana Webseite aufgerufen werden kann, muss nur noch der passende Index (logstash-*") und "@timestamp" in der Konfiguration ausgewählt werden.

Kibana-select-index.gif

Danach müssen die Syslog Clients ihre Logs einfach nur noch an die konfigurierte, öffentliche Adresse (hier 10.10.10.1) senden und die Daten werden dann aufgenommen. Danach beginnt die eigentliche Arbeit, mit dem anpassen der Grok Filter :-)

Quellen

Spende

Wer mir etwas Gutes tun möchte :-) https://blockchain.info/de/address/1C9UE2nzandFrGHdMafWqYhuDVCx1tDrH8


--Denny (Diskussion) 12:43, 15. Okt. 2015 (CEST)