Skip to content
Open-Source Network Security Monitoring Stack
Network Detection & Monitoring

Open-Source NSM Stack: Suricata, Zeek & rsyslog

A comprehensive guide to architecting a production-grade Network Security Monitoring (NSM) stack using Suricata, Zeek, and rsyslog. This setup provides deep packet inspection, signature-based IDS/IPS, protocol metadata extraction, and reliable log routing. Essential for SOCs requiring total network visibility and wire-speed threat detection without commercial licensing constraints.

Pros

  • Unparalleled network visibility with Zeek's protocol analysis and metadata extraction
  • High-performance signature-based intrusion detection and prevention with Suricata
  • Reliable, high-throughput centralized log routing and normalization with rsyslog
  • Zero licensing costs - full enterprise scale capabilities without vendor lock-in
  • Standardized data formats (JSON) ready for ingestion by any SIEM or data lake
  • Highly active open-source communities with thousands of contributors and custom rulesets
  • Platform agnostic - deployable on bare metal, VMs, containers, and cloud environments

Cons

  • Complex initial sensor deployment and traffic mirroring (SPAN/TAP) required
  • High resource consumption at multi-gigabit speeds; requires careful tuning and NIC offloading
  • Requires integration with a centralized SIEM/Dashboard (e.g., ELK, Splunk) for visualization
  • Managing and tuning Suricata rulesets (like Emerging Threats) demands dedicated analyst time
  • Zeek scripting has a steep learning curve for custom protocol parsing and detections

An attacker with SYSTEM on a host can stop your EDR service, clear the event log and edit the audit policy. What they cannot do is prevent the packets from having already crossed the wire. Network monitoring is the only telemetry source that does not live on the machine the adversary controls, which is why it survives as evidence when everything on the endpoint has been rewritten.

An open-source NSM stack gives you that visibility across the whole network rather than one host at a time, and it does most of what a commercial NDR platform does without the licence. The honest exchange is that you pay in engineering time instead of money — this is a system you operate, not a subscription you renew, and an unmaintained sensor is worse than no sensor because it produces a dashboard that implies coverage it no longer has.

Three tools, each doing one job properly: Suricata for signature-based detection, Zeek for protocol analysis and metadata extraction, and rsyslog for getting all of it somewhere central without losing events.


Component Architecture Overview

The separation is the design, not an accident of packaging. Suricata answers “did something known-bad just happen”; Zeek answers “what exactly happened, and what else did that host do”; rsyslog answers “is any of this reaching the place where someone can look at it”. Running one without the others gives you a partial answer to an incident question, and you find out which part is missing at the worst time.

  • Suricata detects. Signature rulesets such as Emerging Threats identify known exploits, C2 callbacks and lateral movement patterns as packets cross the wire. It tells you about things somebody has seen before.
  • Zeek contextualises. It extracts protocol metadata — HTTP headers, DNS queries, TLS fingerprints, SMB activity — from every connection regardless of port, and writes it whether or not anything looked suspicious. That distinction is the whole value: when you learn on Thursday that an IP was malicious, Zeek already has three weeks of everything that talked to it, and Suricata has nothing because no rule existed at the time.
  • rsyslog delivers. It reads JSON from both, buffers to disk when the destination is unavailable, and ships everything onward without dropping events — which is the difference between a gap in your timeline and a complete one.

Component 1: Suricata (The Detection Engine)

Suricata is a multi-threaded IDS/IPS engine that inspects packets against a signature set and alerts in real time. It scales across CPU cores properly, which is what lets it handle multi-gigabit links where single-threaded predecessors gave up.

