89 lines
2.5 KiB
Python
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()
|