[arpwatch] ajout d'un serveur d'envoi

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.
This commit is contained in:
Daniel STAN 2013-07-21 20:08:53 +02:00
parent 37c5a6836e
commit c068df708d
6 changed files with 226 additions and 68 deletions

View file

@ -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'

View file

@ -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

96
surveillance/arpwatch/report.py Executable file
View file

@ -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)

View file

@ -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()

View file

@ -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 $*

View file

@ -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()