What it does:

  • Signature Detection: Matches traffic against rule libraries such as Emerging Threats to catch known exploits, malware callbacks and attack patterns. Everything here is knowledge of past attacks, so a genuinely novel technique passes without comment — which is precisely why Zeek is in this stack.
  • Protocol Inspection: Identifies HTTP, DNS, TLS and other protocols by their behaviour rather than their port number, so SSH on 443 is still recognised as SSH.
  • File Extraction: Pulls files out of traffic for offline analysis. Enable it deliberately: extraction is expensive in both CPU and disk, and on a busy link with a broad file-type filter it will fill a volume faster than anyone expects.
  • Structured Alerts: Emits JSON, which is what makes everything downstream tractable.

Two operational realities worth stating before you deploy. First, running Suricata inline as an IPS puts it in the traffic path, so a crash or a resource exhaustion becomes a network outage rather than a monitoring gap — most organisations should start in IDS mode off a tap and earn their way to inline. Second, packet loss is the silent failure that ruins NSM deployments: when a sensor cannot keep up it discards packets and keeps running, the dashboards stay green, and the detection you were relying on simply never fires. Monitor capture.kernel_drops in stats.log and alert on it, because nothing else will tell you.

Advertisement

Tuning Suricata for Performance

Default settings are conservative and will not use the hardware you bought. The three things that matter most are the capture method, thread distribution and what you choose to log — the last one because EVE JSON output is where sensors most often become disk-bound.

# suricata.yaml
af-packet:
  - interface: eth1
    threads: auto           # Automatically scale based on CPU cores
    cluster-id: 99
    cluster-type: cluster_flow # Balance traffic by flow
    defrag: yes
    use-mmap: yes
    tpacket-v3: yes

# EVE JSON output configuration
outputs:
  - eve-log:
      enabled: yes
      filetype: regular
      filename: eve.json
      types:
        - alert:
            payload: yes             # Include packet payload in alert
            payload-printable: yes
        - http:
            extended: yes
        - dns:
            query: yes
            reply: yes
        - tls:
            extended: yes
        - files:
            force-magic: yes

cluster_flow is the important choice there: it hashes on the flow so both directions of a conversation land on the same thread, which is what allows stateful protocol parsing to work at all. Get it wrong and you will see fragmentary, inexplicable detections rather than an error.

Note that payload: yes on alerts is enabled above. It makes triage far faster because the analyst sees what actually matched instead of inferring it — and it also means packet contents are now written to disk and forwarded to your SIEM, which may include credentials, personal data or cardholder data depending on what crosses that link. That is a data protection decision as much as a technical one, and it should be made deliberately rather than inherited from a sample configuration.

Rules update through suricata-update, ideally daily. Do not run the full Emerging Threats set unfiltered and call it done — a large ruleset on a network it was not tuned for produces thousands of alerts a day, most of them irrelevant to your environment, and a queue nobody can read is functionally the same as no detection at all. Start narrow, add categories deliberately, and disable the noisy signatures with a documented reason so the next person knows why.


Component 2: Zeek (The Protocol Analyst)

Suricata tells you that something matched a rule. Zeek tells you what happened, including on the days when nothing matched.

Zeek is not a signature engine. It is a protocol analyser that decodes more than forty protocols and writes structured metadata for every connection it observes, malicious or not. That unconditional recording is the point. Almost every real investigation begins with an indicator you learned about after the fact — a domain named in an advisory, an IP from a partner notification, a hash from an incident elsewhere — and the only question that matters is whether anything in your network talked to it during the previous month. Zeek can answer that. A signature engine cannot, because at the time it happened, there was no rule.

What Zeek tracks:

  • DNS queries and responses — which host asked for which domain and what came back, which is the single most useful log for finding command-and-control and DGA malware. Note that DNS over HTTPS defeats this entirely: a client resolving through DoH produces one TLS connection to a resolver and no visible queries at all, so blocking or redirecting external DoH is a prerequisite for this visibility rather than an optional hardening step.
  • HTTP traffic — headers, user agents, URIs and response codes in full, plus JA3/JA3S TLS fingerprinting for clustering clients by their handshake characteristics.
  • Windows network protocols — SMB and RPC activity, showing which named pipes were accessed and under which user context. This is where lateral movement becomes visible.
  • All connections — every TCP, UDP and ICMP session with state, duration and bytes transferred, which is what makes exfiltration volume answerable months later.

