Fix Gitea issues #1 and #2, add firewall automation, forwarder telemetry, and dynamic key rotation
CI Test Suite / Run Component Tests & Pipeline Verification (push) Successful in 1m24s
CI Test Suite / Run Component Tests & Pipeline Verification (push) Successful in 1m24s
This commit is contained in:
@@ -8,6 +8,8 @@ import os
|
||||
import sys
|
||||
import json
|
||||
import socket
|
||||
import shutil
|
||||
import ctypes
|
||||
import subprocess
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
@@ -26,6 +28,141 @@ CONFIG_FILE_NAME = "client_config.json"
|
||||
STATE_FILE_NAME = "client_state.json"
|
||||
|
||||
|
||||
def collect_system_metrics() -> Dict[str, Any]:
|
||||
"""
|
||||
Collects cross-platform host health and resource telemetry without requiring external wheels.
|
||||
Returns:
|
||||
{
|
||||
"cpu_percent": float,
|
||||
"memory_percent": float,
|
||||
"memory_total_mb": int,
|
||||
"memory_free_mb": int,
|
||||
"disk_percent_free": float,
|
||||
"disk_total_gb": float,
|
||||
"disk_free_gb": float
|
||||
}
|
||||
"""
|
||||
metrics = {
|
||||
"cpu_percent": 0.0,
|
||||
"memory_percent": 0.0,
|
||||
"memory_total_mb": 0,
|
||||
"memory_free_mb": 0,
|
||||
"disk_percent_free": 0.0,
|
||||
"disk_total_gb": 0.0,
|
||||
"disk_free_gb": 0.0
|
||||
}
|
||||
# 1. Disk metrics
|
||||
try:
|
||||
path = "C:\\" if sys.platform == "win32" else "/"
|
||||
total, used, free = shutil.disk_usage(path)
|
||||
metrics["disk_total_gb"] = round(total / (1024 ** 3), 2)
|
||||
metrics["disk_free_gb"] = round(free / (1024 ** 3), 2)
|
||||
if total > 0:
|
||||
metrics["disk_percent_free"] = round((free / total) * 100, 1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. Memory metrics
|
||||
try:
|
||||
if sys.platform == "win32":
|
||||
class MEMORYSTATUSEX(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("dwLength", ctypes.c_ulong),
|
||||
("dwMemoryLoad", ctypes.c_ulong),
|
||||
("ullTotalPhys", ctypes.c_ulonglong),
|
||||
("ullAvailPhys", ctypes.c_ulonglong),
|
||||
("ullTotalPageFile", ctypes.c_ulonglong),
|
||||
("ullAvailPageFile", ctypes.c_ulonglong),
|
||||
("ullTotalVirtual", ctypes.c_ulonglong),
|
||||
("ullAvailVirtual", ctypes.c_ulonglong),
|
||||
("sullAvailExtendedVirtual", ctypes.c_ulonglong),
|
||||
]
|
||||
stat = MEMORYSTATUSEX()
|
||||
stat.dwLength = ctypes.sizeof(MEMORYSTATUSEX)
|
||||
if ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)):
|
||||
metrics["memory_percent"] = float(stat.dwMemoryLoad)
|
||||
metrics["memory_total_mb"] = int(stat.ullTotalPhys / (1024 * 1024))
|
||||
metrics["memory_free_mb"] = int(stat.ullAvailPhys / (1024 * 1024))
|
||||
else:
|
||||
# Linux: read /proc/meminfo
|
||||
if os.path.exists("/proc/meminfo"):
|
||||
mem_total = 0
|
||||
mem_avail = 0
|
||||
with open("/proc/meminfo", "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
parts = line.split(":")
|
||||
if len(parts) == 2:
|
||||
k, v = parts[0].strip(), parts[1].strip().split()[0]
|
||||
if k == "MemTotal":
|
||||
mem_total = int(v)
|
||||
elif k in ("MemAvailable", "MemFree") and mem_avail == 0:
|
||||
mem_avail = int(v)
|
||||
if mem_total > 0:
|
||||
metrics["memory_total_mb"] = mem_total // 1024
|
||||
metrics["memory_free_mb"] = mem_avail // 1024
|
||||
metrics["memory_percent"] = round(((mem_total - mem_avail) / mem_total) * 100, 1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3. CPU metrics
|
||||
try:
|
||||
if hasattr(os, "getloadavg"):
|
||||
load1, _, _ = os.getloadavg()
|
||||
cpu_count = os.cpu_count() or 1
|
||||
metrics["cpu_percent"] = round(min(100.0, (load1 / cpu_count) * 100), 1)
|
||||
else:
|
||||
try:
|
||||
import psutil
|
||||
metrics["cpu_percent"] = float(psutil.cpu_percent(interval=None))
|
||||
except ImportError:
|
||||
metrics["cpu_percent"] = 0.0
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return metrics
|
||||
|
||||
|
||||
def is_routine_service_noise(signature: str, message: str, severity: str) -> bool:
|
||||
"""
|
||||
Identifies high-frequency benign background service start/stop cycles
|
||||
(e.g., sppsvc, edgeupdate, trustedinstaller, wuauserv, backgroundTaskHost)
|
||||
to prevent false-positive anomaly fatigue and consecutive_run metric bloat.
|
||||
"""
|
||||
if str(severity).upper() != "INFO":
|
||||
return False
|
||||
sig_low = (signature or "").lower()
|
||||
msg_low = (message or "").lower()
|
||||
routine_services = [
|
||||
"sppsvc",
|
||||
"edgeupdate",
|
||||
"trustedinstaller",
|
||||
"wuauserv",
|
||||
"backgroundtaskhost",
|
||||
"service control manager",
|
||||
"systemd",
|
||||
"cron"
|
||||
]
|
||||
is_routine_svc = any(s in sig_low or s in msg_low for s in routine_services)
|
||||
if is_routine_svc:
|
||||
lifecycle_phrases = [
|
||||
"entered the running state",
|
||||
"entered the stopped state",
|
||||
"entered the start pending state",
|
||||
"entered the stop pending state",
|
||||
"started successfully",
|
||||
"stopped successfully",
|
||||
"service stopped",
|
||||
"service started",
|
||||
"session opened",
|
||||
"session closed",
|
||||
"clean",
|
||||
"reloaded"
|
||||
]
|
||||
if any(p in msg_low for p in lifecycle_phrases):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_machine_identifier() -> str:
|
||||
"""
|
||||
Returns the fully qualified hostname of the machine sending/collecting logs.
|
||||
|
||||
Reference in New Issue
Block a user