135 lines
4.9 KiB
Python
Executable file
135 lines
4.9 KiB
Python
Executable file
#!/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)
|