#!/usr/bin/env python3 """ POPRAVEN SERVER со Sysmon обработка """ import traceback from flask import Flask, request, jsonify, render_template_string import json import sqlite3 from datetime import datetime, timedelta import time import os import socket from collections import defaultdict import threading import requests from openai import OpenAI from openai import OpenAI from dotenv import load_dotenv load_dotenv() OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") client = OpenAI( api_key=OPENAI_API_KEY, timeout=15 # максимум 15 секунди да чека ) if OPENAI_API_KEY else None app = Flask(__name__) DB_FILE = "lan_logs_sysmon.db" LOG_DIR = "received_data_sysmon" import secrets ADMIN_KEY = os.environ.get("ADMIN_KEY", "") ADMIN_KEY = os.environ.get("ADMIN_KEY", "") def init_admin_tables(): conn = sqlite3.connect(DB_FILE) c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS environments( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, created_at TEXT )''') c.execute('''CREATE TABLE IF NOT EXISTS env_tokens( id INTEGER PRIMARY KEY AUTOINCREMENT, env_name TEXT, token TEXT UNIQUE, created_at TEXT, expires_at TEXT )''') # migration ако табелата веќе постои c.execute("PRAGMA table_info(env_tokens)") cols = [r[1] for r in c.fetchall()] if "expires_at" not in cols: c.execute("ALTER TABLE env_tokens ADD COLUMN expires_at TEXT") c.execute('''CREATE TABLE IF NOT EXISTS admin_sessions( id INTEGER PRIMARY KEY AUTOINCREMENT, token TEXT UNIQUE, created_at TEXT )''') # default env c.execute("INSERT OR IGNORE INTO environments(name, created_at) VALUES('default', datetime('now'))") conn.commit() conn.close() def require_admin_session(): tok = request.headers.get("X-Admin-Session", "") if not tok: return False conn = sqlite3.connect(DB_FILE) c = conn.cursor() c.execute("SELECT 1 FROM admin_sessions WHERE token = ?", (tok,)) ok = c.fetchone() is not None conn.close() return ok def get_env_from_token(req): tok = req.headers.get("X-Env-Token", "") if not tok: return None conn = sqlite3.connect(DB_FILE) c = conn.cursor() c.execute(""" SELECT env_name FROM env_tokens WHERE token = ? AND (expires_at IS NULL OR expires_at > datetime('now')) """, (tok,)) row = c.fetchone() conn.close() return row[0] if row else None def init_database(): """Креирај подобрена база со Sysmon табели""" os.makedirs(LOG_DIR, exist_ok=True) conn = sqlite3.connect(DB_FILE) c = conn.cursor() # Табела за компјутери c.execute('''CREATE TABLE IF NOT EXISTS computers (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, user TEXT, ip TEXT, os TEXT, first_seen TIMESTAMP, last_seen TIMESTAMP, sysmon_available BOOLEAN DEFAULT 0)''') # --- MIGRATION: add env_name column if missing --- c.execute("PRAGMA table_info(computers)") cols = [r[1] for r in c.fetchall()] if "env_name" not in cols: c.execute("ALTER TABLE computers ADD COLUMN env_name TEXT DEFAULT 'default'") # --- END MIGRATION --- # Табела за историја c.execute('''CREATE TABLE IF NOT EXISTS computer_history (id INTEGER PRIMARY KEY AUTOINCREMENT, computer_id INTEGER, cpu_usage REAL, ram_usage REAL, disk_usage REAL, network_sent_mb REAL, network_recv_mb REAL, timestamp TIMESTAMP, FOREIGN KEY(computer_id) REFERENCES computers(id))''') # Табела за процеси c.execute('''CREATE TABLE IF NOT EXISTS computer_processes (id INTEGER PRIMARY KEY AUTOINCREMENT, computer_id INTEGER, pid INTEGER, name TEXT, cpu_percent REAL, memory_mb REAL, username TEXT, cmdline TEXT, timestamp TIMESTAMP, FOREIGN KEY(computer_id) REFERENCES computers(id))''') # НОВА: Табела за Sysmon настани c.execute('''CREATE TABLE IF NOT EXISTS sysmon_events (id INTEGER PRIMARY KEY AUTOINCREMENT, computer_id INTEGER, event_id INTEGER, event_type TEXT, message TEXT, timestamp TIMESTAMP, details TEXT, FOREIGN KEY(computer_id) REFERENCES computers(id))''') # НОВА: Табела за детални мрежни конекции c.execute('''CREATE TABLE IF NOT EXISTS network_connections (id INTEGER PRIMARY KEY AUTOINCREMENT, computer_id INTEGER, pid INTEGER, local_address TEXT, remote_address TEXT, status TEXT, process_name TEXT, timestamp TIMESTAMP, FOREIGN KEY(computer_id) REFERENCES computers(id))''') # НОВА: Табела за безбедносни настани c.execute('''CREATE TABLE IF NOT EXISTS security_alerts (id INTEGER PRIMARY KEY AUTOINCREMENT, computer_id INTEGER, alert_type TEXT, severity TEXT, description TEXT, timestamp TIMESTAMP, resolved BOOLEAN DEFAULT 0, FOREIGN KEY(computer_id) REFERENCES computers(id))''') conn.commit() conn.close() print(f"✅ Database created: {DB_FILE}") @app.route('/receive', methods=['POST']) def receive_data(): try: data = request.json if not data: return jsonify({"error": "No data"}), 400 env_name = get_env_from_token(request) if not env_name: return jsonify({"error": "Missing or invalid X-Env-Token"}), 401 info = data['info'] computer_name = info['computer_name'] timestamp = datetime.now() security_data = data.get('security_data', {}) conn = sqlite3.connect(DB_FILE) c = conn.cursor() # Провери дали компјутерот веќе постои c.execute("SELECT id FROM computers WHERE name = ?", (computer_name,)) result = c.fetchone() if result: # Компјутерот постои - ажурирај го computer_id = result[0] c.execute('''UPDATE computers SET user = ?, ip = ?, os = ?, last_seen = ?, sysmon_available = ?, env_name = ? WHERE id = ?''', (info['user'], info['ip_address'], info['os'], timestamp.isoformat(), info.get('is_sysmon_available', 0), env_name, computer_id)) else: # Нов компјутер - додади го c.execute('''INSERT INTO computers (name, user, ip, os, first_seen, last_seen, sysmon_available, env_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?)''', (computer_name, info['user'], info['ip_address'], info['os'], timestamp.isoformat(), timestamp.isoformat(), info.get('is_sysmon_available', 0), env_name)) computer_id = c.lastrowid # Додади во историја c.execute('''INSERT INTO computer_history (computer_id, cpu_usage, ram_usage, disk_usage, network_sent_mb, network_recv_mb, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?)''', (computer_id, info['cpu_usage'], info['ram_usage'], info['disk_usage'], info.get('network_sent_mb', 0), info.get('network_recv_mb', 0), info['timestamp'])) # Зачувај ги процесите for proc in data.get('processes', []): c.execute('''INSERT INTO computer_processes (computer_id, pid, name, cpu_percent, memory_mb, username, cmdline, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)''', (computer_id, proc.get('pid'), proc.get('name'), proc.get('cpu_percent', 0), proc.get('memory_mb', 0), proc.get('user'), proc.get('cmdline'), info['timestamp'])) # ЗАЧУВАЈ SYSMON НАСТАНИ sysmon_events_count = 0 for event in security_data.get('sysmon_events', []): try: c.execute('''INSERT INTO sysmon_events (computer_id, event_id, event_type, message, timestamp, details) VALUES (?, ?, ?, ?, ?, ?)''', (computer_id, event.get('event_id'), event.get('event_type', 'Unknown'), event.get('message', ''), event.get('timestamp', info['timestamp']), json.dumps(event))) sysmon_events_count += 1 except Exception as e: print(f"Error saving Sysmon event: {e}") # ЗАЧУВАЈ МРЕЖНИ КОНЕКЦИИ for conn_data in security_data.get('network_connections', []): try: c.execute('''INSERT INTO network_connections (computer_id, pid, local_address, remote_address, status, process_name, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?)''', (computer_id, conn_data.get('pid'), conn_data.get('local_address'), conn_data.get('remote_address'), conn_data.get('status'), conn_data.get('process_name'), info['timestamp'])) except Exception as e: print(f"Error saving network connection: {e}") conn.commit() conn.close() # Зачувај во JSON save_json_file(data) print(f"[{timestamp.strftime('%H:%M:%S')}] 📥 {computer_name}: " f"CPU {info['cpu_usage']}% | Sysmon: {sysmon_events_count} events") return jsonify({ "status": "success", "message": f"Data saved for {computer_name}", "computer_id": computer_id, "event_count": sysmon_events_count, "timestamp": timestamp.isoformat() }) except Exception as e: print(f"[-] Грешка: {e}") return jsonify({"error": str(e)}), 500 def save_json_file(data): """Зачувај во JSON""" info = data['info'] computer_name = info['computer_name'] date_str = datetime.now().strftime("%Y%m%d_%H") # Креирај folder за компјутерот computer_dir = os.path.join(LOG_DIR, computer_name) os.makedirs(computer_dir, exist_ok=True) # Фајл за секој час (за подобро организирање) filepath = os.path.join(computer_dir, f"{date_str}.json") # Додади во постоечки фајл или креирај нов if os.path.exists(filepath): with open(filepath, 'r', encoding='utf-8') as f: existing_data = json.load(f) else: existing_data = [] # Додади нов запис existing_data.append({ "timestamp": info['timestamp'], "info": info, "processes_count": len(data.get('processes', [])), "sysmon_events_count": len(data.get('security_data', {}).get('sysmon_events', [])) }) # Зачувај with open(filepath, 'w', encoding='utf-8') as f: json.dump(existing_data, f, indent=2, ensure_ascii=False) @app.route('/') def dashboard(): """Главен dashboard""" conn = sqlite3.connect(DB_FILE) c = conn.cursor() # Земaj ги сите компјутери c.execute('''SELECT name, user, ip, os, first_seen, last_seen, sysmon_available, env_name FROM computers ORDER BY last_seen DESC''') computers = [] for row in c.fetchall(): computer_name = row[0] # Пресметaj колку записи има во последните 24 часа c.execute('''SELECT COUNT(*) FROM computer_history WHERE computer_id = (SELECT id FROM computers WHERE name = ?) AND timestamp > datetime('now', '-24 hours')''', (computer_name,)) recent_logs = c.fetchone()[0] # Земaj ги последните 5 записи за CPU/RAM c.execute('''SELECT cpu_usage, ram_usage, timestamp FROM computer_history WHERE computer_id = (SELECT id FROM computers WHERE name = ?) ORDER BY timestamp DESC LIMIT 5''', (computer_name,)) recent_metrics = c.fetchall() # Sysmon настани за последните 24 часа c.execute('''SELECT COUNT(*) FROM sysmon_events WHERE computer_id = (SELECT id FROM computers WHERE name = ?) AND timestamp > datetime('now', '-24 hours')''', (computer_name,)) recent_sysmon = c.fetchone()[0] # Пресметaj просек avg_cpu = sum(m[0] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0 avg_ram = sum(m[1] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0 # Статус last_seen = datetime.fromisoformat(row[5]) time_diff = (datetime.now() - last_seen).seconds if time_diff < 60: status = 'online' status_color = 'green' elif time_diff < 300: status = 'idle' status_color = 'orange' else: status = 'offline' status_color = 'gray' computers.append({ 'name': row[0], 'user': row[1], 'ip': row[2], 'os': row[3], 'first_seen': row[4], 'last_seen': row[5], 'sysmon_available': bool(row[6]), 'recent_logs': recent_logs, 'recent_sysmon': recent_sysmon, 'avg_cpu': round(avg_cpu, 1), 'avg_ram': round(avg_ram, 1), 'status': status, 'status_color': status_color, 'env_name': row[7], }) conn.close() # Генерирај HTML return render_template_string(''' LAN Log Server - Sysmon Dashboard

