In this module

NF1.3 Zeek Installation and Configuration

10 hours · Module 1 · Free
What you already know

You've installed packages from apt repositories before. Zeek uses its own repository, which you'll add to your system. The installation is straightforward — the configuration choices are what matter. This sub covers both: installation from the Zeek repository and the configuration decisions that determine what your sensor produces.

Operational Objective

Zeek is the evidence engine that powers every investigation in this course. A working Zeek installation processes network traffic — live or from PCAP — and produces the structured metadata logs (conn.log, dns.log, ssl.log, http.log, and 20+ others) that you'll query in every module from NF2 onward.

Installing Zeek is one command. Configuring it correctly — choosing the right output format, enabling the right protocol analyzers, setting the right paths, and understanding what each configuration option does to your evidence — takes deliberate decisions. This sub walks through both.

Deliverable: A working Zeek installation on your sensor VM, configured for both live capture and PCAP replay, with your first successful PCAP analysis producing logs you can query.

Estimated completion: 40 minutes
ZEEK INSTALLATION AND FIRST RUN 1. Add Repository Zeek LTS repo for Ubuntu packages.zeek.org 2. Install apt install zeek-lts Includes zeek-cut, zeekctl 3. Configure node.cfg, local.zeek Log path, JSON output 4. First Run zeek -r test-capture.pcap Produces conn.log, dns.log, ... KEY CONFIG DECISION: TAB-SEPARATED (DEFAULT) OR JSON OUTPUT Tab-separated: native format, works with zeek-cut, lighter, faster. Best for command-line investigation. JSON: works with jq, easier SIEM ingestion, more verbose. Best for log pipeline integration.

Figure NF1.3 — Zeek installation in four steps. The configuration phase (Step 3) determines the output format and protocol coverage. This course uses tab-separated format for command-line investigation efficiency — JSON configuration is covered as an alternative.

Step 1 — Add the Zeek Repository

Zeek maintains its own package repository with LTS (Long Term Support) and feature releases. Use the LTS release for stability — it receives security updates without breaking changes.

echo 'deb http://download.opensuse.org/repositories/security:/zeek/xUbuntu_24.04/ /' | \
  sudo tee /etc/apt/sources.list.d/zeek.list

curl -fsSL https://download.opensuse.org/repositories/security:zeek/xUbuntu_24.04/Release.key | \
  gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/zeek.gpg > /dev/null

sudo apt update

Verify the repository was added successfully:

apt-cache policy zeek-lts

You should see a version available from the Zeek repository (6.0.x or later for the current LTS).

Step 2 — Install Zeek

sudo apt install -y zeek-lts

The package installs Zeek to /opt/zeek/. The key binaries are in /opt/zeek/bin/:

ls /opt/zeek/bin/zeek /opt/zeek/bin/zeek-cut /opt/zeek/bin/zeekctl

All three should exist. Add Zeek's bin directory to your PATH so you can run Zeek commands without the full path:

echo 'export PATH="/opt/zeek/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Verify the installation:

zeek --version
zeek-cut --version

Both should return version information without errors.

Step 3 — Configure Zeek

Zeek's configuration lives in /opt/zeek/etc/. The three files you'll edit:

node.cfg defines the sensor's capture interface. For the lab, configure a standalone node:

sudo nano /opt/zeek/etc/node.cfg

Set the content to:

[zeek]
type=standalone
host=localhost
interface=eth0

Replace eth0 with your actual management interface name (check with ip link show). For PCAP replay — which is how you'll use Zeek most often in this course — the interface setting doesn't matter because you'll use zeek -r file.pcap directly.

local.zeek controls which scripts and analyzers Zeek loads. The default configuration enables all standard protocol analyzers, which is what you want for the lab. To enable Community ID (for Suricata correlation) and JSON logging (optional), add these lines:

sudo nano /opt/zeek/share/zeek/site/local.zeek

Add at the end:

# Enable Community ID for Zeek-Suricata correlation
@load policy/protocols/conn/community-id-logging

# Uncomment the next line for JSON output instead of tab-separated
# @load policy/tuning/json-logs

Community ID is essential — it's how you correlate Zeek logs with Suricata alerts for the same network flow. JSON logging is a personal preference; this course uses tab-separated (default) because it's more compact and works directly with zeek-cut. You can enable JSON later if your workflow requires it.

zeekctl.cfg controls operational parameters like log rotation and mail notifications. For the lab, the defaults are fine. The key settings to be aware of:

grep -E "LogDir|LogRotationInterval|MailTo" /opt/zeek/etc/zeekctl.cfg

