#!/bin/bash /usr/scripts/python.sh # -*- coding: utf-8 -*- # Ajout d'un whos et d'un tracage aux mails d'arpwatch # Auteurs : Stéphane Glondu, Cyril Cohen, Daniel STAN, Valentin Samir, Vincent Le Gallic # Licence : GPLv2 from __future__ import print_function import sys, os, re import socket import common # Keep away from here les modules qui font des connexions ldap sans le dire (!) import gestion.config from gestion.iptools import AddrInNets from utils.sendmail import sendmail # On récupère les destinataires dans les arguments (très ad hoc) recipients = [ mail for mail in sys.argv[1:] if '@' in mail ] if not recipients: print("Pas de mail fourni: affichage sur stdout.", file=sys.stderr) find_mac = re.compile(r'[0-9A-Fa-f]{1,2}(?::[0-9A-Fa-f]{1,2}){5}') find_ip = re.compile(r'[0-9]{1,3}(?:\.[0-9]{1,3}){3}') #: Regexp pour matcher l'interface. #: Ne matche pas toutes les interface (si un jour eth1 poppe), #: Mais de toutes façons on ne drope que des interfaces qu'on a réussi à identifier find_iface = re.compile(r'eth0(?:\.[0-9]+)?') arpwatched_nets = sum([gestion.config.NETs[nom] for nom in ['all', 'adm', 'accueil', 'isolement', 'personnel-ens', 'evenementiel']], []) #: VLANS dont on ignore les "new station"/"new activity" *si elles ont une ip correspondant au vlan* ignored_vlans = ['accueil', 'wifi'] def get_machine(unformated_mac): """Renvoie les informations sur la machine à partir de sa mac""" # Ceci est importé ici car cela génère une connexion ldap (sic) from gestion.tools.locate_mac import trace_machine, format_mac, info_machine mac = format_mac(unformated_mac) return u"\n" + info_machine(mac) + u"\n" + trace_machine(mac) def get_subject(headers_list): for line in headers_list: if line.lower().startswith('subject:'): return line[9:].strip() return None def drop_report(subject, ip): """Détermine à partir du ``subject`` du mail si il n'est pas nécessaire d'envoyer une notification pour cet évènement. Renvoie ``True`` si il faut le dropper. """ # On récupère l'interface et l'ip dans le sujet ifaces = find_iface.findall(subject) iface = ifaces[0] if ifaces else None if not iface is None: # On détermine le vlan vlans = re.findall(r"\.([^\.]*)$", iface) try: vlan = int(vlans[0]) if vlans else None except ValueError: vlan = None ips = find_ip.findall(subject) ip = ips[0] if ips else None #print("%r, %r" % (ip, iface)) #print(arpwatched_nets) if u"new station" in subject or "new activity" in subject: if not vlan is None: vlannames = [k for (k,v) in gestion.config.vlans.iteritems() if v == vlan] #print("vlannames : %r" % vlannames) if vlannames and vlannames[0] in ignored_vlans: #print("%r in %r ?" % (ip, gestion.config.NETs[vlannames[0]])) if AddrInNets(ip, gestion.config.NETs[vlannames[0]]): # On ignore les new station dont l'IP est sur le bon vlan return True return False def report(texte, fallback=False): """Envoi d'un rapport""" textes = texte.splitlines(True) try: i = textes.index('\n') except ValueError: print("wrong formatted mail", file=sys.stderr) return headers = textes[:i] textes = textes[(i+1):] # Extrait le sujet subject = get_subject(headers) # Cherche les IP dans le corps try: ip = set(find_ip.findall(texte)).pop() except KeyError: ip = None # On complète le message seulement en cas de flip flop if u'flip flop' in subject and ip is not None and AddrInNets(ip, arpwatched_nets): try: macs = find_mac.findall(texte) for mac in macs: textes.append(get_machine(mac)) except Exception as exc: # En cas d'exception, on envoie le traceback import traceback textes.append(u'\n') textes.append(u''.join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))) if fallback: textes.append(u'\nAttention, arpwatch/sendmail_server.py inactif !\n') textes.append(u'\n-- \narpwatch_sendmail.py\n') out = ''.join(textes) if recipients and not drop_report(subject, ip): sendmail(u"arpwatch@crans.org", recipients, subject, out, more_headers = { 'X-Mailer': __file__, }) else: print(out) if __name__ == '__main__': data = sys.stdin.read() # On essaye d'envoyer le mail à la socket de sendmail_server # et au pire, on envoie nous-même le mail try: s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect(common.SOCKET_FILE) s.send(data) s.close() except socket.error: report(data, fallback=True)