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 :

  1. lister les sources ( fichiers, sockets unix ou réseau )
  2. de redistribuer les journaux à plusieurs endroits, sur plusieurs machines
  3. de stocker les journaux en fichier ou en base de données
  4. 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 :

inlinetoc

Installation du serveur

 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:

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 ;)