🛡️ LAN Security Monitor Sysmon Edition

Server: {{ socket.gethostbyname(socket.gethostname()) }}:5555 | Total Computers: {{ computers|length }}

Total Computers
{{ computers|length }}
Monitored Systems
Sysmon Events (24h)
0
Security Events
Network Connections
0
Active Connections

Connected Computers

{% for comp in computers %}
{{ comp.name }}
👤 {{ comp.user }}
🌐 {{ comp.ip }}
🏷️ Env: {{ comp.env_name }}
💻 {{ comp.os[:40] }}
Last seen: {{ comp.last_seen[11:19] }}
{% if comp.sysmon_available %}
🛡️ Sysmon Available {{ comp.recent_sysmon }} events
{% endif %}
{{ comp.avg_cpu }}%
CPU
{{ comp.avg_ram }}%
RAM
{{ comp.recent_logs }}
Logs (24h)
{% endfor %}
''', computers=computers, socket=socket, datetime=datetime) @app.route('/api/computer/') def get_computer_details(computer_name): """Добиј сите детални информации за еден компјутер""" conn = sqlite3.connect(DB_FILE) c = conn.cursor() # Основни информации c.execute('''SELECT id, name, user, ip, os, first_seen, last_seen FROM computers WHERE name = ?''', (computer_name,)) computer_data = c.fetchone() if not computer_data: conn.close() return jsonify({"error": "Computer not found"}), 404 computer_id = computer_data[0] # Број на сите записи c.execute('SELECT COUNT(*) FROM computer_history WHERE computer_id = ?', (computer_id,)) total_logs = c.fetchone()[0] # Сите history записи c.execute('''SELECT cpu_usage, ram_usage, disk_usage, network_sent_mb, network_recv_mb, timestamp FROM computer_history WHERE computer_id = ? ORDER BY timestamp DESC LIMIT 100''', (computer_id,)) history = [] for row in c.fetchall(): history.append({ 'cpu_usage': row[0], 'ram_usage': row[1], 'disk_usage': row[2], 'network_sent_mb': row[3], 'network_recv_mb': row[4], 'timestamp': row[5] }) # Последни метрики current_metrics = history[0] if history else {} # Последни процеси c.execute('''SELECT pid, name, username, cpu_percent, memory_mb, cmdline, timestamp FROM computer_processes WHERE computer_id = ? ORDER BY timestamp DESC LIMIT 100''', (computer_id,)) recent_processes = [] for row in c.fetchall(): recent_processes.append({ 'pid': row[0], 'name': row[1], 'username': row[2], 'cpu_percent': row[3], 'memory_mb': row[4], 'cmdline': row[5], 'timestamp': row[6] }) # SYSMON НАСТАНИ c.execute('''SELECT event_id, event_type, message, timestamp, details FROM sysmon_events WHERE computer_id = ? ORDER BY timestamp DESC LIMIT 200''', (computer_id,)) sysmon_events = [] for row in c.fetchall(): try: details = json.loads(row[4]) if row[4] else {} except: details = {} sysmon_events.append({ 'event_id': row[0], 'event_type': row[1], 'message': row[2], 'timestamp': row[3], 'details': details }) # Мрежни конекции c.execute('''SELECT pid, process_name, local_address, remote_address, status, timestamp FROM network_connections WHERE computer_id = ? ORDER BY timestamp DESC LIMIT 100''', (computer_id,)) network_connections = [] for row in c.fetchall(): network_connections.append({ 'pid': row[0], 'process_name': row[1], 'local_address': row[2], 'remote_address': row[3], 'status': row[4], 'timestamp': row[5] }) conn.close() return jsonify({ 'computer': { 'id': computer_data[0], 'name': computer_data[1], 'user': computer_data[2], 'ip': computer_data[3], 'os': computer_data[4], 'first_seen': computer_data[5], 'last_seen': computer_data[6], 'total_logs': total_logs }, 'history': history, 'current_metrics': current_metrics, 'recent_processes': recent_processes, 'sysmon_events': sysmon_events, 'network_connections': network_connections }) @app.route('/api/computers') def get_computers(): conn = sqlite3.connect(DB_FILE) c = conn.cursor() env = request.headers.get("X-Env", "default") c.execute('''SELECT name, user, ip, os, first_seen, last_seen, sysmon_available FROM computers WHERE env_name = ? ORDER BY last_seen DESC''', (env,)) computers = [] for row in c.fetchall(): computer_name = row[0] # recent logs c.execute('''SELECT COUNT(*) FROM computer_history WHERE computer_id = (SELECT id FROM computers WHERE name = ?) AND timestamp > datetime('now', '-24 hours')''', (computer_name,)) recent_logs = c.fetchone()[0] # last 5 metrics c.execute('''SELECT cpu_usage, ram_usage, timestamp FROM computer_history WHERE computer_id = (SELECT id FROM computers WHERE name = ?) ORDER BY timestamp DESC LIMIT 5''', (computer_name,)) recent_metrics = c.fetchall() c.execute('''SELECT COUNT(*) FROM sysmon_events WHERE computer_id = (SELECT id FROM computers WHERE name = ?) AND timestamp > datetime('now', '-24 hours')''', (computer_name,)) recent_sysmon = c.fetchone()[0] avg_cpu = sum(m[0] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0 avg_ram = sum(m[1] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0 last_seen = datetime.fromisoformat(row[5]) time_diff = (datetime.now() - last_seen).seconds if time_diff < 60: status = 'online' status_color = 'green' elif time_diff < 300: status = 'idle' status_color = 'orange' else: status = 'offline' status_color = 'gray' computers.append({ 'name': row[0], 'user': row[1], 'ip': row[2], 'os': row[3], 'first_seen': row[4], 'last_seen': row[5], 'sysmon_available': bool(row[6]), 'recent_logs': recent_logs, 'recent_sysmon': recent_sysmon, 'avg_cpu': round(avg_cpu, 1), 'avg_ram': round(avg_ram, 1), 'status': status, 'status_color': status_color }) conn.close() return jsonify(computers) @app.route('/api/stats') def get_stats(): """Добиј глобални статистики""" conn = sqlite3.connect(DB_FILE) c = conn.cursor() # Вкупно Sysmon настани за последните 24 часа c.execute('''SELECT COUNT(*) FROM sysmon_events WHERE timestamp > datetime('now', '-24 hours')''') total_sysmon_events = c.fetchone()[0] # Вкупно мрежни конекции за последните 24 часа c.execute('''SELECT COUNT(*) FROM network_connections WHERE timestamp > datetime('now', '-24 hours')''') total_network_connections = c.fetchone()[0] conn.close() return jsonify({ 'total_sysmon_events': total_sysmon_events, 'total_network_connections': total_network_connections, 'server_time': datetime.now().isoformat() }) def build_rag_context(question, computer_name=None, limit_per_table=10): try: conn = sqlite3.connect(DB_FILE) c = conn.cursor() params = [] comp_filter = "" if computer_name: comp_filter = "AND computer_id = (SELECT id FROM computers WHERE name = ?)" params.append(computer_name) # Sysmon настани c.execute( f""" SELECT timestamp, event_id, event_type, message FROM sysmon_events WHERE 1=1 {comp_filter} ORDER BY timestamp DESC LIMIT ? """, params + [limit_per_table], ) sysmon_rows = c.fetchall() # Перформанс историја c.execute( f""" SELECT timestamp, cpu_usage, ram_usage, disk_usage, network_sent_mb, network_recv_mb FROM computer_history WHERE 1=1 {comp_filter} ORDER BY timestamp DESC LIMIT ? """, params + [limit_per_table], ) perf_rows = c.fetchall() # Процеси c.execute( f""" SELECT timestamp, pid, name, cpu_percent, memory_mb FROM computer_processes WHERE 1=1 {comp_filter} ORDER BY timestamp DESC LIMIT ? """, params + [limit_per_table], ) proc_rows = c.fetchall() conn.close() print("[RAG] sysmon:", len(sysmon_rows), "| perf:", len(perf_rows), "| proc:", len(proc_rows)) lines = [] for ts, eid, etype, msg in sysmon_rows: lines.append(f"[SYSMON] {ts} | Event {eid} ({etype}): {msg}") for ts, cpu, ram, disk, nsent, nrecv in perf_rows: lines.append( f"[PERF] {ts} | CPU {cpu}% | RAM {ram}% | Disk {disk}% | " f"Net sent {nsent}MB / recv {nrecv}MB" ) for ts, pid, name, cpu, mem in proc_rows: lines.append(f"[PROC] {ts} | PID {pid} | {name} | CPU {cpu}% | RAM {mem}MB") context = "\n".join(lines) print("[RAG] sample context:\n", "\n".join(lines[:5])) return context except Exception as e: traceback.print_exc() return "" # def ask_llm_with_context(question, context, language="mk"): # if not client: # return "Немаш поставено OPENAI_API_KEY на серверот." # # system_msg = ( # "Ти си асистент за безбедност и систем мониторинг. " # "Одговараш базирано ИСКЛУЧИВО на дадените логови што ти се дадени. " # "Ако нема доволно информации, кажи дека нема доволно податоци. " # "Објаснувај кратко и јасно." # ) # # user_msg = f"Прашање: {question}\n\nЛогови:\n{context or 'Нема логови.'}" # # try: # completion = client.chat.completions.create( # model="gpt-4.1-mini", # messages=[ # {"role": "system", "content": system_msg}, # {"role": "user", "content": user_msg}, # ], # temperature=0.2, # ) # return completion.choices[0].message.content # except Exception as e: # # Може попрецизно: openai.APITimeoutError, openai.APIConnectionError итн. # return f"Грешка при повик кон LLM: {e}" def ask_llm_with_context(question, context, language="mk"): """ Вика OpenAI модел и враќа одговор базиран на контекстот од логови. """ if not client: return "Немаш поставено OPENAI_API_KEY на серверот." system_msg = ( "Ти си асистент за безбедност и систем мониторинг. " "Одговараш базирано ИСКЛУЧИВО на дадените логови што ти се дадени. " "Типови редови во логовите:\n" "- [SYSMON] ... Sysmon безбедносни настани.\n" "- [PERF] ... Општи перформанси (CPU, RAM, диск, мрежа).\n" "- [PROC] ... Информации за поединечни процеси: PID, име, CPU%, RAM MB.\n" "Кога корисникот прашува за процеси и нивна потрошувачка, " "СЕКОГАШ користи ги [PROC] редовите. " "Никогаш не кажувај дека 'нема информации за процеси' ако постои барем еден [PROC] ред. " "Ако навистина НЕМА [PROC] редови, тогаш можеш да кажеш дека нема доволно податоци. " "Објаснувај кратко и јасно." ) user_msg = f"Прашање: {question}\n\nЛогови:\n{context or 'Нема логови.'}" try: print("[LLM] Calling OpenAI…") completion = client.chat.completions.create( model="gpt-4.1-mini", messages=[ {"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}, ], temperature=0.2, ) print("[LLM] Got response") return completion.choices[0].message.content except Exception as e: print("[LLM] ERROR:", e) return f"Грешка при повик кон LLM: {e}" # # def ask_llm_with_context(question, context, language="mk"): # payload = { # "model": "deepseek-r1:1.5b", # "prompt": f"Прашање: {question}\n\nКонтекст:\n{context}", # "stream": False # } # # try: # r = requests.post("http://localhost:11434/api/generate", json=payload) # r.raise_for_status() # data = r.json() # return data.get("response", "") # except Exception as e: # return f"Грешка при повик кон Ollama: {e}" @app.route('/api/export/') def export_computer_data(computer_name): """Експортирај ги сите податоци за еден компјутер""" data = {} conn = sqlite3.connect(DB_FILE) c = conn.cursor() # Основни информации за компјутерот c.execute('''SELECT * FROM computers WHERE name = ?''', (computer_name,)) columns = [description[0] for description in c.description] computer_data = c.fetchone() if computer_data: data['computer'] = dict(zip(columns, computer_data)) # Историја computer_id = computer_data[0] c.execute('''SELECT * FROM computer_history WHERE computer_id = ? ORDER BY timestamp''', (computer_id,)) data['history'] = [dict(zip([d[0] for d in c.description], row)) for row in c.fetchall()] # Sysmon настани c.execute('''SELECT * FROM sysmon_events WHERE computer_id = ? ORDER BY timestamp''', (computer_id,)) data['sysmon_events'] = [dict(zip([d[0] for d in c.description], row)) for row in c.fetchall()] # Процеси c.execute('''SELECT * FROM computer_processes WHERE computer_id = ? ORDER BY timestamp''', (computer_id,)) data['processes'] = [dict(zip([d[0] for d in c.description], row)) for row in c.fetchall()] # Мрежни конекции c.execute('''SELECT * FROM network_connections WHERE computer_id = ? ORDER BY timestamp''', (computer_id,)) data['network_connections'] = [dict(zip([d[0] for d in c.description], row)) for row in c.fetchall()] conn.close() # Креирај JSON одговор from flask import Response response = Response( json.dumps(data, indent=2, default=str), mimetype='application/json', headers={'Content-Disposition': f'attachment; filename={computer_name}_export.json'} ) return response @app.route('/ping') def ping(): return jsonify({ 'status': 'alive', 'server_time': datetime.now().isoformat(), 'version': '2.0_sysmon', 'database': DB_FILE }) def get_local_ip(): try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) local_ip = s.getsockname()[0] s.close() return local_ip except: return "127.0.0.1" def cleanup_old_data(): """Чисти стари податоци од базата (стари од 30 дена)""" while True: time.sleep(3600) # Чисти на секој час try: conn = sqlite3.connect(DB_FILE) c = conn.cursor() cutoff_date = (datetime.now() - timedelta(days=30)).isoformat() # Избриши стари записи c.execute("DELETE FROM computer_history WHERE timestamp < ?", (cutoff_date,)) c.execute("DELETE FROM computer_processes WHERE timestamp < ?", (cutoff_date,)) c.execute("DELETE FROM sysmon_events WHERE timestamp < ?", (cutoff_date,)) c.execute("DELETE FROM network_connections WHERE timestamp < ?", (cutoff_date,)) conn.commit() conn.close() print(f"[Cleanup] Избришани стари записи пред {cutoff_date}") except Exception as e: print(f"[Cleanup] Грешка: {e}") import time @app.route("/api/chat", methods=["POST"]) def api_chat(): try: t0 = time.time() data = request.get_json(force=True) or {} question = data.get("question", "").strip() computer_name = data.get("computer_name") or None if not question: return jsonify({"error": "No question provided"}), 400 context = build_rag_context(question, computer_name=computer_name) t1 = time.time() answer = ask_llm_with_context(question, context) t2 = time.time() print(f"[CHAT] build_rag_context: {t1 - t0:.2f}s, LLM: {t2 - t1:.2f}s") return jsonify({"answer": answer}) except Exception as e: traceback.print_exc() return jsonify({"error": str(e)}), 500 @app.route('/api/computer//sysmon') def get_sysmon_processes(computer_name): """Добиј Sysmon процеси за компјутер""" conn = sqlite3.connect(DB_FILE) c = conn.cursor() # Прво најди го computer_id c.execute('SELECT id FROM computers WHERE name = ?', (computer_name,)) result = c.fetchone() if not result: conn.close() return jsonify({"error": "Computer not found"}), 404 computer_id = result[0] # Земaj ги Sysmon процесите (последни 100) c.execute('''SELECT event_id, event_type, message, timestamp, details FROM sysmon_events WHERE computer_id = ? ORDER BY timestamp DESC LIMIT 100''', (computer_id,)) processes = [] for row in c.fetchall(): try: details = json.loads(row[4]) if row[4] else {} except: details = {} processes.append({ 'process_id': row[0], # Event ID како PID 'process_name': row[1], # Event type како име 'timestamp': row[3], 'message': row[2], 'details': details }) conn.close() return jsonify({ 'computer_name': computer_name, 'processes': processes, 'count': len(processes) }) @app.route('/api/computer//network') def get_network_connections(computer_name): """Добиј мрежни конекции за компјутер""" conn = sqlite3.connect(DB_FILE) c = conn.cursor() # Прво најди го computer_id c.execute('SELECT id FROM computers WHERE name = ?', (computer_name,)) result = c.fetchone() if not result: conn.close() return jsonify({"error": "Computer not found"}), 404 computer_id = result[0] # Земaj ги мрежните конекции (последни 100) c.execute('''SELECT pid, process_name, local_address, remote_address, status, timestamp FROM network_connections WHERE computer_id = ? ORDER BY timestamp DESC LIMIT 100''', (computer_id,)) connections = [] for row in c.fetchall(): # Парсирај IP и порта local = row[2] or "N/A" remote = row[3] or "N/A" connections.append({ 'pid': row[0], 'process_name': row[1] or 'Unknown', 'local_address': local, 'remote_address': remote, 'local_port': local.split(':')[-1] if ':' in local else '', 'remote_port': remote.split(':')[-1] if ':' in remote else '', 'protocol': 'TCP' if ':443' in local or ':80' in local else 'Unknown', 'state': row[4] or 'ESTABLISHED', 'timestamp': row[5] }) conn.close() return jsonify({ 'computer_name': computer_name, 'connections': connections, 'count': len(connections) }) @app.route('/api/computer//detailed-sysmon') def get_detailed_sysmon(computer_name): """Добиј детални Sysmon процеси со повеќе информации""" conn = sqlite3.connect(DB_FILE) c = conn.cursor() c.execute('SELECT id FROM computers WHERE name = ?', (computer_name,)) result = c.fetchone() if not result: conn.close() return jsonify({"error": "Computer not found"}), 404 computer_id = result[0] c.execute('''SELECT event_id, event_type, message, timestamp, details FROM sysmon_events WHERE computer_id = ? ORDER BY timestamp DESC LIMIT 50''', (computer_id,)) detailed_processes = [] for row in c.fetchall(): event_id = row[0] event_type = row[1] message = row[2] timestamp = row[3] details_str = row[4] # Пробај да го парсираш details JSON try: details = json.loads(details_str) if details_str else {} except: details = {} # Екстрактирај информации од details process_info = { 'event_id': event_id, 'event_type': event_type, 'timestamp': timestamp, 'message': message, 'process_name': details.get('ProcessName', 'Unknown'), 'process_id': details.get('ProcessId', event_id), 'command_line': details.get('CommandLine', ''), 'parent_process': details.get('ParentProcessName', ''), 'parent_pid': details.get('ParentProcessId', ''), 'user': details.get('User', ''), 'details': details } # Додади дополнителни полиња за различни типови на настани if event_id in [3, 22]: # Network connection or DNS process_info['source_ip'] = details.get('SourceIp', '') process_info['dest_ip'] = details.get('DestinationIp', '') process_info['dest_port'] = details.get('DestinationPort', '') detailed_processes.append(process_info) conn.close() return jsonify({ 'computer_name': computer_name, 'processes': detailed_processes, 'count': len(detailed_processes) }) @app.route("/api/admin/login", methods=["POST"]) def admin_login(): data = request.get_json(force=True) or {} key = (data.get("admin_key") or "").strip() if not ADMIN_KEY or key != ADMIN_KEY: return jsonify({"error": "Unauthorized"}), 401 session_token = secrets.token_urlsafe(32) conn = sqlite3.connect(DB_FILE) c = conn.cursor() c.execute("INSERT INTO admin_sessions(token, created_at) VALUES(?, datetime('now'))", (session_token,)) conn.commit() conn.close() return jsonify({"ok": True, "session": session_token}) @app.route("/api/admin/environments", methods=["GET"]) def admin_list_envs(): if not require_admin_session(): return jsonify({"error": "Unauthorized"}), 401 conn = sqlite3.connect(DB_FILE) c = conn.cursor() c.execute("SELECT name FROM environments ORDER BY name") envs = [r[0] for r in c.fetchall()] conn.close() return jsonify({"environments": envs}) @app.route("/api/admin/environments", methods=["POST"]) def admin_create_env(): if not require_admin_session(): return jsonify({"error": "Unauthorized"}), 401 data = request.get_json(force=True) or {} name = (data.get("name") or "").strip() if not name: return jsonify({"error": "Missing env name"}), 400 conn = sqlite3.connect(DB_FILE) c = conn.cursor() try: c.execute("INSERT INTO environments(name, created_at) VALUES(?, datetime('now'))", (name,)) conn.commit() except Exception as e: conn.close() return jsonify({"error": str(e)}), 400 conn.close() return jsonify({"ok": True, "name": name}) @app.route("/api/admin/tokens", methods=["POST"]) def admin_generate_token(): if not require_admin_session(): return jsonify({"error": "Unauthorized"}), 401 data = request.get_json(force=True) or {} env = (data.get("env") or "").strip() if not env: return jsonify({"error": "Missing env"}), 400 conn = sqlite3.connect(DB_FILE) c = conn.cursor() c.execute("SELECT 1 FROM environments WHERE name = ?", (env,)) if not c.fetchone(): conn.close() return jsonify({"error": "Environment not found"}), 404 token = secrets.token_urlsafe(32) c.execute(""" INSERT INTO env_tokens(env_name, token, created_at, expires_at) VALUES(?, ?, datetime('now'), datetime('now', '+15 minutes')) """, (env, token)) conn.commit() conn.close() return jsonify({"ok": True, "env": env, "token": token}) if __name__ == '__main__': init_database() init_admin_tables() # Стартувај cleanup thread cleanup_thread = threading.Thread(target=cleanup_old_data, daemon=True) cleanup_thread.start() local_ip = get_local_ip() print("\n" + "=" * 70) print("🚀 ENHANCED LAN LOG SERVER со Sysmon") print("=" * 70) print(f"🌐 Server IP: {local_ip}:5555") print(f"📊 Dashboard: http://{local_ip}:5555/") print(f"📊 Alternative: http://localhost:5555/") print(f"🏥 Health check: http://{local_ip}:5555/ping") print("\n🛡️ Sysmon monitoring е активирано") print("✅ Кликни на секој компјутер за детални информации") print("✅ Сите Sysmon настани се прикажани во табла") print("✅ Кликни на секој настан за детали") print("=" * 70 + "\n") app.run(host='0.0.0.0', port=5555, debug=False, threaded=True)