Zeek Network Security Monitoring on Linux: Scripting, JA4 Fingerprints, and Threat Hunting in 2026
Install Zeek 7.x on Linux, tune AF_PACKET for line-rate capture, write your first detection script, use JA4 TLS fingerprints, and stitch Zeek into a SIEM for real threat hunting.
Zeek is a passive, open-source network security monitor for Linux that turns raw traffic into structured, high-fidelity logs and lets defenders write detection logic in a domain-specific scripting language. Where a signature-based IDS answers "did this packet match a rule?", Zeek answers "what actually happened on the wire, minute by minute, and does it match how this host normally behaves?" In this guide I'll walk you through installing Zeek 7.x on Linux, tuning AF_PACKET for line-rate capture, writing your first detection scripts, using JA4/JA4S TLS fingerprints, and stitching Zeek into an existing SIEM. I'm writing this from the offensive side: every step maps to something an attacker on your network is actively trying to hide.
Zeek isn't an IDS competitor to Suricata. It's an event and metadata engine. Run both; Suricata for signatures, Zeek for behavior and encrypted-traffic analysis.
Zeek 7.x ships JA4/JA4S TLS fingerprinting through community packages, which survives certificate rotation and catches C2 clients that classic JA3 misses.
AF_PACKET v3 with kernel fanout replaces PF_RING for most Linux deployments and hits 10 Gbps on commodity NICs without kernel patches.
The Intel:: framework ingests MISP, CrowdStrike, and OpenCTI feeds and matches indicators against every seen host, URI, and hash in real time.
Zeek's conn.log, ssl.log, dns.log, and files.log answer 80 % of the "what did that host do" questions that come up during incident response.
Deploy in a cluster (manager + workers + logger + proxy) as soon as you exceed one 10 Gbps link or want per-CPU scaling.
What is Zeek used for on Linux?
Zeek is used on Linux as a passive network sensor that reconstructs TCP, UDP, and application-layer sessions (HTTP, DNS, TLS, SSH, SMB, Kerberos, and dozens more) into structured logs and lets analysts run scripts against those events in real time. It is not a firewall, it doesn't block anything, and it doesn't rely on signatures. In a typical blue-team deployment Zeek sits on a SPAN port, TAP, or ERSPAN destination and writes JSON logs to a local directory; a Filebeat or Vector agent then forwards them to Elastic, OpenSearch, or Wazuh for correlation.
So, why does Zeek matter in 2026? Traffic is almost entirely encrypted. Rules-based tools can only inspect what they can decrypt, but Zeek's TLS handler produces certificate metadata, JA4 fingerprints, and connection-shape features that survive TLS 1.3 and Encrypted Client Hello. That's why every commercial NSM (Corelight, Darktrace's ETA layer, Trellix) is either built on Zeek or reimplements what Zeek does. Honestly, when I do a red-team engagement, Zeek is the tool I most fear on the defender side, because it doesn't care that my beacon is HTTPS: it fingerprints my TLS client and notes that no other host has ever spoken that fingerprint before.
Is Zeek better than Suricata?
Zeek isn't better than Suricata; the two answer different questions and are usually deployed together in a defence-in-depth stack. Suricata is a signature-based IDS/IPS that fires on packet-level rules (ET Open, Emerging Threats Pro, custom SIDs). Zeek is a metadata generator and behavioral engine. The comparison table below is the one I actually use when scoping a customer's NSM refresh.
Run both. Point them at the same AF_PACKET fanout group so they see identical traffic, and correlate their logs in your SIEM by 5-tuple and community_id. If you haven't yet built a stacked detection pipeline, my earlier walkthrough of building a multi-layer Linux intrusion detection system with AIDE, auditd, Wazuh, and Suricata is the right starting point. Zeek slots in as the network-metadata layer.
Install Zeek 7.x on Ubuntu, Debian, and RHEL
The official Zeek installation docs maintain OBS repositories for the current stable and LTS branches. I recommend the LTS on production sensors and current stable on staging. Don't run distro-packaged Zeek. It's invariably years behind and lacks JA4 support.
Line-rate capture with AF_PACKET and kernel fanout
For years Zeek deployments defaulted to PF_RING with a kernel module. Since kernel 4.19 stabilised AF_PACKET v3 with fanout groups, the module is unnecessary for the vast majority of workloads and simplifies compliance because you no longer ship an out-of-tree kernel driver. Configure node.cfg to use AF_PACKET and pin workers to specific CPUs:
On the sensor NIC, disable offloads that reassemble or rewrite frames before Zeek sees them. Otherwise your conn.log will show impossible packet sizes and TCP state transitions.
for feat in tso gso gro lro rx tx sg; do
sudo ethtool -K eno2 $feat off 2>/dev/null || true
done
sudo ethtool -G eno2 rx 4096 tx 4096
sudo ip link set eno2 promisc on
Set the CPU governor to performance and disable Intel TurboBoost throttling. A Zeek worker that hits scheduler latency drops packets silently. The capture_loss.log is the first log I open when investigating "we don't see that host" complaints. I hit this exact issue at a Fortune 500 shop last year: everything looked healthy, but a lazy governor was costing us roughly 3% of packets during peak hours.
The logs that matter: conn, ssl, dns, files, weird
Out of the box Zeek writes about 40 log files. In practice, five of them answer most incident-response questions. Enable JSON output (Zeek's TSV format is human-readable but painful for SIEM ingestion):
conn.log: one row per completed flow. 5-tuple, duration, byte counts, TCP state history, community_id. This is the correlation key across every other tool.
ssl.log: TLS handshakes. SNI, issuer, subject, JA4, JA4S, ALPN, version, cipher. If encrypted-traffic analysis is your beat, this is your bread and butter.
dns.log: every query and response. Feed this to your DGA detector; also indispensable when hunting DNS-over-HTTPS abuse (attackers still leak bootstrap A queries).
files.log: anything Zeek was able to extract or hash across HTTP, SMTP, SMB, FTP, and IRC. Pipe SHA-256 to VirusTotal or an in-house YARA scanner.
weird.log: protocol anomalies. Attackers running custom C2 over "HTTP" that isn't quite HTTP show up here first.
Writing your first Zeek script
Zeek's power is its scripting language. Every packet, every connection state change, every reconstructed application-layer event fires a handler you can hook. Here's a small but genuinely useful script: it flags any internal host that resolves a new domain and immediately opens a TLS connection to it. That's a common shape for browser-delivered malware.
# /opt/zeek/share/zeek/site/scripts/new-domain-then-tls.zeek
@load base/frameworks/notice
@load base/protocols/dns
@load base/protocols/ssl
module NewDomainTLS;
export {
redef enum Notice::Type += { New_Domain_Then_TLS };
const watch_window = 30sec &redef;
const internal_nets: set[subnet] = {
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
} &redef;
}
global recently_resolved: table[addr, string] of time
&create_expire=1min;
event DNS::log_dns(rec: DNS::Info) {
if (rec$qtype_name != "A" || ! rec?$answers) return;
for (i in rec$answers) {
recently_resolved[rec$id$orig_h, rec$query] = network_time();
}
}
event SSL::log_ssl(rec: SSL::Info) {
if (rec$id$orig_h !in internal_nets) return;
if (! rec?$server_name) return;
if ([rec$id$orig_h, rec$server_name] !in recently_resolved) return;
local delta = network_time() - recently_resolved[rec$id$orig_h, rec$server_name];
if (delta <= watch_window) {
NOTICE([$note=New_Domain_Then_TLS,
$conn=rec$conn,
$msg=fmt("%s resolved %s and connected via TLS within %s",
rec$id$orig_h, rec$server_name, delta),
$identifier=cat(rec$id$orig_h, rec$server_name)]);
}
}
Load it in local.zeek, run zeekctl deploy, and every hit lands in notice.log. That file is where I want your SIEM correlation rules to fire, not conn.log, which is far too noisy.
JA4 and JA4S TLS fingerprinting
JA3 has served defenders well for six years, but attackers learned to randomise cipher suites and defeat it. JA4, published by FoxIO under a BSL license in 2023, is now the community standard. The JA4 reference implementation ships a Zeek package that adds ja4, ja4s, ja4h, and ja4x fields to ssl.log and http.log. Install it with zkg:
sudo -u zeek zkg install zeek/foxio-llc/ja4
echo "@load packages" | sudo tee -a /opt/zeek/share/zeek/site/local.zeek
sudo -u zeek zeekctl deploy
Now every TLS handshake gets a stable fingerprint that captures the TLS version, cipher list, extension list sorted, ALPN, and signature algorithms. Two properties matter for detection: JA4 is deterministic across sessions from the same client library, and it's easy to pivot on. If your ssl.log shows a JA4 of t13d1516h2_8daaf6152771_02713d6af862 hitting five different SNI names in an hour, and no other host in your fleet shares it, you've found a Cobalt Strike beacon, a Sliver implant, or a red teamer, often before the C2 domain hits any threat feed.
Pair JA4 with community_id so you can cross-reference the same flow across Zeek, Suricata, and any endpoint agent generating flow records. The pattern I use during hunts:
# In your SIEM (Elastic KQL syntax)
ssl.ja4 : "t13d1516h2_8daaf6152771_02713d6af862"
AND NOT ssl.server_name : (*.microsoft.com OR *.googleapis.com)
| stats count by source.ip, ssl.server_name, ssl.ja4s
Threat intelligence with the Intel framework
Zeek's Intel framework is a real-time in-memory matcher for indicators (IPs, domains, URLs, file hashes, certificate hashes, JA4 fingerprints). Feed it from MISP, OpenCTI, or a plain TSV and every event Zeek generates is checked against every indicator. No cron or batch lookup required.
Zeek reloads the files on modification, so a MISP-to-Zeek push script (there are several on GitHub) keeps your sensor current within seconds. Combine this with the Intel::seen events and you can trigger auto-response through your SOAR. My preferred pattern: Zeek fires Notice, Wazuh ingests it, and an active-response script updates the nftables ruleset described in the nftables hardening guide to drop the offending 5-tuple.
Cluster deployment for 10 Gbps and above
Zeek is single-threaded per worker. To scale beyond a couple of Gbps of real traffic you deploy a cluster: one manager, one logger, one or more proxies, and N workers, all coordinated by zeekctl. AF_PACKET fanout distributes flows across workers by 5-tuple hash so a given connection always lands on the same worker, essential for stateful analysis.
Sizing rule of thumb from Corelight's public guidance and my own benchmarks: one AF_PACKET worker per physical core, reserving 25 % of cores for the kernel, logger, and proxy. A dual-socket Xeon Gold with 32 physical cores comfortably handles a full 10 Gbps link running the default script set plus JA4 and Intel. If you see capture_loss above 0.1 % sustained, add workers or drop non-essential scripts (video streaming baselines are a common culprit) before you consider hardware capture cards.
SIEM integration: Wazuh, Elastic, and OpenSearch
Zeek writes JSON to /opt/zeek/logs/current/. Ship it with Filebeat, Vector, or Fluent Bit. The Elastic Zeek integration now normalises fields to ECS 8.x, so source.ip, destination.ip, network.community_id, and tls.client.ja4 all populate correctly out of the box.
For Wazuh, use the wodle-command to tail Zeek logs and forward as rule.group: zeek. Wazuh 5.x ships default correlation rules for Zeek notices, and its indexer clusters happily with the OpenSearch above, so you avoid running two search stores.
How attackers try to evade Zeek
Wearing my red team hat, here's what I actually do when I know Zeek is on the wire, and how you defend against each move:
Blend TLS fingerprints. I switch my implant to use the platform TLS stack (SChannel on Windows, LibreSSL on macOS) so my JA4 matches every other host on the LAN. Defence: baseline JA4 per-host, not per-fleet. A workstation that suddenly speaks a Go TLS fingerprint is suspicious even if that fingerprint is common elsewhere.
Domain fronting and ECH. The SNI leaks nothing useful. Defence: Zeek still sees the destination IP, certificate hash, and the connection shape (bytes-in vs bytes-out over time). A 30-second beacon interval with 5 KB requests is unmistakable in conn.log.
Low and slow DNS tunnelling. One TXT lookup per minute stays under naive DGA detectors. Defence: the exfil.zeek policy plus per-host query-entropy baselining in the Intel framework catches subdomain entropy spikes.
Kill the sensor. If I get onto the Zeek host itself, I stop zeekctl or fill the log disk. Defence: ship logs off-box within seconds (see the Vector config above), harden the sensor with the controls in my Ubuntu 24.04 hardening playbook, and monitor Zeek's own heartbeat with your SIEM.
QUIC everywhere. Zeek's QUIC analyser has matured but coverage lags TCP. Defence: enable the quic and ech analysers from the community packages, and force enterprise Chrome/Edge to disable QUIC for outbound if your policy allows.
The takeaway? Zeek doesn't need to catch me on the first packet. It needs to be there for the second, third, and thousandth. The longitudinal metadata is what convicts a beacon, not any single alert. That's a fundamentally different game from signature IDS, and it's why NSM is having a renaissance in 2026 as encryption and cloud-native traffic patterns keep breaking the old signature-only model.
Frequently Asked Questions
Is Zeek free to use in commercial environments?
Yes. Zeek is released under a BSD 3-Clause license and is free for commercial use with no seat, throughput, or feature restrictions. Corelight sells a hardened enterprise distribution with support, connectors, and additional analytics, but the open-source Zeek from zeek.org is fully functional.
Can Zeek detect malware without signatures?
Yes, but not the way antivirus does. Zeek detects malware by behaviour: unusual JA4 fingerprints, new-domain-then-TLS patterns, DNS tunnelling entropy, files.log hashes matched against threat intel, and anomalous connection shapes. It complements a signature IDS rather than replacing one.
How does Zeek differ from Wireshark?
Wireshark is a packet analyser for interactive, per-packet inspection during troubleshooting. Zeek is a passive, headless monitor that reconstructs sessions and writes structured logs continuously across days or months of traffic. You use Wireshark to answer "what does this one flow look like"; you use Zeek to answer "what did every host on the network do last Tuesday".
What hardware do I need to run Zeek on a 10 Gbps link?
A dual-socket server with 32 physical cores, 128 GB RAM, and a Mellanox ConnectX-6 or Intel E810 NIC with RSS handles a saturated 10 Gbps link running the default policy plus JA4 and Intel framework. For 40 Gbps and above, deploy multiple sensors behind a network broker or use a hardware capture card such as Napatech.
Can Zeek block malicious traffic like Suricata IPS?
No. Zeek is strictly passive. To act on Zeek detections, forward notices to your SIEM or SOAR and trigger an active-response script that updates your firewall. For example, an nftables set on your edge router or an EDR quarantine on the endpoint.
Sigma turns Linux auditd, journald, sshd, and sudo events into portable YAML detections that compile to any SIEM. Learn pySigma, sigma-cli, Zircolite, and CI patterns for detection-as-code in 2026.
A practical 2026 guide to fapolicyd on RHEL and Fedora: how it uses fanotify to gate execve, how to write rules that don't lock you out, the safe permissive-to-enforce rollout, and how it maps to CIS, STIG, and PCI DSS compliance.
A pipeline-first guide to Cilium 1.17 on Linux: writing CiliumNetworkPolicy resources with L7 HTTP/DNS/Kafka rules, wiring Hubble for flow observability, and validating every policy in CI before it reaches production.