mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-22 04:32:21 +00:00
Compare commits
5 Commits
test/PR546
...
0.34.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a4205cc70 | ||
|
|
be3c44fe38 | ||
|
|
cae02e96b9 | ||
|
|
4fc6153160 | ||
|
|
b3f009ad4c |
@@ -82,6 +82,11 @@ jobs:
|
||||
command: |
|
||||
cd /build-static/release
|
||||
make -j6 package
|
||||
- run:
|
||||
name: Run unit tests
|
||||
command: |
|
||||
cd /build-static/release
|
||||
make tests
|
||||
- run:
|
||||
name: Prepare artifacts
|
||||
command: |
|
||||
|
||||
24
.github/dependabot.yml
vendored
24
.github/dependabot.yml
vendored
@@ -1,24 +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.
|
||||
#
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: gitsubmodule
|
||||
schedule:
|
||||
interval: "daily"
|
||||
directory: /
|
||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build-minimal
|
||||
pushd build-minimal
|
||||
cmake -DMINIMAL_BUILD=On -DBUILD_BPF=Off -DBUILD_DRIVER=Off -DCMAKE_BUILD_TYPE=Release -DBUILD_FALCO_UNIT_TESTS=On ..
|
||||
cmake -DMINIMAL_BUILD=On -DBUILD_BPF=Off -DBUILD_DRIVER=Off -DCMAKE_BUILD_TYPE=Release ..
|
||||
popd
|
||||
|
||||
- name: Build
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
pushd build-minimal
|
||||
sudo ./unit_tests/falco_unit_tests
|
||||
make tests
|
||||
popd
|
||||
|
||||
build-ubuntu-focal:
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
pushd build
|
||||
cmake -DBUILD_BPF=On -DBUILD_FALCO_UNIT_TESTS=On ..
|
||||
cmake -DBUILD_BPF=On ..
|
||||
popd
|
||||
|
||||
- name: Build
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
pushd build
|
||||
sudo ./unit_tests/falco_unit_tests
|
||||
make tests
|
||||
popd
|
||||
|
||||
build-ubuntu-focal-debug:
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
pushd build
|
||||
cmake -DCMAKE_BUILD_TYPE=debug -DBUILD_BPF=On -DBUILD_FALCO_UNIT_TESTS=On ..
|
||||
cmake -DCMAKE_BUILD_TYPE=debug -DBUILD_BPF=On ..
|
||||
popd
|
||||
|
||||
- name: Build
|
||||
@@ -106,5 +106,5 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
pushd build
|
||||
sudo ./unit_tests/falco_unit_tests
|
||||
make tests
|
||||
popd
|
||||
|
||||
64
.github/workflows/images_bumper.yml
vendored
64
.github/workflows/images_bumper.yml
vendored
@@ -1,64 +0,0 @@
|
||||
name: Builder and Tester Images Bumper
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
paths-filter:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
builder_changed: ${{ steps.filter.outputs.builder }}
|
||||
tester_changed: ${{ steps.filter.outputs.tester }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
builder:
|
||||
- 'docker/builder/**'
|
||||
tester:
|
||||
- 'docker/tester/**'
|
||||
|
||||
update-builder-tester-images:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: paths-filter
|
||||
if: needs.paths-filter.outputs.builder_changed == 'true' || needs.paths-filter.outputs.tester_changed == 'true'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
password: ${{ secrets.DOCKERHUB_SECRET }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
with:
|
||||
platforms: 'amd64,arm64'
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Build and push new builder image
|
||||
if: needs.paths-filter.outputs.builder_changed == 'true'
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: docker/builder
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: latest
|
||||
push: true
|
||||
|
||||
- name: Build and push new tester image
|
||||
if: needs.paths-filter.outputs.tester_changed == 'true'
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: docker/tester
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: latest
|
||||
push: true
|
||||
@@ -24,8 +24,6 @@ This is a list of production adopters of Falco (in alphabetical order):
|
||||
|
||||
* [Coveo](https://www.coveo.com/) - Coveo stitches together content and data, learning from every interaction, to tailor every experience using AI to drive growth, satisfy customers and develop employee proficiency. All Falco events are centralized in our SIEM for analysis. Understanding what is running on production servers, and the context around why things are running is even more tricky now that we have further abstractions with containers and orchestration systems. Falco is giving us a good visibility inside containers and complement other Host and Network Intrusion Detection Systems. In a near future, we expect to deploy serverless functions to take action when Falco identifies patterns worth taking action for.
|
||||
|
||||
* [Deckhouse](https://deckhouse.io/) - Deckhouse Platform presents to you the opportunity to create homogeneous Kubernetes clusters anywhere and handles comprehensive, automagical management for them. It supplies all the add-ons you need for auto-scaling, observability, security, and service mesh. Falco is used as a part of the [runtime-audit-engine](https://deckhouse.io/documentation/latest/modules/650-runtime-audit-engine/) module to provide threats detection and enforce security compliance out of the box. By pairing with [shell-operator](https://github.com/flant/shell-operator) Falco can be configured by Kubernetes Custom Resources.
|
||||
|
||||
* [Fairwinds](https://fairwinds.com/) - [Fairwinds Insights](https://fairwinds.com/insights), Kubernetes governance software, integrates Falco to offer a single pane of glass view into potential security incidents. Insights adds out-of-the-box integrations and rules filter to reduce alert fatigue and improve security response. The platform adds security prevention, detection, and response capabilities to your existing Kubernetes infrastructure. Security and DevOps teams benefit from a centralized view of container security vulnerability scanning and runtime container security.
|
||||
|
||||
* [Frame.io](https://frame.io/) - Frame.io is a cloud-based (SaaS) video review and collaboration platform that enables users to securely upload source media, work-in-progress edits, dailies, and more into private workspaces where they can invite their team and clients to collaborate on projects. Understanding what is running on production servers, and the context around why things are running is even more tricky now that we have further abstractions like Docker and Kubernetes. To get this needed visibility into our system, we rely on Falco. Falco's ability to collect raw system calls such as open, connect, exec, along with their arguments offer key insights on what is happening on the production system and became the foundation of our intrusion detection and alerting system.
|
||||
|
||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,17 +1,5 @@
|
||||
# Change Log
|
||||
|
||||
## v0.34.1
|
||||
|
||||
Released on 2023-02-20
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* fix(userspace/engine): correctly bump FALCO_ENGINE_VERSION after introduction of new fields [[#2418](https://github.com/falcosecurity/falco/pull/2418)] - [@loresuso](https://github.com/loresuso/)
|
||||
|
||||
### Non user-facing changes
|
||||
|
||||
* fix(dockerfile/no-driver): install ca-certificates [[#2412](https://github.com/falcosecurity/falco/pull/2412)] - [@alacuku](https://github.com/alacuku)
|
||||
|
||||
## v0.34.0
|
||||
|
||||
Released on 2023-02-07
|
||||
|
||||
@@ -18,7 +18,6 @@ option(USE_BUNDLED_DEPS "Bundle hard to find dependencies into the Falco binary"
|
||||
option(BUILD_WARNINGS_AS_ERRORS "Enable building with -Wextra -Werror flags" OFF)
|
||||
option(MINIMAL_BUILD "Build a minimal version of Falco, containing only the engine and basic input/output (EXPERIMENTAL)" OFF)
|
||||
option(MUSL_OPTIMIZED_BUILD "Enable if you want a musl optimized build" OFF)
|
||||
option(BUILD_FALCO_UNIT_TESTS "Build falco unit tests" OFF)
|
||||
|
||||
# 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)
|
||||
@@ -149,7 +148,16 @@ include(falcosecurity-libs)
|
||||
include(jq)
|
||||
|
||||
# nlohmann-json
|
||||
include(njson)
|
||||
set(NJSON_SRC "${PROJECT_BINARY_DIR}/njson-prefix/src/njson")
|
||||
message(STATUS "Using bundled nlohmann-json in '${NJSON_SRC}'")
|
||||
set(NJSON_INCLUDE "${NJSON_SRC}/single_include")
|
||||
ExternalProject_Add(
|
||||
njson
|
||||
URL "https://github.com/nlohmann/json/archive/v3.3.0.tar.gz"
|
||||
URL_HASH "SHA256=2fd1d207b4669a7843296c41d3b6ac5b23d00dec48dba507ba051d14564aa801"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
# b64
|
||||
include(b64)
|
||||
@@ -214,6 +222,7 @@ set(FALCO_BIN_DIR bin)
|
||||
add_subdirectory(scripts)
|
||||
add_subdirectory(userspace/engine)
|
||||
add_subdirectory(userspace/falco)
|
||||
add_subdirectory(tests)
|
||||
|
||||
if(NOT MUSL_OPTIMIZED_BUILD)
|
||||
include(plugins)
|
||||
@@ -223,7 +232,3 @@ include(falcoctl)
|
||||
|
||||
# Packages configuration
|
||||
include(CPackConfig)
|
||||
|
||||
if(BUILD_FALCO_UNIT_TESTS)
|
||||
add_subdirectory(unit_tests)
|
||||
endif()
|
||||
|
||||
61
RELEASE.md
61
RELEASE.md
@@ -28,7 +28,7 @@ The Falco project publishes all sources and the Falco userspace binaries as GitH
|
||||
- `tgz`, `zip` source code
|
||||
- [Libs Releases](https://github.com/falcosecurity/libs/releases)
|
||||
- `tgz`, `zip` source code
|
||||
- [Driver Releases](https://github.com/falcosecurity/libs/releases), marked with `+driver` [build metadata](https://semver.org/).
|
||||
- [Libs Releases](https://github.com/falcosecurity/libs/releases)
|
||||
- `tgz`, `zip` source code
|
||||
- [Falco Rules Releases](https://github.com/falcosecurity/rules/releases)
|
||||
- `tgz`, `zip` source code, each ruleset is tagged separately in a mono-repo fashion, see the [rules release guidelines](https://github.com/falcosecurity/rules/blob/main/RELEASE.md)
|
||||
@@ -69,7 +69,7 @@ At a high level each Falco release needs to follow a pre-determined sequencing o
|
||||
- [4] Falco driver pre-compiled object files push to Falco's Artifacts repo
|
||||
- [5] Falco userspace binary release
|
||||
|
||||
Finally, on the proposed due date the assignees for the upcoming release proceed with the processes described below.
|
||||
Finally, on the proposed due date the assignees for the upcoming release proceed with the processes described below.
|
||||
|
||||
## Pre-Release Checklist
|
||||
|
||||
@@ -87,19 +87,7 @@ Prior to cutting a release the following preparatory steps should take 5 minutes
|
||||
|
||||
- Move the [tasks not completed](https://github.com/falcosecurity/falco/pulls?q=is%3Apr+is%3Aopen) to a new minor milestone
|
||||
|
||||
|
||||
### 3. Release branch
|
||||
|
||||
Assuming we are releasing a non-patch version (like: Falco 0.34.0), a new release branch needs to be created.
|
||||
Its naming will be `release/M.m.x`; for example: `release/0.34.x`.
|
||||
The same branch will then be used for any eventual cherry pick for patch releases.
|
||||
|
||||
For patch releases, instead, the `release/M.m.x` branch should already be in place; no more steps are needed.
|
||||
Double check that any PR that should be part of the tag has been cherry-picked from master!
|
||||
|
||||
### 4. Release PR
|
||||
|
||||
The release PR is meant to be made against the respective `release/M.m.x` branch, **then cherry-picked on master**.
|
||||
### 3. Release PR
|
||||
|
||||
- Double-check if any hard-coded version number is present in the code, it should be not present anywhere:
|
||||
- If any, manually correct it then open an issue to automate version number bumping later
|
||||
@@ -110,22 +98,21 @@ The release PR is meant to be made against the respective `release/M.m.x` branch
|
||||
- Add the latest changes on top the previous `CHANGELOG.md`
|
||||
- Submit a PR with the above modifications
|
||||
- Await PR approval
|
||||
- Close the completed milestone as soon as the PR is merged into the release branch
|
||||
- Cherry pick the PR on master too
|
||||
- Close the completed milestone as soon as the PR is merged
|
||||
|
||||
## Release
|
||||
|
||||
Assume `M.m.p` is the new version.
|
||||
Now assume `x.y.z` is the new version.
|
||||
|
||||
### 1. Create a tag
|
||||
|
||||
- Once the release PR has got merged both on the release branch and on master, and the master CI has done its job, git tag the new release on the release branch:
|
||||
- Once the release PR has got merged, and the CI has done its job on the master, git tag the new release
|
||||
|
||||
```
|
||||
git pull
|
||||
git checkout release/M.m.x
|
||||
git tag M.m.p
|
||||
git push origin M.m.p
|
||||
git checkout master
|
||||
git tag x.y.z
|
||||
git push origin x.y.z
|
||||
```
|
||||
|
||||
> **N.B.**: do NOT use an annotated tag. For reference https://git-scm.com/book/en/v2/Git-Basics-Tagging
|
||||
@@ -135,26 +122,26 @@ Assume `M.m.p` is the new version.
|
||||
### 2. Update the GitHub release
|
||||
|
||||
- [Draft a new release](https://github.com/falcosecurity/falco/releases/new)
|
||||
- Use `M.m.p` both as tag version and release title
|
||||
- Use `x.y.z` both as tag version and release title
|
||||
- Use the following template to fill the release description:
|
||||
```
|
||||
<!-- Substitute M.m.p with the current release version -->
|
||||
<!-- Substitute x.y.z with the current release version -->
|
||||
|
||||
| Packages | Download |
|
||||
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| rpm-x86_64 | [](https://download.falco.org/packages/rpm/falco-M.m.p-x86_64.rpm) |
|
||||
| deb-x86_64 | [](https://download.falco.org/packages/deb/stable/falco-M.m.p-x86_64.deb) |
|
||||
| tgz-x86_64 | [](https://download.falco.org/packages/bin/x86_64/falco-M.m.p-x86_64.tar.gz) |
|
||||
| rpm-aarch64 | [](https://download.falco.org/packages/rpm/falco-M.m.p-aarch64.rpm) |
|
||||
| deb-aarch64 | [](https://download.falco.org/packages/deb/stable/falco-M.m.p-aarch64.deb) |
|
||||
| tgz-aarch64 | [](https://download.falco.org/packages/bin/aarch64/falco-M.m.p-aarch64.tar.gz) |
|
||||
| rpm-x86_64 | [](https://download.falco.org/packages/rpm/falco-x.y.z-x86_64.rpm) |
|
||||
| deb-x86_64 | [](https://download.falco.org/packages/deb/stable/falco-x.y.z-x86_64.deb) |
|
||||
| tgz-x86_64 | [](https://download.falco.org/packages/bin/x86_64/falco-x.y.z-x86_64.tar.gz) |
|
||||
| rpm-aarch64 | [](https://download.falco.org/packages/rpm/falco-x.y.z-aarch64.rpm) |
|
||||
| deb-aarch64 | [](https://download.falco.org/packages/deb/stable/falco-x.y.z-aarch64.deb) |
|
||||
| tgz-aarch64 | [](https://download.falco.org/packages/bin/aarch64/falco-x.y.z-aarch64.tar.gz) |
|
||||
|
||||
| Images |
|
||||
| --------------------------------------------------------------------------- |
|
||||
| `docker pull docker.io/falcosecurity/falco:M.m.p` |
|
||||
| `docker pull public.ecr.aws/falcosecurity/falco:M.m.p` |
|
||||
| `docker pull docker.io/falcosecurity/falco-driver-loader:M.m.p` |
|
||||
| `docker pull docker.io/falcosecurity/falco-no-driver:M.m.p` |
|
||||
| `docker pull docker.io/falcosecurity/falco:x.y.z` |
|
||||
| `docker pull public.ecr.aws/falcosecurity/falco:x.y.z` |
|
||||
| `docker pull docker.io/falcosecurity/falco-driver-loader:x.y.z` |
|
||||
| `docker pull docker.io/falcosecurity/falco-no-driver:x.y.z` |
|
||||
|
||||
<changelog>
|
||||
|
||||
@@ -183,7 +170,7 @@ For each release we archive the meeting notes in git for historical purposes.
|
||||
|
||||
- The notes from the Falco meetings can be [found here](https://hackmd.io/3qYPnZPUQLGKCzR14va_qg).
|
||||
- Note: There may be other notes from working groups that can optionally be added as well as needed.
|
||||
- Add the entire content of the document to a new file in [github.com/falcosecurity/community/tree/master/meeting-notes](https://github.com/falcosecurity/community/tree/master/meeting-notes) as a new file labeled `release-M.m.p.md`
|
||||
- Add the entire content of the document to a new file in [github.com/falcosecurity/community/tree/master/meeting-notes](https://github.com/falcosecurity/community/tree/master/meeting-notes) as a new file labeled `release-x.y.z.md`
|
||||
- Open up a pull request with the new change.
|
||||
|
||||
|
||||
@@ -225,7 +212,7 @@ Driver:
|
||||
|
||||
### Libs repo
|
||||
- Libs version is a git tag (`x.y.z`) and when building Falco the libs version is set via the `FALCOSECURITY_LIBS_VERSION` flag (see above).
|
||||
- Driver version itself is not directly tied to the Falco binary as opposed to the libs version being part of the source code used to compile Falco's userspace binary. This is because of the strict separation between userspace and kernel space artifacts, so things become a bit more interesting here. This is why the concept of a `Default driver` has been introduced to still implicitly declare the compatible driver versions. For example, if the default driver version is `2.0.0+driver`, Falco works with all driver versions >= 2.0.0 and < 3.0.0. This is a consequence of how the driver version is constructed starting from the `Driver API version` and `Driver Schema version`. Driver API and Schema versions are explained in the respective [libs driver doc](https://github.com/falcosecurity/libs/blob/master/driver/README.VERSION.md) -> Falco's `driver-loader` will always fetch the default driver, therefore a Falco release is always "shipped" with the driver version corresponding to the default driver.
|
||||
- Driver version in and of itself is not directly tied to the Falco binary as opposed to the libs version being part of the source code used to compile Falco's userspace binary. This is because of the strict separation between userspace and kernel space artifacts, so things become a bit more interesting here. This is why the concept of a `Default driver` has been introduced to still implicitly declare the compatible driver versions. For example, if the default driver version is `2.0.0+driver`, Falco works with all driver versions >= 2.0.0 and < 3.0.0. This is a consequence of how the driver version is constructed starting from the `Driver API version` and `Driver Schema version`. Driver API and Schema versions are explained in the respective [libs driver doc](https://github.com/falcosecurity/libs/blob/master/driver/README.VERSION.md) -> Falco's `driver-loader` will always fetch the default driver, therefore a Falco release is always "shipped" with the driver version corresponding to the default driver.
|
||||
- See [libs release doc](https://github.com/falcosecurity/libs/blob/master/release.md) for more information.
|
||||
|
||||
### Plugins repo
|
||||
@@ -236,4 +223,4 @@ Driver:
|
||||
### Rules repo
|
||||
- Rulesets are versioned individually through git tags
|
||||
- See [rules release doc](https://github.com/falcosecurity/rules/blob/main/RELEASE.md) for more information.
|
||||
- See [plugins release doc](https://github.com/falcosecurity/plugins/blob/master/release.md) for more information about plugins rulesets.
|
||||
- See [plugins release doc](https://github.com/falcosecurity/plugins/blob/master/release.md) for more information about plugins rulesets.
|
||||
Binary file not shown.
159
cmake/modules/Catch.cmake
Normal file
159
cmake/modules/Catch.cmake
Normal file
@@ -0,0 +1,159 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or
|
||||
# https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
Catch
|
||||
-----
|
||||
|
||||
This module defines a function to help use the Catch test framework.
|
||||
|
||||
The :command:`catch_discover_tests` discovers tests by asking the compiled test
|
||||
executable to enumerate its tests. This does not require CMake to be re-run
|
||||
when tests change. However, it may not work in a cross-compiling environment,
|
||||
and setting test properties is less convenient.
|
||||
|
||||
This command is intended to replace use of :command:`add_test` to register
|
||||
tests, and will create a separate CTest test for each Catch test case. Note
|
||||
that this is in some cases less efficient, as common set-up and tear-down logic
|
||||
cannot be shared by multiple test cases executing in the same instance.
|
||||
However, it provides more fine-grained pass/fail information to CTest, which is
|
||||
usually considered as more beneficial. By default, the CTest test name is the
|
||||
same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
|
||||
|
||||
.. command:: catch_discover_tests
|
||||
|
||||
Automatically add tests with CTest by querying the compiled test executable
|
||||
for available tests::
|
||||
|
||||
catch_discover_tests(target
|
||||
[TEST_SPEC arg1...]
|
||||
[EXTRA_ARGS arg1...]
|
||||
[WORKING_DIRECTORY dir]
|
||||
[TEST_PREFIX prefix]
|
||||
[TEST_SUFFIX suffix]
|
||||
[PROPERTIES name1 value1...]
|
||||
[TEST_LIST var]
|
||||
)
|
||||
|
||||
``catch_discover_tests`` sets up a post-build command on the test executable
|
||||
that generates the list of tests by parsing the output from running the test
|
||||
with the ``--list-test-names-only`` argument. This ensures that the full
|
||||
list of tests is obtained. Since test discovery occurs at build time, it is
|
||||
not necessary to re-run CMake when the list of tests changes.
|
||||
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
|
||||
in order to function in a cross-compiling environment.
|
||||
|
||||
Additionally, setting properties on tests is somewhat less convenient, since
|
||||
the tests are not available at CMake time. Additional test properties may be
|
||||
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
|
||||
more fine-grained test control is needed, custom content may be provided
|
||||
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
|
||||
directory property. The set of discovered tests is made accessible to such a
|
||||
script via the ``<target>_TESTS`` variable.
|
||||
|
||||
The options are:
|
||||
|
||||
``target``
|
||||
Specifies the Catch executable, which must be a known CMake executable
|
||||
target. CMake will substitute the location of the built executable when
|
||||
running the test.
|
||||
|
||||
``TEST_SPEC arg1...``
|
||||
Specifies test cases, wildcarded test cases, tags and tag expressions to
|
||||
pass to the Catch executable with the ``--list-test-names-only`` argument.
|
||||
|
||||
``EXTRA_ARGS arg1...``
|
||||
Any extra arguments to pass on the command line to each test case.
|
||||
|
||||
``WORKING_DIRECTORY dir``
|
||||
Specifies the directory in which to run the discovered test cases. If this
|
||||
option is not provided, the current binary directory is used.
|
||||
|
||||
``TEST_PREFIX prefix``
|
||||
Specifies a ``prefix`` to be prepended to the name of each discovered test
|
||||
case. This can be useful when the same test executable is being used in
|
||||
multiple calls to ``catch_discover_tests()`` but with different
|
||||
``TEST_SPEC`` or ``EXTRA_ARGS``.
|
||||
|
||||
``TEST_SUFFIX suffix``
|
||||
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
|
||||
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
|
||||
be specified.
|
||||
|
||||
``PROPERTIES name1 value1...``
|
||||
Specifies additional properties to be set on all tests discovered by this
|
||||
invocation of ``catch_discover_tests``.
|
||||
|
||||
``TEST_LIST var``
|
||||
Make the list of tests available in the variable ``var``, rather than the
|
||||
default ``<target>_TESTS``. This can be useful when the same test
|
||||
executable is being used in multiple calls to ``catch_discover_tests()``.
|
||||
Note that this variable is only available in CTest.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
function(catch_discover_tests TARGET)
|
||||
cmake_parse_arguments("" "" "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST" "TEST_SPEC;EXTRA_ARGS;PROPERTIES"
|
||||
${ARGN})
|
||||
|
||||
if(NOT _WORKING_DIRECTORY)
|
||||
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
if(NOT _TEST_LIST)
|
||||
set(_TEST_LIST ${TARGET}_TESTS)
|
||||
endif()
|
||||
|
||||
# Generate a unique name based on the extra arguments
|
||||
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
|
||||
string(SUBSTRING ${args_hash} 0 7 args_hash)
|
||||
|
||||
# Define rule to generate test list for aforementioned test executable
|
||||
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
|
||||
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
|
||||
get_property(
|
||||
crosscompiling_emulator
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CROSSCOMPILING_EMULATOR)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET}
|
||||
POST_BUILD
|
||||
BYPRODUCTS "${ctest_tests_file}"
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -D "TEST_TARGET=${TARGET}" -D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>" -D
|
||||
"TEST_EXECUTOR=${crosscompiling_emulator}" -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" -D
|
||||
"TEST_SPEC=${_TEST_SPEC}" -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" -D "TEST_PROPERTIES=${_PROPERTIES}" -D
|
||||
"TEST_PREFIX=${_TEST_PREFIX}" -D "TEST_SUFFIX=${_TEST_SUFFIX}" -D "TEST_LIST=${_TEST_LIST}" -D
|
||||
"CTEST_FILE=${ctest_tests_file}" -P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
|
||||
VERBATIM)
|
||||
|
||||
file(
|
||||
WRITE "${ctest_include_file}"
|
||||
"if(EXISTS \"${ctest_tests_file}\")\n" " include(\"${ctest_tests_file}\")\n" "else()\n"
|
||||
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" "endif()\n")
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
|
||||
# Add discovered tests to directory TEST_INCLUDE_FILES
|
||||
set_property(
|
||||
DIRECTORY
|
||||
APPEND
|
||||
PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}")
|
||||
else()
|
||||
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
|
||||
get_property(
|
||||
test_include_file_set
|
||||
DIRECTORY
|
||||
PROPERTY TEST_INCLUDE_FILE
|
||||
SET)
|
||||
if(NOT ${test_include_file_set})
|
||||
set_property(DIRECTORY PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}")
|
||||
else()
|
||||
message(FATAL_ERROR "Cannot set more than one TEST_INCLUDE_FILE")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
# ######################################################################################################################
|
||||
|
||||
set(_CATCH_DISCOVER_TESTS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake)
|
||||
61
cmake/modules/CatchAddTests.cmake
Normal file
61
cmake/modules/CatchAddTests.cmake
Normal file
@@ -0,0 +1,61 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or
|
||||
# https://cmake.org/licensing for details.
|
||||
|
||||
set(prefix "${TEST_PREFIX}")
|
||||
set(suffix "${TEST_SUFFIX}")
|
||||
set(spec ${TEST_SPEC})
|
||||
set(extra_args ${TEST_EXTRA_ARGS})
|
||||
set(properties ${TEST_PROPERTIES})
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
|
||||
function(add_command NAME)
|
||||
set(_args "")
|
||||
foreach(_arg ${ARGN})
|
||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
|
||||
else()
|
||||
set(_args "${_args} ${_arg}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(script
|
||||
"${script}${NAME}(${_args})\n"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR "Specified test executable '${TEST_EXECUTABLE}' does not exist")
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result)
|
||||
# Catch --list-test-names-only reports the number of tests, so 0 is... surprising
|
||||
if(${result} EQUAL 0)
|
||||
message(WARNING "Test executable '${TEST_EXECUTABLE}' contains no tests!\n")
|
||||
elseif(${result} LESS 0)
|
||||
message(FATAL_ERROR "Error running test executable '${TEST_EXECUTABLE}':\n" " Result: ${result}\n"
|
||||
" Output: ${output}\n")
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
set(test ${line})
|
||||
# use escape commas to handle properly test cases with commands inside the name
|
||||
string(REPLACE "," "\\," test_name ${test})
|
||||
# ...and add to script
|
||||
add_command(add_test "${prefix}${test}${suffix}" ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" "${test_name}" ${extra_args})
|
||||
add_command(set_tests_properties "${prefix}${test}${suffix}" PROPERTIES WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties})
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set properties on the tests
|
||||
add_command(set ${TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
file(WRITE "${CTEST_FILE}" "${script}")
|
||||
27
cmake/modules/DownloadCatch.cmake
Normal file
27
cmake/modules/DownloadCatch.cmake
Normal file
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
include(ExternalProject)
|
||||
|
||||
set(CATCH2_INCLUDE ${CMAKE_BINARY_DIR}/catch2-prefix/include)
|
||||
|
||||
set(CATCH_EXTERNAL_URL URL https://github.com/catchorg/catch2/archive/v2.13.9.tar.gz URL_HASH
|
||||
SHA256=06dbc7620e3b96c2b69d57bf337028bf245a211b3cddb843835bfe258f427a52)
|
||||
|
||||
ExternalProject_Add(
|
||||
catch2
|
||||
PREFIX ${CMAKE_BINARY_DIR}/catch2-prefix
|
||||
${CATCH_EXTERNAL_URL}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/catch2-prefix/src/catch2/single_include/catch2/catch.hpp
|
||||
${CATCH2_INCLUDE}/catch.hpp)
|
||||
28
cmake/modules/DownloadFakeIt.cmake
Normal file
28
cmake/modules/DownloadFakeIt.cmake
Normal file
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
include(ExternalProject)
|
||||
|
||||
set(FAKEIT_INCLUDE ${CMAKE_BINARY_DIR}/fakeit-prefix/include)
|
||||
|
||||
set(FAKEIT_EXTERNAL_URL URL https://github.com/eranpeer/fakeit/archive/2.0.9.tar.gz URL_HASH
|
||||
SHA256=dc4ee7b17a84c959019b92c20fce6dc9426e9e170b6edf84db6cb2e188520cd7)
|
||||
|
||||
ExternalProject_Add(
|
||||
fakeit-external
|
||||
PREFIX ${CMAKE_BINARY_DIR}/fakeit-prefix
|
||||
${FAKEIT_EXTERNAL_URL}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND
|
||||
${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/fakeit-prefix/src/fakeit-external/single_header/catch/fakeit.hpp
|
||||
${FAKEIT_INCLUDE}/fakeit.hpp)
|
||||
31
cmake/modules/FindMakedev.cmake
Normal file
31
cmake/modules/FindMakedev.cmake
Normal file
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
|
||||
# This module is used to understand where the makedev function is defined in the glibc in use. see 'man 3 makedev'
|
||||
# Usage: In your CMakeLists.txt include(FindMakedev)
|
||||
#
|
||||
# In your source code:
|
||||
#
|
||||
# #if HAVE_SYS_MKDEV_H #include <sys/mkdev.h> #endif #ifdef HAVE_SYS_SYSMACROS_H #include <sys/sysmacros.h> #endif
|
||||
#
|
||||
include(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake)
|
||||
|
||||
check_include_file("sys/mkdev.h" HAVE_SYS_MKDEV_H)
|
||||
check_include_file("sys/sysmacros.h" HAVE_SYS_SYSMACROS_H)
|
||||
|
||||
if(HAVE_SYS_MKDEV_H)
|
||||
add_definitions(-DHAVE_SYS_MKDEV_H)
|
||||
endif()
|
||||
if(HAVE_SYS_SYSMACROS_H)
|
||||
add_definitions(-DHAVE_SYS_SYSMACROS_H)
|
||||
endif()
|
||||
@@ -26,8 +26,8 @@ else()
|
||||
# In case you want to test against another driver version (or branch, or commit) just pass the variable -
|
||||
# ie., `cmake -DDRIVER_VERSION=dev ..`
|
||||
if(NOT DRIVER_VERSION)
|
||||
set(DRIVER_VERSION "79f9664cde383950bc084ca1d4230afe79509242")
|
||||
# set(DRIVER_CHECKSUM "SHA256=4d390bdde2c061491cb73d5703a2e0db7bd681a4738b4a9e50252fff3628dd29")
|
||||
set(DRIVER_VERSION "4.0.0+driver")
|
||||
set(DRIVER_CHECKSUM "SHA256=0f71a4e4492847ce6ca35fe6f9ecdf682f603c878397e57d7628a0cd60a29aed")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
|
||||
@@ -27,8 +27,8 @@ else()
|
||||
# In case you want to test against another falcosecurity/libs version (or branch, or commit) just pass the variable -
|
||||
# ie., `cmake -DFALCOSECURITY_LIBS_VERSION=dev ..`
|
||||
if(NOT FALCOSECURITY_LIBS_VERSION)
|
||||
set(FALCOSECURITY_LIBS_VERSION "79f9664cde383950bc084ca1d4230afe79509242")
|
||||
# set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=4d390bdde2c061491cb73d5703a2e0db7bd681a4738b4a9e50252fff3628dd29")
|
||||
set(FALCOSECURITY_LIBS_VERSION "0.10.4")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=30c5c846b6336d51473bb73bc0e6c18f91dd931e346ae34f18ad7ad4a5b904a2")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
|
||||
@@ -1,34 +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.
|
||||
#
|
||||
|
||||
#
|
||||
# nlohmann-json
|
||||
#
|
||||
if(NJSON_INCLUDE)
|
||||
# Adding the custom target we can use it with `add_dependencies()`
|
||||
if(NOT TARGET njson)
|
||||
add_custom_target(njson)
|
||||
endif()
|
||||
else()
|
||||
# We always use the bundled version
|
||||
set(NJSON_SRC "${PROJECT_BINARY_DIR}/njson-prefix/src/njson")
|
||||
set(NJSON_INCLUDE "${NJSON_SRC}/single_include")
|
||||
ExternalProject_Add(
|
||||
njson
|
||||
URL "https://github.com/nlohmann/json/archive/v3.3.0.tar.gz"
|
||||
URL_HASH "SHA256=2fd1d207b4669a7843296c41d3b6ac5b23d00dec48dba507ba051d14564aa801"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
message(STATUS "Using bundled nlohmann-json in '${NJSON_SRC}'")
|
||||
endif()
|
||||
@@ -19,7 +19,6 @@ if(NOT USE_BUNDLED_DEPS)
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system yamlcpp")
|
||||
endif()
|
||||
add_custom_target(yamlcpp)
|
||||
else()
|
||||
set(YAMLCPP_SRC "${PROJECT_BINARY_DIR}/yamlcpp-prefix/src/yamlcpp")
|
||||
message(STATUS "Using bundled yaml-cpp in '${YAMLCPP_SRC}'")
|
||||
|
||||
@@ -29,7 +29,8 @@ RUN source scl_source enable devtoolset-9; \
|
||||
make falco -j${MAKE_JOBS}
|
||||
RUN make package
|
||||
|
||||
# We need `make all` for integration tests.
|
||||
# We need `make tests` and `make all` for integration tests.
|
||||
RUN make tests -j${MAKE_JOBS}
|
||||
RUN make all -j${MAKE_JOBS}
|
||||
|
||||
FROM scratch AS export-stage
|
||||
|
||||
102
falco.yaml
102
falco.yaml
@@ -170,13 +170,6 @@ syscall_event_drops:
|
||||
syscall_event_timeouts:
|
||||
max_consecutives: 1000
|
||||
|
||||
# Enabling this option allows Falco to drop failed syscalls exit events
|
||||
# in the kernel driver before the event is pushed onto the ring buffer.
|
||||
# This can enable some small optimization both in CPU usage and ring buffer usage,
|
||||
# possibly leading to lower number of event losses.
|
||||
# Be careful: enabling it also means losing a bit of visibility on the system.
|
||||
syscall_drop_failed_exit: false
|
||||
|
||||
# --- [Description]
|
||||
#
|
||||
# This is an index that controls the dimension of the syscall buffers.
|
||||
@@ -408,15 +401,6 @@ http_output:
|
||||
enabled: false
|
||||
url: http://some.url
|
||||
user_agent: "falcosecurity/falco"
|
||||
# Tell Falco to not verify the remote server.
|
||||
insecure: false
|
||||
# Path to the CA certificate that can verify the remote server.
|
||||
ca_cert: ""
|
||||
# Path to a specific file that will be used as the CA certificate store.
|
||||
ca_bundle: ""
|
||||
# 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"
|
||||
|
||||
# Falco supports running a gRPC server with two main binding types
|
||||
# 1. Over the network with mandatory mutual TLS authentication (mTLS)
|
||||
@@ -458,89 +442,3 @@ metadata_download:
|
||||
max_mb: 100
|
||||
chunk_wait_us: 1000
|
||||
watch_freq_sec: 1
|
||||
|
||||
|
||||
# base_syscalls ! Use with caution, read carefully !
|
||||
#
|
||||
# --- [Description]
|
||||
#
|
||||
# With this option you are in full control of the set of syscalls that
|
||||
# Falco will enable in the kernel for active tracing.
|
||||
|
||||
# All syscalls and events from each enabled Falco rule are activated
|
||||
# even when choosing this option. This option allows you to define a
|
||||
# set of base syscalls that will be activated in addition to the
|
||||
# syscalls defined in the rules.
|
||||
#
|
||||
# You may ask yourself why do we need to activate syscalls in addition to the rules?
|
||||
#
|
||||
# Falco requires a set of syscalls to build up state in userspace. This is because for
|
||||
# example when spawning a new process or creating a network connection more than one syscall
|
||||
# is involved. Furthermore, properties of a process during its life time can be modified
|
||||
# by syscalls. Falco takes care of this by activating more syscalls than the ones defined
|
||||
# in the rules and by managing a smart process cache table in userspace.
|
||||
# Processes are purged when a process exits.
|
||||
#
|
||||
# Looking back to what this option does, it activates all syscalls from the rules
|
||||
# (including resolved macros) and the ones specified here.
|
||||
#
|
||||
# This puts the end user in the driver seat to tell Falco what it needs, but if not used correctly
|
||||
# Falco logs may be incomplete or wrong or Falco won't work at all. This option however can be
|
||||
# very useful to lower CPU utilization and allowing you to tailor Falco to specific environments
|
||||
# according to your organization's threat model and cost budget.
|
||||
#
|
||||
# !!! When NOT using this option, Falco defaults to adding a static (more verbose) set of syscalls
|
||||
# in addition to the rules system calls Falco needs for its state engine build-up and life-cycle management.
|
||||
#
|
||||
# `base_syscalls.repair` is an experimental alternative to Falco's default state engine enforcement.
|
||||
# `base_syscalls.repair` is designed to be the most resourceful option to ensure Falco runs correctly
|
||||
# while activating a most minimal set of additional syscalls. The recommendations listed in the suggestions
|
||||
# section is effectively what `base_syscalls.repair` is doing in an automated manner. `base_syscalls.repair`
|
||||
# can be used with an empty custom set.
|
||||
#
|
||||
# --- [Usage]
|
||||
#
|
||||
# List of system calls names (<syscall-name>) plus negative ("!<syscall-name>") notation supported.
|
||||
#
|
||||
# base_syscalls.repair: <bool>
|
||||
# base_syscalls.custom_set: [<syscall-name>, <syscall-name>, "!<syscall-name>"]
|
||||
#
|
||||
#
|
||||
# --- [Suggestions]
|
||||
#
|
||||
# Here are a few recommendations that may help you.
|
||||
# Setting `base_syscalls.repair: true` automates these recommendations for you.
|
||||
#
|
||||
# Consider to at minimum add the following syscalls regardless of the syscalls used in the rules.
|
||||
#
|
||||
# [clone, clone3, fork, vfork, execve, execveat, close]
|
||||
#
|
||||
# This is because some Falco fields for an execve* system call are retrieved
|
||||
# from the associated `clone`, `clone3`, `fork`, `vfork` syscall when spawning a
|
||||
# new process. The `close` system call is used to purge file descriptors from Falco's
|
||||
# internal thread / process cache table and should always be added when you have
|
||||
# rules around file descriptors.
|
||||
# (e.g. open, openat, openat2, socket, connect, accept, accept4 ... and many more)
|
||||
#
|
||||
# When network syscalls are used in rules we recommend to at minimum set
|
||||
#
|
||||
# [clone, clone3, fork, vfork, execve, execveat, close, socket, bind, getsockopt]
|
||||
#
|
||||
# It turns out that while you can log `connect` or `accept*` syscalls without the
|
||||
# socket system call, the log however would not contain the ip tuples.
|
||||
# For `listen` and `accept*` system calls you also need the `bind` system call.
|
||||
#
|
||||
# Lastly, if you care about the correct `uid`, `gid` or `sid`, `pgid` of a process when the
|
||||
# running process opens a file or makes a network connection, consider adding the following syscalls:
|
||||
#
|
||||
# setresuid, setsid, setuid, setgid, setpgid, setresgid, setsid, capset, chdir, chroot, fchdir
|
||||
#
|
||||
# We recommend to only exclude syscalls, e.g. "!mprotect" if you need a fast deployment update
|
||||
# (overriding rules), else remove unwanted syscalls from the Falco rules.
|
||||
#
|
||||
# Passing `-o "log_level=debug"` to Falco's cmd args during a dry-run will print the
|
||||
# final set of syscalls to STDOUT.
|
||||
|
||||
base_syscalls:
|
||||
repair: false
|
||||
custom_set: []
|
||||
|
||||
Submodule submodules/falcosecurity-rules updated: 694adf59e0...c558fc7d2d
@@ -624,7 +624,7 @@ trace_files: !mux
|
||||
|
||||
disabled_and_enabled_rules_1:
|
||||
exit_status: 1
|
||||
stderr_contains: "Error: You can not specify both disabled .-D/-T. and enabled .-t. rules"
|
||||
stderr_contains: "Runtime error: You can not specify both disabled .-D/-T. and enabled .-t. rules. Exiting."
|
||||
disable_tags: [a]
|
||||
run_tags: [a]
|
||||
rules_file:
|
||||
@@ -633,7 +633,7 @@ trace_files: !mux
|
||||
|
||||
disabled_and_enabled_rules_2:
|
||||
exit_status: 1
|
||||
stderr_contains: "Error: You can not specify both disabled .-D/-T. and enabled .-t. rules"
|
||||
stderr_contains: "Runtime error: You can not specify both disabled .-D/-T. and enabled .-t. rules. Exiting."
|
||||
disabled_rules:
|
||||
- "open.*"
|
||||
run_tags: [a]
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <engine/source_plugin/plugin_info.h>
|
||||
#include <plugin_info.h>
|
||||
|
||||
static const char *pl_required_api_version = PLUGIN_API_VERSION_STR;
|
||||
static const char *pl_name_base = "test_extract";
|
||||
|
||||
@@ -18,7 +18,7 @@ limitations under the License.
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <engine/source_plugin/plugin_info.h>
|
||||
#include <plugin_info.h>
|
||||
|
||||
static const char *pl_required_api_version = PLUGIN_API_VERSION_STR;
|
||||
static uint32_t pl_id = 999;
|
||||
|
||||
76
tests/CMakeLists.txt
Normal file
76
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,76 @@
|
||||
#
|
||||
# Copyright (C) 2019 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
set(
|
||||
FALCO_TESTS_SOURCES
|
||||
test_base.cpp
|
||||
engine/test_rulesets.cpp
|
||||
engine/test_falco_utils.cpp
|
||||
engine/test_filter_macro_resolver.cpp
|
||||
engine/test_filter_evttype_resolver.cpp
|
||||
engine/test_filter_warning_resolver.cpp
|
||||
engine/test_plugin_requirements.cpp
|
||||
falco/test_yaml_helper.cpp
|
||||
)
|
||||
|
||||
set(FALCO_TESTED_LIBRARIES falco_engine ${YAMLCPP_LIB})
|
||||
|
||||
SET(FALCO_TESTS_ARGUMENTS "" CACHE STRING "Test arguments to pass to the Falco test suite")
|
||||
|
||||
option(FALCO_BUILD_TESTS "Determines whether to build tests." ON)
|
||||
|
||||
if(FALCO_BUILD_TESTS)
|
||||
enable_testing()
|
||||
if(NOT TARGET catch)
|
||||
include(DownloadCatch)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET fakeit)
|
||||
include(DownloadFakeIt)
|
||||
endif()
|
||||
|
||||
add_executable(falco_test ${FALCO_TESTS_SOURCES})
|
||||
|
||||
target_link_libraries(falco_test PUBLIC ${FALCO_TESTED_LIBRARIES})
|
||||
|
||||
if(MINIMAL_BUILD)
|
||||
target_include_directories(
|
||||
falco_test
|
||||
PUBLIC "${CATCH2_INCLUDE}"
|
||||
"${FAKEIT_INCLUDE}"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/engine"
|
||||
"${PROJECT_BINARY_DIR}/userspace/falco"
|
||||
"${YAMLCPP_INCLUDE_DIR}"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/falco")
|
||||
else()
|
||||
target_include_directories(
|
||||
falco_test
|
||||
PUBLIC "${CATCH2_INCLUDE}"
|
||||
"${FAKEIT_INCLUDE}"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/engine"
|
||||
"${PROJECT_BINARY_DIR}/userspace/falco"
|
||||
"${YAMLCPP_INCLUDE_DIR}"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/falco")
|
||||
endif()
|
||||
add_dependencies(falco_test catch2)
|
||||
|
||||
include(CMakeParseArguments)
|
||||
include(CTest)
|
||||
include(Catch)
|
||||
catch_discover_tests(falco_test)
|
||||
separate_arguments(FALCO_TESTS_ARGUMENTS)
|
||||
add_custom_target(tests COMMAND ${CMAKE_CTEST_COMMAND} ${FALCO_TESTS_ARGUMENTS} DEPENDS falco_test)
|
||||
endif()
|
||||
2
tests/OWNERS
Normal file
2
tests/OWNERS
Normal file
@@ -0,0 +1,2 @@
|
||||
labels:
|
||||
- area/tests
|
||||
57
tests/README.md
Normal file
57
tests/README.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Falco unit tests
|
||||
|
||||
This folder contains the unit-tests suite for Falco.
|
||||
The framework we use for unit-tests is [Catch2](https://github.com/catchorg/Catch2), while the one we use for mocking is [FakeIt](https://github.com/eranpeer/FakeIt).
|
||||
|
||||
|
||||
## How to write tests
|
||||
|
||||
When you want to test a new file or test a non tested file, remember four steps:
|
||||
|
||||
- The folder structure here is the same as the one in the `userspace` folder, so `userspace/engine` becomes `tests/engine`.
|
||||
- We call test files with this format `test_<original-file-name>.cpp`
|
||||
- Update the `CMakeLists.txt` file to include your file in `FALCO_TESTS_SOURCES` and change the `FALCO_TESTED_LIBRARIES` accordingly. You might also need to add dependencies, in that case, look at `target_link_libraries` and `target_include_directories`
|
||||
- If you are unsure on how to write tests, refer to our existing tests in this folder and to the [Catch2](https://github.com/catchorg/Catch2/tree/master/docs) documentation.
|
||||
|
||||
## How to execute tests
|
||||
|
||||
The suite can be configured with `cmake` and run with `make`.
|
||||
|
||||
|
||||
In the root folder of Falco, after creating the build directory:
|
||||
|
||||
```bash
|
||||
cd falco
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
|
||||
You can prepare the tests with:
|
||||
|
||||
```
|
||||
cmake ..
|
||||
```
|
||||
|
||||
Optionally, you can customize the test suite by passing custom arguments like the examples below:
|
||||
|
||||
**filter all tests containing the word ctor**
|
||||
|
||||
```bash
|
||||
cmake -DFALCO_TESTS_ARGUMENTS:STRING="-R ctor" ..
|
||||
```
|
||||
|
||||
**verbose execution**
|
||||
|
||||
```bash
|
||||
cmake -DFALCO_TESTS_ARGUMENTS:STRING="-V" ..
|
||||
```
|
||||
|
||||
|
||||
To see a list of all the custom arguments you may pass, execute `ctest --help` in your terminal.
|
||||
|
||||
|
||||
Once you are ready, you can run your configuration with:
|
||||
|
||||
```bash
|
||||
make tests
|
||||
```
|
||||
52
tests/engine/test_falco_utils.cpp
Normal file
52
tests/engine/test_falco_utils.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright (C) 2020 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#include "falco_utils.h"
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("is_unix_scheme matches", "[utils]")
|
||||
{
|
||||
SECTION("rvalue")
|
||||
{
|
||||
bool res = falco::utils::network::is_unix_scheme("unix:///run/falco/falco.sock");
|
||||
REQUIRE(res);
|
||||
}
|
||||
|
||||
SECTION("std::string")
|
||||
{
|
||||
std::string url("unix:///run/falco/falco.sock");
|
||||
bool res = falco::utils::network::is_unix_scheme(url);
|
||||
REQUIRE(res);
|
||||
}
|
||||
|
||||
SECTION("char[]")
|
||||
{
|
||||
char url[] = "unix:///run/falco/falco.sock";
|
||||
bool res = falco::utils::network::is_unix_scheme(url);
|
||||
REQUIRE(res);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("is_unix_scheme does not match", "[utils]")
|
||||
{
|
||||
bool res = falco::utils::network::is_unix_scheme("something:///run/falco/falco.sock");
|
||||
REQUIRE_FALSE(res);
|
||||
}
|
||||
|
||||
TEST_CASE("is_unix_scheme only matches scheme at the start of the string", "[utils]")
|
||||
{
|
||||
bool res = falco::utils::network::is_unix_scheme("/var/run/unix:///falco.sock");
|
||||
REQUIRE_FALSE(res);
|
||||
}
|
||||
237
tests/engine/test_filter_evttype_resolver.cpp
Normal file
237
tests/engine/test_filter_evttype_resolver.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
Copyright (C) 2021 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 "filter_evttype_resolver.h"
|
||||
#include <catch.hpp>
|
||||
#include <sinsp.h>
|
||||
#include <filter/parser.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace libsinsp::filter;
|
||||
|
||||
string to_string(set<uint16_t> s)
|
||||
{
|
||||
string out = "[";
|
||||
for(auto &val : s)
|
||||
{
|
||||
out += out.size() == 1 ? "" : ", ";
|
||||
out += to_string(val);
|
||||
}
|
||||
out += "]";
|
||||
return out;
|
||||
}
|
||||
|
||||
void compare_evttypes(std::unique_ptr<ast::expr> f, set<uint16_t> &expected)
|
||||
{
|
||||
set<uint16_t> actual;
|
||||
filter_evttype_resolver().evttypes(f.get(), actual);
|
||||
for(auto &etype : expected)
|
||||
{
|
||||
REQUIRE(actual.find(etype) != actual.end());
|
||||
}
|
||||
for(auto &etype : actual)
|
||||
{
|
||||
REQUIRE(expected.find(etype) != expected.end());
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ast::expr> compile(const string &fltstr)
|
||||
{
|
||||
return libsinsp::filter::parser(fltstr).parse();
|
||||
}
|
||||
|
||||
TEST_CASE("Should find event types from filter", "[rule_loader]")
|
||||
{
|
||||
set<uint16_t> openat_only{
|
||||
PPME_SYSCALL_OPENAT_E, PPME_SYSCALL_OPENAT_X,
|
||||
PPME_SYSCALL_OPENAT_2_E, PPME_SYSCALL_OPENAT_2_X };
|
||||
|
||||
set<uint16_t> close_only{
|
||||
PPME_SYSCALL_CLOSE_E, PPME_SYSCALL_CLOSE_X };
|
||||
|
||||
set<uint16_t> openat_close{
|
||||
PPME_SYSCALL_OPENAT_E, PPME_SYSCALL_OPENAT_X,
|
||||
PPME_SYSCALL_OPENAT_2_E, PPME_SYSCALL_OPENAT_2_X,
|
||||
PPME_SYSCALL_CLOSE_E, PPME_SYSCALL_CLOSE_X };
|
||||
|
||||
set<uint16_t> not_openat;
|
||||
set<uint16_t> not_openat_close;
|
||||
set<uint16_t> not_close;
|
||||
set<uint16_t> all_events;
|
||||
set<uint16_t> no_events;
|
||||
|
||||
for(uint32_t i = 2; i < PPM_EVENT_MAX; i++)
|
||||
{
|
||||
// Skip events that are unused.
|
||||
if(sinsp::is_unused_event(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
all_events.insert(i);
|
||||
if(openat_only.find(i) == openat_only.end())
|
||||
{
|
||||
not_openat.insert(i);
|
||||
}
|
||||
if(openat_close.find(i) == openat_close.end())
|
||||
{
|
||||
not_openat_close.insert(i);
|
||||
}
|
||||
if (close_only.find(i) == close_only.end())
|
||||
{
|
||||
not_close.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("evt_type_eq")
|
||||
{
|
||||
auto f = compile("evt.type=openat");
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_in")
|
||||
{
|
||||
auto f = compile("evt.type in (openat, close)");
|
||||
compare_evttypes(std::move(f), openat_close);
|
||||
}
|
||||
|
||||
SECTION("evt_type_ne")
|
||||
{
|
||||
auto f = compile("evt.type!=openat");
|
||||
compare_evttypes(std::move(f), not_openat);
|
||||
}
|
||||
|
||||
SECTION("not_evt_type_eq")
|
||||
{
|
||||
auto f = compile("not evt.type=openat");
|
||||
compare_evttypes(std::move(f), not_openat);
|
||||
}
|
||||
|
||||
SECTION("not_evt_type_in")
|
||||
{
|
||||
auto f = compile("not evt.type in (openat, close)");
|
||||
compare_evttypes(std::move(f), not_openat_close);
|
||||
}
|
||||
|
||||
SECTION("not_evt_type_ne")
|
||||
{
|
||||
auto f = compile("not evt.type != openat");
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_or")
|
||||
{
|
||||
auto f = compile("evt.type=openat or evt.type=close");
|
||||
compare_evttypes(std::move(f), openat_close);
|
||||
}
|
||||
|
||||
SECTION("not_evt_type_or")
|
||||
{
|
||||
auto f = compile("evt.type!=openat or evt.type!=close");
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("evt_type_or_ne")
|
||||
{
|
||||
auto f = compile("evt.type=close or evt.type!=openat");
|
||||
compare_evttypes(std::move(f), not_openat);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and")
|
||||
{
|
||||
auto f = compile("evt.type=close and evt.type=openat");
|
||||
compare_evttypes(std::move(f), no_events);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and_non_evt_type")
|
||||
{
|
||||
auto f = compile("evt.type=openat and proc.name=nginx");
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and_non_evt_type_not")
|
||||
{
|
||||
auto f = compile("evt.type=openat and not proc.name=nginx");
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and_nested")
|
||||
{
|
||||
auto f = compile("evt.type=openat and (proc.name=nginx)");
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("evt_type_and_nested_multi")
|
||||
{
|
||||
auto f = compile("evt.type=openat and (evt.type=close and proc.name=nginx)");
|
||||
compare_evttypes(std::move(f), no_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type")
|
||||
{
|
||||
auto f = compile("proc.name=nginx");
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or")
|
||||
{
|
||||
auto f = compile("evt.type=openat or proc.name=nginx");
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or_nested_first")
|
||||
{
|
||||
auto f = compile("(evt.type=openat) or proc.name=nginx");
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or_nested_second")
|
||||
{
|
||||
auto f = compile("evt.type=openat or (proc.name=nginx)");
|
||||
compare_evttypes(std::move(f), all_events);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or_nested_multi")
|
||||
{
|
||||
auto f = compile("evt.type=openat or (evt.type=close and proc.name=nginx)");
|
||||
compare_evttypes(std::move(f), openat_close);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_or_nested_multi_not")
|
||||
{
|
||||
auto f = compile("evt.type=openat or not (evt.type=close and proc.name=nginx)");
|
||||
compare_evttypes(std::move(f), not_close);
|
||||
}
|
||||
|
||||
SECTION("non_evt_type_and_nested_multi_not")
|
||||
{
|
||||
auto f = compile("evt.type=openat and not (evt.type=close and proc.name=nginx)");
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
|
||||
SECTION("ne_and_and")
|
||||
{
|
||||
auto f = compile("evt.type!=openat and evt.type!=close");
|
||||
compare_evttypes(std::move(f), not_openat_close);
|
||||
}
|
||||
|
||||
SECTION("not_not")
|
||||
{
|
||||
auto f = compile("not (not evt.type=openat)");
|
||||
compare_evttypes(std::move(f), openat_only);
|
||||
}
|
||||
}
|
||||
298
tests/engine/test_filter_macro_resolver.cpp
Normal file
298
tests/engine/test_filter_macro_resolver.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
Copyright (C) 2020 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "filter_macro_resolver.h"
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace libsinsp::filter::ast;
|
||||
|
||||
static std::vector<filter_macro_resolver::value_info>::const_iterator find_value(
|
||||
const std::vector<filter_macro_resolver::value_info>& values,
|
||||
const std::string& ref)
|
||||
{
|
||||
return std::find_if(
|
||||
values.begin(),
|
||||
values.end(),
|
||||
[&ref](const filter_macro_resolver::value_info& v) { return v.first == ref; });
|
||||
}
|
||||
|
||||
TEST_CASE("Should resolve macros on a filter AST", "[rule_loader]")
|
||||
{
|
||||
string macro_name = "test_macro";
|
||||
pos_info macro_pos(12, 85, 27);
|
||||
|
||||
SECTION("in the general case")
|
||||
{
|
||||
std::shared_ptr<expr> macro = std::move(
|
||||
unary_check_expr::create("test.field", "", "exists"));
|
||||
|
||||
std::vector<std::unique_ptr<expr>> filter_and;
|
||||
filter_and.push_back(unary_check_expr::create("evt.name", "", "exists"));
|
||||
filter_and.push_back(not_expr::create(value_expr::create(macro_name, macro_pos)));
|
||||
std::shared_ptr<expr> filter = std::move(and_expr::create(filter_and));
|
||||
|
||||
std::vector<std::unique_ptr<expr>> expected_and;
|
||||
expected_and.push_back(unary_check_expr::create("evt.name", "", "exists"));
|
||||
expected_and.push_back(not_expr::create(clone(macro.get())));
|
||||
std::shared_ptr<expr> expected = std::move(and_expr::create(expected_and));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(macro_name, macro);
|
||||
|
||||
// first run
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == macro_pos);
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected.get()));
|
||||
|
||||
// second run
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected.get()));
|
||||
}
|
||||
|
||||
SECTION("with a single node")
|
||||
{
|
||||
std::shared_ptr<expr> macro = std::move(
|
||||
unary_check_expr::create("test.field", "", "exists"));
|
||||
|
||||
std::shared_ptr<expr> filter = std::move(value_expr::create(macro_name, macro_pos));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(macro_name, macro);
|
||||
|
||||
// first run
|
||||
expr* old_filter_ptr = filter.get();
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(filter.get() != old_filter_ptr);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == macro_pos);
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(macro.get()));
|
||||
|
||||
// second run
|
||||
old_filter_ptr = filter.get();
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(filter.get() == old_filter_ptr);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(macro.get()));
|
||||
}
|
||||
|
||||
SECTION("with multiple macros")
|
||||
{
|
||||
string a_macro_name = macro_name + "_1";
|
||||
string b_macro_name = macro_name + "_2";
|
||||
|
||||
pos_info a_macro_pos(11, 75, 43);
|
||||
pos_info b_macro_pos(91, 21, 9);
|
||||
|
||||
std::shared_ptr<expr> a_macro = std::move(
|
||||
unary_check_expr::create("one.field", "", "exists"));
|
||||
std::shared_ptr<expr> b_macro = std::move(
|
||||
unary_check_expr::create("another.field", "", "exists"));
|
||||
|
||||
std::vector<std::unique_ptr<expr>> filter_or;
|
||||
filter_or.push_back(value_expr::create(a_macro_name, a_macro_pos));
|
||||
filter_or.push_back(value_expr::create(b_macro_name, b_macro_pos));
|
||||
std::shared_ptr<expr> filter = std::move(or_expr::create(filter_or));
|
||||
|
||||
std::vector<std::unique_ptr<expr>> expected_or;
|
||||
expected_or.push_back(clone(a_macro.get()));
|
||||
expected_or.push_back(clone(b_macro.get()));
|
||||
std::shared_ptr<expr> expected_filter = std::move(or_expr::create(expected_or));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(a_macro_name, a_macro);
|
||||
resolver.set_macro(b_macro_name, b_macro);
|
||||
|
||||
// first run
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 2);
|
||||
auto a_resolved_itr = find_value(resolver.get_resolved_macros(), a_macro_name);
|
||||
REQUIRE(a_resolved_itr != resolver.get_resolved_macros().end());
|
||||
REQUIRE(a_resolved_itr->first == a_macro_name);
|
||||
REQUIRE(a_resolved_itr->second == a_macro_pos);
|
||||
|
||||
auto b_resolved_itr = find_value(resolver.get_resolved_macros(), b_macro_name);
|
||||
REQUIRE(b_resolved_itr != resolver.get_resolved_macros().end());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(b_resolved_itr->first == b_macro_name);
|
||||
REQUIRE(b_resolved_itr->second == b_macro_pos);
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
|
||||
// second run
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
}
|
||||
|
||||
SECTION("with nested macros")
|
||||
{
|
||||
string a_macro_name = macro_name + "_1";
|
||||
string b_macro_name = macro_name + "_2";
|
||||
|
||||
pos_info a_macro_pos(47, 1, 76);
|
||||
pos_info b_macro_pos(111, 65, 2);
|
||||
|
||||
std::vector<std::unique_ptr<expr>> a_macro_and;
|
||||
a_macro_and.push_back(unary_check_expr::create("one.field", "", "exists"));
|
||||
a_macro_and.push_back(value_expr::create(b_macro_name, b_macro_pos));
|
||||
std::shared_ptr<expr> a_macro = std::move(and_expr::create(a_macro_and));
|
||||
|
||||
std::shared_ptr<expr> b_macro = std::move(
|
||||
unary_check_expr::create("another.field", "", "exists"));
|
||||
|
||||
std::shared_ptr<expr> filter = std::move(value_expr::create(a_macro_name, a_macro_pos));
|
||||
|
||||
std::vector<std::unique_ptr<expr>> expected_and;
|
||||
expected_and.push_back(unary_check_expr::create("one.field", "", "exists"));
|
||||
expected_and.push_back(unary_check_expr::create("another.field", "", "exists"));
|
||||
std::shared_ptr<expr> expected_filter = std::move(and_expr::create(expected_and));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(a_macro_name, a_macro);
|
||||
resolver.set_macro(b_macro_name, b_macro);
|
||||
|
||||
// first run
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 2);
|
||||
auto a_resolved_itr = find_value(resolver.get_resolved_macros(), a_macro_name);
|
||||
REQUIRE(a_resolved_itr != resolver.get_resolved_macros().end());
|
||||
REQUIRE(a_resolved_itr->first == a_macro_name);
|
||||
REQUIRE(a_resolved_itr->second == a_macro_pos);
|
||||
|
||||
auto b_resolved_itr = find_value(resolver.get_resolved_macros(), b_macro_name);
|
||||
REQUIRE(b_resolved_itr != resolver.get_resolved_macros().end());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(b_resolved_itr->first == b_macro_name);
|
||||
REQUIRE(b_resolved_itr->second == b_macro_pos);
|
||||
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
|
||||
// second run
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Should find unknown macros", "[rule_loader]")
|
||||
{
|
||||
string macro_name = "test_macro";
|
||||
pos_info macro_pos(9, 4, 2);
|
||||
|
||||
SECTION("in the general case")
|
||||
{
|
||||
std::vector<std::unique_ptr<expr>> filter_and;
|
||||
filter_and.push_back(unary_check_expr::create("evt.name", "", "exists"));
|
||||
filter_and.push_back(not_expr::create(value_expr::create(macro_name, macro_pos)));
|
||||
std::shared_ptr<expr> filter = std::move(and_expr::create(filter_and));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
REQUIRE(resolver.run(filter) == false);
|
||||
REQUIRE(resolver.get_unknown_macros().size() == 1);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->second == macro_pos);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
}
|
||||
|
||||
SECTION("with nested macros")
|
||||
{
|
||||
string a_macro_name = macro_name + "_1";
|
||||
string b_macro_name = macro_name + "_2";
|
||||
|
||||
pos_info a_macro_pos(32, 84, 9);
|
||||
pos_info b_macro_pos(1, 0, 5);
|
||||
|
||||
std::vector<std::unique_ptr<expr>> a_macro_and;
|
||||
a_macro_and.push_back(unary_check_expr::create("one.field", "", "exists"));
|
||||
a_macro_and.push_back(value_expr::create(b_macro_name, b_macro_pos));
|
||||
std::shared_ptr<expr> a_macro = std::move(and_expr::create(a_macro_and));
|
||||
|
||||
std::shared_ptr<expr> filter = std::move(value_expr::create(a_macro_name, a_macro_pos));
|
||||
auto expected_filter = clone(a_macro.get());
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(a_macro_name, a_macro);
|
||||
|
||||
// first run
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == a_macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == a_macro_pos);
|
||||
REQUIRE(resolver.get_unknown_macros().size() == 1);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->first == b_macro_name);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->second == b_macro_pos);
|
||||
REQUIRE(filter->is_equal(expected_filter.get()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Should undefine macro", "[rule_loader]")
|
||||
{
|
||||
string macro_name = "test_macro";
|
||||
pos_info macro_pos_1(12, 9, 3);
|
||||
pos_info macro_pos_2(9, 6, 3);
|
||||
|
||||
std::shared_ptr<expr> macro = std::move(unary_check_expr::create("test.field", "", "exists"));
|
||||
std::shared_ptr<expr> a_filter = std::move(value_expr::create(macro_name, macro_pos_1));
|
||||
std::shared_ptr<expr> b_filter = std::move(value_expr::create(macro_name, macro_pos_2));
|
||||
filter_macro_resolver resolver;
|
||||
|
||||
resolver.set_macro(macro_name, macro);
|
||||
REQUIRE(resolver.run(a_filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == macro_pos_1);
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(a_filter->is_equal(macro.get()));
|
||||
|
||||
resolver.set_macro(macro_name, NULL);
|
||||
REQUIRE(resolver.run(b_filter) == false);
|
||||
REQUIRE(resolver.get_resolved_macros().empty());
|
||||
REQUIRE(resolver.get_unknown_macros().size() == 1);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_unknown_macros().begin()->second == macro_pos_2);
|
||||
}
|
||||
|
||||
// checks that the macro AST is cloned and not shared across resolved filters
|
||||
TEST_CASE("Should clone macro AST", "[rule_loader]")
|
||||
{
|
||||
string macro_name = "test_macro";
|
||||
pos_info macro_pos(5, 2, 8888);
|
||||
std::shared_ptr<unary_check_expr> macro = std::move(unary_check_expr::create("test.field", "", "exists"));
|
||||
std::shared_ptr<expr> filter = std::move(value_expr::create(macro_name, macro_pos));
|
||||
filter_macro_resolver resolver;
|
||||
|
||||
resolver.set_macro(macro_name, macro);
|
||||
REQUIRE(resolver.run(filter) == true);
|
||||
REQUIRE(resolver.get_resolved_macros().size() == 1);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->first == macro_name);
|
||||
REQUIRE(resolver.get_resolved_macros().begin()->second == macro_pos);
|
||||
REQUIRE(resolver.get_unknown_macros().empty());
|
||||
REQUIRE(filter->is_equal(macro.get()));
|
||||
|
||||
macro->field = "another.field";
|
||||
REQUIRE(!filter->is_equal(macro.get()));
|
||||
}
|
||||
45
tests/engine/test_filter_warning_resolver.cpp
Normal file
45
tests/engine/test_filter_warning_resolver.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright (C) 2020 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "filter_warning_resolver.h"
|
||||
#include <catch.hpp>
|
||||
|
||||
static bool warns(const std::string& condition)
|
||||
{
|
||||
std::set<falco::load_result::warning_code> w;
|
||||
auto ast = libsinsp::filter::parser(condition).parse();
|
||||
filter_warning_resolver().run(ast.get(), w);
|
||||
return !w.empty();
|
||||
}
|
||||
|
||||
TEST_CASE("Should spot warnings in filtering conditions", "[rule_loader]")
|
||||
{
|
||||
SECTION("for unsafe usage of <NA> in k8s audit fields")
|
||||
{
|
||||
REQUIRE(false == warns("ka.field exists"));
|
||||
REQUIRE(false == warns("some.field = <NA>"));
|
||||
REQUIRE(true == warns("jevt.field = <NA>"));
|
||||
REQUIRE(true == warns("ka.field = <NA>"));
|
||||
REQUIRE(true == warns("ka.field == <NA>"));
|
||||
REQUIRE(true == warns("ka.field != <NA>"));
|
||||
REQUIRE(true == warns("ka.field in (<NA>)"));
|
||||
REQUIRE(true == warns("ka.field in (otherval, <NA>)"));
|
||||
REQUIRE(true == warns("ka.field intersects (<NA>)"));
|
||||
REQUIRE(true == warns("ka.field intersects (otherval, <NA>)"));
|
||||
REQUIRE(true == warns("ka.field pmatch (<NA>)"));
|
||||
REQUIRE(true == warns("ka.field pmatch (otherval, <NA>)"));
|
||||
}
|
||||
}
|
||||
269
tests/engine/test_plugin_requirements.cpp
Normal file
269
tests/engine/test_plugin_requirements.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <catch.hpp>
|
||||
#include "falco_engine.h"
|
||||
|
||||
static void check_requirements(
|
||||
bool expect_success,
|
||||
const std::vector<falco_engine::plugin_version_requirement>& plugins,
|
||||
const std::string& ruleset_content)
|
||||
{
|
||||
std::string err;
|
||||
std::unique_ptr<falco_engine> e(new falco_engine());
|
||||
falco::load_result::rules_contents_t c = {{"test", ruleset_content}};
|
||||
|
||||
auto res = e->load_rules(c.begin()->second, c.begin()->first);
|
||||
if (!res->successful())
|
||||
{
|
||||
if (expect_success)
|
||||
{
|
||||
FAIL(res->as_string(false, c));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e->check_plugin_requirements(plugins, err))
|
||||
{
|
||||
if (expect_success)
|
||||
{
|
||||
FAIL(err);
|
||||
}
|
||||
}
|
||||
else if (!expect_success)
|
||||
{
|
||||
FAIL("unexpected successful plugin requirements check");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("check_plugin_requirements must accept", "[rule_loader]")
|
||||
{
|
||||
SECTION("no requirement")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.1.0"}}, "");
|
||||
}
|
||||
|
||||
SECTION("single plugin")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin newer version")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.2.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.1.0"}, {"json", "0.3.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin multiple versions")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.2.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin with alternatives")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit-other", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit-other", "0.5.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives with multiple versions")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit-other", "0.7.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 1.0.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.7.0
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("check_plugin_requirements must reject", "[rule_loader]")
|
||||
{
|
||||
SECTION("no plugin loaded")
|
||||
{
|
||||
check_requirements(false, {}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin wrong name")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit2
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin wrong version")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin multiple versions")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin with alternatives")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin with overlapping alternatives")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit
|
||||
version: 0.4.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit-other", "0.5.0"}, {"json3", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives with multiple versions")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.7.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.4.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 1.0.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.7.0
|
||||
)");
|
||||
}
|
||||
}
|
||||
242
tests/engine/test_rulesets.cpp
Normal file
242
tests/engine/test_rulesets.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
Copyright (C) 2020 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "falco_common.h"
|
||||
#include "evttype_index_ruleset.h"
|
||||
#include <filter.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
static bool exact_match = true;
|
||||
static bool substring_match = false;
|
||||
static uint16_t default_ruleset = 0;
|
||||
static uint16_t non_default_ruleset = 3;
|
||||
static uint16_t other_non_default_ruleset = 2;
|
||||
static std::set<std::string> tags = {"some_tag", "some_other_tag"};
|
||||
static std::set<uint16_t> evttypes = { ppm_event_type::PPME_GENERIC_E };
|
||||
|
||||
static std::shared_ptr<gen_event_filter_factory> create_factory()
|
||||
{
|
||||
std::shared_ptr<gen_event_filter_factory> ret(new sinsp_filter_factory(NULL));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::shared_ptr<libsinsp::filter::ast::expr> create_ast(
|
||||
std::shared_ptr<gen_event_filter_factory> f)
|
||||
{
|
||||
libsinsp::filter::parser parser("evt.type=open");
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> ret(parser.parse());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::shared_ptr<gen_event_filter> create_filter(
|
||||
std::shared_ptr<gen_event_filter_factory> f,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> ast)
|
||||
{
|
||||
sinsp_filter_compiler compiler(f, ast.get());
|
||||
std::shared_ptr<gen_event_filter> filter(compiler.compile());
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
static std::shared_ptr<filter_ruleset> create_ruleset(
|
||||
std::shared_ptr<gen_event_filter_factory> f)
|
||||
{
|
||||
std::shared_ptr<filter_ruleset> ret(new evttype_index_ruleset(f));
|
||||
return ret;
|
||||
}
|
||||
|
||||
TEST_CASE("Should enable/disable on ruleset", "[rulesets]")
|
||||
{
|
||||
auto f = create_factory();
|
||||
auto r = create_ruleset(f);
|
||||
auto ast = create_ast(f);
|
||||
auto filter = create_filter(f, ast);
|
||||
falco_rule rule;
|
||||
rule.name = "one_rule";
|
||||
rule.source = falco_common::syscall_source;
|
||||
rule.tags = tags;
|
||||
|
||||
r->add(rule, filter, ast);
|
||||
|
||||
SECTION("Should enable/disable for exact match w/ default ruleset")
|
||||
{
|
||||
r->enable("one_rule", exact_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 1);
|
||||
|
||||
r->disable("one_rule", exact_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should enable/disable for exact match w/ specific ruleset")
|
||||
{
|
||||
r->enable("one_rule", exact_match, non_default_ruleset);
|
||||
REQUIRE(r->enabled_count(non_default_ruleset) == 1);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
REQUIRE(r->enabled_count(other_non_default_ruleset) == 0);
|
||||
|
||||
r->disable("one_rule", exact_match, non_default_ruleset);
|
||||
REQUIRE(r->enabled_count(non_default_ruleset) == 0);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
REQUIRE(r->enabled_count(other_non_default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should not enable for exact match different rule name")
|
||||
{
|
||||
r->enable("some_other_rule", exact_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should enable/disable for exact match w/ substring and default ruleset")
|
||||
{
|
||||
r->enable("one_rule", substring_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 1);
|
||||
|
||||
r->disable("one_rule", substring_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should not enable for substring w/ exact_match")
|
||||
{
|
||||
r->enable("one_", exact_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should enable/disable for prefix match w/ default ruleset")
|
||||
{
|
||||
r->enable("one_", substring_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 1);
|
||||
|
||||
r->disable("one_", substring_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should enable/disable for suffix match w/ default ruleset")
|
||||
{
|
||||
r->enable("_rule", substring_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 1);
|
||||
|
||||
r->disable("_rule", substring_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should enable/disable for substring match w/ default ruleset")
|
||||
{
|
||||
r->enable("ne_ru", substring_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 1);
|
||||
|
||||
r->disable("ne_ru", substring_match, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should enable/disable for substring match w/ specific ruleset")
|
||||
{
|
||||
r->enable("ne_ru", substring_match, non_default_ruleset);
|
||||
REQUIRE(r->enabled_count(non_default_ruleset) == 1);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
REQUIRE(r->enabled_count(other_non_default_ruleset) == 0);
|
||||
|
||||
r->disable("ne_ru", substring_match, non_default_ruleset);
|
||||
REQUIRE(r->enabled_count(non_default_ruleset) == 0);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
REQUIRE(r->enabled_count(other_non_default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should enable/disable for tags w/ default ruleset")
|
||||
{
|
||||
std::set<std::string> want_tags = {"some_tag"};
|
||||
|
||||
r->enable_tags(want_tags, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 1);
|
||||
|
||||
r->disable_tags(want_tags, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should enable/disable for tags w/ specific ruleset")
|
||||
{
|
||||
std::set<std::string> want_tags = {"some_tag"};
|
||||
|
||||
r->enable_tags(want_tags, non_default_ruleset);
|
||||
REQUIRE(r->enabled_count(non_default_ruleset) == 1);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
REQUIRE(r->enabled_count(other_non_default_ruleset) == 0);
|
||||
|
||||
r->disable_tags(want_tags, non_default_ruleset);
|
||||
REQUIRE(r->enabled_count(non_default_ruleset) == 0);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
REQUIRE(r->enabled_count(other_non_default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should not enable for different tags")
|
||||
{
|
||||
std::set<std::string> want_tags = {"some_different_tag"};
|
||||
|
||||
r->enable_tags(want_tags, default_ruleset);
|
||||
REQUIRE(r->enabled_count(non_default_ruleset) == 0);
|
||||
}
|
||||
|
||||
SECTION("Should enable/disable for overlapping tags")
|
||||
{
|
||||
std::set<std::string> want_tags = {"some_tag", "some_different_tag"};
|
||||
|
||||
r->enable_tags(want_tags, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 1);
|
||||
|
||||
r->disable_tags(want_tags, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("Should enable/disable on ruleset for incremental adding tags", "[rulesets]")
|
||||
{
|
||||
auto f = create_factory();
|
||||
auto r = create_ruleset(f);
|
||||
auto ast = create_ast(f);
|
||||
|
||||
auto rule1_filter = create_filter(f, ast);
|
||||
falco_rule rule1;
|
||||
rule1.name = "one_rule";
|
||||
rule1.source = falco_common::syscall_source;
|
||||
rule1.tags = {"rule1_tag"};
|
||||
r->add(rule1, rule1_filter, ast);
|
||||
|
||||
auto rule2_filter = create_filter(f, ast);
|
||||
falco_rule rule2;
|
||||
rule2.name = "two_rule";
|
||||
rule2.source = falco_common::syscall_source;
|
||||
rule2.tags = {"rule2_tag"};
|
||||
r->add(rule2, rule2_filter, ast);
|
||||
|
||||
std::set<std::string> want_tags;
|
||||
|
||||
want_tags = rule1.tags;
|
||||
r->enable_tags(want_tags, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 1);
|
||||
|
||||
want_tags = rule2.tags;
|
||||
r->enable_tags(want_tags, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 2);
|
||||
|
||||
r->disable_tags(want_tags, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 1);
|
||||
|
||||
want_tags = rule1.tags;
|
||||
r->disable_tags(want_tags, default_ruleset);
|
||||
REQUIRE(r->enabled_count(default_ruleset) == 0);
|
||||
}
|
||||
106
tests/falco/test_yaml_helper.cpp
Normal file
106
tests/falco/test_yaml_helper.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright (C) 2021 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 "configuration.h"
|
||||
#include <catch.hpp>
|
||||
|
||||
string sample_yaml =
|
||||
"base_value:\n"
|
||||
" id: 1\n"
|
||||
" name: 'sample_name'\n"
|
||||
" subvalue:\n"
|
||||
" subvalue2:\n"
|
||||
" boolean: true\n"
|
||||
"base_value_2:\n"
|
||||
" sample_list:\n"
|
||||
" - elem1\n"
|
||||
" - elem2\n"
|
||||
" - elem3\n"
|
||||
;
|
||||
|
||||
TEST_CASE("configuration must load YAML data", "[configuration]")
|
||||
{
|
||||
yaml_helper conf;
|
||||
|
||||
SECTION("broken YAML")
|
||||
{
|
||||
string sample_broken_yaml = sample_yaml + " / bad_symbol";
|
||||
REQUIRE_THROWS(conf.load_from_string(sample_broken_yaml));
|
||||
}
|
||||
|
||||
SECTION("valid YAML")
|
||||
{
|
||||
REQUIRE_NOTHROW(conf.load_from_string(sample_yaml));
|
||||
}
|
||||
|
||||
SECTION("clearing and reloading")
|
||||
{
|
||||
conf.load_from_string(sample_yaml);
|
||||
REQUIRE(conf.is_defined("base_value") == true);
|
||||
conf.clear();
|
||||
REQUIRE(conf.is_defined("base_value") == false);
|
||||
conf.load_from_string(sample_yaml);
|
||||
REQUIRE(conf.is_defined("base_value") == true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("configuration must read YAML fields", "[configuration]")
|
||||
{
|
||||
yaml_helper conf;
|
||||
conf.load_from_string(sample_yaml);
|
||||
|
||||
SECTION("base level")
|
||||
{
|
||||
REQUIRE(conf.is_defined("base_value") == true);
|
||||
REQUIRE(conf.is_defined("base_value_2") == true);
|
||||
REQUIRE(conf.is_defined("unknown_base_value") == false);
|
||||
}
|
||||
|
||||
SECTION("arbitrary depth nesting")
|
||||
{
|
||||
REQUIRE(conf.get_scalar<int>("base_value.id", -1) == 1);
|
||||
REQUIRE(conf.get_scalar<string>("base_value.name", "none") == "sample_name");
|
||||
REQUIRE(conf.get_scalar<bool>("base_value.subvalue.subvalue2.boolean", false) == true);
|
||||
}
|
||||
|
||||
SECTION("list field elements")
|
||||
{
|
||||
REQUIRE(conf.get_scalar<string>("base_value_2.sample_list[0]", "none") == "elem1");
|
||||
REQUIRE(conf.get_scalar<string>("base_value_2.sample_list[1]", "none") == "elem2");
|
||||
REQUIRE(conf.get_scalar<string>("base_value_2.sample_list[2]", "none") == "elem3");
|
||||
}
|
||||
|
||||
SECTION("sequence")
|
||||
{
|
||||
vector<string> seq;
|
||||
conf.get_sequence(seq, "base_value_2.sample_list");
|
||||
REQUIRE(seq.size() == 3);
|
||||
REQUIRE(seq[0] == "elem1");
|
||||
REQUIRE(seq[1] == "elem2");
|
||||
REQUIRE(seq[2] == "elem3");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("configuration must modify YAML fields", "[configuration]")
|
||||
{
|
||||
string key = "base_value.subvalue.subvalue2.boolean";
|
||||
yaml_helper conf;
|
||||
conf.load_from_string(sample_yaml);
|
||||
REQUIRE(conf.get_scalar<bool>(key, false) == true);
|
||||
conf.set_scalar<bool>(key, false);
|
||||
REQUIRE(conf.get_scalar<bool>(key, true) == false);
|
||||
conf.set_scalar<bool>(key, true);
|
||||
REQUIRE(conf.get_scalar<bool>(key, false) == true);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2019 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,18 +13,10 @@ 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.
|
||||
*/
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#define CATCH_CONFIG_CONSOLE_WIDTH 300
|
||||
#include <catch.hpp>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "state.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
bool run(int argc, char** argv, bool& restart, std::string& errstr);
|
||||
bool run(falco::app::state& s, bool& restart, std::string& errstr);
|
||||
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
||||
TEST_CASE("all test cases reside in other .cpp files (empty)", "[multi-file:1]")
|
||||
{
|
||||
}
|
||||
@@ -1,64 +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.
|
||||
#
|
||||
|
||||
message(STATUS "Falco unit tests build enabled")
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG release-1.12.1
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
|
||||
file(GLOB_RECURSE ENGINE_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/engine/*.cpp)
|
||||
file(GLOB_RECURSE FALCO_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/falco/*.cpp)
|
||||
|
||||
set(FALCO_UNIT_TESTS_SOURCES
|
||||
"${ENGINE_TESTS}"
|
||||
"${FALCO_TESTS}"
|
||||
)
|
||||
|
||||
set(FALCO_UNIT_TESTS_INCLUDES
|
||||
PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/userspace
|
||||
${CMAKE_BINARY_DIR}/userspace/falco # we need it to include indirectly `config_falco.h` file
|
||||
${CMAKE_SOURCE_DIR}/userspace/engine # we need it to include indirectly `falco_common.h` file
|
||||
)
|
||||
|
||||
set(FALCO_UNIT_TESTS_DEPENDENCIES
|
||||
gtest
|
||||
gtest_main
|
||||
falco_application
|
||||
)
|
||||
|
||||
get_target_property(FALCO_APPLICATION_LIBRARIES falco_application LINK_LIBRARIES)
|
||||
|
||||
set(FALCO_UNIT_TESTS_LIBRARIES
|
||||
gtest
|
||||
gtest_main
|
||||
falco_application
|
||||
${FALCO_APPLICATION_LIBRARIES}
|
||||
)
|
||||
|
||||
message(STATUS "FALCO_UNIT_TESTS_SOURCES: ${FALCO_UNIT_TESTS_SOURCES}")
|
||||
message(STATUS "FALCO_UNIT_TESTS_INCLUDES: ${FALCO_UNIT_TESTS_INCLUDES}")
|
||||
message(STATUS "FALCO_UNIT_TESTS_DEPENDENCIES: ${FALCO_UNIT_TESTS_DEPENDENCIES}")
|
||||
message(STATUS "FALCO_UNIT_TESTS_LIBRARIES: ${FALCO_UNIT_TESTS_LIBRARIES}")
|
||||
|
||||
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})
|
||||
@@ -1,13 +0,0 @@
|
||||
# Falco unit tests
|
||||
|
||||
## Intro
|
||||
|
||||
Under `unit_tests/engine` and `unit_tests/falco` directories, we have different test suites that could be a single file or an entire directory according to the number and the complexity of tests.
|
||||
|
||||
## Build and Run
|
||||
|
||||
```bash
|
||||
cmake -DMINIMAL_BUILD=On -DBUILD_BPF=Off -DBUILD_DRIVER=Off -DBUILD_FALCO_UNIT_TESTS=On ..
|
||||
make falco_unit_tests
|
||||
sudo ./unit_tests/falco_unit_tests
|
||||
```
|
||||
@@ -1,38 +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 <gtest/gtest.h>
|
||||
#include <engine/falco_utils.h>
|
||||
|
||||
TEST(FalcoUtils, is_unix_scheme)
|
||||
{
|
||||
/* Wrong prefix */
|
||||
ASSERT_EQ(falco::utils::network::is_unix_scheme("something:///run/falco/falco.sock"), false);
|
||||
|
||||
/* Similar prefix, but wrong */
|
||||
ASSERT_EQ(falco::utils::network::is_unix_scheme("unix///falco.sock"), false);
|
||||
|
||||
/* Right prefix, passed as an `rvalue` */
|
||||
ASSERT_EQ(falco::utils::network::is_unix_scheme("unix:///falco.sock"), true);
|
||||
|
||||
/* Right prefix, passed as a `std::string` */
|
||||
std::string url_string("unix:///falco.sock");
|
||||
ASSERT_EQ(falco::utils::network::is_unix_scheme(url_string), true);
|
||||
|
||||
/* Right prefix, passed as a `char[]` */
|
||||
char url_char[] = "unix:///falco.sock";
|
||||
ASSERT_EQ(falco::utils::network::is_unix_scheme(url_char), true);
|
||||
}
|
||||
@@ -1,278 +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 ASSERT_EQd 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 <gtest/gtest.h>
|
||||
#include <engine/filter_macro_resolver.h>
|
||||
|
||||
static std::vector<filter_macro_resolver::value_info>::const_iterator find_value(
|
||||
const std::vector<filter_macro_resolver::value_info>& values,
|
||||
const std::string& ref)
|
||||
{
|
||||
return std::find_if(
|
||||
values.begin(),
|
||||
values.end(),
|
||||
[&ref](const filter_macro_resolver::value_info& v)
|
||||
{ return v.first == ref; });
|
||||
}
|
||||
|
||||
#define MACRO_NAME "test_macro"
|
||||
#define MACRO_A_NAME "test_macro_1"
|
||||
#define MACRO_B_NAME "test_macro_2"
|
||||
|
||||
TEST(MacroResolver, should_resolve_macros_on_a_filter_AST)
|
||||
{
|
||||
libsinsp::filter::ast::pos_info macro_pos(12, 85, 27);
|
||||
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> macro = std::move(libsinsp::filter::ast::unary_check_expr::create("test.field", "", "exists"));
|
||||
|
||||
std::vector<std::unique_ptr<libsinsp::filter::ast::expr>> filter_and;
|
||||
filter_and.push_back(libsinsp::filter::ast::unary_check_expr::create("evt.name", "", "exists"));
|
||||
filter_and.push_back(libsinsp::filter::ast::not_expr::create(libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos)));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> filter = std::move(libsinsp::filter::ast::and_expr::create(filter_and));
|
||||
|
||||
std::vector<std::unique_ptr<libsinsp::filter::ast::expr>> expected_and;
|
||||
expected_and.push_back(libsinsp::filter::ast::unary_check_expr::create("evt.name", "", "exists"));
|
||||
expected_and.push_back(libsinsp::filter::ast::not_expr::create(clone(macro.get())));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> expected = std::move(libsinsp::filter::ast::and_expr::create(expected_and));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(MACRO_NAME, macro);
|
||||
|
||||
// first run
|
||||
ASSERT_TRUE(resolver.run(filter));
|
||||
ASSERT_EQ(resolver.get_resolved_macros().size(), 1);
|
||||
ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_NAME);
|
||||
ASSERT_EQ(resolver.get_resolved_macros().begin()->second, macro_pos);
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_TRUE(filter->is_equal(expected.get()));
|
||||
|
||||
// second run
|
||||
ASSERT_FALSE(resolver.run(filter));
|
||||
ASSERT_TRUE(resolver.get_resolved_macros().empty());
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_TRUE(filter->is_equal(expected.get()));
|
||||
}
|
||||
|
||||
TEST(MacroResolver, should_resolve_macros_on_a_filter_AST_single_node)
|
||||
{
|
||||
libsinsp::filter::ast::pos_info macro_pos(12, 85, 27);
|
||||
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> macro = std::move(libsinsp::filter::ast::unary_check_expr::create("test.field", "", "exists"));
|
||||
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> filter = std::move(libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(MACRO_NAME, macro);
|
||||
|
||||
// first run
|
||||
libsinsp::filter::ast::expr* old_filter_ptr = filter.get();
|
||||
ASSERT_TRUE(resolver.run(filter));
|
||||
ASSERT_NE(filter.get(), old_filter_ptr);
|
||||
ASSERT_EQ(resolver.get_resolved_macros().size(), 1);
|
||||
ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_NAME);
|
||||
ASSERT_EQ(resolver.get_resolved_macros().begin()->second, macro_pos);
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_TRUE(filter->is_equal(macro.get()));
|
||||
|
||||
// second run
|
||||
old_filter_ptr = filter.get();
|
||||
ASSERT_FALSE(resolver.run(filter));
|
||||
ASSERT_EQ(filter.get(), old_filter_ptr);
|
||||
ASSERT_TRUE(resolver.get_resolved_macros().empty());
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_TRUE(filter->is_equal(macro.get()));
|
||||
}
|
||||
|
||||
TEST(MacroResolver, should_resolve_macros_on_a_filter_AST_multiple_macros)
|
||||
{
|
||||
libsinsp::filter::ast::pos_info a_macro_pos(11, 75, 43);
|
||||
libsinsp::filter::ast::pos_info b_macro_pos(91, 21, 9);
|
||||
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> a_macro = std::move(libsinsp::filter::ast::unary_check_expr::create("one.field", "", "exists"));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> b_macro = std::move(libsinsp::filter::ast::unary_check_expr::create("another.field", "", "exists"));
|
||||
|
||||
std::vector<std::unique_ptr<libsinsp::filter::ast::expr>> filter_or;
|
||||
filter_or.push_back(libsinsp::filter::ast::value_expr::create(MACRO_A_NAME, a_macro_pos));
|
||||
filter_or.push_back(libsinsp::filter::ast::value_expr::create(MACRO_B_NAME, b_macro_pos));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> filter = std::move(libsinsp::filter::ast::or_expr::create(filter_or));
|
||||
|
||||
std::vector<std::unique_ptr<libsinsp::filter::ast::expr>> expected_or;
|
||||
expected_or.push_back(clone(a_macro.get()));
|
||||
expected_or.push_back(clone(b_macro.get()));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> expected_filter = std::move(libsinsp::filter::ast::or_expr::create(expected_or));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(MACRO_A_NAME, a_macro);
|
||||
resolver.set_macro(MACRO_B_NAME, b_macro);
|
||||
|
||||
// first run
|
||||
ASSERT_TRUE(resolver.run(filter));
|
||||
ASSERT_EQ(resolver.get_resolved_macros().size(), 2);
|
||||
auto a_resolved_itr = find_value(resolver.get_resolved_macros(), MACRO_A_NAME);
|
||||
ASSERT_NE(a_resolved_itr, resolver.get_resolved_macros().end());
|
||||
ASSERT_STREQ(a_resolved_itr->first.c_str(), MACRO_A_NAME);
|
||||
ASSERT_EQ(a_resolved_itr->second, a_macro_pos);
|
||||
|
||||
auto b_resolved_itr = find_value(resolver.get_resolved_macros(), MACRO_B_NAME);
|
||||
ASSERT_NE(b_resolved_itr, resolver.get_resolved_macros().end());
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_STREQ(b_resolved_itr->first.c_str(), MACRO_B_NAME);
|
||||
ASSERT_EQ(b_resolved_itr->second, b_macro_pos);
|
||||
ASSERT_TRUE(filter->is_equal(expected_filter.get()));
|
||||
|
||||
// second run
|
||||
ASSERT_FALSE(resolver.run(filter));
|
||||
ASSERT_TRUE(resolver.get_resolved_macros().empty());
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_TRUE(filter->is_equal(expected_filter.get()));
|
||||
}
|
||||
|
||||
TEST(MacroResolver, should_resolve_macros_on_a_filter_AST_nested_macros)
|
||||
{
|
||||
libsinsp::filter::ast::pos_info a_macro_pos(47, 1, 76);
|
||||
libsinsp::filter::ast::pos_info b_macro_pos(111, 65, 2);
|
||||
|
||||
std::vector<std::unique_ptr<libsinsp::filter::ast::expr>> a_macro_and;
|
||||
a_macro_and.push_back(libsinsp::filter::ast::unary_check_expr::create("one.field", "", "exists"));
|
||||
a_macro_and.push_back(libsinsp::filter::ast::value_expr::create(MACRO_B_NAME, b_macro_pos));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> a_macro = std::move(libsinsp::filter::ast::and_expr::create(a_macro_and));
|
||||
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> b_macro = std::move(
|
||||
libsinsp::filter::ast::unary_check_expr::create("another.field", "", "exists"));
|
||||
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> filter = std::move(libsinsp::filter::ast::value_expr::create(MACRO_A_NAME, a_macro_pos));
|
||||
|
||||
std::vector<std::unique_ptr<libsinsp::filter::ast::expr>> expected_and;
|
||||
expected_and.push_back(libsinsp::filter::ast::unary_check_expr::create("one.field", "", "exists"));
|
||||
expected_and.push_back(libsinsp::filter::ast::unary_check_expr::create("another.field", "", "exists"));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> expected_filter = std::move(libsinsp::filter::ast::and_expr::create(expected_and));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(MACRO_A_NAME, a_macro);
|
||||
resolver.set_macro(MACRO_B_NAME, b_macro);
|
||||
|
||||
// first run
|
||||
ASSERT_TRUE(resolver.run(filter));
|
||||
ASSERT_EQ(resolver.get_resolved_macros().size(), 2);
|
||||
auto a_resolved_itr = find_value(resolver.get_resolved_macros(), MACRO_A_NAME);
|
||||
ASSERT_NE(a_resolved_itr, resolver.get_resolved_macros().end());
|
||||
ASSERT_STREQ(a_resolved_itr->first.c_str(), MACRO_A_NAME);
|
||||
ASSERT_EQ(a_resolved_itr->second, a_macro_pos);
|
||||
|
||||
auto b_resolved_itr = find_value(resolver.get_resolved_macros(), MACRO_B_NAME);
|
||||
ASSERT_NE(b_resolved_itr, resolver.get_resolved_macros().end());
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_STREQ(b_resolved_itr->first.c_str(), MACRO_B_NAME);
|
||||
ASSERT_EQ(b_resolved_itr->second, b_macro_pos);
|
||||
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_TRUE(filter->is_equal(expected_filter.get()));
|
||||
|
||||
// second run
|
||||
ASSERT_FALSE(resolver.run(filter));
|
||||
ASSERT_TRUE(resolver.get_resolved_macros().empty());
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_TRUE(filter->is_equal(expected_filter.get()));
|
||||
}
|
||||
|
||||
TEST(MacroResolver, should_find_unknown_macros)
|
||||
{
|
||||
libsinsp::filter::ast::pos_info macro_pos(9, 4, 2);
|
||||
|
||||
std::vector<std::unique_ptr<libsinsp::filter::ast::expr>> filter_and;
|
||||
filter_and.push_back(libsinsp::filter::ast::unary_check_expr::create("evt.name", "", "exists"));
|
||||
filter_and.push_back(libsinsp::filter::ast::not_expr::create(libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos)));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> filter = std::move(libsinsp::filter::ast::and_expr::create(filter_and));
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
ASSERT_FALSE(resolver.run(filter));
|
||||
ASSERT_EQ(resolver.get_unknown_macros().size(), 1);
|
||||
ASSERT_STREQ(resolver.get_unknown_macros().begin()->first.c_str(), MACRO_NAME);
|
||||
ASSERT_EQ(resolver.get_unknown_macros().begin()->second, macro_pos);
|
||||
ASSERT_TRUE(resolver.get_resolved_macros().empty());
|
||||
}
|
||||
|
||||
TEST(MacroResolver, should_find_unknown_nested_macros)
|
||||
{
|
||||
libsinsp::filter::ast::pos_info a_macro_pos(32, 84, 9);
|
||||
libsinsp::filter::ast::pos_info b_macro_pos(1, 0, 5);
|
||||
|
||||
std::vector<std::unique_ptr<libsinsp::filter::ast::expr>> a_macro_and;
|
||||
a_macro_and.push_back(libsinsp::filter::ast::unary_check_expr::create("one.field", "", "exists"));
|
||||
a_macro_and.push_back(libsinsp::filter::ast::value_expr::create(MACRO_B_NAME, b_macro_pos));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> a_macro = std::move(libsinsp::filter::ast::and_expr::create(a_macro_and));
|
||||
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> filter = std::move(libsinsp::filter::ast::value_expr::create(MACRO_A_NAME, a_macro_pos));
|
||||
auto expected_filter = clone(a_macro.get());
|
||||
|
||||
filter_macro_resolver resolver;
|
||||
resolver.set_macro(MACRO_A_NAME, a_macro);
|
||||
|
||||
ASSERT_TRUE(resolver.run(filter));
|
||||
ASSERT_EQ(resolver.get_resolved_macros().size(), 1);
|
||||
ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_A_NAME);
|
||||
ASSERT_EQ(resolver.get_resolved_macros().begin()->second, a_macro_pos);
|
||||
ASSERT_EQ(resolver.get_unknown_macros().size(), 1);
|
||||
ASSERT_STREQ(resolver.get_unknown_macros().begin()->first.c_str(), MACRO_B_NAME);
|
||||
ASSERT_EQ(resolver.get_unknown_macros().begin()->second, b_macro_pos);
|
||||
ASSERT_TRUE(filter->is_equal(expected_filter.get()));
|
||||
}
|
||||
|
||||
TEST(MacroResolver, should_undefine_macro)
|
||||
{
|
||||
libsinsp::filter::ast::pos_info macro_pos_1(12, 9, 3);
|
||||
libsinsp::filter::ast::pos_info macro_pos_2(9, 6, 3);
|
||||
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> macro = std::move(libsinsp::filter::ast::unary_check_expr::create("test.field", "", "exists"));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> a_filter = std::move(libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos_1));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> b_filter = std::move(libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos_2));
|
||||
filter_macro_resolver resolver;
|
||||
|
||||
resolver.set_macro(MACRO_NAME, macro);
|
||||
ASSERT_TRUE(resolver.run(a_filter));
|
||||
ASSERT_EQ(resolver.get_resolved_macros().size(), 1);
|
||||
ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_NAME);
|
||||
ASSERT_EQ(resolver.get_resolved_macros().begin()->second, macro_pos_1);
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_TRUE(a_filter->is_equal(macro.get()));
|
||||
|
||||
resolver.set_macro(MACRO_NAME, NULL);
|
||||
ASSERT_FALSE(resolver.run(b_filter));
|
||||
ASSERT_TRUE(resolver.get_resolved_macros().empty());
|
||||
ASSERT_EQ(resolver.get_unknown_macros().size(), 1);
|
||||
ASSERT_STREQ(resolver.get_unknown_macros().begin()->first.c_str(), MACRO_NAME);
|
||||
ASSERT_EQ(resolver.get_unknown_macros().begin()->second, macro_pos_2);
|
||||
}
|
||||
|
||||
/* checks that the macro AST is cloned and not shared across resolved filters */
|
||||
TEST(MacroResolver, should_clone_macro_AST)
|
||||
{
|
||||
libsinsp::filter::ast::pos_info macro_pos(5, 2, 8888);
|
||||
std::shared_ptr<libsinsp::filter::ast::unary_check_expr> macro = std::move(libsinsp::filter::ast::unary_check_expr::create("test.field", "", "exists"));
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> filter = std::move(libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos));
|
||||
filter_macro_resolver resolver;
|
||||
|
||||
resolver.set_macro(MACRO_NAME, macro);
|
||||
ASSERT_TRUE(resolver.run(filter));
|
||||
ASSERT_EQ(resolver.get_resolved_macros().size(), 1);
|
||||
ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_NAME);
|
||||
ASSERT_EQ(resolver.get_resolved_macros().begin()->second, macro_pos);
|
||||
ASSERT_TRUE(resolver.get_unknown_macros().empty());
|
||||
ASSERT_TRUE(filter->is_equal(macro.get()));
|
||||
|
||||
macro->field = "another.field";
|
||||
ASSERT_FALSE(filter->is_equal(macro.get()));
|
||||
}
|
||||
@@ -1,42 +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 ASSERTd 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 <gtest/gtest.h>
|
||||
#include <engine/filter_warning_resolver.h>
|
||||
|
||||
static bool warns(const std::string& condition)
|
||||
{
|
||||
std::set<falco::load_result::warning_code> w;
|
||||
auto ast = libsinsp::filter::parser(condition).parse();
|
||||
filter_warning_resolver().run(ast.get(), w);
|
||||
return !w.empty();
|
||||
}
|
||||
|
||||
TEST(WarningResolver, warnings_in_filtering_conditions)
|
||||
{
|
||||
ASSERT_FALSE(warns("ka.field exists"));
|
||||
ASSERT_FALSE(warns("some.field = <NA>"));
|
||||
ASSERT_TRUE(warns("jevt.field = <NA>"));
|
||||
ASSERT_TRUE(warns("ka.field = <NA>"));
|
||||
ASSERT_TRUE(warns("ka.field == <NA>"));
|
||||
ASSERT_TRUE(warns("ka.field != <NA>"));
|
||||
ASSERT_TRUE(warns("ka.field in (<NA>)"));
|
||||
ASSERT_TRUE(warns("ka.field in (otherval, <NA>)"));
|
||||
ASSERT_TRUE(warns("ka.field intersects (<NA>)"));
|
||||
ASSERT_TRUE(warns("ka.field intersects (otherval, <NA>)"));
|
||||
ASSERT_TRUE(warns("ka.field pmatch (<NA>)"));
|
||||
ASSERT_TRUE(warns("ka.field pmatch (otherval, <NA>)"));
|
||||
}
|
||||
@@ -1,238 +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 <memory>
|
||||
#include <engine/falco_engine.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
static bool check_requirements(std::string& err,
|
||||
const std::vector<falco_engine::plugin_version_requirement>& plugins,
|
||||
const std::string& ruleset_content)
|
||||
{
|
||||
std::unique_ptr<falco_engine> e(new falco_engine());
|
||||
falco::load_result::rules_contents_t c = {{"test", ruleset_content}};
|
||||
|
||||
auto res = e->load_rules(c.begin()->second, c.begin()->first);
|
||||
if(!res->successful())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return e->check_plugin_requirements(plugins, err);
|
||||
}
|
||||
|
||||
TEST(PluginRequirements, check_plugin_requirements_success)
|
||||
{
|
||||
std::string error;
|
||||
|
||||
/* No requirement */
|
||||
ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.1.0"}}, "")) << error << std::endl;
|
||||
|
||||
/* Single plugin */
|
||||
ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Single plugin newer version */
|
||||
ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.2.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Multiple plugins */
|
||||
ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.1.0"}, {"json", "0.3.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Single plugin multiple versions */
|
||||
ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.2.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Single plugin with alternatives */
|
||||
ASSERT_TRUE(check_requirements(error, {{"k8saudit-other", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Multiple plugins with alternatives */
|
||||
ASSERT_TRUE(check_requirements(error, {{"k8saudit-other", "0.5.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Multiple plugins with alternatives with multiple versions */
|
||||
ASSERT_TRUE(check_requirements(error, {{"k8saudit-other", "0.7.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 1.0.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.7.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
TEST(PluginRequirements, check_plugin_requirements_reject)
|
||||
{
|
||||
std::string error;
|
||||
|
||||
/* No plugin loaded */
|
||||
ASSERT_FALSE(check_requirements(error, {}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Single plugin wrong name */
|
||||
ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit2
|
||||
version: 0.1.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Single plugin wrong version */
|
||||
ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Multiple plugins */
|
||||
ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Single plugin multiple versions */
|
||||
ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Single plugin with alternatives */
|
||||
ASSERT_FALSE(check_requirements(error, {{"k8saudit2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Single plugin with overlapping alternatives */
|
||||
ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit
|
||||
version: 0.4.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Multiple plugins with alternatives */
|
||||
ASSERT_FALSE(check_requirements(error, {{"k8saudit-other", "0.5.0"}, {"json3", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
|
||||
/* Multiple plugins with alternatives with multiple versions */
|
||||
ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.7.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.4.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 1.0.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.7.0
|
||||
)")) << error
|
||||
<< std::endl;
|
||||
}
|
||||
@@ -1,177 +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 <gtest/gtest.h>
|
||||
#include <engine/evttype_index_ruleset.h>
|
||||
|
||||
#define RULESET_0 0
|
||||
#define RULESET_1 1
|
||||
#define RULESET_2 2
|
||||
|
||||
/* Helpers methods */
|
||||
static std::shared_ptr<gen_event_filter_factory> create_factory()
|
||||
{
|
||||
std::shared_ptr<gen_event_filter_factory> ret(new sinsp_filter_factory(NULL));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::shared_ptr<filter_ruleset> create_ruleset(std::shared_ptr<gen_event_filter_factory> f)
|
||||
{
|
||||
std::shared_ptr<filter_ruleset> ret(new evttype_index_ruleset(f));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::shared_ptr<libsinsp::filter::ast::expr> create_ast(std::shared_ptr<gen_event_filter_factory> f)
|
||||
{
|
||||
libsinsp::filter::parser parser("evt.type=open");
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> ret(parser.parse());
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::shared_ptr<gen_event_filter> create_filter(
|
||||
std::shared_ptr<gen_event_filter_factory> f,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> ast)
|
||||
{
|
||||
sinsp_filter_compiler compiler(f, ast.get());
|
||||
std::shared_ptr<gen_event_filter> filter(compiler.compile());
|
||||
return filter;
|
||||
}
|
||||
|
||||
TEST(Ruleset, enable_disable_rules_using_names)
|
||||
{
|
||||
auto f = create_factory();
|
||||
auto r = create_ruleset(f);
|
||||
auto ast = create_ast(f);
|
||||
auto filter = create_filter(f, ast);
|
||||
|
||||
falco_rule rule_A = {};
|
||||
rule_A.name = "rule_A";
|
||||
rule_A.source = falco_common::syscall_source;
|
||||
|
||||
falco_rule rule_B = {};
|
||||
rule_B.name = "rule_B";
|
||||
rule_B.source = falco_common::syscall_source;
|
||||
|
||||
falco_rule rule_C = {};
|
||||
rule_C.name = "rule_C";
|
||||
rule_C.source = falco_common::syscall_source;
|
||||
|
||||
r->add(rule_A, filter, ast);
|
||||
r->add(rule_B, filter, ast);
|
||||
r->add(rule_C, filter, ast);
|
||||
|
||||
/* Enable `rule_A` for RULESET_0 */
|
||||
r->enable(rule_A.name, true, RULESET_0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
|
||||
/* Disable `rule_A` for RULESET_1, this should have no effect */
|
||||
r->disable(rule_A.name, true, RULESET_1);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
|
||||
/* Enable a not existing rule for RULESET_2, this should have no effect */
|
||||
r->disable("<NA>", true, RULESET_2);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
|
||||
/* Enable all rules for RULESET_0 */
|
||||
r->enable("rule_", false, RULESET_0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 3);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
|
||||
/* Try to disable all rules with exact match for RULESET_0, this should have no effect */
|
||||
r->disable("rule_", true, RULESET_0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 3);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
|
||||
/* Disable all rules for RULESET_0 */
|
||||
r->disable("rule_", false, RULESET_0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
|
||||
/* Enable rule_C for RULESET_2 without exact_match */
|
||||
r->enable("_C", false, RULESET_2);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 1);
|
||||
}
|
||||
|
||||
TEST(Ruleset, enable_disable_rules_using_tags)
|
||||
{
|
||||
auto f = create_factory();
|
||||
auto r = create_ruleset(f);
|
||||
auto ast = create_ast(f);
|
||||
auto filter = create_filter(f, ast);
|
||||
|
||||
falco_rule rule_A = {};
|
||||
rule_A.name = "rule_A";
|
||||
rule_A.source = falco_common::syscall_source;
|
||||
rule_A.tags = {"first_rule_A_tag", "second_rule_A_tag", "common_tag"};
|
||||
|
||||
falco_rule rule_B = {};
|
||||
rule_B.name = "rule_B";
|
||||
rule_B.source = falco_common::syscall_source;
|
||||
rule_B.tags = {"first_rule_B_tag", "second_rule_B_tag", "common_tag"};
|
||||
|
||||
r->add(rule_A, filter, ast);
|
||||
r->add(rule_B, filter, ast);
|
||||
|
||||
/* Enable `rule_A` for RULESET_0 using its first tag */
|
||||
r->enable_tags({"first_rule_A_tag"}, RULESET_0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
|
||||
/* Disable `rule_A` for RULESET_1 using its first tag, this should have no effect */
|
||||
r->disable_tags({"first_rule_A_tag"}, RULESET_1);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
|
||||
/* Enable a not existing rule for RULESET_0, this should have no effect */
|
||||
r->enable_tags({"<NA_tag>"}, RULESET_0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
|
||||
/* Enable all rules for RULESET_2 */
|
||||
r->enable_tags({"common_tag"}, RULESET_2);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 1);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 2);
|
||||
|
||||
/* Disable `rule_A` for RULESET_0 using its second tag
|
||||
* Note that we have previously enabled it using the first tag,
|
||||
* so here we are using a different tag of the rule t disable it!
|
||||
*/
|
||||
r->disable_tags({"second_rule_A_tag"}, RULESET_0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 2);
|
||||
|
||||
/* Disable all rules for RULESET_2 */
|
||||
r->disable_tags({"common_tag"}, RULESET_2);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_0), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_1), 0);
|
||||
ASSERT_EQ(r->enabled_count(RULESET_2), 0);
|
||||
}
|
||||
@@ -1,390 +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 <falco_engine.h>
|
||||
|
||||
#include <falco/app/state.h>
|
||||
#include <falco/app/actions/actions.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#define ASSERT_NAMES_EQ(a, b) { \
|
||||
EXPECT_EQ(_order(a).size(), _order(b).size()); \
|
||||
ASSERT_EQ(_order(a), _order(b)); \
|
||||
}
|
||||
|
||||
#define ASSERT_NAMES_CONTAIN(a, b) { \
|
||||
ASSERT_NAMES_EQ(unordered_set_intersection(a, b), b); \
|
||||
}
|
||||
|
||||
#define ASSERT_NAMES_NOCONTAIN(a, b) { \
|
||||
ASSERT_NAMES_EQ(unordered_set_intersection(a, b), strset_t({})); \
|
||||
}
|
||||
|
||||
using strset_t = std::unordered_set<std::string>;
|
||||
|
||||
static std::set<std::string> _order(const strset_t& s)
|
||||
{
|
||||
return std::set<std::string>(s.begin(), s.end());
|
||||
}
|
||||
|
||||
static std::string s_sample_ruleset = "sample-ruleset";
|
||||
|
||||
static std::string s_sample_source = falco_common::syscall_source;
|
||||
|
||||
static strset_t s_sample_filters = {
|
||||
"evt.type=connect or evt.type=accept or evt.type=accept4 or evt.type=umount2",
|
||||
"evt.type in (open, ptrace, mmap, execve, read, container)",
|
||||
"evt.type in (open, execve, mprotect) and not evt.type=mprotect"};
|
||||
|
||||
static strset_t s_sample_generic_filters = {
|
||||
"evt.type=syncfs or evt.type=fanotify_init"};
|
||||
|
||||
static strset_t s_sample_nonsyscall_filters = {
|
||||
"evt.type in (procexit, switch, pluginevent, container)"};
|
||||
|
||||
|
||||
// todo(jasondellaluce): once we have deeper and more modular
|
||||
// control on the falco engine, make this a little nicer
|
||||
static std::shared_ptr<falco_engine> mock_engine_from_filters(const strset_t& filters)
|
||||
{
|
||||
// craft a fake ruleset with the given filters
|
||||
int n_rules = 0;
|
||||
std::string dummy_rules;
|
||||
falco::load_result::rules_contents_t content = {{"dummy_rules.yaml", dummy_rules}};
|
||||
for (const auto& f : filters)
|
||||
{
|
||||
n_rules++;
|
||||
dummy_rules +=
|
||||
"- rule: Dummy Rule " + std::to_string(n_rules) + "\n"
|
||||
+ " output: Dummy Output " + std::to_string(n_rules) + "\n"
|
||||
+ " condition: " + f + "\n"
|
||||
+ " desc: Dummy Desc " + std::to_string(n_rules) + "\n"
|
||||
+ " priority: CRITICAL\n\n";
|
||||
}
|
||||
|
||||
// create a falco engine and load the ruleset
|
||||
std::shared_ptr<falco_engine> res(new falco_engine());
|
||||
auto filter_factory = std::shared_ptr<gen_event_filter_factory>(
|
||||
new sinsp_filter_factory(nullptr));
|
||||
auto formatter_factory = std::shared_ptr<gen_event_formatter_factory>(
|
||||
new sinsp_evt_formatter_factory(nullptr));
|
||||
res->add_source(s_sample_source, filter_factory, formatter_factory);
|
||||
res->load_rules(dummy_rules, "dummy_rules.yaml");
|
||||
res->enable_rule("", true, s_sample_ruleset);
|
||||
return res;
|
||||
}
|
||||
|
||||
TEST(ConfigureInterestingSets, engine_codes_syscalls_set)
|
||||
{
|
||||
auto engine = mock_engine_from_filters(s_sample_filters);
|
||||
auto enabled_count = engine->num_rules_for_ruleset(s_sample_ruleset);
|
||||
ASSERT_EQ(enabled_count, s_sample_filters.size());
|
||||
|
||||
// test if event code names were extracted from each rule in test ruleset.
|
||||
auto rules_event_set = engine->event_codes_for_ruleset(s_sample_source);
|
||||
auto rules_event_names = libsinsp::events::event_set_to_names(rules_event_set);
|
||||
ASSERT_NAMES_EQ(rules_event_names, strset_t({
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read", "container"}));
|
||||
|
||||
// test if sc code names were extracted from each rule in test ruleset.
|
||||
// note, this is not supposed to contain "container", as that's an event
|
||||
// not mapped through the ppm_sc_code enumerative.
|
||||
auto rules_sc_set = engine->sc_codes_for_ruleset(s_sample_source);
|
||||
auto rules_sc_names = libsinsp::events::sc_set_to_names(rules_sc_set);
|
||||
ASSERT_NAMES_EQ(rules_sc_names, strset_t({
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read"}));
|
||||
}
|
||||
|
||||
TEST(ConfigureInterestingSets, preconditions_postconditions)
|
||||
{
|
||||
falco::app::state s;
|
||||
auto mock_engine = mock_engine_from_filters(s_sample_filters);
|
||||
|
||||
s.engine = mock_engine;
|
||||
s.config = nullptr;
|
||||
auto result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_FALSE(result.success);
|
||||
ASSERT_NE(result.errstr, "");
|
||||
|
||||
s.engine = nullptr;
|
||||
s.config = std::make_shared<falco_configuration>();
|
||||
result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_FALSE(result.success);
|
||||
ASSERT_NE(result.errstr, "");
|
||||
|
||||
s.engine = mock_engine;
|
||||
s.config = std::make_shared<falco_configuration>();
|
||||
result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
|
||||
auto prev_selection_size = s.selected_sc_set.size();
|
||||
result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
ASSERT_EQ(prev_selection_size, s.selected_sc_set.size());
|
||||
}
|
||||
|
||||
TEST(ConfigureInterestingSets, engine_codes_nonsyscalls_set)
|
||||
{
|
||||
auto filters = s_sample_filters;
|
||||
filters.insert(s_sample_generic_filters.begin(), s_sample_generic_filters.end());
|
||||
filters.insert(s_sample_nonsyscall_filters.begin(), s_sample_nonsyscall_filters.end());
|
||||
|
||||
auto engine = mock_engine_from_filters(filters);
|
||||
auto enabled_count = engine->num_rules_for_ruleset(s_sample_ruleset);
|
||||
ASSERT_EQ(enabled_count, filters.size());
|
||||
|
||||
auto rules_event_set = engine->event_codes_for_ruleset(s_sample_source);
|
||||
auto rules_event_names = libsinsp::events::event_set_to_names(rules_event_set);
|
||||
// note: including even one generic event will cause PPME_GENERIC_E to be
|
||||
// included in the ruleset's event codes. As such, when translating to names,
|
||||
// PPME_GENERIC_E will cause all names of generic events to be added!
|
||||
// This is a good example of information loss from ppm_event_code <-> ppm_sc_code.
|
||||
auto generic_names = libsinsp::events::event_set_to_names({ppm_event_code::PPME_GENERIC_E});
|
||||
auto expected_names = strset_t({
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read", "container", // ruleset
|
||||
"procexit", "switch", "pluginevent"}); // from non-syscall event filters
|
||||
expected_names.insert(generic_names.begin(), generic_names.end());
|
||||
ASSERT_NAMES_EQ(rules_event_names, expected_names);
|
||||
|
||||
auto rules_sc_set = engine->sc_codes_for_ruleset(s_sample_source);
|
||||
auto rules_sc_names = libsinsp::events::sc_set_to_names(rules_sc_set);
|
||||
ASSERT_NAMES_EQ(rules_sc_names, strset_t({
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read",
|
||||
"syncfs", "fanotify_init", // from generic event filters
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(ConfigureInterestingSets, selection_not_allevents)
|
||||
{
|
||||
// run app action with fake engine and without the `-A` option
|
||||
falco::app::state s;
|
||||
s.engine = mock_engine_from_filters(s_sample_filters);
|
||||
s.options.all_events = false;
|
||||
auto result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
|
||||
// todo(jasondellaluce): once we have deeper control on falco's outputs,
|
||||
// also check if a warning has been printed in stderr
|
||||
|
||||
// check that the final selected set is the one expected
|
||||
ASSERT_GT(s.selected_sc_set.size(), 1);
|
||||
auto selected_sc_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
auto expected_sc_names = strset_t({
|
||||
// note: we expect the "read" syscall to have been erased
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", // from ruleset
|
||||
"clone", "clone3", "fork", "vfork", // from sinsp state set (spawned_process)
|
||||
"socket", "bind", "close" // from sinsp state set (network, files)
|
||||
});
|
||||
ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names);
|
||||
|
||||
// check that all IO syscalls have been erased from the selection
|
||||
auto io_set = libsinsp::events::io_sc_set();
|
||||
auto erased_sc_names = libsinsp::events::sc_set_to_names(io_set);
|
||||
ASSERT_NAMES_NOCONTAIN(selected_sc_names, erased_sc_names);
|
||||
|
||||
// check that final selected set is exactly sinsp state + ruleset
|
||||
auto rule_set = s.engine->sc_codes_for_ruleset(s_sample_source, s_sample_ruleset);
|
||||
auto state_set = libsinsp::events::sinsp_state_sc_set();
|
||||
for (const auto &erased : io_set)
|
||||
{
|
||||
rule_set.remove(erased);
|
||||
state_set.remove(erased);
|
||||
}
|
||||
auto union_set = state_set.merge(rule_set);
|
||||
auto inter_set = state_set.intersect(rule_set);
|
||||
ASSERT_EQ(s.selected_sc_set.size(), state_set.size() + rule_set.size() - inter_set.size());
|
||||
ASSERT_EQ(s.selected_sc_set, union_set);
|
||||
}
|
||||
|
||||
TEST(ConfigureInterestingSets, selection_allevents)
|
||||
{
|
||||
// run app action with fake engine and with the `-A` option
|
||||
falco::app::state s;
|
||||
s.engine = mock_engine_from_filters(s_sample_filters);
|
||||
s.options.all_events = true;
|
||||
auto result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
|
||||
// todo(jasondellaluce): once we have deeper control on falco's outputs,
|
||||
// also check if a warning has not been printed in stderr
|
||||
|
||||
// check that the final selected set is the one expected
|
||||
ASSERT_GT(s.selected_sc_set.size(), 1);
|
||||
auto selected_sc_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
auto expected_sc_names = strset_t({
|
||||
// note: we expect the "read" syscall to not be erased
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read", // from ruleset
|
||||
"clone", "clone3", "fork", "vfork", // from sinsp state set (spawned_process)
|
||||
"socket", "bind", "close" // from sinsp state set (network, files)
|
||||
});
|
||||
ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names);
|
||||
|
||||
// check that final selected set is exactly sinsp state + ruleset
|
||||
auto rule_set = s.engine->sc_codes_for_ruleset(s_sample_source, s_sample_ruleset);
|
||||
auto state_set = libsinsp::events::sinsp_state_sc_set();
|
||||
auto union_set = state_set.merge(rule_set);
|
||||
auto inter_set = state_set.intersect(rule_set);
|
||||
ASSERT_EQ(s.selected_sc_set.size(), state_set.size() + rule_set.size() - inter_set.size());
|
||||
ASSERT_EQ(s.selected_sc_set, union_set);
|
||||
}
|
||||
|
||||
TEST(ConfigureInterestingSets, selection_generic_evts)
|
||||
{
|
||||
// run app action with fake engine and without the `-A` option
|
||||
falco::app::state s;
|
||||
s.options.all_events = false;
|
||||
auto filters = s_sample_filters;
|
||||
filters.insert(s_sample_generic_filters.begin(), s_sample_generic_filters.end());
|
||||
s.engine = mock_engine_from_filters(filters);
|
||||
auto result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
|
||||
// check that the final selected set is the one expected
|
||||
ASSERT_GT(s.selected_sc_set.size(), 1);
|
||||
auto selected_sc_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
auto expected_sc_names = strset_t({
|
||||
// note: we expect the "read" syscall to not be erased
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", // from ruleset
|
||||
"syncfs", "fanotify_init", // from ruleset (generic events)
|
||||
"clone", "clone3", "fork", "vfork", // from sinsp state set (spawned_process)
|
||||
"socket", "bind", "close" // from sinsp state set (network, files)
|
||||
});
|
||||
ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names);
|
||||
auto unexpected_sc_names = libsinsp::events::sc_set_to_names(libsinsp::events::io_sc_set());
|
||||
ASSERT_NAMES_NOCONTAIN(selected_sc_names, unexpected_sc_names);
|
||||
}
|
||||
|
||||
// expected combinations precedence:
|
||||
// - final selected set is the union of rules events and base events
|
||||
// (either default or custom positive set)
|
||||
// - events in the custom negative set are removed from the selected set
|
||||
// - if `-A` is not set, events from the IO set are removed from the selected set
|
||||
TEST(ConfigureInterestingSets, selection_custom_base_set)
|
||||
{
|
||||
// run app action with fake engine and without the `-A` option
|
||||
falco::app::state s;
|
||||
s.options.all_events = true;
|
||||
s.engine = mock_engine_from_filters(s_sample_filters);
|
||||
auto default_base_set = libsinsp::events::sinsp_state_sc_set();
|
||||
|
||||
// non-empty custom base set (both positive and negative)
|
||||
s.config->m_base_syscalls_repair = false;
|
||||
s.config->m_base_syscalls_custom_set = {"syncfs", "!accept"};
|
||||
auto result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
auto selected_sc_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
auto expected_sc_names = strset_t({
|
||||
// note: `syncfs` has been added due to the custom base set, and `accept`
|
||||
// has been remove due to the negative base set.
|
||||
// note: `read` is not ignored due to the "-A" option being set.
|
||||
// note: `accept` is not included even though it is matched by the rules,
|
||||
// which means that the custom negation base set has precedence over the
|
||||
// final selection set as a whole
|
||||
// todo(jasondellaluce): add "accept4" once names_to_sc_set is polished on the libs side
|
||||
"connect", "umount2", "open", "ptrace", "mmap", "execve", "read", "syncfs", "sched_process_exit"
|
||||
});
|
||||
ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names);
|
||||
|
||||
// non-empty custom base set (both positive and negative with collision)
|
||||
s.config->m_base_syscalls_repair = false;
|
||||
s.config->m_base_syscalls_custom_set = {"syncfs", "accept", "!accept"};
|
||||
result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
selected_sc_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
// note: in case of collision, negation has priority, so the expected
|
||||
// names are the same as the case above
|
||||
ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names);
|
||||
|
||||
// non-empty custom base set (only positive)
|
||||
s.config->m_base_syscalls_custom_set = {"syncfs"};
|
||||
result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
selected_sc_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
expected_sc_names = strset_t({
|
||||
// note: accept is not negated anymore
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read", "syncfs", "sched_process_exit"
|
||||
});
|
||||
ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names);
|
||||
|
||||
// non-empty custom base set (only negative)
|
||||
s.config->m_base_syscalls_custom_set = {"!accept"};
|
||||
result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
selected_sc_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
expected_sc_names = unordered_set_union(
|
||||
libsinsp::events::sc_set_to_names(default_base_set),
|
||||
strset_t({ "connect", "umount2", "open", "ptrace", "mmap", "execve", "read"}));
|
||||
expected_sc_names.erase("accept");
|
||||
// todo(jasondellaluce): add "accept4" once names_to_sc_set is polished on the libs side
|
||||
expected_sc_names.erase("accept4");
|
||||
ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names);
|
||||
|
||||
// non-empty custom base set (positive, without -A)
|
||||
s.options.all_events = false;
|
||||
s.config->m_base_syscalls_custom_set = {"read"};
|
||||
result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
selected_sc_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
expected_sc_names = strset_t({
|
||||
// note: read is both part of the custom base set and the rules set,
|
||||
// but we expect the unset -A option to take precedence
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "sched_process_exit"
|
||||
});
|
||||
ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names);
|
||||
auto unexpected_sc_names = libsinsp::events::sc_set_to_names(libsinsp::events::io_sc_set());
|
||||
ASSERT_NAMES_NOCONTAIN(selected_sc_names, unexpected_sc_names);
|
||||
}
|
||||
|
||||
TEST(ConfigureInterestingSets, selection_custom_base_set_repair)
|
||||
{
|
||||
// run app action with fake engine and without the `-A` option
|
||||
falco::app::state s;
|
||||
s.options.all_events = false;
|
||||
s.engine = mock_engine_from_filters(s_sample_filters);
|
||||
|
||||
// simulate empty custom set but repair option set.
|
||||
// note: here we use file syscalls (e.g. open, openat) and have a custom
|
||||
// positive set, so we expect syscalls such as "close" to be selected as
|
||||
// repaired. Also, given that we use some network syscalls, we expect "bind"
|
||||
// to be selected event if we negate it, because repairment should have
|
||||
// take precedence.
|
||||
s.config->m_base_syscalls_custom_set = {"openat", "!bind"};
|
||||
s.config->m_base_syscalls_repair = true;
|
||||
auto result = falco::app::actions::configure_interesting_sets(s);
|
||||
ASSERT_TRUE(result.success);
|
||||
ASSERT_EQ(result.errstr, "");
|
||||
auto selected_sc_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
auto expected_sc_names = strset_t({
|
||||
// note: expecting syscalls from mock rules and `sinsp_repair_state_sc_set` enforced syscalls
|
||||
"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "sched_process_exit", \
|
||||
"bind", "socket", "clone3", "close", "setuid"
|
||||
});
|
||||
ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names);
|
||||
auto unexpected_sc_names = libsinsp::events::sc_set_to_names(libsinsp::events::io_sc_set());
|
||||
ASSERT_NAMES_NOCONTAIN(selected_sc_names, unexpected_sc_names);
|
||||
}
|
||||
@@ -1,97 +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 ASSERTd 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 <gtest/gtest.h>
|
||||
#include <falco/app/state.h>
|
||||
#include <falco/app/actions/actions.h>
|
||||
|
||||
#define EXPECT_ACTION_OK(r) { EXPECT_TRUE(r.success); EXPECT_TRUE(r.proceed); EXPECT_EQ(r.errstr, ""); }
|
||||
#define EXPECT_ACTION_FAIL(r) { EXPECT_FALSE(r.success); EXPECT_FALSE(r.proceed); EXPECT_NE(r.errstr, ""); }
|
||||
|
||||
TEST(ActionSelectEventSources, pre_post_conditions)
|
||||
{
|
||||
auto action = falco::app::actions::select_event_sources;
|
||||
|
||||
// requires sources to be already loaded
|
||||
{
|
||||
falco::app::state s;
|
||||
EXPECT_ACTION_FAIL(action(s));
|
||||
}
|
||||
|
||||
// ignore source selection in capture mode
|
||||
{
|
||||
falco::app::state s;
|
||||
s.options.trace_filename = "some_capture_file.scap";
|
||||
EXPECT_TRUE(s.is_capture_mode());
|
||||
EXPECT_ACTION_OK(action(s));
|
||||
}
|
||||
|
||||
// enable all loaded sources by default, even with multiple calls
|
||||
{
|
||||
falco::app::state s;
|
||||
s.loaded_sources = {"syscall", "some_source"};
|
||||
EXPECT_ACTION_OK(action(s));
|
||||
EXPECT_EQ(s.loaded_sources, s.enabled_sources);
|
||||
s.loaded_sources.insert("another_source");
|
||||
EXPECT_ACTION_OK(action(s));
|
||||
EXPECT_EQ(s.loaded_sources, s.enabled_sources);
|
||||
}
|
||||
|
||||
// enable only selected sources
|
||||
{
|
||||
falco::app::state s;
|
||||
s.loaded_sources = {"syscall", "some_source"};
|
||||
s.options.enable_sources = {"syscall"};
|
||||
EXPECT_ACTION_OK(action(s));
|
||||
EXPECT_EQ(s.enabled_sources.size(), 1);
|
||||
EXPECT_EQ(*s.enabled_sources.begin(), "syscall");
|
||||
}
|
||||
|
||||
// enable all loaded sources expect the disabled ones
|
||||
{
|
||||
falco::app::state s;
|
||||
s.loaded_sources = {"syscall", "some_source"};
|
||||
s.options.disable_sources = {"syscall"};
|
||||
EXPECT_ACTION_OK(action(s));
|
||||
EXPECT_EQ(s.enabled_sources.size(), 1);
|
||||
EXPECT_EQ(*s.enabled_sources.begin(), "some_source");
|
||||
}
|
||||
|
||||
// enable unknown sources
|
||||
{
|
||||
falco::app::state s;
|
||||
s.loaded_sources = {"syscall", "some_source"};
|
||||
s.options.enable_sources = {"some_other_source"};
|
||||
EXPECT_ACTION_FAIL(action(s));
|
||||
}
|
||||
|
||||
// disable unknown sources
|
||||
{
|
||||
falco::app::state s;
|
||||
s.loaded_sources = {"syscall", "some_source"};
|
||||
s.options.disable_sources = {"some_other_source"};
|
||||
EXPECT_ACTION_FAIL(action(s));
|
||||
}
|
||||
|
||||
// mix enable and disable sources options
|
||||
{
|
||||
falco::app::state s;
|
||||
s.loaded_sources = {"syscall", "some_source"};
|
||||
s.options.disable_sources = {"syscall"};
|
||||
s.options.enable_sources = {"syscall"};
|
||||
EXPECT_ACTION_FAIL(action(s));
|
||||
}
|
||||
}
|
||||
@@ -1,131 +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 ASSERTd 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 <gtest/gtest.h>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
#include <falco/atomic_signal_handler.h>
|
||||
#include <falco/logger.h>
|
||||
|
||||
TEST(AtomicSignalHandler, lock_free_implementation)
|
||||
{
|
||||
ASSERT_TRUE(falco::atomic_signal_handler().is_lock_free());
|
||||
}
|
||||
|
||||
TEST(AtomicSignalHandler, handle_once_wait_consistency)
|
||||
{
|
||||
constexpr const auto thread_num = 10;
|
||||
constexpr const auto thread_wait_sec = 2;
|
||||
constexpr const auto handler_wait_sec = 1;
|
||||
|
||||
// have a shared signal handler
|
||||
falco::atomic_signal_handler handler;
|
||||
|
||||
// launch a bunch of threads all syncing on the same handler
|
||||
typedef struct
|
||||
{
|
||||
bool handled;
|
||||
uint64_t duration_secs;
|
||||
} task_result_t;
|
||||
std::vector<std::future<task_result_t>> futures;
|
||||
std::vector<std::unique_ptr<std::thread>> threads;
|
||||
for (int i = 0; i < thread_num; i++)
|
||||
{
|
||||
std::packaged_task<task_result_t()> task([&handler, &thread_wait_sec]{
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
task_result_t res;
|
||||
res.handled = false;
|
||||
while (!handler.handled())
|
||||
{
|
||||
if (handler.triggered())
|
||||
{
|
||||
res.handled = handler.handle([&thread_wait_sec]{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(thread_wait_sec));
|
||||
});
|
||||
}
|
||||
}
|
||||
auto diff = std::chrono::high_resolution_clock::now() - start;
|
||||
res.duration_secs = std::chrono::duration_cast<std::chrono::seconds>(diff).count();
|
||||
return res;
|
||||
});
|
||||
futures.push_back(task.get_future());
|
||||
threads.emplace_back();
|
||||
threads[i].reset(new std::thread(std::move(task)));
|
||||
}
|
||||
|
||||
// wait a bit, then trigger the signal handler from the main thread
|
||||
auto total_handled = 0;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
std::this_thread::sleep_for (std::chrono::seconds(handler_wait_sec));
|
||||
handler.trigger();
|
||||
for (int i = 0; i < thread_num; i++)
|
||||
{
|
||||
// we need to check that all threads didn't quit before
|
||||
// the handle() function finished executing
|
||||
futures[i].wait();
|
||||
threads[i]->join();
|
||||
auto res = futures[i].get();
|
||||
if (res.handled)
|
||||
{
|
||||
total_handled++;
|
||||
}
|
||||
ASSERT_GE(res.duration_secs, thread_wait_sec);
|
||||
}
|
||||
|
||||
// check that the total time is consistent with the expectations
|
||||
auto diff = std::chrono::high_resolution_clock::now() - start;
|
||||
auto secs = std::chrono::duration_cast<std::chrono::seconds>(diff).count();
|
||||
ASSERT_GE(secs, thread_wait_sec + handler_wait_sec);
|
||||
|
||||
// check that only one thread handled the signal
|
||||
ASSERT_EQ(total_handled, 1);
|
||||
}
|
||||
|
||||
TEST(AtomicSignalHandler, handle_and_reset)
|
||||
{
|
||||
auto do_nothing = []{};
|
||||
falco::atomic_signal_handler handler;
|
||||
|
||||
ASSERT_FALSE(handler.triggered());
|
||||
ASSERT_FALSE(handler.handled());
|
||||
ASSERT_FALSE(handler.handle(do_nothing));
|
||||
|
||||
handler.trigger();
|
||||
ASSERT_TRUE(handler.triggered());
|
||||
ASSERT_FALSE(handler.handled());
|
||||
|
||||
ASSERT_TRUE(handler.handle(do_nothing));
|
||||
ASSERT_TRUE(handler.triggered());
|
||||
ASSERT_TRUE(handler.handled());
|
||||
ASSERT_FALSE(handler.handle(do_nothing));
|
||||
|
||||
handler.trigger();
|
||||
ASSERT_TRUE(handler.triggered());
|
||||
ASSERT_FALSE(handler.handled());
|
||||
ASSERT_TRUE(handler.handle(do_nothing));
|
||||
ASSERT_TRUE(handler.triggered());
|
||||
ASSERT_TRUE(handler.handled());
|
||||
ASSERT_FALSE(handler.handle(do_nothing));
|
||||
|
||||
handler.reset();
|
||||
ASSERT_FALSE(handler.triggered());
|
||||
ASSERT_FALSE(handler.handled());
|
||||
ASSERT_FALSE(handler.handle(do_nothing));
|
||||
}
|
||||
@@ -1,103 +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 ASSERTd 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 <gtest/gtest.h>
|
||||
#include <falco/configuration.h>
|
||||
|
||||
static std::string sample_yaml =
|
||||
"base_value:\n"
|
||||
" id: 1\n"
|
||||
" name: 'sample_name'\n"
|
||||
" subvalue:\n"
|
||||
" subvalue2:\n"
|
||||
" boolean: true\n"
|
||||
"base_value_2:\n"
|
||||
" sample_list:\n"
|
||||
" - elem1\n"
|
||||
" - elem2\n"
|
||||
" - elem3\n";
|
||||
|
||||
TEST(Configuration, configuration_exceptions)
|
||||
{
|
||||
yaml_helper conf;
|
||||
|
||||
/* Broken YAML */
|
||||
std::string sample_broken_yaml = sample_yaml + " / bad_symbol";
|
||||
EXPECT_ANY_THROW(conf.load_from_string(sample_broken_yaml));
|
||||
|
||||
/* Right YAML */
|
||||
EXPECT_NO_THROW(conf.load_from_string(sample_yaml));
|
||||
}
|
||||
|
||||
TEST(Configuration, configuration_reload)
|
||||
{
|
||||
yaml_helper conf;
|
||||
|
||||
/* Clear and reload config */
|
||||
conf.load_from_string(sample_yaml);
|
||||
ASSERT_TRUE(conf.is_defined("base_value"));
|
||||
conf.clear();
|
||||
ASSERT_FALSE(conf.is_defined("base_value"));
|
||||
conf.load_from_string(sample_yaml);
|
||||
ASSERT_TRUE(conf.is_defined("base_value"));
|
||||
}
|
||||
|
||||
TEST(Configuration, read_yaml_fields)
|
||||
{
|
||||
yaml_helper conf;
|
||||
conf.load_from_string(sample_yaml);
|
||||
|
||||
/* is_defined */
|
||||
ASSERT_TRUE(conf.is_defined("base_value"));
|
||||
ASSERT_TRUE(conf.is_defined("base_value_2"));
|
||||
ASSERT_FALSE(conf.is_defined("unknown_base_value"));
|
||||
|
||||
/* get some fields */
|
||||
ASSERT_EQ(conf.get_scalar<int>("base_value.id", -1), 1);
|
||||
ASSERT_STREQ(conf.get_scalar<std::string>("base_value.name", "none").c_str(), "sample_name");
|
||||
ASSERT_EQ(conf.get_scalar<bool>("base_value.subvalue.subvalue2.boolean", false), true);
|
||||
|
||||
/* get list field elements */
|
||||
ASSERT_STREQ(conf.get_scalar<std::string>("base_value_2.sample_list[0]", "none").c_str(), "elem1");
|
||||
ASSERT_STREQ(conf.get_scalar<std::string>("base_value_2.sample_list[1]", "none").c_str(), "elem2");
|
||||
ASSERT_STREQ(conf.get_scalar<std::string>("base_value_2.sample_list[2]", "none").c_str(), "elem3");
|
||||
|
||||
/* get sequence */
|
||||
std::vector<std::string> seq;
|
||||
conf.get_sequence(seq, "base_value_2.sample_list");
|
||||
ASSERT_EQ(seq.size(), 3);
|
||||
ASSERT_STREQ(seq[0].c_str(), "elem1");
|
||||
ASSERT_STREQ(seq[1].c_str(), "elem2");
|
||||
ASSERT_STREQ(seq[2].c_str(), "elem3");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ set(FALCO_ENGINE_SOURCE_FILES
|
||||
evttype_index_ruleset.cpp
|
||||
formats.cpp
|
||||
filter_macro_resolver.cpp
|
||||
filter_evttype_resolver.cpp
|
||||
filter_warning_resolver.cpp
|
||||
stats_manager.cpp
|
||||
rule_loader.cpp
|
||||
@@ -28,7 +29,9 @@ set(FALCO_ENGINE_SOURCE_FILES
|
||||
|
||||
add_library(falco_engine STATIC ${FALCO_ENGINE_SOURCE_FILES})
|
||||
|
||||
add_dependencies(falco_engine yamlcpp njson)
|
||||
if(USE_BUNDLED_DEPS)
|
||||
add_dependencies(falco_engine yamlcpp njson)
|
||||
endif()
|
||||
|
||||
if(MINIMAL_BUILD)
|
||||
target_include_directories(
|
||||
|
||||
@@ -15,10 +15,13 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
#include "evttype_index_ruleset.h"
|
||||
#include "filter_evttype_resolver.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
evttype_index_ruleset::evttype_index_ruleset(
|
||||
std::shared_ptr<gen_event_filter_factory> f): m_filter_factory(f)
|
||||
{
|
||||
@@ -65,14 +68,14 @@ void evttype_index_ruleset::ruleset_filters::remove_wrapper_from_list(filter_wra
|
||||
|
||||
void evttype_index_ruleset::ruleset_filters::add_filter(std::shared_ptr<filter_wrapper> wrap)
|
||||
{
|
||||
if(wrap->event_codes.empty())
|
||||
if(wrap->evttypes.empty())
|
||||
{
|
||||
// Should run for all event types
|
||||
add_wrapper_to_list(m_filter_all_event_types, wrap);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto &etype : wrap->event_codes)
|
||||
for(auto &etype : wrap->evttypes)
|
||||
{
|
||||
if(m_filter_by_event_type.size() <= etype)
|
||||
{
|
||||
@@ -88,13 +91,13 @@ void evttype_index_ruleset::ruleset_filters::add_filter(std::shared_ptr<filter_w
|
||||
|
||||
void evttype_index_ruleset::ruleset_filters::remove_filter(std::shared_ptr<filter_wrapper> wrap)
|
||||
{
|
||||
if(wrap->event_codes.empty())
|
||||
if(wrap->evttypes.empty())
|
||||
{
|
||||
remove_wrapper_from_list(m_filter_all_event_types, wrap);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto &etype : wrap->event_codes)
|
||||
for(auto &etype : wrap->evttypes)
|
||||
{
|
||||
if( etype < m_filter_by_event_type.size() )
|
||||
{
|
||||
@@ -138,24 +141,14 @@ bool evttype_index_ruleset::ruleset_filters::run(gen_event *evt, falco_rule& mat
|
||||
return false;
|
||||
}
|
||||
|
||||
libsinsp::events::set<ppm_sc_code> evttype_index_ruleset::ruleset_filters::sc_codes()
|
||||
void evttype_index_ruleset::ruleset_filters::evttypes_for_ruleset(std::set<uint16_t> &evttypes)
|
||||
{
|
||||
libsinsp::events::set<ppm_sc_code> res;
|
||||
for(auto &wrap : m_filters)
|
||||
{
|
||||
res.insert(wrap->sc_codes.begin(), wrap->sc_codes.end());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
evttypes.clear();
|
||||
|
||||
libsinsp::events::set<ppm_event_code> evttype_index_ruleset::ruleset_filters::event_codes()
|
||||
{
|
||||
libsinsp::events::set<ppm_event_code> res;
|
||||
for(auto &wrap : m_filters)
|
||||
{
|
||||
res.insert(wrap->event_codes.begin(), wrap->event_codes.end());
|
||||
evttypes.insert(wrap->evttypes.begin(), wrap->evttypes.end());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void evttype_index_ruleset::add(
|
||||
@@ -170,21 +163,18 @@ void evttype_index_ruleset::add(
|
||||
wrap->filter = filter;
|
||||
if(rule.source == falco_common::syscall_source)
|
||||
{
|
||||
wrap->sc_codes = libsinsp::filter::ast::ppm_sc_codes(condition.get());
|
||||
// todo(jasondellaluce): once libsinsp has its fixes, optimize this
|
||||
// by using libsinsp::events::ppm_set_to_event_set(wrap->sc_codes)
|
||||
wrap->event_codes = libsinsp::filter::ast::ppm_event_codes(condition.get());
|
||||
filter_evttype_resolver resolver;
|
||||
resolver.evttypes(condition, wrap->evttypes);
|
||||
}
|
||||
else
|
||||
{
|
||||
wrap->sc_codes = { };
|
||||
wrap->event_codes = { ppm_event_code::PPME_PLUGINEVENT_E };
|
||||
wrap->evttypes = { ppm_event_type::PPME_PLUGINEVENT_E };
|
||||
}
|
||||
m_filters.insert(wrap);
|
||||
}
|
||||
catch (const sinsp_exception& e)
|
||||
{
|
||||
throw falco_exception(std::string(e.what()));
|
||||
throw falco_exception(string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,17 +193,17 @@ void evttype_index_ruleset::clear()
|
||||
m_filters.clear();
|
||||
}
|
||||
|
||||
void evttype_index_ruleset::enable(const std::string &substring, bool match_exact, uint16_t ruleset_id)
|
||||
void evttype_index_ruleset::enable(const string &substring, bool match_exact, uint16_t ruleset_id)
|
||||
{
|
||||
enable_disable(substring, match_exact, true, ruleset_id);
|
||||
}
|
||||
|
||||
void evttype_index_ruleset::disable(const std::string &substring, bool match_exact, uint16_t ruleset_id)
|
||||
void evttype_index_ruleset::disable(const string &substring, bool match_exact, uint16_t ruleset_id)
|
||||
{
|
||||
enable_disable(substring, match_exact, false, ruleset_id);
|
||||
}
|
||||
|
||||
void evttype_index_ruleset::enable_disable(const std::string &substring, bool match_exact, bool enabled, uint16_t ruleset_id)
|
||||
void evttype_index_ruleset::enable_disable(const string &substring, bool match_exact, bool enabled, uint16_t ruleset_id)
|
||||
{
|
||||
while(m_rulesets.size() < (size_t)ruleset_id + 1)
|
||||
{
|
||||
@@ -233,7 +223,7 @@ void evttype_index_ruleset::enable_disable(const std::string &substring, bool ma
|
||||
}
|
||||
else
|
||||
{
|
||||
matches = (substring == "" || (wrap->rule.name.find(substring) != std::string::npos));
|
||||
matches = (substring == "" || (wrap->rule.name.find(substring) != string::npos));
|
||||
}
|
||||
|
||||
if(matches)
|
||||
@@ -250,17 +240,17 @@ void evttype_index_ruleset::enable_disable(const std::string &substring, bool ma
|
||||
}
|
||||
}
|
||||
|
||||
void evttype_index_ruleset::enable_tags(const std::set<std::string> &tags, uint16_t ruleset_id)
|
||||
void evttype_index_ruleset::enable_tags(const set<string> &tags, uint16_t ruleset_id)
|
||||
{
|
||||
enable_disable_tags(tags, true, ruleset_id);
|
||||
}
|
||||
|
||||
void evttype_index_ruleset::disable_tags(const std::set<std::string> &tags, uint16_t ruleset_id)
|
||||
void evttype_index_ruleset::disable_tags(const set<string> &tags, uint16_t ruleset_id)
|
||||
{
|
||||
enable_disable_tags(tags, false, ruleset_id);
|
||||
}
|
||||
|
||||
void evttype_index_ruleset::enable_disable_tags(const std::set<std::string> &tags, bool enabled, uint16_t ruleset_id)
|
||||
void evttype_index_ruleset::enable_disable_tags(const set<string> &tags, bool enabled, uint16_t ruleset_id)
|
||||
{
|
||||
while(m_rulesets.size() < (size_t)ruleset_id + 1)
|
||||
{
|
||||
@@ -269,7 +259,7 @@ void evttype_index_ruleset::enable_disable_tags(const std::set<std::string> &tag
|
||||
|
||||
for(const auto &wrap : m_filters)
|
||||
{
|
||||
std::set<std::string> intersect;
|
||||
std::set<string> intersect;
|
||||
|
||||
set_intersection(tags.begin(), tags.end(),
|
||||
wrap->rule.tags.begin(), wrap->rule.tags.end(),
|
||||
@@ -309,29 +299,12 @@ bool evttype_index_ruleset::run(gen_event *evt, falco_rule& match, uint16_t rule
|
||||
return m_rulesets[ruleset_id]->run(evt, match);
|
||||
}
|
||||
|
||||
void evttype_index_ruleset::enabled_evttypes(std::set<uint16_t> &evttypes, uint16_t ruleset_id)
|
||||
void evttype_index_ruleset::enabled_evttypes(set<uint16_t> &evttypes, uint16_t ruleset_id)
|
||||
{
|
||||
evttypes.clear();
|
||||
for (const auto& e : enabled_event_codes(ruleset_id))
|
||||
if(m_rulesets.size() < (size_t)ruleset_id + 1)
|
||||
{
|
||||
evttypes.insert((uint16_t) e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
libsinsp::events::set<ppm_sc_code> evttype_index_ruleset::enabled_sc_codes(uint16_t ruleset)
|
||||
{
|
||||
if(m_rulesets.size() < (size_t)ruleset + 1)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return m_rulesets[ruleset]->sc_codes();
|
||||
}
|
||||
|
||||
libsinsp::events::set<ppm_event_code> evttype_index_ruleset::enabled_event_codes(uint16_t ruleset)
|
||||
{
|
||||
if(m_rulesets.size() < (size_t)ruleset + 1)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return m_rulesets[ruleset]->event_codes();
|
||||
return m_rulesets[ruleset_id]->evttypes_for_ruleset(evttypes);
|
||||
}
|
||||
|
||||
@@ -70,17 +70,11 @@ public:
|
||||
const std::set<std::string> &tags,
|
||||
uint16_t rulset_id) override;
|
||||
|
||||
// note(jasondellaluce): this is deprecated, must use the new
|
||||
// typing-improved `enabled_event_codes` and `enabled_sc_codes` instead
|
||||
// todo(jasondellaluce): remove this in future code refactors
|
||||
// evttypes for a ruleset
|
||||
void enabled_evttypes(
|
||||
std::set<uint16_t> &evttypes,
|
||||
uint16_t ruleset) override;
|
||||
|
||||
libsinsp::events::set<ppm_sc_code> enabled_sc_codes(uint16_t ruleset) override;
|
||||
|
||||
libsinsp::events::set<ppm_event_code> enabled_event_codes(uint16_t ruleset) override;
|
||||
|
||||
private:
|
||||
|
||||
// Helper used by enable()/disable()
|
||||
@@ -99,8 +93,7 @@ private:
|
||||
struct filter_wrapper
|
||||
{
|
||||
falco_rule rule;
|
||||
libsinsp::events::set<ppm_sc_code> sc_codes;
|
||||
libsinsp::events::set<ppm_event_code> event_codes;
|
||||
std::set<uint16_t> evttypes;
|
||||
std::shared_ptr<gen_event_filter> filter;
|
||||
};
|
||||
|
||||
@@ -120,9 +113,7 @@ private:
|
||||
|
||||
bool run(gen_event *evt, falco_rule& match);
|
||||
|
||||
libsinsp::events::set<ppm_sc_code> sc_codes();
|
||||
|
||||
libsinsp::events::set<ppm_event_code> event_codes();
|
||||
void evttypes_for_ruleset(std::set<uint16_t> &evttypes);
|
||||
|
||||
private:
|
||||
void add_wrapper_to_list(filter_wrapper_list &wrappers, std::shared_ptr<filter_wrapper> wrap);
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
#include "falco_common.h"
|
||||
|
||||
static std::vector<std::string> priority_names = {
|
||||
static vector<string> priority_names = {
|
||||
"Emergency",
|
||||
"Alert",
|
||||
"Critical",
|
||||
@@ -27,7 +27,7 @@ static std::vector<std::string> priority_names = {
|
||||
"Debug"
|
||||
};
|
||||
|
||||
bool falco_common::parse_priority(std::string v, priority_type& out)
|
||||
bool falco_common::parse_priority(string v, priority_type& out)
|
||||
{
|
||||
for (size_t i = 0; i < priority_names.size(); i++)
|
||||
{
|
||||
@@ -44,7 +44,7 @@ bool falco_common::parse_priority(std::string v, priority_type& out)
|
||||
return false;
|
||||
}
|
||||
|
||||
falco_common::priority_type falco_common::parse_priority(std::string v)
|
||||
falco_common::priority_type falco_common::parse_priority(string v)
|
||||
{
|
||||
falco_common::priority_type out;
|
||||
if (!parse_priority(v, out))
|
||||
@@ -54,7 +54,7 @@ falco_common::priority_type falco_common::parse_priority(std::string v)
|
||||
return out;
|
||||
}
|
||||
|
||||
bool falco_common::format_priority(priority_type v, std::string& out, bool shortfmt)
|
||||
bool falco_common::format_priority(priority_type v, string& out, bool shortfmt)
|
||||
{
|
||||
if ((size_t) v < priority_names.size())
|
||||
{
|
||||
@@ -71,12 +71,12 @@ bool falco_common::format_priority(priority_type v, std::string& out, bool short
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string falco_common::format_priority(priority_type v, bool shortfmt)
|
||||
string falco_common::format_priority(priority_type v, bool shortfmt)
|
||||
{
|
||||
std::string out;
|
||||
string out;
|
||||
if(!format_priority(v, out, shortfmt))
|
||||
{
|
||||
throw falco_exception("Unknown priority enum value: " + std::to_string(v));
|
||||
throw falco_exception("Unknown priority enum value: " + to_string(v));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -52,7 +52,7 @@ struct falco_exception : std::exception
|
||||
|
||||
namespace falco_common
|
||||
{
|
||||
const std::string syscall_source = "syscall";
|
||||
const string syscall_source = "syscall";
|
||||
|
||||
// Same as numbers/indices into the above vector
|
||||
enum priority_type
|
||||
|
||||
@@ -15,14 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <io.h>
|
||||
#define srandom srand
|
||||
#define random rand
|
||||
#endif
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
@@ -45,6 +38,7 @@ limitations under the License.
|
||||
|
||||
const std::string falco_engine::s_default_ruleset = "falco-default-ruleset";
|
||||
|
||||
using namespace std;
|
||||
using namespace falco;
|
||||
|
||||
falco_engine::falco_engine(bool seed_rng)
|
||||
@@ -91,7 +85,7 @@ const falco_source* falco_engine::find_source(std::size_t index) const
|
||||
auto ret = m_sources.at(index);
|
||||
if(!ret)
|
||||
{
|
||||
throw falco_exception("Unknown event source index " + std::to_string(index));
|
||||
throw falco_exception("Unknown event source index " + to_string(index));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -175,7 +169,7 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
|
||||
}
|
||||
}
|
||||
|
||||
void falco_engine::load_rules(const std::string &rules_content, bool verbose, bool all_events)
|
||||
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events)
|
||||
{
|
||||
static const std::string no_name = "N/A";
|
||||
|
||||
@@ -228,7 +222,7 @@ void falco_engine::load_rules_file(const std::string &rules_filename, bool verbo
|
||||
interpret_load_result(res, rules_filename, rules_content, verbose);
|
||||
}
|
||||
|
||||
std::unique_ptr<load_result> falco_engine::load_rules_file(const std::string &rules_filename)
|
||||
std::unique_ptr<load_result> falco_engine::load_rules_file(const string &rules_filename)
|
||||
{
|
||||
std::string rules_content;
|
||||
|
||||
@@ -249,7 +243,7 @@ std::unique_ptr<load_result> falco_engine::load_rules_file(const std::string &ru
|
||||
return load_rules(rules_content, rules_filename);
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule(const std::string &substring, bool enabled, const std::string &ruleset)
|
||||
void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset)
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
bool match_exact = false;
|
||||
@@ -267,7 +261,7 @@ void falco_engine::enable_rule(const std::string &substring, bool enabled, const
|
||||
}
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule_exact(const std::string &rule_name, bool enabled, const std::string &ruleset)
|
||||
void falco_engine::enable_rule_exact(const string &rule_name, bool enabled, const string &ruleset)
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
bool match_exact = true;
|
||||
@@ -285,7 +279,7 @@ void falco_engine::enable_rule_exact(const std::string &rule_name, bool enabled,
|
||||
}
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule_by_tag(const std::set<std::string> &tags, bool enabled, const std::string &ruleset)
|
||||
void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, const string &ruleset)
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
@@ -334,23 +328,13 @@ void falco_engine::evttypes_for_ruleset(std::string &source, std::set<uint16_t>
|
||||
find_source(source)->ruleset->enabled_evttypes(evttypes, find_ruleset_id(ruleset));
|
||||
}
|
||||
|
||||
libsinsp::events::set<ppm_sc_code> falco_engine::sc_codes_for_ruleset(const std::string &source, const std::string &ruleset)
|
||||
{
|
||||
return find_source(source)->ruleset->enabled_sc_codes(find_ruleset_id(ruleset));
|
||||
}
|
||||
|
||||
libsinsp::events::set<ppm_event_code> falco_engine::event_codes_for_ruleset(const std::string &source, const std::string &ruleset)
|
||||
{
|
||||
return find_source(source)->ruleset->enabled_event_codes(find_ruleset_id(ruleset));
|
||||
}
|
||||
|
||||
std::shared_ptr<gen_event_formatter> falco_engine::create_formatter(const std::string &source,
|
||||
const std::string &output) const
|
||||
{
|
||||
return find_source(source)->formatter_factory->create_formatter(output);
|
||||
}
|
||||
|
||||
std::unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id)
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id)
|
||||
{
|
||||
// note: there are no thread-safety guarantees on the filter_ruleset::run()
|
||||
// method, but the thread-safety assumptions of falco_engine::process_event()
|
||||
@@ -376,10 +360,10 @@ std::unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size
|
||||
|
||||
if(should_drop_evt() || !source || !source->ruleset->run(ev, source->m_rule, ruleset_id))
|
||||
{
|
||||
return std::unique_ptr<struct rule_result>();
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
|
||||
std::unique_ptr<struct rule_result> res(new rule_result());
|
||||
unique_ptr<struct rule_result> res(new rule_result());
|
||||
res->evt = ev;
|
||||
res->rule = source->m_rule.name;
|
||||
res->source = source->m_rule.source;
|
||||
@@ -391,7 +375,7 @@ std::unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size
|
||||
return res;
|
||||
}
|
||||
|
||||
std::unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev)
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev)
|
||||
{
|
||||
return process_event(source_idx, ev, m_default_ruleset_id);
|
||||
}
|
||||
@@ -427,7 +411,7 @@ std::size_t falco_engine::add_source(const std::string &source,
|
||||
return m_sources.insert(src, source);
|
||||
}
|
||||
|
||||
void falco_engine::describe_rule(std::string *rule) const
|
||||
void falco_engine::describe_rule(string *rule) const
|
||||
{
|
||||
static const char* rule_fmt = "%-50s %s\n";
|
||||
fprintf(stdout, rule_fmt, "Rule", "Description");
|
||||
@@ -450,7 +434,7 @@ void falco_engine::describe_rule(std::string *rule) const
|
||||
|
||||
void falco_engine::print_stats() const
|
||||
{
|
||||
std::string out;
|
||||
string out;
|
||||
m_rule_stats_manager.format(m_rules, out);
|
||||
// todo(jasondellaluce): introduce a logging callback in Falco
|
||||
fprintf(stdout, "%s", out.c_str());
|
||||
@@ -463,7 +447,7 @@ bool falco_engine::is_source_valid(const std::string &source) const
|
||||
|
||||
void falco_engine::read_file(const std::string& filename, std::string& contents)
|
||||
{
|
||||
std::ifstream is;
|
||||
ifstream is;
|
||||
|
||||
is.open(filename);
|
||||
if (!is.is_open())
|
||||
@@ -471,8 +455,8 @@ void falco_engine::read_file(const std::string& filename, std::string& contents)
|
||||
throw falco_exception("Could not open " + filename + " for reading");
|
||||
}
|
||||
|
||||
contents.assign(std::istreambuf_iterator<char>(is),
|
||||
std::istreambuf_iterator<char>());
|
||||
contents.assign(istreambuf_iterator<char>(is),
|
||||
istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
void falco_engine::interpret_load_result(std::unique_ptr<load_result>& res,
|
||||
@@ -575,7 +559,7 @@ void falco_engine::set_sampling_multiplier(double sampling_multiplier)
|
||||
m_sampling_multiplier = sampling_multiplier;
|
||||
}
|
||||
|
||||
void falco_engine::set_extra(std::string &extra, bool replace_container_info)
|
||||
void falco_engine::set_extra(string &extra, bool replace_container_info)
|
||||
{
|
||||
m_extra = extra;
|
||||
m_replace_container_info = replace_container_info;
|
||||
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
// add k8s/mesos/container information to outputs when
|
||||
// available.
|
||||
//
|
||||
void set_extra(std::string &extra, bool replace_container_info);
|
||||
void set_extra(string &extra, bool replace_container_info);
|
||||
|
||||
// Represents the result of matching an event against a set of
|
||||
// rules.
|
||||
@@ -222,30 +222,11 @@ public:
|
||||
//
|
||||
// Given an event source and ruleset, fill in a bitset
|
||||
// containing the event types for which this ruleset can run.
|
||||
// note(jasondellaluce): this is deprecated, must use the new
|
||||
// typing-improved `enabled_event_codes` and `enabled_sc_codes` instead
|
||||
// todo(jasondellaluce): remove this in future code refactors
|
||||
//
|
||||
void evttypes_for_ruleset(std::string &source,
|
||||
std::set<uint16_t> &evttypes,
|
||||
const std::string &ruleset = s_default_ruleset);
|
||||
|
||||
//
|
||||
// Given an event source and ruleset, return the set of ppm_sc_codes
|
||||
// for which this ruleset can run and match events.
|
||||
//
|
||||
libsinsp::events::set<ppm_sc_code> sc_codes_for_ruleset(
|
||||
const std::string &source,
|
||||
const std::string &ruleset = s_default_ruleset);
|
||||
|
||||
//
|
||||
// Given an event source and ruleset, return the set of ppm_event_codes
|
||||
// for which this ruleset can run and match events.
|
||||
//
|
||||
libsinsp::events::set<ppm_event_code> event_codes_for_ruleset(
|
||||
const std::string &source,
|
||||
const std::string &ruleset = s_default_ruleset);
|
||||
|
||||
//
|
||||
// Given a source and output string, return an
|
||||
// gen_event_formatter that can format output strings for an
|
||||
@@ -303,7 +284,7 @@ private:
|
||||
stats_manager m_rule_stats_manager;
|
||||
|
||||
uint16_t m_next_ruleset_id;
|
||||
std::map<std::string, uint16_t> m_known_rulesets;
|
||||
std::map<string, uint16_t> m_known_rulesets;
|
||||
falco_common::priority_type m_min_priority;
|
||||
|
||||
//
|
||||
|
||||
@@ -16,9 +16,9 @@ limitations under the License.
|
||||
|
||||
// The version of rules/filter fields/etc supported by this Falco
|
||||
// engine.
|
||||
#define FALCO_ENGINE_VERSION (17)
|
||||
#define FALCO_ENGINE_VERSION (16)
|
||||
|
||||
// This is the result of running "falco --list -N | sha256sum" and
|
||||
// represents the fields supported by this version of Falco. It's used
|
||||
// at build time to detect a changed set of fields.
|
||||
#define FALCO_FIELDS_CHECKSUM "8684342b994f61ca75a1a494e1197b86b53715c59ad60de3768d4d74ea4ba2c9"
|
||||
#define FALCO_FIELDS_CHECKSUM "cc9d32916c719ce5aea164cdadb56207cbeff20033e278b99101964be7aa77a1"
|
||||
|
||||
@@ -24,10 +24,6 @@ limitations under the License.
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
|
||||
164
userspace/engine/filter_evttype_resolver.cpp
Normal file
164
userspace/engine/filter_evttype_resolver.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "filter_evttype_resolver.h"
|
||||
#include <sinsp.h>
|
||||
|
||||
using namespace libsinsp::filter;
|
||||
|
||||
extern sinsp_evttables g_infotables;
|
||||
|
||||
static bool is_evttype_operator(const std::string& op)
|
||||
{
|
||||
return op == "==" || op == "=" || op == "!=" || op == "in";
|
||||
}
|
||||
|
||||
|
||||
size_t falco_event_types::get_ppm_event_max()
|
||||
{
|
||||
return PPM_EVENT_MAX;
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::inversion(falco_event_types& types)
|
||||
{
|
||||
falco_event_types all_types;
|
||||
evttypes("", all_types);
|
||||
if (types != all_types) // we don't invert the "all types" set
|
||||
{
|
||||
types = all_types.diff(types);
|
||||
}
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::evttypes(const std::string& evtname, falco_event_types& out)
|
||||
{
|
||||
// Fill in from 2 to PPM_EVENT_MAX-1. 0 and 1 are excluded as
|
||||
// those are PPM_GENERIC_E/PPME_GENERIC_X
|
||||
const struct ppm_event_info* etable = g_infotables.m_event_info;
|
||||
for(uint16_t i = 2; i < PPM_EVENT_MAX; i++)
|
||||
{
|
||||
// Skip unused events or events not matching the requested evtname
|
||||
if(!sinsp::is_unused_event(i) && (evtname.empty() || std::string(etable[i].name) == evtname))
|
||||
{
|
||||
out.insert(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::evttypes(
|
||||
ast::expr* filter,
|
||||
std::set<uint16_t>& out) const
|
||||
{
|
||||
visitor v;
|
||||
v.m_expect_value = false;
|
||||
v.m_last_node_evttypes.clear();
|
||||
filter->accept(&v);
|
||||
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;});
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::evttypes(
|
||||
shared_ptr<ast::expr> filter,
|
||||
std::set<uint16_t>& out) const
|
||||
{
|
||||
visitor v;
|
||||
v.m_expect_value = false;
|
||||
v.m_last_node_evttypes.clear();
|
||||
filter.get()->accept(&v);
|
||||
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;} );
|
||||
}
|
||||
|
||||
// "and" nodes evttypes are the intersection of the evttypes of their children.
|
||||
// we initialize the set with "all event types"
|
||||
void filter_evttype_resolver::visitor::visit(ast::and_expr* e)
|
||||
{
|
||||
falco_event_types types;
|
||||
evttypes("", types);
|
||||
m_last_node_evttypes.clear();
|
||||
for (auto &c : e->children)
|
||||
{
|
||||
falco_event_types inters;
|
||||
c->accept(this);
|
||||
types = types.intersect(m_last_node_evttypes);
|
||||
}
|
||||
m_last_node_evttypes = types;
|
||||
}
|
||||
|
||||
// "or" nodes evttypes are the union of the evttypes their children
|
||||
void filter_evttype_resolver::visitor::visit(ast::or_expr* e)
|
||||
{
|
||||
falco_event_types types;
|
||||
m_last_node_evttypes.clear();
|
||||
for (auto &c : e->children)
|
||||
{
|
||||
c->accept(this);
|
||||
types.merge(m_last_node_evttypes);
|
||||
}
|
||||
m_last_node_evttypes = types;
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::visit(ast::not_expr* e)
|
||||
{
|
||||
m_last_node_evttypes.clear();
|
||||
e->child->accept(this);
|
||||
inversion(m_last_node_evttypes);
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::visit(ast::binary_check_expr* e)
|
||||
{
|
||||
m_last_node_evttypes.clear();
|
||||
if (e->field == "evt.type" && is_evttype_operator(e->op))
|
||||
{
|
||||
m_expect_value = true;
|
||||
e->value->accept(this);
|
||||
m_expect_value = false;
|
||||
if (e->op == "!=")
|
||||
{
|
||||
inversion(m_last_node_evttypes);
|
||||
}
|
||||
return;
|
||||
}
|
||||
evttypes("", m_last_node_evttypes);
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::visit(ast::unary_check_expr* e)
|
||||
{
|
||||
m_last_node_evttypes.clear();
|
||||
evttypes("", m_last_node_evttypes);
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::visit(ast::value_expr* e)
|
||||
{
|
||||
m_last_node_evttypes.clear();
|
||||
if (m_expect_value)
|
||||
{
|
||||
evttypes(e->value, m_last_node_evttypes);
|
||||
return;
|
||||
}
|
||||
evttypes("", m_last_node_evttypes);
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::visit(ast::list_expr* e)
|
||||
{
|
||||
m_last_node_evttypes.clear();
|
||||
if (m_expect_value)
|
||||
{
|
||||
for (auto &v : e->values)
|
||||
{
|
||||
evttypes(v, m_last_node_evttypes);
|
||||
}
|
||||
return;
|
||||
}
|
||||
evttypes("", m_last_node_evttypes);
|
||||
}
|
||||
210
userspace/engine/filter_evttype_resolver.h
Normal file
210
userspace/engine/filter_evttype_resolver.h
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filter/parser.h>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
class falco_event_types
|
||||
{
|
||||
private:
|
||||
using vec_t = std::vector<uint8_t>;
|
||||
vec_t m_types{};
|
||||
|
||||
static inline void check_range(uint16_t e)
|
||||
{
|
||||
static const auto enum_max = get_ppm_event_max();
|
||||
if(e > enum_max)
|
||||
{
|
||||
throw std::range_error("invalid event type");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
falco_event_types(falco_event_types&&) = default;
|
||||
falco_event_types(const falco_event_types&) = default;
|
||||
falco_event_types& operator=(falco_event_types&&) = default;
|
||||
falco_event_types& operator=(const falco_event_types&) = default;
|
||||
|
||||
static size_t get_ppm_event_max();
|
||||
|
||||
inline falco_event_types():
|
||||
m_types(get_ppm_event_max() + 1, 0)
|
||||
{
|
||||
}
|
||||
|
||||
inline void insert(uint16_t e)
|
||||
{
|
||||
check_range(e);
|
||||
m_types[e] = 1;
|
||||
}
|
||||
|
||||
void merge(const falco_event_types& other)
|
||||
{
|
||||
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
|
||||
{
|
||||
m_types[i] |= other.m_types[i];
|
||||
}
|
||||
}
|
||||
|
||||
void merge(const std::set<uint16_t>& other)
|
||||
{
|
||||
for(const auto& e : other)
|
||||
{
|
||||
insert(e);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool contains(uint16_t e) const
|
||||
{
|
||||
check_range(e);
|
||||
return m_types[e] != 0;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for(auto& v : m_types)
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool equals(const falco_event_types& other) const
|
||||
{
|
||||
return m_types == other.m_types;
|
||||
}
|
||||
|
||||
falco_event_types diff(const falco_event_types& other)
|
||||
{
|
||||
falco_event_types ret;
|
||||
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
|
||||
{
|
||||
if(m_types[i] == 1 && other.m_types[i] == 0)
|
||||
{
|
||||
ret.m_types[i] = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
falco_event_types intersect(const falco_event_types& other)
|
||||
{
|
||||
falco_event_types ret;
|
||||
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
|
||||
{
|
||||
if(m_types[i] == 1 && other.m_types[i] == 1)
|
||||
{
|
||||
ret.m_types[i] = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void for_each(std::function<bool(uint16_t)> consumer) const
|
||||
{
|
||||
for(uint16_t i = 0; i < m_types.size(); ++i)
|
||||
{
|
||||
if(m_types[i] != 0)
|
||||
{
|
||||
if(!consumer(i))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const falco_event_types& lhs, const falco_event_types& rhs)
|
||||
{
|
||||
return lhs.equals(rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const falco_event_types& lhs, const falco_event_types& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Helper class for finding event types
|
||||
*/
|
||||
class filter_evttype_resolver
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
\brief Collects the evttypes related to the provided event name.
|
||||
The event types are inserted in the set provided as parameter.
|
||||
The set is not cleared before inserting the elements.
|
||||
\param evtname The event name used to search event types. If an empty
|
||||
string is passed, all the available evttypes are collected
|
||||
\param out The set to be filled with the evttypes
|
||||
*/
|
||||
inline void evttypes(const std::string& evtname, falco_event_types& out) const
|
||||
{
|
||||
falco_event_types evt_types;
|
||||
visitor().evttypes(evtname, evt_types);
|
||||
evt_types.for_each([&out](uint16_t val)
|
||||
{out.insert(val); return true; });
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Visits a filter AST and collects all the evttypes for which
|
||||
the filter expression can be evaluated as true. The event types are
|
||||
inserted in the set provided as parameter. The set is not cleared before
|
||||
inserting the elements.
|
||||
\param filter The filter AST to be explored
|
||||
\param out The set to be filled with the evttypes
|
||||
*/
|
||||
void evttypes(
|
||||
libsinsp::filter::ast::expr* filter,
|
||||
std::set<uint16_t>& out) const;
|
||||
|
||||
/*!
|
||||
\brief Overloaded version of evttypes() that supports filters wrapped
|
||||
in shared pointers
|
||||
*/
|
||||
void evttypes(
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> filter,
|
||||
std::set<uint16_t>& out) const;
|
||||
|
||||
private:
|
||||
struct visitor : public libsinsp::filter::ast::expr_visitor
|
||||
{
|
||||
visitor(): m_expect_value(false) {}
|
||||
visitor(visitor&&) = default;
|
||||
visitor& operator = (visitor&&) = default;
|
||||
visitor(const visitor&) = default;
|
||||
visitor& operator = (const visitor&) = default;
|
||||
|
||||
bool m_expect_value;
|
||||
falco_event_types m_last_node_evttypes;
|
||||
|
||||
void visit(libsinsp::filter::ast::and_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::or_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::not_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::value_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::list_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::unary_check_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::binary_check_expr* e) override;
|
||||
void inversion(falco_event_types& types);
|
||||
void evttypes(const std::string& evtname, falco_event_types& out);
|
||||
};
|
||||
};
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
#include "filter_macro_resolver.h"
|
||||
#include "falco_common.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace libsinsp::filter;
|
||||
|
||||
bool filter_macro_resolver::run(libsinsp::filter::ast::expr*& filter)
|
||||
@@ -53,8 +54,8 @@ bool filter_macro_resolver::run(std::shared_ptr<libsinsp::filter::ast::expr>& fi
|
||||
}
|
||||
|
||||
void filter_macro_resolver::set_macro(
|
||||
std::string name,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr> macro)
|
||||
string name,
|
||||
shared_ptr<libsinsp::filter::ast::expr> macro)
|
||||
{
|
||||
m_macros[name] = macro;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ limitations under the License.
|
||||
#include <filter.h>
|
||||
#include <event.h>
|
||||
#include <gen_filter.h>
|
||||
#include <events/sinsp_events.h>
|
||||
|
||||
/*!
|
||||
\brief Manages a set of rulesets. A ruleset is a set of
|
||||
@@ -84,29 +83,10 @@ public:
|
||||
\brief Returns the union of the evttypes of all the rules enabled
|
||||
in a given ruleset
|
||||
\param ruleset_id The id of the ruleset to be used
|
||||
\deprecated Must use the new typing-improved `enabled_event_codes`
|
||||
and `enabled_sc_codes` instead
|
||||
\note todo(jasondellaluce): remove this in future refactors
|
||||
*/
|
||||
virtual void enabled_evttypes(
|
||||
std::set<uint16_t> &evttypes,
|
||||
uint16_t ruleset) = 0;
|
||||
|
||||
/*!
|
||||
\brief Returns the all the ppm_sc_codes matching the rules
|
||||
enabled in a given ruleset.
|
||||
\param ruleset_id The id of the ruleset to be used
|
||||
*/
|
||||
virtual libsinsp::events::set<ppm_sc_code> enabled_sc_codes(
|
||||
uint16_t ruleset) = 0;
|
||||
|
||||
/*!
|
||||
\brief Returns the all the ppm_event_codes matching the rules
|
||||
enabled in a given ruleset.
|
||||
\param ruleset_id The id of the ruleset to be used
|
||||
*/
|
||||
virtual libsinsp::events::set<ppm_event_code> enabled_event_codes(
|
||||
uint16_t ruleset) = 0;
|
||||
|
||||
/*!
|
||||
\brief Find those rules matching the provided substring and enable
|
||||
|
||||
@@ -21,13 +21,13 @@ using namespace falco;
|
||||
|
||||
static const char* no_value = "<NA>";
|
||||
|
||||
static inline bool is_unsafe_field(const std::string& f)
|
||||
static inline bool is_unsafe_field(const string& f)
|
||||
{
|
||||
return !strncmp(f.c_str(), "ka.", strlen("ka."))
|
||||
|| !strncmp(f.c_str(), "jevt.", strlen("jevt."));
|
||||
}
|
||||
|
||||
static inline bool is_equality_operator(const std::string& op)
|
||||
static inline bool is_equality_operator(const string& op)
|
||||
{
|
||||
return op == "==" || op == "=" || op == "!="
|
||||
|| op == "in" || op == "intersects" || op == "pmatch";
|
||||
|
||||
@@ -33,11 +33,11 @@ falco_formats::~falco_formats()
|
||||
{
|
||||
}
|
||||
|
||||
std::string falco_formats::format_event(gen_event *evt, const std::string &rule, const std::string &source,
|
||||
string falco_formats::format_event(gen_event *evt, const std::string &rule, const std::string &source,
|
||||
const std::string &level, const std::string &format, std::set<std::string> &tags,
|
||||
const std::string &hostname) const
|
||||
{
|
||||
std::string line;
|
||||
string line;
|
||||
|
||||
std::shared_ptr<gen_event_formatter> formatter;
|
||||
|
||||
@@ -48,7 +48,7 @@ std::string falco_formats::format_event(gen_event *evt, const std::string &rule,
|
||||
|
||||
if(formatter->get_output_format() == gen_event_formatter::OF_JSON)
|
||||
{
|
||||
std::string json_line;
|
||||
string json_line;
|
||||
|
||||
// Format the event into a json object with all fields resolved
|
||||
formatter->tostring(evt, json_line);
|
||||
@@ -67,14 +67,14 @@ std::string falco_formats::format_event(gen_event *evt, const std::string &rule,
|
||||
Json::Value event;
|
||||
Json::Value rule_tags;
|
||||
Json::FastWriter writer;
|
||||
std::string full_line;
|
||||
string full_line;
|
||||
unsigned int rule_tags_idx = 0;
|
||||
|
||||
// Convert the time-as-nanoseconds to a more json-friendly ISO8601.
|
||||
time_t evttime = evt->get_ts() / 1000000000;
|
||||
char time_sec[20]; // sizeof "YYYY-MM-DDTHH:MM:SS"
|
||||
char time_ns[12]; // sizeof ".sssssssssZ"
|
||||
std::string iso8601evttime;
|
||||
string iso8601evttime;
|
||||
|
||||
strftime(time_sec, sizeof(time_sec), "%FT%T", gmtime(&evttime));
|
||||
snprintf(time_ns, sizeof(time_ns), ".%09luZ", evt->get_ts() % 1000000000);
|
||||
@@ -131,14 +131,14 @@ std::string falco_formats::format_event(gen_event *evt, const std::string &rule,
|
||||
return line.c_str();
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> falco_formats::get_field_values(gen_event *evt, const std::string &source,
|
||||
map<string, string> falco_formats::get_field_values(gen_event *evt, const std::string &source,
|
||||
const std::string &format) const
|
||||
{
|
||||
std::shared_ptr<gen_event_formatter> formatter;
|
||||
|
||||
formatter = m_falco_engine->create_formatter(source, format);
|
||||
|
||||
std::map<std::string, std::string> ret;
|
||||
map<string, string> ret;
|
||||
|
||||
if (! formatter->get_field_values(evt, ret))
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
const std::string &level, const std::string &format, std::set<std::string> &tags,
|
||||
const std::string &hostname) const;
|
||||
|
||||
std::map<std::string, std::string> get_field_values(gen_event *evt, const std::string &source,
|
||||
map<string, string> get_field_values(gen_event *evt, const std::string &source,
|
||||
const std::string &format) const ;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -19,7 +19,6 @@ limitations under the License.
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
/*!
|
||||
\brief Simple wrapper of std::vector that allows random access
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
inline uint16_t get_type() const
|
||||
{
|
||||
// All k8s audit events have the single tag "1". - see falco_engine::process_k8s_audit_event
|
||||
return ppm_event_code::PPME_PLUGINEVENT_E;
|
||||
return ppm_event_type::PPME_PLUGINEVENT_E;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -21,6 +21,7 @@ limitations under the License.
|
||||
|
||||
#include "rule_loader_compiler.h"
|
||||
#include "filter_macro_resolver.h"
|
||||
#include "filter_evttype_resolver.h"
|
||||
#include "filter_warning_resolver.h"
|
||||
|
||||
#define MAX_VISIBILITY ((uint32_t) -1)
|
||||
@@ -69,7 +70,7 @@ static bool is_format_valid(const falco_source& source, std::string fmt, std::st
|
||||
formatter = source.formatter_factory->create_formatter(fmt);
|
||||
return true;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(exception &e)
|
||||
{
|
||||
err = e.what();
|
||||
return false;
|
||||
@@ -454,7 +455,7 @@ void rule_loader::compiler::compile_rule_infos(
|
||||
// failure.
|
||||
sinsp_filter_compiler compiler(cfg.sources.at(r.source)->filter_factory, ast.get());
|
||||
try {
|
||||
std::shared_ptr<gen_event_filter> filter(compiler.compile());
|
||||
shared_ptr<gen_event_filter> filter(compiler.compile());
|
||||
source->ruleset->add(*out.at(rule_id), filter, ast);
|
||||
}
|
||||
catch (const sinsp_exception& e)
|
||||
@@ -495,10 +496,11 @@ void rule_loader::compiler::compile_rule_infos(
|
||||
}
|
||||
|
||||
// populate set of event types and emit an special warning
|
||||
libsinsp::events::set<ppm_event_code> evttypes = { ppm_event_code::PPME_PLUGINEVENT_E };
|
||||
std::set<uint16_t> evttypes = { ppm_event_type::PPME_PLUGINEVENT_E };
|
||||
if(rule.source == falco_common::syscall_source)
|
||||
{
|
||||
evttypes = libsinsp::filter::ast::ppm_event_codes(ast.get());
|
||||
evttypes.clear();
|
||||
filter_evttype_resolver().evttypes(ast, evttypes);
|
||||
if ((evttypes.empty() || evttypes.size() > 100)
|
||||
&& r.warn_evttypes)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ limitations under the License.
|
||||
|
||||
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_YAML_VALIDATE, (err), (ctx)); } }
|
||||
|
||||
|
||||
// Don't call this directly, call decode_val/decode_optional_val instead.
|
||||
template <typename T>
|
||||
static void decode_val_generic(const YAML::Node& item, const char *key, T& out, const rule_loader::context& ctx, bool optional)
|
||||
@@ -87,7 +88,7 @@ static void decode_seq(const YAML::Node& item, const char *key,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void decode_items(const YAML::Node& item, std::vector<T>& out,
|
||||
static void decode_items(const YAML::Node& item, vector<T>& out,
|
||||
const rule_loader::context& ctx)
|
||||
{
|
||||
bool optional = false;
|
||||
@@ -100,7 +101,7 @@ static void decode_items(const YAML::Node& item, std::vector<T>& out,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void decode_tags(const YAML::Node& item, std::set<T>& out,
|
||||
static void decode_tags(const YAML::Node& item, set<T>& out,
|
||||
const rule_loader::context& ctx)
|
||||
{
|
||||
bool optional = true;
|
||||
@@ -135,7 +136,7 @@ static void decode_exception_info_entry(
|
||||
{
|
||||
THROW(val.Scalar().empty(), "Value must be non-empty", valctx);
|
||||
out.is_list = false;
|
||||
THROW(!YAML::convert<std::string>::decode(val, out.item), "Could not decode scalar value", valctx);
|
||||
THROW(!YAML::convert<string>::decode(val, out.item), "Could not decode scalar value", valctx);
|
||||
}
|
||||
if (val.IsSequence())
|
||||
{
|
||||
@@ -399,7 +400,7 @@ static void read_item(
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string priority;
|
||||
string priority;
|
||||
|
||||
// All of these are required
|
||||
decode_val(item, "condition", v.cond, ctx);
|
||||
|
||||
@@ -17,6 +17,8 @@ limitations under the License.
|
||||
#include "stats_manager.h"
|
||||
#include "falco_common.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
stats_manager::stats_manager()
|
||||
: m_total(0)
|
||||
{
|
||||
@@ -36,9 +38,9 @@ void stats_manager::clear()
|
||||
|
||||
void stats_manager::format(
|
||||
const indexed_vector<falco_rule>& rules,
|
||||
std::string& out) const
|
||||
string& out) const
|
||||
{
|
||||
std::string fmt;
|
||||
string fmt;
|
||||
out = "Events detected: " + to_string(m_total) + "\n";
|
||||
out += "Rule counts by severity:\n";
|
||||
for (size_t i = 0; i < m_by_priority.size(); i++)
|
||||
@@ -49,7 +51,7 @@ void stats_manager::format(
|
||||
falco_common::format_priority(
|
||||
(falco_common::priority_type) i, fmt, true);
|
||||
transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
|
||||
out += " " + fmt + ": " + std::to_string(val) + "\n";
|
||||
out += " " + fmt + ": " + to_string(val) + "\n";
|
||||
}
|
||||
}
|
||||
out += "Triggered rules by rule name:\n";
|
||||
@@ -58,7 +60,7 @@ void stats_manager::format(
|
||||
auto val = m_by_rule_id[i].get()->load();
|
||||
if (val > 0)
|
||||
{
|
||||
out += " " + rules.at(i)->name + ": " + std::to_string(val) + "\n";
|
||||
out += " " + rules.at(i)->name + ": " + to_string(val) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,12 +70,12 @@ void stats_manager::on_rule_loaded(const falco_rule& rule)
|
||||
while (m_by_rule_id.size() <= rule.id)
|
||||
{
|
||||
m_by_rule_id.emplace_back();
|
||||
m_by_rule_id[m_by_rule_id.size() - 1].reset(new std::atomic<uint64_t>(0));
|
||||
m_by_rule_id[m_by_rule_id.size() - 1].reset(new atomic<uint64_t>(0));
|
||||
}
|
||||
while (m_by_priority.size() <= (size_t) rule.priority)
|
||||
{
|
||||
m_by_priority.emplace_back();
|
||||
m_by_priority[m_by_priority.size() - 1].reset(new std::atomic<uint64_t>(0));
|
||||
m_by_priority[m_by_priority.size() - 1].reset(new atomic<uint64_t>(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
std::string& out) const;
|
||||
|
||||
private:
|
||||
std::atomic<uint64_t> m_total;
|
||||
std::vector<std::unique_ptr<std::atomic<uint64_t>>> m_by_priority;
|
||||
std::vector<std::unique_ptr<std::atomic<uint64_t>>> m_by_rule_id;
|
||||
atomic<uint64_t> m_total;
|
||||
std::vector<std::unique_ptr<atomic<uint64_t>>> m_by_priority;
|
||||
std::vector<std::unique_ptr<atomic<uint64_t>>> m_by_rule_id;
|
||||
};
|
||||
@@ -15,38 +15,36 @@ configure_file(config_falco.h.in config_falco.h)
|
||||
|
||||
set(
|
||||
FALCO_SOURCES
|
||||
app/app.cpp
|
||||
app/options.cpp
|
||||
app/restart_handler.cpp
|
||||
app/actions/helpers_generic.cpp
|
||||
app/actions/helpers_inspector.cpp
|
||||
app/actions/configure_interesting_sets.cpp
|
||||
app/actions/create_signal_handlers.cpp
|
||||
app/actions/daemonize.cpp
|
||||
app/actions/init_falco_engine.cpp
|
||||
app/actions/init_inspectors.cpp
|
||||
app/actions/init_clients.cpp
|
||||
app/actions/init_outputs.cpp
|
||||
app/actions/list_fields.cpp
|
||||
app/actions/list_plugins.cpp
|
||||
app/actions/load_config.cpp
|
||||
app/actions/load_plugins.cpp
|
||||
app/actions/load_rules_files.cpp
|
||||
app/actions/process_events.cpp
|
||||
app/actions/print_generated_gvisor_config.cpp
|
||||
app/actions/print_help.cpp
|
||||
app/actions/print_ignored_events.cpp
|
||||
app/actions/print_plugin_info.cpp
|
||||
app/actions/print_support.cpp
|
||||
app/actions/print_syscall_events.cpp
|
||||
app/actions/print_version.cpp
|
||||
app/actions/print_page_size.cpp
|
||||
app/actions/compute_syscall_buffer_size.cpp
|
||||
app/actions/select_event_sources.cpp
|
||||
app/actions/start_grpc_server.cpp
|
||||
app/actions/start_webserver.cpp
|
||||
app/actions/validate_rules_files.cpp
|
||||
app/actions/create_requested_paths.cpp
|
||||
application.cpp
|
||||
app_cmdline_options.cpp
|
||||
app_actions/create_signal_handlers.cpp
|
||||
app_actions/daemonize.cpp
|
||||
app_actions/init_falco_engine.cpp
|
||||
app_actions/init_inspectors.cpp
|
||||
app_actions/init_clients.cpp
|
||||
app_actions/init_outputs.cpp
|
||||
app_actions/list_fields.cpp
|
||||
app_actions/list_plugins.cpp
|
||||
app_actions/load_config.cpp
|
||||
app_actions/load_plugins.cpp
|
||||
app_actions/load_rules_files.cpp
|
||||
app_actions/open_inspector.cpp
|
||||
app_actions/process_events.cpp
|
||||
app_actions/print_generated_gvisor_config.cpp
|
||||
app_actions/print_help.cpp
|
||||
app_actions/print_ignored_events.cpp
|
||||
app_actions/print_plugin_info.cpp
|
||||
app_actions/print_support.cpp
|
||||
app_actions/print_syscall_events.cpp
|
||||
app_actions/print_version.cpp
|
||||
app_actions/print_page_size.cpp
|
||||
app_actions/compute_syscall_buffer_size.cpp
|
||||
app_actions/select_event_sources.cpp
|
||||
app_actions/start_grpc_server.cpp
|
||||
app_actions/start_webserver.cpp
|
||||
app_actions/validate_rules_files.cpp
|
||||
app_actions/create_requested_paths.cpp
|
||||
app_actions/configure_interesting_sets.cpp
|
||||
configuration.cpp
|
||||
logger.cpp
|
||||
falco_outputs.cpp
|
||||
@@ -57,6 +55,7 @@ set(
|
||||
event_drops.cpp
|
||||
stats_writer.cpp
|
||||
versions_info.cpp
|
||||
falco.cpp
|
||||
)
|
||||
|
||||
set(
|
||||
@@ -137,29 +136,24 @@ if(NOT MINIMAL_BUILD)
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(
|
||||
falco_application STATIC
|
||||
add_executable(
|
||||
falco
|
||||
${FALCO_SOURCES}
|
||||
)
|
||||
|
||||
add_dependencies(falco_application ${FALCO_DEPENDENCIES})
|
||||
add_dependencies(falco ${FALCO_DEPENDENCIES})
|
||||
|
||||
target_link_libraries(
|
||||
falco_application
|
||||
falco
|
||||
${FALCO_LIBRARIES}
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
falco_application
|
||||
falco
|
||||
PUBLIC
|
||||
${FALCO_INCLUDE_DIRECTORIES}
|
||||
)
|
||||
|
||||
add_executable(falco falco.cpp)
|
||||
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)
|
||||
add_custom_command(
|
||||
TARGET falco
|
||||
|
||||
@@ -1,60 +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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../state.h"
|
||||
#include "../run_result.h"
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
namespace actions {
|
||||
|
||||
falco::app::run_result configure_interesting_sets(falco::app::state& s);
|
||||
falco::app::run_result configure_syscall_buffer_size(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 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);
|
||||
falco::app::run_result init_outputs(falco::app::state& s);
|
||||
falco::app::run_result list_fields(falco::app::state& s);
|
||||
falco::app::run_result list_plugins(falco::app::state& s);
|
||||
falco::app::run_result load_config(falco::app::state& s);
|
||||
falco::app::run_result load_plugins(falco::app::state& s);
|
||||
falco::app::run_result load_rules_files(falco::app::state& s);
|
||||
falco::app::run_result print_generated_gvisor_config(falco::app::state& s);
|
||||
falco::app::run_result print_help(falco::app::state& s);
|
||||
falco::app::run_result print_ignored_events(falco::app::state& s);
|
||||
falco::app::run_result print_page_size(falco::app::state& s);
|
||||
falco::app::run_result print_plugin_info(falco::app::state& s);
|
||||
falco::app::run_result print_support(falco::app::state& s);
|
||||
falco::app::run_result print_syscall_events(falco::app::state& s);
|
||||
falco::app::run_result print_version(falco::app::state& s);
|
||||
falco::app::run_result process_events(falco::app::state& s);
|
||||
falco::app::run_result require_config_file(falco::app::state& s);
|
||||
falco::app::run_result select_event_sources(falco::app::state& s);
|
||||
falco::app::run_result start_grpc_server(falco::app::state& s);
|
||||
falco::app::run_result start_webserver(falco::app::state& s);
|
||||
falco::app::run_result stop_grpc_server(falco::app::state& s);
|
||||
falco::app::run_result stop_webserver(falco::app::state& s);
|
||||
falco::app::run_result unregister_signal_handlers(falco::app::state& s);
|
||||
falco::app::run_result validate_rules_files(falco::app::state& s);
|
||||
|
||||
}; // namespace actions
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
||||
@@ -1,234 +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 "actions.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
static void extract_base_syscalls_names(
|
||||
const std::unordered_set<std::string>& base_syscalls_names,
|
||||
std::unordered_set<std::string>& user_positive_names,
|
||||
std::unordered_set<std::string>& user_negative_names)
|
||||
{
|
||||
for (const std::string &ev : base_syscalls_names)
|
||||
{
|
||||
if (!ev.empty())
|
||||
{
|
||||
if (ev.at(0) == '!')
|
||||
{
|
||||
user_negative_names.insert(ev.substr(1, ev.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
user_positive_names.insert(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void check_for_rules_unsupported_events(falco::app::state& s, const libsinsp::events::set<ppm_sc_code>& rules_sc_set)
|
||||
{
|
||||
/* Unsupported events are those events that are used in the rules
|
||||
* but that are not part of the selected event set. For now, this
|
||||
* is expected to happen only for high volume I/O syscalls for
|
||||
* performance reasons. */
|
||||
auto unsupported_sc_set = rules_sc_set.diff(s.selected_sc_set);
|
||||
if (unsupported_sc_set.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the names of the events (syscall and non syscall events) that were not activated and print them. */
|
||||
auto names = libsinsp::events::sc_set_to_names(unsupported_sc_set);
|
||||
std::cerr << "Loaded rules match syscalls that are not activated (e.g. were removed via config settings such as no -A flag or negative base_syscalls elements) or unsupported with current configuration: warning (unsupported-evttype): " + concat_set_in_order(names) << std::endl;
|
||||
std::cerr << "If syscalls in rules include high volume I/O syscalls (-> activate via `-A` flag), else syscalls may have been removed via base_syscalls option or might be associated with syscalls undefined on your architecture (https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html)" << std::endl;
|
||||
}
|
||||
|
||||
static void select_event_set(falco::app::state& s, const libsinsp::events::set<ppm_sc_code>& rules_sc_set)
|
||||
{
|
||||
/* PPM syscall codes (sc) can be viewed as condensed libsinsp lookup table
|
||||
* to map a system call name to it's actual system syscall id (as defined
|
||||
* by the Linux kernel). Hence here we don't need syscall enter and exit distinction. */
|
||||
auto rules_names = libsinsp::events::sc_set_to_names(rules_sc_set);
|
||||
if (!rules_sc_set.empty())
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "(" + std::to_string(rules_names.size())
|
||||
+ ") syscalls in rules: " + concat_set_in_order(rules_names) + "\n");
|
||||
}
|
||||
|
||||
/* DEFAULT OPTION:
|
||||
* Current `sinsp_state_sc_set()` approach includes multiple steps:
|
||||
* (1) Enforce all positive syscalls from each Falco rule
|
||||
* (2) Enforce static Falco state set (non-adaptive, not conditioned by rules,
|
||||
* but based on PPME event table flags indicating generic sinsp state modifications)
|
||||
* -> Final set is union of (1) and (2)
|
||||
*
|
||||
* Fall-back if no valid positive syscalls in `base_syscalls.custom_set`,
|
||||
* e.g. when using `base_syscalls.custom_set` only for negative syscalls.
|
||||
*/
|
||||
auto base_sc_set = libsinsp::events::sinsp_state_sc_set();
|
||||
|
||||
/* USER OVERRIDE INPUT OPTION `base_syscalls.custom_set` etc. */
|
||||
std::unordered_set<std::string> user_positive_names = {};
|
||||
std::unordered_set<std::string> user_negative_names = {};
|
||||
extract_base_syscalls_names(s.config->m_base_syscalls_custom_set, user_positive_names, user_negative_names);
|
||||
auto user_positive_sc_set = libsinsp::events::names_to_sc_set(user_positive_names);
|
||||
auto user_negative_sc_set = libsinsp::events::names_to_sc_set(user_negative_names);
|
||||
|
||||
if (!user_positive_sc_set.empty())
|
||||
{
|
||||
// user overrides base event set
|
||||
base_sc_set = user_positive_sc_set;
|
||||
|
||||
// we re-transform from sc_set to names to make
|
||||
// sure that bad user inputs are ignored
|
||||
auto user_positive_sc_set_names = libsinsp::events::sc_set_to_names(user_positive_sc_set);
|
||||
falco_logger::log(LOG_DEBUG, "+(" + std::to_string(user_positive_sc_set_names.size())
|
||||
+ ") syscalls added (base_syscalls override): "
|
||||
+ concat_set_in_order(user_positive_sc_set_names) + "\n");
|
||||
auto invalid_positive_sc_set_names = unordered_set_difference(user_positive_names, user_positive_sc_set_names);
|
||||
if (!invalid_positive_sc_set_names.empty())
|
||||
{
|
||||
falco_logger::log(LOG_WARNING, "Invalid (positive) syscall names: warning (base_syscalls override): "
|
||||
+ concat_set_in_order(invalid_positive_sc_set_names));
|
||||
}
|
||||
}
|
||||
|
||||
// selected events are the union of the rules events set and the
|
||||
// base events set (either the default or the user-defined one)
|
||||
s.selected_sc_set = rules_sc_set.merge(base_sc_set);
|
||||
|
||||
/* REPLACE DEFAULT STATE, nothing else. Need to override s.selected_sc_set and have a separate logic block. */
|
||||
if (s.config->m_base_syscalls_repair && user_positive_sc_set.empty())
|
||||
{
|
||||
/* If `base_syscalls.repair` is specified, but `base_syscalls.custom_set` is empty we are replacing
|
||||
* the default `sinsp_state_sc_set()` enforcement with the alternative `sinsp_repair_state_sc_set`.
|
||||
* This approach only activates additional syscalls Falco needs beyond the
|
||||
* syscalls defined in each Falco rule that are absolutely necessary based
|
||||
* on the current rules configuration. */
|
||||
|
||||
// returned set already has rules_sc_set merged
|
||||
s.selected_sc_set = libsinsp::events::sinsp_repair_state_sc_set(rules_sc_set);
|
||||
}
|
||||
|
||||
if (!user_negative_sc_set.empty())
|
||||
{
|
||||
/* Remove negative base_syscalls events. */
|
||||
s.selected_sc_set = s.selected_sc_set.diff(user_negative_sc_set);
|
||||
|
||||
// we re-transform from sc_set to names to make
|
||||
// sure that bad user inputs are ignored
|
||||
auto user_negative_sc_set_names = libsinsp::events::sc_set_to_names(user_negative_sc_set);
|
||||
falco_logger::log(LOG_DEBUG, "-(" + std::to_string(user_negative_sc_set_names.size())
|
||||
+ ") syscalls removed (base_syscalls override): "
|
||||
+ concat_set_in_order(user_negative_sc_set_names) + "\n");
|
||||
auto invalid_negative_sc_set_names = unordered_set_difference(user_negative_names, user_negative_sc_set_names);
|
||||
if (!invalid_negative_sc_set_names.empty())
|
||||
{
|
||||
falco_logger::log(LOG_WARNING, "Invalid (negative) syscall names: warning (base_syscalls override): "
|
||||
+ concat_set_in_order(invalid_negative_sc_set_names));
|
||||
}
|
||||
}
|
||||
|
||||
/* Derive the diff between the additional syscalls added via libsinsp state
|
||||
enforcement and the syscalls from each Falco rule. We avoid printing
|
||||
this in case the user specified a custom set of base syscalls */
|
||||
auto non_rules_sc_set = s.selected_sc_set.diff(rules_sc_set);
|
||||
if (!non_rules_sc_set.empty() && user_positive_sc_set.empty())
|
||||
{
|
||||
auto non_rules_sc_set_names = libsinsp::events::sc_set_to_names(non_rules_sc_set);
|
||||
falco_logger::log(LOG_DEBUG, "+(" + std::to_string(non_rules_sc_set_names.size())
|
||||
+ ") syscalls (Falco's state engine set of syscalls): "
|
||||
+ concat_set_in_order(non_rules_sc_set_names) + "\n");
|
||||
}
|
||||
|
||||
/* -A flag behavior:
|
||||
* (1) default: all syscalls in rules included, sinsp state enforcement
|
||||
without high volume I/O syscalls
|
||||
* (2) -A flag set: all syscalls in rules included, sinsp state enforcement
|
||||
and allowing high volume I/O syscalls */
|
||||
if(!s.options.all_events)
|
||||
{
|
||||
auto ignored_sc_set = libsinsp::events::io_sc_set();
|
||||
auto erased_sc_set = s.selected_sc_set.intersect(ignored_sc_set);
|
||||
s.selected_sc_set = s.selected_sc_set.diff(ignored_sc_set);
|
||||
if (!erased_sc_set.empty())
|
||||
{
|
||||
auto erased_sc_set_names = libsinsp::events::sc_set_to_names(erased_sc_set);
|
||||
falco_logger::log(LOG_DEBUG, "-(" + std::to_string(erased_sc_set_names.size())
|
||||
+ ") ignored syscalls (-> activate via `-A` flag): "
|
||||
+ concat_set_in_order(erased_sc_set_names) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* If a custom set is specified (positive, negative, or both), we attempt
|
||||
* to repair it if configured to do so. */
|
||||
if (s.config->m_base_syscalls_repair && !s.config->m_base_syscalls_custom_set.empty())
|
||||
{
|
||||
/* If base_syscalls.repair is specified enforce state using `sinsp_repair_state_sc_set`.
|
||||
* This approach is an alternative to the default `sinsp_state_sc_set()` state enforcement
|
||||
* and only activates additional syscalls Falco needs beyond the syscalls defined in the
|
||||
* Falco rules that are absolutely necessary based on the current rules configuration. */
|
||||
auto selected_sc_set = s.selected_sc_set;
|
||||
s.selected_sc_set = libsinsp::events::sinsp_repair_state_sc_set(s.selected_sc_set);
|
||||
auto repaired_sc_set = s.selected_sc_set.diff(selected_sc_set);
|
||||
if (!repaired_sc_set.empty())
|
||||
{
|
||||
auto repaired_sc_set_names = libsinsp::events::sc_set_to_names(repaired_sc_set);
|
||||
falco_logger::log(LOG_INFO, "+(" + std::to_string(repaired_sc_set_names.size())
|
||||
+ ") repaired syscalls: " + concat_set_in_order(repaired_sc_set_names) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Hidden safety enforcement for `base_syscalls.custom_set` user
|
||||
* input override option (but keep as general safety enforcement)
|
||||
* -> sched_process_exit trace point activation (procexit event)
|
||||
* is necessary for continuous state engine cleanup,
|
||||
* else memory would grow rapidly and linearly over time. */
|
||||
s.selected_sc_set.insert(ppm_sc_code::PPM_SC_SCHED_PROCESS_EXIT);
|
||||
|
||||
if (!s.selected_sc_set.empty())
|
||||
{
|
||||
auto selected_sc_set_names = libsinsp::events::sc_set_to_names(s.selected_sc_set);
|
||||
falco_logger::log(LOG_DEBUG, "(" + std::to_string(selected_sc_set_names.size())
|
||||
+ ") syscalls selected in total (final set): "
|
||||
+ concat_set_in_order(selected_sc_set_names) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::configure_interesting_sets(falco::app::state& s)
|
||||
{
|
||||
if (s.engine == nullptr || s.config == nullptr)
|
||||
{
|
||||
return run_result::fatal("Broken 'configure_interesting_sets' preconditions: engine and config must be non-null");
|
||||
}
|
||||
|
||||
s.selected_sc_set.clear();
|
||||
|
||||
/* note: the set of events is the richest source of truth about
|
||||
* the events generable by an inspector, because they also carry information
|
||||
* about events that are old, unused, internal, and so on. As such, the
|
||||
* strategy is to first craft the actual set of selected events, and
|
||||
* then use it to obtain a set of enabled kernel tracepoints and a set
|
||||
* of syscall codes. Those last two sets will be passed down to the
|
||||
* inspector to instruct the kernel drivers on which kernel event should
|
||||
* be collected at runtime. */
|
||||
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);
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -1,175 +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 <functional>
|
||||
|
||||
#include "actions.h"
|
||||
#include "../app.h"
|
||||
#include "../signals.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
static std::shared_ptr<falco::app::restart_handler> s_restarter;
|
||||
|
||||
static void terminate_signal_handler(int signal)
|
||||
{
|
||||
falco::app::g_terminate_signal.trigger();
|
||||
}
|
||||
|
||||
static void reopen_outputs_signal_handler(int signal)
|
||||
{
|
||||
falco::app::g_reopen_outputs_signal.trigger();
|
||||
}
|
||||
|
||||
static void restart_signal_handler(int signal)
|
||||
{
|
||||
if (s_restarter != nullptr)
|
||||
{
|
||||
s_restarter->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
bool create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
{
|
||||
ret = run_result::ok();
|
||||
if(signal(sig, func) == SIG_ERR)
|
||||
{
|
||||
char errbuf[1024];
|
||||
if (strerror_r(errno, errbuf, sizeof(errbuf)) != 0)
|
||||
{
|
||||
snprintf(errbuf, sizeof(errbuf)-1, "Errno %d", errno);
|
||||
}
|
||||
|
||||
ret = run_result::fatal(std::string("Could not create signal handler for ") +
|
||||
strsignal(sig) +
|
||||
": " +
|
||||
errbuf);
|
||||
}
|
||||
|
||||
return ret.success;
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::create_signal_handlers(falco::app::state& s)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping signal handlers creation in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
falco::app::g_terminate_signal.reset();
|
||||
falco::app::g_restart_signal.reset();
|
||||
falco::app::g_reopen_outputs_signal.reset();
|
||||
|
||||
if (!g_terminate_signal.is_lock_free()
|
||||
|| !g_restart_signal.is_lock_free()
|
||||
|| !g_reopen_outputs_signal.is_lock_free())
|
||||
{
|
||||
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) ||
|
||||
! create_handler(SIGHUP, ::restart_signal_handler, ret))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
falco::app::restart_handler::watch_list_t files_to_watch;
|
||||
falco::app::restart_handler::watch_list_t dirs_to_watch;
|
||||
if (s.config->m_watch_config_files)
|
||||
{
|
||||
files_to_watch.push_back(s.options.conf_filename);
|
||||
files_to_watch.insert(
|
||||
files_to_watch.end(),
|
||||
s.config->m_loaded_rules_filenames.begin(),
|
||||
s.config->m_loaded_rules_filenames.end());
|
||||
dirs_to_watch.insert(
|
||||
dirs_to_watch.end(),
|
||||
s.config->m_loaded_rules_folders.begin(),
|
||||
s.config->m_loaded_rules_folders.end());
|
||||
}
|
||||
|
||||
s.restarter = std::make_shared<falco::app::restart_handler>([&s]{
|
||||
bool tmp = false;
|
||||
bool success = false;
|
||||
std::string err;
|
||||
falco::app::state tmp_state(s.cmdline, s.options);
|
||||
tmp_state.options.dry_run = true;
|
||||
try
|
||||
{
|
||||
success = falco::app::run(tmp_state, tmp, err);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
err = e.what();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
err = "unknown error";
|
||||
}
|
||||
|
||||
if (!success && s.outputs != nullptr)
|
||||
{
|
||||
std::string rule = "Falco internal: hot restart failure";
|
||||
std::string msg = rule + ": " + err;
|
||||
std::map<std::string, std::string> o = {};
|
||||
auto now = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
s.outputs->handle_msg(now, falco_common::PRIORITY_CRITICAL, msg, rule, o);
|
||||
}
|
||||
|
||||
return success;
|
||||
}, files_to_watch, dirs_to_watch);
|
||||
|
||||
ret = run_result::ok();
|
||||
ret.success = s.restarter->start(ret.errstr);
|
||||
ret.proceed = ret.success;
|
||||
if (ret.success)
|
||||
{
|
||||
s_restarter = s.restarter;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::unregister_signal_handlers(falco::app::state& s)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping unregistering signal handlers in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
s_restarter = nullptr;
|
||||
if (s.restarter != nullptr)
|
||||
{
|
||||
s.restarter->stop();
|
||||
}
|
||||
|
||||
run_result ret;
|
||||
if(! create_handler(SIGINT, SIG_DFL, ret) ||
|
||||
! create_handler(SIGTERM, SIG_DFL, ret) ||
|
||||
! create_handler(SIGUSR1, SIG_DFL, ret) ||
|
||||
! create_handler(SIGHUP, SIG_DFL, ret))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -1,78 +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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../state.h"
|
||||
#include "../run_result.h"
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
namespace actions {
|
||||
|
||||
bool check_rules_plugin_requirements(falco::app::state& s, std::string& err);
|
||||
void print_enabled_event_sources(falco::app::state& s);
|
||||
void activate_interesting_kernel_tracepoints(falco::app::state& s, std::unique_ptr<sinsp>& inspector);
|
||||
void check_for_ignored_events(falco::app::state& s);
|
||||
void format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os);
|
||||
falco::app::run_result open_offline_inspector(falco::app::state& s);
|
||||
falco::app::run_result open_live_inspector(
|
||||
falco::app::state& s,
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
const std::string& source);
|
||||
|
||||
template<class InputIterator>
|
||||
void read_files(InputIterator begin, InputIterator end,
|
||||
std::vector<std::string>& rules_contents,
|
||||
falco::load_result::rules_contents_t& rc)
|
||||
{
|
||||
// Read the contents in a first pass
|
||||
for(auto it = begin; it != end; it++)
|
||||
{
|
||||
std::string &filename = *it;
|
||||
std::ifstream is;
|
||||
is.open(filename);
|
||||
if (!is.is_open())
|
||||
{
|
||||
throw falco_exception("Could not open file " + filename + " for reading");
|
||||
}
|
||||
|
||||
std::string rules_content((std::istreambuf_iterator<char>(is)),
|
||||
std::istreambuf_iterator<char>());
|
||||
rules_contents.emplace_back(std::move(rules_content));
|
||||
}
|
||||
|
||||
// Populate the map in a second pass to avoid
|
||||
// references becoming invalid.
|
||||
auto it = begin;
|
||||
auto rit = rules_contents.begin();
|
||||
for(; it != end && rit != rules_contents.end(); it++, rit++)
|
||||
{
|
||||
rc.emplace(*it, *rit);
|
||||
}
|
||||
|
||||
// Both it and rit must be at the end, otherwise
|
||||
// there's a bug in the above
|
||||
if(it != end || rit != rules_contents.end())
|
||||
{
|
||||
throw falco_exception("Unexpected mismatch in rules content name/rules content sets?");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}; // namespace actions
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
||||
@@ -1,69 +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 "helpers.h"
|
||||
#include <plugin_manager.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
bool falco::app::actions::check_rules_plugin_requirements(falco::app::state& s, std::string& err)
|
||||
{
|
||||
// Ensure that all plugins are compatible with the loaded set of rules
|
||||
// note: offline inspector contains all the loaded plugins
|
||||
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
|
||||
for (const auto &plugin : s.offline_inspector->get_plugin_manager()->plugins())
|
||||
{
|
||||
falco_engine::plugin_version_requirement req;
|
||||
req.name = plugin->name();
|
||||
req.version = plugin->plugin_version().as_string();
|
||||
plugin_reqs.push_back(req);
|
||||
}
|
||||
return s.engine->check_plugin_requirements(plugin_reqs, err);
|
||||
}
|
||||
|
||||
void falco::app::actions::print_enabled_event_sources(falco::app::state& s)
|
||||
{
|
||||
/* Print all enabled sources. */
|
||||
std::string str;
|
||||
for (const auto &s : s.enabled_sources)
|
||||
{
|
||||
str += str.empty() ? "" : ", ";
|
||||
str += s;
|
||||
}
|
||||
falco_logger::log(LOG_INFO, "Enabled event sources: " + str + "\n");
|
||||
}
|
||||
|
||||
void falco::app::actions::format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os)
|
||||
{
|
||||
os << "Name: " << p->name() << std::endl;
|
||||
os << "Description: " << p->description() << std::endl;
|
||||
os << "Contact: " << p->contact() << std::endl;
|
||||
os << "Version: " << p->plugin_version().as_string() << std::endl;
|
||||
os << "Capabilities: " << std::endl;
|
||||
if(p->caps() & CAP_SOURCING)
|
||||
{
|
||||
os << " - Event Sourcing (ID=" << p->id();
|
||||
os << ", source='" << p->event_source() << "')" << std::endl;
|
||||
}
|
||||
if(p->caps() & CAP_EXTRACTION)
|
||||
{
|
||||
os << " - Field Extraction" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,140 +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 "actions.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#include <plugin_manager.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::load_rules_files(falco::app::state& s)
|
||||
{
|
||||
std::string all_rules;
|
||||
|
||||
if (!s.options.rules_filenames.empty())
|
||||
{
|
||||
s.config->m_rules_filenames = s.options.rules_filenames;
|
||||
}
|
||||
|
||||
if(s.config->m_rules_filenames.empty())
|
||||
{
|
||||
return run_result::fatal("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
|
||||
}
|
||||
|
||||
falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
|
||||
for (const auto& path : s.config->m_rules_filenames)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, std::string(" ") + path + "\n");
|
||||
}
|
||||
|
||||
for (const auto &path : s.config->m_rules_filenames)
|
||||
{
|
||||
falco_configuration::read_rules_file_directory(path, s.config->m_loaded_rules_filenames, s.config->m_loaded_rules_folders);
|
||||
}
|
||||
|
||||
std::vector<std::string> rules_contents;
|
||||
falco::load_result::rules_contents_t rc;
|
||||
|
||||
try {
|
||||
read_files(s.config->m_loaded_rules_filenames.begin(),
|
||||
s.config->m_loaded_rules_filenames.end(),
|
||||
rules_contents,
|
||||
rc);
|
||||
}
|
||||
catch(falco_exception& e)
|
||||
{
|
||||
return run_result::fatal(e.what());
|
||||
}
|
||||
|
||||
for(auto &filename : s.config->m_loaded_rules_filenames)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + "\n");
|
||||
std::unique_ptr<falco::load_result> res;
|
||||
|
||||
res = s.engine->load_rules(rc.at(filename), filename);
|
||||
|
||||
if(!res->successful())
|
||||
{
|
||||
// Return the summary version as the error
|
||||
return run_result::fatal(res->as_string(true, rc));
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
std::string err = "";
|
||||
if (!check_rules_plugin_requirements(s, err))
|
||||
{
|
||||
return run_result::fatal(err);
|
||||
}
|
||||
|
||||
for (const auto& substring : s.options.disabled_rule_substrings)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
|
||||
s.engine->enable_rule(substring, false);
|
||||
}
|
||||
|
||||
if(!s.options.disabled_rule_tags.empty())
|
||||
{
|
||||
for(auto &tag : s.options.disabled_rule_tags)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
|
||||
}
|
||||
s.engine->enable_rule_by_tag(s.options.disabled_rule_tags, false);
|
||||
}
|
||||
|
||||
if(!s.options.enabled_rule_tags.empty())
|
||||
{
|
||||
// Since we only want to enable specific
|
||||
// rules, first disable all rules.
|
||||
s.engine->enable_rule(all_rules, false);
|
||||
for(auto &tag : s.options.enabled_rule_tags)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
|
||||
}
|
||||
s.engine->enable_rule_by_tag(s.options.enabled_rule_tags, true);
|
||||
}
|
||||
|
||||
if(s.options.all_events && s.options.modern_bpf)
|
||||
{
|
||||
/* Right now the modern BPF probe doesn't support the -A flag, we implemented just
|
||||
* the "simple set" syscalls.
|
||||
*/
|
||||
falco_logger::log(LOG_INFO, "The '-A' flag has no effect with the modern BPF probe, no further syscalls will be added\n");
|
||||
}
|
||||
|
||||
if (s.options.describe_all_rules)
|
||||
{
|
||||
s.engine->describe_rule(NULL);
|
||||
return run_result::exit();
|
||||
}
|
||||
|
||||
if (!s.options.describe_rule.empty())
|
||||
{
|
||||
s.engine->describe_rule(&(s.options.describe_rule));
|
||||
return run_result::exit();
|
||||
}
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -1,38 +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 "actions.h"
|
||||
#include "helpers.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::print_ignored_events(falco::app::state& s)
|
||||
{
|
||||
if(!s.options.print_ignored_events)
|
||||
{
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
std::cout << "Ignored I/O syscall(s):" << std::endl;
|
||||
for(const auto& it : libsinsp::events::sc_set_to_names(libsinsp::events::io_sc_set()))
|
||||
{
|
||||
std::cout << "- " << it.c_str() << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
return run_result::exit();
|
||||
}
|
||||
@@ -1,117 +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 "actions.h"
|
||||
#include "helpers.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
struct event_entry
|
||||
{
|
||||
bool is_enter;
|
||||
bool available;
|
||||
std::string name;
|
||||
const ppm_event_info* info;
|
||||
};
|
||||
|
||||
static std::vector<event_entry> get_event_entries(bool include_generics, const libsinsp::events::set<ppm_event_code>& available)
|
||||
{
|
||||
event_entry entry;
|
||||
std::vector<event_entry> events;
|
||||
|
||||
// skip generic events
|
||||
for (const auto& e: libsinsp::events::all_event_set())
|
||||
{
|
||||
if (!libsinsp::events::is_generic(e)
|
||||
&& !libsinsp::events::is_old_version_event(e)
|
||||
&& !libsinsp::events::is_unused_event(e)
|
||||
&& !libsinsp::events::is_unknown_event(e))
|
||||
{
|
||||
entry.is_enter = PPME_IS_ENTER(e);
|
||||
entry.available = available.contains(e);
|
||||
entry.info = libsinsp::events::info(e);
|
||||
entry.name = entry.info->name;
|
||||
events.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (include_generics)
|
||||
{
|
||||
// append generic events
|
||||
const auto names = libsinsp::events::event_set_to_names({ppm_event_code::PPME_GENERIC_E});
|
||||
for (const auto& name : names)
|
||||
{
|
||||
entry.is_enter = PPME_IS_ENTER(ppm_event_code::PPME_GENERIC_E);
|
||||
entry.available = available.contains(ppm_event_code::PPME_GENERIC_E);
|
||||
entry.info = libsinsp::events::info(ppm_event_code::PPME_GENERIC_E);
|
||||
entry.name = name;
|
||||
events.push_back(entry);
|
||||
|
||||
entry.is_enter = PPME_IS_ENTER(ppm_event_code::PPME_GENERIC_X);
|
||||
entry.available = available.contains(ppm_event_code::PPME_GENERIC_X);
|
||||
entry.info = libsinsp::events::info(ppm_event_code::PPME_GENERIC_X);
|
||||
entry.name = name;
|
||||
events.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::print_syscall_events(falco::app::state& s)
|
||||
{
|
||||
if(s.options.list_syscall_events)
|
||||
{
|
||||
const auto events = get_event_entries(true, libsinsp::events::all_event_set());
|
||||
|
||||
if(s.options.markdown)
|
||||
{
|
||||
printf("Falco | Dir | Event\n");
|
||||
printf(":-----|:----|:-----\n");
|
||||
}
|
||||
|
||||
for (const auto& e : events)
|
||||
{
|
||||
char dir = e.is_enter ? '>' : '<';
|
||||
if (s.options.markdown)
|
||||
{
|
||||
printf(e.available ? "Yes" : "No");
|
||||
printf(" | %c | **%s**(", dir, e.name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%c %s(", dir, e.name.c_str());
|
||||
}
|
||||
|
||||
for(uint32_t k = 0; k < e.info->nparams; k++)
|
||||
{
|
||||
if(k != 0)
|
||||
{
|
||||
printf(", ");
|
||||
}
|
||||
|
||||
printf("%s %s", param_type_to_string(e.info->params[k].type),
|
||||
e.info->params[k].name);
|
||||
}
|
||||
printf(")\n");
|
||||
}
|
||||
|
||||
return run_result::exit();
|
||||
}
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -1,77 +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 "actions.h"
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
#include "grpc_server.h"
|
||||
#endif
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::start_grpc_server(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
// gRPC server
|
||||
if(s.config->m_grpc_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping starting gRPC server in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
falco_logger::log(LOG_INFO, "gRPC server threadiness equals to " + std::to_string(s.config->m_grpc_threadiness) + "\n");
|
||||
// TODO(fntlnz,leodido): when we want to spawn multiple threads we need to have a queue per thread, or implement
|
||||
// different queuing mechanisms, round robin, fanout? What we want to achieve?
|
||||
s.grpc_server.init(
|
||||
s.config->m_grpc_bind_address,
|
||||
s.config->m_grpc_threadiness,
|
||||
s.config->m_grpc_private_key,
|
||||
s.config->m_grpc_cert_chain,
|
||||
s.config->m_grpc_root_certs,
|
||||
s.config->m_log_level
|
||||
);
|
||||
s.grpc_server_thread = std::thread([&s] {
|
||||
s.grpc_server.run();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::stop_grpc_server(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
if(s.config->m_grpc_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping stopping gRPC server in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
if(s.grpc_server_thread.joinable())
|
||||
{
|
||||
s.grpc_server.shutdown();
|
||||
s.grpc_server_thread.join();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -1,72 +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 "actions.h"
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
#include "webserver.h"
|
||||
#endif
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::start_webserver(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
if(!s.is_capture_mode() && s.config->m_webserver_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping starting webserver in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
std::string ssl_option = (s.config->m_webserver_ssl_enabled ? " (SSL)" : "");
|
||||
falco_logger::log(LOG_INFO, "Starting health webserver with threadiness "
|
||||
+ std::to_string(s.config->m_webserver_threadiness)
|
||||
+ ", listening on port "
|
||||
+ std::to_string(s.config->m_webserver_listen_port)
|
||||
+ ssl_option + "\n");
|
||||
|
||||
s.webserver.start(
|
||||
s.offline_inspector,
|
||||
s.config->m_webserver_threadiness,
|
||||
s.config->m_webserver_listen_port,
|
||||
s.config->m_webserver_k8s_healthz_endpoint,
|
||||
s.config->m_webserver_ssl_certificate,
|
||||
s.config->m_webserver_ssl_enabled);
|
||||
}
|
||||
#endif
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::stop_webserver(falco::app::state& s)
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
if(!s.is_capture_mode() && s.config->m_webserver_enabled)
|
||||
{
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping stopping webserver in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
s.webserver.stop();
|
||||
}
|
||||
#endif
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -1,113 +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 "app.h"
|
||||
#include "state.h"
|
||||
#include "signals.h"
|
||||
#include "actions/actions.h"
|
||||
|
||||
falco::atomic_signal_handler falco::app::g_terminate_signal;
|
||||
falco::atomic_signal_handler falco::app::g_restart_signal;
|
||||
falco::atomic_signal_handler falco::app::g_reopen_outputs_signal;
|
||||
|
||||
using app_action = std::function<falco::app::run_result(falco::app::state&)>;
|
||||
|
||||
bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr)
|
||||
{
|
||||
falco::app::state s;
|
||||
if(!s.options.parse(argc, argv, errstr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for(char **arg = argv; *arg; arg++)
|
||||
{
|
||||
if(s.cmdline.size() > 0)
|
||||
{
|
||||
s.cmdline += " ";
|
||||
}
|
||||
s.cmdline += *arg;
|
||||
}
|
||||
return falco::app::run(s, restart, errstr);
|
||||
}
|
||||
|
||||
bool falco::app::run(falco::app::state& s, bool& restart, std::string& errstr)
|
||||
{
|
||||
// The order here is the order in which the methods will be
|
||||
// called. Before changing the order, ensure that all
|
||||
// dependencies are honored (e.g. don't process events before
|
||||
// loading plugins, opening inspector, etc.).
|
||||
std::list<app_action> run_steps = {
|
||||
falco::app::actions::load_config,
|
||||
falco::app::actions::print_help,
|
||||
falco::app::actions::print_version,
|
||||
falco::app::actions::print_page_size,
|
||||
falco::app::actions::print_generated_gvisor_config,
|
||||
falco::app::actions::print_ignored_events,
|
||||
falco::app::actions::print_syscall_events,
|
||||
falco::app::actions::require_config_file,
|
||||
falco::app::actions::print_plugin_info,
|
||||
falco::app::actions::list_plugins,
|
||||
falco::app::actions::load_plugins,
|
||||
falco::app::actions::init_inspectors,
|
||||
falco::app::actions::init_falco_engine,
|
||||
falco::app::actions::list_fields,
|
||||
falco::app::actions::select_event_sources,
|
||||
falco::app::actions::validate_rules_files,
|
||||
falco::app::actions::load_rules_files,
|
||||
falco::app::actions::print_support,
|
||||
falco::app::actions::init_outputs,
|
||||
falco::app::actions::create_signal_handlers,
|
||||
falco::app::actions::create_requested_paths,
|
||||
falco::app::actions::daemonize,
|
||||
falco::app::actions::init_clients,
|
||||
falco::app::actions::configure_interesting_sets,
|
||||
falco::app::actions::configure_syscall_buffer_size,
|
||||
falco::app::actions::start_grpc_server,
|
||||
falco::app::actions::start_webserver,
|
||||
falco::app::actions::process_events,
|
||||
};
|
||||
|
||||
std::list<app_action> teardown_steps = {
|
||||
falco::app::actions::unregister_signal_handlers,
|
||||
falco::app::actions::stop_grpc_server,
|
||||
falco::app::actions::stop_webserver,
|
||||
};
|
||||
|
||||
falco::app::run_result res = falco::app::run_result::ok();
|
||||
for (auto &func : run_steps)
|
||||
{
|
||||
res = falco::app::run_result::merge(res, func(s));
|
||||
if(!res.proceed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &func : teardown_steps)
|
||||
{
|
||||
res = falco::app::run_result::merge(res, func(s));
|
||||
// note: we always proceed because we don't want to miss teardown steps
|
||||
}
|
||||
|
||||
if(!res.success)
|
||||
{
|
||||
errstr = res.errstr;
|
||||
}
|
||||
|
||||
restart = s.restart;
|
||||
|
||||
return res.success;
|
||||
}
|
||||
@@ -1,204 +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 "restart_handler.h"
|
||||
#include "signals.h"
|
||||
#include "../logger.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30
|
||||
#include <sys/syscall.h>
|
||||
#define gettid() syscall(SYS_gettid)
|
||||
#endif
|
||||
|
||||
falco::app::restart_handler::~restart_handler()
|
||||
{
|
||||
close(m_inotify_fd);
|
||||
stop();
|
||||
}
|
||||
|
||||
void falco::app::restart_handler::trigger()
|
||||
{
|
||||
m_forced.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool falco::app::restart_handler::start(std::string& err)
|
||||
{
|
||||
m_inotify_fd = inotify_init();
|
||||
if (m_inotify_fd < 0)
|
||||
{
|
||||
err = "could not initialize inotify handler";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& f : m_watched_files)
|
||||
{
|
||||
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CLOSE_WRITE);
|
||||
if (wd < 0)
|
||||
{
|
||||
err = "could not watch file: " + f;
|
||||
return false;
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching file '" + f +"'\n");
|
||||
}
|
||||
|
||||
for (const auto &f : m_watched_dirs)
|
||||
{
|
||||
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CREATE | IN_DELETE);
|
||||
if (wd < 0)
|
||||
{
|
||||
err = "could not watch directory: " + f;
|
||||
return false;
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching directory '" + f +"'\n");
|
||||
}
|
||||
|
||||
// launch the watcher thread
|
||||
m_watcher = std::thread(&falco::app::restart_handler::watcher_loop, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void falco::app::restart_handler::stop()
|
||||
{
|
||||
m_stop.store(true, std::memory_order_release);
|
||||
if (m_watcher.joinable())
|
||||
{
|
||||
m_watcher.join();
|
||||
}
|
||||
}
|
||||
|
||||
void falco::app::restart_handler::watcher_loop() noexcept
|
||||
{
|
||||
if (fcntl(m_inotify_fd, F_SETOWN, gettid()) < 0)
|
||||
{
|
||||
// an error occurred, we can't recover
|
||||
// todo(jasondellaluce): should we terminate the process?
|
||||
falco_logger::log(LOG_ERR, "Failed owning inotify handler, shutting down watcher...");
|
||||
return;
|
||||
}
|
||||
|
||||
fd_set set;
|
||||
bool forced = false;
|
||||
bool should_check = false;
|
||||
bool should_restart = false;
|
||||
struct timeval timeout;
|
||||
uint8_t buf[(10 * (sizeof(struct inotify_event) + NAME_MAX + 1))];
|
||||
while (!m_stop.load(std::memory_order_acquire))
|
||||
{
|
||||
// wait for inotify events with a certain timeout.
|
||||
// Note, we'll run through select even before performing a dry-run,
|
||||
// so that we can dismiss in case we have to debounce rapid
|
||||
// subsequent events.
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 100000;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(m_inotify_fd, &set);
|
||||
auto rv = select(m_inotify_fd + 1, &set, NULL, NULL, &timeout);
|
||||
if (rv < 0)
|
||||
{
|
||||
// an error occurred, we can't recover
|
||||
// todo(jasondellaluce): should we terminate the process?
|
||||
falco_logger::log(LOG_ERR, "Failed select with inotify handler, shutting down watcher...");
|
||||
return;
|
||||
}
|
||||
|
||||
// check if there's been a forced restart request
|
||||
forced = m_forced.load(std::memory_order_acquire);
|
||||
m_forced.store(false, std::memory_order_release);
|
||||
|
||||
// no new watch event is received during the timeout
|
||||
if (rv == 0 && !forced)
|
||||
{
|
||||
// perform a dry run. In case no error occurs, we loop back
|
||||
// to the select in order to debounce new inotify events before
|
||||
// actually triggering a restart.
|
||||
if (should_check)
|
||||
{
|
||||
should_check = false;
|
||||
should_restart = m_on_check();
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the previous dry run was successful, and no new
|
||||
// inotify events have been received during the dry run,
|
||||
// then we trigger the restarting signal and quit.
|
||||
// note: quitting is a time optimization, the thread
|
||||
// will be forced to quit anyways later by the Falco app, but
|
||||
// at least we don't make users wait for the timeout.
|
||||
if (should_restart)
|
||||
{
|
||||
should_restart = false;
|
||||
// todo(jasondellaluce): make this a callback too maybe?
|
||||
g_restart_signal.trigger();
|
||||
return;
|
||||
}
|
||||
|
||||
// let's go back to the select
|
||||
continue;
|
||||
}
|
||||
|
||||
// at this point, we either received a new inotify event or a forced
|
||||
// restart. If this happened during a dry run (even if the dry run
|
||||
// was successful), or during a timeout wait since the last successful
|
||||
// dry run before a restart, we dismiss the restart attempt and
|
||||
// perform an additional dry-run for safety purposes (the new inotify
|
||||
// events may be related to bad config/rules files changes).
|
||||
should_restart = false;
|
||||
should_check = false;
|
||||
|
||||
// if there's date on the inotify fd, consume it
|
||||
// (even if there is a forced request too)
|
||||
if (rv > 0)
|
||||
{
|
||||
// note: if available data is less than buffer size, this should
|
||||
// return n > 0 but not filling the buffer. If available data is
|
||||
// more than buffer size, we will loop back to select and behave
|
||||
// like we debounced an event.
|
||||
auto n = read(m_inotify_fd, buf, sizeof(buf));
|
||||
if (n < 0)
|
||||
{
|
||||
// an error occurred, we can't recover
|
||||
// todo(jasondellaluce): should we terminate the process?
|
||||
falco_logger::log(LOG_ERR, "Failed read with inotify handler, shutting down watcher...");
|
||||
return;
|
||||
}
|
||||
// this is an odd case, but if we got here with
|
||||
// no read data, and no forced request, we get back
|
||||
// looping in the select. This can likely happen if
|
||||
// there's data in the inotify fd but the first read
|
||||
// returned no bytes. Likely we'll get back here at the
|
||||
// next select call.
|
||||
else if (n == 0)
|
||||
{
|
||||
// we still proceed in case the request was forced
|
||||
if (!forced)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we consumed the new inotify events or we received a forced
|
||||
// restart request, so we'll perform a dry run after the
|
||||
// next timeout.
|
||||
should_check = true;
|
||||
}
|
||||
}
|
||||
@@ -1,81 +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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace falco
|
||||
{
|
||||
namespace app
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A thread-safe helper for handling hot-reload application restarts.
|
||||
*/
|
||||
class restart_handler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief A function that performs safety checks before confirming
|
||||
* a triggered application restart. Returns true if the application
|
||||
* can safely be restarted.
|
||||
*/
|
||||
using on_check_t = std::function<bool()>;
|
||||
|
||||
/**
|
||||
* @brief A list of files or directories paths to watch.
|
||||
*/
|
||||
using watch_list_t = std::vector<std::string>;
|
||||
|
||||
restart_handler(
|
||||
on_check_t on_check,
|
||||
const watch_list_t& watch_files = {},
|
||||
const watch_list_t& watch_dirs = {})
|
||||
: m_inotify_fd(-1),
|
||||
m_stop(false),
|
||||
m_forced(false),
|
||||
m_on_check(on_check),
|
||||
m_watched_dirs(watch_dirs),
|
||||
m_watched_files(watch_files) { }
|
||||
virtual ~restart_handler();
|
||||
restart_handler(restart_handler&&) = default;
|
||||
restart_handler& operator = (restart_handler&&) = default;
|
||||
restart_handler(const restart_handler&) = delete;
|
||||
restart_handler& operator = (const restart_handler&) = delete;
|
||||
|
||||
bool start(std::string& err);
|
||||
void stop();
|
||||
void trigger();
|
||||
|
||||
private:
|
||||
void watcher_loop() noexcept;
|
||||
|
||||
int m_inotify_fd;
|
||||
std::thread m_watcher;
|
||||
std::atomic<bool> m_stop;
|
||||
std::atomic<bool> m_forced;
|
||||
on_check_t m_on_check;
|
||||
watch_list_t m_watched_dirs;
|
||||
watch_list_t m_watched_files;
|
||||
};
|
||||
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
||||
@@ -1,88 +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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
struct run_result
|
||||
{
|
||||
// Successful result
|
||||
inline static run_result ok()
|
||||
{
|
||||
run_result r;
|
||||
r.success = true;
|
||||
r.errstr = "";
|
||||
r.proceed = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Successful result that causes the program to stop
|
||||
inline static run_result exit()
|
||||
{
|
||||
run_result r = ok();
|
||||
r.proceed = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Failure result that causes the program to stop with an error
|
||||
inline static run_result fatal(const std::string& err)
|
||||
{
|
||||
run_result r;
|
||||
r.success = false;
|
||||
r.errstr = err;
|
||||
r.proceed = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Merges two run results into one
|
||||
inline static run_result merge(const run_result& a, const run_result& b)
|
||||
{
|
||||
auto res = ok();
|
||||
res.proceed = a.proceed && b.proceed;
|
||||
res.success = a.success && b.success;
|
||||
res.errstr = a.errstr;
|
||||
if (!b.errstr.empty())
|
||||
{
|
||||
res.errstr += res.errstr.empty() ? "" : "\n";
|
||||
res.errstr += b.errstr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
run_result(): success(true), errstr(""), proceed(true) {}
|
||||
virtual ~run_result() = default;
|
||||
run_result(run_result&&) = default;
|
||||
run_result& operator = (run_result&&) = default;
|
||||
run_result(const run_result&) = default;
|
||||
run_result& operator = (const run_result&) = default;
|
||||
|
||||
|
||||
// If true, the method completed successfully.
|
||||
bool success;
|
||||
// If success==false, details on the error.
|
||||
std::string errstr;
|
||||
// If true, subsequent methods should be performed. If
|
||||
// false, subsequent methods should *not* be performed
|
||||
// and falco should tear down/exit/restart.
|
||||
bool proceed;
|
||||
};
|
||||
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
||||
@@ -1,29 +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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../atomic_signal_handler.h"
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
extern atomic_signal_handler g_terminate_signal;
|
||||
extern atomic_signal_handler g_restart_signal;
|
||||
extern atomic_signal_handler g_reopen_outputs_signal;
|
||||
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
||||
@@ -1,148 +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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "indexed_vector.h"
|
||||
|
||||
#include "options.h"
|
||||
#include "restart_handler.h"
|
||||
#include "../configuration.h"
|
||||
#include "../stats_writer.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
#include "../grpc_server.h"
|
||||
#include "../webserver.h"
|
||||
#endif
|
||||
|
||||
#include <sinsp.h>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
// Holds the state used and shared by the below methods that
|
||||
// actually implement the application. Declared as a
|
||||
// standalone class to allow for a bit of separation between
|
||||
// application state and instance variables, and to also defer
|
||||
// initializing this state until application::init.
|
||||
struct state
|
||||
{
|
||||
// Holds the info mapped for each loaded event source
|
||||
struct source_info
|
||||
{
|
||||
// The index of the given event source in the state's falco_engine,
|
||||
// as returned by falco_engine::add_source
|
||||
std::size_t engine_idx;
|
||||
// The filtercheck list containing all fields compatible
|
||||
// with the given event source
|
||||
filter_check_list filterchecks;
|
||||
// The inspector assigned to this event source. If in capture mode,
|
||||
// all event source will share the same inspector. If the event
|
||||
// source is a plugin one, the assigned inspector must have that
|
||||
// plugin registered in its plugin manager
|
||||
std::shared_ptr<sinsp> inspector;
|
||||
};
|
||||
|
||||
state():
|
||||
restart(false),
|
||||
loaded_sources(),
|
||||
enabled_sources(),
|
||||
source_infos(),
|
||||
plugin_configs(),
|
||||
selected_sc_set(),
|
||||
syscall_buffer_bytes_size(DEFAULT_DRIVER_BUFFER_BYTES_DIM)
|
||||
{
|
||||
config = std::make_shared<falco_configuration>();
|
||||
engine = std::make_shared<falco_engine>();
|
||||
offline_inspector = std::make_shared<sinsp>();
|
||||
outputs = nullptr;
|
||||
restarter = nullptr;
|
||||
}
|
||||
|
||||
state(const std::string& cmd, const falco::app::options& opts): state()
|
||||
{
|
||||
cmdline = cmd;
|
||||
options = opts;
|
||||
}
|
||||
|
||||
~state() = default;
|
||||
state(state&&) = default;
|
||||
state& operator = (state&&) = default;
|
||||
state(const state&) = default;
|
||||
state& operator = (const state&) = default;
|
||||
|
||||
std::string cmdline;
|
||||
falco::app::options options;
|
||||
std::atomic<bool> restart;
|
||||
|
||||
|
||||
std::shared_ptr<falco_configuration> config;
|
||||
std::shared_ptr<falco_outputs> outputs;
|
||||
std::shared_ptr<falco_engine> engine;
|
||||
|
||||
// The set of loaded event sources (by default, the syscall event
|
||||
// source plus all event sources coming from the loaded plugins)
|
||||
std::unordered_set<std::string> loaded_sources;
|
||||
|
||||
// The set of enabled event sources (can be altered by using
|
||||
// the --enable-source and --disable-source options)
|
||||
std::unordered_set<std::string> enabled_sources;
|
||||
|
||||
// Used to load all plugins to get their info. In capture mode,
|
||||
// this is also used to open the capture file and read its events
|
||||
std::shared_ptr<sinsp> offline_inspector;
|
||||
|
||||
// List of all the information mapped to each event source
|
||||
// indexed by event source name
|
||||
indexed_vector<source_info> source_infos;
|
||||
|
||||
// List of all plugin configurations indexed by plugin name as returned
|
||||
// by their sinsp_plugin::name method
|
||||
indexed_vector<falco_configuration::plugin_config> plugin_configs;
|
||||
|
||||
// Set of syscalls we want the driver to capture
|
||||
libsinsp::events::set<ppm_sc_code> selected_sc_set;
|
||||
|
||||
// Dimension of the syscall buffer in bytes.
|
||||
uint64_t syscall_buffer_bytes_size;
|
||||
|
||||
// Helper responsible for watching of handling hot application restarts
|
||||
std::shared_ptr<restart_handler> restarter;
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
falco::grpc::server grpc_server;
|
||||
std::thread grpc_server_thread;
|
||||
|
||||
falco_webserver webserver;
|
||||
#endif
|
||||
|
||||
inline bool is_capture_mode() const
|
||||
{
|
||||
return !options.trace_filename.empty();
|
||||
}
|
||||
|
||||
inline bool is_gvisor_enabled() const
|
||||
{
|
||||
return !options.gvisor_config.empty();
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace app
|
||||
}; // namespace falco
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,27 +14,26 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
#include "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
/* These indexes could change over the Falco releases. */
|
||||
#define MIN_INDEX 1
|
||||
#define MAX_INDEX 10
|
||||
#define DEFAULT_BYTE_SIZE 1 << 23
|
||||
|
||||
falco::app::run_result falco::app::actions::configure_syscall_buffer_size(falco::app::state& s)
|
||||
application::run_result application::configure_syscall_buffer_size()
|
||||
{
|
||||
/* We don't need to compute the syscall buffer dimension if we are in capture mode or if the
|
||||
* the syscall source is not enabled.
|
||||
*/
|
||||
if(s.is_capture_mode() || s.enabled_sources.find(falco_common::syscall_source) == s.enabled_sources.end() || s.is_gvisor_enabled())
|
||||
if(is_capture_mode() || m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end() || is_gvisor_enabled())
|
||||
{
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
uint16_t index = s.config->m_syscall_buf_size_preset;
|
||||
uint16_t index = m_state->config->m_syscall_buf_size_preset;
|
||||
if(index < MIN_INDEX || index > MAX_INDEX)
|
||||
{
|
||||
return run_result::fatal("The 'syscall_buf_size_preset' value must be between '" + std::to_string(MIN_INDEX) + "' and '" + std::to_string(MAX_INDEX) + "'\n");
|
||||
@@ -49,7 +48,7 @@ falco::app::run_result falco::app::actions::configure_syscall_buffer_size(falco:
|
||||
long page_size = getpagesize();
|
||||
if(page_size <= 0)
|
||||
{
|
||||
s.syscall_buffer_bytes_size = DEFAULT_BYTE_SIZE;
|
||||
m_state->syscall_buffer_bytes_size = DEFAULT_BYTE_SIZE;
|
||||
falco_logger::log(LOG_WARNING, "Unable to get the system page size through 'getpagesize()'. Try to use the default syscall buffer dimension: " + std::to_string(DEFAULT_BYTE_SIZE) + " bytes\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -66,7 +65,7 @@ falco::app::run_result falco::app::actions::configure_syscall_buffer_size(falco:
|
||||
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not greater than '2 * " + std::to_string(page_size) + "' where '" + std::to_string(page_size) + "' is your system page size. Please configure a greater 'syscall_buf_size_preset' value in the Falco configuration file\n");
|
||||
}
|
||||
|
||||
s.syscall_buffer_bytes_size = chosen_size;
|
||||
m_state->syscall_buffer_bytes_size = chosen_size;
|
||||
falco_logger::log(LOG_INFO, "The chosen syscall buffer dimension is: " + std::to_string(chosen_size) + " bytes (" + std::to_string(chosen_size / (uint64_t)(1024 * 1024)) + " MBs)\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
61
userspace/falco/app_actions/configure_interesting_sets.cpp
Normal file
61
userspace/falco/app_actions/configure_interesting_sets.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
void application::configure_interesting_sets()
|
||||
{
|
||||
/// TODO: in the next future we need to change the interface of `enforce_simple_ppm_sc_set`
|
||||
/// and `enforce_sinsp_state_tp` APIs, they shouldn't require an inspector to be called!
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
|
||||
/* Please note: here we fill these 2 sets because we are interested in only some features, if we leave
|
||||
* them empty `libsinsp` will fill them with all the available syscalls and all the available tracepoints!
|
||||
*/
|
||||
|
||||
/* Here the `libsinsp` state set is not enough, we need other syscalls used in the rules,
|
||||
* so we use the `simple_set`, this `simple_set` contains all the syscalls of the `libsinsp` state
|
||||
* plus syscalls for Falco default rules.
|
||||
*/
|
||||
m_state->ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set();
|
||||
m_state->ppm_event_info_of_interest = inspector->get_event_set_from_ppm_sc_set(m_state->ppm_sc_of_interest);
|
||||
|
||||
/* Fill-up the set of event infos of interest */
|
||||
for (uint32_t ev = 2; ev < PPM_EVENT_MAX; ev++)
|
||||
{
|
||||
if (!sinsp::is_old_version_event(ev)
|
||||
&& !sinsp::is_unused_event(ev)
|
||||
&& !sinsp::is_unknown_event(ev))
|
||||
{
|
||||
/* So far we only covered syscalls, so we add other kinds of
|
||||
interesting events. In this case, we are also interested in
|
||||
metaevents and in the procexit tracepoint event. */
|
||||
if (sinsp::is_metaevent(ev) || ev == PPME_PROCEXIT_1_E)
|
||||
{
|
||||
m_state->ppm_event_info_of_interest.insert(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In this case we get the tracepoints for the `libsinsp` state and we remove
|
||||
* the `sched_switch` tracepoint since it is highly noisy and not so useful
|
||||
* for our state/events enrichment.
|
||||
*/
|
||||
m_state->tp_of_interest = inspector->enforce_sinsp_state_tp();
|
||||
m_state->tp_of_interest.erase(SCHED_SWITCH);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
#include "application.h"
|
||||
#include "falco_utils.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -27,20 +27,17 @@ limitations under the License.
|
||||
#endif
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
static int create_dir(const std::string &path);
|
||||
|
||||
falco::app::run_result falco::app::actions::create_requested_paths(falco::app::state& s)
|
||||
application::run_result application::create_requested_paths()
|
||||
{
|
||||
if(!s.options.gvisor_config.empty())
|
||||
if(!m_options.gvisor_config.empty())
|
||||
{
|
||||
// This is bad: parsing gvisor config to get endpoint
|
||||
// to be able to auto-create the path to the file for the user.
|
||||
std::ifstream reader(s.options.gvisor_config);
|
||||
std::ifstream reader(m_options.gvisor_config);
|
||||
if (reader.fail())
|
||||
{
|
||||
return run_result::fatal(s.options.gvisor_config + ": cannot open file");
|
||||
return run_result::fatal(m_options.gvisor_config + ": cannot open file");
|
||||
}
|
||||
|
||||
nlohmann::json parsed_json;
|
||||
@@ -51,7 +48,7 @@ falco::app::run_result falco::app::actions::create_requested_paths(falco::app::s
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
return run_result::fatal(s.options.gvisor_config + ": cannot parse JSON: " + e.what());
|
||||
return run_result::fatal(m_options.gvisor_config + ": cannot parse JSON: " + e.what());
|
||||
}
|
||||
|
||||
try
|
||||
@@ -60,7 +57,7 @@ falco::app::run_result falco::app::actions::create_requested_paths(falco::app::s
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
return run_result::fatal(s.options.gvisor_config + ": failed to fetch config.endpoint: " + e.what());
|
||||
return run_result::fatal(m_options.gvisor_config + ": failed to fetch config.endpoint: " + e.what());
|
||||
}
|
||||
|
||||
int ret = create_dir(gvisor_socket);
|
||||
@@ -70,11 +67,11 @@ falco::app::run_result falco::app::actions::create_requested_paths(falco::app::s
|
||||
}
|
||||
}
|
||||
|
||||
if (s.config->m_grpc_enabled && !s.config->m_grpc_bind_address.empty())
|
||||
if (m_state->config->m_grpc_enabled && !m_state->config->m_grpc_bind_address.empty())
|
||||
{
|
||||
if(falco::utils::network::is_unix_scheme(s.config->m_grpc_bind_address))
|
||||
if(falco::utils::network::is_unix_scheme(m_state->config->m_grpc_bind_address))
|
||||
{
|
||||
auto server_path = s.config->m_grpc_bind_address.substr(
|
||||
auto server_path = m_state->config->m_grpc_bind_address.substr(
|
||||
falco::utils::network::UNIX_SCHEME.length()
|
||||
);
|
||||
int ret = create_dir(server_path);
|
||||
@@ -90,14 +87,14 @@ falco::app::run_result falco::app::actions::create_requested_paths(falco::app::s
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
static int create_dir(const std::string &path)
|
||||
int application::create_dir(const std::string &path)
|
||||
{
|
||||
// Properly reset errno
|
||||
errno = 0;
|
||||
|
||||
std::istringstream f(path);
|
||||
std::string path_until_token;
|
||||
std::string s;
|
||||
istringstream f(path);
|
||||
string path_until_token;
|
||||
string s;
|
||||
// Create all the subfolder stopping at last token (f.eof());
|
||||
// Examples:
|
||||
// "/tmp/foo/bar" -> "", "tmp", "foo" -> mkdir("/") + mkdir("/tmp/") + midir("/tmp/foo/")
|
||||
182
userspace/falco/app_actions/create_signal_handlers.cpp
Normal file
182
userspace/falco/app_actions/create_signal_handlers.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
// This is initially set to a dummy application. When
|
||||
// create_signal_handlers is called, it will be rebound to the
|
||||
// provided application, and in unregister_signal_handlers it will be
|
||||
// rebound back to the dummy application.
|
||||
|
||||
static int inot_fd;
|
||||
|
||||
static void terminate_signal_handler(int signal)
|
||||
{
|
||||
ASSERT(falco::app::g_terminate.is_lock_free());
|
||||
falco::app::g_terminate.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
static void reopen_outputs_signal_handler(int signal)
|
||||
{
|
||||
ASSERT(falco::app::g_reopen_outputs.is_lock_free());
|
||||
falco::app::g_reopen_outputs.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
static void restart_signal_handler(int signal)
|
||||
{
|
||||
ASSERT(falco::app::g_restart.is_lock_free());
|
||||
falco::app::g_restart.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
bool application::create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
{
|
||||
ret = run_result::ok();
|
||||
if(signal(sig, func) == SIG_ERR)
|
||||
{
|
||||
char errbuf[1024];
|
||||
if (strerror_r(errno, errbuf, sizeof(errbuf)) != 0)
|
||||
{
|
||||
snprintf(errbuf, sizeof(errbuf)-1, "Errno %d", errno);
|
||||
}
|
||||
|
||||
ret = run_result::fatal(std::string("Could not create signal handler for ") +
|
||||
strsignal(sig) +
|
||||
": " +
|
||||
errbuf);
|
||||
}
|
||||
|
||||
return ret.success;
|
||||
}
|
||||
|
||||
application::run_result application::create_signal_handlers()
|
||||
{
|
||||
falco::app::g_terminate.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
|
||||
falco::app::g_restart.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
|
||||
falco::app::g_reopen_outputs.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
|
||||
|
||||
if (!g_terminate.is_lock_free()
|
||||
|| !g_restart.is_lock_free()
|
||||
|| !g_reopen_outputs.is_lock_free())
|
||||
{
|
||||
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) ||
|
||||
! create_handler(SIGHUP, ::restart_signal_handler, ret))
|
||||
{
|
||||
// we use the if just to make sure we return at the first failed statement
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
application::run_result application::attach_inotify_signals()
|
||||
{
|
||||
if (m_state->config->m_watch_config_files)
|
||||
{
|
||||
inot_fd = inotify_init();
|
||||
if (inot_fd == -1)
|
||||
{
|
||||
return run_result::fatal("Could not create inotify handler");
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = restart_signal_handler;
|
||||
if (sigaction(SIGIO, &sa, NULL) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to link SIGIO to inotify handler");
|
||||
}
|
||||
|
||||
/* Set owner process that is to receive "I/O possible" signal */
|
||||
if (fcntl(inot_fd, F_SETOWN, getpid()) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to setting owner on inotify handler");
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable "I/O possible" signaling and make I/O nonblocking
|
||||
* for file descriptor
|
||||
*/
|
||||
int flags = fcntl(inot_fd, F_GETFL);
|
||||
if (fcntl(inot_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to setting flags on inotify handler");
|
||||
}
|
||||
|
||||
// Watch conf file
|
||||
int wd = inotify_add_watch(inot_fd, m_options.conf_filename.c_str(), IN_CLOSE_WRITE);
|
||||
if (wd == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to watch conf file");
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + m_options.conf_filename +"\n");
|
||||
|
||||
// Watch rules files
|
||||
for (const auto &rule : m_state->config->m_loaded_rules_filenames)
|
||||
{
|
||||
wd = inotify_add_watch(inot_fd, rule.c_str(), IN_CLOSE_WRITE | IN_ONESHOT);
|
||||
if (wd == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to watch rule file: " + rule);
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + rule +"\n");
|
||||
}
|
||||
|
||||
// Watch specified rules folders, if any:
|
||||
// any newly created/removed file within the folder
|
||||
// will trigger a Falco restart.
|
||||
for (const auto &fld : m_state->config->m_loaded_rules_folders)
|
||||
{
|
||||
// For folders, we watch if any file is created or destroyed within
|
||||
wd = inotify_add_watch(inot_fd, fld.c_str(), IN_CREATE | IN_DELETE | IN_ONESHOT);
|
||||
if (wd == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to watch rule folder: " + fld);
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder\n");
|
||||
}
|
||||
}
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
bool application::unregister_signal_handlers(std::string &errstr)
|
||||
{
|
||||
run_result ret;
|
||||
close(inot_fd);
|
||||
if(! create_handler(SIGINT, SIG_DFL, ret) ||
|
||||
! create_handler(SIGTERM, SIG_DFL, ret) ||
|
||||
! create_handler(SIGUSR1, SIG_DFL, ret) ||
|
||||
! create_handler(SIGHUP, SIG_DFL, ret))
|
||||
{
|
||||
errstr = ret.errstr;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,24 +18,17 @@ limitations under the License.
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "actions.h"
|
||||
#include "application.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)
|
||||
application::run_result application::daemonize()
|
||||
{
|
||||
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) {
|
||||
if (m_options.daemon && !s_daemonized) {
|
||||
pid_t pid, sid;
|
||||
|
||||
pid = fork();
|
||||
@@ -45,11 +38,11 @@ falco::app::run_result falco::app::actions::daemonize(falco::app::state& s)
|
||||
} else if (pid > 0) {
|
||||
// parent. Write child pid to pidfile and exit
|
||||
std::ofstream pidfile;
|
||||
pidfile.open(s.options.pidfilename);
|
||||
pidfile.open(m_options.pidfilename);
|
||||
|
||||
if (!pidfile.good())
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + s.options.pidfilename + ". Exiting.\n");
|
||||
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + m_options.pidfilename + ". Exiting.\n");
|
||||
exit(-1);
|
||||
}
|
||||
pidfile << pid;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,46 +14,39 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
#include "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::init_clients(falco::app::state& s)
|
||||
application::run_result application::init_clients()
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
// k8s is useful only if the syscall source is enabled
|
||||
if (s.enabled_sources.find(falco_common::syscall_source) == s.enabled_sources.end())
|
||||
if (m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end())
|
||||
{
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
auto inspector = s.source_infos.at(falco_common::syscall_source)->inspector;
|
||||
auto inspector = m_state->source_infos.at(falco_common::syscall_source)->inspector;
|
||||
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download max size to " + std::to_string(s.config->m_metadata_download_max_mb) + " MB\n");
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download chunk wait time to " + std::to_string(s.config->m_metadata_download_chunk_wait_us) + " μs\n");
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + std::to_string(s.config->m_metadata_download_watch_freq_sec) + " seconds\n");
|
||||
inspector->set_metadata_download_params(s.config->m_metadata_download_max_mb * 1024 * 1024, s.config->m_metadata_download_chunk_wait_us, s.config->m_metadata_download_watch_freq_sec);
|
||||
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping clients initialization in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download max size to " + to_string(m_state->config->m_metadata_download_max_mb) + " MB\n");
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download chunk wait time to " + to_string(m_state->config->m_metadata_download_chunk_wait_us) + " μs\n");
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + to_string(m_state->config->m_metadata_download_watch_freq_sec) + " seconds\n");
|
||||
inspector->set_metadata_download_params(m_state->config->m_metadata_download_max_mb * 1024 * 1024, m_state->config->m_metadata_download_chunk_wait_us, m_state->config->m_metadata_download_watch_freq_sec);
|
||||
|
||||
//
|
||||
// Run k8s, if required
|
||||
//
|
||||
char *k8s_api_env = NULL;
|
||||
if(!s.options.k8s_api.empty() ||
|
||||
if(!m_options.k8s_api.empty() ||
|
||||
(k8s_api_env = getenv("FALCO_K8S_API")))
|
||||
{
|
||||
// Create string pointers for some config vars
|
||||
// and pass to inspector. The inspector then
|
||||
// owns the pointers.
|
||||
std::string *k8s_api_ptr = new std::string((!s.options.k8s_api.empty() ? s.options.k8s_api : k8s_api_env));
|
||||
std::string *k8s_api_cert_ptr = new std::string(s.options.k8s_api_cert);
|
||||
std::string *k8s_node_name_ptr = new std::string(s.options.k8s_node_name);
|
||||
std::string *k8s_api_ptr = new string((!m_options.k8s_api.empty() ? m_options.k8s_api : k8s_api_env));
|
||||
std::string *k8s_api_cert_ptr = new string(m_options.k8s_api_cert);
|
||||
std::string *k8s_node_name_ptr = new string(m_options.k8s_node_name);
|
||||
|
||||
if(k8s_api_cert_ptr->empty())
|
||||
{
|
||||
@@ -62,7 +55,7 @@ falco::app::run_result falco::app::actions::init_clients(falco::app::state& s)
|
||||
*k8s_api_cert_ptr = k8s_cert_env;
|
||||
}
|
||||
}
|
||||
inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, s.options.verbose);
|
||||
inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, m_options.verbose);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -70,20 +63,20 @@ falco::app::run_result falco::app::actions::init_clients(falco::app::state& s)
|
||||
// Run mesos, if required
|
||||
// todo(leogr): remove in Falco 0,.35
|
||||
//
|
||||
if(!s.options.mesos_api.empty())
|
||||
if(!m_options.mesos_api.empty())
|
||||
{
|
||||
// Differs from init_k8s_client in that it
|
||||
// passes a pointer but the inspector does
|
||||
// *not* own it and does not use it after
|
||||
// init_mesos_client() returns.
|
||||
falco_logger::log(LOG_WARNING, "Mesos support has been DEPRECATED and will be removed in the next version!\n");
|
||||
inspector->init_mesos_client(&(s.options.mesos_api), s.options.verbose);
|
||||
inspector->init_mesos_client(&(m_options.mesos_api), m_options.verbose);
|
||||
}
|
||||
else if(char* mesos_api_env = getenv("FALCO_MESOS_API"))
|
||||
{
|
||||
falco_logger::log(LOG_WARNING, "Mesos support has been DEPRECATED and will be removed in the next version!\n");
|
||||
std::string mesos_api_copy = mesos_api_env;
|
||||
inspector->init_mesos_client(&mesos_api_copy, s.options.verbose);
|
||||
inspector->init_mesos_client(&mesos_api_copy, m_options.verbose);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,57 +14,56 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
#include "application.h"
|
||||
#include <plugin_manager.h>
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
void configure_output_format(falco::app::state& s)
|
||||
void application::configure_output_format()
|
||||
{
|
||||
std::string output_format;
|
||||
bool replace_container_info = false;
|
||||
|
||||
if(s.options.print_additional == "c" || s.options.print_additional == "container")
|
||||
if(m_options.print_additional == "c" || m_options.print_additional == "container")
|
||||
{
|
||||
output_format = "container=%container.name (id=%container.id)";
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(s.options.print_additional == "cg" || s.options.print_additional == "container-gvisor")
|
||||
else if(m_options.print_additional == "cg" || m_options.print_additional == "container-gvisor")
|
||||
{
|
||||
output_format = "container=%container.name (id=%container.id) vpid=%proc.vpid vtid=%thread.vtid";
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(s.options.print_additional == "k" || s.options.print_additional == "kubernetes")
|
||||
else if(m_options.print_additional == "k" || m_options.print_additional == "kubernetes")
|
||||
{
|
||||
output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id";
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(s.options.print_additional == "kg" || s.options.print_additional == "kubernetes-gvisor")
|
||||
else if(m_options.print_additional == "kg" || m_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";
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(s.options.print_additional == "m" || s.options.print_additional == "mesos")
|
||||
else if(m_options.print_additional == "m" || m_options.print_additional == "mesos")
|
||||
{
|
||||
output_format = "task=%mesos.task.name container=%container.id";
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(!s.options.print_additional.empty())
|
||||
else if(!m_options.print_additional.empty())
|
||||
{
|
||||
output_format = s.options.print_additional;
|
||||
output_format = m_options.print_additional;
|
||||
replace_container_info = false;
|
||||
}
|
||||
|
||||
if(!output_format.empty())
|
||||
{
|
||||
s.engine->set_extra(output_format, replace_container_info);
|
||||
m_state->engine->set_extra(output_format, replace_container_info);
|
||||
}
|
||||
}
|
||||
|
||||
void add_source_to_engine(falco::app::state& s, const std::string& src)
|
||||
void application::add_source_to_engine(const std::string& src)
|
||||
{
|
||||
auto src_info = s.source_infos.at(src);
|
||||
auto src_info = m_state->source_infos.at(src);
|
||||
std::shared_ptr<gen_event_filter_factory> filter_factory = nullptr;
|
||||
std::shared_ptr<gen_event_formatter_factory> formatter_factory = nullptr;
|
||||
|
||||
@@ -77,37 +76,37 @@ void add_source_to_engine(falco::app::state& s, const std::string& src)
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &filterchecks = s.source_infos.at(src)->filterchecks;
|
||||
auto &filterchecks = m_state->source_infos.at(src)->filterchecks;
|
||||
filter_factory = std::shared_ptr<gen_event_filter_factory>(
|
||||
new sinsp_filter_factory(src_info->inspector.get(), filterchecks));
|
||||
formatter_factory = std::shared_ptr<gen_event_formatter_factory>(
|
||||
new sinsp_evt_formatter_factory(src_info->inspector.get(), filterchecks));
|
||||
}
|
||||
|
||||
if(s.config->m_json_output)
|
||||
if(m_state->config->m_json_output)
|
||||
{
|
||||
formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
|
||||
}
|
||||
|
||||
src_info->engine_idx = s.engine->add_source(
|
||||
src_info->engine_idx = m_state->engine->add_source(
|
||||
src, filter_factory, formatter_factory);
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::init_falco_engine(falco::app::state& s)
|
||||
application::run_result application::init_falco_engine()
|
||||
{
|
||||
// add all non-syscall event sources in engine
|
||||
for (const auto& src : s.loaded_sources)
|
||||
for (const auto& src : m_state->loaded_sources)
|
||||
{
|
||||
if (src != falco_common::syscall_source)
|
||||
{
|
||||
// we skip the syscall as we want it to be the one added for last
|
||||
// in the engine. This makes the source index assignment easier.
|
||||
add_source_to_engine(s, src);
|
||||
add_source_to_engine(src);
|
||||
}
|
||||
}
|
||||
|
||||
// add syscall as last source
|
||||
add_source_to_engine(s, falco_common::syscall_source);
|
||||
add_source_to_engine(falco_common::syscall_source);
|
||||
|
||||
// note: in capture mode, we can assume that the plugin source index will
|
||||
// be the same in both the falco engine and the sinsp plugin manager.
|
||||
@@ -117,16 +116,16 @@ falco::app::run_result falco::app::actions::init_falco_engine(falco::app::state&
|
||||
// is because in that case event sources are scattered across different
|
||||
// inspectors. Since this is an implementation-based assumption, we
|
||||
// check this and return an error to spot regressions in the future.
|
||||
if (s.is_capture_mode())
|
||||
if (is_capture_mode())
|
||||
{
|
||||
auto manager = s.offline_inspector->get_plugin_manager();
|
||||
auto manager = m_state->offline_inspector->get_plugin_manager();
|
||||
for (const auto &p : manager->plugins())
|
||||
{
|
||||
if (p->caps() & CAP_SOURCING)
|
||||
{
|
||||
bool added = false;
|
||||
auto source_idx = manager->source_idx_by_plugin_id(p->id(), added);
|
||||
auto engine_idx = s.source_infos.at(p->event_source())->engine_idx;
|
||||
auto engine_idx = m_state->source_infos.at(p->event_source())->engine_idx;
|
||||
if (!added || source_idx != engine_idx)
|
||||
{
|
||||
return run_result::fatal("Could not add event source in the engine: " + p->event_source());
|
||||
@@ -135,8 +134,8 @@ falco::app::run_result falco::app::actions::init_falco_engine(falco::app::state&
|
||||
}
|
||||
}
|
||||
|
||||
configure_output_format(s);
|
||||
s.engine->set_min_priority(s.config->m_min_priority);
|
||||
configure_output_format();
|
||||
m_state->engine->set_min_priority(m_state->config->m_min_priority);
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,22 +14,20 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#include "application.h"
|
||||
#include <unordered_set>
|
||||
|
||||
#include <plugin_manager.h>
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
static void init_syscall_inspector(falco::app::state& s, std::shared_ptr<sinsp> inspector)
|
||||
void application::init_syscall_inspector(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
const falco::app::cmdline_options& opts)
|
||||
{
|
||||
inspector->set_buffer_format(s.options.event_buffer_format);
|
||||
inspector->set_buffer_format(opts.event_buffer_format);
|
||||
|
||||
// If required, set the CRI paths
|
||||
for (auto &p : s.options.cri_socket_paths)
|
||||
for (auto &p : opts.cri_socket_paths)
|
||||
{
|
||||
if (!p.empty())
|
||||
{
|
||||
@@ -38,20 +36,19 @@ static void init_syscall_inspector(falco::app::state& s, std::shared_ptr<sinsp>
|
||||
}
|
||||
|
||||
// Decide whether to do sync or async for CRI metadata fetch
|
||||
inspector->set_cri_async(!s.options.disable_cri_async);
|
||||
inspector->set_cri_async(!opts.disable_cri_async);
|
||||
|
||||
//
|
||||
// If required, set the snaplen
|
||||
//
|
||||
if(s.options.snaplen != 0)
|
||||
if(opts.snaplen != 0)
|
||||
{
|
||||
inspector->set_snaplen(s.options.snaplen);
|
||||
inspector->set_snaplen(opts.snaplen);
|
||||
}
|
||||
|
||||
if (s.config->m_syscall_drop_failed_exit)
|
||||
if(!opts.all_events)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Failed syscall exit events are dropped in the kernel driver\n");
|
||||
inspector->set_dropfailed(true);
|
||||
configure_interesting_sets();
|
||||
}
|
||||
|
||||
inspector->set_hostname_and_port_resolution_mode(false);
|
||||
@@ -102,27 +99,27 @@ static bool populate_filterchecks(
|
||||
return true;
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::init_inspectors(falco::app::state& s)
|
||||
application::run_result application::init_inspectors()
|
||||
{
|
||||
std::string err;
|
||||
std::unordered_set<std::string> used_plugins;
|
||||
const auto& all_plugins = s.offline_inspector->get_plugin_manager()->plugins();
|
||||
const auto& all_plugins = m_state->offline_inspector->get_plugin_manager()->plugins();
|
||||
|
||||
for (const auto &src : s.loaded_sources)
|
||||
for (const auto &src : m_state->loaded_sources)
|
||||
{
|
||||
auto src_info = s.source_infos.at(src);
|
||||
auto src_info = m_state->source_infos.at(src);
|
||||
|
||||
// in capture mode, every event source uses the offline inspector.
|
||||
// in live mode, we create a new inspector for each event source
|
||||
src_info->inspector = s.is_capture_mode()
|
||||
? s.offline_inspector
|
||||
src_info->inspector = is_capture_mode()
|
||||
? m_state->offline_inspector
|
||||
: std::make_shared<sinsp>();
|
||||
|
||||
// handle syscall and plugin sources differently
|
||||
// todo(jasondellaluce): change this once we support extracting plugin fields from syscalls too
|
||||
if (src == falco_common::syscall_source)
|
||||
{
|
||||
init_syscall_inspector(s, src_info->inspector);
|
||||
init_syscall_inspector(src_info->inspector, m_options);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -131,10 +128,10 @@ falco::app::run_result falco::app::actions::init_inspectors(falco::app::state& s
|
||||
for (const auto& p : all_plugins)
|
||||
{
|
||||
std::shared_ptr<sinsp_plugin> plugin = nullptr;
|
||||
auto config = s.plugin_configs.at(p->name());
|
||||
auto config = m_state->plugin_configs.at(p->name());
|
||||
auto is_input = p->caps() & CAP_SOURCING && p->event_source() == src;
|
||||
|
||||
if (s.is_capture_mode())
|
||||
if (is_capture_mode())
|
||||
{
|
||||
// in capture mode, every plugin is already registered
|
||||
// in the offline inspector by the load_plugins action
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,14 +17,13 @@ limitations under the License.
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "actions.h"
|
||||
#include "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::init_outputs(falco::app::state& s)
|
||||
application::run_result application::init_outputs()
|
||||
{
|
||||
if (s.config->m_outputs.empty())
|
||||
if (m_state->config->m_outputs.empty())
|
||||
{
|
||||
return run_result::fatal("No output configured, please make sure at least one output is configured and enabled.");
|
||||
}
|
||||
@@ -49,21 +48,15 @@ falco::app::run_result falco::app::actions::init_outputs(falco::app::state& s)
|
||||
hostname = c_hostname;
|
||||
}
|
||||
|
||||
if (s.options.dry_run)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping daemonizing in dry-run\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
s.outputs.reset(new falco_outputs(
|
||||
s.engine,
|
||||
s.config->m_outputs,
|
||||
s.config->m_json_output,
|
||||
s.config->m_json_include_output_property,
|
||||
s.config->m_json_include_tags_property,
|
||||
s.config->m_output_timeout,
|
||||
s.config->m_buffered_outputs,
|
||||
s.config->m_time_format_iso_8601,
|
||||
m_state->outputs.reset(new falco_outputs(
|
||||
m_state->engine,
|
||||
m_state->config->m_outputs,
|
||||
m_state->config->m_json_output,
|
||||
m_state->config->m_json_include_output_property,
|
||||
m_state->config->m_json_include_tags_property,
|
||||
m_state->config->m_output_timeout,
|
||||
m_state->config->m_buffered_outputs,
|
||||
m_state->config->m_time_format_iso_8601,
|
||||
hostname));
|
||||
|
||||
return run_result::ok();
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,21 +14,22 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
#include "fields_info.h"
|
||||
|
||||
#include "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::list_fields(falco::app::state& s)
|
||||
application::run_result application::list_fields()
|
||||
{
|
||||
if(s.options.list_fields)
|
||||
if(m_options.list_fields)
|
||||
{
|
||||
if(s.options.list_source_fields != "" &&
|
||||
!s.engine->is_source_valid(s.options.list_source_fields))
|
||||
if(m_options.list_source_fields != "" &&
|
||||
!m_state->engine->is_source_valid(m_options.list_source_fields))
|
||||
{
|
||||
return run_result::fatal("Value for --list must be a valid source type");
|
||||
}
|
||||
s.engine->list_fields(s.options.list_source_fields, s.options.verbose, s.options.names_only, s.options.markdown);
|
||||
m_state->engine->list_fields(m_options.list_source_fields, m_options.verbose, m_options.names_only, m_options.markdown);
|
||||
return run_result::exit();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,21 +14,18 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#include "application.h"
|
||||
#include <plugin_manager.h>
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::list_plugins(falco::app::state& s)
|
||||
application::run_result application::list_plugins()
|
||||
{
|
||||
if(s.options.list_plugins)
|
||||
if(m_options.list_plugins)
|
||||
{
|
||||
std::ostringstream os;
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
const auto& configs = s.config->m_plugins;
|
||||
const auto& configs = m_state->config->m_plugins;
|
||||
for (auto &c : configs)
|
||||
{
|
||||
// load the plugin (no need to initialize it)
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,22 +14,21 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
#include "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::load_config(falco::app::state& s)
|
||||
application::run_result application::load_config()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!s.options.conf_filename.empty())
|
||||
if (!m_options.conf_filename.empty())
|
||||
{
|
||||
s.config->init(s.options.conf_filename, s.options.cmdline_config_options);
|
||||
m_state->config->init(m_options.conf_filename, m_options.cmdline_config_options);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.config->init(s.options.cmdline_config_options);
|
||||
m_state->config->init(m_options.cmdline_config_options);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
@@ -38,25 +37,25 @@ falco::app::run_result falco::app::actions::load_config(falco::app::state& s)
|
||||
}
|
||||
|
||||
// log after config init because config determines where logs go
|
||||
falco_logger::set_time_format_iso_8601(s.config->m_time_format_iso_8601);
|
||||
falco_logger::set_time_format_iso_8601(m_state->config->m_time_format_iso_8601);
|
||||
falco_logger::log(LOG_INFO, "Falco version: " + std::string(FALCO_VERSION) + " (" + std::string(FALCO_TARGET_ARCH) + ")\n");
|
||||
if (!s.cmdline.empty())
|
||||
if (!m_state->cmdline.empty())
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "CLI args: " + s.cmdline);
|
||||
falco_logger::log(LOG_DEBUG, "CLI args: " + m_state->cmdline);
|
||||
}
|
||||
if (!s.options.conf_filename.empty())
|
||||
if (!m_options.conf_filename.empty())
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Falco initialized with configuration file: " + s.options.conf_filename + "\n");
|
||||
falco_logger::log(LOG_INFO, "Falco initialized with configuration file: " + m_options.conf_filename + "\n");
|
||||
}
|
||||
|
||||
s.config->m_buffered_outputs = !s.options.unbuffered_outputs;
|
||||
m_state->config->m_buffered_outputs = !m_options.unbuffered_outputs;
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::require_config_file(falco::app::state& s)
|
||||
application::run_result application::require_config_file()
|
||||
{
|
||||
if (s.options.conf_filename.empty())
|
||||
if (m_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");
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,16 +14,15 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
#include "application.h"
|
||||
#include <plugin_manager.h>
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::load_plugins(falco::app::state& s)
|
||||
application::run_result application::load_plugins()
|
||||
{
|
||||
#ifdef MUSL_OPTIMIZED
|
||||
if (!s.config->m_plugins.empty())
|
||||
if (!m_state->config->m_plugins.empty())
|
||||
{
|
||||
return run_result::fatal("Can not load/use plugins with musl optimized build");
|
||||
}
|
||||
@@ -32,30 +31,30 @@ falco::app::run_result falco::app::actions::load_plugins(falco::app::state& s)
|
||||
|
||||
// Initialize the set of loaded event sources.
|
||||
// By default, the set includes the 'syscall' event source
|
||||
s.source_infos.clear();
|
||||
s.source_infos.insert(empty_src_info, falco_common::syscall_source);
|
||||
s.loaded_sources = { falco_common::syscall_source };
|
||||
m_state->source_infos.clear();
|
||||
m_state->source_infos.insert(empty_src_info, falco_common::syscall_source);
|
||||
m_state->loaded_sources = { falco_common::syscall_source };
|
||||
|
||||
// Initialize map of plugin configs
|
||||
s.plugin_configs.clear();
|
||||
m_state->plugin_configs.clear();
|
||||
|
||||
// Initialize the offline inspector. This is used to load all the configured
|
||||
// plugins in order to have them available every time we need to access
|
||||
// their static info. If Falco is in capture mode, this inspector is also
|
||||
// used to open and read the trace file
|
||||
s.offline_inspector.reset(new sinsp());
|
||||
m_state->offline_inspector.reset(new sinsp());
|
||||
|
||||
// Load all the configured plugins
|
||||
for(auto &p : s.config->m_plugins)
|
||||
for(auto &p : m_state->config->m_plugins)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Loading plugin '" + p.m_name + "' from file " + p.m_library_path + "\n");
|
||||
auto plugin = s.offline_inspector->register_plugin(p.m_library_path);
|
||||
s.plugin_configs.insert(p, plugin->name());
|
||||
auto plugin = m_state->offline_inspector->register_plugin(p.m_library_path);
|
||||
m_state->plugin_configs.insert(p, plugin->name());
|
||||
if(plugin->caps() & CAP_SOURCING)
|
||||
{
|
||||
auto sname = plugin->event_source();
|
||||
s.source_infos.insert(empty_src_info, sname);
|
||||
s.loaded_sources.insert(sname);
|
||||
m_state->source_infos.insert(empty_src_info, sname);
|
||||
m_state->loaded_sources.insert(sname);
|
||||
}
|
||||
}
|
||||
|
||||
196
userspace/falco/app_actions/load_rules_files.cpp
Normal file
196
userspace/falco/app_actions/load_rules_files.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "application.h"
|
||||
#include <plugin_manager.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
bool application::check_rules_plugin_requirements(std::string& err)
|
||||
{
|
||||
// Ensure that all plugins are compatible with the loaded set of rules
|
||||
// note: offline inspector contains all the loaded plugins
|
||||
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
|
||||
for (const auto &plugin : m_state->offline_inspector->get_plugin_manager()->plugins())
|
||||
{
|
||||
falco_engine::plugin_version_requirement req;
|
||||
req.name = plugin->name();
|
||||
req.version = plugin->plugin_version().as_string();
|
||||
plugin_reqs.push_back(req);
|
||||
}
|
||||
return m_state->engine->check_plugin_requirements(plugin_reqs, err);
|
||||
}
|
||||
|
||||
void application::check_for_ignored_events()
|
||||
{
|
||||
/* Get the events from the rules. */
|
||||
std::set<uint16_t> rule_events;
|
||||
std::string source = falco_common::syscall_source;
|
||||
m_state->engine->evttypes_for_ruleset(source, rule_events);
|
||||
|
||||
/* Get the events we consider interesting from the application state `ppm_sc` codes. */
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
std::unordered_set<uint32_t> events(rule_events.begin(), rule_events.end());
|
||||
|
||||
auto event_names = inspector->get_events_names(events);
|
||||
for (const auto& n : inspector->get_events_names(m_state->ppm_event_info_of_interest))
|
||||
{
|
||||
event_names.erase(n);
|
||||
}
|
||||
|
||||
if(event_names.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the names of the ignored events and print them. */
|
||||
std::cerr << "Rules match ignored syscall: warning (ignored-evttype):" << std::endl;
|
||||
std::cerr << "Loaded rules match the following events: ";
|
||||
bool first = true;
|
||||
for(const auto& it : event_names)
|
||||
{
|
||||
std::cerr << (first ? "" : ", ") << it.c_str();
|
||||
first = false;
|
||||
}
|
||||
std::cerr << std::endl << "These events might be associated with syscalls undefined on your architecture (please take a look here: https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html). If syscalls are instead defined, you have to run Falco with `-A` to catch these events" << std::endl;
|
||||
}
|
||||
|
||||
application::run_result application::load_rules_files()
|
||||
{
|
||||
string all_rules;
|
||||
|
||||
if (!m_options.rules_filenames.empty())
|
||||
{
|
||||
m_state->config->m_rules_filenames = m_options.rules_filenames;
|
||||
}
|
||||
|
||||
if(m_state->config->m_rules_filenames.empty())
|
||||
{
|
||||
return run_result::fatal("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
|
||||
}
|
||||
|
||||
falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
|
||||
for (const auto& path : m_state->config->m_rules_filenames)
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, string(" ") + path + "\n");
|
||||
}
|
||||
|
||||
for (const auto &path : m_state->config->m_rules_filenames)
|
||||
{
|
||||
falco_configuration::read_rules_file_directory(path, m_state->config->m_loaded_rules_filenames, m_state->config->m_loaded_rules_folders);
|
||||
}
|
||||
|
||||
std::vector<std::string> rules_contents;
|
||||
falco::load_result::rules_contents_t rc;
|
||||
|
||||
try {
|
||||
read_files(m_state->config->m_loaded_rules_filenames.begin(),
|
||||
m_state->config->m_loaded_rules_filenames.end(),
|
||||
rules_contents,
|
||||
rc);
|
||||
}
|
||||
catch(falco_exception& e)
|
||||
{
|
||||
return run_result::fatal(e.what());
|
||||
}
|
||||
|
||||
for(auto &filename : m_state->config->m_loaded_rules_filenames)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + "\n");
|
||||
std::unique_ptr<falco::load_result> res;
|
||||
|
||||
res = m_state->engine->load_rules(rc.at(filename), filename);
|
||||
|
||||
if(!res->successful())
|
||||
{
|
||||
// Return the summary version as the error
|
||||
return run_result::fatal(res->as_string(true, rc));
|
||||
}
|
||||
|
||||
// If verbose is true, also print any warnings
|
||||
if(m_options.verbose && res->has_warnings())
|
||||
{
|
||||
fprintf(stderr, "%s\n", res->as_string(true, rc).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string err = "";
|
||||
if (!check_rules_plugin_requirements(err))
|
||||
{
|
||||
return run_result::fatal(err);
|
||||
}
|
||||
|
||||
for (const auto& substring : m_options.disabled_rule_substrings)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
|
||||
m_state->engine->enable_rule(substring, false);
|
||||
}
|
||||
|
||||
if(!m_options.disabled_rule_tags.empty())
|
||||
{
|
||||
for(auto &tag : m_options.disabled_rule_tags)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
|
||||
}
|
||||
m_state->engine->enable_rule_by_tag(m_options.disabled_rule_tags, false);
|
||||
}
|
||||
|
||||
if(!m_options.enabled_rule_tags.empty())
|
||||
{
|
||||
// Since we only want to enable specific
|
||||
// rules, first disable all rules.
|
||||
m_state->engine->enable_rule(all_rules, false);
|
||||
for(auto &tag : m_options.enabled_rule_tags)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
|
||||
}
|
||||
m_state->engine->enable_rule_by_tag(m_options.enabled_rule_tags, true);
|
||||
}
|
||||
|
||||
/* Reading a scap file we have no concepts of ignored events we read all we need. */
|
||||
if(!m_options.all_events && !is_capture_mode())
|
||||
{
|
||||
/* Here we have already initialized the application state with the interesting syscalls,
|
||||
* so we have to check if any event types used by the loaded rules are not considered by
|
||||
* Falco interesting set.
|
||||
*/
|
||||
check_for_ignored_events();
|
||||
}
|
||||
|
||||
if(m_options.all_events && m_options.modern_bpf)
|
||||
{
|
||||
/* Right now the modern BPF probe doesn't support the -A flag, we implemented just
|
||||
* the "simple set" syscalls.
|
||||
*/
|
||||
falco_logger::log(LOG_INFO, "The '-A' flag has no effect with the modern BPF probe, no further syscalls will be added\n");
|
||||
}
|
||||
|
||||
if (m_options.describe_all_rules)
|
||||
{
|
||||
m_state->engine->describe_rule(NULL);
|
||||
return run_result::exit();
|
||||
}
|
||||
|
||||
if (!m_options.describe_rule.empty())
|
||||
{
|
||||
m_state->engine->describe_rule(&(m_options.describe_rule));
|
||||
return run_result::exit();
|
||||
}
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2023 The Falco Authors.
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -20,30 +20,28 @@ limitations under the License.
|
||||
|
||||
#include <plugin_manager.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "application.h"
|
||||
|
||||
/* DEPRECATED: we will remove it in Falco 0.34. */
|
||||
#define FALCO_BPF_ENV_VARIABLE "FALCO_BPF_PROBE"
|
||||
|
||||
using namespace falco::app;
|
||||
using namespace falco::app::actions;
|
||||
|
||||
falco::app::run_result falco::app::actions::open_offline_inspector(falco::app::state& s)
|
||||
application::run_result application::open_offline_inspector()
|
||||
{
|
||||
try
|
||||
{
|
||||
s.offline_inspector->open_savefile(s.options.trace_filename);
|
||||
falco_logger::log(LOG_INFO, "Reading system call events from file: " + s.options.trace_filename + "\n");
|
||||
m_state->offline_inspector->open_savefile(m_options.trace_filename);
|
||||
falco_logger::log(LOG_INFO, "Reading system call events from file: " + m_options.trace_filename + "\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
catch (sinsp_exception &e)
|
||||
{
|
||||
return run_result::fatal("Could not open trace filename " + s.options.trace_filename + " for reading: " + e.what());
|
||||
return run_result::fatal("Could not open trace filename " + m_options.trace_filename + " for reading: " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
falco::app::state& s,
|
||||
application::run_result application::open_live_inspector(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
const std::string& source)
|
||||
{
|
||||
@@ -55,7 +53,7 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
{
|
||||
if (p->caps() & CAP_SOURCING && p->event_source() == source)
|
||||
{
|
||||
auto cfg = s.plugin_configs.at(p->name());
|
||||
auto cfg = m_state->plugin_configs.at(p->name());
|
||||
falco_logger::log(LOG_INFO, "Opening capture with plugin '" + cfg->m_name + "'\n");
|
||||
inspector->open_plugin(cfg->m_name, cfg->m_open_params);
|
||||
return run_result::ok();
|
||||
@@ -63,7 +61,7 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
}
|
||||
return run_result::fatal("Can't open inspector for plugin event source: " + source);
|
||||
}
|
||||
else if (s.options.userspace) /* udig engine. */
|
||||
else if (m_options.userspace) /* udig engine. */
|
||||
{
|
||||
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
|
||||
//
|
||||
@@ -72,16 +70,16 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
falco_logger::log(LOG_INFO, "Opening capture with udig\n");
|
||||
inspector->open_udig();
|
||||
}
|
||||
else if(!s.options.gvisor_config.empty()) /* gvisor engine. */
|
||||
else if(!m_options.gvisor_config.empty()) /* gvisor engine. */
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Opening capture with gVisor. Configuration path: " + s.options.gvisor_config);
|
||||
inspector->open_gvisor(s.options.gvisor_config, s.options.gvisor_root);
|
||||
falco_logger::log(LOG_INFO, "Opening capture with gVisor. Configuration path: " + m_options.gvisor_config);
|
||||
inspector->open_gvisor(m_options.gvisor_config, m_options.gvisor_root);
|
||||
}
|
||||
else if(s.options.modern_bpf) /* modern BPF engine. */
|
||||
else if(m_options.modern_bpf) /* modern BPF engine. */
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Opening capture with modern BPF probe.");
|
||||
falco_logger::log(LOG_INFO, "One ring buffer every '" + std::to_string(s.config->m_cpus_for_each_syscall_buffer) + "' CPUs.");
|
||||
inspector->open_modern_bpf(s.syscall_buffer_bytes_size, s.config->m_cpus_for_each_syscall_buffer, true, s.selected_sc_set);
|
||||
falco_logger::log(LOG_INFO, "One ring buffer every '" + std::to_string(m_state->config->m_cpus_for_each_syscall_buffer) + "' CPUs.");
|
||||
inspector->open_modern_bpf(m_state->syscall_buffer_bytes_size, m_state->config->m_cpus_for_each_syscall_buffer, true, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
|
||||
}
|
||||
else if(getenv(FALCO_BPF_ENV_VARIABLE) != NULL) /* BPF engine. */
|
||||
{
|
||||
@@ -99,14 +97,14 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
bpf_probe_path = full_path;
|
||||
}
|
||||
falco_logger::log(LOG_INFO, "Opening capture with BPF probe. BPF probe path: " + std::string(bpf_probe_path));
|
||||
inspector->open_bpf(bpf_probe_path, s.syscall_buffer_bytes_size, s.selected_sc_set);
|
||||
inspector->open_bpf(bpf_probe_path, m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
|
||||
}
|
||||
else /* Kernel module (default). */
|
||||
{
|
||||
try
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Opening capture with Kernel module");
|
||||
inspector->open_kmod(s.syscall_buffer_bytes_size, s.selected_sc_set);
|
||||
inspector->open_kmod(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
|
||||
}
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
@@ -116,7 +114,7 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Unable to load the driver\n");
|
||||
}
|
||||
inspector->open_kmod(s.syscall_buffer_bytes_size, s.selected_sc_set);
|
||||
inspector->open_kmod(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,5 +123,11 @@ falco::app::run_result falco::app::actions::open_live_inspector(
|
||||
return run_result::fatal(e.what());
|
||||
}
|
||||
|
||||
// This must be done after the open
|
||||
if (!m_options.all_events)
|
||||
{
|
||||
inspector->start_dropping_mode(1);
|
||||
}
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user