Event Groups
Zeek supports enabling and disabling event and hook handlers at runtime through event groups. While named event groups, hook handlers are covered due to their structural similarity to event handlers as well.
Event and hook handlers can be part of multiple event groups. An event or hook handler is disabled if any of the groups it’s part of is disabled. Conversely, event and hook handlers are enabled when all groups they are part of are enabled. When Zeek starts, all event groups are implicitly enabled. An event or hook handler that is not part of any event group is always enabled.
Currently, two types of event groups exist: Attribute and module based.
Attribute Based Event Group
Attribute based event groups come into existence when an event or hook
handler has a &group
attribute. The value of the group
attribute is a string identifying the group. There’s a single global namespace
for attribute based event groups. Two event handlers in different files
or modules, but with the same group attribute value, are part of the same group.
Event and hook handlers can have more than one group attributes.
1event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) &group="http-print-debugging"
2 {
3 print fmt("HTTP request: %s %s (%s->%s)", method, original_URI, c$id$orig_h, c$id$resp_h);
4 }
5
6event http_header(c: connection, is_orig: bool, original_name: string, name: string, value: string) &group="http-print-debugging"
7 {
8 if ( name != "USER-AGENT" && name != "SERVER" )
9 return;
10
11 local snd = is_orig ? c$id$orig_h : c$id$resp_h;
12 local rcv = is_orig ? c$id$resp_h : c$id$orig_h;
13 print fmt("HTTP header : %s=%s (%s->%s)", original_name, value, snd, rcv);
14 }
15
16event http_reply(c: connection, version: string, code: count, reason: string) &group="http-print-debugging"
17 {
18 print fmt("HTTP reply: %s/%s version %s (%s->%s)", code, reason, version, c$id$resp_h, c$id$orig_h);
19 }
This example shows http_request
, http_header
and http_reply
event
handlers, all with a group attribute of http-print-debugging
.
When running Zeek against a pcap containing a single HTTP transaction,
the output is as follows.
$ zeek -r traces/get.trace ./event_groups_attr_01.zeek
HTTP request: GET /download/CHANGES.bro-aux.txt (141.142.228.5->192.150.187.43)
HTTP header : User-Agent=Wget/1.14 (darwin12.2.0) (141.142.228.5->192.150.187.43)
HTTP reply: 200/OK version 1.1 (192.150.187.43->141.142.228.5)
HTTP header : Server=Apache/2.4.3 (Fedora) (192.150.187.43->141.142.228.5)
Such debugging functionality would generally only be enabled on demand. Extending
the above script, we introduce an option and a change handler function from the
configuration framework`
to enable and disable the http-print-debugging
event group at runtime.
1@load base/frameworks/config
2
3redef Config::config_files += { "./myconfig.dat" };
4
5module Debug;
6
7export {
8 option http_print_debugging = F;
9}
10
11event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) &group="http-print-debugging"
12 {
13 print fmt("HTTP request: %s %s (%s->%s)", method, original_URI, c$id$orig_h, c$id$resp_h);
14 }
15
16event http_header(c: connection, is_orig: bool, original_name: string, name: string, value: string) &group="http-print-debugging"
17 {
18 if ( name != "USER-AGENT" && name != "SERVER" )
19 return;
20
21 local snd = is_orig ? c$id$orig_h : c$id$resp_h;
22 local rcv = is_orig ? c$id$resp_h : c$id$orig_h;
23 print fmt("HTTP header : %s=%s (%s->%s)", original_name, value, snd, rcv);
24 }
25
26event http_reply(c: connection, version: string, code: count, reason: string) &group="http-print-debugging"
27 {
28 print fmt("HTTP reply : %s/%s version %s (%s->%s)", code, reason, version, c$id$resp_h, c$id$orig_h);
29 }
30
31event zeek_init()
32 {
33
34 Option::set_change_handler("Debug::http_print_debugging", function(id: string, new_value: bool): bool {
35 print id, new_value;
36 if ( new_value )
37 enable_event_group("http-print-debugging");
38 else
39 disable_event_group("http-print-debugging");
40
41 return new_value;
42 });
43
44 # Trigger the change handler, once.
45 Config::set_value("Debug::http_print_debugging", F);
46 }
Whenever the option Debug::http_print_debugging
is set to T
,
enable_event_group
is invoked to ensure the http-print-debugging
group is enabled. Conversely, when the option is set to F
,
disable_event_group
disables all event handlers in the group
http-print-debugging
.
The very same behavior can be achieved by testing the Debug::http_print_debugging
option within the respective event handlers using and if
statement and
early return. In contrast, event groups work in a more declarative way.
Further, when disabling event handlers via event groups, their implementation
is never invoked and is therefore a more performant way to short-circuit
execution.
Module Based Event Group
Besides attribute based event groups, Zeek supports implicit module based
event groups. Event and hook handlers are part of an event group that
represents the module in which they were implemented. The builtin functions
disable_module_events
and enable_module_events
can
be used to disable and enable all event and hook handlers within modules.
An interesting idea here is to implement enabling and disabling of Zeek packages
at runtime. For example, the CommunityID
package implements its functionality in the CommunityID
and
CommunityID::Notice
modules. The JA3
package implements its event handlers in the JA3
and JA3_Server
modules.
1@load base/frameworks/config
2
3@load ja3
4@load zeek-community-id
5@load zeek-community-id/notice
6
7redef Config::config_files += { "./myconfig.dat" };
8
9module Packages;
10
11export {
12 # All packages off by default.
13 option community_id_enabled = F;
14 option ja3_enabled = F;
15}
16
17event zeek_init()
18 {
19 local package_change_handler = function(id: string, new_value: bool): bool {
20 local modules: set[string];
21
22 if ( id == "Packages::community_id_enabled" )
23 modules = ["CommunityID", "CommunityID::Notice"];
24 else if ( id == "Packages::ja3_enabled" )
25 modules = ["JA3", "JA3_Server"];
26 else
27 {
28 Reporter::error(fmt("Unknown option: %s", id));
29 return new_value;
30 }
31
32 # Toggle the modules.
33 for ( m in modules )
34 if ( new_value )
35 enable_module_events(m);
36 else
37 disable_module_events(m);
38
39 return new_value;
40 };
41
42 Option::set_change_handler("Packages::community_id_enabled", package_change_handler);
43 Option::set_change_handler("Packages::ja3_enabled", package_change_handler);
44
45 Config::set_value("Packages::community_id_enabled", F);
46 Config::set_value("Packages::ja3_enabled", F);
47 }
The above script implements toggling of Zeek package functionality at
runtime via the options Packages::ja3_enabled
and Packages::community_id_enabled
.
While for most packages and deployments a Zeek restart is an acceptable
way to disable or enable a package - generally this isn’t a regular operation -
module based event groups provide a powerful primitive to support runtime
toggling of scripting functionality.
Note
A caveat around the above example: The JA3 package builds up state based
on the ssl_extension
events from SSL ClientHello and ServerHello
messages. When the JA3 event handlers are enabled right during processing
of these events, the resulting JA3 hash might be based on a partial list
of extensions only.
While all ssl_extension
handlers are processed jointly
for each instance of the event, generally state build up and
dynamic enabling and disabling may need careful consideration.