From aa2fbc2d5324defee09981230468d5b685c4268f Mon Sep 17 00:00:00 2001 From: Josh Brower Date: Tue, 28 Jan 2020 21:44:42 -0500 Subject: [PATCH] Zeek - Initial BPF support --- salt/bro/files/local.bro | 3 + salt/bro/init.sls | 34 ++++++- salt/bro/policy/securityonion/bpfconf.bro | 106 ++++++++++++++++++++++ 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 salt/bro/policy/securityonion/bpfconf.bro diff --git a/salt/bro/files/local.bro b/salt/bro/files/local.bro index 42112f7ee..afe4b94ca 100644 --- a/salt/bro/files/local.bro +++ b/salt/bro/files/local.bro @@ -102,6 +102,9 @@ # is currently considered a preview and therefore not loaded by default. @load base/protocols/smb +# BPF Configuration +@load securityonion/bpfconf + # Add the interface to the log event #@load securityonion/add-interface-to-logs.bro diff --git a/salt/bro/init.sls b/salt/bro/init.sls index 6a972cbe7..8f36be420 100644 --- a/salt/bro/init.sls +++ b/salt/bro/init.sls @@ -1,3 +1,7 @@ +{% set interface = salt['pillar.get']('sensor:interface', 'bond0') %} +{% set BPF_ZEEK = salt['pillar.get']('zeek:bpf') %} +{% set BPF_STATUS = 0 %} + # Bro Salt State # Add Bro group brogroup: @@ -103,6 +107,32 @@ zeekcleanscript: - month: '*' - dayweek: '*' +# BPF compilation and configuration +{% if BPF_ZEEK %} + {% set BPF_CALC = salt['cmd.script']('/usr/sbin/so-bpf-compile', interface + ' ' + BPF_ZEEK|join(" ") ) %} + {% if BPF_CALC['stderr'] == "" %} + {% set BPF_STATUS = 1 %} + {% else %} +zeekbpfcompilationfailure: + test.configurable_test_state: + - changes: False + - result: False + - comment: "BPF Syntax Error - Discarding Specified BPF" + {% endif %} +{% endif %} + +zeekbpf: + file.managed: + - name: /opt/so/conf/bro/bpf + - user: 940 + - group: 940 + {% if BPF_STATUS %} + - contents_pillar: zeek:bpf + {% else %} + - contents: + - "ip or not ip" + {% endif %} + # Sync local.bro {% if salt['pillar.get']('static:broversion', '') == 'COMMUNITY' %} localbrosync: @@ -163,6 +193,7 @@ so-bro: - /nsm/bro/extracted:/nsm/bro/extracted:rw - /opt/so/conf/bro/local.bro:/opt/bro/share/bro/site/local.bro:ro - /opt/so/conf/bro/node.cfg:/opt/bro/etc/node.cfg:ro + - /opt/so/conf/bro/bpf:/opt/bro/share/bro/site/bpf:ro - /opt/so/conf/bro/policy/securityonion:/opt/bro/share/bro/policy/securityonion:ro - /opt/so/conf/bro/policy/custom:/opt/bro/share/bro/policy/custom:ro - /opt/so/conf/bro/policy/intel:/opt/bro/share/bro/policy/intel:rw @@ -171,6 +202,5 @@ so-bro: - file: /opt/so/conf/bro/local.bro - file: /opt/so/conf/bro/node.cfg - file: /opt/so/conf/bro/policy - - + - file: /opt/so/conf/bro/bpf {% endif %} diff --git a/salt/bro/policy/securityonion/bpfconf.bro b/salt/bro/policy/securityonion/bpfconf.bro new file mode 100644 index 000000000..595aef8f2 --- /dev/null +++ b/salt/bro/policy/securityonion/bpfconf.bro @@ -0,0 +1,106 @@ +##! This script is to support the bpf.conf file like other network monitoring tools use. +##! Please don't try to learn from this script right now, there are a large number of +##! hacks in it to work around bugs discovered in Bro. + +@load base/frameworks/notice + +module BPFConf; + +export { + ## The file that is watched on disk for BPF filter changes. + ## Two templated variables are available; "sensorname" and "interface". + ## They can be used by surrounding the term by doubled curly braces. + const filename = "/opt/bro/share/bro/site/bpf" &redef; + + redef enum Notice::Type += { + ## Invalid filter notice. + InvalidFilter + }; +} + +global filter_parts: vector of string = vector(); +global current_filter_filename = ""; + +type FilterLine: record { + s: string; +}; + +redef enum PcapFilterID += { + BPFConfPcapFilter, +}; + +event BPFConf::line(description: Input::EventDescription, tpe: Input::Event, s: string) + { + local part = sub(s, /[[:blank:]]*#.*$/, ""); + + # We don't want any blank parts. + if ( part != "" ) + filter_parts[|filter_parts|] = part; + } + +event Input::end_of_data(name: string, source:string) + { + if ( name == "bpfconf" ) + { + local filter = join_string_vec(filter_parts, " "); + capture_filters["bpf.conf"] = filter; + if ( Pcap::precompile_pcap_filter(BPFConfPcapFilter, filter) ) + { + PacketFilter::install(); + } + else + { + NOTICE([$note=InvalidFilter, + $msg=fmt("Compiling packet filter from %s failed", filename), + $sub=filter]); + } + + filter_parts=vector(); + } + } + + +function add_filter_file() + { + local real_filter_filename = BPFConf::filename; + + # Support the interface template value. + #if ( SecurityOnion::sensorname != "" ) + # real_filter_filename = gsub(real_filter_filename, /\{\{sensorname\}\}/, SecurityOnion::sensorname); + + # Support the interface template value. + #if ( SecurityOnion::interface != "" ) + # real_filter_filename = gsub(real_filter_filename, /\{\{interface\}\}/, SecurityOnion::interface); + + #if ( /\{\{/ in real_filter_filename ) + # { + # return; + # } + #else + # Reporter::info(fmt("BPFConf filename set: %s (%s)", real_filter_filename, Cluster::node)); + + if ( real_filter_filename != current_filter_filename ) + { + current_filter_filename = real_filter_filename; + Input::add_event([$source=real_filter_filename, + $name="bpfconf", + $reader=Input::READER_RAW, + $mode=Input::REREAD, + $want_record=F, + $fields=FilterLine, + $ev=BPFConf::line]); + } + } + +#event SecurityOnion::found_sensorname(name: string) +# { +# add_filter_file(); +# } + +event bro_init() &priority=5 + { + if ( BPFConf::filename != "" ) + add_filter_file(); + } + +