Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Déploiement ELK en conditions réelles

Présentation faite lors de l'édition 2016 du BreizhCamp à Rennes

  • Login to see the comments

Déploiement ELK en conditions réelles

  1. 1. BreizhCamp 2015 #BzhCmp https://github.com/geoffroya/BzhCmp2016 BreizhCamp 2016 #BzhCmp Déploiement ELK en conditions réelles Geoffroy ARNOUD
  2. 2. La suite ELK • Elasticsearch o  Moteur de recherche distribué basé sur Lucène o  Stockage type document • Logstash o  Collecte/Réception de données o  Analyse/Formattage o  Stockage (vers ES) • Kibana o  Outil de visualisation
  3. 3. Les objectifs • Mettre en place ELK pour centraliser nos logs (et pas que…) • Satisfaire nos interlocuteurs o Exploitants o Maîtrises d’œuvre o RSSI o DSI
  4. 4. Des conditions réelles
  5. 5. 1ère étape • Centraliser les syslogs
  6. 6. Configuration Rsyslog • /etc/rsyslog.d/syslog.conf *.* @<host>:<port>
  7. 7. Configuration Logstash sur ELK • /etc/logstash/conf.d/logstash.conf input { udp { port => 5514 } } filter { grok { match => [ "message", "%{SYSLOGLINE}" ] overwrite => ["message"] } mutate { replace => ["timestamp", "2016 %{timestamp}"] } date { match => ["timestamp", "YYYY MMM d HH:mm:ss", "YYYY MMM dd HH:mm:ss"] timezone => "Europe/Paris" } } output { elasticsearch { hosts => ["127.0.0.1"] index => "breizhcamp-%{+YYYY.MM.dd}" } }
  8. 8. 2ème étape • Brancher des logs applicatifs de serveurs Linux
  9. 9. Configuration Logstash « clients » • /etc/logstash/conf.d/logstash.conf input { file { path => "/var/log/breizhcamp/access.log" type => "apache" } } filter { grok { match => ["message","%{COMBINEDAPACHELOG}"] } date { match => ["timestamp","dd/MMM/YYYY:HH:mm:ss Z"] timezone => "Europe/Paris" } } output { elasticsearch { hosts => ["<host ELK>"] index => "breizhcamp-%{+YYYY.MM.dd}" } } input { file { path => "/var/log/breizhcamp/application.log" type => "j2e" codec => multiline { pattern => "^%{DATESTAMP:timestamp} " negate => "true" what => "previous" } } } filter { grok { match => ["message","%{DATESTAMP:timestamp} %{DATA:thread} [%{DATA:logLevel}s*] %{DATA:classMethod} : %{GREEDYDATA:detail}"] } date { match => ["timestamp","dd/MM/YYYY HH:mm:ss.SSS"] timezone => "Europe/Paris" } } output { elasticsearch { hosts => ["<host ELK>"] index => "breizhcamp-%{+YYYY.MM.dd}" } }
  10. 10. Premiers problèmes • Comment ne pas éparpiller la configuration ? • Comment gérer les serveurs non Linux (Windows, Solaris) ? • Les exploitants n’ont pas envie d’installer Logstash sur tous les serveurs !
  11. 11. Quelques solutions • Identifier des outils de collecte pour chaque OS o Rsyslog pour Linux o Nxlog pour Windows et Solaris o Filebeat ( RDV à 17h30 en Amphi A) • Remonter des logs sans les analyser : ajout de méta-données à la collecte o devient 07/03/2016 16:15:04.654 Thread-522 [INFO ] c.c.m.d.i.InterfaceImpl.rechercher (126) : appel OK METAS:fmt=JAVA,path=/var/log/breizhcamp/application.log|||07/03/2016 16:15:04.654 Thread-522 [INFO ] c.c.m.d.i.InterfaceImpl.rechercher (126) : appel OK
  12. 12. Résultat • Bénéfices o Centraliser la configuration o Pas/peu d’overhead sur serveurs applicatifs
  13. 13. Configuration Rsyslog • /etc/rsyslog.d/logs.conf # Définition de notre template LOGSTASH pour les envois sur un port TCP $template LogstashForwardFormat,"%syslogtag%|||%msg%" # Chargement du module imfile module(load="imfile" PollingInterval="10") # Logs APACHE # ############### input(type="imfile" File="/var/log/breizhcamp/access.log" Tag="METAS:fmt=MY_APACHE,path=/var/log/breizhcamp/access.log" stateFile="access.log.state") # Logs JAVA # ############# input(type="imfile" File="/var/log/breizhcamp/application.log" Tag="METAS:fmt=JAVA,path=/var/log/breizhcamp/application.log" stateFile="application.log.state") if $syslogtag contains 'METAS' then @@<host>:<port>;LogstashForwardFormat :syslogtag, contains, "METAS" ~
  14. 14. Configuration Nxlog • C:Program files(x86)nxlogconfnxlog.conf <Output out> Module om_tcp Host <host> Port <port> </Output> # Input pour événements Windows <Input evt> Module im_msvistalog Exec $Message = $raw_event; $raw_event = "METAS:fmt=WINDOWS_EVENT,path=windowsEvents|||" + $Message; </Input> # Input pour fichier de logs <Input file1> Module im_file File "C:logsapplication.log" Exec $Message = $raw_event; $raw_event = "METAS:fmt=JAVA,path=" + filename() + "|||" + $Message; </Input> <Route 1> Path evt => out </Route> <Route 2> Path file1 => out </Route>
  15. 15. Configuration Logstash (1/3) • /etc/logstash/conf.d/01_input.conf input { # Input pour rsyslog tcp { port => 5514 tags => ["logs"] } # Input pour NxLog tcp { port => 5516 tags => ["nxlog", "logs"] codec => multiline { pattern => "^METAS" negate => "true" what => "previous" } } }
  16. 16. Configuration Logstash (2/3) • /etc/logstash/conf.d/02_filter.conf filter { grok { match => ["message", "METAS:%{DATA:metas}|||%{GREEDYDATA:message}" ] overwrite => "message" } kv { source => "metas" field_split => "," value_split => "=" prefix => "meta_" } if [meta_fmt] == "JAVA" { multiline { pattern => "^%{DATESTAMP}" negate => "true" what => "previous" } grok { match => ["message","%{DATESTAMP:timestamp} %{DATA:thread} [%{DATA:logLevel}s*] %{DATA:classMethod} : %{GREEDYDATA:detail}"] } date { match => ["timestamp","dd/MM/YYYY HH:mm:ss.SSS"] timezone => "Europe/Paris" } } [...] }
  17. 17. Configuration Logstash (3/3) • /etc/logstash/conf.d/03_output.conf output { elasticsearch { hosts => ["<host ELK>"] index => "breizhcamp-%{+YYYY.MM.dd}" } }
  18. 18. C’est là que le RSSI dit « Stop ! » • « Je ne veux pas de flux sortant des DMZ, ni des zones de sécurité »
  19. 19. Une solution • Ajout d’un tampon pour inverser la logique du flux • Ajout de nouvelles méta-données o Host o Flag multi-ligne pour reconstituer les événements dès le tampon (stack-trace Java) METAS:host=my-server,ml=y,fmt=JAVA,path=/var/log/breizhcamp/application.log|||07/03/2016 16:15:04.654 Thread-522 [INFO ] c.c.m.d.i.InterfaceImpl.rechercher (126) : appel OK
  20. 20. Configuration Logstash tampon (1/2) • /etc/logstash/conf.d/02_filter.conf filter { grok { match => ["message", "METAS:%{DATA:metas}|||%{GREEDYDATA:message}" ] overwrite => "message" } kv { source => "metas" field_split => "," value_split => "=" prefix => "meta_" } if [meta_ml] == "y" { # Gestion du multi-ligne / format par format if [meta_fmt] == "JAVA" { multiline { pattern => "^%{DATESTAMP}" negate => "true" what => "previous" } } [...] } }
  21. 21. Configuration Logstash tampon (2/2) • /etc/logstash/conf.d/03_output.conf output { redis { batch => true batch_events => 50 batch_timeout => 10 host => [ "127.0.0.1" ] port => 6379 password => "BreizhCamp2016" data_type => "list" key => "logstash" } }
  22. 22. Configuration Logstash ELK (1/3) • /etc/logstash/conf.d/01_input_XYZ.conf o 1 fichier par tampon input { redis { host => "<host>" port => <port> password => "BreizhCamp2016" key => "logstash" data_type => "list" tags => ["indexer", "redis"] type => "logs" } }
  23. 23. Configuration Logstash ELK (2/3) • /etc/logstash/conf.d/02_filter_XYZ.conf o 1 fichier par format filter { if [meta_fmt] == "JAVA" { grok { match => [ "message", "%{JAVA}" ] patterns_dir => "/etc/logstash/patterns" } if "_grokparsefailure" not in [tags] { date { match => [ "timestamp", "dd/MM/YYYY HH:mm:ss.SSS" ] timezone => "Europe/Paris" remove_field => [ "timestamp" ] } } } }
  24. 24. Configuration Logstash ELK (3/3) • /etc/logstash/conf.d/03_output.conf output { if "_grokparsefailure" in [tags] { elasticsearch { hosts => [ "localhost" ] index => "idx-grokparsefailure-%{+YYYY.MM.dd}" flush_size => 1000 idle_flush_time => 10 template => "/etc/logstash/templates.d/grokparsefailure-template.json" template_name => "grokparsefailure" document_type => "logs" } } if "_grokparsefailure" not in [tags] { elasticsearch { hosts => [ "localhost" ] index => "breizhcamp-%{+YYYY.MM.dd}" flush_size => 100 idle_flush_time => 1 template => "/etc/logstash/templates.d/default-optimized-template.json" template_name => "default" document_type => "logs" } } }
  25. 25. Synthèse • Collecter avec ce qui est disponible sans impacter l’existant • Utiliser des buffers pour scaler, lisser la charge, respecter la politique de sécurité • Définir des méta-données pour factoriser et standardiser la configuration • Utiliser Excel pour o Inventorier les formats (essayer de restreindre le nombre de champs) o Lister les infos propres à chaque logs/appli  Volume par jour  Besoins de réplication  Rétention
  26. 26. Et revoilà le RSSI… • « Et la confidentialité dans tout ça ? Toutes les données vont dans le même index ! »
  27. 27. Solution • Faire du multi-tenants o Segmenter les données dans Elasticsearch : Utiliser des index distincts  Syslog  SIRH  Finance  App1 o Déployer autant de Kibana que de tenants
  28. 28. Implémentation • Ajout de méta-données : o « app »  domaine • Séparer les Kibana o Instancier N Kibana o Mettre un frontal WEB pour protéger les accès METAS:app=appX,host=my-server,ml=y,fmt=JAVA,path=/var/log/breizhcamp/application.log|||07/03/2016 16:15:04.654 Thread-522 [INFO ] c.c.m.d.i.InterfaceImpl.rechercher (126) : appel OK
  29. 29. Configuration Logstash ELK • /etc/logstash/conf.d/03_output.conf output { if "_grokparsefailure" in [tags] { elasticsearch { hosts => [ "localhost" ] index => "idx-grokparsefailure-%{+YYYY.MM.dd}« flush_size => 1000 idle_flush_time => 10 template => "/etc/logstash/templates.d/grokparsefailure-template.json" template_name => "grokparsefailure" template_overwrite => true document_type => "logs" } } if "_grokparsefailure" not in [tags] { elasticsearch { hosts => [ "localhost" ] index => "idx-%{meta_app}-%{+YYYY.MM.dd}" flush_size => 100 idle_flush_time => 1 template => "/etc/logstash/templates.d/default-optimized-template.json" template_name => "default" template_overwrite => true document_type => "logs" } } }
  30. 30. Multi-instances Kibana • /etc/kibana/kibana-x.yml • /etc/init.d/kibana-x o  BzhCmp2016 / kibana / etc / init.d / kibana-appx server.port: 5610 server.host: "127.0.0.1" kibana.index: ".kibana-X" pid.file: /var/run/kibana/kibana-X.pid logging.dest: /var/log/kibana/kibana-X.log
  31. 31. Configuration Apache • /etc/apache2/sites-enabled/kibana-x <VirtualHost hostname:80> ServerAlias x.hostname <LocationMatch "^/*$"> ProxyPassMatch http://localhost:5610 ProxyPassReverse http://localhost:5610 </LocationMatch> # Accès pour les utilisateurs autorisés <LocationMatch "^/(.*)$"> # Authentification AuthType Basic AuthName "USERS Kibana" AuthbasicProvider file AuthUserFile /etc/apache2/users # Autorisation AuthGroupFile /etc/apache2/groups Require group X ProxyPassMatch http://localhost:5610/$1 ProxyPassReverse http://localhost:5610/$1 Order allow,deny Allow from All Deny from All </LocationMatch> </VirtualHost>
  32. 32. Attention aux petits malins…
  33. 33. Comment faire ? • 2 solutions o Aller chercher des sous et souscrire pour disposer de Shield o Bloquer les requêtes…
  34. 34. Ajout d’une restriction dans Apache • /etc/apache2/sites-enabled/kibana-x <VirtualHost hostname:80> # On vérifie s'il s'agit d'un accès aux settings # Init SetEnvIfNoCase Remote_Addr "^" post_delete=0 SetEnvIfNoCase Remote_Addr "^" url_admin=0 SetEnvIfNoCase Remote_Addr "^" acces_admin=1 # 1ere passe de tests SetEnvIfNoCase Request_Method "POST" post_delete=1 SetEnvIfNoCase Request_Method "DELETE" post_delete=1 # URL à interdire SetEnvIfNoCase Request_URI "/elasticsearch/.kibana-x/index-pattern/" url_admin=1 SetEnvIfNoCase Request_URI "/elasticsearch/.kibana-x/config/" url_admin=1 # URL à autoriser SetEnvIfNoCase Request_URI "/elasticsearch/.kibana-x/index-pattern/_" url_admin=0 # Conjonction (Si une des sous-var vaut 0 => résultat vaut 0) SetEnvIfNoCase post_delete 0 acces_admin=0 SetEnvIfNoCase url_admin 0 acces_admin=0 SetEnvIfNoCase acces_admin 1 block_admin # Accès pour les utilisateurs autorisés <LocationMatch "^/(.*)$"> [...] Order allow,deny Allow from All Deny from env=block_admin </LocationMatch> </VirtualHost>
  35. 35. Synthèse V2 • Utilisation des méta-données pour passer dans un mode multi-tenants o  Permet une scalabilité optimale o  Permet d’avoir une plate-forme unique, avec des niveaux de service différents entre les tenants
  36. 36. La suite… • Utiliser les méta-données pour segmenter encore plus (différencier dev/staging, les composants techniques, les instances, les hyperviseurs, les DC…) • Utiliser Excel à tous les niveaux o Synthèse des formats de logs o Applis :  Requirements (réplication, perfs, rétention…)  Volume • Faire un capacity planning o Dimensionner les tampons :  Combien un tampon peut-il encaisser avant un OOM ?  Combien de downtime ES peut/doit-il gérer ? o Dimensionner les indexer (LS  ES) o Dimensionner le cluster ES
  37. 37. Références • ELK : http://www.elastic.co • Nxlog : http://nxlog-ce.sourceforge.net/ • Rsyslog : http://www.rsyslog.com/ • Sources utilisés lors de cette présentation : https://github.com/geoffroya/BzhCmp2016
  38. 38. Exemples
  39. 39. Exemples

×