Skip to content
~/mandrakis
5 min read

Building a university SOC: Wazuh, MISP and CrowdSec

Why I built a detection-and-response stack from open-source parts instead of buying one, how the enrichment loop fits together, and two lessons that only show up once it's running in production.


A public university is a hard environment to defend. The network is large, deliberately open, full of unmanaged devices, and the budget is fixed by a pay scale rather than a threat model. “Buy the managed SOC” is not a sentence that survives contact with that reality.

So I built one. This is how it’s put together and what running it has taught me.

SOC operations overview dashboard: alert totals, a 24-hour Wazuh alert trend chart, and a recent activity feed.
The operations overview — alert volume, 24h trend, and live activity. Hostnames and addresses are sanitised for publication.

Why custom

Two reasons, one practical and one that turned out to matter more.

The practical one is cost. A commercial SOC platform priced per endpoint or per GB of ingest does not fit a university budget, and the managed-service option means handing detection logic to someone who doesn’t know the environment.

The one that mattered more: building it myself meant I understood every detection decision in the stack. When you buy detection, you inherit someone else’s assumptions about what’s normal. When you build it, the blind spots are yours — which means you know exactly where they are. That knowledge has turned out to be the most valuable thing the project produced.

The stack

The core is Wazuh — open-source SIEM and XDR, doing log collection, agent management, and rule-based alerting across the estate.

Around it sit two sources of external context:

  • MISP for threat intelligence — known-bad indicators with confidence levels and an “intended for detection” flag I lean on heavily.
  • CrowdSec for community IP reputation — a continuously updated view of addresses currently misbehaving across a large sensor network.

Wazuh on its own tells you what happened on your hosts. MISP and CrowdSec tell you what the wider world already knows about the addresses involved. The interesting work is connecting the two.

Wazuh SIEM panel: active agents, critical and high alert counts, a recent alerts table, top triggered rules, and per-agent status.
The Wazuh layer — recent alerts, the most-triggered rules, and per-agent status across the monitored estate.

The enrichment loop

That connection is a custom integration — a piece of code that sits between a raw Wazuh alert and a decision.

When an alert fires, the integration pulls every observable out of it: IP addresses, domains, URLs, email addresses, file hashes. Each observable is checked against MISP, and each IP is additionally checked against a local CrowdSec decision store. The integration then scores the result, assigns a severity, and writes a structured record of what it found.

That structured record is the clever part. Wazuh reads it back in as a log source, which means a handful of purpose-built rules can match on it like any other event. An enriched finding becomes a first-class alert — and a first-class alert can trigger active response, dropping the offending address at the agent firewall automatically.

The result is a closed loop: alert in, enrichment, scoring, rule match, enforced block — without a human in the path for the high-confidence cases.

MISP threat intelligence panel: actionable IOC counts and a table of MISP-driven block actions with scores, agents, and event references.
The MISP view — high-confidence indicator matches from the enrichment pipeline, scored and attributed to the alert that surfaced them.

Feeding the reputation store

CrowdSec’s official community feed is good, but I wanted more breadth, so a second node contributes additional decisions. The trick is avoiding duplication: before those supplementary decisions are imported, anything already covered by the official feed is removed, and each remaining decision keeps its original expiry time rather than being flattened to a default.

That import runs on a schedule via a systemd timer. It has to: imported reputation data has a TTL, and if the sync stops, the extra coverage quietly expires and the enrichment step goes half-blind without anything obviously breaking. Scheduled automation isn’t a nice-to-have here — it’s load-bearing.

CrowdSec defense panel: total banned IPs, the banned-IP table with scenarios and origins, top attacking countries, and top ban scenarios.
The CrowdSec layer — banned addresses by scenario and origin, with the official community feed and the supplementary import side by side.

Two lessons from production

A architecture diagram makes a system look finished. Running it teaches you where it isn’t.

Intelligence TTL is not enforcement TTL

This one cost me an afternoon of confusion. The dashboard shows how long a piece of intelligence is considered valid — a CrowdSec decision might carry a multi-day expiry. But the actual firewall block on the agent follows the SIEM’s active-response timeout, which is a separate, static value.

So the dashboard can confidently show a three-day reputation TTL while the host-level block already lifted hours ago. Both are correct. They are answering different questions. If you build a stack like this, decide deliberately whether enforcement should track intelligence — and until you implement that explicitly, assume it doesn’t.

Intel-only blocking is a stronger claim than it looks

Blocking on a corroborated CrowdSec match is comfortable: it means an address is misbehaving right now, across many sensors. Blocking purely on a MISP indicator is a different kind of decision — you’re acting on someone’s prior assertion that an address is bad, which may be stale, scoped to a context that isn’t yours, or simply wrong.

Intel-driven auto-blocking is powerful and I use it — but only for high-confidence indicators explicitly flagged for detection, and I treat it as a deliberate, narrow policy rather than a blanket rule. The failure mode of “block everything MISP has ever seen” is a self-inflicted outage.

Where this connects

I’m moving toward red team work, and people sometimes ask why a defensive engineering project belongs on a site about that.

This is the answer. Building this stack means I know precisely what an enriched SOC sees: which observables get pulled from an alert, how reputation and intelligence corroborate or fail to, where the timing gaps are, and which detections lean on assumptions thin enough to slip past. You cannot get that from the outside. The blue work is the red preparation.

More on that in a future post.