Dernière modification : 20 juin 2009

Le journal des connexions

Dans le répertoire des fichiers journaux, /var/log, il y en a un dont le nom évoque le mécanisme d'authentification des utilisateurs : /var/log/auth.log. Si on regarde un peu son contenu, on y trouve effectivement des choses intéressantes :

$ less /var/log/auth.log
[…]
Apr  5 20:10:57 chocobo gdm[2836]: pam_unix(gdm:session): session closed for user julie
Apr  5 20:10:57 chocobo gnome-keyring-daemon[3124]: failed to shutdown HAL context: (null)
Apr  5 21:42:40 chocobo gdm[3052]: pam_unix(gdm:session): session opened for user jm by (uid=0)
Apr  5 21:44:19 chocobo sudo:       jm : TTY=pts/0 ; PWD=/home/jm ; USER=root ; COMMAND=/usr/bin/nano /etc/timeouts
[…]

Dans ce répertoire, on trouvera aussi des versions plus anciennes de ce fichier, dont certaines non compressées au format GZ pourront facilement être exploitées. Il est même judicieux d'exploiter systématiquement les deux versions les plus récentes car la rotation des journaux[1] fait que le fichier principal /var/log/auth.log est régulièrement vidé de son contenu. Par exemple :

$ ls /var/log/auth.log*
/var/log/auth.log  /var/log/auth.log.1  /var/log/auth.log.2.gz

Signalons tout de suite que, suivant le niveau de sécurité de votre système, vous n'aurez pas forcément les droits nécessaires à la consultation de ces fichiers. Sur un système Debian par exemple, il vous sera nécessaire soit de vous faire passer pour l'administrateur, soit de vous ajouter dans le groupe adm qui donne le droit de consulter les journaux du système. Cette dernière méthode est bien sûr conseillée, surtout pour la suite qui consistera à automatiser le processus d'analyse du journal. Vous pouvez utiliser pour cela les interfaces graphiques d'administration qui vont bien, ou encore utiliser la commande suivante :

$ sudo usermod -a -G adm $(whoami)

Rappelons que sudo vous fera passer pour l'administrateur alors que usermod change les propriétés d'un utilisateur. L'option -a signifie qu'il s'agit d'ajouter un groupe, -G spécifie le groupe et whoami retourne votre identifiant.

Il nous faut maintenant sélectionner les informations intéressantes, la commande grep est pour cela toute indiquée puisqu'elle permet entre autre de sélectionner des lignes dans un fichier à partir d'expressions régulières. Recherchons tout d'abord les ouvertures de session à l'aide du mot-clef opened en anglais :

$ grep opened /var/log/auth.log
[…]
Apr  1 21:12:27 chocobo su[3336]: pam_unix(su:session): session opened for user root by (uid=1000)
Apr  1 21:41:31 chocobo login[2970]: pam_unix(login:session): session opened for user jm by LOGIN(uid=0)
Apr  1 21:41:55 chocobo gdm[2836]: pam_unix(gdm:session): session opened for user charlelie by (uid=0)
Apr  1 21:42:38 chocobo su[4230]: pam_unix(su:session): session opened for user charlelie by (uid=0)
Apr  1 21:44:52 chocobo gdm[2836]: pam_unix(gdm:session): session opened for user charlelie by (uid=0)
[…]

Comme vous pouvez le constater, il y a plusieurs types de sessions qui peuvent être ouvertes, ici gdm, login et su[2]. Si comme dans mon cas ce sont uniquement les sessions graphiques qui vous intéressent, alors ce sont celles liées au gestionnaire de connexion gdm qu'il faut comptabiliser[3]. Il faut donc ne récupérer que les informations comportant la mention gdm:session :

$ grep gdm:session /var/log/auth.log
[…]
Apr  1 21:50:29 chocobo gdm[2836]: pam_unix(gdm:session): session opened for user jm by (uid=0)
Apr  1 22:08:23 chocobo gdm[2836]: pam_unix(gdm:session): session closed for user jm
Apr  1 22:08:35 chocobo gdm[2836]: pam_unix(gdm:session): session opened for user charlelie by (uid=0)
Apr  1 22:11:47 chocobo gdm[2836]: pam_unix(gdm:session): session closed for user charlelie
[…]

Enfin pour être vraiment sûr de n'attraper que les ouvertures et fermetures de session, on peut ajouter un filtre ne sélectionnant que les lignes qui contiennent opened ou closed :

$ grep gdm:session /var/log/auth.log | grep -E 'opened|closed'

C'est l'opérateur barre verticale « | » qui permet d'enchaîner les opérations de filtrage des deux commandes grep, l'option -E de grep servant à dire qu'on utilise une expression régulière étendue. La barre verticale dans cette dernière expression est alors un opérateur OU, et non l'opérateur d'enchaînement…

Afin de rendre tout cela plus lisible, on peut aussi découper seulement les champs qui nous intéressent, par exemple les dates, heures, utilisateurs et informations d'ouverture / fermeture. La commande magique pour faire cela s'appelle cut, elle découpe des champs dans les lignes d'un fichier texte. On peut lui spécifier un séparateur de champs avec l'option -d, ici c'est l'espace. Cependant dans le fichier journal, le numéro du jour peut être séparé du nom du mois avec un ou deux espaces pour des raisons esthétiques[4]. Du coup cut peut voir un champ vide supplémentaire, ce qui modifie le décompte pour les champs suivants…

