Zeek

Zeek (formerly Bro) is one of the detection engines bundled with the Network Probe (see the bundled services table). It is a Network Security Monitor (NSM): instead of matching traffic against a signature ruleset, it parses every connection and writes detailed, structured logs of what happened on the network. This chapter takes a fresh probe from a masked Zeek service to events visible in the Energy Logserver GUI.

What Zeek does

Zeek is a passive network analyzer focused on protocol detail and behaviour, not on blocking:

  • Writes per-protocol logs: connections (conn.log), DNS, HTTP, SSL/TLS, SSH, SMB, files, and more.

  • Records rich connection metadata: endpoints, duration, bytes, connection state, and detected service.

  • Runs a scripting language for custom detection and enrichment.

  • Detects protocol anomalies and notable events (notice.log).

  • Monitors passively from a mirror or SPAN port, so it adds no latency to the traffic it watches.

Zeek complements a signature engine such as Suricata: Suricata answers “did this match a known attack”, Zeek answers “what exactly happened on the wire”.

What Zeek One Click deploys

The Zeek entry in the Integrations catalog installs the Energy Logserver side of the pipeline in a single action. Open it from Tools → Integrations → Zeek and use One Click Installation.

Component

Purpose

Pipelines

The Network Probe pipeline that reads Zeek’s JSON logs, parses them, and writes the events to the stream-zeek-* indices. The pipeline installs the matching index template.

Note

Unlike the Suricata integration, Zeek One Click does not ship dashboards or predefined alert rules. Zeek’s value is its detailed logs: you read them in Discover and build the dashboards that fit what you monitor. Explore in Discover and Build your own dashboards cover both.

How the pieces fit together

Before configuring anything, it helps to see the whole chain. Zeek captures traffic on the monitored interface and writes its logs to /opt/zeek/spool/zeek/. The Network Probe pipeline reads those logs, parses them, and writes the events into Energy Logserver indices under stream-zeek-*. The GUI, through Discover and any dashboards you build, then reads those indices.

Zeek data flow: you configure the sensor, One Click configures the pipeline, you build the dashboards

One Click sets up the middle of this chain: the pipeline and the indices with their template. Both ends are left to you. On the left you configure Zeek itself, because that depends on your host and network. On the right you explore the data and build dashboards, because what is worth visualizing depends on what you monitor.

Why manual host configuration is needed

One Click configures only the parts that are identical across every deployment. Three things stay in your hands because they are specific to the host:

  • The Zeek service is left masked. Masking is a systemd state in which the service unit is linked to /dev/null, so the service cannot start until you unmask it. This prevents Zeek from running before it has a capture interface and configuration.

  • The capture interface is not auto-detected. The correct interface depends on which network card actually sees the traffic you want to monitor, which no central process can know.

  • Zeek logs in TSV by default, not JSON. Zeek writes tab-separated logs out of the box, but the Network Probe pipeline expects JSON. You switch the output format on the host.

Requirements

  • Root SSH access to the Energy Logserver host running the Network Probe.

  • Admin access to the Energy Logserver GUI.

  • A capture interface that sees the traffic you want to monitor (a SPAN or mirror port).

  • Port 22 (SSH) reachable.

  • Port 443 (GUI) reachable.

Step 1: Deploy the Energy Logserver components (GUI)

  1. Log in to the GUI at https://<IP-ADDRESS-OR-HOSTNAME>.

  2. Open Tools → Integrations and select the Zeek card.

  3. Click One Click and wait for the deployment to finish.

  4. Confirm that the Pipelines step reports its pipeline as activated on every Network Probe you selected.

Step 2: Unmask the Zeek service (SSH)

Connect to the host:

ssh root@<IP-ADDRESS-OR-HOSTNAME>

Check the current state:

systemctl status zeek

Expected output:

○ zeek.service
   Loaded: masked (Reason: Unit zeek.service is masked.)
   Active: inactive (dead)

Unmask the service:

systemctl unmask zeek

The service is now unmasked but not yet running: it starts once the interface and output format are set.

Step 3: Set the capture interface

Zeek reads its node layout from /opt/zeek/etc/node.cfg. List the available interfaces:

ip link show | grep -E "^[0-9]+:"

Example output:

