Compare commits

..

81 Commits

Author SHA1 Message Date
Mark Stemm
cdd4f51db0 Better fix for falco tests after rebase 2022-01-31 11:55:11 -08:00
Mark Stemm
1b112d752a Fixing falco tests after rebase 2022-01-31 11:49:20 -08:00
Mark Stemm
eb86768dfb Fixing falco bugs after rebase 2022-01-31 11:49:04 -08:00
Mark Stemm
b55df884ef falco_engine fix typos 2022-01-31 11:15:46 -08:00
Mark Stemm
debcb1e729 Update test output matches to match new aligned code
The prior falco changes made output printing more consistent by moving
it into a standlone function instead of scattered printf()s.

Some test cases relied on the (inconsistent) print strings, so update
them to the (hopefully more consistent) outputs used now.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-31 11:15:43 -08:00
Mark Stemm
9f88c7cbd0 Falco cleanups. this should pass all tests 2022-01-31 11:15:03 -08:00
Mark Stemm
946a431e55 swappable falco engine cleanups. this should pass all tests 2022-01-31 11:14:07 -08:00
Mark Stemm
7e37fc8210 Rules loading cleanups. This version should pass all tests 2022-01-31 11:14:07 -08:00
Mark Stemm
1ed2bec4d7 Remove temp debug logs (falco_engine) 2022-01-31 11:14:07 -08:00
Mark Stemm
cc4332c8ce More falco_engine cleanups. this should pass all tests 2022-01-31 11:14:04 -08:00
Mark Stemm
c648f2fcfd falco cleanups. this passes most tests 2022-01-31 11:07:33 -08:00
Mark Stemm
03d826d249 swappable falco engine cleanups. this passes mosts tests 2022-01-31 11:07:33 -08:00
Mark Stemm
83fe8d649a Rules loading cleanups. This passes mosts tests 2022-01-31 11:07:33 -08:00
Mark Stemm
4356307412 falco_engine cleanups. this passes most tests 2022-01-31 11:07:33 -08:00
Mark Stemm
d338185524 Move validation to after swengine init
A bigger reorg is probably in order, but this gets validation with -v
to work, at least
2022-01-31 11:07:32 -08:00
Mark Stemm
54dea70482 This version builds 2022-01-31 11:07:30 -08:00
Mark Stemm
08a67b77d6 This version builds 2022-01-31 11:06:29 -08:00
Mark Stemm
22e6205921 Add grpc methods to reload/validate rules files
Add grpc methods to reload/validate a set of rules files. This is only
stubs at the moment, but the implementation will consist of:

- creating a new falco engine
- doing any required initialization
- loading each rules file
- enabling/disabling rules based on command line options
- (for reload) using swappable_falco_engine::replace() to update the
  falco engine.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-31 11:06:29 -08:00
Mark Stemm
91ee079ea6 Use swappable_falco_engine to hold falco engine
Use an instance of swappable_falco_engine obj to hold the falco
engine.

This generally involves:

 - Passing around a reference to a swappable_falco_engine instead of a
   pointer to a falco_engine
 - Using swengine.engine() to access the current falco engine instead
   of the falco_engine pointer.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-31 11:06:25 -08:00
Mark Stemm
4cc05d6f4a Add a notion for a swappable falco engine
New class swappable_falco_engine contains a shared_ptr to a
falco_engine object and has two main methods:

 - engine(): retrieve the current shared pointer
 - replace(): update the engine with a new one

The implementation allows for replace() and engine() to occur on
different threads, using a tbb::concurrent_queue. replace() pushes
onto the queue(), and engine() pops from the queue, replacing the
current engine.

This will be used to replace the falco engine on the fly when
reloading rules on the fly via the grpc interface.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-31 11:03:33 -08:00
Mark Stemm
06b7427ede Move falco formats code out of engine
This isn't used by the engine itself anymore, now that it uses
factories to provide formatters.

This is in preparation for other changes to make the falco engine
hot-swappable while running.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-31 11:03:33 -08:00
Federico Di Pierro
f86423db76 fix(build): fixed build folder path for publish bin static.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-31 17:02:48 +01:00
Federico Di Pierro
5eed3a6638 fix(build): hotfix for release 0.31.0.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-31 17:02:48 +01:00
Leonardo Grasso
d585343483 docs(CHANGELOG.md): last update
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-31 14:01:48 +01:00
Teryl
9e57b5b4ba docs(changelog.md): update for release 0.31.0
Signed-off-by: Teryl <terylt@ibm.com>
2022-01-31 14:01:48 +01:00
Federico Di Pierro
47f38c8ae2 chore(build): dropped centos8 circleci build because it is useless and right now it is causing issues with yum.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-31 12:48:48 +01:00
Federico Di Pierro
332d828204 update(userspace/engine): properly value required_version because it is used by caller.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-28 15:33:22 +01:00
Federico Di Pierro
75c6cfb414 update(userpace/engine): properly implement semver check for required plugin versions.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-28 15:33:22 +01:00
Leonardo Grasso
a4199814a0 fix(tests/engine): correct unit tests
Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-28 15:33:22 +01:00
Leonardo Grasso
24e7e84153 update(rules): updated aws cloudtrail rule bumping plugins version
Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
Co-authored-by: Federico Di Pierro <nierro92@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-28 15:33:22 +01:00
Federico Di Pierro
70bfb2426c fix(userspace/engine): forcefully set PPME_PLUGINEVENT_E event type for "plugin" source events.
This workaround an issue in libs, targeting Falco 0.31.0.

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

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-28 15:33:22 +01:00
Federico Di Pierro
ce3598f801 update(plugins): updated json plugin to latest v0.2.2.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-27 17:22:09 +01:00
Federico Di Pierro
8e6ffc6fc9 fix(userspace/engine): actually make m_filter_all_event_types useful by properly using it as fallback when no filter event types is provided.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-27 17:22:09 +01:00
Luca Guerra
6a42f4a133 new(build): publish both static and glibc binaries
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-26 17:45:50 +01:00
Federico Di Pierro
8d9dd4440f chore(userspace/engine): cleanup unused alternate-lua-dir option and remove config_falco_engine.h.in, now unused since lua scripts are embedded in Falco.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-26 16:19:50 +01:00
Luca Guerra
c49093005d fix(build): do not include plugins in musl builds
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-26 16:18:50 +01:00
Luca Guerra
69767bb51b fix(build): do not show plugin options in musl optimized builds
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-26 16:18:50 +01:00
Andrea Terzolo
7750b6f209 rule: update Copyright in falco rules
Signed-off-by: Andrea Terzolo <s276109@studenti.polito.it>
2022-01-25 18:58:05 +01:00
Andrea Terzolo
8c705448cc rule: add execveat as evt.type for spawned_process macro in falco rules
Signed-off-by: Andrea Terzolo <s276109@studenti.polito.it>
2022-01-25 18:58:05 +01:00
Shay Berkovich
6b9fafb75f rule update(Sudo Potential Privilege Escalation): trigger the most common CVE-2021-3156 exploit
Signed-off-by: Shay Berkovich <sberkovich@blackberry.com>
Co-authored-by: Meera Balsara <mbalsara@blackberry.com>
2022-01-25 17:54:06 +01:00
Shay Berkovich
fdcd7bffd0 rule update(Detect crypto miners using the Stratum protocol): update protocols
Signed-off-by: Shay Berkovich <Sberkovich@blackberry.com>
Co-authored-by: Meera Balsara <mbalsara@blackberry.com>
2022-01-25 17:54:06 +01:00
Shay Berkovich
d989e9c2d5 new(rules): Create Hardlink Over Sensitive Files
New rule to prevent hardlink bypass and symlink rule set to WARNING for consistency
Signed-off-by: Shay Berkovich <sberkovich@blackberry.com>
Co-authored-by: Meera Balsara <mbalsara@blackberry.com>
2022-01-25 17:54:06 +01:00
Federico Di Pierro
996ccf555c rule: updated aws_cloudtrail_rules with correct copyright year and required plugin versions.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-25 17:50:06 +01:00
Federico Di Pierro
2f82a9baa1 Update userspace/falco/falco.cpp
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-24 17:52:31 +01:00
Federico Di Pierro
dfb743838e Update userspace/engine/rules.cpp
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-24 17:52:31 +01:00
Federico Di Pierro
c7609192c7 Update userspace/engine/lua/rule_loader.lua
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-24 17:52:31 +01:00
Federico Di Pierro
4d3fc354fa update(userspace/engine): updated no evt.type specified lua warning string.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-24 17:52:31 +01:00
Federico Di Pierro
43bdfce6e5 update(userspace/falco): divide each plugin infos when dumping list of plugin with a newline.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-24 17:52:31 +01:00
Federico Di Pierro
a3976463d5 update(userspace/engine): fixed lua CMakeLists deps, to let it be gracefully rebuilt when lua files are updated.
Moreover, added back warning about performance impact for rules without event types.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-24 17:52:31 +01:00
Federico Di Pierro
1a485c3447 update(userspace/engine,userspace/falco): improved some string warnings.
Always print warnings while loading rules.
Print a single line when warning for ignored events.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-24 17:52:31 +01:00
Leonardo Grasso
96529300f6 fix(script/falco-driver-loader): fix typo
Co-Authored-By: Thomas Spear <tspear@conquestcyber.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-24 17:49:34 +01:00
Leonardo Grasso
27922faa27 fix(scripts/falco-driver-loader): missing compression formats for .ko files
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-24 17:49:34 +01:00
Leonardo Grasso
8a1de131f4 update(scripts/falco-driver-loader): load the latest version first
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-24 17:49:34 +01:00
Federico Di Pierro
e1e8715a0f build: updated cloudtrail plugin to latest version.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-24 16:12:12 +01:00
Leonardo Grasso
9ae8d281f5 fix(test): falco_hostnetwork_images list is now in k8s_audit_rules.yaml
Co-Authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-24 15:03:12 +01:00
Leonardo Di Donato
c705623f9e update(rules): move falco_hostnetwork_images list to k8s audit rules
Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2022-01-24 15:03:12 +01:00
Leo Di Donato
3640871725 update(rules): remove falco_hostnetwork_images list (unused)
The `falco_hostnetwork_images` list is unused.

This PR removes it to avoid the warning.

```console
When reading rules content: 1 warnings:
list falco_hostnetwork_images not refered to by any rule/macro/list
```

Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2022-01-24 15:03:12 +01:00
Federico Di Pierro
6d507b054c update(build): update libs version for 0.31 release.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-20 14:43:49 +01:00
Federico Di Pierro
f19a1d81c6 update(build): updated plugins to latest versions adding platform name to artifact url.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-20 14:43:49 +01:00
Andrea Terzolo
18c7b6500d refactor: remove apt-config from debian_packages monitoring
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
Co-authored-by: karthikc911 <ckinnovative@gmail.com>
2022-01-20 11:07:47 +01:00
Andrea Terzolo
8239fa41f4 docs: fix priority level "info" to "informational"
Signed-off-by: Andrea Terzolo <andrea.terzolo@polito.it>
2022-01-18 18:49:18 +01:00
yoshi314
a9e7512936 fix setting the variable of User-Agent, it was missing the prefix. Switched to dedicated curl's method to do this
Signed-off-by: Marcin Kowalski <marcin.kowalski@assecobs.pl>
2022-01-18 09:49:34 +01:00
Marcin Kowalski
f67e8bdad7 fix indentation in outputs_http.cpp
add sample config entry for user-agent variable

Signed-off-by: Marcin Kowalski <marcin.kowalski@assecobs.pl>
2022-01-18 09:49:34 +01:00
Marcin Kowalski
a94e6de458 add useragent string to output
Signed-off-by: Marcin Kowalski <marcin.kowalski@assecobs.pl>
2022-01-18 09:49:34 +01:00
Leonardo Grasso
3e9f8c1ef1 chore(userpsace/engine): update fields checksum
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2022-01-17 18:15:43 +01:00
Mark Stemm
d20a326e09 Skip EPF_TABLE_ONLY fields with --list -N
When listing fields with -N (names only), also skip fields with the
EPF_TABLE_ONLY flag. (Skipping fields without -N is handled in libs,
in the as_string() method).

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-17 18:15:43 +01:00
Federico Di Pierro
0c290d98f8 fix(tests): avoid hardcoding plugin version 0.1.0 in plugin tests.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-17 17:20:33 +01:00
Federico Di Pierro
1befb053d0 update(gitignore): drop 2 useless lines from gitignore that are now installed in the build folder.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-17 17:20:33 +01:00
Federico Di Pierro
ae57718bda update(build): updated libs to latest master version. Updated plugins versions. Updated falco engine version.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-01-17 17:20:33 +01:00
Luca Guerra
55ce38cf3a use debian 11 slim as nodriver image
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-17 16:26:07 +01:00
Luca Guerra
18571eb20d ci: build stripped tgz
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-17 16:26:07 +01:00
Luca Guerra
9c449901f3 cmake: do not strip tar gz builds
Signed-off-by: Luca Guerra <luca@guerra.sh>
2022-01-17 16:26:07 +01:00
Jason Dellaluce
4ab8d6db98 refactor(configuration): remove plugin config loading from file feature
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-17 14:55:11 +01:00
Jason Dellaluce
5e354859a9 new(configuration): allow defining plugin config as YAML maps
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-17 14:55:11 +01:00
Jason Dellaluce
f4b79296fc fix: improve nested configuration field support
This fixes the parser introduced in https://github.com/falcosecurity/falco/pull/1792.
Now, nested fields such as `arr[1].subval` are supported, whereas the parser used
to recognize the `.` as an unexpected character.

Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-17 14:55:11 +01:00
Jason Dellaluce
6bf8f34d9f fix(engine): correctly format json output in json_event
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2022-01-14 13:29:33 +01:00
vadim.zyarko
f8f053c7fa Add an emty line to sattisfy the rules tests
Signed-off-by: vadim.zyarko <vadim.zyarko@sysdig.com>
2022-01-13 09:44:57 +01:00
VadimZy
b88a1cbb09 replace .. with table concat
Signed-off-by: vadim.zyarko <vadim.zyarko@sysdig.com>
2022-01-13 09:44:57 +01:00
Mark Stemm
c86615f68c Embed .lua files into falco executable
Instead of having .lua files external to the program responsible for
loading rules, embed the contents of those files into the executable
and load them as strings instead of as files:

Add a cmake custom command below userspace/engine/lua that calls a
bash script lua-to-cpp.sh to generate falco_engine_lua_files.{cpp,hh}
that are compiled into the falco engine library.

The script creates a .cpp file that has const char * symbols for each
file, as well as lists of files that should be loaded when the falco
engine is loaded. There are actually two lists:

- lua_module_strings: these are loaded and also added to the lua
  runtime package.preload table, so they are available when lua code
  require()s them.

- lua_code_strings: these are loaded *and* evaluated, so the functions
  in them are availble to be called from C++.

This simplifies some of the falco_common methods, as there's no need
to keep track of a "main" lua file to load or paths from which the lua
loader should find files for modules, and there's no need to keep
track of an "alternate" lua directory that occurs for debug builds.

Also, there's no need to include any .lua files in the installed
packages, as they're built into the falco binary.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-13 09:26:35 +01:00
Mark Stemm
08df1c63cf Clean up lyaml build a bit
change LYAML_SRC to LYAML_ROOT, which points to the top source
directory now.

LYAML_LIB and (new) LYAML_LUA_DIR are based relative to that
directory.

There's no install step at all now--the static library and the .lua
files are now used directly from the source tree.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-13 09:26:35 +01:00
Mark Stemm
10512b9ef9 Move compiler/parser lua files to a "modules" subdir
This will distinguish it from rule_loader.lua, which is *not* a module
but lua code with functions that can be called directly.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2022-01-13 09:26:35 +01:00
49 changed files with 1358 additions and 615 deletions

View File

