Compare commits

..

1 Commits

Author SHA1 Message Date
Federico Di Pierro
db92f04474 wip
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2022-08-22 15:09:36 +02:00
90 changed files with 1475 additions and 3331 deletions

View File

@@ -360,7 +360,7 @@ jobs:
name: Build and publish falco-driver-loader-dev
command: |
cd /source/falco
docker buildx build --push --build-arg FALCO_IMAGE_TAG=x86_64-master \
docker buildx build --push --build-arg FALCO_IMAGE_TAG=master \
-t falcosecurity/falco-driver-loader:x86_64-master \
-t public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-master \
docker/driver-loader
@@ -409,7 +409,7 @@ jobs:
name: Build and publish falco-driver-loader-dev
command: |
cd /tmp/source-arm64/falco
docker buildx build --push --build-arg FALCO_IMAGE_TAG=aarch64-master \
docker buildx build --push --build-arg FALCO_IMAGE_TAG=master \
-t falcosecurity/falco-driver-loader:aarch64-master \
-t public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-master \
docker/driver-loader
@@ -579,7 +579,7 @@ jobs:
name: Build and publish falco-driver-loader
command: |
cd /source/falco
docker buildx build --push --build-arg FALCO_IMAGE_TAG=x86_64-${CIRCLE_TAG} \
docker buildx build --push --build-arg FALCO_IMAGE_TAG=${CIRCLE_TAG} \
-t "falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}" \
-t "falcosecurity/falco-driver-loader:x86_64-latest" \
-t "public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}" \
@@ -634,7 +634,7 @@ jobs:
name: Build and publish falco-driver-loader
command: |
cd /tmp/source-arm64/falco
docker buildx build --push --build-arg FALCO_IMAGE_TAG=aarch64-${CIRCLE_TAG} \
docker buildx build --push --build-arg FALCO_IMAGE_TAG=${CIRCLE_TAG} \
-t "falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG}" \
-t "falcosecurity/falco-driver-loader:aarch64-latest" \
-t "public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG}" \
@@ -754,7 +754,6 @@ workflows:
- "build-musl"
- "build-arm64"
- "build-centos7"
- "quality-static-analysis"
- "tests-integration":
requires:
- "build-centos7"

View File

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

View File

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

View File

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

View File

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

View File

