Repository navigation
Expand file tree
/
Copy pathsyslog_monitoring.py
More file actions
143 lines (126 loc) · 6.1 KB
/
Copy pathsyslog_monitoring.py
File metadata and controls
143 lines (126 loc) · 6.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import os
import time
import re
import subprocess
import threading
import config
from common import AlertManager
print(
"""
__ __ __ ______ _______ ______
| \\ | \\ | \\ | \\| \\ / \\
| $$ | $$ __ __ ______ ______ ______ \\$$ ______ _______ \\$$$$$$| $$$$$$$\\| $$$$$$\\
| $$__| $$| \\ | \\ / \\ / \\ / \\ | \\ / \\ | \\ | $$ | $$ | $$| $$___\\$$
| $$ $$| $$ | $$| $$$$$$\\| $$$$$$\\| $$$$$$\\| $$| $$$$$$\\| $$$$$$$\\ | $$ | $$ | $$ \\$$ \\
| $$$$$$$$| $$ | $$| $$ | $$| $$ $$| $$ \\$$| $$| $$ | $$| $$ | $$ | $$ | $$ | $$ _\\$$$$$$\\
| $$ | $$| $$__/ $$| $$__/ $$| $$$$$$$$| $$ | $$| $$__/ $$| $$ | $$ _| $$_ | $$__/ $$| \\__| $$
| $$ | $$ \\$$ $$| $$ $$ \\$$ \\| $$ | $$ \\$$ $$| $$ | $$| $$ \\| $$ $$ \\$$ $$
\\$$ \\$$ _\\$$$$$$$| $$$$$$$ \\$$$$$$$ \\$$ \\$$ \\$$$$$$ \\$$ \\$$ \\$$$$$$ \\$$$$$$$ \\$$$$$$
| \\__| $$| $$
\\$$ $$| $$
\\$$$$$$ \\$$
===================================================> System Logs Analysis
Made by: Harsh Raj Singhania
Github: https://github.com/HarshRajSinghania
"""
)
# Pulls a source IP out of a matched line when present, so repeated hits
# from *different* sources (e.g. two hosts both brute-forcing SSH) are
# tracked -- and alerted on -- separately, while repeats from the *same*
# source get deduplicated instead of spamming one notification per line.
_IP_PATTERN = re.compile(r"\bfrom (\d{1,3}(?:\.\d{1,3}){3})\b")
class SyslogMonitor:
def __init__(self, log_files, log_alerts_file,
patterns=None, alert_manager=None):
self.log_files = log_files
self.patterns = patterns or config.SYSLOG_PATTERNS
self.alerts = alert_manager or AlertManager(
log_file=log_alerts_file, app_name="Syslog Monitor",
title="Syslog Monitoring Alert",
)
def monitor_logs(self, log_file):
"""Continuously monitor a log file for suspicious activity."""
try:
with open(log_file, "r") as f:
f.seek(0, os.SEEK_END) # Move to end of file
while True:
line = f.readline()
if not line:
time.sleep(1)
continue
for alert_name, pattern in self.patterns.items():
if re.search(pattern, line):
ip_match = _IP_PATTERN.search(line)
key_suffix = ip_match.group(1) if ip_match else log_file
self.alerts.raise_alert(
f"{alert_name}:{key_suffix}",
f"[ALERT] {alert_name}: {line.strip()}",
)
except FileNotFoundError:
self.alerts.info(f"[ERROR] Log file not found: {log_file}")
except PermissionError:
self.alerts.info(
f"[ERROR] Permission denied reading {log_file} "
f"(syslog monitoring typically needs to run as root)."
)
def monitor_journal(self):
"""Fallback for systems with no rsyslog (journald-only, e.g. recent
Debian/Kali default installs) where /var/log/syslog and
/var/log/auth.log simply don't exist. Tails the live journal
instead so the module isn't just silently doing nothing."""
try:
process = subprocess.Popen(
["journalctl", "-f", "-n", "0", "-o", "short-iso"],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
bufsize=1,
)
except FileNotFoundError:
self.alerts.info(
"[ERROR] No configured log files exist and journalctl is not "
"available either -- syslog monitoring cannot run on this host."
)
return
self.alerts.info("[INFO] Tailing journald as a fallback log source.")
try:
for line in process.stdout:
if not line:
continue
for alert_name, pattern in self.patterns.items():
if re.search(pattern, line):
ip_match = _IP_PATTERN.search(line)
key_suffix = ip_match.group(1) if ip_match else "journald"
self.alerts.raise_alert(
f"{alert_name}:{key_suffix}",
f"[ALERT] {alert_name}: {line.strip()}",
)
finally:
process.terminate()
def run(self):
"""Run the Syslog Monitoring module in separate threads."""
self.alerts.info("[INFO] Syslog Monitoring module is running...")
missing_files = []
for log_file in self.log_files:
if os.path.exists(log_file):
thread = threading.Thread(target=self.monitor_logs, args=(log_file,), daemon=True)
thread.start()
else:
missing_files.append(log_file)
if missing_files:
self.alerts.info(
f"[INFO] {', '.join(missing_files)} not found (no rsyslog on this "
f"host?) -- falling back to journald for system log monitoring."
)
journal_thread = threading.Thread(target=self.monitor_journal, daemon=True)
journal_thread.start()
# Main Execution
if __name__ == "__main__":
syslog_monitor = SyslogMonitor(config.SYSLOG_FILES, config.SYSLOG_LOG_FILE)
syslog_monitor.run()
print("Syslog Monitoring is running. Press Ctrl+C to stop.")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n[INFO] Monitoring stopped.")