#!/usr/bin/env python3 import sys import logging from logger import logger import log_statistics import reader import parser import argparse from enum import StrEnum class Commands(StrEnum): PARSE = "parse" IPV4 = "ipv4", USER = "user", TYPE = "type", RANDOM = "random", DURATION = "duration", FREQ = "freq" def main(args): stdout_handler = logging.StreamHandler(sys.stdout) stdout_handler.addFilter(lambda log: log.levelno <= logging.WARNING) stdout_handler.setLevel(logging.DEBUG) stdout_handler.setFormatter(logging.Formatter('%(levelname)s - %(name)s - %(message)s')) stderr_handler = logging.StreamHandler(sys.stderr) stderr_handler.setLevel(logging.ERROR) stderr_handler.setFormatter(logging.Formatter('%(levelname)s - %(name)s - %(message)s')) logger.addHandler(stdout_handler) logger.addHandler(stderr_handler) if args.log_level == "NONE": logger.disabled = True else: logger.setLevel(args.log_level) try: logs = reader.read_logs(args.filename) except: logger.critical(f"Could not open file {args.filename}") exit(1) if args.subcommand == Commands.PARSE: for line in logs: print(f"{line.timestamp.strftime('%d/%m:%H:%M:%S')};{line.hostname};{line.pid};{line.message}") #1.1.2 if args.subcommand == Commands.IPV4: for line in logs: print(*parser.get_ipv4s_from_log(line), sep=", ") #1.1.3 elif args.subcommand == Commands.USER: for line in logs: print(parser.get_user_from_log(line) or "") #1.1.4 elif args.subcommand == Commands.TYPE: for line in logs: print(parser.get_message_type(line.message)) #1.3.1 elif args.subcommand == Commands.RANDOM: random_logs = log_statistics.random_user_logs(list(logs), args.n) for line in random_logs: print(line.original_line) #1.3.2 elif args.subcommand == Commands.DURATION: log_list = list(logs) if args.group_by_users: durations_by_user = log_statistics.get_average_session_duration_by_user(log_list) for (username, (avg_time, stdev)) in sorted(durations_by_user.items(), key=lambda x: x[1], reverse=True): print(f"{username}: average={avg_time:.4f}s, stdev={stdev:.4f}s") else: (avg_time, stdev) = log_statistics.get_average_session_duration(log_list) print(f"average={avg_time:.4f}s, stdev={stdev:.4f}s") #1.3.3 elif args.subcommand == Commands.FREQ: log_list = list(logs) (most, least) = log_statistics.get_most_and_least_frequent_user(log_list) if most and least: print(f"most frequent user - {most[0]} - {most[1]} login(s)") print(f"least frequent user - {least[0]} - {least[1]} login(s)") else: print("could not determine most and least frequent user") def positive_int(arg): try: val = int(arg) except ValueError: raise argparse.ArgumentTypeError("Must be an integer") if val < 0: raise argparse.ArgumentTypeError("Must be greater than or equal to 0") return val def parse_args(): parser = argparse.ArgumentParser(description="Script for analyzing sshd logs") #1.4.1 parser.add_argument("filename", help="Path of the file to be read") #1.4.2 parser.add_argument("--log-level", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "NONE"], default="INFO", help="Level of logging, default is info") #1.4.3 subparsers = parser.add_subparsers(dest="subcommand", title="subcommands", required=True) subparsers.add_parser(Commands.PARSE, help="Parse logs into semicolon separated structure") subparsers.add_parser(Commands.IPV4, help="Get all IPv4 addresses from log messsages") subparsers.add_parser(Commands.USER, help="Extract user from log messages") subparsers.add_parser(Commands.TYPE, help="Get type of log message") random_parser = subparsers.add_parser(Commands.RANDOM, help="Get specified amount (default is 3) of random logs from random user") random_parser.add_argument("-n", type=positive_int, default=3, help="Amount of random logs to be returned") duration_parser = subparsers.add_parser(Commands.DURATION, help="Get average session duration and standard deviation") duration_parser.add_argument("--group-by-users", action="store_true", help="Whether session durations should be calculated for each user separately") subparsers.add_parser(Commands.FREQ, help="Get most and least frequently logging users") return parser.parse_args() if __name__ == "__main__": args = parse_args() main(args)