fengarde
Health Uyari
- License — License: Apache-2.0
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 8 GitHub stars
Code Gecti
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
Open-source SIEM pipeline: raw security logs → OCSF → correlation rules → alerts. OT and IT in one pipeline. Local-first AI triage, 10-minute self-hosted quickstart.
FENGARDE
Badges above reflect the most recent real run on
main, not a promise —
CI/CodeQL/Scorecard have all run at least once as of 2026-07-19 (post-merge
of PR#2); Scorecard's own findings drove a supply-chain pinning pass
(workflow SHAs, Docker base image digests) that took its score from an
initial ~54 open alerts down to 19, all of which are accepted policy-level
trade-offs (seeSSOT.md§1), not unaddressed gaps.
The open-source SIEM for the European industrial Mittelstand — turns your
factory and IT logs into draft NIS2 incident notifications, with AI triage that
never leaves your network.
FENGARDE ingests logs from multiple sources, normalizes them to a single schema
(OCSF), runs correlation rules over a sliding window,
and surfaces alerts in a dashboard. Storage is OpenSearch. Every service is
independent and talks to the rest of the system only through a message bus, so
you can scale or replace any piece without rewriting the others.
Three things make it different from a generic self-hosted SIEM:
- OCSF-native, not retrofitted. Every source normalizes to the same open
schema from day one, so one rule covers every source that emits the event —
an SSH login, a Windows login and an Active Directory login are one
brute-force detection, not three. Instrument once, stay portable, no vendor
log-format lock-in. - OT and IT in one pipeline. OPC UA and Modbus/TCP normalize into the same
OCSF schema as Active Directory, Windows and your firewall, so the plant floor
runs through the same detection engine, dashboard and report path as the
office network — not a second toolchain. - AI triage that never leaves your network. Local Ollama by default, with a
documented stub fallback — your alert data is never piped through a
third-party LLM API.
On OpenSearch vs. Elastic: storing in OpenSearch means there is no license
asterisk on the storage engine (ADR 003),
which is worth knowing but is not a differentiator against the comparison
people actually make — Wazuh's indexer is an OpenSearch fork too. It only
distinguishes FENGARDE from Elastic-based stacks, so it is not listed above.
New in v0.4: parser packs for the sources this wedge actually needs — MCP/AI-agent
tool-call audit logs, industrial OPC UA control-system events, and n8n automation-platform
logs — plus an incident-report draft hook (the open half of a NIS2/DORA
regulatory-evidence feature; see contracts/reporting.md).
Quickstart (10 minutes)
git clone https://github.com/supermhel/fengarde.git && cd fengarde
make preflight # doctor: checks vm.max_map_count, Docker RAM, free ports
make demo # docker compose up -- a real SSH brute-force alert appears
# in the dashboard within ~30-60s, no manual step
# open http://localhost:8080
No Docker on hand? The whole detection pipeline runs zero-infra:make e2e proves the same SSH brute-force → real alert → idempotent-replay
path with no Redis/OpenSearch/Docker at all.
Demo
See FENGARDE turn a real SSH brute-force burst into a real alert — with zero
infrastructure (no Docker, no Redis, no OpenSearch):
bash tools/demo.sh
This feeds 10 failed SSH logins from one IP through the whole pipeline
(normalize → detect → triage → index), shows the brute-force alert come
out, and replays the event to prove the alert is idempotent (deduped, not
duplicated).
Try it yourself
# Zero-infra: SSH brute-force -> real alert, idempotent under replay (no Docker).
make e2e # runs demo_e2e.py (fast, no narration)
bash tools/demo.sh # same test with banner + story narration
# # Windows: powershell -File tools\demo.ps1
# Full live stack (collect -> normalize -> detect -> index -> dashboard):
make up # docker compose up -d
# ...then open the FENGARDE alert console at:
# http://localhost:8080
make down # stop the stack and remove volumes
First time on the live stack? Run
make preflightfirst — it checksvm.max_map_count, Docker RAM, and free ports, and prints the exact fix for
anything missing. See Pre-flight.
What's real (v0.5.0 released — v0.4.0/v0.5.0 tags live on main)
FENGARDE ships a working detection pipeline. We are deliberate about what is
real versus what is planned — this is a security tool, so accuracy matters more than
a long feature list. See SSOT.md for the authoritative, continuously
updated status — this table is a snapshot, that file is the source of truth.
| Capability | Status | Notes |
|---|---|---|
| Detection pipeline (collect → normalize → detect → index → dashboard) | ✅ Works | End-to-end since v0.1 |
| Parsers (16) | ✅ Works | Cisco ASA, Active Directory, VMware vSphere, Linux SSH, generic syslog, Windows Event Log (incl. account-change 4720/4722/4726/4728/4732), DB audit (GRANT/REVOKE/ALTER), MCP/AI-agent tool-call audit, OPC UA/OT audit, n8n automation-platform audit, DNS query log, Kubernetes audit, CEF (generic appliance), AWS CloudTrail, Sysmon (process/network/file), Modbus/TCP protocol-anomaly detector — all → OCSF |
| Detection rules (27) | ✅ Works | Brute-force (per-IP and sourceless/per-target-host), port-scan, lateral-movement, password-spray, privileged-group grant, after-hours admin, impossible-travel, bank DB priv-esc, DC mass-VM-delete, agent credential-file access / tool-call burst / prompt-injection indicator / destructive-command / egress-non-allowlisted-domain, OT write-outside-maintenance / new-engineering-connection / config-change / Modbus unauthorized-write, n8n new-webhook-exposed / workflow-modified-after-hours, DNS exfil, privileged-container-create, cloud root console login, mass DB-object read, rapid account create/delete, beaconing (periodicity primitive) |
| Rule grammar | ✅ Works | Boolean logic, comparison operators (gt/gte/lt/lte/ne), allowlist suppression (not_in, CIDR + exact), time-of-day (outside_hours) — all fail closed on malformed input |
| Rule prefilter | ✅ Works | Rules bucketed by class_uid equality selection; events only evaluated against candidate rules (fixes the O(rules×events) scan) |
| Anti-dormancy guardrail | ✅ Works | tools/check_rule_producers.py in the CI gate proves every rule's selections are satisfiable by values a real parser actually emits |
| Rule boundary guardrail | ✅ Works for stateful rules; ⚠️ stateless rules untested | Satisfiable ≠ fires, and firing ≠ firing only when it should. eval/attack/fire_check.py proves all 26 MITRE-tagged rules fire on their own fixture, then replays each stateful rule one under its threshold and spread just past its window and requires silence — catching a rule that is too loose, which shows up as false-positive volume months later rather than as a dead rule. 12/12 stateful rules hold; the 14 stateless ones have no generatable near-miss and are reported untested, not passing |
| AI triage (local Ollama) | ✅ Works | Real local-LLM triage via OLLAMA_URL; degrades to a documented passthrough stub with zero infra |
| Triage workflow | ✅ Works | Status + analyst note per alert, persisted via the WS-3 triage API, editable in the dashboard; concurrent writes protected at two layers (in-process lock + OpenSearch optimistic concurrency) |
| Incident-report draft hook | ✅ Works (v0.4) | POST /alerts/{id}/report renders a generic markdown incident report from alert facts, always marked status: draft with a disclaimer; the regulated-content backend is a paid, optional add-on (contracts/reporting.md) |
| Opt-in auth | ✅ Works (v0.4) | Shared-secret FENGARDE_API_KEY on the triage/inventory APIs, opt-in dashboard basic-auth, opt-in Redis AUTH — unset (default) stays fully open, matching v0.1-v0.3 behavior |
| Syslog UDP listener (WS-1) | ✅ Works | Live datagrams → raw.events |
| Multi-tenancy | ✅ Works | tenant_id threaded collector→normalize→detect→index; per-tenant OpenSearch indices, per-tenant rule enablement; isolation proven by tools/test_multi_tenant_isolation.py |
| RBAC | ✅ Works, opt-in | Per-user accounts/roles/tenant scoping via FENGARDE_RBAC_DB; session cookies, CSRF protection, dashboard login UI; unset (default) = pre-RBAC API-key-only behavior, byte-for-byte unchanged |
| Versioned REST API | ✅ Works | contracts/triage-api.yaml (OpenAPI 3.1); every route reachable bare or under /api/v1/...; spec-vs-code drift is CI-tested |
| Outbound alert webhooks | ✅ Works, opt-in | HMAC-SHA256-signed deliveries to operator-configured URLs (contracts/webhooks/*.yml, ships empty); see docs/webhooks.md |
| Parser/rule plugin interface | ✅ Works | External pip package can ship a parser or rule pack via Python entry points, no fork needed; see docs/plugin-development.md |
| Chaos-tested delivery | ✅ Works | make chaos: 40 scenarios, each pipeline service SIGKILLed mid-replay — zero lost, zero duplicate alerts (2026-07-18 run) |
| NIS2 (DE) report template | ✅ Works | Deterministic German/English NIS2 Art. 23 / §32 BSIG draft, additive on the report hook (?template=nis2); every entity-specific fact renders as an explicit [ANALYST MUST PROVIDE] placeholder, never fabricated |
| SNMP parser | 🚧 Planned | Deferred — good first issue |
| NetFlow parser | 🚧 Planned | Deferred (binary format) |
| Custom JSON parser | 🚧 Planned | Deferred |
No AI required. The pipeline produces real alerts with zero infra and no LLM;
Ollama triage is an optional layer that degrades gracefully to a stub.
Performance (fengarde-bench)
python tools/fengarde_bench.py --events 20000 --mixed
One-command, reproducible by anyone with a clone — no Docker required. Measured
2026-07-16 on a 4 vCPU / 15 GB sandbox host (not a fixed reference VPS, see caveat
below):
| Metric | Value |
|---|---|
Sustained EPS (5,000 events, linux_ssh only) |
~12,950 events/sec |
Sustained EPS (20,000 events, mixed ssh/asa/generic_syslog) |
~13,750 events/sec |
| Peak resident memory (20,000-event run) | ~84 MB |
Read before citing these numbers anywhere: this is a zero-infra CPU-bound
baseline — one process, the in-memory bus, MemoryStore — measuring how fast
WS-2/WS-4/WS-3's Python code processes a batch. It excludes real Redis network
I/O, OpenSearch indexing latency, and any real queuing/backpressure behavior a
live stack has. It is not a "FENGARDE handles N events/sec in production"
claim, and there is no p50/p99 ingest→alert latency number yet — batch
processing has no realistic queuing delay to measure; that number only means
something against a live bus. The live-stack number on a defined reference box
is still an open TODO (needs Docker, which this repo's current CI/dev
environment doesn't always have).
Prerequisites
- Docker Desktop (or Docker Engine + Compose v2) with ≥ 4 GB RAM allocated to Docker.
- OS: Linux, macOS, or Windows via WSL2. (The demo stack runs Linux containers; the
pre-flight and demo scripts are POSIXsh.) - Python 3 — only if you want to run the contract tests or contribute a parser.
You do not need Docker to add a parser (see Contributing).
⚠️ PRE-FLIGHT — read this before you start the stack
OpenSearch (the storage engine) will not boot on Linux/WSL2 with default kernel
settings. It needs the vm.max_map_count limit raised. Run this once per machine
(it resets on reboot):
sudo sysctl -w vm.max_map_count=262144
To make it persist across reboots:
echo 'vm.max_map_count=262144' | sudo tee /etc/sysctl.d/99-fengarde.conf
On macOS with Docker Desktop this is handled inside the Docker VM and you can
usually skip it. On Linux/WSL2 it is required — without it you get a JVM crash,
not a helpful error.
make preflight (below) checks this for you and prints the exact fix if anything is
missing.
Quick start
# 1. Check your machine is ready (vm.max_map_count, Docker RAM, free ports)
make preflight
# 2. Bring up the full stack
make demo
make demo runs the pre-flight check, then starts every service with Docker Compose.
All services are long-running daemons with a /health endpoint and a restart policy.
Want to see FENGARDE work without Docker? Run
make e2e— a zero-infra
acceptance test that feeds a real SSH brute-force burst through the whole pipeline
and shows the alert come out the other end (details in
How to see the alert).Honest status: the in-stack live feeder and the live dashboard (which reads
alerts straight from OpenSearch) are the remaining DX2/DX4 items — see
the build plan. The
end-to-end detection logic itself is proven today bymake e2e.
Other handy targets:
make e2e # zero-infra ACCEPTANCE test: SSH brute-force -> real alert (no Docker)
make test # run the full zero-infra contract test suite (no Docker needed)
make up # start the stack detached (docker compose up -d)
make down # stop the stack and remove volumes
Ports
| Port | Service | What it is |
|---|---|---|
| 6379 | redis (siem-bus) |
Message bus between services |
| 9200 | opensearch (siem-store) |
Event/alert storage + query API |
| 5601 | dashboards (siem-dashboards) |
OpenSearch Dashboards |
| 8000 | ws6-inventory |
Inventory API (IP/MAC history) |
| 8080 | ws7-dashboard |
FENGARDE alert console |
| 5514/udp | ws1-collectors |
Live syslog ingestion (unauthenticated — trusted segment only) |
| 8013 | ws3-indexer |
Triage API — internal only, the dashboard proxies to it container-to-container; not published to the host |
make preflight checks the published ports are free before you start.
How to see the alert
The acceptance test for v0.1 is a real brute-force alert, not mock data:
- The signal. 10 failed authentication events from a single source IP within
60 seconds. FENGARDE produces these from a Linux SSHFailed passwordline or a
WindowsEventID 4625— both normalize to the same OCSF Authentication event
(class_uid: 3002,activity_id: 4). - The rule.
contracts/rules/common_bruteforce.yml
matches those events, grouped bysrc_endpoint.ip, withthreshold: 10andwindow_seconds: 60. It is source-agnostic, so it fires identically on SSH or AD. - The alert. When the 10th failure from one IP lands inside the window, the
detection service emits an alert that flows to the indexer and into the dashboard
alert list.
See the whole pipeline produce the alert with no infrastructure — the
acceptance test injects 10 failed SSH logins and asserts a real brute-force alert
reaches the index, and that replaying the same event reuses the same alert id
(idempotent, so at-least-once delivery never double-alerts):
make e2e # or: python tools/demo_e2e.py
Expected tail:
ALERT: Authentication brute-force from single source src=203.0.113.5 score=70 id=...
T7 OK: replay reused alert_id ... -> deduped (alerts count stayed 2)
[OK] FENGARDE v0.1 acceptance: SSH brute-force -> real alert in the index, idempotent under replay. Zero infra.
Prefer a unit-level check? The WS-4 detection contract test asserts the rule fires on
the 10th attempt: cd services/ws4-detection && python test_contract.py.
For the full Dockerized stack (collect → normalize → detect → index → dashboard), runmake demo and open the dashboard at http://localhost:8080.
Architecture
WS-1 Collectors ─raw.events─▶ WS-2 Normalization ─normalized.events─┬─▶ WS-3 Indexer ─▶ OpenSearch
(Cisco ASA / AD / (parsers → OCSF) ├─▶ WS-6 Inventory (IP/MAC)
VMware / Linux SSH) └─▶ WS-4 Detection ─scored.events─▶ WS-3
│ alerts ─▶ WS-3 / WS-7
└─ai.requests─▶ WS-5 AI (stub) ─▶ WS-3/WS-7
WS-7 Dashboard ◀── WS-6 API + alerts
The only coupling between services is the message bus. Everything else is a frozen
contract under contracts/. All source-format heterogeneity is absorbed
at the edge (one parser per source in WS-2); the interior of the system handles a single
schema (OCSF).
| WS | Service | Role | v0.1 status |
|---|---|---|---|
| 1 | services/ws1-collectors |
Collect logs → raw.events |
✅ |
| 2 | services/ws2-normalization |
Parsers → validated OCSF events | ✅ (16 parsers) |
| 3 | services/ws3-indexer |
Routing + OpenSearch indexing (idempotent) | ✅ |
| 4 | services/ws4-detection |
Correlation rules + scoring + windowing | ✅ (27 rules) |
| 5 | services/ws5-ai |
Triage | ✅ real local-LLM (Ollama) since v0.2, stub fallback |
| 6 | services/ws6-inventory |
IP/MAC inventory API (SQLite) | ✅ |
| 7 | services/ws7-dashboard |
Alert console | ✅ |
For current status and the forward roadmap, see SSOT.md (read that first).
For historical design context: the v0.1 build plan
and docs/PHASE0_README.md.
Evaluation & detection quality
"A rule passes CI" and "a rule actually fires on real attack traffic" are
different claims — this repo keeps them separate rather than conflating them,
across three eval lanes under eval/:
| Command | What it proves | Needs |
|---|---|---|
make attack-scorecard |
Declared MITRE ATT&CK/ATT&CK-ICS/ATLAS coverage (which techniques a rule's mitre: block claims), an empirical check that every tagged rule's condition actually fires on its own real producer fixture through the live detection engine, and a boundary check that each stateful rule stays silent one event under its threshold and when its events are spread past its window — three distinct claims, never merged into one number |
Zero infra |
make eval-detection |
Independent-oracle replay: real Windows Security/Sysmon attack corpora (EVTX-ATTACK-SAMPLES, splunk/attack_data) fed through the live pipeline, alerts checked against a ground truth computed separately from the engine's own logic — this is what catches a bug a unit test mirroring the engine's own code cannot. See eval/detection_accuracy/README.md for dataset licensing and setup (both corpora are third-party, not vendored; the target skips cleanly with no datasets fetched) |
Real datasets fetched separately |
make nis2-demo |
End-to-end proof that a real alert becomes a structurally-compliant NIS2 draft (disclaimer, draft status, no fabricated entity facts) — the same checklist eval/report_generator/'s harness runs across 12 synthetic scenarios × 3 stages × 2 languages in CI |
Zero infra |
None of these are optional add-ons bolted on for show — attack-scorecard and
the report-generator eval both run in run_all_tests.sh; eval-detection
is deliberately excluded from the zero-infra gate (a target that always
skips would be noise there) but is the harness that actually caught real
false negatives in the brute-force rule during the 2026-07-21 audit pass.
Contributing
The fastest way to contribute is to add a parser — and you do not need Docker
or OpenSearch to do it. The whole inner loop is one command:
cd services/ws2-normalization && python test_contract.py
See CONTRIBUTING.md for the contribution workflow and
docs/adding-a-parser.md for a step-by-step walkthrough
(copy linux_ssh.py, make three small edits, verify). The three deferred parsers above
are the obvious first PRs — or propose a new detection rule via the
rule request template.
Monitoring AI agents/MCP servers? See docs/agent-monitoring.md.
Curious how FENGARDE compares to Wazuh/Elastic Security/Security Onion? See
docs/vs.md — an honest comparison, not a sales pitch.
Security
FENGARDE services are designed for a localhost / Docker-Compose network only and
are not hardened for internet exposure. Authentication is opt-in
(FENGARDE_API_KEY shared-secret, dashboard basic-auth, Redis AUTH — all default
OFF, matching pre-v0.4 behavior until you opt in). A real identity/RBAC layer
(FENGARDE_RBAC_DB — SQLite users, scrypt hashing, sessions, roles, CSRF-protected
writes, tenant isolation) exists as of v0.5/M4, also opt-in and off by default
(single-process session store, not yet HA — see SSOT.md §2). The detection engine
executes rule files — so only run rules you trust. Need to reach the dashboard
from outside the host? See docs/deployment.md for a reverse-proxy
TLS example. See SECURITY.md for the full threat boundary and how to
report a vulnerability.
Open core — what's free, what's paid
This repository is free and open source forever, under Apache-2.0. Everything in it
— the pipeline, every parser, every detection rule, the dashboard, the triage API, the
generic and NIS2 report templates — is the complete product, not a crippled trial.
There is a separate, closed companion product, FENGARDE-Sec, developed in a private
repository: a paid layer for regulated deployments (legally-validated report content and
model-assisted compliance tooling). It plugs into this repo only through one frozen,
documented seam — the report-backend contract incontracts/reporting.md (REPORT_BACKEND=http). Nothing in
this repo requires it, phones home to it, or degrades without it.
Practically: features never move from this repo to the paid layer. New capability that
fits the seam ships here open; only the regulated/legal content layer is paid.
License
FENGARDE is licensed under the Apache License 2.0. See LICENSE.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi