#! /usr/bin/env python # -*- coding: iso-8859-15 -*- """ Script de déconnection automatique des machines du crans pour les raisons : - upload - p2p - flood - virus Copyright (C) Xavier Pessoles, Étienne Chové, Vincent Bernat Licence : GPL v2 """ ########################### # Import des commmandes : # ########################### import commands import sys from pyPgSQL import PgSQL sys.path.append('/usr/scripts/gestion') from config import upload, virus, p2p, NETs import smtplib from ldap_crans import crans_ldap, crans, invite, base_classes_crans from time import * # Quelques fonctions #################### def machine_online(machine) : """ Retourne True si la machine est connectée au réseau et False si elle ne l'est pas """ # les wifi sont toujours online if machine.ipsec() : return True # arping pour les fixes return not commands.getstatusoutput('/usr/sbin/arping -c 3 %s' % machine.mac())[0] # Variables utiles ################## # création d'une chaine qui ressemble à : (ip_src<<=inet('138.231.136.0/21') or ip_src<<=inet('138.231.148.0/22')) ip_src_in_crans = '(%s)' % ' or '.join([ "ip_src<<=inet('%s')" % net for net in NETs['all'] ]) # Connections : ############### # pgsql pgsql = PgSQL.connect(host='/var/run/postgresql', database='filtrage', user='crans') pgsql.autocommit = True curseur = pgsql.cursor() # smtp mail = smtplib.SMTP('localhost') # ldap ldap = crans_ldap() ########################################### # Vérification de l'upload et du download # ########################################### requete_template = "SELECT ip_crans,round(sum(upload)/1024/1024) AS total FROM %(table)s WHERE upload>download AND date>timestamp 'now' - interval '23 hours 50 minutes' AND date<'now' AND NOT EXISTS ( SELECT 1 FROM exemptes WHERE %(table)s.ip_crans<<=exemptes.ip_crans and %(table)s.ip_ext<<=exemptes.ip_dest) GROUP BY ip_crans ORDER BY total desc" # table générale d'upload requete = requete_template % {'table':'upload'} curseur.execute(requete) uploadeurs_general = curseur.fetchall() # table temporaire d'upload requete = requete_template % {'table':'dump'} curseur.execute(requete) uploadeurs_dump = curseur.fetchall() # concaténation des résultats uploadeurs = {} for i in uploadeurs_general + uploadeurs_dump: if i[0] in uploadeurs: uploadeurs[i[0]]+=float(i[1]) else: uploadeurs[i[0]]=float(i[1]) uploadeurs=uploadeurs.items() # Table des avertis ################### # Avertis hard requete = "SELECT ip_crans FROM avertis_upload where hard='1' and date>timestamp 'now' - interval '1 day'" curseur.execute(requete) avertish = [ x[0] for x in curseur.fetchall() ] # Avertis soft requete = "SELECT ip_crans FROM avertis_upload where soft='1' " curseur.execute(requete) avertiss = [ x[0] for x in curseur.fetchall() ] # Vérification : ################ for IP, elupload in uploadeurs: # On regarde si c'est de l'upload hard if elupload >= upload.hard : # L'adhérent a t il été averti ? ################################ if str(IP) in avertish: continue # Objets LDAP ############# machine = ldap.search('ipHostNumber=%s' % IP,'w')['machine'] if not machine: print 'IP non trouvée (%s)'%IP continue machine=machine[0] hostname = machine.nom() proprio = machine.proprietaire() mid = machine.id() # On évite de blacklister les proprios spéciaux ############################################### if proprio.__class__ == crans or proprio.__class__ == invite: continue # On inscrit l'ip dans la table des avertis ########################################### inscription = "INSERT INTO avertis_upload (ip_crans,date,hard) VALUES ('%s','now','1')" % IP curseur.execute(inscription) # On sanctionne ############### # nb_decos = len([ x for x in e.blacklist() if mktime(strptime(x.split(',')[0],'%d/%m/%Y %H:%M')) > mktime(localtime())-30*24*60*60 and x.split(',')[2]=='autodisc' ]) # if nbdec >= 2: # pass #bl=proprio.blacklist() #for x in bl: # if mktime(strptime(x.split(',')[0],'%d/%m/%Y %H:%M')) > mktime(localtime())-30*24*60*60 and x.split(',')[2]=='autodisc' : # liste+=x # message="""From: %(From)s # To: %(To)s # Subject: Upload Massif # # La machine a dépassé trois fois les limites d'upload durant le dernier mois : # %(liste)s # -- # deconnexion.py # """ # corps = message %{'From':upload.expediteur, # 'To':upload.expediteur, # 'liste':liste} debut = localtime(time()) fin = localtime(time()+60*60*24) proprio.blacklist(["%d/%d/%d %d:%d" % (debut[2],debut[1],debut[0],debut[3],debut[4]),"%d/%d/%d %d:%d" % (fin[2],fin[1],fin[0],fin[3],fin[4]),'autodisc',"Déconn auto. %s Mo" % elupload]) proprio.save() # On envoie un mail a l'adhérent ################################ proprio = proprio.mail() if '@' not in proprio : proprio += '@crans.org' corps = upload.hardmessage % { 'From': upload.expediteur, 'To': proprio, 'upload': elupload, 'hostname': hostname } corps = corps.encode('iso 8859-15') mail.sendmail(upload.expediteur,proprio,corps) # On envoie un mail à disconnect ################################ if upload.disconnect_mail_hard : corps = upload.deconnexion % { 'From': upload.expediteur, 'To': upload.expediteur, 'upload': elupload, 'hostname': hostname } corps = corps.encode('iso 8859-15') mail.sendmail(upload.expediteur,upload.expediteur,corps) # Est ce de l'upload soft ? elif elupload >= upload.soft : # L'adhérent a-t-il déjà été averti ? ##################################### if str(IP) in avertiss: continue # On récupère les informations de LDAP ###################################### machine = ldap.search('ipHostNumber=%s' % IP,'w')['machine'] if not machine: print 'IP non trouvée (%s)'%IP continue machine=machine[0] hostname = machine.nom() proprio = machine.proprietaire().mail() # On envoie un mail à l'adhérent ################################ if '@' not in proprio : proprio += '@crans.org' corps = upload.softmessage % { 'From': upload.expediteur, 'To': proprio, 'upload': elupload, 'hostname':hostname } corps = corps.encode('iso 8859-15') mail.sendmail(upload.expediteur,proprio,corps) # On envoie un mail à disconnect ################################ if upload.disconnect_mail_soft: corps = upload.avertissement % { 'From': upload.expediteur, 'To': upload.expediteur, 'upload': elupload, 'hostname': hostname} corps = corps.encode('iso 8859-15') mail.sendmail(upload.expediteur,upload.expediteur,corps) # On inscrit l'ip dans la table des avertis ########################################### inscription = "INSERT INTO avertis_upload (ip_crans,date,soft) VALUES ('%s','now','1')" % IP print inscription curseur.execute(inscription) # Changement de statut des uploders (Ainsi, les gens ne recoivent des mails qu'une fois et on a en memoire les uploads) requete="UPDATE avertis_upload set hard='f' where hard='t' and date < timestamp ' now' - interval '1 day'" curseur.execute(requete) requete="UPDATE avertis_upload set soft='f' where soft='t' and date < timestamp 'now' - interval '1 day'" curseur.execute(requete) ############################################### # Détection de l'existence de virus ou de P2P # ############################################### # VIRUS ######## # Dans le table virus on sélectionne les ip_src qui appartiennent au reseau requete = "SELECT ip_src,count(ip_src) FROM virus WHERE %s and date > timestamp 'now' - interval '1 hour' group by ip_src" % ip_src_in_crans curseur.execute(requete) veroles = curseur.fetchall() # Recuperation des infectes pour ne pas les reblacklister requete = "SELECT ip_crans FROM avertis_virus" curseur.execute(requete) infectes = curseur.fetchall() for ip, nombre in veroles: # si le type dépasse le seuil, on le blacklist if nombre < virus.virus or [ip] in infectes : continue # lecture des infos de ldap machine = ldap.search('ipHostNumber=%s' % ip,'w' )['machine'][0] hostname = machine.nom() proprio = machine.proprietaire() blacklist = proprio.blacklist() # Inscription dans la table des infectes requete="INSERT INTO avertis_virus (ip_crans,date) VALUES ('%s','now')" % ip curseur.execute(requete) print "Deconnexion virus : %s" % hostname # on récupère les index des lignes de bl ou il y a marqué virus index = [blacklist.index(x) for x in blacklist if 'virus' in x ] if index : proprio.blacklist(( index[0] , ['now','-','virus',hostname] )) proprio.save() else : proprio.blacklist(['now','-','virus',hostname]) proprio.save() # Flood ######## # Dans le table virus on sélectionne les ip_src qui appartiennent au reseau requete = "SELECT ip_src,count(ip_src) FROM flood WHERE %s and date > timestamp 'now' - interval '1 hour' GROUP BY ip_src" % ip_src_in_crans curseur.execute(requete) veroles = curseur.fetchall() # Recuperation des infectes pour ne pas les reblacklister requete = "SELECT ip_crans FROM avertis_virus" curseur.execute(requete) infectes = curseur.fetchall() for ip, nombre in veroles: # si le type dépasse le seuil, on le blacklist if nombre < virus.flood or [ip] in infectes : continue # lecture des infos de ldap machine = ldap.search('ipHostNumber=%s' % ip,'w' )['machine'][0] hostname = machine.nom() proprio = machine.proprietaire() blacklist = proprio.blacklist() # Inscription dans la table des infectes requete="INSERT INTO avertis_virus (ip_crans,date) VALUES ('%s','now')" % ip curseur.execute(requete) print "Deconnexion flood : %s" % hostname # on récupère les index des lignes de bl ou il y a marqué virus index = [blacklist.index(x) for x in blacklist if 'virus' in x ] if index : proprio.blacklist(( index[0] , ['now','-','virus',hostname] )) proprio.save() else : proprio.blacklist(['now','-','virus',hostname]) proprio.save() # Reconnexion si le virus/flood a disparu ######################################### # Dans la table avertis_virus on récupère la liste des infectés requete = "SELECT ip_crans FROM avertis_virus where date < timestamp 'now' - interval '1 hour'" curseur.execute(requete) infectes = [ x[0] for x in curseur.fetchall() ] for IP in infectes : # Nombre de requets de virus requete1="SELECT COUNT(ip_src) FROM virus where ip_src='%s' and date > timestamp 'now' - interval '1 hour'" % IP curseur.execute(requete1) nb_virus = curseur.fetchall() # Nombre de requetes de flood requete2="SELECT COUNT(ip_src) FROM flood where ip_src='%s' and date > timestamp 'now' - interval '1 hour'" % IP curseur.execute(requete2) nb_flood = curseur.fetchall() # On traite que les IP qui sont descendues en dessous des seuils if nb_virus[0][0] < virus.virus and nb_flood[0][0] < virus.flood : machine = ldap.search('ipHostNumber=%s' % IP,'w' )['machine'][0] # si la machine est pas online, on reconnecte #if machine_online(machine) : proprio = machine.proprietaire() bl = proprio.blacklist() hostname = machine.nom() # On stoppe la sanction pour une ligne existante de la blackliste # En prenant en compte le fait que d'autres lignes de blackliste # ont pu s'ajouter. for ligne in bl: if ',-,virus,%s'%hostname in ligne: liste=ligne.split(',') argument=[liste[0],'now',liste[2],liste[3]] print argument,IP index = bl.index(ligne) proprio.blacklist((index,argument)) proprio.save() requete="DELETE FROM avertis_virus where ip_crans='%s'"%IP curseur.execute(requete) print "Reconnexion %s" %hostname # Gestion du P2P : ################## # Dans le table p2p on sélectionne les ip_src qui appartiennent au reseau requete = "SELECT ip_src,id_p2p,count(ip_src) FROM p2p WHERE %s AND date > timestamp 'now' - interval '1 day' GROUP BY ip_src,id_p2p ORDER BY ip_src" % ip_src_in_crans curseur.execute(requete) fraudeurs = curseur.fetchall() # Recuperation des infectes pour ne pas les reblacklister requete = "SELECT ip_crans,protocole FROM avertis_p2p WHERE date > timestamp 'now' - interval '1 day'" curseur.execute(requete) avertisp2p = curseur.fetchall() for ip, id_p2p, nombre in fraudeurs : # On récupére le protocole de p2p : requete="SELECT nom FROM protocole_p2p WHERE id_p2p=%d" % id_p2p curseur.execute(requete) protocole = curseur.fetchall()[0][0] # On ne prend pas en compte s'il est sous le seuil admis, ou s'il est averti if nombre <= p2p.tag or [ip, protocole] in avertisp2p : continue # Recuperation des ref de la machine machine = ldap.search('ipHostNumber=%s' % ip,'w' )['machine'][0] hostname = machine.nom() # proprio = machine.proprietaire() # Envoie du mail à disconnect if p2p.disconnect_mail : corps = p2p.avertissement % { 'From': upload.expediteur, 'To': upload.expediteur, 'protocole': protocole, 'hostname':hostname} corps = corps.encode('iso 8859-15') mail.sendmail(upload.expediteur,upload.expediteur,corps) # Inscription dans la base des avertis requete="INSERT INTO avertis_p2p (ip_crans,date,protocole) VALUES ('%s','now','%s')" % (ip, protocole) curseur.execute(requete) # Blacklistage # date = time() # debut = localtime(date) # 7 jours # fin = localtime(date+60*60*24*7) # proprio.blacklist(["%d/%d/%d %d:%d" % (debut[2],debut[1],debut[0],debut[3],debut[4]),"%d/%d/%d %d:%d" % (fin[2],fin[1],fin[0],fin[3],fin[4]),'p2p',"P2P (auto)" % protocole]) # proprio.save() # fermeture des connections mail.quit()