first add

This commit is contained in:
larssand
2026-06-18 21:03:07 +02:00
parent 191ac4cf55
commit 05038c6ad7
22 changed files with 701 additions and 0 deletions

115
src/fgai/cli.py Normal file
View File

@@ -0,0 +1,115 @@
from __future__ import annotations
import argparse
import json
import sys
from .llm import ollama_summary
from .logs import read_events, summarize_events
from .mitigation import FortiGateClient, parse_allowlist, suggest_block_candidates
from .policies import audit_policies, read_policies
def _print_json(data: object) -> None:
print(json.dumps(data, indent=2, sort_keys=True))
def analyze_logs(args: argparse.Namespace) -> int:
events = read_events(args.logs)
candidates = suggest_block_candidates(
events,
min_events=args.min_events,
min_score=args.min_score,
allowlist=parse_allowlist(args.allowlist),
)
_print_json(
{
"summary": summarize_events(events),
"block_candidates": [
{"src_ip": candidate.src_ip, "score": candidate.score, "reasons": candidate.reasons}
for candidate in candidates
],
}
)
if args.llm:
print("\nLLM summary:")
print(ollama_summary([], candidates, args.model))
return 0
def audit_policy_file(args: argparse.Namespace) -> int:
findings = audit_policies(read_policies(args.config))
_print_json([finding.__dict__ for finding in findings])
if args.llm:
print("\nLLM summary:")
print(ollama_summary(findings, [], args.model))
return 0
def suggest_blocks(args: argparse.Namespace) -> int:
events = read_events(args.logs)
candidates = suggest_block_candidates(
events,
min_events=args.min_events,
min_score=args.min_score,
allowlist=parse_allowlist(args.allowlist),
)
if not candidates:
print("No block candidates met the current thresholds.")
return 0
for candidate in candidates:
print(f"{candidate.src_ip} score={candidate.score} reasons={', '.join(candidate.reasons)}")
if not args.execute:
print(" dry-run: not blocked")
continue
client = FortiGateClient.from_env()
reason = f"fgAI UTM mitigation score={candidate.score}"
response = client.quarantine_ip(candidate.src_ip, args.expiry_minutes, reason)
print(f" blocked: {response}")
return 0
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Local FortiGate AI/ML inspection tool")
subparsers = parser.add_subparsers(required=True)
logs = subparsers.add_parser("analyze-logs", help="Analyze local FortiGate logs")
logs.add_argument("--logs", required=True, help="Path to syslog JSONL or key/value log file")
logs.add_argument("--min-events", type=int, default=3)
logs.add_argument("--min-score", type=int, default=7)
logs.add_argument("--allowlist", default=None, help="Comma-separated IPs/CIDRs never to block")
logs.add_argument("--llm", action="store_true", help="Ask local Ollama to summarize results")
logs.add_argument("--model", default=None, help="Ollama model name")
logs.set_defaults(func=analyze_logs)
policies = subparsers.add_parser("audit-policies", help="Audit FortiOS firewall policy config")
policies.add_argument("--config", required=True, help="Path to FortiOS config backup")
policies.add_argument("--llm", action="store_true", help="Ask local Ollama to summarize results")
policies.add_argument("--model", default=None, help="Ollama model name")
policies.set_defaults(func=audit_policy_file)
blocks = subparsers.add_parser("suggest-blocks", help="Suggest or execute guarded source IP blocks")
blocks.add_argument("--logs", required=True, help="Path to syslog JSONL or key/value log file")
blocks.add_argument("--min-events", type=int, default=3)
blocks.add_argument("--min-score", type=int, default=7)
blocks.add_argument("--allowlist", default=None, help="Comma-separated IPs/CIDRs never to block")
blocks.add_argument("--execute", action="store_true", help="Actually call the FortiGate quarantine API")
blocks.add_argument("--expiry-minutes", type=int, default=60)
blocks.set_defaults(func=suggest_blocks)
return parser
def main(argv: list[str] | None = None) -> int:
parser = build_parser()
args = parser.parse_args(argv)
try:
return args.func(args)
except Exception as exc:
print(f"fgai: {exc}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())