The cost is storage and discipline. Zeek writes a great deal, most of it will never be read, and the retention window you can afford is the hard ceiling on every investigation you will ever run — thirty days of logs makes “when did they first get in” permanently unanswerable if the answer is forty.

Setting Up Zeek

Zeek is configured in its own scripting language, which is the steepest part of this stack to learn and also where its real power sits. A production baseline needs JSON output, file hashing and TLS fingerprinting turned on — none of which are defaults.

# local.zeek

# Enable JA3 TLS fingerprinting for clustering malicious clients
@load policy/protocols/ssl/ja3

# Enable file extraction and hashing
@load frameworks/files/hash-all-files
redef FileExtract::prefix = "/data/zeek/extract_files/";

# Output logs in JSON format for easy ingestion by rsyslog/SIEM
redef LogAscii::use_json = T;

# Track MAC addresses in connection logs for physical tracking
redef record Conn::Info += {
    orig_l2_addr: string &optional &log;
    resp_l2_addr: string &optional &log;
};

Component 3: rsyslog (The Log Pipeline)

Suricata and Zeek will generate terabytes. Getting that to a central point without gaps is the least glamorous component here and the one whose failure is most damaging, because a missing log looks exactly like a quiet network.

Why rsyslog rather than Logstash on the sensor? Logstash is JVM-based and will compete for the CPU cycles Suricata needs to keep up with the wire, which turns a log shipping decision into packet loss. Filebeat is a fairer comparison — it is lightweight and works well — but rsyslog’s disk-assisted queuing is more mature, and it is already installed on every Linux host you own. If your organisation runs Beats everywhere else, use Beats; consistency with the rest of your estate is worth more than the marginal difference.

The property that matters is disk-assisted queuing. When the SIEM is unavailable — a restart, a full disk, a network partition — rsyslog spools to local disk and replays on recovery. Without it, the events generated during the outage are gone, and the outage window is the interval an investigator will inevitably need.

Configuring rsyslog

The configuration has three jobs: find the Suricata and Zeek JSON files, buffer locally when the destination is unreachable, and forward over authenticated TLS so the log stream cannot be read or forged in transit.

# /etc/rsyslog.d/90-nsm-forwarder.conf

# Load modules for file reading and TLS
module(load="imfile" PollingInterval="1")
module(load="omfwd")

# Input: Suricata EVE Logs
input(type="imfile"
      File="/var/log/suricata/eve.json"
      Tag="suricata"
      Ruleset="NSM_Ruleset")

# Input: Zeek Logs (Wildcard pattern)
input(type="imfile"
      File="/opt/zeek/logs/current/*.log"
      Tag="zeek"
      Ruleset="NSM_Ruleset")

# Processing Ruleset with Disk-Assisted Queuing
ruleset(name="NSM_Ruleset") {
    # Send all tagged NSM logs to the central SIEM over TLS
    action(type="omfwd"
        Target="siem.internal.corp"
        Port="6514"
        Protocol="tcp"
        StreamDriver="gtls"
        StreamDriverMode="1"
        StreamDriverAuthMode="x509/name"
        StreamDriverPermittedPeers="*.internal.corp"
        
        # Disk-assisted queue configuration
        queue.filename="nsm_fwd_queue"
        queue.maxdiskspace="50g"     # Keep up to 50GB on disk if SIEM is down
        queue.saveonshutdown="on"
        queue.type="LinkedList"
        resumeRetryCount="-1"        # Infinite retries
    )
}

Multi-Sensor Deployment Architecture

Size that 50GB queue against reality rather than habit: divide it by your observed log rate to get the number of hours of SIEM downtime you can actually absorb. On a busy sensor that figure is often measured in hours, not days, and finding it out during an outage is too late. Size the disk for the longest maintenance window your SIEM team realistically takes.

