Essential ZeekControl Commands

Let’s practice the basic operational commands you’ll use daily:

Check Zeek status:

sudo zeekctl status
Name         Type       Host          Status    Pid    Started
zeek         standalone localhost     running   51924  09 Oct 15:22:23

This shows if Zeek is running and provides the process ID and start time.

Stop Zeek:

sudo zeekctl stop

Zeek will gracefully shut down, closing any open log files cleanly.

Start Zeek:

sudo zeekctl start

Restart Zeek (stop then start):

sudo zeekctl restart

Deploy new configuration:

When you modify configuration files, deploy installs the new configuration and restarts Zeek:

sudo zeekctl deploy

Check configuration without deploying:

sudo zeekctl check
sudo zeekctl check
zeek scripts are ok.

This validates your configuration files without actually deploying changes. Use this to catch errors before restarting Zeek.

Monitoring Zeek Health

Zeek generates its own performance and health statistics. Let’s examine them:

Checking for dropped packets

Check for packet drops:

# View capture_loss.log
cat /opt/zeek/logs/current/capture_loss.log
zeek@zeek-sensor:/usr/local/bin$ cat /opt/zeek/logs/current/capture_loss.log
#separator \x09
#set_separator	,
#empty_field	(empty)
#unset_field	-
#path	capture_loss
#open	2025-10-09-19-19-31
#fields	ts	ts_delta	peer	gaps	acks	percent_lost
#types	time	interval	string	count	count	double
1760051971.131300	60.001036	zeek	0	0	0.0

Note the last 3 columns:

  • gaps: 0 (number of packet gaps detected)
  • acks: 0 (acknowledgments - related to packet capture)
  • percent_lost0.0 ← This is the key metric!

This of course means our Zeek instance is handling 100% of the traffic without dropping any packets. This is ideal and means:

  • Zeek is keeping up with the traffic volume
  • You have sufficient CPU/memory resources
  • Your network interface isn’t being overwhelmed

Check Zeek’s resource usage

# View stats.log
zeek-cut ts mem proc < /opt/zeek/logs/current/stats.log | tail

This shows memory usage and process statistics over time. Watch for:

  • Memory usage trending upward (potential leak)
  • Event queue depth increasing (scripts can’t keep up)

In our case below we can see memory usage was holding stable:

1760040144.969685	248
1760040444.969934	248
1760040744.970859	248
1760041044.971061	248
1760041344.976762	248
1760041644.977121	249
1760041944.977447	249
1760042244.978320	249
1760042544.978550	249
1760042844.979198	249
1760043144.980137	249
1760043444.981138	249

Check for weird/unusual events:

# View weird.log - protocol violations and unusual behavior
cat /opt/zeek/logs/current/weird.log

Zeek logs “weird” events when it sees something that doesn’t conform to protocol specifications. A few weirds are normal, but many could indicate:

  • Network scanning
  • Malformed attack traffic
  • Misconfigured applications

Here are a few examples - again we’ll cover weird.log in its own lesson:

zeek-cut name < /opt/zeek/logs/current/weird.log | sort | uniq -c | sort -rn
      5 bad_TCP_checksum
      4 active_connection_reuse
      3 inappropriate_FIN
      3 data_before_established
      2 above_hole_data_without_any_acks
      1 truncated_tcp_payload
      1 bad_UDP_checksum
  • bad_TCP_checksum: The packet’s integrity check failed, meaning the data may be corrupted.
  • active_connection_reuse: A new connection attempt (SYN packet) was seen for a connection that Zeek already considers to be active. This can sometimes be normal with aggressive NAT devices but could also indicate certain types of network scans.
  • inappropriate_FIN: A TCP packet meant to close a connection (a FIN packet) was received at a time when the connection wasn’t fully established or was in an unexpected state. This is often seen during TCP port scanning where the scanner abruptly terminates the connection attempt.
  • data_before_established: Data was sent from a client before the TCP three-way handshake was fully completed. This is highly unusual for normal traffic.
  • above_hole_data_without_any_acks: The system received a chunk of data that is far ahead of where it should be in the sequence, implying a large amount of data in between was lost. This indicates significant packet loss or reordering on the network.
  • truncated_tcp_payload: The packet header claimed the payload was a certain size, but the actual data received was smaller. This suggests the packet was cut off or damaged somewhere in transit.
  • bad_UDP_checksum: Similar to its TCP counterpart, this indicates the integrity check for a UDP packet failed. While checksums are optional for UDP on IPv4, if a checksum is present and it’s wrong, it points to data corruption.

Log Rotation and Management

As we saw in the previous section, Zeek automatically rotates logs based on your configuration. As we saw there we have current and archived logs.

Current vs. archived logs:

