From 03229d4d08e875eb8b7f274829f98ab93232b215 Mon Sep 17 00:00:00 2001 From: Admin MPCZ Date: Mon, 27 Apr 2026 23:09:05 +0200 Subject: [PATCH] feat(qualys/agents): bouton Check + page audit cible Qualys agent (status + version + logs agent/systeme via SSH) --- app/routers/qualys.py | 16 ++++++ app/services/realtime_audit_service.py | 60 +++++++++++++++++++++++ app/templates/qualys_agent_audit.html | 67 ++++++++++++++++++++++++++ app/templates/qualys_agents.html | 2 + 4 files changed, 145 insertions(+) create mode 100644 app/templates/qualys_agent_audit.html diff --git a/app/routers/qualys.py b/app/routers/qualys.py index 668870e..723847e 100644 --- a/app/routers/qualys.py +++ b/app/routers/qualys.py @@ -1351,3 +1351,19 @@ async def qualys_asset_delete(request: Request, asset_id: int, db=Depends(get_db from app.services.qualys_service import delete_qualys_asset res = delete_qualys_asset(db, asset_id) return JSONResponse(res) + + +@router.get("/qualys/agents/{hostname}/audit-qualys", response_class=HTMLResponse) +def qualys_agent_audit_page(hostname: str, request: Request, db=Depends(get_db)): + """Audit cible Qualys Agent : status service + version + logs (agent + systeme).""" + user = get_current_user(request) + if not user: + return RedirectResponse(url="/login") + perms = get_user_perms(db, user) + if not can_view(perms, "qualys"): + return RedirectResponse(url="/dashboard") + from app.services.realtime_audit_service import audit_qualys_agent_only + audit = audit_qualys_agent_only(hostname) + ctx = base_context(request, db, user) + ctx.update({"audit": audit, "hostname": hostname}) + return templates.TemplateResponse("qualys_agent_audit.html", ctx) diff --git a/app/services/realtime_audit_service.py b/app/services/realtime_audit_service.py index 5346c8a..bcb828e 100644 --- a/app/services/realtime_audit_service.py +++ b/app/services/realtime_audit_service.py @@ -554,3 +554,63 @@ def save_audit_to_db(db, results): db.commit() return updated, inserted + + +# =========================================================================== +# AUDIT CIBLE QUALYS AGENT — pour bouton "Check" sur page Agents inactifs +# Utilise la meme mecanique de connexion que audit_single_server (DB-driven) +# =========================================================================== + +QUALYS_AGENT_CMDS = { + "agent_status": "systemctl status qualys-cloud-agent --no-pager 2>&1 | head -25 || /etc/init.d/qualys-cloud-agent status 2>&1 | head -25", + "agent_log": "tail -50 /var/log/qualys/qualys-cloud-agent.log 2>/dev/null || tail -50 /var/log/qualys-cloud-agent.log 2>/dev/null || echo \"log Qualys introuvable (chemins testes: /var/log/qualys/*, /var/log/qualys-cloud-agent.log)\"", + "system_log": "journalctl -u qualys-cloud-agent --no-pager -n 50 2>/dev/null || tail -50 /var/log/messages 2>/dev/null | grep -i qualys || echo \"journalctl + /var/log/messages indisponibles\"", + "agent_version": "/usr/local/qualys/cloud-agent/bin/qualys-cloud-agent.sh -v 2>&1 || rpm -q qualys-cloud-agent 2>/dev/null || echo \"version introuvable\"", +} + + +def audit_qualys_agent_only(hostname): + """Audit cible Qualys Agent uniquement: status service + version + logs. + Utilise _resolve + _connect + _run comme audit_single_server. + Retourne dict {hostname, status, connection_method, resolved_fqdn, ...cmds}.""" + result = { + "hostname": hostname, + "audit_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "status": "PENDING", + "connection_method": None, + "resolved_fqdn": None, + } + for k in QUALYS_AGENT_CMDS: + result[k] = None + + target = _resolve(hostname) + if not target: + result["status"] = "CONNECTION_FAILED" + result["connection_method"] = f"DNS: aucun suffixe resolu ({hostname})" + return result + result["resolved_fqdn"] = target + + client = _connect(target, hostname) + if not client: + result["status"] = "CONNECTION_FAILED" + result["connection_method"] = f"SSH: connexion echouee a {target}" + return result + + method = _resolve_ssh_method(hostname) or "ssh_key" + result["connection_method"] = f"{method} -> {target}" + + try: + for key, cmd in QUALYS_AGENT_CMDS.items(): + result[key] = _run(client, cmd) or "(empty)" + result["status"] = "OK" + except Exception as e: + result["status"] = "ERROR" + result["error_msg"] = str(e) + finally: + try: + client.close() + except Exception: + pass + + return result + diff --git a/app/templates/qualys_agent_audit.html b/app/templates/qualys_agent_audit.html new file mode 100644 index 0000000..d469f95 --- /dev/null +++ b/app/templates/qualys_agent_audit.html @@ -0,0 +1,67 @@ +{% extends 'base.html' %} +{% block title %}Audit Qualys Agent — {{ hostname }}{% endblock %} +{% block content %} + +
+
+

Audit Qualys Agent

+

{{ hostname }}{% if audit.resolved_fqdn %} → {{ audit.resolved_fqdn }}{% endif %}

+
+
+ Relancer + ← Retour +
+
+ + +
+
+
+ + {% if audit.status == 'OK' %}✓ Connecté{% elif audit.status == 'CONNECTION_FAILED' %}✗ Connexion échouée{% else %}⚠ {{ audit.status }}{% endif %} + + {{ audit.connection_method or '-' }} + {% if audit.error_msg %}{{ audit.error_msg }}{% endif %} +
+ {{ audit.audit_date }} +
+
+ +{% if audit.status == 'OK' %} + + +
+

État du service qualys-cloud-agent

+
{{ audit.agent_status or '(vide)' }}
+
+ + +
+

Version agent

+
{{ audit.agent_version or '(vide)' }}
+
+ + +
+

Log agent Qualys (50 dernières lignes)

+
{{ audit.agent_log or '(vide)' }}
+
+ + +
+

Log système (journalctl / messages)

+
{{ audit.system_log or '(vide)' }}
+
+ +{% else %} + +
+

Impossible de récupérer les informations de l'agent. Vérifie : SSH ouvert (port 22), méthode SSH configurée pour ce serveur dans la fiche serveur, agent installé, OS supporté.

+
+ +{% endif %} + +{% endblock %} diff --git a/app/templates/qualys_agents.html b/app/templates/qualys_agents.html index f5fafa5..32c3a51 100644 --- a/app/templates/qualys_agents.html +++ b/app/templates/qualys_agents.html @@ -290,6 +290,7 @@ function refreshAgents(mode) { Version agent Dernier check-in État + Action {% for a in inactive_agents %} @@ -299,6 +300,7 @@ function refreshAgents(mode) { {{ a.agent_version or '-' }} {% if a.last_checkin %}{{ (a.last_checkin|string)[:10] }}{% else %}-{% endif %} {{ a.etat or '-' }} + Check {% endfor %}