#!/usr/bin/env python3 from systemd import journal from datetime import datetime, timedelta import re # read lines # Import smtplib for the actual sending function import smtplib # Import the email modules we'll need from email.message import EmailMessage from tabulate import tabulate import GeoIP import os import socket import logging import logging.config class connection: def __init__(self,logger): self.logger = logger self.attempts = {} self.gi = GeoIP.open("/usr/share/GeoIP/GeoIP.dat", GeoIP.GEOIP_STANDARD) def __add(self,hostname: str,user: str, pattern: str): self.logger.debug("<<< attempts:{}".format(self.attempts)) self.logger.debug("--- add {} {} {}".format(hostname,user,pattern)) if hostname in self.attempts: if user in self.attempts[hostname]: if pattern in self.attempts[hostname][user]: self.attempts[hostname][user][pattern] += 1 else: self.attempts[hostname][user][pattern] = 1 else: self.attempts[hostname][user] = { pattern: 1 } else: self.attempts[hostname] = { user: { pattern: 1 } } self.logger.debug(">>> attempts:{}".format(self.attempts)) def __get_list(self): liste = [] for h,v1 in self.attempts.items(): self.logger.debug("h {}".format(h)) gcn = self.gi.country_name_by_addr(h) if gcn is not None: g = gcn else: g = "localhost" for u,v2 in v1.items(): self.logger.debug("u {}".format(u)) for s,n in v2.items(): self.logger.debug("s {}".format(s)) self.logger.debug("g {} h {} u {} s {} n {}".format(g,h,u,s,n)) liste.append([g,h,u,s,n]) self.logger.debug(liste) return liste def report(self): headers = { 'Country':'', 'Hostname':'', 'User':'', 'Source':'', 'Number':'' } header = headers.keys() msg = EmailMessage() msg.set_content(tabulate(self.__get_list(),headers)) msg['Subject'] = f'[ssh][attempts] Reporting' msg['From'] = "{}@{}".format(os.getlogin(),socket.gethostname()) msg['To'] = "{}@{}".format(os.getlogin(),'192.168.0.238') self.logger.debug(msg) s = smtplib.SMTP('localhost') s.send_message(msg) s.quit() def addmessage(self, message:str): patterns = { 'pam_unix':r'''^pam_unix[^ ]*[ ](?P[a-z ]+)[; ]+logname=[.]*[ ]uid=[0-9]+[ ]euid=[0-9]+[ ]tty=[sh]+[ ]ruser=[.]*[ ]rhost=(?P[0-9.]+)[ ]+user=(?P[a-zA-Z0-9]+)$''', 'failed':r'''^Failed[ ]password[ ]for[ ]+(?P[a-zA-Z0-9]+)[ ]from[ ](?P[0-9.]+)[ ]port[ ][0-9]+[ ](?P[a-z0-9]+)$''', 'invalid':r'''^Failed[ ]password[ ]for[ ]invalid[ ]user[ ]+(?P[a-zA-Z0-9]+)[ ]from[ ](?P[0-9.]+)[ ]port[ ][0-9]+[ ](?P[a-z0-9]+)$''', 'closed':r'''^Connection[ ]closed[ ]by[ ]authenticating[ ]user[ ](?P[a-zA-Z0-9]+)[ ](?P[0-9.]+)[ ]port[ ][0-9]+[ ](?P[a-z\[\]]+)$''', 'reset':r'''^Connection[ ]reset[ ]by[ ]invalid[ ]user[ ](?P[a-zA-Z0-9]+)[ ](?P[0-9.]+)[ ]port[ ][0-9]+[ ](?P[a-z\[\]]+)$''', 'pam':r'''^PAM[ ][0-9][ ]more[ ]authentication[ ]failures;[ ]logname=[ ]uid=[0-9]+[ ]euid=[0-9]+[ ]tty=ssh[ ]ruser=[ ]rhost=(?P[0-9.]+)[ ]+user=(?P[a-zA-Z0-9]+)$''', 'pamano':r'''^PAM[ ][0-9][ ]more[ ]authentication[ ]failures;[ ]logname=[ ]uid=[0-9]+[ ]euid=[0-9]+[ ]tty=ssh[ ]ruser=[ ]rhost=(?P[0-9.]+)([ ]+user=(?P[a-zA-Z0-9]+|)|)$''', 'stopping':r'''^Received[ ]signal[ ]15;[ ]terminating.$''', 'ignoring':r'''^PAM[ ]service[(]sshd[)][ ]ignoring[ ]max[ ]retries;[ ] [0-9]+ > [0-9]+$''', 'listen':r'''^Server[ ]listening[ ]on[ ][0-9:.]+[ ]port[ ][0-9]+[.]$''' } compiled = { r:re.compile(patterns[r]) for r in iter(patterns) } matched = { r:re.match(compiled[r],message) for r in iter(patterns) } self.logger.debug("$$$ matched: {}".format(matched)) lst = [v for k, v in matched.items() if v] self.logger.debug("lst: {}".format(lst)) pattern = [k for k, v in matched.items() if v] self.logger.debug("pattern: {}".format(pattern)) for p in pattern: match p: case 'listen'|'stopping'|'ignoring': continue case 'pam_unix'|'failed'|'invalid'|'closed'|'reset'|'pam'|'pamano': pattern0 = pattern[0] self.logger.debug("::: pattern0: {}".format(pattern0)) val = lst[0] if lst else None if val: self.__add(hostname=val['hostname'], user=val['user'], pattern=pattern0) case _: print('======== Unmatched =============') break logging.basicConfig(filename='/tmp/ssh_reporting.log',level=logging.DEBUG) logger=logging.getLogger(__name__) j = journal.Reader() c = connection(logger) j.seek_realtime(datetime.now() - timedelta(hours=24)) j.add_match('_SYSTEMD_UNIT=ssh.service') for entry in j: logger.debug(entry['MESSAGE']) c.addmessage(entry['MESSAGE']) logger.debug("--- Generating report") c.report()