1# 2# sshd_auth.py 3# 4# Find number of ssh logins and authorised users (uses /var/log/auth.log) 5# 6# NOTE: This file is now deprecated in favour of the newer journald mechanism 7# used in sshd-journald.py. This parser is still functional but is slower and 8# has less features. Please switch over if possible. 9# 10 11import re 12 13from logparse.formatting import * 14from logparse.util import readlog, resolve 15from logparse import config 16 17import logging 18logger = logging.getLogger(__name__) 19 20def parse_log(): 21 22 logger.warning("NOTE: This sshd parser is now deprecated. Please use sshd-journald if possible.") 23 24 logger.debug("Starting sshd section") 25 section = Section("ssh") 26 logger.debug("Searching for matches in {0}".format(config.prefs.get("logs", "auth"))) 27 matches = re.findall('.*sshd.*Accepted publickey for .* from .*', readlog(config.prefs.get("logs", "auth"))) # get all logins 28 logger.debug("Finished searching for logins") 29 30 logger.debug("Searching for matches in {0}".format(config.prefs.get("logs", "auth"))) 31 authlog = readlog(config.prefs.get("logs", "auth")) 32 33 matches = re.findall('.*sshd.*Accepted publickey for .* from .*', authlog) # get all logins 34 invalid_matches = re.findall(".*sshd.*Invalid user .* from .*", authlog) 35 root_matches = re.findall("Disconnected from authenticating user root", authlog) 36 logger.debug("Finished searching for logins") 37 38 users = [] # list of users with format [username, number of logins] for each item 39 data = [] 40 num = len(matches) # total number of logins 41 for match in matches: 42 entry = re.search('^.*publickey\sfor\s(\w*)\sfrom\s(\S*)', match) # [('user', 'ip')] 43 44 user = entry.group(1) 45 ip = entry.group(2) 46 47 userhost = user + '@' + resolve(ip, fqdn=config.prefs.get("sshd", "resolve-domains")) 48 users.append(userhost) 49 logger.debug("Parsed list of authorised users") 50 51 auth_data = Data(subtitle=plural('login', num) + ' from', items=users) 52 53 if (len(auth_data.items) == 1): # if only one user, do not display no of logins for this user 54 logger.debug("found " + str(len(matches)) + " ssh logins for user " + users[0]) 55 auth_data.subtitle += ' ' + auth_data.items[0] 56 auth_data.orderbyfreq() 57 auth_data.truncl(config.prefs.getint("logparse", "maxlist")) 58 logger.debug("Found " + str(len(matches)) + " ssh logins for users " + str(data)) 59 section.append_data(auth_data) 60 61 invalid_users = [] 62 for match in invalid_matches: 63 entry = re.search('^.*Invalid user (\S+) from (\S+).*', match) # [('user', 'ip')] 64 65 try: 66 user = entry.group(1) 67 ip = entry.group(2) 68 except: # blank user field 69 continue 70 71 userhost = user + '@' + ip 72 invalid_users.append(userhost) 73 logger.debug("Parsed list of invalid users") 74 invalid_data = Data(subtitle=plural("attempted login", len(invalid_matches)) + " from " + plural("invalid user", len(invalid_users), print_quantity=False), items=invalid_users) 75 if (len(invalid_data.items) == 1): # if only one user, do not display no of logins for this user 76 logger.debug("Found " + str(len(invalid_matches)) + " SSH login attempts for invalid user " + invalid_users[0]) 77 invalid_data.subtitle += ' ' + invalid_data.items[0] 78 invalid_data.orderbyfreq() 79 invalid_data.truncl(config.prefs.get("logparse", "maxlist")) 80 logger.debug("Found " + str(len(invalid_matches)) + " SSH login attempts for invalid users " + str(data)) 81 section.append_data(invalid_data) 82 83 logger.debug("Found {0} attempted logins for root".format(str(len(root_matches)))) 84 85 section.append_data(Data(subtitle=plural("attempted login", str(len(root_matches))) + " for root")) 86 87 logger.info("Finished sshd section") 88 return section