One sensor at the perimeter is where most deployments start and where most of them stop, which leaves the blind spot that matters. Perimeter visibility shows traffic entering and leaving; it shows nothing of an attacker moving between two internal hosts on the same VLAN, which is the traffic that characterises the middle of every intrusion. Place sensors at the internal choke points too, and aggregate centrally.

[Internet]
   │
   ▼
[External Router / Firewall]
   │
   ├──▶ (SPAN/TAP) ───┐
   │                  │
[Core Switch]         │  [NSM Sensor 1 Node (Physical/VM)]
   │                  ├──▶ Suricata (AF_PACKET Interface)
   ├──▶ (SPAN/TAP) ───┼──▶ Zeek (AF_PACKET Interface)
   │                  └──▶ rsyslog (Reading JSON)
[Internal Subnets]                  │
                                    │ TLS / TCP 6514
                                    ▼
                          [Central Log Aggregation]
                          (Logstash / Kafka / Wazuh)
                                    │
                                    ▼
                         [SIEM / OpenSearch / Splunk]

Sizing Your Sensors

Under-specified sensors do not fail loudly. They drop packets, and you find out during an incident that the hour you need was never captured. Specify generously:

  • Network Cards: Intel x520/x710. Mature drivers and reliable offload behaviour matter more than headline throughput. Disable receive-side offloads such as LRO and GRO on the capture interface — they reassemble segments before the sensor sees them, which quietly breaks detections that depend on the original segmentation.
  • Memory: 32GB to 64GB per Gbps. Zeek holds connection state in memory and a long-lived flow table grows faster than intuition suggests.
  • CPU: 16–32 cores as a floor. Both Suricata and Zeek scale across cores, and headroom here is what absorbs traffic spikes instead of dropping them.

The other half of sizing is the aggregation tier, which is where these projects usually run aground. A sensor is a fixed cost; the storage, indexing and query capacity behind it grows with retention and is typically several times the sensor spend. Work out the cost of ninety days of searchable Zeek logs before you commit to the architecture, not after the first disk fills.

A note on traffic mirroring, since it decides what the sensors can see at all. A SPAN port is convenient and is oversubscribed by design — when the aggregate mirrored traffic exceeds the port’s capacity the switch discards the excess, silently, and prioritises forwarding over mirroring. A network tap is passive, cannot drop under load, and does not compete with production traffic. Taps cost more and require a maintenance window to install. For any link where the answer genuinely matters, buy the tap.


Tactical Use Cases: Finding the Adversary

A deployed stack is not a detection capability. What turns one into the other is a small number of questions the analysts know how to ask. Three worth building first:

Detecting Cobalt Strike C2 with TLS Fingerprinting

A TLS client’s handshake — cipher ordering, extensions, supported curves — is determined by the library that generated it, and JA3 hashes that into a fingerprint. That makes traffic from a bespoke implant distinguishable from a browser even though both are encrypted and both look like ordinary HTTPS.

Zeek records the JA3 of every TLS connection; Suricata alerts on known-bad hashes; the analyst pivots into Zeek’s connection log to establish how long the channel was open and how many bytes went out.

Be clear about the limits, because JA3 is frequently oversold. Fingerprints are trivially malleable — Cobalt Strike’s malleable C2 profiles exist partly to change them, and an operator can adopt a fingerprint identical to Chrome. Popular hashes also collide across unrelated software, so a match is an indicator, not a verdict. The reliable use is not the blocklist; it is the anomaly. One host with a JA3 that appears nowhere else in your environment is worth an analyst’s attention regardless of whether it is on any list.

Hunting for DGA and C2 Callbacks

Domain generation algorithms exist to make blocklists useless: the malware computes hundreds of candidate domains a day and the operator registers one.

