{"id":217,"date":"2015-05-12T17:00:33","date_gmt":"2015-05-12T07:00:33","guid":{"rendered":"https:\/\/icicimov.com\/blog\/?p=217"},"modified":"2017-01-02T17:38:47","modified_gmt":"2017-01-02T06:38:47","slug":"centralized-logs-collection-with-logstash-elasticsearch-and-kibana-in-amazon-aws","status":"publish","type":"post","link":"https:\/\/icicimov.com\/blog\/?p=217","title":{"rendered":"Centralized logs collection with Logstash, ElasticSearch and Kibana in Amazon AWS"},"content":{"rendered":"<p><div class=\"fx-toc fx-toc-id-217\"><h2 class=\"fx-toc-title\">Table of contents<\/h2><ul class='fx-toc-list level-1'>\n\t<li>\n\t\t<a href=\"https:\/\/icicimov.com\/blog\/?p=217#installation-and-setup\">Installation and setup<\/a>\n\t\t<ul class='toc-even level-2'>\n\t\t\t<li>\n\t\t\t\t<a href=\"https:\/\/icicimov.com\/blog\/?p=217#central-server\">Central Server<\/a>\n\t\t\t\t<ul class='toc-odd level-3'>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"https:\/\/icicimov.com\/blog\/?p=217#kernel-setup\">Kernel Setup<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"https:\/\/icicimov.com\/blog\/?p=217#install-redis-broker-server\">Install Redis Broker Server<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"https:\/\/icicimov.com\/blog\/?p=217#install-and-setup-elastic-search\">Install and setup Elastic Search<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"https:\/\/icicimov.com\/blog\/?p=217#logstash-install-and-config\">Logstash Install and Config<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"https:\/\/icicimov.com\/blog\/?p=217#web-interface\">Web interface<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t<\/ul>\n\t\t\t<li>\n\t\t\t\t<a href=\"https:\/\/icicimov.com\/blog\/?p=217#clients\">Clients<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"https:\/\/icicimov.com\/blog\/?p=217#security\">Security<\/a>\n\t\t\t<\/li>\n<\/ul>\n<\/ul>\n<\/div>\n<br \/>\nLogstash is a tool for managing events and logs. It is very useful for collecting, parsing and storing logs for later use like for example searching. It comes with a web interface for searching through the logs. The picture bellow shows a typical centralized logstash environment. It consists of logstash clients installed on the servers running applications we want to collect logs for and centralized logstash server that does the indexing and storing of the logs. These remote logstash instances are referred as <code>shippers<\/code> and the central one as <code>indexer<\/code> or <code>reader<\/code>.<\/p>\n<p><a href=\"https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/Logstash_central_log_server_architecture.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/Logstash_central_log_server_architecture.png\" alt=\"\" width=\"710\" height=\"434\" class=\"aligncenter size-full wp-image-218\" srcset=\"https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/Logstash_central_log_server_architecture.png 710w, https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/Logstash_central_log_server_architecture-420x257.png 420w\" sizes=\"auto, (max-width: 710px) 100vw, 710px\" \/><\/a><br \/>\n<em><strong>Picture1:<\/strong> Logstash environment<\/em><\/p>\n<p>The broker of choice is Redis and the storage for indexing is Elastic Search, which comes natural since Logstash is part of Elastic Search project. The web interface will be served via Nginx which is light enough for this purpose.<\/p>\n<h1><span id=\"installation-and-setup\">Installation and setup<\/span><\/h1>\n<h2><span id=\"central-server\">Central Server<\/span><\/h2>\n<p>Everything is installed on a single node. We start with some kernel tuning.<\/p>\n<h3><span id=\"kernel-setup\">Kernel Setup<\/span><\/h3>\n<p>This will provide some system, memory optimization and network stack optimization and tuning for our EC2 instance type. At the end of the <code>\/etc\/sysctl.conf<\/code> we add:<\/p>\n<pre><code>### KERNEL ###\n\n# Core dumps\nkernel.core_uses_pid = 1\nkernel.core_pattern = \/mnt\/core-%e-%s-%u-%g-%p-%t\nfs.suid_dumpable = 2\n\n# Turn on execshild\n#kernel.exec-shield = 1\nkernel.randomize_va_space = 1\n# Reboot after 10sec. on kernel panic\nkernel.panic = 10\n\n### IMPROVE SYSTEM MEMORY MANAGEMENT ###\n\n# Increase size of file handles and inode cache\nfs.file-max = 2097152\n\n# Insure we always have enough memory\nvm.min_free_kbytes = 8192\n\n# Do less swapping\nvm.swappiness = 10\nvm.dirty_ratio = 60\nvm.dirty_background_ratio = 2\n\n### GENERAL NETWORK SECURITY OPTIONS ###\n\n# Avoid a smurf attack\nnet.ipv4.icmp_echo_ignore_broadcasts = 1\n\n# Turn on protection for bad icmp error messages\nnet.ipv4.icmp_ignore_bogus_error_responses = 1\n\n# Turn on syncookies for SYN flood attack protection\nnet.ipv4.tcp_syncookies = 1\nnet.ipv4.tcp_max_syn_backlog = 1024\n\n# Turn on timestamping\nnet.ipv4.tcp_timestamps = 1\n\n# Turn on and log spoofed, source routed, and redirect packets\nnet.ipv4.conf.all.log_martians = 1\nnet.ipv4.conf.default.log_martians = 1\n\n# No source routed packets here\nnet.ipv4.conf.all.accept_source_route = 0\nnet.ipv4.conf.default.accept_source_route = 0\n\n# Turn on reverse path filtering\nnet.ipv4.conf.all.rp_filter = 1\nnet.ipv4.conf.default.rp_filter = 1\n\n# Make sure no one can alter the routing tables\nnet.ipv4.conf.all.accept_redirects = 0\nnet.ipv4.conf.default.accept_redirects = 0\nnet.ipv4.conf.all.secure_redirects = 0\nnet.ipv4.conf.default.secure_redirects = 0\n\n# Don't act as a router\nnet.ipv4.ip_forward = 0\nnet.ipv4.conf.all.send_redirects = 0\nnet.ipv4.conf.default.send_redirects = 0\n\n# Number of times SYNACKs for passive TCP connection.\nnet.ipv4.tcp_synack_retries = 2\n\n# Allowed local port range\nnet.ipv4.ip_local_port_range = 2000 65535\n\n# Protect Against TCP Time-Wait\nnet.ipv4.tcp_rfc1337 = 1\n\n# Decrease the time default value for tcp_fin_timeout connection\nnet.ipv4.tcp_fin_timeout = 15\n\n# Decrease the time default value for connections to keep alive\nnet.ipv4.tcp_keepalive_time = 300\nnet.ipv4.tcp_keepalive_probes = 5\nnet.ipv4.tcp_keepalive_intvl = 15\n# This means that the keepalive process waits 300 seconds for socket \n# activity before sending the first keepalive probe, and then resend\n# it every 15 seconds. If no ACK response is received for 5 consecutive \n# times (75s in this case), the connection is marked as broken.\n\n### TUNING NETWORK PERFORMANCE ###\n\n# Disable IPv6\nnet.ipv6.conf.all.disable_ipv6 = 1\nnet.ipv6.conf.default.disable_ipv6 = 1\nnet.ipv6.conf.lo.disable_ipv6 = 1\n\n# Default Socket Receive Buffer\nnet.core.rmem_default = 31457280\n\n# Maximum Socket Receive Buffer\nnet.core.rmem_max = 12582912\n\n# Default Socket Send Buffer\nnet.core.wmem_default = 31457280\n\n# Maximum Socket Send Buffer\nnet.core.wmem_max = 12582912\n\n# Increase number of incoming connections\nnet.core.somaxconn = 5000\n\n# Increase number of incoming connections backlog\nnet.core.netdev_max_backlog = 65536\n\n# Enable TCP window scaling\nnet.ipv4.tcp_window_scaling = 1\n\n# Increase the maximum amount of option memory buffers\nnet.core.optmem_max = 25165824\n\n# Increase the maximum total buffer-space allocatable\n# This is measured in units of pages (4096 bytes)\nnet.ipv4.tcp_mem = 65536 131072 262144\nnet.ipv4.udp_mem = 65536 131072 262144\n\n# Increase the read-buffer space allocatable\nnet.ipv4.tcp_rmem = 8192 87380 16777216\nnet.ipv4.udp_rmem_min = 16384\n\n# Increase the write-buffer-space allocatable\nnet.ipv4.tcp_wmem = 8192 65536 16777216\nnet.ipv4.udp_wmem_min = 16384\n\n# Increase the tcp-time-wait buckets pool size to prevent simple DOS attacks\nnet.ipv4.tcp_max_tw_buckets = 1440000\n\n# TIME_WAIT socket policy\n# Note: if both enabled then disable\n# net.ipv4.tcp_timestamps for servers \n# behind NAT to prevent dropped incoming connections\nnet.ipv4.tcp_tw_recycle = 0\nnet.ipv4.tcp_tw_reuse = 1\n\n# Enable TCP MTU probing (in case of Jumbo Frames enabled)\n#net.ipv4.tcp_mtu_probing = 1\n\n# Speedup retrans (Google recommended)\nnet.ipv4.tcp_slow_start_after_idle = 0\nnet.ipv4.tcp_early_retrans = 1\n\n# Conntrack\n# 288bytes x 131072 = 37748736 (~38MB) max memory usage\nnet.netfilter.nf_conntrack_max = 131072\nnet.netfilter.nf_conntrack_tcp_loose = 1\n\n# NOTE: Enable this if EC2 instance support it\n# -- 10gbe tuning from Intel ixgb driver README -- #\n# turn off selective ACK and timestamps\n#net.ipv4.tcp_sack = 0\n#net.ipv4.tcp_timestamps = 0\n<\/code><\/pre>\n<p>and run:<\/p>\n<pre><code>sysctl -p\n<\/code><\/pre>\n<p>to activate the changes.<\/p>\n<h3><span id=\"install-redis-broker-server\">Install Redis Broker Server<\/span><\/h3>\n<p>We need broker services to buffer all the client updates while the central Logstash service is offline, in case of issues with Logstash or update etc.<\/p>\n<pre><code>root@myserver:~# aptitude install redis-server\n<\/code><\/pre>\n<p>In case all our remote clients were to connect directly to the broker we need to change the listening address in the config file and replace:<\/p>\n<pre><code>bind 127.0.0.1\n<\/code><\/pre>\n<p>with:<\/p>\n<pre><code>bind 0.0.0.0\n<\/code><\/pre>\n<p>in <code>\/etc\/redis\/redis.conf<\/code>. We are going to do this temporarily for testing only since we are going to use <code>stunnel<\/code> to secure Redis communication.<\/p>\n<p>For security reasons we want only the authenticated clients to communicate with the server so we set password in the config file:<\/p>\n<pre><code>requirepass &lt;my-redis-password&gt;\n<\/code><\/pre>\n<p>We also need to open the tcp port <code>6379<\/code> in the firewall (EC2 instance SecurityGroup) in case we enabled remote Redis access.<\/p>\n<p>Enable the overcommit kernel memory option so every <code>malloc()<\/code> operation will succeed:<\/p>\n<pre><code>root@myserver:~# sysctl vm.overcommit_memory=1\n<\/code><\/pre>\n<p>and add it to <code>\/etc\/sysctl.conf<\/code> as well so it persists over bootups. Then start the service:<\/p>\n<pre><code>root@myserver:~# service redis start\n<\/code><\/pre>\n<h3><span id=\"install-and-setup-elastic-search\">Install and setup Elastic Search<\/span><\/h3>\n<p>We need at least version 0.90.7.<\/p>\n<pre><code>root@myserver:~#  wget https:\/\/download.elasticsearch.org\/elasticsearch\/elasticsearch\/elasticsearch-0.90.9.deb\nroot@myserver:~#  dpkg -i elasticsearch-0.90.9.deb\n<\/code><\/pre>\n<p>Then edit the <code>\/etc\/elasticsearch\/elasticsearch.yml<\/code> config file and set some options that suit our needs and VM hardware:<\/p>\n<pre><code>cluster.name: logstash\nnode.name: \"ec2-logger-server\"\npath.data: \/mnt\/elasticsearch,\/mnt2\/elasticsearch\npath.logs: \/mnt2\/log\/elasticsearch\nbootstrap.mlockall: true\nnetwork.host: \"127.0.0.1\"\n\n## Threadpool Settings ##\n# Search pool\nthreadpool.search.type: fixed\nthreadpool.search.size: 20\nthreadpool.search.queue_size: 100\n\n# Bulk pool\nthreadpool.bulk.type: fixed\nthreadpool.bulk.size: 60\nthreadpool.bulk.queue_size: 300\n\n# Index pool\nthreadpool.index.type: fixed\nthreadpool.index.size: 20\nthreadpool.index.queue_size: 100\n\n# Indices settings\nindices.memory.index_buffer_size: 30%\nindices.memory.min_shard_index_buffer_size: 12mb\nindices.memory.min_index_buffer_size: 96mb\n\n# Cache Sizes\nindices.fielddata.cache.size: 15%\nindices.fielddata.cache.expire: 6h\nindices.cache.filter.size: 15%\nindices.cache.filter.expire: 6h\n\n# Indexing Settings for Writes\nindex.refresh_interval: 30s\nindex.translog.flush_threshold_ops: 50000\n<\/code><\/pre>\n<p>We enable <code>bootstrap.mlockall<\/code> to try to lock the process address space so it won&#8217;t be swapped (recommended by ES configuration page). This also requires setting the following options in the <code>\/etc\/default\/elasticsearch<\/code> file:<\/p>\n<pre><code>ES_HEAP_SIZE=2g\nMAX_LOCKED_MEMORY=unlimited\n<\/code><\/pre>\n<p>We have also tuned the search, bulk and index pools giving the bulk pool 3 times more threads since the server will be mostly writing. We also tune the index sizes and cache flushes to reduce the load on the server under heavy writes.<\/p>\n<p>Later on we had to add an additional drive via <code>path.data:\/mnt\/elasticsearch,\/mnt2\/elasticsearch<\/code> option to expand the index disk storage. This option also adds on speed since the combination of the two drives acts like a RAID0 for files, of course compared to chunks as in real RAID. I have also moved the logs to <code>\/mnt2\/elasticsearch<\/code> drive since it is a faster SSD drive.<\/p>\n<p>Recommended Java for ES is Oracle JDK so we install it and make it default on the server:<\/p>\n<pre><code>root@myserver:~$ echo oracle-java7-installer shared\/accepted-oracle-license-v1-1 select true | tee \/usr\/bin\/debconf-set-selections &amp;&amp; echo \"deb http:\/\/ppa.launchpad.net\/webupd8team\/java\/ubuntu precise main\" | tee -a \/etc\/apt\/sources.list &amp;&amp; echo \"deb-src http:\/\/ppa.launchpad.net\/webupd8team\/java\/ubuntu precise main\" | tee -a \/etc\/apt\/sources.list &amp;&amp; apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EEA14886 &amp;&amp; aptitude update &amp;&amp; aptitude install -y oracle-java7-installer &amp;&amp; aptitude install -y oracle-java7-set-default\n<\/code><\/pre>\n<p>In case of SSD drives, we are better off with <code>Noop<\/code> scheduler than the default <code>CFQ (Completely Fair Queuing)<\/code> one which is better for spindles disks.<\/p>\n<pre><code>root@myserver:~# cat \/sys\/block\/xvdg\/queue\/scheduler\nnoop [deadline] cfq\n\nroot@myserver:~# echo noop | tee \/sys\/block\/xvdg\/queue\/scheduler\nnoop\n\nroot@myserver:~# cat \/sys\/block\/xvdg\/queue\/scheduler\n[noop] deadline cfq\n<\/code><\/pre>\n<p>Install the HEAD and Marvel plugins so we have some overview of ES parameters and indices:<\/p>\n<pre><code>root@myserver:~# \/usr\/share\/elasticsearch\/bin\/plugin --install mobz\/elasticsearch-head --verbose\nroot@myserver:~# \/usr\/share\/elasticsearch\/bin\/plugin --install elasticsearch\/marvel\/latest --verbose\n<\/code><\/pre>\n<p>The access to these plugins and ES restful API will be protected via NGINX proxy we set further down on this page. Then we start the service:<\/p>\n<pre><code>root@myserver:~# service elasticsearch start\n<\/code><\/pre>\n<h3><span id=\"logstash-install-and-config\">Logstash Install and Config<\/span><\/h3>\n<p>The installation is a simple download of a jar file that contains all the tools needed:<\/p>\n<pre><code>root@myserver:~# mkdir \/opt\/logstash\nroot@myserver:~# mkdir \/etc\/logstash\nroot@myserver:~# mkdir \/var\/log\/logstash\nroot@myserver:~# cd \/opt\/logstash\nroot@myserver:\/opt\/logstash# wget https:\/\/download.elasticsearch.org\/logstash\/logstash\/logstash-1.3.3-flatjar.jar\nroot@myserver:\/opt\/logstash# ln -sf logstash-1.3.3-flatjar.jar logstash.jar\n<\/code><\/pre>\n<p>Configuration, <code>\/etc\/logstash\/reader.conf<\/code> file:<\/p>\n<pre><code>input {\n  redis {\n    host =&gt; \"127.0.0.1\"\n    type =&gt; \"redis\"\n    data_type =&gt; \"list\"\n    key =&gt; \"logstash\"\n    password =&gt; \"&lt;my-redis-password&gt;\"\n  }\n}\noutput {\n  stdout { debug =&gt; \"true\" }\n  elasticsearch {\n     cluster =&gt; \"logstash\"\n  }\n}\n<\/code><\/pre>\n<p>so pretty simple, we read from the broker and write to the storage which in this case is elastic search node\/cluster we set up before. We start the logstash server from the command line as:<\/p>\n<pre><code>java -jar \/opt\/logstash\/logstash.jar agent --verbose -f \/etc\/logstash\/reader.conf --log \/var\/log\/logstash\/logstash-reader.log &amp;\n<\/code><\/pre>\n<p>and send it running in the background. This is not very convenient so we set up an initd service script <code>\/etc\/init.d\/logstash-reader<\/code>:<\/p>\n<pre><code class=\"bash\">#! \/bin\/sh\n\n### BEGIN INIT INFO\n# Provides:          logstash-shipper\n# Required-Start:    $remote_fs $syslog\n# Required-Stop:     $remote_fs $syslog\n# Default-Start:     2 3 4 5\n# Default-Stop:      0 1 6\n# Short-Description: Start daemon at boot time\n# Description:       Enable service provided by daemon.\n### END INIT INFO\n\n. \/lib\/lsb\/init-functions\n\nmode=\"reader\"\nname=\"logstash-$mode\"\nlogstash_bin=\"\/usr\/bin\/java -- -jar \/opt\/logstash\/logstash.jar\"\nlogstash_conf=\"\/etc\/logstash\/$mode.conf\"\nlogstash_log=\"\/var\/log\/logstash\/$name.log\"\npid_file=\"\/var\/run\/$name.pid\"\n\nNICE_LEVEL=\"-n 19\"\n\nstart () {\n    command=\"\/usr\/bin\/nice ${NICE_LEVEL} ${logstash_bin} agent --verbose -f $logstash_conf --log ${logstash_log}\"\n\n    log_daemon_msg \"Starting $mode\" \"$name\"\n    if start-stop-daemon --start --quiet --oknodo --pidfile \"$pid_file\" -b -m --exec $command; then\n        log_end_msg 0\n    else\n        log_end_msg 1\n    fi\n}\n\nstop () {\n    start-stop-daemon --stop --quiet --oknodo --pidfile \"$pid_file\"\n}\n\nstatus () {\n    status_of_proc -p $pid_file \"\" \"$name\"\n}\n\ncase $1 in\n    start)\n        if status; then exit 0; fi\n        start\n        ;;\n    stop)\n        stop\n        ;;\n    reload)\n        stop\n        start\n        ;;\n    restart)\n        stop\n        start\n        ;;\n    status)\n        status &amp;&amp; exit 0 || exit $?\n        ;;\n    *)\n        echo \"Usage: $0 {start|stop|restart|reload|status}\"\n        exit 1\n        ;;\nesac\n\nexit 0\n<\/code><\/pre>\n<p>and set right permissions and schedule for start up:<\/p>\n<pre><code>root@myserver:~# chmod +x \/etc\/init.d\/logstash-reader\nroot@myserver:~# update-rc.d logstash-reader defaults\n<\/code><\/pre>\n<p>At the end we start the service:<\/p>\n<pre><code>root@myserver:~# service logstash-reader start\n<\/code><\/pre>\n<p>We also need to manage the log files created by logstash so they don&#8217;t grow too big for which I set logrotate job in <code>\/etc\/logrotate.d\/logstash<\/code> file:<\/p>\n<pre><code>\/var\/log\/logstash\/*.log {\n    daily\n    missingok\n    rotate 5\n    compress\n    delaycompress\n    notifempty\n    create 644 root root\n    sharedscripts\n    postrotate\n       \/etc\/init.d\/logstash-shipper reload &gt; \/dev\/null\n    endscript\n}\n<\/code><\/pre>\n<h3><span id=\"web-interface\">Web interface<\/span><\/h3>\n<p>For web interface frontend we use <code>Kibana<\/code>:<\/p>\n<pre><code>root@myserver:\/opt# wget http:\/\/download.elasticsearch.org\/kibana\/kibana\/kibana-latest.zip\nroot@myserver:\/opt# unzip kibana-latest.zip\nroot@myserver:\/opt# mv kibana-latest \/var\/www\/kibana\n<\/code><\/pre>\n<p>We will front by NGINX proxy which gives us more stability, security, SSL and also user access control since Kibana doesn&#8217;t come with one. See <a href=\"\/blog\/server\/Nginx-LDAP-module\/\">Nginx LDAP module on Debian\/Ubuntu<\/a> for the details about compiling NGINX for LDAP support. See <a href=\"\/blog\/server\/Secure-Nginx-with-Naxsi-SSL-GeoIP-and-Google-Page-Speed-modules\/\">Secure Nginx with Naxsi, SSL, GeoIP and Google Page Speed modules on Debian\/Ubuntu<\/a> for the details about compiling NGINX with Naxsi and SSL support.<\/p>\n<p>We are going to setup an SSL proxy for Kibana and ElasticSearch in a SSL enabled virtual host. We put the following in the <code>\/etc\/nginx\/sites-available\/kibana<\/code> file we create (just the related part given for brevity):<\/p>\n<pre><code>server {\n...\n    location \/ {\n       include  \/etc\/nginx\/mysite.rules;\n       try_files $uri $uri\/ \/index.html;\n       auth_ldap \"Restricted\";\n       auth_ldap_servers ldap1;\n       auth_ldap_servers ldap2;\n    }\n\n    ## Kibana proxy\n    location ~ ^\/_aliases$ {\n       proxy_pass http:\/\/127.0.0.1:9200;\n       proxy_read_timeout 90;\n    }\n    location ~ ^\/.*\/_aliases$ {\n       proxy_pass http:\/\/127.0.0.1:9200;\n       proxy_read_timeout 90;\n    }\n    location ~ ^\/_nodes$ {\n       proxy_pass http:\/\/127.0.0.1:9200;\n       proxy_read_timeout 90;\n    }\n    location ~ ^\/.*\/_search$ {\n       proxy_pass http:\/\/127.0.0.1:9200;\n       proxy_read_timeout 90;\n    }\n    location ~ ^\/.*\/_mapping$ {\n       proxy_pass http:\/\/127.0.0.1:9200;\n       proxy_read_timeout 90;\n    }\n\n    ## Password protected end points\n    location ~ ^\/kibana-int\/dashboard\/.*$ {\n       proxy_pass http:\/\/127.0.0.1:9200;\n       proxy_read_timeout 90;\n       limit_except GET {\n          proxy_pass http:\/\/127.0.0.1:9200;\n          auth_ldap \"Restricted\";\n          auth_ldap_servers ldap1;\n          auth_ldap_servers ldap2;\n       }\n    }\n    location ~ ^\/kibana-int\/temp.*$ {\n       proxy_pass http:\/\/127.0.0.1:9200;\n       proxy_read_timeout 90;\n       limit_except GET {\n          proxy_pass http:\/\/127.0.0.1:9200;\n          auth_ldap \"Restricted\";\n          auth_ldap_servers ldap1;\n          auth_ldap_servers ldap2;\n       }\n    }\n    # Protected Proxy access to ES plugins and modules\n    location ~ ^\/elastic\/(_.*) {\n       proxy_read_timeout 90;\n       proxy_pass http:\/\/127.0.0.1:9200\/$1;\n       proxy_redirect http:\/\/127.0.0.1:9200 https:\/\/myserver.mydomain.com\/;\n       auth_ldap \"Restricted\";\n       auth_ldap_servers ldap1;\n       auth_ldap_servers ldap2;\n   }\n...\n}\n<\/code><\/pre>\n<p>At the end we enable the Kibana virtual host, disable the default one and restart Nginx:<\/p>\n<pre><code>root@myserver:\/opt# rm -f \/etc\/nginx\/sites-enabled\/default\nroot@myserver:\/opt# ln -sf \/etc\/nginx\/sites-available\/kibana \/etc\/nginx\/sites-enabled\/kibana\nroot@myserver:\/opt# service nginx configtest\nroot@myserver:\/opt# service nginx restart\n<\/code><\/pre>\n<p>Now we can go and change the ES link for Kibana:<\/p>\n<pre><code>root@myserver:\/opt# vi \/var\/www\/kibana\/config.js\n...\n    \/*elasticsearch: \"http:\/\/\"+window.location.hostname+\":9200\",*\/\n    elasticsearch: \"https:\/\/\"+window.location.hostname+\":443\",\n...\n<\/code><\/pre>\n<p>After all this in place we should see the following dashbord after log in to https:\/\/myserver.mydomain.com:<\/p>\n<p><a href=\"https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/kibana.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/kibana.png\" alt=\"\" width=\"1655\" height=\"669\" class=\"aligncenter size-full wp-image-220\" srcset=\"https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/kibana.png 1655w, https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/kibana-420x170.png 420w, https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/kibana-744x301.png 744w, https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/kibana-768x310.png 768w, https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/kibana-1200x485.png 1200w\" sizes=\"auto, (max-width: 1655px) 100vw, 1655px\" \/><\/a><br \/>\n<em><strong>Picture2:<\/strong> Kibana dashboard<\/em><\/p>\n<p>Obviously some configuration has been done to the dashboard and some data collected already when the screen shot was made.<\/p>\n<p><a href=\"https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/logstash_marvel_plugin.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/logstash_marvel_plugin.png\" alt=\"\" width=\"1648\" height=\"579\" class=\"aligncenter size-full wp-image-221\" srcset=\"https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/logstash_marvel_plugin.png 1648w, https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/logstash_marvel_plugin-420x148.png 420w, https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/logstash_marvel_plugin-744x261.png 744w, https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/logstash_marvel_plugin-768x270.png 768w, https:\/\/icicimov.com\/blog\/wp-content\/uploads\/2017\/01\/logstash_marvel_plugin-1200x422.png 1200w\" sizes=\"auto, (max-width: 1648px) 100vw, 1648px\" \/><\/a><br \/>\n<em><strong>Picture3:<\/strong> ElasticSearch Marvel plugin<\/em><\/p>\n<h2><span id=\"clients\">Clients<\/span><\/h2>\n<p>On the client side we need to set logstash to ship the log files we need and mark them in the same time in a way we can distinguish them later in the searches. But on each client we have to go through the same general installation and setup as outlined in the <code>Logstash install and config<\/code> section of the server set up. The only difference is that I&#8217;ve added <code>-Xmx256m<\/code> to the java start up command when launching Logstash to limit the memory we want the logstash java process to use since on start up it gets very busy parsing files and consumes all the resources available (especially if the process was stopped for some time and has to catch up going through lot of logs). This value should be adjusted according to the available system memory we have to spare on Logstash.<\/p>\n<p>After that the setup of the logstash configuration file will depend on the server it is running on and the applications we need to collect logs for. So, to collect Tomcat logs and send them to Redis server we can put the following in the <code>\/etc\/logstash\/shipper.conf<\/code> logstash configuration file:<\/p>\n<pre>\ninput {\n   file {\n      type => \"tomcat\"\n      path => [ \"\/var\/log\/tomcat7\/catalina.out\" ]\n      codec => multiline {\n        pattern => \"(^\\d+\\serror)|(^.+Exception: .+)|(^\\s+at .+)|(^\\s+... \\d+ more)|(^\\s*Caused by:.+)|(^\\s+)|(^\\s*[{|}])|(^(\\s+)?< \\\/?.+>$)\"\n        what => \"previous\"\n      }\n      tags => \"MYTAG\"\n      sincedb_path => \"\/opt\/logstash\/logstash-tomcat.db\"\n   }\n}\noutput {\n   stdout { }\n   redis {\n      host => \"127.0.0.1\"\n      data_type => \"list\"\n      key => \"logstash\"\n      password => \"my-redis-password\"\n   }\n}\n<\/pre>\n<p>For MongoDB lets say (running two db&#8217;s example):<\/p>\n<pre>\ninput {\n   file {\n      type => \"mongodb\"\n      path => [ \"\/var\/log\/mongodb\/mongodb.log\" ]\n      sincedb_path => \"\/opt\/logstash\/logstash-mongodb.db\"\n      tags => [\"MYTAG1\",\"MYTAG2\"]\n   }\n   file {\n      type => \"mongodb2\"\n      path => [ \"\/var\/log\/mongodb2\/mongodb2.log\" ]\n      sincedb_path => \"\/opt\/logstash\/logstash-mongodb2.db\"\n      tags => [\"MYTAG1\",\"MYTAG2\"]\n   }\n}\nfilter {\n  if [type] ~ \"mongodb\" and [message] !~ \/(.+)\/ {\n    drop { }\n  }\n}\noutput {\n   stdout { }\n   if !(\"_grokparsefailure\" in [tags]) {\n     redis {\n        host => \"127.0.0.1\"\n        data_type => \"list\"\n        key => \"logstash\"\n        password => \"my -redis-password\"\n     }\n   }\n}\n<\/pre>\n<p>For HAProxy, logstash has built in filters and parsers that will sort the messages nicely for us (added geoip tagging below as bonus):<\/p>\n<pre>\ninput {\n   file {\n      type => \"haproxy\"\n      path => [\"\/var\/log\/haproxy.log\"]\n      exclude => [\"*.gz\"]\n      sincedb_path => \"\/opt\/logstash\/logstash-hap.db\"\n   }\n}\nfilter {\n   if [type] == \"haproxy\" {\n     grok {\n       match => {\"message\" => [\"%{HAPROXYHTTP}\"]}\n       add_tag => [\"MYTAG1\",\"MYTAG2\"]\n     }\n     geoip {\n       source => \"client_ip\"\n       target => \"geoip\"\n       database => \"\/usr\/share\/GeoIP\/GeoLiteCity.dat\"\n       add_field => [ \"[geoip][coordinates]\", \"%{[geoip][longitude]}\" ]\n       add_field => [ \"[geoip][coordinates]\", \"%{[geoip][latitude]}\"  ]\n     }\n     mutate {\n       convert => [ \"[geoip][coordinates]\", \"float\"]\n     }\n   }\n}\noutput {\n   stdout { }\n   redis {\n      host => \"127.0.0.1\"\n      data_type => \"list\"\n      key => \"logstash\"\n      password => \"my -redis-password\"\n   }\n}\n<\/pre>\n<p>In case of ActiveMQ host lets say:<\/p>\n<pre>\ninput {\n   file {\n      type => \"activemq\"\n      path => [ \"\/opt\/activemq\/data\/activemq.log*\" ]\n      codec => multiline {\n        pattern => \"(^\\d+\\serror)|(^.+Exception: .+)|(^\\s+at .+)|(^\\s+... \\d+ more)|(^\\s*Caused by:.+)|(^\\s+\\\")|(^\\s*[{|}])\"\n        what => \"previous\"\n      }\n      tags => [\"MYTAG\"]\n      sincedb_path => \"\/opt\/logstash\/logstash-amq.db\"\n   }\n}\nfilter {\n  if [type] == \"activemq\" and [message] !~ \/(.+)\/ {\n    drop { }\n  }\n}\noutput {\n   stdout { }\n   if !(\"_grokparsefailure\" in [tags]) {\n     redis {\n        host => \"127.0.0.1\"\n        data_type => \"list\"\n        key => \"logstash\"\n        password => \"my -redis-password\"\n     }\n   }\n}\n<\/pre>\n<p>Or ElasticSearch:<\/p>\n<pre>\ninput {\n   file {\n      type => \"elasticsearch\"\n      path => [ \"\/var\/log\/elasticsearch\/*.log\" ]\n      codec => multiline {\n        pattern => \"(^\\d+\\serror)|(^.+Exception: .+)|(^\\s+at .+)|(^\\s+... \\d+ more)|(^\\s*Caused by:.+)|(^\\s+\\\")|(^\\s*[{|}])\"\n        what => \"previous\"\n      }\n      tags => [\"MYTAG\"]\n      sincedb_path => \"\/opt\/logstash\/logstash-es.db\"\n   }\n}\nfilter {\n  if [type] == \"elasticsearch\" and [message] !~ \/(.+)\/ {\n    drop { }\n  }\n}\noutput {\n   stdout { }\n   if !(\"_grokparsefailure\" in [tags]) {\n     redis {\n        host => \"127.0.0.1\"\n        data_type => \"list\"\n        key => \"logstash\"\n        password => \"my -redis-password\"\n     }\n   }\n}\n<\/pre>\n<p>To collect logs from an SMTP server and about bounced massages only we put:<\/p>\n<pre>\ninput {\n   file {\n      type => \"postfix\"\n      path => [\"\/var\/log\/mail.log\"]\n      sincedb_path => \"\/opt\/logstash\/logstash-postfix.db\"\n      start_position => \"beginning\"\n   }\n}\nfilter {\n  if [type] == \"postfix\" and [message] =~ \/status=bounced\/ {\n      grok {\n        patterns_dir => \"\/etc\/logstash\/patterns\"\n        match => [\"message\", \"%{POSTFIXBOUNCE}\"]\n        named_captures_only => true\n        add_tag => [\"BOUNCED\",\"SMTP\"]\n      }\n  }\n}\noutput {\n   stdout { }\n   redis {\n      host => \"127.0.0.1\"\n      data_type => \"list\"\n      key => \"logstash\"\n      password => \"my -redis-password\"\n   }\n} \n<\/pre>\n<p>to filter the logs through the <code>POSTFIXBOUNCE<\/code> pattern we set in <code>\/etc\/logstash\/patterns\/postfix<\/code> file:<\/p>\n<pre><code>MONTH \\b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\\b\nMONTHNUM (?:0?[1-9]|1[0-2])\nMONTHDAY (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])\nHOUR (?:2[0123]|[01]?[0-9])\nMINUTE (?:[0-5][0-9])\nSECOND (?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?)\nTIME (?!&lt; [0-9])%{HOUR}:%{MINUTE}(?::%{SECOND})(?![0-9])\nIPV6 ((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\nIPV4 (?&lt;![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])\nIP (?:%{IPV6}|%{IPV4})\nHOSTNAME \\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\\b)\nHOST %{HOSTNAME}\nIPORHOST (?:%{HOSTNAME}|%{IP})\nHOSTPORT %{IPORHOST}:%{POSINT}\nPOSINT \\b(?:[1-9][0-9]*)\\b\nPROG (?:[\\w._\/%-]+)\nSYSLOGPROG %{PROG:program}(?:\\[%{POSINT:pid}\\])?\nSYSLOGTIMESTAMP %{MONTH} +%{MONTHDAY} %{TIME}\nSYSLOGHOST %{IPORHOST}\nSYSLOGBASE %{SYSLOGTIMESTAMP:timestamp} (?:%{SYSLOGFACILITY} )?%{SYSLOGHOST:logsource} %{SYSLOGPROG}:\nQUEUEID (?:[A-F0-9]+|NOQUEUE)\nEMAILADDRESSPART [a-zA-Z0-9_.+-=:]+\nEMAILADDRESS %{EMAILADDRESSPART:local}@%{EMAILADDRESSPART:remote}\nRELAY (?:%{HOSTNAME:relayhost}(?:\\[%{IP:relayip}\\](?::%{POSREAL:relayport})?)?)\nPOSREAL [0-9]+(.[0-9]+)?\nDELAYS %{POSREAL}\/%{POSREAL}\/%{POSREAL}\/%{POSREAL}\nDSN %{POSINT}.%{POSINT}.%{POSINT}\nSTATUS sent|deferred|bounced|expired\nPERMERROR 5[0-9]{2}\nMESSAGELEVEL reject|warning|error|fatal|panic\nGREEDYDATA .*\nQUOTEDSTRING (?:(?&lt;!\\\\)(?:\"(?:\\\\.|[^\\\\\"])*\"|(?:'(?:\\\\.|[^\\\\'])*')|(?:`(?:\\\\.|[^\\\\`])*`)))\nPOSTFIX %{SYSLOGTIMESTAMP:timestamp} %{SYSLOGHOST:hostname} %{COMPPID}: %{QUEUEID:queueid}\nPOSTFIXQMGR %{POSTFIX}: (?:removed|from=&lt;(?:%{EMAILADDRESS:from})?&gt;(?:, size=%{POSINT:size}, nrcpt=%{POSINT:nrcpt} \\(%{GREEDYDATA:queuestatus}\\))?)\nPOSTFIXBOUNCE %{SYSLOGTIMESTAMP:timestamp} %{HOST:host} %{SYSLOGPROG}: %{QUEUEID}: to=&lt; %{EMAILADDRESS:to}&gt;, relay=%{RELAY}, delay=%{POSREAL:delay}, delays=%{DELAYS:delays}, dsn=%{DSN}, status=%{STATUS} %{GREEDYDATA:reason}\n<\/code><\/pre>\n<p>On the servers that run multiple of the above services we simply combine two or more file handlers in the input section. To start the logstash client service we run:<\/p>\n<pre><code>$ sudo service logstash-shipper start\n<\/code><\/pre>\n<p>using the same bash init script as for the reader but named <code>\/etc\/init.d\/logstash-reader<\/code> in this case.<\/p>\n<h2><span id=\"security\">Security<\/span><\/h2>\n<p>Since Redis doesn&#8217;t support SSL we need to secure the data transfer between the clients and the central server. As mentioned before we will setup <code>stunnel<\/code> on the server and clients side for this purpose. On the clients, the <code>logstash-shipper<\/code> process will write to its localhost redis port where stunnel will listen and forward the messages to the remote stunnel on the server via SSL. The server&#8217;s stunnel process will then hand over the data to the local redis server in clear text.<\/p>\n<p>Please refer to the following wiki page <a href=\"https:\/\/icicimov.com\/blog\/?p=223\">Securing Logstash to Redis communication with Stunnel<\/a> for the details.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Logstash is a tool for managing events and logs. It is very useful for collecting, parsing and storing logs for later use like for example searching. It comes with a web interface for searching through the logs. The picture bellow&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,10],"tags":[],"class_list":["post-217","post","type-post","status-publish","format-standard","hentry","category-aws","category-server"],"_links":{"self":[{"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/217","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=217"}],"version-history":[{"count":6,"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/217\/revisions"}],"predecessor-version":[{"id":236,"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/217\/revisions\/236"}],"wp:attachment":[{"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=217"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=217"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/icicimov.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=217"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}