Table des matières
Centralisation des logs via Syslog-ng
Le logiciel Syslog-NG apporte une grande souplesse pour le stockage des journaux systèmes. Il permet de :
- lister les sources ( fichiers, sockets unix ou réseau )
- de redistribuer les journaux à plusieurs endroits, sur plusieurs machines
- de stocker les journaux en fichier ou en base de données
- de bifurquer les informations vers une destination selon le contenu
Cette application est tout simplement un gestionnaire de flux d'informations.
Il peut de plus devenir soit un client émetteur de logs, soit un serveur récepteur de logs. La centralisation des fichiers journaux n'est en fait qu'une façon d'utiliser l'application parmi d'autres choix possibles de gestion.
Voici à titre d'information, les filtres possibles :
- les “facilities” : auth, authpriv, daemon, cron, ftp, lpr, kern, mail, news, syslog, user, uucp, local0, local1, …, local7
- les “niveau” de log : Emergency, Alert, Critical, Error, Warning, Notice, Info or Debug
- le contenu du message : error, failed
- le nom du programme : apache, bind, su, ejabberd, …
Installation du serveur
- Installation du paquet syslog-ng
aptitude install syslog-ng
Configuration du serveur
Pour supprimer les messages intempestifs s'affichant dans la console:
echo "CONSOLE_LOG_LEVEL=1" >> /etc/default/syslog-ng
Ensuite on s'attaque à la configuration du démon syslog-ng sur le serveur central:
section options
Cette section propose de paramétrer le fonctionnement du démon, voici le rôle de certaines options :
Fichier /etc/syslog-ng/syslog-ng.conf
options { chain_hostnames(no); stats_freq(43200); # affiche une ligne de statistique dans le log toute les 12 heures ( 43200/3600 = 12 ) owner(root); group(root); # le démon tourne avec les id/gid de root perm(0640); # affectation des droits sur les fichiers créés par syslog-ng dir_perm(0740); # affectation des droits sur les dossiers créés par syslog-ng create_dirs(yes); # on autorise syslog-ng a créer les dossiers use_fqdn(yes); log_fifo_size(4096); # nombre de lignes que syslog garde en RAM avant de les écrires sur le disque keep_hostname(yes); use_dns(yes); dns_cache(yes); };
section source
Ici on définit les différentes sources depuis lesquelles syslog doit récupérer les messages renvoyés par les différents programmes. Voici ici un exemple où toutes les sources possibles sont regroupées.
La ligne udp() est paramétrée ici de façon à ce que le démon écoute sur le port 514 en protocole UDP pour chacune de ses interfaces réseau (on peut remplacer par une adresse précise au besoin)
source src { unix-stream("/dev/log" max-connections(256)); internal(); file("/proc/kmsg"); udp(ip(0.0.0.0) port(514)); # Ecoute possible sur toutes les interfaces réseaux };
section destination
Cette section permet de définir ou les messages reçus par le démon doivent être renvoyés. On est sur le serveur central, on va donc tout stocker dans des fichiers localement. Pour s'y retrouver dans les messages enregistrés on va essayer de mettre en place une arborescence consistante pour le stockage des logs.
La variable HOST_FROM permet de récupérer le nom de la machine responsable du message, on va donc commencer le tri des messages à ce niveau.
Le second niveau de tri sera fait en fonction du mois, pour chaque mois, nouveau dossier sera créé.
Enfin le dernier niveau de tri sera effectué en fonction de la FACILITY.
Cette arborescence est défini dans la seconde définition, la première nous permet de logguer tous ce qui passe dans un fichier unique qui sera réinitialiser quotidiennement. Les 2 dernières définition nous permettent de séparer les logs issus des serveurs apaches dans des fichiers dédiés.
destination messages { file("/var/log/archive/$HOST_FROM/messages"); }; destination df_syslog { file("/var/log/archive/$HOST_FROM/$R_MONTH/$FACILITY.log" ); }; destination df_httpacc { file("/var/log/archive/$HOST_FROM/$R_MONTH/apache-access.log" ); }; destination df_httperr { file("/var/log/archive/$HOST_FROM/$R_MONTH/apache-error.log" ); }; destination console_all { file("/dev/tty12"); };
section filtres
La section filtre permet d'exclure ou de conserver les messages contenant certaines chaines spécifiques. Les 5 premiers filtres sont relatifs au degré de gravité du message, les 2 derniers filtres recherchent respectivement les chaines “failed” et “denied” dans le message.
L'extension donnée aux différents fichiers de destination sera directement mise en correspondance avec chacun des filtres définis ici.
# définition de filtres génériques qui associés # aux destinations génériques permettent de trier # les logs en fonctions de leur niveau de gravité filter f_info { level(info); }; filter f_notice { level(notice); }; filter f_warn { level(warn); }; filter f_crit { level(crit); }; filter f_err { level(err); }; filter f_failed { message("failed"); }; filter f_denied { message("denied"); };
section log
A partir des définitions de source, destination et filtre, nous pouvons maintenant créer les canaux.
# association des destinations et filtres pour créer le log # tous les messages sont loggués en vrac log { source(src); destination(messages); }; # les logs d'apaches ont le drapeau "final" activé car ils # utilisent la facilité "kern", ceci évitera donc de retrouver # ces messages dans les loggue du kernel log { source(src); filter(f_httpacc); destination(df_httpacc); flags(final); }; log { source(src); filter(f_httperr); destination(df_httperr); flags(final); }; # définition pour les différentes facilité log { source(src); destination(df_syslog); }; # définition pour une sortie console log { source(src); destination(console_all); };
Lancer le serveur
Après lancement du service syslog-ng, on peut arrêter le service par défaut rsyslog de debian :
/etc/init.d/syslog-ng start update-rc.d -f rsyslog remove dpkg -P rsyslog
Configurer les clients
On commence par installer syslog-ng:
aptitude install syslog-ng
Puis on passe à la configuration du démon, c'est beaucoup plus rapide que pour le serveur maitre, on renseigne les options:
$ nano /etc/syslog-ng/syslog-ng.conf options { chain_hostnames(no); stats_freq(43200); owner(root); group(root); perm(0640); dir_perm(0740); create_dirs(yes); use_fqdn(yes); keep_hostname(yes); use_dns(no); };
puis on ajoute la déclaration de source:
source s_all { internal(); unix-stream("/dev/log" ); file("/proc/kmsg"); };
on poursuit avec la destination, ici on indique que les logs seront envoyés vers la machine “log” sur le port udp 514:
destination loghost { udp("log" port (514)); };
et pour finir on agrège le tout:
log { source(s_all); destination(loghost); };
Exemple de récupération des logs apache
Récupération des logs par un pipe. Exemple vu http://peter.blogs.balabit.com/2010/02/how-to-collect-apache-logs-by-syslog-ng/ ici :
Création des pipe :
mknod /var/log/apache2/access.log.pipe p mknod /var/log/apache2/error.log.pipe p
On demande à apache d'écrire dans les pipe :
CustomLog /var/log/apache2/access.log.pipe combined ErrorLog /var/log/apache2/error.log.pipe
Construction des canaux côté client :
##### ## GESTION SPECIFIQUE APACHE ##### source s_apache_access { pipe("/var/log/apache2/access.log.pipe" flags(no-parse) ); }; source s_apache_error { pipe("/var/log/apache2/error.log.pipe" flags(no-parse) ); }; destination apache_access { file("/var/log/apache2/access.log"); }; destination apache_error { file("/var/log/apache2/error.log"); }; # log a distance, route specifique pour apache destination r_apache_access { tcp("syslog.mdl29" port(18080) template("$MSG\n") ); }; destination r_apache_error { tcp("syslog.mdl29" port(18081) template("$MSG\n") ); }; log { source(s_apache_access); destination(apache_access); }; log { source(s_apache_error); destination(apache_error); }; log { source(s_apache_access); destination(r_apache_access); }; log { source(s_apache_error); destination(r_apache_error); };
Construction des canaux côté serveur :
##### ## GESTION APACHE ##### source s_apache_error { tcp(ip(0.0.0.0) port(18081) flags(no-parse) ); }; source s_apache_access { tcp(ip(0.0.0.0) port(18080) flags(no-parse) ); }; # filtre de séparation des logs en provenance d'apache # NON UTILISE filter f_httpacc { program("apache"); }; filter f_httperr { program("apache2"); }; destination df_httpacc { file("/var/log/archive/$HOST_FROM/$R_MONTH/apache-access.log" ); }; destination df_httperr { file("/var/log/archive/$HOST_FROM/$R_MONTH/apache-error.log" ); }; log { source(s_apache_access); destination(df_httpacc); }; log { source(s_apache_error); destination(df_httperr); };
Conservation des logs particuliers sur la machine
Exemple des logs cron :
##### ## TACHES CRON ##### destination cronlog { file("/var/log/cron.log"); }; filter f_cron { facility(cron); }; log { source(s_all); filter(f_cron); destination(cronlog); };
Validation
Sur le serveur maitre on lance tcpdump pour écouter sur le port udp 514:
$ tcpdump -i eth0 udp port 514
Sur le client on démarre le démon syslog-ng:
$ /etc/init.d/syslog-ng restart
On retourne sur le serveur maitre pour vérifier que les messages ont été reçus:
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes 14:46:39.575320 IP mail.mdl29.45944 > log.mdl29.syslog: SYSLOG syslog.info, length: 100 14:46:39.577382 IP mail.mdl29.45944 > log.mdl29.syslog: SYSLOG syslog.notice, length: 94 14:46:39.612647 IP mail.mdl29.47827 > log.mdl29.syslog: SYSLOG syslog.notice, length: 92
Ca marche, et le dossier de stockage a-t-il été créé?
$ ls -l /var/log/archive/*/* total 8 drwxr----- 9 root root 4096 aoû 23 13:22 log.mdl29 drwxr----- 5 root root 4096 aoû 23 14:35 mail.mdl29
C'est bon aussi, on peut supprimer rsyslog des services de la VM cliente:
update-rc.d -f rsyslog remove
Pour créer les répertoires contenant les mails du jour sur le serveur on lance le script syscron-ng:
$ syscron-ng --update $ ls -l /var/log/mail.mdl29 total 8 lrwxrwxrwx 1 root root 62 aoû 23 14:53 authpriv.notice -> /../mail.mdl29/authpriv/../2010-08-23.notice lrwxrwxrwx 1 root root 56 aoû 23 14:53 mail.info -> /../mail.mdl29/mail/../2010-08-23.info lrwxrwxrwx 1 root root 58 aoû 23 14:53 syslog.info -> /../mail.mdl29/syslog/../2010-08-23.info lrwxrwxrwx 1 root root 60 aoû 23 14:53 syslog.notice -> /../mail.mdl29/syslog/../2010-08-23.notice
Script d'archivage des mails: syscron-ng
Le rôle de ce script est:
- de créer des liens symboliques dans le répertoire /var/log/<nom de machine> vers les logs du jour
- d'archiver annuellement les logs en compressant le dossier contenant les logs de l'année obsolète et en supprimant ce même dossier
- d'archiver mensuellement les logs en faisant la même chose mais cette fois-ci pour les mois
C'est une première version je l'ai écris un peu vite mais voici quand même le code en attendant mieux:
$ cat /usr/local/bin/syscron-ng #!/bin/bash #!/bin/bash ################################################################## # Load variables # ################################################################## LOGPATH="/var/log/archive" HOSTLIST=`find $LOGPATH -mindepth 1 -maxdepth 1 -type d | cut -d / -f5` MONTH=`date +%m` # traitement successif de chaque HOST for HOST in $HOSTLIST; do echo "-- processing $HOST" # dossier contenant les logs pour le mois en cours HOSTPATH="$LOGPATH/$HOST" CURRENT="$HOSTPATH/$MONTH" cd $HOSTPATH/ # recupere une liste des archives existantes ARCHS=`find $HOSTPATH/ -mindepth 1 -maxdepth 1 -type f | grep tbz2 | cut -d / -f6` # si le numero de l'archive correspond au numero # du mois en cours l'archive date d'un an, on la supprime for ARCH in $ARCHS; do [ `echo $ARCH | cut -d . -f1` == $MONTH ] && echo " removing $ARCH" && rm $ARCH; done # recupere une liste des dossiers de stockage mensuelle OLDS=`find $HOSTPATH/ -mindepth 1 -type d | cut -d / -f6` for OLD in $OLDS; do # si le numero du dossier ne correspond pas avec # le numero du mois en cours, on cree une nouvelle archive # et on supprime l'ancien dossier if [ $OLD != $MONTH ]; then echo " building $HOSTPATH/$OLD.tbz2" tar cjpf $OLD.tbz2 ./$OLD && rm -rf ./$OLD fi done done cd ~/
Mise à jour du crontab
La fonction d'archivage doit être exécutée mensuellement:
$ nano /etc/crontab ### CRON MONTHLY 2 0 1 * * root /usr/local/bin/syscron-ng
PhpSyslog-NG
Pour disposer d'une interface web permettant l'accès au logs et les enregistrer en base de donnée nous allons utiliser phpsyslog-ng.
wget http://php-syslog-ng.googlecode.com/files/php-syslog-ng-2.9.8m.tar.gz
Ensuite nous devons monter un serveur LAMP, pour cela je vous renvoie à l'article suivant: installer un serveur LAMP (Linux Apache Mysql PHP)
On poursuit avec l'extraction de l'archive:
tar xvpf php-syslog-ng-2.9.8m.tar.gz mv html /var/www/localhost/phpsyslog
Et la définition d'un alias pour le vhost:
[...] Alias /syslog "/var/www/localhost/phpsyslog/" <Directory "/var/www/localhost/phpsyslog/"> Options FollowSymLinks AllowOverride FileInfo Options Order deny,allow Allow from 192.168.10.0/255.255.255.0 Allow from 10.8.3.0/255.255.255.0 </Directory>
Après avoir relancé le serveur apache, il ne reste plus qu'à terminer l'install en se rendant à l'adresse http://localhost/syslog.
/etc/init.d/apache2 reload
Pour le support GD il faut installer ceci:
apt-get install php5-gd
Une fois l'installation faite, on peut modifier la config de syslog-ng pour qu'il enregistre également les messages dans la base de donnée:
nano /etc/syslog-ng/syslog-ng.conf # définition d'une destination pour # enregistrer les logs en base de donnée destination df_mysql { program("/usr/bin/mysql -h localhost -u db_user --password=password db_name" template("INSERT INTO logs (host, facility, priority, level, tag, datetime, program, msg) VALUES ( '$HOST', '$FACILITY', '$PRIORITY', '$LEVEL', '$TAG', '$YEAR-$MONTH-$DAY $HOUR:$MIN:$SEC', '$PROGRAM', '$MSG' ); \n" ) template-escape(yes)); };
Voici quelques filtres spécifiques basés sur le contenu du message ou le nom du programme dont ils sont issus (utile pour limiter les enregistrements en base de donnée):
# filtre de séparation des logs en provenance d'apache filter f_httpacc { not facility(local1) and program("apache"); }; filter f_httperr { facility(local1) and program("apache2"); }; # définition d'un filtre contenant des messages et/ou des # expressions régulières qui ne doivent pas êtres enregistrés # en base de donnée filter f_regexp { message("cron:session") or message("BIND") or message("bdb_equality_candidates") or message("bdb_filter_candidates") or message("bdb_list_candidates") or message("get_filter") or message("test_filter") or message("AND") or message("OR") or message("EQUALITY") or message("attr=") or message("tag=") or message("mail=6") ;}; # définition d'un filtre basé sur le programme # émetteur du message filter f_programs { program("postfix/anvil") or program("snmpd") # or program("EJABBERD") ;}; # définition d'un filtre basé sur le programme # émetteur du message et sur une expression régulière filter f_mixed { program("postfix/qmgr") and message("removed") or program("postfix/smtpd") and message("connect") or program("postfix/smtpd") and message("disconnect") or program("dovecot") and message("ldap") or program("dovecot") and message("master") or program("dovecot") and message("pid") or program("dovecot") and message("client in") or program("dovecot") and message("client out") or program("dovecot") and message("IMAP") or program("su") and message("ejabberd") # or filter(f_info) and program("dovecot") and message("auth") ;}; # définition d'un filtre spécifiques pour limiter # les messages enregistrés en base de donnée: # ordre des niveau de priorité: # - emerg 0 /* system is unusable */ # - alert 1 /* action must be taken immediately */ # - crit 2 /* critical conditions */ # - err 3 /* error conditions */ # - warn 4 /* warning conditions */ # - notice 5 /* normal but significant condition */ # - info 6 /* informational */ # - debug 7 /* debug-level messages */ filter f_mysql { level(emerg..debug) and not filter(f_regexp) and not filter(f_programs) and not filter(f_mixed) and not facility(cron) and not filter(f_httpacc) ;};
Et pour finir:
# association des destinations et filtres pour créer le log # la définition pour le loggue en base de donnée vient en premier # car sinon les définitions ayant le drapeau "final" activé # ne seront pas logguées log { source(src); filter(f_mysql); destination(df_mysql); };
Un redémarrage de syslog et le tour est joué:
/etc/init.d/syslog-ng restart
That's All Folks ;)