From c068df708dff601bd8114a33760df1ae589eabc5 Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Sun, 21 Jul 2013 20:08:53 +0200 Subject: [PATCH] [arpwatch] ajout d'un serveur d'envoi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Celui-ci écoute au travers d'une socket unix. Il permet ainsi de garder une connexion ldap (et pg et cie) en permanence ouverte. Ce qui accélère le traitement des rapports de arpwatch. Pour utiliser ce server, report.py (ancien arpwatch_sendmail.py) tente de se connecter à cette socket et envoie le mail lui-même en cas d'échec. On rajoute également un initscript pour arpwatch_sendmail_server. Il n'est pas parfait et si vous aimez pas, feel free to edit. --- surveillance/arpwatch/common.py | 6 ++ surveillance/arpwatch/initscript | 49 ++++++++++++ surveillance/arpwatch/report.py | 96 ++++++++++++++++++++++++ surveillance/arpwatch/sendmail_server.py | 75 ++++++++++++++++++ surveillance/arpwatch_sendmail | 4 - surveillance/arpwatch_sendmail.py | 64 ---------------- 6 files changed, 226 insertions(+), 68 deletions(-) create mode 100644 surveillance/arpwatch/common.py create mode 100755 surveillance/arpwatch/initscript create mode 100755 surveillance/arpwatch/report.py create mode 100755 surveillance/arpwatch/sendmail_server.py delete mode 100755 surveillance/arpwatch_sendmail delete mode 100755 surveillance/arpwatch_sendmail.py diff --git a/surveillance/arpwatch/common.py b/surveillance/arpwatch/common.py new file mode 100644 index 00000000..7b514fd7 --- /dev/null +++ b/surveillance/arpwatch/common.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +#-*- coding: utf-8 -*- +PIDFILE = '/var/run/arpwatch_sendmail.pid' +USER='arpwatch' +GROUP='adm' +SOCKET_FILE='/var/run/arpwatch_sendmail.sock' diff --git a/surveillance/arpwatch/initscript b/surveillance/arpwatch/initscript new file mode 100755 index 00000000..46498e1c --- /dev/null +++ b/surveillance/arpwatch/initscript @@ -0,0 +1,49 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: arpwatch_sendmail +# Required-Start: $remote_fs +# Required-Stop: $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: arpwatch_sendmail +# Description: Report mail d'arpwatch +### END INIT INFO + +NAME=arpwatch_sendmail +PIDF=/var/run/$NAME.pid +PATH=/usr/scripts/surveillance/arpwatch +ARGS=root@crans.org +BIN="/usr/bin/python $PATH/sendmail_server.py" +DESCR="Script d'envoi de mails arpwatch" + +. /lib/lsb/init-functions + +set -e + +case "$1" in + start) + echo -n "Démarrage de $NAME" + /sbin/start-stop-daemon --start --quiet --pidfile $PIDF --exec $BIN $ARGS + echo "." + ;; + stop) + echo -n "Arrêt de $NAME" + /sbin/start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $PIDF + echo "." + ;; + + restart) + echo -n "Redémarrage (arrêt) de $NAME" + /sbin/start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $PIDF + /bin/sleep 1 + echo -n "Redémarrage (démarrage) de $NAME" + /sbin/start-stop-daemon --start --quiet --pidfile $PIDF --exec $BIN $ARGS + echo "." + ;; + + *) + echo "Usage: /etc/init.d/$NAME {start|stop|restart}" + exit 1 +esac + +exit 0 diff --git a/surveillance/arpwatch/report.py b/surveillance/arpwatch/report.py new file mode 100755 index 00000000..06451e07 --- /dev/null +++ b/surveillance/arpwatch/report.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# -*- 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 +# Licence : GPLv2 + +from __future__ import print_function + +import sys, os, re +import socket + +import common + +sys.path.append('/usr/scripts') +from gestion.tools.locate_mac import trace_machine, format_mac, info_machine +from gestion.config import NETs +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}') +arpwatched_net = NETs['all'] + NETs['adm'] + NETs['accueil'] + NETs['isolement'] + NETs['personnel-ens'] + NETs['evenementiel'] + +def get_machine(unformated_mac): + """Renvoie les informations sur la machine à partir de sa mac""" + 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 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 + if u'flip flop' in subject and ip is not None and AddrInNets(ip, arpwatched_net): + try: + macs = find_mac.findall(texte) + for mac in macs: + textes.append(get_machine(mac)) + except: + # 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: + 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) diff --git a/surveillance/arpwatch/sendmail_server.py b/surveillance/arpwatch/sendmail_server.py new file mode 100755 index 00000000..db94e269 --- /dev/null +++ b/surveillance/arpwatch/sendmail_server.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Serveur sendmail d'arpwatch. +Permet de ne charger qu'une fois le module report (et donc lc_ldap +et cie) +Ceci écoute sur une socket unix les rapports arpwatch (via report.py) +""" + +from __future__ import print_function +import SocketServer +import sys, os, pwd, grp +import common + +class Handler(SocketServer.StreamRequestHandler): + """Gestion d'une requête""" + def handle(self): + self.server.reporter.report(self.request.recv(1024)) + +class MyServer(SocketServer.UnixStreamServer): + """Un server, avec une instance du module report""" + reporter = None + +def run(): + """Serveur permanent""" + import report + + try: + os.remove(common.SOCKET_FILE) + print("Removed old socket", file=sys.stderr) + except OSError: + pass + + # La socket ne doit pas être accessible pour tous: on garde adm + os.umask(007) + os.setgid(grp.getgrnam(common.GROUP).gr_gid) + + serv = SocketServer.UnixStreamServer(common.SOCKET_FILE, Handler) + serv.reporter = report + + serv.serve_forever() + +# fork en arrière plan + pidfile +if __name__ == "__main__": + # do the UNIX double-fork magic, see Stevens' "Advanced + # Programming in the UNIX Environment" for details (ISBN 0201563177) + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError, e: + print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror) + sys.exit(1) + + # decouple from parent environment + os.chdir("/") #don't prevent unmounting.... + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent, print eventual PID before + #print "Daemon PID %d" % pid + open(common.PIDFILE, 'w').write("%d" % pid) + sys.exit(0) + except OSError, e: + print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror) + sys.exit(1) + + # start the daemon main loop + run() + diff --git a/surveillance/arpwatch_sendmail b/surveillance/arpwatch_sendmail deleted file mode 100755 index 352ed405..00000000 --- a/surveillance/arpwatch_sendmail +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -#execution avec des droits suffisants -LANG=fr_FR.UTF-8 sudo -u 'arpwatch' /usr/scripts/surveillance/arpwatch_sendmail.py $* diff --git a/surveillance/arpwatch_sendmail.py b/surveillance/arpwatch_sendmail.py deleted file mode 100755 index cb7f7ca0..00000000 --- a/surveillance/arpwatch_sendmail.py +++ /dev/null @@ -1,64 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -# Ajout d'un whos et d'un tracage aux mails d'arpwatch -# Auteurs : Stéphane Glondu, Cyril Cohen -# Licence : GPLv2 - -import sys, os, re, smtplib -from commands import getstatusoutput - -sys.path.append('/usr/scripts/gestion') -sys.path.append('/usr/scripts/gestion/tools') -from locate_mac import trace_machine, format_mac, info_machine -from config import NETs -from iptools import AddrInNets - -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}') -arpwatched_net = NETs['all'] + NETs['adm'] + NETs['accueil'] + NETs['isolement'] + NETs['personnel-ens'] + NETs['evenementiel'] - - -def get_machine(unformated_mac): - 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 - return None - -if __name__ == "__main__": - texte = sys.stdin.read() #.decode('ISO-8859-15') - textes = texte.splitlines(True) - i = textes.index(u'\n') - subject = get_subject(textes[:i]) - textes[i-1:i-1] = [ - u'MIME-Version: 1.0\n', - u'Content-Type: text/plain; charset=UTF-8\n', - u'Content-Transfer-Encoding: 8bit\n', - ] - - # On récupère les destinataires dans les arguments (très ad hoc) - recipients = sys.argv[2].split(',') - - try : ip = set(find_ip.findall(texte)).pop() - except KeyError: ip = None - # On complète le message - if 'flip flop' in subject and ip is not None and AddrInNets(ip, arpwatched_net): - try: - macs = find_mac.findall(texte) - for mac in macs: - textes.append(get_machine(mac)) - except: - # 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))) - textes.append('\n-- \narpwatch_sendmail.py\n') - - smtp = smtplib.SMTP() - smtp.connect() - smtp.sendmail("arpwatch@crans.org", recipients, u''.join(textes).encode('UTF-8')) - smtp.quit()