@@ -176,38 +176,6 @@ jobs:
pushd build
make tests
popd
# Build using CentOS 8
# This build is static, dependencies are bundled in the Falco binary
"build/centos8":
docker:
- image: centos:8
steps:
- checkout
- run:
name: Update base image
command: dnf update -y
- run:
name: Install dependencies
command: dnf install gcc gcc-c++ git make cmake autoconf automake pkg-config patch libtool elfutils-libelf-devel diffutils kernel-devel kernel-headers kernel-core clang llvm which -y
- run:
name: Prepare project
command: |
mkdir build
pushd build
cmake -DBUILD_BPF=On -DUSE_BUNDLED_DEPS=On ..
popd
- run:
name: Build
command: |
pushd build
KERNELDIR=/lib/modules/$(ls /lib/modules)/build make -j4 all
popd
- run:
name: Run unit tests
command: |
pushd build
make tests
popd
# Build using our own builder base image using centos 7
# This build is static, dependencies are bundled in the Falco binary
"build/centos7":
@@ -391,9 +359,15 @@ jobs:
/source/falco/scripts/publish-rpm -f /build/release/falco-${FALCO_VERSION}-x86_64.rpm -r rpm-dev
- run:
name: Publish bin-dev
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-bin -f /build/release/falco-${FALCO_VERSION}-x86_64.tar.gz -r bin-dev -a x86_64
- run:
name: Publish bin-static-dev
command: |
FALCO_VERSION=$(cat /build-static/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
/source/falco/scripts/publish-bin -f /build-static/release/falco-${FALCO_VERSION}-x86_64.tar.gz -r bin-dev -a x86_64
cp -f /build-static/release/falco-${FALCO_VERSION}-x86_64.tar.gz /build-static/release/falco-${FALCO_VERSION}-static-x86_64.tar.gz
/source/falco/scripts/publish-bin -f /build-static/release/falco-${FALCO_VERSION}-static-x86_64.tar.gz -r bin-dev -a x86_64
"publish/packages-deb-dev":
docker:
- image: docker.io/debian:stable
@@ -505,9 +479,15 @@ jobs:
/source/falco/scripts/publish-rpm -f /build/release/falco-${FALCO_VERSION}-x86_64.rpm -r rpm
- run:
name: Publish bin
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-bin -f /build/release/falco-${FALCO_VERSION}-x86_64.tar.gz -r bin -a x86_64
- run:
name: Publish bin-static
command: |
FALCO_VERSION=$(cat /build-static/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
/source/falco/scripts/publish-bin -f /build-static/release/falco-${FALCO_VERSION}-x86_64.tar.gz -r bin -a x86_64
cp -f /build-static/release/falco-${FALCO_VERSION}-x86_64.tar.gz /build-static/release/falco-${FALCO_VERSION}-static-x86_64.tar.gz
/source/falco/scripts/publish-bin -f /build-static/release/falco-${FALCO_VERSION}-static-x86_64.tar.gz -r bin -a x86_64
"publish/packages-deb":
docker:
- image: docker.io/debian:stable
@@ -618,7 +598,6 @@ workflows:
- "build/ubuntu-focal"
- "build/ubuntu-focal-debug"
- "build/ubuntu-bionic"
- "build/centos8"
- "build/centos7"
- "build/centos7-debug"
- "tests/integration":

5
.gitignore vendored
View File

@@ -10,11 +10,8 @@ test/.phoronix-test-suite
test/results*.json.*
test/build
userspace/engine/lua/lyaml
userspace/engine/lua/lyaml.lua
.vscode/*
.luacheckcache
*.idea*
*.idea*

View File

@@ -1,5 +1,99 @@
# Change Log
## v0.31.0
Released on 2022-01-31
### Major Changes
* new: add support for plugins to extend Falco functionality to new event sources and custom fields [[#1753](https://github.com/falcosecurity/falco/pull/1753)] - [@mstemm](https://github.com/mstemm)
* new: add ability to set User-Agent http header when sending http output. Provide default value of 'falcosecurit/falco'. [[#1850](https://github.com/falcosecurity/falco/pull/1850)] - [@yoshi314](https://github.com/yoshi314)
* new(configuration): support defining plugin init config as a YAML [[#1852](https://github.com/falcosecurity/falco/pull/1852)] - [@jasondellaluce](https://github.com/jasondellaluce)
### Minor Changes
* rules: add the official Falco ECR repository to rules [[#1817](https://github.com/falcosecurity/falco/pull/1817)] - [@calvinbui](https://github.com/calvinbui)
* build: update CircleCI machine image for eBPF tests to a newer version of ubuntu [[#1764](https://github.com/falcosecurity/falco/pull/1764)] - [@mstemm](https://github.com/mstemm)
* update(engine): refactor Falco engine to be agnostic to specific event sources [[#1715](https://github.com/falcosecurity/falco/pull/1715)] - [@mstemm](https://github.com/mstemm)
* build: upgrade civetweb to v1.15 [[#1782](https://github.com/falcosecurity/falco/pull/1782)] - [@FedeDP](https://github.com/FedeDP)
* update: driver version is 319368f1ad778691164d33d59945e00c5752cd27 now [[#1861](https://github.com/falcosecurity/falco/pull/1861)] - [@FedeDP](https://github.com/FedeDP)
* build: allow using local libs source dir by setting `FALCOSECURITY_LIBS_SOURCE_DIR` in cmake [[#1791](https://github.com/falcosecurity/falco/pull/1791)] - [@jasondellaluce](https://github.com/jasondellaluce)
* build: the statically linked binary package is now published with the `-static` suffix [[#1873](https://github.com/falcosecurity/falco/pull/1873)] - [@LucaGuerra](https://github.com/LucaGuerra)
* update!: removed "--alternate-lua-dir" cmdline option as lua scripts are now embedded in Falco executable. [[#1872](https://github.com/falcosecurity/falco/pull/1872)] - [@FedeDP](https://github.com/FedeDP)
* build: switch to dynamic build for the binary package (`.tar.gz`) [[#1853](https://github.com/falcosecurity/falco/pull/1853)] - [@LucaGuerra](https://github.com/LucaGuerra)
* update: simpleconsumer filtering is now being done at kernel level [[#1846](https://github.com/falcosecurity/falco/pull/1846)] - [@FedeDP](https://github.com/FedeDP)
* update(scripts/falco-driver-loader): first try to load the latest kmod version, then fallback to an already installed if any [[#1863](https://github.com/falcosecurity/falco/pull/1863)] - [@leogr](https://github.com/leogr)
* refactor: clean up --list output with better formatting and no duplicate sections across event sources. [[#1816](https://github.com/falcosecurity/falco/pull/1816)] - [@mstemm](https://github.com/mstemm)
* update: embed .lua files used to load/compile rules into the main falco executable, for simplicity and to avoid tampering. [[#1843](https://github.com/falcosecurity/falco/pull/1843)] - [@mstemm](https://github.com/mstemm)
* update: support non-enumerable event sources in gRPC outputs service [[#1840](https://github.com/falcosecurity/falco/pull/1840)] - [@jasondellaluce](https://github.com/jasondellaluce)
* docs: add jasondellaluce to OWNERS [[#1818](https://github.com/falcosecurity/falco/pull/1818)] - [@jasondellaluce](https://github.com/jasondellaluce)
* chore: --list option can be used to selectively list fields related to new sources that are introduced by plugins [[#1839](https://github.com/falcosecurity/falco/pull/1839)] - [@loresuso](https://github.com/loresuso)
* update(userspace/falco): support arbitrary-depth nested values in YAML configuration [[#1792](https://github.com/falcosecurity/falco/pull/1792)] - [@jasondellaluce](https://github.com/jasondellaluce)
* build: bump FakeIt version to 2.0.9 [[#1797](https://github.com/falcosecurity/falco/pull/1797)] - [@jasondellaluce](https://github.com/jasondellaluce)
* update: allow append of new exceptions to rules [[#1780](https://github.com/falcosecurity/falco/pull/1780)] - [@sai-arigeli](https://github.com/sai-arigeli)
* update: Linux packages are now signed with SHA256 [[#1758](https://github.com/falcosecurity/falco/pull/1758)] - [@twa16](https://github.com/twa16)
### Bug Fixes
* fix(scripts/falco-driver-loader): fix for SELinux insmod denials [[#1756](https://github.com/falcosecurity/falco/pull/1756)] - [@dwindsor](https://github.com/dwindsor)
* fix(scripts/falco-driver-loader): correctly clean loaded drivers when using `--clean` [[#1795](https://github.com/falcosecurity/falco/pull/1795)] - [@jasondellaluce](https://github.com/jasondellaluce)
* fix(userspace/falco): in case output_file cannot be opened, throw a falco exception [[#1773](https://github.com/falcosecurity/falco/pull/1773)] - [@FedeDP](https://github.com/FedeDP)
* fix(userspace/engine): support jsonpointer escaping in rule parser [[#1777](https://github.com/falcosecurity/falco/pull/1777)] - [@jasondellaluce](https://github.com/jasondellaluce)
* fix(scripts/falco-driver-loader): support kernel object files in `.zst` and `.gz` compression formats [[#1863](https://github.com/falcosecurity/falco/pull/1863)] - [@leogr](https://github.com/leogr)
* fix(engine): correctly format json output in json_event [[#1847](https://github.com/falcosecurity/falco/pull/1847)] - [@jasondellaluce](https://github.com/jasondellaluce)
* fix: set http output contenttype to text/plain when json output is disabled [[#1829](https://github.com/falcosecurity/falco/pull/1829)] - [@FedeDP](https://github.com/FedeDP)
* fix(userspace/falco): accept 'Content-Type' header that contains "application/json", but it is not strictly equal to it [[#1800](https://github.com/falcosecurity/falco/pull/1800)] - [@FedeDP](https://github.com/FedeDP)
* fix(userspace/engine): supporting enabled-only overwritten rules [[#1775](https://github.com/falcosecurity/falco/pull/1775)] - [@jasondellaluce](https://github.com/jasondellaluce)
### Rule Changes
* rule(Create Symlink Over Sensitive File): corrected typo in rule output [[#1820](https://github.com/falcosecurity/falco/pull/1820)] - [@deepskyblue86](https://github.com/deepskyblue86)
* rule(macro open_write): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce)
* rule(macro open_read): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce)
* rule(macro open_directory): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce)
* rule(Create files below dev): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce)
* rule(Container Drift Detected (open+create)): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce)
* rule(macro sensitive_mount): add containerd socket [[#1815](https://github.com/falcosecurity/falco/pull/1815)] - [@loresuso](https://github.com/loresuso)
* rule(macro spawned_process): monitor also processes spawned by `execveat` [[#1868](https://github.com/falcosecurity/falco/pull/1868)] - [@Andreagit97](https://github.com/Andreagit97)
* rule(Create Hardlink Over Sensitive Files): new rule to detect hard links created over sensitive files [[#1810](https://github.com/falcosecurity/falco/pull/1810)] - [@sberkovich](https://github.com/sberkovich)
* rule(Detect crypto miners using the Stratum protocol): add `stratum2+tcp` and `stratum+ssl` protocols detection [[#1810](https://github.com/falcosecurity/falco/pull/1810)] - [@sberkovich](https://github.com/sberkovich)
* rule(Sudo Potential Privilege Escalation): correct special case for the CVE-2021-3156 exploit [[#1810](https://github.com/falcosecurity/falco/pull/1810)] - [@sberkovich](https://github.com/sberkovich)
* rule(list falco_hostnetwork_images): moved to k8s_audit_rules.yaml to avoid a warning when usng falco_rules.yaml only [[#1681](https://github.com/falcosecurity/falco/pull/1681)] - [@leodido](https://github.com/leodido)
* rule(list deb_binaries): remove `apt-config` [[#1860](https://github.com/falcosecurity/falco/pull/1860)] - [@Andreagit97](https://github.com/Andreagit97)
* rule(Launch Remote File Copy Tools in Container): add additional binaries: curl and wget. [[#1771](https://github.com/falcosecurity/falco/pull/1771)] - [@ec4n6](https://github.com/ec4n6)
* rule(list known_sa_list): add coredns, coredns-autoscaler, endpointslicemirroring-controller, horizontal-pod-autoscaler, job-controller, node-controller (nodelifecycle), persistent-volume-binder, pv-protection-controller, pvc-protection-controller, root-ca-cert-publisher and service-account-controller as allowed service accounts in the kube-system namespace [[#1760](https://github.com/falcosecurity/falco/pull/1760)] - [@sboschman](https://github.com/sboschman)
### Non user-facing changes
* fix: force-set evt.type for plugin source events [[#1878](https://github.com/falcosecurity/falco/pull/1878)] - [@FedeDP](https://github.com/FedeDP)
* fix: updated some warning strings; properly refresh lua files embedded in falco [[#1864](https://github.com/falcosecurity/falco/pull/1864)] - [@FedeDP](https://github.com/FedeDP)
* style(userspace/engine): avoid creating multiple versions of methods only to assume default ruleset. Use a default argument instead. [[#1754](https://github.com/falcosecurity/falco/pull/1754)] - [@FedeDP](https://github.com/FedeDP)
* add raft in the adopters list [[#1776](https://github.com/falcosecurity/falco/pull/1776)] - [@teshsharma](https://github.com/teshsharma)
* build: always populate partial version variables [[#1778](https://github.com/falcosecurity/falco/pull/1778)] - [@dnwe](https://github.com/dnwe)
* build: updated cloudtrail plugin to latest version [[#1865](https://github.com/falcosecurity/falco/pull/1865)] - [@FedeDP](https://github.com/FedeDP)
* replace ".." concatenation with table.concat [[#1834](https://github.com/falcosecurity/falco/pull/1834)] - [@VadimZy](https://github.com/VadimZy)
* fix(userspace/engine): actually make m_filter_all_event_types useful by properly using it as fallback when no filter event types is provided [[#1875](https://github.com/falcosecurity/falco/pull/1875)] - [@FedeDP](https://github.com/FedeDP)
* fix(build): do not show plugin options in musl optimized builds [[#1871](https://github.com/falcosecurity/falco/pull/1871)] - [@LucaGuerra](https://github.com/LucaGuerra)
* fix(aws_cloudtrail_rules.yaml): correct required plugin versions [[#1867](https://github.com/falcosecurity/falco/pull/1867)] - [@FedeDP](https://github.com/FedeDP)
* docs: fix priority level "info" to "informational" [[#1858](https://github.com/falcosecurity/falco/pull/1858)] - [@Andreagit97](https://github.com/Andreagit97)
* Field properties changes [[#1838](https://github.com/falcosecurity/falco/pull/1838)] - [@mstemm](https://github.com/mstemm)
* update(build): updated libs to latest master version; updated plugins versions [[#1856](https://github.com/falcosecurity/falco/pull/1856)] - [@FedeDP](https://github.com/FedeDP)
* Add Giant Swarm to Adopters list [[#1842](https://github.com/falcosecurity/falco/pull/1842)] - [@stone-z](https://github.com/stone-z)
* update(tests): remove `token_bucket` unit tests [[#1798](https://github.com/falcosecurity/falco/pull/1798)] - [@jasondellaluce](https://github.com/jasondellaluce)
* fix(build): use consistent 7-character build abbrev sha [[#1830](https://github.com/falcosecurity/falco/pull/1830)] - [@LucaGuerra](https://github.com/LucaGuerra)
* add Phoenix to adopters list [[#1806](https://github.com/falcosecurity/falco/pull/1806)] - [@kaldyka](https://github.com/kaldyka)
* remove unused files in test directory [[#1801](https://github.com/falcosecurity/falco/pull/1801)] - [@jasondellaluce](https://github.com/jasondellaluce)
* drop Falco luajit module, use the one provied by libs [[#1788](https://github.com/falcosecurity/falco/pull/1788)] - [@FedeDP](https://github.com/FedeDP)
* chore(build): update libs version to 7906f7e [[#1790](https://github.com/falcosecurity/falco/pull/1790)] - [@LucaGuerra](https://github.com/LucaGuerra)
* Add SysFlow to list of libs adopters [[#1747](https://github.com/falcosecurity/falco/pull/1747)] - [@araujof](https://github.com/araujof)
* build: dropped centos8 circleci build because it is useless [[#1882](https://github.com/falcosecurity/falco/pull/1882)] - [@FedeDP](https://github.com/FedeDP)
## v0.30.0
Released on 2021-10-01

View File

@@ -67,6 +67,7 @@ endif()
if(MUSL_OPTIMIZED_BUILD)
set(MUSL_FLAGS "-static -Os -fPIE -pie")
add_definitions(-DMUSL_OPTIMIZED)
endif()
# explicitly set hardening flags
@@ -206,7 +207,9 @@ add_subdirectory(userspace/engine)
add_subdirectory(userspace/falco)
add_subdirectory(tests)
include(plugins)
if(NOT MUSL_OPTIMIZED_BUILD)
include(plugins)
endif()
# Packages configuration
include(CPackConfig)

View File

@@ -10,5 +10,4 @@ endif()
if(CPACK_GENERATOR MATCHES "TGZ")
set(CPACK_SET_DESTDIR "ON")
set(CPACK_STRIP_FILES "OFF")
endif()

View File

@@ -24,8 +24,8 @@ else()
# default below In case you want to test against another falcosecurity/libs version just pass the variable - ie., `cmake
# -DFALCOSECURITY_LIBS_VERSION=dev ..`
if(NOT FALCOSECURITY_LIBS_VERSION)
set(FALCOSECURITY_LIBS_VERSION "bb9bee8e522fc953c2a79093d688d3d82b925e8b")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=ab2f18ff9c8d92dd06088ccfa73d4230fce3617613229f5afd839a37c13b0459")
set(FALCOSECURITY_LIBS_VERSION "319368f1ad778691164d33d59945e00c5752cd27")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=2cf44f06a282e8cee7aa1f775a08ea94c06e275faaf0636b21eb06af28cf4b3f")
endif()
# cd /path/to/build && cmake /path/to/source

View File

@@ -13,12 +13,12 @@
include(ExternalProject)
set(PLUGINS_VERSION "0.1.0-rc1-28-g019437e")
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} PLUGINS_SYSTEM_NAME)
ExternalProject_Add(
cloudtrail-plugin
URL "https://download.falco.org/plugins/dev/cloudtrail-${PLUGINS_VERSION}-${CMAKE_HOST_SYSTEM_PROCESSOR}.tar.gz"
URL_HASH "SHA256=ad9692957c5435238e07d1625e1b247eabe98b85f54de9218367fdd73a6f3f0b"
URL "https://download.falco.org/plugins/stable/cloudtrail-0.2.3-${PLUGINS_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}.tar.gz"
URL_HASH "SHA256=3dfce36f37a4f834b6078c6b78776414472a6ee775e8f262535313cc4031d0b7"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
@@ -27,8 +27,8 @@ install(FILES "${PROJECT_BINARY_DIR}/cloudtrail-plugin-prefix/src/cloudtrail-plu
ExternalProject_Add(
json-plugin
URL "https://download.falco.org/plugins/dev/json-${PLUGINS_VERSION}-${CMAKE_HOST_SYSTEM_PROCESSOR}.tar.gz"
URL_HASH "SHA256=721ea5226b0f623915d0d5c34870589ad33a8ff795b0daa1af72f21a67430077"
URL "https://download.falco.org/plugins/stable/json-0.2.2-${PLUGINS_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}.tar.gz"
URL_HASH "SHA256=83eb411c9f2125695875b229c6e7974e6a4cc7f028be146b79d26db30372af5e"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")

View File

@@ -20,7 +20,7 @@ RUN curl -L -o falco.tar.gz \
RUN sed -e 's/time_format_iso_8601: false/time_format_iso_8601: true/' < /falco/etc/falco/falco.yaml > /falco/etc/falco/falco.yaml.new \
&& mv /falco/etc/falco/falco.yaml.new /falco/etc/falco/falco.yaml
FROM scratch
FROM debian:11-slim
LABEL maintainer="cncf-falco-dev@lists.cncf.io"

View File

@@ -92,7 +92,7 @@ log_level: info
# Minimum rule priority level to load and run. All rules having a
# priority more severe than this level will be loaded/run. Can be one
# of "emergency", "alert", "critical", "error", "warning", "notice",
# "info", "debug".
# "informational", "debug".
priority: debug
# Whether or not output to any of the output channels below is
@@ -246,6 +246,7 @@ program_output:
http_output:
enabled: false
url: http://some.url
user_agent: "falcosecurity/falco"
# Falco supports running a gRPC server with two main binding types
# 1. Over the network with mandatory mutual TLS authentication (mTLS)

View File

@@ -1,5 +1,5 @@
#
# Copyright (C) 2019 The Falco Authors.
# Copyright (C) 2022 The Falco Authors.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,9 @@
# anything semver-compatible.
- required_plugin_versions:
- name: cloudtrail
version: 0.1.0
version: 0.2.3
- name: json
version: 0.2.2
# Note that this rule is disabled by default. It's useful only to
# verify that the cloudtrail plugin is sending events properly. The

View File

@@ -1,5 +1,5 @@
#
# Copyright (C) 2020 The Falco Authors.
# Copyright (C) 2022 The Falco Authors.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -63,11 +63,14 @@
condition: rename or remove
- macro: spawned_process
condition: evt.type = execve and evt.dir=<
condition: evt.type in (execve, execveat) and evt.dir=<
- macro: create_symlink
condition: evt.type in (symlink, symlinkat) and evt.dir=<
- macro: create_hardlink
condition: evt.type in (link, linkat) and evt.dir=<
- macro: chmod
condition: (evt.type in (chmod, fchmod, fchmodat) and evt.dir=<)
@@ -216,7 +219,7 @@
- list: deb_binaries
items: [dpkg, dpkg-preconfigu, dpkg-reconfigur, dpkg-divert, apt, apt-get, aptitude,
frontend, preinst, add-apt-reposit, apt-auto-remova, apt-key,
apt-listchanges, unattended-upgr, apt-add-reposit, apt-config, apt-cache, apt.systemd.dai
apt-listchanges, unattended-upgr, apt-add-reposit, apt-cache, apt.systemd.dai
]
# The truncated dpkg-preconfigu is intentional, process names are
@@ -1871,19 +1874,6 @@
container.image.repository in (falco_sensitive_mount_images) or
container.image.repository startswith quay.io/sysdig/)
# These container images are allowed to run with hostnetwork=true
- list: falco_hostnetwork_images
items: [
gcr.io/google-containers/prometheus-to-sd,
gcr.io/projectcalico-org/typha,
gcr.io/projectcalico-org/node,
gke.gcr.io/gke-metadata-server,
gke.gcr.io/kube-proxy,
gke.gcr.io/netd-amd64,
k8s.gcr.io/ip-masq-agent-amd64
k8s.gcr.io/prometheus-to-sd,
]
# Add conditions to this macro (probably in a separate file,
# overwriting this macro) to specify additional containers that are
# allowed to perform sensitive mounts.
@@ -2709,7 +2699,17 @@
(evt.arg.target in (sensitive_file_names) or evt.arg.target in (sensitive_directory_names))
output: >
Symlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline target=%evt.arg.target linkpath=%evt.arg.linkpath parent_process=%proc.pname)
priority: NOTICE
priority: WARNING
tags: [file, mitre_exfiltration]
- rule: Create Hardlink Over Sensitive Files
desc: Detect hardlink created over sensitive files
condition: >
create_hardlink and
(evt.arg.oldpath in (sensitive_file_names))
output: >
Hardlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline target=%evt.arg.oldpath linkpath=%evt.arg.newpath parent_process=%proc.pname)
priority: WARNING
tags: [file, mitre_exfiltration]
- list: miner_ports
@@ -2820,7 +2820,7 @@
- rule: Detect crypto miners using the Stratum protocol
desc: Miners typically specify the mining pool to connect to with a URI that begins with 'stratum+tcp'
condition: spawned_process and proc.cmdline contains "stratum+tcp"
condition: spawned_process and (proc.cmdline contains "stratum+tcp" or proc.cmdline contains "stratum2+tcp" or proc.cmdline contains "stratum+ssl" or proc.cmdline contains "stratum2+ssl")
output: Possible miner running (command=%proc.cmdline container=%container.info image=%container.image.repository)
priority: CRITICAL
tags: [process, mitre_execution]
@@ -3039,7 +3039,7 @@
# A privilege escalation to root through heap-based buffer overflow
- rule: Sudo Potential Privilege Escalation
desc: Privilege escalation vulnerability affecting sudo (<= 1.9.5p2). Executing sudo using sudoedit -s or sudoedit -i command with command-line argument that ends with a single backslash character from an unprivileged user it's possible to elevate the user privileges to root.
condition: spawned_process and user.uid != 0 and proc.name=sudoedit and (proc.args contains -s or proc.args contains -i) and (proc.args contains "\ " or proc.args endswith \)
condition: spawned_process and user.uid != 0 and (proc.name=sudoedit or proc.name = sudo) and (proc.args contains -s or proc.args contains -i or proc.args contains --login) and (proc.args contains "\ " or proc.args endswith \)
output: "Detect Sudo Privilege Escalation Exploit (CVE-2021-3156) (user=%user.name parent=%proc.pname cmdline=%proc.cmdline %container.info)"
priority: CRITICAL
tags: [filesystem, mitre_privilege_escalation]

View File

@@ -152,6 +152,19 @@
source: k8s_audit
tags: [k8s]
# These container images are allowed to run with hostnetwork=true
- list: falco_hostnetwork_images
items: [
gcr.io/google-containers/prometheus-to-sd,
gcr.io/projectcalico-org/typha,
gcr.io/projectcalico-org/node,
gke.gcr.io/gke-metadata-server,
gke.gcr.io/kube-proxy,
gke.gcr.io/netd-amd64,
k8s.gcr.io/ip-masq-agent-amd64
k8s.gcr.io/prometheus-to-sd,
]
# Corresponds to K8s CIS Benchmark 1.7.4
- rule: Create HostNetwork Pod
desc: Detect an attempt to start a pod using the host network.

View File

@@ -160,15 +160,26 @@ load_kernel_module_compile() {
echo "make CC=${CURRENT_GCC} \$@" >> /tmp/falco-dkms-make
chmod +x /tmp/falco-dkms-make
if dkms install --directive="MAKE='/tmp/falco-dkms-make'" -m "${DRIVER_NAME}" -v "${DRIVER_VERSION}" -k "${KERNEL_RELEASE}" 2>/dev/null; then
echo "* ${DRIVER_NAME} module installed in dkms, trying to insmod"
chcon -t modules_object_t "/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}.ko" > /dev/null 2>&1 || true
chcon -t modules_object_t "/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}.ko.xz" > /dev/null 2>&1 || true
if insmod "/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}.ko" > /dev/null 2>&1; then
echo "* ${DRIVER_NAME} module installed in dkms"
KO_FILE="/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}"
if [ -f "$KO_FILE.ko" ]; then
KO_FILE="$KO_FILE.ko"
elif [ -f "$KO_FILE.ko.gz" ]; then
KO_FILE="$KO_FILE.ko.gz"
elif [ -f "$KO_FILE.ko.xz" ]; then
KO_FILE="$KO_FILE.ko.xz"
elif [ -f "$KO_FILE.ko.zst" ]; then
KO_FILE="$KO_FILE.ko.zst"
else
>&2 echo "${DRIVER_NAME} module file not found"
return
fi
echo "* ${DRIVER_NAME} module found: ${KO_FILE}"
echo "* Trying insmod"
chcon -t modules_object_t "$KO_FILE" > /dev/null 2>&1 || true
if insmod "$KO_FILE" > /dev/null 2>&1; then
echo "* Success: ${DRIVER_NAME} module found and loaded in dkms"
exit 0
elif insmod "/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}.ko.xz" > /dev/null 2>&1; then
echo "* Success: ${DRIVER_NAME} module found and loaded in dkms (xz)"
exit 0
else
echo "* Unable to insmod ${DRIVER_NAME} module"
fi
@@ -196,8 +207,12 @@ load_kernel_module_download() {
if curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${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
insmod "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
exit $?
if insmod "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}"; then
echo "* Success: ${DRIVER_NAME} module found and inserted"
exit 0
else
>&2 echo "Unable to insmod the prebuilt ${DRIVER_NAME} module"
fi
else
>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} module"
return
@@ -241,11 +256,6 @@ load_kernel_module() {
exit 0
fi
echo "* Trying to load a system ${DRIVER_NAME} module, if present"
if modprobe "${DRIVER_NAME}" > /dev/null 2>&1; then
echo "* Success: ${DRIVER_NAME} module found and loaded with modprobe"
exit 0
fi
echo "* Looking for a ${DRIVER_NAME} module locally (kernel ${KERNEL_RELEASE})"
@@ -268,6 +278,13 @@ load_kernel_module() {
load_kernel_module_compile
fi
# Last try (might load a previous driver version)
echo "* Trying to load a system ${DRIVER_NAME} module, if present"
if modprobe "${DRIVER_NAME}" > /dev/null 2>&1; then
echo "* Success: ${DRIVER_NAME} module found and loaded with modprobe"
exit 0
fi
# Not able to download a prebuilt module nor to compile one on-the-fly
>&2 echo "Consider compiling your own ${DRIVER_NAME} driver and loading it or getting in touch with the Falco community"
exit 1

View File

@@ -49,6 +49,7 @@ trace_files: !mux
detect: False
rules_file:
- ../rules/falco_rules.yaml
- ../rules/k8s_audit_rules.yaml
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
- ./rules/k8s_audit/trust_nginx_container.yaml
trace_file: trace_files/k8s_audit/create_nginx_pod_privileged.json
@@ -74,6 +75,7 @@ trace_files: !mux
detect: False
rules_file:
- ../rules/falco_rules.yaml
- ../rules/k8s_audit_rules.yaml
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
- ./rules/k8s_audit/trust_nginx_container.yaml
trace_file: trace_files/k8s_audit/create_nginx_pod_hostnetwork.json

View File

@@ -408,7 +408,8 @@ trace_files: !mux
invalid_overwrite_macro:
exit_status: 1
stdout_contains: |+
.*invalid_base_macro.yaml: Ok
.*invalid_base_macro.yaml: 1 warnings:
macro some macro not refered to by any rule/macro
.*invalid_overwrite_macro.yaml: 1 errors:
Compilation error when compiling "foo": Undefined macro 'foo' used in filter.
---
@@ -424,7 +425,8 @@ trace_files: !mux
invalid_append_macro:
exit_status: 1
stdout_contains: |+
.*invalid_base_macro.yaml: Ok
.*invalid_base_macro.yaml: 1 warnings:
macro some macro not refered to by any rule/macro
.*invalid_append_macro.yaml: 1 errors:
Compilation error when compiling "evt.type=execve foo": 17: syntax error, unexpected 'foo', expecting 'or', 'and'
---
@@ -1181,7 +1183,7 @@ trace_files: !mux
skip_unknown_error:
exit_status: 1
stderr_contains: |+
Could not load rules file.*skip_unknown_error.yaml: 1 errors:
When loading rules: 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
@@ -1198,7 +1200,7 @@ trace_files: !mux
skip_unknown_unspec_error:
exit_status: 1
stderr_contains: |+
Could not load rules file .*skip_unknown_unspec.yaml: 1 errors:
When loading rules: 1 errors:
Rule Contains Unknown Event And Unspecified: error filter_check called with nonexistent field proc.nobody
---
- rule: Contains Unknown Event And Unspecified

View File

@@ -82,7 +82,9 @@ trace_files: !mux
incompat_plugin_rules_version:
exit_status: 1
stderr_contains: "Runtime error: Plugin cloudtrail version 0.1.0 not compatible with required plugin version 100000.0.0. Exiting."
stderr_contains: |+
Runtime error: When loading rules: 1 errors:
Plugin cloudtrail version .* not compatible with required plugin version 100000.0.0
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
rules_file:
- rules/plugins/cloudtrail_incompat_plugin_version.yaml

View File

@@ -42,8 +42,9 @@ TEST_CASE("Should enable/disable for exact match w/ default ruleset", "[rulesets
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable("one_rule", exact_match, enabled);
REQUIRE(r.num_rules_for_ruleset(default_ruleset) == 1);
@@ -57,8 +58,9 @@ TEST_CASE("Should enable/disable for exact match w/ specific ruleset", "[ruleset
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable("one_rule", exact_match, enabled, non_default_ruleset);
REQUIRE(r.num_rules_for_ruleset(non_default_ruleset) == 1);
@@ -76,8 +78,9 @@ TEST_CASE("Should not enable for exact match different rule name", "[rulesets]")
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable("some_other_rule", exact_match, enabled);
REQUIRE(r.num_rules_for_ruleset(default_ruleset) == 0);
@@ -88,8 +91,9 @@ TEST_CASE("Should enable/disable for exact match w/ substring and default rulese
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable("one_rule", substring_match, enabled);
REQUIRE(r.num_rules_for_ruleset(default_ruleset) == 1);
@@ -103,8 +107,9 @@ TEST_CASE("Should not enable for substring w/ exact_match", "[rulesets]")
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable("one_", exact_match, enabled);
REQUIRE(r.num_rules_for_ruleset(default_ruleset) == 0);
@@ -115,8 +120,9 @@ TEST_CASE("Should enable/disable for prefix match w/ default ruleset", "[ruleset
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable("one_", substring_match, enabled);
REQUIRE(r.num_rules_for_ruleset(default_ruleset) == 1);
@@ -130,8 +136,9 @@ TEST_CASE("Should enable/disable for suffix match w/ default ruleset", "[ruleset
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable("_rule", substring_match, enabled);
REQUIRE(r.num_rules_for_ruleset(default_ruleset) == 1);
@@ -145,8 +152,9 @@ TEST_CASE("Should enable/disable for substring match w/ default ruleset", "[rule
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable("ne_ru", substring_match, enabled);
REQUIRE(r.num_rules_for_ruleset(default_ruleset) == 1);
@@ -160,8 +168,9 @@ TEST_CASE("Should enable/disable for substring match w/ specific ruleset", "[rul
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable("ne_ru", substring_match, enabled, non_default_ruleset);
REQUIRE(r.num_rules_for_ruleset(non_default_ruleset) == 1);
@@ -179,9 +188,10 @@ TEST_CASE("Should enable/disable for tags w/ default ruleset", "[rulesets]")
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
std::set<std::string> want_tags = {"some_tag"};
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable_tags(want_tags, enabled);
REQUIRE(r.num_rules_for_ruleset(default_ruleset) == 1);
@@ -195,9 +205,10 @@ TEST_CASE("Should enable/disable for tags w/ specific ruleset", "[rulesets]")
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
std::set<std::string> want_tags = {"some_tag"};
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable_tags(want_tags, enabled, non_default_ruleset);
REQUIRE(r.num_rules_for_ruleset(non_default_ruleset) == 1);
@@ -215,9 +226,10 @@ TEST_CASE("Should not enable for different tags", "[rulesets]")
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
std::set<std::string> want_tags = {"some_different_tag"};
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable_tags(want_tags, enabled);
REQUIRE(r.num_rules_for_ruleset(non_default_ruleset) == 0);
@@ -228,9 +240,10 @@ TEST_CASE("Should enable/disable for overlapping tags", "[rulesets]")
falco_ruleset r;
std::shared_ptr<gen_event_filter> filter = create_filter();
string rule_name = "one_rule";
string source = "syscall";
std::set<std::string> want_tags = {"some_tag", "some_different_tag"};
r.add(rule_name, tags, filter);
r.add(source, rule_name, tags, filter);
r.enable_tags(want_tags, enabled);
REQUIRE(r.num_rules_for_ruleset(default_ruleset) == 1);
@@ -241,16 +254,17 @@ TEST_CASE("Should enable/disable for overlapping tags", "[rulesets]")
TEST_CASE("Should enable/disable for incremental adding tags", "[rulesets]")
{
string source = "syscall";
falco_ruleset r;
std::shared_ptr<gen_event_filter> rule1_filter = create_filter();
string rule1_name = "one_rule";
std::set<std::string> rule1_tags = {"rule1_tag"};
r.add(rule1_name, rule1_tags, rule1_filter);
r.add(source, rule1_name, rule1_tags, rule1_filter);
std::shared_ptr<gen_event_filter> rule2_filter = create_filter();
string rule2_name = "two_rule";
std::set<std::string> rule2_tags = {"rule2_tag"};
r.add(rule2_name, rule2_tags, rule2_filter);
r.add(source, rule2_name, rule2_tags, rule2_filter);
std::set<std::string> want_tags;

View File

@@ -18,8 +18,7 @@ set(FALCO_ENGINE_SOURCE_FILES
falco_engine.cpp
falco_utils.cpp
json_evt.cpp
ruleset.cpp
formats.cpp)
ruleset.cpp)
add_library(falco_engine STATIC ${FALCO_ENGINE_SOURCE_FILES})
add_dependencies(falco_engine njson lyaml lpeg string-view-lite)
@@ -56,6 +55,3 @@ else()
endif()
target_link_libraries(falco_engine "${FALCO_SINSP_LIBRARY}" "${LPEG_LIB}" "${LYAML_LIB}" "${LIBYAML_LIB}" luafiles)
configure_file(config_falco_engine.h.in config_falco_engine.h)

View File

@@ -1,20 +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.
*/
#pragma once
#define FALCO_ENGINE_LUA_DIR "${FALCO_ABSOLUTE_SHARE_DIR}/lua/"
#define FALCO_ENGINE_SOURCE_LUA_DIR "${PROJECT_SOURCE_DIR}/userspace/engine/lua/"

View File

@@ -16,7 +16,6 @@ limitations under the License.
#include <fstream>
#include "config_falco_engine.h"
#include "falco_common.h"
#include "banned.h" // This raises a compilation error when certain functions are used
#include "falco_engine_lua_files.hh"

View File

@@ -20,14 +20,10 @@ limitations under the License.
#include <fstream>
#include <sinsp.h>
#include <plugin.h>
#include "falco_engine.h"
#include "falco_utils.h"
#include "falco_engine_version.h"
#include "config_falco_engine.h"
#include "formats.h"
extern "C" {
#include "lpeg.h"
@@ -44,7 +40,7 @@ const std::string falco_engine::s_default_ruleset = "falco-default-ruleset";
using namespace std;
falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
falco_engine::falco_engine(bool seed_rng)
: m_next_ruleset_id(0),
m_min_priority(falco_common::PRIORITY_DEBUG),
m_sampling_ratio(1), m_sampling_multiplier(0),
@@ -56,8 +52,6 @@ falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
falco_common::init();
falco_rules::init(m_ls);
m_required_plugin_versions.clear();
if(seed_rng)
{
srandom((unsigned) getpid());
@@ -136,6 +130,12 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
{
for(auto &field : fld_class.fields)
{
// Skip fields with the EPF_TABLE_ONLY flag.
if(field.tags.find("EPF_TABLE_ONLY") != field.tags.end())
{
continue;
}
printf("%s\n", field.name.c_str());
}
}
@@ -143,6 +143,11 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
}
}
void falco_engine::set_plugin_infos(std::list<sinsp_plugin::info> &plugin_infos)
{
m_plugin_infos = plugin_infos;
}
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events)
{
uint64_t dummy;
@@ -152,18 +157,20 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version)
{
if(!m_rules)
rulesfile rf;
rf.content = rules_content;
load_result res(rf);
load_rules(rf, verbose, all_events, res);
if(!res.successful)
{
m_rules.reset(new falco_rules(this,
m_ls));
bool include_filenames = false;
for(auto const &it : m_filter_factories)
{
m_rules->add_filter_factory(it.first, it.second);
}
// This version assembles the errors and/or warnings into an exception.
throw falco_exception(res.as_string(include_filenames, verbose));
}
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, required_engine_version, m_required_plugin_versions);
}
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
@@ -191,6 +198,105 @@ void falco_engine::load_rules_file(const string &rules_filename, bool verbose, b
load_rules(rules_content, verbose, all_events, required_engine_version);
}
falco_engine::rulesfile::rulesfile()
{
}
falco_engine::load_result::load_result(const rulesfile &rulesfile)
: rf(rulesfile)
{
}
falco_engine::load_result::~load_result()
{
}
std::string falco_engine::load_result::as_string(bool include_filenames, bool include_warnings)
{
std::ostringstream os;
if(include_filenames)
{
os << rf.name << ": ";
}
if((errors.size() + warnings.size()) == 0)
{
os << "Ok" << std::endl;
} else {
if (errors.size() > 0)
{
os << errors.size() << " errors:" << std::endl;
for(auto err : errors)
{
os << err << std::endl;
}
}
if (include_warnings && warnings.size() > 0)
{
os << warnings.size() << " warnings:" << std::endl;
for(auto warn : warnings)
{
os << warn << std::endl;
}
}
}
return os.str();
}
bool falco_engine::rulesfile::load(const std::string &filename, std::string &errstr)
{
std::ifstream is;
is.open(filename);
if (!is.is_open())
{
errstr = "Could not open rules filename " +
filename + " " + "for reading";
return false;
}
name = filename;
content.assign((istreambuf_iterator<char>(is)),
istreambuf_iterator<char>());
return true;
}
falco_engine::rulesfile::~rulesfile()
{
}
void falco_engine::load_rules(falco_engine::rulesfile &rf,
bool verbose, bool all_events,
falco_engine::load_result &result)
{
if(!m_rules)
{
m_rules.reset(new falco_rules(this,
m_ls));
for(auto const &it : m_filter_factories)
{
m_rules->add_filter_factory(it.first, it.second);
}
}
uint64_t required_engine_version;
std::map<std::string, std::list<std::string>> required_plugin_versions;
result.successful = m_rules->load_rules(rf.content, verbose, all_events,
m_extra, m_replace_container_info,
m_min_priority,
result.warnings,
result.errors,
required_engine_version,
m_plugin_infos);
}
void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
@@ -415,7 +521,7 @@ void falco_engine::add_filter(std::shared_ptr<gen_event_filter> filter,
throw falco_exception(err);
}
it->second->add(rule, tags, filter);
it->second->add(source, rule, tags, filter);
}
bool falco_engine::is_source_valid(const std::string &source)
@@ -423,37 +529,6 @@ bool falco_engine::is_source_valid(const std::string &source)
return (m_rulesets.find(source) != m_rulesets.end());
}
bool falco_engine::is_plugin_compatible(const std::string &name,
const std::string &version,
std::string &required_version)
{
sinsp_plugin::version plugin_version(version.c_str());
if(!plugin_version.m_valid)
{
throw falco_exception(string("Plugin version string ") + version + " not valid");
}
if(m_required_plugin_versions.find(name) == m_required_plugin_versions.end())
{
// No required engine versions, so no restrictions. Compatible.
return true;
}
for(auto &rversion : m_required_plugin_versions[name])
{
sinsp_plugin::version req_version(rversion.c_str());
if(req_version.m_version_major > plugin_version.m_version_major)
{
required_version = rversion;
return false;
}
}
return true;
}
void falco_engine::clear_filters()
{
m_rulesets.clear();
@@ -463,8 +538,6 @@ void falco_engine::clear_filters()
std::shared_ptr<falco_ruleset> ruleset(new falco_ruleset());
m_rulesets[it.first] = ruleset;
}
m_required_plugin_versions.clear();
}
void falco_engine::set_sampling_ratio(uint32_t sampling_ratio)

