Surveillance auth.log plus necessaire
This commit is contained in:
parent
0b12a81e93
commit
6c7c4ecf74
1 changed files with 0 additions and 0 deletions
334
archive/surveillance/parse_auth_log.py
Executable file
334
archive/surveillance/parse_auth_log.py
Executable file
|
@ -0,0 +1,334 @@
|
|||
#! /usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
###############################################################################
|
||||
# parse_auth_log.py : Détecte les problèmes d'authentifications
|
||||
###############################################################################
|
||||
# The authors of this code are
|
||||
# Jérémie Dimino <dimino@crans.org>
|
||||
#
|
||||
# Copyright (C) 2006 Jérémie Dimino
|
||||
# All rights reserved.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
###############################################################################
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.append('/usr/scripts/gestion')
|
||||
|
||||
from ldap_crans import crans_ldap
|
||||
from annuaires_pg import chbre_prises, reverse
|
||||
from affich_tools import cprint
|
||||
import sys, datetime
|
||||
|
||||
base = None
|
||||
# Correspondance prise -> chambre
|
||||
prise_chbre = {}
|
||||
aujourdhui = datetime.date.today()
|
||||
|
||||
def trouve_chambre(bat, prise):
|
||||
""" Trouve la chambre associée à une prise """
|
||||
|
||||
if prise in ('????', 'EXT', 'CRA'):
|
||||
return prise
|
||||
|
||||
if not prise_chbre.has_key(bat):
|
||||
prise_chbre[bat] = reverse(bat)
|
||||
|
||||
chbre = prise_chbre[bat].get(prise) or prise_chbre[bat].get(prise+"-")
|
||||
if chbre:
|
||||
chbre = chbre[0]
|
||||
else:
|
||||
cprint('La prise %s%s est inconnue' % (bat, prise), 'rouge')
|
||||
chbre = "????"
|
||||
|
||||
return chbre
|
||||
|
||||
|
||||
def trouve_prise(chbre):
|
||||
""" Trouve la prise associée à une chambre """
|
||||
|
||||
if chbre in ('EXT', '????', 'CRA'):
|
||||
return chbre
|
||||
else:
|
||||
bat = chbre[0].lower()
|
||||
prise = bat + chbre_prises(bat, chbre[1:])
|
||||
if prise[-1] == '-':
|
||||
prise = prise[:-1]
|
||||
return prise
|
||||
|
||||
|
||||
def __calcul_max(table):
|
||||
""" Calcule les différents maxima (voir parse_auth_log pour plus de détails) """
|
||||
|
||||
nb_m = 0
|
||||
nb_p = 0
|
||||
for prise, erreur_prise in table.items():
|
||||
nb = 0
|
||||
nb_max = 0
|
||||
for mac, erreur_mac in erreur_prise['macs'].items():
|
||||
n = len(erreur_mac['lignes'])
|
||||
erreur_mac['nombre'] = n
|
||||
nb += n
|
||||
nb_max += n
|
||||
erreur_prise['nombre'] = nb
|
||||
erreur_prise['nombre_max'] = nb_max
|
||||
nb_m = max(nb_m, nb_max)
|
||||
nb_p = max(nb_p, nb)
|
||||
|
||||
return nb_m, nb_p
|
||||
|
||||
|
||||
def __ajoute_ligne(errs, ligne, prise, mac, info=None):
|
||||
""" Ajoute une ligne dans le dico pour la prise et la mac donnée """
|
||||
|
||||
erreur_prise = errs.get(prise)
|
||||
if not erreur_prise:
|
||||
erreur_prise = { 'macs': {} }
|
||||
errs[prise] = erreur_prise
|
||||
|
||||
erreur_mac = erreur_prise['macs'].get(mac)
|
||||
if not erreur_mac:
|
||||
erreur_mac = { 'lignes': [], 'info': info }
|
||||
erreur_prise['macs'][mac] = erreur_mac
|
||||
|
||||
erreur_mac['lignes'].append(ligne)
|
||||
|
||||
|
||||
# Correspondance 'noms de mois' -> n°
|
||||
__mois_num = { 'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
|
||||
'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12 }
|
||||
|
||||
def parse_auth_log(fichier_log='/var/log/freeradius/radius_auth.log', jour=None):
|
||||
""" Retourne la liste des problèmes de connexions: retourne deux dicos, la première étant pour les macs qui n'ont pas été trouvées dans la base et la seconde pour les connexions qui ont eu lieu à partir d'une mauvaise prise
|
||||
|
||||
les deux dictionnaires ont la structure suivante:
|
||||
- nombre_mac_max: nombre maximal d'erreurs trouvées pour une mac
|
||||
- nombre_prise_max: nombre maximal d'erreurs trouvées pour une prise
|
||||
- prises: dico associant à une prise la listes des erreurs à partir de cette prise:
|
||||
- nombre: nombre total d'erreurs survenues sur la prise
|
||||
- nombre_max: nombre maximal d'erreurs trouvéss pour une mac
|
||||
- macs: dico associant à une mac la liste des erreurs à partir de cette mac
|
||||
- nombre: le nombre d'erreurs survenues pour cette mac sur cette prise
|
||||
- lignes: la liste des lignes du fichier de logs où la mac apparaît sur cette prise
|
||||
- info: informations sur le propriétaire (vaut None pour la première table)
|
||||
- machine: la machine en question
|
||||
- prise: la prise de sa chambre
|
||||
- chambre: sa chambre
|
||||
- prop: le propriétaire
|
||||
|
||||
Si jour est spécifié (de type datetime.date), toutes les connexions avant cette dates seront ignorées.
|
||||
"""
|
||||
|
||||
try:
|
||||
log = open(fichier_log)
|
||||
except:
|
||||
cprint(u"Impossible d'ouvrir le fichier %s" % fichier_log, 'rouge')
|
||||
sys.exit(1)
|
||||
|
||||
# Les macs non trouvées
|
||||
errs_inconnue = {}
|
||||
|
||||
# Les pc connectés sur la mauvaise prise
|
||||
errs_prise = {}
|
||||
|
||||
# Les recherches déjà effectuées sur les macs
|
||||
mac_machine = {}
|
||||
|
||||
annee = None
|
||||
mois = None
|
||||
|
||||
for ligne in log:
|
||||
champs = ligne.split()
|
||||
prise = champs[5]
|
||||
mac = champs[7]
|
||||
|
||||
if jour != None:
|
||||
nouveau_mois = __mois_num[champs[0]]
|
||||
|
||||
if annee == None:
|
||||
if nouveau_mois > aujourdhui.month:
|
||||
# Là on suppose qu'il s'agit de l'année dernière
|
||||
annee = aujourdhui.year - 1
|
||||
else:
|
||||
annee = aujourdhui.year
|
||||
elif mois > nouveau_mois:
|
||||
# Là on suppose qu'on est passé à l'année suivante
|
||||
annee += 1
|
||||
|
||||
mois = nouveau_mois
|
||||
|
||||
if datetime.date(annee, mois, int(champs[1])) < jour:
|
||||
continue
|
||||
|
||||
info_mac = mac_machine.get(mac)
|
||||
if info_mac:
|
||||
machine = info_mac['machine']
|
||||
else:
|
||||
machine = base.search('mac=' + mac)['machine']
|
||||
info_mac = { 'machine': machine }
|
||||
mac_machine[mac] = info_mac
|
||||
|
||||
if machine == []:
|
||||
__ajoute_ligne(errs_inconnue, ligne, prise, mac)
|
||||
|
||||
else:
|
||||
info = info_mac.get('info_prop')
|
||||
if not info:
|
||||
prop = machine[0].proprietaire()
|
||||
chbre = prop.chbre()
|
||||
info = { 'machine': machine[0],
|
||||
'prise': trouve_prise(chbre),
|
||||
'chambre': chbre,
|
||||
'prop': prop }
|
||||
info_mac['info_prop'] = info
|
||||
|
||||
if prise != info['prise']:
|
||||
__ajoute_ligne(errs_prise, ligne, prise, mac, info)
|
||||
|
||||
log.close()
|
||||
|
||||
nb_inconnue_m, nb_inconnue_p = __calcul_max(errs_inconnue)
|
||||
nb_prise_m, nb_prise_p = __calcul_max(errs_prise)
|
||||
|
||||
return { 'prises': errs_inconnue,
|
||||
'nombre_mac_max': nb_inconnue_m,
|
||||
'nombre_prise_max': nb_inconnue_p }, \
|
||||
{ 'prises': errs_prise,
|
||||
'nombre_mac_max': nb_prise_m,
|
||||
'nombre_prise_max': nb_prise_p }
|
||||
|
||||
|
||||
def affiche_erreurs(errs, message, min_mac=1, min_prise=1):
|
||||
""" Affiche les infos contenues dans un dico renvoyé par parse_auth_log """
|
||||
|
||||
if errs['nombre_mac_max'] >= min_mac and errs['nombre_prise_max'] >= min_prise:
|
||||
|
||||
cprint(message, 'bleu')
|
||||
|
||||
for prise, erreur_prise in errs['prises'].items():
|
||||
|
||||
if erreur_prise['nombre'] >= min_prise and erreur_prise['nombre_max'] >= min_mac:
|
||||
|
||||
chambre = prise[0].upper() + trouve_chambre(prise[0], prise[1:])
|
||||
cprint(u' prise %s (chambre %s)' % (prise, chambre), 'gras')
|
||||
|
||||
for mac, erreur_mac in erreur_prise['macs'].items():
|
||||
if erreur_mac['nombre'] >= min_mac:
|
||||
ligne = erreur_mac['lignes'][-1].strip().split()
|
||||
ligne[5] = '%8s' % ligne[5].replace("adm.crans.org-", "")
|
||||
ligne = ligne[0:3] + ligne[5:]
|
||||
print ' %s (x%d)' % (' '.join(ligne), erreur_mac['nombre'])
|
||||
info = erreur_mac['info']
|
||||
if info != None:
|
||||
prop = info['prop']
|
||||
cprint(u' -> machine de : %-16s %5s (prise %s) <%s>' %
|
||||
(prop.Nom()[:16], info['chambre'], info['prise'], prop.email()))
|
||||
|
||||
|
||||
def __usage(err=''):
|
||||
""" Message d'erreur """
|
||||
|
||||
if err : cprint(err, 'rouge')
|
||||
cprint(u"Tapez %s -h pour plus d'informations" % sys.argv[0].split('/')[-1].split('.')[0])
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def __param_entier(opt, val):
|
||||
""" Récupère un entier passé en ligne de commande """
|
||||
|
||||
try:
|
||||
return int(val)
|
||||
except:
|
||||
__usage(u'La valeur du paramètre %s est incorecte (doit être un entier positif)' % opt)
|
||||
|
||||
|
||||
def __aide():
|
||||
""" Aide """
|
||||
|
||||
cprint(u"""Usage: %s [OPTIONS]
|
||||
Parse les logs d'authentifications et affiche les erreurs trouvées (macs inconnues ou connexion d'une machine sur une prise autre que celle du propriétaire).
|
||||
|
||||
Options:
|
||||
-h, --help affiche cette aide
|
||||
-l, --log <fichier> fichier de log à parser (par défaut: /var/log/freeradius/radius_auth.log)
|
||||
-m, --min-mac <nombre> nombre minimal d'occurrences d'une mac sur une prise pour qu'elle soit reportée
|
||||
-n, --min-prise <nombre> nombre minimal d'erreurs sur une prise pour qu'elle soit reportée
|
||||
-i, --inconnue n'affiche que les erreurs de mac inconnues
|
||||
-p, --prise n'affiche que les connexions sur une prise autre que celle du propriétaire
|
||||
-d, --date <date> ignorer totalement les lignes avant cette date
|
||||
|
||||
<date> peut être:
|
||||
- une date absolue sous la forme AAAAMMJJ
|
||||
- une date relative à aujourd'hui sous la forme +<nombre de jour>
|
||||
Par exemple pour ne considérer que les deux dernier jours: +2
|
||||
|
||||
Rapporter toutes anomalies à <dimino@crans.org>.""" % sys.argv[0].split('/')[-1].split('.')[0])
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
fichier_log = '/var/log/freeradius/radius_auth.log'
|
||||
min_mac = 1
|
||||
min_prise = 1
|
||||
# Info que l'on veut afficher
|
||||
aff_inconnue = True
|
||||
aff_prise = True
|
||||
# Date de départ
|
||||
jour = None
|
||||
|
||||
import getopt, time
|
||||
|
||||
try:
|
||||
options, arg = getopt.getopt(sys.argv[1:], 'hipl:m:n:d:', [ 'help', 'log=', 'min-mac=', 'min-prise=', 'inconnue', 'prise', 'date='])
|
||||
except getopt.error, msg:
|
||||
__usage(unicode(msg))
|
||||
|
||||
for opt, val in options:
|
||||
if opt in [ '-h', '--help' ]:
|
||||
__aide()
|
||||
elif opt in [ '-l', '--log-file' ]:
|
||||
fichier_log = val
|
||||
elif opt in [ '-m', '--min-mac' ]:
|
||||
min_mac = __param_entier(opt, val)
|
||||
elif opt in [ '-n', '--min-prise' ]:
|
||||
min_prise = __param_entier(opt, val)
|
||||
elif opt in [ '-i', '--inconnue' ]:
|
||||
aff_prise = 0
|
||||
elif opt in [ '-p', '--prise' ]:
|
||||
aff_inconnue = 0
|
||||
elif opt in [ '-d', '--date' ]:
|
||||
if val[0] == '+':
|
||||
jour = aujourdhui - datetime.timedelta(__param_entier(opt, val[1:]))
|
||||
else:
|
||||
try:
|
||||
jour = datetime.date(*(time.strptime(val, "%Y%m%d")[0:3]))
|
||||
except:
|
||||
__usage(u'La valeur du paramètre %s est incorecte (doit être de la forme AAAAMMJJ)' % opt)
|
||||
else:
|
||||
cprint(u'Option inconnue: %s' % opt, 'rouge')
|
||||
__usage()
|
||||
|
||||
base = crans_ldap()
|
||||
|
||||
errs_inconnue, errs_prise = parse_auth_log(fichier_log, jour)
|
||||
|
||||
if aff_inconnue:
|
||||
affiche_erreurs(errs_inconnue, u"Pour les connexions suivantes la mac n'a pas été trouvée dans la base:", min_mac, min_prise)
|
||||
|
||||
if aff_prise:
|
||||
affiche_erreurs(errs_prise, u"Les connexions suivantes ont eu lieu sur une prise autre que celle du proriétaire:", min_mac, min_prise)
|
Loading…
Add table
Add a link
Reference in a new issue