#!/usr/bin/env python3 """Agent for implementing and auditing Just-In-Time access provisioning.""" import json import argparse from datetime import datetime from collections import Counter def audit_jit_requests(requests_path): """Audit JIT access requests for compliance and anomalies.""" with open(requests_path) as f: data = json.load(f) requests_list = data if isinstance(data, list) else data.get("requests", []) findings = [] by_status = Counter() by_resource = Counter() for req in requests_list: status = req.get("status", "unknown").lower() by_status[status] += 1 by_resource[req.get("resource", "unknown")] += 1 duration_hours = req.get("duration_hours", req.get("duration", 0)) if duration_hours > 8: findings.append({ "request_id": req.get("id", ""), "issue": f"Long access duration: {duration_hours}h", "severity": "MEDIUM", "user": req.get("requestor", req.get("user", "")), }) if duration_hours > 24: findings[-1]["severity"] = "HIGH" if status == "approved" and not req.get("approver"): findings.append({ "request_id": req.get("id", ""), "issue": "Auto-approved without approver record", "severity": "HIGH", }) granted = req.get("granted_at", "") expired = req.get("expired_at", req.get("revoked_at", "")) if granted and not expired and status == "active": granted_dt = datetime.fromisoformat(granted.replace("Z", "")) if (datetime.utcnow() - granted_dt).total_seconds() > duration_hours * 3600: findings.append({ "request_id": req.get("id", ""), "issue": "Access not revoked after expiration", "severity": "CRITICAL", "user": req.get("requestor", ""), }) return { "total_requests": len(requests_list), "by_status": dict(by_status), "by_resource": dict(by_resource.most_common(10)), "findings": findings, "anomaly_count": len(findings), } def audit_standing_privileges(privileges_path): """Identify standing privileges that should be converted to JIT.""" with open(privileges_path) as f: data = json.load(f) privs = data if isinstance(data, list) else data.get("privileges", []) candidates = [] for priv in privs: role = priv.get("role", priv.get("permission", "")).lower() usage = priv.get("last_used", priv.get("last_access", "")) is_privileged = any(k in role for k in [ "admin", "root", "owner", "superuser", "dba", "operator"]) days_unused = 0 if usage: try: usage_dt = datetime.fromisoformat(usage.replace("Z", "")) days_unused = (datetime.utcnow() - usage_dt).days except ValueError: pass if is_privileged and days_unused > 30: candidates.append({ "user": priv.get("user", priv.get("identity", "")), "role": priv.get("role", ""), "resource": priv.get("resource", priv.get("target", "")), "days_unused": days_unused, "severity": "CRITICAL" if days_unused > 90 else "HIGH", "recommendation": "Convert to JIT access", }) elif is_privileged: candidates.append({ "user": priv.get("user", ""), "role": priv.get("role", ""), "days_unused": days_unused, "severity": "MEDIUM", "recommendation": "Evaluate for JIT conversion", }) return { "total_privileges": len(privs), "jit_candidates": len(candidates), "critical_standing": sum(1 for c in candidates if c["severity"] == "CRITICAL"), "details": candidates[:30], } def calculate_jit_metrics(requests_path): """Calculate JIT program operational metrics.""" with open(requests_path) as f: data = json.load(f) requests_list = data if isinstance(data, list) else data.get("requests", []) approval_times = [] durations = [] auto_approved = 0 total = len(requests_list) for req in requests_list: if req.get("auto_approved", False): auto_approved += 1 requested = req.get("requested_at", "") approved = req.get("approved_at", "") if requested and approved: try: r_dt = datetime.fromisoformat(requested.replace("Z", "")) a_dt = datetime.fromisoformat(approved.replace("Z", "")) approval_times.append((a_dt - r_dt).total_seconds() / 60) except ValueError: pass duration = req.get("duration_hours", 0) if duration: durations.append(duration) return { "total_requests": total, "auto_approved_rate": round(auto_approved / total * 100, 1) if total else 0, "avg_approval_time_min": round(sum(approval_times) / len(approval_times), 1) if approval_times else 0, "avg_duration_hours": round(sum(durations) / len(durations), 1) if durations else 0, "max_duration_hours": max(durations) if durations else 0, "p95_approval_time_min": sorted(approval_times)[int(len(approval_times) * 0.95)] if approval_times else 0, } def generate_jit_policy(): """Generate JIT access provisioning policy.""" return { "risk_levels": { "low": { "approval": "auto-approve", "max_duration": "4h", "examples": ["Read-only database access", "Log viewer"], }, "medium": { "approval": "manager-approve", "max_duration": "8h", "examples": ["Application admin", "Deploy permissions"], }, "high": { "approval": "manager + security team", "max_duration": "4h", "examples": ["Production database write", "IAM admin"], }, "critical": { "approval": "CISO + manager + security team", "max_duration": "2h", "examples": ["Domain admin", "Root access", "Key management"], }, }, "controls": { "session_recording": True, "break_glass_procedure": True, "automatic_revocation": True, "audit_logging": True, "notification_on_grant": True, }, } def main(): parser = argparse.ArgumentParser(description="JIT Access Provisioning Agent") parser.add_argument("--requests", help="JIT requests log JSON") parser.add_argument("--privileges", help="Standing privileges JSON") parser.add_argument("--action", choices=["audit", "standing", "metrics", "policy", "full"], default="full") parser.add_argument("--output", default="jit_access_report.json") args = parser.parse_args() report = {"generated_at": datetime.utcnow().isoformat(), "results": {}} if args.action in ("audit", "full") and args.requests: result = audit_jit_requests(args.requests) report["results"]["audit"] = result print(f"[+] JIT audit: {result['total_requests']} requests, {result['anomaly_count']} issues") if args.action in ("standing", "full") and args.privileges: result = audit_standing_privileges(args.privileges) report["results"]["standing"] = result print(f"[+] Standing privs: {result['jit_candidates']} JIT candidates") if args.action in ("metrics", "full") and args.requests: metrics = calculate_jit_metrics(args.requests) report["results"]["metrics"] = metrics print(f"[+] Avg approval: {metrics['avg_approval_time_min']}min") if args.action in ("policy", "full"): policy = generate_jit_policy() report["results"]["policy"] = policy print("[+] JIT policy generated") with open(args.output, "w") as f: json.dump(report, f, indent=2, default=str) print(f"[+] Report saved to {args.output}") if __name__ == "__main__": main()