Add tests for multiple files, disabled rules.

Add test that cover reading from multiple sets of rule files and
disabling rules. Specific changes:

 - Modify falco to allow multiple -r arguments to read from multiple
   files.
 - In the test multiplex file, add a disabled_rules attribute,
   containing a sequence of rules to disable. Result in -D arguments
   when running falco.
 - In the test multiplex file, 'rules_file' can be a sequence. It
   results in multiple -r arguments when running falco.
 - In the test multiplex file, 'detect_level' can be a squence of
   multiple severity levels. All levels will be checked for in the
   output.
 - Move all test rules files to a rules subdirectory and all trace files
   to a traces subdirectory.
 - Add a small trace file for a simple cat of /dev/null. Used by the
   new tests.
 - Add the following new tests:
     - Reading from multiple files, with the first file being
       empty. Ensure that the rules from the second file are properly
       loaded.
     - Reading from multiple files with the last being empty. Ensures
       that the empty file doesn't overwrite anything from the first
       file.
     - Reading from multiple files with varying severity levels for each
       rule. Ensures that both files are properly read.
     - Disabling rules from a rules file, both with full rule names
       and regexes. Will result in not detecting anything.
This commit is contained in:
Mark Stemm 2016-08-04 12:01:54 -07:00
parent 09405e4fad
commit f1748060c5
11 changed files with 119 additions and 23 deletions

View File

