ELK Stack

My basic configuration.

rsyslog –> Logstash –> Elasticsearch –> Kibana with GeoIP

At this time I do not plan to proxy Kibana with Nginx. The goal here is to keep the setup and configuration as simple as possible.

CentOS 7
Logstash 1.4.2
Kibana 4.0.2
ElasticSearch 1.5.0

This is the fourth time I have set this up and each time I have fought through different errors. My goal is to more thoroughly document the process so that I can recreate it easily the next time.

1. Turn off SE Linux. I fought app armor on Ubuntu doing one of these installs and I am not going to fight SE Linux. Reboot.

shutdown -r now

Check if selinux is on or off.


2. Turn off the firewall.

systemctl stop firewalld
systemctl disable firewalld
systemctl status firewalld

3. Update.

yum update
yum upgrade

4. Configure rsyslog to accept remote logging and name the file appropriately. This is the way we have named our log files and we have a number of scripts that work with them, so they are not changing.

cat /etc/rsyslog.conf | grep -v ^# | egrep -v ^$
$ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
$ModLoad imjournal # provides access to the systemd journal
$ModLoad imklog # reads kernel messages (the same are read from journald)
$ModLoad immark # provides --MARK-- message capability
$ModLoad imudp
$UDPServerRun 514
$ModLoad imtcp
$InputTCPServerRun 514
$WorkDirectory /var/lib/rsyslog
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
$IncludeConfig /etc/rsyslog.d/*.conf
$OmitLocalLogging on
$IMJournalStateFile imjournal.state
*.info;mail.none;authpriv.none;cron.none /var/log/messages
authpriv.* /var/log/secure
mail.* -/var/log/maillog
cron.* /var/log/cron
*.emerg :omusrmsg:*
uucp,news.crit /var/log/spooler
local7.* /var/log/boot.log
$template DynaFile,"/var/log/remote-%fromhost-ip%.log"
*.* -?DynaFile

The last two lines are the ones that count.

$template DynaFile,"/var/log/remote-%fromhost-ip%.log"
*.* -?DynaFile

Restart rsyslog.

systemctl restart rsyslog

5. Install Java and some other tools you might need later.

yum install -y java-1.7.0-openjdk lsof wget rubygems

6. Install the GPG for the ElasticSearch repository and install it but don’t start it yet.

Add the Elasticsearch repository.

cat /etc/yum.repos.d/elasticsearch.repo

name=Elasticsearch repository for 1.5.x packages

And install.

yum update
yum -y install elasticsearch

7. Change your Elasticsearch configuration to suit your needs. I had trouble with Elasticsearch and Logstash only listening on IPv6. As a result I had to explicitly set the IP address as seen below. We also have another Elasticsearch cluster and I don’t want the two talking so I turned off multicast discovery. This is not a problem if you edit your configs first, but I had a terrible time because they were named the same.

cat /etc/elasticsearch/elasticsearch.yml | egrep -v "^#|^$"
cluster.name: logserver
node.name: "elk"
node.master: true
node.data: true
node.max_local_storage_nodes: 1
discovery.zen.ping.multicast.enabled: false

If you have problems with ElasticSearch not listening on IPv4.
In the file /usr/share/elasticsearch/bin/elasticsearch.in.sh

You see the lines:

# Force the JVM to use IPv4 stack
if [ "x$ES_USE_IPV4" != "x" ]; then
     JAVA_OPTS="$JAVA_OPTS -Djava.net.preferIPv4Stack=true"

So in the /etc/sysconfig/elasticsearch file add:

# Tell ES to use IPv4

Here is my entire syconfig file for Elasticsearch.

cat /etc/sysconfig/elasticsearch | egrep -v "^#|^$"

These are good examples from the Elasitcsearch configuration file:

# Use the Cluster Health API [http://localhost:9200/_cluster/health], the
# Node Info API [http://localhost:9200/_nodes] or GUI tools
# such as <http://www.elasticsearch.org/overview/marvel/
# http://github.com/karmi/elasticsearch-paramedic
# http://github.com/lukas-vlcek/bigdesk and
# http://mobz.github.com/elasticsearch-head to inspect the cluster state.

8. Start Elasticsearch.

systemctl enable elasticsearch
systemctl start elasticsearch

9. Test it.

  "status" : 200,
  "name" : "elk",
  "cluster_name" : "logserver",
  "version" : {
    "number" : "1.5.1",
    "build_hash" : "5e38401bc4e4388537a615569ac60925788e1cf4",
    "build_timestamp" : "2015-04-09T13:41:35Z",
    "build_snapshot" : false,
    "lucene_version" : "4.10.4"
  "tagline" : "You Know, for Search"

Other testing examples, some will not work now as we have not finished the Logstash configuration.

curl -XGET

If you need to debug Elasticsearch here is the strace command I used.

strace java -Xms256m -Xmx1g -Djava.awt.headless=true -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+HeapDumpOnOutOfMemoryError -XX:+DisableExplicitGC -Dfile.encoding=UTF-8 -Delasticsearch -Des.pidfile=/var/run/elasticsearch/elasticsearch.pid -Des.path.home=/usr/share/elasticsearch -cp :/usr/share/elasticsearch/lib/elasticsearch-1.5.0.jar:/usr/share/elasticsearch/lib/*:/usr/share/elasticsearch/lib/sigar/* -Des.default.config=/etc/elasticsearch/elasticsearch.yml -Des.default.path.home=/usr/share/elasticsearch -Des.default.path.logs=/var/log/elasticsearch -Des.default.path.data=/var/lib/elasticsearch -Des.default.path.work=/tmp/elasticsearch -Des.default.path.conf=/etc/elasticsearch org.elasticsearch.bootstrap.Elasticsearch -Djava.net.preferIPv6Addresses=false

10. LogStash installation, first enable the repository.

cat /etc/yum.repos.d/logstash.repo
name=logstash repository for 1.5.x packages

Then install Logstash.

yum install -y logstash

I also had some trouble getting Logstash to run properly. It was missing a directory and only listening on IPv6. The first thing I has do was a make specific directory.

mkdir /var/run/nscd

The second thing I had to do was add a Java option in the sysconfig directory.

cat /etc/sysconfig/logstash | egrep -v "^#|^$"

If you have trouble with logstash, here is the strace command I ran to debug it.

strace -o /tmp/strace.log -fe trace=network /opt/logstash/bin/logstash -f /etc/logstash/conf.d/logstash.conf

Below is the error where I had to create the directory to fix.

21790 connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
21790 connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)

Here is my very simple logstash.conf file.

input {
	syslog {
		debug => true
		type => syslog
		port => 5140

filter {

output {
	stdout { }
	elasticsearch {
		cluster => "logserver"
		host => ""

Turn on logstash and start it.

chkconfig logstash on
/etc/init.d/logstash start

I pointed a firewall at this ELK server, and now you should see messages coming into Elasticsearch from Logstash.


11. Install Kibana

Download the gzipped tar file.

wget https://download.elasticsearch.org/kibana/kibana/kibana-4.0.2-linux-x64.tar.gz

Extract it.

tar -xvzf kibana-4.0.2-linux-x64.tar.gz

Make a directory for Kibana and copy the unzipped contents into it.

mkdir /opt/kibana
cp -R kibana-4.0.2-linux-x64/* /opt/kibana/

Here is my basic Kibana configuration file.

cat /opt/kibana/config/kibana.yml | egrep -v "^#|^$"
port: 5601
host: ""
elasticsearch_url: ""
elasticsearch_preserve_host: true
kibana_index: ".kibana"
default_app_id: "discover"
request_timeout: 300000
shard_timeout: 0
verify_ssl: true
- plugins/dashboard/index
- plugins/discover/index
- plugins/doc/index
- plugins/kibana/index
- plugins/markdown_vis/index
- plugins/metric_vis/index
- plugins/settings/index
- plugins/table_vis/index
- plugins/vis_types/index
- plugins/visualize/index

Set it up ready to run.

gem install pleaserun
/usr/local/bin/pleaserun --platform systemd --install /opt/kibana/bin/kibana

Enable Kibana and start it.

systemctl enable kibana
systemctl start kibana

Go and check it out by pointing your browser to:


12. Install GeoLite

Here is the website for more information:


Download the city database.

wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz

I put the database in my /opt/logstash directory but there may be other directories that make more sense.

gunzip GeoLiteCity.dat.gz
mv GeoLiteCity.dat /opt/logstash

13. Here is my /etc/logstash/conf.d/asa.conf configuration file. You will notice that I tag each interface as inside_out or outside_in. That is so that we can track who we are blocking from the inside as well as who we are blocking from the outside. That also lets us create a map for each type of traffic for visualization purposes.

cat /etc/logstash/conf.d/asa.conf
input {
	file {
		path => ["/var/log/remote-"]
		sincedb_path => "/var/log/logstash/since.db"
		start_position => "beginning"
		type => "asa"
	file {
		path => ["/var/log/remote-"]
		sincedb_path => "/var/log/logstash/since.db"
		start_position => "beginning"
		type => "asa"

# begin filter block
filter {
	if [type] == "asa" { # begin ASA block
		grok {
			match => ["message", "%{CISCO_TAGGED_SYSLOG} %{GREEDYDATA:cisco_message}"]

		# Parse the syslog severity and facility
    		syslog_pri { }

    		# Parse the date from the "timestamp" field to the "@timestamp" field
    		date {
      			match => ["timestamp",
        			"MMM dd HH:mm:ss",
        			"MMM  d HH:mm:ss",
        			"MMM dd yyyy HH:mm:ss",
        			"MMM  d yyyy HH:mm:ss"
      			timezone => "America/New_York"

		# Clean up redundant fields if parsing was successful
    		if "_grokparsefailure" not in [tags] {
      			mutate {
        			rename => ["cisco_message", "message"]
        			remove_field => ["timestamp"]

		# Extract fields from the each of the detailed message types
    		# The patterns provided below are included in Logstash since 1.2.0
    		grok {
      			match => [
        			"message", "%{CISCOFW106001}",
       		 		"message", "%{CISCOFW106006_106007_106010}",
        			"message", "%{CISCOFW106014}",
        			"message", "%{CISCOFW106015}",
	        		"message", "%{CISCOFW106021}",
        			"message", "%{CISCOFW106023}",
        			"message", "%{CISCOFW106100}",
        			"message", "%{CISCOFW110002}",
	        		"message", "%{CISCOFW302010}",
        			"message", "%{CISCOFW302013_302014_302015_302016}",
        			"message", "%{CISCOFW302020_302021}",
        			"message", "%{CISCOFW305011}",
	        		"message", "%{CISCOFW313001_313004_313008}",
        			"message", "%{CISCOFW313005}",
        			"message", "%{CISCOFW402117}",
        			"message", "%{CISCOFW402119}",
	        		"message", "%{CISCOFW419001}",
        			"message", "%{CISCOFW419002}",
        			"message", "%{CISCOFW500004}",
        			"message", "%{CISCOFW602303_602304}",
	        		"message", "%{CISCOFW710001_710002_710003_710005_710006}",
        			"message", "%{CISCOFW713172}",
        			"message", "%{CISCOFW733100}"

		# GeoIP for the ASA
		# Source
		if [src_interface] == "Outside" {
 			geoip {
    				source => "src_ip"
	    			target => "geoip"
    				database =>"/opt/logstash/GeoLiteCity.dat"
    				add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
    				add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}"  ]

	  		mutate {
    				convert => [ "[geoip][coordinates]", "float" ]
				add_tag => [ 'outside_in' ]
		} # end source block

		# Destination
		##if [src_interface] !~ "(Inside)|(Bypass)" {
		if [src_interface] == "Inside" {
 			geoip {
    				source => "dst_ip"
	    			target => "geoip"
    				database =>"/opt/logstash/GeoLiteCity.dat"
    				add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
    				add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}"  ]

	  		mutate {
    				convert => [ "[geoip][coordinates]", "float" ]
				add_tag => [ 'inside_out' ]
	} # end ASA
} # end filter block

output {
	stdout { }
	elasticsearch {
		cluster => "logserver"
		host => ""

14. Some final thoughts and hints.

Here are some commands I used for debugging purposes.

lsof -i4
lsof -i6
netstat -l
curl -XGET

Last is a helper script I created.

cat /usr/local/bin/elkstack-restart
systemctl restart elasticsearch
systemctl restart logstash
systemctl restart kibana

Sources in no particular order:

This entry was posted in Linux. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s