diff --git a/__pycache__/config.cpython-313.pyc b/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..ed5a8a2 Binary files /dev/null and b/__pycache__/config.cpython-313.pyc differ diff --git a/config.py b/config.py index 853ee9f..51221b9 100644 --- a/config.py +++ b/config.py @@ -9,3 +9,6 @@ HOME_ASSISTANT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI1MGRhNGI # Google Home Speaker Entity ID in Home Assistant GOOGLE_HOME_SPEAKER_ID = "media_player.nestmini2138" + +# Ollama Host +OLLAMA_HOST = "http://192.168.2.114:11434" \ No newline at end of file diff --git a/monitor_agent.log b/monitor_agent.log new file mode 100644 index 0000000..87d65ee --- /dev/null +++ b/monitor_agent.log @@ -0,0 +1,25 @@ +2025-08-15 14:44:41,331 - INFO - --- Running Monitoring Cycle --- +2025-08-15 14:44:41,332 - INFO - System Logs: {'log': "Failed login attempt for user 'root' from 10.0.0.1"} +2025-08-15 14:44:43,383 - WARNING - Could not parse ping output with jc. Returning raw output. +2025-08-15 14:44:43,384 - INFO - Network Metrics: {'ping_output': '\nPinging 8.8.8.8 with 32 bytes of data:\nReply from 8.8.8.8: bytes=32 time=18ms TTL=111\nReply from 8.8.8.8: bytes=32 time=23ms TTL=111\nReply from 8.8.8.8: bytes=32 time=18ms TTL=111\n\nPing statistics for 8.8.8.8:\n Packets: Sent = 3, Received = 3, Lost = 0 (0% loss),\nApproximate round trip times in milli-seconds:\n Minimum = 18ms, Maximum = 23ms, Average = 19ms\n'} +2025-08-15 14:44:43,384 - INFO - Combined Data: { + "system_logs": { + "log": "Failed login attempt for user 'root' from 10.0.0.1" + }, + "network_metrics": { + "ping_output": "\nPinging 8.8.8.8 with 32 bytes of data:\nReply from 8.8.8.8: bytes=32 time=18ms TTL=111\nReply from 8.8.8.8: bytes=32 time=23ms TTL=111\nReply from 8.8.8.8: bytes=32 time=18ms TTL=111\n\nPing statistics for 8.8.8.8:\n Packets: Sent = 3, Received = 3, Lost = 0 (0% loss),\nApproximate round trip times in milli-seconds:\n Minimum = 18ms, Maximum = 23ms, Average = 19ms\n" + } +} +2025-08-15 14:44:56,562 - INFO - HTTP Request: POST http://192.168.2.114:11434/api/generate "HTTP/1.1 200 OK" +2025-08-15 14:44:56,562 - INFO - LLM Response: After analyzing the system data in JSON format, I've identified a potential issue. + +The log entry indicates a failed login attempt for the 'root' user from IP address 10.0.0.1. While failed login attempts are not uncommon, this one stands out because it is not accompanied by any subsequent successful login attempts or other related log entries. This suggests that the failed login may have been an isolated incident and could be a sign of malicious activity. + +I would categorize this anomaly as medium severity due to the potential security implications. The possible cause could be unauthorized access attempts, which might warrant further investigation into the system's authentication mechanisms and access controls. + +Report: +"A failed login attempt for the 'root' user from IP address 10.0.0.1 indicates a potential security risk with a medium severity level. Further analysis is required to determine the root cause, but it may be indicative of unauthorized access attempts." +2025-08-15 14:44:56,563 - INFO - Anomaly detected, sending alerts... +2025-08-15 14:44:56,885 - INFO - Discord alert sent. +2025-08-15 14:44:56,891 - INFO - Google Home alert sent. +2025-08-15 14:44:56,891 - INFO - --- Cycle Complete, sleeping for 5 minutes --- diff --git a/monitor_agent.py b/monitor_agent.py index 4c9cda7..8fb58f6 100644 --- a/monitor_agent.py +++ b/monitor_agent.py @@ -1,13 +1,23 @@ import json +import platform import subprocess import time -from syslog_rfc5424_parser import parse +import logging +from syslog_rfc5424_parser import SyslogMessage import jc import ollama from discord_webhook import DiscordWebhook, DiscordEmbed import requests import config +# --- Logging Configuration --- +logging.basicConfig(level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler("monitor_agent.log"), + logging.StreamHandler() + ]) + # --- Data Ingestion & Parsing Functions --- def get_system_logs(): @@ -21,17 +31,17 @@ def get_system_logs(): dict: A dictionary representing the parsed log entry. """ mock_log_entry = '<165>1 2025-08-15T12:00:00Z my-host app-name - - [meta sequenceId="1"] { "log": "Failed login attempt for user \'root\' from 10.0.0.1" }' - parsed_log = parse(mock_log_entry) - # The message part is a string, so we need to parse it as JSON - # In a real scenario, you might need to handle non-json messages - if parsed_log.message: - try: - log_content = json.loads(parsed_log.message) - # We can merge the log content with the parsed log for a more complete picture - # For now, we'll just return the content of the log message - return log_content - except json.JSONDecodeError: - return {"log": parsed_log.message} + try: + parsed_log = SyslogMessage.parse(mock_log_entry) + if parsed_log.msg: + try: + log_content = json.loads(parsed_log.msg) + return log_content + except json.JSONDecodeError: + logging.warning(f"Could not parse log message as JSON: {parsed_log.msg}") + return {"log": parsed_log.msg} + except Exception as e: + logging.error(f"Error parsing syslog message: {e}") return {} def get_network_metrics(): @@ -44,17 +54,17 @@ def get_network_metrics(): Returns: dict: A dictionary containing the parsed network metrics. """ - # We ping a reliable address, like Google's DNS, 3 times. - # The '-c 3' argument is for Linux/macOS. For Windows, it would be '-n 3'. - # Since the target is an Ubuntu server, we'll use '-c'. + ping_param = '-n' if platform.system() == "Windows" else '-c' try: - ping_output = subprocess.run(['ping', '-c', '3', '8.8.8.8'], capture_output=True, text=True, check=True).stdout + ping_output = subprocess.run(['ping', ping_param, '3', '8.8.8.8'], capture_output=True, text=True, check=True).stdout parsed_metrics = jc.parse('ping', ping_output) - # We're interested in the summary statistics - if parsed_metrics: + if parsed_metrics and isinstance(parsed_metrics, list): return parsed_metrics[0] + else: + logging.warning("Could not parse ping output with jc. Returning raw output.") + return {"ping_output": ping_output} except (subprocess.CalledProcessError, FileNotFoundError) as e: - # Handle cases where ping fails or is not installed + logging.error(f"Error running ping command: {e}") return {"error": str(e)} return {} @@ -79,12 +89,14 @@ Output Request: If you find an anomaly, provide a report as a single, coherent, Reasoning Hint: Think step by step to come to your conclusion. This is very important.""" try: - response = ollama.generate( + client = ollama.Client(host=config.OLLAMA_HOST) + response = client.generate( model="llama3.1:8b", prompt=prompt ) return response['response'].strip() except Exception as e: + logging.error(f"Error communicating with Ollama: {e}") return f"Error communicating with Ollama: {e}" # --- Alerting Functions --- @@ -97,7 +109,7 @@ def send_discord_alert(message): message (str): The message to send. """ if config.DISCORD_WEBHOOK_URL == "YOUR_DISCORD_WEBHOOK_URL_HERE": - print("Skipping Discord alert: Webhook URL not configured.") + logging.info("Skipping Discord alert: Webhook URL not configured.") return webhook = DiscordWebhook(url=config.DISCORD_WEBHOOK_URL) @@ -105,9 +117,9 @@ def send_discord_alert(message): webhook.add_embed(embed) try: response = webhook.execute() - print("Discord alert sent.") + logging.info("Discord alert sent.") except Exception as e: - print(f"Error sending Discord alert: {e}") + logging.error(f"Error sending Discord alert: {e}") def send_google_home_alert(message): """ @@ -116,9 +128,8 @@ def send_google_home_alert(message): Args: message (str): The message to be spoken. """ - # Long or complex messages should be simplified for better Text-to-Speech delivery. if config.HOME_ASSISTANT_URL == "http://YOUR_HOME_ASSISTANT_IP:8123": - print("Skipping Google Home alert: Home Assistant URL not configured.") + logging.info("Skipping Google Home alert: Home Assistant URL not configured.") return url = f"{config.HOME_ASSISTANT_URL}/api/services/tts/speak" @@ -134,10 +145,10 @@ def send_google_home_alert(message): try: response = requests.post(url, headers=headers, json=payload) - response.raise_for_status() # Raise an exception for bad status codes - print("Google Home alert sent.") + response.raise_for_status() + logging.info("Google Home alert sent.") except requests.exceptions.RequestException as e: - print(f"Error sending Google Home alert: {e}") + logging.error(f"Error sending Google Home alert: {e}") # --- Main Script Logic --- @@ -146,26 +157,28 @@ def main(): The main execution loop for the monitoring agent. """ while True: - print("--- Running Monitoring Cycle ---") + logging.info("--- Running Monitoring Cycle ---") system_logs = get_system_logs() + logging.info(f"System Logs: {system_logs}") network_metrics = get_network_metrics() + logging.info(f"Network Metrics: {network_metrics}") combined_data = { "system_logs": system_logs, "network_metrics": network_metrics } + logging.info(f"Combined Data: {json.dumps(combined_data, indent=2)}") llm_response = analyze_data_with_llm(combined_data) - print(f"LLM Response: {llm_response}") + logging.info(f"LLM Response: {llm_response}") if llm_response != "OK": - print("Anomaly detected, sending alerts...") + logging.info("Anomaly detected, sending alerts...") send_discord_alert(llm_response) send_google_home_alert(llm_response) - print("--- Cycle Complete, sleeping for 5 minutes ---") + logging.info("--- Cycle Complete, sleeping for 5 minutes ---") time.sleep(300) # 300 seconds = 5 minutes if __name__ == "__main__": - main() - + main() \ No newline at end of file