Script de detection des comptes inactifs.
darcs-hash:20060506081915-68412-7b3d9dde523f9ffddef6595dad9f8111860aac09.gz
This commit is contained in:
parent
4e8b66110d
commit
e38a6f4ef4
1 changed files with 207 additions and 0 deletions
207
surveillance/comptes_inactifs.py
Executable file
207
surveillance/comptes_inactifs.py
Executable file
|
@ -0,0 +1,207 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# -*- coding: iso-8859-15 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Repère les comptes inactifs en parsant les logs de derniere connexion
|
||||||
|
de sshd et dovecot.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Copyright (C) 2006 Stéphane Glondu
|
||||||
|
# Licence : GPLv2
|
||||||
|
|
||||||
|
|
||||||
|
import sys, os, sre, time, cPickle
|
||||||
|
from time import mktime, time, localtime, strptime, strftime
|
||||||
|
from socket import gethostname
|
||||||
|
|
||||||
|
host = gethostname()
|
||||||
|
mail_address = u'disconnect@crans.org'
|
||||||
|
mail_sender = u"Comptes inactifs <disconnect@crans.org>"
|
||||||
|
|
||||||
|
sys.path.append('/usr/scripts/gestion')
|
||||||
|
from affich_tools import tableau
|
||||||
|
from email_tools import send_email
|
||||||
|
from ldap_crans import crans_ldap
|
||||||
|
from config import ann_scol
|
||||||
|
db = crans_ldap()
|
||||||
|
|
||||||
|
|
||||||
|
class ComptesInactifs:
|
||||||
|
re = sre.compile(r'^(\w+\s+\d+\s+\d+:\d+:\d+).*(?:'
|
||||||
|
r'dovecot.*Login: user=<|'
|
||||||
|
r'sshd.*Accepted.*for '
|
||||||
|
r')([^ >]+).*$')
|
||||||
|
|
||||||
|
def __init__(self, filename):
|
||||||
|
''' Lecture du dico et acquisition du lock '''
|
||||||
|
self.filename = filename
|
||||||
|
|
||||||
|
# Systeme de lock sommaire
|
||||||
|
self.lockfile = filename + '.lock'
|
||||||
|
if os.path.isfile(self.lockfile):
|
||||||
|
f = file(self.lockfile)
|
||||||
|
(lhost, pid) = f.read().strip().split()
|
||||||
|
f.close()
|
||||||
|
if lhost != host or os.system('ps %s >/dev/null 2>&1') == 0:
|
||||||
|
# Le lock est actif ou sur une autre machine
|
||||||
|
raise RuntimeError('Lock actif')
|
||||||
|
f = file(self.lockfile, 'w')
|
||||||
|
f.write('%s %d\n' % (host, os.getpid()))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# A partir de la, on a le lock
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
self.dic = cPickle.load(file(filename))
|
||||||
|
else:
|
||||||
|
self.dic = {}
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
''' Sauvegarde du dico et suppression du lock '''
|
||||||
|
cPickle.dump(self.dic, file(self.filename, 'w'))
|
||||||
|
os.remove(self.lockfile)
|
||||||
|
|
||||||
|
def update(self, login, timestamp):
|
||||||
|
"""
|
||||||
|
Met a jour l'entree correspondant au login donnee, ainsi que
|
||||||
|
l'entree '!', qui correspond a la plus vieille entree.
|
||||||
|
"""
|
||||||
|
dic = self.dic
|
||||||
|
timestamp = int(timestamp)
|
||||||
|
|
||||||
|
# Mise a jour de l'entree la plus vieille
|
||||||
|
if not dic.has_key('!') or timestamp < dic['!']:
|
||||||
|
dic['!'] = timestamp
|
||||||
|
|
||||||
|
# Mise a jour de l'entree correspondant au login
|
||||||
|
if not dic.has_key(login) or timestamp > dic[login]:
|
||||||
|
dic[login] = timestamp
|
||||||
|
|
||||||
|
def update_from_syslog(self, loglines):
|
||||||
|
""" Met a jour le dico avec les lignes de syslog donnees """
|
||||||
|
annee = localtime(time())[0]
|
||||||
|
now = time() + 600
|
||||||
|
nombre = 0
|
||||||
|
for line in loglines:
|
||||||
|
m = self.re.match(line)
|
||||||
|
if not m: continue
|
||||||
|
date = list(strptime(m.group(1), "%b %d %H:%M:%S"))
|
||||||
|
date[0] = annee
|
||||||
|
t = mktime(date)
|
||||||
|
if t > now:
|
||||||
|
date[0] = annee - 1
|
||||||
|
t = mktime(date)
|
||||||
|
self.update(m.group(2), t)
|
||||||
|
nombre += 1
|
||||||
|
print '%d ligne(s) pertinente(s)' % nombre
|
||||||
|
|
||||||
|
def do_log(self):
|
||||||
|
""" Lit des lignes de log sur l'entree std et met a jour le dico """
|
||||||
|
self.update_from_syslog(sys.stdin)
|
||||||
|
print 'Lecture des logs terminee'
|
||||||
|
|
||||||
|
def do_dump(self):
|
||||||
|
""" Affiche le contenu du dico """
|
||||||
|
liste = self.dic.items()
|
||||||
|
liste.sort(lambda x, y: cmp(x[1], y[1]))
|
||||||
|
data = [(x[0], strftime('%d/%m/%Y %H:%M', localtime(x[1])))
|
||||||
|
for x in liste]
|
||||||
|
print tableau(data,
|
||||||
|
largeur = (20, 18))
|
||||||
|
|
||||||
|
def get_idle_accounts(self, since=32*24*3600):
|
||||||
|
"""
|
||||||
|
Renvoie la liste de ceux qui ne se sont pas connectes depuis
|
||||||
|
since secondes, par defaut un mois (32 jours, pour etre sur).
|
||||||
|
"""
|
||||||
|
oldest = self.dic.get('!', int(time()))
|
||||||
|
limit = int(time()) - since
|
||||||
|
liste = [a
|
||||||
|
for a in os.listdir('/home')
|
||||||
|
if os.path.isdir('/var/mail/' + a)
|
||||||
|
and self.dic.get(a, oldest) < limit]
|
||||||
|
return liste
|
||||||
|
|
||||||
|
def do_summary(self):
|
||||||
|
modele = u"""*Membres inscrits ne s'étant pas connectés depuis plus d'un mois*
|
||||||
|
|
||||||
|
%(inscrits)s
|
||||||
|
Total : %(inscrits_total)d
|
||||||
|
|
||||||
|
*Anciens membres ne s'étant pas connectés depuis plus d'un mois*
|
||||||
|
|
||||||
|
%(anciens)s
|
||||||
|
Total : %(anciens_total)d
|
||||||
|
|
||||||
|
Légende :
|
||||||
|
- F : existence d'un .forward
|
||||||
|
- M : existence de mails non lus
|
||||||
|
|
||||||
|
L'analyse des logs remonte au %(oldest)s.
|
||||||
|
|
||||||
|
--
|
||||||
|
comptes_inactifs.py
|
||||||
|
"""
|
||||||
|
liste = []
|
||||||
|
for x in self.get_idle_accounts():
|
||||||
|
a = db.search('uid=%s' % x)['adherent']
|
||||||
|
if a:
|
||||||
|
liste.append((x, a[0]))
|
||||||
|
else:
|
||||||
|
print 'uid=%s introuvable' % x
|
||||||
|
liste.sort()
|
||||||
|
|
||||||
|
inscrits = []
|
||||||
|
anciens = []
|
||||||
|
|
||||||
|
for (x, a) in liste:
|
||||||
|
date = self.dic.get(x)
|
||||||
|
if date:
|
||||||
|
date = strftime(u'%d/%m/%Y %H:%M', localtime(date))
|
||||||
|
else:
|
||||||
|
date = u'Jamais'
|
||||||
|
forward = os.path.isfile('/home/%s/.forward' % x) and u'X' or u''
|
||||||
|
try:
|
||||||
|
maildir = '/var/mail/%s/new' % x
|
||||||
|
mail = os.path.isdir(maildir) and os.listdir(maildir) and u'X' or u''
|
||||||
|
except:
|
||||||
|
# Arrive quand le script n'a pas les bons droits pour lire
|
||||||
|
# /var/mail
|
||||||
|
mail = u'?'
|
||||||
|
ligne = (a.id(), x, a.Nom(), date, forward, mail)
|
||||||
|
if ann_scol in a.paiement():
|
||||||
|
inscrits.append(ligne)
|
||||||
|
else:
|
||||||
|
anciens.append(ligne)
|
||||||
|
|
||||||
|
titres = (u'aid', u'Login', u'Nom', u'Dernière connexion', u'F', u'M')
|
||||||
|
largeurs = (6, 15, 20, 20, 1, 1)
|
||||||
|
alignements = ('d', 'g', 'c', 'c', 'c', 'c')
|
||||||
|
|
||||||
|
inscrits_total = len(inscrits)
|
||||||
|
inscrits = tableau(inscrits, titres, largeurs, alignements)
|
||||||
|
anciens_total = len(anciens)
|
||||||
|
anciens = tableau(anciens, titres, largeurs, alignements)
|
||||||
|
oldest = strftime(u'%d/%m/%Y %H:%M',
|
||||||
|
localtime(self.dic.get('!', time())))
|
||||||
|
|
||||||
|
send_email(mail_sender,
|
||||||
|
mail_address,
|
||||||
|
u'Comptes inactifs',
|
||||||
|
modele % locals())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = sys.argv[1:]
|
||||||
|
|
||||||
|
if len(args) != 1:
|
||||||
|
sys.stderr.write("Arguments incorrects\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
commande = args[0]
|
||||||
|
if commande not in ('log', 'dump', 'summary'):
|
||||||
|
sys.stderr.write("Commande incorrecte : %s\n" % args[1])
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
ci_db = ComptesInactifs('/usr/scripts/var/comptes_inactifs.dict')
|
||||||
|
eval('ci_db.do_%s()' % commande)
|
||||||
|
ci_db.close()
|
Loading…
Add table
Add a link
Reference in a new issue