/opt/zeek/logs/
├── current/              # Active log files being written
│   ├── conn.log
│   ├── dns.log
│   └── ...
└── 2025-10-03/          # Rotated logs from Oct 3
    ├── conn.12:00:00-13:00:00.log.gz
    ├── conn.13:00:00-14:00:00.log.gz
    └── ...

Reading Compressed Logs

As we can see, our logs are compressed (*.gz) once they are moved to an archived directory.

Because of this, we can no longer directly cat/read the files:

zeek@zeek-sensor:/opt/zeek/logs/2025-10-10$ cat tunnel.21:00:00-22:00:00.log.gz
 ��hU��J�0��'O!�F��$��ۭʂ��"���n�MB�����v��p�s8
                                             3ԣm\�����%�C�fpK(6��O�}׋�!t�s�F��&!�ZcO���Ar�F�ϒ"�X�.}�è:P3N��Jv&�ޞ�����:�,B�e4���������s`�
                                                                                                                                        �z�AD�q����Y��i��v��8����DQU��e��2e"-������L�	K�)�/�l6���������ж7��-#�'�_���

That’s because the data is now binary, we can confirm this by running the output through hexdump:

zeek@zeek-sensor:/opt/zeek/logs/2025-10-10$ cat tunnel.21:00:00-22:00:00.log.gz | hexdump -C
00000000  1f 8b 08 00 20 ba e9 68  00 03 55 8f dd 4a c4 30  |.... ..h..U..J.0|
00000010  10 85 af 27 4f 21 e4 46  c1 86 24 fd df db ad ca  |...'O!.F..$.....|
00000020  82 a0 e8 22 5e 08 a5 b4  e3 6e a0 4d 42 92 8a eb  |..."^....n.MB...|
00000030  d3 db 76 a5 ac 70 18 be  73 38 0c 33 d4 a3 6d 5c  |..v..p..s8.3..m\|
00000040  13 8c bb fa f8 e6 25 a1  1e 43 bd 66 70 4b 28 0e  |......%..C.fpK(.|
00000050  36 9c ea 4f 85 7d 07 d7  8b b9 21 74 d4 73 ef 1c  |6..O.}....!t.s..|
00000060  46 84 da 26 1c 21 8c 5a  63 4f a8 b1 a8 41 72 99  |F..&.!.ZcO...Ar.|
00000070  46 82 cf 92 22 9a 58 16  84 2e 7d 0f c1 c3 a8 3a  |F...".X...}....:|
00000080  50 1d 33 4e 1d ea e3 4a  76 26 87 de 9e b3 85 ec  |P.3N...Jv&......|
00000090  df de 3a 9c 2c 42 d3 06  65 34 a1 b3 99 16 a9 01  |..:.,B..e4......|
000000a0  c1 07 a7 f4 01 9a ae 73  60 8d 0b 17 84 7a 1c 96  |.......s`....z..|
000000b0  41 44 9e 71 91 a4 a5 cc  59 96 c7 69 92 c3 76 ff  |AD.q....Y..i..v.|
000000c0  f0 38 bc df 7f b5 e3 8f  44 51 55 02 b2 84 65 92  |.8......DQU...e.|
000000d0  89 32 65 22 2d 80 83 88  8b c9 e5 4c c4 09 4b e2  |.2e"-......L..K.|
000000e0  29 d8 2f 87 6c 36 bb e7  15 ab dd eb f6 e9 ed ee  |)./.l6..........|
000000f0  85 d0 b6 37 1e ff bd 2d  23 ce 27 91 5f 9a ea d2  |...7...-#.'._...|
00000100  0c 63 01 00 00                                    |.c...|
00000105

OK, but so how can we then actually read the logs?

We have a few options…

Option 1: zcat (easiest)

bash

zcat tunnel.21:00:00-22:00:00.log.gz
zeek@zeek-sensor:/opt/zeek/logs/2025-10-10$ zcat tunnel.21:00:00-22:00:00.log.gz
#separator \x09
#set_separator	,
#empty_field	(empty)
#unset_field	-
#path	tunnel
#open	2025-10-10-21-25-28
#fields	ts	uid	id.orig_h	id.orig_p	id.resp_h	id.resp_p	tunnel_type	action
#types	time	string	addr	port	addr	port	enum	enum
1760145927.673547	CTGLmXFvcuz2e1DD1	64.62.195.158	0	138.197.134.43	0	Tunnel::IP	Tunnel::DISCOVER
#close	2025-10-10-22-00-00

Option 2: gunzip with pipe

gunzip -c tunnel.21:00:00-22:00:00.log.gz
zeek@zeek-sensor:/opt/zeek/logs/2025-10-10$ gunzip -c tunnel.21:00:00-22:00:00.log.gz
#separator \x09
#set_separator	,
#empty_field	(empty)
#unset_field	-
#path	tunnel
#open	2025-10-10-21-25-28
#fields	ts	uid	id.orig_h	id.orig_p	id.resp_h	id.resp_p	tunnel_type	action
#types	time	string	addr	port	addr	port	enum	enum
1760145927.673547	CTGLmXFvcuz2e1DD1	64.62.195.158	0	138.197.134.43	0	Tunnel::IP	Tunnel::DISCOVER
#close	2025-10-10-22-00-00

Option 3: zless (for viewing)

zless tunnel.21:00:00-22:00:00.log.gz
#separator \x09
#set_separator  ,
#empty_field    (empty)
#unset_field    -
#path   tunnel
#open   2025-10-10-21-25-28
#fields ts      uid     id.orig_h       id.orig_p       id.resp_h       id.resp_p       tunnel_type     action
#types  time    string  addr    port    addr    port    enum    enum
1760145927.673547       CTGLmXFvcuz2e1DD1       64.62.195.158   0       138.197.134.43  0       Tunnel::IP      Tunnel::DISCOVER
#close  2025-10-10-22-00-00
tunnel.21:00:00-22:00:00.log.gz (END)

Backup Strategy

Since Zeek logs are valuable sources of forensic data and often required for compliance, a solid backup strategy is essential. The key is balancing storage costs, retention requirements, and operational needs.

Planning Your Backup Approach

Retention periods vary widely based on organizational needs:

  • Typical ranges: 30-90 days for routine operations, 6-12 months for compliance-heavy industries
  • Legal/regulatory requirements: Healthcare (HIPAA), finance (SOX, PCI-DSS), and government sectors often mandate 1-7 years
  • Practical considerations: Balance retention against storage costs - Zeek can generate 10-100+ GB daily depending on network size

Storage destinations to consider:

  • Local backup disks: Fast, simple, but limited by physical capacity
  • Network storage (NFS/SMB): Centralized, easier to manage multiple Zeek instances
  • Cloud storage (S3, Azure Blob): Scalable and cost-effective for long-term retention, though retrieval may be slower
  • Tape archives: Still used in some enterprises for long-term compliance storage

Timing and disk management:

  • Schedule backups during low-activity periods (typically 2-4 AM) to minimize performance impact
  • Keep 15-20% free disk space on active log partitions to prevent write failures during traffic spikes
  • Monitor backup destination capacity - ensure it can hold your full retention period plus some buffer

Basic Backup Script

Here’s a straightforward backup approach that archives logs after they’ve aged a bit:

#!/bin/bash
# backup-zeek-logs.sh

# Configuration
ZEEK_LOGS="/opt/zeek/logs"
BACKUP_DIR="/backup/zeek"
RETENTION_DAYS=90
MIN_FREE_PERCENT=15

# Create backup directory structure
mkdir -p $BACKUP_DIR

# Check available space on backup destination
BACKUP_FREE=$(df -h $BACKUP_DIR | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $BACKUP_FREE -gt $((100 - MIN_FREE_PERCENT)) ]; then
    echo "$(date): WARNING - Backup destination has less than ${MIN_FREE_PERCENT}% free space" >> /var/log/zeek-backup.log
fi

# Sync logs older than 2 days to backup (gives time for log rotation to complete)
find $ZEEK_LOGS -type d -name "20*" -mtime +2 -exec rsync -av {} $BACKUP_DIR/ \;

# Delete backups older than retention period
find $BACKUP_DIR -type d -name "20*" -mtime +$RETENTION_DAYS -exec rm -rf {} \;

# Log completion with basic stats
BACKUP_SIZE=$(du -sh $BACKUP_DIR | awk '{print $1}')
echo "$(date): Backup completed - Total backup size: $BACKUP_SIZE" >> /var/log/zeek-backup.log

Save this as /usr/local/bin/backup-zeek-logs.sh, make it executable:

sudo chmod +x /usr/local/bin/backup-zeek-logs.sh

# Add to daily cron (runs at 2 AM)
sudo crontab -e
# Add:
0 2 * * * /usr/local/bin/backup-zeek-logs.sh

Production Considerations

For production environments, enhance your backup strategy with:

  • Remote storage integration: Sync to S3 (aws s3 sync), cloud storage, or network shares to protect against local hardware failures
  • Compression: Use gzip or tar to reduce storage footprint - Zeek logs compress well (often 10:1 ratios)
  • Encryption: Encrypt backups in transit and at rest, especially if storing off-site or in cloud
  • Automated monitoring: Set up alerts for backup failures, capacity thresholds, or unusual log volumes
  • Regular restore testing: Schedule quarterly restore drills to verify backup integrity - backups are only valuable if you can actually restore them

Quick tip: Many organizations keep “hot” logs (recent 7-30 days) on fast local storage for active investigations, then move older logs to cheaper backup storage for compliance retention.


|TOC| |PREV| |NEXT|