Files
bin/node-collector-mail-synth-transaction

183 lines
7.7 KiB
Python
Executable File

#!/usr/bin/python3
import smtplib, imaplib
import random
import logging
from datetime import datetime
import os, time, shutil
import re
import subprocess
def days_between(d1, d2):
return abs((d2 - d1).days)
def need_to_rotate_log(logfile):
# the log file contains the date per line, so get first line of log, it is
# when the log started
try:
file_obj = open(logfile, 'r')
except:
return 0
Lines = file_obj.readlines()
first_line = Lines[0]
file_obj.close()
# get just the data to convert
m = re.search('(\d+:\d+:\d+ \d+-\d+-\d+) .*', first_line)
start_of_file = datetime. strptime(m.group(1), '%H:%M:%S %d-%m-%y')
now = datetime.now()
log_file_age_days = days_between(now, start_of_file )
return log_file_age_days > 7
# function that sends a message using smtp to port 467
def SendMessage( server, username, password, nonce, f ):
msg='To: {} \r\nFrom: {}\r\nSubject: Test from python\r\nDate: \r\nTEST {}'.format( username, username, str(nonce))
# connect to mail server
try:
logging.info( "about to smtp_sll" );
s = smtplib.SMTP_SSL(host=server)
conn_time = time.time_ns();
logging.info( "about to login" );
s.login(username,password)
login_time = time.time_ns();
logging.info( "about to send" );
s.sendmail( username, username, msg)
send_time = time.time_ns();
logging.info( "sent" );
except smtplib.SMTPConnectError as e:
logging.error( "Failed to connect to {} ({})".format(server, e.strerror))
print('node_mail_synth_trans{what="mail_working"} 0', file=f )
except smtplib.SMTPAuthenticationError as e:
logging.error( "Failed to autheticate to {} with user: {} ({})".format(server, username, e.strerror))
print('node_mail_synth_trans{what="mail_working"} 0', file=f )
except smtplib.SMTPRecipientsRefused as e:
logging.error( "Recipient failed {} : ({})".format(username, e.strerror))
print('node_mail_synth_trans{what="mail_working"} 0', file=f )
except smtplib.SMTPException as e:
logging.error( "Generic SMTP exception, not one I cared about ({})".format(e.strerror))
print('node_mail_synth_trans{what="mail_working"} 0', file=f )
finally:
if s:
s.quit()
quit_time = time.time_ns();
logging.info("Successfully sent email: %d" % nonce )
smtp_elapsed=(quit_time-start_time)/1000000
logging.info("Time to send (ms): "+ str(smtp_elapsed))
print('node_mail_synth_trans{what="smtp_time_in_ms"} %d' % smtp_elapsed, file=f)
print('node_mail_synth_trans{what="smtp_working"} 1', file=f )
# function that searches for message just sent in imap, and deletes it
def ReceiveMessage( server, username, password, nonce, start_time, f ):
try:
imap_start_time = time.time_ns();
conn = imaplib.IMAP4_SSL(server)
imap_conn_time = time.time_ns();
except imaplib.IMAP4.error as e:
logging.error( "IMAP: Failed to connect ({})".format(e.strerror))
print('node_mail_synth_trans{what="mail_working"} 0', file=f )
try:
conn.login(username,password)
imap_login_time = time.time_ns();
except imaplib.IMAP4.error as e:
logging.error( "IMAP: failed to login ({})".format(e.strerror))
print('node_mail_synth_trans{what="mail_working"} 0', file=f )
try:
conn.select('Inbox')
search_str="TEST " + str(nonce)
typ, data = conn.search(None,'(BODY "' + search_str + '")' )
imap_search_time = time.time_ns();
if data[0] == b'':
raise Exception("search returned no data")
for num in data[0].split():
typ, data = conn.fetch(num,'(RFC822)')
conn.store(num, '+FLAGS', '\\Deleted')
imap_retr_time = time.time_ns();
conn.expunge()
imap_del_time = time.time_ns();
except imaplib.IMAP4.error as e:
logging.error( "IMAP: Could not find email {}".format(e.strerror))
print('node_mail_synth_trans{what="mail_working"} 0', file=f )
finally:
if conn:
conn.close()
imap_close_time = time.time_ns();
# adding a 1 second sleep because my logs say it is deleting emails, but the emails are still in my folder, and also deleting ANY TEST emails, so if a subsequent run
# works, it gets rid of the old ones that did fail for some reason
time.sleep(1)
conn.select('Inbox')
search_str="TEST "
typ, data = conn.search(None,'(BODY "' + search_str + '")' )
# if there is still data, something didn't delete as expected, so
# just clean it up
if data[0] != b'':
for num in data[0].split():
typ, data = conn.fetch(num,'(RFC822)')
conn.store(num, '+FLAGS', '\\Deleted')
conn.expunge()
conn.logout()
imap_elapsed=(imap_close_time-imap_start_time)/1000000
total_elapsed=(imap_close_time-start_time)/1000000 - 5000
logging.info("time to recv email: %d" % imap_elapsed )
logging.info("Successfully found and deleted email: %d" % nonce)
print('node_mail_synth_trans{what="imap_time_in_ms"} %d' % imap_elapsed, file=f)
print('node_mail_synth_trans{what="total_time_in_ms"} %d' % total_elapsed, file=f)
print('node_mail_synth_trans{what="imap_working"} 1', file=f )
print('node_mail_synth_trans{what="mail_working"} 1', file=f )
def Fail2BanCount():
try:
o = subprocess.run(["sudo", "/bin/docker", "exec", "mail", "fail2ban"], stdout=subprocess.PIPE, text=True )
logging.info( f"Fail2ban returned: '{str(o.stdout.strip())}' ")
out=str(o.stdout.strip()).split('\n')[1]
ip_addresses = re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', out)
unique_ip_count = len(set(ip_addresses))
print( 'node_mail_synth_trans{what="fail2ban_cnt"} ' + str(unique_ip_count), file=f )
except Exception as e:
logging.error( f"Fail2ban: failed to get output for fail2ban ({e})" )
print('node_mail_synth_trans{what="fail2ban_cnt"} 0', file=f )
# login using travel as its never really used (if I do anything dumb, it should
# have no real impact on mail that matters)
server='mail.depaoli.id.au'
username='travel@depaoli.id.au'
password='%fId0bPIE0Y2JZTzODJ%#p9V'
# set up unique message to send
nonce = random.randint(1,99999999)
# open file for writing prometheus formatted data into
f = open('/srv/docker/container/node-exporter/textfile_collector/mail.prom', 'w')
# put required help/type text in
print('# HELP node_mail_synth_trans details of last synthetic mail transaction (full send/recv of an email)', file=f)
print('# TYPE node_mail_synth_trans gauge', file=f )
# check for time, if its been a week or so, rotate the log
logfile='/var/tmp/mail-tester.log'
if( need_to_rotate_log(logfile) ):
print( 'Rotating log file, its a week old (deletes file from 2 weeks ago)' )
shutil.move( logfile, logfile + '.1' )
# also set up an actual log file while I am learning python / testing this out
logging.basicConfig(filename=logfile, format='%(asctime)s - %(message)s', datefmt='%H:%M:%S %d-%m-%y', level=logging.INFO)
# all the time_ns() calls are just timestamps to compare back to this one for
# relative time elapsed for operations, good for graphing in prometheus
start_time = time.time_ns();
logging.info("start up for: %d" % nonce );
SendMessage( server, username, password, nonce, f )
logging.info("waiting 5 seconds for email to actually be delivered")
time.sleep(5)
ReceiveMessage( server, username, password, nonce, start_time, f )
Fail2BanCount()
# close output file (used for prometheus)
f.close()