LogDir is where Zeek writes logs (default: /opt/zeek/logs/). LogRotationInterval is how often logs rotate (default: 3600 seconds = 1 hour). For the lab, these defaults work.

Step 4 — First PCAP Analysis

Run Zeek against your test PCAP to verify the installation works:

cd /opt/sensor/zeek-logs/
zeek -r /opt/sensor/pcap/test-capture.pcap

Zeek processes the PCAP and produces log files in the current directory. List them:

ls -la *.log

You should see at minimum: conn.log, dns.log, packet_filter.log, and capture_loss.log. Depending on the traffic in your test PCAP, you may also see: http.log, ssl.log, files.log, smtp.log, ssh.log, and others.

Verify the logs contain data:

echo "Connections: $(grep -v '^#' conn.log | wc -l)"
echo "DNS queries: $(grep -v '^#' dns.log 2>/dev/null | wc -l)"

If both counts are greater than zero, your Zeek installation is working correctly.

Run a quick investigation query to confirm zeek-cut works:

cat conn.log | zeek-cut id.orig_h id.resp_h id.resp_p proto | head -10

This extracts source IP, destination IP, destination port, and protocol for the first 10 connections. The output should be tab-separated with clear column values.

Output Format Decision

Tab-separated vs JSON — choose based on your workflow. This course uses tab-separated. Here's why, and when JSON is better.

Zeek's default output is tab-separated values with a header section (lines starting with #) that describes the columns. The format is compact — a conn.log entry is approximately 200-300 bytes. Tab-separated output works directly with zeek-cut, which extracts named columns from the tab-separated format. The investigation queries throughout this course use zeek-cut.

JSON output (@load policy/tuning/json-logs in local.zeek) produces one JSON object per line. Each entry is self-describing — field names are included with every record. JSON is approximately 2-3× larger than tab-separated for the same data. JSON works with jq instead of zeek-cut. JSON is easier to ingest into Elasticsearch, Splunk, and other SIEMs because the field names are embedded.

For this course, use tab-separated (the default). The query patterns are built around zeek-cut, and the compact format is easier to read in a terminal. If your production deployment feeds Zeek logs into a SIEM, enable JSON for that pipeline — but keep tab-separated for local investigation.

Guided Procedure — Verify Zeek Produces Complete Logs
Step 1. Run Zeek against your test PCAP and list all generated log files.
Expected output: At minimum: conn.log, dns.log, packet_filter.log, capture_loss.log. If the PCAP contains web traffic: http.log, ssl.log. If it contains file transfers: files.log. Each log should have data rows (not just headers).
If no logs are generated: Check that Zeek ran without errors: `zeek -r file.pcap 2>&1`. Common issue: wrong path to PCAP. Verify the file exists: `ls -la /opt/sensor/pcap/test-capture.pcap`.
Step 2. Check that Community ID is present in conn.log.
Expected output: `head -20 conn.log | grep community_id` should show a `#fields` line that includes `community_id`. Data rows should include a Community ID hash (format: `1:xxxx/yyyy==`). If the field isn't present, Community ID logging wasn't loaded — re-check local.zeek.
If community_id is missing: Verify the `@load policy/protocols/conn/community-id-logging` line in local.zeek. Re-run `zeek -r file.pcap` after fixing. The Community ID field must be present for Suricata correlation in later modules.
Step 3. Run a zeek-cut query to extract the top 5 destination IPs by connection count.
Expected output: `cat conn.log | zeek-cut id.resp_h | sort | uniq -c | sort -rn | head -5` should return 5 lines with counts and IP addresses. The top destinations are typically DNS resolvers, CDN servers, and common web services.
If zeek-cut reports "unknown field": The field names in conn.log must match exactly. Run `head -1 conn.log | grep '#fields'` to see the available fields. Common mistake: using `id.resp.h` instead of `id.resp_h` (underscores, not dots, separate nested fields after the initial dot).
Decision point

After running Zeek against your test PCAP, you notice that ssl.log exists but http.log doesn't, even though you know the PCAP contains HTTP traffic.

This happens when the HTTP traffic is inside TLS — the browser connects to port 443 using HTTPS, and Zeek sees the TLS handshake (logged to ssl.log) but can't see the HTTP content inside the encrypted channel (so no http.log entries for those connections). Unencrypted HTTP on port 80 would produce http.log entries.

In 2026, the majority of web traffic is HTTPS. If your test PCAP is from a modern browser session, most connections are TLS-encrypted, and http.log will be sparse. This is expected — and it's why ssl.log (with TLS fingerprints, certificates, and JA3 hashes) is more important than http.log for most modern investigations.

