Compare commits

...

242 Commits

Author SHA1 Message Date
Federico Di Pierro
bc714d06fc chore(userspace/falco): do not allow multiple Falco instances.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-10-10 14:22:16 +02:00
Stanley Chan
79d875c28f cleanup(scripts): cleanup systemd unit in RPM installer
Signed-off-by: Stanley Chan <pocketgamer5000@gmail.com>
2022-10-07 14:47:00 +02:00
Stanley Chan
7610ee53e5 cleanup(scripts): cleanup systemd unit in DEB installer
Signed-off-by: Stanley Chan <pocketgamer5000@gmail.com>
2022-10-07 14:47:00 +02:00
Jason Dellaluce
3c02b40a21 chore(userspace/falco): make log message termination consistent
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-06 21:27:06 +02:00
Jason Dellaluce
e85a8c914f chore(userspace/falco): move enabled sources list printout when capture is opened
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-06 21:27:06 +02:00
Jason Dellaluce
21c2b1f472 update(userspace/falco): use unordered_set where possible for faster lookups
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-06 21:27:06 +02:00
Jason Dellaluce
909f6d0961 chore(userspace/falco): make log messages formatting more consistent
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-06 21:27:06 +02:00
Jason Dellaluce
83a83a5853 update(userspace): pass string as const refs when possible
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-06 21:27:06 +02:00
Jason Dellaluce
b4ea2f4da2 fix(userspace/falco): stabilize termination signal handler
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-06 18:21:05 +02:00
Jason Dellaluce
59ba2f9aab fix(userspace/falco): properly terminate threads
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-06 18:21:05 +02:00
Jason Dellaluce
32ec3240b4 fix(rules): add falco no-driver images to k8s_containers macro
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-06 15:44:10 +02:00
Andrea Terzolo
fbac2a9570 tests: fix broken tests
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-10-05 19:38:21 +02:00
Andrea Terzolo
805f0cdd78 chore: bump libs to latest release branch commit
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-10-05 19:38:21 +02:00
Federico Di Pierro
e68151eb07 chore(test,userspace/falco): fixed tests after libs bump.
Moreover, try to create grpc socket folder path only if grpc is actually enabled.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-10-05 19:38:21 +02:00
Andrea Terzolo
ec7ddbbaf8 chore: bump libs/driver to pre-release tag
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-10-05 19:38:21 +02:00
Jason Dellaluce
663c1d073a fix(userspace/falco): check plugin requirements when validating rule files
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-05 13:21:20 +02:00
Jason Dellaluce
bbb821fb8e refactor(userspace/falco): move rules plugin requirements check in an internal funcion
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-05 13:21:20 +02:00
Jason Dellaluce
5781c53ddc fix(userspace): add explicit constructors and initializations
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-10-03 13:04:15 +02:00
Andrea Terzolo
545b58ee14 update(open_inspector): use variable buffer dim in modern bpf
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-28 18:55:06 +02:00
Andrea Terzolo
cf83ff5447 chore: bump libs to latest master
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-28 18:55:06 +02:00
Andrea Terzolo
8d8e7622e1 update(cmd_line): put modern bpf to false
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-28 18:55:06 +02:00
Andrea Terzolo
fd097e94d7 new(cmdline): add support for modern BPF probe
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-28 18:55:06 +02:00
Luca Guerra
6634c896b7 fix(falco): print container info and gvisor info in the same way
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-09-28 12:45:04 +02:00
spyder-kyle
38c823533c Add PIDs to falco_rules.yaml rules
Signed-off-by: Kyle Smith Hanna <kyle.smithhanna@spyderbat.com>
2022-09-27 10:51:00 +02:00
Andrea Terzolo
3aa9267b48 fix(syscall_buffer): set dimension if page size not available
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-27 10:47:59 +02:00
Andrea Terzolo
725714726d update(configuration): define m_syscall_buf_size_preset as uint16_t
improve also some logs for `m_syscall_buf_size_preset` configuration errors

Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:47:59 +02:00
Andrea Terzolo
c9fa585801 update: address some review comments
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Co-authored-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-09-27 10:47:59 +02:00
Andrea Terzolo
90e4634a79 update(syscall_buffer_size): don't crash in case of getpagesize error
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Federico Di Pierro <nierro92@gmail.com>
2022-09-27 10:47:59 +02:00
Andrea Terzolo
b0b2f05eb5 new: configure syscall buffer dimension from Falco
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-27 10:47:59 +02:00
Jason Dellaluce
8aea0935c9 chore(userspace/engine): remove unused var
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
9c240198a0 refactor(userspace/engine): refactor falco_engine with new loader defs
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
f6f763fe84 refactor(userspace/engine): clean up rule collector
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
9b5f3ee99e refactor(userspace/engine): clean up rule compiler
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
89e8f70de0 refactor(userspace/engine): clean up and rename rule reader
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
b0f0105116 refactor(userspace/engine): clean up rule loader
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
5f2267f716 update(userspace/engine): add new loader files to CMakeLists
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
b65157af5e refactor(userspace/engine): split rule loader git history (5)
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
b2b1feb1f2 refactor(userspace/engine): split rule loader git history (4)
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
b900e46dfe refactor(userspace/engine): split rule loader git history (3)
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
a98c9cdd20 refactor(userspace/engine): split rule loader git history (2)
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Jason Dellaluce
2a427925a0 refactor(userspace/engine): split rule loader git history (1)
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-27 10:42:59 +02:00
Andrea Terzolo
c0c37d87f5 fix(process_events): check the return value of open_live_inspector
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-20 18:07:30 +02:00
Andrea Terzolo
f57c67cc96 docs(falco.yaml): fix a typo
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
2022-09-20 11:35:28 +02:00
Andrea Terzolo
7686c03a36 update(app_actions): add a depraction comment for BPF
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-20 11:35:28 +02:00
Andrea Terzolo
aa0abb4288 tests: fix traces-positive/run-shell-untrusted.scap test
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-20 11:35:28 +02:00
Andrea Terzolo
8b927fb010 chore: bump libs version
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-20 11:35:28 +02:00
Andrea Terzolo
a325086363 test(falco): fix broken tests
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-20 11:35:28 +02:00
Andrea Terzolo
1930ec56c7 test(plugin): bump plugin API in test
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-20 11:35:28 +02:00
Andrea Terzolo
3902779409 chore(plugins.cmake): bump plugin versions
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-20 11:35:28 +02:00
Andrea Terzolo
7e37c72431 update: falco works with the latest libs commit
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-20 11:35:28 +02:00
Federico Di Pierro
e068df514c chore(userspace/engine,userspace/falco): upgraded to latest libs.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-09-20 11:35:28 +02:00
Federico Di Pierro
9048d84ed4 chore(cmake): bumped libs to latest master.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-09-20 11:35:28 +02:00
Federico Di Pierro
00459f3447 chore(cmake): dropped SCAP_BPF_PROBE_ENV_VAR_NAME variable; unused.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-20 11:35:28 +02:00
Federico Di Pierro
0274959981 update(userspace/falco, cmake): updated libs to latest master.
Adapted API to sinsp::open API break, and simple consumer API break.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-20 11:35:28 +02:00
Hi120ki
30b56d2960 revert and create new known macro
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:24:40 +02:00
Hi120ki
d6b5789b7a add user_known_mount_in_privileged_containers
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:24:40 +02:00
Hi120ki
af4524491d put open_read in the beginning of the rule
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:22:39 +02:00
Hi120ki
36a08aee13 Update rules/falco_rules.yaml to delete enabled field
Co-authored-by: schie <77834235+darryk10@users.noreply.github.com>
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:22:39 +02:00
Hi120ki
39de011751 Update rules/falco_rules.yaml to add argoexec into allowlist
Co-authored-by: schie <77834235+darryk10@users.noreply.github.com>
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:22:39 +02:00
Hi120ki
a83d38c6d7 add allowlist
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:22:39 +02:00
Hi120ki
86c3a9cd69 revert to container
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:22:39 +02:00
Hi120ki
8473706526 add systemd-sysctl to allowlist
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:22:39 +02:00
Hi120ki
4e622fc033 add host to target
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:22:39 +02:00
Hi120ki
16dca8f905 add rule Read environment variable from /proc files
Signed-off-by: Hi120ki <12624257+hi120ki@users.noreply.github.com>
2022-09-16 14:22:39 +02:00
Mark Stemm
2d5fc0b647 Use the same falco_rule struct for every call to filter_ruleset
Instead of using a falco_rule struct on the stack, use a single value
inside the falco_source struct. It's mutable as find_source returns a
const struct.

At very high event volumes (> 1M syscalls/second), even the tiny time
it takes to create/destroy the struct starts to add up, and this
switch has some small cpu savings.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-09-16 12:50:39 +02:00
Mark Stemm
e5cd5eacf5 Save syscall source separately and check explicitly in process_event
When doing some testing of falco on very high event volumes (> 1.5M
events/second), I found that the time taken to look up a falco_source
struct had a non-negligible contribution to cpu usage.

So instead of looking up the source from the source_idx every time,
separately save the source for syscalls in the falco_engine object
directly. The separately saved copy is only used once someone calls
add_source with source="syscall".

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-09-16 12:50:39 +02:00
Stefano
366bcfd7a3 Added disable by default option to reduce noise
Signed-off-by: darryk10 <stefano.chierici@sysdig.com>
2022-09-16 12:44:38 +02:00
Stefano
c844eb9ef3 Added rule to detect CVE-2019-5736
Co-authored-by: wcc526 <wcc526@gmail.com>
Signed-off-by: darryk10 <stefano.chierici@sysdig.com>
2022-09-16 12:44:38 +02:00
Leonardo Grasso
b71eb7e6ed chore(OWNERS): cleanup inactive reviewer
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-09-14 15:31:25 +02:00
Leonardo Grasso
c732e5d800 update: gRPC server sock defaults to /run/falco/falco.sock
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-09-14 10:27:24 +02:00
Leonardo Grasso
c0ea753262 update(userspace/falco): gVisor sock now defaults to /run/falco/gvisor.sock
Co-authored-by: Vicente J. Jiménez Miras <vjjmiras@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-09-14 10:27:24 +02:00
Vicente JJ. Miras
e4008217b9 Replacing /tmp/gvisor.sock with /run/gvisor.sock
According to the FHS 3.0 (https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s15.html), transient UNIX-domain sockets should be placed under the directory /run, so this commit updates the implicit value generated by the application.

Signed-off-by: Vicente J. Jiménez Miras <vjjmiras@gmail.com>
2022-09-14 10:27:24 +02:00
Jason Dellaluce
9c184af2a1 fix(userspace/falco): adopt stricter memory order semantics
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
d11aec28d5 fix(userspace/falco): move stats collection in event success path
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
d17e173e35 chore(userspace/falco): rename sources app state list for more clarity
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
25e9bd1c91 chore(userspace/falco): fix codespell typo
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
4bc9fc74c8 update(userspace/falco)!: adapt stats writer for multiple parallel event sources
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
b65cc49221 update(userspace/falco): rename init_inspector action into init_inspectors
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
ce769b1fbe fix(test): adapt plugin tests to new error msgs and features
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
65993ad1ed refactor(userspace/falco): support multiple parallel event processing loops
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
f4c6a81ed8 update(userspace/falco): fix plugin list access in rule file loading action
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
f9a152b24c refactor(userspace/falco): generalize responsibilities of init_inspector action
Now, the action takes care of inizializing all app inspectors
(just one in capture mode, one for each evt source in live mode), and of
registering and initializing all loaded plugins in the right inspector as needed.
The plugin initialization logic, which also involves the filtercheck list
population and checks, was moved and refactored from the previous
implementation of the load_plugins action.

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
ed025f1a86 refactor(userspace/falco): init all event sources in falco engine and in the right order
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
8ba779de8c refactor(userspace/falco): restrict load_plugins action responsibilities
Now, the action is in charge of loading all plugins and initializing:
- the offline inspector
- the list of loaded event sources
- the list of loaded plugins and their config

After this action runs, plugins are loaded but not yet initialized.

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
cf8b85ad86 refactor(userspace/falco): turn open inspector action into convenience private methods
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
9cf3d118f6 update(userspace/falco): restrict clients init action to syscall inspector only
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
63bdc1119f cleanup(userspace/falco): remove legacy hacks on source selection action
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
9dc3eb2fc6 update(userspace/falco): reorder actions for their new semantics
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
7bb319b21e update(userspace/falco): add convenience method for merging app run results
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
3f7d61f150 refactor(userspace/falco): re-design application state and methods
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 16:14:15 +02:00
Jason Dellaluce
cf9baea624 fix(userspace/engine): avoid reading duplicate exception values
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-12 15:53:15 +02:00
Federico Di Pierro
ccd3c896de fix(userspace/engine): properly include stdexcept header to fix build.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-09-12 12:28:15 +02:00
Federico Di Pierro
11644ecafc chore(userspace/falco): be somewhat more portable, avoiding assuming that '/' is the path delim.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-09-09 09:59:06 +02:00
Federico Di Pierro
23df49a47f new(userspace/falco): create grpc unix socket and gvisor endpoint path automatically.
It is also able to handle multipart paths, like /run/falco/falco/falco/falco.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-09-09 09:59:06 +02:00
Federico Di Pierro
3e1ef070b8 fix(circleci): falco-driver-loader image build must be done starting from just-pushed falco master image.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Aldo Lacuku <aldo@lacuku.eu>
2022-09-07 16:04:04 +02:00
Aldo Lacuku
670b0733de apply suggestions from the review
Signed-off-by: Aldo Lacuku <aldo@lacuku.eu>
Co-authored-by: Federico Di Pierro <nierro92@gmail.com>
2022-09-07 10:14:03 +02:00
Aldo Lacuku
752c3d8332 update(scripts/falco-driver-loader): minikube environment is now correctly detected
If there is a file in `/etc/VERSION` the script assumes that it is running in a minikube
enviroment. Furthermore the logic in building the `KERNEL_VERSION` has been updated
according to how the kernel and probe modules are saved in the S3 bucket. The kernel version
for the minikube kernels includes also the minikube version.

Signed-off-by: Aldo Lacuku <aldo@lacuku.eu>
2022-09-07 10:14:03 +02:00
Aldo Lacuku
daba85b61c update(scripts/falco-driver-loader): make some config variables configurable using env variables
When running falco-driver-loader in local we need to set some config variables to meaningful values.
Those variables usually are set by the CI during the image/packages builds. The changes introduced
by this commit allow to set them at start-up time using env variables

Signed-off-by: Aldo Lacuku <aldo@lacuku.eu>
2022-09-07 10:14:03 +02:00
Mark Stemm
103d7e08b8 Update tests for rules loading (item names)
Update a few tests related to rules loading to use new names for
items (e.g. "rules content" for top level errors instead of "file")

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-09-07 10:13:02 +02:00
Mark Stemm
0f45cf49db Use enums for rules content item type
Use an enum instead of a string for the item_type aka "parts of a
rules file" field of contexts.

The set of values is mostly defined by the contexts that were already
created. There are a couple of forward-looking values for rule
outputs/macro conditions/etc. that may be useful for later.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-09-07 10:13:02 +02:00
Mark Stemm
7a5a4c32ee Support condition parse errors in rule loading results
In #2098 and #2158, we reworked how rules loading errors/warnings were
returned to provide a richer set of information, including
locations/context for the errors/warnings.

That did *not* include locations within condition expressions,
though. When parsing a condition expression resulted in a
warning/error, the location simply pointed to the condition property
of the rule.

This commit improves this to handle parse errors:

- When libsinsp::filter::parser::parse() throws an exception, use
  get_pos() to get the position within the condition string.
- Add a new context() constructor that takes a filter pos_info instead
  of a YAML::Mark.

Now that positions aren't always related to the location of yaml
nodes, Make up a generic "position" struct for locations and convert
YAML::Mark and parser positions to a position struct.

Also allow a context to contain an alternate content string which is
used to build the snippet. For contexts related to condition strings,
the content is the condition.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-09-07 10:13:02 +02:00
VadimZy
af95455bab dropping fix for list parsing due to the absence of regex portability.
reverting to the inefficient code.

Signed-off-by: VadimZy <vadim.zyarko@sysdig.com>
2022-09-05 17:42:31 +02:00
VadimZy
4b75f213c6 use <onigposix.h> instead of <regex.h>
Signed-off-by: VadimZy <vadim.zyarko@sysdig.com>
2022-09-05 17:42:31 +02:00
VadimZy
0de617a7fb remove sinsp.h public dependencies
Signed-off-by: VadimZy <vadim.zyarko@sysdig.com>
2022-09-05 17:42:31 +02:00
VadimZy
5745faeccc fix tests, remove dead code
Signed-off-by: VadimZy <vadim.zyarko@sysdig.com>
2022-09-05 17:42:31 +02:00
VadimZy
f9ee45b38e Improve Falco engine performance when loading rules and creating the rule sets
- replace std::set<uint16_t> with fixed size vector in event types propagation
- rework lists expansion by replacing repetitive string::find in constantly growing expansion string with regex tokenization
- improve json_event parsing by moving const initializations into static routines

Signed-off-by: VadimZy <vadim.zyarko@sysdig.com>
2022-09-05 17:42:31 +02:00
Jason Dellaluce
7d2f82fddc update(usperspace/engine): bump engine version to 15
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-05 14:40:31 +02:00
Jason Dellaluce
0956bac0de tests: add unit tests for plugin requirements checks in the engine
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-05 14:40:31 +02:00
Jason Dellaluce
1b410ea2cc update(userspace/engine): consider plugin version requirements in engine checks
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-05 14:40:31 +02:00
Jason Dellaluce
52402ac805 update(userspace/engine): support plugin version requirement alternatives in rule reader
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-05 14:40:31 +02:00
Jason Dellaluce
6e0971f1e1 update(userspace/engine): support plugin version requirement alternatives in rule loader
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-09-05 14:40:31 +02:00
Andrea Terzolo
934ae2f1a6 update(OWNERS): add Andrea Terzolo to owners
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-09-02 12:14:42 +02:00
Leonardo Grasso
fab95f38a9 update(.circleci): re-enabled cppcheck
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-09-02 08:54:27 +02:00
Mauro Ezequiel Moltrasio
0be0191c53 Add StackRox to adopters
Co-authored-by: mfosterrox <mfoster@redhat.com>
Signed-off-by: Mauro Ezequiel Moltrasio <mmoltras@redhat.com>
2022-09-01 22:33:25 +02:00
Melissa Kilby
5dcc329339 chore(rules): change FALCO_ENGINE_VERSION to 13
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-09-01 14:45:23 +02:00
Melissa Kilby
721aa30e80 cleanup(rules): cleanup redundant use of always_true macros - 2
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-09-01 14:45:23 +02:00
Melissa Kilby
565ddd70d3 cleanup(rules): cleanup rules disabled by default - 4
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-09-01 14:45:23 +02:00
Leonardo Grasso
c000695816 docs(README.md): cross linking resources and minor improvements
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-08-31 15:27:42 +02:00
Leonardo Grasso
d67edeb39d docs(README.md): update URLs
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-08-31 15:27:42 +02:00
Leonardo Grasso
1a22bfc654 docs(README.md): remove client-rs and client-py SDKs
Those SDKs are now archived projects, see:
 - https://github.com/falcosecurity/evolution/issues/161
 - https://github.com/falcosecurity/evolution/issues/162

Hoping we can restore them in the future.

Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-08-31 15:27:42 +02:00
Federico Di Pierro
bd865450ef new(scripts): improved falco-driver-loader supporting any ubuntu flavor.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-08-30 12:25:37 +02:00
Jason Dellaluce
98b8e390a1 chore(rules): fix old url redirection
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-29 15:42:33 +02:00
Jason Dellaluce
6c1f908ca5 cleanup(cmake): rename legacy cmake variables
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-29 15:42:33 +02:00
Jason Dellaluce
574a4b9f0a update(userspace/falco): fix copyright notice year
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:48:18 +02:00
Jason Dellaluce
c05ad6fde4 update(userspace/falco): fix copyright notice year
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:48:18 +02:00
Jason Dellaluce
e361069092 chore(userspace/falco): fix typos
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:48:18 +02:00
Jason Dellaluce
9c6ad6ce84 update(userspace/falco): use json lib in stats writer
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:48:18 +02:00
Jason Dellaluce
2d8efee73e refactor(userspace/falco): improve design and docs of stats writer
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:48:18 +02:00
Jason Dellaluce
28ff6ad3bd refactor(userspace/falco): rename stats writer source files
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:48:18 +02:00
Jason Dellaluce
2f5461bed0 refactor(userspace/falco): use new stats writer in event processing action
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:48:18 +02:00
Jason Dellaluce
605dd2816d refactor(userspace/falco): re-implement stats writer
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:48:18 +02:00
Jason Dellaluce
c5442ccb41 new(userspace/falco): introduce new refactored stats writer class
This new model uses an async worker and a concurrent queue to handle
stats writing. This ensures better performance, because the live event
processing loop will just need to do a push on the queue instead of writing
to a file (only when the timer triggers), and should be thread-safe by design.

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:48:18 +02:00
Jason Dellaluce
9646308651 update(test): use event source selection in plugins tests
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:47:18 +02:00
Jason Dellaluce
e15d9f6f51 update(test): use event source selection in k8s audit tests
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:47:18 +02:00
Jason Dellaluce
ce0dd918fb refactor(test): enable selecting event sources in regression test suite
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:47:18 +02:00
Jason Dellaluce
cc4ccc40d7 refactor(userspace/falco): implement complete event source selection
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
Co-authored-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-26 12:47:18 +02:00
Jason Dellaluce
0e2a053151 new(userspace/falco): add new cli option to selectively enable event sources
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:47:18 +02:00
Jason Dellaluce
97bf0338b9 refactor(userspace/falco): introduce standalone action for event source selection
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 12:47:18 +02:00
Melissa Kilby
6c12cc655e cleanup(rules): cleanup redundant use of always_true macros
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-08-26 11:40:18 +02:00
Melissa Kilby
7387fffcef cleanup(rules): cleanup rules disabled by default - 3
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-08-26 11:40:18 +02:00
Jason Dellaluce
34ca78786a refactor(userspace/falco): make signal handlers thread-safe
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:31:18 +02:00
Jason Dellaluce
f2aba88a6c refactor(userspace/falco): ensure falco outputs are non-blocking and define exiting condition
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:31:18 +02:00
Jason Dellaluce
bc765f1b7d chore(userspace/falco): log in signal handlers instead than in event processing loop
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:31:18 +02:00
Jason Dellaluce
c2a8efc329 chore(userspace/engine): fix typos
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:26:18 +02:00
Jason Dellaluce
978f192c38 chore(userspace/engine): fix codespell typos
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:26:18 +02:00
Jason Dellaluce
1120fb2564 doc(userspace/engine): define thread-safety guarantees of falco_engine::process_event
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:26:18 +02:00
Jason Dellaluce
1b8847c06b refactor(userspace/engine): make stats manager thread-safe for on_event method
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:26:18 +02:00
Jason Dellaluce
3839fdca1e update(userspace/falco): avoid using zlib in webserver
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:23:17 +02:00
Jason Dellaluce
f599fab439 update(falco.yaml): update default configuration and its comments
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:23:17 +02:00
Jason Dellaluce
2b7bcc87a7 update(userspace/falco): add configuration entry for webserver threadiness
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:23:17 +02:00
Jason Dellaluce
0eacd41cd5 refactor(userspace/falco): support zlib and custom threadiness in webserver
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:23:17 +02:00
Jason Dellaluce
d9b6473db2 refactor(userspace/engine): increase const coherence of falco engine
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-26 11:04:18 +02:00
Melissa Kilby
a6137e9475 update(rules): Directory traversal monitored file read - include failed open attempts w/ new macro open_file_failed
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-08-25 21:44:15 +02:00
Melissa Kilby
dd49038b0d cleanup(rules): Directory traversal monitored file read
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-08-25 21:44:15 +02:00
Melissa Kilby
6efc5b42f7 new(rules): Directory traversal monitored file read
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-08-25 21:44:15 +02:00
Jason Dellaluce
7d3dacc6d7 refactor(userspace/falco): cleanup actions order
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 17:02:15 +02:00
Jason Dellaluce
a9d185f5e1 refactor(userspace/falco): drop inspector dependency on print_plugin_info action
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 17:02:15 +02:00
Jason Dellaluce
bd26bc09c2 refactor(userspace/falco): drop inspector dependency on print_ignored_events action
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 17:02:15 +02:00
Jason Dellaluce
97e3209222 refactor(userspace/falco): drop inspector dependency on load_rule_files action
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 17:02:15 +02:00
Jason Dellaluce
6d30061576 refactor(userspace/falco): drop inspector dependency on list_plugins action
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 17:02:15 +02:00
Jason Dellaluce
2caadd1af5 refactor(userspace/falco): add action for printing syscall events
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 17:02:15 +02:00
Jason Dellaluce
b307853e39 update(userspace/falco): use move semantics in falco logger
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 17:00:15 +02:00
Leonardo Grasso
8e8491f280 update(test/output_files): add "hostname" to fixture
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-08-25 16:59:15 +02:00
Leonardo Grasso
3d61d3427e fix: correct env var name FALCO_HOSTNAME
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-08-25 16:59:15 +02:00
Leonardo Grasso
928e10f0ce fix(userspace/falco): print hostname when json formating is enabled
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-08-25 16:59:15 +02:00
Leonardo Grasso
34ad5c43fb update(userspace/engine): add support for hostname
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-08-25 16:59:15 +02:00
Jason Dellaluce
f7b662f936 update(cmake): bump libs and driver version to 6599e2efebce30a95f27739d655d53f0d5f686e4
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 16:32:15 +02:00
Jason Dellaluce
45bf4db077 fix(cmake/libs): enforce using bundled re2
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 16:32:15 +02:00
Jason Dellaluce
a8353307c7 update(cmake): bump libs and driver version to 2433c822e1c3ed55f6528c18a27373a677ce76af
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 16:32:15 +02:00
Jason Dellaluce
6db7353264 update(tests/engine): sync ast structs to new libs definitions
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 16:32:15 +02:00
Jason Dellaluce
d35dba30ed update(userspace/engine): sync ast structs to new libs definitions
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-25 16:32:15 +02:00
Ian Robertson
8872f256f6 Support multiple URLs for DRIVERS_REPO environment variable (comma separated)
Signed-off-by: Ian Robertson <ian.robertson@wpengine.com>
2022-08-24 18:25:10 +02:00
Ian Robertson
c40a216434 Identify DRIVER_VERSION and ARCH by storing in their proper directories
Signed-off-by: Ian Robertson <ian.robertson@wpengine.com>
2022-08-24 18:25:10 +02:00
Andrea Terzolo
3e3a380702 update(CI): do not check hidden files with codespell
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
2022-08-23 16:29:05 +02:00
Andrea Terzolo
5e65e195ae fix(CI): codespell should ignore ro word
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-23 16:29:05 +02:00
Andrea Terzolo
02fce93d02 update(CI): remove release branches from the push event
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-23 15:55:05 +02:00
Andrea Terzolo
6051f2de81 update(CI): build Falco to run CodeQL Analysis
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-23 15:55:05 +02:00
Andrea Terzolo
9359db904b update(CI): remove python from languages
we use python only in out tests

Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-23 15:55:05 +02:00
Andrea Terzolo
4c3b797003 update(CI): remove codeQL schedule option
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-23 15:55:05 +02:00
Andrea Terzolo
8259a2cd5f new(CI): add CodeQL security scanning to Falco.
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Chris Aniszczyk <caniszczyk@gmail.com>
2022-08-23 15:55:05 +02:00
Jason Dellaluce
e7502431a2 update(userspace/falco): move rate limiter out of falco outputs framework
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-23 15:52:05 +02:00
Jason Dellaluce
bec103de1a docs(falco.yaml): improve rate limiter config docs
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-23 15:52:05 +02:00
Jason Dellaluce
6c74aa1a29 update(userspace/falco): enable per-event-source rate limiter
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-23 15:52:05 +02:00
Jason Dellaluce
af0b624a3a fix(userspace/falco): set alert throttling config defaults
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-23 15:52:05 +02:00
Jason Dellaluce
8760f04bf2 refactor(userspace/falco): make output framework explicitly thread-safe
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-23 15:52:05 +02:00
Jason Dellaluce
88494d1412 update(falco.yaml): disable alert throttling by default
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-23 15:52:05 +02:00
Mark Stemm
8e61e46016 Add an "Ok, with warnings" overall status.
In outputs it could be confusing to see a line:

<filename>: Ok

followed by a set of warnings.

To differentiate this, add a top level status "Ok, with warnings" when
rule loading was successful but had warnings.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-19 11:55:43 +02:00
Mark Stemm
3c7b6e037a Falco engine changes to support multiple files in rule load results
The methods that throw exceptions from stringified results need to
additionally pass a rules_contents_t struct. This also meant that they
need to call the filename + content version of load_rules.

To avoid some duplicate code between the two load_rules_file methods,
move the work of opening the file into a private method
read_file(). It can throw an exception, which is passed through for
the void return method and caught + converted into a load_result error
for the method that returns a load_result.

Also, to avoid duplicate code between the void load_rules and
load_rules_file methods, add a private method interpret_load_result()
which throws an exception if the result has an error and prints
warnings otherwise if verbose is true.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-19 11:55:43 +02:00
Mark Stemm
49b7f0474f Falco application changes to support multiple files in rules results
Application changes to support multiple files when stringifying rules
results:

- In both validate_rules_files and load_rules_files, instead of
  loading each file individually and then calling load_rules(), add a
  separate step that loads all the files at once. The actual rules
  content strings are held in a vector. The map from filename to
  content (reference) points to entries in that vector.

- Both actions do the same work for this step, so put the
  implementation in a shared application template method read_files
  that works on iterators. It uses itertors because the load filenames
  are a list and the validate filenames are a vector.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-19 11:55:43 +02:00
Mark Stemm
98c1e3d3f1 Restructure rules result to properly support multiple files
The old version of rules_result assumed that all errors/warnings were
related to a single file. That was generally correct for errors, as
rules parsing always stopped at the first error, so there is only one
relevant file.

However, for warnings that was not the case. When reading multiple
files A and B, you might get a warning from file A *only* after
reading file B. For example, B might redefine a rule in such a way
that you could get unused list/macro warnings from file A.

To properly address this, make some changes to how contexts are
managed:

- Instead of creating snippets at the time the error/warning was
  generated, create snippets at the time the error/warning is
  converted into a string. This requires passing all rules contents to
  as_string()/as_json(), so define a
  falco::load_result::rules_contents_t map from filename to rules
  content (reference) and pass it in as_string/as_json(). Snippets are
  now generated from the rules content matching the filename in the
  context.
- When creating warnings/errors, there's no need to pass along the
  rules content. This is only used when converting an error into a
  string/json.

Also change snippet() to handle potentially very long lines. Instead
of always printing the entire line matching a location, print up to
snippet_width(param, with default 160 chars)/2 characters surrounding
the column from the location.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-19 11:55:43 +02:00
Melissa Kilby
0828296abc cleanup(rules): cleanup rules disabled by default - 2
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-08-17 10:55:14 +02:00
Andrea Terzolo
6971ed2dce update(PR-template): restore release-note
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
2022-08-11 10:32:21 +02:00
Andrea Terzolo
be10b1f8cb update(PR-template): add some area/kind to the template
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-11 10:32:21 +02:00
Andrea Terzolo
1efea20f57 update(PR-template): set NONE as default release-note
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-11 10:32:21 +02:00
Mark Stemm
fefd23f2f1 fix: print full rule load errors without verbose/-v
The latest released falco always prints full details on errors when
used with -r (read rules)/-V (validate rules). However #2098 changed
this to only print full details when verbose is true.

Fix the regression by always printing errors when loading
rules. Warnings will be printed only with -v.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-10 14:39:17 +02:00
Andrea Terzolo
2a640daf13 update(docs): changelog for version 0.32.2
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-09 12:13:11 +02:00
Melissa Kilby
0ab66c6fb5 update(userspace/falco): rename some buffer kernel side event drop metrics, add comments
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-08-08 10:28:42 +02:00
Melissa Kilby
1588f37788 update(userspace/falco): extend buffer kernel side event drop metrics
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-08-08 10:28:42 +02:00
incertum
66af8ad52b new(userspace/falco): extend ebpf buffer kernel side event drop metrics
Signed-off-by: incertum <melissa.kilby.oss@gmail.com>
2022-08-08 10:28:42 +02:00
Andrea Terzolo
ff247f922d chore(test/utils): remove unused script
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-05 17:56:28 +02:00
Melissa Kilby
e9ba5d751f cleanup(rules): cleanup rules disabled by default
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2022-08-05 14:50:28 +02:00
Andrea Terzolo
c81f3fc87e docs(falco-driver-loader): add some comments in falco-driver-loader
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-05 11:36:28 +02:00
Mark Stemm
a37e2252b2 Update tests to use result struct + json-based validation
Update tests that validated rules files (typically looking for
errors/warnings) to use the new result struct + json based validation:

- When validating rules files, always use json output.

- In test cases, instead of parsing stderr/stdout, use new test
  properties "validate_ok", "validate_errors",
  "validate_warnings". These parse the json output and look for
  specific tuples of (error code, error message, item type, item name)
  in the output.

- There were a few tests that were actually validation tests but using
  the -r argument to load rules. Convert them to validation tests. In
  one case, split the test into two separate tests--one for
  validation, one ensuring that the rule doesn't match anything.

- There were a couple of tests that were duplicates of existing
  validation tests, just checking for the error in a different
  way. Remove them.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-04 14:49:23 +02:00
Mark Stemm
550cdbd176 Falco application changes to support rule loading result struct
Update the load_rules_files and validate_rules_files actions to use
the new falco_engine methods that return a rules result struct. The
app action interface is the same, returning ::fatal on error,
ok()/exit() otherwise. The difference is how any warnings/errors are
obtained--from the struct instead of an exception.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-04 14:49:23 +02:00
Mark Stemm
f7f6d72ac0 Rule loader changes to support result objects
Changes to the rule loader to support result objects:

- Instead of throwing falco_exception on internal error, throw a
  rule_load_exception instead, which contains distinct
  error/message/context information.

- A context object contains a chain of location structs chaining from
  the document root to the object where the error occurred. Each
  location has a file position (as a YAML::Mark), an item
  type (e.g. "rule", "list", "exception"), and an item name (e.g. "Write
  Below Etc"). This will allow showing the exact location of an
  error (e.g. list item/exception field) while also remembering the item
  that contained it.

- All the _info structs now contain a context so errors that occur
  after yaml parsing can still point to the original location in the
  yaml file.

- rule_loader::result is an implementation of the abstract class
  defined in falco_load_result. The implementation keeps track of a
  list of errors/warnigns that used to be in the configuration object,

- Clean up compile_ methods to just throw rule_load_exceptions or
  return nothing, and ensure that all rule_load_exceptions are caught in
  compile(). When caught, errors are added to the result object.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-04 14:49:23 +02:00
Mark Stemm
cbe7cceb87 Modify rule reader to use a result struct
Modify rule reader to use a result struct for errors and warnings:

- Instead of throwing a falco_exception to pass back errors, use a
  rule_load_exception, which contains distinct error codes, messages,
  and a context that points to the location of the error.

- The static method context_yaml_get_context() has moved to a method
  of the rule_loader context object + the result as_string() method.

- As objects are parsed, create relevant context objects as reading
  drills down into the contents of a rule/list/exception. This will
  enable for specific errors in, say, the middle of an exception/list
  while remembering the object that contains it.

- Modify decode_val()/decode_seq() to always return errors as
  exceptions. Previously, it was a mix of a bool return + some
  exceptions.

- decode_val()/decode_seq() are now more consistent about returning
  distinct errors for the property not existing, the property existing
  but not being a scalar/sequence value, and not being convertable to
  the templated value.

- Combine the two nearly identical decode_seq() functions into a
  single one that uses a lambda to perform the final step of adding to
  the vector/set.

- There are some item-specific decode_xxx functions for specific
  item properties like tags, exceptions fields/comps/values, etc.
  that call the above functions.

These changes simplify the calls to decode_seq()/decode_val() as they
don't need to add their own errors when returning false. Also some
calls weren't checking the return value.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-04 14:49:23 +02:00
Mark Stemm
bb44d992ab Change filter_warning_resolver to use warning codes
Now that we have a result struct and set of warning codes, change the
filter_warning_resolver to use them. This involves populating a set of
warning codes instead of strings.

Also, the methods to format warnings into human-readable strings is
now in the falco_load_result static methods, so move the text there
and remove the methods here.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-04 14:49:23 +02:00
Mark Stemm
0066ba49ea Falco engine changes to support load_rules result class
Add new load_rules methods that return a result object instead of
throwing exceptions on error. The existing load_rules methods call the
new methods internally and continue to throw exceptions on
error/return individual values on success.

The result is returned as a unique_ptr so it can be populated while
loading rules (as a part of the configuration object) and then move()d
to the return value.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-04 14:49:23 +02:00
Mark Stemm
8497f25a43 Add a load result interface for use in new load_rules methods
Define a falco_load_result abstract class for use in new load_rules
methods. It's abstract so the implementation details in
rule_loader/rule_reader can be hidden from someone who wants to use
the API to load rules and work with a result.

The class defines a set of error codes/warning codes and has static
methods to get a short and long description of each error/warning.

There are virtual methods to access the important parts of a result:
 - successful or not
 - a string representation of the result, suitable for display to
   users. Takes a verbose argument. When verbose is true, the string is
   multi-line and has full details, including locations, item names,
   etc. When verbose is false, the string is single-line and just
   returns error codes.
 - a json representation of the result, suitable for automated
   parsing/interpretation later.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-08-04 14:49:23 +02:00
Andrea Terzolo
6b7be38e41 test: update a comment
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-04 11:47:22 +02:00
Andrea Terzolo
9d443685ea new(userspace): support SCAP_FILTERED_EVENT return code
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-08-04 11:47:22 +02:00
Jason Dellaluce
928d3225b9 fix(cmake): force using bundled valijson
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-03 15:58:21 +02:00
Jason Dellaluce
a531e8b3ed fix(test): use old event versions in trace tests
Co-authored-by: Andrea Terzolo <andrea.terzolo@polito.it>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-03 15:58:21 +02:00
Jason Dellaluce
07fde46e7c fix(test): sync plugin tests to new plugin loader errors
Co-authored-by: Andrea Terzolo <andrea.terzolo@polito.it>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-03 15:58:21 +02:00
Jason Dellaluce
136b528849 fix(tests): index old version of events in rulesets
Co-authored-by: Andrea Terzolo <andrea.terzolo@polito.it>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-03 15:58:21 +02:00
Jason Dellaluce
a46cbcffe8 fix(engine): index old version of events in rulesets
Co-authored-by: Andrea Terzolo <andrea.terzolo@polito.it>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-03 15:58:21 +02:00
Jason Dellaluce
577ba5904b update(engine): bump version to 14 and update fields checksum
Co-authored-by: Andrea Terzolo <andrea.terzolo@polito.it>
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-03 15:58:21 +02:00
Jason Dellaluce
1b8c8a86ec update(cmake): bump libs version to b4c198773bf05486e122f6d3f7f63be125242413
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-03 15:58:21 +02:00
Jason Dellaluce
7317d80dd8 update(cmake): bump driver version to b4c198773bf05486e122f6d3f7f63be125242413
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-08-03 15:58:21 +02:00
Andrea Terzolo
c8bc5758c3 new(userspace): print architecture information
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-07-31 19:57:29 +02:00
Federico Di Pierro
ae43f30b0d fix(ci): fixed docker manifest circleci.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-07-29 12:29:02 +02:00
Federico Di Pierro
fb579615a3 cleanup(ci): natively builds docker images for x86_64 and arm64 and then use docker manifest to combine them.
This allows for native-speed build of docker images, reducing CI time spent in
docker buildx qemu cross build for arm64 up to 10x.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-07-29 10:11:02 +02:00
Andrea Terzolo
b759e77fda new(userspace): print if the BPF probe is enabled
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-07-28 12:25:57 +02:00
Andrea Terzolo
74b6186f7d new(userspace): print enabled sources when falco starts
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-07-28 12:25:57 +02:00
Mark Stemm
baf5540c30 Remove required_engine_version from falco engine load_rules APIs
The only use of it was to include in --support output, which is
redundant as the support output already includes the full contents of
each rules file.

Additionally, it wasn't even being updated after the switch from lua
rules loading to c++ rules
loading (https://github.com/falcosecurity/falco/pull/1966/ or
surrounding PRs).

This will simplify follow-on changes to add a real "result" to rules
loading methods, as there will be fewer API variants to support.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-07-25 17:57:42 +02:00
Eric Engberg
c3ddd7d5f1 fix: added arch to bpf download url
Signed-off-by: Eric Engberg <eric.engberg@hardrockdigital.com>
2022-07-22 11:06:07 +02:00
Stefano
b378c3a77d Add darryk10 as rules OWNERS as reviewer
Signed-off-by: darryk10 <stefano.chierici@sysdig.com>
2022-07-21 17:42:07 +02:00
Jason Dellaluce
0cab9ba6ed chore(OWNERS): remove duplicates in reviewers
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-07-20 10:39:56 +02:00
Jason Dellaluce
8cb6fc532f cleanup(OWNERS): remove inactive approvers
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-07-20 10:39:56 +02:00
Andrea Terzolo
35db0b4a24 cleanup(userspace): remove unused logic
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-07-14 09:58:50 +02:00
Andrea Terzolo
4136a27de1 new(userspace): add exception management
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-07-14 09:58:50 +02:00
Andrea Terzolo
e73dbd4b42 new(userspace): add current drop_pct
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Shane Lawrence <shane@lawrence.dev>
2022-07-14 09:58:50 +02:00
Andrea Terzolo
b57a2d5a5f update(userspace): introduce nlohmann json library
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-07-14 09:58:50 +02:00
Federico Di Pierro
1bf5f864bc chore(docs): updated release.md template for packages adding aarch64 packages.
Moreover, updated readme using aarch64 instead of arm64 (same that is used in the badge), and adding "-x86_64" suffix to x86 packages.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-07-13 12:01:23 +02:00
Alessandro Brucato
c40d1a5141 Update rules/falco_rules.yaml
Signed-off-by: Alessandro Brucato <alessandro.brucato@sysdig.com>
2022-07-13 11:54:23 +02:00
Alessandro Brucato
409ca4382e Update rules/falco_rules.yaml
Signed-off-by: Alessandro Brucato <alessandro.brucato@sysdig.com>

Co-authored-by: schie <77834235+darryk10@users.noreply.github.com>
2022-07-13 11:54:23 +02:00
Alessandro Brucato
a71a635b7e Update rules/falco_rules.yaml
Signed-off-by: Alessandro Brucato <alessandro.brucato@sysdig.com>

Co-authored-by: schie <77834235+darryk10@users.noreply.github.com>
2022-07-13 11:54:23 +02:00
Alessandro Brucato
07024a2e0f Update rules/falco_rules.yaml
Signed-off-by: Alessandro Brucato <alessandro.brucato@sysdig.com>

Co-authored-by: schie <77834235+darryk10@users.noreply.github.com>
2022-07-13 11:54:23 +02:00
Brucedh
6feeaee0cd Added exception to Launch Privileged Container
Signed-off-by: Alessandro Brucato <alessandro.brucato@sysdig.com>
2022-07-13 11:54:23 +02:00
Andrea Terzolo
a7153f2fd8 fix(userspace): compute the drop ratio in the right way
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: Shane Lawrence <shane@lawrence.dev>
2022-07-13 09:38:22 +02:00
Ravi Ranjan
c078f7c21d Falco Rules/Conditions Updates
Signed-off-by: Ravi Ranjan <ravi.ranjan@elastisys.com>
2022-07-12 12:08:38 +02:00
Aldo Lacuku
46f625c449 chore(engine): remove trailing colon from logs when loading rule files
Signed-off-by: Aldo Lacuku <aldo@lacuku.eu>
2022-07-12 10:40:43 +02:00
125 changed files with 7137 additions and 3316 deletions

View File

@@ -1,4 +1,2 @@
approvers:
- jonahjon
reviewers:
emeritus_approvers:
- jonahjon

View File

@@ -316,56 +316,169 @@ jobs:
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
/source/falco/scripts/publish-deb -f /build/release/falco-${FALCO_VERSION}-x86_64.deb -f /build-arm64/release/falco-${FALCO_VERSION}-aarch64.deb -r deb-dev
"build-docker-dev":
docker:
- image: alpine:3.16
steps:
- attach_workspace:
at: /
- setup_remote_docker:
version: 20.10.12
docker_layer_caching: true
- run:
name: Install deps
command: |
apk update
apk add make bash git docker docker-cli-buildx py3-pip
pip install awscli
- run:
name: Login to registries
command: |
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
- run:
name: Build and publish no-driver-dev
command: |
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
cd /source/falco
docker buildx build --push --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${FALCO_VERSION} \
-t falcosecurity/falco-no-driver:x86_64-master \
-t falcosecurity/falco:x86_64-master-slim \
-t public.ecr.aws/falcosecurity/falco-no-driver:x86_64-master \
-t public.ecr.aws/falcosecurity/falco:x86_64-master-slim \
docker/no-driver
- run:
name: Build and publish falco-dev
command: |
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
cd /source/falco
docker buildx build --push --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} \
-t falcosecurity/falco:x86_64-master \
-t public.ecr.aws/falcosecurity/falco:x86_64-master \
docker/falco
- run:
name: Build and publish falco-driver-loader-dev
command: |
cd /source/falco
docker buildx build --push --build-arg FALCO_IMAGE_TAG=x86_64-master \
-t falcosecurity/falco-driver-loader:x86_64-master \
-t public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-master \
docker/driver-loader
"build-docker-dev-arm64":
machine:
enabled: true
image: ubuntu-2004:202101-01
docker_layer_caching: true
resource_class: arm.medium
steps:
- attach_workspace:
at: /tmp
- run:
name: Install deps
command: |
sudo apt update
sudo apt install groff less python3-pip
pip install awscli
- run:
name: Login to registries
command: |
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
- run:
name: Build and publish no-driver-dev
command: |
FALCO_VERSION=$(cat /tmp/build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
cd /tmp/source-arm64/falco
docker buildx build --push --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${FALCO_VERSION} \
-t falcosecurity/falco-no-driver:aarch64-master \
-t falcosecurity/falco:aarch64-master-slim \
-t public.ecr.aws/falcosecurity/falco-no-driver:aarch64-master \
-t public.ecr.aws/falcosecurity/falco:aarch64-master-slim \
docker/no-driver
- run:
name: Build and publish falco-dev
command: |
FALCO_VERSION=$(cat /tmp/build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
cd /tmp/source-arm64/falco
docker buildx build --push --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} \
-t falcosecurity/falco:aarch64-master \
-t public.ecr.aws/falcosecurity/falco:aarch64-master \
docker/falco
- run:
name: Build and publish falco-driver-loader-dev
command: |
cd /tmp/source-arm64/falco
docker buildx build --push --build-arg FALCO_IMAGE_TAG=aarch64-master \
-t falcosecurity/falco-driver-loader:aarch64-master \
-t public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-master \
docker/driver-loader
# Publish docker packages
"publish-docker-dev":
docker:
- image: cimg/base:stable
user: root
steps:
- attach_workspace:
at: /
- checkout
- setup_remote_docker:
version: 20.10.12
- run:
name: Prepare env
name: Install deps
command: |
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker context create falco-env
docker buildx create falco-env --driver docker-container --use
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
sudo apt update
sudo apt install groff less python3-pip
pip install awscli
- run:
name: Login to aws ECR
name: Login to registries
command: |
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
- run:
name: Build and publish no-driver-dev
name: Upload no-driver-dev manifest to registries
command: |
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
docker buildx build --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${FALCO_VERSION} --platform "arm64,amd64" --push \
-t falcosecurity/falco-no-driver:master \
-t falcosecurity/falco:master-slim \
-t public.ecr.aws/falcosecurity/falco-no-driver:master \
-t public.ecr.aws/falcosecurity/falco:master-slim \
docker/no-driver
docker manifest create falcosecurity/falco-no-driver:master \
falcosecurity/falco-no-driver:aarch64-master \
falcosecurity/falco-no-driver:x86_64-master
docker manifest push falcosecurity/falco-no-driver:master
docker manifest create falcosecurity/falco:master-slim \
falcosecurity/falco:aarch64-master-slim \
falcosecurity/falco:x86_64-master-slim
docker manifest push falcosecurity/falco:master-slim
docker manifest create public.ecr.aws/falcosecurity/falco-no-driver:master \
public.ecr.aws/falcosecurity/falco-no-driver:aarch64-master \
public.ecr.aws/falcosecurity/falco-no-driver:x86_64-master
docker manifest push public.ecr.aws/falcosecurity/falco-no-driver:master
docker manifest create public.ecr.aws/falcosecurity/falco:master-slim \
public.ecr.aws/falcosecurity/falco:aarch64-master-slim \
public.ecr.aws/falcosecurity/falco:x86_64-master-slim
docker manifest push public.ecr.aws/falcosecurity/falco:master-slim
- run:
name: Build and publish dev
name: Upload falco-dev manifest to registries
command: |
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
docker buildx build --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} --platform "arm64,amd64" --push \
-t falcosecurity/falco:master \
-t public.ecr.aws/falcosecurity/falco:master \
docker/falco
docker manifest create falcosecurity/falco:master \
falcosecurity/falco:aarch64-master \
falcosecurity/falco:x86_64-master
docker manifest push falcosecurity/falco:master
docker manifest create public.ecr.aws/falcosecurity/falco:master \
public.ecr.aws/falcosecurity/falco:aarch64-master \
public.ecr.aws/falcosecurity/falco:x86_64-master
docker manifest push public.ecr.aws/falcosecurity/falco:master
- run:
name: Build and publish dev falco-driver-loader-dev
name: Upload falco-driver-loader-dev manifest to registries
command: |
docker buildx build --build-arg FALCO_IMAGE_TAG=master --platform "arm64,amd64" --push \
-t falcosecurity/falco-driver-loader:master \
-t public.ecr.aws/falcosecurity/falco-driver-loader:master \
docker/driver-loader
docker manifest create falcosecurity/falco-driver-loader:master \
falcosecurity/falco-driver-loader:aarch64-master \
falcosecurity/falco-driver-loader:x86_64-master
docker manifest push falcosecurity/falco-driver-loader:master
docker manifest create public.ecr.aws/falcosecurity/falco-driver-loader:master \
public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-master \
public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-master
docker manifest push public.ecr.aws/falcosecurity/falco-driver-loader:master
# Publish the packages
"publish-packages":
@@ -417,62 +530,222 @@ jobs:
command: |
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
/source/falco/scripts/publish-deb -f /build/release/falco-${FALCO_VERSION}-x86_64.deb -f /build-arm64/release/falco-${FALCO_VERSION}-aarch64.deb -r deb
"build-docker":
docker:
- image: alpine:3.16
steps:
- attach_workspace:
at: /
- setup_remote_docker:
version: 20.10.12
docker_layer_caching: true
- run:
name: Install deps
command: |
apk update
apk add make bash git docker docker-cli-buildx py3-pip
pip install awscli
- run:
name: Login to registries
command: |
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
- run:
name: Build and publish no-driver
command: |
cd /source/falco
docker buildx build --push --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${CIRCLE_TAG} \
-t "falcosecurity/falco-no-driver:x86_64-${CIRCLE_TAG}" \
-t falcosecurity/falco-no-driver:x86_64-latest \
-t "falcosecurity/falco:x86_64-${CIRCLE_TAG}-slim" \
-t "falcosecurity/falco:x86_64-latest-slim" \
-t "public.ecr.aws/falcosecurity/falco-no-driver:x86_64-${CIRCLE_TAG}" \
-t "public.ecr.aws/falcosecurity/falco-no-driver:x86_64-latest" \
-t "public.ecr.aws/falcosecurity/falco:x86_64-${CIRCLE_TAG}-slim" \
-t "public.ecr.aws/falcosecurity/falco:x86_64-latest-slim" \
docker/no-driver
- run:
name: Build and publish falco
command: |
cd /source/falco
docker buildx build --push --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${CIRCLE_TAG} \
-t "falcosecurity/falco:x86_64-${CIRCLE_TAG}" \
-t "falcosecurity/falco:x86_64-latest" \
-t "public.ecr.aws/falcosecurity/falco:x86_64-${CIRCLE_TAG}" \
-t "public.ecr.aws/falcosecurity/falco:x86_64-latest" \
docker/falco
- run:
name: Build and publish falco-driver-loader
command: |
cd /source/falco
docker buildx build --push --build-arg FALCO_IMAGE_TAG=x86_64-${CIRCLE_TAG} \
-t "falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}" \
-t "falcosecurity/falco-driver-loader:x86_64-latest" \
-t "public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}" \
-t "public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-latest" \
docker/driver-loader
"build-docker-arm64":
machine:
enabled: true
image: ubuntu-2004:202101-01
docker_layer_caching: true
resource_class: arm.medium
steps:
- attach_workspace:
at: /tmp
- run:
name: Install deps
command: |
sudo apt update
sudo apt install groff less python3-pip
pip install awscli
- run:
name: Login to registries
command: |
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
- run:
name: Build and publish no-driver
command: |
cd /tmp/source-arm64/falco
docker buildx build --push --build-arg VERSION_BUCKET=bin --build-arg FALCO_VERSION=${CIRCLE_TAG} \
-t falcosecurity/falco-no-driver:aarch64-${CIRCLE_TAG} \
-t falcosecurity/falco-no-driver:aarch64-latest \
-t falcosecurity/falco:aarch64-${CIRCLE_TAG}-slim \
-t "falcosecurity/falco:aarch64-latest-slim" \
-t public.ecr.aws/falcosecurity/falco-no-driver:aarch64-${CIRCLE_TAG} \
-t "public.ecr.aws/falcosecurity/falco-no-driver:aarch64-latest" \
-t public.ecr.aws/falcosecurity/falco:aarch64-${CIRCLE_TAG}-slim \
-t "public.ecr.aws/falcosecurity/falco:aarch64-latest-slim" \
docker/no-driver
- run:
name: Build and publish falco
command: |
cd /tmp/source-arm64/falco
docker buildx build --push --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${CIRCLE_TAG} \
-t "falcosecurity/falco:aarch64-${CIRCLE_TAG}" \
-t "falcosecurity/falco:aarch64-latest" \
-t "public.ecr.aws/falcosecurity/falco:aarch64-${CIRCLE_TAG}" \
-t "public.ecr.aws/falcosecurity/falco:aarch64-latest" \
docker/falco
- run:
name: Build and publish falco-driver-loader
command: |
cd /tmp/source-arm64/falco
docker buildx build --push --build-arg FALCO_IMAGE_TAG=aarch64-${CIRCLE_TAG} \
-t "falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG}" \
-t "falcosecurity/falco-driver-loader:aarch64-latest" \
-t "public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG}" \
-t "public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-latest" \
docker/driver-loader
# Publish docker packages
"publish-docker":
docker:
- image: cimg/base:stable
user: root
steps:
- attach_workspace:
at: /
- checkout
- setup_remote_docker:
version: 20.10.12
- run:
name: Prepare env
name: Install deps
command: |
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker context create falco-env
docker buildx create falco-env --driver docker-container --use
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
sudo apt update
sudo apt install groff less python3-pip
pip install awscli
- run:
name: Login to aws ECR
name: Login to registries
command: |
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
- run:
name: Build and publish no-driver
name: Upload no-driver manifest to registries
command: |
docker buildx build --build-arg VERSION_BUCKET=bin --build-arg FALCO_VERSION=${CIRCLE_TAG} --platform "arm64,amd64" --push \
-t "falcosecurity/falco-no-driver:${CIRCLE_TAG}" \
-t falcosecurity/falco-no-driver:latest \
-t "falcosecurity/falco:${CIRCLE_TAG}-slim" \
-t "falcosecurity/falco:latest-slim" \
-t "public.ecr.aws/falcosecurity/falco-no-driver:${CIRCLE_TAG}" \
-t "public.ecr.aws/falcosecurity/falco-no-driver:latest" \
-t "public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}-slim" \
-t "public.ecr.aws/falcosecurity/falco:latest-slim" \
docker/no-driver
docker manifest create falcosecurity/falco-no-driver:${CIRCLE_TAG} \
falcosecurity/falco-no-driver:aarch64-${CIRCLE_TAG} \
falcosecurity/falco-no-driver:x86_64-${CIRCLE_TAG}
docker manifest push falcosecurity/falco-no-driver:${CIRCLE_TAG}
docker manifest create falcosecurity/falco-no-driver:latest \
falcosecurity/falco-no-driver:aarch64-latest \
falcosecurity/falco-no-driver:x86_64-latest
docker manifest push falcosecurity/falco-no-driver:latest
docker manifest create falcosecurity/falco:${CIRCLE_TAG}-slim \
falcosecurity/falco:aarch64-${CIRCLE_TAG}-slim \
falcosecurity/falco:x86_64-${CIRCLE_TAG}-slim
docker manifest push falcosecurity/falco:${CIRCLE_TAG}-slim
docker manifest create falcosecurity/falco:latest-slim \
falcosecurity/falco:aarch64-latest-slim \
falcosecurity/falco:x86_64-latest-slim
docker manifest push falcosecurity/falco:latest-slim
docker manifest create public.ecr.aws/falcosecurity/falco-no-driver:${CIRCLE_TAG} \
public.ecr.aws/falcosecurity/falco-no-driver:aarch64-${CIRCLE_TAG} \
public.ecr.aws/falcosecurity/falco-no-driver:x86_64-${CIRCLE_TAG}
docker manifest push public.ecr.aws/falcosecurity/falco-no-driver:${CIRCLE_TAG}
docker manifest create public.ecr.aws/falcosecurity/falco-no-driver:latest \
public.ecr.aws/falcosecurity/falco-no-driver:aarch64-latest \
public.ecr.aws/falcosecurity/falco-no-driver:x86_64-latest
docker manifest push public.ecr.aws/falcosecurity/falco-no-driver:latest
docker manifest create public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}-slim \
public.ecr.aws/falcosecurity/falco:aarch64-${CIRCLE_TAG}-slim \
public.ecr.aws/falcosecurity/falco:x86_64-${CIRCLE_TAG}-slim
docker manifest push public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}-slim
docker manifest create public.ecr.aws/falcosecurity/falco:latest-slim \
public.ecr.aws/falcosecurity/falco:aarch64-latest-slim \
public.ecr.aws/falcosecurity/falco:x86_64-latest-slim
docker manifest push public.ecr.aws/falcosecurity/falco:latest-slim
- run:
name: Build and publish falco
name: Upload falco manifest to registries
command: |
docker buildx build --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} --platform "arm64,amd64" --push \
-t "falcosecurity/falco:${CIRCLE_TAG}" \
-t "falcosecurity/falco:latest" \
-t "public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}" \
-t "public.ecr.aws/falcosecurity/falco:latest" \
docker/falco
docker manifest create falcosecurity/falco:${CIRCLE_TAG} \
falcosecurity/falco:aarch64-${CIRCLE_TAG} \
falcosecurity/falco:x86_64-${CIRCLE_TAG}
docker manifest push falcosecurity/falco:${CIRCLE_TAG}
docker manifest create falcosecurity/falco:latest \
falcosecurity/falco:aarch64-latest \
falcosecurity/falco:x86_64-latest
docker manifest push falcosecurity/falco:latest
docker manifest create public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG} \
public.ecr.aws/falcosecurity/falco:aarch64-${CIRCLE_TAG} \
public.ecr.aws/falcosecurity/falco:x86_64-${CIRCLE_TAG}
docker manifest push public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}
docker manifest create public.ecr.aws/falcosecurity/falco:latest \
public.ecr.aws/falcosecurity/falco:aarch64-latest \
public.ecr.aws/falcosecurity/falco:x86_64-latest
docker manifest push public.ecr.aws/falcosecurity/falco:latest
- run:
name: Build and publish falco-driver-loader
name: Upload falco-driver-loader manifest to registries
command: |
docker buildx build --build-arg FALCO_IMAGE_TAG=${CIRCLE_TAG} --platform "arm64,amd64" --push \
-t "falcosecurity/falco-driver-loader:${CIRCLE_TAG}" \
-t "falcosecurity/falco-driver-loader:latest" \
-t "public.ecr.aws/falcosecurity/falco-driver-loader:${CIRCLE_TAG}" \
-t "public.ecr.aws/falcosecurity/falco-driver-loader:latest" \
docker/driver-loader
docker manifest create falcosecurity/falco-driver-loader:${CIRCLE_TAG} \
falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG} \
falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}
docker manifest push falcosecurity/falco-driver-loader:${CIRCLE_TAG}
docker manifest create falcosecurity/falco-driver-loader:latest \
falcosecurity/falco-driver-loader:aarch64-latest \
falcosecurity/falco-driver-loader:x86_64-latest
docker manifest push falcosecurity/falco-driver-loader:latest
docker manifest create public.ecr.aws/falcosecurity/falco-driver-loader:${CIRCLE_TAG} \
public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG} \
public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}
docker manifest push public.ecr.aws/falcosecurity/falco-driver-loader:${CIRCLE_TAG}
docker manifest create public.ecr.aws/falcosecurity/falco-driver-loader:latest \
public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-latest \
public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-latest
docker manifest push public.ecr.aws/falcosecurity/falco-driver-loader:latest
workflows:
version: 2.1
@@ -481,6 +754,7 @@ workflows:
- "build-musl"
- "build-arm64"
- "build-centos7"
- "quality-static-analysis"
- "tests-integration":
requires:
- "build-centos7"
@@ -527,7 +801,7 @@ workflows:
requires:
- "tests-integration"
- "tests-integration-arm64"
- "publish-docker-dev":
- "build-docker-dev":
context:
- falco
- test-infra
@@ -540,6 +814,31 @@ workflows:
- "publish-packages-dev"
- "publish-packages-deb-dev"
- "tests-driver-loader-integration"
- "build-docker-dev-arm64":
context:
- falco
- test-infra
filters:
tags:
ignore: /.*/
branches:
only: master
requires:
- "publish-packages-dev"
- "publish-packages-deb-dev"
- "tests-driver-loader-integration"
- "publish-docker-dev":
context:
- falco
- test-infra
filters:
tags:
ignore: /.*/
branches:
only: master
requires:
- "build-docker-dev"
- "build-docker-dev-arm64"
# - "quality/static-analysis" # This is temporarily disabled: https://github.com/falcosecurity/falco/issues/1526
release:
jobs:
@@ -595,7 +894,7 @@ workflows:
only: /.*/
branches:
ignore: /.*/
- "publish-docker":
- "build-docker":
context:
- falco
- test-infra
@@ -607,3 +906,27 @@ workflows:
only: /.*/
branches:
ignore: /.*/
- "build-docker-arm64":
context:
- falco
- test-infra
requires:
- "publish-packages"
- "publish-packages-deb"
filters:
tags:
only: /.*/
branches:
ignore: /.*/
- "publish-docker":
context:
- falco
- test-infra
requires:
- "build-docker"
- "build-docker-arm64"
filters:
tags:
only: /.*/
branches:
ignore: /.*/

View File

@@ -1,3 +1,4 @@
aks
creat
chage
ro

View File

@@ -22,6 +22,8 @@
> /kind feature
> /kind release
> If contributing rules or changes to rules, please make sure to also uncomment one of the following line:
> /kind rule-update
@@ -46,6 +48,8 @@ Please remove the leading whitespace before the `/kind <>` you uncommented.
> /area proposals
> /area CI
<!--
Please remove the leading whitespace before the `/area <>` you uncommented.
-->

75
.github/workflows/codeql.yaml vendored Normal file
View File

@@ -0,0 +1,75 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-20.04
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
- name: Update base image
run: sudo apt update -y
- name: Install build dependencies
run: sudo DEBIAN_FRONTEND=noninteractive apt install libssl-dev libyaml-dev libc-ares-dev libprotobuf-dev protobuf-compiler libjq-dev libyaml-cpp-dev libgrpc++-dev protobuf-compiler-grpc rpm libelf-dev cmake build-essential libcurl4-openssl-dev linux-headers-$(uname -r) clang llvm git -y
- name: Prepare project
run: |
mkdir build
pushd build
cmake -DBUILD_BPF=On ..
popd
- name: Build
run: |
pushd build
KERNELDIR=/lib/modules/$(uname -r)/build make -j4 all
popd
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@@ -11,4 +11,4 @@ jobs:
skip: .git
ignore_words_file: .codespellignore
check_filenames: true
check_hidden: true
check_hidden: false

View File

@@ -76,6 +76,8 @@ This is a list of production adopters of Falco (in alphabetical order):
* [SysFlow](https://sysflow.io) SysFlow is a cloud-native system telemetry framework that focuses on data abstraction, behavioral analytics, and noise reduction. At its core, SysFlow exposes a compact open telemetry format that records workload behaviors by connecting event and flow representations of process control flows, file interactions, and network communications. The resulting abstraction encodes a graph structure that enables provenance reasoning on host and container environments, and fast retrieval of security-relevant information.
* [StackRox](https://stackrox.io) is the industrys first Kubernetes-native security platform enabling organizations to build, deploy, and run cloud-native applications securely. The platform works with Kubernetes environments and integrates with DevOps and security tools, enabling teams to operationalize and secure their supply chain, infrastructure, and workloads. StackRox aims to harness containerized applications development speed while giving operations and security teams greater context and risk profiling. StackRox leverages cloud-native principles and declarative artifacts to automate DevSecOps best practices.
## Adding a name
If you would like to add your name to this file, submit a pull request with your change.

View File

@@ -1,5 +1,17 @@
# Change Log
## v0.32.2
Released on 2022-08-09
### Major Changes
### Bug Fixes
* fix: Added ARCH to bpf download URL [[#2142](https://github.com/falcosecurity/falco/pull/2142)] - [@eric-engberg](https://github.com/eric-engberg)
## v0.32.1
Released on 2022-07-11

View File

@@ -27,6 +27,14 @@ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_SYSTEM_NAME MATCHES "Linux
endif()
endif()
# Modern BPF is not supported on not Linux systems and in MINIMAL_BUILD
if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD)
option(BUILD_FALCO_MODERN_BPF "Build modern BPF support for Falco" OFF)
if(BUILD_FALCO_MODERN_BPF)
add_definitions(-DHAS_MODERN_BPF)
endif()
endif()
# We shouldn't need to set this, see https://gitlab.kitware.com/cmake/cmake/-/issues/16419
option(EP_UPDATE_DISCONNECTED "ExternalProject update disconnected" OFF)
if (${EP_UPDATE_DISCONNECTED})
@@ -56,16 +64,19 @@ if(NOT DEFINED FALCO_ETC_DIR)
set(FALCO_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/falco")
endif()
if(NOT DRAIOS_DEBUG_FLAGS)
set(DRAIOS_DEBUG_FLAGS "-D_DEBUG")
# This will be used to print the architecture for which Falco is compiled.
set(FALCO_TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR})
if(NOT FALCO_EXTRA_DEBUG_FLAGS)
set(FALCO_EXTRA_DEBUG_FLAGS "-D_DEBUG")
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
if(CMAKE_BUILD_TYPE STREQUAL "debug")
set(KBUILD_FLAGS "${DRAIOS_DEBUG_FLAGS} ${DRAIOS_FEATURE_FLAGS}")
set(KBUILD_FLAGS "${FALCO_EXTRA_DEBUG_FLAGS} ${FALCO_EXTRA_FEATURE_FLAGS}")
else()
set(CMAKE_BUILD_TYPE "release")
set(KBUILD_FLAGS "${DRAIOS_FEATURE_FLAGS}")
set(KBUILD_FLAGS "${FALCO_EXTRA_FEATURE_FLAGS}")
add_definitions(-DBUILD_TYPE_RELEASE)
endif()
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
@@ -86,7 +97,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "release")
set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -D_FORTIFY_SOURCE=2")
endif()
set(CMAKE_COMMON_FLAGS "${FALCO_SECURITY_FLAGS} -Wall -ggdb ${DRAIOS_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
set(CMAKE_COMMON_FLAGS "${FALCO_SECURITY_FLAGS} -Wall -ggdb ${FALCO_EXTRA_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
if(BUILD_WARNINGS_AS_ERRORS)
set(CMAKE_SUPPRESSED_WARNINGS
@@ -98,8 +109,8 @@ endif()
set(CMAKE_C_FLAGS "${CMAKE_COMMON_FLAGS}")
set(CMAKE_CXX_FLAGS "--std=c++0x ${CMAKE_COMMON_FLAGS} -Wno-class-memaccess")
set(CMAKE_C_FLAGS_DEBUG "${DRAIOS_DEBUG_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${DRAIOS_DEBUG_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG")
@@ -111,6 +122,11 @@ set(DRIVER_NAME "falco")
set(DRIVER_DEVICE_NAME "falco")
set(DRIVERS_REPO "https://download.falco.org/driver")
# If no path is provided, try to search the BPF probe in: `home/.falco/falco-bpf.o`
# This is the same fallback that we had in the libraries: `SCAP_PROBE_BPF_FILEPATH`.
set(FALCO_PROBE_BPF_FILEPATH ".${DRIVER_NAME}/${DRIVER_NAME}-bpf.o")
add_definitions(-DFALCO_PROBE_BPF_FILEPATH="${FALCO_PROBE_BPF_FILEPATH}")
if(NOT DEFINED FALCO_COMPONENT_NAME)
set(FALCO_COMPONENT_NAME "${CMAKE_PROJECT_NAME}")
endif()

12
OWNERS
View File

@@ -1,18 +1,12 @@
approvers:
- fntlnz
- kris-nova
- leodido
- mstemm
- leogr
- jasondellaluce
- fededp
- andreagit97
reviewers:
- fntlnz
- kaizhe
emeritus_approvers:
- fntlnz
- kris-nova
- leodido
- mfdii
- mstemm
- leogr
- jasondellaluce
- fededp

View File

@@ -7,7 +7,7 @@
Want to talk? Join us on the [#falco](https://kubernetes.slack.com/messages/falco) channel in the [Kubernetes Slack](https://slack.k8s.io).
### Latest releases
## Latest releases
Read the [change log](CHANGELOG.md).
@@ -51,12 +51,12 @@ Notes:
| | development | stable |
|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| rpm | [![rpm-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Frpm-dev%2Ffalco-%26delimiter=aarch64)][1] | [![rpm](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Frpm%2Ffalco-%26delimiter=aarch64)][2] |
| deb | [![deb-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fdeb-dev%2Fstable%2Ffalco-%26delimiter=aarch64)][3] | [![deb](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fdeb%2Fstable%2Ffalco-%26delimiter=aarch64)][4] |
| binary | [![bin-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%20%22falco-%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fbin-dev%2Fx86_64%2Ffalco-)][5] | [![bin](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%20%22falco-%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fbin%2Fx86_64%2Ffalco-)][6] |
| rpm-arm64 | [![rpm-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Frpm-dev%2Ffalco-%26delimiter=x86_64)][1] | [![rpm](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Frpm%2Ffalco-%26delimiter=x86_64)][2] |
| deb-arm64 | [![deb-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fdeb-dev%2Fstable%2Ffalco-%26delimiter=x86_64)][3] | [![deb](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fdeb%2Fstable%2Ffalco-%26delimiter=x86_64)][4] |
| binary-arm64 | [![bin-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%20%22falco-%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fbin-dev%2Faarch64%2Ffalco-)][7] | [![bin](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%20%22falco-%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fbin%2Faarch64%2Ffalco-)][8] |
| rpm-x86_64 | [![rpm-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Frpm-dev%2Ffalco-%26delimiter=aarch64)][1] | [![rpm](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Frpm%2Ffalco-%26delimiter=aarch64)][2] |
| deb-x86_64 | [![deb-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fdeb-dev%2Fstable%2Ffalco-%26delimiter=aarch64)][3] | [![deb](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fdeb%2Fstable%2Ffalco-%26delimiter=aarch64)][4] |
| binary-x86_64 | [![bin-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%20%22falco-%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fbin-dev%2Fx86_64%2Ffalco-)][5] | [![bin](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%20%22falco-%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fbin%2Fx86_64%2Ffalco-)][6] |
| rpm-aarch64 | [![rpm-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Frpm-dev%2Ffalco-%26delimiter=x86_64)][1] | [![rpm](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Frpm%2Ffalco-%26delimiter=x86_64)][2] |
| deb-aarch64 | [![deb-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fdeb-dev%2Fstable%2Ffalco-%26delimiter=x86_64)][3] | [![deb](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-before%28substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%22falco-%22%29%2C%22.asc%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fdeb%2Fstable%2Ffalco-%26delimiter=x86_64)][4] |
| binary-aarch64 | [![bin-dev](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%20%22falco-%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fbin-dev%2Faarch64%2Ffalco-)][7] | [![bin](https://img.shields.io/badge/dynamic/xml?color=%2300aec7&style=flat-square&label=Falco&query=substring-after%28%28%2F%2A%5Bname%28%29%3D%27ListBucketResult%27%5D%2F%2A%5Bname%28%29%3D%27Contents%27%5D%29%5Blast%28%29%5D%2F%2A%5Bname%28%29%3D%27Key%27%5D%2C%20%22falco-%22%29&url=https%3A%2F%2Ffalco-distribution.s3-eu-west-1.amazonaws.com%2F%3Fprefix%3Dpackages%2Fbin%2Faarch64%2Ffalco-)][8] |
---
@@ -66,7 +66,7 @@ Falco can also be extended to other data sources by using plugins.
Falco has a rich set of security rules specifically built for Kubernetes, Linux, and cloud-native.
If a rule is violated in a system, Falco will send an alert notifying the user of the violation and its severity.
### What can Falco detect?
## What can Falco detect?
Falco can detect and alert on any behavior that involves making Linux system calls.
Falco alerts can be triggered by the use of specific system calls, their arguments, and by properties of the calling process.
@@ -80,11 +80,11 @@ For example, Falco can easily detect incidents including but not limited to:
- A standard system binary, such as `ls`, is making an outbound network connection.
- A privileged pod is started in a Kubernetes cluster.
### Installing Falco
## Installing Falco
If you would like to run Falco in **production** please adhere to the [official installation guide](https://falco.org/docs/getting-started/installation/).
##### Kubernetes
### Kubernetes
| Tool | Link | Note |
|----------|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
@@ -93,22 +93,20 @@ If you would like to run Falco in **production** please adhere to the [official
| Kind | [Tutorial](https://falco.org/docs/getting-started/third-party/#kind) | Running Falco with kind requires a driver on the host system. |
| GKE | [Tutorial](https://falco.org/docs/getting-started/third-party/#gke) | We suggest using the eBPF driver for running Falco on GKE. |
### Developing
## Developing
Falco is designed to be extensible such that it can be built into cloud-native applications and infrastructure.
Falco has a [gRPC](https://falco.org/docs/grpc/) endpoint and an API defined in [protobuf](https://github.com/falcosecurity/falco/blob/master/userspace/falco/outputs.proto).
The Falco Project supports various SDKs for this endpoint.
##### SDKs
### SDKs
| Language | Repository |
|----------|---------------------------------------------------------|
| Go | [client-go](https://github.com/falcosecurity/client-go) |
| Rust | [client-rs](https://github.com/falcosecurity/client-rs) |
| Python | [client-py](https://github.com/falcosecurity/client-py) |
### Plugins
## Plugins
Falco comes with a [plugin framework](https://falco.org/docs/plugins/) that extends it to potentially any cloud detection scenario. Plugins are shared libraries that conform to a documented API and allow for:
@@ -118,18 +116,18 @@ Falco comes with a [plugin framework](https://falco.org/docs/plugins/) that exte
The Falco Project maintains [various plugins](https://github.com/falcosecurity/plugins) and provides SDKs for plugin development.
##### SDKs
### SDKs
| Language | Repository |
|----------|-------------------------------------------------------------------------------|
| Go | [falcosecurity/plugin-sdk-go](https://github.com/falcosecurity/plugin-sdk-go) |
### Documentation
## Documentation
The [Official Documentation](https://falco.org/docs/) is the best resource to learn about Falco.
### Join the Community
## Join the Community
To get involved with The Falco Project please visit [the community repository](https://github.com/falcosecurity/community) to find more.
@@ -139,23 +137,36 @@ How to reach out?
- [Join the Falco mailing list](https://lists.cncf.io/g/cncf-falco-dev)
- [Read the Falco documentation](https://falco.org/docs/)
## How to contribute
### Contributing
See the [CONTRIBUTING.md](https://github.com/falcosecurity/.github/blob/master/CONTRIBUTING.md).
### Security Audit
See the [contributing guide](https://github.com/falcosecurity/.github/blob/main/CONTRIBUTING.md) and the [code of conduct](https://github.com/falcosecurity/evolution/CODE_OF_CONDUCT.md).
## Security Audit
A third party security audit was performed by Cure53, you can see the full report [here](./audits/SECURITY_AUDIT_2019_07.pdf).
### Reporting security vulnerabilities
## Reporting security vulnerabilities
Please report security vulnerabilities following the community process documented [here](https://github.com/falcosecurity/.github/blob/master/SECURITY.md).
Please report security vulnerabilities following the community process documented [here](https://github.com/falcosecurity/.github/blob/main/SECURITY.md).
### License Terms
## License
Falco is licensed to you under the [Apache 2.0](./COPYING) open source license.
## Project Evolution
The [falcosecurity/evolution](https://github.com/falcosecurity/evolution) repository is the official space for the community to work together, discuss ideas, and document processes. It is also a place to make decisions. Check it out to find more helpful resources.
## Resources
- [Governance](https://github.com/falcosecurity/evolution/blob/main/GOVERNANCE.md)
- [Code Of Conduct](https://github.com/falcosecurity/evolution/blob/main/CODE_OF_CONDUCT.md)
- [Maintainers Guidelines](https://github.com/falcosecurity/evolution/blob/main/MAINTAINERS_GUIDELINES.md)
- [Maintainers List](https://github.com/falcosecurity/evolution/blob/main/MAINTAINERS.md)
- [Repositories Guidelines](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md)
- [Repositories List](https://github.com/falcosecurity/evolution/blob/main/README.md#repositories)
- [Adopters List](https://github.com/falcosecurity/falco/blob/master/ADOPTERS.md)
[1]: https://download.falco.org/?prefix=packages/rpm-dev/
[2]: https://download.falco.org/?prefix=packages/rpm/
@@ -164,4 +175,4 @@ Falco is licensed to you under the [Apache 2.0](./COPYING) open source license.
[5]: https://download.falco.org/?prefix=packages/bin-dev/x86_64/
[6]: https://download.falco.org/?prefix=packages/bin/x86_64/
[7]: https://download.falco.org/?prefix=packages/bin-dev/aarch64/
[8]: https://download.falco.org/?prefix=packages/bin/aarch64/
[8]: https://download.falco.org/?prefix=packages/bin/aarch64/

View File

@@ -68,9 +68,12 @@ Now assume `x.y.z` is the new version.
| Packages | Download |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| rpm | [![rpm](https://img.shields.io/badge/Falco-x.y.z-%2300aec7?style=flat-square)](https://download.falco.org/packages/rpm/falco-x.y.z-x86_64.rpm) |
| deb | [![deb](https://img.shields.io/badge/Falco-x.y.z-%2300aec7?style=flat-square)](https://download.falco.org/packages/deb/stable/falco-x.y.z-x86_64.deb) |
| tgz | [![tgz](https://img.shields.io/badge/Falco-x.y.z-%2300aec7?style=flat-square)](https://download.falco.org/packages/bin/x86_64/falco-x.y.z-x86_64.tar.gz) |
| rpm-x86_64 | [![rpm](https://img.shields.io/badge/Falco-x.y.z-%2300aec7?style=flat-square)](https://download.falco.org/packages/rpm/falco-x.y.z-x86_64.rpm) |
| deb-x86_64 | [![deb](https://img.shields.io/badge/Falco-x.y.z-%2300aec7?style=flat-square)](https://download.falco.org/packages/deb/stable/falco-x.y.z-x86_64.deb) |
| tgz-x86_64 | [![tgz](https://img.shields.io/badge/Falco-x.y.z-%2300aec7?style=flat-square)](https://download.falco.org/packages/bin/x86_64/falco-x.y.z-x86_64.tar.gz) |
| rpm-aarch64 | [![rpm](https://img.shields.io/badge/Falco-x.y.z-%2300aec7?style=flat-square)](https://download.falco.org/packages/rpm/falco-x.y.z-aarch64.rpm) |
| deb-aarch64 | [![deb](https://img.shields.io/badge/Falco-x.y.z-%2300aec7?style=flat-square)](https://download.falco.org/packages/deb/stable/falco-x.y.z-aarch64.deb) |
| tgz-aarch64 | [![tgz](https://img.shields.io/badge/Falco-x.y.z-%2300aec7?style=flat-square)](https://download.falco.org/packages/bin/aarch64/falco-x.y.z-aarch64.tar.gz) |
| Images |
| --------------------------------------------------------------------------- |

View File

@@ -26,8 +26,8 @@ else()
# In case you want to test against another driver version (or branch, or commit) just pass the variable -
# ie., `cmake -DDRIVER_VERSION=dev ..`
if(NOT DRIVER_VERSION)
set(DRIVER_VERSION "2.0.0+driver")
set(DRIVER_CHECKSUM "SHA256=e616dfe27f95670a63150339ea2484937c5ce9b7e42d176de86c3f61481ae676")
set(DRIVER_VERSION "9ec78ad55ff558f3381941111b6bf313e043b4b0")
set(DRIVER_CHECKSUM "SHA256=333a0aec05653ade6ff0dbdd057a8fe84abe32c07a22626288c2028b1ebc7d2e")
endif()
# cd /path/to/build && cmake /path/to/source
@@ -45,4 +45,4 @@ set(DRIVER_NAME "falco")
set(DRIVER_PACKAGE_NAME "falco")
set(DRIVER_COMPONENT_NAME "falco-driver")
add_subdirectory(${DRIVER_SOURCE_DIR} ${PROJECT_BINARY_DIR}/driver)
add_subdirectory(${DRIVER_SOURCE_DIR} ${PROJECT_BINARY_DIR}/driver)

View File

@@ -27,8 +27,8 @@ else()
# In case you want to test against another falcosecurity/libs version (or branch, or commit) just pass the variable -
# ie., `cmake -DFALCOSECURITY_LIBS_VERSION=dev ..`
if(NOT FALCOSECURITY_LIBS_VERSION)
set(FALCOSECURITY_LIBS_VERSION "0.7.0")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=3adc1620c0e830554a54cdd486158dc2c0c40552e113785b70fbbc99edb7d96f")
set(FALCOSECURITY_LIBS_VERSION "9ec78ad55ff558f3381941111b6bf313e043b4b0")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=333a0aec05653ade6ff0dbdd057a8fe84abe32c07a22626288c2028b1ebc7d2e")
endif()
# cd /path/to/build && cmake /path/to/source
@@ -49,7 +49,6 @@ if(MUSL_OPTIMIZED_BUILD)
add_definitions(-DMUSL_OPTIMIZED)
endif()
set(SCAP_BPF_PROBE_ENV_VAR_NAME "FALCO_BPF_PROBE")
set(SCAP_HOST_ROOT_ENV_VAR_NAME "HOST_ROOT")
if(NOT LIBSCAP_DIR)
@@ -61,6 +60,9 @@ set(LIBSINSP_DIR "${FALCOSECURITY_LIBS_SOURCE_DIR}")
# configure gVisor support
set(BUILD_LIBSCAP_GVISOR ${BUILD_FALCO_GVISOR} CACHE BOOL "")
# configure modern BPF support
set(BUILD_LIBSCAP_MODERN_BPF ${BUILD_FALCO_MODERN_BPF} CACHE BOOL "")
# explicitly disable the tests/examples of this dependency
set(CREATE_TEST_TARGETS OFF CACHE BOOL "")
set(BUILD_LIBSCAP_EXAMPLES OFF CACHE BOOL "")
@@ -68,6 +70,8 @@ set(BUILD_LIBSCAP_EXAMPLES OFF CACHE BOOL "")
set(USE_BUNDLED_TBB ON CACHE BOOL "")
set(USE_BUNDLED_B64 ON CACHE BOOL "")
set(USE_BUNDLED_JSONCPP ON CACHE BOOL "")
set(USE_BUNDLED_VALIJSON ON CACHE BOOL "")
set(USE_BUNDLED_RE2 ON CACHE BOOL "")
list(APPEND CMAKE_MODULE_PATH "${FALCOSECURITY_LIBS_SOURCE_DIR}/cmake/modules")
@@ -83,4 +87,4 @@ endif()
include(driver)
include(libscap)
include(libsinsp)
include(libsinsp)

View File

@@ -19,11 +19,11 @@ if(NOT DEFINED PLUGINS_COMPONENT_NAME)
set(PLUGINS_COMPONENT_NAME "${CMAKE_PROJECT_NAME}-plugins")
endif()
set(PLUGIN_K8S_AUDIT_VERSION "0.3.0")
set(PLUGIN_K8S_AUDIT_VERSION "0.4.0-rc1")
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
set(PLUGIN_K8S_AUDIT_HASH "214915fc2a61d147d64aaf4cb29c3fc6a513eda621dad1dfe77f2fd7099b31e1")
set(PLUGIN_K8S_AUDIT_HASH "9b77560861ae2b1539a32a542e0b282b4ae83e0a8c26aad7ecefd3e721e9eb99")
else() # aarch64
set(PLUGIN_K8S_AUDIT_HASH "d9b4610714df581043db76ecb4caf3a41aae5494cf61ab8740a3749bfac8457e")
set(PLUGIN_K8S_AUDIT_HASH "9c7de9a1213dc2e125f1ad2302818e5d34a7c95bfc67532b9d37395c60785d02")
endif()
ExternalProject_Add(
@@ -39,18 +39,18 @@ install(FILES "${PROJECT_BINARY_DIR}/k8saudit-plugin-prefix/src/k8saudit-plugin/
ExternalProject_Add(
k8saudit-rules
URL "https://download.falco.org/plugins/stable/k8saudit-rules-${PLUGIN_K8S_AUDIT_VERSION}.tar.gz"
URL_HASH "SHA256=3913a8c6095794c7de6a97a2a64953a0fa4f87caab014d11b2c8f9221eb77591"
URL_HASH "SHA256=f65982fd1c6bc12ae8db833c36127a70252464bd5983fd75c39b91d630eb7f40"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
install(FILES "${PROJECT_BINARY_DIR}/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml" DESTINATION "${FALCO_ETC_DIR}" COMPONENT "${PLUGINS_COMPONENT_NAME}")
set(PLUGIN_CLOUDTRAIL_VERSION "0.5.0")
set(PLUGIN_CLOUDTRAIL_VERSION "0.6.0-rc1")
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
set(PLUGIN_CLOUDTRAIL_HASH "ca6c0d087b37090145ef0c92f10d1dd32bb2a08c7bae83cc6fb7a1ba712f3182")
set(PLUGIN_CLOUDTRAIL_HASH "a6c6acf16f7b4acd2b836e2be514346ee15a1e5adce936bd97ab6338d16ad6f9")
else() # aarch64
set(PLUGIN_CLOUDTRAIL_HASH "f6e12d3bd16ae0f504ed2bb56d13531d15b7d55beb1b63932cbe603cff941372")
set(PLUGIN_CLOUDTRAIL_HASH "a6105cb3864a613b3488c60c723163630484bc36b2aa219fb1c730c7735fb5fa")
endif()
ExternalProject_Add(
@@ -66,18 +66,18 @@ install(FILES "${PROJECT_BINARY_DIR}/cloudtrail-plugin-prefix/src/cloudtrail-plu
ExternalProject_Add(
cloudtrail-rules
URL "https://download.falco.org/plugins/stable/cloudtrail-rules-${PLUGIN_CLOUDTRAIL_VERSION}.tar.gz"
URL_HASH "SHA256=7f88fb6b530f8ee739b65d38a36c69cdc70398576299b90118bd7324dbdb5f46"
URL_HASH "SHA256=4df7a0d56300d6077807bc205a8ab7ab3b45c495adcc209c5cca1e8da6fc93c6"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
install(FILES "${PROJECT_BINARY_DIR}/cloudtrail-rules-prefix/src/cloudtrail-rules/aws_cloudtrail_rules.yaml" DESTINATION "${FALCO_ETC_DIR}" COMPONENT "${PLUGINS_COMPONENT_NAME}")
set(PLUGIN_JSON_VERSION "0.5.0")
set(PLUGIN_JSON_VERSION "0.6.0-rc1")
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
set(PLUGIN_JSON_HASH "b422c4f08bb54ccd384a87c5922e120d5731028c87742ef657cacf936447c202")
set(PLUGIN_JSON_HASH "7969e4731e529c5a9d9895ee52ec1845d4d1889cfa3562170288bb7a593bf6b9")
else() # aarch64
set(PLUGIN_JSON_HASH "8358f04325d8a9e9675f38fae8d13a250fb132dcf6741fd0f9830e8c39f48aed")
set(PLUGIN_JSON_HASH "c19fd1b64228ff95b1dc88d441143017807aa59ba57ae868a5f7db85b93bff99")
endif()
ExternalProject_Add(

View File

@@ -25,11 +25,10 @@ else()
"--force"
"--inconclusive"
"--inline-suppr" # allows to specify suppressions directly in source code
"--project=${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json" # use the compilation database as source
"--quiet"
"--xml" # we want to generate a report
"--output-file=${CMAKE_CURRENT_BINARY_DIR}/static-analysis-reports/cppcheck/cppcheck.xml" # generate the report under the reports folder in the build folder
"-i${CMAKE_CURRENT_BINARY_DIR}"# exclude the build folder
"${CMAKE_SOURCE_DIR}"
)
endif() # CPPCHECK

View File

@@ -2,5 +2,4 @@ labels:
- area/integration
approvers:
- leogr
reviewers:
- leogr

View File

@@ -9,10 +9,10 @@ shift
# Build type can be "debug" or "release", fallbacks to "release" by default
BUILD_TYPE=$(echo "$BUILD_TYPE" | tr "[:upper:]" "[:lower:]")
DRAIOS_DEBUG_FLAGS=
FALCO_EXTRA_DEBUG_FLAGS=
case "$BUILD_TYPE" in
"debug")
DRAIOS_DEBUG_FLAGS="-D_DEBUG -DNDEBUG"
FALCO_EXTRA_DEBUG_FLAGS="-D_DEBUG -DNDEBUG"
;;
*)
BUILD_TYPE="release"
@@ -37,7 +37,7 @@ case "$CMD" in
-DBUILD_BPF="$BUILD_BPF" \
-DBUILD_WARNINGS_AS_ERRORS="$BUILD_WARNINGS_AS_ERRORS" \
-DFALCO_VERSION="$FALCO_VERSION" \
-DDRAIOS_DEBUG_FLAGS="$DRAIOS_DEBUG_FLAGS" \
-DFALCO_EXTRA_DEBUG_FLAGS="$FALCO_EXTRA_DEBUG_FLAGS" \
-DUSE_BUNDLED_DEPS=ON \
"$SOURCE_DIR/falco"
exit "$(printf '%d\n' $?)"

View File

@@ -73,7 +73,9 @@ watch_config_files: true
# time zone, as governed by /etc/localtime.
time_format_iso_8601: false
# Whether to output events in json or text
# If "true", print falco alert messages and rules file
# loading/validation results as json, which allows for easier
# consumption by downstream programs. Default is "false".
json_output: false
# When using json output, whether or not to include the "output" property
@@ -167,6 +169,61 @@ syscall_event_drops:
syscall_event_timeouts:
max_consecutives: 1000
# --- [Description]
#
# This is an index that controls the dimension of the syscall buffers.
# The syscall buffer is the shared space between Falco and its drivers where all the syscall events
# are stored.
# Falco uses a syscall buffer for every online CPU, and all these buffers share the same dimension.
# So this parameter allows you to control the size of all the buffers!
#
# --- [Usage]
#
# You can choose between different indexes: from `1` to `10` (`0` is reserved for future uses).
# Every index corresponds to a dimension in bytes:
#
# [(*), 1 MB, 2 MB, 4 MB, 8 MB, 16 MB, 32 MB, 64 MB, 128 MB, 256 MB, 512 MB]
# ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
# | | | | | | | | | | |
# 0 1 2 3 4 5 6 7 8 9 10
#
# As you can see the `0` index is reserved, while the index `1` corresponds to
# `1 MB` and so on.
#
# These dimensions in bytes derive from the fact that the buffer size must be:
# (1) a power of 2.
# (2) a multiple of your system_page_dimension.
# (3) greater than `2 * (system_page_dimension)`.
#
# According to these constraints is possible that sometimes you cannot use all the indexes, let's consider an
# example to better understand it:
# If you have a `page_size` of 1 MB the first available buffer size is 4 MB because 2 MB is exactly
# `2 * (system_page_size)` -> `2 * 1 MB`, but this is not enough we need more than `2 * (system_page_size)`!
# So from this example is clear that if you have a page size of 1 MB the first index that you can use is `3`.
#
# Please note: this is a very extreme case just to let you understand the mechanism, usually the page size is something
# like 4 KB so you have no problem at all and you can use all the indexes (from `1` to `10`).
#
# To check your system page size use the Falco `--page-size` command line option. The output on a system with a page
# size of 4096 Bytes (4 KB) should be the following:
#
# "Your system page size is: 4096 bytes."
#
# --- [Suggestions]
#
# Before the introduction of this param the buffer size was fixed to 8 MB (so index `4`, as you can see
# in the default value below).
# You can increase the buffer size when you face syscall drops. A size of 16 MB (so index `5`) can reduce
# syscall drops in production-heavy systems without noticeable impact. Very large buffers however could
# slow down the entire machine.
# On the other side you can try to reduce the buffer size to speed up the system, but this could
# increase the number of syscall drops!
# As a final remark consider that the buffer size is mapped twice in the process' virtual memory so a buffer of 8 MB
# will result in a 16 MB area in the process virtual memory.
# Please pay attention when you use this parameter and change it only if the default size doesn't fit your use case.
syscall_buf_size_preset: 4
# Falco continuously monitors outputs performance. When an output channel does not allow
# to deliver an alert within a given deadline, an error is reported indicating
# which output is blocking notifications.
@@ -185,19 +242,22 @@ syscall_event_timeouts:
output_timeout: 2000
# A throttling mechanism implemented as a token bucket limits the
# rate of falco notifications. This throttling is controlled by the following configuration
# options:
# rate of Falco notifications. One rate limiter is assigned to each event
# source, so that alerts coming from one can't influence the throttling
# mechanism of the others. This is controlled by the following options:
# - rate: the number of tokens (i.e. right to send a notification)
# gained per second. Defaults to 1.
# gained per second. When 0, the throttling mechanism is disabled.
# Defaults to 0.
# - max_burst: the maximum number of tokens outstanding. Defaults to 1000.
#
# With these defaults, falco could send up to 1000 notifications after
# an initial quiet period, and then up to 1 notification per second
# With these defaults, the throttling mechanism is disabled.
# For example, by setting rate to 1 Falco could send up to 1000 notifications
# after an initial quiet period, and then up to 1 notification per second
# afterward. It would gain the full burst back after 1000 seconds of
# no activity.
outputs:
rate: 1
rate: 0
max_burst: 1000
# Where security notifications should go.
@@ -222,9 +282,10 @@ file_output:
stdout_output:
enabled: true
# Falco contains an embedded webserver that can be used to accept K8s
# Audit Events. These config options control the behavior of that
# webserver. (By default, the webserver is enabled).
# Falco contains an embedded webserver that is used to implement an health
# endpoint for checking if Falco is up and running. These config options control
# the behavior of that webserver. By default, the webserver is enabled and
# the endpoint is /healthz.
#
# The ssl_certificate is a combination SSL Certificate and corresponding
# key contained in a single file. You can generate a key/cert as follows:
@@ -232,11 +293,10 @@ stdout_output:
# $ openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
# $ cat certificate.pem key.pem > falco.pem
# $ sudo cp falco.pem /etc/falco/falco.pem
#
# It also exposes a healthy endpoint that can be used to check if Falco is up and running
# By default the endpoint is /healthz
webserver:
enabled: true
# when threadiness is 0, Falco automatically guesses it depending on the number of online cores
threadiness: 0
listen_port: 8765
k8s_healthz_endpoint: /healthz
ssl_enabled: false
@@ -291,7 +351,7 @@ http_output:
# gRPC server using an unix socket
grpc:
enabled: false
bind_address: "unix:///var/run/falco.sock"
bind_address: "unix:///run/falco/falco.sock"
# when threadiness is 0, Falco automatically guesses it depending on the number of online cores
threadiness: 0

View File

@@ -5,7 +5,6 @@ reviewers:
- fntlnz
- mfdii
- kaizhe
- mstemm
- darryk10
labels:
- area/rules

File diff suppressed because it is too large Load Diff

View File

@@ -41,3 +41,34 @@ case "$1" in
fi
;;
esac
# Based off what debhelper dh_systemd_enable/13.3.4 would have added
# ref: https://www.debian.org/doc/manuals/debmake-doc/ch05.en.html#debhelper
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
# This will only remove masks created by d-s-h on package removal.
deb-systemd-helper unmask 'falco.service' >/dev/null || true
# was-enabled defaults to true, so new installations run enable.
if deb-systemd-helper --quiet was-enabled 'falco.service'; then
# Enables the unit on first installation, creates new
# symlinks on upgrades if the unit file has changed.
deb-systemd-helper enable 'falco.service' >/dev/null || true
else
# Update the statefile to add new symlinks (if any), which need to be
# cleaned up on purge. Also remove old symlinks.
deb-systemd-helper update-state 'falco.service' >/dev/null || true
fi
fi
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
if [ -n "$2" ]; then
_dh_action=restart
else
_dh_action=start
fi
deb-systemd-invoke $_dh_action 'falco.service' >/dev/null || true
fi
fi

View File

@@ -15,3 +15,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Based off what debhelper dh_systemd_enable/13.3.4 would have added
# ref: https://www.debian.org/doc/manuals/debmake-doc/ch05.en.html#debhelper
set -e
if [ -d /run/systemd/system ] && [ "$1" = remove ]; then
systemctl --system daemon-reload >/dev/null || true
fi
if [ "$1" = "remove" ]; then
if [ -x "/usr/bin/deb-systemd-helper" ]; then
deb-systemd-helper mask 'falco.service' >/dev/null || true
fi
fi
if [ "$1" = "purge" ]; then
if [ -x "/usr/bin/deb-systemd-helper" ]; then
deb-systemd-helper purge 'falco.service' >/dev/null || true
deb-systemd-helper unmask 'falco.service' >/dev/null || true
fi
fi

View File

@@ -17,6 +17,14 @@
#
set -e
# Based off what debhelper dh_systemd_enable/13.3.4 would have added
# ref: https://www.debian.org/doc/manuals/debmake-doc/ch05.en.html#debhelper
# Currently running falco service uses the driver, so stop it before driver cleanup
if [ -d /run/systemd/system ] && [ "$1" = remove ]; then
deb-systemd-invoke stop 'falco.service' >/dev/null || true
fi
case "$1" in
remove|upgrade|deconfigure)
/usr/bin/falco-driver-loader --clean

View File

@@ -113,13 +113,17 @@ get_target_id() {
elif [ -f "${HOST_ROOT}/etc/centos-release" ]; then
# Older CentOS distros
OS_ID=centos
elif [ -f "${HOST_ROOT}/etc/VERSION" ]; then
OS_ID=minikube
else
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community"
exit 1
fi
# Overwrite the OS_ID if /etc/VERSION file is present.
# Not sure if there is a better way to detect minikube.
if [ -f "${HOST_ROOT}/etc/VERSION" ]; then
OS_ID=minikube
fi
case "${OS_ID}" in
("amzn")
if [[ $VERSION_ID == "2" ]]; then
@@ -129,8 +133,13 @@ get_target_id() {
fi
;;
("ubuntu")
if [[ $KERNEL_RELEASE == *"aws"* ]]; then
TARGET_ID="ubuntu-aws"
# Extract the flavor from the kernelrelease
# Examples:
# 5.0.0-1028-aws-5.0 -> ubuntu-aws-5.0
# 5.15.0-1009-aws -> ubuntu-aws
if [[ $KERNEL_RELEASE =~ -([a-zA-Z]+)(-.*)?$ ]];
then
TARGET_ID="ubuntu-${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
else
TARGET_ID="ubuntu-generic"
fi
@@ -139,6 +148,18 @@ get_target_id() {
KERNEL_RELEASE="${VERSION_ID}"
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
;;
("minikube")
TARGET_ID="${OS_ID}"
# Extract the minikube version. Ex. With minikube version equal to "v1.26.0-1655407986-14197" the extracted version
# will be "1.26.0"
if [[ $(cat ${HOST_ROOT}/etc/VERSION) =~ ([0-9]+(\.[0-9]+){2}) ]]; then
# kernel version for minikube is always in "1_minikubeversion" format. Ex "1_1.26.0".
KERNEL_VERSION="1_${BASH_REMATCH[1]}"
else
echo "* Unable to extract minikube version from ${HOST_ROOT}/etc/VERSION"
exit 1
fi
;;
(*)
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
;;
@@ -235,13 +256,13 @@ load_kernel_module_download() {
get_target_id
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
local URL=$(echo "${DRIVERS_REPO}/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" | sed s/+/%2B/g)
local URL=$(echo "${1}/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" | sed s/+/%2B/g)
echo "* Trying to download a prebuilt ${DRIVER_NAME} module from ${URL}"
if curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" "${URL}"; then
if curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" "${URL}"; then
echo "* Download succeeded"
chcon -t modules_object_t "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
if insmod "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}"; then
chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
if insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}"; then
echo "* Success: ${DRIVER_NAME} module found and inserted"
exit 0
else
@@ -261,6 +282,13 @@ print_clean_termination() {
echo
}
print_filename_components() {
echo " - driver name: ${DRIVER_NAME}"
echo " - target identifier: ${TARGET_ID}"
echo " - kernel release: ${KERNEL_RELEASE}"
echo " - kernel version: ${KERNEL_VERSION}"
}
clean_kernel_module() {
echo
echo "================ Cleaning phase ================"
@@ -349,16 +377,21 @@ load_kernel_module() {
get_target_id
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
echo "* Filename '${FALCO_KERNEL_MODULE_FILENAME}' is composed of:"
print_filename_components
if [ -f "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" ]; then
echo "* Found a prebuilt ${DRIVER_NAME} module at ${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}, loading it"
chcon -t modules_object_t "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
insmod "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" ]; then
echo "* Found a prebuilt ${DRIVER_NAME} module at ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}, loading it"
chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
exit $?
fi
if [ -n "$ENABLE_DOWNLOAD" ]; then
load_kernel_module_download
IFS=", " read -r -a urls <<< "${DRIVERS_REPO}"
for url in "${urls[@]}"; do
load_kernel_module_download $url
done
fi
if [ -n "$ENABLE_COMPILE" ]; then
@@ -482,8 +515,8 @@ load_bpf_probe_compile() {
make -C "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf" > /dev/null
mkdir -p "${HOME}/.falco"
mv "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf/probe.o" "${HOME}/.falco/${BPF_PROBE_FILENAME}"
mkdir -p "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}"
mv "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf/probe.o" "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then
rm -r /tmp/kernel
@@ -493,47 +526,56 @@ load_bpf_probe_compile() {
load_bpf_probe_download() {
local URL
URL=$(echo "${DRIVERS_REPO}/${DRIVER_VERSION}/${BPF_PROBE_FILENAME}" | sed s/+/%2B/g)
URL=$(echo "${1}/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" | sed s/+/%2B/g)
echo "* Trying to download a prebuilt eBPF probe from ${URL}"
if ! curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${BPF_PROBE_FILENAME}" "${URL}"; then
if ! curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" "${URL}"; then
>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} eBPF probe"
return
return 1
fi
return 0
}
load_bpf_probe() {
echo "* Mounting debugfs"
if [ ! -d /sys/kernel/debug/tracing ]; then
echo "* Mounting debugfs"
mount -t debugfs nodev /sys/kernel/debug
fi
get_target_id
BPF_PROBE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.o"
echo "* Filename '${BPF_PROBE_FILENAME}' is composed of:"
print_filename_components
if [ -n "$ENABLE_DOWNLOAD" ]; then
if [ -f "${HOME}/.falco/${BPF_PROBE_FILENAME}" ]; then
echo "* Skipping download, eBPF probe is already present in ${HOME}/.falco/${BPF_PROBE_FILENAME}"
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then
echo "* Skipping download, eBPF probe is already present in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
else
load_bpf_probe_download
IFS=", " read -r -a urls <<< "${DRIVERS_REPO}"
for url in "${urls[@]}"; do
load_bpf_probe_download $url
if [ $? -eq 0 ]; then
break
fi
done
fi
fi
if [ -n "$ENABLE_COMPILE" ]; then
if [ -f "${HOME}/.falco/${BPF_PROBE_FILENAME}" ]; then
echo "* Skipping compilation, eBPF probe is already present in ${HOME}/.falco/${BPF_PROBE_FILENAME}"
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then
echo "* Skipping compilation, eBPF probe is already present in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
else
load_bpf_probe_compile
fi
fi
if [ -f "${HOME}/.falco/${BPF_PROBE_FILENAME}" ]; then
echo "* eBPF probe located in ${HOME}/.falco/${BPF_PROBE_FILENAME}"
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then
echo "* eBPF probe located in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
ln -sf "${HOME}/.falco/${BPF_PROBE_FILENAME}" "${HOME}/.falco/${DRIVER_NAME}-bpf.o" \
ln -sf "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" "${HOME}/.falco/${DRIVER_NAME}-bpf.o" \
&& echo "* Success: eBPF probe symlinked to ${HOME}/.falco/${DRIVER_NAME}-bpf.o"
exit $?
else
@@ -559,7 +601,7 @@ print_usage() {
echo " --source-only skip execution and allow sourcing in another script"
echo ""
echo "Environment variables:"
echo " DRIVERS_REPO specify a different URL where to look for prebuilt Falco drivers"
echo " DRIVERS_REPO specify different URL(s) where to look for prebuilt Falco drivers (comma separated)"
echo " DRIVER_NAME specify a different name for the driver"
echo " DRIVER_INSECURE_DOWNLOAD whether you want to allow insecure downloads or not"
echo ""
@@ -592,7 +634,7 @@ if [[ -z "$MAX_RMMOD_WAIT" ]]; then
MAX_RMMOD_WAIT=60
fi
DRIVER_VERSION="@DRIVER_VERSION@"
DRIVER_VERSION=${DRIVER_VERSION:-"@DRIVER_VERSION@"}
DRIVER_NAME=${DRIVER_NAME:-"@DRIVER_NAME@"}
FALCO_VERSION="@FALCO_VERSION@"
@@ -662,7 +704,7 @@ if [ -z "$has_opts" ]; then
fi
if [ -z "$source_only" ]; then
echo "* Running falco-driver-loader for: falco version=${FALCO_VERSION}, driver version=${DRIVER_VERSION}"
echo "* Running falco-driver-loader for: falco version=${FALCO_VERSION}, driver version=${DRIVER_VERSION}, arch=${ARCH}, kernel release=${KERNEL_RELEASE}, kernel version=${KERNEL_VERSION}"
if [ "$(id -u)" != 0 ]; then
>&2 echo "This program must be run as root (or with sudo)"

View File

@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
set -e
mod_version="@DRIVER_VERSION@"
dkms add -m falco -v $mod_version --rpm_safe_upgrade
@@ -29,3 +30,35 @@ else
echo -e "Module build for the currently running kernel was skipped since the"
echo -e "kernel source for this kernel does not seem to be installed."
fi
# validate rpm macros by `rpm -qp --scripts <rpm>`
# RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_syntax
# systemd_post macro expands to
# if postinst:
# `systemd-update-helper install-system-units <service>`
%systemd_post 'falco.service'
# post install mirrored from .deb
if [ $1 -eq 1 ]; then
# This will only remove masks created on package removal.
/usr/bin/systemctl --system unmask 'falco.service' >/dev/null || true
# enable falco on installation
# note: DEB postinstall script checks for changed symlinks
/usr/bin/systemctl --system enable 'falco.service' >/dev/null || true
# start falco on installation
/usr/bin/systemctl --system start 'falco.service' >/dev/null || true
fi
# post upgrade mirrored from .deb
if [ $1 -gt 1 ]; then
if [ -d /run/systemd/system ]; then
/usr/bin/systemctl --system daemon-reload >/dev/null || true
# restart falco on upgrade if service is already running
/usr/bin/systemctl --system condrestart 'falco.service' >/dev/null || true
fi
fi

View File

@@ -14,3 +14,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
set -e
# post uninstall mirrored from .deb
if [ -d /run/systemd/system ] && [ "$1" = 0 ]; then
/usr/bin/systemctl --system daemon-reload >/dev/null || true
/usr/bin/systemctl --system mask 'falco.service' >/dev/null || true
fi
# validate rpm macros by `rpm -qp --scripts <rpm>`
# RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_syntax
# systemd_postun_with_restart macro expands to
# if package upgrade, not uninstall:
# `systemd-update-helper mark-restart-system-units <service>`
%systemd_postun_with_restart 'falco.service'

View File

@@ -14,5 +14,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
set -e
# pre uninstall mirrored from .deb
# Currently running falco service uses the driver, so stop it before driver cleanup
if [ -d /run/systemd/system ] && [ $1 -eq 0 ]; then
# stop falco service before uninstall
/usr/bin/systemctl --system stop 'falco.service' >/dev/null || true
fi
/usr/bin/falco-driver-loader --clean
# validate rpm macros by `rpm -qp --scripts <rpm>`
# RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_syntax
# systemd_preun macro expands to
# if preuninstall:
# `systemd-update-helper remove-system-units <service>`
%systemd_preun 'falco.service'

View File

@@ -1,15 +0,0 @@
stdout_output:
enabled: true
plugins:
- name: cloudtrail
library_path: BUILD_DIR/cloudtrail-plugin-prefix/src/cloudtrail-plugin/libcloudtrail.so
init_config: ""
open_params: "BUILD_DIR/test/trace_files/plugins/alice_start_instances.json"
- name: test_source
library_path: BUILD_DIR/test/plugins/libtest_source.so
init_config: ""
open_params: ""
# Optional
load_plugins: [cloudtrail, test_source]

View File

@@ -19,6 +19,7 @@ trace_files: !mux
compat_engine_v4_create_disallowed_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
@@ -30,6 +31,7 @@ trace_files: !mux
compat_engine_v4_create_allowed_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
@@ -40,6 +42,7 @@ trace_files: !mux
compat_engine_v4_create_privileged_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
@@ -50,6 +53,7 @@ trace_files: !mux
compat_engine_v4_create_privileged_trusted_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -60,6 +64,7 @@ trace_files: !mux
compat_engine_v4_create_unprivileged_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
@@ -69,6 +74,7 @@ trace_files: !mux
compat_engine_v4_create_hostnetwork_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
@@ -79,6 +85,7 @@ trace_files: !mux
compat_engine_v4_create_hostnetwork_trusted_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -90,6 +97,7 @@ trace_files: !mux
user_outside_allowed_set:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -101,6 +109,7 @@ trace_files: !mux
user_in_allowed_set:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -113,6 +122,7 @@ trace_files: !mux
create_disallowed_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -124,6 +134,7 @@ trace_files: !mux
create_allowed_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -134,6 +145,7 @@ trace_files: !mux
create_privileged_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -145,6 +157,7 @@ trace_files: !mux
create_privileged_no_secctx_1st_container_2nd_container_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -156,6 +169,7 @@ trace_files: !mux
create_privileged_2nd_container_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -166,6 +180,7 @@ trace_files: !mux
create_privileged_trusted_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -175,6 +190,7 @@ trace_files: !mux
create_unprivileged_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -183,6 +199,7 @@ trace_files: !mux
create_unprivileged_trusted_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -193,6 +210,7 @@ trace_files: !mux
create_sensitive_mount_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -204,6 +222,7 @@ trace_files: !mux
create_sensitive_mount_2nd_container_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -214,6 +233,7 @@ trace_files: !mux
create_sensitive_mount_trusted_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -223,6 +243,7 @@ trace_files: !mux
create_unsensitive_mount_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -231,6 +252,7 @@ trace_files: !mux
create_unsensitive_mount_trusted_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -241,6 +263,7 @@ trace_files: !mux
create_hostnetwork_pod:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -251,6 +274,7 @@ trace_files: !mux
create_hostnetwork_trusted_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -260,6 +284,7 @@ trace_files: !mux
create_nohostnetwork_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -268,6 +293,7 @@ trace_files: !mux
create_nohostnetwork_trusted_pod:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -278,6 +304,7 @@ trace_files: !mux
create_nodeport_service:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -289,6 +316,7 @@ trace_files: !mux
create_nonodeport_service:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -299,6 +327,7 @@ trace_files: !mux
create_configmap_private_creds:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -310,6 +339,7 @@ trace_files: !mux
create_configmap_no_private_creds:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -320,6 +350,7 @@ trace_files: !mux
anonymous_user:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -331,6 +362,7 @@ trace_files: !mux
pod_exec:
detect: True
detect_level: NOTICE
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -342,6 +374,7 @@ trace_files: !mux
pod_attach:
detect: True
detect_level: NOTICE
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -353,6 +386,7 @@ trace_files: !mux
namespace_outside_allowed_set:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -364,6 +398,7 @@ trace_files: !mux
namespace_in_allowed_set:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -375,6 +410,7 @@ trace_files: !mux
create_pod_in_kube_system_namespace:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -386,6 +422,7 @@ trace_files: !mux
create_pod_in_kube_public_namespace:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -397,6 +434,7 @@ trace_files: !mux
create_serviceaccount_in_kube_system_namespace:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -408,6 +446,7 @@ trace_files: !mux
create_serviceaccount_in_kube_public_namespace:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -419,6 +458,7 @@ trace_files: !mux
system_clusterrole_deleted:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -430,6 +470,7 @@ trace_files: !mux
system_clusterrole_modified:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -441,6 +482,7 @@ trace_files: !mux
attach_cluster_admin_role:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -452,6 +494,7 @@ trace_files: !mux
create_cluster_role_wildcard_resources:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -463,6 +506,7 @@ trace_files: !mux
create_cluster_role_wildcard_verbs:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -474,6 +518,7 @@ trace_files: !mux
create_writable_cluster_role:
detect: True
detect_level: NOTICE
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -485,6 +530,7 @@ trace_files: !mux
create_pod_exec_cluster_role:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -496,6 +542,7 @@ trace_files: !mux
create_deployment:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -507,6 +554,7 @@ trace_files: !mux
delete_deployment:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -518,6 +566,7 @@ trace_files: !mux
create_service:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -529,6 +578,7 @@ trace_files: !mux
delete_service:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -540,6 +590,7 @@ trace_files: !mux
create_configmap:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -551,6 +602,7 @@ trace_files: !mux
delete_configmap:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -562,6 +614,7 @@ trace_files: !mux
create_namespace:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -575,6 +628,7 @@ trace_files: !mux
delete_namespace:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -586,6 +640,7 @@ trace_files: !mux
create_serviceaccount:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -597,6 +652,7 @@ trace_files: !mux
delete_serviceaccount:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -608,6 +664,7 @@ trace_files: !mux
create_clusterrole:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -619,6 +676,7 @@ trace_files: !mux
delete_clusterrole:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -630,6 +688,7 @@ trace_files: !mux
create_clusterrolebinding:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -641,6 +700,7 @@ trace_files: !mux
delete_clusterrolebinding:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -652,6 +712,7 @@ trace_files: !mux
create_secret:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -664,6 +725,7 @@ trace_files: !mux
create_service_account_token_secret:
detect: False
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -673,6 +735,7 @@ trace_files: !mux
create_kube_system_secret:
detect: False
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -682,6 +745,7 @@ trace_files: !mux
delete_secret:
detect: True
detect_level: INFO
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -692,6 +756,7 @@ trace_files: !mux
fal_01_003:
detect: False
enable_source: k8s_audit
rules_file:
- ../rules/falco_rules.yaml
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
@@ -702,6 +767,7 @@ trace_files: !mux
json_pointer_correct_parse:
detect: True
detect_level: WARNING
enable_source: k8s_audit
rules_file:
- ./rules/k8s_audit/single_rule_with_json_pointer.yaml
detect_counts:

View File

@@ -77,6 +77,10 @@ class FalcoTest(Test):
else:
self.stderr_not_contains = [self.stderr_not_contains]
self.validate_ok = self.params.get('validate_ok', '*', default='')
self.validate_warnings = self.params.get('validate_warnings', '*', default='')
self.validate_errors = self.params.get('validate_errors', '*', default='')
self.exit_status = self.params.get('exit_status', '*', default=0)
self.should_detect = self.params.get('detect', '*', default=False)
self.check_detection_counts = self.params.get('check_detection_counts', '*', default=True)
@@ -93,6 +97,7 @@ class FalcoTest(Test):
self.all_events = self.params.get('all_events', '*', default=False)
self.priority = self.params.get('priority', '*', default='debug')
self.addl_cmdline_opts = self.params.get('addl_cmdline_opts', '*', default='')
self.enable_source = self.params.get('enable_source', '*', default='')
self.rules_file = self.params.get(
'rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
@@ -105,8 +110,18 @@ class FalcoTest(Test):
if self.validate_rules_file == False:
self.validate_rules_file = []
else:
# Always enable json output when validating rules
# files. Makes parsing errors/warnings easier
self.json_output = True
if not isinstance(self.validate_rules_file, list):
self.validate_rules_file = [self.validate_rules_file]
# can be either empty, a string, or a list
if self.enable_source == '':
self.enable_source = []
else:
if not isinstance(self.enable_source, list):
self.enable_source = [self.enable_source]
self.rules_args = ""
@@ -153,13 +168,6 @@ class FalcoTest(Test):
detect_counts[key] = value
self.detect_counts = detect_counts
self.rules_warning = self.params.get(
'rules_warning', '*', default=False)
if self.rules_warning == False:
self.rules_warning = set()
else:
self.rules_warning = set(self.rules_warning)
# Maps from rule name to set of evttypes
self.rules_events = self.params.get('rules_events', '*', default=False)
if self.rules_events == False:
@@ -232,7 +240,7 @@ class FalcoTest(Test):
self.grpcurl_res = None
self.grpc_observer = None
self.grpc_address = self.params.get(
'address', 'grpc/*', default='/var/run/falco.sock')
'address', 'grpc/*', default='/run/falco/falco.sock')
if self.grpc_address.startswith("unix://"):
self.is_grpc_using_unix_socket = True
self.grpc_address = self.grpc_address[len("unix://"):]
@@ -265,22 +273,6 @@ class FalcoTest(Test):
if self.package != 'None':
self.uninstall_package()
def check_rules_warnings(self, res):
found_warning = set()
for match in re.finditer('Rule ([^:]+): warning \(([^)]+)\):', res.stderr.decode("utf-8")):
rule = match.group(1)
warning = match.group(2)
found_warning.add(rule)
self.log.debug("Expected warning rules: {}".format(self.rules_warning))
self.log.debug("Actual warning rules: {}".format(found_warning))
if found_warning != self.rules_warning:
self.fail("Expected rules with warnings {} does not match actual rules with warnings {}".format(
self.rules_warning, found_warning))
def check_rules_events(self, res):
found_events = {}
@@ -376,7 +368,68 @@ class FalcoTest(Test):
return True
def check_json_output(self, res):
def get_validate_json(self, res):
if self.validate_json is None:
# The first line of stdout should be the validation result as json
self.validate_json = json.loads(res.stdout.decode("utf-8").partition('\n')[0])
return self.validate_json
def check_validate_ok(self, res):
if self.validate_ok != '':
vobj = self.get_validate_json(res)
for expected in self.validate_ok:
found = False
for vres in vobj["falco_load_results"]:
if vres["successful"] and os.path.basename(vres["name"]) == expected:
found = True
break
if not found:
self.fail("Validation json did not contain a successful result for file '{}'".format(expected))
def check_validate_warnings(self, res):
if self.validate_warnings != '':
vobj = self.get_validate_json(res)
for warnobj in self.validate_warnings:
found = False
for vres in vobj["falco_load_results"]:
for warning in vres["warnings"]:
if warning["code"] == warnobj["code"]:
if ("message" in warnobj and warning["message"] == warnobj["message"]) or ("message_contains" in warnobj and warnobj["message_contains"] in warning["message"]):
for loc in warning["context"]["locations"]:
if loc["item_type"] == warnobj["item_type"] and loc["item_name"] == warnobj["item_name"]:
found = True
break
if not found:
if "message" in warnobj:
self.fail("Validation json did not contain a warning '{}' for '{}' '{}' with message '{}'".format(
warnobj["code"], warnobj["item_type"], warnobj["item_name"], warnobj["message"]))
else:
self.fail("Validation json did not contain a warning '{}' for '{}' '{}' with message containing '{}'".format(
warnobj["code"], warnobj["item_type"], warnobj["item_name"], warnobj["message_contains"]))
def check_validate_errors(self, res):
if self.validate_errors != '':
vobj = self.get_validate_json(res)
for errobj in self.validate_errors:
found = False
for vres in vobj["falco_load_results"]:
for error in vres["errors"]:
if error["code"] == errobj["code"]:
if ("message" in errobj and error["message"] == errobj["message"]) or ("message_contains" in errobj and errobj["message_contains"] in error["message"]):
for loc in error["context"]["locations"]:
if loc["item_type"] == errobj["item_type"] and loc["item_name"] == errobj["item_name"]:
found = True
break
if not found:
if "message" in errobj:
self.fail("Validation json did not contain a error '{}' for '{}' '{}' with message '{}'".format(
errobj["code"], errobj["item_type"], errobj["item_name"], errobj["message"]))
else:
self.fail("Validation json did not contain a error '{}' for '{}' '{}' with message containing '{}'".format(
errobj["code"], errobj["item_type"], errobj["item_name"], errobj["message_contains"]))
def check_json_event_output(self, res):
if self.json_output:
# Just verify that any lines starting with '{' are valid json objects.
# Doesn't do any deep inspection of the contents.
@@ -578,15 +631,22 @@ class FalcoTest(Test):
# This sets falco_binary_path as a side-effect.
self.install_package()
self.validate_json = None
trace_arg = self.trace_file
if self.trace_file:
trace_arg = "-e {}".format(self.trace_file)
extra_cmdline = ''
for source in self.enable_source:
extra_cmdline += ' --enable-source="{}"'.format(source)
extra_cmdline += ' ' + self.addl_cmdline_opts
# Run falco
cmd = '{} {} {} -c {} {} -o json_output={} -o json_include_output_property={} -o json_include_tags_property={} -o priority={} -v {}'.format(
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output,
self.json_include_output_property, self.json_include_tags_property, self.priority, self.addl_cmdline_opts)
self.json_include_output_property, self.json_include_tags_property, self.priority, extra_cmdline)
for tag in self.disable_tags:
cmd += ' -T {}'.format(tag)
@@ -603,7 +663,7 @@ class FalcoTest(Test):
if self.time_iso_8601:
cmd += ' -o time_format_iso_8601=true'
self.falco_proc = process.SubProcess(cmd)
self.falco_proc = process.SubProcess(cmd, env=dict(os.environ, FALCO_HOSTNAME="test-falco-hostname"))
res = self.falco_proc.run(timeout=180, sig=9)
@@ -644,18 +704,22 @@ class FalcoTest(Test):
self.error("Falco command \"{}\" exited with unexpected return value {} (!= {})".format(
cmd, res.exit_status, self.exit_status))
self.check_validate_ok(res)
self.check_validate_errors(res)
self.check_validate_warnings(res)
# No need to check any outputs if the falco process exited abnormally.
if res.exit_status != 0:
return
self.check_rules_warnings(res)
if len(self.rules_events) > 0:
self.check_rules_events(res)
if len(self.validate_rules_file) == 0 and self.check_detection_counts:
self.check_detections(res)
if len(self.detect_counts) > 0:
self.check_detections_by_rule(res)
self.check_json_output(res)
if not self.validate_rules_file:
self.check_json_event_output(res)
self.check_outputs()
self.check_output_strictly_contains(res)
self.check_grpc()

View File

@@ -20,22 +20,55 @@ trace_files: !mux
builtin_rules_no_warnings:
detect: False
trace_file: trace_files/empty.scap
rules_warning: False
# The rules_events part of this test was mistakenly disabled when
# generic events (e.g. k8s_audit support) was added (#1715).
# The implementation no longer prints messages of the form:
# "Event types for rule (<RULE>): (<EVENT TYPES>)
# And without that output, none of the checks below rules_events
# are considered.
# XXX/mstemm add it back
test_warnings:
detect: False
trace_file: trace_files/empty.scap
rules_file: rules/falco_rules_warnings.yaml
rules_warning:
- no_evttype
- evttype_not_equals
- leading_not
- not_equals_at_end
- not_at_end
- not_equals_and_not
- leading_in_not_equals_at_evttype
- not_with_evttypes
- not_with_evttypes_addl
validate_rules_file: rules/falco_rules_warnings.yaml
validate_warnings:
- item_type: rule
item_name: no_evttype
code: LOAD_NO_EVTTYPE
message: "Rule matches too many evt.type values. This has a significant performance penalty."
- item_type: rule
item_name: evttype_not_equals
code: LOAD_NO_EVTTYPE
message: "Rule matches too many evt.type values. This has a significant performance penalty."
- item_type: rule
item_name: leading_not
code: LOAD_NO_EVTTYPE
message: "Rule matches too many evt.type values. This has a significant performance penalty."
- item_type: rule
item_name: not_equals_at_end
code: LOAD_NO_EVTTYPE
message: "Rule matches too many evt.type values. This has a significant performance penalty."
- item_type: rule
item_name: not_at_end
code: LOAD_NO_EVTTYPE
message: "Rule matches too many evt.type values. This has a significant performance penalty."
- item_type: rule
item_name: not_equals_and_not
code: LOAD_NO_EVTTYPE
message: "Rule matches too many evt.type values. This has a significant performance penalty."
- item_type: rule
item_name: leading_in_not_equals_at_evttype
code: LOAD_NO_EVTTYPE
message: "Rule matches too many evt.type values. This has a significant performance penalty."
- item_type: rule
item_name: not_with_evttypes
code: LOAD_NO_EVTTYPE
message: "Rule matches too many evt.type values. This has a significant performance penalty."
- item_type: rule
item_name: not_with_evttypes_addl
code: LOAD_NO_EVTTYPE
message: "Rule matches too many evt.type values. This has a significant performance penalty."
rules_events:
- no_warnings: [execve]
- no_evttype: [all]
@@ -251,159 +284,149 @@ trace_files: !mux
invalid_not_yaml:
exit_status: 1
stdout_is: |+
1 errors:
Rules content is not yaml
validate_errors:
- item_type: rules content
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Rules content is not yaml"
validate_rules_file:
- rules/invalid_not_yaml.yaml
trace_file: trace_files/cat_write.scap
invalid_not_array:
exit_status: 1
stdout_is: |+
1 errors:
Rules content is not yaml array of objects
validate_errors:
- item_type: rules content
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Rules content is not yaml array of objects"
validate_rules_file:
- rules/invalid_not_array.yaml
trace_file: trace_files/cat_write.scap
invalid_array_item_not_object:
exit_status: 1
stdout_is: |+
1 errors:
Unexpected element type. Each element should be a yaml associative array.
---
- foo
---
validate_errors:
- item_type: rules content item
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Unexpected element type. Each element should be a yaml associative array."
validate_rules_file:
- rules/invalid_array_item_not_object.yaml
trace_file: trace_files/cat_write.scap
invalid_engine_version_not_number:
exit_status: 1
stdout_is: |+
1 errors:
Value of required_engine_version must be a number
---
- required_engine_version: not-a-number
---
validate_errors:
- item_type: required_engine_version
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Can't decode YAML scalar value"
validate_rules_file:
- rules/invalid_engine_version_not_number.yaml
trace_file: trace_files/cat_write.scap
invalid_yaml_parse_error:
exit_status: 1
validate_errors:
- item_type: rules content
item_name: ""
code: LOAD_ERR_YAML_PARSE
message: "yaml-cpp: error at line 1, column 11: illegal map value"
validate_rules_file:
- rules/invalid_yaml_parse_error.yaml
trace_file: trace_files/cat_write.scap
invalid_list_without_items:
exit_status: 1
stdout_is: |+
1 errors:
List must have property items
---
- list: bad_list
no_items: foo
---
validate_errors:
- item_type: list
item_name: bad_list
code: LOAD_ERR_YAML_VALIDATE
message: "Item has no mapping for key 'items'"
validate_rules_file:
- rules/invalid_list_without_items.yaml
trace_file: trace_files/cat_write.scap
invalid_macro_without_condition:
exit_status: 1
stdout_is: |+
1 errors:
Macro must have property condition
---
- macro: bad_macro
nope: 1
---
validate_errors:
- item_type: macro
item_name: bad_macro
code: LOAD_ERR_YAML_VALIDATE
message: "Item has no mapping for key 'condition'"
validate_rules_file:
- rules/invalid_macro_without_condition.yaml
trace_file: trace_files/cat_write.scap
invalid_rule_without_output:
exit_status: 1
stdout_is: |+
1 errors:
Rule must have properties 'condition', 'output', 'desc', and 'priority'
---
- rule: no output rule
desc: some desc
condition: evt.type=fork
priority: INFO
---
validate_errors:
- item_type: rule
item_name: no output rule
code: LOAD_ERR_YAML_VALIDATE
message: "Item has no mapping for key 'output'"
validate_rules_file:
- rules/invalid_rule_without_output.yaml
trace_file: trace_files/cat_write.scap
invalid_append_rule_without_condition:
exit_status: 1
stdout_is: |+
1 errors:
Appended rule must have exceptions or condition property
---
- rule: no condition rule
append: true
---
validate_errors:
- item_type: rule
item_name: no condition rule
code: LOAD_ERR_VALIDATE
message: "Appended rule must have exceptions or condition property"
validate_rules_file:
- rules/invalid_append_rule_without_condition.yaml
trace_file: trace_files/cat_write.scap
invalid_append_macro_dangling:
exit_status: 1
stdout_is: |+
1 errors:
Macro dangling append has 'append' key but no macro by that name already exists
---
- macro: dangling append
condition: and evt.type=execve
append: true
---
validate_errors:
- item_type: macro
item_name: dangling append
code: LOAD_ERR_VALIDATE
message: "Macro has 'append' key but no macro by that name already exists"
validate_rules_file:
- rules/invalid_append_macro_dangling.yaml
trace_file: trace_files/cat_write.scap
invalid_list_append_dangling:
exit_status: 1
stdout_is: |+
1 errors:
List my_list has 'append' key but no list by that name already exists
---
- list: my_list
items: [not-cat]
append: true
---
validate_errors:
- item_type: list
item_name: my_list
code: LOAD_ERR_VALIDATE
message: "List has 'append' key but no list by that name already exists"
validate_rules_file:
- rules/list_append_failure.yaml
trace_file: trace_files/cat_write.scap
invalid_rule_append_dangling:
exit_status: 1
stdout_is: |+
1 errors:
Rule my_rule has 'append' key but no rule by that name already exists
---
- rule: my_rule
condition: evt.type=open
append: true
---
validate_errors:
- item_type: rule
item_name: my_rule
code: LOAD_ERR_VALIDATE
message: "Rule has 'append' key but no rule by that name already exists"
validate_rules_file:
- rules/rule_append_failure.yaml
trace_file: trace_files/cat_write.scap
invalid_overwrite_macro:
exit_status: 1
stdout_contains: |+
.*invalid_base_macro.yaml: Ok
.*invalid_overwrite_macro.yaml: 1 errors:
Compilation error when compiling "foo": Undefined macro 'foo' used in filter.
---
- macro: some macro
condition: foo
append: false
---
validate_ok: [invalid_base_macro.yaml]
validate_errors:
- item_type: macro
item_name: some macro
code: LOAD_ERR_VALIDATE
message: "Undefined macro 'foo' used in filter."
validate_warnings:
- item_type: macro
item_name: some macro
code: LOAD_UNUSED_MACRO
message: "Macro not referred to by any other rule/macro"
validate_rules_file:
- rules/invalid_base_macro.yaml
- rules/invalid_overwrite_macro.yaml
@@ -411,18 +434,17 @@ trace_files: !mux
invalid_append_macro:
exit_status: 1
stdout_contains: |+
.*invalid_base_macro.yaml: Ok
.*invalid_append_macro.yaml: 1 errors:
Compilation error when compiling "evt.type=execve foo": 17: unexpected token after 'execve', expecting 'or', 'and'
---
- macro: some macro
condition: evt.type=execve
- macro: some macro
condition: foo
append: true
---
validate_ok: [invalid_base_macro.yaml]
validate_errors:
- item_type: macro
item_name: some macro
code: LOAD_ERR_COMPILE_CONDITION
message: "unexpected token after 'execve', expecting 'or', 'and'"
validate_warnings:
- item_type: macro
item_name: some macro
code: LOAD_UNUSED_MACRO
message: "Macro not referred to by any other rule/macro"
validate_rules_file:
- rules/invalid_base_macro.yaml
- rules/invalid_append_macro.yaml
@@ -430,49 +452,39 @@ trace_files: !mux
invalid_overwrite_macro_multiple_docs:
exit_status: 1
stdout_is: |+
1 errors:
Compilation error when compiling "foo": Undefined macro 'foo' used in filter.
---
- macro: some macro
condition: foo
append: false
---
validate_errors:
- item_type: macro
item_name: some macro
code: LOAD_ERR_VALIDATE
message: "Undefined macro 'foo' used in filter."
validate_warnings:
- item_type: macro
item_name: some macro
code: LOAD_UNUSED_MACRO
message: "Macro not referred to by any other rule/macro"
validate_rules_file:
- rules/invalid_overwrite_macro_multiple_docs.yaml
trace_file: trace_files/cat_write.scap
invalid_append_macro_multiple_docs:
exit_status: 1
stdout_is: |+
1 errors:
Compilation error when compiling "evt.type=execve foo": 17: unexpected token after 'execve', expecting 'or', 'and'
---
- macro: some macro
condition: evt.type=execve
- macro: some macro
condition: foo
append: true
---
validate_errors:
- item_type: macro
item_name: some macro
code: LOAD_ERR_COMPILE_CONDITION
message: "unexpected token after 'execve', expecting 'or', 'and'"
validate_rules_file:
- rules/invalid_append_macro_multiple_docs.yaml
trace_file: trace_files/cat_write.scap
invalid_overwrite_rule:
exit_status: 1
stdout_contains: |+
.*invalid_base_rule.yaml: Ok
.*invalid_overwrite_rule.yaml: 1 errors:
Undefined macro 'bar' used in filter.
---
- rule: some rule
desc: some desc
condition: bar
output: some output
priority: INFO
append: false
---
validate_ok: [invalid_base_rule.yaml]
validate_errors:
- item_type: rule
item_name: some rule
code: LOAD_ERR_VALIDATE
message: "Undefined macro 'bar' used in filter."
validate_rules_file:
- rules/invalid_base_rule.yaml
- rules/invalid_overwrite_rule.yaml
@@ -480,24 +492,12 @@ trace_files: !mux
invalid_append_rule:
exit_status: 1
stdout_contains: |+
.*invalid_base_rule.yaml: Ok
.*invalid_append_rule.yaml: 1 errors:
Compilation error when compiling "evt.type=open bar": 15: unexpected token after 'open', expecting 'or', 'and'
---
- rule: some rule
desc: some desc
condition: evt.type=open
output: some output
priority: INFO
- rule: some rule
desc: some desc
condition: bar
output: some output
priority: INFO
append: true
---
validate_ok: [invalid_base_rule.yaml]
validate_errors:
- item_type: rule
item_name: some rule
code: LOAD_ERR_COMPILE_CONDITION
message: "unexpected token after 'open', expecting 'or', 'and'"
validate_rules_file:
- rules/invalid_base_rule.yaml
- rules/invalid_append_rule.yaml
@@ -505,96 +505,66 @@ trace_files: !mux
invalid_overwrite_rule_multiple_docs:
exit_status: 1
stdout_is: |+
1 errors:
Undefined macro 'bar' used in filter.
---
- rule: some rule
desc: some desc
condition: bar
output: some output
priority: INFO
append: false
---
validate_errors:
- item_type: rule
item_name: some rule
code: LOAD_ERR_VALIDATE
message: "Undefined macro 'bar' used in filter."
validate_rules_file:
- rules/invalid_overwrite_rule_multiple_docs.yaml
trace_file: trace_files/cat_write.scap
invalid_append_rule_multiple_docs:
exit_status: 1
stdout_contains: |+
Compilation error when compiling "evt.type=open bar": 15: unexpected token after 'open', expecting 'or', 'and'
---
- rule: some rule
desc: some desc
condition: evt.type=open
output: some output
priority: INFO
- rule: some rule
desc: some desc
condition: bar
output: some output
priority: INFO
append: true
---
validate_errors:
- item_type: rule
item_name: some rule
code: LOAD_ERR_COMPILE_CONDITION
message: "unexpected token after 'open', expecting 'or', 'and'"
validate_rules_file:
- rules/invalid_append_rule_multiple_docs.yaml
trace_file: trace_files/cat_write.scap
invalid_missing_rule_name:
exit_status: 1
stdout_is: |+
1 errors:
Rule name is empty
---
- rule:
desc: some desc
condition: evt.type=execve
output: some output
---
validate_errors:
- item_type: rule
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Mapping for key 'rule' is empty"
validate_rules_file:
- rules/invalid_missing_rule_name.yaml
trace_file: trace_files/cat_write.scap
invalid_missing_list_name:
exit_status: 1
stdout_is: |+
1 errors:
List name is empty
---
- list:
items: [foo]
---
validate_errors:
- item_type: list
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Mapping for key 'list' is empty"
validate_rules_file:
- rules/invalid_missing_list_name.yaml
trace_file: trace_files/cat_write.scap
invalid_missing_macro_name:
exit_status: 1
stdout_is: |+
1 errors:
Macro name is empty
---
- macro:
condition: evt.type=execve
---
validate_errors:
- item_type: macro
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Mapping for key 'macro' is empty"
validate_rules_file:
- rules/invalid_missing_macro_name.yaml
trace_file: trace_files/cat_write.scap
invalid_rule_output:
exit_status: 1
stdout_is: |+
1 errors:
Invalid output format 'An open was seen %not_a_real_field': 'invalid formatting token not_a_real_field'
---
- rule: rule_with_invalid_output
desc: A rule with an invalid output field
condition: evt.type=open
output: "An open was seen %not_a_real_field"
priority: WARNING
---
validate_errors:
- item_type: rule
item_name: rule_with_invalid_output
code: LOAD_ERR_COMPILE_OUTPUT
message: "invalid formatting token not_a_real_field"
validate_rules_file:
- rules/invalid_rule_output.yaml
trace_file: trace_files/cat_write.scap
@@ -622,13 +592,13 @@ trace_files: !mux
rules_file:
- rules/single_rule_enabled_flag.yaml
trace_file: trace_files/cat_write.scap
disabled_rule_using_false_enabled_flag_only:
detect: False
rules_file:
- rules/disabled_rule_using_enabled_flag_only.yaml
trace_file: trace_files/cat_write.scap
enabled_rule_using_false_enabled_flag_only:
detect: True
detect_level: WARNING
@@ -754,7 +724,7 @@ trace_files: !mux
- "Write below etc": 1
- "System procs network activity": 1
- "Mkdir binary dirs": 1
- "System user interactive": 1
- "System user interactive": 0
- "DB program spawned process": 1
- "Non sudo setuid": 1
- "Create files below dev": 1
@@ -1025,13 +995,6 @@ trace_files: !mux
- open_12: 0
- open_13: 0
list_append_failure:
exit_status: 1
stderr_contains: "List my_list has 'append' key but no list by that name already exists"
rules_file:
- rules/list_append_failure.yaml
trace_file: trace_files/cat_write.scap
list_append:
detect: True
detect_level: WARNING
@@ -1045,13 +1008,6 @@ trace_files: !mux
- rules/list_append_false.yaml
trace_file: trace_files/cat_write.scap
macro_append_failure:
exit_status: 1
stderr_contains: "Macro my_macro has 'append' key but no macro by that name already exists"
rules_file:
- rules/macro_append_failure.yaml
trace_file: trace_files/cat_write.scap
macro_append:
detect: True
detect_level: WARNING
@@ -1065,13 +1021,6 @@ trace_files: !mux
- rules/macro_append_false.yaml
trace_file: trace_files/cat_write.scap
rule_append_failure:
exit_status: 1
stderr_contains: "Rule my_rule has 'append' key but no rule by that name already exists"
rules_file:
- rules/rule_append_failure.yaml
trace_file: trace_files/cat_write.scap
rule_append_skipped:
detect: False
priority: ERROR
@@ -1149,13 +1098,21 @@ trace_files: !mux
- rules/catchall_order.yaml
detect_counts:
- open_dev_null: 1
dev_null: 0
dev_null: 6
trace_file: trace_files/cat_write.scap
skip_unknown_noevt:
validate_skip_unknown_noevt:
validate_warnings:
- item_type: rule
item_name: "Contains Unknown Event And Skipping"
code: LOAD_UNKNOWN_FIELD
message: "filter_check called with nonexistent field proc.nobody"
validate_rules_file:
- rules/skip_unknown_evt.yaml
trace_file: trace_files/cat_write.scap
detect_skip_unknown_noevt:
detect: False
rules_warning:
- Contains Unknown Event And Skipping
rules_file:
- rules/skip_unknown_evt.yaml
trace_file: trace_files/cat_write.scap
@@ -1168,41 +1125,34 @@ trace_files: !mux
skip_unknown_error:
exit_status: 1
stderr_contains: |+
Could not load rules file.*skip_unknown_error.yaml: 1 errors:
Rule Contains Unknown Event And Not Skipping: error filter_check called with nonexistent field proc.nobody
---
- rule: Contains Unknown Event And Not Skipping
desc: Contains an unknown event
condition: proc.nobody=cat
output: Never
skip-if-unknown-filter: false
priority: INFO
---
rules_file:
validate_errors:
- item_type: rule
item_name: "Contains Unknown Event And Not Skipping"
code: LOAD_ERR_COMPILE_CONDITION
message: "filter_check called with nonexistent field proc.nobody"
validate_rules_file:
- rules/skip_unknown_error.yaml
trace_file: trace_files/cat_write.scap
skip_unknown_unspec_error:
exit_status: 1
stderr_contains: |+
Could not load rules file .*skip_unknown_unspec.yaml: 1 errors:
Rule Contains Unknown Event And Unspecified: error filter_check called with nonexistent field proc.nobody
---
- rule: Contains Unknown Event And Unspecified
desc: Contains an unknown event
condition: proc.nobody=cat
output: Never
priority: INFO
---
rules_file:
validate_errors:
- item_type: rule
item_name: "Contains Unknown Event And Unspecified"
code: LOAD_ERR_COMPILE_CONDITION
message: "filter_check called with nonexistent field proc.nobody"
validate_rules_file:
- rules/skip_unknown_unspec.yaml
trace_file: trace_files/cat_write.scap
engine_version_mismatch:
exit_status: 1
stderr_contains: Rules require engine version 9999999, but engine version is
rules_file:
validate_errors:
- item_type: required_engine_version
item_name: ""
code: LOAD_ERR_VALIDATE
message_contains: "Rules require engine version 9999999, but engine version is"
validate_rules_file:
- rules/engine_version_mismatch.yaml
trace_file: trace_files/cat_write.scap

View File

@@ -20,175 +20,99 @@ trace_files: !mux
rule_exception_no_fields:
exit_status: 1
stdout_is: |+
1 errors:
Rule exception item ex1: must have fields property with a list of fields
---
- rule: My Rule
desc: Some desc
condition: evt.type=open and proc.name=cat
output: Some output
exceptions:
- name: ex1
priority: error
---
validate_errors:
- item_type: exception
item_name: ex1
code: LOAD_ERR_YAML_VALIDATE
message: "Item has no mapping for key 'fields'"
validate_rules_file:
- rules/exceptions/item_no_fields.yaml
trace_file: trace_files/cat_write.scap
rule_exception_no_name:
exit_status: 1
stdout_is: |+
1 errors:
Rule exception item must have name property
---
- rule: My Rule
desc: Some desc
condition: evt.type=open and proc.name=cat
output: Some output
exceptions:
- fields: [proc.name, fd.filename]
priority: error
---
validate_errors:
- item_type: exception
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Item has no mapping for key 'name'"
validate_rules_file:
- rules/exceptions/item_no_name.yaml
trace_file: trace_files/cat_write.scap
rule_exception_append_no_name:
exit_status: 1
stdout_is: |+
1 errors:
Rule exception item must have name property
---
- rule: My Rule
exceptions:
- values:
- [nginx, /tmp/foo]
append: true
---
validate_errors:
- item_type: exception
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Item has no mapping for key 'name'"
validate_rules_file:
- rules/exceptions/append_item_no_name.yaml
trace_file: trace_files/cat_write.scap
rule_exception_unknown_fields:
exit_status: 1
stdout_is: |+
1 errors:
Rule exception item ex1: field name not.exist is not a supported filter field
---
- rule: My Rule
desc: Some desc
condition: evt.type=open and proc.name=cat
output: Some output
exceptions:
- name: ex1
fields: [not.exist]
priority: error
---
validate_errors:
- item_type: exception
item_name: ex1
code: LOAD_ERR_VALIDATE
message: "'not.exist' is not a supported filter field"
validate_rules_file:
- rules/exceptions/item_unknown_fields.yaml
trace_file: trace_files/cat_write.scap
rule_exception_comps_fields_len_mismatch:
exit_status: 1
stdout_is: |+
1 errors:
Rule exception item ex1: fields and comps lists must have equal length
---
- rule: My Rule
desc: Some desc
condition: evt.type=open and proc.name=cat
output: Some output
exceptions:
- name: ex1
fields: [proc.name, fd.filename]
comps: [=]
priority: error
---
validate_errors:
- item_type: exception
item_name: ex1
code: LOAD_ERR_VALIDATE
message: "Fields and comps lists must have equal length"
validate_rules_file:
- rules/exceptions/item_comps_fields_len_mismatch.yaml
trace_file: trace_files/cat_write.scap
rule_exception_unknown_comp:
exit_status: 1
stdout_is: |+
1 errors:
Rule exception item ex1: comparison operator no-comp is not a supported comparison operator
---
- rule: My Rule
desc: Some desc
condition: evt.type=open and proc.name=cat
output: Some output
exceptions:
- name: ex1
fields: [proc.name, fd.filename]
comps: [=, no-comp]
priority: error
---
validate_errors:
- item_type: exception
item_name: ex1
code: LOAD_ERR_VALIDATE
message: "'no-comp' is not a supported comparison operator"
validate_rules_file:
- rules/exceptions/item_unknown_comp.yaml
trace_file: trace_files/cat_write.scap
rule_exception_fields_values_len_mismatch:
exit_status: 1
stdout_is: |+
1 errors:
Exception item ex1: fields and values lists must have equal length
---
- rule: My Rule
desc: Some desc
condition: evt.type=open and proc.name=cat
output: Some output
exceptions:
- name: ex1
fields: [proc.name, fd.filename]
values:
- [nginx]
priority: error
---
validate_errors:
- item_type: exception
item_name: ex1
code: LOAD_ERR_VALIDATE
message: "Fields and values lists must have equal length"
validate_rules_file:
- rules/exceptions/item_fields_values_len_mismatch.yaml
trace_file: trace_files/cat_write.scap
rule_exception_append_fields_values_len_mismatch:
exit_status: 1
stdout_is: |+
1 errors:
Exception item ex1: fields and values lists must have equal length
---
- rule: My Rule
desc: Some desc
condition: evt.type=open and proc.name=cat
output: Some output
exceptions:
- name: ex1
fields: [proc.name, fd.filename]
priority: error
- rule: My Rule
exceptions:
- name: ex1
values:
- [nginx]
append: true
---
validate_errors:
- item_type: exception
item_name: ex1
code: LOAD_ERR_VALIDATE
message: "Fields and values lists must have equal length"
validate_rules_file:
- rules/exceptions/append_item_fields_values_len_mismatch.yaml
trace_file: trace_files/cat_write.scap
rule_exception_append_item_not_in_rule:
exit_status: 1
stdout_is: |+
1 errors:
Rule exception new item ex2: must have fields property with a list of fields
---
- rule: My Rule
exceptions:
- name: ex2
values:
- [apache, /tmp]
append: true
---
validate_errors:
- item_type: exception
item_name: ex2
code: LOAD_ERR_VALIDATE
message: "Rule exception must have fields property with a list of fields"
validate_rules_file:
- rules/exceptions/append_item_not_in_rule.yaml
trace_file: trace_files/cat_write.scap
@@ -325,7 +249,7 @@ trace_files: !mux
rules_file:
- rules/exceptions/rule_exception_new_single_field_append.yaml
trace_file: trace_files/cat_write.scap
rule_exception_new_second_field_append:
detect: False
detect_level: WARNING
@@ -335,18 +259,11 @@ trace_files: !mux
rule_exception_new_append_no_field:
exit_status: 1
stdout_is: |+
1 errors:
Rule exception new item proc_cmdline: must have fields property with a list of fields
---
- rule: Open From Cat
exceptions:
- name: proc_cmdline
comps: in
values:
- "cat /dev/null"
append: true
---
validate_errors:
- item_type: exception
item_name: proc_cmdline
code: LOAD_ERR_VALIDATE
message: "Rule exception must have fields property with a list of fields"
validate_rules_file:
- rules/exceptions/rule_exception_new_no_field_append.yaml
trace_file: trace_files/cat_write.scap

View File

@@ -35,6 +35,7 @@ trace_files: !mux
stdout_contains: "ct.id"
detect_create_instance:
enable_source: aws_cloudtrail
detect: True
detect_level: INFO
rules_file:
@@ -44,6 +45,7 @@ trace_files: !mux
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
detect_create_instance_bigevent:
enable_source: aws_cloudtrail
detect: True
detect_level: INFO
rules_file:
@@ -52,16 +54,9 @@ trace_files: !mux
- 'Cloudtrail Create Instance': 1
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances_bigevent.yaml
multiple_source_plugins:
exit_status: 1
stderr_contains: "Can not load multiple plugins with event sourcing capability: 'cloudtrail' already loaded."
conf_file: BUILD_DIR/test/confs/plugins/multiple_source_plugins.yaml
rules_file:
- rules/plugins/cloudtrail_create_instances.yaml
incompatible_extract_sources:
exit_status: 1
stderr_contains: "Plugin '.*' has field extraction capability but is not compatible with any enabled event source"
stderr_contains: "Plugin '.*' has field extraction capability but is not compatible with any known event source"
conf_file: BUILD_DIR/test/confs/plugins/incompatible_extract_sources.yaml
rules_file:
- rules/plugins/cloudtrail_create_instances.yaml
@@ -75,7 +70,7 @@ trace_files: !mux
incompat_plugin_api:
exit_status: 1
stderr_contains: "Plugin required API version '10000000.0.0' is not supported by the plugin API version of the framework '.*'"
stderr_contains: "plugin required API version '10000000.0.0' not compatible with the framework's API version '.*'"
conf_file: BUILD_DIR/test/confs/plugins/incompatible_plugin_api.yaml
rules_file:
- rules/plugins/cloudtrail_create_instances.yaml
@@ -89,27 +84,30 @@ trace_files: !mux
wrong_plugin_path:
exit_status: 1
stderr_contains: "error loading plugin.*No such file or directory. Exiting"
stderr_contains: "cannot load plugin.*No such file or directory"
conf_file: BUILD_DIR/test/confs/plugins/wrong_plugin_path.yaml
rules_file:
- rules/plugins/cloudtrail_incompat_plugin_version.yaml
no_plugins_unknown_source:
detect: False
rules_file:
exit_status: 0
validate_warnings:
- item_type: rule
item_name: Cloudtrail Create Instance
code: LOAD_UNKNOWN_SOURCE
message: "Unknown source aws_cloudtrail, skipping"
validate_rules_file:
- rules/plugins/cloudtrail_create_instances.yaml
trace_file: trace_files/empty.scap
rules_warning:
- Cloudtrail Create Instance
stderr_contains: "Rule Cloudtrail Create Instance: warning .unknown-source.: unknown source aws_cloudtrail, skipping"
no_plugins_unknown_source_rule_exception:
detect: False
rules_file:
exit_status: 0
validate_warnings:
- item_type: rule
item_name: Cloudtrail Create Instance
code: LOAD_UNKNOWN_SOURCE
message: "Unknown source aws_cloudtrail, skipping"
validate_rules_file:
- rules/plugins/cloudtrail_create_instances_exceptions.yaml
trace_file: trace_files/empty.scap
rules_warning:
- Cloudtrail Create Instance
stderr_contains: "Rule Cloudtrail Create Instance: warning .unknown-source.: unknown source aws_cloudtrail, skipping"

View File

@@ -30,6 +30,7 @@ traces: !mux
container-privileged:
trace_file: traces-positive/container-privileged.scap
all_events: True
detect: True
detect_level: INFO
detect_counts:
@@ -37,6 +38,7 @@ traces: !mux
container-sensitive-mount:
trace_file: traces-positive/container-sensitive-mount.scap
all_events: True
detect: True
detect_level: INFO
detect_counts:
@@ -51,6 +53,7 @@ traces: !mux
db-program-spawned-process:
trace_file: traces-positive/db-program-spawned-process.scap
all_events: True
detect: True
detect_level: NOTICE
detect_counts:
@@ -59,7 +62,7 @@ traces: !mux
falco-event-generator:
trace_file: traces-positive/falco-event-generator.scap
detect: True
detect_level: [ERROR, WARNING, INFO, NOTICE, DEBUG]
detect_level: [ERROR, WARNING, NOTICE, DEBUG]
detect_counts:
- "Write below binary dir": 1
- "Read sensitive file untrusted": 3
@@ -68,7 +71,7 @@ traces: !mux
- "Write below etc": 1
- "System procs network activity": 1
- "Mkdir binary dirs": 1
- "System user interactive": 1
- "System user interactive": 0
- "DB program spawned process": 1
- "Non sudo setuid": 1
- "Create files below dev": 1
@@ -120,8 +123,10 @@ traces: !mux
# falco-event-generator.scap so the rule is still being tested.
run-shell-untrusted:
trace_file: traces-positive/run-shell-untrusted.scap
detect: False
detect: True
detect_level: DEBUG
detect_counts:
- "Run shell untrusted": 1
system-binaries-network-activity:
trace_file: traces-positive/system-binaries-network-activity.scap
@@ -132,6 +137,7 @@ traces: !mux
system-user-interactive:
trace_file: traces-positive/system-user-interactive.scap
all_events: True
detect: True
detect_level: INFO
detect_counts:
@@ -139,6 +145,7 @@ traces: !mux
user-mgmt-binaries:
trace_file: traces-positive/user-mgmt-binaries.scap
all_events: True
detect: True
detect_level: NOTICE
detect_counts:
@@ -169,6 +176,7 @@ traces: !mux
# When a new version of the scap files is generated this should then become "traces-positive"
docker-compose:
trace_file: traces-negative/docker-compose.scap
all_events: True
detect: True
detect_level: NOTICE
detect_counts:

View File

@@ -1,8 +1,8 @@
{"output":"2016-08-04T16:17:57.881781397+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881781397Z", "output_fields": {"evt.time.iso8601":1470327477881781397,"proc.cmdline":"cat /dev/null"}}
{"output":"2016-08-04T16:17:57.881785348+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881785348Z", "output_fields": {"evt.time.iso8601":1470327477881785348,"proc.cmdline":"cat /dev/null"}}
{"output":"2016-08-04T16:17:57.881796705+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881796705Z", "output_fields": {"evt.time.iso8601":1470327477881796705,"proc.cmdline":"cat /dev/null"}}
{"output":"2016-08-04T16:17:57.881799840+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881799840Z", "output_fields": {"evt.time.iso8601":1470327477881799840,"proc.cmdline":"cat /dev/null"}}
{"output":"2016-08-04T16:17:57.882003104+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882003104Z", "output_fields": {"evt.time.iso8601":1470327477882003104,"proc.cmdline":"cat /dev/null"}}
{"output":"2016-08-04T16:17:57.882008208+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882008208Z", "output_fields": {"evt.time.iso8601":1470327477882008208,"proc.cmdline":"cat /dev/null"}}
{"output":"2016-08-04T16:17:57.882045694+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882045694Z", "output_fields": {"evt.time.iso8601":1470327477882045694,"proc.cmdline":"cat /dev/null"}}
{"output":"2016-08-04T16:17:57.882054739+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882054739Z", "output_fields": {"evt.time.iso8601":1470327477882054739,"proc.cmdline":"cat /dev/null"}}
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.881781397+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881781397Z", "output_fields": {"evt.time.iso8601":1470327477881781397,"proc.cmdline":"cat /dev/null"}}
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.881785348+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881785348Z", "output_fields": {"evt.time.iso8601":1470327477881785348,"proc.cmdline":"cat /dev/null"}}
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.881796705+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881796705Z", "output_fields": {"evt.time.iso8601":1470327477881796705,"proc.cmdline":"cat /dev/null"}}
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.881799840+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.881799840Z", "output_fields": {"evt.time.iso8601":1470327477881799840,"proc.cmdline":"cat /dev/null"}}
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.882003104+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882003104Z", "output_fields": {"evt.time.iso8601":1470327477882003104,"proc.cmdline":"cat /dev/null"}}
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.882008208+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882008208Z", "output_fields": {"evt.time.iso8601":1470327477882008208,"proc.cmdline":"cat /dev/null"}}
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.882045694+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882045694Z", "output_fields": {"evt.time.iso8601":1470327477882045694,"proc.cmdline":"cat /dev/null"}}
{"hostname":"test-falco-hostname","output":"2016-08-04T16:17:57.882054739+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","source":"syscall","tags":["filesystem","process","testing"],"time":"2016-08-04T16:17:57.882054739Z", "output_fields": {"evt.time.iso8601":1470327477882054739,"proc.cmdline":"cat /dev/null"}}

View File

@@ -19,7 +19,7 @@ limitations under the License.
#include <string.h>
#include <plugin_info.h>
static const char *pl_required_api_version = "1.0.0";
static const char *pl_required_api_version = PLUGIN_API_VERSION_STR;
static const char *pl_name_base = "test_extract";
static char pl_name[1024];
static const char *pl_desc = "Test Plugin For Regression Tests";

View File

@@ -20,7 +20,7 @@ limitations under the License.
#include <plugin_info.h>
static const char *pl_required_api_version = "1.0.0";
static const char *pl_required_api_version = PLUGIN_API_VERSION_STR;
static uint32_t pl_id = 999;
static const char *pl_name = "test_source";
static const char *pl_desc = "Test Plugin For Regression Tests";

View File

@@ -1,19 +0,0 @@
#
# Copyright (C) 2019 The Falco Authors.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
- macro: my_macro
condition: proc.name=not-cat
append: true

View File

@@ -1,25 +0,0 @@
#!/usr/bin/env bash
#
# Copyright (C) 2019 The Falco Authors.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# 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

@@ -22,6 +22,7 @@ set(
engine/test_filter_macro_resolver.cpp
engine/test_filter_evttype_resolver.cpp
engine/test_filter_warning_resolver.cpp
engine/test_plugin_requirements.cpp
falco/test_configuration.cpp
)

View File

@@ -21,20 +21,20 @@ TEST_CASE("is_unix_scheme matches", "[utils]")
{
SECTION("rvalue")
{
bool res = falco::utils::network::is_unix_scheme("unix:///var/run/falco.sock");
bool res = falco::utils::network::is_unix_scheme("unix:///run/falco/falco.sock");
REQUIRE(res);
}
SECTION("std::string")
{
std::string url("unix:///var/run/falco.sock");
std::string url("unix:///run/falco/falco.sock");
bool res = falco::utils::network::is_unix_scheme(url);
REQUIRE(res);
}
SECTION("char[]")
{
char url[] = "unix:///var/run/falco.sock";
char url[] = "unix:///run/falco/falco.sock";
bool res = falco::utils::network::is_unix_scheme(url);
REQUIRE(res);
}
@@ -42,7 +42,7 @@ TEST_CASE("is_unix_scheme matches", "[utils]")
TEST_CASE("is_unix_scheme does not match", "[utils]")
{
bool res = falco::utils::network::is_unix_scheme("something:///var/run/falco.sock");
bool res = falco::utils::network::is_unix_scheme("something:///run/falco/falco.sock");
REQUIRE_FALSE(res);
}

View File

@@ -35,10 +35,10 @@ string to_string(set<uint16_t> s)
return out;
}
void compare_evttypes(ast::expr* f, set<uint16_t> &expected)
void compare_evttypes(std::unique_ptr<ast::expr> f, set<uint16_t> &expected)
{
set<uint16_t> actual;
filter_evttype_resolver().evttypes(f, actual);
filter_evttype_resolver().evttypes(f.get(), actual);
for(auto &etype : expected)
{
REQUIRE(actual.find(etype) != actual.end());
@@ -49,7 +49,7 @@ void compare_evttypes(ast::expr* f, set<uint16_t> &expected)
}
}
ast::expr* compile(const string &fltstr)
std::unique_ptr<ast::expr> compile(const string &fltstr)
{
return libsinsp::filter::parser(fltstr).parse();
}
@@ -57,12 +57,14 @@ ast::expr* compile(const string &fltstr)
TEST_CASE("Should find event types from filter", "[rule_loader]")
{
set<uint16_t> openat_only{
PPME_SYSCALL_OPENAT_E, PPME_SYSCALL_OPENAT_X,
PPME_SYSCALL_OPENAT_2_E, PPME_SYSCALL_OPENAT_2_X };
set<uint16_t> close_only{
PPME_SYSCALL_CLOSE_E, PPME_SYSCALL_CLOSE_X };
set<uint16_t> openat_close{
PPME_SYSCALL_OPENAT_E, PPME_SYSCALL_OPENAT_X,
PPME_SYSCALL_OPENAT_2_E, PPME_SYSCALL_OPENAT_2_X,
PPME_SYSCALL_CLOSE_E, PPME_SYSCALL_CLOSE_X };
@@ -73,9 +75,8 @@ TEST_CASE("Should find event types from filter", "[rule_loader]")
set<uint16_t> no_events;
for(uint32_t i = 2; i < PPM_EVENT_MAX; i++)
{
// Skip "old" event versions that have been replaced
// by newer event versions, or events that are unused.
if(g_infotables.m_event_info[i].flags & (EF_OLD_VERSION | EF_UNUSED))
// Skip events that are unused.
if(g_infotables.m_event_info[i].flags & EF_UNUSED)
{
continue;
}
@@ -97,138 +98,138 @@ TEST_CASE("Should find event types from filter", "[rule_loader]")
SECTION("evt_type_eq")
{
auto f = compile("evt.type=openat");
compare_evttypes(f, openat_only);
compare_evttypes(std::move(f), openat_only);
}
SECTION("evt_type_in")
{
auto f = compile("evt.type in (openat, close)");
compare_evttypes(f, openat_close);
compare_evttypes(std::move(f), openat_close);
}
SECTION("evt_type_ne")
{
auto f = compile("evt.type!=openat");
compare_evttypes(f, not_openat);
compare_evttypes(std::move(f), not_openat);
}
SECTION("not_evt_type_eq")
{
auto f = compile("not evt.type=openat");
compare_evttypes(f, not_openat);
compare_evttypes(std::move(f), not_openat);
}
SECTION("not_evt_type_in")
{
auto f = compile("not evt.type in (openat, close)");
compare_evttypes(f, not_openat_close);
compare_evttypes(std::move(f), not_openat_close);
}
SECTION("not_evt_type_ne")
{
auto f = compile("not evt.type != openat");
compare_evttypes(f, openat_only);
compare_evttypes(std::move(f), openat_only);
}
SECTION("evt_type_or")
{
auto f = compile("evt.type=openat or evt.type=close");
compare_evttypes(f, openat_close);
compare_evttypes(std::move(f), openat_close);
}
SECTION("not_evt_type_or")
{
auto f = compile("evt.type!=openat or evt.type!=close");
compare_evttypes(f, all_events);
compare_evttypes(std::move(f), all_events);
}
SECTION("evt_type_or_ne")
{
auto f = compile("evt.type=close or evt.type!=openat");
compare_evttypes(f, not_openat);
compare_evttypes(std::move(f), not_openat);
}
SECTION("evt_type_and")
{
auto f = compile("evt.type=close and evt.type=openat");
compare_evttypes(f, no_events);
compare_evttypes(std::move(f), no_events);
}
SECTION("evt_type_and_non_evt_type")
{
auto f = compile("evt.type=openat and proc.name=nginx");
compare_evttypes(f, openat_only);
compare_evttypes(std::move(f), openat_only);
}
SECTION("evt_type_and_non_evt_type_not")
{
auto f = compile("evt.type=openat and not proc.name=nginx");
compare_evttypes(f, openat_only);
compare_evttypes(std::move(f), openat_only);
}
SECTION("evt_type_and_nested")
{
auto f = compile("evt.type=openat and (proc.name=nginx)");
compare_evttypes(f, openat_only);
compare_evttypes(std::move(f), openat_only);
}
SECTION("evt_type_and_nested_multi")
{
auto f = compile("evt.type=openat and (evt.type=close and proc.name=nginx)");
compare_evttypes(f, no_events);
compare_evttypes(std::move(f), no_events);
}
SECTION("non_evt_type")
{
auto f = compile("proc.name=nginx");
compare_evttypes(f, all_events);
compare_evttypes(std::move(f), all_events);
}
SECTION("non_evt_type_or")
{
auto f = compile("evt.type=openat or proc.name=nginx");
compare_evttypes(f, all_events);
compare_evttypes(std::move(f), all_events);
}
SECTION("non_evt_type_or_nested_first")
{
auto f = compile("(evt.type=openat) or proc.name=nginx");
compare_evttypes(f, all_events);
compare_evttypes(std::move(f), all_events);
}
SECTION("non_evt_type_or_nested_second")
{
auto f = compile("evt.type=openat or (proc.name=nginx)");
compare_evttypes(f, all_events);
compare_evttypes(std::move(f), all_events);
}
SECTION("non_evt_type_or_nested_multi")
{
auto f = compile("evt.type=openat or (evt.type=close and proc.name=nginx)");
compare_evttypes(f, openat_close);
compare_evttypes(std::move(f), openat_close);
}
SECTION("non_evt_type_or_nested_multi_not")
{
auto f = compile("evt.type=openat or not (evt.type=close and proc.name=nginx)");
compare_evttypes(f, not_close);
compare_evttypes(std::move(f), not_close);
}
SECTION("non_evt_type_and_nested_multi_not")
{
auto f = compile("evt.type=openat and not (evt.type=close and proc.name=nginx)");
compare_evttypes(f, openat_only);
compare_evttypes(std::move(f), openat_only);
}
SECTION("ne_and_and")
{
auto f = compile("evt.type!=openat and evt.type!=close");
compare_evttypes(f, not_openat_close);
compare_evttypes(std::move(f), not_openat_close);
}
SECTION("not_not")
{
auto f = compile("not (not evt.type=openat)");
compare_evttypes(f, openat_only);
compare_evttypes(std::move(f), openat_only);
}
}
}

View File

@@ -26,19 +26,18 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
SECTION("in the general case")
{
shared_ptr<expr> macro(
new unary_check_expr("test.field", "", "exists"));
std::shared_ptr<expr> macro = std::move(
unary_check_expr::create("test.field", "", "exists"));
expr* filter = new and_expr({
new unary_check_expr("evt.name", "", "exists"),
new not_expr(
new value_expr(macro_name)
),
});
expr* expected_filter = new and_expr({
new unary_check_expr("evt.name", "", "exists"),
new not_expr(clone(macro.get())),
});
std::vector<std::unique_ptr<expr>> filter_and;
filter_and.push_back(unary_check_expr::create("evt.name", "", "exists"));
filter_and.push_back(not_expr::create(value_expr::create(macro_name)));
std::shared_ptr<expr> filter = std::move(and_expr::create(filter_and));
std::vector<std::unique_ptr<expr>> expected_and;
expected_and.push_back(unary_check_expr::create("evt.name", "", "exists"));
expected_and.push_back(not_expr::create(clone(macro.get())));
std::shared_ptr<expr> expected = std::move(and_expr::create(expected_and));
filter_macro_resolver resolver;
resolver.set_macro(macro_name, macro);
@@ -48,46 +47,41 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
REQUIRE(resolver.get_resolved_macros().size() == 1);
REQUIRE(*resolver.get_resolved_macros().begin() == macro_name);
REQUIRE(resolver.get_unknown_macros().empty());
REQUIRE(filter->is_equal(expected_filter));
REQUIRE(filter->is_equal(expected.get()));
// second run
REQUIRE(resolver.run(filter) == false);
REQUIRE(resolver.get_resolved_macros().empty());
REQUIRE(resolver.get_unknown_macros().empty());
REQUIRE(filter->is_equal(expected_filter));
delete filter;
delete expected_filter;
REQUIRE(filter->is_equal(expected.get()));
}
SECTION("with a single node")
{
shared_ptr<expr> macro(
new unary_check_expr("test.field", "", "exists"));
std::shared_ptr<expr> macro = std::move(
unary_check_expr::create("test.field", "", "exists"));
expr* filter = new value_expr(macro_name);
std::shared_ptr<expr> filter = std::move(value_expr::create(macro_name));
filter_macro_resolver resolver;
resolver.set_macro(macro_name, macro);
// first run
expr* old_filter_ptr = filter;
expr* old_filter_ptr = filter.get();
REQUIRE(resolver.run(filter) == true);
REQUIRE(filter != old_filter_ptr);
REQUIRE(filter.get() != old_filter_ptr);
REQUIRE(resolver.get_resolved_macros().size() == 1);
REQUIRE(*resolver.get_resolved_macros().begin() == macro_name);
REQUIRE(resolver.get_unknown_macros().empty());
REQUIRE(filter->is_equal(macro.get()));
// second run
old_filter_ptr = filter;
old_filter_ptr = filter.get();
REQUIRE(resolver.run(filter) == false);
REQUIRE(filter == old_filter_ptr);
REQUIRE(filter.get() == old_filter_ptr);
REQUIRE(resolver.get_resolved_macros().empty());
REQUIRE(resolver.get_unknown_macros().empty());
REQUIRE(filter->is_equal(macro.get()));
delete filter;
}
SECTION("with multiple macros")
@@ -95,19 +89,20 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
string a_macro_name = macro_name + "_1";
string b_macro_name = macro_name + "_2";
shared_ptr<expr> a_macro(
new unary_check_expr("one.field", "", "exists"));
shared_ptr<expr> b_macro(
new unary_check_expr("another.field", "", "exists"));
std::shared_ptr<expr> a_macro = std::move(
unary_check_expr::create("one.field", "", "exists"));
std::shared_ptr<expr> b_macro = std::move(
unary_check_expr::create("another.field", "", "exists"));
expr* filter = new or_expr({
new value_expr(a_macro_name),
new value_expr(b_macro_name),
});
expr* expected_filter = new or_expr({
clone(a_macro.get()),
clone(b_macro.get()),
});
std::vector<std::unique_ptr<expr>> filter_or;
filter_or.push_back(value_expr::create(a_macro_name));
filter_or.push_back(value_expr::create(b_macro_name));
std::shared_ptr<expr> filter = std::move(or_expr::create(filter_or));
std::vector<std::unique_ptr<expr>> expected_or;
expected_or.push_back(clone(a_macro.get()));
expected_or.push_back(clone(b_macro.get()));
std::shared_ptr<expr> expected_filter = std::move(or_expr::create(expected_or));
filter_macro_resolver resolver;
resolver.set_macro(a_macro_name, a_macro);
@@ -121,16 +116,13 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
REQUIRE(resolver.get_resolved_macros().find(b_macro_name)
!= resolver.get_resolved_macros().end());
REQUIRE(resolver.get_unknown_macros().empty());
REQUIRE(filter->is_equal(expected_filter));
REQUIRE(filter->is_equal(expected_filter.get()));
// second run
REQUIRE(resolver.run(filter) == false);
REQUIRE(resolver.get_resolved_macros().empty());
REQUIRE(resolver.get_unknown_macros().empty());
REQUIRE(filter->is_equal(expected_filter));
delete filter;
delete expected_filter;
REQUIRE(filter->is_equal(expected_filter.get()));
}
SECTION("with nested macros")
@@ -138,18 +130,20 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
string a_macro_name = macro_name + "_1";
string b_macro_name = macro_name + "_2";
shared_ptr<expr> a_macro(new and_expr({
new unary_check_expr("one.field", "", "exists"),
new value_expr(b_macro_name),
}));
shared_ptr<expr> b_macro(
new unary_check_expr("another.field", "", "exists"));
std::vector<std::unique_ptr<expr>> a_macro_and;
a_macro_and.push_back(unary_check_expr::create("one.field", "", "exists"));
a_macro_and.push_back(value_expr::create(b_macro_name));
std::shared_ptr<expr> a_macro = std::move(and_expr::create(a_macro_and));
expr* filter = new value_expr(a_macro_name);
expr* expected_filter = new and_expr({
new unary_check_expr("one.field", "", "exists"),
new unary_check_expr("another.field", "", "exists"),
});
std::shared_ptr<expr> b_macro = std::move(
unary_check_expr::create("another.field", "", "exists"));
std::shared_ptr<expr> filter = std::move(value_expr::create(a_macro_name));
std::vector<std::unique_ptr<expr>> expected_and;
expected_and.push_back(unary_check_expr::create("one.field", "", "exists"));
expected_and.push_back(unary_check_expr::create("another.field", "", "exists"));
std::shared_ptr<expr> expected_filter = std::move(and_expr::create(expected_and));
filter_macro_resolver resolver;
resolver.set_macro(a_macro_name, a_macro);
@@ -163,16 +157,13 @@ TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
REQUIRE(resolver.get_resolved_macros().find(b_macro_name)
!= resolver.get_resolved_macros().end());
REQUIRE(resolver.get_unknown_macros().empty());
REQUIRE(filter->is_equal(expected_filter));
REQUIRE(filter->is_equal(expected_filter.get()));
// second run
REQUIRE(resolver.run(filter) == false);
REQUIRE(resolver.get_resolved_macros().empty());
REQUIRE(resolver.get_unknown_macros().empty());
REQUIRE(filter->is_equal(expected_filter));
delete filter;
delete expected_filter;
REQUIRE(filter->is_equal(expected_filter.get()));
}
}
@@ -182,20 +173,16 @@ TEST_CASE("Should find unknown macros", "[rule_loader]")
SECTION("in the general case")
{
expr* filter = new and_expr({
new unary_check_expr("evt.name", "", "exists"),
new not_expr(
new value_expr(macro_name)
),
});
std::vector<std::unique_ptr<expr>> filter_and;
filter_and.push_back(unary_check_expr::create("evt.name", "", "exists"));
filter_and.push_back(not_expr::create(value_expr::create(macro_name)));
std::shared_ptr<expr> filter = std::move(and_expr::create(filter_and));
filter_macro_resolver resolver;
REQUIRE(resolver.run(filter) == false);
REQUIRE(resolver.get_unknown_macros().size() == 1);
REQUIRE(*resolver.get_unknown_macros().begin() == macro_name);
REQUIRE(resolver.get_resolved_macros().empty());
delete filter;
}
SECTION("with nested macros")
@@ -203,13 +190,13 @@ TEST_CASE("Should find unknown macros", "[rule_loader]")
string a_macro_name = macro_name + "_1";
string b_macro_name = macro_name + "_2";
shared_ptr<expr> a_macro(new and_expr({
new unary_check_expr("one.field", "", "exists"),
new value_expr(b_macro_name),
}));
std::vector<std::unique_ptr<expr>> a_macro_and;
a_macro_and.push_back(unary_check_expr::create("one.field", "", "exists"));
a_macro_and.push_back(value_expr::create(b_macro_name));
std::shared_ptr<expr> a_macro = std::move(and_expr::create(a_macro_and));
expr* filter = new value_expr(a_macro_name);
expr* expected_filter = clone(a_macro.get());
std::shared_ptr<expr> filter = std::move(value_expr::create(a_macro_name));
auto expected_filter = clone(a_macro.get());
filter_macro_resolver resolver;
resolver.set_macro(a_macro_name, a_macro);
@@ -220,19 +207,16 @@ TEST_CASE("Should find unknown macros", "[rule_loader]")
REQUIRE(*resolver.get_resolved_macros().begin() == a_macro_name);
REQUIRE(resolver.get_unknown_macros().size() == 1);
REQUIRE(*resolver.get_unknown_macros().begin() == b_macro_name);
REQUIRE(filter->is_equal(expected_filter));
delete filter;
delete expected_filter;
REQUIRE(filter->is_equal(expected_filter.get()));
}
}
TEST_CASE("Should undefine macro", "[rule_loader]")
{
string macro_name = "test_macro";
shared_ptr<expr> macro(new unary_check_expr("test.field", "", "exists"));
expr* a_filter = new value_expr(macro_name);
expr* b_filter = new value_expr(macro_name);
std::shared_ptr<expr> macro = std::move(unary_check_expr::create("test.field", "", "exists"));
std::shared_ptr<expr> a_filter = std::move(value_expr::create(macro_name));
std::shared_ptr<expr> b_filter = std::move(value_expr::create(macro_name));
filter_macro_resolver resolver;
resolver.set_macro(macro_name, macro);
@@ -247,18 +231,14 @@ TEST_CASE("Should undefine macro", "[rule_loader]")
REQUIRE(resolver.get_resolved_macros().empty());
REQUIRE(resolver.get_unknown_macros().size() == 1);
REQUIRE(*resolver.get_unknown_macros().begin() == macro_name);
delete a_filter;
delete b_filter;
}
// checks that the macro AST is cloned and not shared across resolved filters
TEST_CASE("Should clone macro AST", "[rule_loader]")
{
string macro_name = "test_macro";
shared_ptr<unary_check_expr> macro(
new unary_check_expr("test.field", "", "exists"));
expr* filter = new value_expr(macro_name);
std::shared_ptr<unary_check_expr> macro = std::move(unary_check_expr::create("test.field", "", "exists"));
std::shared_ptr<expr> filter = std::move(value_expr::create(macro_name));
filter_macro_resolver resolver;
resolver.set_macro(macro_name, macro);
@@ -268,8 +248,6 @@ TEST_CASE("Should clone macro AST", "[rule_loader]")
REQUIRE(resolver.get_unknown_macros().empty());
REQUIRE(filter->is_equal(macro.get()));
macro.get()->field = "another.field";
macro->field = "another.field";
REQUIRE(!filter->is_equal(macro.get()));
delete filter;
}

View File

@@ -19,10 +19,9 @@ limitations under the License.
static bool warns(const std::string& condition)
{
std::set<std::string> w;
std::set<falco::load_result::warning_code> w;
auto ast = libsinsp::filter::parser(condition).parse();
filter_warning_resolver().run(ast, w);
delete ast;
filter_warning_resolver().run(ast.get(), w);
return !w.empty();
}

View File

@@ -0,0 +1,269 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <memory>
#include <catch.hpp>
#include "falco_engine.h"
static void check_requirements(
bool expect_success,
const std::vector<falco_engine::plugin_version_requirement>& plugins,
const std::string& ruleset_content)
{
std::string err;
std::unique_ptr<falco_engine> e(new falco_engine());
falco::load_result::rules_contents_t c = {{"test", ruleset_content}};
auto res = e->load_rules(c.begin()->second, c.begin()->first);
if (!res->successful())
{
if (expect_success)
{
FAIL(res->as_string(false, c));
}
return;
}
if (!e->check_plugin_requirements(plugins, err))
{
if (expect_success)
{
FAIL(err);
}
}
else if (!expect_success)
{
FAIL("unexpected successful plugin requirements check");
}
}
TEST_CASE("check_plugin_requirements must accept", "[rule_loader]")
{
SECTION("no requirement")
{
check_requirements(true, {{"k8saudit", "0.1.0"}}, "");
}
SECTION("single plugin")
{
check_requirements(true, {{"k8saudit", "0.1.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
)");
}
SECTION("single plugin newer version")
{
check_requirements(true, {{"k8saudit", "0.2.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
)");
}
SECTION("multiple plugins")
{
check_requirements(true, {{"k8saudit", "0.1.0"}, {"json", "0.3.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
- name: json
version: 0.3.0
)");
}
SECTION("single plugin multiple versions")
{
check_requirements(true, {{"k8saudit", "0.2.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
- required_plugin_versions:
- name: k8saudit
version: 0.2.0
)");
}
SECTION("single plugin with alternatives")
{
check_requirements(true, {{"k8saudit-other", "0.5.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
alternatives:
- name: k8saudit-other
version: 0.4.0
)");
}
SECTION("multiple plugins with alternatives")
{
check_requirements(true, {{"k8saudit-other", "0.5.0"}, {"json2", "0.5.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
alternatives:
- name: k8saudit-other
version: 0.4.0
- name: json
version: 0.3.0
alternatives:
- name: json2
version: 0.1.0
)");
}
SECTION("multiple plugins with alternatives with multiple versions")
{
check_requirements(true, {{"k8saudit-other", "0.7.0"}, {"json2", "0.5.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
alternatives:
- name: k8saudit-other
version: 0.4.0
- name: json
version: 0.3.0
alternatives:
- name: json2
version: 0.1.0
- required_plugin_versions:
- name: k8saudit
version: 1.0.0
alternatives:
- name: k8saudit-other
version: 0.7.0
)");
}
}
TEST_CASE("check_plugin_requirements must reject", "[rule_loader]")
{
SECTION("no plugin loaded")
{
check_requirements(false, {}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
)");
}
SECTION("single plugin wrong name")
{
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
- required_plugin_versions:
- name: k8saudit2
version: 0.1.0
)");
}
SECTION("single plugin wrong version")
{
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.2.0
)");
}
SECTION("multiple plugins")
{
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
- name: json
version: 0.3.0
)");
}
SECTION("single plugin multiple versions")
{
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
- required_plugin_versions:
- name: k8saudit
version: 0.2.0
)");
}
SECTION("single plugin with alternatives")
{
check_requirements(false, {{"k8saudit2", "0.5.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
alternatives:
- name: k8saudit-other
version: 0.4.0
)");
}
SECTION("single plugin with overlapping alternatives")
{
check_requirements(false, {{"k8saudit", "0.5.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
alternatives:
- name: k8saudit
version: 0.4.0
)");
}
SECTION("multiple plugins with alternatives")
{
check_requirements(false, {{"k8saudit-other", "0.5.0"}, {"json3", "0.5.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.1.0
alternatives:
- name: k8saudit-other
version: 0.4.0
- name: json
version: 0.3.0
alternatives:
- name: json2
version: 0.1.0
)");
}
SECTION("multiple plugins with alternatives with multiple versions")
{
check_requirements(false, {{"k8saudit", "0.7.0"}, {"json2", "0.5.0"}}, R"(
- required_plugin_versions:
- name: k8saudit
version: 0.4.0
alternatives:
- name: k8saudit-other
version: 0.4.0
- name: json
version: 0.3.0
alternatives:
- name: json2
version: 0.1.0
- required_plugin_versions:
- name: k8saudit
version: 1.0.0
alternatives:
- name: k8saudit-other
version: 0.7.0
)");
}
}

View File

@@ -13,6 +13,7 @@
set(FALCO_ENGINE_SOURCE_FILES
falco_common.cpp
falco_engine.cpp
falco_load_result.cpp
falco_utils.cpp
json_evt.cpp
evttype_index_ruleset.cpp
@@ -20,9 +21,11 @@ set(FALCO_ENGINE_SOURCE_FILES
filter_macro_resolver.cpp
filter_evttype_resolver.cpp
filter_warning_resolver.cpp
stats_manager.cpp
rule_loader.cpp
rule_reader.cpp
stats_manager.cpp)
rule_loader_reader.cpp
rule_loader_collector.cpp
rule_loader_compiler.cpp)
add_library(falco_engine STATIC ${FALCO_ENGINE_SOURCE_FILES})
add_dependencies(falco_engine njson string-view-lite)

View File

@@ -18,6 +18,8 @@ limitations under the License.
#include <unistd.h>
#include <string>
#include <fstream>
#include <functional>
#include <utility>
#include <sinsp.h>
#include <plugin.h>
@@ -25,7 +27,8 @@ limitations under the License.
#include "falco_engine.h"
#include "falco_utils.h"
#include "falco_engine_version.h"
#include "rule_reader.h"
#include "rule_loader_reader.h"
#include "rule_loader_compiler.h"
#include "formats.h"
@@ -36,9 +39,12 @@ limitations under the License.
const std::string falco_engine::s_default_ruleset = "falco-default-ruleset";
using namespace std;
using namespace falco;
falco_engine::falco_engine(bool seed_rng)
: m_next_ruleset_id(0),
: m_syscall_source(NULL),
m_syscall_source_idx(SIZE_MAX),
m_next_ruleset_id(0),
m_min_priority(falco_common::PRIORITY_DEBUG),
m_sampling_ratio(1), m_sampling_multiplier(0),
m_replace_container_info(false)
@@ -54,7 +60,7 @@ falco_engine::falco_engine(bool seed_rng)
falco_engine::~falco_engine()
{
m_rules.clear();
m_rule_loader.clear();
m_rule_collector.clear();
m_rule_stats_manager.clear();
m_sources.clear();
}
@@ -64,7 +70,7 @@ uint32_t falco_engine::engine_version()
return (uint32_t) FALCO_ENGINE_VERSION;
}
falco_source* falco_engine::find_source(const std::string& name)
const falco_source* falco_engine::find_source(const std::string& name) const
{
auto ret = m_sources.at(name);
if(!ret)
@@ -74,7 +80,7 @@ falco_source* falco_engine::find_source(const std::string& name)
return ret;
}
falco_source* falco_engine::find_source(std::size_t index)
const falco_source* falco_engine::find_source(std::size_t index) const
{
auto ret = m_sources.at(index);
if(!ret)
@@ -91,7 +97,7 @@ static std::string fieldclass_key(const gen_event_filter_factory::filter_fieldcl
return fld_info.name + fld_info.shortdesc;
}
void falco_engine::list_fields(std::string &source, bool verbose, bool names_only, bool markdown)
void falco_engine::list_fields(std::string &source, bool verbose, bool names_only, bool markdown) const
{
// Maps from field class name + short desc to list of event
// sources for which this field class can be used.
@@ -99,14 +105,14 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
// Do a first pass to group together classes that are
// applicable to multiple event sources.
for(auto &it : m_sources)
for(const auto &it : m_sources)
{
if(source != "" && source != it.name)
{
continue;
}
for(auto &fld_class : it.filter_factory->get_fields())
for(const auto &fld_class : it.filter_factory->get_fields())
{
fieldclass_event_sources[fieldclass_key(fld_class)].insert(it.name);
}
@@ -118,7 +124,7 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
// In the second pass, actually print info, skipping duplicate
// field classes and also printing info on supported sources.
for(auto &it : m_sources)
for(const auto &it : m_sources)
{
if(source != "" && source != it.name)
{
@@ -165,80 +171,76 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events)
{
uint64_t dummy;
static const std::string no_name = "N/A";
return load_rules(rules_content, verbose, all_events, dummy);
std::unique_ptr<load_result> res = load_rules(rules_content, no_name);
interpret_load_result(res, no_name, rules_content, verbose);
}
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version)
std::unique_ptr<load_result> falco_engine::load_rules(const std::string &rules_content, const std::string &name)
{
rule_loader::configuration cfg(rules_content, m_sources);
rule_loader::configuration cfg(rules_content, m_sources, name);
cfg.min_priority = m_min_priority;
cfg.output_extra = m_extra;
cfg.replace_output_container_info = m_replace_container_info;
cfg.default_ruleset_id = m_default_ruleset_id;
std::ostringstream os;
rule_reader reader;
bool success = reader.load(cfg, m_rule_loader);
if (success)
rule_loader::reader reader;
if (reader.read(cfg, m_rule_collector))
{
for (auto &src : m_sources)
{
src.ruleset = src.ruleset_factory->new_ruleset();
}
rule_loader::compiler compiler;
m_rules.clear();
success = m_rule_loader.compile(cfg, m_rules);
compiler.compile(cfg, m_rule_collector, m_rules);
}
if (!cfg.errors.empty())
if (cfg.res->successful())
{
os << cfg.errors.size() << " errors:" << std::endl;
for(auto &err : cfg.errors)
m_rule_stats_manager.clear();
for (const auto &r : m_rules)
{
os << err << std::endl;
m_rule_stats_manager.on_rule_loaded(r);
}
}
if (!cfg.warnings.empty())
{
os << cfg.warnings.size() << " warnings:" << std::endl;
for(auto &warn : cfg.warnings)
{
os << warn << std::endl;
}
}
if(!success)
{
throw falco_exception(os.str());
}
if (verbose && os.str() != "") {
// todo(jasondellaluce): introduce a logging callback in Falco
fprintf(stderr, "When reading rules content: %s", os.str().c_str());
}
return std::move(cfg.res);
}
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
void falco_engine::load_rules_file(const std::string &rules_filename, bool verbose, bool all_events)
{
uint64_t dummy;
std::string rules_content;
return load_rules_file(rules_filename, verbose, all_events, dummy);
read_file(rules_filename, rules_content);
std::unique_ptr<load_result> res = load_rules(rules_content, rules_filename);
interpret_load_result(res, rules_filename, rules_content, verbose);
}
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version)
std::unique_ptr<load_result> falco_engine::load_rules_file(const string &rules_filename)
{
ifstream is;
std::string rules_content;
is.open(rules_filename);
if (!is.is_open())
try {
read_file(rules_filename, rules_content);
}
catch (falco_exception &e)
{
throw falco_exception("Could not open rules filename " +
rules_filename + " " +
"for reading");
rule_loader::context ctx(rules_filename);
std::unique_ptr<rule_loader::result> res(new rule_loader::result(rules_filename));
res->add_error(load_result::LOAD_ERR_FILE_READ, e.what(), ctx);
return std::move(res);
}
string rules_content((istreambuf_iterator<char>(is)),
istreambuf_iterator<char>());
load_rules(rules_content, verbose, all_events, required_engine_version);
return load_rules(rules_content, rules_filename);
}
void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset)
@@ -246,7 +248,7 @@ void falco_engine::enable_rule(const string &substring, bool enabled, const stri
uint16_t ruleset_id = find_ruleset_id(ruleset);
bool match_exact = false;
for(auto &it : m_sources)
for(const auto &it : m_sources)
{
if(enabled)
{
@@ -264,7 +266,7 @@ void falco_engine::enable_rule_exact(const string &rule_name, bool enabled, cons
uint16_t ruleset_id = find_ruleset_id(ruleset);
bool match_exact = true;
for(auto &it : m_sources)
for(const auto &it : m_sources)
{
if(enabled)
{
@@ -281,7 +283,7 @@ void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, con
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
for(auto &it : m_sources)
for(const auto &it : m_sources)
{
if(enabled)
{
@@ -314,7 +316,7 @@ uint64_t falco_engine::num_rules_for_ruleset(const std::string &ruleset)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
uint64_t ret = 0;
for (auto &src : m_sources)
for (const auto &src : m_sources)
{
ret += src.ruleset->enabled_count(ruleset_id);
}
@@ -327,28 +329,44 @@ void falco_engine::evttypes_for_ruleset(std::string &source, std::set<uint16_t>
}
std::shared_ptr<gen_event_formatter> falco_engine::create_formatter(const std::string &source,
const std::string &output)
const std::string &output) const
{
return find_source(source)->formatter_factory->create_formatter(output);
}
unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id)
{
falco_rule rule;
if(should_drop_evt() || !find_source(source_idx)->ruleset->run(ev, rule, ruleset_id))
// note: there are no thread-safety guarantees on the filter_ruleset::run()
// method, but the thread-safety assumptions of falco_engine::process_event()
// imply that concurrent invokers use different and non-switchable values of
// source_idx, which means that at any time each filter_ruleset will only
// be accessed by a single thread.
const falco_source *source;
if(source_idx == m_syscall_source_idx)
{
source = m_syscall_source;
}
else
{
source = find_source(source_idx);
}
if(should_drop_evt() || !source || !source->ruleset->run(ev, source->m_rule, ruleset_id))
{
return unique_ptr<struct rule_result>();
}
unique_ptr<struct rule_result> res(new rule_result());
res->evt = ev;
res->rule = rule.name;
res->source = rule.source;
res->format = rule.output;
res->priority_num = rule.priority;
res->tags = rule.tags;
res->exception_fields = rule.exception_fields;
m_rule_stats_manager.on_event(rule);
res->rule = source->m_rule.name;
res->source = source->m_rule.source;
res->format = source->m_rule.output;
res->priority_num = source->m_rule.priority;
res->tags = source->m_rule.tags;
res->exception_fields = source->m_rule.exception_fields;
m_rule_stats_manager.on_event(source->m_rule);
return res;
}
@@ -364,7 +382,15 @@ std::size_t falco_engine::add_source(const std::string &source,
// evttype_index_ruleset is the default ruleset implementation
std::shared_ptr<filter_ruleset_factory> ruleset_factory(
new evttype_index_ruleset_factory(filter_factory));
return add_source(source, filter_factory, formatter_factory, ruleset_factory);
size_t idx = add_source(source, filter_factory, formatter_factory, ruleset_factory);
if(source == falco_common::syscall_source)
{
m_syscall_source_idx = idx;
m_syscall_source = find_source(m_syscall_source_idx);
}
return idx;
}
std::size_t falco_engine::add_source(const std::string &source,
@@ -381,7 +407,7 @@ std::size_t falco_engine::add_source(const std::string &source,
return m_sources.insert(src, source);
}
void falco_engine::describe_rule(string *rule)
void falco_engine::describe_rule(string *rule) const
{
static const char* rule_fmt = "%-50s %s\n";
fprintf(stdout, rule_fmt, "Rule", "Description");
@@ -402,7 +428,7 @@ void falco_engine::describe_rule(string *rule)
}
}
void falco_engine::print_stats()
void falco_engine::print_stats() const
{
string out;
m_rule_stats_manager.format(m_rules, out);
@@ -410,57 +436,110 @@ void falco_engine::print_stats()
fprintf(stdout, "%s", out.c_str());
}
bool falco_engine::is_source_valid(const std::string &source)
bool falco_engine::is_source_valid(const std::string &source) const
{
return m_sources.at(source) != nullptr;
}
bool falco_engine::check_plugin_requirements(
const std::vector<plugin_version_requirement>& plugins,
void falco_engine::read_file(const std::string& filename, std::string& contents)
{
ifstream is;
is.open(filename);
if (!is.is_open())
{
throw falco_exception("Could not open " + filename + " for reading");
}
contents.assign(istreambuf_iterator<char>(is),
istreambuf_iterator<char>());
}
void falco_engine::interpret_load_result(std::unique_ptr<load_result>& res,
const std::string& rules_filename,
const std::string& rules_content,
bool verbose)
{
falco::load_result::rules_contents_t rc = {{rules_filename, rules_content}};
if(!res->successful())
{
// The output here is always the full e.g. "verbose" output.
throw falco_exception(res->as_string(true, rc).c_str());
}
if(verbose && res->has_warnings())
{
// Here, verbose controls whether to additionally
// "log" e.g. print to stderr. What's logged is always
// non-verbose so it fits on a single line.
// todo(jasondellaluce): introduce a logging callback in Falco
fprintf(stderr, "%s\n", res->as_string(false, rc).c_str());
}
}
static bool check_plugin_requirement_alternatives(
const std::vector<falco_engine::plugin_version_requirement>& plugins,
const rule_loader::plugin_version_info::requirement_alternatives& alternatives,
std::string& err)
{
for (const auto &req : m_rule_loader.required_plugin_versions())
for (const auto &req : alternatives)
{
bool found = false;
for (const auto &plugin : plugins)
{
if (req.first == plugin.name)
if (req.name == plugin.name)
{
found = true;
sinsp_version req_version(req.version);
sinsp_version plugin_version(plugin.version);
if(!plugin_version.m_valid)
{
err = "Plugin '" + req.first
err = "Plugin '" + plugin.name
+ "' has invalid version string '"
+ plugin.version + "'";
return false;
}
for (const auto &reqver: req.second)
if (!plugin_version.check(req_version))
{
sinsp_version req_version(reqver);
if (!plugin_version.check(req_version))
{
err = "Plugin '" + plugin.name
+ "' version '" + plugin.version
+ "' is not compatible with required plugin version '"
+ reqver + "'";
return false;
}
err = "Plugin '" + plugin.name
+ "' version '" + plugin.version
+ "' is not compatible with required plugin version '"
+ req.version + "'";
return false;
}
return true;
}
}
if (!found)
}
return false;
}
bool falco_engine::check_plugin_requirements(
const std::vector<plugin_version_requirement>& plugins,
std::string& err) const
{
err = "";
for (const auto &alternatives : m_rule_collector.required_plugin_versions())
{
if (!check_plugin_requirement_alternatives(plugins, alternatives, err))
{
err = "Plugin '" + req.first + "' is required but not loaded";
if (err.empty())
{
for (const auto& req : alternatives)
{
err += err.empty() ? "" : ", ";
err += req.name + " (>= " + req.version + ")";
}
err = "Plugin requirement not satisfied, must load one of: " + err;
}
return false;
}
}
return true;
}
void falco_engine::complete_rule_loading()
void falco_engine::complete_rule_loading() const
{
for (auto &src : m_sources)
for (const auto &src : m_sources)
{
src.ruleset->on_loading_complete();
}
@@ -482,7 +561,7 @@ void falco_engine::set_extra(string &extra, bool replace_container_info)
m_replace_container_info = replace_container_info;
}
inline bool falco_engine::should_drop_evt()
inline bool falco_engine::should_drop_evt() const
{
if(m_sampling_multiplier == 0)
{

View File

@@ -22,6 +22,7 @@ limitations under the License.
#pragma once
#include <atomic>
#include <string>
#include <memory>
#include <set>
@@ -31,9 +32,11 @@ limitations under the License.
#include "gen_filter.h"
#include "filter_ruleset.h"
#include "rule_loader.h"
#include "rule_loader_collector.h"
#include "stats_manager.h"
#include "falco_common.h"
#include "falco_source.h"
#include "falco_load_result.h"
//
// This class acts as the primary interface between a program and the
@@ -55,7 +58,7 @@ public:
// Print to stdout (using printf) a description of each field supported by this engine.
// If source is non-empty, only fields for the provided source are printed.
void list_fields(std::string &source, bool verbose, bool names_only, bool markdown);
void list_fields(std::string &source, bool verbose, bool names_only, bool markdown) const;
//
// Load rules either directly or from a filename.
@@ -64,11 +67,10 @@ public:
void load_rules(const std::string &rules_content, bool verbose, bool all_events);
//
// Identical to above, but also returns the required engine version for the file/content.
// (If no required engine version is specified, returns 0).
//
void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version);
void load_rules(const std::string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version);
// Identical to above, but returns a result object instead of
// throwing exceptions on error.
std::unique_ptr<falco::load_result> load_rules_file(const std::string &rules_filename);
std::unique_ptr<falco::load_result> load_rules(const std::string &rules_content, const std::string &name);
//
// Enable/Disable any rules matching the provided substring.
@@ -98,7 +100,7 @@ public:
// Internally, this can be used to release unused resources before starting
// processing events with process_event().
//
void complete_rule_loading();
void complete_rule_loading() const;
// Only load rules having this priority or more severe.
void set_min_priority(falco_common::priority_type priority);
@@ -121,12 +123,12 @@ public:
// Print details on the given rule. If rule is NULL, print
// details on all rules.
//
void describe_rule(std::string *rule);
void describe_rule(std::string *rule) const;
//
// Print statistics on how many events matched each rule.
//
void print_stats();
void print_stats() const;
//
// Set the sampling ratio, which can affect which events are
@@ -165,18 +167,33 @@ public:
//
// 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 rule that matched. If no rule matched, returns nullptr.
//
// 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.
// This method should be invoked only after having initialized and
// configured the engine. In particular, invoking this with a source_idx
// not previosly-returned by a call to add_source() would cause a
// falco_exception to be thrown.
//
// the returned rule_result is allocated and must be delete()d.
// This method is thread-safe only with the assumption that every invoker
// uses a different source_idx. Moreover, each invoker must not switch
// source_idx in subsequent invocations of this method.
// Considering that each invoker is related to a unique event source, it
// is safe to assume that each invoker will pass a different event
// to this method too, since two distinct sources cannot possibly produce
// the same event. Lastly, filterchecks and formatters (and their factories)
// that are used to populate the conditions for a given event-source
// ruleset must not be reused across rulesets of other event sources.
// These assumptions guarantee thread-safety because internally the engine
// is partitioned by event sources. However, each ruleset assigned to each
// event source is not thread-safe of its own, so invoking this method
// concurrently with the same source_idx would inherently cause data races
// and lead to undefined behavior.
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id);
//
// Wrapper assuming the default ruleset
// Wrapper assuming the default ruleset.
//
// This inherits the same thread-safety guarantees.
//
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev);
@@ -188,7 +205,7 @@ public:
std::size_t add_source(const std::string &source,
std::shared_ptr<gen_event_filter_factory> filter_factory,
std::shared_ptr<gen_event_formatter_factory> formatter_factory);
//
// Equivalent to above, but allows specifying a ruleset factory
// for the newly added source.
@@ -200,7 +217,7 @@ public:
// Return whether or not there is a valid filter/formatter
// factory for this source.
bool is_source_valid(const std::string &source);
bool is_source_valid(const std::string &source) const;
//
// Given an event source and ruleset, fill in a bitset
@@ -216,10 +233,10 @@ public:
// event.
//
std::shared_ptr<gen_event_formatter> create_formatter(const std::string &source,
const std::string &output);
const std::string &output) const;
// The rule loader definition is aliased as it is exactly what we need
typedef rule_loader::plugin_version_info plugin_version_requirement;
typedef rule_loader::plugin_version_info::requirement plugin_version_requirement;
//
// Returns true if the provided list of plugins satisfies all the
@@ -230,22 +247,39 @@ public:
//
bool check_plugin_requirements(
const std::vector<plugin_version_requirement>& plugins,
std::string& err);
std::string& err) const;
private:
// Throws falco_exception if the file can not be read
void read_file(const std::string& filename, std::string& contents);
// For load_rules methods that throw exceptions on error,
// interpret a load_result and throw an exception if needed.
void interpret_load_result(std::unique_ptr<falco::load_result>& res,
const std::string& rules_filename,
const std::string& rules_content,
bool verbose);
indexed_vector<falco_source> m_sources;
falco_source* find_source(std::size_t index);
falco_source* find_source(const std::string& name);
const falco_source* find_source(std::size_t index) const;
const falco_source* find_source(const std::string& name) const;
// To allow the engine to be extremely fast for syscalls (can
// be > 1M events/sec), we save the syscall source/source_idx
// separately and check it explicitly in process_event()
const falco_source* m_syscall_source;
std::atomic<size_t> m_syscall_source_idx;
//
// Determine whether the given event should be matched at all
// against the set of rules, given the current sampling
// ratio/multiplier.
//
inline bool should_drop_evt();
inline bool should_drop_evt() const;
rule_loader m_rule_loader;
rule_loader::collector m_rule_collector;
indexed_vector<falco_rule> m_rules;
stats_manager m_rule_stats_manager;

View File

@@ -16,9 +16,9 @@ limitations under the License.
// The version of rules/filter fields/etc supported by this Falco
// engine.
#define FALCO_ENGINE_VERSION (13)
#define FALCO_ENGINE_VERSION (15)
// This is the result of running "falco --list -N | sha256sum" and
// represents the fields supported by this version of Falco. It's used
// at build time to detect a changed set of fields.
#define FALCO_FIELDS_CHECKSUM "94290ff98e5affc85b2287b09a3f4054918f14e90db1ac4bfd6d5ce4e910329c"
#define FALCO_FIELDS_CHECKSUM "674c6cf2bc1c105038c8676f018fa3d1431d86597df428453441f5d859cad284"

View File

@@ -0,0 +1,104 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "falco_load_result.h"
static const std::string error_codes[] = {
"LOAD_ERR_FILE_READ",
"LOAD_ERR_YAML_PARSE",
"LOAD_ERR_YAML_VALIDATE",
"LOAD_ERR_COMPILE_CONDITION",
"LOAD_ERR_COMPILE_OUTPUT",
"LOAD_ERR_VALIDATE"
};
const std::string& falco::load_result::error_code_str(error_code ec)
{
return error_codes[ec];
}
static const std::string error_strings[] = {
"File read error",
"YAML parse error",
"Error validating internal structure of YAML file",
"Error compiling condition",
"Error compiling output",
"Error validating rule/macro/list/exception objects"
};
const std::string& falco::load_result::error_str(error_code ec)
{
return error_strings[ec];
}
static const std::string error_descs[] = {
"This occurs when falco can not read a given file. Check permissions and whether the file exists.",
"This occurs when the rules content is not valid YAML.",
"This occurs when the internal structure of the YAML file is incorrect. Examples include not consisting of a sequence of maps, a given rule/macro/list item not having required keys, values not having the right type (e.g. the items property of a list not being a sequence), etc.",
"This occurs when a condition string can not be compiled to a filter object.",
"This occurs when an output string can not be compiled to an output object.",
"This occurs when a rule/macro/list item is incorrect. Examples include a condition field referring to an undefined macro, falco engine/plugin version mismatches, items with append without any existing item, exception fields/comps having different lengths, etc."
};
const std::string& falco::load_result::error_desc(error_code ec)
{
return error_strings[ec];
}
static const std::string warning_codes[] = {
"LOAD_UNKNOWN_SOURCE",
"LOAD_UNSAFE_NA_CHECK",
"LOAD_NO_EVTTYPE",
"LOAD_UNKNOWN_FIELD",
"LOAD_UNUSED_MACRO",
"LOAD_UNUSED_LIST",
"LOAD_UNKNOWN_ITEM"
};
const std::string& falco::load_result::warning_code_str(warning_code wc)
{
return warning_codes[wc];
}
static const std::string warning_strings[] = {
"Unknown event source",
"Unsafe <NA> comparison in condition",
"Condition has no event-type restriction",
"Unknown field in condition",
"Unused macro",
"Unused list",
"Unknown rules file item"
};
const std::string& falco::load_result::warning_str(warning_code wc)
{
return warning_strings[wc];
}
static const std::string warning_descs[] = {
"A rule has a unknown event source. This can occur when reading rules content without having a corresponding plugin loaded, etc. The rule will be silently ignored.",
"Comparing a field value with <NA> is unsafe and can lead to unpredictable behavior of the rule condition. If you need to check for the existence of a field, consider using the 'exists' operator instead.",
"A rule condition matches too many evt.type values. This has a significant performance penalty. Make the condition more specific by adding an evt.type field or further restricting the number of evt.type values in the condition.",
"A rule condition refers to a field that does not exist. This is normally an error, but if a rule has a skip-if-unknown-filter property, the error is downgraded to a warning.",
"A macro is defined in the rules content but is not used by any other macro or rule.",
"A list is defined in the rules content but is not used by any other list, macro, or rule.",
"An unknown top-level object is in the rules content. It will be ignored."
};
const std::string& falco::load_result::warning_desc(warning_code wc)
{
return warning_descs[wc];
}

View File

@@ -0,0 +1,112 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <functional>
#include <string>
#include <nlohmann/json.hpp>
namespace falco
{
// Represents the result of loading a rules file.
class load_result {
public:
enum error_code {
LOAD_ERR_FILE_READ = 0,
LOAD_ERR_YAML_PARSE,
LOAD_ERR_YAML_VALIDATE,
LOAD_ERR_COMPILE_CONDITION,
LOAD_ERR_COMPILE_OUTPUT,
LOAD_ERR_VALIDATE
};
// The error code as a string
static const std::string& error_code_str(error_code ec);
// A short string representation of the error
static const std::string& error_str(error_code ec);
// A longer description of what the error represents and the
// impact.
static const std::string& error_desc(error_code ec);
enum warning_code {
LOAD_UNKNOWN_SOURCE = 0,
LOAD_UNSAFE_NA_CHECK,
LOAD_NO_EVTTYPE,
LOAD_UNKNOWN_FIELD,
LOAD_UNUSED_MACRO,
LOAD_UNUSED_LIST,
LOAD_UNKNOWN_ITEM
};
virtual ~load_result() = default;
// The warning code as a string
static const std::string& warning_code_str(warning_code ec);
// A short string representation of the warning
static const std::string& warning_str(warning_code ec);
// A longer description of what the warning represents and the
// impact.
static const std::string& warning_desc(warning_code ec);
// If true, the rules were loaded successfully and can be used
// against events. If false, there were one or more
// errors--use one of the as_xxx methods to return information
// about why the rules could not be loaded.
virtual bool successful() = 0;
// If true, there were one or more warnings. successful() and
// has_warnings() can both be true if there were only warnings.
virtual bool has_warnings() = 0;
// This represents a set of rules contents as a mapping from
// rules content name (usually filename) to rules content. The
// rules content is actually a reference to the actual string
// to avoid copies. Using reference_wrapper allows the
// reference to be held in the stl map (bare references can't
// be copied/assigned, but reference_wrappers can).
//
// It's used in the as_string/as_json() methods below.
typedef std::map<std::string, std::reference_wrapper<const std::string>> rules_contents_t;
// This contains a human-readable version of the result,
// suitable for display to end users.
//
// The provided rules_contents_t should map from content name
// to rules content (reference) for each rules_content that has
// been passed to rule_loader::compile() or
// rule_reader::load().
//
// When verbose is true, the returned value has full details
// on the result including document locations/context.
//
// When verbose is false, the returned value is a short string
// with the success value and a list of
// errors/warnings. Suitable for simple one-line display.
virtual const std::string& as_string(bool verbose, const rules_contents_t& contents) = 0;
// This contains the full result structure as json, suitable
// for automated parsing/interpretation downstream.
virtual const nlohmann::json& as_json(const rules_contents_t& contents) = 0;
};
} // namespace falco

View File

@@ -26,6 +26,12 @@ limitations under the License.
*/
struct falco_rule
{
falco_rule(): id(0), priority(falco_common::PRIORITY_DEBUG) {}
falco_rule(falco_rule&&) = default;
falco_rule& operator = (falco_rule&&) = default;
falco_rule(const falco_rule&) = default;
falco_rule& operator = (const falco_rule&) = default;
std::size_t id;
std::string source;
std::string name;

View File

@@ -26,13 +26,34 @@ limitations under the License.
*/
struct falco_source
{
falco_source() = default;
falco_source(falco_source&&) = default;
falco_source& operator = (falco_source&&) = default;
falco_source(const falco_source& s):
name(s.name),
ruleset_factory(s.ruleset_factory),
filter_factory(s.filter_factory),
formatter_factory(s.formatter_factory) { };
falco_source& operator = (const falco_source& s)
{
name = s.name;
ruleset_factory = s.ruleset_factory;
filter_factory = s.filter_factory;
formatter_factory = s.formatter_factory;
return *this;
};
std::string name;
std::shared_ptr<filter_ruleset> ruleset;
std::shared_ptr<filter_ruleset_factory> ruleset_factory;
std::shared_ptr<gen_event_filter_factory> filter_factory;
std::shared_ptr<gen_event_formatter_factory> formatter_factory;
inline bool is_field_defined(std::string field) const
// Used by the filter_ruleset interface. Filled in when a rule
// matches an event.
mutable falco_rule m_rule;
inline bool is_field_defined(const std::string& field) const
{
auto *chk = filter_factory->new_filtercheck(field.c_str());
if (chk)

View File

@@ -17,41 +17,42 @@ limitations under the License.
#include "filter_evttype_resolver.h"
#include <sinsp.h>
using namespace std;
using namespace libsinsp::filter;
extern sinsp_evttables g_infotables;
static bool is_evttype_operator(const string& op)
static bool is_evttype_operator(const std::string& op)
{
return op == "==" || op == "=" || op == "!=" || op == "in";
}
void filter_evttype_resolver::visitor::inversion(set<uint16_t>& types)
size_t falco_event_types::get_ppm_event_max()
{
set<uint16_t> all_types;
return PPM_EVENT_MAX;
}
void filter_evttype_resolver::visitor::inversion(falco_event_types& types)
{
falco_event_types all_types;
evttypes("", all_types);
if (types != all_types) // we don't invert the "all types" set
{
set<uint16_t> diff = types;
types.clear();
set_difference(
all_types.begin(), all_types.end(), diff.begin(), diff.end(),
inserter(types, types.begin()));
types = all_types.diff(types);
}
}
void filter_evttype_resolver::visitor::evttypes(string evtname, set<uint16_t>& out)
void filter_evttype_resolver::visitor::evttypes(const std::string& evtname, falco_event_types& out)
{
// Fill in from 2 to PPM_EVENT_MAX-1. 0 and 1 are excluded as
// those are PPM_GENERIC_E/PPME_GENERIC_X
const struct ppm_event_info* etable = g_infotables.m_event_info;
for(uint16_t i = 2; i < PPM_EVENT_MAX; i++)
{
// Skip "old" event versions, unused events, or events not matching
// the requested evtname
if(!(etable[i].flags & (EF_OLD_VERSION | EF_UNUSED))
&& (evtname.empty() || string(etable[i].name) == evtname))
// Skip unused events or events not matching the requested evtname
if(!(etable[i].flags & EF_UNUSED)
&& (evtname.empty() || std::string(etable[i].name) == evtname))
{
out.insert(i);
}
@@ -60,42 +61,38 @@ void filter_evttype_resolver::visitor::evttypes(string evtname, set<uint16_t>& o
void filter_evttype_resolver::evttypes(
ast::expr* filter,
set<uint16_t>& out) const
std::set<uint16_t>& out) const
{
visitor v;
v.m_expect_value = false;
v.m_last_node_evttypes.clear();
filter->accept(&v);
out.insert(v.m_last_node_evttypes.begin(), v.m_last_node_evttypes.end());
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;});
}
void filter_evttype_resolver::evttypes(
shared_ptr<ast::expr> filter,
set<uint16_t>& out) const
std::set<uint16_t>& out) const
{
visitor v;
v.m_expect_value = false;
v.m_last_node_evttypes.clear();
filter.get()->accept(&v);
out.insert(v.m_last_node_evttypes.begin(), v.m_last_node_evttypes.end());
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;} );
}
// "and" nodes evttypes are the intersection of the evttypes of their children.
// we initialize the set with "all event types"
void filter_evttype_resolver::visitor::visit(ast::and_expr* e)
{
set<uint16_t> types, inters;
falco_event_types types;
evttypes("", types);
m_last_node_evttypes.clear();
for (auto &c : e->children)
{
inters.clear();
falco_event_types inters;
c->accept(this);
set_intersection(
types.begin(), types.end(),
m_last_node_evttypes.begin(), m_last_node_evttypes.end(),
inserter(inters, inters.begin()));
types = inters;
types = types.intersect(m_last_node_evttypes);
}
m_last_node_evttypes = types;
}
@@ -103,12 +100,12 @@ void filter_evttype_resolver::visitor::visit(ast::and_expr* e)
// "or" nodes evttypes are the union of the evttypes their children
void filter_evttype_resolver::visitor::visit(ast::or_expr* e)
{
set<uint16_t> types;
falco_event_types types;
m_last_node_evttypes.clear();
for (auto &c : e->children)
{
c->accept(this);
types.insert(m_last_node_evttypes.begin(), m_last_node_evttypes.end());
types.merge(m_last_node_evttypes);
}
m_last_node_evttypes = types;
}

View File

@@ -20,6 +20,128 @@ limitations under the License.
#include <string>
#include <set>
#include <memory>
#include <functional>
#include <stdexcept>
class falco_event_types
{
private:
using vec_t = std::vector<uint8_t>;
vec_t m_types{};
static inline void check_range(uint16_t e)
{
static const auto enum_max = get_ppm_event_max();
if(e > enum_max)
{
throw std::range_error("invalid event type");
}
}
public:
falco_event_types(falco_event_types&&) = default;
falco_event_types(const falco_event_types&) = default;
falco_event_types& operator=(falco_event_types&&) = default;
falco_event_types& operator=(const falco_event_types&) = default;
static size_t get_ppm_event_max();
inline falco_event_types():
m_types(get_ppm_event_max() + 1, 0)
{
}
inline void insert(uint16_t e)
{
check_range(e);
m_types[e] = 1;
}
void merge(const falco_event_types& other)
{
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
{
m_types[i] |= other.m_types[i];
}
}
void merge(const std::set<uint16_t>& other)
{
for(const auto& e : other)
{
insert(e);
}
}
inline bool contains(uint16_t e) const
{
check_range(e);
return m_types[e] != 0;
}
void clear()
{
for(auto& v : m_types)
{
v = 0;
}
}
bool equals(const falco_event_types& other) const
{
return m_types == other.m_types;
}
falco_event_types diff(const falco_event_types& other)
{
falco_event_types ret;
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
{
if(m_types[i] == 1 && other.m_types[i] == 0)
{
ret.m_types[i] = 1;
}
}
return ret;
}
falco_event_types intersect(const falco_event_types& other)
{
falco_event_types ret;
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
{
if(m_types[i] == 1 && other.m_types[i] == 1)
{
ret.m_types[i] = 1;
}
}
return ret;
}
void for_each(std::function<bool(uint16_t)> consumer) const
{
for(uint16_t i = 0; i < m_types.size(); ++i)
{
if(m_types[i] != 0)
{
if(!consumer(i))
{
return;
}
}
}
}
};
inline bool operator==(const falco_event_types& lhs, const falco_event_types& rhs)
{
return lhs.equals(rhs);
}
inline bool operator!=(const falco_event_types& lhs, const falco_event_types& rhs)
{
return !(lhs == rhs);
}
/*!
\brief Helper class for finding event types
@@ -35,9 +157,12 @@ public:
string is passed, all the available evttypes are collected
\param out The set to be filled with the evttypes
*/
inline void evttypes(std::string evtname, std::set<uint16_t>& out) const
inline void evttypes(const std::string& evtname, falco_event_types& out) const
{
visitor().evttypes(evtname, out);
falco_event_types evt_types;
visitor().evttypes(evtname, evt_types);
evt_types.for_each([&out](uint16_t val)
{out.insert(val); return true; });
}
/*!
@@ -63,8 +188,14 @@ public:
private:
struct visitor : public libsinsp::filter::ast::expr_visitor
{
visitor(): m_expect_value(false) {}
visitor(visitor&&) = default;
visitor& operator = (visitor&&) = default;
visitor(const visitor&) = default;
visitor& operator = (const visitor&) = default;
bool m_expect_value;
std::set<uint16_t> m_last_node_evttypes;
falco_event_types m_last_node_evttypes;
void visit(libsinsp::filter::ast::and_expr* e) override;
void visit(libsinsp::filter::ast::or_expr* e) override;
@@ -73,7 +204,7 @@ private:
void visit(libsinsp::filter::ast::list_expr* e) override;
void visit(libsinsp::filter::ast::unary_check_expr* e) override;
void visit(libsinsp::filter::ast::binary_check_expr* e) override;
void inversion(std::set<uint16_t>& types);
void evttypes(std::string evtname, std::set<uint16_t>& out);
void inversion(falco_event_types& types);
void evttypes(const std::string& evtname, falco_event_types& out);
};
};

View File

@@ -27,13 +27,12 @@ bool filter_macro_resolver::run(libsinsp::filter::ast::expr*& filter)
v.m_unknown_macros = &m_unknown_macros;
v.m_resolved_macros = &m_resolved_macros;
v.m_macros = &m_macros;
v.m_last_node_changed = false;
v.m_last_node = filter;
v.m_node_substitute = nullptr;
filter->accept(&v);
if (v.m_last_node_changed)
if (v.m_node_substitute)
{
delete filter;
filter = v.m_last_node;
filter = v.m_node_substitute.release();
}
return !m_resolved_macros.empty();
}
@@ -46,12 +45,11 @@ bool filter_macro_resolver::run(std::shared_ptr<libsinsp::filter::ast::expr>& fi
v.m_unknown_macros = &m_unknown_macros;
v.m_resolved_macros = &m_resolved_macros;
v.m_macros = &m_macros;
v.m_last_node_changed = false;
v.m_last_node = filter.get();
v.m_node_substitute = nullptr;
filter->accept(&v);
if (v.m_last_node_changed)
if (v.m_node_substitute)
{
filter.reset(v.m_last_node);
filter = std::move(v.m_node_substitute);
}
return !m_resolved_macros.empty();
}
@@ -63,12 +61,12 @@ void filter_macro_resolver::set_macro(
m_macros[name] = macro;
}
const set<string>& filter_macro_resolver::get_unknown_macros() const
const unordered_set<string>& filter_macro_resolver::get_unknown_macros() const
{
return m_unknown_macros;
}
const set<string>& filter_macro_resolver::get_resolved_macros() const
const unordered_set<string>& filter_macro_resolver::get_resolved_macros() const
{
return m_resolved_macros;
}
@@ -78,14 +76,12 @@ void filter_macro_resolver::visitor::visit(ast::and_expr* e)
for (size_t i = 0; i < e->children.size(); i++)
{
e->children[i]->accept(this);
if (m_last_node_changed)
if (m_node_substitute)
{
delete e->children[i];
e->children[i] = m_last_node;
e->children[i] = std::move(m_node_substitute);
}
}
m_last_node = e;
m_last_node_changed = false;
m_node_substitute = nullptr;
}
void filter_macro_resolver::visitor::visit(ast::or_expr* e)
@@ -93,46 +89,39 @@ void filter_macro_resolver::visitor::visit(ast::or_expr* e)
for (size_t i = 0; i < e->children.size(); i++)
{
e->children[i]->accept(this);
if (m_last_node_changed)
if (m_node_substitute)
{
delete e->children[i];
e->children[i] = m_last_node;
e->children[i] = std::move(m_node_substitute);
}
}
m_last_node = e;
m_last_node_changed = false;
m_node_substitute = nullptr;
}
void filter_macro_resolver::visitor::visit(ast::not_expr* e)
{
e->child->accept(this);
if (m_last_node_changed)
if (m_node_substitute)
{
delete e->child;
e->child = m_last_node;
e->child = std::move(m_node_substitute);
}
m_last_node = e;
m_last_node_changed = false;
m_node_substitute = nullptr;
}
void filter_macro_resolver::visitor::visit(ast::list_expr* e)
{
m_last_node = e;
m_last_node_changed = false;
m_node_substitute = nullptr;
}
void filter_macro_resolver::visitor::visit(ast::binary_check_expr* e)
{
// avoid exploring checks, so that we can be sure that each
// value_expr* node visited is a macro identifier
m_last_node = e;
m_last_node_changed = false;
m_node_substitute = nullptr;
}
void filter_macro_resolver::visitor::visit(ast::unary_check_expr* e)
{
m_last_node = e;
m_last_node_changed = false;
m_node_substitute = nullptr;
}
void filter_macro_resolver::visitor::visit(ast::value_expr* e)
@@ -143,15 +132,20 @@ void filter_macro_resolver::visitor::visit(ast::value_expr* e)
auto macro = m_macros->find(e->value);
if (macro != m_macros->end() && macro->second) // skip null-ptr macros
{
ast::expr* new_node = ast::clone(macro->second.get());
new_node->accept(this); // this sets m_last_node
m_last_node_changed = true;
m_node_substitute = nullptr;
auto new_node = ast::clone(macro->second.get());
new_node->accept(this);
// new_node might already have set a non-NULL m_node_substitute.
// if not, the right substituted is the newly-cloned node.
if (!m_node_substitute)
{
m_node_substitute = std::move(new_node);
}
m_resolved_macros->insert(e->value);
}
else
{
m_last_node = e;
m_last_node_changed = false;
m_node_substitute = nullptr;
m_unknown_macros->insert(e->value);
}
}

View File

@@ -18,8 +18,8 @@ limitations under the License.
#include <filter/parser.h>
#include <string>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <memory>
/*!
@@ -63,7 +63,7 @@ class filter_macro_resolver
substituted during the last invocation of run(). Should be
non-empty if the last invocation of run() returned true.
*/
const std::set<std::string>& get_resolved_macros() const;
const std::unordered_set<std::string>& get_resolved_macros() const;
/*!
\brief Returns a set containing the names of all the macros
@@ -71,20 +71,25 @@ class filter_macro_resolver
A macro remains unresolved if it is found inside the processed
filter but it was not defined with set_macro();
*/
const std::set<std::string>& get_unknown_macros() const;
const std::unordered_set<std::string>& get_unknown_macros() const;
private:
typedef std::map<
typedef std::unordered_map<
std::string,
std::shared_ptr<libsinsp::filter::ast::expr>
> macro_defs;
struct visitor : public libsinsp::filter::ast::expr_visitor
{
bool m_last_node_changed;
libsinsp::filter::ast::expr* m_last_node;
std::set<std::string>* m_unknown_macros;
std::set<std::string>* m_resolved_macros;
visitor() = default;
visitor(visitor&&) = default;
visitor& operator = (visitor&&) = default;
visitor(const visitor&) = delete;
visitor& operator = (const visitor&) = delete;
std::unique_ptr<libsinsp::filter::ast::expr> m_node_substitute;
std::unordered_set<std::string>* m_unknown_macros;
std::unordered_set<std::string>* m_resolved_macros;
macro_defs* m_macros;
void visit(libsinsp::filter::ast::and_expr* e) override;
@@ -96,7 +101,7 @@ class filter_macro_resolver
void visit(libsinsp::filter::ast::binary_check_expr* e) override;
};
std::set<std::string> m_unknown_macros;
std::set<std::string> m_resolved_macros;
std::unordered_set<std::string> m_unknown_macros;
std::unordered_set<std::string> m_resolved_macros;
macro_defs m_macros;
};

View File

@@ -151,5 +151,7 @@ public:
class filter_ruleset_factory
{
public:
virtual ~filter_ruleset_factory() = default;
virtual std::shared_ptr<filter_ruleset> new_ruleset() = 0;
};

View File

@@ -17,8 +17,9 @@ limitations under the License.
#include <sinsp.h>
#include "filter_warning_resolver.h"
using namespace falco;
static const char* no_value = "<NA>";
static const char* warn_unsafe_na_check = "unsafe-na-check";
static inline bool is_unsafe_field(const string& f)
{
@@ -34,7 +35,7 @@ static inline bool is_equality_operator(const string& op)
bool filter_warning_resolver::run(
libsinsp::filter::ast::expr* filter,
std::set<string>& warnings) const
std::set<load_result::warning_code>& warnings) const
{
visitor v;
auto size = warnings.size();
@@ -44,22 +45,6 @@ bool filter_warning_resolver::run(
return warnings.size() > size;
}
// todo(jasondellaluce): use an hard-coded map once we support more warnings
bool filter_warning_resolver::format(
const std::string& code,
std::string& out) const
{
if (code == warn_unsafe_na_check)
{
out = "comparing a field value with <NA> is unsafe and can lead to "
"unpredictable behavior of the rule condition. If you need to "
" check for the existence of a field, consider using the "
"'exists' operator instead.";
return true;
}
return false;
}
void filter_warning_resolver::visitor::visit(
libsinsp::filter::ast::binary_check_expr* e)
{
@@ -76,7 +61,7 @@ void filter_warning_resolver::visitor::visit(
{
if (m_is_equality_check && e->value == no_value)
{
m_warnings->insert(warn_unsafe_na_check);
m_warnings->insert(load_result::LOAD_UNSAFE_NA_CHECK);
}
}
@@ -86,6 +71,6 @@ void filter_warning_resolver::visitor::visit(
if (m_is_equality_check
&& std::find(e->values.begin(), e->values.end(), no_value) != e->values.end())
{
m_warnings->insert(warn_unsafe_na_check);
m_warnings->insert(load_result::LOAD_UNSAFE_NA_CHECK);
}
}
}

View File

@@ -21,6 +21,7 @@ limitations under the License.
#include <set>
#include <memory>
#include "falco_common.h"
#include "falco_load_result.h"
/*!
\brief Searches for bad practices in filter conditions and
@@ -42,40 +43,19 @@ public:
*/
bool run(
libsinsp::filter::ast::expr* filter,
std::set<std::string>& warnings) const;
/*!
\brief Given a warning code retrieved through run(), returns
a verbose message describing the problem of the warning.
\param code The warning code string
\param out The string to be filled-out with the warning message
\return true if the warning code is recognized, false otherwise
*/
bool format(const std::string& code, std::string& out) const;
/*!
\brief Given a warning code retrieved through run(), returns
a verbose message describing the problem of the warning.
\param code The warning code string
\return The warning message string
\throw falco_exception if the warning code is not recognized
*/
inline std::string format(const std::string& code) const
{
std::string v;
if (!format(code, v))
{
throw falco_exception("unrecognized warning code: " + code);
}
return v;
}
std::set<falco::load_result::warning_code>& warnings) const;
private:
struct visitor : public libsinsp::filter::ast::base_expr_visitor
{
visitor(): m_is_equality_check(false) {}
visitor(visitor&&) = default;
visitor& operator = (visitor&&) = default;
visitor(const visitor&) = delete;
visitor& operator = (const visitor&) = delete;
bool m_is_equality_check;
std::set<std::string>* m_warnings;
std::set<falco::load_result::warning_code>* m_warnings;
void visit(libsinsp::filter::ast::value_expr* e) override;
void visit(libsinsp::filter::ast::list_expr* e) override;

View File

@@ -20,7 +20,7 @@ limitations under the License.
#include "falco_engine.h"
#include "banned.h" // This raises a compilation error when certain functions are used
falco_formats::falco_formats(std::shared_ptr<falco_engine> engine,
falco_formats::falco_formats(std::shared_ptr<const falco_engine> engine,
bool json_include_output_property,
bool json_include_tags_property)
: m_falco_engine(engine),
@@ -34,7 +34,8 @@ falco_formats::~falco_formats()
}
string falco_formats::format_event(gen_event *evt, const std::string &rule, const std::string &source,
const std::string &level, const std::string &format, std::set<std::string> &tags)
const std::string &level, const std::string &format, std::set<std::string> &tags,
const std::string &hostname) const
{
string line;
@@ -83,6 +84,7 @@ string falco_formats::format_event(gen_event *evt, const std::string &rule, cons
event["rule"] = rule;
event["priority"] = level;
event["source"] = source;
event["hostname"] = hostname;
if(m_json_include_output_property)
{
@@ -130,7 +132,7 @@ string falco_formats::format_event(gen_event *evt, const std::string &rule, cons
}
map<string, string> falco_formats::get_field_values(gen_event *evt, const std::string &source,
const std::string &format)
const std::string &format) const
{
std::shared_ptr<gen_event_formatter> formatter;

View File

@@ -24,19 +24,20 @@ limitations under the License.
class falco_formats
{
public:
falco_formats(std::shared_ptr<falco_engine> engine,
falco_formats(std::shared_ptr<const falco_engine> engine,
bool json_include_output_property,
bool json_include_tags_property);
virtual ~falco_formats();
std::string format_event(gen_event *evt, const std::string &rule, const std::string &source,
const std::string &level, const std::string &format, std::set<std::string> &tags);
const std::string &level, const std::string &format, std::set<std::string> &tags,
const std::string &hostname) const;
map<string, string> get_field_values(gen_event *evt, const std::string &source,
const std::string &format);
const std::string &format) const ;
protected:
std::shared_ptr<falco_engine> m_falco_engine;
std::shared_ptr<const falco_engine> m_falco_engine;
bool m_json_include_output_property;
bool m_json_include_tags_property;
};

View File

@@ -28,7 +28,12 @@ template <typename T>
class indexed_vector
{
public:
indexed_vector() = default;
virtual ~indexed_vector() = default;
indexed_vector(indexed_vector&&) = default;
indexed_vector& operator = (indexed_vector&&) = default;
indexed_vector(const indexed_vector&) = default;
indexed_vector& operator = (const indexed_vector&) = default;
/*!
\brief Returns the number of elements
@@ -68,7 +73,7 @@ public:
\param index String index of the element to be added in the vector
\return The numeric index assigned to the element
*/
virtual inline size_t insert(T& entry, const std::string& index)
virtual inline size_t insert(const T& entry, const std::string& index)
{
size_t id;
auto prev = m_index.find(index);
@@ -89,7 +94,7 @@ public:
*/
virtual inline T* at(size_t id) const
{
if (id <= m_entries.size())
if (id < m_entries.size())
{
return (T* const) &m_entries[id];
}
@@ -131,5 +136,5 @@ public:
private:
std::vector<T> m_entries;
std::map<std::string, size_t> m_index;
std::unordered_map<std::string, size_t> m_index;
};

View File

@@ -515,16 +515,17 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
size_t idx_len = 0;
for(auto &info : m_info.m_fields)
for(const auto &info : get_info().m_fields)
{
if(m_aliases.find(info.m_name) == m_aliases.end())
auto iter = get_aliases().find(info.m_name);
if( iter == get_aliases().end())
{
throw falco_exception("Could not find alias for field name " + info.m_name);
}
m_uses_paths = info.m_uses_paths;
auto &al = m_aliases[info.m_name];
auto &al = iter->second;
// What follows the match must not be alphanumeric or a dot
if(strncmp(info.m_name.c_str(), str, info.m_name.size()) == 0 &&
@@ -692,11 +693,6 @@ size_t json_event_filter_check::parsed_size()
}
}
json_event_filter_check::check_info &json_event_filter_check::get_info()
{
return m_info;
}
void json_event_filter_check::add_extracted_value(const std::string &str)
{
m_evalues.first.emplace_back(json_event_value(str));
@@ -793,9 +789,9 @@ std::string jevt_filter_check::s_jevt_rawtime_field = "jevt.rawtime";
std::string jevt_filter_check::s_jevt_value_field = "jevt.value";
std::string jevt_filter_check::s_jevt_obj_field = "jevt.obj";
jevt_filter_check::jevt_filter_check()
const jevt_filter_check::check_info &jevt_filter_check::get_info() const
{
m_info = {"jevt",
static const check_info info = {"jevt",
"generic ways to access json events",
"",
{{s_jevt_time_field, "json event timestamp as a string that includes the nanosecond part"},
@@ -803,6 +799,11 @@ jevt_filter_check::jevt_filter_check()
{s_jevt_rawtime_field, "absolute event timestamp, i.e. nanoseconds from epoch."},
{s_jevt_value_field, "General way to access single property from json object. The syntax is [<json pointer expression>]. The property is returned as a string", IDX_REQUIRED, IDX_KEY},
{s_jevt_obj_field, "The entire json object, stringified"}}};
return info;
}
jevt_filter_check::jevt_filter_check()
{
}
jevt_filter_check::~jevt_filter_check()
@@ -1282,71 +1283,77 @@ bool k8s_audit_filter_check::extract_any_privileged(const json &j,
return true;
}
k8s_audit_filter_check::k8s_audit_filter_check()
const json_event_filter_check::check_info &k8s_audit_filter_check::get_info() const
{
m_info = {"ka",
"Access K8s Audit Log Events",
"Fields with an IDX_ALLOWED annotation can be indexed (e.g. ka.req.containers.image[k] returns the image for the kth container). The index is optional--without any index the field returns values for all items. The index must be numeric with an IDX_NUMERIC annotation, and can be any string with an IDX_KEY annotation. Fields with an IDX_REQUIRED annotation require an index.",
{{"ka.auditid", "The unique id of the audit event"},
{"ka.stage", "Stage of the request (e.g. RequestReceived, ResponseComplete, etc.)"},
{"ka.auth.decision", "The authorization decision"},
{"ka.auth.reason", "The authorization reason"},
{"ka.user.name", "The user name performing the request"},
{"ka.user.groups", "The groups to which the user belongs"},
{"ka.impuser.name", "The impersonated user name"},
{"ka.verb", "The action being performed"},
{"ka.uri", "The request URI as sent from client to server"},
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
{"ka.target.name", "The target object name"},
{"ka.target.namespace", "The target object namespace"},
{"ka.target.resource", "The target object resource"},
{"ka.target.subresource", "The target object subresource"},
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
{"ka.req.binding.subject.has_name", "Deprecated, always returns \"N/A\". Only provided for backwards compatibility", IDX_REQUIRED, IDX_KEY},
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
{"ka.req.pod.containers.image", "When the request object refers to a pod, the container's images.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.image", "Deprecated by ka.req.pod.containers.image. Returns the image of the first container only"},
{"ka.req.pod.containers.image.repository", "The same as req.container.image, but only the repository part (e.g. falcosecurity/falco).", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.image.repository", "Deprecated by ka.req.pod.containers.image.repository. Returns the repository of the first container only"},
{"ka.req.pod.host_ipc", "When the request object refers to a pod, the value of the hostIPC flag."},
{"ka.req.pod.host_network", "When the request object refers to a pod, the value of the hostNetwork flag."},
{"ka.req.container.host_network", "Deprecated alias for ka.req.pod.host_network"},
{"ka.req.pod.host_pid", "When the request object refers to a pod, the value of the hostPID flag."},
{"ka.req.pod.containers.host_port", "When the request object refers to a pod, all container's hostPort values.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.privileged", "When the request object refers to a pod, the value of the privileged flag for all containers.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.privileged", "Deprecated by ka.req.pod.containers.privileged. Returns true if any container has privileged=true"},
{"ka.req.pod.containers.allow_privilege_escalation", "When the request object refers to a pod, the value of the allowPrivilegeEscalation flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.read_only_fs", "When the request object refers to a pod, the value of the readOnlyRootFilesystem flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.run_as_user", "When the request object refers to a pod, the runAsUser uid specified in the security context for the pod. See ....containers.run_as_user for the runAsUser for individual containers"},
{"ka.req.pod.containers.run_as_user", "When the request object refers to a pod, the runAsUser uid for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.eff_run_as_user", "When the request object refers to a pod, the initial uid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no uid is specified", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.run_as_group", "When the request object refers to a pod, the runAsGroup gid specified in the security context for the pod. See ....containers.run_as_group for the runAsGroup for individual containers"},
{"ka.req.pod.containers.run_as_group", "When the request object refers to a pod, the runAsGroup gid for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.eff_run_as_group", "When the request object refers to a pod, the initial gid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no gid is specified", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.proc_mount", "When the request object refers to a pod, the procMount types for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.fs_group", "When the request object refers to a pod, the fsGroup gid specified by the security context."},
{"ka.req.pod.supplemental_groups", "When the request object refers to a pod, the supplementalGroup gids specified by the security context."},
{"ka.req.pod.containers.add_capabilities", "When the request object refers to a pod, all capabilities to add when running the container.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.service.type", "When the request object refers to a service, the service type"},
{"ka.req.service.ports", "When the request object refers to a service, the service's ports", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.volumes.hostpath", "When the request object refers to a pod, all hostPath paths specified for all volumes", IDX_ALLOWED, IDX_NUMERIC, true},
{"ka.req.volume.hostpath", "Deprecated by ka.req.pod.volumes.hostpath. Return true if the provided (host) path prefix is used by any volume", IDX_ALLOWED, IDX_KEY},
{"ka.req.pod.volumes.flexvolume_driver", "When the request object refers to a pod, all flexvolume drivers specified for all volumes", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.volumes.volume_type", "When the request object refers to a pod, all volume types for all volumes", IDX_ALLOWED, IDX_NUMERIC},
{"ka.resp.name", "The response object name"},
{"ka.response.code", "The response code"},
{"ka.response.reason", "The response reason (usually present only for failures)"},
{"ka.useragent", "The useragent of the client who made the request to the apiserver"}}};
static const json_event_filter_check::check_info
info = {"ka",
"Access K8s Audit Log Events",
"Fields with an IDX_ALLOWED annotation can be indexed (e.g. ka.req.containers.image[k] returns the image for the kth container). The index is optional--without any index the field returns values for all items. The index must be numeric with an IDX_NUMERIC annotation, and can be any string with an IDX_KEY annotation. Fields with an IDX_REQUIRED annotation require an index.",
{{"ka.auditid", "The unique id of the audit event"},
{"ka.stage", "Stage of the request (e.g. RequestReceived, ResponseComplete, etc.)"},
{"ka.auth.decision", "The authorization decision"},
{"ka.auth.reason", "The authorization reason"},
{"ka.user.name", "The user name performing the request"},
{"ka.user.groups", "The groups to which the user belongs"},
{"ka.impuser.name", "The impersonated user name"},
{"ka.verb", "The action being performed"},
{"ka.uri", "The request URI as sent from client to server"},
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
{"ka.target.name", "The target object name"},
{"ka.target.namespace", "The target object namespace"},
{"ka.target.resource", "The target object resource"},
{"ka.target.subresource", "The target object subresource"},
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
{"ka.req.binding.subject.has_name", "Deprecated, always returns \"N/A\". Only provided for backwards compatibility", IDX_REQUIRED, IDX_KEY},
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
{"ka.req.pod.containers.image", "When the request object refers to a pod, the container's images.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.image", "Deprecated by ka.req.pod.containers.image. Returns the image of the first container only"},
{"ka.req.pod.containers.image.repository", "The same as req.container.image, but only the repository part (e.g. falcosecurity/falco).", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.image.repository", "Deprecated by ka.req.pod.containers.image.repository. Returns the repository of the first container only"},
{"ka.req.pod.host_ipc", "When the request object refers to a pod, the value of the hostIPC flag."},
{"ka.req.pod.host_network", "When the request object refers to a pod, the value of the hostNetwork flag."},
{"ka.req.container.host_network", "Deprecated alias for ka.req.pod.host_network"},
{"ka.req.pod.host_pid", "When the request object refers to a pod, the value of the hostPID flag."},
{"ka.req.pod.containers.host_port", "When the request object refers to a pod, all container's hostPort values.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.privileged", "When the request object refers to a pod, the value of the privileged flag for all containers.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.privileged", "Deprecated by ka.req.pod.containers.privileged. Returns true if any container has privileged=true"},
{"ka.req.pod.containers.allow_privilege_escalation", "When the request object refers to a pod, the value of the allowPrivilegeEscalation flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.read_only_fs", "When the request object refers to a pod, the value of the readOnlyRootFilesystem flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.run_as_user", "When the request object refers to a pod, the runAsUser uid specified in the security context for the pod. See ....containers.run_as_user for the runAsUser for individual containers"},
{"ka.req.pod.containers.run_as_user", "When the request object refers to a pod, the runAsUser uid for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.eff_run_as_user", "When the request object refers to a pod, the initial uid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no uid is specified", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.run_as_group", "When the request object refers to a pod, the runAsGroup gid specified in the security context for the pod. See ....containers.run_as_group for the runAsGroup for individual containers"},
{"ka.req.pod.containers.run_as_group", "When the request object refers to a pod, the runAsGroup gid for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.eff_run_as_group", "When the request object refers to a pod, the initial gid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no gid is specified", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.containers.proc_mount", "When the request object refers to a pod, the procMount types for all containers", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.fs_group", "When the request object refers to a pod, the fsGroup gid specified by the security context."},
{"ka.req.pod.supplemental_groups", "When the request object refers to a pod, the supplementalGroup gids specified by the security context."},
{"ka.req.pod.containers.add_capabilities", "When the request object refers to a pod, all capabilities to add when running the container.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.service.type", "When the request object refers to a service, the service type"},
{"ka.req.service.ports", "When the request object refers to a service, the service's ports", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.volumes.hostpath", "When the request object refers to a pod, all hostPath paths specified for all volumes", IDX_ALLOWED, IDX_NUMERIC, true},
{"ka.req.volume.hostpath", "Deprecated by ka.req.pod.volumes.hostpath. Return true if the provided (host) path prefix is used by any volume", IDX_ALLOWED, IDX_KEY},
{"ka.req.pod.volumes.flexvolume_driver", "When the request object refers to a pod, all flexvolume drivers specified for all volumes", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.pod.volumes.volume_type", "When the request object refers to a pod, all volume types for all volumes", IDX_ALLOWED, IDX_NUMERIC},
{"ka.resp.name", "The response object name"},
{"ka.response.code", "The response code"},
{"ka.response.reason", "The response reason (usually present only for failures)"},
{"ka.useragent", "The useragent of the client who made the request to the apiserver"}}};
return info;
{
m_aliases = {
}
const std::unordered_map<std::string, k8s_audit_filter_check::alias> &k8s_audit_filter_check::get_aliases() const
{
static const std::unordered_map<std::string, k8s_audit_filter_check::alias>
aliases = {
{"ka.auditid", {{"/auditID"_json_pointer}}},
{"ka.stage", {{"/stage"_json_pointer}}},
{"ka.auth.decision", {{"/annotations/authorization.k8s.io~1decision"_json_pointer}}},
@@ -1404,7 +1411,11 @@ k8s_audit_filter_check::k8s_audit_filter_check()
{"ka.response.code", {{"/responseStatus/code"_json_pointer}}},
{"ka.response.reason", {{"/responseStatus/reason"_json_pointer}}},
{"ka.useragent", {{"/userAgent"_json_pointer}}}};
}
return aliases;
}
k8s_audit_filter_check::k8s_audit_filter_check()
{
}
k8s_audit_filter_check::~k8s_audit_filter_check()
@@ -1475,14 +1486,14 @@ std::list<gen_event_filter_factory::filter_fieldclass_info> json_event_filter_fa
for(auto &chk: m_defined_checks)
{
json_event_filter_check::check_info &info = chk->get_info();
const json_event_filter_check::check_info &info = chk->get_info();
gen_event_filter_factory::filter_fieldclass_info cinfo;
cinfo.name = info.m_name;
cinfo.desc = info.m_desc;
cinfo.shortdesc = info.m_shortdesc;
for(auto &field : info.m_fields)
for(const auto &field : info.m_fields)
{
gen_event_filter_factory::filter_field_info info;
info.name = field.m_name;

View File

@@ -173,7 +173,7 @@ public:
};
json_event_filter_check();
virtual ~json_event_filter_check();
virtual ~json_event_filter_check() = 0;
virtual int32_t parse_field_name(const char *str, bool alloc_state, bool needed_for_filtering);
void add_filter_value(const char *str, uint32_t len, uint32_t i = 0);
@@ -197,7 +197,7 @@ public:
// brackets (e.g. ka.image[foo])
size_t parsed_size();
check_info &get_info();
virtual const check_info &get_info() const = 0;
//
// Allocate a new check of the same type. Must be overridden.
@@ -260,9 +260,9 @@ protected:
//
// The version of parse_field_name in this base class will
// check a field specification against all the aliases.
std::map<std::string, struct alias> m_aliases;
virtual const std::unordered_map<std::string, alias> &get_aliases() const = 0;
check_info m_info;
//check_info m_info;
// The actual field name parsed in parse_field_name.
std::string m_field;
@@ -315,11 +315,18 @@ public:
int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) final;
json_event_filter_check *allocate_new();
json_event_filter_check *allocate_new() override;
const check_info &get_info() const override;
protected:
bool extract_values(json_event *jevt) final;
const std::unordered_map<std::string, alias> &get_aliases() const override
{
static std::unordered_map<std::string, alias> a;
return a;
};
private:
@@ -340,9 +347,12 @@ public:
k8s_audit_filter_check();
virtual ~k8s_audit_filter_check();
json_event_filter_check *allocate_new();
json_event_filter_check *allocate_new() override;
// Extract all images/image repositories from the provided containers
const check_info &get_info() const override;
const std::unordered_map<std::string, alias> &get_aliases() const override;
// Extract all images/image repositories from the provided containers
static bool extract_images(const nlohmann::json &j,
json_event_filter_check &jchk);

File diff suppressed because it is too large Load Diff

View File

@@ -16,49 +16,247 @@ limitations under the License.
#pragma once
#include <map>
#include <string>
#include <vector>
#include <yaml-cpp/yaml.h>
#include "falco_rule.h"
#include <nlohmann/json.hpp>
#include "falco_source.h"
#include "falco_load_result.h"
#include "indexed_vector.h"
/*!
\brief Ruleset loader of the falco engine
*/
class rule_loader
namespace rule_loader
{
public:
/*!
\brief Represents a section of text from which a certain info
struct has been decoded
*/
struct context
class context
{
std::string content;
public:
// The kinds of items that can be in rules
// content. These generally map to yaml items but a
// few are more specific (e.g. "within condition
// expression", "value for yaml node", etc.)
enum item_type {
VALUE_FOR = 0,
EXCEPTIONS,
EXCEPTION,
EXCEPTION_VALUES,
EXCEPTION_VALUE,
RULES_CONTENT,
RULES_CONTENT_ITEM,
REQUIRED_ENGINE_VERSION,
REQUIRED_PLUGIN_VERSIONS,
REQUIRED_PLUGIN_VERSIONS_ENTRY,
REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE,
LIST,
LIST_ITEM,
MACRO,
MACRO_CONDITION,
RULE,
RULE_CONDITION,
CONDITION_EXPRESSION,
RULE_OUTPUT,
RULE_OUTPUT_EXPRESSION,
RULE_PRIORITY
};
/*!
\brief Wraps an error by adding info about the text section
*/
inline std::string error(std::string err) const
{
std::string cnt = content;
err += "\n---\n";
err += trim(cnt);
err += "\n---";
return err;
}
static const std::string& item_type_as_string(enum item_type it);
/*!
\brief Appends another text section info to this one
*/
inline void append(context& m)
static const size_t default_snippet_width = 160;
struct position
{
content += "\n\n";
content += m.content;
}
position() : pos(0), line(0), column(0) {};
position(const YAML::Mark& mark) : pos(mark.pos), line(mark.line), column(mark.column) {};
~position() = default;
position(position&&) = default;
position& operator = (position&&) = default;
position(const position&) = default;
position& operator = (const position&) = default;
int pos;
int line;
int column;
};
struct location
{
location(): item_type(context::item_type::VALUE_FOR) {}
location(
const std::string n,
const position& p,
context::item_type i,
const std::string in):
name(n), pos(p), item_type(i), item_name(in) {}
location(location&&) = default;
location& operator = (location&&) = default;
location(const location&) = default;
location& operator = (const location&) = default;
// A name for the content this location refers
// to. Will generally be a filename, can also
// refer to a rule/macro condition when the
// location points into a condition string.
std::string name;
// The original location in the document
position pos;
// The kind of item at this location
// (e.g. "list", "macro", "rule", "exception", etc)
context::item_type item_type;
// The name of this item (e.g. "Write Below Etc",
// etc).
std::string item_name;
};
context(const std::string& name);
context(const YAML::Node& item,
item_type item_type,
const std::string item_name,
const context& parent);
// Build a context from a condition expression +
// parser position. This does not use the original
// yaml content because:
// - YAML block indicators will remove whitespace/newlines/wrapping
// from the YAML node containing the condition expression.
// - When compiling, the condition expression has expanded
// macro and list references with their values.
context(const libsinsp::filter::ast::pos_info& pos,
const std::string& condition,
const context& parent);
virtual ~context() = default;
context(context&&) = default;
context& operator = (context&&) = default;
context(const context&) = default;
context& operator = (const context&) = default;
// Return the content name (generally filename) for
// this context
const std::string& name() const;
// Return a snippet of the provided rules content
// corresponding to this context.
// Uses the provided rules_contents to look up the original
// rules content for a given location name.
// (If this context has a non-empty alt_content, it
// will be used to create the snippet, ignoring the
// provided rules_contents).
std::string snippet(const falco::load_result::rules_contents_t& rules_contents, size_t snippet_width = default_snippet_width) const;
std::string as_string();
nlohmann::json as_json();
private:
void init(const std::string& name,
const position& pos,
const item_type item_type,
const std::string item_name,
const context& parent);
// A chain of locations from the current item, its
// parent, possibly older ancestors.
std::vector<location> m_locs;
// If non-empty, this content will be used when
// creating snippets. Used for contexts involving
// condition expressions.
std::string alt_content;
};
struct warning
{
warning(): ctx("no-filename-given") {}
warning(
falco::load_result::warning_code w,
const std::string& m,
const context& c): wc(w), msg(m), ctx(c) {}
warning(warning&&) = default;
warning& operator = (warning&&) = default;
warning(const warning&) = default;
warning& operator = (const warning&) = default;
falco::load_result::warning_code wc;
std::string msg;
context ctx;
};
struct error
{
error(): ctx("no-filename-given") {}
error(
falco::load_result::error_code e,
const std::string& m,
const context& c): ec(e), msg(m), ctx(c) {}
error(error&&) = default;
error& operator = (error&&) = default;
error(const error&) = default;
error& operator = (const error&) = default;
falco::load_result::error_code ec;
std::string msg;
context ctx;
};
class rule_load_exception : public std::exception
{
public:
rule_load_exception(falco::load_result::error_code ec, std::string msg, const context& ctx);
virtual ~rule_load_exception();
rule_load_exception(rule_load_exception&&) = default;
rule_load_exception& operator = (rule_load_exception&&) = default;
rule_load_exception(const rule_load_exception&) = default;
rule_load_exception& operator = (const rule_load_exception&) = default;
const char* what();
falco::load_result::error_code ec;
std::string msg;
context ctx;
std::string errstr;
};
/*!
\brief Contains the result of loading rule definitions
*/
class result : public falco::load_result
{
public:
result(const std::string &name);
virtual ~result() = default;
result(result&&) = default;
result& operator = (result&&) = default;
result(const result&) = default;
result& operator = (const result&) = default;
virtual bool successful() override;
virtual bool has_warnings() override;
virtual const std::string& as_string(bool verbose, const falco::load_result::rules_contents_t& contents) override;
virtual const nlohmann::json& as_json(const falco::load_result::rules_contents_t& contents) override;
void add_error(falco::load_result::error_code ec,
const std::string& msg,
const context& ctx);
void add_warning(falco::load_result::warning_code ec,
const std::string& msg,
const context& ctx);
protected:
const std::string& as_summary_string();
const std::string& as_verbose_string(const falco::load_result::rules_contents_t& contents);
std::string name;
bool success;
std::vector<error> errors;
std::vector<warning> warnings;
std::string res_summary_string;
std::string res_verbose_string;
nlohmann::json res_json;
};
/*!
@@ -68,13 +266,23 @@ public:
{
explicit configuration(
const std::string& cont,
const indexed_vector<falco_source>& srcs)
: content(cont), sources(srcs) {}
const indexed_vector<falco_source>& srcs,
std::string name)
: content(cont), sources(srcs), name(name),
default_ruleset_id(0), replace_output_container_info(false),
min_priority(falco_common::PRIORITY_DEBUG)
{
res.reset(new result(name));
}
configuration(configuration&&) = default;
configuration& operator = (configuration&&) = default;
configuration(const configuration&) = delete;
configuration& operator = (const configuration&) = delete;
const std::string& content;
const indexed_vector<falco_source>& sources;
std::vector<std::string> errors;
std::vector<std::string> warnings;
std::string name;
std::unique_ptr<result> res;
std::string output_extra;
uint16_t default_ruleset_id;
bool replace_output_container_info;
@@ -86,6 +294,14 @@ public:
*/
struct engine_version_info
{
engine_version_info(context &ctx);
~engine_version_info() = default;
engine_version_info(engine_version_info&&) = default;
engine_version_info& operator = (engine_version_info&&) = default;
engine_version_info(const engine_version_info&) = default;
engine_version_info& operator = (const engine_version_info&) = default;
context ctx;
uint32_t version;
};
@@ -94,15 +310,49 @@ public:
*/
struct plugin_version_info
{
std::string name;
std::string version;
struct requirement
{
requirement() = default;
requirement(const std::string n, const std::string v):
name(n), version(v) { }
requirement(requirement&&) = default;
requirement& operator = (requirement&&) = default;
requirement(const requirement&) = default;
requirement& operator = (const requirement&) = default;
std::string name;
std::string version;
};
typedef std::vector<requirement> requirement_alternatives;
// This differs from the other _info structs by having
// a default constructor. This allows it to be used
// by falco_engine, which aliases the type.
plugin_version_info();
plugin_version_info(context &ctx);
~plugin_version_info() = default;
plugin_version_info(plugin_version_info&&) = default;
plugin_version_info& operator = (plugin_version_info&&) = default;
plugin_version_info(const plugin_version_info&) = default;
plugin_version_info& operator = (const plugin_version_info&) = default;
context ctx;
requirement_alternatives alternatives;
};
/*!
\brief Represents infos about a list
\brief Represents infos about a list
*/
struct list_info
{
list_info(context &ctx);
~list_info() = default;
list_info(list_info&&) = default;
list_info& operator = (list_info&&) = default;
list_info(const list_info&) = default;
list_info& operator = (const list_info&) = default;
context ctx;
bool used;
size_t index;
@@ -112,11 +362,19 @@ public:
};
/*!
\brief Represents infos about a macro
\brief Represents infos about a macro
*/
struct macro_info
{
macro_info(context &ctx);
~macro_info() = default;
macro_info(macro_info&&) = default;
macro_info& operator = (macro_info&&) = default;
macro_info(const macro_info&) = default;
macro_info& operator = (const macro_info&) = default;
context ctx;
context cond_ctx;
bool used;
size_t index;
size_t visibility;
@@ -130,6 +388,13 @@ public:
*/
struct rule_exception_info
{
rule_exception_info(context &ctx);
~rule_exception_info() = default;
rule_exception_info(rule_exception_info&&) = default;
rule_exception_info& operator = (rule_exception_info&&) = default;
rule_exception_info(const rule_exception_info&) = default;
rule_exception_info& operator = (const rule_exception_info&) = default;
/*!
\brief This is necessary due to the dynamic-typed nature of
exceptions. Each of fields, comps, and values, can either be a
@@ -137,6 +402,14 @@ public:
this easier to implement in C++, that is not non-dynamic-typed.
*/
struct entry {
entry(): is_list(false) {}
explicit entry(const std::string& i): is_list(false), item(i) {}
explicit entry(const std::vector<entry>& v): is_list(true), items(v) {}
entry(entry&&) = default;
entry& operator = (entry&&) = default;
entry(const entry&) = default;
entry& operator = (const entry&) = default;
bool is_list;
std::string item;
std::vector<entry> items;
@@ -148,6 +421,7 @@ public:
}
};
context ctx;
std::string name;
entry fields;
entry comps;
@@ -155,11 +429,20 @@ public:
};
/*!
\brief Represents infos about a rule
\brief Represents infos about a rule
*/
struct rule_info
{
rule_info(context &ctx);
~rule_info() = default;
rule_info(rule_info&&) = default;
rule_info& operator = (rule_info&&) = default;
rule_info(const rule_info&) = default;
rule_info& operator = (const rule_info&) = default;
context ctx;
context cond_ctx;
context output_ctx;
size_t index;
size_t visibility;
std::string name;
@@ -174,67 +457,4 @@ public:
bool warn_evttypes;
bool skip_if_unknown_filter;
};
virtual ~rule_loader() = default;
/*!
\brief Erases all the internal state and definitions
*/
virtual void clear();
/*!
\brief Uses the internal state to compile a list of falco_rules
*/
virtual bool compile(configuration& cfg, indexed_vector<falco_rule>& out) const;
/*!
\brief Returns the set of all required versions for each plugin according
to the internal definitions.
*/
virtual const std::map<std::string, std::set<std::string>> required_plugin_versions() const;
/*!
\brief Defines an info block. If a similar info block is found
in the internal state (e.g. another rule with same name), then
the previous definition gets overwritten
*/
virtual void define(configuration& cfg, engine_version_info& info);
virtual void define(configuration& cfg, plugin_version_info& info);
virtual void define(configuration& cfg, list_info& info);
virtual void define(configuration& cfg, macro_info& info);
virtual void define(configuration& cfg, rule_info& info);
/*!
\brief Appends an info block to an existing one. An exception
is thrown if no existing definition can be matched with the appended
one
*/
virtual void append(configuration& cfg, list_info& info);
virtual void append(configuration& cfg, macro_info& info);
virtual void append(configuration& cfg, rule_info& info);
/*!
\brief Updates the 'enabled' flag of an existing definition
*/
virtual void enable(configuration& cfg, rule_info& info);
private:
void compile_list_infos(
configuration& cfg,
indexed_vector<list_info>& out) const;
void compile_macros_infos(
configuration& cfg,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& out) const;
void compile_rule_infos(
configuration& cfg,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& macros,
indexed_vector<falco_rule>& out) const;
uint32_t m_cur_index;
indexed_vector<rule_info> m_rule_infos;
indexed_vector<macro_info> m_macro_infos;
indexed_vector<list_info> m_list_infos;
std::map<std::string, std::set<std::string>> m_required_plugin_versions;
};

View File

@@ -0,0 +1,280 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <string>
#include <version.h>
#include "falco_engine.h"
#include "rule_loader_collector.h"
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_VALIDATE, (err), (ctx)); } }
static inline bool is_operator_defined(const std::string& op)
{
auto ops = libsinsp::filter::parser::supported_operators();
return find(ops.begin(), ops.end(), op) != ops.end();
}
template <typename T>
static inline void define_info(indexed_vector<T>& infos, T& info, uint32_t id)
{
auto prev = infos.at(info.name);
if (prev)
{
info.index = prev->index;
info.visibility = id;
*prev = info;
}
else
{
info.index = id;
info.visibility = id;
infos.insert(info, info.name);
}
}
template <typename T>
static inline void append_info(T* prev, T& info, uint32_t id)
{
prev->visibility = id;
}
static void validate_exception_info(
const falco_source& source,
rule_loader::rule_exception_info &ex)
{
if (ex.fields.is_list)
{
if (!ex.comps.is_valid())
{
ex.comps.is_list = true;
for (size_t i = 0; i < ex.fields.items.size(); i++)
{
ex.comps.items.push_back(rule_loader::rule_exception_info::entry("="));
}
}
THROW(ex.fields.items.size() != ex.comps.items.size(),
"Fields and comps lists must have equal length",
ex.ctx);
for (auto &v : ex.comps.items)
{
THROW(!is_operator_defined(v.item),
std::string("'") + v.item + "' is not a supported comparison operator",
ex.ctx);
}
for (auto &v : ex.fields.items)
{
THROW(!source.is_field_defined(v.item),
std::string("'") + v.item + "' is not a supported filter field",
ex.ctx);
}
}
else
{
if (!ex.comps.is_valid())
{
ex.comps.is_list = false;
ex.comps.item = "in";
}
THROW(ex.comps.is_list,
"Fields and comps must both be strings",
ex.ctx);
THROW((ex.comps.item != "in" && ex.comps.item != "pmatch" && ex.comps.item != "intersects"),
"When fields is a single value, comps must be one of (in, pmatch, intersects)",
ex.ctx);
THROW(!source.is_field_defined(ex.fields.item),
std::string("'") + ex.fields.item + "' is not a supported filter field",
ex.ctx);
}
}
void rule_loader::collector::clear()
{
m_cur_index = 0;
m_rule_infos.clear();
m_list_infos.clear();
m_macro_infos.clear();
m_required_plugin_versions.clear();
}
const std::vector<rule_loader::plugin_version_info::requirement_alternatives>& rule_loader::collector::required_plugin_versions() const
{
return m_required_plugin_versions;
}
const indexed_vector<rule_loader::list_info>& rule_loader::collector::lists() const
{
return m_list_infos;
}
const indexed_vector<rule_loader::macro_info>& rule_loader::collector::macros() const
{
return m_macro_infos;
}
const indexed_vector<rule_loader::rule_info>& rule_loader::collector::rules() const
{
return m_rule_infos;
}
void rule_loader::collector::define(configuration& cfg, engine_version_info& info)
{
auto v = falco_engine::engine_version();
THROW(v < info.version, "Rules require engine version "
+ std::to_string(info.version) + ", but engine version is " + std::to_string(v),
info.ctx);
}
void rule_loader::collector::define(configuration& cfg, plugin_version_info& info)
{
std::unordered_set<std::string> plugin_names;
for (const auto& req : info.alternatives)
{
sinsp_version plugin_version(req.version);
THROW(!plugin_version.m_valid,
"Invalid required version '" + req.version
+ "' for plugin '" + req.name + "'",
info.ctx);
THROW(plugin_names.find(req.name) != plugin_names.end(),
"Defined multiple alternative version requirements for plugin '"
+ req.name + "'",
info.ctx);
plugin_names.insert(req.name);
}
m_required_plugin_versions.push_back(info.alternatives);
}
void rule_loader::collector::define(configuration& cfg, list_info& info)
{
define_info(m_list_infos, info, m_cur_index++);
}
void rule_loader::collector::append(configuration& cfg, list_info& info)
{
auto prev = m_list_infos.at(info.name);
THROW(!prev,
"List has 'append' key but no list by that name already exists",
info.ctx);
prev->items.insert(prev->items.end(), info.items.begin(), info.items.end());
append_info(prev, info, m_cur_index++);
}
void rule_loader::collector::define(configuration& cfg, macro_info& info)
{
define_info(m_macro_infos, info, m_cur_index++);
}
void rule_loader::collector::append(configuration& cfg, macro_info& info)
{
auto prev = m_macro_infos.at(info.name);
THROW(!prev,
"Macro has 'append' key but no macro by that name already exists",
info.ctx);
prev->cond += " ";
prev->cond += info.cond;
append_info(prev, info, m_cur_index++);
}
void rule_loader::collector::define(configuration& cfg, rule_info& info)
{
auto source = cfg.sources.at(info.source);
if (!source)
{
cfg.res->add_warning(falco::load_result::LOAD_UNKNOWN_SOURCE,
"Unknown source " + info.source + ", skipping",
info.ctx);
return;
}
auto prev = m_rule_infos.at(info.name);
THROW(prev && prev->source != info.source,
"Rule has been re-defined with a different source",
info.ctx);
for (auto &ex : info.exceptions)
{
THROW(!ex.fields.is_valid(),
"Rule exception item must have fields property with a list of fields",
ex.ctx);
validate_exception_info(*source, ex);
}
define_info(m_rule_infos, info, m_cur_index++);
}
void rule_loader::collector::append(configuration& cfg, rule_info& info)
{
auto prev = m_rule_infos.at(info.name);
THROW(!prev,
"Rule has 'append' key but no rule by that name already exists",
info.ctx);
THROW(info.cond.empty() && info.exceptions.empty(),
"Appended rule must have exceptions or condition property",
info.ctx);
auto source = cfg.sources.at(prev->source);
// note: this is not supposed to happen
THROW(!source,
std::string("Unknown source ") + prev->source,
info.ctx);
if (!info.cond.empty())
{
prev->cond += " ";
prev->cond += info.cond;
}
for (auto &ex : info.exceptions)
{
auto prev_ex = find_if(prev->exceptions.begin(), prev->exceptions.end(),
[&ex](const rule_loader::rule_exception_info& i)
{ return i.name == ex.name; });
if (prev_ex == prev->exceptions.end())
{
THROW(!ex.fields.is_valid(),
"Rule exception must have fields property with a list of fields",
ex.ctx);
THROW(ex.values.empty(),
"Rule exception must have values property with a list of values",
ex.ctx);
validate_exception_info(*source, ex);
prev->exceptions.push_back(ex);
}
else
{
THROW(ex.fields.is_valid(),
"Can not append exception fields to existing exception, only values",
ex.ctx);
THROW(ex.comps.is_valid(),
"Can not append exception comps to existing exception, only values",
ex.ctx);
prev_ex->values.insert(
prev_ex->values.end(), ex.values.begin(), ex.values.end());
}
}
append_info(prev, info, m_cur_index++);
}
void rule_loader::collector::enable(configuration& cfg, rule_info& info)
{
auto prev = m_rule_infos.at(info.name);
THROW(!prev,
"Rule has 'enabled' key but no rule by that name already exists",
info.ctx);
prev->enabled = info.enabled;
}

View File

@@ -0,0 +1,97 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <vector>
#include "rule_loader.h"
#include "indexed_vector.h"
namespace rule_loader
{
/*!
\brief Collector for the ruleset loader of the falco engine
*/
class collector
{
public:
collector(): m_cur_index(0) { }
virtual ~collector() = default;
collector(collector&&) = default;
collector& operator = (collector&&) = default;
collector(const collector&) = delete;
collector& operator = (const collector&) = delete;
/*!
\brief Erases all the internal state and definitions
*/
virtual void clear();
/*!
\brief Returns the set of all defined required plugin versions
*/
virtual const std::vector<plugin_version_info::requirement_alternatives>& required_plugin_versions() const;
/*!
\brief Returns the list of defined lists
*/
virtual const indexed_vector<list_info>& lists() const;
/*!
\brief Returns the list of defined macros
*/
virtual const indexed_vector<macro_info>& macros() const;
/*!
\brief Returns the list of defined rules
*/
virtual const indexed_vector<rule_info>& rules() const;
/*!
\brief Defines an info block. If a similar info block is found
in the internal state (e.g. another rule with same name), then
the previous definition gets overwritten
*/
virtual void define(configuration& cfg, engine_version_info& info);
virtual void define(configuration& cfg, plugin_version_info& info);
virtual void define(configuration& cfg, list_info& info);
virtual void define(configuration& cfg, macro_info& info);
virtual void define(configuration& cfg, rule_info& info);
/*!
\brief Appends an info block to an existing one. An exception
is thrown if no existing definition can be matched with the appended
one
*/
virtual void append(configuration& cfg, list_info& info);
virtual void append(configuration& cfg, macro_info& info);
virtual void append(configuration& cfg, rule_info& info);
/*!
\brief Updates the 'enabled' flag of an existing definition
*/
virtual void enable(configuration& cfg, rule_info& info);
private:
uint32_t m_cur_index;
indexed_vector<rule_info> m_rule_infos;
indexed_vector<macro_info> m_macro_infos;
indexed_vector<list_info> m_list_infos;
std::vector<plugin_version_info::requirement_alternatives> m_required_plugin_versions;
};
}; // namespace rule_loader

View File

@@ -0,0 +1,542 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <string>
#include <memory>
#include <set>
#include <vector>
#include "rule_loader_compiler.h"
#include "filter_macro_resolver.h"
#include "filter_evttype_resolver.h"
#include "filter_warning_resolver.h"
#define MAX_VISIBILITY ((uint32_t) -1)
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_VALIDATE, (err), (ctx)); } }
static std::string s_container_info_fmt = "%container.info";
static std::string s_default_extra_fmt = "%container.name (id=%container.id)";
using namespace libsinsp::filter;
// todo(jasondellaluce): this breaks string escaping in lists and exceptions
static void quote_item(std::string& e)
{
if (e.find(" ") != std::string::npos && e[0] != '"' && e[0] != '\'')
{
e = '"' + e + '"';
}
}
static void paren_item(std::string& e)
{
if(e[0] != '(')
{
e = '(' + e + ')';
}
}
static inline bool is_operator_defined(const std::string& op)
{
auto ops = libsinsp::filter::parser::supported_operators();
return find(ops.begin(), ops.end(), op) != ops.end();
}
static inline bool is_operator_for_list(const std::string& op)
{
auto ops = libsinsp::filter::parser::supported_operators(true);
return find(ops.begin(), ops.end(), op) != ops.end();
}
static bool is_format_valid(const falco_source& source, std::string fmt, std::string& err)
{
try
{
std::shared_ptr<gen_event_formatter> formatter;
formatter = source.formatter_factory->create_formatter(fmt);
return true;
}
catch(exception &e)
{
err = e.what();
return false;
}
}
static void build_rule_exception_infos(
const std::vector<rule_loader::rule_exception_info>& exceptions,
std::set<std::string>& exception_fields,
std::string& condition)
{
std::string tmp;
for (auto &ex : exceptions)
{
std::string icond;
if(!ex.fields.is_list)
{
for (auto &val : ex.values)
{
THROW(val.is_list,
"Expected values array to contain a list of strings",
ex.ctx)
icond += icond.empty()
? ("(" + ex.fields.item + " "
+ ex.comps.item + " (")
: ", ";
exception_fields.insert(ex.fields.item);
tmp = val.item;
quote_item(tmp);
icond += tmp;
}
icond += icond.empty() ? "" : "))";
}
else
{
icond = "(";
for (auto &values : ex.values)
{
THROW(ex.fields.items.size() != values.items.size(),
"Fields and values lists must have equal length",
ex.ctx);
icond += icond == "(" ? "" : " or ";
icond += "(";
uint32_t k = 0;
std::string istr;
for (auto &field : ex.fields.items)
{
icond += k == 0 ? "" : " and ";
if (values.items[k].is_list)
{
istr = "(";
for (auto &v : values.items[k].items)
{
tmp = v.item;
quote_item(tmp);
istr += istr == "(" ? "" : ", ";
istr += tmp;
}
istr += ")";
}
else
{
istr = values.items[k].item;
if(is_operator_for_list(ex.comps.items[k].item))
{
paren_item(istr);
}
else
{
quote_item(istr);
}
}
icond += " " + field.item;
icond += " " + ex.comps.items[k].item + " " + istr;
exception_fields.insert(field.item);
k++;
}
icond += ")";
}
icond += ")";
if (icond == "()")
{
icond = "";
}
}
condition += icond.empty() ? "" : " and not " + icond;
}
}
// todo(jasondellaluce): this breaks string escaping in lists
static bool resolve_list(std::string& cnd, const rule_loader::list_info& list)
{
static std::string blanks = " \t\n\r";
static std::string delims = blanks + "(),=";
std::string new_cnd;
size_t start, end;
bool used = false;
start = cnd.find(list.name);
while (start != std::string::npos)
{
// the characters surrounding the name must
// be delims of beginning/end of string
end = start + list.name.length();
if ((start == 0 || delims.find(cnd[start - 1]) != std::string::npos)
&& (end >= cnd.length() || delims.find(cnd[end]) != std::string::npos))
{
// shift pointers to consume all whitespaces
while (start > 0
&& blanks.find(cnd[start - 1]) != std::string::npos)
{
start--;
}
while (end < cnd.length()
&& blanks.find(cnd[end]) != std::string::npos)
{
end++;
}
// create substitution string by concatenating all values
std::string sub = "";
for (auto &v : list.items)
{
if (!sub.empty())
{
sub += ", ";
}
sub += v;
}
// if substituted list is empty, we need to
// remove a comma from the left or the right
if (sub.empty())
{
if (start > 0 && cnd[start - 1] == ',')
{
start--;
}
else if (end < cnd.length() && cnd[end] == ',')
{
end++;
}
}
// compose new string with substitution
new_cnd = "";
if (start > 0)
{
new_cnd += cnd.substr(0, start) + " ";
}
new_cnd += sub + " ";
if (end <= cnd.length())
{
new_cnd += cnd.substr(end);
}
cnd = new_cnd;
start += sub.length() + 1;
used = true;
}
start = cnd.find(list.name, start + 1);
}
return used;
}
static void resolve_macros(
indexed_vector<rule_loader::macro_info>& macros,
std::shared_ptr<ast::expr>& ast,
uint32_t visibility,
const rule_loader::context &ctx)
{
filter_macro_resolver macro_resolver;
for (auto &m : macros)
{
if (m.index < visibility)
{
macro_resolver.set_macro(m.name, m.cond_ast);
}
}
macro_resolver.run(ast);
// Note: only complaining about the first unknown macro
THROW(!macro_resolver.get_unknown_macros().empty(),
std::string("Undefined macro '")
+ *macro_resolver.get_unknown_macros().begin()
+ "' used in filter.",
ctx);
for (auto &m : macro_resolver.get_resolved_macros())
{
macros.at(m)->used = true;
}
}
// note: there is no visibility order between filter conditions and lists
static std::shared_ptr<ast::expr> parse_condition(
std::string condition,
indexed_vector<rule_loader::list_info>& lists,
const rule_loader::context &ctx)
{
for (auto &l : lists)
{
if (resolve_list(condition, l))
{
l.used = true;
}
}
libsinsp::filter::parser p(condition);
p.set_max_depth(1000);
try
{
std::shared_ptr<ast::expr> res_ptr(p.parse());
return res_ptr;
}
catch (const sinsp_exception& e)
{
rule_loader::context parsectx(p.get_pos(), condition, ctx);
throw rule_loader::rule_load_exception(
falco::load_result::LOAD_ERR_COMPILE_CONDITION,
e.what(),
parsectx);
}
}
static void apply_output_substitutions(
rule_loader::configuration& cfg,
std::string& out)
{
if (out.find(s_container_info_fmt) != std::string::npos)
{
if (cfg.replace_output_container_info)
{
out = replace(out, s_container_info_fmt, cfg.output_extra);
return;
}
out = replace(out, s_container_info_fmt, s_default_extra_fmt);
}
out += cfg.output_extra.empty() ? "" : " " + cfg.output_extra;
}
void rule_loader::compiler::compile_list_infos(
configuration& cfg,
const collector& col,
indexed_vector<list_info>& out) const
{
std::string tmp;
std::vector<std::string> used;
for (auto &list : col.lists())
{
list_info v = list;
v.items.clear();
for (auto &item : list.items)
{
const auto ref = col.lists().at(item);
if (ref && ref->index < list.visibility)
{
used.push_back(ref->name);
for (auto val : ref->items)
{
quote_item(val);
v.items.push_back(val);
}
}
else
{
tmp = item;
quote_item(tmp);
v.items.push_back(tmp);
}
}
v.used = false;
out.insert(v, v.name);
}
for (auto &v : used)
{
out.at(v)->used = true;
}
}
// note: there is a visibility ordering between macros
void rule_loader::compiler::compile_macros_infos(
configuration& cfg,
const collector& col,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& out) const
{
for (auto &m : col.macros())
{
macro_info entry = m;
entry.cond_ast = parse_condition(m.cond, lists, m.cond_ctx);
entry.used = false;
out.insert(entry, m.name);
}
for (auto &m : out)
{
resolve_macros(out, m.cond_ast, m.visibility, m.ctx);
}
}
void rule_loader::compiler::compile_rule_infos(
configuration& cfg,
const collector& col,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& macros,
indexed_vector<falco_rule>& out) const
{
std::string err, condition;
std::set<falco::load_result::load_result::warning_code> warn_codes;
filter_warning_resolver warn_resolver;
for (auto &r : col.rules())
{
// skip the rule if below the minimum priority
if (r.priority > cfg.min_priority)
{
continue;
}
auto source = cfg.sources.at(r.source);
// note: this is not supposed to happen
THROW(!source,
std::string("Unknown source ") + r.source,
r.ctx);
// build filter AST by parsing the condition, building exceptions,
// and resolving lists and macros
falco_rule rule;
condition = r.cond;
if (!r.exceptions.empty())
{
build_rule_exception_infos(
r.exceptions, rule.exception_fields, condition);
}
auto ast = parse_condition(condition, lists, r.cond_ctx);
resolve_macros(macros, ast, MAX_VISIBILITY, r.ctx);
// check for warnings in the filtering condition
warn_codes.clear();
if (warn_resolver.run(ast.get(), warn_codes))
{
for (auto &w : warn_codes)
{
cfg.res->add_warning(w, "", r.ctx);
}
}
// build rule output message
rule.output = r.output;
if (r.source == falco_common::syscall_source)
{
apply_output_substitutions(cfg, rule.output);
}
if(!is_format_valid(*cfg.sources.at(r.source), rule.output, err))
{
throw rule_load_exception(
falco::load_result::load_result::LOAD_ERR_COMPILE_OUTPUT,
err,
r.output_ctx);
}
// construct rule definition and compile it to a filter
rule.name = r.name;
rule.source = r.source;
rule.description = r.desc;
rule.priority = r.priority;
rule.tags = r.tags;
auto rule_id = out.insert(rule, rule.name);
out.at(rule_id)->id = rule_id;
// This also compiles the filter, and might throw a
// falco_exception with details on the compilation
// failure.
try {
source->ruleset->add(*out.at(rule_id), ast);
}
catch (const falco_exception& e)
{
// Allow errors containing "nonexistent field" if
// skip_if_unknown_filter is true
std::string err = e.what();
if (err.find("nonexistent field") != std::string::npos &&
r.skip_if_unknown_filter)
{
cfg.res->add_warning(
falco::load_result::load_result::LOAD_UNKNOWN_FIELD,
e.what(),
r.cond_ctx);
}
else
{
throw rule_loader::rule_load_exception(
falco::load_result::load_result::LOAD_ERR_COMPILE_CONDITION,
e.what(),
r.cond_ctx);
}
}
// By default rules are enabled/disabled for the default ruleset
if(r.enabled)
{
source->ruleset->enable(rule.name, true, cfg.default_ruleset_id);
}
else
{
source->ruleset->disable(rule.name, true, cfg.default_ruleset_id);
}
// populate set of event types and emit an special warning
std::set<uint16_t> evttypes = { ppm_event_type::PPME_PLUGINEVENT_E };
if(rule.source == falco_common::syscall_source)
{
evttypes.clear();
filter_evttype_resolver().evttypes(ast, evttypes);
if ((evttypes.empty() || evttypes.size() > 100)
&& r.warn_evttypes)
{
cfg.res->add_warning(
falco::load_result::load_result::LOAD_NO_EVTTYPE,
"Rule matches too many evt.type values. This has a significant performance penalty.",
r.ctx);
}
}
}
}
void rule_loader::compiler::compile(
configuration& cfg,
const collector& col,
indexed_vector<falco_rule>& out) const
{
indexed_vector<list_info> lists;
indexed_vector<macro_info> macros;
// expand all lists, macros, and rules
try
{
compile_list_infos(cfg, col, lists);
compile_macros_infos(cfg, col, lists, macros);
compile_rule_infos(cfg, col, lists, macros, out);
}
catch(rule_load_exception &e)
{
cfg.res->add_error(e.ec, e.msg, e.ctx);
}
// print info on any dangling lists or macros that were not used anywhere
for (auto &m : macros)
{
if (!m.used)
{
cfg.res->add_warning(
falco::load_result::load_result::LOAD_UNUSED_MACRO,
"Macro not referred to by any other rule/macro",
m.ctx);
}
}
for (auto &l : lists)
{
if (!l.used)
{
cfg.res->add_warning(
falco::load_result::LOAD_UNUSED_LIST,
"List not referred to by any other rule/macro",
l.ctx);
}
}
}

View File

@@ -0,0 +1,69 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include "rule_loader.h"
#include "rule_loader_collector.h"
#include "indexed_vector.h"
#include "falco_rule.h"
namespace rule_loader
{
/*!
\brief Compiler for the ruleset loader of the falco engine
*/
class compiler
{
public:
compiler() = default;
virtual ~compiler() = default;
compiler(compiler&&) = default;
compiler& operator = (compiler&&) = default;
compiler(const compiler&) = default;
compiler& operator = (const compiler&) = default;
/*!
\brief Compiles a list of falco rules
*/
virtual void compile(
configuration& cfg,
const collector& col,
indexed_vector<falco_rule>& out) const;
private:
void compile_list_infos(
configuration& cfg,
const collector& col,
indexed_vector<list_info>& out) const;
void compile_macros_infos(
configuration& cfg,
const collector& col,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& out) const;
void compile_rule_infos(
configuration& cfg,
const collector& col,
indexed_vector<list_info>& lists,
indexed_vector<macro_info>& macros,
indexed_vector<falco_rule>& out) const;
};
}; // namespace rule_loader

View File

@@ -0,0 +1,487 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <string>
#include <vector>
#include "rule_loader_reader.h"
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_YAML_VALIDATE, (err), (ctx)); } }
// Don't call this directly, call decode_val/decode_optional_val instead.
template <typename T>
static void decode_val_generic(const YAML::Node& item, const char *key, T& out, const rule_loader::context& ctx, bool optional)
{
const YAML::Node& val = item[key];
if(!val.IsDefined() && optional)
{
return;
}
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
THROW(val.IsNull(), std::string("Mapping for key '") + key + "' is empty", ctx);
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx);
THROW(!val.IsScalar(), "Value is not a scalar value", valctx);
THROW(val.Scalar().empty(), "Value must be non-empty", valctx);
THROW(!YAML::convert<T>::decode(val, out), "Can't decode YAML scalar value", valctx);
}
template <typename T>
static void decode_val(const YAML::Node& item, const char *key, T& out, const rule_loader::context& ctx)
{
bool optional = false;
decode_val_generic(item, key, out, ctx, optional);
}
template <typename T>
static void decode_optional_val(const YAML::Node& item, const char *key, T& out, const rule_loader::context& ctx)
{
bool optional = true;
decode_val_generic(item, key, out, ctx, optional);
}
// Don't call this directly, call decode_items/decode_tags instead.
template <typename T>
static void decode_seq(const YAML::Node& item, const char *key,
std::function<void(T)> inserter,
const rule_loader::context &ctx, bool optional)
{
const YAML::Node& val = item[key];
if(!val.IsDefined() && optional)
{
return;
}
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx);
THROW(!val.IsSequence(), "Value is not a sequence", valctx);
T value;
for(const YAML::Node& v : val)
{
rule_loader::context ictx(v, rule_loader::context::LIST_ITEM, "", valctx);
THROW(!v.IsScalar(), "sequence value is not scalar", ictx);
THROW(!YAML::convert<T>::decode(v, value), "Can't decode YAML sequence value", ictx);
inserter(value);
}
}
template <typename T>
static void decode_items(const YAML::Node& item, vector<T>& out,
const rule_loader::context& ctx)
{
bool optional = false;
std::function<void(T)> inserter = [&out] (T value) {
out.push_back(value);
};
decode_seq(item, "items", inserter, ctx, optional);
}
template <typename T>
static void decode_tags(const YAML::Node& item, set<T>& out,
const rule_loader::context& ctx)
{
bool optional = true;
std::function<void(T)> inserter = [&out] (T value) {
out.insert(value);
};
decode_seq(item, "tags", inserter, ctx, optional);
}
// Don't call this directly, call decode_exception_{fields,comps,values} instead
static void decode_exception_info_entry(
const YAML::Node& item,
const char *key,
rule_loader::rule_exception_info::entry& out,
const rule_loader::context& ctx,
bool optional)
{
const YAML::Node& val = (key == NULL ? item : item[key]);
if(!val.IsDefined() && optional)
{
return;
}
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, (key == NULL ? "" : key), ctx);
if (val.IsScalar())
{
THROW(val.Scalar().empty(), "Value must be non-empty", valctx);
out.is_list = false;
THROW(!YAML::convert<string>::decode(val, out.item), "Could not decode scalar value", valctx);
}
if (val.IsSequence())
{
out.is_list = true;
for(const YAML::Node& v : val)
{
rule_loader::rule_exception_info::entry tmp;
rule_loader::context lctx(v, rule_loader::context::EXCEPTION, "", valctx);
// Optional is always false once you get past the outer values
optional = false;
decode_exception_info_entry(v, NULL, tmp, lctx, optional);
out.items.push_back(tmp);
}
}
}
static void decode_exception_fields(
const YAML::Node& item,
rule_loader::rule_exception_info::entry& out,
const rule_loader::context& ctx,
bool optional)
{
decode_exception_info_entry(item, "fields", out, ctx, optional);
}
static void decode_exception_comps(
const YAML::Node& item,
rule_loader::rule_exception_info::entry& out,
const rule_loader::context& ctx)
{
bool optional = true;
decode_exception_info_entry(item, "comps", out, ctx, optional);
}
static void decode_exception_values(
const YAML::Node& item,
rule_loader::rule_exception_info::entry& out,
const rule_loader::context& ctx)
{
bool optional = false;
decode_exception_info_entry(item, NULL, out, ctx, optional);
}
static void read_rule_exceptions(
const YAML::Node& item,
rule_loader::rule_info& v,
const rule_loader::context& parent,
bool append)
{
const YAML::Node& exs = item["exceptions"];
// No exceptions property, or an exceptions property with
// nothing in it, are allowed
if(!exs.IsDefined() || exs.IsNull())
{
return;
}
rule_loader::context exes_ctx(exs, rule_loader::context::EXCEPTIONS, "", parent);
THROW(!exs.IsSequence(), "Rule exceptions must be a sequence", exes_ctx);
for (auto &ex : exs)
{
// Make a temp context to verify simple properties
// about the exception.
std::string name;
rule_loader::context tmp(ex, rule_loader::context::EXCEPTION, "", exes_ctx);
THROW(!ex.IsMap(), "Rule exception must be a mapping", tmp);
decode_val(ex, "name", name, tmp);
// Now use a real context including the exception name.
rule_loader::context ex_ctx(ex, rule_loader::context::EXCEPTION, name, parent);
rule_loader::rule_exception_info v_ex(ex_ctx);
v_ex.name = name;
// note: the legacy lua loader used to throw a "xxx must strings" error
// fields are optional when append is true
decode_exception_fields(ex, v_ex.fields, ex_ctx, append);
decode_exception_comps(ex, v_ex.comps, ex_ctx);
const YAML::Node& exvals = ex["values"];
if (exvals.IsDefined())
{
rule_loader::context vals_ctx(exvals, rule_loader::context::EXCEPTION_VALUES, "", ex_ctx);
THROW(!exvals.IsSequence(),
"Rule exception values must be a sequence", vals_ctx);
for (auto &val : exvals)
{
rule_loader::context vctx(val, rule_loader::context::EXCEPTION_VALUE, "", vals_ctx);
rule_loader::rule_exception_info::entry v_ex_val;
decode_exception_values(val, v_ex_val, vctx);
v_ex.values.push_back(v_ex_val);
}
}
v.exceptions.push_back(v_ex);
}
}
static void read_item(
rule_loader::configuration& cfg,
rule_loader::collector& collector,
const YAML::Node& item,
const rule_loader::context& parent)
{
rule_loader::context tmp(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent);
THROW(!item.IsMap(), "Unexpected element type. "
"Each element should be a yaml associative array.", tmp);
if (item["required_engine_version"].IsDefined())
{
rule_loader::context ctx(item, rule_loader::context::REQUIRED_ENGINE_VERSION, "", parent);
rule_loader::engine_version_info v(ctx);
decode_val(item, "required_engine_version", v.version, ctx);
collector.define(cfg, v);
}
else if(item["required_plugin_versions"].IsDefined())
{
const YAML::Node& req_plugin_vers = item["required_plugin_versions"];
rule_loader::context ctx(req_plugin_vers, rule_loader::context::REQUIRED_PLUGIN_VERSIONS, "", parent);
THROW(!req_plugin_vers.IsSequence(),
"Value of required_plugin_versions must be a sequence",
ctx);
for(const YAML::Node& plugin : req_plugin_vers)
{
rule_loader::plugin_version_info::requirement r;
// Use a temp context until we can get a name
rule_loader::context tmp(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, "", ctx);
THROW(!plugin.IsMap(), "Plugin version must be a mapping", tmp);
decode_val(plugin, "name", r.name, tmp);
rule_loader::context pctx(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, r.name, ctx);
rule_loader::plugin_version_info v(pctx);
decode_val(plugin, "version", r.version, pctx);
v.alternatives.push_back(r);
const YAML::Node& alternatives = plugin["alternatives"];
if(alternatives.IsDefined())
{
THROW(!alternatives.IsSequence(),
"Value of plugin version alternatives must be a sequence",
pctx);
for (const auto &req : alternatives)
{
tmp = rule_loader::context(req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, "", pctx);
THROW(!req.IsMap(), "Plugin version alternative must be a mapping", tmp);
decode_val(req, "name", r.name, tmp);
tmp = rule_loader::context(req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, r.name, pctx);
decode_val(req, "version", r.version, tmp);
v.alternatives.push_back(r);
}
}
collector.define(cfg, v);
}
}
else if(item["list"].IsDefined())
{
std::string name;
// Using tmp context until name is decoded
rule_loader::context tmp(item, rule_loader::context::LIST, "", parent);
decode_val(item, "list", name, tmp);
rule_loader::context ctx(item, rule_loader::context::LIST, name, parent);
rule_loader::list_info v(ctx);
bool append = false;
decode_val(item, "list", v.name, ctx);
decode_items(item, v.items, ctx);
decode_optional_val(item, "append", append, ctx);
if(append)
{
collector.append(cfg, v);
}
else
{
collector.define(cfg, v);
}
}
else if(item["macro"].IsDefined())
{
std::string name;
// Using tmp context until name is decoded
rule_loader::context tmp(item, rule_loader::context::MACRO, "", parent);
decode_val(item, "macro", name, tmp);
rule_loader::context ctx(item, rule_loader::context::MACRO, name, parent);
rule_loader::macro_info v(ctx);
v.name = name;
bool append = false;
decode_val(item, "condition", v.cond, ctx);
// Now set the proper context for the condition now that we know it exists
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::MACRO_CONDITION, "", ctx);
decode_optional_val(item, "append", append, ctx);
if(append)
{
collector.append(cfg, v);
}
else
{
collector.define(cfg, v);
}
}
else if(item["rule"].IsDefined())
{
std::string name;
// Using tmp context until name is decoded
rule_loader::context tmp(item, rule_loader::context::RULE, "", parent);
decode_val(item, "rule", name, tmp);
rule_loader::context ctx(item, rule_loader::context::RULE, name, parent);
rule_loader::rule_info v(ctx);
v.name = name;
bool append = false;
v.enabled = true;
v.warn_evttypes = true;
v.skip_if_unknown_filter = false;
decode_optional_val(item, "append", append, ctx);
if(append)
{
decode_optional_val(item, "condition", v.cond, ctx);
if(item["condition"].IsDefined())
{
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx);
}
read_rule_exceptions(item, v, ctx, append);
collector.append(cfg, v);
}
else
{
// If the rule does *not* have any of
// condition/output/desc/priority, it *must*
// have an enabled property. Use the enabled
// property to set the enabled status of an
// earlier rule.
if (!item["condition"].IsDefined() &&
!item["output"].IsDefined() &&
!item["desc"].IsDefined() &&
!item["priority"].IsDefined())
{
decode_val(item, "enabled", v.enabled, ctx);
collector.enable(cfg, v);
}
else
{
string priority;
// All of these are required
decode_val(item, "condition", v.cond, ctx);
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx);
decode_val(item, "output", v.output, ctx);
v.output_ctx = rule_loader::context(item["output"], rule_loader::context::RULE_OUTPUT, "", ctx);
decode_val(item, "desc", v.desc, ctx);
decode_val(item, "priority", priority, ctx);
v.output = trim(v.output);
v.source = falco_common::syscall_source;
rule_loader::context prictx(item["priority"], rule_loader::context::RULE_PRIORITY, "", ctx);
THROW(!falco_common::parse_priority(priority, v.priority),
"Invalid priority", prictx);
decode_optional_val(item, "source", v.source, ctx);
decode_optional_val(item, "enabled", v.enabled, ctx);
decode_optional_val(item, "warn_evttypes", v.warn_evttypes, ctx);
decode_optional_val(item, "skip-if-unknown-filter", v.skip_if_unknown_filter, ctx);
decode_tags(item, v.tags, ctx);
read_rule_exceptions(item, v, ctx, append);
collector.define(cfg, v);
}
}
}
else
{
rule_loader::context ctx(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent);
cfg.res->add_warning(falco::load_result::LOAD_UNKNOWN_ITEM, "Unknown top level item", ctx);
}
}
bool rule_loader::reader::read(rule_loader::configuration& cfg, collector& collector)
{
std::vector<YAML::Node> docs;
try
{
docs = YAML::LoadAll(cfg.content);
}
catch(const exception& e)
{
rule_loader::context ctx(cfg.name);
cfg.res->add_error(falco::load_result::LOAD_ERR_YAML_PARSE, e.what(), ctx);
return false;
}
for (auto doc = docs.begin(); doc != docs.end(); doc++)
{
if (doc->IsDefined() && !doc->IsNull())
{
rule_loader::context ctx(cfg.name);
try {
THROW(!doc->IsMap() && !doc->IsSequence(),
"Rules content is not yaml",
ctx);
THROW(!doc->IsSequence(),
"Rules content is not yaml array of objects",
ctx);
for (auto it = doc->begin(); it != doc->end(); it++)
{
if (!it->IsNull())
{
read_item(cfg, collector, *it, ctx);
}
}
}
catch (rule_loader::rule_load_exception &e)
{
cfg.res->add_error(e.ec, e.msg, e.ctx);
// Although we *could* continue on to the next doc,
// as it's effectively a new rules file, for
// consistency we stop at the first error.
return false;
};
}
}
return true;
}

View File

@@ -16,22 +16,30 @@ limitations under the License.
#pragma once
#include <map>
#include <string>
#include <vector>
#include "rule_loader.h"
#include "rule_loader_collector.h"
namespace rule_loader
{
/*!
\brief Reads the contents of a ruleset
*/
class rule_reader
class reader
{
public:
virtual ~rule_reader() = default;
reader() = default;
virtual ~reader() = default;
reader(reader&&) = default;
reader& operator = (reader&&) = default;
reader(const reader&) = default;
reader& operator = (const reader&) = default;
/*!
\brief Reads the contents of a ruleset and uses a loader to store
\brief Reads the contents of a ruleset and uses a collector to store
thew new definitions
*/
virtual bool load(rule_loader::configuration& cfg, rule_loader& loader);
virtual bool read(configuration& cfg, collector& loader);
};
}; // namespace rule_loader

View File

@@ -1,329 +0,0 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "rule_reader.h"
#define THROW(cond, err) { if (cond) { throw falco_exception(err); } }
static rule_loader::context yaml_get_context(
const string& content,
const vector<YAML::Node>& docs,
vector<YAML::Node>::iterator doc,
YAML::iterator node)
{
rule_loader::context m;
YAML::Node item = *node++;
YAML::Node cur_doc = *doc++;
// include the "- " sequence mark
size_t from = item.Mark().pos - 2;
size_t to = 0;
if (node != cur_doc.end())
{
// end of item is beginning of next item
to = node->Mark().pos - 2;
}
else if (doc != docs.end())
{
// end of item is beginning of next doc
to = doc->Mark().pos - 4;
}
else
{
// end of item is end of file contents
to = content.length();
}
m.content = content.substr(from, to - from);
m.content = trim(m.content);
return m;
}
template <typename T>
static bool decode_val(const YAML::Node& v, T& out)
{
return v.IsDefined() && v.IsScalar() && YAML::convert<T>::decode(v, out);
}
template <typename T>
static bool decode_seq(const YAML::Node& item, vector<T>& out)
{
if (item.IsDefined() && item.IsSequence())
{
T value;
for(const YAML::Node& v : item)
{
THROW(!v.IsScalar() || !YAML::convert<T>::decode(v, value),
"Can't decode YAML sequence value: " + YAML::Dump(v));
out.push_back(value);
}
return true;
}
return false;
}
template <typename T>
static bool decode_seq(const YAML::Node& item, set<T>& out)
{
if (item.IsDefined() && item.IsSequence())
{
T value;
for(const YAML::Node& v : item)
{
THROW(!v.IsScalar() || !YAML::convert<T>::decode(v, value),
"Can't decode YAML sequence value: " + YAML::Dump(v));
out.insert(value);
}
return true;
}
return false;
}
static bool decode_exception_info_entry(
const YAML::Node& item,
rule_loader::rule_exception_info::entry& out)
{
if (item.IsDefined())
{
if (item.IsScalar())
{
out.is_list = false;
if (YAML::convert<string>::decode(item, out.item))
{
return true;
}
}
if (item.IsSequence())
{
out.is_list = true;
rule_loader::rule_exception_info::entry tmp;
for(const YAML::Node& v : item)
{
if (!decode_exception_info_entry(v, tmp))
{
return false;
}
out.items.push_back(tmp);
}
return true;
}
}
return false;
}
static void read_rule_exceptions(
const YAML::Node& item,
rule_loader::rule_info& v)
{
// An exceptions property with nothing in it is allowed
if(item.IsNull())
{
return;
}
THROW(!item.IsSequence(), "Rule exceptions must be a sequence");
for (auto &ex : item)
{
rule_loader::rule_exception_info v_ex;
THROW(!decode_val(ex["name"], v_ex.name) || v_ex.name.empty(),
"Rule exception item must have name property");
// note: the legacy lua loader used to throw a "xxx must strings" error
decode_exception_info_entry(ex["fields"], v_ex.fields);
decode_exception_info_entry(ex["comps"], v_ex.comps);
if (ex["values"].IsDefined())
{
THROW(!ex["values"].IsSequence(),
"Rule exception values must be a sequence");
for (auto &val : ex["values"])
{
rule_loader::rule_exception_info::entry v_ex_val;
decode_exception_info_entry(val, v_ex_val);
v_ex.values.push_back(v_ex_val);
}
}
v.exceptions.push_back(v_ex);
}
}
static void read_item(
rule_loader::configuration& cfg,
rule_loader& loader,
const YAML::Node& item,
const rule_loader::context& ctx)
{
if (item["required_engine_version"].IsDefined())
{
rule_loader::engine_version_info v;
THROW(!decode_val(item["required_engine_version"], v.version),
"Value of required_engine_version must be a number");
loader.define(cfg, v);
}
else if(item["required_plugin_versions"].IsDefined())
{
THROW(!item["required_plugin_versions"].IsSequence(),
"Value of required_plugin_versions must be a sequence");
for(const YAML::Node& plugin : item["required_plugin_versions"])
{
rule_loader::plugin_version_info v;
THROW(!decode_val(plugin["name"], v.name) || v.name.empty(),
"required_plugin_versions item must have name property");
THROW(!decode_val(plugin["version"], v.version) || v.version.empty(),
"required_plugin_versions item must have version property");
loader.define(cfg, v);
}
}
else if(item["list"].IsDefined())
{
rule_loader::list_info v;
v.ctx = ctx;
bool append = false;
THROW(!decode_val(item["list"], v.name) || v.name.empty(),
"List name is empty");
THROW(!decode_seq(item["items"], v.items),
"List must have property items");
if(decode_val(item["append"], append) && append)
{
loader.append(cfg, v);
}
else
{
loader.define(cfg, v);
}
}
else if(item["macro"].IsDefined())
{
rule_loader::macro_info v;
v.ctx = ctx;
bool append = false;
THROW(!decode_val(item["macro"], v.name) || v.name.empty(),
"Macro name is empty");
THROW(!decode_val(item["condition"], v.cond) || v.cond.empty(),
"Macro must have property condition");
if(decode_val(item["append"], append) && append)
{
loader.append(cfg, v);
}
else
{
loader.define(cfg, v);
}
}
else if(item["rule"].IsDefined())
{
rule_loader::rule_info v;
v.ctx = ctx;
bool append = false;
v.enabled = true;
v.warn_evttypes = true;
v.skip_if_unknown_filter = false;
THROW(!decode_val(item["rule"], v.name) || v.name.empty(),
"Rule name is empty");
if(decode_val(item["append"], append) && append)
{
decode_val(item["condition"], v.cond);
if (item["exceptions"].IsDefined())
{
read_rule_exceptions(item["exceptions"], v);
}
loader.append(cfg, v);
}
else
{
string priority;
bool has_enabled = decode_val(item["enabled"], v.enabled);
bool has_defs = decode_val(item["condition"], v.cond)
&& decode_val(item["output"], v.output)
&& decode_val(item["desc"], v.desc)
&& decode_val(item["priority"], priority);
if (!has_defs)
{
THROW(!has_enabled, "Rule must have properties 'condition', 'output', 'desc', and 'priority'");
loader.enable(cfg, v);
}
else
{
v.output = trim(v.output);
v.source = falco_common::syscall_source;
THROW(!falco_common::parse_priority(priority, v.priority),
"Invalid priority");
decode_val(item["source"], v.source);
decode_val(item["warn_evttypes"], v.warn_evttypes);
decode_val(item["skip-if-unknown-filter"], v.skip_if_unknown_filter);
decode_seq(item["tags"], v.tags);
if (item["exceptions"].IsDefined())
{
read_rule_exceptions(item["exceptions"], v);
}
loader.define(cfg, v);
}
}
}
else
{
cfg.warnings.push_back("Unknown top level object");
}
}
bool rule_reader::load(rule_loader::configuration& cfg, rule_loader& loader)
{
std::vector<YAML::Node> docs;
try
{
docs = YAML::LoadAll(cfg.content);
}
catch(const exception& e)
{
cfg.errors.push_back("Could not load YAML file: " + string(e.what()));
return false;
}
for (auto doc = docs.begin(); doc != docs.end(); doc++)
{
if (doc->IsDefined() && !doc->IsNull())
{
if(!doc->IsMap() && !doc->IsSequence())
{
cfg.errors.push_back("Rules content is not yaml");
return false;
}
if(!doc->IsSequence())
{
cfg.errors.push_back(
"Rules content is not yaml array of objects");
return false;
}
for (auto it = doc->begin(); it != doc->end(); it++)
{
if (!it->IsNull())
{
auto ctx = yaml_get_context(cfg.content, docs, doc, it);
YAML::Node item = *it;
try
{
THROW(!item.IsMap(), "Unexpected element type. "
"Each element should be a yaml associative array.");
read_item(cfg, loader, item, ctx);
}
catch(const exception& e)
{
cfg.errors.push_back(ctx.error(e.what()));
return false;
}
}
}
}
}
return true;
}

View File

@@ -45,37 +45,48 @@ void stats_manager::format(
out += "Rule counts by severity:\n";
for (size_t i = 0; i < m_by_priority.size(); i++)
{
if (m_by_priority[i] > 0)
auto val = m_by_priority[i].get()->load();
if (val > 0)
{
falco_common::format_priority(
(falco_common::priority_type) i, fmt, true);
transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
out += " " + fmt;
out += ": " + to_string(m_by_priority[i]) + "\n";
out += " " + fmt + ": " + to_string(val) + "\n";
}
}
out += "Triggered rules by rule name:\n";
for (size_t i = 0; i < m_by_rule_id.size(); i++)
{
if (m_by_rule_id[i] > 0)
auto val = m_by_rule_id[i].get()->load();
if (val > 0)
{
out += " " + rules.at(i)->name;
out += ": " + to_string(m_by_rule_id[i]) + "\n";
out += " " + rules.at(i)->name + ": " + to_string(val) + "\n";
}
}
}
void stats_manager::on_rule_loaded(const falco_rule& rule)
{
while (m_by_rule_id.size() <= rule.id)
{
m_by_rule_id.emplace_back();
m_by_rule_id[m_by_rule_id.size() - 1].reset(new atomic<uint64_t>(0));
}
while (m_by_priority.size() <= (size_t) rule.priority)
{
m_by_priority.emplace_back();
m_by_priority[m_by_priority.size() - 1].reset(new atomic<uint64_t>(0));
}
}
void stats_manager::on_event(const falco_rule& rule)
{
if (m_by_rule_id.size() <= rule.id)
if (m_by_rule_id.size() <= rule.id
|| m_by_priority.size() <= (size_t) rule.priority)
{
m_by_rule_id.resize(rule.id + 1, (uint64_t) 0);
throw falco_exception("rule id or priority out of bounds");
}
if (m_by_priority.size() <= (size_t) rule.priority)
{
m_by_priority.resize((size_t) rule.priority + 1, (uint64_t) 0);
}
m_total++;
m_by_rule_id[rule.id]++;
m_by_priority[(size_t) rule.priority]++;
m_total.fetch_add(1, std::memory_order_relaxed);
m_by_rule_id[rule.id]->fetch_add(1, std::memory_order_relaxed);
m_by_priority[(size_t) rule.priority]->fetch_add(1, std::memory_order_relaxed);
}

View File

@@ -18,17 +18,26 @@ limitations under the License.
#include <vector>
#include <string>
#include <atomic>
#include <memory>
#include "falco_rule.h"
#include "indexed_vector.h"
/*!
\brief Manager for the internal statistics of the rule engine
\brief Manager for the internal statistics of the rule engine.
The on_event() is thread-safe and non-blocking, and it can be used
concurrently across many callers in parallel.
All the other methods are not thread safe.
*/
class stats_manager
{
public:
stats_manager();
virtual ~stats_manager();
stats_manager(stats_manager&&) = default;
stats_manager& operator = (stats_manager&&) = default;
stats_manager(const stats_manager&) = default;
stats_manager& operator = (const stats_manager&) = default;
/*!
\brief Erases the internal state and statistics data
@@ -36,19 +45,29 @@ public:
virtual void clear();
/*!
\brief Callback for when a given rule matches an event
\brief Callback for when a new rule is loaded in the engine.
Rules must be passed through this method before submitting them as
an argument of on_event().
*/
virtual void on_rule_loaded(const falco_rule& rule);
/*!
\brief Callback for when a given rule matches an event.
This method is thread-safe.
\throws falco_exception if rule has not been passed to
on_rule_loaded() first
*/
virtual void on_event(const falco_rule& rule);
/*!
\brief Formats the internal statistics into the out string
\brief Formats the internal statistics into the out string.
*/
virtual void format(
const indexed_vector<falco_rule>& rules,
std::string& out) const;
private:
uint64_t m_total;
std::vector<uint64_t> m_by_priority;
std::vector<uint64_t> m_by_rule_id;
atomic<uint64_t> m_total;
std::vector<std::unique_ptr<atomic<uint64_t>>> m_by_priority;
std::vector<std::unique_ptr<atomic<uint64_t>>> m_by_rule_id;
};

View File

@@ -20,7 +20,7 @@ set(
app_actions/create_signal_handlers.cpp
app_actions/daemonize.cpp
app_actions/init_falco_engine.cpp
app_actions/init_inspector.cpp
app_actions/init_inspectors.cpp
app_actions/init_clients.cpp
app_actions/init_outputs.cpp
app_actions/list_fields.cpp
@@ -35,10 +35,17 @@ set(
app_actions/print_ignored_events.cpp
app_actions/print_plugin_info.cpp
app_actions/print_support.cpp
app_actions/print_syscall_events.cpp
app_actions/print_version.cpp
app_actions/print_page_size.cpp
app_actions/compute_syscall_buffer_size.cpp
app_actions/select_event_sources.cpp
app_actions/start_grpc_server.cpp
app_actions/start_webserver.cpp
app_actions/validate_rules_files.cpp
app_actions/create_requested_paths.cpp
app_actions/configure_interesting_sets.cpp
app_actions/gain_lock.cpp
configuration.cpp
logger.cpp
falco_outputs.cpp
@@ -47,7 +54,7 @@ set(
outputs_stdout.cpp
outputs_syslog.cpp
event_drops.cpp
statsfilewriter.cpp
stats_writer.cpp
falco.cpp
)
@@ -61,9 +68,10 @@ set(
"${YAMLCPP_INCLUDE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}"
"${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include"
)
list(APPEND FALCO_INCLUDE_DIRECTORIES "${FALCO_EXTRA_INCLUDE_DIRS}")
set(
FALCO_DEPENDENCIES
string-view-lite

View File

@@ -0,0 +1,71 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
using namespace falco::app;
/* These indexes could change over the Falco releases. */
#define MIN_INDEX 1
#define MAX_INDEX 10
#define DEFAULT_BYTE_SIZE 1 << 23
application::run_result application::configure_syscall_buffer_size()
{
/* We don't need to compute the syscall buffer dimension if we are in capture mode or if the
* the syscall source is not enabled.
*/
if(is_capture_mode() || m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end())
{
return run_result::ok();
}
uint16_t index = m_state->config->m_syscall_buf_size_preset;
if(index < MIN_INDEX || index > MAX_INDEX)
{
return run_result::fatal("The 'syscall_buf_size_preset' value must be between '" + std::to_string(MIN_INDEX) + "' and '" + std::to_string(MAX_INDEX) + "'\n");
}
/* Sizes from `1 MB` to `512 MB`. The index `0` is reserved, users cannot use it! */
std::vector<uint32_t> vect{0, 1 << 20, 1 << 21, 1 << 22, DEFAULT_BYTE_SIZE, 1 << 24, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29};
uint64_t chosen_size = vect[index];
/* If the page size is not valid we return here. */
long page_size = getpagesize();
if(page_size <= 0)
{
m_state->syscall_buffer_bytes_size = DEFAULT_BYTE_SIZE;
falco_logger::log(LOG_WARNING, "Unable to get the system page size through 'getpagesize()'. Try to use the default syscall buffer dimension: " + std::to_string(DEFAULT_BYTE_SIZE) + " bytes\n");
return run_result::ok();
}
/* Check if the chosen size is a multiple of the page size. */
if(chosen_size % page_size != 0)
{
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not a multiple of your system page size '" + std::to_string(page_size) + "'. Please configure a greater 'syscall_buf_size_preset' value in the Falco configuration file\n");
}
/* Check if the chosen size is greater than `2 * page_size`. */
if((chosen_size / page_size) <= 2)
{
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not greater than '2 * " + std::to_string(page_size) + "' where '" + std::to_string(page_size) + "' is your system page size. Please configure a greater 'syscall_buf_size_preset' value in the Falco configuration file\n");
}
m_state->syscall_buffer_bytes_size = chosen_size;
falco_logger::log(LOG_INFO, "The chosen syscall buffer dimension is: " + std::to_string(chosen_size) + " bytes (" + std::to_string(chosen_size / (uint64_t)(1024 * 1024)) + " MBs)\n");
return run_result::ok();
}

View File

@@ -0,0 +1,43 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
using namespace falco::app;
void application::configure_interesting_sets()
{
/// TODO: in the next future we need to change the interface of `enforce_simple_ppm_sc_set`
/// and `enforce_sinsp_state_tp` APIs, they shouldn't require an inspector to be called!
std::unique_ptr<sinsp> inspector(new sinsp());
/* Please note: here we fill these 2 sets because we are interested in only some features, if we leave
* them empty `libsinsp` will fill them with all the available syscalls and all the available tracepoints!
*/
/* Here the `libsinsp` state set is not enough, we need other syscalls used in the rules,
* so we use the `simple_set`, this `simple_set` contains all the syscalls of the `libsinsp` state
* plus syscalls for Falco default rules.
*/
m_state->ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set();
/* In this case we get the tracepoints for the `libsinsp` state and we remove
* the `sched_switch` tracepoint since it is highly noisy and not so useful
* for our state/events enrichment.
*/
m_state->tp_of_interest = inspector->enforce_sinsp_state_tp();
m_state->tp_of_interest.erase(SCHED_SWITCH);
}

View File

@@ -0,0 +1,111 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include "falco_utils.h"
#include <sys/stat.h>
#ifndef CPPPATH_SEP
#ifdef _MSC_VER
#define CPPPATH_SEP "\\"
#else
#define CPPPATH_SEP "/"
#endif
#endif
using namespace falco::app;
application::run_result application::create_requested_paths()
{
if(!m_options.gvisor_config.empty())
{
// This is bad: parsing gvisor config to get endpoint
// to be able to auto-create the path to the file for the user.
std::ifstream reader(m_options.gvisor_config);
if (reader.fail())
{
return run_result::fatal(m_options.gvisor_config + ": cannot open file");
}
nlohmann::json parsed_json;
std::string gvisor_socket;
try
{
parsed_json = nlohmann::json::parse(reader);
}
catch (const std::exception &e)
{
return run_result::fatal(m_options.gvisor_config + ": cannot parse JSON: " + e.what());
}
try
{
gvisor_socket = parsed_json["trace_session"]["sinks"][0]["config"]["endpoint"];
}
catch (const std::exception &e)
{
return run_result::fatal(m_options.gvisor_config + ": failed to fetch config.endpoint: " + e.what());
}
int ret = create_dir(gvisor_socket);
if (ret != 0)
{
return run_result::fatal(gvisor_socket + ": " + strerror(errno));
}
}
if (m_state->config->m_grpc_enabled && !m_state->config->m_grpc_bind_address.empty())
{
if(falco::utils::network::is_unix_scheme(m_state->config->m_grpc_bind_address))
{
auto server_path = m_state->config->m_grpc_bind_address.substr(
falco::utils::network::UNIX_SCHEME.length()
);
int ret = create_dir(server_path);
if(ret != 0)
{
return run_result::fatal(server_path + ": " + strerror(errno));
}
}
}
// TODO: eventually other files written by Falco whose destination is
// customizable by users, must be handled here.
return run_result::ok();
}
int application::create_dir(const std::string &path)
{
// Properly reset errno
errno = 0;
istringstream f(path);
string path_until_token;
string s;
// Create all the subfolder stopping at last token (f.eof());
// Examples:
// "/tmp/foo/bar" -> "", "tmp", "foo" -> mkdir("/") + mkdir("/tmp/") + midir("/tmp/foo/")
// "tmp/foo/bar" -> "tmp", "foo" -> mkdir("tmp/") + midir("tmp/foo/")
while (getline(f, s, *CPPPATH_SEP) && !f.eof()) {
path_until_token += s + CPPPATH_SEP;
int ret = mkdir(path_until_token.c_str(), 0600);
if (ret != 0 && errno != EEXIST)
{
return ret;
}
}
return 0;
}

View File

@@ -36,16 +36,19 @@ static int inot_fd;
static void signal_callback(int signal)
{
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
s_app.get().terminate();
}
static void reopen_outputs(int signal)
{
falco_logger::log(LOG_INFO, "SIGUSR1 received, reopening outputs...\n");
s_app.get().reopen_outputs();
}
static void restart_falco(int signal)
{
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
s_app.get().restart();
}
@@ -72,15 +75,14 @@ bool application::create_handler(int sig, void (*func)(int), run_result &ret)
application::run_result application::create_signal_handlers()
{
run_result ret;
s_app = *this;
if(! create_handler(SIGINT, ::signal_callback, ret) ||
! create_handler(SIGTERM, ::signal_callback, ret) ||
! create_handler(SIGUSR1, ::reopen_outputs, ret) ||
! create_handler(SIGHUP, ::restart_falco, ret))
{
return ret;
s_app = dummy;
}
s_app = *this;
return ret;
}
@@ -91,7 +93,7 @@ application::run_result application::attach_inotify_signals()
inot_fd = inotify_init();
if (inot_fd == -1)
{
return run_result::fatal("Could not create inotify handler.");
return run_result::fatal("Could not create inotify handler");
}
struct sigaction sa;
@@ -100,13 +102,13 @@ application::run_result application::attach_inotify_signals()
sa.sa_handler = restart_falco;
if (sigaction(SIGIO, &sa, NULL) == -1)
{
return run_result::fatal("Failed to link SIGIO to inotify handler.");
return run_result::fatal("Failed to link SIGIO to inotify handler");
}
/* Set owner process that is to receive "I/O possible" signal */
if (fcntl(inot_fd, F_SETOWN, getpid()) == -1)
{
return run_result::fatal("Failed to setting owner on inotify handler.");
return run_result::fatal("Failed to setting owner on inotify handler");
}
/*
@@ -116,14 +118,14 @@ application::run_result application::attach_inotify_signals()
int flags = fcntl(inot_fd, F_GETFL);
if (fcntl(inot_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1)
{
return run_result::fatal("Failed to setting flags on inotify handler.");
return run_result::fatal("Failed to setting flags on inotify handler");
}
// Watch conf file
int wd = inotify_add_watch(inot_fd, m_options.conf_filename.c_str(), IN_CLOSE_WRITE);
if (wd == -1)
{
return run_result::fatal("Failed to watch conf file.");
return run_result::fatal("Failed to watch conf file");
}
falco_logger::log(LOG_DEBUG, "Watching " + m_options.conf_filename +"\n");
@@ -135,7 +137,7 @@ application::run_result application::attach_inotify_signals()
{
return run_result::fatal("Failed to watch rule file: " + rule);
}
falco_logger::log(LOG_DEBUG, "Watching " + rule +".\n");
falco_logger::log(LOG_DEBUG, "Watching " + rule +"\n");
}
// Watch specified rules folders, if any:
@@ -149,7 +151,7 @@ application::run_result application::attach_inotify_signals()
{
return run_result::fatal("Failed to watch rule folder: " + fld);
}
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder.\n");
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder\n");
}
}
return run_result::ok();

View File

@@ -34,7 +34,7 @@ application::run_result application::daemonize()
pid = fork();
if (pid < 0) {
// error
return run_result::fatal("Could not fork.");
return run_result::fatal("Could not fork");
} else if (pid > 0) {
// parent. Write child pid to pidfile and exit
std::ofstream pidfile;
@@ -54,7 +54,7 @@ application::run_result application::daemonize()
// Become own process group.
sid = setsid();
if (sid < 0) {
return run_result::fatal("Could not set session id.");
return run_result::fatal("Could not set session id");
}
// Set umask so no files are world anything or group writable.
@@ -62,7 +62,7 @@ application::run_result application::daemonize()
// Change working directory to '/'
if ((chdir("/")) < 0) {
return run_result::fatal("Could not change working directory to '/'.");
return run_result::fatal("Could not change working directory to '/'");
}
// Close stdin, stdout, stderr and reopen to /dev/null

View File

@@ -0,0 +1,45 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include <sys/file.h>
using namespace falco::app;
#define FALCO_LOCK_FILE "falco.lock"
application::run_result application::gain_lock()
{
m_lock_fd = open(FALCO_LOCK_FILE, O_CREAT | O_WRONLY, 0644);
if (m_lock_fd < 0)
{
return run_result::fatal(std::string("Failed to open " FALCO_LOCK_FILE " lock file: ") + strerror(errno));
}
if (flock(m_lock_fd, LOCK_EX | LOCK_NB) == -1)
{
return run_result::fatal("A lock is present on " FALCO_LOCK_FILE " Another Falco instance running?");
}
return run_result::ok();
}
bool application::release_lock(std::string &errstr)
{
if (m_lock_fd > 0)
{
close(m_lock_fd);
}
return true;
}

View File

@@ -22,15 +22,17 @@ application::run_result application::init_clients()
{
#ifndef MINIMAL_BUILD
// k8s and mesos clients are useful only if syscall source is enabled
if (!is_syscall_source_enabled())
if (m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end())
{
return run_result::ok();
}
auto inspector = m_state->source_infos.at(falco_common::syscall_source)->inspector;
falco_logger::log(LOG_DEBUG, "Setting metadata download max size to " + to_string(m_state->config->m_metadata_download_max_mb) + " MB\n");
falco_logger::log(LOG_DEBUG, "Setting metadata download chunk wait time to " + to_string(m_state->config->m_metadata_download_chunk_wait_us) + " μs\n");
falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + to_string(m_state->config->m_metadata_download_watch_freq_sec) + " seconds\n");
m_state->inspector->set_metadata_download_params(m_state->config->m_metadata_download_max_mb * 1024 * 1024, m_state->config->m_metadata_download_chunk_wait_us, m_state->config->m_metadata_download_watch_freq_sec);
inspector->set_metadata_download_params(m_state->config->m_metadata_download_max_mb * 1024 * 1024, m_state->config->m_metadata_download_chunk_wait_us, m_state->config->m_metadata_download_watch_freq_sec);
//
// Run k8s, if required
@@ -53,7 +55,7 @@ application::run_result application::init_clients()
*k8s_api_cert_ptr = k8s_cert_env;
}
}
m_state->inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, m_options.verbose);
inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, m_options.verbose);
}
//
@@ -65,12 +67,12 @@ application::run_result application::init_clients()
// passes a pointer but the inspector does
// *not* own it and does not use it after
// init_mesos_client() returns.
m_state->inspector->init_mesos_client(&(m_options.mesos_api), m_options.verbose);
inspector->init_mesos_client(&(m_options.mesos_api), m_options.verbose);
}
else if(char* mesos_api_env = getenv("FALCO_MESOS_API"))
{
std::string mesos_api_copy = mesos_api_env;
m_state->inspector->init_mesos_client(&mesos_api_copy, m_options.verbose);
inspector->init_mesos_client(&mesos_api_copy, m_options.verbose);
}
#endif

View File

@@ -15,6 +15,7 @@ limitations under the License.
*/
#include "application.h"
#include <plugin_manager.h>
using namespace falco::app;
@@ -45,7 +46,7 @@ void application::configure_output_format()
}
else if(m_options.gvisor_config != "")
{
output_format = "container=%container.id pid=%proc.vpid tid=%thread.vtid";
output_format = "container=%container.name (id=%container.id) vpid=%proc.vpid vtid=%thread.vtid";
replace_container_info = true;
}
@@ -55,40 +56,80 @@ void application::configure_output_format()
}
}
application::run_result application::init_falco_engine()
void application::add_source_to_engine(const std::string& src)
{
configure_output_format();
auto src_info = m_state->source_infos.at(src);
std::shared_ptr<gen_event_filter_factory> filter_factory = nullptr;
std::shared_ptr<gen_event_formatter_factory> formatter_factory = nullptr;
// Create "factories" that can create filters/formatters for syscalls
if (src == falco_common::syscall_source)
{
filter_factory = std::shared_ptr<gen_event_filter_factory>(
new sinsp_filter_factory(src_info->inspector.get()));
formatter_factory = std::shared_ptr<gen_event_formatter_factory>(
new sinsp_evt_formatter_factory(src_info->inspector.get()));
}
else
{
auto &filterchecks = m_state->source_infos.at(src)->filterchecks;
filter_factory = std::shared_ptr<gen_event_filter_factory>(
new sinsp_filter_factory(src_info->inspector.get(), filterchecks));
formatter_factory = std::shared_ptr<gen_event_formatter_factory>(
new sinsp_evt_formatter_factory(src_info->inspector.get(), filterchecks));
}
// libs requires raw pointer, we should modify libs to use reference/shared_ptr
std::shared_ptr<gen_event_filter_factory> syscall_filter_factory(new sinsp_filter_factory(m_state->inspector.get()));
// libs requires raw pointer, we should modify libs to use reference/shared_ptr
std::shared_ptr<gen_event_formatter_factory> syscall_formatter_factory(new sinsp_evt_formatter_factory(m_state->inspector.get()));
m_state->syscall_source_idx = m_state->engine->add_source(falco_common::syscall_source, syscall_filter_factory, syscall_formatter_factory);
if(m_state->config->m_json_output)
{
syscall_formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
}
for(const auto &src : m_options.disable_sources)
src_info->engine_idx = m_state->engine->add_source(
src, filter_factory, formatter_factory);
}
application::run_result application::init_falco_engine()
{
// add all non-syscall event sources in engine
for (const auto& src : m_state->loaded_sources)
{
if (m_state->enabled_sources.find(src) == m_state->enabled_sources.end())
if (src != falco_common::syscall_source)
{
return run_result::fatal("Attempted disabling unknown event source: " + src);
// we skip the syscall as we want it to be the one added for last
// in the engine. This makes the source index assignment easier.
add_source_to_engine(src);
}
m_state->enabled_sources.erase(src);
}
// todo(jasondellaluce,leogr): change this once we attain multiple active source
if(m_state->enabled_sources.empty())
// add syscall as last source
add_source_to_engine(falco_common::syscall_source);
// note: in capture mode, we can assume that the plugin source index will
// be the same in both the falco engine and the sinsp plugin manager.
// This assumption stands because the plugin manager stores sources in a
// vector, and the syscall source is appended in the engine *after* the sources
// coming from plugins. The reason why this can't work with live mode,
// is because in that case event sources are scattered across different
// inspectors. Since this is an implementation-based assumption, we
// check this and return an error to spot regressions in the future.
if (is_capture_mode())
{
return run_result::fatal("At least one event source needs to be enabled");
auto manager = m_state->offline_inspector->get_plugin_manager();
for (const auto &p : manager->plugins())
{
if (p->caps() & CAP_SOURCING)
{
bool added = false;
auto source_idx = manager->source_idx_by_plugin_id(p->id(), added);
auto engine_idx = m_state->source_infos.at(p->event_source())->engine_idx;
if (!added || source_idx != engine_idx)
{
return run_result::fatal("Could not add event source in the engine: " + p->event_source());
}
}
}
}
configure_output_format();
m_state->engine->set_min_priority(m_state->config->m_min_priority);
return run_result::ok();

View File

@@ -1,58 +0,0 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
using namespace falco::app;
application::run_result application::init_inspector()
{
m_state->inspector->set_buffer_format(m_options.event_buffer_format);
// If required, set the CRI paths
for (auto &p : m_options.cri_socket_paths)
{
if (!p.empty())
{
m_state->inspector->add_cri_socket_path(p);
}
}
// Decide whether to do sync or async for CRI metadata fetch
m_state->inspector->set_cri_async(!m_options.disable_cri_async);
//
// If required, set the snaplen
//
if(m_options.snaplen != 0)
{
m_state->inspector->set_snaplen(m_options.snaplen);
}
if(!m_options.all_events)
{
// Drop EF_DROP_SIMPLE_CONS kernel side
m_state->inspector->set_simple_consumer();
// Eventually, drop any EF_DROP_SIMPLE_CONS event
// that reached userspace (there are some events that are not syscall-based
// like signaldeliver, that have the EF_DROP_SIMPLE_CONS flag)
m_state->inspector->set_drop_event_flags(EF_DROP_SIMPLE_CONS);
}
m_state->inspector->set_hostname_and_port_resolution_mode(false);
return run_result::ok();
}

View File

@@ -0,0 +1,195 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include <unordered_set>
#include <plugin_manager.h>
using namespace falco::app;
void application::init_syscall_inspector(
std::shared_ptr<sinsp> inspector,
const falco::app::cmdline_options& opts)
{
inspector->set_buffer_format(opts.event_buffer_format);
// If required, set the CRI paths
for (auto &p : opts.cri_socket_paths)
{
if (!p.empty())
{
inspector->add_cri_socket_path(p);
}
}
// Decide whether to do sync or async for CRI metadata fetch
inspector->set_cri_async(!opts.disable_cri_async);
//
// If required, set the snaplen
//
if(opts.snaplen != 0)
{
inspector->set_snaplen(opts.snaplen);
}
if(!opts.all_events)
{
configure_interesting_sets();
}
inspector->set_hostname_and_port_resolution_mode(false);
}
static bool populate_filterchecks(
std::shared_ptr<sinsp> inspector,
const std::string& source,
filter_check_list& filterchecks,
std::unordered_set<std::string>& used_plugins,
std::string& err)
{
std::vector<const filter_check_info*> info;
for(const auto& p : inspector->get_plugin_manager()->plugins())
{
if (!(p->caps() & CAP_EXTRACTION))
{
continue;
}
// check if some fields are overlapping on this event sources
info.clear();
filterchecks.get_all_fields(info);
for (auto &info : info)
{
for (int32_t i = 0; i < info->m_nfields; i++)
{
// check if one of the fields extractable by the plugin
// is already provided by another filtercheck for this source
std::string fname = info->m_fields[i].m_name;
for (auto &f : p->fields())
{
if (std::string(f.m_name) == fname)
{
err = "Plugin '" + p->name()
+ "' supports extraction of field '" + fname
+ "' that is overlapping for source '" + source + "'";
return false;
}
}
}
}
// add plugin filterchecks to the event source
filterchecks.add_filter_check(sinsp_plugin::new_filtercheck(p));
used_plugins.insert(p->name());
}
return true;
}
application::run_result application::init_inspectors()
{
std::string err;
std::unordered_set<std::string> used_plugins;
const auto& all_plugins = m_state->offline_inspector->get_plugin_manager()->plugins();
for (const auto &src : m_state->loaded_sources)
{
auto src_info = m_state->source_infos.at(src);
// in capture mode, every event source uses the offline inspector.
// in live mode, we create a new inspector for each event source
src_info->inspector = is_capture_mode()
? m_state->offline_inspector
: std::make_shared<sinsp>();
// handle syscall and plugin sources differently
// todo(jasondellaluce): change this once we support extracting plugin fields from syscalls too
if (src == falco_common::syscall_source)
{
init_syscall_inspector(src_info->inspector, m_options);
continue;
}
// load and init all plugins compatible with this event source
// (if in capture mode, all plugins will be inited on the same inspector)
for (const auto& p : all_plugins)
{
std::shared_ptr<sinsp_plugin> plugin = nullptr;
auto config = m_state->plugin_configs.at(p->name());
auto is_input = p->caps() & CAP_SOURCING && p->event_source() == src;
if (is_capture_mode())
{
// in capture mode, every plugin is already registered
// in the offline inspector by the load_plugins action
plugin = p;
}
else
{
// in live mode, for the inspector assigned to the given
// event source, we must register the plugin supporting
// that event source and also plugins with field extraction
// capability that are compatible with that event source
if (is_input || (p->caps() & CAP_EXTRACTION && p->is_source_compatible(src)))
{
plugin = src_info->inspector->register_plugin(config->m_library_path);
}
}
// init the plugin, if we registered it into an inspector
// (in capture mode, this is true for every plugin)
if (plugin)
{
if (!plugin->init(config->m_init_config, err))
{
return run_result::fatal(err);
}
if (is_input)
{
auto gen_check = src_info->inspector->new_generic_filtercheck();
src_info->filterchecks.add_filter_check(gen_check);
}
used_plugins.insert(plugin->name());
}
}
// populate filtercheck list for this inspector
if (!populate_filterchecks(
src_info->inspector,
src,
src_info->filterchecks,
used_plugins,
err))
{
return run_result::fatal(err);
}
}
// check if some plugin with field extraction capability remains unused
for (const auto& p : all_plugins)
{
if(used_plugins.find(p->name()) == used_plugins.end()
&& p->caps() & CAP_EXTRACTION
&& !(p->caps() & CAP_SOURCING && p->is_source_compatible(p->event_source())))
{
return run_result::fatal("Plugin '" + p->name()
+ "' has field extraction capability but is not compatible with any known event source");
}
}
return run_result::ok();
}

View File

@@ -25,9 +25,12 @@ application::run_result application::init_outputs()
{
// read hostname
std::string hostname;
if(char* env_hostname = getenv("FALCO_GRPC_HOSTNAME"))
char* env_hostname = getenv("FALCO_HOSTNAME");
// todo(leogr): keep FALCO_GRPC_HOSTNAME for backward compatibility. Shall we deprecate it?
if(env_hostname || (env_hostname = getenv("FALCO_GRPC_HOSTNAME")))
{
hostname = env_hostname;
falco_logger::log(LOG_INFO, "Hostname value has been overridden via environment variable to: " + hostname + "\n");
}
else
{
@@ -40,20 +43,16 @@ application::run_result application::init_outputs()
hostname = c_hostname;
}
m_state->outputs->init(m_state->engine,
m_state->config->m_json_output,
m_state->config->m_json_include_output_property,
m_state->config->m_json_include_tags_property,
m_state->config->m_output_timeout,
m_state->config->m_notifications_rate, m_state->config->m_notifications_max_burst,
m_state->config->m_buffered_outputs,
m_state->config->m_time_format_iso_8601,
hostname);
for(auto output : m_state->config->m_outputs)
{
m_state->outputs->add_output(output);
}
m_state->outputs.reset(new falco_outputs(
m_state->engine,
m_state->config->m_outputs,
m_state->config->m_json_output,
m_state->config->m_json_include_output_property,
m_state->config->m_json_include_tags_property,
m_state->config->m_output_timeout,
m_state->config->m_buffered_outputs,
m_state->config->m_time_format_iso_8601,
hostname));
return run_result::ok();
}

View File

@@ -33,12 +33,5 @@ application::run_result application::list_fields()
return run_result::exit();
}
if(m_options.list_syscall_events)
{
// We know this function doesn't hold into the raw pointer value
list_events(m_state->inspector.get(), m_options.markdown);
return run_result::exit();
}
return run_result::ok();
}

View File

@@ -24,14 +24,17 @@ application::run_result application::list_plugins()
if(m_options.list_plugins)
{
std::ostringstream os;
const auto &plugins = m_state->inspector->get_plugin_manager()->plugins();
for (auto &p : plugins)
std::unique_ptr<sinsp> inspector(new sinsp());
const auto& configs = m_state->config->m_plugins;
for (auto &c : configs)
{
format_plugin_info(p, os);
// load the plugin (no need to initialize it)
auto plugin = inspector->register_plugin(c.m_library_path);
format_plugin_info(plugin, os);
os << std::endl;
}
printf("%lu Plugins Loaded:\n\n%s\n", plugins.size(), os.str().c_str());
printf("%lu Plugins Loaded:\n\n%s\n", configs.size(), os.str().c_str());
return run_result::exit();
}

View File

@@ -26,8 +26,8 @@ application::run_result application::load_config()
falco_logger::set_time_format_iso_8601(m_state->config->m_time_format_iso_8601);
// log after config init because config determines where logs go
falco_logger::log(LOG_INFO, "Falco version " + std::string(FALCO_VERSION) + "\n");
falco_logger::log(LOG_INFO, "Falco initialized with configuration file " + m_options.conf_filename + "\n");
falco_logger::log(LOG_INFO, "Falco version: " + std::string(FALCO_VERSION) + " (" + std::string(FALCO_TARGET_ARCH) + ")\n");
falco_logger::log(LOG_INFO, "Falco initialized with configuration file: " + m_options.conf_filename + "\n");
}
else
{

View File

@@ -27,117 +27,34 @@ application::run_result application::load_plugins()
return run_result::fatal("Can not load/use plugins with musl optimized build");
}
#endif
auto empty_src_info = state::source_info{};
// The only enabled event source is syscall by default
m_state->enabled_sources = {falco_common::syscall_source};
// Initialize the set of loaded event sources.
// By default, the set includes the 'syscall' event source
m_state->source_infos.clear();
m_state->source_infos.insert(empty_src_info, falco_common::syscall_source);
m_state->loaded_sources = { falco_common::syscall_source };
std::string err = "";
std::shared_ptr<sinsp_plugin> loaded_plugin = nullptr;
// Initialize map of plugin configs
m_state->plugin_configs.clear();
// Initialize the offline inspector. This is used to load all the configured
// plugins in order to have them available every time we need to access
// their static info. If Falco is in capture mode, this inspector is also
// used to open and read the trace file
m_state->offline_inspector.reset(new sinsp());
// Load all the configured plugins
for(auto &p : m_state->config->m_plugins)
{
falco_logger::log(LOG_INFO, "Loading plugin (" + p.m_name + ") from file " + p.m_library_path + "\n");
auto plugin = m_state->inspector->register_plugin(p.m_library_path);
if (!plugin->init(p.m_init_config, err))
{
return run_result::fatal(err);
}
falco_logger::log(LOG_INFO, "Loading plugin '" + p.m_name + "' from file " + p.m_library_path + "\n");
auto plugin = m_state->offline_inspector->register_plugin(p.m_library_path);
m_state->plugin_configs.insert(p, plugin->name());
if(plugin->caps() & CAP_SOURCING)
{
if (!is_capture_mode())
{
// todo(jasondellaluce): change this once we support multiple enabled event sources
if(loaded_plugin)
{
return run_result::fatal("Can not load multiple plugins with event sourcing capability: '"
+ loaded_plugin->name()
+ "' already loaded");
}
loaded_plugin = plugin;
m_state->enabled_sources = {plugin->event_source()};
m_state->inspector->set_input_plugin(p.m_name, p.m_open_params);
}
// Init filtercheck list for the plugin's source and add the
// event-generic filterchecks
auto &filterchecks = m_state->plugin_filter_checks[plugin->event_source()];
filterchecks.add_filter_check(m_state->inspector->new_generic_filtercheck());
// Factories that can create filters/formatters for the event source of the plugin.
std::shared_ptr<gen_event_filter_factory> filter_factory(new sinsp_filter_factory(m_state->inspector.get(), filterchecks));
std::shared_ptr<gen_event_formatter_factory> formatter_factory(new sinsp_evt_formatter_factory(m_state->inspector.get(), filterchecks));
if(m_state->config->m_json_output)
{
formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
}
// note: here we assume that the source index will be the same in
// both the falco engine and the sinsp plugin manager. This assumption
// stands because the plugin manager stores sources in a vector, and
// the syscall source is appended in the engine *after* the sources
// coming from plugins. Since this is an implementation-based
// assumption, we check this and return an error to spot
// regressions in the future. We keep it like this for to avoid the
// overhead of additional mappings at runtime, but we may consider
// mapping the two indexes under something like std::unordered_map in the future.
bool added = false;
auto source_idx = m_state->inspector->get_plugin_manager()->source_idx_by_plugin_id(plugin->id(), added);
auto source_idx_engine = m_state->engine->add_source(plugin->event_source(), filter_factory, formatter_factory);
if (!added || source_idx != source_idx_engine)
{
return run_result::fatal("Could not add event source in the engine: " + plugin->event_source());
}
}
}
// Iterate over the plugins with extractor capability and add them to the
// filtercheck list of their compatible sources
std::vector<const filter_check_info*> filtercheck_info;
for(const auto& p : m_state->inspector->get_plugin_manager()->plugins())
{
if (!(p->caps() & CAP_EXTRACTION))
{
continue;
}
bool used = false;
for (auto &it : m_state->plugin_filter_checks)
{
// check if the event source is compatible with this plugin
if (p->is_source_compatible(it.first))
{
// check if some fields are overlapping on this event sources
filtercheck_info.clear();
it.second.get_all_fields(filtercheck_info);
for (auto &info : filtercheck_info)
{
for (int32_t i = 0; i < info->m_nfields; i++)
{
// check if one of the fields extractable by the plugin
// is already provided by another filtercheck for this source
std::string fname = info->m_fields[i].m_name;
for (auto &f : p->fields())
{
if (std::string(f.m_name) == fname)
{
return run_result::fatal(
"Plugin '" + p->name()
+ "' supports extraction of field '" + fname
+ "' that is overlapping for source '" + it.first + "'");
}
}
}
}
// add plugin filterchecks to the event source
it.second.add_filter_check(sinsp_plugin::new_filtercheck(p));
used = true;
}
}
if (!used)
{
return run_result::fatal("Plugin '" + p->name()
+ "' has field extraction capability but is not compatible with any enabled event source");
auto sname = plugin->event_source();
m_state->source_infos.insert(empty_src_info, sname);
m_state->loaded_sources.insert(sname);
}
}

View File

@@ -19,53 +19,83 @@ limitations under the License.
using namespace falco::app;
bool application::check_rules_plugin_requirements(std::string& err)
{
// Ensure that all plugins are compatible with the loaded set of rules
// note: offline inspector contains all the loaded plugins
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
for (const auto &plugin : m_state->offline_inspector->get_plugin_manager()->plugins())
{
falco_engine::plugin_version_requirement req;
req.name = plugin->name();
req.version = plugin->plugin_version().as_string();
plugin_reqs.push_back(req);
}
return m_state->engine->check_plugin_requirements(plugin_reqs, err);
}
void application::check_for_ignored_events()
{
std::set<uint16_t> evttypes;
sinsp_evttables* einfo = m_state->inspector->get_event_info_tables();
const struct ppm_event_info* etable = einfo->m_event_info;
/* Get the events from the rules. */
std::set<uint16_t> rule_events;
std::string source = falco_common::syscall_source;
m_state->engine->evttypes_for_ruleset(source, evttypes);
m_state->engine->evttypes_for_ruleset(source, rule_events);
// Save event names so we don't warn for both the enter and exit event.
std::set<std::string> warn_event_names;
/* Get the events we consider interesting from the application state `ppm_sc` codes. */
std::unique_ptr<sinsp> inspector(new sinsp());
auto interesting_events = inspector->get_event_set_from_ppm_sc_set(m_state->ppm_sc_of_interest);
std::unordered_set<uint32_t> ignored_events;
for(auto evtnum : evttypes)
for(const auto& it : rule_events)
{
if(evtnum == PPME_GENERIC_E || evtnum == PPME_GENERIC_X)
/* If we have the old version of the event we will have also the recent one
* so we can avoid analyzing the presence of old events.
*/
if(sinsp::is_old_version_event(it))
{
continue;
}
if(!sinsp::simple_consumer_consider_evtnum(evtnum))
/* Here we are interested only in syscall events the internal events are not
* altered without the `-A` flag.
*
* TODO: We could consider also the tracepoint events here but right now we don't have
* the support from the libraries.
*/
if(!sinsp::is_syscall_event(it))
{
std::string name = etable[evtnum].name;
if(warn_event_names.find(name) == warn_event_names.end())
{
warn_event_names.insert(name);
}
continue;
}
/* If the event is not generated by the running system we don't print
* any warning right now.
*/
if(!sinsp::is_generable_event(it))
{
continue;
}
/* If the event is not in this set it is not considered by Falco. */
if(interesting_events.find(it) == interesting_events.end())
{
ignored_events.insert(it);
}
}
// Print a single warning with the list of ignored events
if (!warn_event_names.empty())
if(ignored_events.empty())
{
std::string skipped_events;
bool first = true;
for (const auto& evtname : warn_event_names)
{
if (first)
{
skipped_events += evtname;
first = false;
} else
{
skipped_events += "," + evtname;
}
}
fprintf(stderr,"Rules match ignored syscall: warning (ignored-evttype):\n loaded rules match the following events: %s;\n but these events are not returned unless running falco with -A\n", skipped_events.c_str());
return;
}
/* Get the names of the ignored events and print them. */
auto event_names = inspector->get_events_names(ignored_events);
std::cerr << std::endl << "Rules match ignored syscall: warning (ignored-evttype):" << std::endl;
std::cerr << "Loaded rules match the following events:" << std::endl;
for(const auto& it : event_names)
{
std::cerr << "\t- " << it.c_str() << std::endl;
}
std::cerr << "But these events are not returned unless running falco with -A" << std::endl << std::endl;
}
application::run_result application::load_rules_files()
@@ -93,34 +123,44 @@ application::run_result application::load_rules_files()
falco_configuration::read_rules_file_directory(path, m_state->config->m_loaded_rules_filenames, m_state->config->m_loaded_rules_folders);
}
for (const auto& filename : m_state->config->m_loaded_rules_filenames)
{
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
uint64_t required_engine_version;
std::vector<std::string> rules_contents;
falco::load_result::rules_contents_t rc;
try {
m_state->engine->load_rules_file(filename, m_options.verbose, m_options.all_events, required_engine_version);
}
catch(falco_exception &e)
{
return run_result::fatal(string("Could not load rules file ") + filename + ": " + e.what());
}
m_state->required_engine_versions[filename] = required_engine_version;
try {
read_files(m_state->config->m_loaded_rules_filenames.begin(),
m_state->config->m_loaded_rules_filenames.end(),
rules_contents,
rc);
}
catch(falco_exception& e)
{
return run_result::fatal(e.what());
}
// Ensure that all plugins are compatible with the loaded set of rules
std::string plugin_vers_err = "";
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
for (const auto &plugin : m_state->inspector->get_plugin_manager()->plugins())
{
falco_engine::plugin_version_requirement req;
req.name = plugin->name();
req.version = plugin->plugin_version().as_string();
plugin_reqs.push_back(req);
}
if (!m_state->engine->check_plugin_requirements(plugin_reqs, plugin_vers_err))
for(auto &filename : m_state->config->m_loaded_rules_filenames)
{
return run_result::fatal(plugin_vers_err);
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + "\n");
std::unique_ptr<falco::load_result> res;
res = m_state->engine->load_rules(rc.at(filename), filename);
if(!res->successful())
{
// Return the summary version as the error
return run_result::fatal(res->as_string(true, rc));
}
// If verbose is true, also print any warnings
if(m_options.verbose && res->has_warnings())
{
fprintf(stderr, "%s\n", res->as_string(true, rc).c_str());
}
}
std::string err = "";
if (!check_rules_plugin_requirements(err))
{
return run_result::fatal(err);
}
for (const auto& substring : m_options.disabled_rule_substrings)
@@ -150,11 +190,13 @@ application::run_result application::load_rules_files()
m_state->engine->enable_rule_by_tag(m_options.enabled_rule_tags, true);
}
if(!m_options.all_events)
/* Reading a scap file we have no concepts of ignored events we read all we need. */
if(!m_options.all_events && !is_capture_mode())
{
// For syscalls, see if any event types used by the
// loaded rules are ones with the EF_DROP_SIMPLE_CONS
// label.
/* Here we have already initialized the application state with the interesting syscalls,
* so we have to check if any event types used by the loaded rules are not considered by
* Falco interesting set.
*/
check_for_ignored_events();
}

View File

@@ -18,86 +18,115 @@ limitations under the License.
#include <sys/stat.h>
#include <fcntl.h>
#include <plugin_manager.h>
#include "application.h"
/* DEPRECATED: we will remove it in Falco 0.34. */
#define FALCO_BPF_ENV_VARIABLE "FALCO_BPF_PROBE"
using namespace falco::app;
typedef std::function<void(std::shared_ptr<sinsp> inspector)> open_t;
application::run_result application::open_inspector()
application::run_result application::open_offline_inspector()
{
// Notify engine that we finished loading and enabling all rules
m_state->engine->complete_rule_loading();
if(is_capture_mode())
try
{
// Try to open the trace file as a
// capture file first.
try {
m_state->inspector->open(m_options.trace_filename);
falco_logger::log(LOG_INFO, "Reading system call events from file: " + m_options.trace_filename + "\n");
}
catch(sinsp_exception &e)
{
return run_result::fatal("Could not open trace filename " + m_options.trace_filename + " for reading: " + e.what());
}
m_state->offline_inspector->open_savefile(m_options.trace_filename);
falco_logger::log(LOG_INFO, "Reading system call events from file: " + m_options.trace_filename + "\n");
return run_result::ok();
}
else
catch (sinsp_exception &e)
{
try
return run_result::fatal("Could not open trace filename " + m_options.trace_filename + " for reading: " + e.what());
}
}
application::run_result application::open_live_inspector(
std::shared_ptr<sinsp> inspector,
const std::string& source)
{
try
{
if (source != falco_common::syscall_source) /* Plugin engine */
{
if(m_options.userspace)
for (const auto& p: inspector->get_plugin_manager()->plugins())
{
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
//
// Falco uses a ptrace(2) based userspace implementation.
// Regardless of the implementation, the underlying method remains the same.
m_state->inspector->open_udig();
}
else if(m_options.gvisor_config != "")
{
falco_logger::log(LOG_INFO, "Enabled event collection from gVisor. Configuration path: " + m_options.gvisor_config);
m_state->inspector->open_gvisor(m_options.gvisor_config, m_options.gvisor_root);
}
else
{
m_state->inspector->open();
if (p->caps() & CAP_SOURCING && p->event_source() == source)
{
auto cfg = m_state->plugin_configs.at(p->name());
falco_logger::log(LOG_INFO, "Opening capture with plugin '" + cfg->m_name + "'\n");
inspector->open_plugin(cfg->m_name, cfg->m_open_params);
return run_result::ok();
}
}
return run_result::fatal("Can't open inspector for plugin event source: " + source);
}
catch(sinsp_exception &e)
else if (m_options.userspace) /* udig engine. */
{
// If syscall input source is enabled and not through userspace instrumentation
if (is_syscall_source_enabled() && !m_options.userspace)
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
//
// Falco uses a ptrace(2) based userspace implementation.
// Regardless of the implementation, the underlying method remains the same.
falco_logger::log(LOG_INFO, "Opening capture with udig\n");
inspector->open_udig();
}
else if(!m_options.gvisor_config.empty()) /* gvisor engine. */
{
falco_logger::log(LOG_INFO, "Opening capture with gVisor. Configuration path: " + m_options.gvisor_config);
inspector->open_gvisor(m_options.gvisor_config, m_options.gvisor_root);
}
else if(m_options.modern_bpf) /* modern BPF engine. */
{
falco_logger::log(LOG_INFO, "Opening capture with modern BPF probe");
inspector->open_modern_bpf(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
}
else if(getenv(FALCO_BPF_ENV_VARIABLE) != NULL) /* BPF engine. */
{
const char *bpf_probe_path = std::getenv(FALCO_BPF_ENV_VARIABLE);
char full_path[PATH_MAX];
/* If the path is empty try to load the probe from the default path. */
if(strncmp(bpf_probe_path, "", 1) == 0)
{
const char *home = std::getenv("HOME");
if(!home)
{
return run_result::fatal("Cannot get the env variable 'HOME'");
}
snprintf(full_path, PATH_MAX, "%s/%s", home, FALCO_PROBE_BPF_FILEPATH);
bpf_probe_path = full_path;
}
falco_logger::log(LOG_INFO, "Opening capture with BPF probe. BPF probe path: " + std::string(bpf_probe_path));
inspector->open_bpf(bpf_probe_path, m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
}
else /* Kernel module (default). */
{
try
{
falco_logger::log(LOG_INFO, "Opening capture with Kernel module");
inspector->open_kmod(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
}
catch(sinsp_exception &e)
{
// Try to insert the Falco kernel module
falco_logger::log(LOG_INFO, "Trying to inject the Kernel module and opening the capture again...");
if(system("modprobe " DRIVER_NAME " > /dev/null 2> /dev/null"))
{
falco_logger::log(LOG_ERR, "Unable to load the driver.\n");
falco_logger::log(LOG_ERR, "Unable to load the driver\n");
}
m_state->inspector->open();
}
else
{
return run_result::fatal(e.what());
inspector->open_kmod(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
}
}
}
catch (sinsp_exception &e)
{
return run_result::fatal(e.what());
}
// This must be done after the open
if(!m_options.all_events)
if (!m_options.all_events)
{
m_state->inspector->start_dropping_mode(1);
inspector->start_dropping_mode(1);
}
return run_result::ok();
}
bool application::close_inspector(std::string &errstr)
{
if(m_state->inspector != nullptr)
{
m_state->inspector->close();
}
return true;
}

View File

@@ -18,53 +18,50 @@ limitations under the License.
using namespace falco::app;
void application::print_all_ignored_events()
{
sinsp_evttables* einfo = m_state->inspector->get_event_info_tables();
const struct ppm_event_info* etable = einfo->m_event_info;
const struct ppm_syscall_desc* stable = einfo->m_syscall_info_table;
std::set<string> ignored_event_names;
for(uint32_t j = 0; j < PPM_EVENT_MAX; j++)
{
if(!sinsp::simple_consumer_consider_evtnum(j))
{
std::string name = etable[j].name;
// Ignore event names NA*
if(name.find("NA") != 0)
{
ignored_event_names.insert(name);
}
}
}
for(uint32_t j = 0; j < PPM_SC_MAX; j++)
{
if(!sinsp::simple_consumer_consider_syscallid(j))
{
std::string name = stable[j].name;
// Ignore event names NA*
if(name.find("NA") != 0)
{
ignored_event_names.insert(name);
}
}
}
printf("Ignored Event(s):");
for(auto it : ignored_event_names)
{
printf(" %s", it.c_str());
}
printf("\n");
}
/// TODO: probably in the next future would be more meaningful to print the ignored syscalls rather than
/// the ignored events, or maybe change the name of the events since right now they are almost the same of
/// the syscalls.
application::run_result application::print_ignored_events()
{
if(m_options.print_ignored_events)
/* If the option is true we print the events ignored with Falco `-A`, otherwise
* we return immediately.
*/
if(!m_options.print_ignored_events)
{
print_all_ignored_events();
return run_result::exit();
return run_result::ok();
}
return run_result::ok();
/* Fill the application syscall and tracepoint sets.
* The execution will be interrupted after this call so
* we don't care if we populate these sets even if the `-A` flag
* is not set.
*/
configure_interesting_sets();
/* Search for all the ignored syscalls. */
std::unique_ptr<sinsp> inspector(new sinsp());
std::unordered_set<uint32_t> all_ppm_sc = inspector->get_all_ppm_sc();
std::unordered_set<uint32_t> ignored_ppm_sc;
for(const auto& it : all_ppm_sc)
{
/* If the syscall is not in this set we ignore it. */
if(m_state->ppm_sc_of_interest.find(it) == m_state->ppm_sc_of_interest.end())
{
ignored_ppm_sc.insert(it);
}
}
/* Obtain the ignored events names from the ignored syscalls. */
auto ignored_events = inspector->get_event_set_from_ppm_sc_set(ignored_ppm_sc);
auto event_names = inspector->get_events_names(ignored_events);
std::cout << "Ignored Event(s):" << std::endl;
for(const auto& it : event_names)
{
std::cout << "- " << it.c_str() << std::endl;
}
std::cout << std::endl;
return run_result::exit();
}

View File

@@ -0,0 +1,37 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
using namespace falco::app;
application::run_result application::print_page_size()
{
if(m_options.print_page_size)
{
long page_size = getpagesize();
if(page_size <= 0)
{
return run_result::fatal("\nUnable to get the system page size through 'getpagesize()'\n");
}
else
{
falco_logger::log(LOG_INFO, "Your system page size is: " + std::to_string(page_size) + " bytes\n");
}
return run_result::exit();
}
return run_result::ok();
}

View File

@@ -47,13 +47,14 @@ application::run_result application::print_plugin_info()
#else // MUSL_OPTIMIZED
if(!m_options.print_plugin_info.empty())
{
std::unique_ptr<sinsp> inspector(new sinsp());
for(auto &pc : m_state->config->m_plugins)
{
if (pc.m_name == m_options.print_plugin_info
|| pc.m_library_path == m_options.print_plugin_info)
{
// load the plugin
auto p = m_state->inspector->register_plugin(pc.m_library_path);
auto p = inspector->register_plugin(pc.m_library_path);
// print plugin descriptive info
std::ostringstream os;

View File

@@ -21,7 +21,7 @@ limitations under the License.
using namespace falco::app;
static std::string read_file(std::string &filename)
static std::string read_file(const std::string &filename)
{
std::ifstream t(filename);
std::string str((std::istreambuf_iterator<char>(t)),
@@ -58,7 +58,6 @@ application::run_result application::print_support()
nlohmann::json finfo;
finfo["name"] = filename;
nlohmann::json variant;
variant["required_engine_version"] = m_state->required_engine_versions[filename];
variant["content"] = read_file(filename);
finfo["variants"].push_back(variant);
support["rules_files"].push_back(finfo);

View File

@@ -0,0 +1,34 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
#include <fields_info.h>
using namespace falco::app;
application::run_result application::print_syscall_events()
{
if(m_options.list_syscall_events)
{
// We know this function doesn't hold into the raw pointer value
std::unique_ptr<sinsp> inspector(new sinsp());
list_events(inspector.get(), m_options.markdown);
return run_result::exit();
}
return run_result::ok();
}

Some files were not shown because too many files have changed in this diff Show More