sshd-bruteforce-detector/main.py
2026-03-23 11:19:04 -03:00

89 lines
2.5 KiB
Python

import logging
from systemd import journal
from datetime import timedelta
from config import Config
from utils import filter_message_list
from message_parse import Message
from sys import exit
import detector
import signal
import time
import logs
running = True
logger = logging.getLogger("sshd-bruteforce-detector")
def handle_exit_signal(signum, _):
global running
global logger
running = False
logger.info(f"{signal.Signals(signum).name} received. Stopping manually...")
if signum == signal.SIGINT:
exit(130)
else:
exit(0)
def main():
logs.setup_logging()
global logger
config = Config()
logger = logging.getLogger("sshd-bruteforce-detector")
window = config.get_window()
max_attempts = config.get_max_attempts()
j = journal.Reader()
j.add_match(_SYSTEMD_UNIT="sshd.service")
j.seek_tail()
j.get_previous()
messages: dict[str, list] = {}
message_detected = False
logger.info(
f"Starting sshd-bruteforce-detector with configuration: window={window} max_attempts={max_attempts}"
)
global running
while running:
for entry in j:
message_str = entry["MESSAGE"]
if Message.is_failed_auth_message(message_str):
message = Message(message_str, entry["__REALTIME_TIMESTAMP"])
if not messages.get(str(message.rhost)):
messages[str(message.rhost)] = []
messages[str(message.rhost)].append(message)
logger.info(
f"Authentication failure from host {message.rhost} for user {message.user} on sshd"
)
message_detected = True
new_messages = messages.copy()
for ip, message_list in messages.items():
new_messages[ip] = filter_message_list(
message_list, timedelta(seconds=window)
)
if (
detector.detect_suspect_messages(new_messages[ip], max_attempts)
and message_detected
):
alert_window = new_messages[ip][-1].date - new_messages[ip][0].date
logger.critical(
f"{len(new_messages[ip])} authentication failure events from {ip} on sshd in {alert_window.seconds} seconds."
)
message_detected = False
time.sleep(0.3)
if __name__ == "__main__":
signal.signal(signal.SIGTERM, handle_exit_signal)
signal.signal(signal.SIGINT, handle_exit_signal)
main()