The domain that loves BSD

Home About Me Archive Contact

Implement Zeek on FreeBSD


Today it is not enough to have protective measures only to secure your network, infrastructure and data. Detective measures are even more import now, as the best approach to security is to implement a strategy that is based on the rule that you will get breached someday. So you better detect an event when it has passed your protective measures and detect is soon!

Only Argus is implemented in the SoCruel.NU network currently (see also my blog post How to capture session data with Argus on FreeBSD. So the logical next step is to get more visibility on the application and services side. An example of a current missing insight on my network is e.g. which domains are queried by the systems on my network?

And no other tool than Zeek can answer this question the best! Zeek is a network security monitoring platform which generates rich network metadata that is very valuable for general network troubleshooting, getting insight in what happens on your network, and even for incident response and forensics! Every network should run Zeek! So find below the instructions to implement it on our favorite Operating System FreeBSD!

This blog post is the first blog post of a series of posts about Zeek on FreeBSD! So more to come after this one.

Technical prerequisites

The following technical prerequisites have to be in place to be able to implement what is described in this post:

  • an up to date and supported FreeBSD system version 12.x
  • the FreeBSD system must have at least 2 Network Interface Cards (NICs)
  • 1 NIC is used for managing and using the system. This NIC has an IP address configured. This is interface em0 in this blog post.
  • (at least) 1 NIC is used to to capture packets from a network. This NIC is connected to a SPAN port or network TAP. This NIC does NOT have an IP address configured. This is interface em1 in this blog post.


No software can be implemented in the right way without defining the requirements for its intended use. So I’ve also defined requirements for the Zeek implementation discussed in this post:

  • implement Zeek on 1 host (you can implement Zeek in a distributed fashion on more hosts!)
  • use JSON based logging (1)
  • netmap is not used (not that much of network traffic is processed to be needing this!)
  • the host needs access to the internet (DNS, HTTP and HTTPS) to be able to fetch and install software

(1) : the main reason why we want to log in JSON format is that in the future we want to ship the Zeek logs to a log management system.

The configuration of the interfaces

The FreeBSD host which runs Zeek has 2 NICs per the requirements above. Assuming that these 2 NICs are called em0 and em1, these can be setup like this:

$ sudo sysrc ifconfig_em0="inet netmask" (2)
$ sudo sysrc ifconfig_em1="monitor up"

(2): it is assumed here that the host running zeek is connected to the subnet or network with the em0 interface. The em1 interface is connected to a SPAN port or network TAP.

Install required packages

Before Zeek is installed and configured, some other software and packages are needed for this implementation:

We need jq because we log in JSON format. And we want to be able to query these logs! And the best way to query JSON is using jq!

With datamash we can do calculations and stuff on the output of the queries we perform.

The commands to install these packages are:

$ sudo pkg install -y jq
$ sudo pkg install -y datamash

Install and configure Zeek

The base

Now it is time to install the Zeek software. We do this using the following command:

$ sudo pkg install -y zeek

We need to edit 3 files to configure Zeek. These files are:

  • /usr/local/etc/zeekctl.cfg
  • /usr/local/etc/node.cfg
  • /usr/local/etc/networks.cfg

The networks.cfg file defines the local private networks of the environment zeek is installed in. The format of this file is like:

# Address range    Description        LAN     DMZ     WiFi

The node.cfg file defines the nodes of our zeek implementation. As per the requirement defined above we run zeek on 1 node and we have 1 interface which captures the network traffic (change the interface for your own configuration):


It is possible to monitor more than 1 interface with Zeek running on a single host. This is discussed later in this series.

The last configuration file is called zeekctl.cfg and configures the zeekctl utility. The following contents have been chosen for our implementation:

# Mail options
MailTo = admin@domain.tld
MailConnectionSummary = 1
MinDiskSpace = 5
MailHostUpDown = 1

# Logging options
LogRotationInterval = 86400
LogExpireInterval = 35day
StatsLogEnable = 1
StatsLogExpireInterval = 35

# Other options
StatusCmdShowAll = 0
CrashExpireInterval = 0
SitePolicyScripts = local.zeek
LogDir = /var/zeek/logs
SpoolDir = /var/zeek/spool
CfgDir = /usr/local/etc

The mail options speak for themselves. If you have a MTA running on your host (like e.g. the DMA, you can get a summary send to the address specified in the MailTo item when you have set the MailConnectionSummary setting to 1. With these logging options, the logs are roteted each day (86400 seconds) and the logs are kept for 35 days. The site policy script (a file called local.zeek) is in the /usr/local/share/zeek/site folder. The SpoolDir entry defines the directory of th ecurrent log files. The archived log files are in the directory /var/zeek/logs (LogDir configuration entry).

Enable JSON logging

So we have done the base setup of zeek. Now we can add the logging in JSON format as we want. We do this by using the following command:

$ sudo tee -a /usr/local/share/zeek/site/local.zeek > /dev/null <<EOT
? # Enable JSON logging
? @load policy/tuning/json-logs

Add a cron entry

To enable Zeek maintenance we need to add an entry to the cron daemon:

$ sudo echo "*/5 * * * * root /usr/local/bin/zeekctl cron" >> /etc/crontab

Start Zeek

To finish the base setup of Zeek we have to do one more step. And that is enable Zeek in the startup script rc.conf. We can do this using:

$ sudo sysrc zeek_enable="YES"

The base setup is ready now. So we can start Zeek and capture some network traffci with it. To start Zeek we issue the command:

$ sudo service zeek deploy

We need this command as we have a new configuration. Every time the Zeek configuration changes you have to use the above command sudo service zeek deploy! In between you can start en stop Zeek by using the sudo service zeek start and sudo service zeek stop commands.

Now we check if traffic is actually logged by Zeek by looking at the contents of the connections log file conn.log:

$ cd /var/zeek/logs/current
$ head -1 conn.log | jq

With the last command you should see the contents of the first logged connection in JSON format. It should look like the below:

  "ts": 1611708585.385558,
  "uid": "CWttSl429dBU80Tip6",
  "id.orig_h": "",
  "id.orig_p": 45341,
  "id.resp_h": "",
  "id.resp_p": 53,
  "proto": "udp",
  "service": "dns",
  "duration": 0.0005099773406982422,
  "orig_bytes": 86,
  "resp_bytes": 163,
  "conn_state": "SF",
  "local_orig": true,
  "local_resp": true,
  "missed_bytes": 0,
  "history": "Dd",
  "orig_pkts": 2,
  "orig_ip_bytes": 142,
  "resp_pkts": 2,
  "resp_ip_bytes": 219

Wrap up

This blog post covered the base setup of Zeek on FreeBSD. Zeek logs in its own text based format by default. This format can be queried with the zeek-cut tool. But here Zeek is configured to log in JSON format. These logs can still be queried using the jq tool.

This is the first blog post in a series of posts about Zeek on FreeBSD. The next blog post is about configuring Zeek to run as a normal user zeek instead of root.


Some (other) resources about this subject:

Updated: December 19, 2020