Zeek Configuration¶
This page covers how to prepare a capture system, place your sensor correctly, and deploy Zeek using the Docker-based docker-zeek package from Active Countermeasures (included in the download bundle). It also covers scaling and, for environments with an existing Zeek-compatible sensor (e.g. Corelight), the exact log types and formats AC-Hunter requires.
If you are bringing your own Zeek deployment, skip to Log File and Format Reference. Note that third-party sensors may not produce the open_conn, open_http, and open_ssl logs that improve long-connection detection; AC-Hunter will work without them but coverage will be reduced.
Capture System Overview¶
For Zeek to supply logs to AC-Hunter, your network must deliver a copy of the traffic you want to analyze to the Zeek sensor. Zeek does not sit in the path of production traffic; it analyzes the copy. The following elements are required.
Traffic copy (SPAN / mirror / TAP)¶
A SPAN port, mirror port, or network TAP must be configured on your network equipment so that a duplicate of the relevant traffic is sent to the interface where the Zeek sensor will capture. Only managed switches, routers, or dedicated TAP devices support this. Unmanaged switches cannot do it. The copy should include both outbound and inbound Internet traffic (or whatever scope you intend to analyze). Placement and scope depend on your network design; see Getting Started for a high-level view of how packet capture fits into the pipeline.
Sensor host: two network interfaces¶
The Zeek sensor should be a dedicated host with two network interfaces (NICs):
-
Capture interface: Connected to the SPAN port, mirror port, or TAP. This interface receives the traffic copy and is used only by Zeek for capture. It typically has no IP address and no routing; it is not used for normal host traffic or management.
-
Management interface: Connected to your management or data network. This interface is used for administrative access (SSH, package updates, monitoring) and for shipping Zeek logs to the AC-Hunter server. It has an IP address and normal network connectivity.
Separating capture and management ensures that Zeek can consume the full capacity of the capture link without competing with admin traffic, and that the sensor remains manageable even when the capture segment is isolated.
Sizing the capture NIC¶
The capture interface must be able to handle the peak throughput of the traffic being mirrored to it. If the NIC can’t keep up, packets are silently dropped and Zeek will have gaps in its logs.
| Mirrored traffic | Minimum capture NIC | Notes |
|---|---|---|
| Up to 1 Gbps | 1 GbE | Sufficient for most small-to-mid environments |
| 1–10 Gbps | 10 GbE | Common for campus or datacenter edge links |
| 10+ Gbps | 25 GbE / 40 GbE+ | May also require kernel tuning or hardware offload (AF_PACKET, PF_RING) |
Key considerations:
- Match or exceed the mirror source. If you are mirroring a 10 Gbps uplink, a 1 GbE capture NIC will drop most of the traffic. The capture NIC should be rated at or above the peak rate of the mirrored link.
- Account for both directions. A SPAN/mirror copies both inbound and outbound traffic. A 1 Gbps Internet link with 500 Mbps down and 200 Mbps up produces up to 700 Mbps of mirrored traffic. Size the capture NIC for the combined total, not just the download speed.
- Check actual utilization, not link speed. A 10 Gbps uplink that averages 800 Mbps with peaks of 1.5 Gbps can be served by a 10 GbE capture NIC; you don’t necessarily need to match the link’s rated speed, just its real-world peak.
- The management NIC has minimal requirements. Log files are small relative to raw traffic. A 1 GbE management interface is sufficient for almost all deployments.
- Use a dedicated NIC, not a VLAN or sub-interface. The capture interface should be a physically separate port in promiscuous mode with no IP stack overhead.
Virtualization¶
Running Zeek inside a virtual machine is not recommended for packet capture. The core issues:
- Timing sensitivity. Packet capture is time-critical. VM scheduling delays, especially on a loaded host, can cause bursts of traffic to arrive faster than Zeek can process them.
- Hypervisor overhead. Each packet passes through the virtualization layer before reaching Zeek. At high packet rates this overhead alone can meaningfully increase the percentage of dropped packets.
- Resource contention. Other VMs on the same host can compete for CPU and memory. A Zeek sensor that handles its normal load well may fall behind if neighboring VMs spike, and intermittent packet loss of this type is notoriously difficult to diagnose.
- Promiscuous mode complexity. Passing a physical NIC in promiscuous mode through a hypervisor can be complex and is sometimes unsupported or poorly documented by the hypervisor vendor.
For standard on-premise deployments, use dedicated physical hardware for the Zeek sensor. If your environment requires a virtualized or cloud-based approach, use a sensor product engineered for that environment. The AC-Hunter server, by contrast, can run in a VM if given sufficient resources.
Summary¶
- Network side: Configure a SPAN/mirror/TAP so a copy of the desired traffic is sent to the sensor’s capture port.
- Sensor side: One NIC for capture (no IP, sized for peak mirrored throughput), one NIC for management and log delivery.
- Hardware: Use dedicated physical hardware for the Zeek sensor; avoid running Zeek in a VM.
Sensor Placement¶
Where you place the Zeek sensor relative to your network determines what AC-Hunter can see and how accurately it can attribute traffic to internal hosts.
Place the sensor inside the firewall¶
Place your SPAN/mirror port on the internal interface of your firewall or on the internal core switch so Zeek sees traffic between your internal hosts and the Internet.
If Zeek captures traffic outside the firewall, most firewalls perform Network Address Translation (NAT), which replaces internal source IP addresses with a single public IP. In that position, Zeek only sees the public address, not the actual internal host that originated the connection. AC-Hunter can still detect threats, but every detection would require cross-referencing your firewall’s outbound traffic logs to identify the responsible host. This adds significant complexity to every investigation and is why this configuration is not recommended.
HTTP proxy environments¶
If your organization routes outbound web traffic through an HTTP proxy, place the Zeek sensor where it can see traffic from internal endpoints to the proxy, not just the proxy’s outbound connection to the Internet. The internal-to-proxy traffic carries the real source IP of each client; the proxy-to-Internet traffic shows only the proxy’s IP as the source, making internal host attribution impossible.
DNS forwarder environments¶
Some environments use an internal DNS forwarder that resolves all client queries by forwarding them upstream to an external resolver (common with DNS filtering or blacklisting services). When Zeek is placed at the perimeter, it only sees traffic between the forwarder and the external resolver, not the individual endpoints querying the forwarder. This means AC-Hunter’s C2-over-DNS detection will attribute suspicious DNS patterns to the forwarder’s IP rather than the actual compromised host.
Recommended solution: Deploy a second Zeek sensor on the internal segment between your endpoints and the internal DNS forwarders, so it can observe per-endpoint DNS queries. Configure BPF filters on both sensors to exclude the forwarder-to-resolver traffic on the internal sensor (and vice versa), preventing the same DNS traffic from being captured twice. See Improving Packet Capture Performance for details on BPF capture filtering.
Installing Zeek¶
AC-Hunter uses docker-zeek to run a single-system Zeek cluster inside a Docker container. The Zeek CLI is included in the download bundle (see Download the Install Bundle). The instructions below will get Zeek installed and capturing on your sensor host.
Upgrading an existing sensor?
If you already have an older version of docker-zeek installed (especially v6 or earlier), follow the Updating Zeek instructions instead of starting fresh. There are migration steps for orphaned zkg volumes and previously installed packages.
1. Install Docker¶
Install Docker on the Zeek sensor host. A quick way to install it on Linux:
Otherwise, follow the official Docker install instructions for your operating system.
2. Extract and install the Zeek CLI¶
Copy the Zeek archive from the download bundle to the sensor host, then extract and install it:
Rocky / RHEL / Alma / CentOS
On these distros, /usr/local/bin may not be in sudo's secure_path, which can cause sudo zeek start to fail with a "command not found" error. Either invoke the binary by full path (sudo /usr/local/bin/zeek start) or add /usr/local/bin to secure_path in /etc/sudoers.
3. Start Zeek¶
On first run, the CLI will prompt you to pick network interfaces.
Select the network interface connected to your SPAN/mirror/TAP (the capture NIC), not the management interface. If you're unsure which interface is which, check your NIC assignments beforehand with ip link or ifconfig. The capture interface is typically the one with no IP address assigned.
After startup, Zeek will begin capturing on the selected interface and writing logs to /opt/zeek/logs/.
Once Zeek is running, configure log transport so logs are shipped to the AC-Hunter server automatically. See Zeek Log Transport for setup instructions. You can verify that logs are being generated by checking the log directory:
You should see conn.log, dns.log, and other log files being actively written.
CLI commands¶
zeek start Start the Zeek container
zeek stop Stop the Zeek container
zeek restart Restart the Zeek container
zeek status Show container and process status
zeek readpcap Process a pcap file offline
Processing a pcap¶
To process a pcap file offline:
Logs default to /opt/zeek/manual-logs/.
Customization¶
For instructions on customizing the Zeek installation, installing plugins, and adding custom Zeek scripts or zeekctl config files, see the docker-zeek README.
Updating Zeek¶
To update Zeek, download the latest install bundle, stop the running container, replace the CLI binary, and start again:
The new CLI pulls the Docker image version it was built for, so replacing the binary is what brings in the new image.
Your node.cfg and networks.cfg are preserved across upgrades. If you customized zeekctl.cfg or 100-default.zeek, your previous version is saved as .bak. Reapply your changes to the new file.
If the CLI warns about orphaned zkg volumes from an older version, see Migrating from older versions below.
After confirming the new container is working, you can free up disk space by removing the old image:
sudo docker images activecm/zeek # show what's installed
sudo docker rmi activecm/zeek:6.2.1 # replace with the tag you had
Migrating from older versions¶
In older versions of docker-zeek (v6 and prior), Zeek packages were managed using Docker volumes. In v8, these volumes are unused.
To check whether you previously installed custom packages with zkg install, list the contents of the script volume:
The v6 defaults are bro-interface-setup, bro-doctor, ja3, and zeek-open-connections. Anything else is a package you added.
- To keep using a custom package, bake it into your own image (see Adding Custom Packages in the docker-zeek README) before removing the old volumes.
- Otherwise, remove the unused volumes:
Diagnosing issues¶
If Zeek crashes right after starting, check the log output:
If Zeek is running and you want to check status:
Next: connect the sensor to AC-Hunter. Once Zeek is running and generating logs, follow the Zeek Log Transport guide to install ZeekShip and start delivering logs to the AC-Hunter server.
Scaling¶
Horizontal scaling (recommended first step)¶
When you need more capacity or coverage, scale horizontally by adding more Zeek sensors. Deploy additional sensors using the same Docker-based Zeek image (docker-zeek) on different network segments. Each sensor captures a copy of the traffic from its segment and forwards its logs to the same AC-Hunter server. AC-Hunter treats each sensor as a separate data source (e.g. by sensor name under the remote logs directory). This approach distributes capture load across multiple hosts and segments and is the preferred way to scale for most environments.
High-performance scaling on a single segment¶
If you need to capture on a single, very high-throughput segment (e.g. a 10 Gbps+ link where a single Zeek node is insufficient), refer to the official Zeek documentation and Zeek deployment guides for clustering, load balancing, and hardware offload (e.g. AF_Packet, PF_RING). Any such deployment must still produce Zeek logs that meet the log file and format requirements described in the reference section below so that AC-Hunter can import them.
Log File and Format Reference (Bring Your Own Zeek)¶
The following sections define the log types, formats, directory layout, and field expectations that AC-Hunter’s importer relies on. Use this as a reference if you are bringing your own Zeek (e.g. Corelight, a custom Zeek cluster, or another Zeek-compatible sensor) and need to ensure compatibility.
Log file types¶
The importer recognizes the following log types by filename prefix (the basename of the file must start with one of these strings):
| Prefix | Description | Required for |
|---|---|---|
conn |
Connection summary log | — |
open_conn |
Connections still open at rotation | open_http, open_ssl |
dns |
DNS queries and responses | — |
http |
HTTP protocol analysis | Requires conn in same hour |
open_http |
HTTP on open connections | Requires open_conn in same hour |
ssl |
SSL/TLS (e.g. JA3, SNI) | Requires conn in same hour |
open_ssl |
SSL/TLS on open connections | Requires open_conn in same hour |
Excluded prefixes: Files whose basename starts with conn_summary or conn-summary are not treated as connection logs and are skipped.
Not imported: Other Zeek log types (e.g. x509, capture_loss, stats, known_certs) are not imported and are reported as invalid log type.
File formats¶
Supported formats:
- Zeek TSV (tab-separated): Standard Zeek log format with a header block followed by tab-separated data rows.
- JSON (NDJSON): One JSON object per line, with field names matching Zeek/JSON naming (e.g.
ts,uid,id.orig_h,id.resp_h). - Compression: Gzip (
.gz) is supported. Files may be named e.g.conn.logorconn.log.gz. If both exist, the more recently modified file is used.
TSV header requirements: The header must include #separator, #set_separator, #empty_field, #unset_field, #path, #fields, and #types. The value of #path must match the log type (e.g. a file named open_conn.log must have #path open_conn). A mismatch produces an error.
File extensions: Only files ending in .log or .gz are considered.
Directory layout¶
- Logs are discovered by recursively walking the log directory.
- Date folders: Use
YYYY-MM-DD(e.g.2024-04-29) as subdirectory names to group logs by day. The hour is derived from the filename when present. - Multiple sensors: Each subdirectory under the remote log root is treated as a separate sensor (e.g.
remotelogs/sensor1/,remotelogs/sensor2/). Sensor names are normalized to lowercase with spaces and hyphens replaced by underscores. - Local sensor: If the local log directory has no subdirectories but contains log files (or date folders), it is treated as a single sensor named
localhost.
File naming conventions¶
- Simple:
conn.log,dns.log,http.log,ssl.log, and the correspondingopen_conn.log,open_http.log,open_ssl.log. - Hourly rotation: Optional time range in the filename, e.g.
conn.00:00:00-01:00:00.log,open_ssl.23:00:00-00:00:00.log. The importer parses the hour from this to group logs. - Multiple writers: Names like
conn_red.log,dns_blue.log.gzare accepted as long as the basename still starts with the log prefix (conn,dns, etc.).
Open_* logs (connections still open)¶
Zeek can write open_* logs for connections still open when a log file is rotated. These improve coverage for long-lived or incomplete connections.
open_conn: Same schema asconn, for connections not yet closed when the log was written.open_http/open_ssl: Same schema ashttp/ssl, for those open connections. Imported only whenopen_connis present for the same hour.httpandsslare imported only whenconnexists for that hour.
Ensure your Zeek deployment is configured to emit open_* logs if you want that coverage (see zeek-open-connections).
Key fields by log type¶
The importer expects Zeek field names and types consistent with standard Zeek output. Main fields (by log type):
conn / open_conn: ts, uid, id.orig_h, id.orig_p, id.resp_h, id.resp_p, proto, service, duration, orig_bytes, resp_bytes, conn_state, local_orig, local_resp, missed_bytes, history, orig_pkts, orig_ip_bytes, resp_pkts, resp_ip_bytes, tunnel_parents; optional agent_hostname, agent_uuid.
dns: ts, uid, connection id fields, trans_id, rtt, query, qclass, qclass_name, qtype, qtype_name, rcode, rcode_name, AA, TC, RD, RA, Z, answers, TTLs, rejected; optional agent_hostname, agent_uuid.
http / open_http: ts, uid, connection id fields, trans_depth, method, host, uri, referrer, version, user_agent, origin, body lengths, status_code, status_msg, and related HTTP fields; optional agent_hostname, agent_uuid.
ssl / open_ssl: ts, uid, connection id fields, version, cipher, server_name, session_id, resumed, established, validation_status, subject, issuer, ja3, ja3s; optional agent_hostname, agent_uuid.
Field types (count, interval, addr, port, set, vector, etc.) must be parseable; optional or newer Zeek fields may be omitted or unset.
Summary (compatibility)¶
- At least conn (or open_conn) and dns are needed for a useful import; http and ssl (and their open_* variants) depend on the corresponding conn/open_conn.
- Formats: Zeek TSV (with correct
#path) or NDJSON; optional.gz. - Layout: Recursive directory tree; use
YYYY-MM-DDand optional hourly filenames for correct day/hour grouping. - Enable and ship open_conn, open_http, and open_ssl for better coverage of long-lived or incomplete connections.