mirror of
https://github.com/falcosecurity/falco.git
synced 2025-07-31 06:01:52 +00:00
commit
1c21b3bc8a
2
.gitignore
vendored
2
.gitignore
vendored
@ -12,7 +12,7 @@ test/results*.json.*
|
||||
userspace/falco/lua/re.lua
|
||||
userspace/falco/lua/lpeg.so
|
||||
|
||||
docker/event-generator/event-generator
|
||||
docker/event-generator/event_generator
|
||||
docker/event-generator/mysqld
|
||||
docker/event-generator/httpd
|
||||
docker/event-generator/sha1sum
|
||||
|
@ -247,6 +247,7 @@
|
||||
condition: bin_dir and evt.dir = < and open_write and not package_mgmt_procs
|
||||
output: "File below a known binary directory opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
tags: [filesystem]
|
||||
|
||||
- macro: write_etc_common
|
||||
condition: >
|
||||
@ -266,6 +267,7 @@
|
||||
condition: write_etc_common and not proc.sname=fbash
|
||||
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
tags: [filesystem]
|
||||
|
||||
# Within a fbash session, the severity is lowered to INFO
|
||||
- rule: Write below etc in installer
|
||||
@ -273,6 +275,7 @@
|
||||
condition: write_etc_common and proc.sname=fbash
|
||||
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name) within pipe installer session"
|
||||
priority: INFO
|
||||
tags: [filesystem]
|
||||
|
||||
- macro: cmp_cp_by_passwd
|
||||
condition: proc.name in (cmp, cp) and proc.pname=passwd
|
||||
@ -282,6 +285,7 @@
|
||||
condition: sensitive_files and open_read and server_procs and not proc_is_new 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
|
||||
tags: [filesystem]
|
||||
|
||||
- list: read_sensitive_file_binaries
|
||||
items: [iptables, ps, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, sshd, vsftpd, systemd]
|
||||
@ -296,6 +300,7 @@
|
||||
and not proc.cmdline contains /usr/bin/mandb
|
||||
output: "Sensitive file opened for reading by non-trusted program (user=%user.name name=%proc.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
tags: [filesystem]
|
||||
|
||||
# Only let rpm-related programs write to the rpm database
|
||||
- rule: Write below rpm database
|
||||
@ -303,24 +308,28 @@
|
||||
condition: fd.name startswith /var/lib/rpm and open_write and not proc.name in (dnf,rpm,rpmkey,yum) and not ansible_running_python
|
||||
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
tags: [filesystem, software_mgmt]
|
||||
|
||||
- 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: proc.pname in (db_server_binaries) and spawned_process and not proc.name in (db_server_binaries)
|
||||
output: "Database-related program spawned process other than itself (user=%user.name program=%proc.cmdline parent=%proc.pname)"
|
||||
priority: WARNING
|
||||
tags: [process, database]
|
||||
|
||||
- rule: Modify binary dirs
|
||||
desc: an attempt to modify any file below a set of binary directories.
|
||||
condition: bin_dir_rename and modify and not package_mgmt_procs
|
||||
output: "File below known binary directory renamed/removed (user=%user.name command=%proc.cmdline operation=%evt.type file=%fd.name %evt.args)"
|
||||
priority: WARNING
|
||||
tags: [filesystem]
|
||||
|
||||
- rule: Mkdir binary dirs
|
||||
desc: an attempt to create a directory below a set of binary directories.
|
||||
condition: mkdir and bin_dir_mkdir and not package_mgmt_procs
|
||||
output: "Directory below known binary directory created (user=%user.name command=%proc.cmdline directory=%evt.arg.path)"
|
||||
priority: WARNING
|
||||
tags: [filesystem]
|
||||
|
||||
# Don't load shared objects coming from unexpected places
|
||||
# Commenting this out for now--there are lots of shared library
|
||||
@ -346,6 +355,7 @@
|
||||
and not proc.pname in (sysdigcloud_binaries)
|
||||
output: "Namespace change (setns) by unexpected program (user=%user.name command=%proc.cmdline parent=%proc.pname %container.info)"
|
||||
priority: WARNING
|
||||
tags: [process]
|
||||
|
||||
- list: known_shell_spawn_binaries
|
||||
items: [
|
||||
@ -373,6 +383,7 @@
|
||||
and not parent_linux_image_upgrade_script
|
||||
output: "Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline pcmdline=%proc.pcmdline)"
|
||||
priority: WARNING
|
||||
tags: [host, shell]
|
||||
|
||||
- macro: trusted_containers
|
||||
condition: (container.image startswith sysdig/agent or
|
||||
@ -388,6 +399,7 @@
|
||||
condition: (open_read or open_write) and container and container.privileged=true and not trusted_containers
|
||||
output: File opened for read/write by privileged container (user=%user.name command=%proc.cmdline %container.info file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [container, cis]
|
||||
|
||||
- macro: sensitive_mount
|
||||
condition: (container.mount.dest[/proc*] != "N/A")
|
||||
@ -397,6 +409,7 @@
|
||||
condition: (open_read or open_write) and container and sensitive_mount and not trusted_containers
|
||||
output: File opened for read/write by container mounting sensitive directory (user=%user.name command=%proc.cmdline %container.info file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [container, cis]
|
||||
|
||||
# Anything run interactively by root
|
||||
# - condition: evt.type != switch and user.name = root and proc.name != sshd and interactive
|
||||
@ -408,6 +421,7 @@
|
||||
condition: spawned_process and system_users and interactive
|
||||
output: "System user ran an interactive command (user=%user.name command=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
tags: [users]
|
||||
|
||||
- rule: Run shell in container
|
||||
desc: a shell was spawned by a non-shell program in a container. Container entrypoints are excluded.
|
||||
@ -420,6 +434,7 @@
|
||||
and not trusted_containers
|
||||
output: "Shell spawned in a container other than entrypoint (user=%user.name %container.info shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
tags: [container, shell]
|
||||
|
||||
# sockfamily ip is to exclude certain processes (like 'groups') that communicate on unix-domain sockets
|
||||
- rule: System procs network activity
|
||||
@ -427,6 +442,7 @@
|
||||
condition: (fd.sockfamily = ip and system_procs) and (inbound or outbound)
|
||||
output: "Known system binary sent/received network traffic (user=%user.name command=%proc.cmdline connection=%fd.name)"
|
||||
priority: WARNING
|
||||
tags: [network]
|
||||
|
||||
# With the current restriction on system calls handled by falco
|
||||
# (e.g. excluding read/write/sendto/recvfrom/etc, this rule won't
|
||||
@ -443,12 +459,14 @@
|
||||
condition: evt.type=setuid and evt.dir=> and not user.name=root and not proc.name in (userexec_binaries, mail_binaries, sshd, dbus-daemon-lau, ping, ping6, critical-stack-)
|
||||
output: "Unexpected setuid call by non-sudo, non-root program (user=%user.name parent=%proc.pname command=%proc.cmdline uid=%evt.arg.uid)"
|
||||
priority: WARNING
|
||||
tags: [users]
|
||||
|
||||
- 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: spawned_process and proc.name in (user_mgmt_binaries) and not proc.name in (su, sudo) and not container and not proc.pname in (cron_binaries, systemd, run-parts)
|
||||
output: "User management binary command run outside of container (user=%user.name command=%proc.cmdline parent=%proc.pname)"
|
||||
priority: WARNING
|
||||
tags: [host, users]
|
||||
|
||||
- list: allowed_dev_files
|
||||
items: [/dev/null, /dev/stdin, /dev/stdout, /dev/stderr, /dev/tty, /dev/random, /dev/urandom, /dev/console]
|
||||
@ -463,6 +481,7 @@
|
||||
and not fd.name in (allowed_dev_files)
|
||||
output: "File created below /dev by untrusted program (user=%user.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
tags: [filesystem]
|
||||
|
||||
# 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
|
||||
@ -470,18 +489,21 @@
|
||||
condition: evt.type=listen and proc.sname=fbash
|
||||
output: "Unexpected listen call by a process in a fbash session (command=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
tags: [network]
|
||||
|
||||
- rule: Installer bash starts session
|
||||
desc: an attempt by a program in a pipe installer session to start a new session
|
||||
condition: evt.type=setsid and proc.sname=fbash
|
||||
output: "Unexpected setsid call by a process in fbash session (command=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
tags: [process]
|
||||
|
||||
- rule: Installer bash non https connection
|
||||
desc: an attempt by a program in a pipe installer session to make an outgoing connection on a non-http(s) port
|
||||
condition: proc.sname=fbash and outbound and not fd.sport in (80, 443, 53)
|
||||
output: "Outbound connection on non-http(s) port by a process in a fbash session (command=%proc.cmdline connection=%fd.name)"
|
||||
priority: WARNING
|
||||
tags: [network]
|
||||
|
||||
# It'd be nice if we could warn when processes in a fbash session try
|
||||
# to download from any nonstandard location? This is probably blocked
|
||||
@ -495,6 +517,7 @@
|
||||
condition: evt.type=execve and proc.name in (chkconfig, systemctl) and proc.sname=fbash
|
||||
output: "Service management program run by process in a fbash session (command=%proc.cmdline)"
|
||||
priority: INFO
|
||||
tags: [software_mgmt]
|
||||
|
||||
# Notice when processes try to run any package management binary within a fbash session.
|
||||
# Note: this is not a WARNING, as you'd expect some package management
|
||||
@ -504,6 +527,7 @@
|
||||
condition: evt.type=execve and package_mgmt_procs and proc.sname=fbash
|
||||
output: "Package management program run by process in a fbash session (command=%proc.cmdline)"
|
||||
priority: INFO
|
||||
tags: [software_mgmt]
|
||||
|
||||
###########################
|
||||
# Application-Related Rules
|
||||
|
@ -113,6 +113,16 @@ class FalcoTest(Test):
|
||||
outputs.append(output)
|
||||
self.outputs = outputs
|
||||
|
||||
self.disable_tags = self.params.get('disable_tags', '*', default='')
|
||||
|
||||
if self.disable_tags == '':
|
||||
self.disable_tags=[]
|
||||
|
||||
self.run_tags = self.params.get('run_tags', '*', default='')
|
||||
|
||||
if self.run_tags == '':
|
||||
self.run_tags=[]
|
||||
|
||||
def check_rules_warnings(self, res):
|
||||
|
||||
found_warning = sets.Set()
|
||||
@ -180,13 +190,18 @@ class FalcoTest(Test):
|
||||
triggered_rules = match.group(1)
|
||||
|
||||
for rule, count in self.detect_counts.iteritems():
|
||||
expected_line = '{}: {}'.format(rule, count)
|
||||
match = re.search(expected_line, triggered_rules)
|
||||
expected = '{}: (\d+)'.format(rule)
|
||||
match = re.search(expected, triggered_rules)
|
||||
|
||||
if match is None:
|
||||
self.fail("Could not find a line '{}' in triggered rule counts '{}'".format(expected_line, triggered_rules))
|
||||
actual_count = 0
|
||||
else:
|
||||
self.log.debug("Found expected count for {}: {}".format(rule, match.group()))
|
||||
actual_count = int(match.group(1))
|
||||
|
||||
if actual_count != count:
|
||||
self.fail("Different counts for rule {}: expected={}, actual={}".format(rule, count, actual_count))
|
||||
else:
|
||||
self.log.debug("Found expected count for rule {}: {}".format(rule, count))
|
||||
|
||||
def check_outputs(self):
|
||||
for output in self.outputs:
|
||||
@ -223,6 +238,12 @@ class FalcoTest(Test):
|
||||
cmd = '{}/userspace/falco/falco {} {} -c {} -e {} -o json_output={} -v'.format(
|
||||
self.falcodir, self.rules_args, self.disabled_args, self.conf_file, self.trace_file, self.json_output)
|
||||
|
||||
for tag in self.disable_tags:
|
||||
cmd += ' -T {}'.format(tag)
|
||||
|
||||
for tag in self.run_tags:
|
||||
cmd += ' -t {}'.format(tag)
|
||||
|
||||
self.falco_proc = process.SubProcess(cmd)
|
||||
|
||||
res = self.falco_proc.run(timeout=180, sig=9)
|
||||
|
@ -154,6 +154,25 @@ trace_files: !mux
|
||||
- rules/single_rule_enabled_flag.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
disabled_and_enabled_rules_1:
|
||||
exit_status: 1
|
||||
stderr_contains: "Runtime error: You can not specify both disabled .-D/-T. and enabled .-t. rules. Exiting."
|
||||
disable_tags: [a]
|
||||
run_tags: [a]
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
disabled_and_enabled_rules_2:
|
||||
exit_status: 1
|
||||
stderr_contains: "Runtime error: You can not specify both disabled .-D/-T. and enabled .-t. rules. Exiting."
|
||||
disabled_rules:
|
||||
- "open.*"
|
||||
run_tags: [a]
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
null_output_field:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
@ -200,3 +219,267 @@ trace_files: !mux
|
||||
- "Create files below dev": 1
|
||||
- "Modify binary dirs": 2
|
||||
- "Change thread namespace": 2
|
||||
|
||||
disabled_tags_a:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
disable_tags: [a]
|
||||
detect_counts:
|
||||
- open_1: 0
|
||||
- open_2: 1
|
||||
- open_3: 1
|
||||
- open_4: 0
|
||||
- open_5: 0
|
||||
- open_6: 1
|
||||
- open_7: 0
|
||||
- open_8: 0
|
||||
- open_9: 0
|
||||
- open_10: 0
|
||||
- open_11: 1
|
||||
- open_12: 1
|
||||
- open_13: 1
|
||||
|
||||
disabled_tags_b:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
disable_tags: [b]
|
||||
detect_counts:
|
||||
- open_1: 1
|
||||
- open_2: 0
|
||||
- open_3: 1
|
||||
- open_4: 0
|
||||
- open_5: 1
|
||||
- open_6: 0
|
||||
- open_7: 0
|
||||
- open_8: 0
|
||||
- open_9: 1
|
||||
- open_10: 0
|
||||
- open_11: 1
|
||||
- open_12: 1
|
||||
- open_13: 1
|
||||
|
||||
disabled_tags_c:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
disable_tags: [c]
|
||||
detect_counts:
|
||||
- open_1: 1
|
||||
- open_2: 1
|
||||
- open_3: 0
|
||||
- open_4: 1
|
||||
- open_5: 0
|
||||
- open_6: 0
|
||||
- open_7: 0
|
||||
- open_8: 1
|
||||
- open_9: 0
|
||||
- open_10: 0
|
||||
- open_11: 1
|
||||
- open_12: 1
|
||||
- open_13: 1
|
||||
|
||||
disabled_tags_ab:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
disable_tags: [a, b]
|
||||
detect_counts:
|
||||
- open_1: 0
|
||||
- open_2: 0
|
||||
- open_3: 1
|
||||
- open_4: 0
|
||||
- open_5: 0
|
||||
- open_6: 0
|
||||
- open_7: 0
|
||||
- open_8: 0
|
||||
- open_9: 0
|
||||
- open_10: 0
|
||||
- open_11: 1
|
||||
- open_12: 1
|
||||
- open_13: 1
|
||||
|
||||
disabled_tags_abc:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
disable_tags: [a, b, c]
|
||||
detect_counts:
|
||||
- open_1: 0
|
||||
- open_2: 0
|
||||
- open_3: 0
|
||||
- open_4: 0
|
||||
- open_5: 0
|
||||
- open_6: 0
|
||||
- open_7: 0
|
||||
- open_8: 0
|
||||
- open_9: 0
|
||||
- open_10: 0
|
||||
- open_11: 1
|
||||
- open_12: 1
|
||||
- open_13: 1
|
||||
|
||||
run_tags_a:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
run_tags: [a]
|
||||
detect_counts:
|
||||
- open_1: 1
|
||||
- open_2: 0
|
||||
- open_3: 0
|
||||
- open_4: 1
|
||||
- open_5: 1
|
||||
- open_6: 0
|
||||
- open_7: 1
|
||||
- open_8: 1
|
||||
- open_9: 1
|
||||
- open_10: 1
|
||||
- open_11: 0
|
||||
- open_12: 0
|
||||
- open_13: 0
|
||||
|
||||
run_tags_b:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
run_tags: [b]
|
||||
detect_counts:
|
||||
- open_1: 0
|
||||
- open_2: 1
|
||||
- open_3: 0
|
||||
- open_4: 1
|
||||
- open_5: 0
|
||||
- open_6: 1
|
||||
- open_7: 1
|
||||
- open_8: 1
|
||||
- open_9: 0
|
||||
- open_10: 1
|
||||
- open_11: 0
|
||||
- open_12: 0
|
||||
- open_13: 0
|
||||
|
||||
run_tags_c:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
run_tags: [c]
|
||||
detect_counts:
|
||||
- open_1: 0
|
||||
- open_2: 0
|
||||
- open_3: 1
|
||||
- open_4: 0
|
||||
- open_5: 1
|
||||
- open_6: 1
|
||||
- open_7: 1
|
||||
- open_8: 0
|
||||
- open_9: 1
|
||||
- open_10: 1
|
||||
- open_11: 0
|
||||
- open_12: 0
|
||||
- open_13: 0
|
||||
|
||||
run_tags_ab:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
run_tags: [a, b]
|
||||
detect_counts:
|
||||
- open_1: 1
|
||||
- open_2: 1
|
||||
- open_3: 0
|
||||
- open_4: 1
|
||||
- open_5: 1
|
||||
- open_6: 1
|
||||
- open_7: 1
|
||||
- open_8: 1
|
||||
- open_9: 1
|
||||
- open_10: 1
|
||||
- open_11: 0
|
||||
- open_12: 0
|
||||
- open_13: 0
|
||||
|
||||
run_tags_bc:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
run_tags: [b, c]
|
||||
detect_counts:
|
||||
- open_1: 0
|
||||
- open_2: 1
|
||||
- open_3: 1
|
||||
- open_4: 1
|
||||
- open_5: 1
|
||||
- open_6: 1
|
||||
- open_7: 1
|
||||
- open_8: 1
|
||||
- open_9: 1
|
||||
- open_10: 1
|
||||
- open_11: 0
|
||||
- open_12: 0
|
||||
- open_13: 0
|
||||
|
||||
run_tags_abc:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
run_tags: [a, b, c]
|
||||
detect_counts:
|
||||
- open_1: 1
|
||||
- open_2: 1
|
||||
- open_3: 1
|
||||
- open_4: 1
|
||||
- open_5: 1
|
||||
- open_6: 1
|
||||
- open_7: 1
|
||||
- open_8: 1
|
||||
- open_9: 1
|
||||
- open_10: 1
|
||||
- open_11: 0
|
||||
- open_12: 0
|
||||
- open_13: 0
|
||||
|
||||
run_tags_d:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/tagged_rules.yaml
|
||||
trace_file: trace_files/open-multiple-files.scap
|
||||
run_tags: [d]
|
||||
detect_counts:
|
||||
- open_1: 0
|
||||
- open_2: 0
|
||||
- open_3: 0
|
||||
- open_4: 0
|
||||
- open_5: 0
|
||||
- open_6: 0
|
||||
- open_7: 0
|
||||
- open_8: 0
|
||||
- open_9: 0
|
||||
- open_10: 0
|
||||
- open_11: 1
|
||||
- open_12: 0
|
||||
- open_13: 0
|
||||
|
93
test/rules/tagged_rules.yaml
Normal file
93
test/rules/tagged_rules.yaml
Normal file
@ -0,0 +1,93 @@
|
||||
- macro: open_read
|
||||
condition: (evt.type=open or evt.type=openat) and evt.is_open_read=true and fd.typechar='f'
|
||||
|
||||
- rule: open_1
|
||||
desc: open one
|
||||
condition: open_read and fd.name=/tmp/file-1
|
||||
output: Open one (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [a]
|
||||
|
||||
- rule: open_2
|
||||
desc: open two
|
||||
condition: open_read and fd.name=/tmp/file-2
|
||||
output: Open two (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [b]
|
||||
|
||||
- rule: open_3
|
||||
desc: open three
|
||||
condition: open_read and fd.name=/tmp/file-3
|
||||
output: Open three (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [c]
|
||||
|
||||
- rule: open_4
|
||||
desc: open four
|
||||
condition: open_read and fd.name=/tmp/file-4
|
||||
output: Open four (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [a, b]
|
||||
|
||||
- rule: open_5
|
||||
desc: open file
|
||||
condition: open_read and fd.name=/tmp/file-5
|
||||
output: Open file (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [a, c]
|
||||
|
||||
- rule: open_6
|
||||
desc: open six
|
||||
condition: open_read and fd.name=/tmp/file-6
|
||||
output: Open six (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [b, c]
|
||||
|
||||
- rule: open_7
|
||||
desc: open seven
|
||||
condition: open_read and fd.name=/tmp/file-7
|
||||
output: Open seven (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [a, b, c]
|
||||
|
||||
- rule: open_8
|
||||
desc: open eight
|
||||
condition: open_read and fd.name=/tmp/file-8
|
||||
output: Open eight (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [b, a]
|
||||
|
||||
- rule: open_9
|
||||
desc: open nine
|
||||
condition: open_read and fd.name=/tmp/file-9
|
||||
output: Open nine (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [c, a]
|
||||
|
||||
- rule: open_10
|
||||
desc: open ten
|
||||
condition: open_read and fd.name=/tmp/file-10
|
||||
output: Open ten (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [b, c, a]
|
||||
|
||||
- rule: open_11
|
||||
desc: open eleven
|
||||
condition: open_read and fd.name=/tmp/file-11
|
||||
output: Open eleven (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [d]
|
||||
|
||||
- rule: open_12
|
||||
desc: open twelve
|
||||
condition: open_read and fd.name=/tmp/file-12
|
||||
output: Open twelve (file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: []
|
||||
|
||||
- rule: open_13
|
||||
desc: open thirteen
|
||||
condition: open_read and fd.name=/tmp/file-13
|
||||
output: Open thirteen (file=%fd.name)
|
||||
priority: WARNING
|
||||
|
BIN
test/trace_files/open-multiple-files.scap
Normal file
BIN
test/trace_files/open-multiple-files.scap
Normal file
Binary file not shown.
@ -40,7 +40,8 @@ string lua_print_stats = "print_stats";
|
||||
using namespace std;
|
||||
|
||||
falco_engine::falco_engine(bool seed_rng)
|
||||
: m_rules(NULL), m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
: m_rules(NULL), m_next_ruleset_id(0),
|
||||
m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
m_replace_container_info(false)
|
||||
{
|
||||
luaopen_lpeg(m_ls);
|
||||
@ -55,6 +56,8 @@ falco_engine::falco_engine(bool seed_rng)
|
||||
{
|
||||
srandom((unsigned) getpid());
|
||||
}
|
||||
|
||||
m_default_ruleset_id = find_ruleset_id(m_default_ruleset);
|
||||
}
|
||||
|
||||
falco_engine::~falco_engine()
|
||||
@ -107,20 +110,52 @@ void falco_engine::load_rules_file(const string &rules_filename, bool verbose, b
|
||||
load_rules(rules_content, verbose, all_events);
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule(string &pattern, bool enabled)
|
||||
void falco_engine::enable_rule(const string &pattern, bool enabled, const string &ruleset)
|
||||
{
|
||||
m_evttype_filter->enable(pattern, enabled);
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
m_evttype_filter->enable(pattern, enabled, ruleset_id);
|
||||
}
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev)
|
||||
void falco_engine::enable_rule(const string &pattern, bool enabled)
|
||||
{
|
||||
enable_rule(pattern, enabled, m_default_ruleset);
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, const string &ruleset)
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
m_evttype_filter->enable_tags(tags, enabled, ruleset_id);
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled)
|
||||
{
|
||||
enable_rule_by_tag(tags, enabled, m_default_ruleset);
|
||||
}
|
||||
|
||||
uint16_t falco_engine::find_ruleset_id(const std::string &ruleset)
|
||||
{
|
||||
auto it = m_known_rulesets.lower_bound(ruleset);
|
||||
|
||||
if(it == m_known_rulesets.end() ||
|
||||
it->first != ruleset)
|
||||
{
|
||||
it = m_known_rulesets.emplace_hint(it,
|
||||
std::make_pair(ruleset, m_next_ruleset_id++));
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev, uint16_t ruleset_id)
|
||||
{
|
||||
if(should_drop_evt())
|
||||
{
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
|
||||
if(!m_evttype_filter->run(ev))
|
||||
if(!m_evttype_filter->run(ev, ruleset_id))
|
||||
{
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
@ -155,6 +190,11 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev)
|
||||
return res;
|
||||
}
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev)
|
||||
{
|
||||
return process_event(ev, m_default_ruleset_id);
|
||||
}
|
||||
|
||||
void falco_engine::describe_rule(string *rule)
|
||||
{
|
||||
return m_rules->describe_rule(rule);
|
||||
@ -182,10 +222,11 @@ void falco_engine::print_stats()
|
||||
}
|
||||
|
||||
void falco_engine::add_evttype_filter(string &rule,
|
||||
list<uint32_t> &evttypes,
|
||||
set<uint32_t> &evttypes,
|
||||
set<string> &tags,
|
||||
sinsp_filter* filter)
|
||||
{
|
||||
m_evttype_filter->add(rule, evttypes, filter);
|
||||
m_evttype_filter->add(rule, evttypes, tags, filter);
|
||||
}
|
||||
|
||||
void falco_engine::clear_filters()
|
||||
|
@ -20,6 +20,7 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include "sinsp.h"
|
||||
#include "filter.h"
|
||||
@ -47,9 +48,24 @@ public:
|
||||
void load_rules(const std::string &rules_content, bool verbose, bool all_events);
|
||||
|
||||
//
|
||||
// Enable/Disable any rules matching the provided pattern (regex).
|
||||
// Enable/Disable any rules matching the provided pattern
|
||||
// (regex). When provided, enable/disable these rules in the
|
||||
// context of the provided ruleset. The ruleset (id) can later
|
||||
// be passed as an argument to process_event(). This allows
|
||||
// for different sets of rules being active at once.
|
||||
//
|
||||
void enable_rule(std::string &pattern, bool enabled);
|
||||
void enable_rule(const std::string &pattern, bool enabled, const std::string &ruleset);
|
||||
|
||||
// Wrapper that assumes the default ruleset
|
||||
void enable_rule(const std::string &pattern, bool enabled);
|
||||
|
||||
//
|
||||
// Enable/Disable any rules with any of the provided tags (set, exact matches only)
|
||||
//
|
||||
void enable_rule_by_tag(const std::set<std::string> &tags, bool enabled, const std::string &ruleset);
|
||||
|
||||
// Wrapper that assumes the default ruleset
|
||||
void enable_rule_by_tag(const std::set<std::string> &tags, bool enabled);
|
||||
|
||||
struct rule_result {
|
||||
sinsp_evt *evt;
|
||||
@ -58,12 +74,30 @@ public:
|
||||
std::string format;
|
||||
};
|
||||
|
||||
//
|
||||
// Return the ruleset id corresponding to this ruleset name,
|
||||
// creating a new one if necessary. If you provide any ruleset
|
||||
// to enable_rule/enable_rule_by_tag(), you should look up the
|
||||
// ruleset id and pass it to process_event().
|
||||
//
|
||||
uint16_t find_ruleset_id(const std::string &ruleset);
|
||||
|
||||
//
|
||||
// Given an event, check it against the set of rules in the
|
||||
// engine and if a matching rule is found, return details on
|
||||
// the rule that matched. If no rule matched, returns NULL.
|
||||
//
|
||||
// the reutrned rule_result is allocated and must be delete()d.
|
||||
// When ruleset_id is provided, use the enabled/disabled status
|
||||
// associated with the provided ruleset. This is only useful
|
||||
// when you have previously called enable_rule/enable_rule_by_tag
|
||||
// with a ruleset string.
|
||||
//
|
||||
// the returned rule_result is allocated and must be delete()d.
|
||||
std::unique_ptr<rule_result> process_event(sinsp_evt *ev, uint16_t ruleset_id);
|
||||
|
||||
//
|
||||
// Wrapper assuming the default ruleset
|
||||
//
|
||||
std::unique_ptr<rule_result> process_event(sinsp_evt *ev);
|
||||
|
||||
//
|
||||
@ -78,11 +112,12 @@ public:
|
||||
void print_stats();
|
||||
|
||||
//
|
||||
// Add a filter, which is related to the specified list of
|
||||
// Add a filter, which is related to the specified set of
|
||||
// event types, to the engine.
|
||||
//
|
||||
void add_evttype_filter(std::string &rule,
|
||||
list<uint32_t> &evttypes,
|
||||
std::set<uint32_t> &evttypes,
|
||||
std::set<std::string> &tags,
|
||||
sinsp_filter* filter);
|
||||
|
||||
// Clear all existing filters.
|
||||
@ -120,6 +155,8 @@ private:
|
||||
inline bool should_drop_evt();
|
||||
|
||||
falco_rules *m_rules;
|
||||
uint16_t m_next_ruleset_id;
|
||||
std::map<string, uint16_t> m_known_rulesets;
|
||||
std::unique_ptr<sinsp_evttype_filter> m_evttype_filter;
|
||||
|
||||
//
|
||||
@ -146,6 +183,8 @@ private:
|
||||
double m_sampling_multiplier;
|
||||
|
||||
std::string m_lua_main_filename = "rule_loader.lua";
|
||||
std::string m_default_ruleset = "falco-default-ruleset";
|
||||
uint32_t m_default_ruleset_id;
|
||||
|
||||
std::string m_extra;
|
||||
bool m_replace_container_info;
|
||||
|
@ -308,8 +308,12 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
|
||||
|
||||
install_filter(filter_ast.filter.value)
|
||||
|
||||
if (v['tags'] == nil) then
|
||||
v['tags'] = {}
|
||||
end
|
||||
|
||||
-- Pass the filter and event types back up
|
||||
falco_rules.add_filter(rules_mgr, v['rule'], evttypes)
|
||||
falco_rules.add_filter(rules_mgr, v['rule'], evttypes, v['tags'])
|
||||
|
||||
-- Rule ASTs are merged together into one big AST, with "OR" between each
|
||||
-- rule.
|
||||
|
@ -65,42 +65,55 @@ void falco_rules::clear_filters()
|
||||
|
||||
int falco_rules::add_filter(lua_State *ls)
|
||||
{
|
||||
if (! lua_islightuserdata(ls, -3) ||
|
||||
! lua_isstring(ls, -2) ||
|
||||
if (! lua_islightuserdata(ls, -4) ||
|
||||
! lua_isstring(ls, -3) ||
|
||||
! lua_istable(ls, -2) ||
|
||||
! lua_istable(ls, -1))
|
||||
{
|
||||
throw falco_exception("Invalid arguments passed to add_filter()\n");
|
||||
}
|
||||
|
||||
falco_rules *rules = (falco_rules *) lua_topointer(ls, -3);
|
||||
const char *rulec = lua_tostring(ls, -2);
|
||||
falco_rules *rules = (falco_rules *) lua_topointer(ls, -4);
|
||||
const char *rulec = lua_tostring(ls, -3);
|
||||
|
||||
list<uint32_t> evttypes;
|
||||
set<uint32_t> evttypes;
|
||||
|
||||
lua_pushnil(ls); /* first key */
|
||||
while (lua_next(ls, -3) != 0) {
|
||||
// key is at index -2, value is at index
|
||||
// -1. We want the keys.
|
||||
evttypes.insert(luaL_checknumber(ls, -2));
|
||||
|
||||
// Remove value, keep key for next iteration
|
||||
lua_pop(ls, 1);
|
||||
}
|
||||
|
||||
set<string> tags;
|
||||
|
||||
lua_pushnil(ls); /* first key */
|
||||
while (lua_next(ls, -2) != 0) {
|
||||
// key is at index -2, value is at index
|
||||
// -1. We want the keys.
|
||||
evttypes.push_back(luaL_checknumber(ls, -2));
|
||||
tags.insert(lua_tostring(ls, -1));
|
||||
|
||||
// Remove value, keep key for next iteration
|
||||
lua_pop(ls, 1);
|
||||
}
|
||||
|
||||
std::string rule = rulec;
|
||||
rules->add_filter(rule, evttypes);
|
||||
rules->add_filter(rule, evttypes, tags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void falco_rules::add_filter(string &rule, list<uint32_t> &evttypes)
|
||||
void falco_rules::add_filter(string &rule, set<uint32_t> &evttypes, set<string> &tags)
|
||||
{
|
||||
// While the current rule was being parsed, a sinsp_filter
|
||||
// object was being populated by lua_parser. Grab that filter
|
||||
// and pass it to the engine.
|
||||
sinsp_filter *filter = m_lua_parser->get_filter(true);
|
||||
|
||||
m_engine->add_evttype_filter(rule, evttypes, filter);
|
||||
m_engine->add_evttype_filter(rule, evttypes, tags, filter);
|
||||
}
|
||||
|
||||
int falco_rules::enable_rule(lua_State *ls)
|
||||
|
@ -18,7 +18,7 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
#include "sinsp.h"
|
||||
|
||||
@ -42,7 +42,7 @@ class falco_rules
|
||||
|
||||
private:
|
||||
void clear_filters();
|
||||
void add_filter(string &rule, list<uint32_t> &evttypes);
|
||||
void add_filter(string &rule, std::set<uint32_t> &evttypes, std::set<string> &tags);
|
||||
void enable_rule(string &rule, bool enabled);
|
||||
|
||||
lua_parser* m_lua_parser;
|
||||
|
@ -60,6 +60,7 @@ static void usage()
|
||||
" -A Monitor all events, including those with EF_DROP_FALCO flag.\n"
|
||||
" -d, --daemon Run as a daemon\n"
|
||||
" -D <pattern> Disable any rules matching the regex <pattern>. Can be specified multiple times.\n"
|
||||
" Can not be specified with -t.\n"
|
||||
" -e <events_file> Read the events from <events_file> (in .scap format) instead of tapping into live.\n"
|
||||
" -k <url>, --k8s-api=<url>\n"
|
||||
" Enable Kubernetes support by connecting to the API server\n"
|
||||
@ -100,6 +101,10 @@ static void usage()
|
||||
" Can be specified multiple times to read from multiple files.\n"
|
||||
" -s <stats_file> If specified, write statistics related to falco's reading/processing of events\n"
|
||||
" to this file. (Only useful in live mode).\n"
|
||||
" -T <tag> Disable any rules with a tag=<tag>. Can be specified multiple times.\n"
|
||||
" Can not be specified with -t.\n"
|
||||
" -t <tag> Only run those rules with a tag=<tag>. Can be specified multiple times.\n"
|
||||
" Can not be specified with -T/-D.\n"
|
||||
" -v Verbose output.\n"
|
||||
"\n"
|
||||
);
|
||||
@ -259,12 +264,15 @@ int falco_init(int argc, char **argv)
|
||||
{
|
||||
set<string> disabled_rule_patterns;
|
||||
string pattern;
|
||||
string all_rules = ".*";
|
||||
set<string> disabled_rule_tags;
|
||||
set<string> enabled_rule_tags;
|
||||
|
||||
//
|
||||
// Parse the args
|
||||
//
|
||||
while((op = getopt_long(argc, argv,
|
||||
"hc:AdD:e:k:K:Ll:m:o:P:p:r:s:vw:",
|
||||
"hc:AdD:e:k:K:Ll:m:o:P:p:r:s:T:t:vw:",
|
||||
long_options, &long_index)) != -1)
|
||||
{
|
||||
switch(op)
|
||||
@ -339,6 +347,12 @@ int falco_init(int argc, char **argv)
|
||||
case 's':
|
||||
stats_filename = optarg;
|
||||
break;
|
||||
case 'T':
|
||||
disabled_rule_tags.insert(optarg);
|
||||
break;
|
||||
case 't':
|
||||
enabled_rule_tags.insert(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
@ -421,12 +435,40 @@ int falco_init(int argc, char **argv)
|
||||
falco_logger::log(LOG_INFO, "Parsed rules from file " + filename + "\n");
|
||||
}
|
||||
|
||||
// You can't both disable and enable rules
|
||||
if((disabled_rule_patterns.size() + disabled_rule_tags.size() > 0) &&
|
||||
enabled_rule_tags.size() > 0) {
|
||||
throw std::invalid_argument("You can not specify both disabled (-D/-T) and enabled (-t) rules");
|
||||
}
|
||||
|
||||
for (auto pattern : disabled_rule_patterns)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Disabling rules matching pattern: " + pattern + "\n");
|
||||
engine->enable_rule(pattern, false);
|
||||
}
|
||||
|
||||
if(disabled_rule_tags.size() > 0)
|
||||
{
|
||||
for(auto tag : disabled_rule_tags)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
|
||||
}
|
||||
engine->enable_rule_by_tag(disabled_rule_tags, false);
|
||||
}
|
||||
|
||||
if(enabled_rule_tags.size() > 0)
|
||||
{
|
||||
|
||||
// Since we only want to enable specific
|
||||
// rules, first disable all rules.
|
||||
engine->enable_rule(all_rules, false);
|
||||
for(auto tag : enabled_rule_tags)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
|
||||
}
|
||||
engine->enable_rule_by_tag(enabled_rule_tags, true);
|
||||
}
|
||||
|
||||
outputs->init(config.m_json_output, config.m_notifications_rate, config.m_notifications_max_burst);
|
||||
|
||||
if(!all_events)
|
||||
|
Loading…
Reference in New Issue
Block a user