Zeek logs every query, and the signal is in the pattern rather than any individual name — high-entropy labels, long runs of NXDOMAIN responses, resolution attempts from a host that has no business making them at 04:00. The practical caveats are real: legitimate software produces NXDOMAIN bursts too (Chrome’s startup probes are the classic example), CDN and telemetry domains are often high-entropy by design, and any threshold you pick will be wrong somewhere. Baseline against your own network for a fortnight before turning anything into an alert, or you will hand the SOC a queue of noise and teach them to ignore this detection permanently.

Detecting Lateral Movement and Credential Access

Enumerating shares, running PsExec, dumping credentials — all of it crosses the network, and all of it is visible in SMB.

Zeek records which named pipes were accessed and under which user context. Access to \PIPE\lsarpc or \PIPE\srvsvc from a workstation is unusual; \PIPE\svcctl combined with a service binary written to ADMIN$ is close to a signature for remote service execution. Suricata covers the known tooling patterns.

This detection depends entirely on sensor placement. Workstation-to-workstation SMB never reaches the perimeter, so if your only sensor is at the edge this section describes a capability you do not have. It also depends on SMB signing and encryption settings — SMB3 encryption, which is increasingly the default on modern Windows, removes the pipe-level visibility and leaves you with connection metadata alone. That is a good security outcome and a real reduction in what this detection can see, and it is worth knowing which of the two you have before you rely on it.


Integration with SIEM / SOAR

On its own this stack produces excellent data and no workflow. A SIEM is where the two halves of the story meet, and Wazuh is the natural open-source pairing.

The complementarity is genuine: Wazuh covers endpoint telemetry and file integrity, and the NSM stack covers everything an agent cannot be installed on — printers, cameras, embedded controllers, contractor laptops, the IoT device somebody plugged into a spare port. Correlating the two is what converts “a suspicious process ran” and “an unusual outbound connection occurred” into one incident with a beginning and an end.

Wazuh parses Suricata alerts natively and maps them to MITRE ATT&CK techniques, which makes coverage discussions concrete. Treat the resulting heat map with suspicion, though: a technique shows as covered because a rule exists that mentions it, not because anyone has tested that the rule fires on a real execution of that technique in your environment. The only way to know is to run the technique deliberately and check whether the alert arrives.

Budget for the field mapping work rather than assuming it. Zeek’s JSON does not arrive in ECS or CIM format, and a field that was never mapped, or mapped as the wrong type, returns zero results to a query instead of an error — a blank result set is visually identical to “nothing malicious happened”, and that is the most dangerous failure mode in this entire architecture. Validate every new log source by searching for something you know is present.

Conclusion

Suricata, Zeek and rsyslog remain the reference architecture for open-source network monitoring, and the capability at the end genuinely rivals commercial NDR. What it does not do is cost nothing. The licence is free; the engineering is not.

Be honest with yourself about that before starting, because it is the reason these deployments fail. Somebody has to tune the ruleset and keep tuning it as the network changes, watch the drop counters, size the storage, maintain the sensors’ operating systems, and rebuild field mappings when Zeek changes a log format on upgrade. That is a fraction of an engineer’s time indefinitely, not a project with an end date. A commercial NDR platform is largely a way of converting that ongoing time into an annual invoice — and for a team of three covering everything, that trade is often the correct one. The right choice depends on whether you have engineering capacity or budget, and being clear-eyed about which is the scarcer resource in your organisation.

If you do have the capacity, the payoff is real: full control over what is collected and how long it is kept, no per-gigabyte pricing shaping your detection strategy, and data in open formats that will outlive any vendor relationship. Start with one well-placed sensor covering an internal choke point, get the drop counters clean, get retention right, and only then add coverage. A single sensor you actually operate is worth more than five you deployed and stopped watching.


Share article

Subscribe to my newsletter

Receive my case study and the latest articles on my WhatsApp Channel.

Warning

Ask CyberROX AI