Support for log filter policy hooks

This adds a "policy" hook into the logging framework's streams and
filters to replace the existing log filter predicates. The hook
signature is as follows:

    hook(rec: any, id: Log::ID, filter: Log::Filter);

The logging manager invokes hooks on each log record. Hooks can veto
log records via a break, and modify them if necessary. Log filters
inherit the stream-level hook, but can override or remove the hook as
needed.

The distribution's existing log streams now come with pre-defined
hooks that users can add handlers to. Their name is standardized as
"log_policy" by convention, with additional suffixes when a module
provides multiple streams. The following adds a handler to the Conn
module's default log policy hook:

    hook Conn::log_policy(rec: Conn::Info, id: Log::ID, filter: Log::Filter)
            {
            if ( some_veto_reason(rec) )
                break;
            }

By default, this handler will get invoked for any log filter
associated with the Conn::LOG stream.

The existing predicates are deprecated for removal in 4.1 but continue
to work.
This commit is contained in:
Christian Kreibich 2020-09-21 16:09:36 -07:00
parent c8545c85d8
commit 1bd658da8f
61 changed files with 289 additions and 85 deletions

View file

@ -6,6 +6,9 @@ export {
## The Broker logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## The type of a Broker activity being logged.
type Type: enum {
## An informational status update.
@ -32,7 +35,7 @@ export {
event zeek_init() &priority=5
{
Log::create_stream(Broker::LOG, [$columns=Info, $path="broker"]);
Log::create_stream(Broker::LOG, [$columns=Info, $path="broker", $policy=log_policy]);
}
function log_status(ev: string, endpoint: EndpointInfo, msg: string)

View file

@ -115,6 +115,9 @@ export {
## The cluster logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## The record type which contains the column fields of the cluster log.
type Info: record {
## The time at which a cluster message was generated.
@ -374,7 +377,7 @@ event zeek_init() &priority=5
terminate();
}
Log::create_stream(Cluster::LOG, [$columns=Info, $path="cluster"]);
Log::create_stream(Cluster::LOG, [$columns=Info, $path="cluster", $policy=log_policy]);
}
function create_store(name: string, persistent: bool &default=F): Cluster::StoreInfo

View file

@ -10,6 +10,9 @@ export {
## The config logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## Represents the data in config.log.
type Info: record {
## Timestamp at which the configuration change occured.
@ -152,7 +155,7 @@ function config_option_changed(ID: string, new_value: any, location: string): an
event zeek_init() &priority=10
{
Log::create_stream(LOG, [$columns=Info, $ev=log_config, $path="config"]);
Log::create_stream(LOG, [$columns=Info, $ev=log_config, $path="config", $policy=log_policy]);
# Limit logging to the manager - everyone else just feeds off it.
@if ( !Cluster::is_enabled() || Cluster::local_node_type() == Cluster::MANAGER )

View file

@ -7,6 +7,9 @@ export {
## Add the DPD logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## The record type defining the columns to log in the DPD logging stream.
type Info: record {
## Timestamp for when protocol analysis failed.
@ -47,7 +50,7 @@ redef record connection += {
event zeek_init() &priority=5
{
Log::create_stream(DPD::LOG, [$columns=Info, $path="dpd"]);
Log::create_stream(DPD::LOG, [$columns=Info, $path="dpd", $policy=log_policy]);
}
event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) &priority=10

View file

@ -14,6 +14,9 @@ export {
LOG
};
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## A structure which parameterizes a type of file analysis.
type AnalyzerArgs: record {
## An event which will be generated for all new file contents,
@ -318,7 +321,7 @@ global analyzer_add_callbacks: table[Files::Tag] of function(f: fa_file, args: A
event zeek_init() &priority=5
{
Log::create_stream(Files::LOG, [$columns=Info, $ev=log_files, $path="files"]);
Log::create_stream(Files::LOG, [$columns=Info, $ev=log_files, $path="files", $policy=log_policy]);
}
function set_info(f: fa_file)

View file

@ -10,6 +10,8 @@ module Intel;
export {
redef enum Log::ID += { LOG };
global log_policy: Log::PolicyHook;
## Enum type to represent various types of intelligence data.
type Type: enum {
## An IP address.
@ -225,7 +227,7 @@ global min_data_store: MinDataStore &redef;
event zeek_init() &priority=5
{
Log::create_stream(LOG, [$columns=Info, $ev=log_intel, $path="intel"]);
Log::create_stream(LOG, [$columns=Info, $ev=log_intel, $path="intel", $policy=log_policy]);
}
# Function that abstracts expiration of different types.

View file

@ -43,21 +43,6 @@ export {
## Individual writers can use a different value.
const unset_field = "-" &redef;
## Type defining the content of a logging stream.
type Stream: record {
## A record type defining the log's columns.
columns: any;
## Event that will be raised once for each log entry.
## The event receives a single same parameter, an instance of
## type ``columns``.
ev: any &optional;
## A path that will be inherited by any filters added to the
## stream which do not already specify their own path.
path: string &optional;
};
## Builds the default path values for log filters if not otherwise
## specified by a filter. The default implementation uses *id*
## to derive a name. Upon adding a filter to a stream, if neither
@ -232,7 +217,8 @@ export {
## fields set to the values to be logged.
##
## Returns: True if the entry is to be recorded.
pred: function(rec: any): bool &optional;
pred: function(rec: any): bool &optional
&deprecated="Remove in 4.1. PolicyHooks will replace the $pred function.";
## Output path for recording entries matching this
## filter.
@ -322,6 +308,60 @@ export {
config: table[string] of string &default=table();
};
## A hook type to implement filtering policy. Hook handlers can
## veto the logging of a record or alter it prior to logging.
## You can pass arbitrary state into the hook via the
## filter argument and its config member.
##
## rec: An instance of the stream's ``columns`` type with its
## fields set to the values to be logged.
##
## id: The ID associated with the logging stream the filter
## belongs to.
##
## filter: The :zeek:type:`Log::Filter` instance that controls
## the fate of the given log record.
type PolicyHook: hook(rec: any, id: ID, filter: Filter);
# To allow Filters to have a policy hook that refers to
# Filters, the Filter type must exist. So redef now to add the
# hook to the record.
redef record Filter += {
## Policy hooks can adjust log entry values and veto
## the writing of a log entry for the record passed
## into it. Any hook that breaks from its body signals
## that Zeek won't log the entry passed into it.
##
## When no policy hook is defined, the filter inherits
## the hook from the stream it's associated with.
policy: PolicyHook &optional;
};
## Type defining the content of a logging stream.
type Stream: record {
## A record type defining the log's columns.
columns: any;
## Event that will be raised once for each log entry.
## The event receives a single same parameter, an instance of
## type ``columns``.
ev: any &optional;
## A path that will be inherited by any filters added to the
## stream which do not already specify their own path.
path: string &optional;
## Policy hooks can adjust log records and veto their
## writing. Any hook handler that breaks from its body
## signals that Zeek won't log the entry passed into
## it. You can pass arbitrary state into the hook via
## the filter instance and its config table.
##
## New Filters created for this stream will inherit
## this policy hook, unless they provide their own.
policy: PolicyHook &optional;
};
## Sentinel value for indicating that a filter was not found when looked up.
const no_filter: Filter = [$name="<not found>"];

View file

@ -7,6 +7,8 @@ module NetControl;
export {
redef enum Log::ID += { DROP_LOG };
global log_policy_drop: Log::PolicyHook;
## Stops all packets involving an IP address from being forwarded.
##
## a: The address to be dropped.
@ -57,7 +59,7 @@ export {
event zeek_init() &priority=5
{
Log::create_stream(NetControl::DROP_LOG, [$columns=DropInfo, $ev=log_netcontrol_drop, $path="netcontrol_drop"]);
Log::create_stream(NetControl::DROP_LOG, [$columns=DropInfo, $ev=log_netcontrol_drop, $path="netcontrol_drop", $policy=log_policy_drop]);
}
function drop_connection(c: conn_id, t: interval, location: string &default="") : string

View file

@ -19,6 +19,9 @@ export {
## The framework's logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
# ###
# ### Generic functions and events.
# ###
@ -366,7 +369,7 @@ global rule_entities: table[Entity, RuleType] of Rule;
event zeek_init() &priority=5
{
Log::create_stream(NetControl::LOG, [$columns=Info, $ev=log_netcontrol, $path="netcontrol"]);
Log::create_stream(NetControl::LOG, [$columns=Info, $ev=log_netcontrol, $path="netcontrol", $policy=log_policy]);
}
function entity_to_info(info: Info, e: Entity)

View file

@ -7,6 +7,8 @@ module NetControl;
export {
redef enum Log::ID += { SHUNT };
global log_policy_shunt: Log::PolicyHook;
## Stops forwarding a uni-directional flow's packets to Zeek.
##
## f: The flow to shunt.
@ -38,7 +40,7 @@ export {
event zeek_init() &priority=5
{
Log::create_stream(NetControl::SHUNT, [$columns=ShuntInfo, $ev=log_netcontrol_shunt, $path="netcontrol_shunt"]);
Log::create_stream(NetControl::SHUNT, [$columns=ShuntInfo, $ev=log_netcontrol_shunt, $path="netcontrol_shunt", $policy=log_policy_shunt]);
}
function shunt_flow(f: flow_id, t: interval, location: string &default="") : string

View file

@ -16,6 +16,10 @@ export {
ALARM_LOG,
};
## Default logging policy hooks for the streams.
global log_policy: Log::PolicyHook;
global log_policy_alarm: Log::PolicyHook;
## Scripts creating new notices need to redef this enum to add their
## own specific notice types which would then get used when they call
## the :zeek:id:`NOTICE` function. The convention is to give a general
@ -388,9 +392,9 @@ function log_mailing_postprocessor(info: Log::RotationInfo): bool
event zeek_init() &priority=5
{
Log::create_stream(Notice::LOG, [$columns=Info, $ev=log_notice, $path="notice"]);
Log::create_stream(Notice::LOG, [$columns=Info, $ev=log_notice, $path="notice", $policy=log_policy]);
Log::create_stream(Notice::ALARM_LOG, [$columns=Notice::Info, $path="notice_alarm"]);
Log::create_stream(Notice::ALARM_LOG, [$columns=Notice::Info, $path="notice_alarm", $policy=log_policy_alarm]);
# If Zeek is configured for mailing notices, set up mailing for alarms.
# Make sure that this alarm log is also output as text so that it can
# be packaged up and emailed later.

View file

@ -17,6 +17,9 @@ export {
## The weird logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
redef enum Notice::Type += {
## Generic unusual but notice-worthy weird activity.
Activity,
@ -298,7 +301,7 @@ const notice_actions = {
event zeek_init() &priority=5
{
Log::create_stream(Weird::LOG, [$columns=Info, $ev=log_weird, $path="weird"]);
Log::create_stream(Weird::LOG, [$columns=Info, $ev=log_weird, $path="weird", $policy=log_policy]);
}
function flow_id_string(src: addr, dst: addr): string

View file

@ -13,6 +13,8 @@ export {
redef enum Log::ID += { LOG };
global log_policy: Log::PolicyHook;
## Log controller constructor.
##
## dpid: OpenFlow switch datapath id.
@ -48,7 +50,7 @@ export {
event zeek_init() &priority=5
{
Log::create_stream(OpenFlow::LOG, [$columns=Info, $ev=log_openflow, $path="openflow"]);
Log::create_stream(OpenFlow::LOG, [$columns=Info, $ev=log_openflow, $path="openflow", $policy=log_policy]);
}
function log_flow_mod(state: ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool

View file

@ -14,6 +14,9 @@ export {
## Add the packet filter logging stream.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## Add notice types related to packet filter errors.
redef enum Notice::Type += {
## This notice is generated if a packet filter cannot be compiled.
@ -159,7 +162,7 @@ event filter_change_tracking()
event zeek_init() &priority=5
{
Log::create_stream(PacketFilter::LOG, [$columns=Info, $path="packet_filter"]);
Log::create_stream(PacketFilter::LOG, [$columns=Info, $path="packet_filter", $policy=log_policy]);
# Preverify the capture and restrict filters to give more granular failure messages.
for ( id, cf in capture_filters )

View file

@ -17,6 +17,9 @@ export {
## The reporter logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## The record type which contains the column fields of the reporter log.
type Info: record {
## The network time at which the reporter event was generated.
@ -37,7 +40,7 @@ export {
event zeek_init() &priority=5
{
Log::create_stream(Reporter::LOG, [$columns=Info, $path="reporter"]);
Log::create_stream(Reporter::LOG, [$columns=Info, $path="reporter", $policy=log_policy]);
}
event reporter_info(t: time, msg: string, location: string) &priority=-5

View file

@ -35,6 +35,9 @@ export {
## The signature logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## These are the default actions you can apply to signature matches.
## All of them write the signature record to the logging stream unless
## declared otherwise.
@ -142,7 +145,7 @@ global did_sig_log: set[string] &read_expire = 1 hr;
event zeek_init() &priority=5
{
Log::create_stream(Signatures::LOG, [$columns=Info, $ev=log_signature, $path="signatures"]);
Log::create_stream(Signatures::LOG, [$columns=Info, $ev=log_signature, $path="signatures", $policy=log_policy]);
}
# Returns true if the given signature has already been triggered for the given

View file

@ -14,6 +14,9 @@ export {
## The software logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## Scripts detecting new types of software need to redef this enum to add
## their own specific software types which would then be used when they
## create :zeek:type:`Software::Info` records.
@ -123,7 +126,7 @@ export {
event zeek_init() &priority=5
{
Log::create_stream(Software::LOG, [$columns=Info, $ev=log_software, $path="software"]);
Log::create_stream(Software::LOG, [$columns=Info, $ev=log_software, $path="software", $policy=log_policy]);
}
type Description: record {

View file

@ -13,6 +13,9 @@ export {
## The tunnel logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
## Types of interesting activity that can occur with a tunnel.
type Action: enum {
## A new tunnel (encapsulating "connection") has been seen.
@ -94,7 +97,7 @@ redef likely_server_ports += { ayiya_ports, teredo_ports, gtpv1_ports, vxlan_por
event zeek_init() &priority=5
{
Log::create_stream(Tunnel::LOG, [$columns=Info, $path="tunnel"]);
Log::create_stream(Tunnel::LOG, [$columns=Info, $path="tunnel", $policy=log_policy]);
Analyzer::register_for_ports(Analyzer::ANALYZER_AYIYA, ayiya_ports);
Analyzer::register_for_ports(Analyzer::ANALYZER_TEREDO, teredo_ports);