View File

@@ -32,7 +32,6 @@ limitations under the License.
#include "rules.h"
#include "ruleset.h"
#include "config_falco_engine.h"
#include "falco_common.h"
//
@@ -44,7 +43,7 @@ limitations under the License.
class falco_engine : public falco_common
{
public:
falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR);
falco_engine(bool seed_rng=true);
virtual ~falco_engine();
// A given engine has a version which identifies the fields
@@ -57,6 +56,12 @@ public:
// If source is non-empty, only fields for the provided source are printed.
void list_fields(std::string &source, bool verbose, bool names_only);
// Optional--pass info on the currently loaded set of
// plugins. If provided, when loading rules content plugin
// info will be checked against any required_plugin_version
// blocks in the rules content.
void set_plugin_infos(std::list<sinsp_plugin::info> &plugin_infos);
//
// Load rules either directly or from a filename.
//
@@ -70,6 +75,44 @@ public:
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);
// Represents a block of rules. The name is only for
// logging/identification purposes, but generally is the path
// to the file that contained content.
class rulesfile {
public:
rulesfile();
virtual ~rulesfile();
bool load(const std::string &filename, std::string &errstr);
std::string name;
std::string content;
};
// Represents the result of loading a block of rules.
// - successful: true if the file was loaded, false otherwise
// - warnings: any warnings from loading the rules
// - errors: any errors from loading the rules
class load_result {
public:
load_result(const rulesfile &rulesfile);
~load_result();
std::string as_string(bool include_filenames, bool include_warnings);
const rulesfile &rf;
bool successful;
std::list<std::string> warnings;
std::list<std::string> errors;
};
// Improved variant that explicitly passes back a load_result
// struct containing lists of errors/warnings, etc. instead of
// throwing an exception on error.
void load_rules(rulesfile &rf,
bool verbose, bool all_events,
load_result &result);
//
// Enable/Disable any rules matching the provided substring.
// If the substring is "", all rules are enabled/disabled.
@@ -207,12 +250,6 @@ public:
std::shared_ptr<gen_event_formatter> create_formatter(const std::string &source,
const std::string &output);
// Return whether the provided plugin name + version is
// compatible with the current set of loaded rules files.
// required_version will be filled in with the required
// version when the method returns false.
bool is_plugin_compatible(const std::string &name, const std::string &version, std::string &required_version);
private:
//
@@ -222,6 +259,9 @@ private:
//
inline bool should_drop_evt();
// Optional--info on any loaded plugins.
std::list<sinsp_plugin::info> m_plugin_infos;
// Maps from event source to object that can generate filters from rules
std::map<std::string, std::shared_ptr<gen_event_filter_factory>> m_filter_factories;
@@ -236,10 +276,6 @@ private:
std::map<string, uint16_t> m_known_rulesets;
falco_common::priority_type m_min_priority;
// Maps from plugin to a list of required plugin versions
// found in any loaded rules files.
std::map<std::string, std::list<std::string>> m_required_plugin_versions;
void populate_rule_result(unique_ptr<struct rule_result> &res, gen_event *ev);
//