1: lo: <LOOPBACK,UP,LOWER_UP> ...          # loopback, do not use
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> ...

Choose the interface connected to the SPAN or mirror source, not the management interface. Back up the configuration, then set the interface:

cp /opt/zeek/etc/node.cfg /opt/zeek/etc/node.cfg.backup

Edit /opt/zeek/etc/node.cfg and point the standalone node at your interface:

[zeek]
type=standalone
host=localhost
interface=<INTERFACE>

Note

Replace <INTERFACE> with the name from this step (for example ens192). This is the deployment-specific counterpart to the capture-interface note in Installation.

Optionally, set your internal address ranges in /opt/zeek/etc/networks.cfg. Zeek uses them to classify traffic direction, which populates the locality fields (private or public) on the events.

Step 4: Switch Zeek output to JSON

The Network Probe pipeline parses JSON, so enable Zeek’s JSON log policy. Back up the site configuration first:

cp /opt/zeek/share/zeek/site/local.zeek /opt/zeek/share/zeek/site/local.zeek.backup

Append the JSON policy load to /opt/zeek/share/zeek/site/local.zeek:

# Write all logs as JSON for the Network Probe pipeline
@load policy/tuning/json-logs.zeek

Step 5: Deploy and verify Zeek

Check the configuration, then deploy it. zeekctl deploy validates the scripts, installs them, and starts Zeek:

zeekctl check
zeekctl deploy

Confirm Zeek is running:

zeekctl status

Expected output (example):

Name         Type       Host          Status    Pid    Started
zeek         standalone localhost      running   12345  ...

Because the zeek.service unit starts Zeek through zeekctl deploy, use zeekctl status, not systemctl status zeek, to read the true state of the running node.

Confirm the logs are now JSON. Give Zeek a moment to see traffic on the interface, then read the current connection log:

head -2 /opt/zeek/spool/zeek/conn.log

A JSON line confirms the format:

{"ts":1731234567.123456,"uid":"CHFgkX3PJpvTcqGvi3","id.orig_h":"10.4.9.10", ...}

If you still see tab-separated columns with #separator header lines, the policy did not load: recheck Step 4 and run zeekctl deploy again.

Enable start on boot:

systemctl enable zeek

Step 6: Confirm the pipeline is reading the logs

The One Click deployment placed the Zeek pipeline on the probe. Confirm the files are present and the service is running:

ls /etc/logserver-probe/conf.d/zeek/
systemctl status logserver-probe

Expected pipeline files:

04-zeek-input.conf   10-zeek-filter.conf   30.output.conf   ...

The input reads Zeek’s per-protocol logs from /opt/zeek/spool/zeek/ (conn.log, dns.log, http.log, ssl.log, and the rest) as JSON, and the output writes them to stream-zeek-*. Follow the probe log to confirm it picked up the files:

journalctl -u logserver-probe -f

Step 7: Verify events reach the Data Node

Check that the index exists and is filling:

curl -u <USER>:<PASSWORD> "http://localhost:9200/_cat/indices/stream-zeek-*?v"

Expected output (example):

health status index                   docs.count  store.size
green  open   stream-zeek-2025.11.15        12345       6.1mb

Confirm the document count is climbing:

curl -u <USER>:<PASSWORD> "http://localhost:9200/stream-zeek-*/_count?pretty"

A count of 0 means nothing has been indexed yet: go back and confirm Zeek is running (Step 5) and writing JSON, and that the probe is active (Step 6).

Verify end to end with test traffic

Zeek is passive, so it logs traffic that crosses the monitored link. Generate a little traffic across that link, then confirm it lands as Zeek events.

# DNS lookup and an HTTP request over the monitored path
dig example.com
curl -s http://example.com/ > /dev/null

After a short wait, look for the matching event types on the host:

sleep 30
cat /opt/zeek/spool/zeek/dns.log  | tail -2
cat /opt/zeek/spool/zeek/http.log | tail -2

DNS and HTTP entries confirm that Zeek sees the link. The next section confirms the pipeline carried them into the GUI.

Explore in Discover

Discover is where you read the raw Zeek events. Open Menu → Discover and select the index pattern:

stream-zeek-*

If the pattern does not exist yet, create it once under Management → Stack Management → Index Patterns with stream-zeek-* and @timestamp as the time field.

