"""YakPanel - read-only security checklist (local server probes).""" import os import re from fastapi import APIRouter, Depends from app.api.auth import get_current_user from app.models.user import User from app.core.utils import read_file, exec_shell_sync router = APIRouter(prefix="/security", tags=["security"]) @router.get("/checklist") async def security_checklist(current_user: User = Depends(get_current_user)): """Non-destructive hints: SSH config, firewall helper, fail2ban. Not a full audit.""" items: list[dict] = [] sshd = "/etc/ssh/sshd_config" body = read_file(sshd) if os.path.isfile(sshd) else None if isinstance(body, str) and body: if re.search(r"^\s*PasswordAuthentication\s+no\s*$", body, re.MULTILINE | re.IGNORECASE): items.append({ "id": "ssh_password_auth", "ok": True, "title": "SSH password auth", "detail": "PasswordAuthentication appears set to no (prefer key-based login).", }) elif re.search(r"^\s*PasswordAuthentication\s+yes", body, re.MULTILINE | re.IGNORECASE): items.append({ "id": "ssh_password_auth", "ok": False, "title": "SSH password auth", "detail": "PasswordAuthentication is yes — consider disabling and using SSH keys.", }) else: items.append({ "id": "ssh_password_auth", "ok": None, "title": "SSH password auth", "detail": "Could not find an explicit PasswordAuthentication line (defaults depend on distro).", }) else: items.append({ "id": "ssh_password_auth", "ok": None, "title": "SSH password auth", "detail": "/etc/ssh/sshd_config not readable from the panel process.", }) ufw_out, _ = exec_shell_sync("ufw status 2>/dev/null", timeout=5) ufw = ufw_out or "" if "Status: active" in ufw: items.append({"id": "ufw", "ok": True, "title": "UFW firewall", "detail": "UFW reports active."}) elif "Status: inactive" in ufw: items.append({ "id": "ufw", "ok": None, "title": "UFW firewall", "detail": "UFW installed but inactive — enable if this host is public.", }) else: items.append({ "id": "ufw", "ok": None, "title": "UFW firewall", "detail": "UFW not detected (OK if you use firewalld/iptables only).", }) f2_out, _ = exec_shell_sync("systemctl is-active fail2ban 2>/dev/null", timeout=5) f2_active = (f2_out or "").strip() == "active" items.append({ "id": "fail2ban", "ok": f2_active, "title": "fail2ban", "detail": "fail2ban is active." if f2_active else "fail2ban not active (optional hardening).", }) return {"items": items, "disclaimer": "YakPanel reads local settings only; this is not a compliance scan."}