add syslog
This commit is contained in:
14
README.md
14
README.md
@@ -18,6 +18,18 @@ Analyze local logs:
|
|||||||
fgai analyze-logs --logs logs/fg_syslog.jsonl
|
fgai analyze-logs --logs logs/fg_syslog.jsonl
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Listen for FortiGate syslog locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fgai listen-syslog --port 5514 --output logs/fg_syslog.jsonl
|
||||||
|
```
|
||||||
|
|
||||||
|
UDP port `514` normally needs root privileges on Linux:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo .venv/bin/fgai listen-syslog --port 514 --output logs/fg_syslog.jsonl
|
||||||
|
```
|
||||||
|
|
||||||
Test FortiGate API access:
|
Test FortiGate API access:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -67,7 +79,7 @@ Example FortiGate syslog target, run on the FortiGate CLI and replace the server
|
|||||||
config log syslogd setting
|
config log syslogd setting
|
||||||
set status enable
|
set status enable
|
||||||
set server "192.0.2.50"
|
set server "192.0.2.50"
|
||||||
set port 514
|
set port 5514
|
||||||
set mode udp
|
set mode udp
|
||||||
set format default
|
set format default
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from .llm import ollama_summary
|
|||||||
from .logs import read_events, summarize_events
|
from .logs import read_events, summarize_events
|
||||||
from .mitigation import FortiGateClient, parse_allowlist, suggest_block_candidates
|
from .mitigation import FortiGateClient, parse_allowlist, suggest_block_candidates
|
||||||
from .policies import audit_policies, read_policies
|
from .policies import audit_policies, read_policies
|
||||||
|
from .syslog_server import listen_udp_syslog
|
||||||
|
|
||||||
|
|
||||||
def _print_json(data: object) -> None:
|
def _print_json(data: object) -> None:
|
||||||
@@ -93,6 +94,11 @@ def fetch_policies(args: argparse.Namespace) -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def listen_syslog(args: argparse.Namespace) -> int:
|
||||||
|
listen_udp_syslog(args.host, args.port, args.output)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def build_parser() -> argparse.ArgumentParser:
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
parser = argparse.ArgumentParser(description="Local FortiGate AI/ML inspection tool")
|
parser = argparse.ArgumentParser(description="Local FortiGate AI/ML inspection tool")
|
||||||
subparsers = parser.add_subparsers(required=True)
|
subparsers = parser.add_subparsers(required=True)
|
||||||
@@ -128,6 +134,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
fetch.add_argument("--output", help="Write JSON response to this file")
|
fetch.add_argument("--output", help="Write JSON response to this file")
|
||||||
fetch.set_defaults(func=fetch_policies)
|
fetch.set_defaults(func=fetch_policies)
|
||||||
|
|
||||||
|
listener = subparsers.add_parser("listen-syslog", help="Listen for UDP syslog and append to a local log file")
|
||||||
|
listener.add_argument("--host", default="0.0.0.0", help="Bind address")
|
||||||
|
listener.add_argument("--port", type=int, default=5514, help="UDP port. Use 514 only with sudo/capability.")
|
||||||
|
listener.add_argument("--output", default="logs/fg_syslog.jsonl", help="File to append received logs to")
|
||||||
|
listener.set_defaults(func=listen_syslog)
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
23
src/fgai/syslog_server.py
Normal file
23
src/fgai/syslog_server.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import socket
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def listen_udp_syslog(host: str, port: int, output: str, *, max_bytes: int = 65535) -> None:
|
||||||
|
output_path = Path(output)
|
||||||
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
sock.bind((host, port))
|
||||||
|
|
||||||
|
print(f"Listening for UDP syslog on {host}:{port}")
|
||||||
|
print(f"Writing logs to {output_path}")
|
||||||
|
with output_path.open("a", encoding="utf-8", buffering=1) as handle:
|
||||||
|
while True:
|
||||||
|
data, address = sock.recvfrom(max_bytes)
|
||||||
|
message = data.decode("utf-8", errors="replace").strip()
|
||||||
|
if not message:
|
||||||
|
continue
|
||||||
|
handle.write(f"{message}\n")
|
||||||
|
print(f"{address[0]}:{address[1]} {message}")
|
||||||
Reference in New Issue
Block a user