Merge pull request #83 from draios/add-correctness-tests

Add correctness tests
This commit is contained in:
Mark Stemm
2016-05-25 18:13:07 -07:00
10 changed files with 321 additions and 89 deletions

5
.gitignore vendored
View File

@@ -1,4 +1,9 @@
/build*
*~
test/falco_test.pyc
test/falco_tests.yaml
test/traces-negative
test/traces-positive
userspace/falco/lua/re.lua
userspace/falco/lua/lpeg.so

View File

@@ -9,6 +9,14 @@ install:
- sudo apt-get --force-yes install g++-4.8
- sudo apt-get install rpm linux-headers-$(uname -r)
- git clone https://github.com/draios/sysdig.git ../sysdig
- sudo apt-get install -y python-pip libvirt-dev jq
- cd ..
- curl -Lo avocado-36.0-tar.gz https://github.com/avocado-framework/avocado/archive/36.0lts.tar.gz
- tar -zxvf avocado-36.0-tar.gz
- cd avocado-36.0lts
- sudo pip install -r requirements-travis.txt
- sudo python setup.py install
- cd ../falco
before_script:
- export KERNELDIR=/lib/modules/$(ls /lib/modules | sort | head -1)/build
script:
@@ -28,7 +36,7 @@ script:
- make VERBOSE=1
- make package
- cd ..
- sudo test/falco_trace_regression.sh build/userspace/falco/falco
- sudo test/run_regression_tests.sh
notifications:
webhooks:
urls:

View File

