Add read-only OCSentinel debug dashboard
This commit is contained in:
109
infra/debug-dashboard/app.py
Normal file
109
infra/debug-dashboard/app.py
Normal file
@@ -0,0 +1,109 @@
|
||||
import hmac
|
||||
import json
|
||||
import os
|
||||
from functools import wraps
|
||||
|
||||
import psycopg
|
||||
from flask import Flask, Response, abort, render_template
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
def db_connection():
|
||||
return psycopg.connect(
|
||||
host=os.environ["DB_HOST"],
|
||||
port=os.getenv("DB_PORT", "5432"),
|
||||
dbname=os.environ["DB_NAME"],
|
||||
user=os.environ["DB_USER"],
|
||||
password=os.environ["DB_PASSWORD"],
|
||||
connect_timeout=5,
|
||||
)
|
||||
|
||||
|
||||
def requires_auth(view):
|
||||
@wraps(view)
|
||||
def wrapped(*args, **kwargs):
|
||||
auth = __import__("flask").request.authorization
|
||||
expected_user = os.environ["DASHBOARD_USER"]
|
||||
expected_password = os.environ["DASHBOARD_PASSWORD"]
|
||||
valid = auth and hmac.compare_digest(auth.username or "", expected_user) and hmac.compare_digest(auth.password or "", expected_password)
|
||||
if not valid:
|
||||
return Response("Authentication required", 401, {"WWW-Authenticate": 'Basic realm="OCSentinel Debug"'})
|
||||
return view(*args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@requires_auth
|
||||
def overview():
|
||||
with db_connection() as connection, connection.cursor() as cursor:
|
||||
cursor.execute("SELECT * FROM ocsentinel.organization_summary")
|
||||
summary = cursor.fetchone()
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT machine_name, organization_name, received_at, alert_state,
|
||||
total_events, unique_ip_count, cve_total, cve_critical
|
||||
FROM (
|
||||
SELECT machine_name, received_at, alert_state, total_events,
|
||||
unique_ip_count, cve_total, cve_critical,
|
||||
payload #>> '{NinjaOne,OrganizationName}' AS organization_name
|
||||
FROM ocsentinel.current_device_status
|
||||
) AS status
|
||||
ORDER BY received_at DESC NULLS LAST
|
||||
LIMIT 100
|
||||
"""
|
||||
)
|
||||
reports = cursor.fetchall()
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT machine_name, alert_state, total_events, unique_ip_count,
|
||||
received_at, payload
|
||||
FROM ocsentinel.current_device_status
|
||||
WHERE alert_state IN ('warning', 'critical')
|
||||
ORDER BY CASE alert_state WHEN 'critical' THEN 0 ELSE 1 END, received_at DESC
|
||||
"""
|
||||
)
|
||||
alerts = cursor.fetchall()
|
||||
|
||||
return render_template("overview.html", summary=summary, reports=reports, alerts=alerts)
|
||||
|
||||
|
||||
@app.get("/device/<machine_name>")
|
||||
@requires_auth
|
||||
def device(machine_name):
|
||||
with db_connection() as connection, connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT machine_name, first_seen_at, last_seen_at, last_client_version,
|
||||
generated_at_utc, received_at, alert_state, base_alert_state,
|
||||
total_events, unique_ip_count, cve_total, cve_critical, payload
|
||||
FROM ocsentinel.current_device_status
|
||||
WHERE machine_name = %s
|
||||
""",
|
||||
(machine_name,),
|
||||
)
|
||||
report = cursor.fetchone()
|
||||
|
||||
if report is None:
|
||||
abort(404)
|
||||
|
||||
payload = report[12]
|
||||
return render_template("device.html", report=report, payload=payload, payload_pretty=json.dumps(payload, indent=2, ensure_ascii=False))
|
||||
|
||||
|
||||
@app.get("/healthz")
|
||||
def healthz():
|
||||
try:
|
||||
with db_connection() as connection, connection.cursor() as cursor:
|
||||
cursor.execute("SELECT 1")
|
||||
return {"status": "ok"}
|
||||
except Exception:
|
||||
return {"status": "unavailable"}, 503
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8080)
|
||||
Reference in New Issue
Block a user