mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-21 04:02:14 +00:00
Compare commits
81 Commits
embed-lua-
...
add-grpc-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdd4f51db0 | ||
|
|
1b112d752a | ||
|
|
eb86768dfb | ||
|
|
b55df884ef | ||
|
|
debcb1e729 | ||
|
|
9f88c7cbd0 | ||
|
|
946a431e55 | ||
|
|
7e37fc8210 | ||
|
|
1ed2bec4d7 | ||
|
|
cc4332c8ce | ||
|
|
c648f2fcfd | ||
|
|
03d826d249 | ||
|
|
83fe8d649a | ||
|
|
4356307412 | ||
|
|
d338185524 | ||
|
|
54dea70482 | ||
|
|
08a67b77d6 | ||
|
|
22e6205921 | ||
|
|
91ee079ea6 | ||
|
|
4cc05d6f4a | ||
|
|
06b7427ede | ||
|
|
f86423db76 | ||
|
|
5eed3a6638 | ||
|
|
d585343483 | ||
|
|
9e57b5b4ba | ||
|
|
47f38c8ae2 | ||
|
|
332d828204 | ||
|
|
75c6cfb414 | ||
|
|
a4199814a0 | ||
|
|
24e7e84153 | ||
|
|
70bfb2426c | ||
|
|
ce3598f801 | ||
|
|
8e6ffc6fc9 | ||
|
|
6a42f4a133 | ||
|
|
8d9dd4440f | ||
|
|
c49093005d | ||
|
|
69767bb51b | ||
|
|
7750b6f209 | ||
|
|
8c705448cc | ||
|
|
6b9fafb75f | ||
|
|
fdcd7bffd0 | ||
|
|
d989e9c2d5 | ||
|
|
996ccf555c | ||
|
|
2f82a9baa1 | ||
|
|
dfb743838e | ||
|
|
c7609192c7 | ||
|
|
4d3fc354fa | ||
|
|
43bdfce6e5 | ||
|
|
a3976463d5 | ||
|
|
1a485c3447 | ||
|
|
96529300f6 | ||
|
|
27922faa27 | ||
|
|
8a1de131f4 | ||
|
|
e1e8715a0f | ||
|
|
9ae8d281f5 | ||
|
|
c705623f9e | ||
|
|
3640871725 | ||
|
|
6d507b054c | ||
|
|
f19a1d81c6 | ||
|
|
18c7b6500d | ||
|
|
8239fa41f4 | ||
|
|
a9e7512936 | ||
|
|
f67e8bdad7 | ||
|
|
a94e6de458 | ||
|
|
3e9f8c1ef1 | ||
|
|
d20a326e09 | ||
|
|
0c290d98f8 | ||
|
|
1befb053d0 | ||
|
|
ae57718bda | ||
|
|
55ce38cf3a | ||
|
|
18571eb20d | ||
|
|
9c449901f3 | ||
|
|
4ab8d6db98 | ||
|
|
5e354859a9 | ||
|
|
f4b79296fc | ||
|
|
6bf8f34d9f | ||
|
|
f8f053c7fa | ||
|
|
b88a1cbb09 | ||
|
|
c86615f68c | ||
|
|
08df1c63cf | ||
|
|
10512b9ef9 |
@@ -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
5
.gitignore
vendored
@@ -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*
|
||||
|
||||
94
CHANGELOG.md
94
CHANGELOG.md
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -10,5 +10,4 @@ endif()
|
||||
|
||||
if(CPACK_GENERATOR MATCHES "TGZ")
|
||||
set(CPACK_SET_DESTDIR "ON")
|
||||
set(CPACK_STRIP_FILES "OFF")
|
||||
endif()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 "")
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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/"
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
//
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
51
userspace/falco/rules.proto
Normal file
51
userspace/falco/rules.proto
Normal 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;
|
||||
}
|
||||
256
userspace/falco/swappable_falco_engine.cpp
Normal file
256
userspace/falco/swappable_falco_engine.cpp
Normal 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;
|
||||
}
|
||||
119
userspace/falco/swappable_falco_engine.h
Normal file
119
userspace/falco/swappable_falco_engine.h
Normal 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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user