Deploying Zeek using systemd
Added in version 8.1.
Zeek includes a systemd generator
that reads the <PREFIX>/etc/zeek/zeek.conf configuration file and instantiates systemd
unit files for the manager, logger, proxy and worker processes accordingly.
The generator is installed at <PREFIX>/bin/zeek-systemd-generator.
systemd automatically runs generators during system startup, or on demand (see below).
Note
zeek.conf file’s focus is a system-level Zeek deployment tailored for
appliance or container use cases. It presents an alternative to a
ZeekControl managed deployment.
Installation
After installing Zeek on your system, link Zeek’s systemd generator into one of the standard generator directories (create it if it does not exist):
# mkdir -p /etc/systemd/system-generators/
# ln -s <PREFIX>/bin/zeek-systemd-generator /etc/systemd/system-generators/
Single Host Walkthrough
For a small Zeek deployment running two AF_PACKET worker processes on eth1, one logger, one proxy, and the manager, the following configuration is sufficient:
interface = af_packet::eth1
workers = 2
manager = 1
loggers = 1
proxies = 1
This minimal format is termed the section-less configuration style. See the Multiple Interfaces section for the more verbose INI-style configuration that is required when you want to listen on multiple interfaces.
After running systemctl daemon-reload, which executes zeek-systemd-generator,
the generated unit files are found in the /run/systemd/generator directory of
the system:
# tree /run/systemd/generator | grep zeek
├── zeek-archiver.service
├── zeek-logger@.service
├── zeek-manager.service
├── zeek-proxy@.service
├── zeek-setup.service
├── zeek.target
├── zeek.target.wants
│ ├── zeek-archiver.service -> ../zeek-archiver.service
│ ├── zeek-logger@1.service -> ../zeek-logger@.service
│ ├── zeek-manager.service -> ../zeek-manager.service
│ ├── zeek-proxy@1.service -> ../zeek-proxy@.service
│ ├── zeek-setup.service -> ../zeek-setup.service
│ ├── zeek-worker@1.service -> ../zeek-worker@.service
│ └── zeek-worker@2.service -> ../zeek-worker@.service
├── zeek-worker@1.service.d
│ └── 10-zeek-systemd-generator.conf
├── zeek-worker@2.service.d
│ └── 10-zeek-systemd-generator.conf
└── zeek-worker@.service
If you’re curious about all these different unit files and processes, take a look at Cluster Architectures and Spawning a Cluster. Zeek is a relatively complex multi-process application.
Note
Running systemctl daemon-reload does not provide immediate feedback about
failures when running generators. If you do not see service unit files created,
check for error messages using journalctl.
# journalctl -f
Jul 27 17:10:31 tinkyx1 systemd[1]: Reloading...
Jul 27 17:10:31 tinkyx1 /etc/systemd/system-generators/zeek-systemd-generator: config /opt/zeek-dev-prod/etc/zeek/zeek.conf is invalid
Jul 27 17:10:31 tinkyx1 /etc/systemd/system-generators/zeek-systemd-generator: invalid workers value: '16x'
Jul 27 17:10:31 tinkyx1 (sd-exec-[2365550]: /etc/systemd/system-generators/zeek-systemd-generator failed with exit status 1.
You can also invoke the generator, optionally passing –config to validate the
zeek.conf file beforehand. The units will be generated into directory provided
as the last argument. If --config is not provided, it defaults to <PREFIX/etc/zeek/zeek.confg.
# <PREFIX>/bin/zeek-systemd-generator --config <PREFIX>/etc/zeek/zeek.conf /tmp/out
config <PREFIX>/etc/zeek/zeek.conf is invalid
invalid workers value: '1typo'
If you have AppArmor or SELinux enabled, watch out for messages in the system
logs; there may be extra configuration required to allow running the
zeek-systemd-generator and zeek-cluster-layout-generator executables
from where you installed them.
Start Zeek using systemctl with the zeek.target unit:
# systemctl start zeek.target
Inspect the status of the individual processes with systemd-cgtop zeek.slice
or systemctl status zeek.slice:
# systemctl status zeek.slice
● zeek.slice - Slice /zeek
Loaded: loaded
Active: active since Mon 2026-04-20 17:40:28 CEST; 3 days ago
Tasks: 90
Memory: 477.8M (peak: 2.5G swap: 0B swap peak: 999.1M)
CPU: 1h 9min 36.236s
CGroup: /zeek.slice
├─zeek-archiver.slice
│ └─zeek-archiver.service
│ └─2601102 /opt/zeek/bin/zeek-archiver /opt/zeek/var/spool/zeek/log-queue /opt/zeek/var/logs/zeek
├─zeek-loggers.slice
│ └─zeek-logger@1.service
│ └─2601103 /opt/zeek/bin/zeek policy/misc/systemd-generator local frameworks/cluster/backend/zeromq
├─zeek-manager.slice
│ └─zeek-manager.service
│ └─2601104 /opt/zeek/bin/zeek policy/misc/systemd-generator local frameworks/cluster/backend/zeromq
├─zeek-proxies.slice
│ └─zeek-proxy@1.service
│ └─2601112 /opt/zeek/bin/zeek policy/misc/systemd-generator local frameworks/cluster/backend/zeromq
└─zeek-workers.slice
├─zeek-worker@1.service
│ └─2601123 /opt/zeek/bin/zeek -i af_packet::eth1 policy/misc/systemd-generator local frameworks/cluster/backend/zeromq
└─zeek-worker@2.service
└─2601107 /opt/zeek/bin/zeek -i af_packet::eth1 policy/misc/systemd-generator local frameworks/cluster/backend/zeromq
Individual Zeek processes are started as user zeek and group zeek. This is
configurable with the user and group keys in the zeek.conf file.
To inspect the logs of the individual Zeek processes, use journalctl, maybe
together with --reverse or --follow:
# journalctl --follow -u 'zeek*'
Apr 24 13:42:14 tinkyx1 systemd[1]: Starting zeek-worker@1.service - Zeek Worker 1...
Apr 24 13:42:14 tinkyx1 systemd[1]: Started zeek-worker@2.service - Zeek Worker 2.
Apr 24 13:42:14 tinkyx1 systemd[1]: Started zeek-worker@1.service - Zeek Worker 1.
Apr 24 13:42:14 tinkyx1 systemd[1]: Reached target zeek.target - The Zeek Network Security Monitor.
Apr 24 13:42:15 tinkyx1 zeek-worker-2[2602816]: listening on eth1
Apr 24 13:42:15 tinkyx1 zeek-worker-1[2602825]: listening on eth1
Apr 24 13:46:44 tinkyx1 systemd[1]: zeek-worker@1.service: Main process exited, code=dumped, status=6/ABRT
Apr 24 13:46:44 tinkyx1 systemd[1]: zeek-worker@1.service: Failed with result 'core-dump'.
Apr 24 13:46:45 tinkyx1 systemd[1]: zeek-worker@1.service: Scheduled restart job, restart counter is at 2.
Apr 24 13:46:45 tinkyx1 systemd[1]: Starting zeek-worker@1.service - Zeek Worker 1...
Apr 24 13:46:45 tinkyx1 systemd[1]: Started zeek-worker@1.service - Zeek Worker 1.
Apr 24 13:46:45 tinkyx1 zeek-worker-1[2603512]: listening on eth1
This output shows zeek-worker@1 terminating due to a SIGABRT (here, kill -SIGABRT
was used for demonstration purposes) and systemd automatically restarting the process
and reporting the current restart counter value. Use systemctl show zeek-worker@1
to inspect this counter and various other details.
For debugging of crashes and coredump handling in general, we recommend installing
systemd-coredump
if your distro doesn’t automatically do so and setting up /etc/systemd/coredump.conf as needed.
Inspecting coredumps is then possible with coredumpctl list, coredumpctl debug, etc:
# PAGER= coredumpctl info | head -20
PID: 2603455 (zeek)
UID: 997 (zeek)
GID: 995 (zeek)
Signal: 6 (ABRT)
Timestamp: Fri 2026-04-24 13:46:43 CEST (48min ago)
Command Line: /opt/zeek/bin/zeek -i af_packet::eth1 policy/misc/systemd-generator local frameworks/cluster/backend/zeromq
Executable: /opt/zeek/bin/zeek
Control Group: /zeek.slice/zeek-workers.slice/zeek-worker@1.service
Unit: zeek-worker@1.service
Slice: zeek-workers.slice
Boot ID: 0d20f470e7a9413d92f7324c27866992
Machine ID: c9dbccfd439e43b598fa6153f5fa9e3e
Hostname: tinkyx1
Storage: /var/lib/systemd/coredump/core.zeek.997.0d20f470e7a9413d92f7324c27866992.2603455.1777031203000000.zst (present)
Size on Disk: 10.4M
Message: Process 2603455 (zeek) of user 997 dumped core.
Multiple Interfaces
Added in version 9.0.
To monitor multiple interfaces, the zeek.conf file supports INI-style sections
where there is one section per interface. Each interface section is named. The
name of the secution is used in systemd’s unit files, working directories, and also
the cluster node name. The names used in the sample configuration below are eth1
and eth2, but could also be internal and external or anything
descriptive.
When using the INI-style format, place non-interface options into the [zeek]
section, conventionally put at the top of the file:
[zeek]
manager = 1
loggers = 3
proxies = 7
[interface eth1]
interface = af_packet::eth1
workers = 4
workers_cpu_list = 4-7
worker_args = AF_Packet::fanout_id=42
[interface eth2]
interface = af_packet::eth2
workers = 4
workers_cpu_list = 8-11
worker_args = AF_Packet::fanout_id=4711
worker_env =
LD_PRELOAD=/usr/local/lib/libjemalloc.so
MALLOC_CONF=prof:true,prof_prefix:jeprof.out,prof_final:true,lg_prof_interval:26
With AF_PACKET, the respective workers have to use different fanout groups, so we need
to pass an explicit AF_Packet::fanout_id setting via the worker_args option.
This configuration also pins workers sequentially onto CPUs 4 through 11
and enables jemalloc profiling for all workers listening on eth2 via the
worker_env option.
You will find that the worker unit names include the section names when
configured like this. I.e., instead of zeek-worker@1, the first worker
for each interface is zeek-worker-eth1@1 and zeek-worker-eth2@2.
The CLUSTER_NODE and Cluster::node values change accordingly:
worker-1 is worker-eth1-1 and worker-eth2-1, respectively.
You can see these values in the generated cluster-layout.zeek file
at <PREFIX>/var/spool/zeek/generated-scripts/.
Note
You may wonder why there’s even a section-less configuration style.
It stems mostly from the initial zeek.conf version only supporting
a single interface and not having sections. Further, working directories,
unit names and the CLUSTER_NODE value for worker processes exclude
the interface section name as an identifier to make them a bit shorter.
The INI-style format works with a single interface just as well, so you can just use that exclusively and ignore the section-less style.
Warning
When lowering the number of processes in zeek.conf and running only
systemctl daemon-reload and systemctl restart zeek.target, systemd
will keep information about the units for the old processes around and attempt
to restart them. You’ll observe errors in the journalctl output as follows:
Sep 19 20:50:44 tinkyx1 (zeek)[519898]: zeek-worker-wifi@2.service: Referenced but unset environment variable evaluates to an empty string: INTERFACE
Sep 19 20:50:44 tinkyx1 zeek-worker-wifi-2[519898]: error in /opt/zeek-dev-prod/share/zeek/base/frameworks/cluster/main.zeek, line 421: 'worker-wifi-2' is not a valid node in the Cluster::nodes configuration
Sep 19 20:50:44 tinkyx1 zeek-worker-wifi-2[519898]: fatal error: errors occurred while initializing
Here, the number of workers for the wifi interface was changed from two to one. The second worker unit is still around and fails to restart.
We’ve reported this observation upstream under systemd/systemd#40092.
To avoid running into this issue when changing the number of processes, run
systemctl stop zeek.target before running systemctl daemon-reload,
and then run systemctl start zeek.target.
Changing the number of processes is rarely done in production, so this is a reasonable workaround.
Worker Interface and Environment Templating
For worker processes specifically, the interface and worker_env options
support templating. For example, selecting a worker-specific Napatech stream
for the interface can be expressed as follows:
[interface napatech]
interface = napatech::${worker_index0}
workers = 32
The ${worker_index0} variable expands to the zero-based index of each worker
for this interface (0 through 31) and so every worker receives an individual
interface napatech::0, napatech::1 through napatech::31. This can
also be useful to select a specific NETMAP pipe per worker.
Templating also works for the single interface section-less configuration style:
interface = netmap::eth1{${worker_index0}
workers = 32
manager = 1
loggers = 2
proxies = 4
Following is a list of the supported variables.
|
The worker index starting at 1. Resets to 1 per interface section. |
|
The worker index starting at 0. Resets to 0 per interface section. |
|
The worker index within the host. Starts at 1 and doesn’t reset. |
|
The worker index within the host. Starts at 0 and doesn’t reset. |
|
The CPU a worker will be pinned on. Only available if |
|
The name part from the |
To use templating in environment variables, use the same variable names
in the worker_env:
[zeek]
manager = 1
loggers = 3
proxies = 7
[interface eth1]
interface = af_packet::${interface_section_name}
workers = 8
workers_cpu_list = 4-11
worker_env =
INTF=${interface_section_name}
CPU=${worker_cpu}
Multi Host Walkthrough
Added in version 9.0.
Note
Multi-host support using the zeek.conf is still being refined. We plan to finalize this towards Zeek 10.0. We welcome testing and feedback!
Two assumptions for running a multi-host Zeek cluster exist. First, all nodes use compatible Zeek versions. This usually means deploying the exact same Zeek build, plugins and packages everywhere. Second, name resolution on each system allows to find the other hosts addresses.
To create a Zeek cluster that spans multiple hosts, the format of the zeek.conf
file does not change. However, you place the configuration files of all hosts of the
cluster at <PREFIX>/etc/zeek/cluster/<hostname>.zeek.conf on each host.
That means that every host has visibility into every other host’s configuration.
Use some configuration management tool to keep the <PREFIX>/etc/zeek/cluster
directories in sync. In a container environment, a volume mount is a viable
alternative, too.
When no <PREFIX>/etc/zeek/zeek.conf file exists, the zeek-systemd-generator
will attempt to open <PREFIX>/etc/zeek/cluster/<hostname>.zeek.conf using the
hostname of the system it is running on. If the file is found, it’ll use it to configure
the appropriate systemd services. The zeek-cluster-layout-generator executable uses
the contents of <PREFIX>/etc/cluster to create the cluster-layout.zeek file.
For a minimal three host cluster, where host c-mgr runs manager, 3 loggers,
2 proxies and the archiver and two hosts c-w-01 and c-w-02 that each
run 4 workers, listening on their eth1 interface using AF_PACKET, the following
configuration files are sufficient.
# <PREFIX>/etc/zeek/cluster/c-mgr.zeek.conf
[zeek]
manager = 1
loggers = 3
proxies = 2
archiver = 1
cluster_backend_args = misc/zeromq-multi-host-auto-setup
# <PREFIX>/etc/zeek/cluster/c-w-01.zeek.conf
[zeek]
cluster_backend_args = misc/zeromq-multi-host-auto-setup
[interface eth1]
interface = af_packet::eth1
workers = 4
# <PREFIX>/etc/zeek/cluster/c-w-02.zeek.conf
[zeek]
cluster_backend_args = misc/zeromq-multi-host-auto-setup
[interface eth1]
interface = af_packet::eth1
workers = 4
You synchronize the full <PREFIX>/etc/zeek/cluster directory with these
three files to all systems:
etc
`-- zeek
`-- cluster
|-- c-mgr.zeek.conf
|-- c-w-01.zeek.conf
`-- c-w-02.zeek.conf
Then, run the following commands on each system to start all processes on each of the hosts:
# systemctl daemon-reload
# systemctl start zeek.target
On the c-mgr host, in <PREFIX>/var/spool/zeek/logger-{1,2,3}/cluster.log,
you should see each of the Zeek nodes saying hello to each other node.
# for f in logger-*; do echo $f/cluster.log; head -3 $f/cluster.log; done
logger-1/cluster.log
{"ts":1784135106.521939,"node":"c-mgr-logger-1","message":"got hello from c-w-01-worker-eth1-1 (zeromq_c-w-01-worker-eth1-1_c-w-01_55_NfzaVa4ZA3BAmvqPx9)"}
{"ts":1784135106.521939,"node":"c-mgr-logger-1","message":"got hello from c-w-01-worker-eth1-2 (zeromq_c-w-01-worker-eth1-2_c-w-01_56_NL4dCr4F5Hca3bxLKe)"}
{"ts":1784135106.521939,"node":"c-mgr-logger-1","message":"got hello from manager (zeromq_manager_c-mgr_91_NIIFQV17DZe55r15Bd)"}
logger-2/cluster.log
{"ts":1784135106.446018,"node":"c-mgr-logger-2","message":"got hello from c-w-01-worker-eth1-3 (zeromq_c-w-01-worker-eth1-3_c-w-01_57_NIupbB37F2DKh2SDCg)"}
{"ts":1784135106.446018,"node":"c-mgr-logger-2","message":"got hello from manager (zeromq_manager_c-mgr_91_NIIFQV17DZe55r15Bd)"}
{"ts":1784135106.446263,"node":"c-mgr-logger-2","message":"got hello from c-mgr-logger-3 (zeromq_c-mgr-logger-3_c-mgr_90_Nig6uU2hdCFqaJr0c6)"}
logger-3/cluster.log
{"ts":1784135106.413847,"node":"c-mgr-logger-3","message":"got hello from c-w-01-worker-eth1-3 (zeromq_c-w-01-worker-eth1-3_c-w-01_57_NIupbB37F2DKh2SDCg)"}
{"ts":1784135106.413847,"node":"c-mgr-logger-3","message":"got hello from manager (zeromq_manager_c-mgr_91_NIIFQV17DZe55r15Bd)"}
{"ts":1784135106.445795,"node":"c-mgr-logger-3","message":"got hello from c-mgr-logger-2 (zeromq_c-mgr-logger-2_c-mgr_89_NAfhZd445aDO9AY8ed)"}
# cat logger-*/cluster.log | jq .node | sort | uniq -c
13 "c-mgr-logger-1"
13 "c-mgr-logger-2"
13 "c-mgr-logger-3"
13 "c-mgr-proxy-1"
13 "c-mgr-proxy-2"
13 "c-w-01-worker-eth1-1"
13 "c-w-01-worker-eth1-2"
13 "c-w-01-worker-eth1-3"
13 "c-w-01-worker-eth1-4"
13 "c-w-02-worker-eth1-1"
13 "c-w-02-worker-eth1-2"
13 "c-w-02-worker-eth1-3"
13 "c-w-02-worker-eth1-4"
13 "manager"
You successfully started up a multi-host Zeek cluster!
Archiver Support
Besides the Zeek processes participating in a cluster, support for log archiving is
enabled by default on hosts that run least one logger process or run the manager process:
This functionality is provided by zeek-archiver
and is visible earlier in the zeek.slice output:
CGroup: /zeek.slice
├─zeek-archiver.slice
│ └─zeek-archiver.service
│ └─2601102 /opt/zeek/bin/zeek-archiver /opt/zeek/var/spool/zeek/log-queue /opt/zeek/var/logs/zeek
To customize this functionality, you may set the archiver option in zeek.conf
to 0 (disabled), 1 (enabled using zeek-archiver),
or a path to a custom archiver executable that is expected to run continuously.
The latter may be used to run a log shipper that follows log files
created within the working directories of loggers.
The working directory for the archiver process is <PREFIX>/var/spool/zeek
such that ./logger-*/ can be used to find the loggers’ working directories,
or the ./log-queue/ directory into which loggers rotate their completed
logs by default.
zeek-archiver’s implementation is currently poll-based, listing the contents
of the log-queue directory in regular intervals, then compressing and rotating any
found logs into <PREFIX>/var/log/zeek/<date>/. Further parameters to the archiver
can be given via archiver_args.
[zeek]
...
archiver = 1
# Enable verbose logging and reduce the polling interval from 30 to 3 seconds.
archiver_args = -v -i 3
Using a custom archiver, like filebeat, looks as follows:
[zeek]
...
# Switch to using solely corelight/json-streaming-logs for logging.
args = corelight/json-streaming-logs JSONStreaming::disable_default_logs=T
# Switch to filebeat and cap resources a bit.
archiver = /path/to/filebeat
archiver_args = run -c /path/to/filebeat.yml --modules zeek
archiver_cpu_set = 15,16
archiver_memory_max = 2G
You’ll need to adapt the logging logic of Zeek and provide a matching filebeat.yml
file for this to work properly.
Process Capabilities
This section is about Linux capabilities as they apply to Zeek processes. See the
capabilities manpage
for details. systemd supports the
AmbientCapabilities and CapabilityBoundingSet
settings. For usability reasons, systemd units for Zeek workers default the AmbientCapabilities
to CAP_NET_RAW. The CapabilityBoundingSet setting is left alone.
If you want to control process capabilities yourself, for example to switch to file-based capabilities or add additional ambient capabilities, do so using systemd drop-in files.
Concretely, to modify the AmbientCapabilities for all Zeek worker processes,
place a drop-in unit file either into the zeek-worker@.service.d drop-in directory
when using the section-less format, or into a prefix drop-in directory named
zeek-worker-.service.d when using the INI-like format. The latter is a bit magic:
systemd searches for drop-in directories using all dash-separated prefixes of a unit
name followed by .service.d. Worker unit files have the zeek-worker- prefix
when using the INI-like format as for zeek-worker-eth1@.service and
zeek-worker-eth2@.service with interface section names eth1 and eth2.
# cat /etc/systemd/system/zeek-worker-.service.d/10-drop-capabilities.conf
[Service]
# Remove all ambient capabilities from all worker processes.
AmbientCapabilities=
# cat /etc/systemd/system/zeek-worker-.service.d/10-more-capabilities.conf
[Service]
# Set capabilities for worker processes to add CAP_NET_ADMIN and
# CAP_IPC_LOCK in addition to CAP_NET_RAW
AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN CAP_IPC_LOCK
When unsetting AmbientCapabilities as on the first line, Zeek workers listening
on a real interface will fail to start because they are not permitted to read raw
packets anymore. If you have previously used file-based capabilities, you can
continue doing, either by setcap the executable during installation, or update
the zeek file capabilities by placing a drop-in file for the zeek-setup.service
unit that adds an ExecStart command to run setcap. This is shown below, setcap
is used to add CAP_NET_RAW and CAP_NET_ADMIN to the effective and permissible
capability sets:
# cat /etc/systemd/system/zeek-setup.service.d/10-setcap-zeek.conf
[Service]
# Add file-based capabilities during zeek-setup.service execution
ExecStart=/usr/sbin/setcap cap_net_raw,cap_net_admin+ep ./bin/zeek
The zeek-setup.service runs with a working directory of zeek.conf’s base_dir
option (PREFIX by default) and so using the relative path to ./bin/zeek works out.
Note that if you do this, the manager, logger and proxy processes will also receive
CAP_NET_RAW and CAP_NET_ADMIN capabilities, even though they likely do not need
those.
To verify the final capabilities of a running process, find its PID via
systemctl status zeek.slice and use getpcaps on it:
# getpcaps 129003
129003: cap_net_admin,cap_net_raw=ep
Using PF_RING with the zeek-systemd-generator
In contrast to ZeekControl’s convenience option lb_method for setting
up PF_RING, the zeek-systemd-generator approach exposes you directly to
PF_RING’s underlying libpcap configuration.
As a prerequisite, ensure you have PF_RING installed. You can do this by either
compiling from source as shown in the PF_RING Setup Guide, or by installing
the packages from packages.ntop.org.
PF_RING provides a custom libpcap version that Zeek can use. Either compile Zeek
using --with-pcap=/usr/local, or set LD_PRELOAD=/usr/local/lib/libpcap.so
to preload PF_RING’s libpcap version. The following configuration does the latter:
# zeek.conf
[zeek]
manager = 1
loggers = 1
proxies = 2
[interface enp8s0]
interface = enp8s0
workers = 4
# Preload PF_RING's libpcap.so library and setup the environment for
# flow-balancing on inner five tuples. Find more environment variables
# recognized by PF_RING's libpcap version here:
#
# https://www.ntop.org/guides/pf_ring/api/libpcap.html
#
worker_env =
LD_PRELOAD=/usr/local/lib/libpcap.so
PCAP_PF_RING_APPNAME=zeek-${interface_section_name}
PCAP_PF_RING_CLUSTER_ID=42
PCAP_PF_RING_USE_CLUSTER_PER_INNER_FLOW_5_TUPLE=1
Note
The LD_PRELOAD environment variable is ignored if the Zeek executable
has file-based capabilities set. Compile Zeek using --with-pcap if you
need file-based capabilities.
After running systemctl daemon-reload and systemctl start zeek.target,
check with journalctl for any error. Check /proc/net/pf_ring/ for the
status files for the Zeek workers:
# ls -l /proc/net/pf_ring/[0-9]*
-r--r--r-- 1 root root 0 Aug 26 11:16 /proc/net/pf_ring/4710-enp8s0.31
-r--r--r-- 1 root root 0 Aug 26 11:16 /proc/net/pf_ring/4711-enp8s0.30
-r--r--r-- 1 root root 0 Aug 26 11:16 /proc/net/pf_ring/4717-enp8s0.29
-r--r--r-- 1 root root 0 Aug 26 11:16 /proc/net/pf_ring/4720-enp8s0.32
# grep 'Appl' /proc/net/pf_ring/[0-9]*
/proc/net/pf_ring/4710-enp8s0.31:Appl. Name : zeek-enp8s0
/proc/net/pf_ring/4711-enp8s0.30:Appl. Name : zeek-enp8s0
/proc/net/pf_ring/4717-enp8s0.29:Appl. Name : zeek-enp8s0
/proc/net/pf_ring/4720-enp8s0.32:Appl. Name : zeek-enp8s0
As you can see, the Application Name setting from the PCAP_PF_RING_APPNAME
environment variable has been applied. You’ll also find the Cluster Id and
various other status information in these files. If you use multiple interfaces,
you must set a different PCAP_PF_RING_CLUSTER_ID for each of them:
[zeek]
manager = 1
loggers = 1
proxies = 2
[interface enp7s0]
interface = enp7s0
workers = 4
worker_env =
PCAP_PF_RING_APPNAME=zeek-${interface_section_name}
PCAP_PF_RING_CLUSTER_ID=21
PCAP_PF_RING_USE_CLUSTER_PER_INNER_FLOW_5_TUPLE=1
[interface enp8s0]
interface = enp8s0
workers = 4
worker_env =
PCAP_PF_RING_APPNAME=zeek-${interface_section_name}
PCAP_PF_RING_CLUSTER_ID=42
PCAP_PF_RING_USE_CLUSTER_PER_INNER_FLOW_5_TUPLE=1
This example does not use LD_PRELOAD and assumes the Zeek executable
is linked to PF_RING’s libpcap.so library instead.
Cluster Layout Notes
Note
Some of the current requirements around the static cluster-layout.zeek
file are historic and there’s ideas to relax some of them.
As of version 9.0, each host needs to be statically aware of all other hosts in
a cluster and able to resolve their IP addresses though the hostname as used in
the configuration filenames. Adding the required entries into /etc/hosts on
the respective systems is one way to achieve this, DNS is another. Container
orchestrators usually allow specifying hostnames for containers or pods,
and provide name resolution services as well.
The zeek-cluster-layout-generator executable creates the contents
of a cluster-layout.zeek file.
You can point the zeek-cluster-layout-generator executable at a
<PREFIX>/etc/zeek/cluster directory and inspect the generated layout
for a multi-host Zeek cluster.
If you do not specify a cluster_address option within the individual
<hostname>.zeek.conf files, the generated cluster-layout.zeek file
contains extra script code to use Zeek’s DNS functionality to lookup the
addresses of the involved hosts.
# zeek-cluster-layout-generator -C ./cluster/
# Auto-generated by zeek-cluster-layout-generator
...
const hosts: table[string] of Host &ordered &redef;
redef Cluster::hosts += {
["c-mgr"] = [$ip=(blocking_lookup_hostname("c-mgr") as vector of addr)[0]],
["c-w-01"] = [$ip=(blocking_lookup_hostname("c-w-01") as vector of addr)[0]],
["c-w-02"] = [$ip=(blocking_lookup_hostname("c-w-02") as vector of addr)[0]],
};
redef Cluster::nodes += {
["manager"] = [$node_type=Cluster::MANAGER, $ip=hosts["c-mgr"]$ip, $p=27760/tcp, $metrics_port=9991/tcp],
["c-mgr-logger-1"] = [$node_type=Cluster::LOGGER, $ip=hosts["c-mgr"]$ip, $p=27761/tcp, $manager="manager", $metrics_port=9992/tcp],
}
This means that each Zeek process will determine the addresses of all other
hosts in a cluster when loading the generated cluster-layout.zeek script
during startup. That’s why it is important to have a functional /etc/hosts
or DNS setup.
If you want to control IP addresses and cluster node prefixes used,
set cluster_address and cluster_node_prefix explicitly for every
host as in the following example. Setting cluster_node_prefix to an
empty string unsets the prefix for that host.
# <PREFIX>/etc/zeek/cluster/c-mgr.zeek.conf
[zeek]
manager = 1
loggers = 3
proxies = 2
archiver = 1
cluster_node_prefix =
cluster_address = 10.0.0.1
cluster_backend_args = misc/zeromq-multi-host-auto-setup
# <PREFIX>/etc/zeek/cluster/c-w-01.zeek.conf
[zeek]
cluster_node_prefix = worker-01
cluster_address = 10.0.0.2
cluster_backend_args = misc/zeromq-multi-host-auto-setup
[interface eth1]
interface = af_packet::eth1
workers = 4
# <PREFIX>/etc/zeek/cluster/c-w-02.zeek.conf
[zeek]
cluster_node_prefix = worker-02
cluster_address = 10.0.0.3
cluster_backend_args = misc/zeromq-multi-host-auto-setup
[interface eth1]
interface = af_packet::eth1
workers = 4
If you want to use your own completely-custom cluster-layout.zeek file,
it is possible to set the cluster_layout option to a path. The generated
zeek-setup.service will copy the file into <PREFIX>/var/spool/zeek/generated-scripts
instead of dynamically creating a layout.
[zeek]
cluster_layout = /path/to/custom-cluster-layout.zeek
cluster_backend_args = misc/zeromq-multi-host-auto-setup
...
Cluster Layout Debugging
You can debug the resulting layout using Zeek itself:
# zeek -b <PREFIX>/var/spool/zeek/generated-scripts/cluster-layout.zeek -e 'print Cluster::nodes'
{
[c-w-02-worker-vxlan-4] = [node_type=Cluster::WORKER, ip=fd00:dead:beef::12, zone_id=, p=0/unknown, manager=manager, id=<uninitialized>, metrics_port=9994/tcp],
[c-w-01-worker-vxlan-3] = [node_type=Cluster::WORKER, ip=fd00:dead:beef::11, zone_id=, p=0/unknown, manager=manager, id=<uninitialized>, metrics_port=9993/tcp],
...
}
For the single-host case, the invocation of zeek-cluster-layout-generator contains
explicit logger, proxy, worker counts rather than reading zeek.conf files:
# zeek-cluster-layout-generator -W 1 -a 192.168.0.1 -m 0 -p 5000
# Auto-generated by zeek-cluster-layout-generator
redef Cluster::nodes += {
["manager"] = [$node_type=Cluster::MANAGER, $ip=192.168.0.1, $p=5000/tcp, $metrics_port=0/tcp],
["logger-1"] = [$node_type=Cluster::LOGGER, $ip=192.168.0.1, $p=5001/tcp, $manager="manager", $metrics_port=0/tcp],
["proxy-1"] = [$node_type=Cluster::PROXY, $ip=192.168.0.1, $p=5002/tcp, $manager="manager", $metrics_port=0/tcp],
["worker-1"] = [$node_type=Cluster::WORKER, $ip=192.168.0.1, $manager="manager", $metrics_port=0/tcp],
};
...
You can use the zeek-cluster-layout-generator executable in non-Linux
and non-systemd environments as well.