Zeek events in Discover

If you see “No results match your search criteria”, work through these causes in order:

  1. Wrong time range. The most common cause. Set the time picker to Last 1 hour right after starting Zeek, or Last 7 days for older events.

  2. Zeek not running. Run zeekctl status on the host. If it is not running, deploy it (Step 5).

  3. No traffic. Confirm logs exist with tail -2 /opt/zeek/spool/zeek/conn.log.

  4. Pipeline not running. Run systemctl status logserver-probe.

Useful searches

Zeek events carry the log type in netflow.zeek.type and the connection detail under netflow.*. The Discover search bar uses Lucene syntax:

netflow.zeek.type:"conn"                         # connection records
netflow.zeek.type:"dns"                          # DNS queries
netflow.zeek.type:"http"                         # HTTP requests
netflow.zeek.type:"ssl"                          # TLS sessions
netflow.src.ip:"<IP-ADDRESS>"                    # by source
netflow.dst.ip:"<IP-ADDRESS>"                    # by destination
netflow.dst.port:443                             # by destination port
netflow.dst.badip:true                           # destination flagged as a known bad IP

Columns worth adding

The default _source column is hard to scan. Add these fields from the field list on the left to get a readable table:

netflow.zeek.type       # log type (conn, dns, http, ssl, ...)
netflow.src.ip          # source
netflow.dst.ip          # destination
netflow.dst.port        # destination port
netflow.zeek.service    # detected service
netflow.protocol_name   # transport protocol

Build your own dashboards

Zeek One Click does not import dashboards, so you build the views you need. Create visualizations under Menu → Visualize → Create visualization against the stream-zeek-* index pattern, then collect them on a dashboard. A useful starter set:

Visualization

Type

Bucket / field

Top source IPs

Data table

Terms on netflow.src.ip

Protocol distribution

Pie

Terms on netflow.zeek.service

Traffic timeline

Line

Date histogram on @timestamp

Connection destinations map

Coordinate map

Geohash on the destination geo-point field, where geoip enrichment is enabled

A dashboard built from Zeek visualizations

Save the visualizations, add them to a new dashboard under Menu → Dashboard → Create dashboard, and set the time picker to a range that covers your traffic.

Maintenance

Log rotation

Zeek’s logs exist in two places, with separate retention: as files on the sensor, and as indexed events in Energy Logserver. You manage both, because a full sensor disk stops capture and unbounded indices fill the Data Node.

  1. Zeek rotates its own logs. Zeek closes the current logs in /opt/zeek/spool/zeek/ on the interval set by LogRotationInterval in /opt/zeek/etc/zeekctl.cfg; the zeekctl cron maintenance pass then compresses them and moves them to dated directories under /opt/zeek/logs/. The probe installs the cron entry */5 * * * * /opt/zeek/bin/zeekctl cron for this. If rotation is not running (an empty /opt/zeek/logs/, or LogRotationInterval=0), enable it there rather than adding a second rotation mechanism.

  2. Archived logs (/opt/zeek/logs/) are the rotated, compressed logs kept on the sensor. Purge them on a schedule so the disk does not fill:

    find /opt/zeek/logs/ -mtime +30 -delete
    
  3. Indexed events (stream-zeek-*). One Click does not install an index-management policy for these indices, so nothing prunes them by default and they grow until the Data Node fills. Create an Index Management policy (rollover and deletion) for stream-zeek-* in the GUI to bound their retention. Deleting log files on the sensor does not remove data already in Energy Logserver, and the reverse also holds.

Warning

Do not point logrotate at /opt/zeek/spool/zeek/. Zeek keeps those files open while writing, so rotating them underneath it leaves Zeek writing to a deleted file and breaks the Network Probe’s read position (the sincedb). Let Zeek rotate its own logs, and purge the archive in /opt/zeek/logs/ instead.

Zeek updates

# Back up the configuration first
tar -czf /root/zeek-config-$(date +%Y%m%d).tar.gz /opt/zeek/etc/

systemctl stop zeek
yum update zeek -y
zeekctl deploy

Performance monitoring

zeekctl status      # is the node running
zeekctl netstats    # received vs dropped packets
du -sh /opt/zeek/spool/zeek/   # current log volume

Troubleshooting

