mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 19:52:08 +00:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d26e0f77b8 | ||
|
|
836d7c0815 | ||
|
|
dd9fb0dbac | ||
|
|
805e6a99cc | ||
|
|
8122b86f4c | ||
|
|
9652de9f5d | ||
|
|
80a4cc13d7 | ||
|
|
dac99b444c | ||
|
|
d57046292f | ||
|
|
d6fc8c63e5 | ||
|
|
b94cda0b12 | ||
|
|
dfe08bdecb | ||
|
|
79a5f9f3d9 | ||
|
|
466373359c | ||
|
|
b46e4188d8 | ||
|
|
6bcef8f94e | ||
|
|
cf6857f13b | ||
|
|
9c2cff370d | ||
|
|
1a4ff6781b | ||
|
|
f15dca4a3b | ||
|
|
43122a21e5 | ||
|
|
eb59f3603b | ||
|
|
72910f23e9 | ||
|
|
4c258afd9b | ||
|
|
aa67a0270a | ||
|
|
60721592e5 | ||
|
|
9e67d90e19 | ||
|
|
afc4798d4c | ||
|
|
6bcc73aeff | ||
|
|
d4e7325c06 | ||
|
|
9fe7230d31 | ||
|
|
145036e923 | ||
|
|
4074148435 | ||
|
|
1728a5febc | ||
|
|
1533734fc4 | ||
|
|
f6c624a4e0 | ||
|
|
50bc0037e5 | ||
|
|
b0ef64b449 | ||
|
|
f4f7ccf777 | ||
|
|
ae28be023e | ||
|
|
28e7050f0f | ||
|
|
910788850a | ||
|
|
a41e3df45d | ||
|
|
06c4133b90 | ||
|
|
61d9383e8f | ||
|
|
60d6368c08 | ||
|
|
ff288f70b3 | ||
|
|
fb292e6fbb | ||
|
|
6e4b7663ca | ||
|
|
0326210f49 | ||
|
|
11f6fc5d14 | ||
|
|
7badc31cb1 | ||
|
|
11c7e23569 | ||
|
|
08a00609a1 | ||
|
|
630167d9ad | ||
|
|
80d52963d6 | ||
|
|
835ac52f4f | ||
|
|
543734af3c | ||
|
|
186614dff4 | ||
|
|
52127d4c8a | ||
|
|
04c1a11136 | ||
|
|
68465f6f2e | ||
|
|
18f99582da | ||
|
|
e8a6f72bc9 | ||
|
|
db178840d6 | ||
|
|
7c3c8eccc4 | ||
|
|
6e717daa95 | ||
|
|
d15cf450fc | ||
|
|
f70b28bfb4 | ||
|
|
ca80e69baa | ||
|
|
d8c6af821d |
@@ -28,7 +28,7 @@ jobs:
|
||||
build-modern-bpf-skeleton:
|
||||
# See https://github.com/actions/runner/issues/409#issuecomment-1158849936
|
||||
runs-on: ${{ (inputs.arch == 'aarch64' && 'ubuntu-22.04-arm') || 'ubuntu-latest' }}
|
||||
container: fedora:latest
|
||||
container: fedora:41
|
||||
steps:
|
||||
# Always install deps before invoking checkout action, to properly perform a full clone.
|
||||
- name: Install build dependencies
|
||||
@@ -59,7 +59,12 @@ jobs:
|
||||
# Always install deps before invoking checkout action, to properly perform a full clone.
|
||||
- name: Install build deps
|
||||
run: |
|
||||
sudo apt update && sudo apt install -y --no-install-recommends ca-certificates cmake curl wget build-essential git pkg-config autoconf automake libtool m4 rpm
|
||||
sudo apt update && sudo apt install -y --no-install-recommends ca-certificates cmake curl wget build-essential git pkg-config autoconf automake libtool m4 rpm alien
|
||||
|
||||
- name: Install systemd rpm macros
|
||||
run: |
|
||||
wget https://www.rpmfind.net/linux/centos-stream/9-stream/BaseOS/${{ inputs.arch }}/os/Packages/systemd-rpm-macros-252-51.el9.noarch.rpm
|
||||
sudo alien -d -i systemd-rpm-macros-252-51.el9.noarch.rpm
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
@@ -60,6 +60,8 @@ This is a list of production adopters of Falco (in alphabetical order):
|
||||
|
||||
* [Shopify](https://www.shopify.com) - Shopify is the leading multi-channel commerce platform. Merchants use Shopify to design, set up, and manage their stores across multiple sales channels, including mobile, web, social media, marketplaces, brick-and-mortar locations, and pop-up shops. The platform also provides merchants with a powerful back-office and a single view of their business, from payments to shipping. The Shopify platform was engineered for reliability and scale, making enterprise-level technology available to businesses of all sizes. Shopify uses Falco to complement its Host and Network Intrusion Detection Systems.
|
||||
|
||||
* [SafeDep](https://safedep.io/) - SafeDep is a open source software supply chain security platform that helps organizations identify and mitigate risks in their dependencies. At its core, SafeDep offers [`vet`](https://github.com/safedep/vet) a free and open source tool for detecting vulnerabilities, malicious code, and quality issues in open source components, while the enterprise offering, SafeDep Cloud, provides centralized control, data aggregation, and advanced features like malware analysis for large-scale deployments across thousands of repositories.
|
||||
|
||||
* [Sight Machine](https://www.sightmachine.com) - Sight Machine is the category leader for manufacturing analytics and used by Global 500 companies to make better, faster decisions about their operations. Sight Machine uses Falco to help enforce SOC2 compliance as well as a tool for real time security monitoring and alerting in Kubernetes.
|
||||
|
||||
* [Skyscanner](https://www.skyscanner.net) - Skyscanner is the world's travel search engine for flights, hotels and car rentals. Most of our infrastructure is based on Kubernetes, and our Security team is using Falco to monitor anomalies at runtime, integrating Falco's findings with our internal ChatOps tooling to provide insight on the behavior of our machines in production. We also postprocess and store Falco's results to generate dashboards for auditing purposes.
|
||||
|
||||
147
CHANGELOG.md
147
CHANGELOG.md
@@ -1,5 +1,152 @@
|
||||
# Change Log
|
||||
|
||||
## v0.41.3
|
||||
|
||||
Released on 2025-07-01
|
||||
|
||||
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* update: bump container plugin to v0.3.1 [[#3629](https://github.com/falcosecurity/falco/pull/3629)] - [@FedeDP](https://github.com/FedeDP)
|
||||
|
||||
|
||||
|
||||
|
||||
### Statistics
|
||||
|
||||
| MERGED PRS | NUMBER |
|
||||
|-----------------|--------|
|
||||
| Not user-facing | 0 |
|
||||
| Release note | 1 |
|
||||
| Total | 1 |
|
||||
|
||||
## v0.41.2
|
||||
|
||||
Released on 2025-06-17
|
||||
|
||||
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* update(build): update container plugin to 0.3.0 [[#3619](https://github.com/falcosecurity/falco/pull/3619)] - [@ekoops](https://github.com/ekoops)
|
||||
|
||||
|
||||
|
||||
### Non user-facing changes
|
||||
|
||||
* update(build): update container plugin to 0.2.6 [[#3611](https://github.com/falcosecurity/falco/pull/3611)] - [@leogr](https://github.com/leogr)
|
||||
|
||||
### Statistics
|
||||
|
||||
| MERGED PRS | NUMBER |
|
||||
|-----------------|--------|
|
||||
| Not user-facing | 1 |
|
||||
| Release note | 1 |
|
||||
| Total | 2 |
|
||||
|
||||
## v0.41.1
|
||||
|
||||
Released on 2025-06-05
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix(userspace/falco): when collecting metrics for stats_writer, create a `libs_metrics_collector` for each source [[#3585](https://github.com/falcosecurity/falco/pull/3585)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* fix(userspace/falco): only enable prometheus metrics once all inspectors have been opened [[#3588](https://github.com/falcosecurity/falco/pull/3588)] - [@FedeDP](https://github.com/FedeDP)
|
||||
|
||||
### Statistics
|
||||
|
||||
| MERGED PRS | NUMBER |
|
||||
|-----------------|--------|
|
||||
| Not user-facing | 0 |
|
||||
| Release note | 2 |
|
||||
| Total | 2 |
|
||||
|
||||
## v0.41.0
|
||||
|
||||
Released on 2025-05-29
|
||||
|
||||
### Breaking Changes :warning:
|
||||
|
||||
* cleanup(engine)!: only consider .yaml/.yml rule files [[#3551](https://github.com/falcosecurity/falco/pull/3551)] - [@LucaGuerra](https://github.com/LucaGuerra)
|
||||
* cleanup(userspace)!: deprecate print of `container.info` [[#3543](https://github.com/falcosecurity/falco/pull/3543)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* cleanup(userspace/falco)!: drop deprecated in 0.40.0 CLI flags. [[#3496](https://github.com/falcosecurity/falco/pull/3496)] - [@FedeDP](https://github.com/FedeDP)
|
||||
|
||||
|
||||
### Major Changes
|
||||
|
||||
* new(falco): add json_include_output_fields option [[#3527](https://github.com/falcosecurity/falco/pull/3527)] - [@LucaGuerra](https://github.com/LucaGuerra)
|
||||
* new(build,userspace): switch to use container plugin [[#3482](https://github.com/falcosecurity/falco/pull/3482)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* new(docker,scripts,ci): use an override config file to enable ISO 8601 output timeformat on docker images [[#3488](https://github.com/falcosecurity/falco/pull/3488)] - [@FedeDP](https://github.com/FedeDP)
|
||||
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* chore(build): update falcoctl to v0.11.2, rules for artifact follow to v4 [[#3580](https://github.com/falcosecurity/falco/pull/3580)] - [@LucaGuerra](https://github.com/LucaGuerra)
|
||||
* update(cmake): bumped falcoctl to 0.11.1 and rules to 4.0.0. [[#3577](https://github.com/falcosecurity/falco/pull/3577)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* update(containers): update opencontainers labels [[#3575](https://github.com/falcosecurity/falco/pull/3575)] - [@LucaGuerra](https://github.com/LucaGuerra)
|
||||
* update(metrics): improve restart/hot_reload conditions inspection [[#3562](https://github.com/falcosecurity/falco/pull/3562)] - [@incertum](https://github.com/incertum)
|
||||
* update: empty `values` in `exceptions` won't emit a warning anymore [[#3529](https://github.com/falcosecurity/falco/pull/3529)] - [@leogr](https://github.com/leogr)
|
||||
* chore(falco.yaml): enable libs_logger by default with info level [[#3507](https://github.com/falcosecurity/falco/pull/3507)] - [@FedeDP](https://github.com/FedeDP)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix(metrics/prometheus): gracefully handle multiple event sources, avoid erroneous duplicate metrics [[#3563](https://github.com/falcosecurity/falco/pull/3563)] - [@incertum](https://github.com/incertum)
|
||||
* fix(ci): properly install rpm systemd-rpm-macro package on building packages pipeline [[#3521](https://github.com/falcosecurity/falco/pull/3521)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* fix(userspace/falco): init cmdline options after loading all config files [[#3493](https://github.com/falcosecurity/falco/pull/3493)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* fix(cmake): add support for 16K kernel page to jemalloc [[#3490](https://github.com/falcosecurity/falco/pull/3490)] - [@Darkness4](https://github.com/Darkness4)
|
||||
* fix(userspace/falco): fix jemalloc enabled in minimal build. [[#3478](https://github.com/falcosecurity/falco/pull/3478)] - [@FedeDP](https://github.com/FedeDP)
|
||||
|
||||
|
||||
|
||||
### Non user-facing changes
|
||||
|
||||
* chore(deps): Bump submodules/falcosecurity-rules from `4ccf111` to `cb17833` [[#3572](https://github.com/falcosecurity/falco/pull/3572)] - [@dependabot[bot]](https://github.com/apps/dependabot)
|
||||
* update(cmake/rules): bump to falco-rules-4.0.0-rc1 [[#3567](https://github.com/falcosecurity/falco/pull/3567)] - [@leogr](https://github.com/leogr)
|
||||
* cleanup(userspace/falco): drop unused `libs_metrics_collector` variable. [[#3566](https://github.com/falcosecurity/falco/pull/3566)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* update(cmake): update libs and driver to latest master [[#3564](https://github.com/falcosecurity/falco/pull/3564)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* fix(build): fixed container custom_target `sed` command. [[#3556](https://github.com/falcosecurity/falco/pull/3556)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* chore(deps): Bump submodules/falcosecurity-rules from `ae6ed41` to `4ccf111` [[#3555](https://github.com/falcosecurity/falco/pull/3555)] - [@dependabot[bot]](https://github.com/apps/dependabot)
|
||||
* fix(cmake): fix bundled c-ares cmake issue with e.g. SLES [[#3559](https://github.com/falcosecurity/falco/pull/3559)] - [@terror96](https://github.com/terror96)
|
||||
* chore(deps): Bump submodules/falcosecurity-rules from `1d2c6b1` to `ae6ed41` [[#3553](https://github.com/falcosecurity/falco/pull/3553)] - [@dependabot[bot]](https://github.com/apps/dependabot)
|
||||
* chore: revert "chore(deps): Bump submodules/falcosecurity-rules from `1d2c6b1` to `371e431`" [[#3552](https://github.com/falcosecurity/falco/pull/3552)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* update(cmake): update libs and driver to latest master [[#3550](https://github.com/falcosecurity/falco/pull/3550)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* update(cmake): update libs and driver to latest master [[#3549](https://github.com/falcosecurity/falco/pull/3549)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* update(adopters): added SafeDep as adopter [[#3548](https://github.com/falcosecurity/falco/pull/3548)] - [@KunalSin9h](https://github.com/KunalSin9h)
|
||||
* update(cmake): update libs and driver to latest master [[#3547](https://github.com/falcosecurity/falco/pull/3547)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* update(cmake): update libs and driver to latest master [[#3541](https://github.com/falcosecurity/falco/pull/3541)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* fix(userspace): fixed engine `openssl` dep. [[#3535](https://github.com/falcosecurity/falco/pull/3535)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* fix(userspace/falco): fix outputs_http timeout [[#3523](https://github.com/falcosecurity/falco/pull/3523)] - [@benierc](https://github.com/benierc)
|
||||
* fix(ci): use clang-19 to build modern_ebpf skeleton. [[#3537](https://github.com/falcosecurity/falco/pull/3537)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* update(cmake): update libs and driver to latest master [[#3531](https://github.com/falcosecurity/falco/pull/3531)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* update(cmake): update libs and driver to latest master [[#3530](https://github.com/falcosecurity/falco/pull/3530)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* update(cmake): update libs and driver to latest master [[#3525](https://github.com/falcosecurity/falco/pull/3525)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* update(cmake): update libs and driver to latest master [[#3520](https://github.com/falcosecurity/falco/pull/3520)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* update(cmake): update libs and driver to latest master [[#3516](https://github.com/falcosecurity/falco/pull/3516)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* docs(README.md): cleanups and enhancements [[#3514](https://github.com/falcosecurity/falco/pull/3514)] - [@leogr](https://github.com/leogr)
|
||||
* update(cmake): update libs and driver to latest master [[#3511](https://github.com/falcosecurity/falco/pull/3511)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* chore(deps): Bump submodules/falcosecurity-rules from `1d2c6b1` to `371e431` [[#3510](https://github.com/falcosecurity/falco/pull/3510)] - [@dependabot[bot]](https://github.com/apps/dependabot)
|
||||
* update(cmake): update libs and driver to latest master [[#3508](https://github.com/falcosecurity/falco/pull/3508)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* update(cmake): update libs and driver to latest master [[#3506](https://github.com/falcosecurity/falco/pull/3506)] - [@github-actions[bot]](https://github.com/apps/github-actions)
|
||||
* fix(userspace/falco): when counting `-M` timeout, do not account for async events [[#3505](https://github.com/falcosecurity/falco/pull/3505)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* chore(deps): Bump submodules/falcosecurity-rules from `d8415c1` to `1d2c6b1` [[#3504](https://github.com/falcosecurity/falco/pull/3504)] - [@dependabot[bot]](https://github.com/apps/dependabot)
|
||||
* docs(proposals): correct typo in example [[#3499](https://github.com/falcosecurity/falco/pull/3499)] - [@leogr](https://github.com/leogr)
|
||||
* fix(docker): fixed entrypoints paths with new docker context. [[#3492](https://github.com/falcosecurity/falco/pull/3492)] - [@FedeDP](https://github.com/FedeDP)
|
||||
* feat(falco/app): move actions not using config before `load_config` [[#3483](https://github.com/falcosecurity/falco/pull/3483)] - [@ekoops](https://github.com/ekoops)
|
||||
* refactor(falco/app): apply early return pattern in actions code [[#3484](https://github.com/falcosecurity/falco/pull/3484)] - [@ekoops](https://github.com/ekoops)
|
||||
* chore(deps): Bump submodules/falcosecurity-rules from `abf6637` to `d8415c1` [[#3489](https://github.com/falcosecurity/falco/pull/3489)] - [@dependabot[bot]](https://github.com/apps/dependabot)
|
||||
* Add NETWAYS Web Services to ADOPTERS.md [[#3487](https://github.com/falcosecurity/falco/pull/3487)] - [@mocdaniel](https://github.com/mocdaniel)
|
||||
* chore: add back Falco static package to the release template. [[#3472](https://github.com/falcosecurity/falco/pull/3472)] - [@FedeDP](https://github.com/FedeDP)
|
||||
|
||||
### Statistics
|
||||
|
||||
| MERGED PRS | NUMBER |
|
||||
|-----------------|--------|
|
||||
| Not user-facing | 36 |
|
||||
| Release note | 17 |
|
||||
| Total | 53 |
|
||||
|
||||
## v0.40.0
|
||||
|
||||
Released on 2025-01-28
|
||||
|
||||
@@ -267,6 +267,12 @@ if(NOT WIN32
|
||||
AND NOT MUSL_OPTIMIZED_BUILD
|
||||
)
|
||||
include(falcoctl)
|
||||
set(CONTAINER_VERSION "0.3.1")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(CONTAINER_HASH "2c8f351448b30044560affd493e7af75dd1207a9ee9c26970e5aa41f1371059a")
|
||||
else() # arm64
|
||||
set(CONTAINER_HASH "8ebe2d7f691ba7fd47534cbffc640fd6830e5057742f185e0fda8fed7ab192a4")
|
||||
endif()
|
||||
include(container_plugin)
|
||||
|
||||
# Generate a binary_dir/falco.yaml that automatically enables the plugin to be used for local
|
||||
@@ -277,7 +283,8 @@ if(NOT WIN32
|
||||
container
|
||||
COMMAND sed -i 's,^load_plugins: .*,load_plugins: [container],g'
|
||||
${CMAKE_BINARY_DIR}/falco.yaml
|
||||
COMMAND sed -i 's,libcontainer.so,${CONTAINER_LIBRARY},g' ${CMAKE_BINARY_DIR}/falco.yaml
|
||||
COMMAND sed -i 's,library_path: libcontainer.so,library_path: ${CONTAINER_LIBRARY},g'
|
||||
${CMAKE_BINARY_DIR}/falco.yaml
|
||||
DEPENDS container_plugin
|
||||
)
|
||||
# Let `make falco` also download container plugin
|
||||
|
||||
@@ -73,7 +73,7 @@ if(NOT MSVC)
|
||||
|
||||
if(BUILD_WARNINGS_AS_ERRORS)
|
||||
set(CMAKE_SUPPRESSED_WARNINGS
|
||||
"-Wno-unused-parameter -Wno-unused-variable -Wno-unused-but-set-variable -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -Wno-implicit-fallthrough -Wno-format-truncation -Wno-stringop-truncation -Wno-stringop-overflow -Wno-restrict"
|
||||
"-Wno-unused-parameter -Wno-unused-variable -Wno-unused-but-set-variable -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -Wno-implicit-fallthrough -Wno-format-truncation -Wno-stringop-truncation -Wno-stringop-overflow -Wno-restrict -Wno-deprecated-declarations"
|
||||
)
|
||||
set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
|
||||
set(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} -Wextra ${CMAKE_SUPPRESSED_WARNINGS}")
|
||||
|
||||
@@ -47,6 +47,7 @@ else()
|
||||
BUILD_IN_SOURCE 1
|
||||
CMAKE_ARGS -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=NEW
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY}
|
||||
-DCMAKE_INSTALL_LIBDIR=lib
|
||||
-DCARES_SHARED=${BUILD_SHARED_LIBS}
|
||||
-DCARES_STATIC=${CARES_STATIC_OPTION}
|
||||
-DCARES_STATIC_PIC=${ENABLE_PIC}
|
||||
|
||||
@@ -35,9 +35,9 @@ else()
|
||||
# FALCOSECURITY_LIBS_VERSION. In case you want to test against another driver version (or
|
||||
# branch, or commit) just pass the variable - ie., `cmake -DDRIVER_VERSION=dev ..`
|
||||
if(NOT DRIVER_VERSION)
|
||||
set(DRIVER_VERSION "dc16ffa8553155ef16f739cc5105bff2e07b1984")
|
||||
set(DRIVER_VERSION "8.1.0+driver")
|
||||
set(DRIVER_CHECKSUM
|
||||
"SHA256=1d974bc06ba4b74ea8b598fa3b204b6efd873790c09516f7fe72465c4b354d66"
|
||||
"SHA256=182e6787bf86249a846a3baeb4dcd31578b76d4a13efa16ce3f44d66b18a77a6"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -20,16 +20,16 @@ option(ADD_FALCOCTL_DEPENDENCY "Add falcoctl dependency while building falco" ON
|
||||
if(ADD_FALCOCTL_DEPENDENCY)
|
||||
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} FALCOCTL_SYSTEM_NAME)
|
||||
|
||||
set(FALCOCTL_VERSION "0.11.0")
|
||||
set(FALCOCTL_VERSION "0.11.2")
|
||||
|
||||
message(STATUS "Building with falcoctl: ${FALCOCTL_VERSION}")
|
||||
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(FALCOCTL_SYSTEM_PROC_GO "amd64")
|
||||
set(FALCOCTL_HASH "b9d0e0f50813e7172a945f36f70c5c3c16a677ab4c85b35b6f7a155bc92768fc")
|
||||
set(FALCOCTL_HASH "8d55818987c90e54f7406e1c1441a18df1f485db858bb0b3efda5db217be3b48")
|
||||
else() # aarch64
|
||||
set(FALCOCTL_SYSTEM_PROC_GO "arm64")
|
||||
set(FALCOCTL_HASH "689c625d1d414cbf53d39ef94083a53dda3ea4ac4908799fb85f4519e21442e0")
|
||||
set(FALCOCTL_HASH "7c36404b5b7a515df25e7dc6d827a74ebc8526b1b49850954bbdd40860961bc2")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
|
||||
@@ -42,9 +42,9 @@ else()
|
||||
# version (or branch, or commit) just pass the variable - ie., `cmake
|
||||
# -DFALCOSECURITY_LIBS_VERSION=dev ..`
|
||||
if(NOT FALCOSECURITY_LIBS_VERSION)
|
||||
set(FALCOSECURITY_LIBS_VERSION "dc16ffa8553155ef16f739cc5105bff2e07b1984")
|
||||
set(FALCOSECURITY_LIBS_VERSION "0.21.0")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM
|
||||
"SHA256=1d974bc06ba4b74ea8b598fa3b204b6efd873790c09516f7fe72465c4b354d66"
|
||||
"SHA256=9e977001dd42586df42a5dc7e7a948c297124865a233402e44bdec68839d322a"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Copyright (C) 2024 The Falco Authors.
|
||||
# Copyright (C) 2025 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
|
||||
@@ -18,9 +18,9 @@ include(ExternalProject)
|
||||
|
||||
if(NOT DEFINED FALCOSECURITY_RULES_FALCO_PATH)
|
||||
# falco_rules.yaml
|
||||
set(FALCOSECURITY_RULES_FALCO_VERSION "falco-rules-3.2.0")
|
||||
set(FALCOSECURITY_RULES_FALCO_VERSION "falco-rules-4.0.0")
|
||||
set(FALCOSECURITY_RULES_FALCO_CHECKSUM
|
||||
"SHA256=b3990bf0209cfbf6a903b361e458a1f5851a9a5aeee808ad26a5ddbe1377157d"
|
||||
"SHA256=132320ddbfa1e2580981ed1bdd3ee3d0128a1e2306b2bee8978d1f0a930d6127"
|
||||
)
|
||||
set(FALCOSECURITY_RULES_FALCO_PATH
|
||||
"${PROJECT_BINARY_DIR}/falcosecurity-rules-falco-prefix/src/falcosecurity-rules-falco/falco_rules.yaml"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
FROM debian:buster
|
||||
|
||||
LABEL maintainer="cncf-falco-dev@lists.cncf.io"
|
||||
LABEL org.opencontainers.image.source="https://github.com/falcosecurity/falco"
|
||||
LABEL org.opencontainers.image.authors='The Falco Authors https://falco.org' \
|
||||
org.opencontainers.image.url='https://falco.org' \
|
||||
org.opencontainers.image.source='https://github.com/falcosecurity/falco' \
|
||||
org.opencontainers.image.vendor='Falco Organization' \
|
||||
org.opencontainers.image.licenses='Apache-2.0' \
|
||||
maintainer="cncf-falco-dev@lists.cncf.io"
|
||||
|
||||
LABEL usage="docker run -i -t --privileged -v /root/.falco:/root/.falco -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro -v /etc:/host/etc:ro falcosecurity/falco-driver-loader:latest-buster [driver] [options]"
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
ARG FALCO_IMAGE_TAG=latest
|
||||
FROM docker.io/falcosecurity/falco:${FALCO_IMAGE_TAG}-debian
|
||||
|
||||
LABEL maintainer="cncf-falco-dev@lists.cncf.io"
|
||||
LABEL org.opencontainers.image.source="https://github.com/falcosecurity/falco"
|
||||
LABEL org.opencontainers.image.authors='The Falco Authors https://falco.org' \
|
||||
org.opencontainers.image.url='https://falco.org' \
|
||||
org.opencontainers.image.source='https://github.com/falcosecurity/falco' \
|
||||
org.opencontainers.image.vendor='Falco Organization' \
|
||||
org.opencontainers.image.licenses='Apache-2.0' \
|
||||
maintainer="cncf-falco-dev@lists.cncf.io"
|
||||
|
||||
LABEL usage="docker run -i -t --privileged -v /root/.falco:/root/.falco -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro -v /etc:/host/etc:ro falcosecurity/falco-driver-loader:latest [driver] [options]"
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
FROM debian:12-slim
|
||||
|
||||
LABEL maintainer="cncf-falco-dev@lists.cncf.io"
|
||||
LABEL org.opencontainers.image.source="https://github.com/falcosecurity/falco/docker/falco-debian"
|
||||
LABEL org.opencontainers.image.authors='The Falco Authors https://falco.org' \
|
||||
org.opencontainers.image.url='https://falco.org' \
|
||||
org.opencontainers.image.source='https://github.com/falcosecurity/falco' \
|
||||
org.opencontainers.image.vendor='Falco Organization' \
|
||||
org.opencontainers.image.licenses='Apache-2.0' \
|
||||
maintainer="cncf-falco-dev@lists.cncf.io"
|
||||
|
||||
LABEL usage="docker run -i -t --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v /proc:/host/proc:ro -v /etc:/host/etc:ro falcosecurity/falco:latest-debian"
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
FROM cgr.dev/chainguard/wolfi-base
|
||||
|
||||
LABEL maintainer="cncf-falco-dev@lists.cncf.io"
|
||||
LABEL org.opencontainers.image.source="https://github.com/falcosecurity/falco"
|
||||
LABEL org.opencontainers.image.authors='The Falco Authors https://falco.org' \
|
||||
org.opencontainers.image.url='https://falco.org' \
|
||||
org.opencontainers.image.source='https://github.com/falcosecurity/falco' \
|
||||
org.opencontainers.image.vendor='Falco Organization' \
|
||||
org.opencontainers.image.licenses='Apache-2.0' \
|
||||
maintainer="cncf-falco-dev@lists.cncf.io"
|
||||
|
||||
LABEL usage="docker run -i -t --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v /proc:/host/proc:ro -v /etc:/host/etc:ro falcosecurity/falco:latest"
|
||||
# NOTE: for the "least privileged" use case, please refer to the official documentation
|
||||
|
||||
40
falco.yaml
40
falco.yaml
@@ -143,8 +143,31 @@
|
||||
# Also, nested include is not allowed, ie: included config files won't be able to include other config files.
|
||||
#
|
||||
# Like for 'rules_files', specifying a folder will load all the configs files present in it in a lexicographical order.
|
||||
#
|
||||
# 3 merge-strategies are available:
|
||||
# `append` (default):
|
||||
# * existing sequence keys will be appended
|
||||
# * existing scalar keys will be overridden
|
||||
# * non-existing keys will be added
|
||||
# `override`:
|
||||
# * existing keys will be overridden
|
||||
# * non-existing keys will be added
|
||||
# `add-only`:
|
||||
# * existing keys will be ignored
|
||||
# * non-existing keys will be added
|
||||
#
|
||||
# Each item on the list can be either a yaml map or a simple string.
|
||||
# The simple string will be interpreted as the config file path, and the `append` merge-strategy will be enforced.
|
||||
# When the item is a yaml map instead, it will be of the form: ` path: foo\n strategy: X`.
|
||||
# When `strategy` is omitted, once again `append` is used.
|
||||
#
|
||||
# When a merge-strategy is enabled for a folder entry, all the included config files will use that merge-strategy.
|
||||
config_files:
|
||||
- /etc/falco/config.d
|
||||
# Example of config file specified as yaml map with strategy made explicit.
|
||||
#- path: $HOME/falco_local_configs/
|
||||
# strategy: add-only
|
||||
|
||||
|
||||
# [Stable] `watch_config_files`
|
||||
#
|
||||
@@ -167,8 +190,8 @@ watch_config_files: true
|
||||
# Falco rules can be specified using files or directories, which are loaded at
|
||||
# startup.
|
||||
#
|
||||
# If the entry is a file, it will be read directly. If the entry is a directory,
|
||||
# all files within that directory will be read in alphabetical order.
|
||||
# If the entry is a yaml file, it will be read directly. If the entry is a directory,
|
||||
# all yaml files within that directory will be read in alphabetical order.
|
||||
#
|
||||
# The falco_rules.yaml file ships with the Falco package and is overridden with
|
||||
# every new software version. falco_rules.local.yaml is only created if it
|
||||
@@ -196,6 +219,10 @@ watch_config_files: true
|
||||
# "first match wins" principle. However, enabling the `all` matching option may result
|
||||
# in a performance penalty. We recommend carefully testing this alternative setting
|
||||
# before deploying it in production. Read more under the `rule_matching` configuration.
|
||||
#
|
||||
# Since Falco 0.41 only files with .yml and .yaml extensions are considered,
|
||||
# including directory contents. This means that you may specify directories that
|
||||
# contain yaml files for rules and other files which will be ignored.
|
||||
rules_files:
|
||||
- /etc/falco/falco_rules.yaml
|
||||
- /etc/falco/falco_rules.local.yaml
|
||||
@@ -568,6 +595,13 @@ json_include_output_property: true
|
||||
# information.
|
||||
json_include_message_property: false
|
||||
|
||||
# [Incubating] `json_include_output_fields_property`
|
||||
#
|
||||
# When using JSON output in Falco, you have the option to include the individual
|
||||
# output fields for easier access. To reduce the logging volume, it is recommended
|
||||
# to turn it off if it's not necessary for your use case.
|
||||
json_include_output_fields_property: true
|
||||
|
||||
# [Stable] `json_include_tags_property`
|
||||
#
|
||||
# When using JSON output in Falco, you have the option to include the "tags"
|
||||
@@ -746,6 +780,8 @@ http_output:
|
||||
echo: false
|
||||
compress_uploads: false
|
||||
keep_alive: false
|
||||
# Maximum consecutive timeouts of libcurl to ignore
|
||||
max_consecutive_timeouts: 5
|
||||
|
||||
# [Stable] `program_output`
|
||||
#
|
||||
|
||||
@@ -10,7 +10,7 @@ artifact:
|
||||
every: 6h0m0s
|
||||
falcoVersions: http://localhost:8765/versions
|
||||
refs:
|
||||
- falco-rules:3
|
||||
- falco-rules:4
|
||||
indexes:
|
||||
- name: falcosecurity
|
||||
url: https://falcosecurity.github.io/falcoctl/index.yaml
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
371e43167e1b70ef0600d8fd30c8a338663ccb56
|
||||
1
submodules/falcosecurity-rules
Submodule
1
submodules/falcosecurity-rules
Submodule
Submodule submodules/falcosecurity-rules added at 4ccf111c36
@@ -28,7 +28,7 @@ TEST_F(test_falco_engine, extra_format_all) {
|
||||
priority: INFO
|
||||
)END";
|
||||
|
||||
m_engine->add_extra_output_format("evt.type=%evt.type", "", {}, "", false);
|
||||
m_engine->add_extra_output_format("evt.type=%evt.type", "", {}, "");
|
||||
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;
|
||||
|
||||
EXPECT_EQ(get_compiled_rule_output("legit_rule"),
|
||||
@@ -50,7 +50,7 @@ TEST_F(test_falco_engine, extra_format_by_rule) {
|
||||
priority: INFO
|
||||
)END";
|
||||
|
||||
m_engine->add_extra_output_format("evt.type=%evt.type", "", {}, "legit_rule", false);
|
||||
m_engine->add_extra_output_format("evt.type=%evt.type", "", {}, "legit_rule");
|
||||
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;
|
||||
|
||||
EXPECT_EQ(get_compiled_rule_output("legit_rule"), "out 1 evt.type=%evt.type");
|
||||
@@ -81,9 +81,9 @@ TEST_F(test_falco_engine, extra_format_by_tag_rule) {
|
||||
tags: [tag1, tag2]
|
||||
)END";
|
||||
|
||||
m_engine->add_extra_output_format("extra 1", "", {"tag1"}, "", false);
|
||||
m_engine->add_extra_output_format("extra 2", "", {}, "another_rule", false);
|
||||
m_engine->add_extra_output_format("extra 3", "", {"tag1", "tag2"}, "", false);
|
||||
m_engine->add_extra_output_format("extra 1", "", {"tag1"}, "");
|
||||
m_engine->add_extra_output_format("extra 2", "", {}, "another_rule");
|
||||
m_engine->add_extra_output_format("extra 3", "", {"tag1", "tag2"}, "");
|
||||
|
||||
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;
|
||||
|
||||
@@ -92,32 +92,7 @@ TEST_F(test_falco_engine, extra_format_by_tag_rule) {
|
||||
EXPECT_EQ(get_compiled_rule_output("a_third_rule"), "out 3 extra 1 extra 3");
|
||||
}
|
||||
|
||||
TEST_F(test_falco_engine, extra_format_replace_container_info) {
|
||||
std::string rules_content = R"END(
|
||||
- rule: legit_rule
|
||||
desc: legit rule description
|
||||
condition: evt.type=open
|
||||
output: out 1 (%container.info)
|
||||
priority: INFO
|
||||
tags: [tag1]
|
||||
|
||||
- rule: another_rule
|
||||
desc: legit rule description
|
||||
condition: evt.type=open
|
||||
output: out 2
|
||||
priority: INFO
|
||||
tags: [tag1]
|
||||
)END";
|
||||
|
||||
m_engine->add_extra_output_format("extra 1", "", {}, "", true);
|
||||
|
||||
ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string;
|
||||
|
||||
EXPECT_EQ(get_compiled_rule_output("legit_rule"), "out 1 (extra 1)");
|
||||
EXPECT_EQ(get_compiled_rule_output("another_rule"), "out 2 extra 1");
|
||||
}
|
||||
|
||||
TEST_F(test_falco_engine, extra_format_do_not_replace_container_info) {
|
||||
TEST_F(test_falco_engine, extra_format_empty_container_info) {
|
||||
std::string rules_content = R"END(
|
||||
- rule: legit_rule
|
||||
desc: legit rule description
|
||||
|
||||
@@ -245,6 +245,316 @@ TEST(Configuration, configuration_config_files_override) {
|
||||
std::filesystem::remove("conf_3.yaml");
|
||||
}
|
||||
|
||||
TEST(Configuration, configuration_config_files_sequence_strategy_default) {
|
||||
const std::string main_conf_yaml = yaml_helper::configs_key +
|
||||
":\n"
|
||||
" - conf_2.yaml\n" // default merge-strategy: append
|
||||
" - conf_3.yaml\n"
|
||||
"foo: [ bar ]\n"
|
||||
"base_value:\n"
|
||||
" id: 1\n"
|
||||
" name: foo\n";
|
||||
const std::string conf_yaml_2 =
|
||||
"foo: [ bar2 ]\n" // append to foo sequence
|
||||
"base_value_2:\n"
|
||||
" id: 2\n";
|
||||
const std::string conf_yaml_3 =
|
||||
"base_value:\n" // override base_value
|
||||
" id: 3\n";
|
||||
|
||||
std::ofstream outfile("main.yaml");
|
||||
outfile << main_conf_yaml;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_2.yaml");
|
||||
outfile << conf_yaml_2;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_3.yaml");
|
||||
outfile << conf_yaml_3;
|
||||
outfile.close();
|
||||
|
||||
std::vector<std::string> cmdline_config_options;
|
||||
falco_configuration falco_config;
|
||||
config_loaded_res res;
|
||||
ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options));
|
||||
|
||||
// main + conf_2 + conf_3
|
||||
ASSERT_EQ(res.size(), 3);
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("foo"));
|
||||
std::vector<std::string> foos;
|
||||
auto expected_foos = std::vector<std::string>{"bar", "bar2"};
|
||||
ASSERT_NO_THROW(falco_config.m_config.get_sequence<std::vector<std::string>>(foos, "foo"));
|
||||
ASSERT_EQ(foos.size(), 2); // 2 elements in `foo` sequence because we appended to it
|
||||
for(size_t i = 0; i < foos.size(); ++i) {
|
||||
EXPECT_EQ(foos[i], expected_foos[i])
|
||||
<< "Vectors foo's and expected_foo's differ at index " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 3); // overridden!
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined(
|
||||
"base_value.name")); // no more present since entire `base_value` block was overridden
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value_2.id", 0), 2);
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined
|
||||
|
||||
std::filesystem::remove("main.yaml");
|
||||
std::filesystem::remove("conf_2.yaml");
|
||||
std::filesystem::remove("conf_3.yaml");
|
||||
}
|
||||
|
||||
TEST(Configuration, configuration_config_files_sequence_strategy_append) {
|
||||
const std::string main_conf_yaml = yaml_helper::configs_key +
|
||||
":\n"
|
||||
" - path: conf_2.yaml\n"
|
||||
" strategy: append\n"
|
||||
" - conf_3.yaml\n"
|
||||
"foo: [ bar ]\n"
|
||||
"base_value:\n"
|
||||
" id: 1\n"
|
||||
" name: foo\n";
|
||||
const std::string conf_yaml_2 =
|
||||
"foo: [ bar2 ]\n" // append to foo sequence
|
||||
"base_value_2:\n"
|
||||
" id: 2\n";
|
||||
const std::string conf_yaml_3 =
|
||||
"base_value:\n" // override base_value
|
||||
" id: 3\n";
|
||||
|
||||
std::ofstream outfile("main.yaml");
|
||||
outfile << main_conf_yaml;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_2.yaml");
|
||||
outfile << conf_yaml_2;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_3.yaml");
|
||||
outfile << conf_yaml_3;
|
||||
outfile.close();
|
||||
|
||||
std::vector<std::string> cmdline_config_options;
|
||||
falco_configuration falco_config;
|
||||
config_loaded_res res;
|
||||
ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options));
|
||||
|
||||
// main + conf_2 + conf_3
|
||||
ASSERT_EQ(res.size(), 3);
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("foo"));
|
||||
std::vector<std::string> foos;
|
||||
auto expected_foos = std::vector<std::string>{"bar", "bar2"};
|
||||
ASSERT_NO_THROW(falco_config.m_config.get_sequence<std::vector<std::string>>(foos, "foo"));
|
||||
ASSERT_EQ(foos.size(), 2); // 2 elements in `foo` sequence because we appended to it
|
||||
for(size_t i = 0; i < foos.size(); ++i) {
|
||||
EXPECT_EQ(foos[i], expected_foos[i])
|
||||
<< "Vectors foo's and expected_foo's differ at index " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 3); // overridden!
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined(
|
||||
"base_value.name")); // no more present since entire `base_value` block was overridden
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value_2.id", 0), 2);
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined
|
||||
|
||||
std::filesystem::remove("main.yaml");
|
||||
std::filesystem::remove("conf_2.yaml");
|
||||
std::filesystem::remove("conf_3.yaml");
|
||||
}
|
||||
|
||||
TEST(Configuration, configuration_config_files_sequence_strategy_override) {
|
||||
const std::string main_conf_yaml = yaml_helper::configs_key +
|
||||
":\n"
|
||||
" - path: conf_2.yaml\n"
|
||||
" strategy: override\n"
|
||||
" - conf_3.yaml\n"
|
||||
"foo: [ bar ]\n"
|
||||
"base_value:\n"
|
||||
" id: 1\n"
|
||||
" name: foo\n";
|
||||
const std::string conf_yaml_2 =
|
||||
"foo: [ bar2 ]\n" // override foo sequence
|
||||
"base_value_2:\n"
|
||||
" id: 2\n";
|
||||
const std::string conf_yaml_3 =
|
||||
"base_value:\n" // override base_value
|
||||
" id: 3\n";
|
||||
|
||||
std::ofstream outfile("main.yaml");
|
||||
outfile << main_conf_yaml;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_2.yaml");
|
||||
outfile << conf_yaml_2;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_3.yaml");
|
||||
outfile << conf_yaml_3;
|
||||
outfile.close();
|
||||
|
||||
std::vector<std::string> cmdline_config_options;
|
||||
falco_configuration falco_config;
|
||||
config_loaded_res res;
|
||||
ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options));
|
||||
|
||||
// main + conf_2 + conf_3
|
||||
ASSERT_EQ(res.size(), 3);
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("foo"));
|
||||
std::vector<std::string> foos;
|
||||
auto expected_foos = std::vector<std::string>{"bar2"};
|
||||
ASSERT_NO_THROW(falco_config.m_config.get_sequence<std::vector<std::string>>(foos, "foo"));
|
||||
ASSERT_EQ(foos.size(), 1); // one element in `foo` sequence because we overrode it
|
||||
for(size_t i = 0; i < foos.size(); ++i) {
|
||||
EXPECT_EQ(foos[i], expected_foos[i])
|
||||
<< "Vectors foo's and expected_foo's differ at index " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 3); // overridden!
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined(
|
||||
"base_value.name")); // no more present since entire `base_value` block was overridden
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value_2.id", 0), 2);
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined
|
||||
|
||||
std::filesystem::remove("main.yaml");
|
||||
std::filesystem::remove("conf_2.yaml");
|
||||
std::filesystem::remove("conf_3.yaml");
|
||||
}
|
||||
|
||||
TEST(Configuration, configuration_config_files_sequence_strategy_addonly) {
|
||||
/* Test that included config files are able to override configs from main file */
|
||||
const std::string main_conf_yaml = yaml_helper::configs_key +
|
||||
":\n"
|
||||
" - path: conf_2.yaml\n"
|
||||
" strategy: add-only\n"
|
||||
" - conf_3.yaml\n"
|
||||
"foo: [ bar ]\n"
|
||||
"base_value:\n"
|
||||
" id: 1\n"
|
||||
" name: foo\n";
|
||||
const std::string conf_yaml_2 =
|
||||
"foo: [ bar2 ]\n" // ignored: add-only strategy
|
||||
"base_value_2:\n"
|
||||
" id: 2\n";
|
||||
const std::string conf_yaml_3 =
|
||||
"base_value:\n" // override base_value
|
||||
" id: 3\n";
|
||||
|
||||
std::ofstream outfile("main.yaml");
|
||||
outfile << main_conf_yaml;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_2.yaml");
|
||||
outfile << conf_yaml_2;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_3.yaml");
|
||||
outfile << conf_yaml_3;
|
||||
outfile.close();
|
||||
|
||||
std::vector<std::string> cmdline_config_options;
|
||||
falco_configuration falco_config;
|
||||
config_loaded_res res;
|
||||
ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options));
|
||||
|
||||
// main + conf_2 + conf_3
|
||||
ASSERT_EQ(res.size(), 3);
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("foo"));
|
||||
std::vector<std::string> foos;
|
||||
auto expected_foos =
|
||||
std::vector<std::string>{"bar"}; // bar2 is ignored because of merge-strategy: add-only
|
||||
ASSERT_NO_THROW(falco_config.m_config.get_sequence<std::vector<std::string>>(foos, "foo"));
|
||||
ASSERT_EQ(foos.size(), 1); // one element in `foo` sequence because we overrode it
|
||||
for(size_t i = 0; i < foos.size(); ++i) {
|
||||
EXPECT_EQ(foos[i], expected_foos[i])
|
||||
<< "Vectors foo's and expected_foo's differ at index " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 3); // overridden!
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined(
|
||||
"base_value.name")); // no more present since entire `base_value` block was overridden
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value_2.id", 0), 2);
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined
|
||||
|
||||
std::filesystem::remove("main.yaml");
|
||||
std::filesystem::remove("conf_2.yaml");
|
||||
std::filesystem::remove("conf_3.yaml");
|
||||
}
|
||||
|
||||
TEST(Configuration, configuration_config_files_sequence_wrong_strategy) {
|
||||
const std::string main_conf_yaml = yaml_helper::configs_key +
|
||||
":\n"
|
||||
" - path: conf_2.yaml\n"
|
||||
" strategy: wrong\n"
|
||||
" - conf_3.yaml\n"
|
||||
"foo: [ bar ]\n"
|
||||
"base_value:\n"
|
||||
" id: 1\n"
|
||||
" name: foo\n";
|
||||
const std::string conf_yaml_2 =
|
||||
"foo: [ bar2 ]\n" // append to foo sequence
|
||||
"base_value_2:\n"
|
||||
" id: 2\n";
|
||||
const std::string conf_yaml_3 =
|
||||
"base_value:\n" // override base_value
|
||||
" id: 3\n";
|
||||
|
||||
std::ofstream outfile("main.yaml");
|
||||
outfile << main_conf_yaml;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_2.yaml");
|
||||
outfile << conf_yaml_2;
|
||||
outfile.close();
|
||||
|
||||
outfile.open("conf_3.yaml");
|
||||
outfile << conf_yaml_3;
|
||||
outfile.close();
|
||||
|
||||
std::vector<std::string> cmdline_config_options;
|
||||
falco_configuration falco_config;
|
||||
config_loaded_res res;
|
||||
ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options));
|
||||
|
||||
// main
|
||||
ASSERT_EQ(res.size(), 3);
|
||||
auto validation = res["main.yaml"];
|
||||
// Since we are using a wrong strategy, the validation should fail
|
||||
// but the enforced strategy should be "append"
|
||||
ASSERT_NE(validation, yaml_helper::validation_ok);
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("foo"));
|
||||
std::vector<std::string> foos;
|
||||
auto expected_foos = std::vector<std::string>{"bar", "bar2"};
|
||||
ASSERT_NO_THROW(falco_config.m_config.get_sequence<std::vector<std::string>>(foos, "foo"));
|
||||
ASSERT_EQ(foos.size(), 2); // 2 elements in `foo` sequence because we appended to it
|
||||
for(size_t i = 0; i < foos.size(); ++i) {
|
||||
EXPECT_EQ(foos[i], expected_foos[i])
|
||||
<< "Vectors foo's and expected_foo's differ at index " << i;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value.id", 0), 3); // overridden!
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined(
|
||||
"base_value.name")); // no more present since entire `base_value` block was overridden
|
||||
ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id"));
|
||||
ASSERT_EQ(falco_config.m_config.get_scalar<int>("base_value_2.id", 0), 2);
|
||||
ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined
|
||||
|
||||
std::filesystem::remove("main.yaml");
|
||||
std::filesystem::remove("conf_2.yaml");
|
||||
std::filesystem::remove("conf_3.yaml");
|
||||
}
|
||||
|
||||
TEST(Configuration, configuration_config_files_unexistent) {
|
||||
/* Test that including an unexistent file just skips it */
|
||||
const std::string main_conf_yaml = yaml_helper::configs_key +
|
||||
|
||||
@@ -36,6 +36,14 @@ if(EMSCRIPTEN)
|
||||
target_compile_options(falco_engine PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0")
|
||||
endif()
|
||||
|
||||
set(ENGINE_LIBRARIES sinsp nlohmann_json::nlohmann_json yaml-cpp)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD)
|
||||
# Used by falco_utils.cpp
|
||||
add_dependencies(falco_engine openssl)
|
||||
list(APPEND ENGINE_LIBRARIES "${OPENSSL_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
target_include_directories(falco_engine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${TBB_INCLUDE_DIR})
|
||||
|
||||
target_link_libraries(falco_engine PUBLIC sinsp nlohmann_json::nlohmann_json yaml-cpp)
|
||||
target_link_libraries(falco_engine PUBLIC ${ENGINE_LIBRARIES})
|
||||
|
||||
@@ -891,17 +891,6 @@ std::shared_ptr<filter_ruleset> falco_engine::ruleset_for_source(std::size_t sou
|
||||
return source->ruleset;
|
||||
}
|
||||
|
||||
void falco_engine::read_file(const std::string &filename, std::string &contents) {
|
||||
std::ifstream is;
|
||||
|
||||
is.open(filename);
|
||||
if(!is.is_open()) {
|
||||
throw falco_exception("Could not open " + filename + " for reading");
|
||||
}
|
||||
|
||||
contents.assign(std::istreambuf_iterator<char>(is), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
static bool check_plugin_requirement_alternatives(
|
||||
const std::vector<falco_engine::plugin_version_requirement> &plugins,
|
||||
const rule_loader::plugin_version_info::requirement_alternatives &alternatives,
|
||||
@@ -986,9 +975,8 @@ void falco_engine::set_sampling_multiplier(double sampling_multiplier) {
|
||||
void falco_engine::add_extra_output_format(const std::string &format,
|
||||
const std::string &source,
|
||||
const std::set<std::string> &tags,
|
||||
const std::string &rule,
|
||||
bool replace_container_info) {
|
||||
m_extra_output_format.push_back({format, source, tags, rule, replace_container_info});
|
||||
const std::string &rule) {
|
||||
m_extra_output_format.push_back({format, source, tags, rule});
|
||||
}
|
||||
|
||||
void falco_engine::add_extra_output_formatted_field(const std::string &key,
|
||||
|
||||
@@ -199,8 +199,7 @@ public:
|
||||
void add_extra_output_format(const std::string &format,
|
||||
const std::string &source,
|
||||
const std::set<std::string> &tags,
|
||||
const std::string &rule,
|
||||
bool replace_container_info);
|
||||
const std::string &rule);
|
||||
|
||||
// You can optionally add fields that will only show up in the object
|
||||
// output (e.g. json, gRPC) alongside other output_fields
|
||||
@@ -379,9 +378,6 @@ private:
|
||||
|
||||
filter_ruleset::engine_state_funcs m_engine_state;
|
||||
|
||||
// Throws falco_exception if the file can not be read
|
||||
void read_file(const std::string &filename, std::string &contents);
|
||||
|
||||
indexed_vector<falco_source> m_sources;
|
||||
|
||||
inline const falco_source *find_source(std::size_t index) {
|
||||
|
||||
@@ -20,7 +20,7 @@ limitations under the License.
|
||||
|
||||
// The version of this Falco engine
|
||||
#define FALCO_ENGINE_VERSION_MAJOR 0
|
||||
#define FALCO_ENGINE_VERSION_MINOR 47
|
||||
#define FALCO_ENGINE_VERSION_MINOR 50
|
||||
#define FALCO_ENGINE_VERSION_PATCH 0
|
||||
|
||||
#define FALCO_ENGINE_VERSION \
|
||||
@@ -36,4 +36,4 @@ limitations under the License.
|
||||
// It represents the fields supported by this version of Falco,
|
||||
// the event types, and the underlying driverevent schema. It's used to
|
||||
// detetect changes in engine version in our CI jobs.
|
||||
#define FALCO_ENGINE_CHECKSUM "1478da9cefb00623e5158e6582ff5322a2118f804c61ddb65f57079353257611"
|
||||
#define FALCO_ENGINE_CHECKSUM "c111251b08cfb00790515cd62fbe0b6c3d0b62035f7d9bbb1aea80f41d7986f9"
|
||||
|
||||
@@ -24,11 +24,13 @@ falco_formats::falco_formats(std::shared_ptr<const falco_engine> engine,
|
||||
bool json_include_output_property,
|
||||
bool json_include_tags_property,
|
||||
bool json_include_message_property,
|
||||
bool json_include_output_fields_property,
|
||||
bool time_format_iso_8601):
|
||||
m_falco_engine(engine),
|
||||
m_json_include_output_property(json_include_output_property),
|
||||
m_json_include_tags_property(json_include_tags_property),
|
||||
m_json_include_message_property(json_include_message_property),
|
||||
m_json_include_output_fields_property(json_include_output_fields_property),
|
||||
m_time_format_iso_8601(time_format_iso_8601) {}
|
||||
|
||||
falco_formats::~falco_formats() {}
|
||||
@@ -79,7 +81,9 @@ std::string falco_formats::format_event(sinsp_evt *evt,
|
||||
std::string json_fields_prefix;
|
||||
|
||||
// Resolve message fields
|
||||
message_formatter->tostring(evt, json_fields_message);
|
||||
if(m_json_include_output_fields_property) {
|
||||
message_formatter->tostring(evt, json_fields_message);
|
||||
}
|
||||
// Resolve prefix (e.g. time) fields
|
||||
prefix_formatter->tostring(evt, json_fields_prefix);
|
||||
|
||||
@@ -118,36 +122,38 @@ std::string falco_formats::format_event(sinsp_evt *evt,
|
||||
event["message"] = message;
|
||||
}
|
||||
|
||||
event["output_fields"] = nlohmann::json::parse(json_fields_message);
|
||||
if(m_json_include_output_fields_property) {
|
||||
event["output_fields"] = nlohmann::json::parse(json_fields_message);
|
||||
|
||||
auto prefix_fields = nlohmann::json::parse(json_fields_prefix);
|
||||
if(prefix_fields.is_object()) {
|
||||
for(auto const &el : prefix_fields.items()) {
|
||||
event["output_fields"][el.key()] = el.value();
|
||||
}
|
||||
}
|
||||
|
||||
for(auto const &ef : extra_fields) {
|
||||
std::string fformat = ef.second.first;
|
||||
if(fformat.size() == 0) {
|
||||
continue;
|
||||
auto prefix_fields = nlohmann::json::parse(json_fields_prefix);
|
||||
if(prefix_fields.is_object()) {
|
||||
for(auto const &el : prefix_fields.items()) {
|
||||
event["output_fields"][el.key()] = el.value();
|
||||
}
|
||||
}
|
||||
|
||||
if(!(fformat[0] == '*')) {
|
||||
fformat = "*" + fformat;
|
||||
}
|
||||
for(auto const &ef : extra_fields) {
|
||||
std::string fformat = ef.second.first;
|
||||
if(fformat.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ef.second.second) // raw field
|
||||
{
|
||||
std::string json_field_map;
|
||||
auto field_formatter = m_falco_engine->create_formatter(source, fformat);
|
||||
field_formatter->tostring_withformat(evt,
|
||||
json_field_map,
|
||||
sinsp_evt_formatter::OF_JSON);
|
||||
auto json_obj = nlohmann::json::parse(json_field_map);
|
||||
event["output_fields"][ef.first] = json_obj[ef.first];
|
||||
} else {
|
||||
event["output_fields"][ef.first] = format_string(evt, fformat, source);
|
||||
if(!(fformat[0] == '*')) {
|
||||
fformat = "*" + fformat;
|
||||
}
|
||||
|
||||
if(ef.second.second) // raw field
|
||||
{
|
||||
std::string json_field_map;
|
||||
auto field_formatter = m_falco_engine->create_formatter(source, fformat);
|
||||
field_formatter->tostring_withformat(evt,
|
||||
json_field_map,
|
||||
sinsp_evt_formatter::OF_JSON);
|
||||
auto json_obj = nlohmann::json::parse(json_field_map);
|
||||
event["output_fields"][ef.first] = json_obj[ef.first];
|
||||
} else {
|
||||
event["output_fields"][ef.first] = format_string(evt, fformat, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
bool json_include_output_property,
|
||||
bool json_include_tags_property,
|
||||
bool json_include_message_property,
|
||||
bool json_include_output_fields_property,
|
||||
bool time_format_iso_8601);
|
||||
virtual ~falco_formats();
|
||||
|
||||
@@ -52,5 +53,6 @@ protected:
|
||||
bool m_json_include_output_property;
|
||||
bool m_json_include_tags_property;
|
||||
bool m_json_include_message_property;
|
||||
bool m_json_include_output_fields_property;
|
||||
bool m_time_format_iso_8601;
|
||||
};
|
||||
|
||||
@@ -119,8 +119,7 @@ const char rule_schema_string[] = LONG_STRING_CONST(
|
||||
"values": {}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"values"
|
||||
"name"
|
||||
],
|
||||
"title": "Exception"
|
||||
},
|
||||
|
||||
@@ -272,7 +272,6 @@ struct extra_output_format_conf {
|
||||
std::string m_source;
|
||||
std::set<std::string> m_tags;
|
||||
std::string m_rule;
|
||||
bool m_replace_container_info;
|
||||
};
|
||||
|
||||
struct extra_output_field_conf {
|
||||
|
||||
@@ -434,18 +434,16 @@ void rule_loader::compiler::compile_rule_infos(configuration& cfg,
|
||||
continue;
|
||||
}
|
||||
|
||||
if(extra.m_replace_container_info) {
|
||||
if(rule.output.find(s_container_info_fmt) != std::string::npos) {
|
||||
rule.output = replace(rule.output, s_container_info_fmt, extra.m_format);
|
||||
} else {
|
||||
rule.output = rule.output + " " + extra.m_format;
|
||||
}
|
||||
} else {
|
||||
rule.output = rule.output + " " + extra.m_format;
|
||||
}
|
||||
rule.output = rule.output + " " + extra.m_format;
|
||||
}
|
||||
|
||||
if(rule.output.find(s_container_info_fmt) != std::string::npos) {
|
||||
cfg.res->add_warning(falco::load_result::load_result::LOAD_DEPRECATED_ITEM,
|
||||
"%container.info is deprecated and no more useful, and will be "
|
||||
"dropped by Falco 1.0.0. "
|
||||
"The container plugin will automatically add required fields to "
|
||||
"the output message.",
|
||||
r.ctx);
|
||||
rule.output = replace(rule.output, s_container_info_fmt, s_default_extra_fmt);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,33 @@ public:
|
||||
inline static const std::string validation_failed = "failed";
|
||||
inline static const std::string validation_none = "none";
|
||||
|
||||
enum config_files_strategy {
|
||||
STRATEGY_APPEND, // append to existing sequence keys, override scalar keys and add new ones
|
||||
STRATEGY_OVERRIDE, // override existing keys (sequences too) and add new ones
|
||||
STRATEGY_ADDONLY // only add new keys and ignore existing ones
|
||||
};
|
||||
|
||||
static enum config_files_strategy strategy_from_string(const std::string& strategy) {
|
||||
if(strategy == "override") {
|
||||
return yaml_helper::STRATEGY_OVERRIDE;
|
||||
}
|
||||
if(strategy == "add-only") {
|
||||
return yaml_helper::STRATEGY_ADDONLY;
|
||||
}
|
||||
return yaml_helper::STRATEGY_APPEND;
|
||||
}
|
||||
|
||||
static std::string strategy_to_string(const enum config_files_strategy strategy) {
|
||||
switch(strategy) {
|
||||
case yaml_helper::STRATEGY_OVERRIDE:
|
||||
return "override";
|
||||
case yaml_helper::STRATEGY_ADDONLY:
|
||||
return "add-only";
|
||||
default:
|
||||
return "append";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all the YAML document represented by the input string.
|
||||
* Since this is used by rule loader, does not process env vars.
|
||||
@@ -137,6 +164,7 @@ public:
|
||||
}
|
||||
|
||||
void include_config_file(const std::string& include_file_path,
|
||||
enum config_files_strategy strategy = STRATEGY_APPEND,
|
||||
const nlohmann::json& schema = {},
|
||||
std::vector<std::string>* schema_warnings = nullptr) {
|
||||
auto loaded_nodes = load_from_file_int(include_file_path, schema, schema_warnings);
|
||||
@@ -152,10 +180,24 @@ public:
|
||||
"' directive in included config file " +
|
||||
include_file_path + ".");
|
||||
}
|
||||
// We allow to override keys.
|
||||
// We don't need to use `get_node()` here,
|
||||
// since key is a top-level one.
|
||||
m_root[key] = n.second;
|
||||
switch(strategy) {
|
||||
case STRATEGY_APPEND:
|
||||
if(n.second.IsSequence()) {
|
||||
for(const auto& item : n.second) {
|
||||
m_root[key].push_back(item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// fallthrough
|
||||
case STRATEGY_OVERRIDE:
|
||||
m_root[key] = n.second;
|
||||
break;
|
||||
case STRATEGY_ADDONLY:
|
||||
if(!m_root[key].IsDefined()) {
|
||||
m_root[key] = n.second;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ set(FALCO_INCLUDE_DIRECTORIES
|
||||
)
|
||||
|
||||
set(FALCO_DEPENDENCIES cxxopts)
|
||||
set(FALCO_LIBRARIES falco_engine sinsp yaml-cpp)
|
||||
set(FALCO_LIBRARIES falco_engine)
|
||||
|
||||
if(USE_JEMALLOC)
|
||||
list(APPEND FALCO_DEPENDENCIES jemalloc)
|
||||
@@ -127,7 +127,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD)
|
||||
"${GRPC_LIBRARIES}"
|
||||
"${PROTOBUF_LIB}"
|
||||
"${CARES_LIB}"
|
||||
"${OPENSSL_LIBRARIES}"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -52,8 +52,7 @@ static void add_suggested_output(const falco::app::state& s,
|
||||
s.engine->add_extra_output_format(format_suggested_field(fldinfo),
|
||||
src,
|
||||
eo.m_tags,
|
||||
eo.m_rule,
|
||||
false);
|
||||
eo.m_rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,11 +61,7 @@ static void add_suggested_output(const falco::app::state& s,
|
||||
void configure_output_format(falco::app::state& s) {
|
||||
for(auto& eo : s.config->m_append_output) {
|
||||
if(eo.m_format != "") {
|
||||
s.engine->add_extra_output_format(eo.m_format,
|
||||
eo.m_source,
|
||||
eo.m_tags,
|
||||
eo.m_rule,
|
||||
false);
|
||||
s.engine->add_extra_output_format(eo.m_format, eo.m_source, eo.m_tags, eo.m_rule);
|
||||
}
|
||||
|
||||
// Add suggested filtercheck formats to each source output
|
||||
@@ -94,40 +89,25 @@ void configure_output_format(falco::app::state& s) {
|
||||
}
|
||||
|
||||
// See https://falco.org/docs/rules/style-guide/
|
||||
const std::string container_info =
|
||||
"container_image=%container.image.repository "
|
||||
"container_image_tag=%container.image.tag";
|
||||
const std::string k8s_info = "k8s_ns=%k8s.ns.name k8s_pod_name=%k8s.pod.name";
|
||||
const std::string gvisor_info = "vpid=%proc.vpid vtid=%thread.vtid";
|
||||
|
||||
if(s.options.print_additional == "c" || s.options.print_additional == "container") {
|
||||
s.engine->add_extra_output_format(container_info,
|
||||
falco_common::syscall_source,
|
||||
{},
|
||||
"",
|
||||
true);
|
||||
} else if(s.options.print_additional == "cg" ||
|
||||
s.options.print_additional == "container-gvisor") {
|
||||
s.engine->add_extra_output_format(gvisor_info + " " + container_info,
|
||||
falco_common::syscall_source,
|
||||
{},
|
||||
"",
|
||||
true);
|
||||
} else if(s.options.print_additional == "k" || s.options.print_additional == "kubernetes") {
|
||||
s.engine->add_extra_output_format(container_info + " " + k8s_info,
|
||||
falco_common::syscall_source,
|
||||
{},
|
||||
"",
|
||||
true);
|
||||
} else if(s.options.print_additional == "kg" ||
|
||||
s.options.print_additional == "kubernetes-gvisor") {
|
||||
s.engine->add_extra_output_format(gvisor_info + " " + container_info + " " + k8s_info,
|
||||
falco_common::syscall_source,
|
||||
{},
|
||||
"",
|
||||
true);
|
||||
} else if(!s.options.print_additional.empty()) {
|
||||
s.engine->add_extra_output_format(s.options.print_additional, "", {}, "", false);
|
||||
if(!s.options.print_additional.empty()) {
|
||||
falco_logger::log(falco_logger::level::WARNING,
|
||||
"The -p/--print option is deprecated and will be removed. Use -o "
|
||||
"append_output=... instead.\n");
|
||||
|
||||
if(s.options.print_additional == "c" || s.options.print_additional == "container" ||
|
||||
s.options.print_additional == "k" || s.options.print_additional == "kubernetes") {
|
||||
// Don't do anything, we don't need these anymore
|
||||
// since container plugin takes care of suggesting the output format fields itself.
|
||||
} else if(s.options.print_additional == "cg" ||
|
||||
s.options.print_additional == "container-gvisor" ||
|
||||
s.options.print_additional == "kg" ||
|
||||
s.options.print_additional == "kubernetes-gvisor") {
|
||||
s.engine->add_extra_output_format(gvisor_info, falco_common::syscall_source, {}, "");
|
||||
} else {
|
||||
s.engine->add_extra_output_format(s.options.print_additional, "", {}, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ falco::app::run_result falco::app::actions::init_outputs(falco::app::state& s) {
|
||||
s.config->m_json_include_output_property,
|
||||
s.config->m_json_include_tags_property,
|
||||
s.config->m_json_include_message_property,
|
||||
s.config->m_json_include_output_fields_property,
|
||||
s.config->m_output_timeout,
|
||||
s.config->m_buffered_outputs,
|
||||
s.config->m_outputs_queue_capacity,
|
||||
|
||||
@@ -66,6 +66,10 @@ falco::app::run_result falco::app::actions::load_config(const falco::app::state&
|
||||
}
|
||||
}
|
||||
|
||||
s.config->m_falco_reload_ts = (int64_t)std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
|
||||
s.config->m_buffered_outputs = !s.options.unbuffered_outputs;
|
||||
|
||||
return apply_deprecated_options(s);
|
||||
|
||||
@@ -485,6 +485,10 @@ falco::app::run_result falco::app::actions::process_events(falco::app::state& s)
|
||||
}
|
||||
|
||||
if(s.enabled_sources.size() == 1) {
|
||||
if(s.on_inspectors_opened != nullptr) {
|
||||
s.on_inspectors_opened();
|
||||
}
|
||||
|
||||
// optimization: with only one source we don't spawn additional threads
|
||||
process_inspector_events(s,
|
||||
src_info->inspector,
|
||||
@@ -514,6 +518,9 @@ falco::app::run_result falco::app::actions::process_events(falco::app::state& s)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(s.enabled_sources.size() > 1 && s.on_inspectors_opened != nullptr) {
|
||||
s.on_inspectors_opened();
|
||||
}
|
||||
|
||||
// wait for event processing to terminate for all sources
|
||||
// if a thread terminates with an error, we trigger the app termination
|
||||
|
||||
@@ -44,6 +44,7 @@ falco::app::run_result falco::app::actions::start_webserver(falco::app::state& s
|
||||
std::to_string(webserver_config.m_listen_port) + ssl_option + "\n");
|
||||
|
||||
state.webserver.start(state, webserver_config);
|
||||
state.on_inspectors_opened = [&state]() { state.webserver.enable_prometheus_metrics(state); };
|
||||
#endif
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -117,9 +117,9 @@ void options::define(cxxopts::Options& opts)
|
||||
("N", "Only print field names when used in conjunction with the --list option. It has no effect when used with other options.", cxxopts::value(names_only)->default_value("false"))
|
||||
("o,option", "Set the value of option <opt> to <val>. Overrides values in the configuration file. <opt> can be identified using its location in the configuration file using dot notation. Elements of list entries can be accessed via square brackets [].\n E.g. base.id = val\n base.subvalue.subvalue2 = val\n base.list[1]=val", cxxopts::value(cmdline_config_options), "<opt>=<val>")
|
||||
("plugin-info", "Print info for the plugin specified by <plugin_name> and exit.\nThis includes all descriptive information like name and author, along with the\nschema format for the init configuration and a list of suggested open parameters.\n<plugin_name> can be the plugin's name or its configured 'library_path'.", cxxopts::value(print_plugin_info), "<plugin_name>")
|
||||
("p,print", "Print (or replace) additional information in the rule's output.\nUse -pc or -pcontainer to append container details to syscall events.\nUse -pk or -pkubernetes to add both container and Kubernetes details to syscall events.\nIf using gVisor, choose -pcg or -pkg variants (or -pcontainer-gvisor and -pkubernetes-gvisor, respectively).\nIf a syscall rule's output contains %container.info, it will be replaced with the corresponding details. Otherwise, these details will be directly appended to the rule's output.\nAlternatively, use -p <output_format> for a custom format. In this case, the given <output_format> will be appended to the rule's output without any replacement to all events, including plugin events.", cxxopts::value(print_additional), "<output_format>")
|
||||
("p,print", "DEPRECATED: use -o append_output... instead. Print additional information in the rule's output.\nUse -pc or -pcontainer to append container details to syscall events.\nUse -pk or -pkubernetes to add both container and Kubernetes details to syscall events.\nIf using gVisor, choose -pcg or -pkg variants (or -pcontainer-gvisor and -pkubernetes-gvisor, respectively).\nThe details will be directly appended to the rule's output.\nAlternatively, use -p <output_format> for a custom format. In this case, the given <output_format> will be appended to the rule's output without any replacement to all events, including plugin events.", cxxopts::value(print_additional), "<output_format>")
|
||||
("P,pidfile", "Write PID to specified <pid_file> path. By default, no PID file is created.", cxxopts::value(pidfilename)->default_value(""), "<pid_file>")
|
||||
("r", "Rules file or directory to be loaded. This option can be passed multiple times. Falco defaults to the values in the configuration file when this option is not specified.", cxxopts::value<std::vector<std::string>>(), "<rules_file>")
|
||||
("r", "Rules file or directory to be loaded. This option can be passed multiple times. Falco defaults to the values in the configuration file when this option is not specified. Only files with .yml or .yaml extension are considered.", cxxopts::value<std::vector<std::string>>(), "<rules_file>")
|
||||
("support", "Print support information, including version, rules files used, loaded configuration, etc., and exit. The output is in JSON format.", cxxopts::value(print_support)->default_value("false"))
|
||||
("U,unbuffered", "Turn off output buffering for configured outputs. This causes every single line emitted by Falco to be flushed, which generates higher CPU usage but is useful when piping those outputs into another process or a script.", cxxopts::value(unbuffered_outputs)->default_value("false"))
|
||||
("V,validate", "Read the contents of the specified <rules_file> file(s), validate the loaded rules, and exit. This option can be passed multiple times to validate multiple files.", cxxopts::value(validate_rules_filenames), "<rules_file>")
|
||||
|
||||
@@ -116,6 +116,9 @@ struct state {
|
||||
|
||||
falco_webserver webserver;
|
||||
#endif
|
||||
// Set by start_webserver to start prometheus metrics
|
||||
// once all inspectors are opened.
|
||||
std::function<void()> on_inspectors_opened = nullptr;
|
||||
|
||||
inline bool is_capture_mode() const { return config->m_engine_mode == engine_kind_t::REPLAY; }
|
||||
|
||||
|
||||
@@ -38,7 +38,30 @@ const char config_schema_string[] = LONG_STRING_CONST(
|
||||
"config_files": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"strategy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"append",
|
||||
"override",
|
||||
"add-only"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"watch_config_files": {
|
||||
@@ -98,6 +121,9 @@ const char config_schema_string[] = LONG_STRING_CONST(
|
||||
"json_include_message_property": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"json_include_output_fields_property": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"json_include_tags_property": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -514,6 +540,9 @@ const char config_schema_string[] = LONG_STRING_CONST(
|
||||
},
|
||||
"keep_alive": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"max_consecutive_timeouts": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"minProperties": 1,
|
||||
|
||||
@@ -69,6 +69,7 @@ falco_configuration::falco_configuration():
|
||||
m_json_include_output_property(true),
|
||||
m_json_include_tags_property(true),
|
||||
m_json_include_message_property(false),
|
||||
m_json_include_output_fields_property(true),
|
||||
m_rule_matching(falco_common::rule_matching::FIRST),
|
||||
m_watch_config_files(true),
|
||||
m_buffered_outputs(false),
|
||||
@@ -161,10 +162,12 @@ void falco_configuration::merge_config_files(const std::string &config_name,
|
||||
m_loaded_configs_filenames.push_back(config_name);
|
||||
const auto ppath = std::filesystem::path(config_name);
|
||||
// Parse files to be included
|
||||
std::vector<std::string> include_files;
|
||||
m_config.get_sequence<std::vector<std::string>>(include_files, yaml_helper::configs_key);
|
||||
for(const std::string &include_file : include_files) {
|
||||
auto include_file_path = std::filesystem::path(include_file);
|
||||
std::list<falco_configuration::config_files_config> include_files;
|
||||
m_config.get_sequence<std::list<falco_configuration::config_files_config>>(
|
||||
include_files,
|
||||
yaml_helper::configs_key);
|
||||
for(const auto &include_file : include_files) {
|
||||
auto include_file_path = std::filesystem::path(include_file.m_path);
|
||||
if(include_file_path == ppath) {
|
||||
throw std::logic_error("Config error: '" + yaml_helper::configs_key +
|
||||
"' directive tried to recursively include main config file: " +
|
||||
@@ -175,14 +178,15 @@ void falco_configuration::merge_config_files(const std::string &config_name,
|
||||
continue;
|
||||
}
|
||||
if(std::filesystem::is_regular_file(include_file_path)) {
|
||||
m_loaded_configs_filenames.push_back(include_file);
|
||||
m_config.include_config_file(include_file_path.string(),
|
||||
m_loaded_configs_filenames.push_back(include_file.m_path);
|
||||
m_config.include_config_file(include_file.m_path,
|
||||
include_file.m_strategy,
|
||||
m_config_schema,
|
||||
&validation_status);
|
||||
// Only report top most schema validation status
|
||||
res[include_file_path.string()] = validation_status[0];
|
||||
res[include_file.m_path] = validation_status[0];
|
||||
} else if(std::filesystem::is_directory(include_file_path)) {
|
||||
m_loaded_configs_folders.push_back(include_file);
|
||||
m_loaded_configs_folders.push_back(include_file.m_path);
|
||||
std::vector<std::string> v;
|
||||
const auto it_options = std::filesystem::directory_options::follow_directory_symlink |
|
||||
std::filesystem::directory_options::skip_permission_denied;
|
||||
@@ -194,7 +198,10 @@ void falco_configuration::merge_config_files(const std::string &config_name,
|
||||
}
|
||||
std::sort(v.begin(), v.end());
|
||||
for(const auto &f : v) {
|
||||
m_config.include_config_file(f, m_config_schema, &validation_status);
|
||||
m_config.include_config_file(f,
|
||||
include_file.m_strategy,
|
||||
m_config_schema,
|
||||
&validation_status);
|
||||
// Only report top most schema validation status
|
||||
res[f] = validation_status[0];
|
||||
}
|
||||
@@ -338,6 +345,8 @@ void falco_configuration::load_yaml(const std::string &config_name) {
|
||||
m_json_include_tags_property = m_config.get_scalar<bool>("json_include_tags_property", true);
|
||||
m_json_include_message_property =
|
||||
m_config.get_scalar<bool>("json_include_message_property", false);
|
||||
m_json_include_output_fields_property =
|
||||
m_config.get_scalar<bool>("json_include_output_fields_property", true);
|
||||
|
||||
m_outputs.clear();
|
||||
falco::outputs::config file_output;
|
||||
@@ -447,6 +456,11 @@ void falco_configuration::load_yaml(const std::string &config_name) {
|
||||
keep_alive = m_config.get_scalar<bool>("http_output.keep_alive", false);
|
||||
http_output.options["keep_alive"] = keep_alive ? std::string("true") : std::string("false");
|
||||
|
||||
uint8_t max_consecutive_timeouts;
|
||||
max_consecutive_timeouts =
|
||||
m_config.get_scalar<uint8_t>("http_output.max_consecutive_timeouts", 5);
|
||||
http_output.options["max_consecutive_timeouts"] = std::to_string(max_consecutive_timeouts);
|
||||
|
||||
m_outputs.push_back(http_output);
|
||||
}
|
||||
|
||||
@@ -720,13 +734,21 @@ void falco_configuration::read_rules_file_directory(const std::string &path,
|
||||
std::sort(dir_filenames.begin(), dir_filenames.end());
|
||||
|
||||
for(std::string &ent : dir_filenames) {
|
||||
rules_filenames.push_back(ent);
|
||||
// only consider yaml files
|
||||
if(falco::utils::matches_wildcard("*.yaml", ent) ||
|
||||
falco::utils::matches_wildcard("*.yml", ent)) {
|
||||
rules_filenames.push_back(ent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Assume it's a file and just add to
|
||||
// rules_filenames. If it can't be opened/etc that
|
||||
// will be reported later..
|
||||
rules_filenames.push_back(path);
|
||||
// also, only consider yaml files
|
||||
if(falco::utils::matches_wildcard("*.yaml", path) ||
|
||||
falco::utils::matches_wildcard("*.yml", path)) {
|
||||
rules_filenames.push_back(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,11 @@ public:
|
||||
std::string m_open_params;
|
||||
};
|
||||
|
||||
struct config_files_config {
|
||||
std::string m_path;
|
||||
yaml_helper::config_files_strategy m_strategy;
|
||||
};
|
||||
|
||||
struct kmod_config {
|
||||
int16_t m_buf_size_preset;
|
||||
bool m_drop_failed_exit;
|
||||
@@ -147,6 +152,7 @@ public:
|
||||
bool m_json_include_output_property;
|
||||
bool m_json_include_tags_property;
|
||||
bool m_json_include_message_property;
|
||||
bool m_json_include_output_fields_property;
|
||||
std::string m_log_level;
|
||||
std::vector<falco::outputs::config> m_outputs;
|
||||
|
||||
@@ -208,7 +214,15 @@ public:
|
||||
gvisor_config m_gvisor = {};
|
||||
|
||||
yaml_helper m_config;
|
||||
|
||||
//
|
||||
// Runtime-Generated values (not user-configurable)
|
||||
//
|
||||
|
||||
// JSON schema generated from a hardcoded string
|
||||
nlohmann::json m_config_schema;
|
||||
// Timestamp of most recent configuration reload
|
||||
int64_t m_falco_reload_ts{0};
|
||||
|
||||
private:
|
||||
void merge_config_files(const std::string& config_name, config_loaded_res& res);
|
||||
@@ -424,4 +438,39 @@ struct convert<falco_configuration::plugin_config> {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct convert<falco_configuration::config_files_config> {
|
||||
static Node encode(const falco_configuration::config_files_config& rhs) {
|
||||
Node node;
|
||||
node["path"] = rhs.m_path;
|
||||
node["strategy"] = yaml_helper::strategy_to_string(rhs.m_strategy);
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, falco_configuration::config_files_config& rhs) {
|
||||
if(!node.IsMap()) {
|
||||
// Single string mode defaults to append strategy
|
||||
rhs.m_path = node.as<std::string>();
|
||||
rhs.m_strategy = yaml_helper::STRATEGY_APPEND;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Path is required
|
||||
if(!node["path"]) {
|
||||
return false;
|
||||
}
|
||||
rhs.m_path = node["path"].as<std::string>();
|
||||
|
||||
// Strategy is not required
|
||||
if(!node["strategy"]) {
|
||||
rhs.m_strategy = yaml_helper::STRATEGY_APPEND;
|
||||
} else {
|
||||
std::string strategy = node["strategy"].as<std::string>();
|
||||
rhs.m_strategy = yaml_helper::strategy_from_string(strategy);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace YAML
|
||||
|
||||
@@ -31,8 +31,36 @@ namespace fs = std::filesystem;
|
||||
|
||||
/*!
|
||||
\class falco_metrics
|
||||
\brief This class is used to convert the metrics provided by the application
|
||||
and falco libs into a string to be return by the metrics endpoint.
|
||||
\brief Converts metrics provided by the application and Falco libraries into a formatted string
|
||||
for the metrics endpoint.
|
||||
|
||||
## Metrics Overview
|
||||
This section explains why looping over inspectors is necessary.
|
||||
Falco utilizes multiple inspectors when loading plugins with an event source.
|
||||
Most metrics should only be retrieved once, ideally by the syscalls inspector if applicable.
|
||||
To maximize metrics retrieval and prevent duplicate data, the syscalls inspector is always
|
||||
positioned at index 0 in the loop when it exists.
|
||||
|
||||
Wrapper fields: See https://falco.org/docs/concepts/metrics/
|
||||
- `engine_name` and `event_source` are pushed for each inspector.
|
||||
- All other wrapper fields are agnostic and should be retrieved once.
|
||||
|
||||
## Metrics Collection Behavior
|
||||
- `rules_counters_enabled` -> Agnostic; resides in falco; retrieved from the state, not an
|
||||
inspector; only performed once.
|
||||
- `resource_utilization_enabled` -> Agnostic; resides in libs; inspector is irrelevant;
|
||||
only performed once.
|
||||
- `state_counters_enabled` -> Semi-agnostic; resides in libs; must be retrieved by the syscalls
|
||||
inspector if applicable.
|
||||
- `kernel_event_counters_enabled` -> Resides in libs; must be retrieved by the syscalls
|
||||
inspector; not available for other inspectors.
|
||||
- `kernel_event_counters_per_cpu_enabled` -> Resides in libs; must be retrieved by the syscalls
|
||||
inspector; not available for other inspectors.
|
||||
- `libbpf_stats_enabled` -> Resides in libs; must be retrieved by the syscalls inspector;
|
||||
not available for other inspectors.
|
||||
- `plugins_metrics_enabled` -> Must be retrieved for each inspector.
|
||||
- `jemalloc_stats_enabled` -> Agnostic; resides in falco; inspector is irrelevant;
|
||||
only performed once.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@@ -42,108 +70,354 @@ namespace fs = std::filesystem;
|
||||
|
||||
https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format
|
||||
*/
|
||||
const std::string falco_metrics::content_type = "text/plain; version=0.0.4";
|
||||
const std::string falco_metrics::content_type_prometheus = "text/plain; version=0.0.4";
|
||||
|
||||
/*!
|
||||
\brief this method takes an application \c state and returns a textual representation of
|
||||
its configured metrics.
|
||||
std::string falco_metrics::falco_to_text_prometheus(
|
||||
const falco::app::state& state,
|
||||
libs::metrics::prometheus_metrics_converter& prometheus_metrics_converter,
|
||||
std::vector<metrics_v2>& additional_wrapper_metrics) {
|
||||
std::string prometheus_text;
|
||||
|
||||
The current implementation returns a Prometheus exposition formatted string.
|
||||
*/
|
||||
std::string falco_metrics::to_text(const falco::app::state& state) {
|
||||
// # HELP falcosecurity_falco_version_info https://falco.org/docs/metrics/
|
||||
// # TYPE falcosecurity_falco_version_info gauge
|
||||
// falcosecurity_falco_version_info{version="0.41.0-100+334ca42"} 1
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"version",
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
{{"version", FALCO_VERSION}});
|
||||
|
||||
#if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__)
|
||||
// Note that the rule counter metrics are retrieved from the state, not from any inspector
|
||||
// Distinguish between config and rules files using labels, following Prometheus best
|
||||
// practices: https://prometheus.io/docs/practices/naming/#labels
|
||||
|
||||
// # HELP falcosecurity_falco_sha256_rules_files_info https://falco.org/docs/metrics/
|
||||
// # TYPE falcosecurity_falco_sha256_rules_files_info gauge
|
||||
// falcosecurity_falco_sha256_rules_files_info{file_name="falco_rules.yaml",sha256="6f0078862a26528cb50a860f9ebebbfbe3162e5009187089c73cb0cdf91d0b06"}
|
||||
// 1
|
||||
for(const auto& item : state.config.get()->m_loaded_rules_filenames_sha256sum) {
|
||||
fs::path fs_path = item.first;
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"sha256_rules_files",
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
{{"file_name", fs_path.filename()}, {"sha256", item.second}});
|
||||
}
|
||||
|
||||
// # HELP falcosecurity_falco_sha256_config_files_info https://falco.org/docs/metrics/
|
||||
// # TYPE falcosecurity_falco_sha256_config_files_info gauge
|
||||
// falcosecurity_falco_sha256_config_files_info{file_name="falco.yaml",sha256="f97de5fa6f513b5e07cd9f29ee9904ee4267cb120ef6501f8555543d5a98dd1c"}
|
||||
// 1
|
||||
for(const auto& item : state.config.get()->m_loaded_configs_filenames_sha256sum) {
|
||||
fs::path fs_path = item.first;
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"sha256_config_files",
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
{{"file_name", fs_path.filename()}, {"sha256", item.second}});
|
||||
}
|
||||
|
||||
#endif
|
||||
// # HELP falcosecurity_falco_outputs_queue_num_drops_total https://falco.org/docs/metrics/
|
||||
// # TYPE falcosecurity_falco_outputs_queue_num_drops_total counter
|
||||
// falcosecurity_falco_outputs_queue_num_drops_total 0
|
||||
additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric(
|
||||
"outputs_queue_num_drops",
|
||||
METRICS_V2_MISC,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
state.outputs->get_outputs_queue_num_drops()));
|
||||
|
||||
// # HELP falcosecurity_falco_reload_timestamp_nanoseconds https://falco.org/docs/metrics/
|
||||
// # TYPE falcosecurity_falco_reload_timestamp_nanoseconds gauge
|
||||
// falcosecurity_falco_reload_timestamp_nanoseconds 1748338536592811359
|
||||
additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric(
|
||||
"reload_ts",
|
||||
METRICS_V2_MISC,
|
||||
METRIC_VALUE_TYPE_S64,
|
||||
METRIC_VALUE_UNIT_TIME_TIMESTAMP_NS,
|
||||
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
|
||||
state.config->m_falco_reload_ts));
|
||||
|
||||
if(state.config->m_metrics_flags & METRICS_V2_RULE_COUNTERS) {
|
||||
// rules_counters_enabled
|
||||
const stats_manager& rule_stats_manager = state.engine->get_rule_stats_manager();
|
||||
const indexed_vector<falco_rule>& rules = state.engine->get_rules();
|
||||
const std::vector<std::unique_ptr<std::atomic<uint64_t>>>& rules_by_id =
|
||||
rule_stats_manager.get_by_rule_id();
|
||||
// Distinguish between rules counters using labels, following Prometheus best
|
||||
// practices: https://prometheus.io/docs/practices/naming/#labels
|
||||
for(size_t i = 0; i < rules_by_id.size(); i++) {
|
||||
auto rule = rules.at(i);
|
||||
auto count = rules_by_id[i]->load();
|
||||
if(count > 0) {
|
||||
// # HELP falcosecurity_falco_rules_matches_total https://falco.org/docs/metrics/
|
||||
// # TYPE falcosecurity_falco_rules_matches_total counter
|
||||
// falcosecurity_falco_rules_matches_total{priority="4",rule_name="Read sensitive
|
||||
// file
|
||||
// untrusted",source="syscall",tag_T1555="true",tag_container="true",tag_filesystem="true",tag_host="true",tag_maturity_stable="true",tag_mitre_credential_access="true"}
|
||||
// 32 # HELP falcosecurity_falco_rules_matches_total https://falco.org/docs/metrics/
|
||||
// # TYPE falcosecurity_falco_rules_matches_total counter
|
||||
// falcosecurity_falco_rules_matches_total{priority="5",rule_name="Terminal shell in
|
||||
// container",source="syscall",tag_T1059="true",tag_container="true",tag_maturity_stable="true",tag_mitre_execution="true",tag_shell="true"}
|
||||
// 1
|
||||
auto metric = libs::metrics::libsinsp_metrics::new_metric(
|
||||
"rules_matches",
|
||||
METRICS_V2_RULE_COUNTERS,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
count);
|
||||
prometheus_metrics_converter.convert_metric_to_unit_convention(metric);
|
||||
std::map<std::string, std::string> const_labels = {
|
||||
{"rule_name", rule->name},
|
||||
{"priority", std::to_string(rule->priority)},
|
||||
{"source", rule->source},
|
||||
};
|
||||
std::for_each(rule->tags.cbegin(),
|
||||
rule->tags.cend(),
|
||||
[&const_labels](std::string const& tag) {
|
||||
const_labels.emplace(std::string{"tag_"} + tag, "true");
|
||||
});
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric,
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
const_labels);
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef HAS_JEMALLOC
|
||||
if(state.config->m_metrics_flags & METRICS_V2_JEMALLOC_STATS) {
|
||||
// jemalloc_stats_enabled
|
||||
nlohmann::json j;
|
||||
malloc_stats_print(
|
||||
[](void* to, const char* from) {
|
||||
nlohmann::json* j = (nlohmann::json*)to;
|
||||
*j = nlohmann::json::parse(from);
|
||||
},
|
||||
&j,
|
||||
"Jmdablxeg");
|
||||
const auto& j_stats = j["jemalloc"]["stats"];
|
||||
for(auto it = j_stats.begin(); it != j_stats.end(); ++it) {
|
||||
if(it.value().is_number_unsigned()) {
|
||||
std::uint64_t val = it.value().template get<std::uint64_t>();
|
||||
std::string key = "jemalloc." + it.key();
|
||||
auto metric = libs::metrics::libsinsp_metrics::new_metric(
|
||||
key.c_str(),
|
||||
METRICS_V2_JEMALLOC_STATS,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_MEMORY_BYTES,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
val);
|
||||
prometheus_metrics_converter.convert_metric_to_unit_convention(metric);
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric,
|
||||
"falcosecurity",
|
||||
"falco");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return prometheus_text;
|
||||
}
|
||||
|
||||
std::string falco_metrics::sources_to_text_prometheus(
|
||||
const falco::app::state& state,
|
||||
libs::metrics::prometheus_metrics_converter& prometheus_metrics_converter,
|
||||
std::vector<metrics_v2>& additional_wrapper_metrics) {
|
||||
static const char* all_driver_engines[] = {BPF_ENGINE,
|
||||
KMOD_ENGINE,
|
||||
MODERN_BPF_ENGINE,
|
||||
SOURCE_PLUGIN_ENGINE,
|
||||
NODRIVER_ENGINE,
|
||||
GVISOR_ENGINE};
|
||||
static re2::RE2 drops_buffer_pattern("n_drops_buffer_([^_]+(?:_[^_]+)*)_(enter|exit)$");
|
||||
static re2::RE2 cpu_pattern("(\\d+)");
|
||||
|
||||
std::vector<std::shared_ptr<sinsp>> inspectors;
|
||||
std::vector<libs::metrics::libs_metrics_collector> metrics_collectors;
|
||||
std::string prometheus_text;
|
||||
bool agent_info_written = false;
|
||||
bool machine_info_written = false;
|
||||
|
||||
// Then, source-bound metrics
|
||||
for(const auto& source : state.enabled_sources) {
|
||||
auto source_info = state.source_infos.at(source);
|
||||
auto source_inspector = source_info->inspector;
|
||||
inspectors.emplace_back(source_inspector);
|
||||
metrics_collectors.emplace_back(
|
||||
libs::metrics::libs_metrics_collector(source_inspector.get(),
|
||||
state.config->m_metrics_flags));
|
||||
}
|
||||
libs::metrics::prometheus_metrics_converter prometheus_metrics_converter;
|
||||
std::string prometheus_text;
|
||||
|
||||
for(auto inspector : inspectors) {
|
||||
// Falco wrapper metrics
|
||||
//
|
||||
for(size_t i = 0; i < sizeof(all_driver_engines) / sizeof(const char*); i++) {
|
||||
if(inspector->check_current_engine(all_driver_engines[i])) {
|
||||
// First thing: list of enabled engine names
|
||||
|
||||
// Falco wrapper metrics Part A: Repeated for each inspector, accounting for plugins w/
|
||||
// event sources
|
||||
|
||||
/* Examples ...
|
||||
# HELP falcosecurity_scap_engine_name_info https://falco.org/docs/metrics/
|
||||
# TYPE falcosecurity_scap_engine_name_info gauge
|
||||
falcosecurity_scap_engine_name_info{engine_name="source_plugin",evt_source="dummy"} 1
|
||||
# HELP falcosecurity_scap_engine_name_info https://falco.org/docs/metrics/
|
||||
# TYPE falcosecurity_scap_engine_name_info gauge
|
||||
falcosecurity_scap_engine_name_info{engine_name="bpf",evt_source="syscall"} 1
|
||||
*/
|
||||
|
||||
for(size_t j = 0; j < sizeof(all_driver_engines) / sizeof(const char*); j++) {
|
||||
if(source_inspector->check_current_engine(all_driver_engines[j])) {
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"engine_name",
|
||||
"falcosecurity",
|
||||
"scap",
|
||||
{{"engine_name", all_driver_engines[i]}});
|
||||
{{"engine_name", std::string(all_driver_engines[j])},
|
||||
{"evt_source", source}});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const scap_agent_info* agent_info = inspector->get_agent_info();
|
||||
const scap_machine_info* machine_info = inspector->get_machine_info();
|
||||
libs::metrics::libs_metrics_collector libs_metrics_collector(inspector.get(), 0);
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"version",
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
{{"version", FALCO_VERSION}});
|
||||
// Inspectors' metrics collectors
|
||||
// Libs metrics categories
|
||||
//
|
||||
// resource_utilization_enabled
|
||||
// state_counters_enabled
|
||||
// kernel_event_counters_enabled
|
||||
// kernel_event_counters_per_cpu_enabled
|
||||
// libbpf_stats_enabled
|
||||
auto metrics_collector =
|
||||
libs::metrics::libs_metrics_collector(source_inspector.get(),
|
||||
state.config->m_metrics_flags);
|
||||
metrics_collector.snapshot();
|
||||
auto metrics_snapshot = metrics_collector.get_metrics();
|
||||
|
||||
// Source plugin
|
||||
if(source != falco_common::syscall_source) {
|
||||
// Performed repeatedly for each inspectors' libs metrics collector
|
||||
for(auto& metric : metrics_snapshot) {
|
||||
if(metric.flags & METRICS_V2_PLUGINS) {
|
||||
prometheus_metrics_converter.convert_metric_to_unit_convention(metric);
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric,
|
||||
"falcosecurity",
|
||||
"plugins");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Source syscall
|
||||
for(auto& metric : metrics_snapshot) {
|
||||
prometheus_metrics_converter.convert_metric_to_unit_convention(metric);
|
||||
std::string prometheus_subsystem = "scap";
|
||||
|
||||
if(metric.flags & METRICS_V2_RESOURCE_UTILIZATION) {
|
||||
prometheus_subsystem = "falco";
|
||||
}
|
||||
|
||||
if(metric.flags & METRICS_V2_PLUGINS) {
|
||||
prometheus_subsystem = "plugins";
|
||||
}
|
||||
|
||||
// raw incoming in form of for example n_evts_cpu_15 or n_drops_cpu_15
|
||||
if(strncmp(metric.name, "n_evts_cpu", 10) == 0 ||
|
||||
strncmp(metric.name, "n_drops_cpu", 11) == 0) // prefix match
|
||||
{
|
||||
std::string name_str(metric.name);
|
||||
std::string cpu_number;
|
||||
if(re2::RE2::PartialMatch(name_str, cpu_pattern, &cpu_number)) {
|
||||
re2::RE2::GlobalReplace(&name_str, cpu_pattern, "");
|
||||
// possible double __ will be sanitized within libs
|
||||
auto metric_new = libs::metrics::libsinsp_metrics::new_metric(
|
||||
name_str.c_str(),
|
||||
METRICS_V2_KERNEL_COUNTERS_PER_CPU,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
metric.value.u64);
|
||||
const std::map<std::string, std::string>& const_labels = {
|
||||
{"cpu", cpu_number}};
|
||||
/* Examples ...
|
||||
# HELP falcosecurity_scap_n_evts_cpu_total
|
||||
https://falco.org/docs/metrics/ # TYPE
|
||||
falcosecurity_scap_n_evts_cpu_total counter
|
||||
falcosecurity_scap_n_evts_cpu_total{cpu="7"} 237
|
||||
# HELP falcosecurity_scap_n_drops_cpu_total
|
||||
https://falco.org/docs/metrics/ # TYPE
|
||||
falcosecurity_scap_n_drops_cpu_total counter
|
||||
falcosecurity_scap_n_drops_cpu_total{cpu="7"} 0
|
||||
*/
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric_new,
|
||||
"falcosecurity",
|
||||
prometheus_subsystem,
|
||||
const_labels);
|
||||
}
|
||||
} else if(strcmp(metric.name, "n_drops_buffer_total") == 0) {
|
||||
// Skip the libs aggregate metric since we distinguish between buffer drops
|
||||
// using labels similar to the rules_matches
|
||||
continue;
|
||||
} else if(strncmp(metric.name, "n_drops_buffer", 14) == 0) // prefix match
|
||||
{
|
||||
std::string drop;
|
||||
std::string dir;
|
||||
std::string name_str(metric.name);
|
||||
if(re2::RE2::FullMatch(name_str, drops_buffer_pattern, &drop, &dir)) {
|
||||
auto metric_new = libs::metrics::libsinsp_metrics::new_metric(
|
||||
"n_drops_buffer",
|
||||
METRICS_V2_KERNEL_COUNTERS,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
metric.value.u64);
|
||||
const std::map<std::string, std::string>& const_labels = {{"drop", drop},
|
||||
{"dir", dir}};
|
||||
/* Examples ...
|
||||
# HELP falcosecurity_scap_n_drops_buffer_total
|
||||
https://falco.org/docs/metrics/ # TYPE
|
||||
falcosecurity_scap_n_drops_buffer_total counter
|
||||
falcosecurity_scap_n_drops_buffer_total{dir="enter",drop="clone_fork"} 0
|
||||
# HELP falcosecurity_scap_n_drops_buffer_total
|
||||
https://falco.org/docs/metrics/ # TYPE
|
||||
falcosecurity_scap_n_drops_buffer_total counter
|
||||
falcosecurity_scap_n_drops_buffer_total{dir="exit",drop="clone_fork"} 0
|
||||
*/
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric_new,
|
||||
"falcosecurity",
|
||||
prometheus_subsystem,
|
||||
const_labels);
|
||||
}
|
||||
} else {
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric,
|
||||
"falcosecurity",
|
||||
prometheus_subsystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Source wrapper metrics Part B: Agnostic, performed only once.
|
||||
if(agent_info_written && machine_info_written) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const scap_agent_info* agent_info = nullptr;
|
||||
if(!agent_info_written) {
|
||||
agent_info = source_inspector->get_agent_info();
|
||||
}
|
||||
const scap_machine_info* machine_info = nullptr;
|
||||
if(!machine_info_written) {
|
||||
machine_info = source_inspector->get_machine_info();
|
||||
}
|
||||
|
||||
// Not all scap engines report agent and machine infos.
|
||||
// However, recent lib refactors enable a linux lite platform, allowing non-syscall
|
||||
// inspectors to retrieve these metrics if the syscall inspector is unavailable.
|
||||
// We only push these info once.
|
||||
if(agent_info) {
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"kernel_release",
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
{{"kernel_release", agent_info->uname_r}});
|
||||
}
|
||||
if(machine_info) {
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"hostname",
|
||||
"falcosecurity",
|
||||
"evt",
|
||||
{{"hostname", machine_info->hostname}});
|
||||
}
|
||||
|
||||
#if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__)
|
||||
// Distinguish between config and rules files using labels, following Prometheus best
|
||||
// practices: https://prometheus.io/docs/practices/naming/#labels
|
||||
for(const auto& item : state.config.get()->m_loaded_rules_filenames_sha256sum) {
|
||||
fs::path fs_path = item.first;
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"sha256_rules_files",
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
{{"file_name", fs_path.filename()}, {"sha256", item.second}});
|
||||
}
|
||||
|
||||
for(const auto& item : state.config.get()->m_loaded_configs_filenames_sha256sum) {
|
||||
fs::path fs_path = item.first;
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"sha256_config_files",
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
{{"file_name", fs_path.filename()}, {"sha256", item.second}});
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
for(const std::string& source : inspector->event_sources()) {
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"evt_source",
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
{{"evt_source", source}});
|
||||
}
|
||||
std::vector<metrics_v2> additional_wrapper_metrics;
|
||||
|
||||
if(agent_info) {
|
||||
additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric(
|
||||
"start_ts",
|
||||
METRICS_V2_MISC,
|
||||
@@ -151,8 +425,25 @@ std::string falco_metrics::to_text(const falco::app::state& state) {
|
||||
METRIC_VALUE_UNIT_TIME_TIMESTAMP_NS,
|
||||
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
|
||||
agent_info->start_ts_epoch));
|
||||
auto now = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric(
|
||||
"duration_sec",
|
||||
METRICS_V2_MISC,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_TIME_S_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
(uint64_t)((now - agent_info->start_ts_epoch) / ONE_SECOND_IN_NS)));
|
||||
agent_info_written = true;
|
||||
}
|
||||
|
||||
if(machine_info) {
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
"hostname",
|
||||
"falcosecurity",
|
||||
"evt",
|
||||
{{"hostname", machine_info->hostname}});
|
||||
additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric(
|
||||
"host_boot_ts",
|
||||
METRICS_V2_MISC,
|
||||
@@ -167,225 +458,41 @@ std::string falco_metrics::to_text(const falco::app::state& state) {
|
||||
METRIC_VALUE_UNIT_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
|
||||
machine_info->num_cpus));
|
||||
machine_info_written = true;
|
||||
}
|
||||
additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric(
|
||||
"outputs_queue_num_drops",
|
||||
METRICS_V2_MISC,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
state.outputs->get_outputs_queue_num_drops()));
|
||||
} // End inspector loop
|
||||
|
||||
return prometheus_text;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief this method takes an application \c state and returns a textual representation of
|
||||
its configured metrics.
|
||||
|
||||
The current implementation returns a Prometheus exposition formatted string.
|
||||
*/
|
||||
std::string falco_metrics::to_text_prometheus(const falco::app::state& state) {
|
||||
libs::metrics::prometheus_metrics_converter prometheus_metrics_converter;
|
||||
std::string prometheus_text;
|
||||
|
||||
std::vector<metrics_v2> additional_wrapper_metrics;
|
||||
|
||||
// Falco global metrics, once
|
||||
prometheus_text += falco_to_text_prometheus(state,
|
||||
prometheus_metrics_converter,
|
||||
additional_wrapper_metrics);
|
||||
// Metrics for each source
|
||||
prometheus_text += sources_to_text_prometheus(state,
|
||||
prometheus_metrics_converter,
|
||||
additional_wrapper_metrics);
|
||||
|
||||
for(auto metric : additional_wrapper_metrics) {
|
||||
prometheus_metrics_converter.convert_metric_to_unit_convention(metric);
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(metric,
|
||||
"falcosecurity",
|
||||
"falco");
|
||||
}
|
||||
|
||||
if(agent_info) {
|
||||
auto now = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric(
|
||||
"duration_sec",
|
||||
METRICS_V2_MISC,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_TIME_S_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
(uint64_t)((now - agent_info->start_ts_epoch) / ONE_SECOND_IN_NS)));
|
||||
}
|
||||
|
||||
for(auto metric : additional_wrapper_metrics) {
|
||||
prometheus_metrics_converter.convert_metric_to_unit_convention(metric);
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(metric,
|
||||
"falcosecurity",
|
||||
"falco");
|
||||
}
|
||||
|
||||
// Falco metrics categories
|
||||
//
|
||||
// rules_counters_enabled
|
||||
if(state.config->m_metrics_flags & METRICS_V2_RULE_COUNTERS) {
|
||||
const stats_manager& rule_stats_manager = state.engine->get_rule_stats_manager();
|
||||
const indexed_vector<falco_rule>& rules = state.engine->get_rules();
|
||||
const std::vector<std::unique_ptr<std::atomic<uint64_t>>>& rules_by_id =
|
||||
rule_stats_manager.get_by_rule_id();
|
||||
// Distinguish between rules counters using labels, following Prometheus best practices:
|
||||
// https://prometheus.io/docs/practices/naming/#labels
|
||||
for(size_t i = 0; i < rules_by_id.size(); i++) {
|
||||
auto rule = rules.at(i);
|
||||
auto count = rules_by_id[i]->load();
|
||||
if(count > 0) {
|
||||
/* Examples ...
|
||||
# HELP falcosecurity_falco_rules_matches_total
|
||||
https://falco.org/docs/metrics/ # TYPE
|
||||
falcosecurity_falco_rules_matches_total counter
|
||||
falcosecurity_falco_rules_matches_total{priority="4",rule_name="Read
|
||||
sensitive file
|
||||
untrusted",source="syscall",tag_T1555="true",tag_container="true",tag_filesystem="true",tag_host="true",tag_maturity_stable="true",tag_mitre_credential_access="true"}
|
||||
10 # HELP falcosecurity_falco_rules_matches_total
|
||||
https://falco.org/docs/metrics/ # TYPE
|
||||
falcosecurity_falco_rules_matches_total counter
|
||||
falcosecurity_falco_rules_matches_total{priority="5",rule_name="Unexpected
|
||||
UDP
|
||||
Traffic",source="syscall",tag_TA0011="true",tag_container="true",tag_host="true",tag_maturity_incubating="true",tag_mitre_exfiltration="true",tag_network="true"}
|
||||
1
|
||||
*/
|
||||
auto metric = libs::metrics::libsinsp_metrics::new_metric(
|
||||
"rules_matches",
|
||||
METRICS_V2_RULE_COUNTERS,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
rules_by_id[i]->load());
|
||||
prometheus_metrics_converter.convert_metric_to_unit_convention(metric);
|
||||
std::map<std::string, std::string> const_labels = {
|
||||
{"rule_name", rule->name},
|
||||
{"priority", std::to_string(rule->priority)},
|
||||
{"source", rule->source},
|
||||
};
|
||||
std::for_each(rule->tags.cbegin(),
|
||||
rule->tags.cend(),
|
||||
[&const_labels](std::string const& tag) {
|
||||
const_labels.emplace(std::string{"tag_"} + tag, "true");
|
||||
});
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric,
|
||||
"falcosecurity",
|
||||
"falco",
|
||||
const_labels);
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef HAS_JEMALLOC
|
||||
if(state.config->m_metrics_flags & METRICS_V2_JEMALLOC_STATS) {
|
||||
nlohmann::json j;
|
||||
malloc_stats_print(
|
||||
[](void* to, const char* from) {
|
||||
nlohmann::json* j = (nlohmann::json*)to;
|
||||
*j = nlohmann::json::parse(from);
|
||||
},
|
||||
&j,
|
||||
"Jmdablxeg");
|
||||
const auto& j_stats = j["jemalloc"]["stats"];
|
||||
for(auto it = j_stats.begin(); it != j_stats.end(); ++it) {
|
||||
if(it.value().is_number_unsigned()) {
|
||||
std::uint64_t val = it.value().template get<std::uint64_t>();
|
||||
std::string key = "jemalloc." + it.key();
|
||||
auto metric = libs::metrics::libsinsp_metrics::new_metric(
|
||||
key.c_str(),
|
||||
METRICS_V2_JEMALLOC_STATS,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_MEMORY_BYTES,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
val);
|
||||
prometheus_metrics_converter.convert_metric_to_unit_convention(metric);
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric,
|
||||
"falcosecurity",
|
||||
"falco");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Libs metrics categories
|
||||
//
|
||||
// resource_utilization_enabled
|
||||
// state_counters_enabled
|
||||
// kernel_event_counters_enabled
|
||||
// libbpf_stats_enabled
|
||||
for(auto metrics_collector : metrics_collectors) {
|
||||
metrics_collector.snapshot();
|
||||
auto metrics_snapshot = metrics_collector.get_metrics();
|
||||
|
||||
for(auto& metric : metrics_snapshot) {
|
||||
prometheus_metrics_converter.convert_metric_to_unit_convention(metric);
|
||||
std::string prometheus_subsystem = "scap";
|
||||
|
||||
if(metric.flags & METRICS_V2_RESOURCE_UTILIZATION) {
|
||||
prometheus_subsystem = "falco";
|
||||
}
|
||||
|
||||
if(metric.flags & METRICS_V2_PLUGINS) {
|
||||
prometheus_subsystem = "plugins";
|
||||
}
|
||||
|
||||
// raw incoming in form of for example n_evts_cpu_15 or n_drops_cpu_15
|
||||
if(strncmp(metric.name, "n_evts_cpu", 10) == 0 ||
|
||||
strncmp(metric.name, "n_drops_cpu", 11) == 0) // prefix match
|
||||
{
|
||||
std::string name_str(metric.name);
|
||||
re2::RE2 pattern("(\\d+)");
|
||||
std::string cpu_number;
|
||||
if(re2::RE2::PartialMatch(name_str, pattern, &cpu_number)) {
|
||||
re2::RE2::GlobalReplace(&name_str, pattern, "");
|
||||
// possible double __ will be sanitized within libs
|
||||
auto metric_new = libs::metrics::libsinsp_metrics::new_metric(
|
||||
name_str.c_str(),
|
||||
METRICS_V2_KERNEL_COUNTERS_PER_CPU,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
metric.value.u64);
|
||||
const std::map<std::string, std::string>& const_labels = {{"cpu", cpu_number}};
|
||||
/* Examples ...
|
||||
# HELP falcosecurity_scap_n_evts_cpu_total https://falco.org/docs/metrics/
|
||||
# TYPE falcosecurity_scap_n_evts_cpu_total counter
|
||||
falcosecurity_scap_n_evts_cpu_total{cpu="7"} 237
|
||||
# HELP falcosecurity_scap_n_drops_cpu_total https://falco.org/docs/metrics/
|
||||
# TYPE falcosecurity_scap_n_drops_cpu_total counter
|
||||
falcosecurity_scap_n_drops_cpu_total{cpu="7"} 0
|
||||
*/
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric_new,
|
||||
"falcosecurity",
|
||||
prometheus_subsystem,
|
||||
const_labels);
|
||||
}
|
||||
} else if(strcmp(metric.name, "n_drops_buffer_total") == 0) {
|
||||
// Skip the libs aggregate metric since we distinguish between buffer drops using
|
||||
// labels similar to the rules_matches
|
||||
continue;
|
||||
} else if(strncmp(metric.name, "n_drops_buffer", 14) == 0) // prefix match
|
||||
{
|
||||
re2::RE2 pattern("n_drops_buffer_([^_]+(?:_[^_]+)*)_(enter|exit)$");
|
||||
std::string drop;
|
||||
std::string dir;
|
||||
std::string name_str(metric.name);
|
||||
if(re2::RE2::FullMatch(name_str, pattern, &drop, &dir)) {
|
||||
auto metric_new = libs::metrics::libsinsp_metrics::new_metric(
|
||||
"n_drops_buffer",
|
||||
METRICS_V2_KERNEL_COUNTERS,
|
||||
METRIC_VALUE_TYPE_U64,
|
||||
METRIC_VALUE_UNIT_COUNT,
|
||||
METRIC_VALUE_METRIC_TYPE_MONOTONIC,
|
||||
metric.value.u64);
|
||||
const std::map<std::string, std::string>& const_labels = {{"drop", drop},
|
||||
{"dir", dir}};
|
||||
/* Examples ...
|
||||
# HELP falcosecurity_scap_n_drops_buffer_total
|
||||
https://falco.org/docs/metrics/ # TYPE
|
||||
falcosecurity_scap_n_drops_buffer_total counter
|
||||
falcosecurity_scap_n_drops_buffer_total{dir="enter",drop="clone_fork"} 0
|
||||
# HELP falcosecurity_scap_n_drops_buffer_total
|
||||
https://falco.org/docs/metrics/ # TYPE
|
||||
falcosecurity_scap_n_drops_buffer_total counter
|
||||
falcosecurity_scap_n_drops_buffer_total{dir="exit",drop="clone_fork"} 0
|
||||
*/
|
||||
prometheus_text +=
|
||||
prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric_new,
|
||||
"falcosecurity",
|
||||
prometheus_subsystem,
|
||||
const_labels);
|
||||
}
|
||||
} else {
|
||||
prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(
|
||||
metric,
|
||||
"falcosecurity",
|
||||
prometheus_subsystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
return prometheus_text;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,16 @@ struct state;
|
||||
|
||||
class falco_metrics {
|
||||
public:
|
||||
static const std::string content_type;
|
||||
static std::string to_text(const falco::app::state& state);
|
||||
static const std::string content_type_prometheus;
|
||||
static std::string to_text_prometheus(const falco::app::state& state);
|
||||
|
||||
private:
|
||||
static std::string falco_to_text_prometheus(
|
||||
const falco::app::state& state,
|
||||
libs::metrics::prometheus_metrics_converter& prometheus_metrics_converter,
|
||||
std::vector<metrics_v2>& additional_wrapper_metrics);
|
||||
static std::string sources_to_text_prometheus(
|
||||
const falco::app::state& state,
|
||||
libs::metrics::prometheus_metrics_converter& prometheus_metrics_converter,
|
||||
std::vector<metrics_v2>& additional_wrapper_metrics);
|
||||
};
|
||||
|
||||
@@ -45,6 +45,7 @@ falco_outputs::falco_outputs(std::shared_ptr<falco_engine> engine,
|
||||
bool json_include_output_property,
|
||||
bool json_include_tags_property,
|
||||
bool json_include_message_property,
|
||||
bool json_include_output_fields_property,
|
||||
uint32_t timeout,
|
||||
bool buffered,
|
||||
size_t outputs_queue_capacity,
|
||||
@@ -54,6 +55,7 @@ falco_outputs::falco_outputs(std::shared_ptr<falco_engine> engine,
|
||||
json_include_output_property,
|
||||
json_include_tags_property,
|
||||
json_include_message_property,
|
||||
json_include_output_fields_property,
|
||||
time_format_iso_8601)),
|
||||
m_buffered(buffered),
|
||||
m_json_output(json_output),
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
bool json_include_output_property,
|
||||
bool json_include_tags_property,
|
||||
bool json_include_message_property,
|
||||
bool json_include_output_fields_property,
|
||||
uint32_t timeout,
|
||||
bool buffered,
|
||||
size_t outputs_queue_capacity,
|
||||
|
||||
@@ -36,6 +36,8 @@ bool falco::outputs::output_http::init(const config &oc,
|
||||
|
||||
m_curl = nullptr;
|
||||
m_http_headers = nullptr;
|
||||
m_max_consecutive_timeouts =
|
||||
static_cast<uint8_t>(std::stoi(m_oc.options["max_consecutive_timeouts"]) & 0xFF);
|
||||
CURLcode res = CURLE_FAILED_INIT;
|
||||
|
||||
m_curl = curl_easy_init();
|
||||
@@ -103,7 +105,16 @@ bool falco::outputs::output_http::init(const config &oc,
|
||||
|
||||
void falco::outputs::output_http::output(const message *msg) {
|
||||
CURLcode res = curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, msg->msg.c_str());
|
||||
CHECK_RES(curl_easy_perform(m_curl));
|
||||
uint8_t curl_easy_platform_calls = 0;
|
||||
|
||||
if(res == CURLE_OK) {
|
||||
do {
|
||||
res = curl_easy_perform(m_curl);
|
||||
curl_easy_platform_calls++;
|
||||
} while(res == CURLE_OPERATION_TIMEDOUT &&
|
||||
curl_easy_platform_calls <= m_max_consecutive_timeouts);
|
||||
}
|
||||
|
||||
if(res != CURLE_OK) {
|
||||
falco_logger::log(
|
||||
falco_logger::level::ERR,
|
||||
|
||||
@@ -36,6 +36,7 @@ class output_http : public abstract_output {
|
||||
private:
|
||||
CURL *m_curl;
|
||||
struct curl_slist *m_http_headers;
|
||||
uint8_t m_max_consecutive_timeouts;
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
|
||||
@@ -233,7 +233,6 @@ void stats_writer::worker() noexcept {
|
||||
bool use_file = !m_config->m_metrics_output_file.empty();
|
||||
auto tick = stats_writer::get_ticker();
|
||||
auto last_tick = tick;
|
||||
auto first_tick = tick;
|
||||
|
||||
while(true) {
|
||||
// blocks until a message becomes availables
|
||||
@@ -244,35 +243,33 @@ void stats_writer::worker() noexcept {
|
||||
return;
|
||||
}
|
||||
|
||||
// this helps waiting for the first tick
|
||||
tick = stats_writer::get_ticker();
|
||||
if(first_tick != tick) {
|
||||
if(last_tick != tick) {
|
||||
m_total_samples++;
|
||||
}
|
||||
last_tick = tick;
|
||||
|
||||
try {
|
||||
if(use_outputs) {
|
||||
std::string rule = "Falco internal: metrics snapshot";
|
||||
std::string msg = "Falco metrics snapshot";
|
||||
m_outputs->handle_msg(m.ts,
|
||||
falco_common::PRIORITY_INFORMATIONAL,
|
||||
msg,
|
||||
rule,
|
||||
m.output_fields);
|
||||
}
|
||||
if(last_tick != tick) {
|
||||
m_total_samples++;
|
||||
}
|
||||
last_tick = tick;
|
||||
|
||||
if(use_file) {
|
||||
nlohmann::json jmsg;
|
||||
jmsg["sample"] = m_total_samples;
|
||||
jmsg["output_fields"] = m.output_fields;
|
||||
m_file_output << jmsg.dump() << std::endl;
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
falco_logger::log(falco_logger::level::ERR,
|
||||
"stats_writer (worker): " + std::string(e.what()) + "\n");
|
||||
try {
|
||||
if(use_outputs) {
|
||||
std::string rule = "Falco internal: metrics snapshot";
|
||||
std::string msg = "Falco metrics snapshot";
|
||||
m_outputs->handle_msg(m.ts,
|
||||
falco_common::PRIORITY_INFORMATIONAL,
|
||||
msg,
|
||||
rule,
|
||||
m.output_fields);
|
||||
}
|
||||
|
||||
if(use_file) {
|
||||
nlohmann::json jmsg;
|
||||
jmsg["sample"] = m_total_samples;
|
||||
jmsg["output_fields"] = m.output_fields;
|
||||
m_file_output << jmsg.dump() << std::endl;
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
falco_logger::log(falco_logger::level::ERR,
|
||||
"stats_writer (worker): " + std::string(e.what()) + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,6 +350,7 @@ void stats_writer::collector::get_metrics_output_fields_wrapper(
|
||||
/* Wrapper fields useful for statistical analyses and attributions. Always enabled. */
|
||||
output_fields["evt.time"] =
|
||||
now; /* Some ETLs may prefer a consistent timestamp within output_fields. */
|
||||
output_fields["falco.reload_ts"] = m_writer->m_config->m_falco_reload_ts;
|
||||
output_fields["falco.version"] = FALCO_VERSION;
|
||||
if(agent_info) {
|
||||
output_fields["falco.start_ts"] = agent_info->start_ts_epoch;
|
||||
@@ -416,7 +414,8 @@ void stats_writer::collector::get_metrics_output_fields_wrapper(
|
||||
|
||||
void stats_writer::collector::get_metrics_output_fields_additional(
|
||||
nlohmann::json& output_fields,
|
||||
double stats_snapshot_time_delta_sec) {
|
||||
double stats_snapshot_time_delta_sec,
|
||||
const std::string& src) {
|
||||
// Falco metrics categories
|
||||
//
|
||||
// rules_counters_enabled
|
||||
@@ -480,7 +479,8 @@ void stats_writer::collector::get_metrics_output_fields_additional(
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__)
|
||||
if(m_writer->m_libs_metrics_collector && m_writer->m_output_rule_metrics_converter) {
|
||||
if(m_writer->m_libs_metrics_collectors.find(src) != m_writer->m_libs_metrics_collectors.end() &&
|
||||
m_writer->m_output_rule_metrics_converter) {
|
||||
// Libs metrics categories
|
||||
//
|
||||
// resource_utilization_enabled
|
||||
@@ -489,8 +489,9 @@ void stats_writer::collector::get_metrics_output_fields_additional(
|
||||
// libbpf_stats_enabled
|
||||
|
||||
// Refresh / New snapshot
|
||||
m_writer->m_libs_metrics_collector->snapshot();
|
||||
auto metrics_snapshot = m_writer->m_libs_metrics_collector->get_metrics();
|
||||
auto& libs_metrics_collector = m_writer->m_libs_metrics_collectors[src];
|
||||
libs_metrics_collector->snapshot();
|
||||
auto metrics_snapshot = libs_metrics_collector->get_metrics();
|
||||
// Cache n_evts and n_drops to derive n_drops_perc.
|
||||
uint64_t n_evts = 0;
|
||||
uint64_t n_drops = 0;
|
||||
@@ -613,7 +614,8 @@ void stats_writer::collector::collect(const std::shared_ptr<sinsp>& inspector,
|
||||
uint64_t num_evts) {
|
||||
if(m_writer->has_output()) {
|
||||
#if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__)
|
||||
if(!m_writer->m_libs_metrics_collector) {
|
||||
if(m_writer->m_libs_metrics_collectors.find(src) ==
|
||||
m_writer->m_libs_metrics_collectors.end()) {
|
||||
uint32_t flags = m_writer->m_config->m_metrics_flags;
|
||||
// Note: ENGINE_FLAG_BPF_STATS_ENABLED check has been moved to libs, that is, when
|
||||
// libbpf stats is not enabled in the kernel settings we won't collect them even if the
|
||||
@@ -627,7 +629,7 @@ void stats_writer::collector::collect(const std::shared_ptr<sinsp>& inspector,
|
||||
flags &= ~(METRICS_V2_KERNEL_COUNTERS | METRICS_V2_KERNEL_COUNTERS_PER_CPU |
|
||||
METRICS_V2_STATE_COUNTERS | METRICS_V2_LIBBPF_STATS);
|
||||
}
|
||||
m_writer->m_libs_metrics_collector =
|
||||
m_writer->m_libs_metrics_collectors[src] =
|
||||
std::make_unique<libs::metrics::libs_metrics_collector>(inspector.get(), flags);
|
||||
}
|
||||
|
||||
@@ -659,7 +661,8 @@ void stats_writer::collector::collect(const std::shared_ptr<sinsp>& inspector,
|
||||
num_evts,
|
||||
now,
|
||||
stats_snapshot_time_delta_sec);
|
||||
get_metrics_output_fields_additional(output_fields, stats_snapshot_time_delta_sec);
|
||||
|
||||
get_metrics_output_fields_additional(output_fields, stats_snapshot_time_delta_sec, src);
|
||||
|
||||
/* Send message in the queue */
|
||||
stats_writer::msg msg;
|
||||
|
||||
@@ -79,10 +79,13 @@ public:
|
||||
fields.
|
||||
*/
|
||||
void get_metrics_output_fields_additional(nlohmann::json& output_fields,
|
||||
double stats_snapshot_time_delta_sec);
|
||||
double stats_snapshot_time_delta_sec,
|
||||
const std::string& src);
|
||||
|
||||
std::shared_ptr<stats_writer> m_writer;
|
||||
stats_writer::ticker_t m_last_tick = 0;
|
||||
// Init m_last_tick w/ invalid value to enable metrics logging immediately after
|
||||
// startup/reload
|
||||
stats_writer::ticker_t m_last_tick = std::numeric_limits<ticker_t>::max();
|
||||
uint64_t m_last_now = 0;
|
||||
uint64_t m_last_n_evts = 0;
|
||||
uint64_t m_last_n_drops = 0;
|
||||
@@ -151,7 +154,9 @@ private:
|
||||
tbb::concurrent_bounded_queue<stats_writer::msg> m_queue;
|
||||
#endif
|
||||
#if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__)
|
||||
std::unique_ptr<libs::metrics::libs_metrics_collector> m_libs_metrics_collector;
|
||||
// Per source map of libs metrics collectors
|
||||
std::unordered_map<std::string, std::unique_ptr<libs::metrics::libs_metrics_collector>>
|
||||
m_libs_metrics_collectors;
|
||||
std::unique_ptr<libs::metrics::output_rule_metrics_converter> m_output_rule_metrics_converter;
|
||||
#endif
|
||||
std::shared_ptr<falco_outputs> m_outputs;
|
||||
|
||||
@@ -58,11 +58,6 @@ void falco_webserver::start(const falco::app::state &state,
|
||||
res.set_content(versions_json_str, "application/json");
|
||||
});
|
||||
|
||||
if(state.config->m_metrics_enabled && webserver_config.m_prometheus_metrics_enabled) {
|
||||
m_server->Get("/metrics", [&state](const httplib::Request &, httplib::Response &res) {
|
||||
res.set_content(falco_metrics::to_text(state), falco_metrics::content_type);
|
||||
});
|
||||
}
|
||||
// run server in a separate thread
|
||||
if(!m_server->is_valid()) {
|
||||
m_server = nullptr;
|
||||
@@ -105,3 +100,13 @@ void falco_webserver::stop() {
|
||||
m_running = false;
|
||||
}
|
||||
}
|
||||
|
||||
void falco_webserver::enable_prometheus_metrics(const falco::app::state &state) {
|
||||
if(state.config->m_metrics_enabled &&
|
||||
state.config->m_webserver_config.m_prometheus_metrics_enabled) {
|
||||
m_server->Get("/metrics", [&state](const httplib::Request &, httplib::Response &res) {
|
||||
res.set_content(falco_metrics::to_text_prometheus(state),
|
||||
falco_metrics::content_type_prometheus);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
virtual void start(const falco::app::state& state,
|
||||
const falco_configuration::webserver_config& webserver_config);
|
||||
virtual void stop();
|
||||
virtual void enable_prometheus_metrics(const falco::app::state& state);
|
||||
|
||||
private:
|
||||
bool m_running = false;
|
||||
|
||||
Reference in New Issue
Block a user