@ -26,8 +26,28 @@ class FalcoTest(Test):
self.json_output = self.params.get('json_output', '*', default=False)
self.rules_file = self.params.get('rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
if not os.path.isabs(self.rules_file):
self.rules_file = os.path.join(self.basedir, self.rules_file)
if not isinstance(self.rules_file, list):
self.rules_file = [self.rules_file]
self.rules_args = ""
for file in self.rules_file:
if not os.path.isabs(file):
file = os.path.join(self.basedir, file)
self.rules_args = self.rules_args + "-r " + file + " "
self.disabled_rules = self.params.get('disabled_rules', '*', default='')
if self.disabled_rules == '':
self.disabled_rules = []
if not isinstance(self.disabled_rules, list):
self.disabled_rules = [self.disabled_rules]
self.disabled_args = ""
for rule in self.disabled_rules:
self.disabled_args = self.disabled_args + "-D " + rule + " "
self.rules_warning = self.params.get('rules_warning', '*', default=False)
if self.rules_warning == False:
@ -49,6 +69,9 @@ class FalcoTest(Test):
if self.should_detect:
self.detect_level = self.params.get('detect_level', '*')
if not isinstance(self.detect_level, list):
self.detect_level = [self.detect_level]
# Doing this in 2 steps instead of simply using
# module_is_loaded to avoid logging lsmod output to the log.
lsmod_output = process.system_output("lsmod", verbose=False)
@ -105,16 +128,17 @@ class FalcoTest(Test):
if events_detected == 0:
self.fail("Detected {} events when should have detected > 0".format(events_detected))
level_line = '(?i){}: (\d+)'.format(self.detect_level)
match = re.search(level_line, res.stdout)
for level in self.detect_level:
level_line = '(?i){}: (\d+)'.format(level)
match = re.search(level_line, res.stdout)
if match is None:
self.fail("Could not find a line '{}: <count>' in falco output".format(self.detect_level))
if match is None:
self.fail("Could not find a line '{}: <count>' in falco output".format(level))
events_detected = int(match.group(1))
events_detected = int(match.group(1))
if not events_detected > 0:
self.fail("Detected {} events at level {} when should have detected > 0".format(events_detected, self.detect_level))
if not events_detected > 0:
self.fail("Detected {} events at level {} when should have detected > 0".format(events_detected, level))
def check_json_output(self, res):
if self.json_output:
@ -131,8 +155,8 @@ class FalcoTest(Test):
self.log.info("Trace file %s", self.trace_file)
# Run the provided trace file though falco
cmd = '{}/userspace/falco/falco -r {} -c {}/../falco.yaml -e {} -o json_output={} -v'.format(
self.falcodir, self.rules_file, self.falcodir, self.trace_file, self.json_output)
cmd = '{}/userspace/falco/falco {} {} -c {}/../falco.yaml -e {} -o json_output={} -v'.format(
self.falcodir, self.rules_args, self.disabled_args, self.falcodir, self.trace_file, self.json_output)
self.falco_proc = process.SubProcess(cmd)

View File

@ -1,13 +1,13 @@
trace_files: !mux
builtin_rules_no_warnings:
detect: False
trace_file: empty.scap
trace_file: trace_files/empty.scap
rules_warning: False
test_warnings:
detect: False
trace_file: empty.scap
rules_file: falco_rules_warnings.yaml
trace_file: trace_files/empty.scap
rules_file: rules/falco_rules_warnings.yaml
rules_warning:
- no_evttype
- evttype_not_equals
@ -60,3 +60,48 @@ trace_files: !mux
- repeated_evttypes_with_in: [open]
- repeated_evttypes_with_separate_in: [open]
- repeated_evttypes_with_mix: [open]
multiple_rules_first_empty:
detect: True
detect_level: WARNING
rules_file:
- rules/empty_rules.yaml
- rules/single_rule.yaml
trace_file: trace_files/cat_write.scap
multiple_rules_last_empty:
detect: True
detect_level: WARNING
rules_file:
- rules/single_rule.yaml
- rules/empty_rules.yaml
trace_file: trace_files/cat_write.scap
multiple_rules:
detect: True
detect_level:
- WARNING
- INFO
- ERROR
rules_file:
- rules/single_rule.yaml
- rules/double_rule.yaml
trace_file: trace_files/cat_write.scap
disabled_rules:
detect: False
rules_file:
- rules/empty_rules.yaml
- rules/single_rule.yaml
disabled_rules:
- open_from_cat
trace_file: trace_files/cat_write.scap
disabled_rules_using_regex:
detect: False
rules_file:
- rules/empty_rules.yaml
- rules/single_rule.yaml
disabled_rules:
- "open.*"
trace_file: trace_files/cat_write.scap

View File

@ -0,0 +1,13 @@
# This ruleset depends on the is_cat macro defined in single_rule.yaml
- rule: exec_from_cat
desc: A process named cat does execve
condition: evt.type=execve and is_cat
output: "An exec was seen (command=%proc.cmdline)"
priority: ERROR
- rule: access_from_cat
desc: A process named cat does an access
condition: evt.type=access and is_cat
output: "An access was seen (command=%proc.cmdline)"
priority: INFO

View File

View File

@ -0,0 +1,8 @@
- macro: is_cat
condition: proc.name=cat
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

Binary file not shown.

View File

@ -33,7 +33,7 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
init_cmdline_options(cmdline_options);
m_rules_filename = m_config->get_scalar<string>("rules_file", "/etc/falco_rules.yaml");
m_rules_filenames.push_back(m_config->get_scalar<string>("rules_file", "/etc/falco_rules.yaml"));
m_json_output = m_config->get_scalar<bool>("json_output", false);
falco_outputs::output_config file_output;

View File

@ -123,7 +123,7 @@ class falco_configuration
void init(std::string conf_filename, std::list<std::string> &cmdline_options);
void init(std::list<std::string> &cmdline_options);
std::string m_rules_filename;
std::list<std::string> m_rules_filenames;
bool m_json_output;
std::vector<falco_outputs::output_config> m_outputs;
private:

View File

@ -2,6 +2,9 @@
#include <stdio.h>
#include <fstream>
#include <set>
#include <list>
#include <string>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
@ -41,6 +44,7 @@ static void usage()
" -p, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
" -e <events_file> Read the events from <events_file> (in .scap format) instead of tapping into live.\n"
" -r <rules_file> Rules file (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n"
" Can be specified multiple times to read from multiple files.\n"
" -D <pattern> Disable any rules matching the regex <pattern>. Can be specified multiple times.\n"
" -L Show the name and description of all rules and exit.\n"
" -l <rule> Show the name and description of the rule with name <rule> and exit.\n"
@ -140,7 +144,7 @@ int falco_init(int argc, char **argv)
int long_index = 0;
string scap_filename;
string conf_filename;
string rules_filename;
list<string> rules_filenames;
bool daemon = false;
string pidfilename = "/var/run/falco.pid";
bool describe_all_rules = false;
@ -192,7 +196,7 @@ int falco_init(int argc, char **argv)
scap_filename = optarg;
break;
case 'r':
rules_filename = optarg;
rules_filenames.push_back(optarg);
break;
case 'D':
pattern = optarg;
@ -273,14 +277,16 @@ int falco_init(int argc, char **argv)
falco_logger::log(LOG_INFO, "Falco initialized. No configuration file found, proceeding with defaults\n");
}
if (rules_filename.size())
if (rules_filenames.size())
{
config.m_rules_filename = rules_filename;
config.m_rules_filenames = rules_filenames;
}
engine->load_rules_file(rules_filename, verbose, all_events);
falco_logger::log(LOG_INFO, "Parsed rules from file " + rules_filename + "\n");
for (auto filename : config.m_rules_filenames)
{
engine->load_rules_file(filename, verbose, all_events);
falco_logger::log(LOG_INFO, "Parsed rules from file " + filename + "\n");
}
for (auto pattern : disabled_rule_patterns)
{