Une solution consiste alors à remplacer au préalable toute succession de deux espaces par un seul espace suivi d'un zéro : Apr 1 devient Apr 01. Pour réaliser cela, on peut utiliser la commande sed qui sait remplacer des caractères dans un texte. Il ne reste plus qu'à enchaîner toutes ces commandes, les champs intéressants sont les n° 1, 2, 3, 8 et 11 :

$ grep gdm:session /var/log/auth.log | grep -E 'opened|closed' | sed 's/  / 0/' | cut -d ' ' -f 1-3,8,11
[…]
Mar 31 20:58:38 opened jm
Mar 31 23:28:25 closed jm
Apr 01 20:48:44 opened jm
Apr 01 21:41:14 closed jm
Apr 01 21:41:55 opened charlelie
Apr 01 21:42:53 closed charlelie
[…]

Voilà qui est nettement plus lisible ! Éventuellement vous pouvez récupérer mon script session-monitor.sh qui place le résultat de cette commande dans un fichier texte à consulter tranquillement ensuite.

Calculer les durées de session

Maintenant nous allons utiliser les informations d'ouverture et fermeture de session précédentes pour calculer la durée de connexion de chaque utilisateur, chaque jour et chaque semaine. Cette fois-ci nous allons laisser tomber les scripts shell pour le langage Python, bien plus adapté à cette tâche. La lecture du fichier journal se transpose comme suit :

import string

SESSIONMGR = 'gdm:session'		# filter to retrieve session start/end
AUTHLOG = '/var/log/auth.log'		# system log file of session infos

FileId = open(AUTHLOG)
Data = filter(
	lambda x: SESSIONMGR in x and ('opened' in x or 'closed' in x),
	FileId.readlines(),
)
FileId.close()
Text = []
for x in Data:
	x = x.split()
	Text.append( string.join( [x[0], x[1], x[2], x[7], x[10]] ) )

L'équivalent des grep est réalisé dans la fonction filter, la fonction lambda code le test sur la présence de gdm:session et de opened ou closed. L'équivalent de sed et cut est réalisé dans la boucle for avec split pour séparer les champs et string.join pour recoller ceux qui nous intéressent.

Le calcul de la durée de session s'effectue ensuite en convertissant les dates sous forme de texte en dates sous forme d'objet datetime. Cet objet fait partie du module Python standard éponyme. On convertit de texte à datetime grâce à la fonction datetime.datetime.strptime, la différence entre deux datetime donne un objet timedelta dont on peut ensuite récupérer le nombre de jours et de secondes équivalents :

import datetime

DATEFORMAT = '%b %d %H:%M:%S'			# date format in system logs

Start = datetime.datetime.strptime(
	LineStart[:LineStart.index(' opened')],
	DATEFORMAT,
)
End = datetime.datetime.strptime(
	LineEnd[:LineEnd.index(' closed')],
	DATEFORMAT,
)
Duration = End - Start
Duration = Duration.days*24 + Duration.seconds/3600.0

Voilà pour les bases, vous aurez compris qu'il s'agit ensuite de parcourir la liste des information ouverture / fermeture de session afin de lancer ces petits calculs sur les bonnes lignes LineStart et LineEnd, tout en enregistrant les résultats dans des dictionnaires afin de produire les statistiques quotidiennes ou hebdomadaires. Dans mon script session-monitor.py, j'ai codé ceci en modifiant un objet dictionnaire standard du langage. La modification réalisée permet principalement d'ajouter des durées à des clefs existantes et d'afficher d'une manière lisible le contenu du dictionnaire, c'est-à-dire les statistiques. Les clefs du dictionnaire sont composées d'une date et d'un nom d'utilisateur. Démonstration :

$ python Desktop/session-monitor.py
[…]
2009-03-31: charlelie 0h30
2009-03-31: jm 2h29
2009-04-01: charlelie 0h06
2009-04-01: jm 1h46
[…]
2009-S13: charlelie 3h33
2009-S13: jm 7h39
2009-S13: julie 1h08
2009-S14: charlelie 1h54
[…]
5-Friday: jm 3h36
5-Friday: julie 0h32
6-Saturday: charlelie 3h20
6-Saturday: jm 9h09
[…]

Nous avons donc des statistiques par jour, par semaine et par jour de la semaine[5]. Vous avez donc maintenant tous les outils pour connaître l'utilisation qui est faite de vos ordinateurs et pourrez donc réagir en cas d'abus. Rappelons à ce propos que l'abus des écrans nuit gravement à la santé, surtout chez nos petits chérubins[6]. Certains médecins recommandent de ne pas dépasser 1h par semaine par année d'âge, soit 6h/sem. pour un enfant de 6 ans. L'avantage de l'ordinateur est que, contrairement aux autres écrans, il est suffisamment ouvert pour qu'on puisse récupérer les informations qui pourraient nous alerter. ;-)

Notes

[1] mécanisme permettant de ne pas saturer le disque avec des informations système obsolètes qui s'accumulent au fil des jours

[2] login est une session en console uniquement, su est une session ouverte avec la commande sudo

[3] votre système peut aussi utiliser un autre gestionnaire de connexion comme kdm ou encore xdm

[4] on garde ainsi l'alignement lorsque le nombre tient sur un chiffre et non deux

[5] attention toutefois, il s'agit de sommes de durée et non d'une moyenne par jour de la semaine

[6] voir par exemple Le temps passé devant un écran remplace l'activité physique chez les enfants et les jeunes au Canada