Don't troubleshoot a "missing" http.log if the traffic was encrypted. Module NF4 covers what you can and can't see in encrypted traffic.

Compliance Myth: "You should compile Zeek from source for best performance"

The Zeek repository packages are compiled with standard optimizations and include all protocol analyzers. Compiling from source gives you the option to disable specific analyzers (minor performance gain) or apply patches (rarely needed). For any environment under 5 Gbps, the repository package performs identically to a custom compilation.

Compiling from source adds build-time dependencies, requires manual updates, and makes it your responsibility to track security patches. The repository package receives security updates through standard apt upgrade. Unless you have a specific technical reason (custom patches, embedded deployment, unusual architecture), use the repository package.

Next
NF1.4 — Zeek Log Directory Structure. Zeek is installed and producing logs. NF1.4 takes you through every log file Zeek generates — what each contains, when it's created, and which investigation questions each answers.
mkdir -p /opt/sensor/zeek-logs/test-run && cd /opt/sensor/zeek-logs/test-run
zeek -r /opt/sensor/pcap/test-capture.pcap

# Question 1
cat conn.log | zeek-cut id.orig_h | sort -u | wc -l

# Question 2
cat dns.log | zeek-cut query | sort -u | head -20

# Question 3
cat ssl.log | zeek-cut server_name subject | sort -u | head -20
Try it: Process a PCAP and explore the logs

Setup. Your sensor VM with Zeek installed and a test PCAP in /opt/sensor/pcap/.

Task. Create a dedicated directory for this analysis, run Zeek, and answer three questions from the logs: (1) How many unique source IPs are in the PCAP? (2) What DNS domains were queried? (3) What TLS certificates were presented?

Expected result. Three numeric or list outputs that characterise the traffic in your test PCAP. The specific values depend on your PCAP content, but you should get non-zero results for at least questions 1 and 2.

Debugging branch. If ssl.log doesn't exist: the test PCAP may not contain TLS traffic. Try a different PCAP — most modern web browsing PCAPs from malware-traffic-analysis.net contain TLS. If dns.log is empty: the PCAP may not contain DNS traffic (rare but possible in filtered captures).

Checkpoint — before moving on
1. Run `zeek --version` and `zeek-cut --version` — both should return version information without errors. (§ Step 2)
2. Process a PCAP file with `zeek -r` and verify that conn.log contains data rows with Community ID fields. (§ Step 4 and Guided Procedure)
3. Explain the tradeoff between tab-separated and JSON output and state which this course uses. (§ Output Format Decision)
zeek --version
zeek-cut --version
zeekctl --version 2>/dev/null
ls /opt/zeek/bin/{zeek,zeek-cut,zeekctl}
grep -v '^#\|^$' /opt/zeek/etc/node.cfg
grep "community-id\|json-logs" /opt/zeek/share/zeek/site/local.zeek
cd /tmp && zeek -r /opt/sensor/pcap/test-capture.pcap
echo "Log files: $(ls *.log | wc -l)"
echo "Connections: $(grep -v '^#' conn.log | wc -l)"
echo "Community ID present: $(head -20 conn.log | grep -c community_id)"
rm -f /tmp/*.log
@load-sigs policy/protocols/ftp/detect
event zeek_init() {
  Analyzer::disable_analyzer(Analyzer::ANALYZER_FTP);
}
zkg install zeek/zeek-community-id
zkg install zeek/zeek-af_packet-plugin  # for high-performance AF_PACKET capture
zkg install salesforce/ja3  # JA3 TLS fingerprinting

You've built the sensor and mapped the evidence landscape.

NF0 established why network evidence matters when every other source is compromised. NF1 built your Zeek + Suricata sensor with the 10 investigation query patterns. From here, every module teaches protocol-specific investigation against real attack scenarios.

  • DNS deep dive (NF3) — tunnelling detection, DGA analysis, passive DNS infrastructure mapping, and the INC-NE-2026-0227 AiTM phishing DNS trail
  • Protocol analysis (NF4–NF7) — HTTP/HTTPS, SMB lateral movement, SSH tunnelling, and email protocol investigation with Zeek metadata and PCAP
  • Detection and hunting (NF8–NF11) — Suricata rule writing, C2 beacon detection with JA3, NetFlow analytics, and proactive network threat hunting
  • NSM architecture (NF13) — production sensor deployment at 1–10 Gbps with Arkime, Security Onion, and enterprise storage planning
  • INC-NE-2026-0830 capstone (NF14) — multi-stage investigation using only network evidence: phishing → domain-fronted C2 → lateral movement → DNS tunnel exfiltration
Unlock the full course with Premium See Full Syllabus

Cancel anytime