@@ -38,12 +38,16 @@
- macro: modify
condition: rename or remove
- macro: spawn_process
condition: syscall.type = execve and evt.dir=<
- macro: spawned_process
condition: evt.type = execve and evt.dir=<
# File categories
- macro: terminal_file_fd
condition: fd.name=/dev/ptmx or fd.directory=/dev/pts
# This really should be testing that the directory begins with these
# prefixes but sysdig's filter doesn't have a "starts with" operator
# (yet).
- macro: bin_dir
condition: fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin)
@@ -52,6 +56,8 @@
- macro: bin_dir_rename
condition: evt.arg[1] contains /bin/ or evt.arg[1] contains /sbin/ or evt.arg[1] contains /usr/bin/ or evt.arg[1] contains /usr/sbin/
# This really should be testing that the directory begins with /etc,
# but sysdig's filter doesn't have a "starts with" operator (yet).
- macro: etc_dir
condition: fd.directory contains /etc
@@ -74,25 +80,31 @@
tac, link, chroot, vdir, chown, touch, ls, dd, uname, true, pwd, date,
chgrp, chmod, mktemp, cat, mknod, sync, ln, false, rm, mv, cp, echo,
readlink, sleep, stty, mkdir, df, dir, rmdir, touch)
- macro: adduser_binaries
condition: proc.name in (adduser, deluser, addgroup, delgroup)
- macro: login_binaries
condition: proc.name in (bin, login, su, sbin, nologin, bin, faillog, lastlog, newgrp, sg)
# dpkg -L passwd | grep bin | xargs -L 1 basename | tr "\\n" ","
# dpkg -L login | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
- macro: login_binaries
condition: proc.name in (login, systemd-logind, su, nologin, faillog, lastlog, newgrp, sg)
# dpkg -L passwd | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
- macro: passwd_binaries
condition: >
proc.name in (sbin, shadowconfig, sbin, grpck, pwunconv, grpconv, pwck,
proc.name in (shadowconfig, grpck, pwunconv, grpconv, pwck,
groupmod, vipw, pwconv, useradd, newusers, cppw, chpasswd, usermod,
groupadd, groupdel, grpunconv, chgpasswd, userdel, bin, chage, chsh,
groupadd, groupdel, grpunconv, chgpasswd, userdel, chage, chsh,
gpasswd, chfn, expiry, passwd, vigr, cpgr)
# repoquery -l shadow-utils | grep bin | xargs -L 1 basename | tr "\\n" ","
# repoquery -l shadow-utils | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
- macro: shadowutils_binaries
condition: >
proc.name in (chage, gpasswd, lastlog, newgrp, sg, adduser, chpasswd,
groupadd, groupdel, groupmems, groupmod, grpck, grpconv, grpunconv,
newusers, pwck, pwconv, pwunconv, useradd, userdel, usermod, vigr, vipw)
proc.name in (chage, gpasswd, lastlog, newgrp, sg, adduser, deluser, chpasswd,
groupadd, groupdel, addgroup, delgroup, groupmems, groupmod, grpck, grpconv, grpunconv,
newusers, pwck, pwconv, pwunconv, useradd, userdel, usermod, vigr, vipw, unix_chkpwd)
- macro: sysdigcloud_binaries
condition: proc.name in (setup-backend, dragent)
- macro: sysdigcloud_binaries_parent
condition: proc.pname in (setup-backend, dragent)
- macro: docker_binaries
condition: proc.name in (docker, exe)
@@ -103,25 +115,33 @@
- macro: db_server_binaries
condition: proc.name in (mysqld)
- macro: server_binaries
condition: http_server_binaries or db_server_binaries or docker_binaries or proc.name in (sshd)
- macro: db_server_binaries_parent
condition: proc.pname in (mysqld)
- macro: server_binaries
condition: (http_server_binaries or db_server_binaries or docker_binaries or proc.name in (sshd))
# The truncated dpkg-preconfigu is intentional, process names are
# truncated at the sysdig level.
- macro: package_mgmt_binaries
condition: proc.name in (dpkg, rpm)
condition: proc.name in (dpkg, dpkg-preconfigu, rpm, yum)
# A canonical set of processes that run other programs with different
# privileges or as a different user.
- macro: userexec_binaries
condition: proc.name in (sudo, su)
- macro: user_mgmt_binaries
condition: (login_binaries or passwd_binaries or shadowutils_binaries)
- macro: system_binaries
condition: coreutils_binaries or adduser_binaries or login_binaries or passwd_binaries or shadowutils_binaries
condition: (coreutils_binaries or user_mgmt_binaries)
- macro: mail_binaries
condition: proc.name in (sendmail, postfix, procmail)
condition: proc.name in (sendmail, sendmail-msp, postfix, procmail)
- macro: sensitive_files
condition: fd.name contains /etc/shadow or fd.name = /etc/sudoers or fd.directory = /etc/sudoers.d or fd.directory = /etc/pam.d or fd.name = /etc/pam.conf
condition: (fd.name contains /etc/shadow or fd.name = /etc/sudoers or fd.directory = /etc/sudoers.d or fd.directory = /etc/pam.d or fd.name = /etc/pam.conf)
# Indicates that the process is new. Currently detected using time
# since process was started, using a threshold of 5 seconds.
@@ -130,7 +150,7 @@
# Network
- macro: inbound
condition: (syscall.type=listen and evt.dir=>) or (syscall.type=accept and evt.dir=<)
condition: ((syscall.type=listen and evt.dir=>) or (syscall.type=accept and evt.dir=<))
# Currently sendto is an ignored syscall, otherwise this could also check for (syscall.type=sendto and evt.dir=>)
- macro: outbound
@@ -141,7 +161,7 @@
# Ssh
- macro: ssh_error_message
condition: evt.arg.data contains "Invalid user" or evt.arg.data contains "preauth"
condition: (evt.arg.data contains "Invalid user" or evt.arg.data contains "preauth" or evt.arg.data contains "Failed password")
# System
- macro: modules
@@ -149,9 +169,9 @@
- macro: container
condition: container.id != host
- macro: interactive
condition: (proc.aname=sshd and proc.name != sshd) or proc.name=systemd-logind
condition: ((proc.aname=sshd and proc.name != sshd) or proc.name=systemd-logind)
- macro: syslog
condition: fd.name = /dev/log
condition: fd.name in (/dev/log, /run/systemd/journal/syslog)
- macro: cron
condition: proc.name in (cron, crond)
- macro: parent_cron
@@ -169,32 +189,32 @@
- rule: write_binary_dir
desc: an attempt to write to any file below a set of binary directories
condition: evt.dir = > and open_write and bin_dir
condition: evt.dir = < and open_write and not package_mgmt_binaries and bin_dir
output: "File below a known binary directory opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: WARNING
- rule: write_etc
desc: an attempt to write to any file below /etc
condition: evt.dir = > and open_write and etc_dir
condition: evt.dir = < and open_write and not shadowutils_binaries and not sysdigcloud_binaries_parent and not package_mgmt_binaries and etc_dir
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: WARNING
- rule: read_sensitive_file_untrusted
desc: an attempt to read any sensitive file (e.g. files containing user/password/authentication information). Exceptions are made for known trusted programs.
condition: open_read and not server_binaries and not userexec_binaries and not proc.name in (iptables, ps, systemd-logind, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, bash) and not cron and sensitive_files
condition: open_read and not user_mgmt_binaries and not userexec_binaries and not proc.name in (iptables, ps, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, bash, sshd) and not cron and sensitive_files
output: "Sensitive file opened for reading by non-trusted program (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: WARNING
- rule: read_sensitive_file_trusted_after_startup
desc: an attempt to read any sensitive file (e.g. files containing user/password/authentication information) by a trusted program after startup. Trusted programs might read these files at startup to load initial state, but not afterwards.
condition: open_read and server_binaries and not proc_is_new and sensitive_files
condition: open_read and server_binaries and not proc_is_new and sensitive_files and proc.name!="sshd"
output: "Sensitive file opened for reading by trusted program after startup (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: WARNING
- rule: db_program_spawn_process
desc: a database-server related program spawning a new process after startup. This shouldn\'t occur and is a follow on from some SQL injection attacks.
condition: db_server_binaries and not proc_is_new and spawn_process
output: "Database-related program spawned new process after startup (user=%user.name command=%proc.cmdline)"
- rule: db_program_spawned_process
desc: a database-server related program spawned a new process other than itself. This shouldn\'t occur and is a follow on from some SQL injection attacks.
condition: db_server_binaries_parent and not db_server_binaries and spawned_process
output: "Database-related program spawned process other than itself (user=%user.name program=%proc.cmdline parent=%proc.pname)"
priority: WARNING
- rule: modify_binary_dirs
@@ -218,11 +238,12 @@
# output: "Loaded .so from unexpected dir (%user.name %proc.name %evt.dir %evt.type %evt.args %fd.name)"
# priority: WARNING
- rule: syscall_returns_eaccess
desc: any system call that returns EACCESS. This is not always a strong indication of a problem, hence the INFO priority.
condition: evt.res = EACCESS
output: "System call returned EACCESS (user=%user.name command=%proc.cmdline syscall=%evt.type args=%evt.args)"
priority: INFO
# Temporarily disabling this rule as it's tripping over https://github.com/draios/sysdig/issues/598
# - rule: syscall_returns_eaccess
# desc: any system call that returns EACCESS. This is not always a strong indication of a problem, hence the INFO priority.
# condition: evt.res = EACCESS
# output: "System call returned EACCESS (user=%user.name command=%proc.cmdline syscall=%evt.type args=%evt.args)"
# priority: INFO
- rule: change_thread_namespace
desc: an attempt to change a program/thread\'s namespace (commonly done as a part of creating a container) by calling setns.
@@ -232,7 +253,7 @@
- rule: run_shell_untrusted
desc: an attempt to spawn a shell by a non-shell program. Exceptions are made for trusted binaries.
condition: proc.name = bash and evt.dir=< and evt.type=execve and proc.pname exists and not parent_cron and not proc.pname in (bash, sshd, sudo, docker, su, tmux, screen, emacs, systemd, flock, fs-bash, nginx, monit, supervisord)
condition: not container and proc.name = bash and spawned_process and proc.pname exists and not parent_cron and not proc.pname in (bash, sshd, sudo, docker, su, tmux, screen, emacs, systemd, login, flock, fbash, nginx, monit, supervisord, dragent)
output: "Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)"
priority: WARNING
@@ -243,13 +264,13 @@
- rule: system_user_interactive
desc: an attempt to run interactive commands by a system (i.e. non-login) user
condition: spawn_process and system_users and interactive
condition: spawned_process and system_users and interactive
output: "System user ran an interactive command (user=%user.name command=%proc.cmdline)"
priority: WARNING
- rule: run_shell_in_container
desc: an attempt to spawn a shell by a non-shell program in a container. Container entrypoints are excluded.
condition: container and proc.name = bash and evt.dir=< and evt.type=execve and proc.pname exists and not proc.pname in (bash, docker)
desc: a shell was spawned by a non-shell program in a container. Container entrypoints are excluded.
condition: container and proc.name = bash and spawned_process and proc.pname exists and not proc.pname in (bash, docker)
output: "Shell spawned in a container other than entrypoint (user=%user.name container_id=%container.id container_name=%container.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)"
priority: WARNING
@@ -260,22 +281,26 @@
output: "Known system binary sent/received network traffic (user=%user.name command=%proc.cmdline connection=%fd.name)"
priority: WARNING
- rule: ssh_error_syslog
desc: any ssh errors (failed logins, disconnects, ...) sent to syslog
condition: syslog and ssh_error_message and evt.dir = <
output: "sshd sent error message to syslog (error=%evt.buffer)"
priority: WARNING
# With the current restriction on system calls handled by falco
# (e.g. excluding read/write/sendto/recvfrom/etc, this rule won't
# trigger).
# - rule: ssh_error_syslog
# desc: any ssh errors (failed logins, disconnects, ...) sent to syslog
# condition: syslog and ssh_error_message and evt.dir = <
# output: "sshd sent error message to syslog (error=%evt.buffer)"
# priority: WARNING
# sshd, sendmail-msp, sendmail attempt to setuid to root even when running as non-root. Excluding here to avoid meaningless FPs
- rule: non_sudo_setuid
desc: an attempt to change users by calling setuid. sudo/su are excluded. user "root" is also excluded, as setuid calls typically involve dropping privileges.
condition: evt.type=setuid and evt.dir=> and not user.name=root and not userexec_binaries
condition: evt.type=setuid and evt.dir=> and not user.name=root and not userexec_binaries and not proc.name in (sshd, sendmail-msp, sendmail)
output: "Unexpected setuid call by non-sudo, non-root program (user=%user.name command=%proc.cmdline uid=%evt.arg.uid)"
priority: WARNING
- rule: user_mgmt_binaries
desc: activity by any programs that can manage users, passwords, or permissions. sudo and su are excluded. Activity in containers is also excluded--some containers create custom users on top of a base linux distribution at startup.
condition: spawn_process and not proc.name in (su, sudo) and not container and (adduser_binaries or login_binaries or passwd_binaries or shadowutils_binaries)
output: "User management binary command run outside of container (user=%user.name command=%proc.cmdline)"
condition: spawned_process and not proc.name in (su, sudo) and not container and user_mgmt_binaries and not parent_cron and not proc.pname in (systemd, run-parts)
output: "User management binary command run outside of container (user=%user.name command=%proc.cmdline parent=%proc.pname)"
priority: WARNING
# (we may need to add additional checks against false positives, see: https://bugs.launchpad.net/ubuntu/+source/rkhunter/+bug/86153)
@@ -285,17 +310,17 @@
output: "File created below /dev by untrusted program (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: WARNING
# fs-bash is a restricted version of bash suitable for use in curl <curl> | sh installers.
# fbash is a small shell script that runs bash, and is suitable for use in curl <curl> | fbash installers.
- rule: installer_bash_starts_network_server
desc: an attempt by any program that is a child of fs-bash to start listening for network connections
condition: evt.type=listen and proc.aname=fs-bash
output: "Unexpected listen call by a child process of fs-bash (command=%proc.cmdline)"
desc: an attempt by any program that is a child of fbash to start listening for network connections
condition: evt.type=listen and proc.aname=fbash
output: "Unexpected listen call by a child process of fbash (command=%proc.cmdline)"
priority: WARNING
- rule: installer_bash_starts_session
desc: an attempt by any program that is a child of fs-bash to start a new session (process group)
condition: evt.type=setsid and proc.aname=fs-bash
output: "Unexpected setsid call by a child process of fs-bash (command=%proc.cmdline)"
desc: an attempt by any program that is a child of fbash to start a new session (process group)
condition: evt.type=setsid and proc.aname=fbash
output: "Unexpected setsid call by a child process of fbash (command=%proc.cmdline)"
priority: WARNING
###########################

63
test/falco_test.py Normal file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env python
import os
import re
from avocado import Test
from avocado.utils import process
from avocado.utils import linux_modules
class FalcoTest(Test):
def setUp(self):
"""
Load the sysdig kernel module if not already loaded.
"""
self.falcodir = self.params.get('falcodir', '/', default=os.path.join(self.basedir, '../build'))
self.should_detect = self.params.get('detect', '*')
self.trace_file = self.params.get('trace_file', '*')
# 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)
if linux_modules.parse_lsmod_for_module(lsmod_output, 'sysdig_probe') == {}:
self.log.debug("Loading sysdig kernel module")
process.run('sudo insmod {}/driver/sysdig-probe.ko'.format(self.falcodir))
self.str_variant = self.trace_file
def test(self):
self.log.info("Trace file %s", self.trace_file)
# Run the provided trace file though falco
cmd = '{}/userspace/falco/falco -r {}/../rules/falco_rules.yaml -c {}/../falco.yaml -e {}'.format(
self.falcodir, self.falcodir, self.falcodir, self.trace_file)
self.falco_proc = process.SubProcess(cmd)
res = self.falco_proc.run(timeout=60, sig=9)
if res.exit_status != 0:
self.error("Falco command \"{}\" exited with non-zero return value {}".format(
cmd, res.exit_status))
# Get the number of events detected.
res = re.search('Events detected: (\d+)', res.stdout)
if res is None:
self.fail("Could not find a line 'Events detected: <count>' in falco output")
events_detected = int(res.group(1))
if not self.should_detect and events_detected > 0:
self.fail("Detected {} events when should have detected none".format(events_detected))
if self.should_detect and events_detected == 0:
self.fail("Detected {} events when should have detected > 0".format(events_detected))
pass
if __name__ == "__main__":
main()

View File

@@ -1,30 +0,0 @@
#!/bin/bash
set -eu
SCRIPT=$(readlink -f $0)
BASEDIR=$(dirname $SCRIPT)
FALCO=$1
BUILDDIR=$(dirname $FALCO)
# Load the built kernel module by hand
insmod $BUILDDIR/../../driver/sysdig-probe.ko
# For now, simply ensure that falco can run without errors.
FALCO_CMDLINE="$FALCO -c $BASEDIR/../falco.yaml -r $BASEDIR/../rules/falco_rules.yaml"
echo "Running falco: $FALCO_CMDLINE"
$FALCO_CMDLINE > $BASEDIR/falco.log 2>&1 &
FALCO_PID=$!
echo "Falco started, pid $FALCO_PID"
sleep 10
if kill -0 $FALCO_PID > /dev/null 2>&1; then
echo "Falco ran successfully"
kill $FALCO_PID
ret=0
else
echo "Falco did not start successfully. Full program output:"
cat $BASEDIR/falco.log
ret=1
fi
exit $ret

62
test/run_regression_tests.sh Executable file
View File

@@ -0,0 +1,62 @@
#!/bin/bash
SCRIPT=$(readlink -f $0)
SCRIPTDIR=$(dirname $SCRIPT)
MULT_FILE=$SCRIPTDIR/falco_tests.yaml
function download_trace_files() {
for TRACE in traces-positive traces-negative ; do
curl -so $SCRIPTDIR/$TRACE.zip https://s3.amazonaws.com/download.draios.com/falco-tests/$TRACE.zip &&
unzip -d $SCRIPTDIR $SCRIPTDIR/$TRACE.zip &&
rm -rf $SCRIPTDIR/$TRACE.zip
done
}
function prepare_multiplex_file() {
echo "trace_files: !mux" > $MULT_FILE
for trace in $SCRIPTDIR/traces-positive/*.scap ; do
[ -e "$trace" ] || continue
NAME=`basename $trace .scap`
cat << EOF >> $MULT_FILE
$NAME:
detect: True
trace_file: $trace
EOF
done
for trace in $SCRIPTDIR/traces-negative/*.scap ; do
[ -e "$trace" ] || continue
NAME=`basename $trace .scap`
cat << EOF >> $MULT_FILE
$NAME:
detect: False
trace_file: $trace
EOF
done
echo "Contents of $MULT_FILE:"
cat $MULT_FILE
}
function run_tests() {
CMD="avocado run --multiplex $MULT_FILE --job-results-dir $SCRIPTDIR/job-results -- $SCRIPTDIR/falco_test.py"
echo "Running: $CMD"
$CMD
TEST_RC=$?
}
function print_test_failure_details() {
echo "Showing full job logs for any tests that failed:"
jq '.tests[] | select(.status != "PASS") | .logfile' $SCRIPTDIR/job-results/latest/results.json | xargs cat
}
download_trace_files
prepare_multiplex_file
run_tests
if [ $TEST_RC -ne 0 ]; then
print_test_failure_details
fi
exit $TEST_RC

9
test/utils/run_sysdig.sh Normal file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Run sysdig excluding all events that aren't used by falco and also
# excluding other high-volume events that aren't essential. This
# results in smaller trace files.
# The remaining arguments are taken from the command line.
exec sudo sysdig not evt.type in '(mprotect,brk,mq_timedreceive,mq_receive,mq_timedsend,mq_send,getrusage,procinfo,rt_sigprocmask,rt_sigaction,ioctl,clock_getres,clock_gettime,clock_nanosleep,clock_settime,close,epoll_create,epoll_create1,epoll_ctl,epoll_pwait,epoll_wait,eventfd,fcntl,fcntl64,fstat,fstat64,fstatat64,fstatfs,fstatfs64,futex,getitimer,gettimeofday,ioprio_get,ioprio_set,llseek,lseek,lstat,lstat64,mmap,mmap2,munmap,nanosleep,poll,ppoll,pread,pread64,preadv,procinfo,pselect6,pwrite,pwrite64,pwritev,read,readv,recv,recvfrom,recvmmsg,recvmsg,sched_yield,select,send,sendfile,sendfile64,sendmmsg,sendmsg,sendto,setitimer,settimeofday,shutdown,splice,stat,stat64,statfs,statfs64,switch,tee,timer_create,timer_delete,timerfd_create,timerfd_gettime,timerfd_settime,timer_getoverrun,timer_gettime,timer_settime,wait4,write,writev) and user.name!=ec2-user' $@

View File

@@ -28,6 +28,14 @@ extern "C" {
#include "utils.h"
#include <yaml-cpp/yaml.h>
bool g_terminate = false;
//
// Helper functions
//
static void signal_callback(int signal)
{
g_terminate = true;
}
//
// Program help
@@ -67,6 +75,7 @@ static void display_fatal_err(const string &msg, bool daemon)
string lua_on_event = "on_event";
string lua_add_output = "add_output";
string lua_print_stats = "print_stats";
// Splitting into key=value or key.subkey=value will be handled by configuration class.
std::list<string> cmdline_options;
@@ -90,7 +99,11 @@ void do_inspect(sinsp* inspector,
res = inspector->next(&ev);
if(res == SCAP_TIMEOUT)
if (g_terminate)
{
break;
}
else if(res == SCAP_TIMEOUT)
{
continue;
}
@@ -199,6 +212,26 @@ void add_output(lua_State *ls, output_config oc)
}
// Print statistics on the the rules that triggered
void print_stats(lua_State *ls)
{
lua_getglobal(ls, lua_print_stats.c_str());
if(lua_isfunction(ls, -1))
{
if(lua_pcall(ls, 0, 0, 0) != 0)
{
const char* lerr = lua_tostring(ls, -1);
string err = "Error invoking function print_stats: " + string(lerr);
throw sinsp_exception(err);
}
}
else
{
throw sinsp_exception("No function " + lua_print_stats + " found in lua rule loader module");
}
}
//
// ARGUMENT PARSING AND PROGRAM SETUP
@@ -398,6 +431,20 @@ int falco_init(int argc, char **argv)
add_output(ls, *it);
}
if(signal(SIGINT, signal_callback) == SIG_ERR)
{
fprintf(stderr, "An error occurred while setting SIGINT signal handler.\n");
result = EXIT_FAILURE;
goto exit;
}
if(signal(SIGTERM, signal_callback) == SIG_ERR)
{
fprintf(stderr, "An error occurred while setting SIGTERM signal handler.\n");
result = EXIT_FAILURE;
goto exit;
}
if (scap_filename.size())
{
inspector->open(scap_filename);
@@ -406,7 +453,7 @@ int falco_init(int argc, char **argv)
{
try
{
inspector->open();
inspector->open(200);
}
catch(sinsp_exception e)
{
@@ -478,6 +525,8 @@ int falco_init(int argc, char **argv)
ls);
inspector->close();
print_stats(ls);
}
catch(sinsp_exception& e)
{

View File

@@ -2,6 +2,8 @@ local mod = {}
levels = {"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Informational", "Debug"}
mod.levels = levels
local outputs = {}
function mod.stdout(evt, level, format)

View File

@@ -230,12 +230,51 @@ function describe_rule(name)
end
end
local rule_output_counts = {total=0, by_level={}, by_name={}}
for idx, level in ipairs(output.levels) do
rule_output_counts[level] = 0
end
function on_event(evt_, rule_id)
if state.rules_by_idx[rule_id] == nil then
error ("rule_loader.on_event(): event with invalid rule_id: ", rule_id)
end
output.event(evt_, state.rules_by_idx[rule_id].level, state.rules_by_idx[rule_id].output)
rule_output_counts.total = rule_output_counts.total + 1
local rule = state.rules_by_idx[rule_id]
if rule_output_counts.by_level[rule.level] == nil then
rule_output_counts.by_level[rule.level] = 1
else
rule_output_counts.by_level[rule.level] = rule_output_counts.by_level[rule.level] + 1
end
if rule_output_counts.by_name[rule.rule] == nil then
rule_output_counts.by_name[rule.rule] = 1
else
rule_output_counts.by_name[rule.rule] = rule_output_counts.by_name[rule.rule] + 1
end
output.event(evt_, rule.level, rule.output)
end
function print_stats()
print("Events detected: "..rule_output_counts.total)
print("Rule counts by severity:")
for idx, level in ipairs(output.levels) do
-- To keep the output concise, we only print 0 counts for error, warning, and info levels
if rule_output_counts[level] > 0 or level == "Error" or level == "Warning" or level == "Informational" then
print (" "..level..": "..rule_output_counts[level])
end
end
print("Triggered rules by rule name:")
for name, count in pairs(rule_output_counts.by_name) do
print (" "..name..": "..count)
end
end