@@ -59,16 +59,16 @@ endif()
# This will be used to print the architecture for which Falco is compiled.
set(FALCO_TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR})
if(NOT FALCO_EXTRA_DEBUG_FLAGS)
set(FALCO_EXTRA_DEBUG_FLAGS "-D_DEBUG")
if(NOT DRAIOS_DEBUG_FLAGS)
set(DRAIOS_DEBUG_FLAGS "-D_DEBUG")
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
if(CMAKE_BUILD_TYPE STREQUAL "debug")
set(KBUILD_FLAGS "${FALCO_EXTRA_DEBUG_FLAGS} ${FALCO_EXTRA_FEATURE_FLAGS}")
set(KBUILD_FLAGS "${DRAIOS_DEBUG_FLAGS} ${DRAIOS_FEATURE_FLAGS}")
else()
set(CMAKE_BUILD_TYPE "release")
set(KBUILD_FLAGS "${FALCO_EXTRA_FEATURE_FLAGS}")
set(KBUILD_FLAGS "${DRAIOS_FEATURE_FLAGS}")
add_definitions(-DBUILD_TYPE_RELEASE)
endif()
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
@@ -89,7 +89,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "release")
set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -D_FORTIFY_SOURCE=2")
endif()
set(CMAKE_COMMON_FLAGS "${FALCO_SECURITY_FLAGS} -Wall -ggdb ${FALCO_EXTRA_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
set(CMAKE_COMMON_FLAGS "${FALCO_SECURITY_FLAGS} -Wall -ggdb ${DRAIOS_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
if(BUILD_WARNINGS_AS_ERRORS)
set(CMAKE_SUPPRESSED_WARNINGS
@@ -101,8 +101,8 @@ endif()
set(CMAKE_C_FLAGS "${CMAKE_COMMON_FLAGS}")
set(CMAKE_CXX_FLAGS "--std=c++0x ${CMAKE_COMMON_FLAGS} -Wno-class-memaccess")
set(CMAKE_C_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${DRAIOS_DEBUG_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${DRAIOS_DEBUG_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG")
@@ -114,11 +114,6 @@ set(DRIVER_NAME "falco")
set(DRIVER_DEVICE_NAME "falco")
set(DRIVERS_REPO "https://download.falco.org/driver")
# If no path is provided, try to search the BPF probe in: `home/.falco/falco-bpf.o`
# This is the same fallback that we had in the libraries: `SCAP_PROBE_BPF_FILEPATH`.
set(FALCO_PROBE_BPF_FILEPATH ".${DRIVER_NAME}/${DRIVER_NAME}-bpf.o")
add_definitions(-DFALCO_PROBE_BPF_FILEPATH="${FALCO_PROBE_BPF_FILEPATH}")
if(NOT DEFINED FALCO_COMPONENT_NAME)
set(FALCO_COMPONENT_NAME "${CMAKE_PROJECT_NAME}")
endif()

2
OWNERS
View File

@@ -3,9 +3,9 @@ approvers:
- leogr
- jasondellaluce
- fededp
- andreagit97
reviewers:
- kaizhe
- mfdii
emeritus_approvers:
- fntlnz
- kris-nova

View File

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

View File

@@ -26,8 +26,8 @@ else()
# In case you want to test against another driver version (or branch, or commit) just pass the variable -
# ie., `cmake -DDRIVER_VERSION=dev ..`
if(NOT DRIVER_VERSION)
set(DRIVER_VERSION "fd46dd139a8e35692a7d40ab2f0ed2016df827cf")
set(DRIVER_CHECKSUM "SHA256=7c14a4b5f282c9e1e4496f5416d73c3132c72954d581e1a737f82ffa0e3a6bdd")
set(DRIVER_VERSION "d673b2722e8a730013bfe525eaa417945ef8b723")
set(DRIVER_CHECKSUM "SHA256=de7a2fb42da781d5b867f25a009cfdf8b03a1c12dc9a5d2abba08c7afb5a14d4")
endif()
# cd /path/to/build && cmake /path/to/source

View File

@@ -27,8 +27,8 @@ else()
# In case you want to test against another falcosecurity/libs version (or branch, or commit) just pass the variable -
# ie., `cmake -DFALCOSECURITY_LIBS_VERSION=dev ..`
if(NOT FALCOSECURITY_LIBS_VERSION)
set(FALCOSECURITY_LIBS_VERSION "fd46dd139a8e35692a7d40ab2f0ed2016df827cf")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=7c14a4b5f282c9e1e4496f5416d73c3132c72954d581e1a737f82ffa0e3a6bdd")
set(FALCOSECURITY_LIBS_VERSION "d673b2722e8a730013bfe525eaa417945ef8b723")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=de7a2fb42da781d5b867f25a009cfdf8b03a1c12dc9a5d2abba08c7afb5a14d4")
endif()
# cd /path/to/build && cmake /path/to/source
@@ -49,6 +49,7 @@ if(MUSL_OPTIMIZED_BUILD)
add_definitions(-DMUSL_OPTIMIZED)
endif()
set(SCAP_BPF_PROBE_ENV_VAR_NAME "FALCO_BPF_PROBE")
set(SCAP_HOST_ROOT_ENV_VAR_NAME "HOST_ROOT")
if(NOT LIBSCAP_DIR)
@@ -68,7 +69,6 @@ set(USE_BUNDLED_TBB ON CACHE BOOL "")
set(USE_BUNDLED_B64 ON CACHE BOOL "")
set(USE_BUNDLED_JSONCPP ON CACHE BOOL "")
set(USE_BUNDLED_VALIJSON ON CACHE BOOL "")
set(USE_BUNDLED_RE2 ON CACHE BOOL "")
list(APPEND CMAKE_MODULE_PATH "${FALCOSECURITY_LIBS_SOURCE_DIR}/cmake/modules")

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,9 +15,10 @@
# limitations under the License.
#
# The latest Falco Engine version is 9.
# Starting with version 8, the Falco engine supports exceptions.
# However the Falco rules file does not use them by default.
- required_engine_version: 13
- required_engine_version: 9
# Currently disabled as read/write are ignored syscalls. The nearly
# similar open_write/open_read check for files being opened for
@@ -28,20 +29,13 @@
# condition: (syscall.type=read and evt.dir=> and fd.type in (file, directory))
- macro: open_write
condition: (evt.type in (open,openat,openat2) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0)
condition: evt.type in (open,openat,openat2) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0
- macro: open_read
condition: (evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0)
condition: evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0
- macro: open_directory
condition: (evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='d' and fd.num>=0)
# Failed file open attempts, useful to detect threat actors making mistakes
# https://man7.org/linux/man-pages/man3/errno.3.html
# evt.res=ENOENT - No such file or directory
# evt.res=EACCESS - Permission denied
- macro: open_file_failed
condition: (evt.type in (open,openat,openat2) and fd.typechar='f' and fd.num=-1 and evt.res startswith E)
condition: evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='d' and fd.num>=0
- macro: never_true
condition: (evt.num=0)
@@ -57,32 +51,32 @@
condition: (proc.name!="<NA>")
- macro: rename
condition: (evt.type in (rename, renameat, renameat2))
condition: evt.type in (rename, renameat, renameat2)
- macro: mkdir
condition: (evt.type in (mkdir, mkdirat))
condition: evt.type in (mkdir, mkdirat)
- macro: remove
condition: (evt.type in (rmdir, unlink, unlinkat))
condition: evt.type in (rmdir, unlink, unlinkat)
- macro: modify
condition: (rename or remove)
condition: rename or remove
- macro: spawned_process
condition: (evt.type in (execve, execveat) and evt.dir=<)
condition: evt.type in (execve, execveat) and evt.dir=<
- macro: create_symlink
condition: (evt.type in (symlink, symlinkat) and evt.dir=<)
condition: evt.type in (symlink, symlinkat) and evt.dir=<
- macro: create_hardlink
condition: (evt.type in (link, linkat) and evt.dir=<)
condition: evt.type in (link, linkat) and evt.dir=<
- macro: chmod
condition: (evt.type in (chmod, fchmod, fchmodat) and evt.dir=<)
# File categories
- macro: bin_dir
condition: (fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin))
condition: fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin)
- macro: bin_dir_mkdir
condition: >
@@ -111,7 +105,7 @@
evt.arg.newpath startswith /usr/sbin/)
- macro: etc_dir
condition: (fd.name startswith /etc/)
condition: fd.name startswith /etc/
# This detects writes immediately below / or any write anywhere below /root
- macro: root_dir
@@ -375,9 +369,11 @@
# use the fd.*ip and fd.*ip.name fields to match connection
# information against ips, netmasks, and complete domain names.
#
# To use this rule, you should enable it and
# To use this rule, you should modify consider_all_outbound_conns and
# populate allowed_{source,destination}_{ipaddrs,networks,domains} with the
# values that make sense for your environment.
- macro: consider_all_outbound_conns
condition: (never_true)
# Note that this can be either individual IPs or netmasks
- list: allowed_outbound_destination_ipaddrs
@@ -392,15 +388,17 @@
- rule: Unexpected outbound connection destination
desc: Detect any outbound connection to a destination outside of an allowed set of ips, networks, or domain names
condition: >
outbound and not
consider_all_outbound_conns and outbound and not
((fd.sip in (allowed_outbound_destination_ipaddrs)) or
(fd.snet in (allowed_outbound_destination_networks)) or
(fd.sip.name in (allowed_outbound_destination_domains)))
enabled: false
output: Disallowed outbound connection destination (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
priority: NOTICE
tags: [network]
- macro: consider_all_inbound_conns
condition: (never_true)
- list: allowed_inbound_source_ipaddrs
items: ['"127.0.0.1"']
@@ -413,11 +411,10 @@
- rule: Unexpected inbound connection source
desc: Detect any inbound connection from a source outside of an allowed set of ips, networks, or domain names
condition: >
inbound and not
consider_all_inbound_conns and inbound and not
((fd.cip in (allowed_inbound_source_ipaddrs)) or
(fd.cnet in (allowed_inbound_source_networks)) or
(fd.cip.name in (allowed_inbound_source_domains)))
enabled: false
output: Disallowed inbound connection source (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
priority: NOTICE
tags: [network]
@@ -467,23 +464,30 @@
tags: [file, mitre_persistence]
# This rule is not enabled by default, as there are many legitimate
# readers of shell config files.
# readers of shell config files. If you want to enable it, modify the
# following macro.
- macro: consider_shell_config_reads
condition: (never_true)
- rule: Read Shell Configuration File
desc: Detect attempts to read shell configuration files by non-shell programs
condition: >
open_read and
consider_shell_config_reads and
(fd.filename in (shell_config_filenames) or
fd.name in (shell_config_files) or
fd.directory in (shell_config_directories)) and
(not proc.name in (shell_binaries))
enabled: false
output: >
a shell configuration file was read by a non-shell program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
priority:
WARNING
tags: [file, mitre_discovery]
- macro: consider_all_cron_jobs
condition: (never_true)
- macro: user_known_cron_jobs
condition: (never_true)
@@ -492,8 +496,8 @@
condition: >
((open_write and fd.name startswith /etc/cron) or
(spawned_process and proc.name = "crontab")) and
consider_all_cron_jobs and
not user_known_cron_jobs
enabled: false
output: >
Cron jobs were scheduled to run (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
file=%fd.name container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
@@ -908,10 +912,7 @@
items: [/boot, /lib, /lib64, /usr/lib, /usr/local/lib, /usr/local/sbin, /usr/local/bin, /root/.ssh]
- macro: user_ssh_directory
condition: (fd.name contains '/.ssh/' and fd.name glob '/home/*/.ssh/*')
- macro: directory_traversal
condition: (fd.nameraw contains '../' and fd.nameraw glob '*../*../*')
condition: (fd.name glob '/home/*/.ssh/*')
# google_accounts_(daemon)
- macro: google_accounts_daemon_writing_ssh
@@ -956,24 +957,6 @@
priority: ERROR
tags: [filesystem, mitre_persistence]
# ******************************************************************************
# * "Directory traversal monitored file read" requires FALCO_ENGINE_VERSION 13 *
# ******************************************************************************
- rule: Directory traversal monitored file read
desc: >
Web applications can be vulnerable to directory traversal attacks that allow accessing files outside of the web app's root directory (e.g. Arbitrary File Read bugs).
System directories like /etc are typically accessed via absolute paths. Access patterns outside of this (here path traversal) can be regarded as suspicious.
This rule includes failed file open attempts.
condition: (open_read or open_file_failed) and (etc_dir or user_ssh_directory or fd.name startswith /root/.ssh or fd.name contains "id_rsa") and directory_traversal and not proc.pname in (shell_binaries)
enabled: true
output: >
Read monitored file via directory traversal (username=%user.name useruid=%user.uid user_loginuid=%user.loginuid program=%proc.name exe=%proc.exepath
command=%proc.cmdline parent=%proc.pname file=%fd.name fileraw=%fd.nameraw parent=%proc.pname
gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository returncode=%evt.res cwd=%proc.cwd)
priority: WARNING
tags: [filesystem, mitre_discovery, mitre_exfiltration, mitre_credential_access]
# This rule is disabled by default as many system management tools
# like ansible, etc can read these files/paths. Enable it using this macro.
@@ -2097,7 +2080,7 @@
# This list allows for easy additions to the set of commands allowed
# to run shells in containers without having to without having to copy
# and override the entire run shell in container macro. Once
# https://github.com/falcosecurity/falco/issues/255 is fixed this will be a
# https://github.com/draios/falco/issues/255 is fixed this will be a
# bit easier, as someone could append of any of the existing lists.
- list: user_known_shell_spawn_binaries
items: []
@@ -2160,7 +2143,6 @@
http_proxy_procs and
not allowed_ssh_proxy_env and
proc.env icontains HTTP_PROXY
enabled: false
output: >
Program run with disallowed HTTP_PROXY environment variable
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline env=%proc.env parent=%proc.pname container_id=%container.id image=%container.image.repository)
@@ -2676,6 +2658,9 @@
WARNING
tags: [process, mitre_defense_evasion]
- macro: consider_all_chmods
condition: (always_true)
- list: user_known_chmod_applications
items: [hyperkube, kubelet, k3s-agent]
@@ -2691,7 +2676,7 @@
this means that the application will run with the privileges of the owning user or group respectively.
Detect setuid or setgid bits set via chmod
condition: >
chmod and (evt.arg.mode contains "S_ISUID" or evt.arg.mode contains "S_ISGID")
consider_all_chmods and chmod and (evt.arg.mode contains "S_ISUID" or evt.arg.mode contains "S_ISGID")
and not proc.name in (user_known_chmod_applications)
and not exe_running_docker_save
and not user_known_set_setuid_or_setgid_bit_conditions
@@ -2706,7 +2691,10 @@
- list: exclude_hidden_directories
items: [/root/.cassandra]
# The rule is disabled by default.
# To use this rule, you should modify consider_hidden_file_creation.
- macro: consider_hidden_file_creation
condition: (never_true)
- macro: user_known_create_hidden_file_activities
condition: (never_true)
@@ -2716,9 +2704,9 @@
((modify and evt.arg.newpath contains "/.") or
(mkdir and evt.arg.path contains "/.") or
(open_write and evt.arg.flags contains "O_CREAT" and fd.name contains "/." and not fd.name pmatch (exclude_hidden_directories))) and
consider_hidden_file_creation and
not user_known_create_hidden_file_activities
and not exe_running_docker_save
enabled: false
output: >
Hidden file or directory created (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
file=%fd.name newpath=%evt.arg.newpath container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
@@ -2912,16 +2900,26 @@
priority: WARNING
tags: [container, mitre_execution]
# This rule is enabled by default.
# If you want to disable it, modify the following macro.
- macro: consider_packet_socket_communication
condition: (always_true)
- list: user_known_packet_socket_binaries
items: []
- rule: Packet socket created in container
desc: Detect new packet socket at the device driver (OSI Layer 2) level in a container. Packet socket could be used for ARP Spoofing and privilege escalation(CVE-2020-14386) by attacker.
condition: evt.type=socket and evt.arg[0]=AF_PACKET and container and not proc.name in (user_known_packet_socket_binaries)
condition: evt.type=socket and evt.arg[0]=AF_PACKET and consider_packet_socket_communication and container and not proc.name in (user_known_packet_socket_binaries)
output: Packet socket was created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline socket_info=%evt.args container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
priority: NOTICE
tags: [network, mitre_discovery]
# Change to (always_true) to enable rule 'Network connection outside local subnet'
- macro: enabled_rule_network_only_subnet
condition: (never_true)
# Namespaces where the rule is enforce
- list: namespace_scope_network_only_subnet
items: []
@@ -2932,8 +2930,8 @@
fd.ip = "0.0.0.0" or
fd.net = "127.0.0.0/8"
# # The rule is disabled by default.
# # How to test:
# # Change macro enabled_rule_network_only_subnet to condition: always_true
# # Add 'default' to namespace_scope_network_only_subnet
# # Run:
# kubectl run --generator=run-pod/v1 -n default -i --tty busybox --image=busybox --rm -- wget google.com -O /var/google.html
@@ -2942,11 +2940,11 @@
- rule: Network Connection outside Local Subnet
desc: Detect traffic to image outside local subnet.
condition: >
enabled_rule_network_only_subnet and
inbound_outbound and
container and
not network_local_subnet and
k8s.ns.name in (namespace_scope_network_only_subnet)
enabled: false
output: >
Network connection outside local subnet
(command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id
@@ -2955,6 +2953,9 @@
priority: WARNING
tags: [network]
- macro: allowed_port
condition: (never_true)
- list: allowed_image
items: [] # add image to monitor, i.e.: bitnami/nginx
@@ -2975,12 +2976,12 @@
- rule: Outbound or Inbound Traffic not to Authorized Server Process and Port
desc: Detect traffic that is not to authorized server process and port.
condition: >
allowed_port and
inbound_outbound and
container and
container.image.repository in (allowed_image) and
not proc.name in (authorized_server_binary) and
not fd.sport in (authorized_server_port)
enabled: false
output: >
Network connection outside authorized port and binary
(command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id
@@ -3009,7 +3010,7 @@
# Two things to pay attention to:
# 1) In most cases, 'docker cp' will not be identified, but the assumption is that if an attacker gained access to the container runtime daemon, they are already privileged
# 2) Drift rules will be noisy in environments in which containers are built (e.g. docker build)
# These two rules are not enabled by default.
# These two rules are not enabled by default. Use `never_true` in macro condition to enable them.
- macro: user_known_container_drift_activities
condition: (always_true)
@@ -3018,6 +3019,7 @@
desc: New executable created in a container due to chmod
condition: >
chmod and
consider_all_chmods and
container and
not runc_writing_exec_fifo and
not runc_writing_var_lib_docker and
@@ -3026,7 +3028,6 @@
((evt.arg.mode contains "S_IXUSR") or
(evt.arg.mode contains "S_IXGRP") or
(evt.arg.mode contains "S_IXOTH"))
enabled: false
output: Drift detected (chmod), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type)
priority: ERROR
@@ -3043,7 +3044,6 @@
not runc_writing_var_lib_docker and
not user_known_container_drift_activities and
evt.rawres>=0
enabled: false
output: Drift detected (open+create), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type)
priority: ERROR
@@ -3105,11 +3105,6 @@
- macro: mount_info
condition: (proc.args="" or proc.args intersects ("-V", "-l", "-h"))
- macro: known_gke_mount_in_privileged_containers
condition:
(k8s.ns.name = kube-system
and container.image.repository = gke.gcr.io/gcp-compute-persistent-disk-csi-driver)
- macro: user_known_mount_in_privileged_containers
condition: (never_true)
@@ -3120,18 +3115,21 @@
and container.privileged=true
and proc.name=mount
and not mount_info
and not known_gke_mount_in_privileged_containers
and not user_known_mount_in_privileged_containers
output: Mount was executed inside a privileged container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
priority: WARNING
tags: [container, cis, mitre_lateral_movement]
- macro: consider_userfaultfd_activities
condition: (always_true)
- list: user_known_userfaultfd_processes
items: []
- rule: Unprivileged Delegation of Page Faults Handling to a Userspace Process
desc: Detect a successful unprivileged userfaultfd syscall which might act as an attack primitive to exploit other bugs
condition: >
consider_userfaultfd_activities and
evt.type = userfaultfd and
user.uid != 0 and
(evt.rawres >= 0 or evt.res != -1) and
@@ -3205,36 +3203,6 @@
priority: CRITICAL
tags: [mitre_initial_access]
- list: docker_binaries
items: [dockerd, containerd-shim, "runc:[1:CHILD]", pause]
- macro: docker_procs
condition: proc.name in (docker_binaries)
- rule: Modify Container Entrypoint
desc: This rule detect an attempt to write on container entrypoint symlink (/proc/self/exe). Possible CVE-2019-5736 Container Breakout exploitation attempt.
condition: >
open_write and container and (fd.name=/proc/self/exe or fd.name startswith /proc/self/fd/) and not docker_procs and not proc.cmdline = "runc:[1:CHILD] init"
enabled: false
output: >
Detect Potential Container Breakout Exploit (CVE-2019-5736) (user=%user.name process=%proc.name file=%fd.name cmdline=%proc.cmdline %container.info)
priority: WARNING
tags: [container, filesystem, mitre_initial_access]
# Application rules have moved to application_rules.yaml. Please look
# there if you want to enable them by adding to
# falco_rules.local.yaml.
- list: known_binaries_to_read_environment_variables_from_proc_files
items: [scsi_id, argoexec]
- rule: Read environment variable from /proc files
desc: An attempt to read process environment variables from /proc files
condition: >
open_read and container and (fd.name glob /proc/*/environ)
and not proc.name in (known_binaries_to_read_environment_variables_from_proc_files)
output: >
Environment variables were retrieved from /proc files (user=%user.name user_loginuid=%user.loginuid program=%proc.name
command=%proc.cmdline file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)
priority: WARNING
tags: [filesystem, mitre_credential_access, mitre_discovery]

View File

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

View File

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

View File

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

View File

@@ -97,7 +97,6 @@ class FalcoTest(Test):
self.all_events = self.params.get('all_events', '*', default=False)
self.priority = self.params.get('priority', '*', default='debug')
self.addl_cmdline_opts = self.params.get('addl_cmdline_opts', '*', default='')
self.enable_source = self.params.get('enable_source', '*', default='')
self.rules_file = self.params.get(
'rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
@@ -115,13 +114,6 @@ class FalcoTest(Test):
self.json_output = True
if not isinstance(self.validate_rules_file, list):
self.validate_rules_file = [self.validate_rules_file]
# can be either empty, a string, or a list
if self.enable_source == '':
self.enable_source = []
else:
if not isinstance(self.enable_source, list):
self.enable_source = [self.enable_source]
self.rules_args = ""
@@ -240,7 +232,7 @@ class FalcoTest(Test):
self.grpcurl_res = None
self.grpc_observer = None
self.grpc_address = self.params.get(
'address', 'grpc/*', default='/run/falco/falco.sock')
'address', 'grpc/*', default='/var/run/falco.sock')
if self.grpc_address.startswith("unix://"):
self.is_grpc_using_unix_socket = True
self.grpc_address = self.grpc_address[len("unix://"):]
@@ -638,15 +630,10 @@ class FalcoTest(Test):
if self.trace_file:
trace_arg = "-e {}".format(self.trace_file)
extra_cmdline = ''
for source in self.enable_source:
extra_cmdline += ' --enable-source="{}"'.format(source)
extra_cmdline += ' ' + self.addl_cmdline_opts
# Run falco
cmd = '{} {} {} -c {} {} -o json_output={} -o json_include_output_property={} -o json_include_tags_property={} -o priority={} -v {}'.format(
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output,
self.json_include_output_property, self.json_include_tags_property, self.priority, extra_cmdline)
self.json_include_output_property, self.json_include_tags_property, self.priority, self.addl_cmdline_opts)
for tag in self.disable_tags:
cmd += ' -T {}'.format(tag)
@@ -663,7 +650,7 @@ class FalcoTest(Test):
if self.time_iso_8601:
cmd += ' -o time_format_iso_8601=true'
self.falco_proc = process.SubProcess(cmd, env=dict(os.environ, FALCO_HOSTNAME="test-falco-hostname"))
self.falco_proc = process.SubProcess(cmd)
res = self.falco_proc.run(timeout=180, sig=9)

View File

@@ -285,7 +285,7 @@ trace_files: !mux
invalid_not_yaml:
exit_status: 1
validate_errors:
- item_type: rules content
- item_type: file
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Rules content is not yaml"
@@ -296,7 +296,7 @@ trace_files: !mux
invalid_not_array:
exit_status: 1
validate_errors:
- item_type: rules content
- item_type: file
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Rules content is not yaml array of objects"
@@ -307,7 +307,7 @@ trace_files: !mux
invalid_array_item_not_object:
exit_status: 1
validate_errors:
- item_type: rules content item
- item_type: item
item_name: ""
code: LOAD_ERR_YAML_VALIDATE
message: "Unexpected element type. Each element should be a yaml associative array."
@@ -329,7 +329,7 @@ trace_files: !mux
invalid_yaml_parse_error:
exit_status: 1
validate_errors:
- item_type: rules content
- item_type: file
item_name: ""
code: LOAD_ERR_YAML_PARSE
message: "yaml-cpp: error at line 1, column 11: illegal map value"
@@ -1098,7 +1098,7 @@ trace_files: !mux
- rules/catchall_order.yaml
detect_counts:
- open_dev_null: 1
dev_null: 6
dev_null: 0
trace_file: trace_files/cat_write.scap
validate_skip_unknown_noevt:

View File

@@ -35,7 +35,6 @@ trace_files: !mux
stdout_contains: "ct.id"
detect_create_instance:
enable_source: aws_cloudtrail
detect: True
detect_level: INFO
rules_file:
@@ -45,7 +44,6 @@ trace_files: !mux
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
detect_create_instance_bigevent:
enable_source: aws_cloudtrail
detect: True
detect_level: INFO
rules_file:
@@ -54,9 +52,16 @@ trace_files: !mux
- 'Cloudtrail Create Instance': 1
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances_bigevent.yaml
multiple_source_plugins:
exit_status: 1
stderr_contains: "Can not load multiple plugins with event sourcing capability: 'cloudtrail' already loaded."
conf_file: BUILD_DIR/test/confs/plugins/multiple_source_plugins.yaml
rules_file:
- rules/plugins/cloudtrail_create_instances.yaml
incompatible_extract_sources:
exit_status: 1
stderr_contains: "Plugin '.*' has field extraction capability but is not compatible with any known event source"
stderr_contains: "Plugin '.*' has field extraction capability but is not compatible with any enabled event source"
conf_file: BUILD_DIR/test/confs/plugins/incompatible_extract_sources.yaml
rules_file:
- rules/plugins/cloudtrail_create_instances.yaml

View File

@@ -123,10 +123,8 @@ traces: !mux
# falco-event-generator.scap so the rule is still being tested.
run-shell-untrusted:
trace_file: traces-positive/run-shell-untrusted.scap
detect: True
detect: False
detect_level: DEBUG
detect_counts:
- "Run shell untrusted": 1
system-binaries-network-activity:
trace_file: traces-positive/system-binaries-network-activity.scap

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,10 +35,10 @@ string to_string(set<uint16_t> s)
return out;
}
void compare_evttypes(std::unique_ptr<ast::expr> f, set<uint16_t> &expected)
void compare_evttypes(ast::expr* f, set<uint16_t> &expected)
{
set<uint16_t> actual;
filter_evttype_resolver().evttypes(f.get(), actual);
filter_evttype_resolver().evttypes(f, actual);
for(auto &etype : expected)
{
REQUIRE(actual.find(etype) != actual.end());
@@ -49,7 +49,7 @@ void compare_evttypes(std::unique_ptr<ast::expr> f, set<uint16_t> &expected)
}
}
std::unique_ptr<ast::expr> compile(const string &fltstr)
ast::expr* compile(const string &fltstr)
{
return libsinsp::filter::parser(fltstr).parse();
}
@@ -98,138 +98,138 @@ TEST_CASE("Should find event types from filter", "[rule_loader]")
SECTION("evt_type_eq")
{
auto f = compile("evt.type=openat");
compare_evttypes(std::move(f), openat_only);
compare_evttypes(f, openat_only);
}
SECTION("evt_type_in")
{
auto f = compile("evt.type in (openat, close)");
compare_evttypes(std::move(f), openat_close);
compare_evttypes(f, openat_close);
}
SECTION("evt_type_ne")
{
auto f = compile("evt.type!=openat");
compare_evttypes(std::move(f), not_openat);
compare_evttypes(f, not_openat);
}
SECTION("not_evt_type_eq")
{
auto f = compile("not evt.type=openat");
compare_evttypes(std::move(f), not_openat);
compare_evttypes(f, not_openat);
}
SECTION("not_evt_type_in")
{
auto f = compile("not evt.type in (openat, close)");
compare_evttypes(std::move(f), not_openat_close);
compare_evttypes(f, not_openat_close);
}
SECTION("not_evt_type_ne")
{
auto f = compile("not evt.type != openat");
compare_evttypes(std::move(f), openat_only);
compare_evttypes(f, openat_only);
}
SECTION("evt_type_or")
{
auto f = compile("evt.type=openat or evt.type=close");
compare_evttypes(std::move(f), openat_close);
compare_evttypes(f, openat_close);
}
SECTION("not_evt_type_or")
{
auto f = compile("evt.type!=openat or evt.type!=close");
compare_evttypes(std::move(f), all_events);
compare_evttypes(f, all_events);
}
SECTION("evt_type_or_ne")
{
auto f = compile("evt.type=close or evt.type!=openat");
compare_evttypes(std::move(f), not_openat);
compare_evttypes(f, not_openat);
}
SECTION("evt_type_and")
{
auto f = compile("evt.type=close and evt.type=openat");
compare_evttypes(std::move(f), no_events);
compare_evttypes(f, no_events);
}
SECTION("evt_type_and_non_evt_type")
{
auto f = compile("evt.type=openat and proc.name=nginx");
compare_evttypes(std::move(f), openat_only);
compare_evttypes(f, openat_only);
}
SECTION("evt_type_and_non_evt_type_not")
{
auto f = compile("evt.type=openat and not proc.name=nginx");
compare_evttypes(std::move(f), openat_only);
compare_evttypes(f, openat_only);
}
SECTION("evt_type_and_nested")
{
auto f = compile("evt.type=openat and (proc.name=nginx)");
compare_evttypes(std::move(f), openat_only);
compare_evttypes(f, openat_only);
}
SECTION("evt_type_and_nested_multi")
{
auto f = compile("evt.type=openat and (evt.type=close and proc.name=nginx)");
compare_evttypes(std::move(f), no_events);
compare_evttypes(f, no_events);
}
SECTION("non_evt_type")
{
auto f = compile("proc.name=nginx");
compare_evttypes(std::move(f), all_events);
compare_evttypes(f, all_events);
}
SECTION("non_evt_type_or")
{
auto f = compile("evt.type=openat or proc.name=nginx");
compare_evttypes(std::move(f), all_events);
compare_evttypes(f, all_events);
}
SECTION("non_evt_type_or_nested_first")
{
auto f = compile("(evt.type=openat) or proc.name=nginx");
compare_evttypes(std::move(f), all_events);
compare_evttypes(f, all_events);
}
SECTION("non_evt_type_or_nested_second")
{
auto f = compile("evt.type=openat or (proc.name=nginx)");
compare_evttypes(std::move(f), all_events);
compare_evttypes(f, all_events);
}
SECTION("non_evt_type_or_nested_multi")
{
auto f = compile("evt.type=openat or (evt.type=close and proc.name=nginx)");
compare_evttypes(std::move(f), openat_close);
compare_evttypes(f, openat_close);
}
SECTION("non_evt_type_or_nested_multi_not")
{
auto f = compile("evt.type=openat or not (evt.type=close and proc.name=nginx)");
compare_evttypes(std::move(f), not_close);
compare_evttypes(f, not_close);
}
SECTION("non_evt_type_and_nested_multi_not")
{
auto f = compile("evt.type=openat and not (evt.type=close and proc.name=nginx)");
compare_evttypes(std::move(f), openat_only);
compare_evttypes(f, openat_only);
}
SECTION("ne_and_and")
{
auto f = compile("evt.type!=openat and evt.type!=close");
compare_evttypes(std::move(f), not_openat_close);
compare_evttypes(f, not_openat_close);
}
SECTION("not_not")
{
auto f = compile("not (not evt.type=openat)");
compare_evttypes(std::move(f), openat_only);
compare_evttypes(f, openat_only);
}
}

View File

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

View File

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

View File

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

View File

@@ -41,9 +41,7 @@ using namespace std;
using namespace falco;
falco_engine::falco_engine(bool seed_rng)
: m_syscall_source(NULL),
m_syscall_source_idx(SIZE_MAX),
m_next_ruleset_id(0),
: m_next_ruleset_id(0),
m_min_priority(falco_common::PRIORITY_DEBUG),
m_sampling_ratio(1), m_sampling_multiplier(0),
m_replace_container_info(false)
@@ -69,7 +67,7 @@ uint32_t falco_engine::engine_version()
return (uint32_t) FALCO_ENGINE_VERSION;
}
const falco_source* falco_engine::find_source(const std::string& name) const
falco_source* falco_engine::find_source(const std::string& name)
{
auto ret = m_sources.at(name);
if(!ret)
@@ -79,7 +77,7 @@ const falco_source* falco_engine::find_source(const std::string& name) const
return ret;
}
const falco_source* falco_engine::find_source(std::size_t index) const
falco_source* falco_engine::find_source(std::size_t index)
{
auto ret = m_sources.at(index);
if(!ret)
@@ -96,7 +94,7 @@ static std::string fieldclass_key(const gen_event_filter_factory::filter_fieldcl
return fld_info.name + fld_info.shortdesc;
}
void falco_engine::list_fields(std::string &source, bool verbose, bool names_only, bool markdown) const
void falco_engine::list_fields(std::string &source, bool verbose, bool names_only, bool markdown)
{
// Maps from field class name + short desc to list of event
// sources for which this field class can be used.
@@ -104,14 +102,14 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
// Do a first pass to group together classes that are
// applicable to multiple event sources.
for(const auto &it : m_sources)
for(auto &it : m_sources)
{
if(source != "" && source != it.name)
{
continue;
}
for(const auto &fld_class : it.filter_factory->get_fields())
for(auto &fld_class : it.filter_factory->get_fields())
{
fieldclass_event_sources[fieldclass_key(fld_class)].insert(it.name);
}
@@ -123,7 +121,7 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
// In the second pass, actually print info, skipping duplicate
// field classes and also printing info on supported sources.
for(const auto &it : m_sources)
for(auto &it : m_sources)
{
if(source != "" && source != it.name)
{
@@ -196,15 +194,6 @@ std::unique_ptr<load_result> falco_engine::load_rules(const std::string &rules_c
m_rule_loader.compile(cfg, m_rules);
}
if (cfg.res->successful())
{
m_rule_stats_manager.clear();
for (const auto &r : m_rules)
{
m_rule_stats_manager.on_rule_loaded(r);
}
}
return std::move(cfg.res);
}
@@ -245,7 +234,7 @@ void falco_engine::enable_rule(const string &substring, bool enabled, const stri
uint16_t ruleset_id = find_ruleset_id(ruleset);
bool match_exact = false;
for(const auto &it : m_sources)
for(auto &it : m_sources)
{
if(enabled)
{
@@ -263,7 +252,7 @@ void falco_engine::enable_rule_exact(const string &rule_name, bool enabled, cons
uint16_t ruleset_id = find_ruleset_id(ruleset);
bool match_exact = true;
for(const auto &it : m_sources)
for(auto &it : m_sources)
{
if(enabled)
{
@@ -280,7 +269,7 @@ void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, con
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
for(const auto &it : m_sources)
for(auto &it : m_sources)
{
if(enabled)
{
@@ -313,7 +302,7 @@ uint64_t falco_engine::num_rules_for_ruleset(const std::string &ruleset)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
uint64_t ret = 0;
for (const auto &src : m_sources)
for (auto &src : m_sources)
{
ret += src.ruleset->enabled_count(ruleset_id);
}
@@ -326,44 +315,28 @@ void falco_engine::evttypes_for_ruleset(std::string &source, std::set<uint16_t>
}
std::shared_ptr<gen_event_formatter> falco_engine::create_formatter(const std::string &source,
const std::string &output) const
const std::string &output)
{
return find_source(source)->formatter_factory->create_formatter(output);
}
unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id)
{
// note: there are no thread-safety guarantees on the filter_ruleset::run()
// method, but the thread-safety assumptions of falco_engine::process_event()
// imply that concurrent invokers use different and non-switchable values of
// source_idx, which means that at any time each filter_ruleset will only
// be accessed by a single thread.
const falco_source *source;
if(source_idx == m_syscall_source_idx)
{
source = m_syscall_source;
}
else
{
source = find_source(source_idx);
}
if(should_drop_evt() || !source || !source->ruleset->run(ev, source->m_rule, ruleset_id))
falco_rule rule;
if(should_drop_evt() || !find_source(source_idx)->ruleset->run(ev, rule, ruleset_id))
{
return unique_ptr<struct rule_result>();
}
unique_ptr<struct rule_result> res(new rule_result());
res->evt = ev;
res->rule = source->m_rule.name;
res->source = source->m_rule.source;
res->format = source->m_rule.output;
res->priority_num = source->m_rule.priority;
res->tags = source->m_rule.tags;
res->exception_fields = source->m_rule.exception_fields;
m_rule_stats_manager.on_event(source->m_rule);
res->rule = rule.name;
res->source = rule.source;
res->format = rule.output;
res->priority_num = rule.priority;
res->tags = rule.tags;
res->exception_fields = rule.exception_fields;
m_rule_stats_manager.on_event(rule);
return res;
}
@@ -379,15 +352,7 @@ std::size_t falco_engine::add_source(const std::string &source,
// evttype_index_ruleset is the default ruleset implementation
std::shared_ptr<filter_ruleset_factory> ruleset_factory(
new evttype_index_ruleset_factory(filter_factory));
size_t idx = add_source(source, filter_factory, formatter_factory, ruleset_factory);
if(source == falco_common::syscall_source)
{
m_syscall_source_idx = idx;
m_syscall_source = find_source(m_syscall_source_idx);
}
return idx;
return add_source(source, filter_factory, formatter_factory, ruleset_factory);
}
std::size_t falco_engine::add_source(const std::string &source,
@@ -404,7 +369,7 @@ std::size_t falco_engine::add_source(const std::string &source,
return m_sources.insert(src, source);
}
void falco_engine::describe_rule(string *rule) const
void falco_engine::describe_rule(string *rule)
{
static const char* rule_fmt = "%-50s %s\n";
fprintf(stdout, rule_fmt, "Rule", "Description");
@@ -425,7 +390,7 @@ void falco_engine::describe_rule(string *rule) const
}
}
void falco_engine::print_stats() const
void falco_engine::print_stats()
{
string out;
m_rule_stats_manager.format(m_rules, out);
@@ -433,7 +398,7 @@ void falco_engine::print_stats() const
fprintf(stdout, "%s", out.c_str());
}
bool falco_engine::is_source_valid(const std::string &source) const
bool falco_engine::is_source_valid(const std::string &source)
{
return m_sources.at(source) != nullptr;
}
@@ -475,68 +440,53 @@ void falco_engine::interpret_load_result(std::unique_ptr<load_result>& res,
}
}
static bool check_plugin_requirement_alternatives(
const std::vector<falco_engine::plugin_version_requirement>& plugins,
const rule_loader::plugin_version_info::requirement_alternatives& alternatives,
bool falco_engine::check_plugin_requirements(
const std::vector<plugin_version_requirement>& plugins,
std::string& err)
{
for (const auto &req : alternatives)
for (const auto &req : m_rule_loader.required_plugin_versions())
{
bool found = false;
for (const auto &plugin : plugins)
{
if (req.name == plugin.name)
if (req.first == plugin.name)
{
sinsp_version req_version(req.version);
found = true;
sinsp_version plugin_version(plugin.version);
if(!plugin_version.m_valid)
{
err = "Plugin '" + plugin.name
err = "Plugin '" + req.first
+ "' has invalid version string '"
+ plugin.version + "'";
return false;
}
if (!plugin_version.check(req_version))
for (const auto &reqver: req.second)
{
err = "Plugin '" + plugin.name
+ "' version '" + plugin.version
+ "' is not compatible with required plugin version '"
+ req.version + "'";
return false;
sinsp_version req_version(reqver);
if (!plugin_version.check(req_version))
{
err = "Plugin '" + plugin.name
+ "' version '" + plugin.version
+ "' is not compatible with required plugin version '"
+ reqver + "'";
return false;
}
}
return true;
}
}
}
return false;
}
bool falco_engine::check_plugin_requirements(
const std::vector<plugin_version_requirement>& plugins,
std::string& err) const
{
err = "";
for (const auto &alternatives : m_rule_loader.required_plugin_versions())
{
if (!check_plugin_requirement_alternatives(plugins, alternatives, err))
if (!found)
{
if (err.empty())
{
for (const auto& req : alternatives)
{
err += err.empty() ? "" : ", ";
err += req.name + " (>= " + req.version + ")";
}
err = "Plugin requirement not satisfied, must load one of: " + err;
}
err = "Plugin '" + req.first + "' is required but not loaded";
return false;
}
}
return true;
}
void falco_engine::complete_rule_loading() const
void falco_engine::complete_rule_loading()
{
for (const auto &src : m_sources)
for (auto &src : m_sources)
{
src.ruleset->on_loading_complete();
}
@@ -558,7 +508,7 @@ void falco_engine::set_extra(string &extra, bool replace_container_info)
m_replace_container_info = replace_container_info;
}
inline bool falco_engine::should_drop_evt() const
inline bool falco_engine::should_drop_evt()
{
if(m_sampling_multiplier == 0)
{

View File

@@ -22,7 +22,6 @@ limitations under the License.
#pragma once
#include <atomic>
#include <string>
#include <memory>
#include <set>
@@ -57,7 +56,7 @@ public:
// Print to stdout (using printf) a description of each field supported by this engine.
// If source is non-empty, only fields for the provided source are printed.
void list_fields(std::string &source, bool verbose, bool names_only, bool markdown) const;
void list_fields(std::string &source, bool verbose, bool names_only, bool markdown);
//
// Load rules either directly or from a filename.
@@ -99,7 +98,7 @@ public:
// Internally, this can be used to release unused resources before starting
// processing events with process_event().
//
void complete_rule_loading() const;
void complete_rule_loading();
// Only load rules having this priority or more severe.
void set_min_priority(falco_common::priority_type priority);
@@ -122,12 +121,12 @@ public:
// Print details on the given rule. If rule is NULL, print
// details on all rules.
//
void describe_rule(std::string *rule) const;
void describe_rule(std::string *rule);
//
// Print statistics on how many events matched each rule.
//
void print_stats() const;
void print_stats();
//
// Set the sampling ratio, which can affect which events are
@@ -166,33 +165,18 @@ public:
//
// Given an event, check it against the set of rules in the
// engine and if a matching rule is found, return details on
// the rule that matched. If no rule matched, returns nullptr.
// the rule that matched. If no rule matched, returns NULL.
//
// This method should be invoked only after having initialized and
// configured the engine. In particular, invoking this with a source_idx
// not previosly-returned by a call to add_source() would cause a
// falco_exception to be thrown.
// When ruleset_id is provided, use the enabled/disabled status
// associated with the provided ruleset. This is only useful
// when you have previously called enable_rule/enable_rule_by_tag
// with a ruleset string.
//
// This method is thread-safe only with the assumption that every invoker
// uses a different source_idx. Moreover, each invoker must not switch
// source_idx in subsequent invocations of this method.
// Considering that each invoker is related to a unique event source, it
// is safe to assume that each invoker will pass a different event
// to this method too, since two distinct sources cannot possibly produce
// the same event. Lastly, filterchecks and formatters (and their factories)
// that are used to populate the conditions for a given event-source
// ruleset must not be reused across rulesets of other event sources.
// These assumptions guarantee thread-safety because internally the engine
// is partitioned by event sources. However, each ruleset assigned to each
// event source is not thread-safe of its own, so invoking this method
// concurrently with the same source_idx would inherently cause data races
// and lead to undefined behavior.
// the returned rule_result is allocated and must be delete()d.
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id);
//
// Wrapper assuming the default ruleset.
//
// This inherits the same thread-safety guarantees.
// Wrapper assuming the default ruleset
//
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev);
@@ -216,7 +200,7 @@ public:
// Return whether or not there is a valid filter/formatter
// factory for this source.
bool is_source_valid(const std::string &source) const;
bool is_source_valid(const std::string &source);
//
// Given an event source and ruleset, fill in a bitset
@@ -232,10 +216,10 @@ public:
// event.
//
std::shared_ptr<gen_event_formatter> create_formatter(const std::string &source,
const std::string &output) const;
const std::string &output);
// The rule loader definition is aliased as it is exactly what we need
typedef rule_loader::plugin_version_info::requirement plugin_version_requirement;
typedef rule_loader::plugin_version_info plugin_version_requirement;
//
// Returns true if the provided list of plugins satisfies all the
@@ -246,7 +230,7 @@ public:
//
bool check_plugin_requirements(
const std::vector<plugin_version_requirement>& plugins,
std::string& err) const;
std::string& err);
private:
@@ -262,21 +246,15 @@ private:
indexed_vector<falco_source> m_sources;
const falco_source* find_source(std::size_t index) const;
const falco_source* find_source(const std::string& name) const;
// To allow the engine to be extremely fast for syscalls (can
// be > 1M events/sec), we save the syscall source/source_idx
// separately and check it explicitly in process_event()
const falco_source* m_syscall_source;
std::atomic<size_t> m_syscall_source_idx;
falco_source* find_source(std::size_t index);
falco_source* find_source(const std::string& name);
//
// Determine whether the given event should be matched at all
// against the set of rules, given the current sampling
// ratio/multiplier.
//
inline bool should_drop_evt() const;
inline bool should_drop_evt();
rule_loader m_rule_loader;
indexed_vector<falco_rule> m_rules;

View File

@@ -16,7 +16,7 @@ limitations under the License.
// The version of rules/filter fields/etc supported by this Falco
// engine.
#define FALCO_ENGINE_VERSION (15)
#define FALCO_ENGINE_VERSION (14)
// This is the result of running "falco --list -N | sha256sum" and
// represents the fields supported by this version of Falco. It's used

View File

@@ -32,10 +32,6 @@ struct falco_source
std::shared_ptr<gen_event_filter_factory> filter_factory;
std::shared_ptr<gen_event_formatter_factory> formatter_factory;
// Used by the filter_ruleset interface. Filled in when a rule
// matches an event.
mutable falco_rule m_rule;
inline bool is_field_defined(std::string field) const
{
auto *chk = filter_factory->new_filtercheck(field.c_str());

View File

@@ -27,24 +27,21 @@ static bool is_evttype_operator(const string& op)
return op == "==" || op == "=" || op == "!=" || op == "in";
}
size_t falco_event_types::get_ppm_event_max()
void filter_evttype_resolver::visitor::inversion(set<uint16_t>& types)
{
return PPM_EVENT_MAX;
}
void filter_evttype_resolver::visitor::inversion(falco_event_types& types)
{
falco_event_types all_types;
set<uint16_t> all_types;
evttypes("", all_types);
if (types != all_types) // we don't invert the "all types" set
{
types = all_types.diff(types);
set<uint16_t> diff = types;
types.clear();
set_difference(
all_types.begin(), all_types.end(), diff.begin(), diff.end(),
inserter(types, types.begin()));
}
}
void filter_evttype_resolver::visitor::evttypes(string evtname, falco_event_types& out)
void filter_evttype_resolver::visitor::evttypes(string evtname, set<uint16_t>& out)
{
// Fill in from 2 to PPM_EVENT_MAX-1. 0 and 1 are excluded as
// those are PPM_GENERIC_E/PPME_GENERIC_X
@@ -62,38 +59,42 @@ void filter_evttype_resolver::visitor::evttypes(string evtname, falco_event_type
void filter_evttype_resolver::evttypes(
ast::expr* filter,
std::set<uint16_t>& out) const
set<uint16_t>& out) const
{
visitor v;
v.m_expect_value = false;
v.m_last_node_evttypes.clear();
filter->accept(&v);
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;});
out.insert(v.m_last_node_evttypes.begin(), v.m_last_node_evttypes.end());
}
void filter_evttype_resolver::evttypes(
shared_ptr<ast::expr> filter,
std::set<uint16_t>& out) const
set<uint16_t>& out) const
{
visitor v;
v.m_expect_value = false;
v.m_last_node_evttypes.clear();
filter.get()->accept(&v);
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;} );
out.insert(v.m_last_node_evttypes.begin(), v.m_last_node_evttypes.end());
}
// "and" nodes evttypes are the intersection of the evttypes of their children.
// we initialize the set with "all event types"
void filter_evttype_resolver::visitor::visit(ast::and_expr* e)
{
falco_event_types types;
set<uint16_t> types, inters;
evttypes("", types);
m_last_node_evttypes.clear();
for (auto &c : e->children)
{
falco_event_types inters;
inters.clear();
c->accept(this);
types = types.intersect(m_last_node_evttypes);
set_intersection(
types.begin(), types.end(),
m_last_node_evttypes.begin(), m_last_node_evttypes.end(),
inserter(inters, inters.begin()));
types = inters;
}
m_last_node_evttypes = types;
}
@@ -101,12 +102,12 @@ void filter_evttype_resolver::visitor::visit(ast::and_expr* e)
// "or" nodes evttypes are the union of the evttypes their children
void filter_evttype_resolver::visitor::visit(ast::or_expr* e)
{
falco_event_types types;
set<uint16_t> types;
m_last_node_evttypes.clear();
for (auto &c : e->children)
{
c->accept(this);
types.merge(m_last_node_evttypes);
types.insert(m_last_node_evttypes.begin(), m_last_node_evttypes.end());
}
m_last_node_evttypes = types;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -131,5 +131,5 @@ public:
private:
std::vector<T> m_entries;
std::unordered_map<std::string, size_t> m_index;
std::map<std::string, size_t> m_index;
};

View File

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

View File

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

View File

@@ -21,7 +21,6 @@ limitations under the License.
#include "filter_evttype_resolver.h"
#include "filter_warning_resolver.h"
#include <version.h>
#include <string>
#include <sstream>
#define MAX_VISIBILITY ((uint32_t) -1)
@@ -36,100 +35,22 @@ static string s_default_extra_fmt = "%container.name (id=%container.id)";
using namespace std;
using namespace libsinsp::filter;
static const std::string item_type_strings[] = {
"value for",
"exceptions",
"exception",
"exception values",
"exception value",
"rules content",
"rules content item",
"required_engine_version",
"required plugin versions",
"required plugin versions entry",
"required plugin versions alternative",
"list",
"list item",
"macro",
"macro condition",
"rule",
"rule condition",
"condition expression",
"rule output",
"rule output expression",
"rule priority"
};
const std::string& rule_loader::context::item_type_as_string(enum item_type it)
{
return item_type_strings[it];
}
rule_loader::context::context(const std::string& name)
: name(name)
{
// This ensures that every context has one location, even if
// that location is effectively the whole document.
location loc = {name, position(), rule_loader::context::RULES_CONTENT, ""};
m_locs.push_back(loc);
}
rule_loader::context::context(const YAML::Node &item,
const item_type item_type,
const std::string item_type,
const std::string item_name,
const context& parent)
{
init(parent.name(), position(item.Mark()), item_type, item_name, parent);
}
rule_loader::context::context(const libsinsp::filter::ast::pos_info& pos,
const std::string& condition,
const context& parent)
: alt_content(condition)
{
// Contexts based on conditions don't use the
// filename. Instead the "name" is just the condition, and
// uses a short prefix of the condition.
std::string name = "\"" + condition.substr(0, 20) + "...\"";
std::replace(name.begin(), name.end(), '\n', ' ');
std::replace(name.begin(), name.end(), '\r', ' ');
std::string item_name = "";
// Convert the parser position to a context location. Both
// they have the same basic info (position, line, column).
// parser line/columns are 1-indexed while yaml marks are
// 0-indexed, though.
position condpos;
condpos.pos = pos.idx;
condpos.line = pos.line-1;
condpos.column = pos.col-1;
init(name, condpos, rule_loader::context::CONDITION_EXPRESSION, item_name, parent);
}
const std::string& rule_loader::context::name() const
{
// All valid contexts should have at least one location.
if(m_locs.empty())
{
throw falco_exception("rule_loader::context without location?");
}
return m_locs.front().name;
}
void rule_loader::context::init(const std::string& name,
const position& pos,
const item_type item_type,
const std::string item_name,
const context& parent)
: name(parent.name)
{
// Copy parent locations
m_locs = parent.m_locs;
// Add current item to back
location loc = {name, pos, item_type, item_name};
location loc = {item.Mark(), item_type, item_name};
m_locs.push_back(loc);
}
@@ -137,10 +58,12 @@ std::string rule_loader::context::as_string()
{
std::ostringstream os;
// All valid contexts should have at least one location.
// If no locations (can happen for initial file-level
// context), just note it was somewhere in the file
if(m_locs.empty())
{
throw falco_exception("rule_loader::context without location?");
os << "In " << name << ":" << std::endl;
return os.str();
}
bool first = true;
@@ -150,7 +73,7 @@ std::string rule_loader::context::as_string()
os << (first ? "In " : " ");
first = false;
os << item_type_as_string(loc.item_type);
os << loc.item_type;
if(!loc.item_name.empty())
{
os << " '" << loc.item_name << "'";
@@ -158,9 +81,9 @@ std::string rule_loader::context::as_string()
os << ": ";
os << "("
<< loc.name << ":"
<< loc.pos.line << ":"
<< loc.pos.column
<< name << ":"
<< loc.mark.line << ":"
<< loc.mark.column
<< ")" << std::endl;
}
@@ -173,28 +96,41 @@ nlohmann::json rule_loader::context::as_json()
ret["locations"] = nlohmann::json::array();
// All valid contexts should have at least one location.
if(m_locs.empty())
{
throw falco_exception("rule_loader::context without location?");
}
for(auto& loc : m_locs)
{
nlohmann::json jloc, jpos;
jloc["item_type"] = item_type_as_string(loc.item_type);
jloc["item_name"] = loc.item_name;
jloc["item_type"] = "file";
jloc["item_name"] = "";
jpos["name"] = loc.name;
jpos["line"] = loc.pos.line;
jpos["column"] = loc.pos.column;
jpos["offset"] = loc.pos.pos;
jpos["filename"] = name;
jpos["line"] = 0;
jpos["column"] = 0;
jpos["offset"] = 0;
jloc["position"] = jpos;
ret["locations"].push_back(jloc);
}
else
{
for(auto& loc : m_locs)
{
nlohmann::json jloc, jpos;
jloc["item_type"] = loc.item_type;
jloc["item_name"] = loc.item_name;
jpos["filename"] = name;
jpos["line"] = loc.mark.line;
jpos["column"] = loc.mark.column;
jpos["offset"] = loc.mark.pos;
jloc["position"] = jpos;
ret["locations"].push_back(jloc);
}
}
return ret;
}
@@ -202,29 +138,25 @@ nlohmann::json rule_loader::context::as_json()
std::string rule_loader::context::snippet(const falco::load_result::rules_contents_t& rules_contents,
size_t snippet_width) const
{
// All valid contexts should have at least one location.
std::string ret;
if(m_locs.empty())
{
throw falco_exception("rule_loader::context without location?");
}
rule_loader::context::location loc = m_locs.back();
auto it = rules_contents.find(loc.name);
if(alt_content.empty() && it == rules_contents.end())
{
return "<No context for file + " + loc.name + ">\n";
}
// If not using alt content, the last location's name must be found in rules_contents
const std::string& snip_content = (!alt_content.empty() ? alt_content : it->second.get());
if(snip_content.empty())
{
return "<No context available>\n";
}
size_t from = loc.pos.pos;
rule_loader::context::location loc = m_locs.back();
auto it = rules_contents.find(name);
if(it == rules_contents.end())
{
return "<No context available>\n";
}
const std::string& snip_content = it->second;
size_t from = loc.mark.pos;
// In some cases like this, where the content ends with a
// dangling property value:
@@ -240,10 +172,10 @@ std::string rule_loader::context::snippet(const falco::load_result::rules_conten
// However, some lines can be very very long, so the walk
// forwards/walk backwards is capped at a maximum of
// snippet_width/2 characters in either direction.
for(; from > 0 && snip_content.at(from) != '\n' && (loc.pos.pos - from) < (snippet_width/2); from--);
for(; from > 0 && snip_content.at(from) != '\n' && (loc.mark.pos - from) < (snippet_width/2); from--);
size_t to = loc.pos.pos;
for(; to < snip_content.size()-1 && snip_content.at(to) != '\n' && (to - loc.pos.pos) < (snippet_width/2); to++);
size_t to = loc.mark.pos;
for(; to < snip_content.size()-1 && snip_content.at(to) != '\n' && (to - loc.mark.pos) < (snippet_width/2); to++);
// Don't include the newlines
if(snip_content.at(from) == '\n')
@@ -255,29 +187,24 @@ std::string rule_loader::context::snippet(const falco::load_result::rules_conten
to--;
}
std::string ret = snip_content.substr(from, to-from+1);
if(snip_content.empty())
{
return "<No context available>\n";
}
ret = snip_content.substr(from, to-from+1);
// Replace the initial/end characters with '...' if the walk
// forwards/backwards was incomplete
if(loc.pos.pos - from >= (snippet_width/2))
if(loc.mark.pos - from >= (snippet_width/2))
{
ret.replace(0, 3, "...");
ret.replace(0, 2, "...");
}
if(to - loc.pos.pos >= (snippet_width/2))
if(to - loc.mark.pos >= (snippet_width/2))
{
ret.replace(ret.size()-3, 3, "...");
ret.replace(ret.size()-2, ret.size(), "...");
}
ret += "\n";
// Add a blank line with a marker at the position within the snippet
ret += std::string(loc.pos.pos-from, ' ') + '^' + "\n";
ret += std::string(loc.mark.pos-from, ' ') + '^' + "\n";
return ret;
}
@@ -877,12 +804,10 @@ static shared_ptr<ast::expr> parse_condition(
}
catch (const sinsp_exception& e)
{
rule_loader::context parsectx(p.get_pos(), condition, ctx);
throw rule_loader::rule_load_exception(
load_result::LOAD_ERR_COMPILE_CONDITION,
e.what(),
parsectx);
ctx);
}
}
@@ -911,7 +836,7 @@ void rule_loader::clear()
m_required_plugin_versions.clear();
}
const std::vector<rule_loader::plugin_version_info::requirement_alternatives>& rule_loader::required_plugin_versions() const
const std::map<std::string, std::set<std::string>> rule_loader::required_plugin_versions() const
{
return m_required_plugin_versions;
}
@@ -926,21 +851,11 @@ void rule_loader::define(configuration& cfg, engine_version_info& info)
void rule_loader::define(configuration& cfg, plugin_version_info& info)
{
std::unordered_set<std::string> plugin_names;
for (const auto& req : info.alternatives)
{
sinsp_version plugin_version(req.version);
THROW(!plugin_version.m_valid,
"Invalid required version '" + req.version
+ "' for plugin '" + req.name + "'",
info.ctx);
THROW(plugin_names.find(req.name) != plugin_names.end(),
"Defined multiple alternative version requirements for plugin '"
+ req.name + "'",
info.ctx);
plugin_names.insert(req.name);
}
m_required_plugin_versions.push_back(info.alternatives);
sinsp_version plugin_version(info.version);
THROW(!plugin_version.m_valid, "Invalid required version '" + info.version
+ "' for plugin '" + info.name + "'",
info.ctx);
m_required_plugin_versions[info.name].insert(info.version);
}
void rule_loader::define(configuration& cfg, list_info& info)

View File

@@ -33,66 +33,19 @@ limitations under the License.
class rule_loader
{
public:
class context
{
public:
// The kinds of items that can be in rules
// content. These generally map to yaml items but a
// few are more specific (e.g. "within condition
// expression", "value for yaml node", etc.)
enum item_type {
VALUE_FOR = 0,
EXCEPTIONS,
EXCEPTION,
EXCEPTION_VALUES,
EXCEPTION_VALUE,
RULES_CONTENT,
RULES_CONTENT_ITEM,
REQUIRED_ENGINE_VERSION,
REQUIRED_PLUGIN_VERSIONS,
REQUIRED_PLUGIN_VERSIONS_ENTRY,
REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE,
LIST,
LIST_ITEM,
MACRO,
MACRO_CONDITION,
RULE,
RULE_CONDITION,
CONDITION_EXPRESSION,
RULE_OUTPUT,
RULE_OUTPUT_EXPRESSION,
RULE_PRIORITY
};
static const std::string& item_type_as_string(enum item_type it);
static const size_t default_snippet_width = 160;
struct position
{
position() : pos(0), line(0), column(0) {};
position(const YAML::Mark& mark) : pos(mark.pos), line(mark.line), column(mark.column) {};
~position() = default;
int pos;
int line;
int column;
};
struct location
{
// A name for the content this location refers
// to. Will generally be a filename, can also
// refer to a rule/macro condition when the
// location points into a condition string.
std::string name;
// The original location in the document
position pos;
YAML::Mark mark;
// The kind of item at this location
// (e.g. "list", "macro", "rule", "exception", etc)
context::item_type item_type;
std::string item_type;
// The name of this item (e.g. "Write Below Etc",
// etc).
@@ -101,54 +54,26 @@ public:
context(const std::string& name);
context(const YAML::Node& item,
item_type item_type,
const std::string item_type,
const std::string item_name,
const context& parent);
// Build a context from a condition expression +
// parser position. This does not use the original
// yaml content because:
// - YAML block indicators will remove whitespace/newlines/wrapping
// from the YAML node containing the condition expression.
// - When compiling, the condition expression has expanded
// macro and list references with their values.
context(const libsinsp::filter::ast::pos_info& pos,
const std::string& condition,
const context& parent);
virtual ~context() = default;
// Return the content name (generally filename) for
// this context
const std::string& name() const;
// Return a snippet of the provided rules content
// corresponding to this context.
// Uses the provided rules_contents to look up the original
// rules content for a given location name.
// (If this context has a non-empty alt_content, it
// will be used to create the snippet, ignoring the
// provided rules_contents).
std::string snippet(const falco::load_result::rules_contents_t& rules_contents, size_t snippet_width = default_snippet_width) const;
std::string as_string();
nlohmann::json as_json();
private:
void init(const std::string& name,
const position& pos,
const item_type item_type,
const std::string item_name,
const context& parent);
std::string name;
// A chain of locations from the current item, its
// parent, possibly older ancestors.
std::vector<location> m_locs;
// If non-empty, this content will be used when
// creating snippets. Used for contexts involving
// condition expressions.
std::string alt_content;
};
struct warning
@@ -257,18 +182,6 @@ public:
*/
struct plugin_version_info
{
struct requirement
{
requirement() = default;
requirement(const std::string n, const std::string v):
name(n), version(v) { }
std::string name;
std::string version;
};
typedef std::vector<requirement> requirement_alternatives;
// This differs from the other _info structs by having
// a default constructor. This allows it to be used
// by falco_engine, which aliases the type.
@@ -277,7 +190,8 @@ public:
~plugin_version_info() = default;
context ctx;
requirement_alternatives alternatives;
std::string name;
std::string version;
};
/*!
@@ -389,7 +303,7 @@ public:
\brief Returns the set of all required versions for each plugin according
to the internal definitions.
*/
virtual const std::vector<plugin_version_info::requirement_alternatives>& required_plugin_versions() const;
virtual const std::map<std::string, std::set<std::string>> required_plugin_versions() const;
/*!
\brief Defines an info block. If a similar info block is found
@@ -434,5 +348,5 @@ private:
indexed_vector<rule_info> m_rule_infos;
indexed_vector<macro_info> m_macro_infos;
indexed_vector<list_info> m_list_infos;
std::vector<plugin_version_info::requirement_alternatives> m_required_plugin_versions;
std::map<std::string, std::set<std::string>> m_required_plugin_versions;
};

View File

@@ -34,7 +34,7 @@ static void decode_val_generic(const YAML::Node& item, const char *key, T& out,
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
THROW(val.IsNull(), std::string("Mapping for key '") + key + "' is empty", ctx);
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx);
rule_loader::context valctx(val, "value for", key, ctx);
THROW(!val.IsScalar(), "Value is not a scalar value", valctx);
THROW(val.Scalar().empty(), "Value must be non-empty", valctx);
@@ -72,13 +72,13 @@ static void decode_seq(const YAML::Node& item, const char *key,
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx);
rule_loader::context valctx(val, "value for", key, ctx);
THROW(!val.IsSequence(), "Value is not a sequence", valctx);
T value;
for(const YAML::Node& v : val)
{
rule_loader::context ictx(v, rule_loader::context::LIST_ITEM, "", valctx);
rule_loader::context ictx(v, "list item", "", valctx);
THROW(!v.IsScalar(), "sequence value is not scalar", ictx);
THROW(!YAML::convert<T>::decode(v, value), "Can't decode YAML sequence value", ictx);
inserter(value);
@@ -128,7 +128,7 @@ static void decode_exception_info_entry(
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, (key == NULL ? "" : key), ctx);
rule_loader::context valctx(val, "value for", (key == NULL ? "" : key), ctx);
if (val.IsScalar())
{
@@ -139,10 +139,10 @@ static void decode_exception_info_entry(
if (val.IsSequence())
{
out.is_list = true;
rule_loader::rule_exception_info::entry tmp;
for(const YAML::Node& v : val)
{
rule_loader::rule_exception_info::entry tmp;
rule_loader::context lctx(v, rule_loader::context::EXCEPTION, "", valctx);
rule_loader::context lctx(v, "list exception entry", "", valctx);
// Optional is always false once you get past the outer values
optional = false;
@@ -196,7 +196,7 @@ static void read_rule_exceptions(
return;
}
rule_loader::context exes_ctx(exs, rule_loader::context::EXCEPTIONS, "", parent);
rule_loader::context exes_ctx(exs, "exceptions", "", parent);
THROW(!exs.IsSequence(), "Rule exceptions must be a sequence", exes_ctx);
@@ -205,13 +205,13 @@ static void read_rule_exceptions(
// Make a temp context to verify simple properties
// about the exception.
std::string name;
rule_loader::context tmp(ex, rule_loader::context::EXCEPTION, "", exes_ctx);
rule_loader::context tmp(ex, "exception", "", exes_ctx);
THROW(!ex.IsMap(), "Rule exception must be a mapping", tmp);
decode_val(ex, "name", name, tmp);
// Now use a real context including the exception name.
rule_loader::context ex_ctx(ex, rule_loader::context::EXCEPTION, name, parent);
rule_loader::context ex_ctx(ex, "exception", name, parent);
rule_loader::rule_exception_info v_ex(ex_ctx);
v_ex.name = name;
@@ -223,12 +223,12 @@ static void read_rule_exceptions(
const YAML::Node& exvals = ex["values"];
if (exvals.IsDefined())
{
rule_loader::context vals_ctx(exvals, rule_loader::context::EXCEPTION_VALUES, "", ex_ctx);
rule_loader::context vals_ctx(exvals, "exception values", "", ex_ctx);
THROW(!exvals.IsSequence(),
"Rule exception values must be a sequence", vals_ctx);
for (auto &val : exvals)
{
rule_loader::context vctx(val, rule_loader::context::EXCEPTION_VALUE, "", vals_ctx);
rule_loader::context vctx(val, "exception value", "", vals_ctx);
rule_loader::rule_exception_info::entry v_ex_val;
decode_exception_values(val, v_ex_val, vctx);
@@ -245,13 +245,13 @@ static void read_item(
const YAML::Node& item,
const rule_loader::context& parent)
{
rule_loader::context tmp(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent);
rule_loader::context tmp(item, "item", "", parent);
THROW(!item.IsMap(), "Unexpected element type. "
"Each element should be a yaml associative array.", tmp);
if (item["required_engine_version"].IsDefined())
{
rule_loader::context ctx(item, rule_loader::context::REQUIRED_ENGINE_VERSION, "", parent);
rule_loader::context ctx(item, "required_engine_version", "", parent);
rule_loader::engine_version_info v(ctx);
decode_val(item, "required_engine_version", v.version, ctx);
@@ -260,7 +260,7 @@ static void read_item(
else if(item["required_plugin_versions"].IsDefined())
{
const YAML::Node& req_plugin_vers = item["required_plugin_versions"];
rule_loader::context ctx(req_plugin_vers, rule_loader::context::REQUIRED_PLUGIN_VERSIONS, "", parent);
rule_loader::context ctx(req_plugin_vers, "required_plugin_versions", "", parent);
THROW(!req_plugin_vers.IsSequence(),
"Value of required_plugin_versions must be a sequence",
@@ -268,33 +268,16 @@ static void read_item(
for(const YAML::Node& plugin : req_plugin_vers)
{
rule_loader::plugin_version_info::requirement r;
// Use a temp context until we can get a name
rule_loader::context tmp(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, "", ctx);
std::string name;
rule_loader::context tmp(plugin, "plugin version", "", ctx);
THROW(!plugin.IsMap(), "Plugin version must be a mapping", tmp);
decode_val(plugin, "name", r.name, tmp);
rule_loader::context pctx(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, r.name, ctx);
rule_loader::plugin_version_info v(pctx);
decode_val(plugin, "version", r.version, pctx);
v.alternatives.push_back(r);
decode_val(plugin, "name", name, tmp);
const YAML::Node& alternatives = plugin["alternatives"];
if(alternatives.IsDefined())
{
THROW(!alternatives.IsSequence(),
"Value of plugin version alternatives must be a sequence",
pctx);
for (const auto &req : alternatives)
{
tmp = rule_loader::context(req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, "", pctx);
THROW(!req.IsMap(), "Plugin version alternative must be a mapping", tmp);
decode_val(req, "name", r.name, tmp);
tmp = rule_loader::context(req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, r.name, pctx);
decode_val(req, "version", r.version, tmp);
v.alternatives.push_back(r);
}
}
rule_loader::context pctx(plugin, "plugin version", name, ctx);
rule_loader::plugin_version_info v(pctx);
decode_val(plugin, "version", v.version, pctx);
v.name = name;
loader.define(cfg, v);
}
@@ -303,10 +286,10 @@ static void read_item(
{
std::string name;
// Using tmp context until name is decoded
rule_loader::context tmp(item, rule_loader::context::LIST, "", parent);
rule_loader::context tmp(item, "list", "", parent);
decode_val(item, "list", name, tmp);
rule_loader::context ctx(item, rule_loader::context::LIST, name, parent);
rule_loader::context ctx(item, "list", name, parent);
rule_loader::list_info v(ctx);
bool append = false;
@@ -328,10 +311,10 @@ static void read_item(
{
std::string name;
// Using tmp context until name is decoded
rule_loader::context tmp(item, rule_loader::context::MACRO, "", parent);
rule_loader::context tmp(item, "macro", "", parent);
decode_val(item, "macro", name, tmp);
rule_loader::context ctx(item, rule_loader::context::MACRO, name, parent);
rule_loader::context ctx(item, "macro", name, parent);
rule_loader::macro_info v(ctx);
v.name = name;
@@ -339,7 +322,7 @@ static void read_item(
decode_val(item, "condition", v.cond, ctx);
// Now set the proper context for the condition now that we know it exists
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::MACRO_CONDITION, "", ctx);
v.cond_ctx = rule_loader::context(item["condition"], "condition", "", ctx);
decode_optional_val(item, "append", append, ctx);
@@ -357,10 +340,10 @@ static void read_item(
std::string name;
// Using tmp context until name is decoded
rule_loader::context tmp(item, rule_loader::context::RULE, "", parent);
rule_loader::context tmp(item, "rule", "", parent);
decode_val(item, "rule", name, tmp);
rule_loader::context ctx(item, rule_loader::context::RULE, name, parent);
rule_loader::context ctx(item, "rule", name, parent);
rule_loader::rule_info v(ctx);
v.name = name;
@@ -376,7 +359,7 @@ static void read_item(
decode_optional_val(item, "condition", v.cond, ctx);
if(item["condition"].IsDefined())
{
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx);
v.cond_ctx = rule_loader::context(item["condition"], "condition", "", ctx);
}
read_rule_exceptions(item, v, ctx, append);
loader.append(cfg, v);
@@ -402,17 +385,17 @@ static void read_item(
// All of these are required
decode_val(item, "condition", v.cond, ctx);
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx);
v.cond_ctx = rule_loader::context(item["condition"], "condition", "", ctx);
decode_val(item, "output", v.output, ctx);
v.output_ctx = rule_loader::context(item["output"], rule_loader::context::RULE_OUTPUT, "", ctx);
v.output_ctx = rule_loader::context(item["output"], "output", "", ctx);
decode_val(item, "desc", v.desc, ctx);
decode_val(item, "priority", priority, ctx);
v.output = trim(v.output);
v.source = falco_common::syscall_source;
rule_loader::context prictx(item["priority"], rule_loader::context::RULE_PRIORITY, "", ctx);
rule_loader::context prictx(item["priority"], "priority value", "", ctx);
THROW(!falco_common::parse_priority(priority, v.priority),
"Invalid priority", prictx);
decode_optional_val(item, "source", v.source, ctx);
@@ -427,7 +410,7 @@ static void read_item(
}
else
{
rule_loader::context ctx(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent);
rule_loader::context ctx(item, "unknown", "", parent);
cfg.res->add_warning(load_result::LOAD_UNKNOWN_ITEM, "Unknown top level item", ctx);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -36,19 +36,16 @@ static int inot_fd;
static void signal_callback(int signal)
{
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
s_app.get().terminate();
}
static void reopen_outputs(int signal)
{
falco_logger::log(LOG_INFO, "SIGUSR1 received, reopening outputs...\n");
s_app.get().reopen_outputs();
}
static void restart_falco(int signal)
{
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
s_app.get().restart();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,58 +21,51 @@ using namespace falco::app;
void application::check_for_ignored_events()
{
/* Get the events from the rules. */
std::set<uint16_t> rule_events;
std::set<uint16_t> evttypes;
sinsp_evttables* einfo = m_state->inspector->get_event_info_tables();
const struct ppm_event_info* etable = einfo->m_event_info;
std::string source = falco_common::syscall_source;
m_state->engine->evttypes_for_ruleset(source, rule_events);
m_state->engine->evttypes_for_ruleset(source, evttypes);
/* Get the events we consider interesting from the application state `ppm_sc` codes. */
std::unique_ptr<sinsp> inspector(new sinsp());
auto interesting_events = inspector->get_event_set_from_ppm_sc_set(m_state->ppm_sc_of_interest);
std::unordered_set<uint32_t> ignored_events;
// Save event names so we don't warn for both the enter and exit event.
std::set<std::string> warn_event_names;
for(const auto& it : rule_events)
for(auto evtnum : evttypes)
{
/* If we have the old version of the event we will have also the recent one
* so we can avoid analyzing the presence of old events.
*/
if(sinsp::is_old_version_event(it))
if(evtnum == PPME_GENERIC_E || evtnum == PPME_GENERIC_X)
{
continue;
}
/* Here we are interested only in syscall events the internal events are not
* altered without the `-A` flag.
*
* TODO: We could consider also the tracepoint events here but right now we don't have
* the support from the libraries.
*/
if(!sinsp::is_syscall_event(it))
if(!sinsp::simple_consumer_consider_evtnum(evtnum))
{
continue;
}
/* If the event is not in this set it is not considered by Falco. */
if(interesting_events.find(it) == interesting_events.end())
{
ignored_events.insert(it);
std::string name = etable[evtnum].name;
if(warn_event_names.find(name) == warn_event_names.end())
{
warn_event_names.insert(name);
}
}
}
if(ignored_events.empty())
// Print a single warning with the list of ignored events
if (!warn_event_names.empty())
{
return;
std::string skipped_events;
bool first = true;
for (const auto& evtname : warn_event_names)
{
if (first)
{
skipped_events += evtname;
first = false;
} else
{
skipped_events += "," + evtname;
}
}
fprintf(stderr,"Rules match ignored syscall: warning (ignored-evttype):\n loaded rules match the following events: %s;\n but these events are not returned unless running falco with -A\n", skipped_events.c_str());
}
/* Get the names of the ignored events and print them. */
auto event_names = inspector->get_events_names(ignored_events);
std::cerr << std::endl << "Rules match ignored syscall: warning (ignored-evttype):" << std::endl;
std::cerr << "Loaded rules match the following events:" << std::endl;
for(const auto& it : event_names)
{
std::cerr << "\t- " << it.c_str() << std::endl;
}
std::cerr << "But these events are not returned unless running falco with -A" << std::endl << std::endl;
}
application::run_result application::load_rules_files()
@@ -135,10 +128,9 @@ application::run_result application::load_rules_files()
}
// Ensure that all plugins are compatible with the loaded set of rules
// note: offline inspector contains all the loaded plugins
std::string plugin_vers_err = "";
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
for (const auto &plugin : m_state->offline_inspector->get_plugin_manager()->plugins())
for (const auto &plugin : m_state->inspector->get_plugin_manager()->plugins())
{
falco_engine::plugin_version_requirement req;
req.name = plugin->name();
@@ -177,13 +169,11 @@ application::run_result application::load_rules_files()
m_state->engine->enable_rule_by_tag(m_options.enabled_rule_tags, true);
}
/* Reading a scap file we have no concepts of ignored events we read all we need. */
if(!m_options.all_events && !is_capture_mode())
if(!m_options.all_events)
{
/* Here we have already initialized the application state with the interesting syscalls,
* so we have to check if any event types used by the loaded rules are not considered by
* Falco interesting set.
*/
// For syscalls, see if any event types used by the
// loaded rules are ones with the EF_DROP_SIMPLE_CONS
// label.
check_for_ignored_events();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,18 +20,14 @@ limitations under the License.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unordered_map>
#include "falco_utils.h"
#include "event_drops.h"
#ifndef MINIMAL_BUILD
#include "webserver.h"
#endif
#include "stats_writer.h"
#include "statsfilewriter.h"
#include "application.h"
#include "falco_outputs.h"
#include "token_bucket.h"
#include "app_cmdline_options.h"
#include <plugin_manager.h>
@@ -40,54 +36,36 @@ using namespace falco::app;
//
// Event processing loop
//
application::run_result application::do_inspect(
std::shared_ptr<sinsp> inspector,
const std::string& source, // an empty source represents capture mode
std::shared_ptr<stats_writer> statsw,
syscall_evt_drop_mgr &sdropmgr,
bool check_drops_and_timeouts,
uint64_t duration_to_tot_ns,
uint64_t &num_evts)
application::run_result application::do_inspect(syscall_evt_drop_mgr &sdropmgr,
uint64_t duration_to_tot_ns,
uint64_t &num_evts)
{
int32_t rc;
sinsp_evt* ev;
stats_writer::collector stats_collector(statsw);
StatsFileWriter writer;
uint64_t duration_start = 0;
uint32_t timeouts_since_last_success_or_msg = 0;
token_bucket rate_limiter;
bool rate_limiter_enabled = m_state->config->m_notifications_rate > 0;
bool source_engine_idx_found = false;
bool is_capture_mode = source.empty();
bool syscall_source_engine_idx = m_state->source_infos.at(falco_common::syscall_source)->engine_idx;
std::size_t source_engine_idx = 0;
std::vector<std::string> source_names = inspector->get_plugin_manager()->sources();
source_names.push_back(falco_common::syscall_source);
if (!is_capture_mode)
{
source_engine_idx = m_state->source_infos.at(source)->engine_idx;
}
std::size_t source_idx;
bool source_idx_found = false;
// if enabled, init rate limiter
if (rate_limiter_enabled)
{
rate_limiter.init(
m_state->config->m_notifications_rate,
m_state->config->m_notifications_max_burst);
}
// reset event counter
num_evts = 0;
// init drop manager if we are inspecting syscalls
if (check_drops_and_timeouts)
sdropmgr.init(m_state->inspector,
m_state->outputs,
m_state->config->m_syscall_evt_drop_actions,
m_state->config->m_syscall_evt_drop_threshold,
m_state->config->m_syscall_evt_drop_rate,
m_state->config->m_syscall_evt_drop_max_burst,
m_state->config->m_syscall_evt_simulate_drops);
if (m_options.stats_filename != "")
{
sdropmgr.init(inspector,
m_state->outputs, // drop manager has its own rate limiting logic
m_state->config->m_syscall_evt_drop_actions,
m_state->config->m_syscall_evt_drop_threshold,
m_state->config->m_syscall_evt_drop_rate,
m_state->config->m_syscall_evt_drop_max_burst,
m_state->config->m_syscall_evt_simulate_drops);
string errstr;
if (!writer.init(m_state->inspector, m_options.stats_filename, m_options.stats_interval, errstr))
{
return run_result::fatal(errstr);
}
}
//
@@ -95,11 +73,26 @@ application::run_result application::do_inspect(
//
while(1)
{
rc = inspector->next(&ev);
if(m_state->terminate.load(std::memory_order_seq_cst)
|| m_state->restart.load(std::memory_order_seq_cst))
rc = m_state->inspector->next(&ev);
writer.handle();
if(m_state->reopen_outputs)
{
falco_logger::log(LOG_INFO, "SIGUSR1 received, reopening outputs...\n");
m_state->outputs->reopen_outputs();
m_state->reopen_outputs = false;
}
if(m_state->terminate)
{
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
break;
}
else if (m_state->restart)
{
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
break;
}
else if(rc == SCAP_TIMEOUT)
@@ -108,7 +101,8 @@ application::run_result application::do_inspect(
{
timeouts_since_last_success_or_msg++;
if(timeouts_since_last_success_or_msg > m_state->config->m_syscall_evt_timeout_max_consecutives
&& check_drops_and_timeouts)
&& is_syscall_source_enabled()
&& !is_gvisor_enabled())
{
std::string rule = "Falco internal: timeouts notification";
std::string msg = rule + ". " + std::to_string(m_state->config->m_syscall_evt_timeout_max_consecutives) + " consecutive timeouts without event.";
@@ -142,32 +136,7 @@ application::run_result application::do_inspect(
//
// Event read error.
//
return run_result::fatal(inspector->getlasterr());
}
// if we are in live mode, we already have the right source engine idx
if (is_capture_mode)
{
source_engine_idx = syscall_source_engine_idx;
if (ev->get_type() == PPME_PLUGINEVENT_E)
{
// note: here we can assume that the source index will be the same
// in both the falco engine and the sinsp plugin manager. See the
// comment in init_falco_engine.cpp for more details.
source_engine_idx = inspector->get_plugin_manager()->source_idx_by_plugin_id(*(int32_t *)ev->get_param(0)->m_val, source_engine_idx_found);
if (!source_engine_idx_found)
{
return run_result::fatal("Unknown plugin ID in inspector: " + std::to_string(*(int32_t *)ev->get_param(0)->m_val));
}
}
// for capture mode, the source name can change at every event
stats_collector.collect(inspector, source_names[source_engine_idx]);
}
else
{
// for live mode, the source name is constant
stats_collector.collect(inspector, source);
return run_result::fatal(m_state->inspector->getlasterr());
}
// Reset the timeouts counter, Falco successfully got an event to process
@@ -184,27 +153,38 @@ application::run_result application::do_inspect(
}
}
if(check_drops_and_timeouts && !sdropmgr.process_event(inspector, ev))
if(!sdropmgr.process_event(m_state->inspector, ev))
{
return run_result::fatal("Drop manager internal error");
}
if(!ev->simple_consumer_consider() && !m_options.all_events)
{
continue;
}
source_idx = m_state->syscall_source_idx;
if (ev->get_type() == PPME_PLUGINEVENT_E)
{
// note: here we can assume that the source index will be the same
// in both the falco engine and the sinsp plugin manager. See the
// comment in load_plugins.cpp for more details.
source_idx = m_state->inspector->get_plugin_manager()->source_idx_by_plugin_id(*(int32_t *)ev->get_param(0)->m_val, source_idx_found);
if (!source_idx_found)
{
return run_result::fatal("Unknown plugin ID in inspector: " + std::to_string(*(int32_t *)ev->get_param(0)->m_val));
}
}
// As the inspector has no filter at its level, all
// events are returned here. Pass them to the falco
// engine, which will match the event against the set
// of rules. If a match is found, pass the event to
// the outputs.
unique_ptr<falco_engine::rule_result> res = m_state->engine->process_event(source_engine_idx, ev);
unique_ptr<falco_engine::rule_result> res = m_state->engine->process_event(source_idx, ev);
if(res)
{
if (!rate_limiter_enabled || rate_limiter.claim())
{
m_state->outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format, res->tags);
}
else
{
falco_logger::log(LOG_DEBUG, "Skipping rate-limited notification for rule " + res->rule + "\n");
}
m_state->outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format, res->tags);
}
num_evts++;
@@ -213,181 +193,47 @@ application::run_result application::do_inspect(
return run_result::ok();
}
void application::process_inspector_events(
std::shared_ptr<sinsp> inspector,
std::shared_ptr<stats_writer> statsw,
std::string source, // an empty source represents capture mode
application::run_result* res) noexcept
{
try
{
double duration;
scap_stats cstats;
uint64_t num_evts = 0;
syscall_evt_drop_mgr sdropmgr;
bool is_capture_mode = source.empty();
bool check_drops_timeouts = is_capture_mode
|| (source == falco_common::syscall_source && !is_gvisor_enabled());
duration = ((double)clock()) / CLOCKS_PER_SEC;
*res = do_inspect(inspector, source, statsw, sdropmgr, check_drops_timeouts,
uint64_t(m_options.duration_to_tot*ONE_SECOND_IN_NS),
num_evts);
duration = ((double)clock()) / CLOCKS_PER_SEC - duration;
inspector->get_capture_stats(&cstats);
if(m_options.verbose)
{
if (source == falco_common::syscall_source)
{
fprintf(stderr, "Driver Events:%" PRIu64 "\nDriver Drops:%" PRIu64 "\n",
cstats.n_evts,
cstats.n_drops);
}
fprintf(stderr, "%sElapsed time: %.3lf, Captured Events: %" PRIu64 ", %.2lf eps\n",
(is_capture_mode ? "" : ("("+source+") ").c_str()),
duration,
num_evts,
num_evts / duration);
}
if (check_drops_timeouts)
{
sdropmgr.print_stats();
}
}
catch(const std::exception& e)
{
*res = run_result::fatal(e.what());
}
}
static std::shared_ptr<stats_writer> init_stats_writer(const cmdline_options& opts)
{
auto statsw = std::make_shared<stats_writer>();
if (!opts.stats_filename.empty())
{
std::string err;
if (!stats_writer::init_ticker(opts.stats_interval, err))
{
throw falco_exception(err);
}
statsw.reset(new stats_writer(opts.stats_filename));
}
return statsw;
}
application::run_result application::process_events()
{
application::run_result res = run_result::ok();
syscall_evt_drop_mgr sdropmgr;
// Used for stats
double duration;
scap_stats cstats;
uint64_t num_evts = 0;
run_result ret;
// Notify engine that we finished loading and enabling all rules
m_state->engine->complete_rule_loading();
duration = ((double)clock()) / CLOCKS_PER_SEC;
// Initialize stats writer
auto statsw = init_stats_writer(m_options);
ret = do_inspect(sdropmgr,
uint64_t(m_options.duration_to_tot*ONE_SECOND_IN_NS),
num_evts);
// Start processing events
if(is_capture_mode())
duration = ((double)clock()) / CLOCKS_PER_SEC - duration;
m_state->inspector->get_capture_stats(&cstats);
if(m_options.verbose)
{
res = open_offline_inspector();
if (!res.success)
{
return res;
}
fprintf(stderr, "Driver Events:%" PRIu64 "\nDriver Drops:%" PRIu64 "\n",
cstats.n_evts,
cstats.n_drops);
process_inspector_events(m_state->offline_inspector, statsw, "", &res);
m_state->offline_inspector->close();
// Honor -M also when using a trace file.
// Since inspection stops as soon as all events have been consumed
// just await the given duration is reached, if needed.
if(m_options.duration_to_tot > 0)
{
std::this_thread::sleep_for(std::chrono::seconds(m_options.duration_to_tot));
}
fprintf(stderr, "Elapsed time: %.3lf, Captured Events: %" PRIu64 ", %.2lf eps\n",
duration,
num_evts,
num_evts / duration);
}
else
// Honor -M also when using a trace file.
// Since inspection stops as soon as all events have been consumed
// just await the given duration is reached, if needed.
if(is_capture_mode() && m_options.duration_to_tot > 0)
{
typedef struct
{
// the name of the source of which events are processed
std::string source;
// the result of the event processing loop
application::run_result res;
// if non-null, the thread on which events are processed
std::unique_ptr<std::thread> thread;
} live_context;
// start event processing for all enabled sources
std::vector<live_context> ctxs;
ctxs.reserve(m_state->enabled_sources.size());
for (auto source : m_state->enabled_sources)
{
auto src_info = m_state->source_infos.at(source);
auto ctx_idx = ctxs.size();
ctxs.emplace_back();
ctxs[ctx_idx].source = source;
try
{
falco_logger::log(LOG_DEBUG, "Opening event source '" + source + "'\n");
open_live_inspector(src_info->inspector, source);
if (m_state->enabled_sources.size() == 1)
{
// optimization: with only one source we don't spawn additional threads
process_inspector_events(src_info->inspector, statsw, source, &ctxs[ctx_idx].res);
}
else
{
ctxs[ctx_idx].thread.reset(new std::thread(
&application::process_inspector_events,
this, src_info->inspector, statsw, source, &ctxs[ctx_idx].res));
}
}
catch (std::exception &e)
{
ctxs[ctx_idx].res = run_result::fatal(e.what());
break;
}
}
// wait for event processing to terminate for all sources
// if a thread terminates with an error, we trigger the app termination
// to force all other event streams to termiante too.
// We accomulate the errors in a single run_result.
size_t closed_count = 0;
bool forced_termination = false;
while (closed_count < ctxs.size())
{
if (!res.success && !forced_termination)
{
terminate();
forced_termination = true;
}
for (auto &ctx : ctxs)
{
if (ctx.thread)
{
if (!ctx.thread->joinable())
{
continue;
}
ctx.thread->join();
ctx.thread = nullptr;
}
falco_logger::log(LOG_DEBUG, "Closing event source '" + ctx.source + "'\n");
m_state->source_infos.at(ctx.source)->inspector->close();
res = run_result::merge(res, ctx.res);
closed_count++;
}
}
std::this_thread::sleep_for(std::chrono::seconds(m_options.duration_to_tot));
}
m_state->engine->print_stats();
sdropmgr.print_stats();
return res;
return ret;
}

View File

@@ -1,70 +0,0 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "application.h"
using namespace falco::app;
application::run_result application::select_event_sources()
{
m_state->enabled_sources = m_state->loaded_sources;
// event sources selection is meaningless when reading trace files
if (is_capture_mode())
{
return run_result::ok();
}
if (!m_options.enable_sources.empty() && !m_options.disable_sources.empty())
{
return run_result::fatal("You can not mix --enable-source and --disable-source");
}
if (!m_options.enable_sources.empty())
{
m_state->enabled_sources.clear();
for(const auto &src : m_options.enable_sources)
{
if (m_state->loaded_sources.find(src) == m_state->loaded_sources.end())
{
return run_result::fatal("Attempted enabling an unknown event source: " + src);
}
m_state->enabled_sources.insert(src);
}
}
else if (!m_options.disable_sources.empty())
{
for(const auto &src : m_options.disable_sources)
{
if (m_state->loaded_sources.find(src) == m_state->loaded_sources.end())
{
return run_result::fatal("Attempted disabling an unknown event source: " + src);
}
m_state->enabled_sources.erase(src);
}
}
if(m_state->enabled_sources.empty())
{
return run_result::fatal("Must enable at least one event source");
}
/* Print all enabled sources. */
std::ostringstream os;
std::copy(m_state->enabled_sources.begin(), m_state->enabled_sources.end(), std::ostream_iterator<std::string>(os, ", "));
std::string result = os.str();
result.pop_back();
falco_logger::log(LOG_INFO, "Enabled event sources: " + result + "\n");
return run_result::ok();
}

View File

@@ -27,14 +27,8 @@ application::run_result application::start_webserver()
if(!is_capture_mode() && m_state->config->m_webserver_enabled)
{
std::string ssl_option = (m_state->config->m_webserver_ssl_enabled ? " (SSL)" : "");
falco_logger::log(LOG_INFO, "Starting health webserver with threadiness "
+ to_string(m_state->config->m_webserver_threadiness)
+ ", listening on port "
+ to_string(m_state->config->m_webserver_listen_port)
+ ssl_option + "\n");
falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(m_state->config->m_webserver_listen_port) + ssl_option + "\n");
m_state->webserver.start(
m_state->config->m_webserver_threadiness,
m_state->config->m_webserver_listen_port,
m_state->config->m_webserver_k8s_healthz_endpoint,
m_state->config->m_webserver_ssl_certificate,

View File

@@ -155,18 +155,17 @@ void cmdline_options::define()
#else
("c", "Configuration file. If not specified tries " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ".", cxxopts::value(conf_filename), "<path>")
#endif
("A", "Monitor all events, including not interesting ones. Please use the `-i` command line option to see the ignored events. This option is implicit when the capture is not live.", cxxopts::value(all_events)->default_value("false"))
("A", "Monitor all events, including those with EF_DROP_SIMPLE_CONS flag.", cxxopts::value(all_events)->default_value("false"))
("b,print-base64", "Print data buffers in base64. This is useful for encoding binary data that needs to be used over media designed to consume this format.")
("cri", "Path to CRI socket for container metadata. Use the specified socket to fetch data from a CRI-compatible runtime. If not specified, uses libs default. It can be passed multiple times to specify socket to be tried until a successful one is found.", cxxopts::value(cri_socket_paths), "<path>")
("d,daemon", "Run as a daemon.", cxxopts::value(daemon)->default_value("false"))
("disable-cri-async", "Disable asynchronous CRI metadata fetching. This is useful to let the input event wait for the container metadata fetch to finish before moving forward. Async fetching, in some environments leads to empty fields for container metadata when the fetch is not fast enough to be completed asynchronously. This can have a performance penalty on your environment depending on the number of containers and the frequency at which they are created/started/stopped.", cxxopts::value(disable_cri_async)->default_value("false"))
("disable-source", "Disable a specific event source. Available event sources are: syscall or any source from a configured plugin with event sourcing capability. It can be passed multiple times. It has no offect when reading events from a trace file. Can not disable all event sources. Can not be mixed with enable-source.", cxxopts::value(disable_sources), "<event_source>")
("disable-source", "Disable a specific event source. Available event sources are: syscall or any source from a configured plugin with event sourcing capability. It can be passed multiple times. Can not disable all event sources.", cxxopts::value(disable_sources), "<event_source>")
("D", "Disable any rules with names having the substring <substring>. Can be specified multiple times. Can not be specified with -t.", cxxopts::value(disabled_rule_substrings), "<substring>")
("e", "Read the events from a trace file <events_file> in .scap format instead of tapping into live.", cxxopts::value(trace_filename), "<events_file>")
("enable-source", "Enable a specific event source. If used, only event sources passed with this options get enabled. Available event sources are: syscall or any source from a configured plugin with event sourcing capability. It can be passed multiple times. It has no offect when reading events from a trace file. Can not be mixed with disable-source.", cxxopts::value(enable_sources), "<event_source>")
("e", "Read the events from <events_file> in .scap format instead of tapping into live.", cxxopts::value(trace_filename), "<events_file>")
#ifdef HAS_GVISOR
("g,gvisor-config", "Parse events from gVisor using the specified configuration file. A falco-compatible configuration file can be generated with --gvisor-generate-config and can be used for both runsc and Falco.", cxxopts::value(gvisor_config), "<gvisor_config>")
("gvisor-generate-config", "Generate a configuration file that can be used for gVisor.", cxxopts::value<std::string>(gvisor_generate_config_with_socket)->implicit_value("/run/falco/gvisor.sock"), "<socket_path>")
("gvisor-generate-config", "Generate a configuration file that can be used for gVisor.", cxxopts::value<std::string>(gvisor_generate_config_with_socket)->implicit_value("/tmp/gvisor.sock"), "<socket_path>")
("gvisor-root", "gVisor root directory for storage of container state. Equivalent to runsc --root flag.", cxxopts::value(gvisor_root), "<gvisor_root>")
#endif
("i", "Print all events that are ignored by default (i.e. without the -A flag) and exit.", cxxopts::value(print_ignored_events)->default_value("false"))
@@ -203,9 +202,7 @@ void cmdline_options::define()
("u,userspace", "Parse events from userspace. To be used in conjunction with the ptrace(2) based driver (pdig)", cxxopts::value(userspace)->default_value("false"))
("V,validate", "Read the contents of the specified rules(s) file and exit. Can be specified multiple times to validate multiple files.", cxxopts::value(validate_rules_filenames), "<rules_file>")
("v", "Verbose output.", cxxopts::value(verbose)->default_value("false"))
("version", "Print version number.", cxxopts::value(print_version_info)->default_value("false"))
("page-size", "Print the system page size (may help you to choose the right syscall buffer size).", cxxopts::value(print_page_size)->default_value("false"));
("version", "Print version number.", cxxopts::value(print_version_info)->default_value("false"));
m_cmdline_opts.set_width(140);
}

View File

@@ -42,7 +42,6 @@ public:
bool disable_cri_async;
std::vector<std::string> disable_sources;
std::vector<std::string> disabled_rule_substrings;
std::vector<std::string> enable_sources;
std::string trace_filename;
std::string gvisor_config;
std::string gvisor_generate_config_with_socket;
@@ -78,7 +77,6 @@ public:
std::vector<std::string> validate_rules_filenames;
bool verbose;
bool print_version_info;
bool print_page_size;
bool parse(int argc, char **argv, std::string &errstr);

View File

@@ -41,17 +41,13 @@ application::run_result::~run_result()
application::state::state()
: restart(false),
terminate(false),
loaded_sources(),
enabled_sources(),
source_infos(),
plugin_configs(),
ppm_sc_of_interest(),
tp_of_interest()
reopen_outputs(false),
enabled_sources({falco_common::syscall_source})
{
config = std::make_shared<falco_configuration>();
outputs = std::make_shared<falco_outputs>();
engine = std::make_shared<falco_engine>();
offline_inspector = std::make_shared<sinsp>();
outputs = nullptr;
inspector = std::make_shared<sinsp>();
}
application::state::~state()
@@ -71,17 +67,15 @@ void application::terminate()
{
if(m_state != nullptr)
{
m_state->terminate.store(true, std::memory_order_seq_cst);
m_state->terminate = true;
}
}
void application::reopen_outputs()
{
if(m_state != nullptr && m_state->outputs != nullptr)
if(m_state != nullptr)
{
// note: it is ok to do this inside the signal handler because
// in the current falco_outputs implementation this is non-blocking
m_state->outputs->reopen_outputs();
m_state->reopen_outputs = true;
}
}
@@ -89,7 +83,7 @@ void application::restart()
{
if(m_state != nullptr)
{
m_state->restart.store(true, std::memory_order_seq_cst);
m_state->restart = true;
}
}
@@ -131,36 +125,33 @@ bool application::run(std::string &errstr, bool &restart)
std::list<std::function<run_result()>> run_steps = {
std::bind(&application::print_help, this),
std::bind(&application::print_version, this),
std::bind(&application::print_page_size, this),
std::bind(&application::print_generated_gvisor_config, this),
std::bind(&application::print_ignored_events, this),
std::bind(&application::print_syscall_events, this),
std::bind(&application::load_config, this),
std::bind(&application::create_signal_handlers, this),
std::bind(&application::load_config, this),
std::bind(&application::init_inspector, this),
std::bind(&application::print_plugin_info, this),
std::bind(&application::list_plugins, this),
std::bind(&application::load_plugins, this),
std::bind(&application::init_inspectors, this),
std::bind(&application::init_falco_engine, this),
std::bind(&application::list_fields, this),
std::bind(&application::select_event_sources, this),
std::bind(&application::list_plugins, this),
std::bind(&application::validate_rules_files, this),
std::bind(&application::load_rules_files, this),
std::bind(&application::print_ignored_events, this),
std::bind(&application::print_support, this),
std::bind(&application::attach_inotify_signals, this),
std::bind(&application::create_requested_paths, this),
std::bind(&application::daemonize, this),
std::bind(&application::init_outputs, this),
std::bind(&application::init_clients, this),
std::bind(&application::configure_syscall_buffer_size, this),
#ifndef MINIMAL_BUILD
std::bind(&application::start_grpc_server, this),
std::bind(&application::start_webserver, this),
#endif
std::bind(&application::open_inspector, this),
std::bind(&application::process_events, this)
};
std::list<std::function<bool(std::string &)>> teardown_steps = {
std::bind(&application::close_inspector, this, _1),
std::bind(&application::unregister_signal_handlers, this, _1),
#ifndef MINIMAL_BUILD
std::bind(&application::stop_grpc_server, this, _1),

View File

@@ -17,17 +17,14 @@ limitations under the License.
#pragma once
#include "configuration.h"
#include "stats_writer.h"
#ifndef MINIMAL_BUILD
#include "grpc_server.h"
#include "webserver.h"
#include "indexed_vector.h"
#endif
#include "app_cmdline_options.h"
#include <string>
#include <atomic>
namespace falco {
namespace app {
@@ -60,65 +57,32 @@ private:
// standalone class to allow for a bit of separation between
// application state and instance variables, and to also defer
// initializing this state until application::init.
struct state
{
// Holds the info mapped for each loaded event source
struct source_info
{
// The index of the given event source in the state's falco_engine,
// as returned by falco_engine::add_source
std::size_t engine_idx;
// The filtercheck list containing all fields compatible
// with the given event source
filter_check_list filterchecks;
// The inspector assigned to this event source. If in capture mode,
// all event source will share the same inspector. If the event
// source is a plugin one, the assigned inspector must have that
// plugin registered in its plugin manager
std::shared_ptr<sinsp> inspector;
};
struct state {
state();
virtual ~state();
std::atomic<bool> restart;
std::atomic<bool> terminate;
bool restart;
bool terminate;
bool reopen_outputs;
std::shared_ptr<falco_configuration> config;
std::shared_ptr<falco_outputs> outputs;
std::shared_ptr<falco_engine> engine;
// The set of loaded event sources (by default, the syscall event
// source plus all event sources coming from the loaded plugins)
std::set<std::string> loaded_sources;
// The set of enabled event sources (can be altered by using
// the --enable-source and --disable-source options)
std::shared_ptr<sinsp> inspector;
std::set<std::string> enabled_sources;
// Used to load all plugins to get their info. In capture mode,
// this is also used to open the capture file and read its events
std::shared_ptr<sinsp> offline_inspector;
// The event source index that correspond to "syscall"
std::size_t syscall_source_idx;
// List of all the information mapped to each event source
// indexed by event source name
indexed_vector<source_info> source_infos;
// List of all plugin configurations indexed by plugin name as returned
// by their sinsp_plugin::name method
indexed_vector<falco_configuration::plugin_config> plugin_configs;
// All filterchecks created by plugins go in this
// list. If we ever support multiple event sources at
// the same time, this, and the factories created in
// init_inspector/load_plugins, will have to be a map
// from event source to filtercheck list.
std::map<std::string, filter_check_list> plugin_filter_checks;
std::string cmdline;
// Set of syscalls we want the driver to capture
std::unordered_set<uint32_t> ppm_sc_of_interest;
// Set of tracepoints we want the driver to capture
std::unordered_set<uint32_t> tp_of_interest;
// Dimension of the syscall buffer in bytes.
uint64_t syscall_buffer_bytes_size;
#ifndef MINIMAL_BUILD
falco::grpc::server grpc_server;
std::thread grpc_server_thread;
@@ -157,21 +121,6 @@ private:
return r;
}
// Merges two run results into one
inline static run_result merge(const run_result& a, const run_result& b)
{
auto res = ok();
res.proceed = a.proceed && b.proceed;
res.success = a.success && b.success;
res.errstr = a.errstr;
if (!b.errstr.empty())
{
res.errstr += res.errstr.empty() ? "" : "\n";
res.errstr += b.errstr;
}
return res;
}
run_result();
virtual ~run_result();
@@ -234,7 +183,7 @@ private:
run_result attach_inotify_signals();
run_result daemonize();
run_result init_falco_engine();
run_result init_inspectors();
run_result init_inspector();
run_result init_clients();
run_result init_outputs();
run_result list_fields();
@@ -242,19 +191,14 @@ private:
run_result load_config();
run_result load_plugins();
run_result load_rules_files();
run_result create_requested_paths();
run_result open_inspector();
run_result print_generated_gvisor_config();
run_result print_help();
run_result print_ignored_events();
run_result print_plugin_info();
run_result print_support();
run_result print_syscall_events();
run_result print_version();
run_result print_page_size();
run_result process_events();
run_result select_event_sources();
void configure_interesting_sets();
application::run_result configure_syscall_buffer_size();
#ifndef MINIMAL_BUILD
run_result start_grpc_server();
run_result start_webserver();
@@ -271,30 +215,21 @@ private:
#endif
// Methods called by the above methods
int create_dir(const std::string &path);
bool create_handler(int sig, void (*func)(int), run_result &ret);
void configure_output_format();
void check_for_ignored_events();
void print_all_ignored_events();
void format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os) const;
run_result open_offline_inspector();
run_result open_live_inspector(std::shared_ptr<sinsp> inspector, const std::string& source);
void add_source_to_engine(const std::string& src);
void init_syscall_inspector(std::shared_ptr<sinsp> inspector, const falco::app::cmdline_options& opts);
run_result do_inspect(
std::shared_ptr<sinsp> inspector,
const std::string& source, // an empty source represents capture mode
std::shared_ptr<stats_writer> statsw,
syscall_evt_drop_mgr &sdropmgr,
bool check_drops_and_timeouts,
uint64_t duration_to_tot_ns,
uint64_t &num_evts);
void process_inspector_events(
std::shared_ptr<sinsp> inspector,
std::shared_ptr<stats_writer> statsw,
std::string source, // an empty source represents capture mode
run_result* res) noexcept;
run_result do_inspect(syscall_evt_drop_mgr &sdropmgr,
uint64_t duration_to_tot_ns,
uint64_t &num_events);
inline bool is_syscall_source_enabled() const
{
return m_state->enabled_sources.find(falco_common::syscall_source)
!= m_state->enabled_sources.end();
}
/* Returns true if we are in capture mode. */
inline bool is_capture_mode() const
{
return !m_options.trace_filename.empty();

View File

@@ -35,7 +35,6 @@ falco_configuration::falco_configuration():
m_buffered_outputs(false),
m_time_format_iso_8601(false),
m_webserver_enabled(false),
m_webserver_threadiness(0),
m_webserver_listen_port(8765),
m_webserver_k8s_healthz_endpoint("/healthz"),
m_webserver_ssl_enabled(false),
@@ -192,7 +191,7 @@ void falco_configuration::init(string conf_filename, const vector<string> &cmdli
m_output_timeout = m_config->get_scalar<uint32_t>("output_timeout", 2000);
m_notifications_rate = m_config->get_scalar<uint32_t>("outputs.rate", 0);
m_notifications_rate = m_config->get_scalar<uint32_t>("outputs.rate", 1);
m_notifications_max_burst = m_config->get_scalar<uint32_t>("outputs.max_burst", 1000);
string priority = m_config->get_scalar<string>("priority", "debug");
@@ -208,15 +207,10 @@ void falco_configuration::init(string conf_filename, const vector<string> &cmdli
falco_logger::log_syslog = m_config->get_scalar<bool>("log_syslog", true);
m_webserver_enabled = m_config->get_scalar<bool>("webserver.enabled", false);
m_webserver_threadiness = m_config->get_scalar<uint32_t>("webserver.threadiness", 0);
m_webserver_listen_port = m_config->get_scalar<uint32_t>("webserver.listen_port", 8765);
m_webserver_k8s_healthz_endpoint = m_config->get_scalar<string>("webserver.k8s_healthz_endpoint", "/healthz");
m_webserver_ssl_enabled = m_config->get_scalar<bool>("webserver.ssl_enabled", false);
m_webserver_ssl_certificate = m_config->get_scalar<string>("webserver.ssl_certificate", "/etc/falco/falco.pem");
if(m_webserver_threadiness == 0)
{
m_webserver_threadiness = falco::utils::hardware_concurrency();
}
std::list<string> syscall_event_drop_acts;
m_config->get_sequence(syscall_event_drop_acts, "syscall_event_drops.actions");
@@ -285,11 +279,6 @@ void falco_configuration::init(string conf_filename, const vector<string> &cmdli
throw logic_error("Error reading config file(" + m_config_file + "): metadata download watch frequency seconds must be an unsigned integer > 0");
}
/* We put this value in the configuration file because in this way we can change the dimension at every reload.
* The default value is `4` -> 8 MB.
*/
m_syscall_buf_size_preset = m_config->get_scalar<uint64_t>("syscall_buf_size_preset", 4);
std::set<std::string> load_plugins;
bool load_plugins_node_defined = m_config->is_defined("load_plugins");

View File

@@ -250,7 +250,6 @@ public:
std::string m_grpc_root_certs;
bool m_webserver_enabled;
uint32_t m_webserver_threadiness;
uint32_t m_webserver_listen_port;
std::string m_webserver_k8s_healthz_endpoint;
bool m_webserver_ssl_enabled;
@@ -269,9 +268,6 @@ public:
uint32_t m_metadata_download_chunk_wait_us;
uint32_t m_metadata_download_watch_freq_sec;
// Index corresponding to the syscall buffer dimension.
uint64_t m_syscall_buf_size_preset;
std::vector<plugin_config> m_plugins;
private:

View File

@@ -117,7 +117,7 @@ bool syscall_evt_drop_mgr::process_event(std::shared_ptr<sinsp> inspector, sinsp
{
m_num_actions++;
return perform_actions(evt->get_ts(), delta, inspector->check_current_engine(BPF_ENGINE));
return perform_actions(evt->get_ts(), delta, inspector->is_bpf_enabled());
}
else
{
@@ -142,16 +142,18 @@ bool syscall_evt_drop_mgr::perform_actions(uint64_t now, scap_stats &delta, bool
std::string rule = "Falco internal: syscall event drop";
std::string msg = rule + ". " + std::to_string(delta.n_drops) + " system calls dropped in last second.";
bool should_exit = false;
for(auto &act : m_actions)
{
switch(act)
{
case syscall_evt_drop_action::IGNORE:
return true;
break;
case syscall_evt_drop_action::LOG:
falco_logger::log(LOG_DEBUG, std::move(msg));
return true;
falco_logger::log(LOG_DEBUG, msg);
break;
case syscall_evt_drop_action::ALERT:
{
@@ -187,18 +189,24 @@ bool syscall_evt_drop_mgr::perform_actions(uint64_t now, scap_stats &delta, bool
output_fields["n_drops_bug"] = std::to_string(delta.n_drops_bug); /* Number of kernel side bug drops (invalid condition in the kernel instrumentation). */
output_fields["ebpf_enabled"] = std::to_string(bpf_enabled);
m_outputs->handle_msg(now, falco_common::PRIORITY_DEBUG, msg, rule, output_fields);
return true;
break;
}
case syscall_evt_drop_action::EXIT:
falco_logger::log(LOG_CRIT, std::move(msg));
falco_logger::log(LOG_CRIT, "Exiting.");
return false;
should_exit = true;
break;
default:
falco_logger::log(LOG_ERR, "Ignoring unknown action " + std::to_string(int(act)));
return true;
break;
}
}
if(should_exit)
{
falco_logger::log(LOG_CRIT, msg);
falco_logger::log(LOG_CRIT, "Exiting.");
return false;
}
return true;
}

View File

@@ -23,8 +23,10 @@ limitations under the License.
#include "logger.h"
#include "banned.h" // This raises a compilation error when certain functions are used
static void display_fatal_err(const string &&msg)
static void display_fatal_err(const string &msg)
{
falco_logger::log(LOG_ERR, msg);
/**
* If stderr logging is not enabled, also log to stderr. When
* daemonized this will simply write to /dev/null.
@@ -33,8 +35,6 @@ static void display_fatal_err(const string &&msg)
{
std::cerr << msg;
}
falco_logger::log(LOG_ERR, std::move(msg));
}
//

View File

@@ -39,49 +39,69 @@ limitations under the License.
using namespace std;
static const char* s_internal_source = "internal";
falco_outputs::falco_outputs(
std::shared_ptr<falco_engine> engine,
const std::vector<falco::outputs::config>& outputs,
bool json_output,
bool json_include_output_property,
bool json_include_tags_property,
uint32_t timeout,
bool buffered,
bool time_format_iso_8601,
std::string hostname)
falco_outputs::falco_outputs():
m_initialized(false),
m_buffered(true),
m_json_output(false),
m_time_format_iso_8601(false),
m_hostname("")
{
}
falco_outputs::~falco_outputs()
{
if(m_initialized)
{
this->stop_worker();
for(auto o : m_outputs)
{
delete o;
}
}
}
void falco_outputs::init(std::shared_ptr<falco_engine> engine,
bool json_output,
bool json_include_output_property,
bool json_include_tags_property,
uint32_t timeout,
uint32_t rate, uint32_t max_burst, bool buffered,
bool time_format_iso_8601, std::string hostname)
{
// Cannot be initialized more than one time.
if(m_initialized)
{
throw falco_exception("falco_outputs already initialized");
}
m_formats.reset(new falco_formats(engine, json_include_output_property, json_include_tags_property));
m_json_output = json_output;
m_timeout = std::chrono::milliseconds(timeout);
m_notifications_tb.init(rate, max_burst);
m_buffered = buffered;
m_time_format_iso_8601 = time_format_iso_8601;
m_hostname = hostname;
for(const auto& output : outputs)
{
add_output(output);
}
m_worker_thread = std::thread(&falco_outputs::worker, this);
m_initialized = true;
}
falco_outputs::~falco_outputs()
{
this->stop_worker();
for(auto o : m_outputs)
{
delete o;
}
}
// This function is called only at initialization-time by the constructor
// This function has to be called after init() since some configuration settings
// need to be passed to the output plugins. Then, although the worker has started,
// the worker is still on hold, waiting for a message.
// Thus it is still safe to call add_output() before any message has been enqueued.
void falco_outputs::add_output(falco::outputs::config oc)
{
if(!m_initialized)
{
throw falco_exception("cannot add output: falco_outputs not initialized yet");
}
falco::outputs::abstract_output *oo;
if(oc.name == "file")
@@ -122,6 +142,12 @@ void falco_outputs::add_output(falco::outputs::config oc)
void falco_outputs::handle_event(gen_event *evt, string &rule, string &source,
falco_common::priority_type priority, string &format, std::set<std::string> &tags)
{
if(!m_notifications_tb.claim())
{
falco_logger::log(LOG_DEBUG, "Skipping rate-limited notification for rule " + rule + "\n");
return;
}
falco_outputs::ctrl_msg cmsg = {};
cmsg.ts = evt->get_ts();
cmsg.priority = priority;
@@ -149,14 +175,12 @@ void falco_outputs::handle_event(gen_event *evt, string &rule, string &source,
sformat += " " + format;
}
cmsg.msg = m_formats->format_event(
evt, rule, source, falco_common::format_priority(priority), sformat, tags, m_hostname
);
cmsg.msg = m_formats->format_event(evt, rule, source, falco_common::format_priority(priority), sformat, tags);
cmsg.fields = m_formats->get_field_values(evt, source, sformat);
cmsg.tags.insert(tags.begin(), tags.end());
cmsg.type = ctrl_msg_type::CTRL_MSG_OUTPUT;
this->push(cmsg);
m_queue.push(cmsg);
}
void falco_outputs::handle_msg(uint64_t ts,
@@ -168,7 +192,7 @@ void falco_outputs::handle_msg(uint64_t ts,
falco_outputs::ctrl_msg cmsg = {};
cmsg.ts = ts;
cmsg.priority = priority;
cmsg.source = s_internal_source;
cmsg.source = "internal";
cmsg.rule = rule;
cmsg.fields = output_fields;
@@ -219,17 +243,17 @@ void falco_outputs::handle_msg(uint64_t ts,
}
cmsg.type = ctrl_msg_type::CTRL_MSG_OUTPUT;
this->push(cmsg);
m_queue.push(cmsg);
}
void falco_outputs::cleanup_outputs()
{
this->push_ctrl(falco_outputs::ctrl_msg_type::CTRL_MSG_CLEANUP);
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_CLEANUP);
}
void falco_outputs::reopen_outputs()
{
this->push_ctrl(falco_outputs::ctrl_msg_type::CTRL_MSG_REOPEN);
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_REOPEN);
}
void falco_outputs::stop_worker()
@@ -238,31 +262,22 @@ void falco_outputs::stop_worker()
wd.start([&](void *) -> void {
falco_logger::log(LOG_NOTICE, "output channels still blocked, discarding all remaining notifications\n");
m_queue.clear();
this->push_ctrl(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP);
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP);
});
wd.set_timeout(m_timeout, nullptr);
this->push_ctrl(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP);
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP);
if(m_worker_thread.joinable())
{
m_worker_thread.join();
}
}
inline void falco_outputs::push_ctrl(ctrl_msg_type cmt)
inline void falco_outputs::push(ctrl_msg_type cmt)
{
falco_outputs::ctrl_msg cmsg = {};
cmsg.type = cmt;
this->push(cmsg);
}
inline void falco_outputs::push(const ctrl_msg& cmsg)
{
if (!m_queue.try_push(cmsg))
{
fprintf(stderr, "Fatal error: Output queue reached maximum capacity. Exiting.\n");
exit(EXIT_FAILURE);
}
m_queue.push(cmsg);
}
// todo(leogr,leodido): this function is not supposed to throw exceptions, and with "noexcept",

View File

@@ -21,71 +21,57 @@ limitations under the License.
#include "gen_filter.h"
#include "falco_common.h"
#include "token_bucket.h"
#include "falco_engine.h"
#include "outputs.h"
#include "formats.h"
#include "tbb/concurrent_queue.h"
/*!
\brief This class acts as the primary interface between a program and the
falco output engine. The falco rules engine is implemented by a
separate class falco_engine.
All methods in this class are thread-safe. The output framework supports
a multi-producer model where messages are stored in a queue and consumed
by each configured output asynchrounously.
*/
//
// This class acts as the primary interface between a program and the
// falco output engine. The falco rules engine is implemented by a
// separate class falco_engine.
//
class falco_outputs
{
public:
falco_outputs(
std::shared_ptr<falco_engine> engine,
const std::vector<falco::outputs::config>& outputs,
bool json_output,
bool json_include_output_property,
bool json_include_tags_property,
uint32_t timeout,
bool buffered,
bool time_format_iso_8601,
std::string hostname);
falco_outputs();
virtual ~falco_outputs();
/*!
\brief Format then send the event to all configured outputs (`evt`
is an event that has matched some rule).
*/
void init(std::shared_ptr<falco_engine> engine,
bool json_output,
bool json_include_output_property,
bool json_include_tags_property,
uint32_t timeout,
uint32_t rate, uint32_t max_burst, bool buffered,
bool time_format_iso_8601, std::string hostname);
void add_output(falco::outputs::config oc);
// Format then send the event to all configured outputs (`evt` is an event that has matched some rule).
void handle_event(gen_event *evt, std::string &rule, std::string &source,
falco_common::priority_type priority, std::string &format, std::set<std::string> &tags);
/*!
\brief Format then send a generic message to all outputs.
Not necessarily associated with any event.
*/
// Format then send a generic message to all outputs. Not necessarily associated with any event.
void handle_msg(uint64_t now,
falco_common::priority_type priority,
std::string &msg,
std::string &rule,
std::map<std::string, std::string> &output_fields);
/*!
\brief Sends a cleanup message to all outputs.
Each output can have an implementation-specific behavior.
In general, this is used to flush or clean output buffers.
*/
void cleanup_outputs();
/*!
\brief Sends a message to all outputs that causes them to be closed and
reopened. Each output can have an implementation-specific behavior.
*/
void reopen_outputs();
private:
std::unique_ptr<falco_formats> m_formats;
bool m_initialized;
std::vector<falco::outputs::abstract_output *> m_outputs;
// Rate limits notifications
token_bucket m_notifications_tb;
bool m_buffered;
bool m_json_output;
bool m_time_format_iso_8601;
@@ -110,9 +96,7 @@ private:
falco_outputs_cbq m_queue;
std::thread m_worker_thread;
inline void push(const ctrl_msg& cmsg);
inline void push_ctrl(ctrl_msg_type cmt);
inline void push(ctrl_msg_type cmt);
void worker() noexcept;
void stop_worker();
void add_output(falco::outputs::config oc);
};

View File

@@ -72,7 +72,7 @@ static void gpr_log_dispatcher_func(gpr_log_func_args* args)
string copy = "grpc: ";
copy.append(args->message);
copy.push_back('\n');
falco_logger::log(priority, std::move(copy));
falco_logger::log(priority, copy);
}
void falco::grpc::server::thread_process(int thread_index)

View File

@@ -134,7 +134,7 @@ void falco_logger::set_sinsp_logging(bool enable, const std::string& severity, c
bool falco_logger::log_stderr = true;
bool falco_logger::log_syslog = true;
void falco_logger::log(int priority, const string&& msg)
void falco_logger::log(int priority, const string msg)
{
if(priority > falco_logger::level)

View File

@@ -30,7 +30,7 @@ class falco_logger
static void set_sinsp_logging(bool enable, const std::string& severity, const std::string& prefix);
static void log(int priority, const string&& msg);
static void log(int priority, const string msg);
static int level;
static bool log_stderr;

View File

@@ -1,200 +0,0 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <sys/time.h>
#include <signal.h>
#include <nlohmann/json.hpp>
#include <atomic>
#include <nlohmann/json.hpp>
#include "falco_common.h"
#include "stats_writer.h"
#include "logger.h"
#include "banned.h" // This raises a compilation error when certain functions are used
#include "logger.h"
// note: ticker_t is an uint16_t, which is enough because we don't care about
// overflows here. Threads calling stats_writer::handle() will just
// check that this value changed since their last observation.
static std::atomic<stats_writer::ticker_t> s_timer((stats_writer::ticker_t) 0);
static void timer_handler(int signum)
{
s_timer.fetch_add(1, std::memory_order_relaxed);
}
bool stats_writer::init_ticker(uint32_t interval_msec, string &err)
{
struct itimerval timer;
struct sigaction handler;
memset (&handler, 0, sizeof (handler));
handler.sa_handler = &timer_handler;
if (sigaction(SIGALRM, &handler, NULL) == -1)
{
err = string("Could not set up signal handler for periodic timer: ") + strerror(errno);
return false;
}
timer.it_value.tv_sec = interval_msec / 1000;
timer.it_value.tv_usec = (interval_msec % 1000) * 1000;
timer.it_interval = timer.it_value;
if (setitimer(ITIMER_REAL, &timer, NULL) == -1)
{
err = string("Could not set up periodic timer: ") + strerror(errno);
return false;
}
return true;
}
stats_writer::ticker_t stats_writer::get_ticker()
{
return s_timer.load(std::memory_order_relaxed);
}
stats_writer::stats_writer()
: m_initialized(false), m_total_samples(0)
{
}
stats_writer::stats_writer(const std::string &filename)
: m_initialized(true), m_total_samples(0)
{
m_output.exceptions(ofstream::failbit | ofstream::badbit);
m_output.open(filename, ios_base::app);
m_worker = std::thread(&stats_writer::worker, this);
}
stats_writer::~stats_writer()
{
if (m_initialized)
{
stop_worker();
m_output.close();
}
}
bool stats_writer::has_output() const
{
return m_initialized;
}
void stats_writer::stop_worker()
{
stats_writer::msg msg;
msg.stop = true;
push(msg);
if(m_worker.joinable())
{
m_worker.join();
}
}
inline void stats_writer::push(const stats_writer::msg& m)
{
if (!m_queue.try_push(m))
{
fprintf(stderr, "Fatal error: Stats queue reached maximum capacity. Exiting.\n");
exit(EXIT_FAILURE);
}
}
void stats_writer::worker() noexcept
{
stats_writer::msg m;
nlohmann::json jmsg;
auto tick = stats_writer::get_ticker();
auto last_tick = tick;
while(true)
{
// blocks until a message becomes availables
m_queue.pop(m);
if (m.stop)
{
return;
}
// update records for this event source
jmsg[m.source]["cur"]["events"] = m.stats.n_evts;
jmsg[m.source]["delta"]["events"] = m.delta.n_evts;
if (m.source == falco_common::syscall_source)
{
jmsg[m.source]["cur"]["drops"] = m.stats.n_drops;
jmsg[m.source]["cur"]["preemptions"] = m.stats.n_preemptions;
jmsg[m.source]["cur"]["drop_pct"] = (m.stats.n_evts == 0 ? 0.0 : (100.0*m.stats.n_drops/m.stats.n_evts));
jmsg[m.source]["delta"]["drops"] = m.delta.n_drops;
jmsg[m.source]["delta"]["preemptions"] = m.delta.n_preemptions;
jmsg[m.source]["delta"]["drop_pct"] = (m.delta.n_evts == 0 ? 0.0 : (100.0*m.delta.n_drops/m.delta.n_evts));
}
tick = stats_writer::get_ticker();
if (last_tick != tick)
{
m_total_samples++;
try
{
jmsg["sample"] = m_total_samples;
m_output << jmsg.dump() << endl;
}
catch(const exception &e)
{
falco_logger::log(LOG_ERR, "stats_writer (worker): " + string(e.what()) + "\n");
}
}
}
}
stats_writer::collector::collector(std::shared_ptr<stats_writer> writer)
: m_writer(writer), m_last_tick(0), m_samples(0)
{
}
void stats_writer::collector::collect(std::shared_ptr<sinsp> inspector, const std::string& src)
{
// just skip if no output is configured
if (m_writer->has_output())
{
// collect stats once per each ticker period
auto tick = stats_writer::get_ticker();
if (tick != m_last_tick)
{
stats_writer::msg msg;
msg.stop = false;
msg.source = src;
inspector->get_capture_stats(&msg.stats);
m_samples++;
if(m_samples == 1)
{
msg.delta = msg.stats;
}
else
{
msg.delta.n_evts = msg.stats.n_evts - m_last_stats.n_evts;
msg.delta.n_drops = msg.stats.n_drops - m_last_stats.n_drops;
msg.delta.n_preemptions = msg.stats.n_preemptions - m_last_stats.n_preemptions;
}
m_last_tick = tick;
m_last_stats = msg.stats;
m_writer->push(msg);
}
}
}

View File

@@ -1,130 +0,0 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <fstream>
#include <string>
#include <map>
#include <sinsp.h>
#include "tbb/concurrent_queue.h"
/*!
\brief Writes stats samples collected from inspectors into a given output.
Users must use a stats_writer::collector in order to collect and write stats
into a given stats_writer. This class is thread-safe, and can be shared
across multiple stats_writer::collector instances from different threads.
*/
class stats_writer
{
public:
/*!
\brief Value of a ticker that dictates when stats are collected
*/
typedef uint16_t ticker_t;
/*!
\brief Collects stats samples from an inspector and uses a writer
to print them in a given output. Stats are collected periodically every
time the value of stats_writer::get_ticker() changes.
This class is not thread-safe.
*/
class collector
{
public:
/*!
\brief Initializes the collector with the given writer
*/
explicit collector(std::shared_ptr<stats_writer> writer);
/*!
\brief Collects one stats sample from an inspector
and for the given event source name
*/
void collect(std::shared_ptr<sinsp> inspector, const std::string& src);
private:
std::shared_ptr<stats_writer> m_writer;
stats_writer::ticker_t m_last_tick;
uint64_t m_samples;
scap_stats m_last_stats;
};
stats_writer(const stats_writer&) = delete;
stats_writer(stats_writer&&) = delete;
stats_writer& operator=(const stats_writer&) = delete;
stats_writer& operator=(stats_writer&&) = delete;
~stats_writer();
/*!
\brief Initializes a writer without any output.
With this constructor, has_output() always returns false
*/
stats_writer();
/*!
\brief Initializes a writer that prints to a file at the given filename.
With this constructor, has_output() always returns true
*/
explicit stats_writer(const std::string &filename);
/*!
\brief Returns true if the writer is configured with a valid output
*/
inline bool has_output() const;
/*!
\brief Initializes the ticker with a given interval period defined
in milliseconds. Subsequent calls to init_ticker will dismiss the
previously-initialized ticker. Internally, this uses a timer
signal handler.
*/
static bool init_ticker(uint32_t interval_msec, std::string &err);
/*!
\brief Returns the current value of the ticker.
This function is thread-safe.
*/
inline static ticker_t get_ticker();
private:
struct msg
{
bool stop;
scap_stats delta;
scap_stats stats;
std::string source;
};
void worker() noexcept;
void stop_worker();
inline void push(const stats_writer::msg& m);
bool m_initialized;
uint64_t m_total_samples;
std::thread m_worker;
std::ofstream m_output;
tbb::concurrent_bounded_queue<stats_writer::msg> m_queue;
// note: in this way, only collectors can push into the queue
friend class stats_writer::collector;
};

View File

@@ -0,0 +1,116 @@
/*
Copyright (C) 2019 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <sys/time.h>
#include <signal.h>
#include <nlohmann/json.hpp>
#include "statsfilewriter.h"
#include "banned.h" // This raises a compilation error when certain functions are used
#include "logger.h"
using namespace std;
static bool g_save_stats = false;
static void timer_handler (int signum)
{
g_save_stats = true;
}
StatsFileWriter::StatsFileWriter()
: m_num_stats(0)
{
}
StatsFileWriter::~StatsFileWriter()
{
m_output.close();
}
bool StatsFileWriter::init(std::shared_ptr<sinsp> inspector, string &filename, uint32_t interval_msec, string &errstr)
{
struct itimerval timer;
struct sigaction handler;
m_inspector = inspector;
m_output.exceptions ( ofstream::failbit | ofstream::badbit );
m_output.open(filename, ios_base::app);
memset (&handler, 0, sizeof (handler));
handler.sa_handler = &timer_handler;
if (sigaction(SIGALRM, &handler, NULL) == -1)
{
errstr = string("Could not set up signal handler for periodic timer: ") + strerror(errno);
return false;
}
timer.it_value.tv_sec = interval_msec / 1000;
timer.it_value.tv_usec = (interval_msec % 1000) * 1000;
timer.it_interval = timer.it_value;
if (setitimer(ITIMER_REAL, &timer, NULL) == -1)
{
errstr = string("Could not set up periodic timer: ") + strerror(errno);
return false;
}
return true;
}
void StatsFileWriter::handle()
{
if (g_save_stats)
{
scap_stats cstats;
scap_stats delta;
nlohmann::json jmsg;
g_save_stats = false;
m_num_stats++;
m_inspector->get_capture_stats(&cstats);
if(m_num_stats == 1)
{
delta = cstats;
}
else
{
delta.n_evts = cstats.n_evts - m_last_stats.n_evts;
delta.n_drops = cstats.n_drops - m_last_stats.n_drops;
delta.n_preemptions = cstats.n_preemptions - m_last_stats.n_preemptions;
}
try
{
jmsg["sample"] = m_num_stats;
jmsg["cur"]["events"] = cstats.n_evts;
jmsg["cur"]["drops"] = cstats.n_drops;
jmsg["cur"]["preemptions"] = cstats.n_preemptions;
jmsg["cur"]["drop_pct"] = (cstats.n_evts == 0 ? 0 : (100.0*cstats.n_drops/cstats.n_evts));
jmsg["delta"]["events"] = delta.n_evts;
jmsg["delta"]["drops"] = delta.n_drops;
jmsg["delta"]["preemptions"] = delta.n_preemptions;
jmsg["delta"]["drop_pct"] = (delta.n_evts == 0 ? 0 : (100.0*delta.n_drops/delta.n_evts));
m_output << jmsg.dump() << endl;
}
catch(const exception &e)
{
falco_logger::log(LOG_ERR, "StatsFileWriter (handle): " + string(e.what()) + "\n");
}
m_last_stats = cstats;
}
}

View File

@@ -0,0 +1,47 @@
/*
Copyright (C) 2019 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <fstream>
#include <string>
#include <map>
#include <sinsp.h>
// Periodically collects scap stats files and writes them to a file as
// json.
class StatsFileWriter {
public:
StatsFileWriter();
virtual ~StatsFileWriter();
// Returns success as bool. On false fills in errstr.
bool init(std::shared_ptr<sinsp> inspector, std::string &filename,
uint32_t interval_msec,
string &errstr);
// Should be called often (like for each event in a sinsp
// loop).
void handle();
protected:
uint32_t m_num_stats;
std::shared_ptr<sinsp> m_inspector;
std::ofstream m_output;
scap_stats m_last_stats;
};

View File

@@ -15,7 +15,6 @@ limitations under the License.
*/
#include "webserver.h"
#include "falco_utils.h"
#include <atomic>
falco_webserver::~falco_webserver()
@@ -24,11 +23,10 @@ falco_webserver::~falco_webserver()
}
void falco_webserver::start(
uint32_t threadiness,
uint32_t listen_port,
std::string& healthz_endpoint,
std::string &ssl_certificate,
bool ssl_enabled)
uint32_t listen_port,
std::string& healthz_endpoint,
std::string &ssl_certificate,
bool ssl_enabled)
{
if (m_running)
{
@@ -48,9 +46,6 @@ void falco_webserver::start(
m_server = new httplib::Server();
}
// configure server
m_server->new_task_queue = [&threadiness] { return new httplib::ThreadPool(threadiness); };
// setup healthz endpoint
m_server->Get(healthz_endpoint,
[](const httplib::Request &, httplib::Response &res) {
@@ -66,7 +61,7 @@ void falco_webserver::start(
}
std::atomic<bool> failed;
failed.store(false, std::memory_order_release);
failed.store(false, std::memory_order_relaxed);
m_server_thread = std::thread([this, listen_port, &failed]
{
try

View File

@@ -25,7 +25,6 @@ class falco_webserver
public:
virtual ~falco_webserver();
virtual void start(
uint32_t threadiness,
uint32_t listen_port,
std::string& healthz_endpoint,
std::string &ssl_certificate,