“certificate signature failure” during One Click

If One Click fails at the pipeline step with unable to execute operation! certificate signature failure, this is not a Zeek problem and is not related to the manual steps above. It comes from inconsistent license-service certificates and affects every integration installed through One Click. See Network Probe Troubleshooting for diagnosis and resolution.

Zeek will not start

Check the configuration and the diagnostic output:

zeekctl check
zeekctl diag
tail -50 /opt/zeek/spool/zeek/stderr.log

Common causes:

  • Interface does not exist: the interface in node.cfg is wrong. Recheck Step 3 with ip link show.

  • Script error: zeekctl check reports the failing line. Fix it and run zeekctl deploy again.

  • No packets arriving: confirm the mirror or SPAN port is delivering traffic with tcpdump -i <INTERFACE> -c 10.

Events reach the sensor but not the index

Zeek writes its logs, but the Network Probe pipeline is not reading them. The usual cause is file permissions: the probe reads Zeek’s logs through membership in the zeek group, so the log files must be group-readable by zeek.

ls -l /opt/zeek/spool/zeek/*.log
chown zeek:zeek /opt/zeek/spool/zeek/*.log
chmod 640 /opt/zeek/spool/zeek/*.log

640 with group zeek is enough; the probe user (logserver-probe) does not need world-readable files. The Network Probe package adds logserver-probe to the zeek group at install time, so this membership normally already exists. Confirm it, and add it if missing:

id logserver-probe | grep -o zeek || gpasswd -a logserver-probe zeek

If you just added the membership, restart the pipeline so it takes effect:

systemctl restart logserver-probe

If the log format is the problem instead, confirm it is JSON (head -2 /opt/zeek/spool/zeek/conn.log); if it is still TSV, redo Step 4.

If the pipeline read position is stuck after a bad rotation, clear the sincedb and let it re-read (this reprocesses existing logs):

systemctl stop logserver-probe
rm -f /var/lib/logserver-probe/plugins/inputs/file/.sincedb_*
systemctl start logserver-probe

Packet drops

A sustained drop rate means Zeek is not keeping up with the traffic and is missing events. Check the counters:

zeekctl netstats

If drops are high, apply these in order:

  1. Spread the load across worker processes.

    Cause: a single Zeek process is bound to one core, which caps how much traffic it can parse.

    Trade-off: each worker holds its own state and buffers, so more workers use more CPU and memory. Raise the count gradually and re-measure.

    Set lb_procs in /opt/zeek/etc/node.cfg:

    [zeek]
    type=standalone
    host=localhost
    interface=<INTERFACE>
    lb_procs=2
    
  2. Increase the capture ring buffer.

    Cause: the ring buffer absorbs bursts between the NIC and Zeek. When it fills, the kernel drops packets before Zeek sees them.

    Trade-off: a larger ring pins more memory per interface. Double it and re-measure rather than setting it very high at once.

    ethtool -G <INTERFACE> rx 4096
    

High CPU or memory use

Cause: Zeek parses and logs every protocol it recognizes, so on a busy link the volume of parsed events drives both CPU and log size.

Trade-off: disabling logs you do not use lowers load but removes that visibility. Turn off only what you knowingly do not need, in /opt/zeek/share/zeek/site/local.zeek, then zeekctl deploy. Before that, confirm the load is Zeek and not the pipeline:

ps -o pid,%cpu,%mem,cmd -C zeek

Definition of terms

Term

Meaning

NSM

Network Security Monitor. Zeek observes and records network activity in detail rather than blocking it.

conn.log

Zeek’s connection log: one record per TCP, UDP, or ICMP flow, with endpoints, duration, bytes, and state.

zeekctl

Zeek’s control utility. zeekctl deploy validates, installs, and (re)starts the configuration; zeekctl cron runs the log-rotation and maintenance pass.

networks.cfg

/opt/zeek/etc/networks.cfg, where you list the address ranges Zeek treats as your internal network. Zeek uses them to classify traffic direction and locality.

Masked service

A systemd state in which a unit is linked to /dev/null and cannot start until unmasked.

sincedb

The Network Probe’s record of how far it has read each log file, so it does not re-read from the start.

Index pattern

A wildcard such as stream-zeek-* that tells the GUI which indices to read.