View File

@@ -22,4 +22,3 @@ limitations under the License.
// 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 "4de812495f8529ac20bda2b9774462b15911a51df293d59fe9ccb6b922fdeb9d"

View File

@@ -1529,12 +1529,9 @@ void json_event_formatter::set_format(output_format of, const std::string &forma
bool json_event_formatter::tostring_withformat(gen_event *gevt, std::string &output, gen_event_formatter::output_format of)
{
json_event *ev = static_cast<json_event *>(gevt);
std::string ret;
if(of == OF_JSON)
{
ret = tojson(ev);
output = tojson(ev);
return true;
}
else

View File

@@ -10,7 +10,7 @@
# "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.
file(GLOB_RECURSE lua_module_files ${CMAKE_CURRENT_SOURCE_DIR} *.lua)
file(GLOB_RECURSE lua_files ${CMAKE_CURRENT_SOURCE_DIR} *.lua)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/falco_engine_lua_files.cpp
COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/lua-to-cpp.sh ${CMAKE_CURRENT_SOURCE_DIR} ${LYAML_LUA_DIR} ${CMAKE_CURRENT_BINARY_DIR}

View File

@@ -293,19 +293,20 @@ function split_lines(rules_content)
end
function get_orig_yaml_obj(rules_lines, row)
local ret = ""
idx = row
local t = {}
while (idx <= #rules_lines) do
t[#t + 1] = rules_lines[idx]
idx = idx + 1
idx = row
while (idx <= #rules_lines) do
ret = ret..rules_lines[idx].."\n"
idx = idx + 1
if idx > #rules_lines or rules_lines[idx] == "" or string.sub(rules_lines[idx], 1, 1) == '-' then
break
end
end
return ret
if idx > #rules_lines or rules_lines[idx] == "" or string.sub(rules_lines[idx], 1, 1) == '-' then
break
end
end
t[#t + 1] = ""
local ret = ""
ret = table.concat(t, "\n")
return ret
end
function get_lines(rules_lines, row, num_lines)
@@ -754,65 +755,69 @@ end
-- Populates exfields with all fields used
function build_exception_condition_string_multi_fields(eitem, exfields)
local fields = eitem['fields']
local comps = eitem['comps']
local fields = eitem['fields']
local comps = eitem['comps']
local icond = "("
local icond = {}
for i, values in ipairs(eitem['values']) do
icond[#icond + 1] = "("
if #fields ~= #values then
return nil, "Exception item "..eitem['name']..": fields and values lists must have equal length"
end
local lcount = 0
for i, values in ipairs(eitem['values']) do
if #fields ~= #values then
return nil, "Exception item " .. eitem['name'] .. ": fields and values lists must have equal length"
end
if icond ~= "(" then
icond=icond.." or "
end
if lcount ~= 0 then
icond[#icond + 1] = " or "
end
lcount = lcount + 1
icond=icond.."("
icond[#icond + 1] = "("
for k=1,#fields do
if k > 1 then
icond=icond.." and "
end
local ival = values[k]
local istr = ""
for k = 1, #fields do
if k > 1 then
icond[#icond + 1] = " and "
end
local ival = values[k]
local istr = ""
-- If ival is a table, express it as (titem1, titem2, etc)
if type(ival) == "table" then
istr = "("
for _, item in ipairs(ival) do
if istr ~= "(" then
istr = istr..", "
end
istr = istr..quote_item(item)
end
istr = istr..")"
else
-- If the corresponding operator is one that works on lists, possibly add surrounding parentheses.
if defined_list_comp_operators[comps[k]] then
istr = paren_item(ival)
else
-- Quote the value if not already quoted
istr = quote_item(ival)
end
end
-- If ival is a table, express it as (titem1, titem2, etc)
if type(ival) == "table" then
istr = "("
for _, item in ipairs(ival) do
if istr ~= "(" then
istr = istr .. ", "
end
istr = istr .. quote_item(item)
end
istr = istr .. ")"
else
-- If the corresponding operator is one that works on lists, possibly add surrounding parentheses.
if defined_list_comp_operators[comps[k]] then
istr = paren_item(ival)
else
-- Quote the value if not already quoted
istr = quote_item(ival)
end
end
icond = icond..fields[k].." "..comps[k].." "..istr
exfields[fields[k]] = true
end
icond[#icond + 1] = fields[k] .. " " .. comps[k] .. " " .. istr
exfields[fields[k]] = true
end
icond=icond..")"
end
icond[#icond + 1] = ")"
end
icond = icond..")"
icond[#icond + 1] = ")"
-- Don't return a trivially empty condition string
if icond == "()" then
icond = ""
end
-- Don't return a trivially empty condition string
local ret = table.concat(icond)
if ret == "()" then
return "", nil
end
return icond, nil
return ret, nil
end
@@ -1054,9 +1059,9 @@ function load_rules(rules_content,
else
num_evttypes = falco_rules.add_filter(rules_mgr, lua_parser, v['rule'], v['source'], v['tags'])
if num_evttypes == 0 or num_evttypes > 100 then
if v['source'] == "syscall" and (num_evttypes == 0 or num_evttypes > 100) then
if warn_evttypes == true then
msg = "Rule "..v['rule']..": warning (no-evttype):"
msg = "Rule "..v['rule']..": warning (no-evttype):\n".." matches too many evt.type values.\n".." This has a significant performance penalty."
warnings[#warnings + 1] = msg
end
end

View File

@@ -145,11 +145,19 @@ int falco_rules::add_filter(lua_State *ls)
lua_pop(ls, 1);
}
size_t num_evttypes = lp->filter()->evttypes().size();
// todo(jasondellaluce,leogr,fededp): temp workaround, remove when fixed in libs
size_t num_evttypes = 1; // assume plugin
if(source == "syscall" || source == "k8s_audit")
{
num_evttypes = lp->filter()->evttypes().size();
}
try {
try
{
rules->add_filter(lp->filter(), rule, source, tags);
} catch (exception &e) {
}
catch (exception &e)
{
std::string errstr = string("Could not add rule to falco engine: ") + e.what();
lua_pushstring(ls, errstr.c_str());
lua_error(ls);
@@ -389,13 +397,17 @@ static void get_lua_table_list_values(lua_State *ls,
}
void falco_rules::load_rules(const string &rules_content,
bool falco_rules::load_rules(const string &rules_content,
bool verbose, bool all_events,
string &extra, bool replace_container_info,
falco_common::priority_type min_priority,
std::list<std::string> &warnings,
std::list<std::string> &errors,
uint64_t &required_engine_version,
std::map<std::string, std::list<std::string>> &required_plugin_versions)
std::list<sinsp_plugin::info> &plugin_infos)
{
std::map<std::string, std::list<std::string>> required_plugin_versions;
lua_getglobal(m_ls, m_lua_load_rules.c_str());
if(lua_isfunction(m_ls, -1))
{
@@ -424,46 +436,42 @@ void falco_rules::load_rules(const string &rules_content,
bool successful = lua_toboolean(m_ls, -5);
required_engine_version = lua_tonumber(m_ls, -4);
get_lua_table_list_values(m_ls, -3, required_plugin_versions);
std::list<std::string> errors = get_lua_table_values(m_ls, -2);
std::list<std::string> warnings = get_lua_table_values(m_ls, -1);
errors = get_lua_table_values(m_ls, -2);
warnings = get_lua_table_values(m_ls, -1);
// Concatenate errors/warnings
std::ostringstream os;
if (errors.size() > 0)
lua_pop(m_ls, 4);
// Also check plugin compatibility. Only meaningful if
// plugin_infos is non-empty.
for(auto &pinfo : plugin_infos)
{
os << errors.size() << " errors:" << std::endl;
for(auto err : errors)
if(required_plugin_versions.find(pinfo.name) == required_plugin_versions.end())
{
os << err << std::endl;
// No required engine versions, so no restrictions. Compatible.
continue;
}
}
if (warnings.size() > 0)
{
os << warnings.size() << " warnings:" << std::endl;
for(auto warn : warnings)
for(auto &rversion : required_plugin_versions[pinfo.name])
{
os << warn << std::endl;
sinsp_plugin::version req_version(rversion.c_str());
if(!pinfo.plugin_version.check(req_version))
{
errors.push_back(std::string("Plugin ") + pinfo.name + " version " + pinfo.plugin_version.as_string() + " not compatible with required plugin version " + rversion);
successful = false;
}
}
}
if(!successful)
{
throw falco_exception(os.str());
return false;
}
if (verbose && os.str() != "") {
// We don't really have a logging callback
// from the falco engine, but this would be a
// good place to use it.
fprintf(stderr, "When reading rules content: %s", os.str().c_str());
}
lua_pop(m_ls, 4);
} else {
throw falco_exception("No function " + m_lua_load_rules + " found in lua rule module");
}
return true;
}
void falco_rules::describe_rule(std::string *rule)

View File

@@ -39,11 +39,13 @@ class falco_rules
void add_filter_factory(const std::string &source,
std::shared_ptr<gen_event_filter_factory> factory);
void load_rules(const string &rules_content, bool verbose, bool all_events,
bool load_rules(const string &rules_content, bool verbose, bool all_events,
std::string &extra, bool replace_container_info,
falco_common::priority_type min_priority,
std::list<std::string> &warnings,
std::list<std::string> &errors,
uint64_t &required_engine_version,
std::map<std::string, std::list<std::string>> &required_plugin_versions);
std::list<sinsp_plugin::info> &plugin_infos);
void describe_rule(string *rule);
bool is_source_valid(const std::string &source);

View File

@@ -66,7 +66,7 @@ void falco_ruleset::ruleset_filters::remove_wrapper_from_list(filter_wrapper_lis
void falco_ruleset::ruleset_filters::add_filter(std::shared_ptr<filter_wrapper> wrap)
{
std::set<uint16_t> fevttypes = wrap->filter->evttypes();
std::set<uint16_t> fevttypes = wrap->evttypes();
if(fevttypes.empty())
{
@@ -91,7 +91,7 @@ void falco_ruleset::ruleset_filters::add_filter(std::shared_ptr<filter_wrapper>
void falco_ruleset::ruleset_filters::remove_filter(std::shared_ptr<filter_wrapper> wrap)
{
std::set<uint16_t> fevttypes = wrap->filter->evttypes();
std::set<uint16_t> fevttypes = wrap->evttypes();
if(fevttypes.empty())
{
@@ -118,18 +118,16 @@ uint64_t falco_ruleset::ruleset_filters::num_filters()
bool falco_ruleset::ruleset_filters::run(gen_event *evt)
{
if(evt->get_type() >= m_filter_by_event_type.size())
{
return false;
}
for(auto &wrap : m_filter_by_event_type[evt->get_type()])
{
if(wrap->filter->run(evt))
{
return true;
}
}
if(evt->get_type() < m_filter_by_event_type.size())
{
for(auto &wrap : m_filter_by_event_type[evt->get_type()])
{
if(wrap->filter->run(evt))
{
return true;
}
}
}
// Finally, try filters that are not specific to an event type.
for(auto &wrap : m_filter_all_event_types)
@@ -149,16 +147,18 @@ void falco_ruleset::ruleset_filters::evttypes_for_ruleset(std::set<uint16_t> &ev
for(auto &wrap : m_filters)
{
auto fevttypes = wrap->filter->evttypes();
auto fevttypes = wrap->evttypes();
evttypes.insert(fevttypes.begin(), fevttypes.end());
}
}
void falco_ruleset::add(string &name,
void falco_ruleset::add(string &source,
string &name,
set<string> &tags,
std::shared_ptr<gen_event_filter> filter)
{
std::shared_ptr<filter_wrapper> wrap(new filter_wrapper());
wrap->source = source;
wrap->name = name;
wrap->tags = tags;
wrap->filter = filter;

View File

@@ -34,7 +34,8 @@ public:
falco_ruleset();
virtual ~falco_ruleset();
void add(std::string &name,
void add(string &source,
std::string &name,
std::set<std::string> &tags,
std::shared_ptr<gen_event_filter> filter);
@@ -73,9 +74,21 @@ private:
class filter_wrapper {
public:
std::string source;
std::string name;
std::set<std::string> tags;
std::shared_ptr<gen_event_filter> filter;
std::set<uint16_t> evttypes()
{
// todo(jasondellaluce,leogr): temp workarond, remove when fixed in libs
if(source == "syscall" || source == "k8s_audit")
{
return filter->evttypes();
}
// else assume plugins
return {ppm_event_type::PPME_PLUGINEVENT_E};
// workaround end
}
};
typedef std::list<std::shared_ptr<filter_wrapper>> filter_wrapper_list;

View File

@@ -25,6 +25,8 @@ set(
event_drops.cpp
statsfilewriter.cpp
falco.cpp
formats.cpp
swappable_falco_engine.cpp
)
set(
@@ -76,6 +78,8 @@ if(NOT MINIMAL_BUILD)
${CMAKE_CURRENT_BINARY_DIR}/version.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/outputs.grpc.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/outputs.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/rules.grpc.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/rules.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/schema.pb.cc
)
@@ -150,6 +154,10 @@ if(NOT MINIMAL_BUILD)
${CMAKE_CURRENT_BINARY_DIR}/outputs.grpc.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/outputs.grpc.pb.h
${CMAKE_CURRENT_BINARY_DIR}/outputs.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/rules.grpc.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/rules.grpc.pb.h
${CMAKE_CURRENT_BINARY_DIR}/rules.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/rules.pb.h
${CMAKE_CURRENT_BINARY_DIR}/outputs.pb.h
${CMAKE_CURRENT_BINARY_DIR}/schema.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/schema.pb.h
@@ -166,6 +174,12 @@ if(NOT MINIMAL_BUILD)
COMMAND ${PROTOC} -I ${CMAKE_CURRENT_SOURCE_DIR} --grpc_out=. --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}
${CMAKE_CURRENT_SOURCE_DIR}/outputs.proto
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
# Falco gRPC Rules API
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/rules.proto
COMMAND ${PROTOC} -I ${CMAKE_CURRENT_SOURCE_DIR} --cpp_out=. ${CMAKE_CURRENT_SOURCE_DIR}/rules.proto
COMMAND ${PROTOC} -I ${CMAKE_CURRENT_SOURCE_DIR} --grpc_out=. --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}
${CMAKE_CURRENT_SOURCE_DIR}/rules.proto
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
endif()

View File

@@ -148,6 +148,10 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
}
http_output.options["url"] = url;
string user_agent;
user_agent = m_config->get_scalar<string>("http_output.user_agent","falcosecurity/falco");
http_output.options["user_agent"] = user_agent;
m_outputs.push_back(http_output);
}

View File

@@ -168,6 +168,10 @@ private:
int nodeIdx = std::stoi(key.substr(i + 1, close_param_idx - i - 1));
ret.reset(ret[nodeIdx]);
i = close_param_idx;
if (i < key.size() - 1 && key[i + 1] == '.')
{
i++;
}
}
}
}
@@ -276,49 +280,61 @@ private:
};
namespace YAML {
template<>
struct convert<nlohmann::json> {
static bool decode(const Node& node, nlohmann::json& res)
{
int int_val;
double double_val;
bool bool_val;
std::string str_val;
nlohmann::json sub{};
switch (node.Type()) {
case YAML::NodeType::Map:
for (auto &&it: node)
{
YAML::convert<nlohmann::json>::decode(it.second, sub);
res[it.first.as<std::string>()] = sub;
}
break;
case YAML::NodeType::Sequence:
for (auto &&it : node)
{
YAML::convert<nlohmann::json>::decode(it, sub);
res.emplace_back(sub);
}
break;
case YAML::NodeType::Scalar:
if (YAML::convert<int>::decode(node, int_val))
{
res = int_val;
}
else if (YAML::convert<double>::decode(node, double_val))
{
res = double_val;
}
else if (YAML::convert<bool>::decode(node, bool_val))
{
res = bool_val;
}
else if (YAML::convert<std::string>::decode(node, str_val))
{
res = str_val;
}
default:
break;
}
return true;
}
};
template<>
struct convert<falco_configuration::plugin_config> {
static bool read_file_from_key(const Node &node, const std::string &prefix, std::string &value)
{
std::string key = prefix;
if(node[key])
{
value = node[key].as<std::string>();
return true;
}
key += "_file";
if(node[key])
{
std::string path = node[key].as<std::string>();
// prepend share dir if path is not absolute
if(path.at(0) != '/')
{
path = string(FALCO_ENGINE_PLUGINS_DIR) + path;
}
// Intentionally letting potential
// exception be thrown, will get
// caught when reading config.
std::ifstream f(path);
std::string str((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
value = str;
return true;
}
return false;
}
// Note that the distinction between
// init_config/init_config_file and
// open_params/open_params_file is lost. But also,
// this class doesn't write yaml config anyway.
// Note that this loses the distinction between init configs
// defined as YAML maps or as opaque strings.
static Node encode(const falco_configuration::plugin_config & rhs) {
Node node;
node["name"] = rhs.m_name;
@@ -338,36 +354,44 @@ namespace YAML {
{
return false;
}
else
{
rhs.m_name = node["name"].as<std::string>();
}
rhs.m_name = node["name"].as<std::string>();
if(!node["library_path"])
{
return false;
}
rhs.m_library_path = node["library_path"].as<std::string>();
if(rhs.m_library_path.at(0) != '/')
{
// prepend share dir if path is not absolute
rhs.m_library_path = string(FALCO_ENGINE_PLUGINS_DIR) + rhs.m_library_path;
}
if(!node["init_config"])
{
return false;
}
// By convention, if the init config is a YAML map we convert it
// in a JSON object string. This is useful for plugins implementing
// the `get_init_schema` API symbol, which right now support the
// JSON Schema specific. If we ever support other schema/data types,
// we may want to bundle the conversion logic in an ad-hoc class.
// The benefit of this is being able of parsing/editing the config as
// a YAML map instead of having an opaque string.
if (node["init_config"].IsMap())
{
nlohmann::json json;
YAML::convert<nlohmann::json>::decode(node["init_config"], json);
rhs.m_init_config = json.dump();
}
else
{
rhs.m_library_path = node["library_path"].as<std::string>();
// prepend share dir if path is not absolute
if(rhs.m_library_path.at(0) != '/')
{
rhs.m_library_path = string(FALCO_ENGINE_PLUGINS_DIR) + rhs.m_library_path;
}
rhs.m_init_config = node["init_config"].as<std::string>();
}
if(!read_file_from_key(node, string("init_config"), rhs.m_init_config))
if(node["open_params"])
{
return false;
}
if(node["open_params"] &&
!read_file_from_key(node, string("open_params"), rhs.m_open_params))
{
return false;
rhs.m_open_params = node["open_params"].as<std::string>();
}
return true;

View File

@@ -46,6 +46,7 @@ limitations under the License.
#include "configuration.h"
#include "falco_engine.h"
#include "falco_engine_version.h"
#include "swappable_falco_engine.h"
#include "config_falco.h"
#include "statsfilewriter.h"
#ifndef MINIMAL_BUILD
@@ -94,7 +95,6 @@ static void usage()
" -h, --help Print this page\n"
" -c Configuration file (default " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ")\n"
" -A Monitor all events, including those with EF_DROP_SIMPLE_CONS flag.\n"
" --alternate-lua-dir <path> Specify an alternate path for loading Falco lua files\n"
" -b, --print-base64 Print data buffers in base64.\n"
" This is useful for encoding binary data that needs to be used over media designed to.\n"
" --cri <path> Path to CRI socket for container metadata.\n"
@@ -137,8 +137,12 @@ static void usage()
" -l <rule> Show the name and description of the rule with name <rule> and exit.\n"
" --list [<source>] List all defined fields. If <source> is provided, only list those fields for\n"
" the source <source>. Current values for <source> are \"syscall\", \"k8s_audit\"\n"
#ifndef MUSL_OPTIMIZED_BUILD
" --list-fields-markdown [<source>]\n"
" List fields in md\n"
#ifndef MUSL_OPTIMIZED
" --list-plugins Print info on all loaded plugins and exit.\n"
#endif
#ifndef MINIMAL_BUILD
" -m <url[,marathon_url]>, --mesos-api <url[,marathon_url]>\n"
" Enable Mesos support by connecting to the API server\n"
" specified as argument. E.g. \"http://admin:password@127.0.0.1:5050\".\n"
@@ -206,7 +210,7 @@ std::list<string> cmdline_options;
#ifndef MINIMAL_BUILD
// Read a jsonl file containing k8s audit events and pass each to the engine.
void read_k8s_audit_trace_file(falco_engine *engine,
void read_k8s_audit_trace_file(swappable_falco_engine &swengine,
falco_outputs *outputs,
string &trace_filename)
{
@@ -226,7 +230,7 @@ void read_k8s_audit_trace_file(falco_engine *engine,
continue;
}
if(!k8s_audit_handler::accept_data(engine, outputs, line, errstr))
if(!k8s_audit_handler::accept_data(swengine, outputs, line, errstr))
{
falco_logger::log(LOG_ERR, "Could not read k8s audit event line #" + to_string(line_num) + ", \"" + line + "\": " + errstr + ", stopping");
return;
@@ -247,7 +251,7 @@ static std::string read_file(std::string filename)
//
// Event processing loop
//
uint64_t do_inspect(falco_engine *engine,
uint64_t do_inspect(swappable_falco_engine &swengine,
falco_outputs *outputs,
sinsp* inspector,
std::string &event_source,
@@ -380,7 +384,7 @@ uint64_t do_inspect(falco_engine *engine,
// engine, which will match the event against the set
// of rules. If a match is found, pass the event to
// the outputs.
unique_ptr<falco_engine::rule_result> res = engine->process_event(event_source, ev);
unique_ptr<falco_engine::rule_result> res = swengine.engine()->process_event(event_source, ev);
if(res)
{
outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format, res->tags);
@@ -433,13 +437,13 @@ static void print_all_ignored_events(sinsp *inspector)
printf("\n");
}
static void check_for_ignored_events(sinsp &inspector, falco_engine &engine)
static void check_for_ignored_events(sinsp &inspector, swappable_falco_engine &swengine)
{
std::set<uint16_t> evttypes;
sinsp_evttables* einfo = inspector.get_event_info_tables();
const struct ppm_event_info* etable = einfo->m_event_info;
engine.evttypes_for_ruleset(syscall_source, evttypes);
swengine.engine()->evttypes_for_ruleset(syscall_source, evttypes);
// Save event names so we don't warn for both the enter and exit event.
std::set<std::string> warn_event_names;
@@ -456,21 +460,39 @@ static void check_for_ignored_events(sinsp &inspector, falco_engine &engine)
std::string name = etable[evtnum].name;
if(warn_event_names.find(name) == warn_event_names.end())
{
printf("Loaded rules use event %s, but this event is not returned unless running falco with -A\n", name.c_str());
warn_event_names.insert(name);
}
}
}
// Print a single warning with the list of ignored events
if (!warn_event_names.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());
}
}
static void list_source_fields(falco_engine *engine, bool verbose, bool names_only, std::string &source)
static void list_source_fields(swappable_falco_engine &swengine, bool verbose, bool names_only, std::string &source)
{
if(source.size() > 0 &&
!engine->is_source_valid(source))
!swengine.engine()->is_source_valid(source))
{
throw std::invalid_argument("Value for --list must be a valid source type");
}
engine->list_fields(source, verbose, names_only);
swengine.engine()->list_fields(source, verbose, names_only);
}
//
@@ -481,7 +503,9 @@ int falco_init(int argc, char **argv)
int result = EXIT_SUCCESS;
sinsp* inspector = NULL;
sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL;
falco_engine *engine = NULL;
swappable_falco_engine::config engine_config;
swappable_falco_engine swengine;
std::string errstr;
falco_outputs *outputs = NULL;
syscall_evt_drop_mgr sdropmgr;
int op;
@@ -498,7 +522,6 @@ int falco_init(int argc, char **argv)
list<string> validate_rules_filenames;
string stats_filename = "";
uint64_t stats_interval = 5000;
bool verbose = false;
bool names_only = false;
bool all_events = false;
#ifndef MINIMAL_BUILD
@@ -507,9 +530,7 @@ int falco_init(int argc, char **argv)
string *k8s_node_name = 0;
string* mesos_api = 0;
#endif
string output_format = "";
uint32_t snaplen = 0;
bool replace_container_info = false;
int duration_to_tot = 0;
bool print_ignored_events = false;
bool list_flds = false;
@@ -518,9 +539,6 @@ int falco_init(int argc, char **argv)
bool print_support = false;
string cri_socket_path;
bool cri_async = true;
set<string> disable_sources;
bool disable_syscall = false;
bool disable_k8s_audit = false;
bool userspace = false;
// Used for writing trace files
@@ -531,21 +549,19 @@ int falco_init(int argc, char **argv)
bool compress = false;
bool buffered_outputs = true;
bool buffered_cmdline = false;
std::map<string,uint64_t> required_engine_versions;
// Used for stats
double duration;
scap_stats cstats;
#ifndef MINIMAL_BUILD
falco_webserver webserver;
falco_webserver webserver(swengine);
falco::grpc::server grpc_server;
std::thread grpc_server_thread;
#endif
static struct option long_options[] =
{
{"alternate-lua-dir", required_argument, 0},
{"cri", required_argument, 0},
{"daemon", no_argument, 0, 'd'},
{"disable-cri-async", no_argument, 0, 0},
@@ -556,9 +572,7 @@ int falco_init(int argc, char **argv)
{"k8s-api", required_argument, 0, 'k'},
{"k8s-node", required_argument, 0},
{"list", optional_argument, 0},
#ifndef MUSL_OPTIMIZED_BUILD
{"list-plugins", no_argument, 0},
#endif
{"mesos-api", required_argument, 0, 'm'},
{"option", required_argument, 0, 'o'},
{"pidfile", required_argument, 0, 'P'},
@@ -576,12 +590,7 @@ int falco_init(int argc, char **argv)
try
{
set<string> disabled_rule_substrings;
string substring;
string all_rules = "";
string alternate_lua_dir = FALCO_ENGINE_SOURCE_LUA_DIR;
set<string> disabled_rule_tags;
set<string> enabled_rule_tags;
//
// Parse the args
@@ -609,7 +618,7 @@ int falco_init(int argc, char **argv)
break;
case 'D':
substring = optarg;
disabled_rule_substrings.insert(substring);
engine_config.disabled_rule_substrings.insert(substring);
break;
case 'e':
trace_filename = optarg;
@@ -662,23 +671,23 @@ int falco_init(int argc, char **argv)
case 'p':
if(string(optarg) == "c" || string(optarg) == "container")
{
output_format = "container=%container.name (id=%container.id)";
replace_container_info = true;
engine_config.output_format = "container=%container.name (id=%container.id)";
engine_config.replace_container_info = true;
}
else if(string(optarg) == "k" || string(optarg) == "kubernetes")
{
output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id";
replace_container_info = true;
engine_config.output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id";
engine_config.replace_container_info = true;
}
else if(string(optarg) == "m" || string(optarg) == "mesos")
{
output_format = "task=%mesos.task.name container=%container.id";
replace_container_info = true;
engine_config.output_format = "task=%mesos.task.name container=%container.id";
engine_config.replace_container_info = true;
}
else
{
output_format = optarg;
replace_container_info = false;
engine_config.output_format = optarg;
engine_config.replace_container_info = false;
}
break;
case 'r':
@@ -691,10 +700,10 @@ int falco_init(int argc, char **argv)
stats_filename = optarg;
break;
case 'T':
disabled_rule_tags.insert(optarg);
engine_config.disabled_rule_tags.insert(optarg);
break;
case 't':
enabled_rule_tags.insert(optarg);
engine_config.enabled_rule_tags.insert(optarg);
break;
case 'U':
buffered_outputs = false;
@@ -704,7 +713,7 @@ int falco_init(int argc, char **argv)
userspace = true;
break;
case 'v':
verbose = true;
engine_config.verbose = true;
break;
case 'V':
validate_rules_filenames.push_back(optarg);
@@ -751,7 +760,7 @@ int falco_init(int argc, char **argv)
list_flds_source = optarg;
}
}
#ifndef MUSL_OPTIMIZED_BUILD
#ifndef MUSL_OPTIMIZED
else if (string(long_options[long_index].name) == "list-plugins")
{
list_plugins = true;
@@ -769,17 +778,7 @@ int falco_init(int argc, char **argv)
{
if(optarg != NULL)
{
disable_sources.insert(optarg);
}
}
else if (string(long_options[long_index].name)== "alternate-lua-dir")
{
if(optarg != NULL)
{
alternate_lua_dir = optarg;
if (alternate_lua_dir.back() != '/') {
alternate_lua_dir += '/';
}
engine_config.event_sources.erase(std::string(optarg));
}
}
break;
@@ -790,6 +789,10 @@ int falco_init(int argc, char **argv)
}
/////////////////////////////////////////////////////////
// Create and configure inspector
/////////////////////////////////////////////////////////
inspector = new sinsp();
inspector->set_buffer_format(event_buffer_format);
@@ -817,39 +820,6 @@ int falco_init(int argc, char **argv)
return EXIT_SUCCESS;
}
engine = new falco_engine(true, alternate_lua_dir);
engine->set_extra(output_format, replace_container_info);
// Create "factories" that can create filters/formatters for
// syscalls and k8s audit events.
std::shared_ptr<gen_event_filter_factory> syscall_filter_factory(new sinsp_filter_factory(inspector));
std::shared_ptr<gen_event_filter_factory> k8s_audit_filter_factory(new json_event_filter_factory());
std::shared_ptr<gen_event_formatter_factory> syscall_formatter_factory(new sinsp_evt_formatter_factory(inspector));
std::shared_ptr<gen_event_formatter_factory> k8s_audit_formatter_factory(new json_event_formatter_factory(k8s_audit_filter_factory));
engine->add_source(syscall_source, syscall_filter_factory, syscall_formatter_factory);
engine->add_source(k8s_audit_source, k8s_audit_filter_factory, k8s_audit_formatter_factory);
if(disable_sources.size() > 0)
{
auto it = disable_sources.begin();
while(it != disable_sources.end())
{
if(*it != syscall_source && *it != k8s_audit_source)
{
it = disable_sources.erase(it);
continue;
}
++it;
}
disable_syscall = disable_sources.count(syscall_source) > 0;
disable_k8s_audit = disable_sources.count(k8s_audit_source) > 0;
if (disable_syscall && disable_k8s_audit) {
throw std::invalid_argument("The event source \"syscall\" and \"k8s_audit\" can not be disabled together");
}
}
// Some combinations of arguments are not allowed.
if (daemon && pidfilename == "") {
throw std::invalid_argument("If -d is provided, a pid file must also be provided");
@@ -885,31 +855,6 @@ int falco_init(int argc, char **argv)
}
}
if(validate_rules_filenames.size() > 0)
{
falco_logger::log(LOG_INFO, "Validating rules file(s):\n");
for(auto file : validate_rules_filenames)
{
falco_logger::log(LOG_INFO, " " + file + "\n");
}
for(auto file : validate_rules_filenames)
{
// Only include the prefix if there is more than one file
std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : "");
try {
engine->load_rules_file(file, verbose, all_events);
}
catch(falco_exception &e)
{
printf("%s%s", prefix.c_str(), e.what());
throw;
}
printf("%sOk\n", prefix.c_str());
}
falco_logger::log(LOG_INFO, "Ok\n");
goto exit;
}
falco_configuration config;
if (conf_filename.size())
{
@@ -929,25 +874,16 @@ int falco_init(int argc, char **argv)
// plugin was found, the source is the source of that
// plugin.
std::string event_source = syscall_source;
engine_config.json_output = config.m_json_output;
engine_config.min_priority = config.m_min_priority;
// All filterchecks created by plugins go in this
// list. If we ever support multiple event sources at
// the same time, this (and the below factories) will
// have to be a map from event source to filtercheck
// list.
filter_check_list plugin_filter_checks;
// Factories that can create filters/formatters for
// the (single) source supported by the (single) input plugin.
std::shared_ptr<gen_event_filter_factory> plugin_filter_factory(new sinsp_filter_factory(inspector, plugin_filter_checks));
std::shared_ptr<gen_event_formatter_factory> plugin_formatter_factory(new sinsp_evt_formatter_factory(inspector, plugin_filter_checks));
// Load and validate the configured plugins, if any.
std::shared_ptr<sinsp_plugin> input_plugin;
std::list<std::shared_ptr<sinsp_plugin>> extractor_plugins;
for(auto &p : config.m_plugins)
{
std::shared_ptr<sinsp_plugin> plugin;
#ifdef MUSL_OPTIMIZED_BUILD
#ifdef MUSL_OPTIMIZED
throw std::invalid_argument(string("Can not load/use plugins with musl optimized build"));
#else
falco_logger::log(LOG_INFO, "Loading plugin (" + p.m_name + ") from file " + p.m_library_path + "\n");
@@ -955,7 +891,7 @@ int falco_init(int argc, char **argv)
plugin = sinsp_plugin::register_plugin(inspector,
p.m_library_path,
(p.m_init_config.empty() ? NULL : (char *)p.m_init_config.c_str()),
plugin_filter_checks);
swengine.plugin_filter_checks());
#endif
if(plugin->type() == TYPE_SOURCE_PLUGIN)
@@ -976,7 +912,9 @@ int falco_init(int argc, char **argv)
inspector->set_input_plugin_open_params(p.m_open_params.c_str());
}
engine->add_source(event_source, plugin_filter_factory, plugin_formatter_factory);
// For now, if an input plugin is configured, the built-in sources are disabled.
engine_config.event_sources.clear();
engine_config.event_sources.insert(splugin->event_source());
} else {
extractor_plugins.push_back(plugin);
@@ -1012,20 +950,25 @@ int falco_init(int argc, char **argv)
}
}
if(config.m_json_output)
// If there are no event sources, due to a combination
// of --disable-source and lack of plugin config,
// raise an error.
if(engine_config.event_sources.empty())
{
syscall_formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
k8s_audit_formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
plugin_formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
throw std::invalid_argument("No event sources configured. Check use of --disable-source and plugin configuration");
}
std::list<sinsp_plugin::info> infos = sinsp_plugin::plugin_infos(inspector);
engine_config.plugin_infos = sinsp_plugin::plugin_infos(inspector);
if (!swengine.init(engine_config, inspector, errstr))
{
throw std::runtime_error("Could not initialize falco engine: " + errstr);
}
if(list_plugins)
{
std::ostringstream os;
for(auto &info : infos)
for(auto &info : engine_config.plugin_infos)
{
os << "Name: " << info.name << std::endl;
os << "Description: " << info.description << std::endl;
@@ -1041,15 +984,16 @@ int falco_init(int argc, char **argv)
{
os << "Type: extractor plugin" << std::endl;
}
os << std::endl;
}
printf("%lu Plugins Loaded:\n\n%s\n", infos.size(), os.str().c_str());
printf("%lu Plugins Loaded:\n\n%s\n", engine_config.plugin_infos.size(), os.str().c_str());
return EXIT_SUCCESS;
}
if(list_flds)
{
list_source_fields(engine, verbose, names_only, list_flds_source);
list_source_fields(swengine, engine_config.verbose, names_only, list_flds_source);
return EXIT_SUCCESS;
}
@@ -1058,8 +1002,6 @@ int falco_init(int argc, char **argv)
config.m_rules_filenames = rules_filenames;
}
engine->set_min_priority(config.m_min_priority);
if(buffered_cmdline)
{
config.m_buffered_outputs = buffered_outputs;
@@ -1070,6 +1012,48 @@ int falco_init(int argc, char **argv)
throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
}
if(validate_rules_filenames.size() > 0)
{
std::list<falco_engine::rulesfile> validate_rules;
falco_logger::log(LOG_INFO, "Validating rules file(s):\n");
for(auto file : validate_rules_filenames)
{
falco_logger::log(LOG_INFO, " " + file + "\n");
}
if (!swappable_falco_engine::open_files(validate_rules_filenames, validate_rules, errstr))
{
throw falco_exception(errstr);
}
std::string load_result;
bool ret = swengine.validate(validate_rules, load_result);
if(!ret)
{
// Print to stdout and also throw an error
printf("%s", load_result.c_str());
throw falco_exception(load_result);
}
else
{
// load_result might contain warnings. Print them
// if verbose is true.
if(engine_config.verbose && !load_result.empty())
{
printf("%s", load_result.c_str());
}
else
{
printf("Ok\n");
}
}
falco_logger::log(LOG_INFO, "Ok\n");
goto exit;
}
falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
for (auto filename : config.m_rules_filenames)
{
@@ -1079,70 +1063,35 @@ int falco_init(int argc, char **argv)
for (auto filename : config.m_rules_filenames)
{
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
uint64_t required_engine_version;
try {
engine->load_rules_file(filename, verbose, all_events, required_engine_version);
}
catch(falco_exception &e)
{
std::string prefix = "Could not load rules file " + filename + ": ";
throw falco_exception(prefix + e.what());
}
required_engine_versions[filename] = required_engine_version;
}
// Ensure that all plugins are compatible with the loaded set of rules
for(auto &info : infos)
std::list<falco_engine::rulesfile> rulesfiles;
if (!swappable_falco_engine::open_files(config.m_rules_filenames, rulesfiles, errstr))
{
std::string required_version;
throw falco_exception(errstr);
}
if(!engine->is_plugin_compatible(info.name, info.plugin_version.as_string(), required_version))
{
throw std::invalid_argument(std::string("Plugin ") + info.name + " version " + info.plugin_version.as_string() + " not compatible with required plugin version " + required_version);
}
std::string load_result;
bool ret = swengine.replace(rulesfiles, load_result);
if (!ret)
{
throw falco_exception(string("When loading rules: ") + load_result);
}
// load_result might contain warnings. Print them
// if verbose is true.
if(engine_config.verbose && !load_result.empty())
{
fprintf(stderr, "When loading rules: %s", load_result.c_str());
}
// You can't both disable and enable rules
if((disabled_rule_substrings.size() + disabled_rule_tags.size() > 0) &&
enabled_rule_tags.size() > 0) {
if((engine_config.disabled_rule_substrings.size() + engine_config.disabled_rule_tags.size() > 0) &&
engine_config.enabled_rule_tags.size() > 0) {
throw std::invalid_argument("You can not specify both disabled (-D/-T) and enabled (-t) rules");
}
for (auto substring : disabled_rule_substrings)
{
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
engine->enable_rule(substring, false);
}
if(disabled_rule_tags.size() > 0)
{
for(auto tag : disabled_rule_tags)
{
falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
}
engine->enable_rule_by_tag(disabled_rule_tags, false);
}
if(enabled_rule_tags.size() > 0)
{
// Since we only want to enable specific
// rules, first disable all rules.
engine->enable_rule(all_rules, false);
for(auto tag : enabled_rule_tags)
{
falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
}
engine->enable_rule_by_tag(enabled_rule_tags, true);
}
// For syscalls, see if any event types used by the
// loaded rules are ones with the EF_DROP_SIMPLE_CONS
// label.
check_for_ignored_events(*inspector, *engine);
if(print_support)
{
nlohmann::json support;
@@ -1173,13 +1122,14 @@ int falco_init(int argc, char **argv)
support["engine_info"]["engine_version"] = FALCO_ENGINE_VERSION;
support["config"] = read_file(conf_filename);
support["rules_files"] = nlohmann::json::array();
for(auto filename : config.m_rules_filenames)
for(auto &rf : rulesfiles)
{
nlohmann::json finfo;
finfo["name"] = filename;
finfo["name"] = rf.name;
nlohmann::json variant;
variant["required_engine_version"] = required_engine_versions[filename];
variant["content"] = read_file(filename);
// XXX/mstemm add this back
//variant["required_engine_version"] = rf.required_engine_version;
variant["content"] = rf.content;
finfo["variants"].push_back(variant);
support["rules_files"].push_back(finfo);
}
@@ -1206,6 +1156,13 @@ int falco_init(int argc, char **argv)
if(!all_events)
{
// For syscalls, see if any event types used by the
// loaded rules are ones with the EF_DROP_SIMPLE_CONS
// label.
if(engine_config.contains_event_source(syscall_source))
{
check_for_ignored_events(*inspector, swengine);
}
// Drop EF_DROP_SIMPLE_CONS kernel side
inspector->set_simple_consumer();
// Eventually, drop any EF_DROP_SIMPLE_CONS event
@@ -1216,13 +1173,13 @@ int falco_init(int argc, char **argv)
if (describe_all_rules)
{
engine->describe_rule(NULL);
swengine.engine()->describe_rule(NULL);
goto exit;
}
if (describe_rule != "")
{
engine->describe_rule(&describe_rule);
swengine.engine()->describe_rule(&describe_rule);
goto exit;
}
@@ -1315,7 +1272,7 @@ int falco_init(int argc, char **argv)
outputs = new falco_outputs();
outputs->init(engine,
outputs->init(swengine,
config.m_json_output,
config.m_json_include_output_property,
config.m_json_include_tags_property,
@@ -1400,13 +1357,14 @@ int falco_init(int argc, char **argv)
open_t open_f;
// Default mode: both event sources enabled
if (!disable_syscall && !disable_k8s_audit) {
if (engine_config.contains_event_source(syscall_source) &&
engine_config.contains_event_source(k8s_audit_source)) {
open_f = open_cb;
}
if (disable_syscall) {
if (!engine_config.contains_event_source(syscall_source)) {
open_f = open_nodriver_cb;
}
if (disable_k8s_audit) {
if (!engine_config.contains_event_source(k8s_audit_source)) {
open_f = open_cb;
}
@@ -1417,7 +1375,7 @@ int falco_init(int argc, char **argv)
catch(sinsp_exception &e)
{
// If syscall input source is enabled and not through userspace instrumentation
if (!disable_syscall && !userspace)
if (engine_config.contains_event_source(syscall_source) && !userspace)
{
// Try to insert the Falco kernel module
if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null"))
@@ -1460,7 +1418,7 @@ int falco_init(int argc, char **argv)
k8s_api_cert = new string(k8s_cert_env);
}
}
inspector->init_k8s_client(k8s_api, k8s_api_cert, k8s_node_name, verbose);
inspector->init_k8s_client(k8s_api, k8s_api_cert, k8s_node_name, engine_config.verbose);
k8s_api = 0;
k8s_api_cert = 0;
}
@@ -1476,7 +1434,7 @@ int falco_init(int argc, char **argv)
}
}
k8s_api = new string(k8s_api_env);
inspector->init_k8s_client(k8s_api, k8s_api_cert, k8s_node_name, verbose);
inspector->init_k8s_client(k8s_api, k8s_api_cert, k8s_node_name, engine_config.verbose);
}
else
{
@@ -1492,14 +1450,14 @@ int falco_init(int argc, char **argv)
//
if(mesos_api)
{
inspector->init_mesos_client(mesos_api, verbose);
inspector->init_mesos_client(mesos_api, engine_config.verbose);
}
else if(char* mesos_api_env = getenv("FALCO_MESOS_API"))
{
if(mesos_api_env != NULL)
{
mesos_api = new string(mesos_api_env);
inspector->init_mesos_client(mesos_api, verbose);
inspector->init_mesos_client(mesos_api, engine_config.verbose);
}
}
delete mesos_api;
@@ -1510,11 +1468,11 @@ int falco_init(int argc, char **argv)
falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + to_string(config.m_metadata_download_watch_freq_sec) + " seconds\n");
inspector->set_metadata_download_params(config.m_metadata_download_max_mb * 1024 * 1024, config.m_metadata_download_chunk_wait_us, config.m_metadata_download_watch_freq_sec);
if(trace_filename.empty() && config.m_webserver_enabled && !disable_k8s_audit)
if(trace_filename.empty() && config.m_webserver_enabled && engine_config.contains_event_source(k8s_audit_source))
{
std::string ssl_option = (config.m_webserver_ssl_enabled ? " (SSL)" : "");
falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(config.m_webserver_listen_port) + ssl_option + "\n");
webserver.init(&config, engine, outputs);
webserver.init(&config, outputs);
webserver.start();
}
@@ -1541,7 +1499,7 @@ int falco_init(int argc, char **argv)
if(!trace_filename.empty() && !trace_is_scap)
{
#ifndef MINIMAL_BUILD
read_k8s_audit_trace_file(engine,
read_k8s_audit_trace_file(swengine,
outputs,
trace_filename);
#endif
@@ -1550,7 +1508,7 @@ int falco_init(int argc, char **argv)
{
uint64_t num_evts;
num_evts = do_inspect(engine,
num_evts = do_inspect(swengine,
outputs,
inspector,
event_source,
@@ -1566,7 +1524,7 @@ int falco_init(int argc, char **argv)
inspector->get_capture_stats(&cstats);
if(verbose)
if(engine_config.verbose)
{
fprintf(stderr, "Driver Events:%" PRIu64 "\nDriver Drops:%" PRIu64 "\n",
cstats.n_evts,
@@ -1589,7 +1547,7 @@ int falco_init(int argc, char **argv)
}
inspector->close();
engine->print_stats();
swengine.engine()->print_stats();
sdropmgr.print_stats();
#ifndef MINIMAL_BUILD
webserver.stop();
@@ -1619,7 +1577,6 @@ int falco_init(int argc, char **argv)
exit:
delete inspector;
delete engine;
delete outputs;
return result;

View File

@@ -60,7 +60,7 @@ falco_outputs::~falco_outputs()
}
}
void falco_outputs::init(falco_engine *engine,
void falco_outputs::init(swappable_falco_engine &swengine,
bool json_output,
bool json_include_output_property,
bool json_include_tags_property,
@@ -74,7 +74,7 @@ void falco_outputs::init(falco_engine *engine,
throw falco_exception("falco_outputs already initialized");
}
m_formats.reset(new falco_formats(engine, json_include_output_property, json_include_tags_property));
m_formats.reset(new falco_formats(swengine, json_include_output_property, json_include_tags_property));
m_json_output = json_output;

View File

@@ -23,7 +23,7 @@ limitations under the License.
#include "json_evt.h"
#include "falco_common.h"
#include "token_bucket.h"
#include "falco_engine.h"
#include "swappable_falco_engine.h"
#include "outputs.h"
#include "formats.h"
#include "tbb/concurrent_queue.h"
@@ -39,7 +39,7 @@ public:
falco_outputs();
virtual ~falco_outputs();
void init(falco_engine *engine,
void init(swappable_falco_engine &swengine,
bool json_output,
bool json_include_output_property,
bool json_include_tags_property,

View File

@@ -17,13 +17,13 @@ limitations under the License.
#include <json/json.h>
#include "formats.h"
#include "falco_engine.h"
#include "swappable_falco_engine.h"
#include "banned.h" // This raises a compilation error when certain functions are used
falco_formats::falco_formats(falco_engine *engine,
falco_formats::falco_formats(swappable_falco_engine &swengine,
bool json_include_output_property,
bool json_include_tags_property)
: m_falco_engine(engine),
: m_swengine(swengine),
m_json_include_output_property(json_include_output_property),
m_json_include_tags_property(json_include_tags_property)
{
@@ -40,7 +40,7 @@ string falco_formats::format_event(gen_event *evt, const std::string &rule, cons
std::shared_ptr<gen_event_formatter> formatter;
formatter = m_falco_engine->create_formatter(source, format);
formatter = m_swengine.engine()->create_formatter(source, format);
// Format the original output string, regardless of output format
formatter->tostring_withformat(evt, line, gen_event_formatter::OF_NORMAL);
@@ -134,7 +134,7 @@ map<string, string> falco_formats::get_field_values(gen_event *evt, const std::s
{
std::shared_ptr<gen_event_formatter> formatter;
formatter = m_falco_engine->create_formatter(source, format);
formatter = m_swengine.engine()->create_formatter(source, format);
map<string, string> ret;

View File

@@ -28,12 +28,12 @@ extern "C"
#include <gen_filter.h>
#include "falco_engine.h"
#include "swappable_falco_engine.h"
class falco_formats
{
public:
falco_formats(falco_engine *engine,
falco_formats(swappable_falco_engine &swengine,
bool json_include_output_property,
bool json_include_tags_property);
virtual ~falco_formats();
@@ -45,7 +45,7 @@ public:
const std::string &format);
protected:
falco_engine *m_falco_engine;
swappable_falco_engine &m_swengine;
bool m_json_include_output_property;
bool m_json_include_tags_property;
};

View File

@@ -139,6 +139,78 @@ void request_context<version::service, version::request, version::response>::end
start(srv);
}
template<>
void request_context<rules::service, rules::rules_files, rules::reload_response>::start(server* srv)
{
m_state = request_context_base::REQUEST;
m_srv_ctx.reset(new ::grpc::ServerContext);
auto srvctx = m_srv_ctx.get();
m_res_writer.reset(new ::grpc::ServerAsyncResponseWriter<rules::reload_response>(srvctx));
m_req.Clear();
auto cq = srv->m_completion_queue.get();
// Request to start processing given requests.
// Using "this" - ie., the memory address of this context - as the tag that uniquely identifies the request.
// In this way, different contexts can serve different requests concurrently.
(srv->m_rules_svc.*m_request_func)(srvctx, &m_req, m_res_writer.get(), cq, cq, this);
}
template<>
void request_context<rules::service, rules::rules_files, rules::reload_response>::process(server* srv)
{
rules::reload_response res;
(srv->*m_process_func)(m_srv_ctx.get(), m_req, res);
// Notify the gRPC runtime that this processing is done
m_state = request_context_base::FINISH;
// Using "this"- ie., the memory address of this context - to uniquely identify the event.
m_res_writer->Finish(res, ::grpc::Status::OK, this);
}
template<>
void request_context<rules::service, rules::rules_files, rules::reload_response>::end(server* srv, bool error)
{
// todo(leodido) > handle processing errors here
// Ask to start processing requests
start(srv);
}
template<>
void request_context<rules::service, rules::rules_files, rules::validate_response>::start(server* srv)
{
m_state = request_context_base::REQUEST;
m_srv_ctx.reset(new ::grpc::ServerContext);
auto srvctx = m_srv_ctx.get();
m_res_writer.reset(new ::grpc::ServerAsyncResponseWriter<rules::validate_response>(srvctx));
m_req.Clear();
auto cq = srv->m_completion_queue.get();
// Request to start processing given requests.
// Using "this" - ie., the memory address of this context - as the tag that uniquely identifies the request.
// In this way, different contexts can serve different requests concurrently.
(srv->m_rules_svc.*m_request_func)(srvctx, &m_req, m_res_writer.get(), cq, cq, this);
}
template<>
void request_context<rules::service, rules::rules_files, rules::validate_response>::process(server* srv)
{
rules::validate_response res;
(srv->*m_process_func)(m_srv_ctx.get(), m_req, res);
// Notify the gRPC runtime that this processing is done
m_state = request_context_base::FINISH;
// Using "this"- ie., the memory address of this context - to uniquely identify the event.
m_res_writer->Finish(res, ::grpc::Status::OK, this);
}
template<>
void request_context<rules::service, rules::rules_files, rules::validate_response>::end(server* srv, bool error)
{
// todo(leodido) > handle processing errors here
// Ask to start processing requests
start(srv);
}
template<>
void request_bidi_context<outputs::service, outputs::request, outputs::response>::start(server* srv)
{

View File

@@ -194,6 +194,7 @@ void falco::grpc::server::run()
{
m_server_builder.RegisterService(&m_output_svc);
m_server_builder.RegisterService(&m_version_svc);
m_server_builder.RegisterService(&m_rules_svc);
m_completion_queue = m_server_builder.AddCompletionQueue();
m_server = m_server_builder.BuildAndStart();
@@ -213,6 +214,8 @@ void falco::grpc::server::run()
REGISTER_UNARY(version::request, version::response, version::service, version, version, context_num)
REGISTER_STREAM(outputs::request, outputs::response, outputs::service, get, get, context_num)
REGISTER_BIDI(outputs::request, outputs::response, outputs::service, sub, sub, context_num)
REGISTER_UNARY(rules::rules_files, rules::reload_response, rules::service, reload_rules, reload_rules, context_num)
REGISTER_UNARY(rules::rules_files, rules::validate_response, rules::service, validate_rules, validate_rules, context_num)
m_threads.resize(m_threadiness);
int thread_idx = 0;

View File

@@ -46,6 +46,7 @@ public:
outputs::service::AsyncService m_output_svc;
version::service::AsyncService m_version_svc;
rules::service::AsyncService m_rules_svc;
std::unique_ptr<::grpc::ServerCompletionQueue> m_completion_queue;

View File

@@ -77,13 +77,21 @@ void falco::grpc::server_impl::version(const context& ctx, const version::reques
version = FALCO_VERSION;
res.set_engine_version(FALCO_ENGINE_VERSION);
res.set_engine_fields_checksum(FALCO_FIELDS_CHECKSUM);
res.set_engine_fields_checksum(FALCO_FIELDS_CHECKSUM);
res.set_major(FALCO_VERSION_MAJOR);
res.set_minor(FALCO_VERSION_MINOR);
res.set_patch(FALCO_VERSION_PATCH);
}
void falco::grpc::server_impl::reload_rules(const context& ctx, const rules::rules_files& rules_files, rules::reload_response& res)
{
}
void falco::grpc::server_impl::validate_rules(const context& ctx, const rules::rules_files& rules_files, rules::validate_response& res)
{
}
void falco::grpc::server_impl::shutdown()
{
m_stop = true;

View File

@@ -19,6 +19,7 @@ limitations under the License.
#include <atomic>
#include "outputs.grpc.pb.h"
#include "version.grpc.pb.h"
#include "rules.grpc.pb.h"
#include "grpc_context.h"
namespace falco
@@ -43,6 +44,10 @@ protected:
// Version
void version(const context& ctx, const version::request& req, version::response& res);
// Rules
void reload_rules(const context& ctx, const rules::rules_files& rules_files, rules::reload_response& res);
void validate_rules(const context& ctx, const rules::rules_files& rules_files, rules::validate_response& res);
private:
std::atomic<bool> m_stop{false};
};

View File

@@ -34,11 +34,14 @@ void falco::outputs::output_http::output(const message *msg)
} else {
slist1 = curl_slist_append(slist1, "Content-Type: text/plain");
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);
curl_easy_setopt(curl, CURLOPT_URL, m_oc.options["url"].c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg->msg.c_str());
curl_easy_setopt(curl, CURLOPT_USERAGENT, m_oc.options["user_agent"].c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
@@ -50,4 +53,4 @@ void falco::outputs::output_http::output(const message *msg)
curl_slist_free_all(slist1);
slist1 = NULL;
}
}
}

View File

@@ -0,0 +1,51 @@
/*
Copyright (C) 2020 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.
*/
syntax = "proto3";
package falco.rules;
option go_package = "github.com/falcosecurity/client-go/pkg/api/rules";
// Functions to reload a set of rules files (replacing any currently
// loaded rules), or validate a set of candidate rules files.
service service {
rpc reload_rules(rules_files) returns (reload_response);
rpc validate_rules(rules_files) returns (validate_response);
}
message rules_file
{
string filename = 1;
string content = 2;
}
message rules_files
{
repeated rules_file files = 1;
}
message reload_response
{
bool successful = 1;
string error = 2;
}
message validate_response
{
bool successful = 1;
string error = 2;
}

View File

@@ -0,0 +1,256 @@
/*
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 <fstream>
#include "logger.h"
#include "swappable_falco_engine.h"
std::string swappable_falco_engine::syscall_source = "syscall";
std::string swappable_falco_engine::k8s_audit_source = "k8s_audit";
swappable_falco_engine::config::config()
: json_output(false), verbose(false), replace_container_info(false),
event_sources{syscall_source, k8s_audit_source}
{
}
swappable_falco_engine::config::~config()
{
}
bool swappable_falco_engine::config::contains_event_source(const std::string &source)
{
return (event_sources.find(source) != event_sources.end());
}
bool swappable_falco_engine::open_files(std::list<std::string> &filenames,
std::list<falco_engine::rulesfile> &rulesfiles,
std::string &errstr)
{
rulesfiles.clear();
for(const auto &filename : filenames)
{
std::string errstr;
rulesfiles.emplace_back();
falco_engine::rulesfile &rf = rulesfiles.back();
if (!rf.load(filename, errstr))
{
return false;
}
}
errstr = "";
return true;
}
swappable_falco_engine::swappable_falco_engine()
{
}
swappable_falco_engine::~swappable_falco_engine()
{
}
bool swappable_falco_engine::init(swappable_falco_engine::config &cfg, sinsp *inspector, std::string &errstr)
{
m_config = cfg;
m_inspector = inspector;
// Initialize some engine with no rules
std::list<falco_engine::rulesfile> empty;
return replace(empty, errstr);
}
std::shared_ptr<falco_engine> swappable_falco_engine::engine()
{
std::shared_ptr<falco_engine> new_engine;
while(m_pending_falco_engine.try_pop(new_engine))
{
m_engine=new_engine;
}
if(m_engine == NULL)
{
throw falco_exception("No engine, must call replace() first");
}
return m_engine;
}
filter_check_list &swappable_falco_engine::plugin_filter_checks()
{
return m_plugin_filter_checks;
}
bool swappable_falco_engine::replace(std::list<falco_engine::rulesfile> &rulesfiles,
std::string &load_result)
{
std::shared_ptr<falco_engine> new_engine;
new_engine = create_new(rulesfiles, load_result);
if (new_engine == NULL)
{
return false;
}
m_pending_falco_engine.push(new_engine);
return true;
}
bool swappable_falco_engine::validate(std::list<falco_engine::rulesfile> &rulesfiles,
std::string &load_result)
{
std::shared_ptr<falco_engine> new_engine;
new_engine = create_new(rulesfiles, load_result);
return (new_engine != NULL);
}
std::shared_ptr<falco_engine> swappable_falco_engine::create_new(std::list<falco_engine::rulesfile> &rulesfiles,
std::string &load_result)
{
std::shared_ptr<falco_engine> ret = make_shared<falco_engine>();
load_result = "";
if(!m_inspector)
{
load_result = "No inspector provided yet";
ret = NULL;
return ret;
}
ret->set_extra(m_config.output_format, m_config.replace_container_info);
ret->set_min_priority(m_config.min_priority);
ret->set_plugin_infos(m_config.plugin_infos);
// Create "factories" that can create filters/formatters for
// each supported source.
for(const auto &source : m_config.event_sources)
{
std::shared_ptr<gen_event_filter_factory> filter_factory;
std::shared_ptr<gen_event_formatter_factory> formatter_factory;
if(source == syscall_source)
{
// This use of m_inspector looks unsafe, as it
// may have been created on a different thread
// than the thread where create_new() was
// called. But the inspector is only *used*
// when evaluating filters, and that is only
// done on the thread processing events and
// calling engine().
filter_factory.reset(new sinsp_filter_factory(m_inspector));
formatter_factory.reset(new sinsp_evt_formatter_factory(m_inspector));
}
else if (source == k8s_audit_source)
{
filter_factory.reset(new json_event_filter_factory());
formatter_factory.reset(new json_event_formatter_factory(filter_factory));
}
else
{
// Assumed to be a source plugin
filter_factory.reset(new sinsp_filter_factory(m_inspector, m_plugin_filter_checks));
formatter_factory.reset(new sinsp_evt_formatter_factory(m_inspector, m_plugin_filter_checks));
}
if(m_config.json_output)
{
formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
}
ret->add_source(source, filter_factory, formatter_factory);
}
// Note that we load all rules files, even if one of them has an error.
bool successful = true;
// We include filenames if there is more than one file
bool include_filenames = (rulesfiles.size() > 1);
// We include warnings if verbose
bool include_warnings = m_config.verbose;
std::ostringstream os;
for(auto &rf : rulesfiles)
{
falco_engine::load_result res(rf);
// XXX/mstemm all_events is actually unused, remove it.
bool all_events = false;
ret->load_rules(rf,
m_config.verbose, all_events,
res);
os << res.as_string(include_filenames, include_warnings);
if(!res.successful)
{
successful = false;
}
}
load_result = os.str();
if(!successful)
{
ret = NULL;
return ret;
}
for (auto substring : m_config.disabled_rule_substrings)
{
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
ret->enable_rule(substring, false);
}
if(m_config.disabled_rule_tags.size() > 0)
{
for(auto tag : m_config.disabled_rule_tags)
{
falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
}
ret->enable_rule_by_tag(m_config.disabled_rule_tags, false);
}
if(m_config.enabled_rule_tags.size() > 0)
{
string all_rules = "";
// Since we only want to enable specific
// rules, first disable all rules.
ret->enable_rule(all_rules, false);
for(auto tag : m_config.enabled_rule_tags)
{
falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
}
ret->enable_rule_by_tag(m_config.enabled_rule_tags, true);
}
return ret;
}

View File

@@ -0,0 +1,119 @@
/*
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 <list>
#include <memory>
#include <string>
#include <set>
#include <utility>
#include "tbb/concurrent_queue.h"
#include <sinsp.h>
#include <filter_check_list.h>
#include <falco_engine.h>
class swappable_falco_engine
{
public:
static std::string syscall_source;
static std::string k8s_audit_source;
class config {
public:
config();
virtual ~config();
bool contains_event_source(const std::string &source);
bool json_output;
bool verbose;
std::string output_format;
bool replace_container_info;
std::set<std::string> event_sources;
falco_common::priority_type min_priority;
std::list<sinsp_plugin::info> plugin_infos;
std::set<std::string> disabled_rule_substrings;
std::set<std::string> disabled_rule_tags;
std::set<std::string> enabled_rule_tags;
};
// Helper to load a set of files from filenames
static bool open_files(std::list<std::string> &filenames,
std::list<falco_engine::rulesfile> &rulesfiles,
std::string &errstr);
swappable_falco_engine();
virtual ~swappable_falco_engine();
bool init(config &cfg, sinsp *inspector, std::string &errstr);
std::shared_ptr<falco_engine> engine();
filter_check_list &plugin_filter_checks();
// Create a new engine, configure it using the saved config,
// load the provided set of rules files, and queue it to
// replace the current engine.
//
// This can be called from a different thread than the one
// calling engine().
//
// Returns true on success, false otherwise.
//
// load_result will be filled in with details on any errors (and warnings,
// if config.verbose is true).
bool replace(std::list<falco_engine::rulesfile> &rulesfiles,
std::string &load_result);
// Create a new engine, configure it, load the provided set of
// rules files, but do *not* queue it to replace the current
// engine.
//
// This can be called from a different thread than the one
// calling engine().
//
// Returns true on success, false otherwise.
//
// load_result will be filled in with details on any errors (and warnings,
// if config.verbose is true).
bool validate(std::list<falco_engine::rulesfile> &rulesfiles,
std::string &load_result);
private:
// Does everything but enqueue the new engine. Returns a
// shared_ptr to a new falco_engine on success, an empty
// shared_ptr on failure.
//
// load_result will be filled in with details on any errors (and warnings,
// if config.verbose is true).
std::shared_ptr<falco_engine> create_new(std::list<falco_engine::rulesfile> &rulesfiles,
std::string &load_result);
sinsp *m_inspector;
config m_config;
filter_check_list m_plugin_filter_checks;
std::shared_ptr<falco_engine> m_engine;
// If non-empty the head item will be moved to m_falco_engine
// with the next call to engine()
tbb::concurrent_queue<std::shared_ptr<falco_engine>> m_pending_falco_engine;
};

View File

@@ -27,8 +27,8 @@ using namespace std;
string k8s_audit_handler::m_k8s_audit_event_source = "k8s_audit";
k8s_audit_handler::k8s_audit_handler(falco_engine *engine, falco_outputs *outputs):
m_engine(engine), m_outputs(outputs)
k8s_audit_handler::k8s_audit_handler(swappable_falco_engine &swengine, falco_outputs *outputs):
m_swengine(swengine), m_outputs(outputs)
{
}
@@ -45,7 +45,7 @@ bool k8s_healthz_handler::handleGet(CivetServer *server, struct mg_connection *c
return true;
}
bool k8s_audit_handler::accept_data(falco_engine *engine,
bool k8s_audit_handler::accept_data(swappable_falco_engine &swengine,
falco_outputs *outputs,
std::string &data,
std::string &errstr)
@@ -89,7 +89,7 @@ bool k8s_audit_handler::accept_data(falco_engine *engine,
try
{
res = engine->process_event(m_k8s_audit_event_source, &jev);
res = swengine.engine()->process_event(m_k8s_audit_event_source, &jev);
}
catch(...)
{
@@ -120,7 +120,7 @@ bool k8s_audit_handler::accept_data(falco_engine *engine,
bool k8s_audit_handler::accept_uploaded_data(std::string &post_data, std::string &errstr)
{
return k8s_audit_handler::accept_data(m_engine, m_outputs, post_data, errstr);
return k8s_audit_handler::accept_data(m_swengine, m_outputs, post_data, errstr);
}
bool k8s_audit_handler::handleGet(CivetServer *server, struct mg_connection *conn)
@@ -177,7 +177,8 @@ bool k8s_audit_handler::handlePost(CivetServer *server, struct mg_connection *co
return true;
}
falco_webserver::falco_webserver():
falco_webserver::falco_webserver(swappable_falco_engine &swengine):
m_swengine(swengine),
m_config(NULL)
{
}
@@ -188,11 +189,9 @@ falco_webserver::~falco_webserver()
}
void falco_webserver::init(falco_configuration *config,
falco_engine *engine,
falco_outputs *outputs)
{
m_config = config;
m_engine = engine;
m_outputs = outputs;
}
@@ -214,11 +213,6 @@ void falco_webserver::start()
throw falco_exception("No config provided to webserver");
}
if(!m_engine)
{
throw falco_exception("No engine provided to webserver");
}
if(!m_outputs)
{
throw falco_exception("No outputs provided to webserver");
@@ -253,7 +247,7 @@ void falco_webserver::start()
throw falco_exception("Could not create embedded webserver");
}
m_k8s_audit_handler = make_unique<k8s_audit_handler>(m_engine, m_outputs);
m_k8s_audit_handler = make_unique<k8s_audit_handler>(m_swengine, m_outputs);
m_server->addHandler(m_config->m_webserver_k8s_audit_endpoint, *m_k8s_audit_handler);
m_k8s_healthz_handler = make_unique<k8s_healthz_handler>();
m_server->addHandler(m_config->m_webserver_k8s_healthz_endpoint, *m_k8s_healthz_handler);

View File

@@ -19,26 +19,26 @@ limitations under the License.
#include "CivetServer.h"
#include "configuration.h"
#include "falco_engine.h"
#include "swappable_falco_engine.h"
#include "falco_outputs.h"
class k8s_audit_handler : public CivetHandler
{
public:
k8s_audit_handler(falco_engine *engine, falco_outputs *outputs);
k8s_audit_handler(swappable_falco_engine &swengine, falco_outputs *outputs);
virtual ~k8s_audit_handler();
bool handleGet(CivetServer *server, struct mg_connection *conn);
bool handlePost(CivetServer *server, struct mg_connection *conn);
static bool accept_data(falco_engine *engine,
static bool accept_data(swappable_falco_engine &swengine,
falco_outputs *outputs,
std::string &post_data, std::string &errstr);
static std::string m_k8s_audit_event_source;
private:
falco_engine *m_engine;
swappable_falco_engine &m_swengine;
falco_outputs *m_outputs;
bool accept_uploaded_data(std::string &post_data, std::string &errstr);
};
@@ -60,18 +60,17 @@ public:
class falco_webserver
{
public:
falco_webserver();
falco_webserver(swappable_falco_engine &swengine);
virtual ~falco_webserver();
void init(falco_configuration *config,
falco_engine *engine,
falco_outputs *outputs);
void start();
void stop();
private:
falco_engine *m_engine;
swappable_falco_engine &m_swengine;
falco_configuration *m_config;
falco_outputs *m_outputs;
unique_ptr<CivetServer> m_server;