mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 11:42:06 +00:00
Compare commits
81 Commits
update/pid
...
add-load-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd004fea27 | ||
|
|
5db61a1623 | ||
|
|
4d590fa6ee | ||
|
|
513f122aff | ||
|
|
5ffffeeada | ||
|
|
3dae1cbf91 | ||
|
|
12735bdfb1 | ||
|
|
f7c628f623 | ||
|
|
b2374b3c19 | ||
|
|
93e8be1e32 | ||
|
|
b246bcb052 | ||
|
|
6251af0ab6 | ||
|
|
ce79e01ae8 | ||
|
|
9db4c9b2cb | ||
|
|
dba685eeda | ||
|
|
4f8d11acdd | ||
|
|
3c47915c56 | ||
|
|
0ec492086e | ||
|
|
442d1accbe | ||
|
|
79577237a1 | ||
|
|
08237b946f | ||
|
|
62e762a467 | ||
|
|
e6fe0a516d | ||
|
|
a6b12a5c97 | ||
|
|
b15a51a825 | ||
|
|
c8122ff474 | ||
|
|
88dcdaac8a | ||
|
|
eabf49892d | ||
|
|
901fca2257 | ||
|
|
cc8d6705f6 | ||
|
|
26f626c1d5 | ||
|
|
acaaa0b4ca | ||
|
|
63ba15962b | ||
|
|
f163780d62 | ||
|
|
01093d2dfc | ||
|
|
a6c2bf7123 | ||
|
|
988703b601 | ||
|
|
66841d8009 | ||
|
|
43ae8b0cac | ||
|
|
9a5f625d5f | ||
|
|
799c09e638 | ||
|
|
2f267a044e | ||
|
|
7b6d45c394 | ||
|
|
368796df61 | ||
|
|
8d6c6900d3 | ||
|
|
34d796439f | ||
|
|
c8d1637130 | ||
|
|
600318aaae | ||
|
|
ba1528e3c2 | ||
|
|
37ea9b25c4 | ||
|
|
b66bf2c6e4 | ||
|
|
0d4e77bcbe | ||
|
|
6cdb740786 | ||
|
|
9a12a93342 | ||
|
|
84fe33a029 | ||
|
|
8fbf49bbba | ||
|
|
f10d0499d2 | ||
|
|
259095651b | ||
|
|
e5b9dd2dfc | ||
|
|
9620515e40 | ||
|
|
4f3181cb1c | ||
|
|
fe66abf15c | ||
|
|
527c42c030 | ||
|
|
aa62b65c70 | ||
|
|
78e2ddc63e | ||
|
|
179b191f70 | ||
|
|
a439d5b556 | ||
|
|
828fa7d14d | ||
|
|
590b034a55 | ||
|
|
54ab1eed9e | ||
|
|
05f483d751 | ||
|
|
c73e43c973 | ||
|
|
e8ee850dee | ||
|
|
105f2f6ee3 | ||
|
|
ce6368a89e | ||
|
|
0faa45669b | ||
|
|
aa6061681d | ||
|
|
86e76924a1 | ||
|
|
e7c5139563 | ||
|
|
bc12e567e5 | ||
|
|
b2ad928d6c |
1
.github/workflows/reusable_build_docker.yaml
vendored
1
.github/workflows/reusable_build_docker.yaml
vendored
@@ -71,3 +71,4 @@ jobs:
|
||||
with:
|
||||
name: falco-images
|
||||
path: /tmp/falco-*.tar
|
||||
retention-days: 1
|
||||
|
||||
57
.github/workflows/reusable_build_packages.yaml
vendored
57
.github/workflows/reusable_build_packages.yaml
vendored
@@ -36,6 +36,7 @@ jobs:
|
||||
with:
|
||||
name: bpf_probe_${{ inputs.arch }}.skel.h
|
||||
path: skeleton-build/skel_dir/bpf_probe.skel.h
|
||||
retention-days: 1
|
||||
|
||||
build-packages:
|
||||
# See https://github.com/actions/runner/issues/409#issuecomment-1158849936
|
||||
@@ -158,3 +159,59 @@ jobs:
|
||||
name: falco-${{ inputs.version }}-static-x86_64.tar.gz
|
||||
path: |
|
||||
${{ github.workspace }}/build/falco-${{ inputs.version }}-static-x86_64.tar.gz
|
||||
|
||||
build-wasm-package:
|
||||
if: ${{ inputs.arch == 'x86_64' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Always install deps before invoking checkout action, to properly perform a full clone.
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt install cmake build-essential git emscripten -y
|
||||
|
||||
- name: Select node version
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Prepare project
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
emcmake cmake \
|
||||
-DBUILD_BPF=Off \
|
||||
-DBUILD_DRIVER=Off \
|
||||
-DBUILD_LIBSCAP_MODERN_BPF=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DUSE_BUNDLED_DEPS=On \
|
||||
-DFALCO_ETC_DIR=/etc/falco \
|
||||
-DBUILD_FALCO_UNIT_TESTS=On \
|
||||
-DFALCO_VERSION=${{ inputs.version }} \
|
||||
..
|
||||
|
||||
- name: Build project
|
||||
run: |
|
||||
cd build
|
||||
emmake make -j6 all
|
||||
|
||||
- name: Run unit Tests
|
||||
run: |
|
||||
cd build
|
||||
node ./unit_tests/falco_unit_tests.js
|
||||
|
||||
- name: Build packages
|
||||
run: |
|
||||
cd build
|
||||
emmake make -j6 package
|
||||
|
||||
- name: Upload Falco WASM package
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: falco-${{ inputs.version }}-wasm.tar.gz
|
||||
path: |
|
||||
${{ github.workspace }}/build/falco-${{ inputs.version }}-wasm.tar.gz
|
||||
|
||||
12
.github/workflows/reusable_test_packages.yaml
vendored
12
.github/workflows/reusable_test_packages.yaml
vendored
@@ -44,6 +44,13 @@ jobs:
|
||||
cd falco-${{ inputs.version }}-${{ inputs.arch }}
|
||||
sudo cp -r * /
|
||||
|
||||
# x86_64 job run on ubuntu-22.04 and here we can install kernel-headers
|
||||
- name: Install dependencies for falco-driver-loader tests on x86
|
||||
if: ${{ inputs.arch == 'x86_64' }}
|
||||
run: |
|
||||
sudo apt update -y
|
||||
sudo apt install -y --no-install-recommends build-essential clang make llvm gcc dkms linux-headers-$(uname -r)
|
||||
|
||||
- name: Install go-junit-report
|
||||
run: |
|
||||
pushd submodules/falcosecurity-testing
|
||||
@@ -56,6 +63,8 @@ jobs:
|
||||
go generate ./...
|
||||
popd
|
||||
|
||||
# Right now we are not able to install kernel-headers on our ARM64 self-hosted runner.
|
||||
# For this reason, we disable the falco-driver-loader tests, which require kernel headers on the host.
|
||||
- name: Run regression tests
|
||||
run: |
|
||||
pushd submodules/falcosecurity-testing
|
||||
@@ -63,6 +72,9 @@ jobs:
|
||||
if ${{ inputs.static && 'false' || 'true' }}; then
|
||||
./build/falcoctl.test -test.timeout=90s -test.v >> ./report.txt 2>&1 || true
|
||||
./build/k8saudit.test -test.timeout=90s -test.v >> ./report.txt 2>&1 || true
|
||||
if ${{ inputs.arch == 'x86_64' && 'true' || 'false' }}; then
|
||||
sudo ./build/falco-driver-loader.test -test.timeout=90s -test.v >> ./report.txt 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
cat ./report.txt | go-junit-report -set-exit-code > report.xml
|
||||
popd
|
||||
|
||||
@@ -20,6 +20,14 @@ option(MINIMAL_BUILD "Build a minimal version of Falco, containing only the engi
|
||||
option(MUSL_OPTIMIZED_BUILD "Enable if you want a musl optimized build" OFF)
|
||||
option(BUILD_FALCO_UNIT_TESTS "Build falco unit tests" OFF)
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
set(USE_BUNDLED_DEPS ON CACHE BOOL "" FORCE)
|
||||
set(BUILD_DRIVER OFF CACHE BOOL "" FORCE)
|
||||
set(ENABLE_DKMS OFF CACHE BOOL "" FORCE)
|
||||
set(BUILD_BPF OFF CACHE BOOL "" FORCE)
|
||||
set(CPACK_GENERATOR TGZ CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
|
||||
# gVisor is currently only supported on Linux x86_64
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD)
|
||||
option(BUILD_FALCO_GVISOR "Build gVisor support for Falco" ON)
|
||||
@@ -68,7 +76,11 @@ if(NOT DEFINED FALCO_ETC_DIR)
|
||||
endif()
|
||||
|
||||
# This will be used to print the architecture for which Falco is compiled.
|
||||
set(FALCO_TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR})
|
||||
if (EMSCRIPTEN)
|
||||
set(FALCO_TARGET_ARCH "wasm")
|
||||
else()
|
||||
set(FALCO_TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
|
||||
if(NOT FALCO_EXTRA_DEBUG_FLAGS)
|
||||
set(FALCO_EXTRA_DEBUG_FLAGS "-D_DEBUG")
|
||||
@@ -95,7 +107,10 @@ endif()
|
||||
|
||||
# explicitly set hardening flags
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
set(FALCO_SECURITY_FLAGS "-Wl,-z,relro,-z,now -fstack-protector-strong")
|
||||
set(FALCO_SECURITY_FLAGS "")
|
||||
if(NOT EMSCRIPTEN)
|
||||
set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -Wl,-z,relro,-z,now -fstack-protector-strong")
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "release")
|
||||
set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -D_FORTIFY_SOURCE=2")
|
||||
endif()
|
||||
@@ -154,19 +169,17 @@ include(jq)
|
||||
# nlohmann-json
|
||||
include(njson)
|
||||
|
||||
# b64
|
||||
include(b64)
|
||||
|
||||
# yaml-cpp
|
||||
include(yaml-cpp)
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
if(NOT WIN32 AND NOT APPLE AND NOT MINIMAL_BUILD AND NOT EMSCRIPTEN)
|
||||
# OpenSSL
|
||||
include(openssl)
|
||||
|
||||
# libcurl
|
||||
include(curl)
|
||||
|
||||
# todo(jasondellaluce,rohith-raju): support webserver for non-linux builds too
|
||||
# cpp-httlib
|
||||
include(cpp-httplib)
|
||||
endif()
|
||||
@@ -174,14 +187,18 @@ endif()
|
||||
include(cxxopts)
|
||||
|
||||
# One TBB
|
||||
include(tbb)
|
||||
if (NOT EMSCRIPTEN)
|
||||
include(tbb)
|
||||
endif()
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
if (NOT MINIMAL_BUILD)
|
||||
include(zlib)
|
||||
include(cares)
|
||||
include(protobuf)
|
||||
# gRPC
|
||||
include(grpc)
|
||||
if (NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN)
|
||||
include(cares)
|
||||
include(protobuf)
|
||||
# gRPC
|
||||
include(grpc)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Installation
|
||||
@@ -212,12 +229,11 @@ add_subdirectory(scripts)
|
||||
add_subdirectory(userspace/engine)
|
||||
add_subdirectory(userspace/falco)
|
||||
|
||||
if(NOT MUSL_OPTIMIZED_BUILD)
|
||||
if(NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT MUSL_OPTIMIZED_BUILD)
|
||||
include(plugins)
|
||||
include(falcoctl)
|
||||
endif()
|
||||
|
||||
include(falcoctl)
|
||||
|
||||
# Packages configuration
|
||||
include(CPackConfig)
|
||||
|
||||
|
||||
18
README.md
18
README.md
@@ -46,8 +46,7 @@ Considerations and guidance for Falco adopters:
|
||||
|
||||
## How to Contribute
|
||||
|
||||
Please refer to 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) for more information on how to contribute.
|
||||
|
||||
Please refer to the [contributing guide](https://github.com/falcosecurity/.github/blob/main/CONTRIBUTING.md) and the [code of conduct](https://github.com/falcosecurity/evolution/blob/main/CODE_OF_CONDUCT.md) for more information on how to contribute.
|
||||
|
||||
## Join the Community
|
||||
|
||||
@@ -73,11 +72,24 @@ To report security vulnerabilities, please follow the community process outlined
|
||||
|
||||
Stay updated with Falco's evolving capabilities by exploring the [Falco Roadmap](https://github.com/orgs/falcosecurity/projects/5), which provides insights into the features currently under development and planned for future releases.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Falco is licensed to you under the [Apache 2.0](./COPYING) open source license.
|
||||
|
||||
## Why is Falco in C++ rather than Go or {language}?
|
||||
|
||||
1. The first lines of code at the base of Falco were written some time ago, where Go didn't yet have the same level of maturity and adoption as today.
|
||||
2. The Falco execution model is sequential and mono-thread due to the statefulness requirements of the tool, and so most of the concurrency-related selling points of the Go runtime would not be leveraged at all.
|
||||
3. The Falco code deals with very low-level programming in many places (e.g. some headers are shared with the eBPF probe and the Kernel module), and we all know that interfacing Go with C is possible but brings tons of complexity and tradeoffs to the table.
|
||||
4. As a security tool meant to consume a crazy high throughput of events per second, Falco needs to squeeze performance in all hot paths at runtime and requires deep control on memory allocation, which the Go runtime can't provide (there's also garbage collection involved).
|
||||
5. Although Go didn't suit the engineering requirements of the core of Falco, we still thought that it could be a good candidate for writing Falco extensions through the plugin system. This is the main reason we gave special attention and high priority to the development of the plugin-sdk-go.
|
||||
6. Go is not a requirement for having statically-linked binaries. In fact, we provide fully-static Falco builds since few years. The only issue with those is that the plugin system can't be supported with the current dynamic library model we currently have.
|
||||
7. The plugin system has been envisioned to support multiple languages, so on our end maintaining a C-compatible codebase is the best strategy to ensure maximum cross-language compatibility.
|
||||
8. In general, plugins have GLIBC requirements/dependencies because they have low-level C bindings required for dynamic loading. A potential solution for the future could be to also support plugin to be statically-linked at compilation time and so released as bundled in the Falco binary. Although no work started yet in this direction, this would solve most issues you reported and would provide a totally-static binary too. Of course, this would not be compatible with dynamic loading anymore, but it may be a viable solution for our static-build flavor of Falco.
|
||||
9. Memory safety is definitely a concern and we try our best to keep an high level of quality even though C++ is quite error prone. For instance, we try to use smart pointers whenever possible, we build the libraries with an address sanitizer in our CI, we run Falco through Valgrind before each release, and have ways to stress-test it to detect performance regressions or weird memory usage (e.g. https://github.com/falcosecurity/event-generator). On top of that, we also have third parties auditing the codebase by time to time. None of this make a perfect safety standpoint of course, but we try to maximize our odds. Go would definitely make our life easier from this perspective, however the tradeoffs never made it worth it so far due to the points above.
|
||||
10. The C++ codebase of falcosecurity/libs, which is at the core of Falco, is quite large and complex. Porting all that code to another language would be a major effort requiring lots of development resource and with an high chance of failure and regression. As such, our approach so far has been to choose refactors and code polishing instead, up until we'll reach an optimal level of stability, quality, and modularity, on that portion of code. This would allow further developments to be smoother and more feasibile in the future.
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
- [Governance](https://github.com/falcosecurity/evolution/blob/main/GOVERNANCE.md)
|
||||
|
||||
@@ -20,24 +20,36 @@ set(CPACK_PACKAGE_VERSION "${FALCO_VERSION}")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${FALCO_VERSION_MAJOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${FALCO_VERSION_MINOR}")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "${FALCO_VERSION_PATCH}")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}")
|
||||
set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_SOURCE_DIR}/cmake/cpack/CMakeCPackOptions.cmake")
|
||||
set(CPACK_STRIP_FILES "ON")
|
||||
set(CPACK_PACKAGE_RELOCATABLE "OFF")
|
||||
if (EMSCRIPTEN)
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-wasm")
|
||||
else()
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
# Built packages will include only the following components
|
||||
set(CPACK_INSTALL_CMAKE_PROJECTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR};${FALCO_COMPONENT_NAME};${FALCO_COMPONENT_NAME};/"
|
||||
"${CMAKE_CURRENT_BINARY_DIR};${DRIVER_COMPONENT_NAME};${DRIVER_COMPONENT_NAME};/"
|
||||
"${CMAKE_CURRENT_BINARY_DIR};${FALCO_COMPONENT_NAME};${FALCO_COMPONENT_NAME};/"
|
||||
)
|
||||
if(NOT MUSL_OPTIMIZED_BUILD) # static builds do not have plugins
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux") # only Linux has drivers
|
||||
list(APPEND CPACK_INSTALL_CMAKE_PROJECTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR};${PLUGINS_COMPONENT_NAME};${PLUGINS_COMPONENT_NAME};/"
|
||||
)
|
||||
"${CMAKE_CURRENT_BINARY_DIR};${DRIVER_COMPONENT_NAME};${DRIVER_COMPONENT_NAME};/")
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT MUSL_OPTIMIZED_BUILD) # static builds do not have plugins
|
||||
list(APPEND CPACK_INSTALL_CMAKE_PROJECTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR};${PLUGINS_COMPONENT_NAME};${PLUGINS_COMPONENT_NAME};/")
|
||||
endif()
|
||||
|
||||
if(NOT CPACK_GENERATOR)
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set(CPACK_GENERATOR DEB RPM TGZ)
|
||||
else()
|
||||
set(CPACK_GENERATOR TGZ)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Using package generators: ${CPACK_GENERATOR}")
|
||||
|
||||
@@ -15,11 +15,12 @@ cmake_minimum_required(VERSION 3.5.1)
|
||||
project(driver-repo NONE)
|
||||
|
||||
include(ExternalProject)
|
||||
message(STATUS "Driver repository: ${DRIVER_REPO}")
|
||||
message(STATUS "Driver version: ${DRIVER_VERSION}")
|
||||
|
||||
ExternalProject_Add(
|
||||
driver
|
||||
URL "https://github.com/falcosecurity/libs/archive/${DRIVER_VERSION}.tar.gz"
|
||||
URL "https://github.com/${DRIVER_REPO}/archive/${DRIVER_VERSION}.tar.gz"
|
||||
URL_HASH "${DRIVER_CHECKSUM}"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
||||
@@ -20,18 +20,29 @@ if(DRIVER_SOURCE_DIR)
|
||||
set(DRIVER_VERSION "0.0.0-local")
|
||||
message(STATUS "Using local version for driver: '${DRIVER_SOURCE_DIR}'")
|
||||
else()
|
||||
# DRIVER_REPO accepts a repository name (<org name>/<repo name>) alternative to the falcosecurity/libs repository.
|
||||
# In case you want to test against a fork of falcosecurity/libs just pass the variable -
|
||||
# ie., `cmake -DDRIVER_REPO=<your-gh-handle>/libs ..`
|
||||
if (NOT DRIVER_REPO)
|
||||
set(DRIVER_REPO "falcosecurity/libs")
|
||||
endif()
|
||||
|
||||
# DRIVER_VERSION accepts a git reference (branch name, commit hash, or tag) to the falcosecurity/libs repository
|
||||
# which contains the driver source code under the `/driver` directory.
|
||||
# The chosen driver version must be compatible with the given FALCOSECURITY_LIBS_VERSION.
|
||||
# In case you want to test against another driver version (or branch, or commit) just pass the variable -
|
||||
# ie., `cmake -DDRIVER_VERSION=dev ..`
|
||||
if(NOT DRIVER_VERSION)
|
||||
set(DRIVER_VERSION "942a2249b7b9f65def0a01acfb1fba84f629b3bf")
|
||||
set(DRIVER_CHECKSUM "SHA256=8670d7b24fad659674cea90b9b3d86e5d0775a6b2faedc0d5303f910242282ff")
|
||||
set(DRIVER_VERSION "6.0.0+driver")
|
||||
set(DRIVER_CHECKSUM "SHA256=573cef7b9c69cfe1d5d8b873d2a20ad8235a2a96997df6bcebd120692dee7a91")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}" -DDRIVER_VERSION=${DRIVER_VERSION} -DDRIVER_CHECKSUM=${DRIVER_CHECKSUM}
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}"
|
||||
-DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}"
|
||||
-DDRIVER_REPO=${DRIVER_REPO}
|
||||
-DDRIVER_VERSION=${DRIVER_VERSION}
|
||||
-DDRIVER_CHECKSUM=${DRIVER_CHECKSUM}
|
||||
${DRIVER_CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${DRIVER_CMAKE_WORKING_DIR})
|
||||
|
||||
# cmake --build .
|
||||
|
||||
@@ -15,14 +15,14 @@ include(ExternalProject)
|
||||
|
||||
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} FALCOCTL_SYSTEM_NAME)
|
||||
|
||||
set(FALCOCTL_VERSION "0.5.1")
|
||||
set(FALCOCTL_VERSION "0.6.0")
|
||||
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(FALCOCTL_SYSTEM_PROC_GO "amd64")
|
||||
set(FALCOCTL_HASH "ea7c89134dc745a1cbdbcf8f839d3b47851a40e1aebee20702a606b03b45b897")
|
||||
set(FALCOCTL_HASH "b81c36449b525e1de871288741beeadead021ac133d9b306f0636be1befe58a5")
|
||||
else() # aarch64
|
||||
set(FALCOCTL_SYSTEM_PROC_GO "arm64")
|
||||
set(FALCOCTL_HASH "22797200bf0e4c7c45f69207ed85218a3839115a302dc07939d3006778d41300")
|
||||
set(FALCOCTL_HASH "6e99fd765f67cdd46fa8c5b2969e97497856d2e615698ced04046c8898187b18")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
|
||||
@@ -15,11 +15,12 @@ cmake_minimum_required(VERSION 3.5.1)
|
||||
project(falcosecurity-libs-repo NONE)
|
||||
|
||||
include(ExternalProject)
|
||||
message(STATUS "Libs repository: ${FALCOSECURITY_LIBS_REPO}")
|
||||
message(STATUS "Libs version: ${FALCOSECURITY_LIBS_VERSION}")
|
||||
|
||||
ExternalProject_Add(
|
||||
falcosecurity-libs
|
||||
URL "https://github.com/falcosecurity/libs/archive/${FALCOSECURITY_LIBS_VERSION}.tar.gz"
|
||||
URL "https://github.com/${FALCOSECURITY_LIBS_REPO}/archive/${FALCOSECURITY_LIBS_VERSION}.tar.gz"
|
||||
URL_HASH "${FALCOSECURITY_LIBS_CHECKSUM}"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
||||
@@ -23,17 +23,25 @@ if(FALCOSECURITY_LIBS_SOURCE_DIR)
|
||||
set(FALCOSECURITY_LIBS_VERSION "0.0.0-local")
|
||||
message(STATUS "Using local version of falcosecurity/libs: '${FALCOSECURITY_LIBS_SOURCE_DIR}'")
|
||||
else()
|
||||
# FALCOSECURITY_LIBS_REPO accepts a repository name (<org name>/<repo name>) alternative to the falcosecurity/libs repository.
|
||||
# In case you want to test against a fork of falcosecurity/libs just pass the variable -
|
||||
# ie., `cmake -DFALCOSECURITY_LIBS_REPO=<your-gh-handle>/libs ..`
|
||||
if (NOT FALCOSECURITY_LIBS_REPO)
|
||||
set(FALCOSECURITY_LIBS_REPO "falcosecurity/libs")
|
||||
endif()
|
||||
|
||||
# FALCOSECURITY_LIBS_VERSION accepts a git reference (branch name, commit hash, or tag) to the falcosecurity/libs repository.
|
||||
# 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 "942a2249b7b9f65def0a01acfb1fba84f629b3bf")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=8670d7b24fad659674cea90b9b3d86e5d0775a6b2faedc0d5303f910242282ff")
|
||||
set(FALCOSECURITY_LIBS_VERSION "0.13.0-rc1")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=a75526b664bce2ba05912e056e48be39b0b1cb797b2055d107e55afbee2c8233")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}"
|
||||
-DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}"
|
||||
-DFALCOSECURITY_LIBS_REPO=${FALCOSECURITY_LIBS_REPO}
|
||||
-DFALCOSECURITY_LIBS_VERSION=${FALCOSECURITY_LIBS_VERSION}
|
||||
-DFALCOSECURITY_LIBS_CHECKSUM=${FALCOSECURITY_LIBS_CHECKSUM}
|
||||
${FALCOSECURITY_LIBS_CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${FALCOSECURITY_LIBS_CMAKE_WORKING_DIR})
|
||||
@@ -45,8 +53,10 @@ endif()
|
||||
|
||||
set(LIBS_PACKAGE_NAME "falcosecurity")
|
||||
|
||||
add_definitions(-D_GNU_SOURCE)
|
||||
add_definitions(-DHAS_CAPTURE)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
add_definitions(-D_GNU_SOURCE)
|
||||
add_definitions(-DHAS_CAPTURE)
|
||||
endif()
|
||||
|
||||
if(MUSL_OPTIMIZED_BUILD)
|
||||
add_definitions(-DMUSL_OPTIMIZED)
|
||||
|
||||
35
falco.yaml
35
falco.yaml
@@ -134,6 +134,12 @@
|
||||
# By arranging the order of files and rules thoughtfully, you can ensure that
|
||||
# desired customizations and rule behaviors are prioritized and applied as
|
||||
# intended.
|
||||
#
|
||||
# With Falco 0.36 and beyond, it's now possible to apply multiple rules that match
|
||||
# the same event type, eliminating concerns about rule prioritization based on the
|
||||
# "first match wins" principle. However, enabling the `all` matching option may result
|
||||
# in a performance penalty. We recommend carefully testing this alternative setting
|
||||
# before deploying it in production. Read more under the `rule_matching` configuration.
|
||||
rules_file:
|
||||
- /etc/falco/falco_rules.yaml
|
||||
- /etc/falco/falco_rules.local.yaml
|
||||
@@ -266,6 +272,9 @@ buffered_outputs: false
|
||||
|
||||
# [Stable] `outputs`
|
||||
#
|
||||
# [DEPRECATED]
|
||||
# This config is deprecated and it will be removed in Falco 0.37
|
||||
#
|
||||
# A throttling mechanism, implemented as a token bucket, can be used to control
|
||||
# the rate of Falco outputs. Each event source has its own rate limiter,
|
||||
# ensuring that alerts from one source do not affect the throttling of others.
|
||||
@@ -297,11 +306,17 @@ outputs:
|
||||
# - `all`: Falco will continue checking conditions of rules even if a matching
|
||||
# one was already found
|
||||
#
|
||||
# Rules conditions are evaluated in the order they are defined in the rulesfiles.
|
||||
# Rules conditions are evaluated in the order they are defined in the rules files.
|
||||
# For this reason, when using `first` as value, only the first defined rule will
|
||||
# trigger, possibly shadowing other rules.
|
||||
# In case `all` is used as value, rules still trigger in the order they were
|
||||
# defined.
|
||||
#
|
||||
# Effectively, with this setting, it is now possible to apply multiple rules that match
|
||||
# the same event type. This eliminates concerns about rule prioritization based on the
|
||||
# "first match wins" principle. However, enabling the `all` matching option may result in
|
||||
# a performance penalty. We recommend carefully testing this alternative setting before
|
||||
# deploying it in production.
|
||||
rule_matching: first
|
||||
|
||||
|
||||
@@ -359,6 +374,14 @@ http_output:
|
||||
# Path to a folder that will be used as the CA certificate store. CA certificate need to be
|
||||
# stored as indivitual PEM files in this directory.
|
||||
ca_path: "/etc/ssl/certs"
|
||||
# Tell Falco to use mTLS
|
||||
mtls: false
|
||||
# Path to the client cert.
|
||||
client_cert: "/etc/ssl/certs/client.crt"
|
||||
# Path to the client key.
|
||||
client_key: "/etc/ssl/certs/client.key"
|
||||
# Whether to echo server answers to stdout
|
||||
echo: false
|
||||
|
||||
# [Stable] `program_output`
|
||||
#
|
||||
@@ -510,8 +533,8 @@ log_level: info
|
||||
# operational logs. It allows you to specify the desired log level for the `libs`
|
||||
# library specifically, providing more granular control over the logging
|
||||
# behavior of the underlying components used by Falco. Only logs of a certain
|
||||
# severity level or higher will be emitted. Supported levels: "emergency",
|
||||
# "alert", "critical", "error", "warning", "notice", "info", "debug". It is not
|
||||
# severity level or higher will be emitted. Supported levels: "fatal",
|
||||
# "critical", "error", "warning", "notice", "info", "debug", "trace". It is not
|
||||
# recommended for production use.
|
||||
libs_logger:
|
||||
enabled: false
|
||||
@@ -613,7 +636,7 @@ syscall_event_drops:
|
||||
max_burst: 1
|
||||
simulate_drops: false
|
||||
|
||||
# [Experimental] `metrics`
|
||||
# [Stable] `metrics`
|
||||
#
|
||||
# Generates "Falco internal: metrics snapshot" rule output when `priority=info` at minimum
|
||||
# By selecting `output_file`, equivalent JSON output will be appended to a file.
|
||||
@@ -736,6 +759,8 @@ syscall_event_drops:
|
||||
metrics:
|
||||
enabled: false
|
||||
interval: 1h
|
||||
# Typically, in production, you only use `output_rule` or `output_file`, but not both.
|
||||
# However, if you have a very unique use case, you can use both together.
|
||||
output_rule: true
|
||||
# output_file: /tmp/falco_stats.jsonl
|
||||
resource_utilization_enabled: true
|
||||
@@ -811,7 +836,7 @@ syscall_buf_size_preset: 4
|
||||
# visibility into the system.
|
||||
syscall_drop_failed_exit: false
|
||||
|
||||
# [Experimental] `base_syscalls`, use with caution, read carefully
|
||||
# [Stable] `base_syscalls`, use with caution, read carefully
|
||||
#
|
||||
# --- [Description]
|
||||
#
|
||||
|
||||
@@ -15,40 +15,42 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# Systemd
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/scripts/systemd)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-kmod-inject.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-kmod.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-bpf.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-modern-bpf.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-custom.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falcoctl-artifact-follow.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
|
||||
# Debian
|
||||
configure_file(debian/postinst.in debian/postinst COPYONLY)
|
||||
configure_file(debian/postrm.in debian/postrm COPYONLY)
|
||||
configure_file(debian/prerm.in debian/prerm COPYONLY)
|
||||
|
||||
# Rpm
|
||||
configure_file(rpm/postinstall.in rpm/postinstall COPYONLY)
|
||||
configure_file(rpm/postuninstall.in rpm/postuninstall COPYONLY)
|
||||
configure_file(rpm/preuninstall.in rpm/preuninstall COPYONLY)
|
||||
|
||||
configure_file(falco-driver-loader falco-driver-loader @ONLY)
|
||||
|
||||
# Install Falcoctl config file
|
||||
if(NOT DEFINED FALCOCTL_ETC_DIR)
|
||||
set(FALCOCTL_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/falcoctl")
|
||||
endif()
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/falcoctl/falcoctl.yaml DESTINATION "${FALCOCTL_ETC_DIR}" COMPONENT "${FALCO_COMPONENT_NAME}")
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
# Systemd
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/scripts/systemd)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-kmod-inject.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-kmod.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-bpf.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-modern-bpf.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falco-custom.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/scripts/systemd/falcoctl-artifact-follow.service"
|
||||
"${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY)
|
||||
|
||||
# Debian
|
||||
configure_file(debian/postinst.in debian/postinst COPYONLY)
|
||||
configure_file(debian/postrm.in debian/postrm COPYONLY)
|
||||
configure_file(debian/prerm.in debian/prerm COPYONLY)
|
||||
|
||||
# Rpm
|
||||
configure_file(rpm/postinstall.in rpm/postinstall COPYONLY)
|
||||
configure_file(rpm/postuninstall.in rpm/postuninstall COPYONLY)
|
||||
configure_file(rpm/preuninstall.in rpm/preuninstall COPYONLY)
|
||||
|
||||
# driver loader
|
||||
configure_file(falco-driver-loader falco-driver-loader @ONLY)
|
||||
install(PROGRAMS ${PROJECT_BINARY_DIR}/scripts/falco-driver-loader
|
||||
DESTINATION ${FALCO_BIN_DIR} COMPONENT "${FALCO_COMPONENT_NAME}")
|
||||
endif()
|
||||
|
||||
# Install Falcoctl config file
|
||||
if (NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT MUSL_OPTIMIZED_BUILD)
|
||||
if(NOT DEFINED FALCOCTL_ETC_DIR)
|
||||
set(FALCOCTL_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/falcoctl")
|
||||
endif()
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/falcoctl/falcoctl.yaml DESTINATION "${FALCOCTL_ETC_DIR}" COMPONENT "${FALCO_COMPONENT_NAME}")
|
||||
endif()
|
||||
|
||||
@@ -117,7 +117,9 @@ get_target_id() {
|
||||
# Older RHEL distros
|
||||
OS_ID=rhel
|
||||
else
|
||||
return 1
|
||||
# No target id can be determinand
|
||||
TARGET_ID="undetermined"
|
||||
return
|
||||
fi
|
||||
|
||||
# Overwrite the OS_ID if /etc/VERSION file is present.
|
||||
@@ -163,7 +165,7 @@ get_target_id() {
|
||||
then
|
||||
ARCH_extra="-${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
|
||||
fi
|
||||
if [[ $(uname -v) =~ ([0-9]+\.[0-9]+\.[0-9]+\-[0-9]+) ]];
|
||||
if [[ ${DRIVER_KERNEL_VERSION} =~ ([0-9]+\.[0-9]+\.[0-9]+\-[0-9]+) ]];
|
||||
then
|
||||
KERNEL_RELEASE="${BASH_REMATCH[1]}${ARCH_extra}"
|
||||
fi
|
||||
@@ -187,9 +189,9 @@ get_target_id() {
|
||||
# from the following `uname -v` result
|
||||
# `#26~22.04.1-Ubuntu SMP Mon Apr 24 01:58:15 UTC 2023`
|
||||
# we obtain the kernelversion`26~22.04.1`
|
||||
if [[ $(uname -v) =~ (^\#[0-9]+\~[^-]*-Ubuntu .*$) ]];
|
||||
if [[ ${DRIVER_KERNEL_VERSION} =~ (^\#[0-9]+\~[^-]*-Ubuntu .*$) ]];
|
||||
then
|
||||
KERNEL_VERSION=$(uname -v | sed 's/#\([^-\\ ]*\).*/\1/g')
|
||||
KERNEL_VERSION=$(echo "${DRIVER_KERNEL_VERSION}" | sed 's/#\([^-\\ ]*\).*/\1/g')
|
||||
fi
|
||||
;;
|
||||
("flatcar")
|
||||
@@ -227,7 +229,6 @@ get_target_id() {
|
||||
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
flatcar_relocate_tools() {
|
||||
@@ -258,7 +259,7 @@ flatcar_relocate_tools() {
|
||||
|
||||
load_kernel_module_compile() {
|
||||
# Skip dkms on UEK hosts because it will always fail
|
||||
if [[ $(uname -r) == *uek* ]]; then
|
||||
if [[ ${DRIVER_KERNEL_RELEASE} == *uek* ]]; then
|
||||
>&2 echo "Skipping because the dkms install always fail (on UEK hosts)"
|
||||
return
|
||||
fi
|
||||
@@ -269,7 +270,7 @@ load_kernel_module_compile() {
|
||||
fi
|
||||
|
||||
if [ "${TARGET_ID}" == "flatcar" ]; then
|
||||
KERNEL_RELEASE=$(uname -r)
|
||||
KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE}
|
||||
echo "* Flatcar detected (version ${VERSION_ID}); relocating kernel tools"
|
||||
flatcar_relocate_tools
|
||||
fi
|
||||
@@ -343,9 +344,9 @@ load_kernel_module_download() {
|
||||
print_clean_termination() {
|
||||
echo
|
||||
echo "[SUCCESS] Cleaning phase correctly terminated."
|
||||
echo
|
||||
echo
|
||||
echo "================ Cleaning phase ================"
|
||||
echo
|
||||
echo
|
||||
}
|
||||
|
||||
print_filename_components() {
|
||||
@@ -355,10 +356,24 @@ print_filename_components() {
|
||||
echo " - kernel version: ${KERNEL_VERSION}"
|
||||
}
|
||||
|
||||
print_as_env_vars() {
|
||||
echo "ARCH=\"${ARCH}\""
|
||||
echo "KERNEL_RELEASE=\"${KERNEL_RELEASE}\""
|
||||
echo "KERNEL_VERSION=\"${KERNEL_VERSION}\""
|
||||
echo "ENABLE_COMPILE=\"${ENABLE_COMPILE}\""
|
||||
echo "ENABLE_DOWNLOAD=\"${ENABLE_DOWNLOAD}\""
|
||||
echo "TARGET_ID=\"${TARGET_ID}\""
|
||||
echo "DRIVER=\"${DRIVER}\""
|
||||
echo "DRIVERS_REPO=\"${DRIVERS_REPO}\""
|
||||
echo "DRIVER_VERSION=\"${DRIVER_VERSION}\""
|
||||
echo "DRIVER_NAME=\"${DRIVER_NAME}\""
|
||||
echo "FALCO_VERSION=\"${FALCO_VERSION}\""
|
||||
}
|
||||
|
||||
clean_kernel_module() {
|
||||
echo
|
||||
echo
|
||||
echo "================ Cleaning phase ================"
|
||||
echo
|
||||
echo
|
||||
|
||||
if ! hash lsmod > /dev/null 2>&1; then
|
||||
>&2 echo "This program requires lsmod."
|
||||
@@ -401,7 +416,7 @@ clean_kernel_module() {
|
||||
echo "[WARNING] '${KMOD_NAME}' module is still loaded, you could have incompatibility issues."
|
||||
echo
|
||||
fi
|
||||
|
||||
|
||||
if ! hash dkms >/dev/null 2>&1; then
|
||||
echo "- Skipping dkms remove (dkms not found)."
|
||||
print_clean_termination
|
||||
@@ -528,7 +543,7 @@ load_bpf_probe_compile() {
|
||||
MINIKUBE_VERSION="$(cat "${HOST_ROOT}/etc/VERSION")"
|
||||
echo "* Minikube detected (${MINIKUBE_VERSION}), using linux kernel sources for minikube kernel"
|
||||
local kernel_version
|
||||
kernel_version=$(uname -r)
|
||||
kernel_version=${DRIVER_KERNEL_RELEASE}
|
||||
local -r kernel_version_major=$(echo "${kernel_version}" | cut -d. -f1)
|
||||
local -r kernel_version_minor=$(echo "${kernel_version}" | cut -d. -f2)
|
||||
local -r kernel_version_patch=$(echo "${kernel_version}" | cut -d. -f3)
|
||||
@@ -541,9 +556,9 @@ load_bpf_probe_compile() {
|
||||
fi
|
||||
|
||||
if [ -n "${BPF_USE_LOCAL_KERNEL_SOURCES}" ]; then
|
||||
local -r kernel_version_major=$(uname -r | cut -d. -f1)
|
||||
local -r kernel_version=$(uname -r | cut -d- -f1)
|
||||
KERNEL_EXTRA_VERSION="-$(uname -r | cut -d- -f2)"
|
||||
local -r kernel_version_major=$(echo "${DRIVER_KERNEL_RELEASE}" | cut -d. -f1)
|
||||
local -r kernel_version=$(echo "${DRIVER_KERNEL_RELEASE}" | cut -d- -f1)
|
||||
KERNEL_EXTRA_VERSION="-$(echo "${DRIVER_KERNEL_RELEASE}" | cut -d- -f2)"
|
||||
|
||||
echo "* Using downloaded kernel sources for kernel version ${kernel_version}..."
|
||||
|
||||
@@ -666,13 +681,16 @@ print_usage() {
|
||||
echo " --clean try to remove an already present driver installation"
|
||||
echo " --compile try to compile the driver locally (default true)"
|
||||
echo " --download try to download a prebuilt driver (default true)"
|
||||
echo " --source-only skip execution and allow sourcing in another script"
|
||||
echo " --source-only skip execution and allow sourcing in another script using `. falco-driver-loader`"
|
||||
echo " --print-env skip execution and print env variables for other tools to consume"
|
||||
echo ""
|
||||
echo "Environment variables:"
|
||||
echo " DRIVERS_REPO specify different URL(s) where to look for prebuilt Falco drivers (comma separated)"
|
||||
echo " DRIVER_NAME specify a different name for the driver"
|
||||
echo " DRIVER_INSECURE_DOWNLOAD whether you want to allow insecure downloads or not"
|
||||
echo " DRIVER_CURL_OPTIONS specify additional options to be passed to curl command used to download Falco drivers"
|
||||
echo " DRIVER_KERNEL_RELEASE specify the kernel release for which to download/build the driver in the same format used by 'uname -r' (e.g. '6.1.0-10-cloud-amd64')"
|
||||
echo " DRIVER_KERNEL_VERSION specify the kernel version for which to download/build the driver in the same format used by 'uname -v' (e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')"
|
||||
echo ""
|
||||
echo "Versions:"
|
||||
echo " Falco version ${FALCO_VERSION}"
|
||||
@@ -682,13 +700,16 @@ print_usage() {
|
||||
|
||||
ARCH=$(uname -m)
|
||||
|
||||
KERNEL_RELEASE=$(uname -r)
|
||||
DRIVER_KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE:-$(uname -r)}
|
||||
KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE}
|
||||
|
||||
if ! hash sed > /dev/null 2>&1; then
|
||||
>&2 echo "This program requires sed"
|
||||
exit 1
|
||||
fi
|
||||
KERNEL_VERSION=$(uname -v | sed 's/#\([[:digit:]]\+\).*/\1/')
|
||||
|
||||
DRIVER_KERNEL_VERSION=${DRIVER_KERNEL_VERSION:-$(uname -v)}
|
||||
KERNEL_VERSION=$(echo "${DRIVER_KERNEL_VERSION}" | sed 's/#\([[:digit:]]\+\).*/\1/')
|
||||
|
||||
DRIVERS_REPO=${DRIVERS_REPO:-"@DRIVERS_REPO@"}
|
||||
|
||||
@@ -709,7 +730,8 @@ DRIVER_VERSION=${DRIVER_VERSION:-"@DRIVER_VERSION@"}
|
||||
DRIVER_NAME=${DRIVER_NAME:-"@DRIVER_NAME@"}
|
||||
FALCO_VERSION="@FALCO_VERSION@"
|
||||
|
||||
TARGET_ID="placeholder" # when no target id can be fetched, we try to build the driver from source anyway, using a placeholder name
|
||||
TARGET_ID=
|
||||
get_target_id
|
||||
|
||||
DRIVER="module"
|
||||
if [ -v FALCO_BPF_PROBE ]; then
|
||||
@@ -724,6 +746,7 @@ ENABLE_DOWNLOAD=
|
||||
clean=
|
||||
has_args=
|
||||
has_opts=
|
||||
print_env=
|
||||
source_only=
|
||||
while test $# -gt 0; do
|
||||
case "$1" in
|
||||
@@ -760,6 +783,10 @@ while test $# -gt 0; do
|
||||
source_only="true"
|
||||
shift
|
||||
;;
|
||||
--print-env)
|
||||
print_env="true"
|
||||
shift
|
||||
;;
|
||||
--*)
|
||||
>&2 echo "Unknown option: $1"
|
||||
print_usage
|
||||
@@ -778,55 +805,61 @@ if [ -z "$has_opts" ]; then
|
||||
ENABLE_DOWNLOAD="yes"
|
||||
fi
|
||||
|
||||
if [ -z "$source_only" ]; then
|
||||
echo "* Running falco-driver-loader for: falco version=${FALCO_VERSION}, driver version=${DRIVER_VERSION}, arch=${ARCH}, kernel release=${KERNEL_RELEASE}, kernel version=${KERNEL_VERSION}"
|
||||
if [ -n "$source_only" ]; then
|
||||
# Return or exit, depending if we've been sourced.
|
||||
(return 0 2>/dev/null) && return || exit 0
|
||||
fi
|
||||
|
||||
if [ "$(id -u)" != 0 ]; then
|
||||
>&2 echo "This program must be run as root (or with sudo)"
|
||||
if [ -n "$print_env" ]; then
|
||||
print_as_env_vars
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "* Running falco-driver-loader for: falco version=${FALCO_VERSION}, driver version=${DRIVER_VERSION}, arch=${ARCH}, kernel release=${KERNEL_RELEASE}, kernel version=${KERNEL_VERSION}"
|
||||
|
||||
if [ "$(id -u)" != 0 ]; then
|
||||
>&2 echo "This program must be run as root (or with sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$TARGET_ID" = "undetermined" ]; then
|
||||
if [ -n "$ENABLE_COMPILE" ]; then
|
||||
ENABLE_DOWNLOAD=
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community. Trying to compile anyway."
|
||||
else
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$clean" ]; then
|
||||
if [ -n "$has_opts" ]; then
|
||||
>&2 echo "Cannot use --clean with other options"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
get_target_id
|
||||
res=$?
|
||||
if [ $res != 0 ]; then
|
||||
if [ -n "$ENABLE_COMPILE" ]; then
|
||||
ENABLE_DOWNLOAD=
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community. Trying to compile anyway."
|
||||
else
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community."
|
||||
exit 1
|
||||
fi
|
||||
echo "* Running falco-driver-loader with: driver=$DRIVER, clean=yes"
|
||||
case $DRIVER in
|
||||
module)
|
||||
clean_kernel_module
|
||||
;;
|
||||
bpf)
|
||||
>&2 echo "--clean not supported for driver=bpf"
|
||||
exit 1
|
||||
esac
|
||||
else
|
||||
if ! hash curl > /dev/null 2>&1; then
|
||||
>&2 echo "This program requires curl"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$clean" ]; then
|
||||
if [ -n "$has_opts" ]; then
|
||||
>&2 echo "Cannot use --clean with other options"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "* Running falco-driver-loader with: driver=$DRIVER, clean=yes"
|
||||
case $DRIVER in
|
||||
echo "* Running falco-driver-loader with: driver=$DRIVER, compile=${ENABLE_COMPILE:-"no"}, download=${ENABLE_DOWNLOAD:-"no"}"
|
||||
case $DRIVER in
|
||||
module)
|
||||
clean_kernel_module
|
||||
load_kernel_module
|
||||
;;
|
||||
bpf)
|
||||
>&2 echo "--clean not supported for driver=bpf"
|
||||
exit 1
|
||||
esac
|
||||
else
|
||||
if ! hash curl > /dev/null 2>&1; then
|
||||
>&2 echo "This program requires curl"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "* Running falco-driver-loader with: driver=$DRIVER, compile=${ENABLE_COMPILE:-"no"}, download=${ENABLE_DOWNLOAD:-"no"}"
|
||||
case $DRIVER in
|
||||
module)
|
||||
load_kernel_module
|
||||
;;
|
||||
bpf)
|
||||
load_bpf_probe
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
load_bpf_probe
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -8,7 +8,8 @@ Wants=falcoctl-artifact-follow.service
|
||||
Type=simple
|
||||
User=root
|
||||
Environment=FALCO_BPF_PROBE=
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
ExecStart=/usr/bin/falco
|
||||
ExecReload=kill -1 $MAINPID
|
||||
UMask=0077
|
||||
TimeoutSec=30
|
||||
RestartSec=15s
|
||||
|
||||
@@ -7,7 +7,8 @@ Wants=falcoctl-artifact-follow.service
|
||||
[Service]
|
||||
Type=simple
|
||||
User=%u
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
ExecStart=/usr/bin/falco
|
||||
ExecReload=kill -1 $MAINPID
|
||||
UMask=0077
|
||||
TimeoutSec=30
|
||||
RestartSec=15s
|
||||
|
||||
@@ -9,7 +9,8 @@ Wants=falcoctl-artifact-follow.service
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
ExecStart=/usr/bin/falco
|
||||
ExecReload=kill -1 $MAINPID
|
||||
UMask=0077
|
||||
TimeoutSec=30
|
||||
RestartSec=15s
|
||||
|
||||
@@ -7,7 +7,8 @@ Wants=falcoctl-artifact-follow.service
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid --modern-bpf
|
||||
ExecStart=/usr/bin/falco --modern-bpf
|
||||
ExecReload=kill -1 $MAINPID
|
||||
UMask=0077
|
||||
TimeoutSec=30
|
||||
RestartSec=15s
|
||||
|
||||
Submodule submodules/falcosecurity-rules updated: 0d0e333151...6ed73fee78
Submodule submodules/falcosecurity-testing updated: bbb796b546...91100227b0
@@ -28,9 +28,17 @@ file(GLOB_RECURSE FALCO_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/falco/*.cpp)
|
||||
|
||||
set(FALCO_UNIT_TESTS_SOURCES
|
||||
"${ENGINE_TESTS}"
|
||||
"${FALCO_TESTS}"
|
||||
falco/test_configuration.cpp
|
||||
falco/app/actions/test_select_event_sources.cpp
|
||||
)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
list(APPEND FALCO_UNIT_TESTS_SOURCES
|
||||
falco/test_atomic_signal_handler.cpp
|
||||
falco/app/actions/test_configure_interesting_sets.cpp
|
||||
falco/app/actions/test_configure_syscall_buffer_num.cpp)
|
||||
endif()
|
||||
|
||||
set(FALCO_UNIT_TESTS_INCLUDES
|
||||
PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/userspace
|
||||
@@ -62,3 +70,10 @@ add_executable(falco_unit_tests ${FALCO_UNIT_TESTS_SOURCES})
|
||||
target_include_directories(falco_unit_tests ${FALCO_UNIT_TESTS_INCLUDES})
|
||||
target_link_libraries(falco_unit_tests ${FALCO_UNIT_TESTS_LIBRARIES})
|
||||
add_dependencies(falco_unit_tests ${FALCO_UNIT_TESTS_DEPENDENCIES})
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
target_compile_options(falco_unit_tests PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0")
|
||||
target_link_options(falco_unit_tests PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0")
|
||||
target_link_options(falco_unit_tests PRIVATE "-sALLOW_MEMORY_GROWTH=1")
|
||||
target_link_options(falco_unit_tests PRIVATE "-sEXPORTED_FUNCTIONS=['_main','_htons','_ntohs']")
|
||||
endif()
|
||||
|
||||
@@ -45,8 +45,8 @@ TEST(FalcoUtils, parse_prometheus_interval)
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1m"), 60000UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1h"), 3600000UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1d"), 86400000UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1w"), 604800000UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1y"), 31536000000UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1w"), 604800000UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1y"), (unsigned long)31536000000UL);
|
||||
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("300ms"), 300UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("255s"), 255000UL);
|
||||
@@ -57,14 +57,17 @@ TEST(FalcoUtils, parse_prometheus_interval)
|
||||
|
||||
/* Test matrix for concatenated time interval examples. */
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1h3m2s1ms"), 3600000UL + 3 * 60000UL + 2 * 1000UL + 1UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1y1w1d1h1m1s1ms"), 31536000000UL + 604800000UL + 86400000UL + 3600000UL + 60000UL + 1000UL + 1UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1y1w1d1h1m1s1ms"),(unsigned long) 31536000000UL + 604800000UL + 86400000UL + 3600000UL + 60000UL + 1000UL + 1UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("2h5m"), 2 * 3600000UL + 5 * 60000UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("2h 5m"), 2 * 3600000UL + 5 * 60000UL);
|
||||
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("200"), 200UL);
|
||||
|
||||
/* Invalid, non prometheus compliant time ordering will result in 0ms. */
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1ms1y"), 0UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1t1y"), 0UL);
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("1t"), 0UL);
|
||||
|
||||
/* Deprecated option to pass a numeric value in ms without prometheus compliant time unit,
|
||||
* will result in 0ms and as a result the end user will receive an error warning.
|
||||
*/
|
||||
ASSERT_EQ(falco::utils::parse_prometheus_interval("200"), 0UL);
|
||||
}
|
||||
|
||||
@@ -88,16 +88,88 @@ TEST(Configuration, modify_yaml_fields)
|
||||
{
|
||||
std::string key = "base_value.subvalue.subvalue2.boolean";
|
||||
yaml_helper conf;
|
||||
|
||||
|
||||
/* Get original value */
|
||||
conf.load_from_string(sample_yaml);
|
||||
ASSERT_EQ(conf.get_scalar<bool>(key, false), true);
|
||||
|
||||
|
||||
/* Modify the original value */
|
||||
conf.set_scalar<bool>(key, false);
|
||||
ASSERT_EQ(conf.get_scalar<bool>(key, true), false);
|
||||
|
||||
|
||||
/* Modify it again */
|
||||
conf.set_scalar<bool>(key, true);
|
||||
ASSERT_EQ(conf.get_scalar<bool>(key, false), true);
|
||||
}
|
||||
|
||||
TEST(Configuration, configuration_environment_variables)
|
||||
{
|
||||
// Set an environment variable for testing purposes
|
||||
std::string env_var_value = "envVarValue";
|
||||
std::string env_var_name = "ENV_VAR";
|
||||
std::string default_value = "default";
|
||||
setenv(env_var_name.c_str(), env_var_value.c_str(), 1);
|
||||
yaml_helper conf;
|
||||
|
||||
std::string sample_yaml =
|
||||
"base_value:\n"
|
||||
" id: $ENV_VAR\n"
|
||||
" name: '${ENV_VAR}'\n"
|
||||
" string: my_string\n"
|
||||
" invalid: $${ENV_VAR}\n"
|
||||
" invalid_env: $$ENV_VAR\n"
|
||||
" escaped: \"${ENV_VAR}\"\n"
|
||||
" subvalue:\n"
|
||||
" subvalue2:\n"
|
||||
" boolean: ${UNSED_XX_X_X_VAR}\n"
|
||||
"base_value_2:\n"
|
||||
" sample_list:\n"
|
||||
" - ${ENV_VAR}\n"
|
||||
" - ' ${ENV_VAR}'\n"
|
||||
" - $UNSED_XX_X_X_VAR\n";
|
||||
conf.load_from_string(sample_yaml);
|
||||
|
||||
/* Check if the base values are defined */
|
||||
ASSERT_TRUE(conf.is_defined("base_value"));
|
||||
ASSERT_TRUE(conf.is_defined("base_value_2"));
|
||||
ASSERT_FALSE(conf.is_defined("unknown_base_value"));
|
||||
|
||||
/* Test fetching of a regular string without any environment variable */
|
||||
std::string base_value_string = conf.get_scalar<std::string>("base_value.string", default_value);
|
||||
ASSERT_EQ(base_value_string, "my_string");
|
||||
|
||||
/* Test fetching of escaped environment variable format. Should return the string as-is after stripping the leading `$` */
|
||||
std::string base_value_invalid = conf.get_scalar<std::string>("base_value.invalid", default_value);
|
||||
ASSERT_EQ(base_value_invalid, "${ENV_VAR}");
|
||||
|
||||
/* Test fetching of invalid escaped environment variable format. Should return the string as-is */
|
||||
std::string base_value_invalid_env = conf.get_scalar<std::string>("base_value.invalid_env", default_value);
|
||||
ASSERT_EQ(base_value_invalid_env, "$$ENV_VAR");
|
||||
|
||||
/* Test fetching of strings that contain environment variables */
|
||||
std::string base_value_id = conf.get_scalar<std::string>("base_value.id", default_value);
|
||||
ASSERT_EQ(base_value_id, "$ENV_VAR"); // Does not follow the `${VAR}` format, so it should be treated as a regular string
|
||||
|
||||
std::string base_value_name = conf.get_scalar<std::string>("base_value.name", default_value);
|
||||
ASSERT_EQ(base_value_name, env_var_value); // Proper environment variable format
|
||||
|
||||
std::string base_value_escaped = conf.get_scalar<std::string>("base_value.escaped", default_value);
|
||||
ASSERT_EQ(base_value_escaped, env_var_value); // Environment variable within quotes
|
||||
|
||||
/* Test fetching of an undefined environment variable. Expected to return the default value.*/
|
||||
std::string unknown_boolean = conf.get_scalar<std::string>("base_value.subvalue.subvalue2.boolean", default_value);
|
||||
ASSERT_EQ(unknown_boolean, default_value);
|
||||
|
||||
/* Test fetching of environment variables from a list */
|
||||
std::string base_value_2_list_0 = conf.get_scalar<std::string>("base_value_2.sample_list[0]", default_value);
|
||||
ASSERT_EQ(base_value_2_list_0, env_var_value); // Proper environment variable format
|
||||
|
||||
std::string base_value_2_list_1 = conf.get_scalar<std::string>("base_value_2.sample_list[1]", default_value);
|
||||
ASSERT_EQ(base_value_2_list_1, " ${ENV_VAR}"); // Environment variable preceded by a space, hence treated as a regular string
|
||||
|
||||
std::string base_value_2_list_2 = conf.get_scalar<std::string>("base_value_2.sample_list[2]", default_value);
|
||||
ASSERT_EQ(base_value_2_list_2, "$UNSED_XX_X_X_VAR"); // Does not follow the `${VAR}` format, so should be treated as a regular string
|
||||
|
||||
/* Clear the set environment variable after testing */
|
||||
unsetenv(env_var_name.c_str());
|
||||
}
|
||||
|
||||
@@ -29,6 +29,10 @@ set(FALCO_ENGINE_SOURCE_FILES
|
||||
|
||||
add_library(falco_engine STATIC ${FALCO_ENGINE_SOURCE_FILES})
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
target_compile_options(falco_engine PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0")
|
||||
endif()
|
||||
|
||||
add_dependencies(falco_engine yamlcpp njson)
|
||||
|
||||
if(MINIMAL_BUILD)
|
||||
|
||||
@@ -189,26 +189,69 @@ void falco_engine::load_rules(const std::string &rules_content, bool verbose, bo
|
||||
|
||||
std::unique_ptr<load_result> falco_engine::load_rules(const std::string &rules_content, const std::string &name)
|
||||
{
|
||||
rule_loader::configuration cfg(rules_content, m_sources, name);
|
||||
cfg.min_priority = m_min_priority;
|
||||
cfg.output_extra = m_extra;
|
||||
cfg.replace_output_container_info = m_replace_container_info;
|
||||
cfg.default_ruleset_id = m_default_ruleset_id;
|
||||
std::vector<std::reference_wrapper<const std::string>> rules_contents;
|
||||
std::vector<std::reference_wrapper<const std::string>> names;
|
||||
|
||||
rule_loader::reader reader;
|
||||
if (reader.read(cfg, m_rule_collector))
|
||||
rules_contents.emplace_back(rules_content);
|
||||
names.emplace_back(name);
|
||||
|
||||
return load_rules_refs(rules_contents, names);
|
||||
}
|
||||
|
||||
std::unique_ptr<load_result> falco_engine::load_rules(const std::vector<std::string> &rules_contents,
|
||||
const std::vector<std::string> &names)
|
||||
{
|
||||
std::vector<std::reference_wrapper<const std::string>> rules_contents_refs(rules_contents.begin(), rules_contents.end());
|
||||
std::vector<std::reference_wrapper<const std::string>> names_refs(names.begin(), names.end());
|
||||
|
||||
return load_rules_refs(rules_contents_refs, names_refs);
|
||||
}
|
||||
|
||||
std::unique_ptr<load_result> falco_engine::load_rules_refs(const std::vector<std::reference_wrapper<const std::string>> &rules_contents,
|
||||
const std::vector<std::reference_wrapper<const std::string>> &names)
|
||||
{
|
||||
if(rules_contents.size() != names.size() ||
|
||||
rules_contents.size() == 0)
|
||||
{
|
||||
for (auto &src : m_sources)
|
||||
{
|
||||
src.ruleset = src.ruleset_factory->new_ruleset();
|
||||
}
|
||||
rule_loader::context ctx("Provided rules contents arrays");
|
||||
|
||||
rule_loader::compiler compiler;
|
||||
m_rules.clear();
|
||||
compiler.compile(cfg, m_rule_collector, m_rules);
|
||||
std::unique_ptr<rule_loader::result> res(new rule_loader::result("Provided rules contents arrays"));
|
||||
|
||||
res->add_error(load_result::LOAD_ERR_FILE_READ, "Lists of rules contents and names must have same non-zero length", ctx);
|
||||
|
||||
// Old gcc versions (e.g. 4.8.3) won't allow move elision but newer versions
|
||||
// (e.g. 10.2.1) would complain about the redundant move.
|
||||
#if __GNUC__ > 4
|
||||
return res;
|
||||
#else
|
||||
return std::move(res);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cfg.res->successful())
|
||||
std::unique_ptr<rule_loader::configuration> cfg;
|
||||
for(size_t idx = 0; idx < rules_contents.size(); idx++)
|
||||
{
|
||||
cfg = std::make_unique<rule_loader::configuration>(rules_contents[idx], m_sources, names[idx]);
|
||||
cfg->min_priority = m_min_priority;
|
||||
cfg->output_extra = m_extra;
|
||||
cfg->replace_output_container_info = m_replace_container_info;
|
||||
cfg->default_ruleset_id = m_default_ruleset_id;
|
||||
|
||||
rule_loader::reader reader;
|
||||
if (reader.read(*(cfg.get()), m_rule_collector))
|
||||
{
|
||||
for (auto &src : m_sources)
|
||||
{
|
||||
src.ruleset = src.ruleset_factory->new_ruleset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule_loader::compiler compiler;
|
||||
m_rules.clear();
|
||||
compiler.compile(*(cfg.get()), m_rule_collector, m_rules);
|
||||
|
||||
if (cfg->res->successful())
|
||||
{
|
||||
m_rule_stats_manager.clear();
|
||||
for (const auto &r : m_rules)
|
||||
@@ -217,7 +260,7 @@ std::unique_ptr<load_result> falco_engine::load_rules(const std::string &rules_c
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(cfg.res);
|
||||
return std::move(cfg->res);
|
||||
}
|
||||
|
||||
void falco_engine::load_rules_file(const std::string &rules_filename, bool verbose, bool all_events)
|
||||
@@ -233,23 +276,44 @@ void falco_engine::load_rules_file(const std::string &rules_filename, bool verbo
|
||||
|
||||
std::unique_ptr<load_result> falco_engine::load_rules_file(const std::string &rules_filename)
|
||||
{
|
||||
std::string rules_content;
|
||||
std::vector<std::string> rules_filenames;
|
||||
|
||||
try {
|
||||
read_file(rules_filename, rules_content);
|
||||
}
|
||||
catch (falco_exception &e)
|
||||
rules_filenames.emplace_back(rules_filename);
|
||||
|
||||
return load_rules_files(rules_filenames);
|
||||
}
|
||||
|
||||
std::unique_ptr<load_result> falco_engine::load_rules_files(const std::vector<std::string> &rules_filenames)
|
||||
{
|
||||
std::vector<std::string> rules_contents;
|
||||
|
||||
for(auto &filename : rules_filenames)
|
||||
{
|
||||
rule_loader::context ctx(rules_filename);
|
||||
std::string rules_content;
|
||||
|
||||
std::unique_ptr<rule_loader::result> res(new rule_loader::result(rules_filename));
|
||||
try {
|
||||
read_file(filename, rules_content);
|
||||
rules_contents.emplace_back(std::move(rules_content));
|
||||
}
|
||||
catch (falco_exception &e)
|
||||
{
|
||||
rule_loader::context ctx(filename);
|
||||
|
||||
res->add_error(load_result::LOAD_ERR_FILE_READ, e.what(), ctx);
|
||||
std::unique_ptr<rule_loader::result> res(new rule_loader::result(filename));
|
||||
|
||||
return res;
|
||||
res->add_error(load_result::LOAD_ERR_FILE_READ, e.what(), ctx);
|
||||
|
||||
// Old gcc versions (e.g. 4.8.3) won't allow move elision but newer versions
|
||||
// (e.g. 10.2.1) would complain about the redundant move.
|
||||
#if __GNUC__ > 4
|
||||
return res;
|
||||
#else
|
||||
return std::move(res);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return load_rules(rules_content, rules_filename);
|
||||
return load_rules(rules_contents, rules_filenames);
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule(const std::string &substring, bool enabled, const std::string &ruleset)
|
||||
@@ -562,12 +626,12 @@ void falco_engine::describe_rule(std::string *rule, bool json) const
|
||||
output["lists"] = lists_array;
|
||||
|
||||
json_str = writer.write(output);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// build json information for just the specified rule
|
||||
auto ri = m_rule_collector.rules().at(*rule);
|
||||
if(ri == nullptr)
|
||||
if(ri == nullptr || ri->unknown_source)
|
||||
{
|
||||
throw falco_exception("Rule \"" + *rule + "\" is not loaded");
|
||||
}
|
||||
|
||||
@@ -23,9 +23,11 @@ limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -73,6 +75,15 @@ public:
|
||||
std::unique_ptr<falco::load_result> load_rules_file(const std::string &rules_filename);
|
||||
std::unique_ptr<falco::load_result> load_rules(const std::string &rules_content, const std::string &name);
|
||||
|
||||
//
|
||||
// Identical to above, but allows providing a vector of files
|
||||
// instead of a single file at a time. (This speeds up loading
|
||||
// a bit because rule compilation can be deferred until all
|
||||
// the files are read).
|
||||
std::unique_ptr<falco::load_result> load_rules_files(const std::vector<std::string> &rules_filenames);
|
||||
std::unique_ptr<falco::load_result> load_rules(const std::vector<std::string> &rules_contents,
|
||||
const std::vector<std::string> &names);
|
||||
|
||||
//
|
||||
// Enable/Disable any rules matching the provided substring.
|
||||
// If the substring is "", all rules are enabled/disabled.
|
||||
@@ -273,6 +284,11 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
// Used by all the load_rules_* variants above with
|
||||
// reference_wrapper to avoid copies.
|
||||
std::unique_ptr<falco::load_result> load_rules_refs(const std::vector<std::reference_wrapper<const std::string>> &rules_contents,
|
||||
const std::vector<std::reference_wrapper<const std::string>> &names);
|
||||
|
||||
// Throws falco_exception if the file can not be read
|
||||
void read_file(const std::string& filename, std::string& contents);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
// The version of this Falco engine.
|
||||
#define FALCO_ENGINE_VERSION (22)
|
||||
#define FALCO_ENGINE_VERSION (25)
|
||||
|
||||
// This is the result of running the following command:
|
||||
// FALCO="falco -c ./falco.yaml"
|
||||
@@ -23,4 +23,4 @@ limitations under the License.
|
||||
// It represents the fields supported by this version of Falco,
|
||||
// the event types, and the underlying driverevent schema. It's used to
|
||||
// detetect changes in engine version in our CI jobs.
|
||||
#define FALCO_ENGINE_CHECKSUM "41dc03aebd3dd09e1aaaa1bb25055b65902c7c61c180cb9486d1d9850d18d67b"
|
||||
#define FALCO_ENGINE_CHECKSUM "41b5dc700216b243d294b40c46264d4e89d0ee00098fdc1c21bb4b1e7639da06"
|
||||
|
||||
@@ -62,7 +62,7 @@ static const std::string warning_codes[] = {
|
||||
"LOAD_UNKNOWN_SOURCE",
|
||||
"LOAD_UNSAFE_NA_CHECK",
|
||||
"LOAD_NO_EVTTYPE",
|
||||
"LOAD_UNKNOWN_FIELD",
|
||||
"LOAD_UNKNOWN_FILTER",
|
||||
"LOAD_UNUSED_MACRO",
|
||||
"LOAD_UNUSED_LIST",
|
||||
"LOAD_UNKNOWN_ITEM"
|
||||
@@ -77,7 +77,7 @@ static const std::string warning_strings[] = {
|
||||
"Unknown event source",
|
||||
"Unsafe <NA> comparison in condition",
|
||||
"Condition has no event-type restriction",
|
||||
"Unknown field in condition",
|
||||
"Unknown field or event-type in condition or output",
|
||||
"Unused macro",
|
||||
"Unused list",
|
||||
"Unknown rules file item"
|
||||
@@ -92,7 +92,7 @@ static const std::string warning_descs[] = {
|
||||
"A rule has a unknown event source. This can occur when reading rules content without having a corresponding plugin loaded, etc. The rule will be silently ignored.",
|
||||
"Comparing a field value with <NA> is unsafe and can lead to unpredictable behavior of the rule condition. If you need to check for the existence of a field, consider using the 'exists' operator instead.",
|
||||
"A rule condition matches too many evt.type values. This has a significant performance penalty. Make the condition more specific by adding an evt.type field or further restricting the number of evt.type values in the condition.",
|
||||
"A rule condition refers to a field that does not exist. This is normally an error, but if a rule has a skip-if-unknown-filter property, the error is downgraded to a warning.",
|
||||
"A rule condition or output refers to a field or evt.type that does not exist. This is normally an error, but if a rule has a skip-if-unknown-filter property, the error is downgraded to a warning.",
|
||||
"A macro is defined in the rules content but is not used by any other macro or rule.",
|
||||
"A list is defined in the rules content but is not used by any other list, macro, or rule.",
|
||||
"An unknown top-level object is in the rules content. It will be ignored."
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
LOAD_UNKNOWN_SOURCE = 0,
|
||||
LOAD_UNSAFE_NA_CHECK,
|
||||
LOAD_NO_EVTTYPE,
|
||||
LOAD_UNKNOWN_FIELD,
|
||||
LOAD_UNKNOWN_FILTER,
|
||||
LOAD_UNUSED_MACRO,
|
||||
LOAD_UNUSED_LIST,
|
||||
LOAD_UNKNOWN_ITEM
|
||||
|
||||
@@ -62,63 +62,51 @@ uint64_t parse_prometheus_interval(std::string interval_str)
|
||||
|
||||
if(!interval_str.empty())
|
||||
{
|
||||
/* Option 1: Passing interval directly in ms. Will be deprecated in the future. */
|
||||
if(std::all_of(interval_str.begin(), interval_str.end(), ::isdigit))
|
||||
re2::StringPiece input(interval_str);
|
||||
std::string args[14];
|
||||
re2::RE2::Arg arg0(&args[0]);
|
||||
re2::RE2::Arg arg1(&args[1]);
|
||||
re2::RE2::Arg arg2(&args[2]);
|
||||
re2::RE2::Arg arg3(&args[3]);
|
||||
re2::RE2::Arg arg4(&args[4]);
|
||||
re2::RE2::Arg arg5(&args[5]);
|
||||
re2::RE2::Arg arg6(&args[6]);
|
||||
re2::RE2::Arg arg7(&args[7]);
|
||||
re2::RE2::Arg arg8(&args[8]);
|
||||
re2::RE2::Arg arg9(&args[9]);
|
||||
re2::RE2::Arg arg10(&args[10]);
|
||||
re2::RE2::Arg arg11(&args[11]);
|
||||
re2::RE2::Arg arg12(&args[12]);
|
||||
re2::RE2::Arg arg13(&args[13]);
|
||||
const re2::RE2::Arg* const matches[14] = {&arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, &arg10, &arg11, &arg12, &arg13};
|
||||
|
||||
const std::map<std::string, int>& named_groups = s_rgx_prometheus_time_duration.NamedCapturingGroups();
|
||||
int num_groups = s_rgx_prometheus_time_duration.NumberOfCapturingGroups();
|
||||
re2::RE2::FullMatchN(input, s_rgx_prometheus_time_duration, matches, num_groups);
|
||||
|
||||
static const char* all_prometheus_units[7] = {
|
||||
PROMETHEUS_UNIT_Y, PROMETHEUS_UNIT_W, PROMETHEUS_UNIT_D, PROMETHEUS_UNIT_H,
|
||||
PROMETHEUS_UNIT_M, PROMETHEUS_UNIT_S, PROMETHEUS_UNIT_MS };
|
||||
|
||||
static const uint64_t all_prometheus_time_conversions[7] = {
|
||||
ONE_YEAR_TO_MS, ONE_WEEK_TO_MS, ONE_DAY_TO_MS, ONE_HOUR_TO_MS,
|
||||
ONE_MINUTE_TO_MS, ONE_SECOND_TO_MS, ONE_MS_TO_MS };
|
||||
|
||||
for(size_t i = 0; i < sizeof(all_prometheus_units) / sizeof(const char*); i++)
|
||||
{
|
||||
/* todo: deprecate for Falco 0.36. */
|
||||
interval = std::stoull(interval_str, nullptr, 0);
|
||||
}
|
||||
/* Option 2: Passing a Prometheus compliant time duration.
|
||||
* https://prometheus.io/docs/prometheus/latest/querying/basics/#time-durations
|
||||
*/
|
||||
else
|
||||
{
|
||||
re2::StringPiece input(interval_str);
|
||||
std::string args[14];
|
||||
re2::RE2::Arg arg0(&args[0]);
|
||||
re2::RE2::Arg arg1(&args[1]);
|
||||
re2::RE2::Arg arg2(&args[2]);
|
||||
re2::RE2::Arg arg3(&args[3]);
|
||||
re2::RE2::Arg arg4(&args[4]);
|
||||
re2::RE2::Arg arg5(&args[5]);
|
||||
re2::RE2::Arg arg6(&args[6]);
|
||||
re2::RE2::Arg arg7(&args[7]);
|
||||
re2::RE2::Arg arg8(&args[8]);
|
||||
re2::RE2::Arg arg9(&args[9]);
|
||||
re2::RE2::Arg arg10(&args[10]);
|
||||
re2::RE2::Arg arg11(&args[11]);
|
||||
re2::RE2::Arg arg12(&args[12]);
|
||||
re2::RE2::Arg arg13(&args[13]);
|
||||
const re2::RE2::Arg* const matches[14] = {&arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, &arg10, &arg11, &arg12, &arg13};
|
||||
|
||||
const std::map<std::string, int>& named_groups = s_rgx_prometheus_time_duration.NamedCapturingGroups();
|
||||
int num_groups = s_rgx_prometheus_time_duration.NumberOfCapturingGroups();
|
||||
re2::RE2::FullMatchN(input, s_rgx_prometheus_time_duration, matches, num_groups);
|
||||
|
||||
static const char* all_prometheus_units[7] = {
|
||||
PROMETHEUS_UNIT_Y, PROMETHEUS_UNIT_W, PROMETHEUS_UNIT_D, PROMETHEUS_UNIT_H,
|
||||
PROMETHEUS_UNIT_M, PROMETHEUS_UNIT_S, PROMETHEUS_UNIT_MS };
|
||||
|
||||
static const uint64_t all_prometheus_time_conversions[7] = {
|
||||
ONE_YEAR_TO_MS, ONE_WEEK_TO_MS, ONE_DAY_TO_MS, ONE_HOUR_TO_MS,
|
||||
ONE_MINUTE_TO_MS, ONE_SECOND_TO_MS, ONE_MS_TO_MS };
|
||||
|
||||
for(size_t i = 0; i < sizeof(all_prometheus_units) / sizeof(const char*); i++)
|
||||
std::string cur_interval_str;
|
||||
uint64_t cur_interval = 0;
|
||||
const auto &group_it = named_groups.find(all_prometheus_units[i]);
|
||||
if(group_it != named_groups.end())
|
||||
{
|
||||
std::string cur_interval_str;
|
||||
uint64_t cur_interval = 0;
|
||||
const auto &group_it = named_groups.find(all_prometheus_units[i]);
|
||||
if(group_it != named_groups.end())
|
||||
cur_interval_str = args[group_it->second - 1];
|
||||
if(!cur_interval_str.empty())
|
||||
{
|
||||
cur_interval_str = args[group_it->second - 1];
|
||||
if(!cur_interval_str.empty())
|
||||
{
|
||||
cur_interval = std::stoull(cur_interval_str, nullptr, 0);
|
||||
}
|
||||
if(cur_interval > 0)
|
||||
{
|
||||
interval += cur_interval * all_prometheus_time_conversions[i];
|
||||
}
|
||||
cur_interval = std::stoull(cur_interval_str, nullptr, 0);
|
||||
}
|
||||
if(cur_interval > 0)
|
||||
{
|
||||
interval += cur_interval * all_prometheus_time_conversions[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -547,8 +547,8 @@ rule_loader::rule_exception_info::rule_exception_info(context &ctx)
|
||||
|
||||
rule_loader::rule_info::rule_info(context &ctx)
|
||||
: ctx(ctx), cond_ctx(ctx), output_ctx(ctx), index(0), visibility(0),
|
||||
priority(falco_common::PRIORITY_DEBUG), enabled(true),
|
||||
warn_evttypes(true), skip_if_unknown_filter(false)
|
||||
unknown_source(false), priority(falco_common::PRIORITY_DEBUG),
|
||||
enabled(true), warn_evttypes(true), skip_if_unknown_filter(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -449,6 +449,7 @@ namespace rule_loader
|
||||
context output_ctx;
|
||||
size_t index;
|
||||
size_t visibility;
|
||||
bool unknown_source;
|
||||
std::string name;
|
||||
std::string cond;
|
||||
std::string source;
|
||||
|
||||
@@ -54,7 +54,7 @@ static inline void append_info(T* prev, T& info, uint32_t id)
|
||||
}
|
||||
|
||||
static void validate_exception_info(
|
||||
const falco_source& source,
|
||||
const falco_source* source,
|
||||
rule_loader::rule_exception_info &ex)
|
||||
{
|
||||
if (ex.fields.is_list)
|
||||
@@ -76,11 +76,14 @@ static void validate_exception_info(
|
||||
std::string("'") + v.item + "' is not a supported comparison operator",
|
||||
ex.ctx);
|
||||
}
|
||||
for (auto &v : ex.fields.items)
|
||||
if (source)
|
||||
{
|
||||
THROW(!source.is_field_defined(v.item),
|
||||
std::string("'") + v.item + "' is not a supported filter field",
|
||||
ex.ctx);
|
||||
for (auto &v : ex.fields.items)
|
||||
{
|
||||
THROW(!source->is_field_defined(v.item),
|
||||
std::string("'") + v.item + "' is not a supported filter field",
|
||||
ex.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -96,9 +99,12 @@ static void validate_exception_info(
|
||||
THROW((ex.comps.item != "in" && ex.comps.item != "pmatch" && ex.comps.item != "intersects"),
|
||||
"When fields is a single value, comps must be one of (in, pmatch, intersects)",
|
||||
ex.ctx);
|
||||
THROW(!source.is_field_defined(ex.fields.item),
|
||||
std::string("'") + ex.fields.item + "' is not a supported filter field",
|
||||
ex.ctx);
|
||||
if (source)
|
||||
{
|
||||
THROW(!source->is_field_defined(ex.fields.item),
|
||||
std::string("'") + ex.fields.item + "' is not a supported filter field",
|
||||
ex.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,26 +206,25 @@ void rule_loader::collector::append(configuration& cfg, macro_info& info)
|
||||
|
||||
void rule_loader::collector::define(configuration& cfg, rule_info& info)
|
||||
{
|
||||
auto prev = m_rule_infos.at(info.name);
|
||||
THROW(prev && prev->source != info.source,
|
||||
"Rule has been re-defined with a different source",
|
||||
info.ctx);
|
||||
|
||||
auto source = cfg.sources.at(info.source);
|
||||
if (!source)
|
||||
{
|
||||
info.unknown_source = true;
|
||||
cfg.res->add_warning(falco::load_result::LOAD_UNKNOWN_SOURCE,
|
||||
"Unknown source " + info.source + ", skipping",
|
||||
info.ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
auto prev = m_rule_infos.at(info.name);
|
||||
THROW(prev && prev->source != info.source,
|
||||
"Rule has been re-defined with a different source",
|
||||
info.ctx);
|
||||
|
||||
for (auto &ex : info.exceptions)
|
||||
{
|
||||
THROW(!ex.fields.is_valid(),
|
||||
"Rule exception item must have fields property with a list of fields",
|
||||
ex.ctx);
|
||||
validate_exception_info(*source, ex);
|
||||
"Rule exception item must have fields property with a list of fields",
|
||||
ex.ctx);
|
||||
validate_exception_info(source, ex);
|
||||
}
|
||||
|
||||
define_info(m_rule_infos, info, m_cur_index++);
|
||||
@@ -236,11 +241,17 @@ void rule_loader::collector::append(configuration& cfg, rule_info& info)
|
||||
"Appended rule must have exceptions or condition property",
|
||||
info.ctx);
|
||||
|
||||
auto source = cfg.sources.at(prev->source);
|
||||
// note: this is not supposed to happen
|
||||
THROW(!source,
|
||||
std::string("Unknown source ") + prev->source,
|
||||
info.ctx);
|
||||
// note: source can be nullptr in case we've collected a
|
||||
// rule for which the source is unknown
|
||||
falco_source* source = nullptr;
|
||||
if (!prev->unknown_source)
|
||||
{
|
||||
// note: if the source is not unknown, this should not return nullptr
|
||||
source = cfg.sources.at(prev->source);
|
||||
THROW(!source,
|
||||
std::string("Unknown source ") + prev->source,
|
||||
info.ctx);
|
||||
}
|
||||
|
||||
if (!info.cond.empty())
|
||||
{
|
||||
@@ -261,7 +272,7 @@ void rule_loader::collector::append(configuration& cfg, rule_info& info)
|
||||
THROW(ex.values.empty(),
|
||||
"Rule exception must have values property with a list of values",
|
||||
ex.ctx);
|
||||
validate_exception_info(*source, ex);
|
||||
validate_exception_info(source, ex);
|
||||
prev->exceptions.push_back(ex);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -375,6 +375,12 @@ void rule_loader::compiler::compile_macros_infos(
|
||||
}
|
||||
}
|
||||
|
||||
static bool err_is_unknown_type_or_field(const std::string& err)
|
||||
{
|
||||
return err.find("nonexistent field") != std::string::npos
|
||||
|| err.find("invalid formatting token") != std::string::npos
|
||||
|| err.find("unknown event type") != std::string::npos;
|
||||
}
|
||||
|
||||
void rule_loader::compiler::compile_rule_infos(
|
||||
configuration& cfg,
|
||||
@@ -388,17 +394,22 @@ void rule_loader::compiler::compile_rule_infos(
|
||||
filter_warning_resolver warn_resolver;
|
||||
for (auto &r : col.rules())
|
||||
{
|
||||
// skip the rule if it has an unknown source
|
||||
if (r.unknown_source)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip the rule if below the minimum priority
|
||||
if (r.priority > cfg.min_priority)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// note: this should not be nullptr if the source is not unknown
|
||||
auto source = cfg.sources.at(r.source);
|
||||
// note: this is not supposed to happen
|
||||
|
||||
THROW(!source,
|
||||
std::string("Unknown source ") + r.source,
|
||||
std::string("Unknown source at compile-time") + r.source,
|
||||
r.ctx);
|
||||
|
||||
// build filter AST by parsing the condition, building exceptions,
|
||||
@@ -433,6 +444,14 @@ void rule_loader::compiler::compile_rule_infos(
|
||||
|
||||
if(!is_format_valid(*cfg.sources.at(r.source), rule.output, err))
|
||||
{
|
||||
if (err_is_unknown_type_or_field(err) && r.skip_if_unknown_filter)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::load_result::LOAD_UNKNOWN_FILTER,
|
||||
err,
|
||||
r.output_ctx);
|
||||
continue;
|
||||
}
|
||||
throw rule_load_exception(
|
||||
falco::load_result::load_result::LOAD_ERR_COMPILE_OUTPUT,
|
||||
err,
|
||||
@@ -463,25 +482,20 @@ void rule_loader::compiler::compile_rule_infos(
|
||||
// skip_if_unknown_filter is true
|
||||
std::string err = e.what();
|
||||
|
||||
if (err.find("nonexistent field") != std::string::npos &&
|
||||
r.skip_if_unknown_filter)
|
||||
if (err_is_unknown_type_or_field(err) && r.skip_if_unknown_filter)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::load_result::LOAD_UNKNOWN_FIELD,
|
||||
e.what(),
|
||||
falco::load_result::load_result::LOAD_UNKNOWN_FILTER,
|
||||
err,
|
||||
r.cond_ctx);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
rule_loader::context ctx(compiler.get_pos(),
|
||||
condition,
|
||||
r.cond_ctx);
|
||||
|
||||
throw rule_loader::rule_load_exception(
|
||||
falco::load_result::load_result::LOAD_ERR_COMPILE_CONDITION,
|
||||
e.what(),
|
||||
ctx);
|
||||
}
|
||||
rule_loader::context ctx(compiler.get_pos(), condition, r.cond_ctx);
|
||||
throw rule_loader::rule_load_exception(
|
||||
falco::load_result::load_result::LOAD_ERR_COMPILE_CONDITION,
|
||||
err,
|
||||
ctx);
|
||||
}
|
||||
|
||||
// By default rules are enabled/disabled for the default ruleset
|
||||
|
||||
@@ -22,7 +22,7 @@ set(
|
||||
app/actions/helpers_inspector.cpp
|
||||
app/actions/configure_interesting_sets.cpp
|
||||
app/actions/create_signal_handlers.cpp
|
||||
app/actions/daemonize.cpp
|
||||
app/actions/pidfile.cpp
|
||||
app/actions/init_falco_engine.cpp
|
||||
app/actions/init_inspectors.cpp
|
||||
app/actions/init_clients.cpp
|
||||
@@ -75,7 +75,6 @@ list(APPEND FALCO_INCLUDE_DIRECTORIES "${FALCO_EXTRA_INCLUDE_DIRS}")
|
||||
|
||||
set(
|
||||
FALCO_DEPENDENCIES
|
||||
b64
|
||||
cxxopts
|
||||
)
|
||||
|
||||
@@ -90,7 +89,7 @@ if(USE_BUNDLED_DEPS)
|
||||
list(APPEND FALCO_DEPENDENCIES yamlcpp)
|
||||
endif()
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD)
|
||||
list(
|
||||
APPEND FALCO_SOURCES
|
||||
outputs_grpc.cpp
|
||||
@@ -119,7 +118,7 @@ if(NOT MINIMAL_BUILD)
|
||||
"${CARES_INCLUDE}"
|
||||
)
|
||||
|
||||
if(USE_BUNDLED_GRPC)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND USE_BUNDLED_GRPC)
|
||||
list(APPEND FALCO_DEPENDENCIES grpc)
|
||||
list(APPEND FALCO_LIBRARIES "${GRPC_LIBRARIES}")
|
||||
endif()
|
||||
@@ -143,6 +142,10 @@ add_library(
|
||||
${FALCO_SOURCES}
|
||||
)
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
target_compile_options(falco_application PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0")
|
||||
endif()
|
||||
|
||||
add_dependencies(falco_application ${FALCO_DEPENDENCIES})
|
||||
|
||||
target_link_libraries(
|
||||
@@ -161,7 +164,17 @@ add_dependencies(falco falco_application ${FALCO_DEPENDENCIES})
|
||||
target_link_libraries(falco falco_application ${FALCO_LIBRARIES})
|
||||
target_include_directories(falco PUBLIC ${FALCO_INCLUDE_DIRECTORIES})
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
if (EMSCRIPTEN)
|
||||
target_compile_options(falco PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0")
|
||||
target_link_options(falco PRIVATE "-sALLOW_MEMORY_GROWTH=1")
|
||||
target_link_options(falco PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0")
|
||||
target_link_options(falco PRIVATE "-sMODULARIZE=1")
|
||||
target_link_options(falco PRIVATE "-sEXPORT_ES6=1")
|
||||
target_link_options(falco PRIVATE "-sEXPORTED_RUNTIME_METHODS=['FS', 'callMain']")
|
||||
target_link_options(falco PRIVATE "-sEXPORTED_FUNCTIONS=['_main','_htons','_ntohs']")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD)
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version.grpc.pb.cc
|
||||
@@ -200,4 +213,12 @@ if(MUSL_OPTIMIZED_BUILD AND CMAKE_BUILD_TYPE STREQUAL "release")
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS falco RUNTIME DESTINATION ${FALCO_BIN_DIR} COMPONENT "${FALCO_COMPONENT_NAME}")
|
||||
if (NOT EMSCRIPTEN)
|
||||
install(TARGETS falco RUNTIME DESTINATION ${FALCO_BIN_DIR} COMPONENT "${FALCO_COMPONENT_NAME}")
|
||||
else()
|
||||
install(FILES
|
||||
"$<TARGET_FILE_DIR:falco>/falco.js"
|
||||
"$<TARGET_FILE_DIR:falco>/falco.wasm"
|
||||
DESTINATION ${FALCO_BIN_DIR}
|
||||
COMPONENT "${FALCO_COMPONENT_NAME}")
|
||||
endif()
|
||||
|
||||
@@ -28,7 +28,7 @@ falco::app::run_result configure_syscall_buffer_size(falco::app::state& s);
|
||||
falco::app::run_result configure_syscall_buffer_num(falco::app::state& s);
|
||||
falco::app::run_result create_requested_paths(falco::app::state& s);
|
||||
falco::app::run_result create_signal_handlers(falco::app::state& s);
|
||||
falco::app::run_result daemonize(falco::app::state& s);
|
||||
falco::app::run_result pidfile(falco::app::state& s);
|
||||
falco::app::run_result init_clients(falco::app::state& s);
|
||||
falco::app::run_result init_falco_engine(falco::app::state& s);
|
||||
falco::app::run_result init_inspectors(falco::app::state& s);
|
||||
|
||||
@@ -214,6 +214,7 @@ static void select_event_set(falco::app::state& s, const libsinsp::events::set<p
|
||||
|
||||
falco::app::run_result falco::app::actions::configure_interesting_sets(falco::app::state& s)
|
||||
{
|
||||
#ifdef __linux__
|
||||
if (s.engine == nullptr || s.config == nullptr)
|
||||
{
|
||||
return run_result::fatal("Broken 'configure_interesting_sets' preconditions: engine and config must be non-null");
|
||||
@@ -232,5 +233,7 @@ falco::app::run_result falco::app::actions::configure_interesting_sets(falco::ap
|
||||
auto rules_sc_set = s.engine->sc_codes_for_ruleset(falco_common::syscall_source);
|
||||
select_event_set(s, rules_sc_set);
|
||||
check_for_rules_unsupported_events(s, rules_sc_set);
|
||||
|
||||
#endif
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::configure_syscall_buffer_num(falco::app::state& s)
|
||||
{
|
||||
#ifdef __linux__
|
||||
if(!s.options.modern_bpf)
|
||||
{
|
||||
return run_result::ok();
|
||||
@@ -37,6 +38,6 @@ falco::app::run_result falco::app::actions::configure_syscall_buffer_num(falco::
|
||||
falco_logger::log(LOG_WARNING, "you required a buffer every '" + std::to_string(s.config->m_cpus_for_each_syscall_buffer) + "' CPUs but there are only '" + std::to_string(online_cpus) + "' online CPUs. Falco changed the config to: one buffer every '" + std::to_string(online_cpus) + "' CPUs\n");
|
||||
s.config->m_cpus_for_each_syscall_buffer = online_cpus;
|
||||
}
|
||||
|
||||
#endif
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::configure_syscall_buffer_size(falco::app::state& s)
|
||||
{
|
||||
#ifdef __linux__
|
||||
/* 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.
|
||||
*/
|
||||
@@ -71,5 +72,7 @@ falco::app::run_result falco::app::actions::configure_syscall_buffer_size(falco:
|
||||
|
||||
s.syscall_buffer_bytes_size = chosen_size;
|
||||
falco_logger::log(LOG_INFO, "The chosen syscall buffer dimension is: " + std::to_string(chosen_size) + " bytes (" + std::to_string(chosen_size / (uint64_t)(1024 * 1024)) + " MBs)\n");
|
||||
|
||||
#endif // __linux__
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ limitations under the License.
|
||||
#include "../app.h"
|
||||
#include "../signals.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <signal.h>
|
||||
#endif // __linux__
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
@@ -48,6 +50,7 @@ static void restart_signal_handler(int signal)
|
||||
bool create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
{
|
||||
ret = run_result::ok();
|
||||
#ifdef __linux__
|
||||
if(signal(sig, func) == SIG_ERR)
|
||||
{
|
||||
char errbuf[1024];
|
||||
@@ -61,12 +64,15 @@ bool create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
": " +
|
||||
errbuf);
|
||||
}
|
||||
|
||||
#endif
|
||||
return ret.success;
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::create_signal_handlers(falco::app::state& s)
|
||||
{
|
||||
auto ret = run_result::ok();
|
||||
|
||||
#ifdef __linux__
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping signal handlers creation in dry-run\n");
|
||||
@@ -84,7 +90,6 @@ falco::app::run_result falco::app::actions::create_signal_handlers(falco::app::s
|
||||
falco_logger::log(LOG_WARNING, "Bundled atomics implementation is not lock-free, signal handlers may be unstable\n");
|
||||
}
|
||||
|
||||
run_result ret;
|
||||
if(! create_handler(SIGINT, ::terminate_signal_handler, ret) ||
|
||||
! create_handler(SIGTERM, ::terminate_signal_handler, ret) ||
|
||||
! create_handler(SIGUSR1, ::reopen_outputs_signal_handler, ret) ||
|
||||
@@ -146,11 +151,14 @@ falco::app::run_result falco::app::actions::create_signal_handlers(falco::app::s
|
||||
{
|
||||
s_restarter = s.restarter;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::unregister_signal_handlers(falco::app::state& s)
|
||||
{
|
||||
#ifdef __linux__
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping unregistering signal handlers in dry-run\n");
|
||||
@@ -171,5 +179,7 @@ falco::app::run_result falco::app::actions::unregister_signal_handlers(falco::ap
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2023 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/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "actions.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
static bool s_daemonized = false;
|
||||
|
||||
falco::app::run_result falco::app::actions::daemonize(falco::app::state& s)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping daemonizing in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
// If daemonizing, do it here so any init errors will
|
||||
// be returned in the foreground process.
|
||||
if (s.options.daemon && !s_daemonized) {
|
||||
pid_t pid, sid;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
// error
|
||||
return run_result::fatal("Could not fork");
|
||||
} else if (pid > 0) {
|
||||
// parent. Write child pid to pidfile and exit
|
||||
std::ofstream pidfile;
|
||||
pidfile.open(s.options.pidfilename);
|
||||
|
||||
if (!pidfile.good())
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + s.options.pidfilename + ". Exiting.\n");
|
||||
exit(-1);
|
||||
}
|
||||
pidfile << pid;
|
||||
pidfile.close();
|
||||
exit(0);
|
||||
}
|
||||
// if here, child.
|
||||
|
||||
// Become own process group.
|
||||
sid = setsid();
|
||||
if (sid < 0) {
|
||||
return run_result::fatal("Could not set session id");
|
||||
}
|
||||
|
||||
// Set umask so no files are world anything or group writable.
|
||||
umask(027);
|
||||
|
||||
// Change working directory to '/'
|
||||
if ((chdir("/")) < 0) {
|
||||
return run_result::fatal("Could not change working directory to '/'");
|
||||
}
|
||||
|
||||
// Close stdin, stdout, stderr and reopen to /dev/null
|
||||
close(0);
|
||||
close(1);
|
||||
close(2);
|
||||
open("/dev/null", O_RDONLY);
|
||||
open("/dev/null", O_RDWR);
|
||||
open("/dev/null", O_RDWR);
|
||||
|
||||
s_daemonized = true;
|
||||
}
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -91,7 +91,7 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
//
|
||||
// Falco uses a ptrace(2) based userspace implementation.
|
||||
// Regardless of the implementation, the underlying method remains the same.
|
||||
falco_logger::log(LOG_INFO, "Opening '" + source + "' source with udig\n");
|
||||
falco_logger::log(LOG_WARNING, "The udig engine is deprecated and will be removed in Falco 0.37. Opening '" + source + "' source with udig\n");
|
||||
inspector->open_udig();
|
||||
}
|
||||
else if(s.is_gvisor_enabled()) /* gvisor engine. */
|
||||
|
||||
@@ -21,7 +21,7 @@ using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::init_clients(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
// k8s is useful only if the syscall source is enabled
|
||||
if (s.is_capture_mode() || !s.is_source_enabled(falco_common::syscall_source))
|
||||
{
|
||||
|
||||
@@ -22,27 +22,32 @@ using namespace falco::app::actions;
|
||||
|
||||
void configure_output_format(falco::app::state& s)
|
||||
{
|
||||
// See https://falco.org/docs/rules/style-guide/
|
||||
const std::string container_info = "container_id=%container.id container_image=%container.image.repository container_image_tag=%container.image.tag container_name=%container.name";
|
||||
const std::string k8s_info = "k8s_ns=%k8s.ns.name k8s_pod_name=%k8s.pod.name";
|
||||
const std::string gvisor_info = "vpid=%proc.vpid vtid=%thread.vtid";
|
||||
|
||||
std::string output_format;
|
||||
bool replace_container_info = false;
|
||||
|
||||
if(s.options.print_additional == "c" || s.options.print_additional == "container")
|
||||
{
|
||||
output_format = "container=%container.name (id=%container.id)";
|
||||
output_format = container_info;
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(s.options.print_additional == "cg" || s.options.print_additional == "container-gvisor")
|
||||
{
|
||||
output_format = "container=%container.name (id=%container.id) vpid=%proc.vpid vtid=%thread.vtid";
|
||||
output_format = gvisor_info + " " + container_info;
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(s.options.print_additional == "k" || s.options.print_additional == "kubernetes")
|
||||
{
|
||||
output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id";
|
||||
output_format = container_info + " " + k8s_info;
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(s.options.print_additional == "kg" || s.options.print_additional == "kubernetes-gvisor")
|
||||
{
|
||||
output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id vpid=%proc.vpid vtid=%thread.vtid";
|
||||
output_format = gvisor_info + " " + container_info + " " + k8s_info;
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(!s.options.print_additional.empty())
|
||||
|
||||
@@ -51,7 +51,7 @@ falco::app::run_result falco::app::actions::init_outputs(falco::app::state& s)
|
||||
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping daemonizing in dry-run\n");
|
||||
falco_logger::log(LOG_DEBUG, "Skipping outputs initialization in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,20 +25,7 @@ static void apply_deprecated_options(
|
||||
const falco::app::options& opts,
|
||||
const std::shared_ptr<falco_configuration>& cfg)
|
||||
{
|
||||
if (!opts.stats_output_file.empty() || !opts.stats_interval.empty())
|
||||
{
|
||||
falco_logger::log(LOG_WARNING, "Options '-s' and '--stats-interval' will be deprecated in the future, metrics must be configured through config file");
|
||||
if (!opts.stats_output_file.empty())
|
||||
{
|
||||
cfg->m_metrics_enabled = true;
|
||||
cfg->m_metrics_output_file = opts.stats_output_file;
|
||||
if (!opts.stats_interval.empty())
|
||||
{
|
||||
cfg->m_metrics_interval_str = opts.stats_interval;
|
||||
cfg->m_metrics_interval = falco::utils::parse_prometheus_interval(cfg->m_metrics_interval_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Keep for future use cases.
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::load_config(falco::app::state& s)
|
||||
@@ -80,13 +67,15 @@ falco::app::run_result falco::app::actions::load_config(falco::app::state& s)
|
||||
|
||||
falco::app::run_result falco::app::actions::require_config_file(falco::app::state& s)
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (s.options.conf_filename.empty())
|
||||
{
|
||||
#ifndef BUILD_TYPE_RELEASE
|
||||
return run_result::fatal(std::string("You must create a config file at ") + FALCO_SOURCE_CONF_FILE + ", " + FALCO_INSTALL_CONF_FILE + " or by passing -c");
|
||||
#else
|
||||
#else // BUILD_TYPE_RELEASE
|
||||
return run_result::fatal(std::string("You must create a config file at ") + FALCO_INSTALL_CONF_FILE + " or by passing -c");
|
||||
#endif
|
||||
#endif // BUILD_TYPE_RELEASE
|
||||
}
|
||||
#endif // __EMSCRIPTEN__
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -22,10 +22,10 @@ using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::load_plugins(falco::app::state& s)
|
||||
{
|
||||
#ifdef MUSL_OPTIMIZED
|
||||
#if defined(MUSL_OPTIMIZED) or defined(__EMSCRIPTEN__)
|
||||
if (!s.config->m_plugins.empty())
|
||||
{
|
||||
return run_result::fatal("Can not load/use plugins with musl optimized build");
|
||||
return run_result::fatal("Loading plugins dynamic libraries is not supported by this Falco build");
|
||||
}
|
||||
#endif
|
||||
// Initialize the set of loaded event sources.
|
||||
|
||||
@@ -50,11 +50,25 @@ falco::app::run_result falco::app::actions::load_rules_files(falco::app::state&
|
||||
}
|
||||
|
||||
std::vector<std::string> rules_contents;
|
||||
std::vector<std::string> rules_filenames;
|
||||
falco::load_result::rules_contents_t rc;
|
||||
std::string filenames;
|
||||
|
||||
for(auto &filename : s.config->m_loaded_rules_filenames)
|
||||
{
|
||||
if(!filenames.empty())
|
||||
{
|
||||
filenames += ", ";
|
||||
}
|
||||
|
||||
filenames += filename;
|
||||
|
||||
rules_filenames.push_back(filename);
|
||||
}
|
||||
|
||||
try {
|
||||
read_files(s.config->m_loaded_rules_filenames.begin(),
|
||||
s.config->m_loaded_rules_filenames.end(),
|
||||
read_files(rules_filenames.begin(),
|
||||
rules_filenames.end(),
|
||||
rules_contents,
|
||||
rc);
|
||||
}
|
||||
@@ -64,25 +78,22 @@ falco::app::run_result falco::app::actions::load_rules_files(falco::app::state&
|
||||
}
|
||||
|
||||
std::string err = "";
|
||||
for(auto &filename : s.config->m_loaded_rules_filenames)
|
||||
|
||||
falco_logger::log(LOG_INFO, "Loading rules from file(s): " + filenames);
|
||||
std::unique_ptr<falco::load_result> res;
|
||||
|
||||
res = s.engine->load_rules(rules_contents, rules_filenames);
|
||||
|
||||
if(!res->successful())
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + "\n");
|
||||
std::unique_ptr<falco::load_result> res;
|
||||
// Return the summary version as the error
|
||||
err = res->as_string(true, rc);
|
||||
}
|
||||
|
||||
res = s.engine->load_rules(rc.at(filename), filename);
|
||||
|
||||
if(!res->successful())
|
||||
{
|
||||
// Return the summary version as the error
|
||||
err = res->as_string(true, rc);
|
||||
break;
|
||||
}
|
||||
|
||||
// If verbose is true, also print any warnings
|
||||
if(s.options.verbose && res->has_warnings())
|
||||
{
|
||||
fprintf(stderr, "%s\n", res->as_string(true, rc).c_str());
|
||||
}
|
||||
// If verbose is true, also print any warnings
|
||||
if(s.options.verbose && res->has_warnings())
|
||||
{
|
||||
fprintf(stderr, "%s\n", res->as_string(true, rc).c_str());
|
||||
}
|
||||
|
||||
// note: we have an egg-and-chicken problem here. We would like to check
|
||||
|
||||
52
userspace/falco/app/actions/pidfile.cpp
Normal file
52
userspace/falco/app/actions/pidfile.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright (C) 2023 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/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "actions.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::pidfile(falco::app::state& s)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping pidfile creation in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
if (!s.options.pidfilename.empty())
|
||||
{
|
||||
int64_t self_pid = getpid();
|
||||
|
||||
std::ofstream pidfile;
|
||||
pidfile.open(s.options.pidfilename);
|
||||
|
||||
if (!pidfile.good())
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Could not write pid to pidfile " + s.options.pidfilename + ". Exiting.\n");
|
||||
exit(-1);
|
||||
}
|
||||
pidfile << self_pid;
|
||||
pidfile.close();
|
||||
|
||||
}
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -24,12 +24,6 @@ using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::print_plugin_info(falco::app::state& s)
|
||||
{
|
||||
#ifdef MUSL_OPTIMIZED
|
||||
if(!s.options.print_plugin_info.empty())
|
||||
{
|
||||
return run_result::fatal("Can not load or use plugins with musl optimized build");
|
||||
}
|
||||
#else // MUSL_OPTIMIZED
|
||||
if(!s.options.print_plugin_info.empty())
|
||||
{
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
@@ -110,7 +104,6 @@ falco::app::run_result falco::app::actions::print_plugin_info(falco::app::state&
|
||||
}
|
||||
return run_result::fatal("can't find plugin and print its info: " + s.options.print_plugin_info);
|
||||
}
|
||||
#endif // MUSL_OPTIMIZED
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -30,13 +30,10 @@ limitations under the License.
|
||||
#include "helpers.h"
|
||||
#include "../options.h"
|
||||
#include "../signals.h"
|
||||
#include "../../semaphore.h"
|
||||
#include "../../falco_semaphore.h"
|
||||
#include "../../stats_writer.h"
|
||||
#include "../../falco_outputs.h"
|
||||
#include "../../event_drops.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
#include "../../webserver.h"
|
||||
#endif
|
||||
|
||||
#include <plugin_manager.h>
|
||||
|
||||
@@ -430,15 +427,14 @@ static falco::app::run_result init_stats_writer(
|
||||
|
||||
if(std::all_of(config->m_metrics_interval_str.begin(), config->m_metrics_interval_str.end(), ::isdigit))
|
||||
{
|
||||
falco_logger::log(LOG_WARNING, "Metrics interval was passed as numeric value without Prometheus time unit, this option will be deprecated in the future");
|
||||
return falco::app::run_result::fatal("Metrics interval was passed as numeric value without Prometheus time unit. Please specify a time unit");
|
||||
}
|
||||
|
||||
if (config->m_metrics_enabled && !sw->has_output())
|
||||
{
|
||||
falco_logger::log(LOG_WARNING, "Metrics are enabled with no output configured, no snapshot will be collected");
|
||||
return falco::app::run_result::fatal("Metrics are enabled with no output configured. Please enable at least one output channel");
|
||||
}
|
||||
|
||||
|
||||
falco_logger::log(LOG_INFO, "Setting metrics interval to " + config->m_metrics_interval_str + ", equivalent to " + std::to_string(config->m_metrics_interval) + " (ms)\n");
|
||||
|
||||
auto res = falco::app::run_result::ok();
|
||||
@@ -496,6 +492,13 @@ falco::app::run_result falco::app::actions::process_events(falco::app::state& s)
|
||||
{
|
||||
print_enabled_event_sources(s);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if(s.enabled_sources.size() > 1)
|
||||
{
|
||||
return run_result::fatal("enabling multiple event sources is not supported by this Falco build");
|
||||
}
|
||||
#endif
|
||||
|
||||
// start event processing for all enabled sources
|
||||
falco::semaphore termination_sem(s.enabled_sources.size());
|
||||
std::vector<live_context> ctxs;
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
#include "actions.h"
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
#include "grpc_server.h"
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,7 @@ using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::start_grpc_server(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
// gRPC server
|
||||
if(s.config->m_grpc_enabled)
|
||||
{
|
||||
@@ -56,7 +56,7 @@ falco::app::run_result falco::app::actions::start_grpc_server(falco::app::state&
|
||||
|
||||
falco::app::run_result falco::app::actions::stop_grpc_server(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
if(s.config->m_grpc_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
#include "actions.h"
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
#include "webserver.h"
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,7 @@ using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::start_webserver(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
if(!s.is_capture_mode() && s.config->m_webserver_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
@@ -55,7 +55,7 @@ falco::app::run_result falco::app::actions::start_webserver(falco::app::state& s
|
||||
|
||||
falco::app::run_result falco::app::actions::stop_webserver(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
if(!s.is_capture_mode() && s.config->m_webserver_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
|
||||
@@ -80,7 +80,7 @@ bool falco::app::run(falco::app::state& s, bool& restart, std::string& errstr)
|
||||
falco::app::actions::init_outputs,
|
||||
falco::app::actions::create_signal_handlers,
|
||||
falco::app::actions::create_requested_paths,
|
||||
falco::app::actions::daemonize,
|
||||
falco::app::actions::pidfile,
|
||||
falco::app::actions::init_clients,
|
||||
falco::app::actions::configure_interesting_sets,
|
||||
falco::app::actions::configure_syscall_buffer_size,
|
||||
|
||||
@@ -36,6 +36,7 @@ options::options()
|
||||
list_plugins(false),
|
||||
list_syscall_events(false),
|
||||
markdown(false),
|
||||
userspace(false),
|
||||
modern_bpf(false),
|
||||
dry_run(false),
|
||||
nodriver(false)
|
||||
@@ -142,11 +143,6 @@ bool options::parse(int argc, char **argv, std::string &errstr)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (daemon && pidfilename == "") {
|
||||
errstr = std::string("If -d is provided, a pid file must also be provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
list_fields = m_cmdline_parsed.count("list") > 0 ? true : false;
|
||||
|
||||
int open_modes = 0;
|
||||
@@ -173,64 +169,63 @@ const std::string& options::usage()
|
||||
void options::define(cxxopts::Options& opts)
|
||||
{
|
||||
opts.add_options()
|
||||
("h,help", "Print this page", cxxopts::value(help)->default_value("false"))
|
||||
("h,help", "Print this help list and exit.", cxxopts::value(help)->default_value("false"))
|
||||
#ifdef BUILD_TYPE_RELEASE
|
||||
("c", "Configuration file. If not specified uses " FALCO_INSTALL_CONF_FILE ".", cxxopts::value(conf_filename), "<path>")
|
||||
#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 supported by Falco defined in rules and configs. Please use the -i option to list the events ignored by default without -A. This option affects live captures only. Setting -A can impact performance.", cxxopts::value(all_events)->default_value("false"))
|
||||
("A", "Monitor all events supported by Falco and defined in rules and configs. Some events are ignored by default when -A is not specified (the -i option lists these events ignored). Using -A can impact performance. This option has no effect when reproducing events from a capture file.", 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 the libs default. This option 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. By default, all loaded sources get enabled. Available sources are 'syscall' and all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. This 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>")
|
||||
("dry-run", "Run Falco without proceesing events. Can be useful for checking that the configuration and rules do not have any errors.", cxxopts::value(dry_run)->default_value("false"))
|
||||
("D", "Disable any rules with names having the substring <substring>. This option can be passed multiple times. Can not be mixed 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, all loaded sources get disabled by default and only the ones passed with this option get enabled. Available sources are 'syscall' and all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. This has no offect when reading events from a trace file. Can not be mixed with --disable-source.", cxxopts::value(enable_sources), "<event_source>")
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
("cri", "Path to CRI socket for container metadata. Use the specified <path> to fetch data from a CRI-compatible runtime. If not specified, built-in defaults for commonly known paths are used. This option can be passed multiple times to specify a list of sockets to be tried until a successful one is found.", cxxopts::value(cri_socket_paths), "<path>")
|
||||
("disable-cri-async", "Turn off 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"))
|
||||
#endif
|
||||
("disable-source", "Turn off a specific <event_source>. By default, all loaded sources get enabled. Available sources are 'syscall' plus all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times, but turning off all event sources simultaneously is not permitted. This option can not be mixed with --enable-source. This option has no effect when reproducing events from a capture file.", cxxopts::value(disable_sources), "<event_source>")
|
||||
("dry-run", "Run Falco without processing events. It can help check that the configuration and rules do not have any errors.", cxxopts::value(dry_run)->default_value("false"))
|
||||
("D", "Turn off any rules with names having the substring <substring>. This option can be passed multiple times. It cannot be mixed with -t.", cxxopts::value(disabled_rule_substrings), "<substring>")
|
||||
("e", "Reproduce the events by reading from the given <capture_file> instead of opening a live session. Only capture files in .scap format are supported.", cxxopts::value(trace_filename), "<events_file>")
|
||||
("enable-source", "Enable a specific <event_source>. By default, all loaded sources get enabled. Available sources are 'syscall' plus all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. When using this option, only the event sources specified by it will be enabled. This option can not be mixed with --disable-source. This option has no effect when reproducing events from a capture file.", cxxopts::value(enable_sources), "<event_source>")
|
||||
#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-root", "gVisor root directory for storage of container state. Equivalent to runsc --root flag.", cxxopts::value(gvisor_root), "<gvisor_root>")
|
||||
("g,gvisor-config", "Collect 'syscall' events from gVisor using the specified <gvisor_config> file. A Falco-compatible configuration file can be generated with --gvisor-generate-config and utilized for both runsc and Falco.", cxxopts::value(gvisor_config), "<gvisor_config>")
|
||||
("gvisor-generate-config", "Generate a configuration file that can be used for gVisor and exit. See --gvisor-config for more details.", cxxopts::value<std::string>(gvisor_generate_config_with_socket)->implicit_value("/run/falco/gvisor.sock"), "<socket_path>")
|
||||
("gvisor-root", "Set gVisor root directory for storage of container state when used in conjunction with --gvisor-config. The <gvisor_root> to be passed is the one usually passed to runsc --root flag.", cxxopts::value(gvisor_root), "<gvisor_root>")
|
||||
#endif
|
||||
#ifdef HAS_MODERN_BPF
|
||||
("modern-bpf", "Use BPF modern probe driver to instrument the kernel.", cxxopts::value(modern_bpf)->default_value("false"))
|
||||
("modern-bpf", "Use the BPF modern probe driver to instrument the kernel and observe 'syscall' events.", cxxopts::value(modern_bpf)->default_value("false"))
|
||||
#endif
|
||||
("i", "Print all high volume syscalls that are ignored by default for performance reasons (i.e. without the -A flag) and exit.", cxxopts::value(print_ignored_events)->default_value("false"))
|
||||
#ifndef MINIMAL_BUILD
|
||||
("k,k8s-api", "Enable Kubernetes support by connecting to the API server specified as argument. E.g. \"http://admin:password@127.0.0.1:8080\". The API server can also be specified via the environment variable FALCO_K8S_API.", cxxopts::value(k8s_api), "<url>")
|
||||
("K,k8s-api-cert", "Use the provided files names to authenticate user and (optionally) verify the K8S API server identity. Each entry must specify full (absolute, or relative to the current directory) path to the respective file. Private key password is optional (needed only if key is password protected). CA certificate is optional. For all files, only PEM file format is supported. Specifying CA certificate only is obsoleted - when single entry is provided for this option, it will be interpreted as the name of a file containing bearer token. Note that the format of this command-line option prohibits use of files whose names contain ':' or '#' characters in the file name.", cxxopts::value(k8s_api_cert), "(<bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>])")
|
||||
("k8s-node", "The node name will be used as a filter when requesting metadata of pods to the API server. Usually, this should be set to the current node on which Falco is running. If empty, no filter is set, which may have a performance penalty on large clusters.", cxxopts::value(k8s_node_name), "<node_name>")
|
||||
("i", "Print those events that are ignored by default for performance reasons and exit. See -A for more details.", cxxopts::value(print_ignored_events)->default_value("false"))
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
("k,k8s-api", "Enable Kubernetes metadata support by connecting to the given API server <URL>\n(e.g. \"http://admin:password@127.0.0.1:8080\". The API server can also be specified via the environment variable FALCO_K8S_API.", cxxopts::value(k8s_api), "<URL>")
|
||||
("K,k8s-api-cert", "Use the provided file names to authenticate the user and (optionally) verify the K8S API server identity. Each entry must specify the full (absolute or relative to the current directory) path to the respective file. Passing a private key password is optional (unless the key is password-protected). CA certificate is optional. For all files, only the PEM file format is supported. Specifying the CA certificate only is obsoleted - when a single entry is provided for this option, it will be interpreted as the name of a file containing the bearer token. Note that the format of this command-line option prohibits the use of files whose names contain ':' or '#' characters in the file name. This option has effect only when used in conjunction with -k.", cxxopts::value(k8s_api_cert), "(<bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>])")
|
||||
("k8s-node", "Filter Kubernetes metadata for a specified <node_name>. The node name will be used as a filter when requesting metadata of pods to the API server. Usually, this should be set to the current node on which Falco is running. No filter is set if empty, which may have a performance penalty on large clusters. This option has effect only when used in conjunction with -k.", cxxopts::value(k8s_node_name), "<node_name>")
|
||||
#endif
|
||||
("L", "Show the name and description of all rules and exit. If json_output is set to true, it prints details about all rules, macros and lists in JSON format", cxxopts::value(describe_all_rules)->default_value("false"))
|
||||
("l", "Show the name and description of the rule with name <rule> and exit. If json_output is set to true, it prints details about the rule in JSON format", cxxopts::value(describe_rule), "<rule>")
|
||||
("list", "List all defined fields. If <source> is provided, only list those fields for the source <source>. Current values for <source> are \"syscall\" or any source from a configured plugin with event sourcing capability.", cxxopts::value(list_source_fields)->implicit_value(""), "<source>")
|
||||
("list-syscall-events", "List all defined system call events.", cxxopts::value<bool>(list_syscall_events))
|
||||
#ifndef MUSL_OPTIMIZED
|
||||
("L", "Show the name and description of all rules and exit. If json_output is set to true, it prints details about all rules, macros, and lists in JSON format.", cxxopts::value(describe_all_rules)->default_value("false"))
|
||||
("l", "Show the name and description of the rule specified <rule> and exit. If json_output is set to true, it prints details about the rule in JSON format.", cxxopts::value(describe_rule), "<rule>")
|
||||
("list", "List all defined fields and exit. If <source> is provided, only list those fields for the source <source>. Current values for <source> are \"syscall\" or any source from a configured plugin with event sourcing capability.", cxxopts::value(list_source_fields)->implicit_value(""), "<source>")
|
||||
("list-syscall-events", "List all defined 'syscall' events and exit.", cxxopts::value<bool>(list_syscall_events))
|
||||
("list-plugins", "Print info on all loaded plugins and exit.", cxxopts::value(list_plugins)->default_value("false"))
|
||||
("M", "Stop Falco execution after <num_seconds> are passed.", cxxopts::value(duration_to_tot)->default_value("0"), "<num_seconds>")
|
||||
("markdown", "Print output in Markdown format when used in conjunction with --list or --list-syscall-events options. It has no effect when used with other options.", cxxopts::value<bool>(markdown))
|
||||
("N", "Only print field names when used in conjunction with the --list option. It has no effect when used with other options.", cxxopts::value(names_only)->default_value("false"))
|
||||
("nodriver", "Do not use a driver to instrument the kernel. If a loaded plugin has event-sourcing capability and can produce system events, it will be used for event collection. Otherwise, no event will be collected.", cxxopts::value(nodriver)->default_value("false"))
|
||||
("o,option", "Set the value of option <opt> to <val>. Overrides values in the configuration file. <opt> can be identified using its location in the configuration file using dot notation. Elements of list entries can be accessed via square brackets [].\n E.g. base.id = val\n base.subvalue.subvalue2 = val\n base.list[1]=val", cxxopts::value(cmdline_config_options), "<opt>=<val>")
|
||||
("plugin-info", "Print info for the plugin specified by <plugin_name> and exit.\nThis includes all descriptive information like name and author, along with the\nschema format for the init configuration and a list of suggested open parameters.\n<plugin_name> can be the plugin's name or its configured 'library_path'.", cxxopts::value(print_plugin_info), "<plugin_name>")
|
||||
("p,print", "Print (or replace) additional information in the rule's output.\nUse -pc or -pcontainer to append container details.\nUse -pk or -pkubernetes to add both container and Kubernetes details.\nIf using gVisor, choose -pcg or -pkg variants (or -pcontainer-gvisor and -pkubernetes-gvisor, respectively).\nIf a rule's output contains %container.info, it will be replaced with the corresponding details. Otherwise, these details will be directly appended to the rule's output.\nAlternatively, use -p <output_format> for a custom format. In this case, the given <output_format> will be appended to the rule's output without any replacement.", cxxopts::value(print_additional), "<output_format>")
|
||||
("P,pidfile", "Write PID to specified <pid_file> path. By default, no PID file is created.", cxxopts::value(pidfilename)->default_value(""), "<pid_file>")
|
||||
("r", "Rules file or directory to be loaded. This option can be passed multiple times. Falco defaults to the values in the configuration file when this option is not specified.", cxxopts::value<std::vector<std::string>>(), "<rules_file>")
|
||||
("S,snaplen", "Collect only the first <len> bytes of each I/O buffer for 'syscall' events. By default, the first 80 bytes are collected by the driver and sent to the user space for processing. Use this option with caution since it can have a strong performance impact.", cxxopts::value(snaplen)->default_value("0"), "<len>")
|
||||
("support", "Print support information, including version, rules files used, loaded configuration, etc., and exit. The output is in JSON format.", cxxopts::value(print_support)->default_value("false"))
|
||||
("T", "Turn off any rules with a tag=<tag>. This option can be passed multiple times. This option can not be mixed with -t.", cxxopts::value<std::vector<std::string>>(), "<tag>")
|
||||
("t", "Only enable those rules with a tag=<tag>. This option can be passed multiple times. This option can not be mixed with -T/-D.", cxxopts::value<std::vector<std::string>>(), "<tag>")
|
||||
("U,unbuffered", "Turn off output buffering for configured outputs. This causes every single line emitted by Falco to be flushed, which generates higher CPU usage but is useful when piping those outputs into another process or a script.", cxxopts::value(unbuffered_outputs)->default_value("false"))
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
("u,userspace", "[DEPRECATED: this option will be removed in Falco 0.37] Use a userspace driver to collect 'syscall' events. To be used in conjunction with the ptrace(2) based driver (pdig).", cxxopts::value(userspace)->default_value("false"))
|
||||
#endif
|
||||
("M", "Stop collecting after <num_seconds> reached.", cxxopts::value(duration_to_tot)->default_value("0"), "<num_seconds>")
|
||||
("markdown", "When used with --list/--list-syscall-events, print the content in Markdown format", cxxopts::value<bool>(markdown))
|
||||
("N", "When used with --list, only print field names.", cxxopts::value(names_only)->default_value("false"))
|
||||
("nodriver", "Do not use a driver to instrument the kernel. If a loaded plugin has event sourcing capability and can produce system events, it will be used to for event collection.", cxxopts::value(nodriver)->default_value("false"))
|
||||
("o,option", "Set the value of option <opt> to <val>. Overrides values in configuration file. <opt> can be identified using its location in configuration file using dot notation. Elements which are entries of lists can be accessed via square brackets [].\n E.g. base.id = val\n base.subvalue.subvalue2 = val\n base.list[1]=val", cxxopts::value(cmdline_config_options), "<opt>=<val>")
|
||||
("plugin-info", "Print info for a single plugin and exit.\nThis includes all descriptivo info like name and author, along with the\nschema format for the init configuration and a list of suggested open parameters.\n<plugin_name> can be the name of the plugin or its configured library_path.", cxxopts::value(print_plugin_info), "<plugin_name>")
|
||||
("p,print", "Add additional information to each falco notification's output.\nWith -pc or -pcontainer will use a container-friendly format.\nWith -pk or -pkubernetes will use a kubernetes-friendly format.\nAdditionally, specifying -pc/-pk will change the interpretation of %container.info in rule output fields.", cxxopts::value(print_additional), "<output_format>")
|
||||
("P,pidfile", "When run as a daemon, write pid to specified file", cxxopts::value(pidfilename)->default_value("/var/run/falco.pid"), "<pid_file>")
|
||||
("r", "Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml). This option can be passed multiple times to read from multiple files/directories.", cxxopts::value<std::vector<std::string>>(), "<rules_file>")
|
||||
("s", "If specified, append statistics related to Falco's reading/processing of events to this file (only useful in live mode).", cxxopts::value(stats_output_file), "<stats_file>")
|
||||
("stats-interval", "When using -s <stats_file>, write statistics every <msec> ms. This uses signals, and has a minimum threshold of 100 ms. Defaults to 5000 (5 seconds).", cxxopts::value(stats_interval), "<msec>")
|
||||
("S,snaplen", "Capture the first <len> bytes of each I/O buffer. By default, the first 80 bytes are captured. Use this option with caution, it can have a strong performance impact.", cxxopts::value(snaplen)->default_value("0"), "<len>")
|
||||
("support", "Print support information including version, rules files used, etc. and exit.", cxxopts::value(print_support)->default_value("false"))
|
||||
("T", "Disable any rules with a tag=<tag>. This option can be passed multiple times. Can not be mized with -t", cxxopts::value<std::vector<std::string>>(), "<tag>")
|
||||
("t", "Only run those rules with a tag=<tag>. This option can be passed multiple times. Can not be mixed with -T/-D.", cxxopts::value<std::vector<std::string>>(), "<tag>")
|
||||
("U,unbuffered", "Turn off output buffering to configured outputs. This causes every single line emitted by falco to be flushed which generates higher CPU usage but is useful when piping those outputs into another process or into a script.", cxxopts::value(unbuffered_outputs)->default_value("false"))
|
||||
("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. This option can be passed 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 ring-buffer size).", cxxopts::value(print_page_size)->default_value("false"));
|
||||
("V,validate", "Read the contents of the specified <rules_file> file(s), validate the loaded rules, and exit. This option can be passed multiple times to validate multiple files.", cxxopts::value(validate_rules_filenames), "<rules_file>")
|
||||
("v", "Enable verbose output.", cxxopts::value(verbose)->default_value("false"))
|
||||
("version", "Print version information and exit.", cxxopts::value(print_version_info)->default_value("false"))
|
||||
("page-size", "Print the system page size and exit. This utility may help choose the right syscall ring buffer size.", cxxopts::value(print_page_size)->default_value("false"));
|
||||
|
||||
|
||||
opts.set_width(140);
|
||||
|
||||
@@ -42,7 +42,6 @@ public:
|
||||
bool all_events;
|
||||
sinsp_evt::param_fmt event_buffer_format;
|
||||
std::vector<std::string> cri_socket_paths;
|
||||
bool daemon;
|
||||
bool disable_cri_async;
|
||||
std::vector<std::string> disable_sources;
|
||||
std::vector<std::string> disabled_rule_substrings;
|
||||
@@ -70,8 +69,6 @@ public:
|
||||
std::string pidfilename;
|
||||
// Rules list as passed by the user, via cmdline option '-r'
|
||||
std::list<std::string> rules_filenames;
|
||||
std::string stats_output_file;
|
||||
std::string stats_interval;
|
||||
uint64_t snaplen;
|
||||
bool print_support;
|
||||
std::set<std::string> disabled_rule_tags;
|
||||
|
||||
@@ -31,8 +31,9 @@ limitations under the License.
|
||||
|
||||
falco::app::restart_handler::~restart_handler()
|
||||
{
|
||||
close(m_inotify_fd);
|
||||
stop();
|
||||
close(m_inotify_fd);
|
||||
m_inotify_fd = -1;
|
||||
}
|
||||
|
||||
void falco::app::restart_handler::trigger()
|
||||
@@ -42,6 +43,7 @@ void falco::app::restart_handler::trigger()
|
||||
|
||||
bool falco::app::restart_handler::start(std::string& err)
|
||||
{
|
||||
#ifdef __linux__
|
||||
m_inotify_fd = inotify_init();
|
||||
if (m_inotify_fd < 0)
|
||||
{
|
||||
@@ -73,16 +75,19 @@ bool falco::app::restart_handler::start(std::string& err)
|
||||
|
||||
// launch the watcher thread
|
||||
m_watcher = std::thread(&falco::app::restart_handler::watcher_loop, this);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void falco::app::restart_handler::stop()
|
||||
{
|
||||
#ifdef __linux__
|
||||
m_stop.store(true, std::memory_order_release);
|
||||
if (m_watcher.joinable())
|
||||
{
|
||||
m_watcher.join();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void falco::app::restart_handler::watcher_loop() noexcept
|
||||
|
||||
@@ -22,7 +22,7 @@ limitations under the License.
|
||||
#include "restart_handler.h"
|
||||
#include "../configuration.h"
|
||||
#include "../stats_writer.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
#include "../grpc_server.h"
|
||||
#include "../webserver.h"
|
||||
#endif
|
||||
@@ -137,7 +137,7 @@ struct state
|
||||
// Helper responsible for watching of handling hot application restarts
|
||||
std::shared_ptr<restart_handler> restarter;
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
falco::grpc::server grpc_server;
|
||||
std::thread grpc_server_thread;
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ falco_configuration::falco_configuration():
|
||||
m_json_include_tags_property(true),
|
||||
m_notifications_rate(0),
|
||||
m_notifications_max_burst(1000),
|
||||
m_watch_config_files(true),
|
||||
m_rule_matching(falco_common::rule_matching::FIRST),
|
||||
m_watch_config_files(true),
|
||||
m_buffered_outputs(false),
|
||||
m_time_format_iso_8601(false),
|
||||
m_output_timeout(2000),
|
||||
@@ -194,6 +194,10 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
|
||||
bool insecure;
|
||||
insecure = config.get_scalar<bool>("http_output.insecure", false);
|
||||
http_output.options["insecure"] = insecure? std::string("true") : std::string("false");
|
||||
|
||||
bool echo;
|
||||
echo = config.get_scalar<bool>("http_output.echo", false);
|
||||
http_output.options["echo"] = echo? std::string("true") : std::string("false");
|
||||
|
||||
std::string ca_cert;
|
||||
ca_cert = config.get_scalar<std::string>("http_output.ca_cert", "");
|
||||
@@ -207,6 +211,18 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
|
||||
ca_path = config.get_scalar<std::string>("http_output.ca_path", "/etc/ssl/certs");
|
||||
http_output.options["ca_path"] = ca_path;
|
||||
|
||||
bool mtls;
|
||||
mtls = config.get_scalar<bool>("http_output.mtls", false);
|
||||
http_output.options["mtls"] = mtls? std::string("true") : std::string("false");
|
||||
|
||||
std::string client_cert;
|
||||
client_cert = config.get_scalar<std::string>("http_output.client_cert", "/etc/ssl/certs/client.crt");
|
||||
http_output.options["client_cert"] = client_cert;
|
||||
|
||||
std::string client_key;
|
||||
client_key = config.get_scalar<std::string>("http_output.client_key", "/etc/ssl/certs/client.key");
|
||||
http_output.options["client_key"] = client_key;
|
||||
|
||||
m_outputs.push_back(http_output);
|
||||
}
|
||||
|
||||
@@ -240,9 +256,16 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
|
||||
config.get_scalar<std::string>("libs_logger.severity", "debug"),
|
||||
"[libs]: ");
|
||||
|
||||
falco_logger::log_stderr = config.get_scalar<bool>("log_stderr", false);
|
||||
falco_logger::log_syslog = config.get_scalar<bool>("log_syslog", true);
|
||||
|
||||
m_output_timeout = config.get_scalar<uint32_t>("output_timeout", 2000);
|
||||
|
||||
m_notifications_rate = config.get_scalar<uint32_t>("outputs.rate", 0);
|
||||
if(m_notifications_rate != 0)
|
||||
{
|
||||
falco_logger::log(LOG_WARNING, "'output.rate' config is deprecated and it will be removed in Falco 0.37\n");
|
||||
}
|
||||
m_notifications_max_burst = config.get_scalar<uint32_t>("outputs.max_burst", 1000);
|
||||
|
||||
std::string rule_matching = config.get_scalar<std::string>("rule_matching", "first");
|
||||
@@ -260,9 +283,6 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
|
||||
m_buffered_outputs = config.get_scalar<bool>("buffered_outputs", false);
|
||||
m_time_format_iso_8601 = config.get_scalar<bool>("time_format_iso_8601", false);
|
||||
|
||||
falco_logger::log_stderr = config.get_scalar<bool>("log_stderr", false);
|
||||
falco_logger::log_syslog = config.get_scalar<bool>("log_syslog", true);
|
||||
|
||||
m_webserver_enabled = config.get_scalar<bool>("webserver.enabled", false);
|
||||
m_webserver_threadiness = config.get_scalar<uint32_t>("webserver.threadiness", 0);
|
||||
m_webserver_listen_port = config.get_scalar<uint32_t>("webserver.listen_port", 8765);
|
||||
|
||||
@@ -86,6 +86,8 @@ bool syscall_evt_drop_mgr::process_event(std::shared_ptr<sinsp> inspector, sinsp
|
||||
delta.n_drops_buffer_dir_file_exit = stats.n_drops_buffer_dir_file_exit - m_last_stats.n_drops_buffer_dir_file_exit;
|
||||
delta.n_drops_buffer_other_interest_enter = stats.n_drops_buffer_other_interest_enter - m_last_stats.n_drops_buffer_other_interest_enter;
|
||||
delta.n_drops_buffer_other_interest_exit = stats.n_drops_buffer_other_interest_exit - m_last_stats.n_drops_buffer_other_interest_exit;
|
||||
delta.n_drops_buffer_close_exit = stats.n_drops_buffer_close_exit - m_last_stats.n_drops_buffer_close_exit;
|
||||
delta.n_drops_buffer_proc_exit = stats.n_drops_buffer_proc_exit - m_last_stats.n_drops_buffer_proc_exit;
|
||||
delta.n_drops_scratch_map = stats.n_drops_scratch_map - m_last_stats.n_drops_scratch_map;
|
||||
delta.n_drops_pf = stats.n_drops_pf - m_last_stats.n_drops_pf;
|
||||
delta.n_drops_bug = stats.n_drops_bug - m_last_stats.n_drops_bug;
|
||||
@@ -118,7 +120,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->check_current_engine(BPF_ENGINE) || inspector->check_current_engine(MODERN_BPF_ENGINE));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -183,6 +185,8 @@ bool syscall_evt_drop_mgr::perform_actions(uint64_t now, scap_stats &delta, bool
|
||||
*/
|
||||
output_fields["n_drops_buffer_other_interest_enter"] = std::to_string(delta.n_drops_buffer_other_interest_enter);
|
||||
output_fields["n_drops_buffer_other_interest_exit"] = std::to_string(delta.n_drops_buffer_other_interest_exit);
|
||||
output_fields["n_drops_buffer_close_exit"] = std::to_string(delta.n_drops_buffer_close_exit);
|
||||
output_fields["n_drops_buffer_proc_exit"] = std::to_string(delta.n_drops_buffer_proc_exit);
|
||||
output_fields["n_drops_scratch_map"] = std::to_string(delta.n_drops_scratch_map); /* Number of kernel side scratch map drops. */
|
||||
output_fields["n_drops_page_faults"] = std::to_string(delta.n_drops_pf); /* Number of kernel side page faults drops (invalid memory access). */
|
||||
output_fields["n_drops_bug"] = std::to_string(delta.n_drops_bug); /* Number of kernel side bug drops (invalid condition in the kernel instrumentation). */
|
||||
|
||||
@@ -26,8 +26,7 @@ limitations under the License.
|
||||
static void display_fatal_err(const std::string &&msg)
|
||||
{
|
||||
/**
|
||||
* If stderr logging is not enabled, also log to stderr. When
|
||||
* daemonized this will simply write to /dev/null.
|
||||
* If stderr logging is not enabled, also log to stderr.
|
||||
*/
|
||||
if (! falco_logger::log_stderr)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
#include <google/protobuf/util/time_util.h>
|
||||
#endif
|
||||
|
||||
@@ -30,7 +30,7 @@ limitations under the License.
|
||||
#include "outputs_program.h"
|
||||
#include "outputs_stdout.h"
|
||||
#include "outputs_syslog.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
#include "outputs_http.h"
|
||||
#include "outputs_grpc.h"
|
||||
#endif
|
||||
@@ -64,13 +64,16 @@ falco_outputs::falco_outputs(
|
||||
{
|
||||
add_output(output);
|
||||
}
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
m_worker_thread = std::thread(&falco_outputs::worker, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
falco_outputs::~falco_outputs()
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
this->stop_worker();
|
||||
#endif
|
||||
for(auto o : m_outputs)
|
||||
{
|
||||
delete o;
|
||||
@@ -98,7 +101,7 @@ void falco_outputs::add_output(falco::outputs::config oc)
|
||||
{
|
||||
oo = new falco::outputs::output_syslog();
|
||||
}
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD)
|
||||
else if(oc.name == "http")
|
||||
{
|
||||
oo = new falco::outputs::output_http();
|
||||
@@ -113,8 +116,16 @@ void falco_outputs::add_output(falco::outputs::config oc)
|
||||
throw falco_exception("Output not supported: " + oc.name);
|
||||
}
|
||||
|
||||
oo->init(oc, m_buffered, m_hostname, m_json_output);
|
||||
m_outputs.push_back(oo);
|
||||
std::string init_err;
|
||||
if (oo->init(oc, m_buffered, m_hostname, m_json_output, init_err))
|
||||
{
|
||||
m_outputs.push_back(oo);
|
||||
}
|
||||
else
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Failed to init output: " + init_err);
|
||||
delete(oo);
|
||||
}
|
||||
}
|
||||
|
||||
void falco_outputs::handle_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
@@ -245,7 +256,9 @@ void falco_outputs::stop_worker()
|
||||
watchdog<void *> wd;
|
||||
wd.start([&](void *) -> void {
|
||||
falco_logger::log(LOG_NOTICE, "output channels still blocked, discarding all remaining notifications\n");
|
||||
#ifndef __EMSCRIPTEN__
|
||||
m_queue.clear();
|
||||
#endif
|
||||
this->push_ctrl(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP);
|
||||
});
|
||||
wd.set_timeout(m_timeout, nullptr);
|
||||
@@ -266,11 +279,18 @@ inline void falco_outputs::push_ctrl(ctrl_msg_type cmt)
|
||||
|
||||
inline void falco_outputs::push(const ctrl_msg& cmsg)
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (!m_queue.try_push(cmsg))
|
||||
{
|
||||
fprintf(stderr, "Fatal error: Output queue reached maximum capacity. Exiting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#else
|
||||
for (auto o : m_outputs)
|
||||
{
|
||||
process_msg(o, cmsg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// todo(leogr,leodido): this function is not supposed to throw exceptions, and with "noexcept",
|
||||
@@ -289,28 +309,16 @@ void falco_outputs::worker() noexcept
|
||||
do
|
||||
{
|
||||
// Block until a message becomes available.
|
||||
#ifndef __EMSCRIPTEN__
|
||||
m_queue.pop(cmsg);
|
||||
#endif
|
||||
|
||||
for(const auto o : m_outputs)
|
||||
for(auto o : m_outputs)
|
||||
{
|
||||
wd.set_timeout(timeout, o->get_name());
|
||||
try
|
||||
{
|
||||
switch(cmsg.type)
|
||||
{
|
||||
case ctrl_msg_type::CTRL_MSG_OUTPUT:
|
||||
o->output(&cmsg);
|
||||
break;
|
||||
case ctrl_msg_type::CTRL_MSG_CLEANUP:
|
||||
case ctrl_msg_type::CTRL_MSG_STOP:
|
||||
o->cleanup();
|
||||
break;
|
||||
case ctrl_msg_type::CTRL_MSG_REOPEN:
|
||||
o->reopen();
|
||||
break;
|
||||
default:
|
||||
falco_logger::log(LOG_DEBUG, "Outputs worker received an unknown message type\n");
|
||||
}
|
||||
process_msg(o, cmsg);
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
@@ -320,3 +328,22 @@ void falco_outputs::worker() noexcept
|
||||
wd.cancel_timeout();
|
||||
} while(cmsg.type != ctrl_msg_type::CTRL_MSG_STOP);
|
||||
}
|
||||
|
||||
inline void falco_outputs::process_msg(falco::outputs::abstract_output* o, const ctrl_msg& cmsg)
|
||||
{
|
||||
switch(cmsg.type)
|
||||
{
|
||||
case ctrl_msg_type::CTRL_MSG_OUTPUT:
|
||||
o->output(&cmsg);
|
||||
break;
|
||||
case ctrl_msg_type::CTRL_MSG_CLEANUP:
|
||||
case ctrl_msg_type::CTRL_MSG_STOP:
|
||||
o->cleanup();
|
||||
break;
|
||||
case ctrl_msg_type::CTRL_MSG_REOPEN:
|
||||
o->reopen();
|
||||
break;
|
||||
default:
|
||||
falco_logger::log(LOG_DEBUG, "Outputs worker received an unknown message type\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ limitations under the License.
|
||||
#include "falco_engine.h"
|
||||
#include "outputs.h"
|
||||
#include "formats.h"
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include "tbb/concurrent_queue.h"
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\brief This class acts as the primary interface between a program and the
|
||||
@@ -105,9 +107,10 @@ private:
|
||||
ctrl_msg_type type;
|
||||
};
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
typedef tbb::concurrent_bounded_queue<ctrl_msg> falco_outputs_cbq;
|
||||
|
||||
falco_outputs_cbq m_queue;
|
||||
#endif
|
||||
|
||||
std::thread m_worker_thread;
|
||||
inline void push(const ctrl_msg& cmsg);
|
||||
@@ -115,4 +118,5 @@ private:
|
||||
void worker() noexcept;
|
||||
void stop_worker();
|
||||
void add_output(falco::outputs::config oc);
|
||||
inline void process_msg(falco::outputs::abstract_output* o, const ctrl_msg& cmsg);
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
@@ -62,14 +62,17 @@ struct message
|
||||
class abstract_output
|
||||
{
|
||||
public:
|
||||
virtual ~abstract_output() {}
|
||||
virtual ~abstract_output() = default;
|
||||
|
||||
void init(const config& oc, bool buffered, const std::string& hostname, bool json_output)
|
||||
virtual bool init(const config& oc, bool buffered, const std::string& hostname, bool json_output, std::string &err)
|
||||
{
|
||||
m_oc = oc;
|
||||
m_buffered = buffered;
|
||||
m_hostname = hostname;
|
||||
m_json_output = json_output;
|
||||
|
||||
err = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the output's name as per its configuration.
|
||||
|
||||
@@ -18,91 +18,107 @@ limitations under the License.
|
||||
#include "logger.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
#define CHECK_RES(fn) res = res == CURLE_OK ? fn : res
|
||||
|
||||
static size_t noop_write_callback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
// We don't want to echo anything. Just return size of bytes ignored
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
bool falco::outputs::output_http::init(const config& oc, bool buffered, const std::string& hostname, bool json_output, std::string &err)
|
||||
{
|
||||
if (!falco::outputs::abstract_output::init(oc, buffered, hostname, json_output, err)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_curl = nullptr;
|
||||
m_http_headers = nullptr;
|
||||
CURLcode res = CURLE_FAILED_INIT;
|
||||
|
||||
m_curl = curl_easy_init();
|
||||
if(!m_curl)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "libcurl failed to initialize the handle: " + std::string(curl_easy_strerror(res)));
|
||||
return false;
|
||||
}
|
||||
if(m_json_output)
|
||||
{
|
||||
m_http_headers = curl_slist_append(m_http_headers, "Content-Type: application/json");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_http_headers = curl_slist_append(m_http_headers, "Content-Type: text/plain");
|
||||
}
|
||||
res = curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_http_headers);
|
||||
|
||||
// if the URL is quoted the quotes should be removed to satisfy libcurl expected format
|
||||
std::string unquotedUrl = m_oc.options["url"];
|
||||
if (!unquotedUrl.empty() && (
|
||||
(unquotedUrl.front() == '\"' && unquotedUrl.back() == '\"') ||
|
||||
(unquotedUrl.front() == '\'' && unquotedUrl.back() == '\'')
|
||||
))
|
||||
{
|
||||
unquotedUrl = libsinsp::filter::unescape_str(unquotedUrl);
|
||||
}
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_URL, unquotedUrl.c_str()));
|
||||
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_USERAGENT, m_oc.options["user_agent"].c_str()));
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, -1L));
|
||||
|
||||
if(m_oc.options["insecure"] == std::string("true"))
|
||||
{
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L));
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYHOST, 0L));
|
||||
}
|
||||
|
||||
if(m_oc.options["mtls"] == std::string("true"))
|
||||
{
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSLCERT, m_oc.options["client_cert"].c_str()));
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSLKEY, m_oc.options["client_key"].c_str()));
|
||||
}
|
||||
|
||||
if (!m_oc.options["ca_cert"].empty())
|
||||
{
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_CAINFO, m_oc.options["ca_cert"].c_str()));
|
||||
}
|
||||
else if(!m_oc.options["ca_bundle"].empty())
|
||||
{
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_CAINFO, m_oc.options["ca_bundle"].c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_CAPATH, m_oc.options["ca_path"].c_str()));
|
||||
}
|
||||
|
||||
if(m_oc.options["echo"] == std::string("false"))
|
||||
{
|
||||
// If echo==true, libcurl defaults to fwrite to stdout, ie: echoing
|
||||
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, noop_write_callback));
|
||||
}
|
||||
|
||||
if(res != CURLE_OK)
|
||||
{
|
||||
err = "libcurl error: " + std::string(curl_easy_strerror(res));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void falco::outputs::output_http::output(const message *msg)
|
||||
{
|
||||
CURL *curl = NULL;
|
||||
CURLcode res = CURLE_FAILED_INIT;
|
||||
struct curl_slist *slist1;
|
||||
slist1 = NULL;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if(curl)
|
||||
CURLcode res = curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, msg->msg.c_str());
|
||||
CHECK_RES(curl_easy_perform(m_curl));
|
||||
if(res != CURLE_OK)
|
||||
{
|
||||
if (m_json_output)
|
||||
{
|
||||
slist1 = curl_slist_append(slist1, "Content-Type: application/json");
|
||||
} else {
|
||||
slist1 = curl_slist_append(slist1, "Content-Type: text/plain");
|
||||
}
|
||||
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);
|
||||
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
// if the URL is quoted the quotes should be removed to satisfy libcurl expected format
|
||||
std::string unquotedUrl = m_oc.options["url"];
|
||||
if (!unquotedUrl.empty() && (
|
||||
(unquotedUrl.front() == '\"' && unquotedUrl.back() == '\"') ||
|
||||
(unquotedUrl.front() == '\'' && unquotedUrl.back() == '\'')
|
||||
))
|
||||
{
|
||||
unquotedUrl = libsinsp::filter::unescape_str(unquotedUrl);
|
||||
}
|
||||
res = curl_easy_setopt(curl, CURLOPT_URL, unquotedUrl.c_str());
|
||||
}
|
||||
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg->msg.c_str());
|
||||
}
|
||||
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
res = curl_easy_setopt(curl, CURLOPT_USERAGENT, m_oc.options["user_agent"].c_str());
|
||||
}
|
||||
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1L);
|
||||
}
|
||||
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
if(m_oc.options["insecure"] == std::string("true"))
|
||||
{
|
||||
res = curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
if (!m_oc.options["ca_cert"].empty())
|
||||
{
|
||||
res = curl_easy_setopt(curl, CURLOPT_CAINFO, m_oc.options["ca_cert"].c_str());
|
||||
}else if(!m_oc.options["ca_bundle"].empty())
|
||||
{
|
||||
res = curl_easy_setopt(curl, CURLOPT_CAINFO, m_oc.options["ca_bundle"].c_str());
|
||||
}else{
|
||||
res = curl_easy_setopt(curl, CURLOPT_CAPATH, m_oc.options["ca_path"].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if(res == CURLE_OK)
|
||||
{
|
||||
res = curl_easy_perform(curl);
|
||||
}
|
||||
|
||||
if(res != CURLE_OK)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "libcurl error: " + std::string(curl_easy_strerror(res)));
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
curl = NULL;
|
||||
curl_slist_free_all(slist1);
|
||||
slist1 = NULL;
|
||||
falco_logger::log(LOG_ERR, "libcurl failed to perform call: " + std::string(curl_easy_strerror(res)));
|
||||
}
|
||||
}
|
||||
|
||||
void falco::outputs::output_http::cleanup()
|
||||
{
|
||||
curl_easy_cleanup(m_curl);
|
||||
m_curl = nullptr;
|
||||
curl_slist_free_all(m_http_headers);
|
||||
m_http_headers = nullptr;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,13 @@ namespace outputs
|
||||
|
||||
class output_http : public abstract_output
|
||||
{
|
||||
void output(const message *msg);
|
||||
bool init(const config& oc, bool buffered, const std::string& hostname, bool json_output, std::string &err) override;
|
||||
void output(const message *msg) override;
|
||||
void cleanup() override;
|
||||
|
||||
private:
|
||||
CURL *m_curl;
|
||||
struct curl_slist *m_http_headers;
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
|
||||
@@ -32,6 +32,7 @@ limitations under the License.
|
||||
// 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 timer_t s_timerid;
|
||||
|
||||
static void timer_handler(int signum)
|
||||
{
|
||||
@@ -51,25 +52,29 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string &err)
|
||||
return false;
|
||||
}
|
||||
|
||||
timer_t timerid;
|
||||
struct sigevent sev = {};
|
||||
/* Create the timer */
|
||||
sev.sigev_notify = SIGEV_SIGNAL;
|
||||
sev.sigev_signo = SIGALRM;
|
||||
sev.sigev_value.sival_ptr = &timerid;
|
||||
if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) == -1) {
|
||||
sev.sigev_value.sival_ptr = &s_timerid;
|
||||
#ifndef __EMSCRIPTEN__
|
||||
// delete any previously set timer
|
||||
timer_delete(s_timerid);
|
||||
if (timer_create(CLOCK_MONOTONIC, &sev, &s_timerid) == -1) {
|
||||
err = std::string("Could not create periodic timer: ") + strerror(errno);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
timer.it_value.tv_sec = interval_msec / 1000;
|
||||
timer.it_value.tv_nsec = (interval_msec % 1000) * 1000 * 1000;
|
||||
timer.it_interval = timer.it_value;
|
||||
|
||||
if (timer_settime(timerid, 0, &timer, NULL) == -1) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (timer_settime(s_timerid, 0, &timer, NULL) == -1) {
|
||||
err = std::string("Could not set up periodic timer: ") + strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -102,7 +107,9 @@ stats_writer::stats_writer(
|
||||
|
||||
if (m_initialized)
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
m_worker = std::thread(&stats_writer::worker, this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,11 +117,17 @@ stats_writer::~stats_writer()
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
stop_worker();
|
||||
#endif
|
||||
if (!m_config->m_metrics_output_file.empty())
|
||||
{
|
||||
m_file_output.close();
|
||||
}
|
||||
// delete timerID and reset timer
|
||||
#ifndef __EMSCRIPTEN__
|
||||
timer_delete(s_timerid);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,11 +144,13 @@ void stats_writer::stop_worker()
|
||||
|
||||
inline void stats_writer::push(const stats_writer::msg& m)
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (!m_queue.try_push(m))
|
||||
{
|
||||
fprintf(stderr, "Fatal error: Stats queue reached maximum capacity. Exiting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void stats_writer::worker() noexcept
|
||||
@@ -151,7 +166,9 @@ void stats_writer::worker() noexcept
|
||||
while(true)
|
||||
{
|
||||
// blocks until a message becomes availables
|
||||
#ifndef __EMSCRIPTEN__
|
||||
m_queue.pop(m);
|
||||
#endif
|
||||
if (m.stop)
|
||||
{
|
||||
return;
|
||||
@@ -247,7 +264,7 @@ void stats_writer::collector::get_metrics_output_fields_additional(
|
||||
const scap_agent_info* agent_info = inspector->get_agent_info();
|
||||
const scap_machine_info* machine_info = inspector->get_machine_info();
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
#if !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__)
|
||||
/* Resource utilization, CPU and memory usage etc. */
|
||||
uint32_t nstats = 0;
|
||||
int32_t rc = 0;
|
||||
|
||||
@@ -22,7 +22,9 @@ limitations under the License.
|
||||
|
||||
#include <sinsp.h>
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include "tbb/concurrent_queue.h"
|
||||
#endif
|
||||
#include "falco_outputs.h"
|
||||
#include "configuration.h"
|
||||
|
||||
@@ -143,7 +145,9 @@ private:
|
||||
uint64_t m_total_samples;
|
||||
std::thread m_worker;
|
||||
std::ofstream m_file_output;
|
||||
#ifndef __EMSCRIPTEN__
|
||||
tbb::concurrent_bounded_queue<stats_writer::msg> m_queue;
|
||||
#endif
|
||||
std::shared_ptr<falco_outputs> m_outputs;
|
||||
std::shared_ptr<const falco_configuration> m_config;
|
||||
|
||||
|
||||
@@ -72,6 +72,41 @@ public:
|
||||
get_node(node, key);
|
||||
if(node.IsDefined())
|
||||
{
|
||||
std::string value = node.as<std::string>();
|
||||
|
||||
// Helper function to convert string to the desired type T
|
||||
auto convert_str_to_t = [&default_value](const std::string& str) -> T {
|
||||
std::stringstream ss(str);
|
||||
T result;
|
||||
if (ss >> result) return result;
|
||||
return default_value;
|
||||
};
|
||||
|
||||
// If the value starts with `$$`, check for a subsequent `{...}`
|
||||
if (value.size() >= 3 && value[0] == '$' && value[1] == '$')
|
||||
{
|
||||
// If after stripping the first `$`, the string format is like `${VAR}`, treat it as a plain string and don't resolve.
|
||||
if (value[2] == '{' && value[value.size() - 1] == '}')
|
||||
{
|
||||
value = value.substr(1);
|
||||
return convert_str_to_t(value);
|
||||
}
|
||||
else return convert_str_to_t(value);
|
||||
}
|
||||
|
||||
// Check if the value is an environment variable reference
|
||||
if(value.size() >= 2 && value[0] == '$' && value[1] == '{' && value[value.size() - 1] == '}')
|
||||
{
|
||||
// Format: ${ENV_VAR_NAME}
|
||||
std::string env_var = value.substr(2, value.size() - 3);
|
||||
|
||||
const char* env_value = std::getenv(env_var.c_str()); // Get the environment variable value
|
||||
if(env_value) return convert_str_to_t(env_value);
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
// If it's not an environment variable reference, return the value as is
|
||||
return node.as<T>();
|
||||
}
|
||||
|
||||
@@ -118,10 +153,10 @@ private:
|
||||
* The provided key string can navigate the document in its
|
||||
* nested nodes, with arbitrary depth. The key string follows
|
||||
* this regular language:
|
||||
*
|
||||
*
|
||||
* Key := NodeKey ('.' NodeKey)*
|
||||
* NodeKey := (any)+ ('[' (integer)+ ']')*
|
||||
*
|
||||
*
|
||||
* Some examples of accepted key strings:
|
||||
* - NodeName
|
||||
* - ListValue[3].subvalue
|
||||
@@ -146,7 +181,7 @@ private:
|
||||
if (i > 0 && nodeKey.empty() && key[i - 1] != '.')
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Parsing error: expected '.' character at pos "
|
||||
"Parsing error: expected '.' character at pos "
|
||||
+ std::to_string(i - 1));
|
||||
}
|
||||
nodeKey += c;
|
||||
@@ -157,7 +192,7 @@ private:
|
||||
if (nodeKey.empty())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Parsing error: unexpected character at pos "
|
||||
"Parsing error: unexpected character at pos "
|
||||
+ std::to_string(i));
|
||||
}
|
||||
ret.reset(ret[nodeKey]);
|
||||
@@ -181,7 +216,7 @@ private:
|
||||
throw std::runtime_error("Config error at key \"" + key + "\": " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void get_sequence_from_node(T& ret, const YAML::Node& node) const
|
||||
{
|
||||
@@ -250,7 +285,7 @@ namespace YAML {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user