Compare commits

...

60 Commits

Author SHA1 Message Date
Andrea Terzolo
f8680182c5 tmp
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-12-19 15:57:07 +01:00
Samuel Gaist
d99c137b09 feat(outputs_http): implement keep alive
Signed-off-by: Samuel Gaist <samuel.gaist@idiap.ch>
2023-12-18 17:41:02 +01:00
Samuel Gaist
691bc8b04d feat(outputs_http): implement support for compressed upload
Signed-off-by: Samuel Gaist <samuel.gaist@idiap.ch>
2023-12-18 17:41:02 +01:00
Mark Stemm
ab0133d1dd Add unit tests for enabling/disabling rules
Add unit tests for enabling/disabling rules, covering:

 - matching names by substring
 - using "" to match all rules
 - matching names exactly
 - using ruleset ids in addition to ruleset names

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2023-12-18 15:58:04 +01:00
Mark Stemm
334302e525 Allow enabling rules by ruleset id in addition to name
Add alternate enable_* methods that allow enabling rulesets by ruleset
id in addition to name. This might be used by some filter_rulesets to
enable/disable rules on the fly via the falco engine.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2023-12-18 15:58:04 +01:00
Federico Di Pierro
1ab4e9e0fc chore(ci): enable aarch64 falco driver loader tests.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-18 15:26:02 +01:00
Federico Di Pierro
9e1e68f64b chore(unit_tests): added more tests for yaml env vars expansion.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-14 12:44:47 +01:00
Federico Di Pierro
752e8bf16c chore(falco.yaml): use HOME env var for ebpf probe path.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-13 17:45:45 +01:00
Federico Di Pierro
cbbcb61153 new(unit_tests,userspace): properly support env var expansions in all scalar values of yaml file.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-13 17:03:46 +01:00
Federico Di Pierro
3b095a5eda chore(unit_tests): added tests around empty config value resolving to default.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-13 17:03:46 +01:00
Federico Di Pierro
7805bf5ad5 fix(userspace,unit_tests): fixed bool parsing.
Moreover, added some more tests around env vars.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-13 17:03:46 +01:00
Federico Di Pierro
0c0fb63008 chore(unit_test,userspace): allow env var to get expanded in yaml even when part of a string.
Moreover, support env variable embedding another env variable.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-13 17:03:46 +01:00
Andrea Terzolo
ed346e90cd update(falco): bump engine version and checksum
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-12-13 16:59:46 +01:00
Andrea Terzolo
b190a60da7 chore: bump to latest libs commit
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-12-13 16:59:46 +01:00
Andrea Terzolo
34a896f3a5 new(.gitignore): ignore local CMakeUserPresets.json
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-12-13 16:59:46 +01:00
dependabot[bot]
1a338e1a39 build(deps): Bump submodules/falcosecurity-rules
Bumps [submodules/falcosecurity-rules](https://github.com/falcosecurity/rules) from `dd38952` to `262f569`.
- [Release notes](https://github.com/falcosecurity/rules/releases)
- [Commits](dd38952168...262f56986e)

---
updated-dependencies:
- dependency-name: submodules/falcosecurity-rules
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-13 16:01:46 +01:00
Luca Guerra
e3f54a14a6 update(readme): add actuated.dev badge
Signed-off-by: Luca Guerra <luca@guerra.sh>
2023-12-12 18:56:44 +01:00
Nitro Cao
4bfc42eb7d feat(falco): monitor events with more types for rules directory
Signed-off-by: Nitro Cao <jaycecao520@gmail.com>
2023-12-12 18:49:44 +01:00
Federico Di Pierro
47959abfed chore(docker): improve usage helper message.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-12 18:23:44 +01:00
Federico Di Pierro
8db79da647 chore(cmake,docker): bumped falcoctl to v0.7.0-beta5.
Moreover, small fix in docker images entrypoints regarding the name printed in usage.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-12 18:23:44 +01:00
dependabot[bot]
9c01f3518a build(deps): Bump submodules/falcosecurity-rules
Bumps [submodules/falcosecurity-rules](https://github.com/falcosecurity/rules) from `64e2adb` to `dd38952`.
- [Release notes](https://github.com/falcosecurity/rules/releases)
- [Commits](64e2adb309...dd38952168)

---
updated-dependencies:
- dependency-name: submodules/falcosecurity-rules
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-12 14:37:43 +01:00
Federico Di Pierro
f2ebdfaf8e fix(docker): small fixes in docker entrypoints for new driver loader.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-12 09:56:42 +01:00
Federico Aponte
e427c800f3 chore(build): fix error using find_package with ExternalProject_Add
Signed-off-by: Federico Aponte <federico.aponte@sysdig.com>
2023-12-11 16:52:39 +01:00
Federico Aponte
5e17ba6c23 chore(build): allow usage of non-bundled nlohmann-json
Signed-off-by: Federico Aponte <federico.aponte@sysdig.com>
2023-12-11 16:52:39 +01:00
Federico Di Pierro
e177898d2b update(cmake): bumped falcoctl to v0.7.0-beta4.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
2dfd687912 update(submodules): bumped falcosecurity/testing to latest master.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
b7538429b8 update(cmake): bumped falcoctl to v0.7.0-beta3.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
1de6f10ad6 update(cmake): bump falcoctl to 0.7.0-beta2.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
5f59fee54f update(submodules): bumped falcosecurity-testing to latest dev.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
4d566b2c71 chore(cmake): bumped falcoctl to v0.7.0-beta1.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
96f50ddac5 chore(scripts): integrate back master changes FALCO_DRIVER_CHOICE and FALCOCTL_ENABLED .
Also, env variables always have precedence over dialog (ie: if they are set, we always skip dialog).

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
ade27c2546 chore(scripts): use new default value for falcoctl driver.host-root config key.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
2244cc6f71 update(cmake): bump falcoctl to v0.7.0-alpha2.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
1dd47668dd chore(scripts): only configure falcoctl driver type when a driver is selected.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
5eb2ae8d76 chore(cmake/modules): bumped falcoctl to latest alpha.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
33451cf0bc fix(scripts): fixed driver names for new falcoctl/Falco naming.
But keep old systemd units names to avoid a breaking change.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
27161bb508 chore(scripts/systemd): properly make use of new config keys in systemd units.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
0c9538241d chore(docker): cleaned up useless removal of falcoctl.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>

Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
7452c5dc98 chore(cmake): bumped falcoctl to dev from https://github.com/falcosecurity/falcoctl/pull/343.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Federico Di Pierro
be100f7ad5 new(docker,scripts): dropped falco-driver-loader in favor of new falcoctl driver command.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-11 16:37:39 +01:00
Luca Guerra
8cf9b35b0e new(ci): run CI jobs on ARM64
Signed-off-by: Luca Guerra <luca@guerra.sh>
2023-12-07 18:26:28 +01:00
Luca Guerra
6e4ccb0007 update(ci): enable actuated.dev
Signed-off-by: Luca Guerra <luca@guerra.sh>
2023-12-07 18:26:28 +01:00
Federico Aponte
44b7352180 cleanup: fix several warnings from a Clang build
Signed-off-by: Federico Aponte <federico.aponte@sysdig.com>
2023-12-06 16:40:26 +01:00
Vicente J. Jiménez Miras
13991f1ea7 Add use of FALCO_DRIVER_CHOICE and FALCOCTL_ENABLED env vars
Signed-off-by: Vicente J. Jiménez Miras <vjjmiras@gmail.com>
2023-12-06 10:13:25 +01:00
Andrea Terzolo
10226a6c87 chore(falco): bump libs to 000d576ef877cb115cbb56f97187a1d62221e2bd
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-12-06 10:08:25 +01:00
Federico Aponte
e558c4f5a5 chore(build): remove outdated development libs
Signed-off-by: Federico Aponte <federico.aponte@sysdig.com>
2023-12-06 05:46:25 +01:00
Federico Di Pierro
0ba0dd8671 chore(docker/falco): add back some deps to falco docker image.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
2023-12-05 18:34:26 +01:00
Jason Dellaluce
305ed75268 update(submodules): bump falcosecurity-testing
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2023-12-02 09:38:15 +01:00
Jason Dellaluce
390a13bd40 update(userspace): optimizations in validation and description steps
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2023-12-02 09:38:15 +01:00
Jason Dellaluce
67542ec88e new(userspace/falco): support -L when validating for parity
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2023-12-02 09:38:15 +01:00
Jason Dellaluce
e3943ccac3 refactor(userspace/engine): uniform json lib in rules description and not print from engine
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2023-12-02 09:38:15 +01:00
dependabot[bot]
95968defa5 build(deps): Bump submodules/falcosecurity-testing
Bumps [submodules/falcosecurity-testing](https://github.com/falcosecurity/testing) from `92c313f` to `5248e6d`.
- [Commits](92c313f5ca...5248e6dff9)

---
updated-dependencies:
- dependency-name: submodules/falcosecurity-testing
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-01 13:12:12 +01:00
Luca Guerra
6411eed4a7 cleanup(falco): remove decode_uri as it is no longer used
Signed-off-by: Luca Guerra <luca@guerra.sh>
2023-11-29 17:42:06 +01:00
Andrea Terzolo
c5364be191 new: print system info when Falco starts
Print kernel info when Falco starts with a kernel driver

Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
2023-11-28 22:14:05 +01:00
Luca Guerra
ce4d28ef90 chore(falco): update to libs on nov 28th
Signed-off-by: Luca Guerra <luca@guerra.sh>
2023-11-28 17:14:04 +01:00
Melissa Kilby
3b068919d0 update(cmake): bump libs and driver to c2fd308 plus bump falco engine version
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2023-11-28 12:57:04 +01:00
Melissa Kilby
3e4566e5af cleanup(userspace/falco): minor adjustments to stats writer and rebase correction
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2023-11-28 12:57:04 +01:00
Melissa Kilby
9cb4c09500 cleanup(userspace/falco): enable sinsp_stats_v2
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2023-11-28 12:57:04 +01:00
Melissa Kilby
8196ee3b83 cleanup(libsinsp): simplify metrics flags config handling
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2023-11-28 12:57:04 +01:00
Melissa Kilby
af7192bdc3 update(userspace/falco): add libsinsp state metrics option
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
2023-11-28 12:57:04 +01:00
72 changed files with 1459 additions and 1588 deletions

View File

@@ -23,6 +23,13 @@ jobs:
arch: x86_64
version: ${{ needs.fetch-version.outputs.version }}
build-dev-packages-arm64:
needs: [fetch-version]
uses: ./.github/workflows/reusable_build_packages.yaml
with:
arch: aarch64
version: ${{ needs.fetch-version.outputs.version }}
test-dev-packages:
needs: [fetch-version, build-dev-packages]
uses: ./.github/workflows/reusable_test_packages.yaml
@@ -35,6 +42,16 @@ jobs:
static: ${{ matrix.static != '' && true || false }}
version: ${{ needs.fetch-version.outputs.version }}
test-dev-packages-arm64:
needs: [fetch-version, build-dev-packages]
uses: ./.github/workflows/reusable_test_packages.yaml
strategy:
fail-fast: false
with:
arch: aarch64
static: ${{ matrix.static != '' && true || false }}
version: ${{ needs.fetch-version.outputs.version }}
build-dev-minimal:
uses: ./.github/workflows/reusable_build_dev.yaml
with:
@@ -42,7 +59,15 @@ jobs:
git_ref: ${{ github.event.pull_request.head.sha }}
minimal: true
build_type: Debug
build-dev-minimal-arm64:
uses: ./.github/workflows/reusable_build_dev.yaml
with:
arch: aarch64
git_ref: ${{ github.event.pull_request.head.sha }}
minimal: true
build_type: Debug
# builds using system deps, checking out the PR's code
# note: this also runs a command that generates an output of form: "<engine_version> <some_hash>",
# of which <some_hash> is computed by hashing in order the following:

View File

@@ -48,7 +48,7 @@ jobs:
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
@@ -56,7 +56,7 @@ jobs:
run: sudo apt update -y
- name: Install build dependencies
run: sudo DEBIAN_FRONTEND=noninteractive apt install libssl-dev libyaml-dev libc-ares-dev libprotobuf-dev protobuf-compiler libjq-dev libyaml-cpp-dev libgrpc++-dev protobuf-compiler-grpc rpm libelf-dev cmake build-essential libcurl4-openssl-dev linux-headers-$(uname -r) clang llvm git -y
run: sudo DEBIAN_FRONTEND=noninteractive apt install libssl-dev libc-ares-dev libprotobuf-dev protobuf-compiler libyaml-cpp-dev libgrpc++-dev protobuf-compiler-grpc rpm libelf-dev cmake build-essential libcurl4-openssl-dev linux-headers-$(uname -r) clang llvm git -y
- name: Prepare project
run: |

View File

@@ -27,14 +27,13 @@ on:
required: false
default: ''
type: string
jobs:
build-and-test:
# See https://github.com/actions/runner/issues/409#issuecomment-1158849936
runs-on: ${{ (inputs.arch == 'aarch64' && fromJSON('[ "self-hosted", "linux", "ARM64" ]')) || 'ubuntu-22.04' }}
container: ${{ (inputs.arch == 'aarch64' && 'ubuntu:22.04') || '' }}
runs-on: ${{ (inputs.arch == 'aarch64' && 'actuated-arm64-8cpu-16gb') || 'ubuntu-22.04' }}
outputs:
cmdout: ${{ steps.run_cmd.outputs.out }}
cmdout: ${{ steps.run_cmd.outputs.out }}
steps:
- name: Checkout
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
@@ -44,14 +43,14 @@ jobs:
- name: Update base image
run: sudo apt update -y
- name: Install build dependencies
run: sudo DEBIAN_FRONTEND=noninteractive apt install libjq-dev libelf-dev libyaml-cpp-dev cmake build-essential git -y
run: sudo DEBIAN_FRONTEND=noninteractive apt install libelf-dev libyaml-cpp-dev cmake build-essential git -y
- name: Install build dependencies (non-minimal)
if: inputs.minimal != true
run: sudo DEBIAN_FRONTEND=noninteractive apt install libssl-dev libyaml-dev libc-ares-dev libprotobuf-dev protobuf-compiler libgrpc++-dev protobuf-compiler-grpc rpm libcurl4-openssl-dev linux-headers-$(uname -r) clang llvm -y
run: sudo DEBIAN_FRONTEND=noninteractive apt install libssl-dev libc-ares-dev libprotobuf-dev protobuf-compiler libgrpc++-dev protobuf-compiler-grpc rpm libcurl4-openssl-dev linux-headers-$(uname -r) clang llvm -y
- name: Prepare project
run: |
mkdir build
@@ -74,7 +73,7 @@ jobs:
- name: Run unit tests
run: |
pushd build
sudo ./unit_tests/falco_unit_tests
sudo ./unit_tests/falco_unit_tests
popd
- name: Run command

View File

@@ -27,7 +27,7 @@ on:
jobs:
build-docker:
# See https://github.com/actions/runner/issues/409#issuecomment-1158849936
runs-on: ${{ (inputs.arch == 'aarch64' && fromJSON('[ "self-hosted", "linux", "ARM64" ]')) || 'ubuntu-latest' }}
runs-on: ${{ (inputs.arch == 'aarch64' && 'actuated-arm64-8cpu-16gb') || 'ubuntu-latest' }}
env:
TARGETARCH: ${{ (inputs.arch == 'aarch64' && 'arm64') || 'amd64' }}
steps:

View File

@@ -14,7 +14,7 @@ on:
jobs:
build-modern-bpf-skeleton:
# See https://github.com/actions/runner/issues/409#issuecomment-1158849936
runs-on: ${{ (inputs.arch == 'aarch64' && fromJSON('[ "self-hosted", "linux", "ARM64" ]')) || 'ubuntu-latest' }}
runs-on: ${{ (inputs.arch == 'aarch64' && 'actuated-arm64-8cpu-16gb') || 'ubuntu-latest' }}
container: fedora:latest
steps:
# Always install deps before invoking checkout action, to properly perform a full clone.
@@ -40,7 +40,7 @@ jobs:
build-packages:
# See https://github.com/actions/runner/issues/409#issuecomment-1158849936
runs-on: ${{ (inputs.arch == 'aarch64' && fromJSON('[ "self-hosted", "linux", "ARM64" ]')) || 'ubuntu-latest' }}
runs-on: ${{ (inputs.arch == 'aarch64' && 'actuated-arm64-8cpu-16gb') || 'ubuntu-latest' }}
needs: [build-modern-bpf-skeleton]
container: centos:7
steps:

View File

@@ -19,7 +19,7 @@ on:
jobs:
test-packages:
# See https://github.com/actions/runner/issues/409#issuecomment-1158849936
runs-on: ${{ (inputs.arch == 'aarch64' && fromJSON('[ "self-hosted", "linux", "ARM64" ]')) || 'ubuntu-latest' }}
runs-on: ${{ (inputs.arch == 'aarch64' && 'actuated-arm64-8cpu-16gb') || 'ubuntu-latest' }}
steps:
- name: Checkout
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
@@ -44,12 +44,25 @@ jobs:
cd falco-${{ inputs.version }}-${{ inputs.arch }}
sudo cp -r * /
# x86_64 job run on ubuntu-22.04 and here we can install kernel-headers
- name: Install dependencies for falco-driver-loader tests on x86
if: ${{ inputs.arch == 'x86_64' }}
- name: Install dependencies for falco-driver-loader tests
run: |
sudo apt update -y
sudo apt install -y --no-install-recommends build-essential clang make llvm gcc dkms linux-headers-$(uname -r)
sudo apt install -y --no-install-recommends build-essential clang make llvm gcc dkms
- name: Install kernel headers (workaround)
if: inputs.arch == 'aarch64'
run: |
sudo mkdir -p /usr/src
sudo git clone --depth 1 --branch v$(uname -r) git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git /usr/src/linux
sudo rm /lib/modules/$(uname -r)/build
sudo ln -s /usr/src/linux-headers-$(uname -r)/ /lib/modules/$(uname -r)/build
sudo rm /lib/modules/$(uname -r)/source
sudo ln -s /usr/src/linux-headers-$(uname -r)/ /lib/modules/$(uname -r)/source
- name: Install kernel headers
if: inputs.arch == 'x86_64'
run: |
sudo apt install -y --no-install-recommends linux-headers-$(uname -r)
- name: Install go-junit-report
run: |
@@ -63,23 +76,36 @@ jobs:
go generate ./...
popd
# Right now we are not able to install kernel-headers on our ARM64 self-hosted runner.
# For this reason, we disable the falco-driver-loader tests, which require kernel headers on the host.
- name: Run regression tests
- name: Run Falco regression tests
env:
# fixme(leogr): this is a workaround for https://github.com/falcosecurity/falco/issues/2784
HOST_ROOT: ""
run: |
pushd submodules/falcosecurity-testing
./build/falco.test -falco-static=${{ inputs.static && 'true' || 'false' }} -test.timeout=90s -test.v >> ./report.txt 2>&1 || true
if ${{ inputs.static && 'false' || 'true' }}; then
./build/falcoctl.test -test.timeout=90s -test.v >> ./report.txt 2>&1 || true
./build/k8saudit.test -test.timeout=90s -test.v >> ./report.txt 2>&1 || true
if ${{ inputs.arch == 'x86_64' && 'true' || 'false' }}; then
sudo ./build/falco-driver-loader.test -test.timeout=90s -test.v >> ./report.txt 2>&1 || true
fi
fi
cat ./report.txt | go-junit-report -set-exit-code > report.xml
popd
- name: Run Falcoctl regression tests
env:
HOST_ROOT: ""
run: |
pushd submodules/falcosecurity-testing
./build/falcoctl.test -test.timeout=90s -test.v >> ./report.txt 2>&1 || true
popd
- name: Run K8saudit regression tests
env:
HOST_ROOT: ""
run: |
pushd submodules/falcosecurity-testing
./build/k8saudit.test -test.timeout=90s -test.v >> ./report.txt 2>&1 || true
popd
- name: Run Falco driver loader regression tests
env:
HOST_ROOT: ""
run: |
pushd submodules/falcosecurity-testing
sudo ./build/falco-driver-loader.test -test.timeout=90s -test.v
popd
- name: Test Summary

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@
.vscode/*
*.idea*
CMakeUserPresets.json

View File

@@ -2,7 +2,7 @@
[![Latest release](https://img.shields.io/github/v/release/falcosecurity/falco?style=for-the-badge)](https://github.com/falcosecurity/falco/releases/latest) [![Supported Architectures](https://img.shields.io/badge/ARCHS-x86__64%7Caarch64-blueviolet?style=for-the-badge)](https://github.com/falcosecurity/falco/releases/latest) [![License](https://img.shields.io/github/license/falcosecurity/falco?style=for-the-badge)](COPYING) [![Docs](https://img.shields.io/badge/docs-latest-green.svg?style=for-the-badge)](https://falco.org/docs)
[![Falco Core Repository](https://github.com/falcosecurity/evolution/blob/main/repos/badges/falco-core-blue.svg)](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#core-scope) [![Stable](https://img.shields.io/badge/status-stable-brightgreen?style=for-the-badge)](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#stable) [![OpenSSF Best Practices](https://img.shields.io/cii/summary/2317?label=OpenSSF%20Best%20Practices&style=for-the-badge)](https://bestpractices.coreinfrastructure.org/projects/2317)
[![Falco Core Repository](https://github.com/falcosecurity/evolution/blob/main/repos/badges/falco-core-blue.svg)](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#core-scope) [![Stable](https://img.shields.io/badge/status-stable-brightgreen?style=for-the-badge)](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#stable) [![OpenSSF Best Practices](https://img.shields.io/cii/summary/2317?label=OpenSSF%20Best%20Practices&style=for-the-badge)](https://bestpractices.coreinfrastructure.org/projects/2317) <a href="https://actuated.dev/"><img alt="Arm CI sponsored by Actuated" src="https://docs.actuated.dev/images/actuated-badge.png" width="120px"></img></a>
[![Falco](https://falco.org/img/brand/falco-horizontal-color.svg)](https://falco.org)

View File

@@ -34,8 +34,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 "7cbc03a535ead9d530f7b77ffd68766d5e22da74")
set(DRIVER_CHECKSUM "SHA256=94d110ad1738cce2635fd15d41701bea5e061fd9a5a4be3f2ee8ec7a28fe50cc")
set(DRIVER_VERSION "ea23169c13df2ce5d8ba7d6faecdaa65f36140cb")
set(DRIVER_CHECKSUM "SHA256=c84c80a9a2241667e1c0be7a611f071c9b0264ac81b98103d2272b939337d02f")
endif()
# cd /path/to/build && cmake /path/to/source

View File

@@ -16,14 +16,14 @@ include(ExternalProject)
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} FALCOCTL_SYSTEM_NAME)
set(FALCOCTL_VERSION "0.6.2")
set(FALCOCTL_VERSION "0.7.0-beta5")
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
set(FALCOCTL_SYSTEM_PROC_GO "amd64")
set(FALCOCTL_HASH "2d06d7577dbae91fb085f71477ff6e22076a815978bddd036984fa077236a515")
set(FALCOCTL_HASH "e08cdd3bed96bda2e45f54d86aec0f1ad986963ff30624578283b218829df225")
else() # aarch64
set(FALCOCTL_SYSTEM_PROC_GO "arm64")
set(FALCOCTL_HASH "0b711a1b3499f479d999f4f4d2c94fc4f0bc23a2506711b613e6eedb0593631b")
set(FALCOCTL_HASH "582b6b73f77cfdf66dbcddadaa1073fa1802f24bd1670c1cf578e524fd3e8486")
endif()
ExternalProject_Add(

View File

@@ -26,17 +26,17 @@ if(FALCOSECURITY_LIBS_SOURCE_DIR)
else()
# FALCOSECURITY_LIBS_REPO accepts a repository name (<org name>/<repo name>) alternative to the falcosecurity/libs repository.
# In case you want to test against a fork of falcosecurity/libs just pass the variable -
# ie., `cmake -DFALCOSECURITY_LIBS_REPO=<your-gh-handle>/libs ..`
# ie., `cmake -DFALCOSECURITY_LIBS_REPO=<your-gh-handle>/libs ..`
if (NOT FALCOSECURITY_LIBS_REPO)
set(FALCOSECURITY_LIBS_REPO "falcosecurity/libs")
endif()
# FALCOSECURITY_LIBS_VERSION accepts a git reference (branch name, commit hash, or tag) to the falcosecurity/libs repository.
# In case you want to test against another falcosecurity/libs version (or branch, or commit) just pass the variable -
# ie., `cmake -DFALCOSECURITY_LIBS_VERSION=dev ..`
# ie., `cmake -DFALCOSECURITY_LIBS_VERSION=dev ..`
if(NOT FALCOSECURITY_LIBS_VERSION)
set(FALCOSECURITY_LIBS_VERSION "7cbc03a535ead9d530f7b77ffd68766d5e22da74")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=94d110ad1738cce2635fd15d41701bea5e061fd9a5a4be3f2ee8ec7a28fe50cc")
set(FALCOSECURITY_LIBS_VERSION "ea23169c13df2ce5d8ba7d6faecdaa65f36140cb")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=c84c80a9a2241667e1c0be7a611f071c9b0264ac81b98103d2272b939337d02f")
endif()
# cd /path/to/build && cmake /path/to/source
@@ -84,12 +84,11 @@ set(CREATE_TEST_TARGETS OFF CACHE BOOL "")
set(BUILD_LIBSCAP_EXAMPLES OFF CACHE BOOL "")
set(USE_BUNDLED_TBB ON CACHE BOOL "")
set(USE_BUNDLED_B64 ON CACHE BOOL "")
set(USE_BUNDLED_JSONCPP ON CACHE BOOL "")
set(USE_BUNDLED_NLOHMANN_JSON ON CACHE BOOL "")
set(USE_BUNDLED_VALIJSON ON CACHE BOOL "")
set(USE_BUNDLED_RE2 ON CACHE BOOL "")
set(USE_BUNDLED_UTHASH ON CACHE BOOL "")
set(USE_BUNDLED_TINYDIR ON CACHE BOOL "")
list(APPEND CMAKE_MODULE_PATH "${FALCOSECURITY_LIBS_SOURCE_DIR}/cmake/modules")

View File

@@ -1,28 +0,0 @@
# SPDX-License-Identifier: Apache-2.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.
#
set(LIBYAML_SRC "${PROJECT_BINARY_DIR}/libyaml-prefix/src/libyaml")
set(LIBYAML_INSTALL_DIR "${LIBYAML_SRC}/target")
message(STATUS "Using bundled libyaml in '${LIBYAML_SRC}'")
set(LIBYAML_LIB "${LIBYAML_SRC}/src/.libs/libyaml.a")
externalproject_add(
libyaml
URL "https://github.com/yaml/libyaml/releases/download/0.2.5/yaml-0.2.5.tar.gz"
URL_HASH "SHA256=c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4"
CONFIGURE_COMMAND ./configure --prefix=${LIBYAML_INSTALL_DIR} CFLAGS=-fPIC CPPFLAGS=-fPIC --enable-static=true --enable-shared=false
BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1
BUILD_BYPRODUCTS ${LIBYAML_LIB}
INSTALL_COMMAND ${CMD_MAKE} install
)

View File

@@ -12,24 +12,15 @@
# 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
if(USE_BUNDLED_NLOHMANN_JSON)
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}'")
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/njson-prefix -DJSON_BuildTests=OFF -DBUILD_TESTING=OFF
)
set(nlohmann_json_DIR ${PROJECT_BINARY_DIR}/njson-prefix/include)
else()
find_package(nlohmann_json CONFIG REQUIRED)
add_custom_target(njson)
endif()

View File

@@ -18,6 +18,28 @@
#
print_usage() {
echo ""
echo "Usage:"
echo " docker run -i -t --privileged -v /root/.falco:/root/.falco -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro -v /etc:/host/etc:ro falcosecurity/falco-driver-loader-legacy:latest [driver] [options]"
echo ""
echo "Available drivers:"
echo " kmod kernel module (default)"
echo " ebpf eBPF probe"
echo ""
echo "Options:"
echo " --help show this help message"
echo " --clean try to remove an already present driver installation"
echo " --compile try to compile the driver locally (default true)"
echo " --download try to download a prebuilt driver (default true)"
echo " --print-env skip execution and print env variables for other tools to consume"
echo ""
echo "Environment variables:"
echo " FALCOCTL_DRIVER_REPOS specify different URL(s) where to look for prebuilt Falco drivers (comma separated)"
echo " FALCOCTL_DRIVER_NAME specify a different name for the driver"
echo ""
}
echo "* Setting up /usr/src links from host"
for i in "$HOST_ROOT/usr/src"/*
@@ -26,4 +48,64 @@ do
ln -s "$i" "/usr/src/$base"
done
/usr/bin/falco-driver-loader "$@"
ENABLE_COMPILE="false"
ENABLE_DOWNLOAD="false"
has_driver=
has_opts=
while test $# -gt 0; do
case "$1" in
kmod|ebpf)
if [ -n "$has_driver" ]; then
>&2 echo "Only one driver per invocation"
print_usage
exit 1
else
/usr/bin/falcoctl driver config --type $1
has_driver="true"
fi
;;
-h|--help)
print_usage
exit 0
;;
--clean)
/usr/bin/falcoctl driver cleanup
exit 0
;;
--compile)
ENABLE_COMPILE="true"
has_opts="true"
;;
--download)
ENABLE_DOWNLOAD="true"
has_opts="true"
;;
--source-only)
>&2 echo "Support dropped in Falco 0.37.0."
print_usage
exit 1
;;
--print-env)
/usr/bin/falcoctl driver printenv
exit 0
;;
--*)
>&2 echo "Unknown option: $1"
print_usage
exit 1
;;
*)
>&2 echo "Unknown driver: $1"
print_usage
exit 1
;;
esac
shift
done
if [ -z "$has_opts" ]; then
ENABLE_COMPILE="true"
ENABLE_DOWNLOAD="true"
fi
/usr/bin/falcoctl driver install --compile=$ENABLE_COMPILE --download=$ENABLE_DOWNLOAD

View File

@@ -18,6 +18,28 @@
#
print_usage() {
echo ""
echo "Usage:"
echo " docker run -i -t --privileged -v /root/.falco:/root/.falco -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro -v /etc:/host/etc:ro falcosecurity/falco-driver-loader:latest [driver] [options]"
echo ""
echo "Available drivers:"
echo " kmod kernel module (default)"
echo " ebpf eBPF probe"
echo ""
echo "Options:"
echo " --help show this help message"
echo " --clean try to remove an already present driver installation"
echo " --compile try to compile the driver locally (default true)"
echo " --download try to download a prebuilt driver (default true)"
echo " --print-env skip execution and print env variables for other tools to consume"
echo ""
echo "Environment variables:"
echo " FALCOCTL_DRIVER_REPOS specify different URL(s) where to look for prebuilt Falco drivers (comma separated)"
echo " FALCOCTL_DRIVER_NAME specify a different name for the driver"
echo ""
}
echo "* Setting up /usr/src links from host"
for i in "$HOST_ROOT/usr/src"/*
@@ -26,4 +48,64 @@ do
ln -s "$i" "/usr/src/$base"
done
/usr/bin/falco-driver-loader "$@"
ENABLE_COMPILE="false"
ENABLE_DOWNLOAD="false"
has_driver=
has_opts=
while test $# -gt 0; do
case "$1" in
kmod|ebpf)
if [ -n "$has_driver" ]; then
>&2 echo "Only one driver per invocation"
print_usage
exit 1
else
/usr/bin/falcoctl driver config --type $1
has_driver="true"
fi
;;
-h|--help)
print_usage
exit 0
;;
--clean)
/usr/bin/falcoctl driver cleanup
exit 0
;;
--compile)
ENABLE_COMPILE="true"
has_opts="true"
;;
--download)
ENABLE_DOWNLOAD="true"
has_opts="true"
;;
--source-only)
>&2 echo "Support dropped in Falco 0.37.0."
print_usage
exit 1
;;
--print-env)
/usr/bin/falcoctl driver printenv
exit 0
;;
--*)
>&2 echo "Unknown option: $1"
print_usage
exit 1
;;
*)
>&2 echo "Unknown driver: $1"
print_usage
exit 1
;;
esac
shift
done
if [ -z "$has_opts" ]; then
ENABLE_COMPILE="true"
ENABLE_DOWNLOAD="true"
fi
/usr/bin/falcoctl driver install --compile=$ENABLE_COMPILE --download=$ENABLE_DOWNLOAD

View File

@@ -19,17 +19,26 @@ RUN cp /etc/skel/.bashrc /root && cp /etc/skel/.profile /root
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
bc \
bison \
ca-certificates \
clang \
curl \
dkms \
dwarves \
flex \
gcc \
gcc-11 \
gnupg2 \
jq \
libelf1 \
libc6-dev \
libelf-dev \
libssl-dev \
llvm \
make \
netcat-openbsd \
patchelf \
xz-utils \
&& rm -rf /var/lib/apt/lists/*
RUN curl -s https://falco.org/repo/falcosecurity-packages.asc | apt-key add - \

View File

@@ -17,6 +17,29 @@
# limitations under the License.
#
print_usage() {
echo ""
echo "Usage:"
echo " docker run -i -t --privileged -v /root/.falco:/root/.falco -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro -v /etc:/host/etc:ro -e 'FALCO_DRIVER_LOADER_OPTIONS=[driver] [options]' falcosecurity/falco:latest"
echo ""
echo "Available FALCO_DRIVER_LOADER_OPTIONS drivers:"
echo " kmod kernel module (default)"
echo " ebpf eBPF probe"
echo ""
echo "FALCO_DRIVER_LOADER_OPTIONS options:"
echo " --help show this help message"
echo " --clean try to remove an already present driver installation"
echo " --compile try to compile the driver locally (default true)"
echo " --download try to download a prebuilt driver (default true)"
echo " --print-env skip execution and print env variables for other tools to consume"
echo ""
echo "Environment variables:"
echo " FALCOCTL_DRIVER_REPOS specify different URL(s) where to look for prebuilt Falco drivers (comma separated)"
echo " FALCOCTL_DRIVER_NAME specify a different name for the driver"
echo ""
}
# Set the SKIP_DRIVER_LOADER variable to skip loading the driver
if [[ -z "${SKIP_DRIVER_LOADER}" ]]; then
@@ -29,9 +52,69 @@ if [[ -z "${SKIP_DRIVER_LOADER}" ]]; then
done
# convert the optional space-separated env variable FALCO_DRIVER_LOADER_OPTIONS to array, prevent
# shell expansion and use it as argument list for falco-driver-loader
# shell expansion and use it as argument list for falcoctl
read -a falco_driver_loader_option_arr <<< $FALCO_DRIVER_LOADER_OPTIONS
/usr/bin/falco-driver-loader "${falco_driver_loader_option_arr[@]}"
ENABLE_COMPILE="false"
ENABLE_DOWNLOAD="false"
has_driver=
has_opts=
for opt in "${falco_driver_loader_option_arr[@]}"
do
case "$opt" in
kmod|ebpf)
if [ -n "$has_driver" ]; then
>&2 echo "Only one driver per invocation"
print_usage
exit 1
else
/usr/bin/falcoctl driver config --type $opt
has_driver="true"
fi
;;
-h|--help)
print_usage
exit 0
;;
--clean)
/usr/bin/falcoctl driver cleanup
exit 0
;;
--compile)
ENABLE_COMPILE="true"
has_opts="true"
;;
--download)
ENABLE_DOWNLOAD="true"
has_opts="true"
;;
--source-only)
>&2 echo "Support dropped in Falco 0.37.0."
print_usage
exit 1
;;
--print-env)
/usr/bin/falcoctl driver printenv
exit 0
;;
--*)
>&2 echo "Unknown option: $1"
print_usage
exit 1
;;
*)
>&2 echo "Unknown driver: $1"
print_usage
exit 1
;;
esac
done
if [ -z "$has_opts" ]; then
ENABLE_COMPILE="true"
ENABLE_DOWNLOAD="true"
fi
/usr/bin/falcoctl driver install --compile=$ENABLE_COMPILE --download=$ENABLE_DOWNLOAD
fi
exec "$@"

View File

@@ -15,7 +15,7 @@ RUN curl -L -o falco.tar.gz \
tar -xvf falco.tar.gz && \
rm -f falco.tar.gz && \
mv falco-${FALCO_VERSION}-$(uname -m) falco && \
rm -rf /falco/usr/src/falco-* /falco/usr/bin/falco-driver-loader
rm -rf /falco/usr/src/falco-*
RUN sed -e 's/time_format_iso_8601: false/time_format_iso_8601: true/' < /falco/etc/falco/falco.yaml > /falco/etc/falco/falco.yaml.new \
&& mv /falco/etc/falco/falco.yaml.new /falco/etc/falco/falco.yaml

View File

@@ -16,7 +16,7 @@ RUN FALCO_VERSION_URLENCODED=$(echo -n ${FALCO_VERSION}|jq -sRr @uri) && \
tar -xvf falco.tar.gz && \
rm -f falco.tar.gz && \
mv falco-${FALCO_VERSION}-$(uname -m) falco && \
rm -rf /falco/usr/src/falco-* /falco/usr/bin/falco-driver-loader
rm -rf /falco/usr/src/falco-*
RUN sed -e 's/time_format_iso_8601: false/time_format_iso_8601: true/' < /falco/etc/falco/falco.yaml > /falco/etc/falco/falco.yaml.new \
&& mv /falco/etc/falco/falco.yaml.new /falco/etc/falco/falco.yaml

View File

@@ -318,7 +318,7 @@ engine:
drop_failed_exit: false
ebpf:
# path to the elf file to load.
probe: /root/.falco/falco-bpf.o
probe: ${HOME}/.falco/falco-bpf.o
buf_size_preset: 4
drop_failed_exit: false
modern_ebpf:
@@ -566,6 +566,8 @@ http_output:
client_key: "/etc/ssl/certs/client.key"
# Whether to echo server answers to stdout
echo: false
compress_uploads: false
keep_alive: false
# [Stable] `program_output`
#
@@ -917,13 +919,22 @@ syscall_event_drops:
# number of CPUs to determine overall usage. Memory metrics are provided in raw
# units (`kb` for `RSS`, `PSS` and `VSZ` or `bytes` for `container_memory_used`)
# and can be uniformly converted to megabytes (MB) using the
# `convert_memory_to_mb` functionality. In environments such as Kubernetes, it
# is crucial to track Falco's container memory usage. To customize the path of
# the memory metric file, you can create an environment variable named
# `FALCO_CGROUP_MEM_PATH` and set it to the desired file path. By default, Falco
# uses the file `/sys/fs/cgroup/memory/memory.usage_in_bytes` to monitor
# container memory usage, which aligns with Kubernetes'
# `container_memory_working_set_bytes` metric.
# `convert_memory_to_mb` functionality. In environments such as Kubernetes when
# deployed as daemonset, it is crucial to track Falco's container memory usage.
# To customize the path of the memory metric file, you can create an environment
# variable named `FALCO_CGROUP_MEM_PATH` and set it to the desired file path. By
# default, Falco uses the file `/sys/fs/cgroup/memory/memory.usage_in_bytes` to
# monitor container memory usage, which aligns with Kubernetes'
# `container_memory_working_set_bytes` metric. Finally, we emit the overall host
# CPU and memory usages, along with the total number of processes and open file
# descriptors (fds) on the host, obtained from the proc file system unrelated to
# Falco's monitoring. These metrics help assess Falco's usage in relation to the
# server's workload intensity.
#
# `state_counters_enabled`: Emit counters related to Falco's state engine, including
# added, removed threads or file descriptors (fds), and failed lookup, store, or
# retrieve actions in relation to Falco's underlying process cache table (threadtable).
# We also log the number of currently cached containers if applicable.
#
# `kernel_event_counters_enabled`: Emit kernel side event and drop counters, as
# an alternative to `syscall_event_drops`, but with some differences. These
@@ -956,6 +967,7 @@ metrics:
output_rule: true
# output_file: /tmp/falco_stats.jsonl
resource_utilization_enabled: true
state_counters_enabled: true
kernel_event_counters_enabled: true
libbpf_stats_enabled: true
convert_memory_to_mb: true

View File

@@ -41,11 +41,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
configure_file(rpm/postinstall.in rpm/postinstall COPYONLY)
configure_file(rpm/postuninstall.in rpm/postuninstall COPYONLY)
configure_file(rpm/preuninstall.in rpm/preuninstall COPYONLY)
# driver loader
configure_file(falco-driver-loader falco-driver-loader @ONLY)
install(PROGRAMS ${PROJECT_BINARY_DIR}/scripts/falco-driver-loader
DESTINATION ${FALCO_BIN_DIR} COMPONENT "${FALCO_COMPONENT_NAME}")
endif()
# Install Falcoctl config file
@@ -53,5 +48,6 @@ if (NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT MUSL_OPTIMIZED_BUILD)
if(NOT DEFINED FALCOCTL_ETC_DIR)
set(FALCOCTL_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/falcoctl")
endif()
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/falcoctl/falcoctl.yaml DESTINATION "${FALCOCTL_ETC_DIR}" COMPONENT "${FALCO_COMPONENT_NAME}")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/falcoctl/falcoctl.yaml.in ${PROJECT_BINARY_DIR}/scripts/falcoctl/falcoctl.yaml)
install(FILES ${PROJECT_BINARY_DIR}/scripts/falcoctl/falcoctl.yaml DESTINATION "${FALCOCTL_ETC_DIR}" COMPONENT "${FALCO_COMPONENT_NAME}")
endif()

View File

@@ -18,6 +18,8 @@
#
chosen_driver=
chosen_unit=
CHOICE=
# Every time we call this script we want to stat from a clean state.
echo "[POST-INSTALL] Disable all possible 'falco' services:"
@@ -36,39 +38,63 @@ systemctl --system disable 'falcoctl-artifact-follow.service' || true
systemctl --system unmask falcoctl-artifact-follow.service || true
if [ "$1" = "configure" ]; then
if [ -x /usr/bin/dialog ] && [ "${FALCO_FRONTEND}" != "noninteractive" ]; then
# If dialog is installed, create a dialog to let users choose the correct driver for them
CHOICE=$(dialog --clear --title "Falco drivers" --menu "Choose your preferred driver:" 12 55 4 \
1 "Manual configuration (no unit is started)" \
2 "Kmod" \
3 "eBPF" \
4 "Modern eBPF" \
2>&1 >/dev/tty)
case $CHOICE in
2)
chosen_driver="kmod"
;;
3)
chosen_driver="bpf"
;;
4)
chosen_driver="modern-bpf"
;;
esac
if [ -n "$chosen_driver" ]; then
CHOICE=$(dialog --clear --title "Falcoctl" --menu "Do you want to follow automatic ruleset updates?" 10 40 2 \
1 "Yes" \
2 "No" \
case $FALCO_DRIVER_CHOICE in
kmod)
CHOICE=2
;;
ebpf)
CHOICE=3
;;
modern_ebpf)
CHOICE=4
;;
esac
if [ -z $CHOICE ] && [ -x /usr/bin/dialog ] && [ "${FALCO_FRONTEND}" != "noninteractive" ]; then
# If dialog is installed, create a dialog to let users choose the correct driver for them
CHOICE=$(dialog --clear --title "Falco drivers" --menu "Choose your preferred driver:" 12 55 4 \
1 "Manual configuration (no unit is started)" \
2 "Kmod" \
3 "eBPF" \
4 "Modern eBPF" \
2>&1 >/dev/tty)
fi
case $CHOICE in
2)
chosen_driver="kmod"
chosen_unit="kmod"
;;
3)
chosen_driver="ebpf"
chosen_unit="bpf"
;;
4)
chosen_driver="modern_ebpf"
chosen_unit="modern-bpf"
;;
esac
if [ -n "$CHOICE" ]; then
echo "[POST-INSTALL] Configure falcoctl driver type:"
falcoctl driver config --type $chosen_driver
CHOICE=
case $FALCOCTL_ENABLED in
no)
CHOICE=2
;;
esac
if [ -z $CHOICE ] && [ -x /usr/bin/dialog ] && [ "${FALCO_FRONTEND}" != "noninteractive" ]; then
CHOICE=$(dialog --clear --title "Falcoctl" --menu "Do you want to follow automatic ruleset updates?" 10 40 2 \
1 "Yes" \
2 "No" \
2>&1 >/dev/tty)
fi
case $CHOICE in
2)
# we don't want falcoctl enabled, we mask it
systemctl --system mask falcoctl-artifact-follow.service || true
# we don't want falcoctl enabled, we mask it
systemctl --system mask falcoctl-artifact-follow.service || true
;;
esac
fi
clear
fi
fi
clear
fi
set -e
@@ -76,25 +102,25 @@ set -e
echo "[POST-INSTALL] Trigger deamon-reload:"
systemctl --system daemon-reload || true
# If needed, try to load/compile the driver through falco-driver-loader
# If needed, try to load/compile the driver through falcoctl
case "$chosen_driver" in
"kmod")
# Only compile for kmod, in this way we use dkms
echo "[POST-INSTALL] Call 'falco-driver-loader --compile module':"
falco-driver-loader --compile module
echo "[POST-INSTALL] Call 'falcoctl driver install for kmod:"
falcoctl driver install --download=false
;;
"bpf")
echo "[POST-INSTALL] Call 'falco-driver-loader bpf':"
falco-driver-loader bpf
"ebpf")
echo "[POST-INSTALL] Call 'falcoctl driver install for ebpf':"
falcoctl driver install
;;
esac
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
if [ -n "$chosen_driver" ]; then
if [ -n "$chosen_unit" ]; then
# we do this in 2 steps because `enable --now` is not always supported
echo "[POST-INSTALL] Enable 'falco-$chosen_driver.service':"
systemctl --system enable "falco-$chosen_driver.service" || true
echo "[POST-INSTALL] Start 'falco-$chosen_driver.service':"
systemctl --system start "falco-$chosen_driver.service" || true
echo "[POST-INSTALL] Enable 'falco-$chosen_unit.service':"
systemctl --system enable "falco-$chosen_unit.service" || true
echo "[POST-INSTALL] Start 'falco-$chosen_unit.service':"
systemctl --system start "falco-$chosen_unit.service" || true
fi
fi

View File

@@ -31,7 +31,7 @@ case "$1" in
systemctl --system stop 'falco-custom.service' || true
systemctl --system stop 'falcoctl-artifact-follow.service' || true
echo "[PRE-REMOVE] Call 'falco-driver-loader --clean:'"
falco-driver-loader --clean
echo "[PRE-REMOVE] Call 'falcoctl driver cleanup:'"
falcoctl driver cleanup
;;
esac

View File

@@ -1,866 +0,0 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: Apache-2.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.
#
# Simple script that desperately tries to load the kernel instrumentation by
# looking for it in a bunch of ways. Convenient when running Falco inside
# a container or in other weird environments.
#
#
# Returns 1 if $cos_ver > $base_ver, 0 otherwise
#
cos_version_greater() {
if [[ $cos_ver == "${base_ver}" ]]; then
return 0
fi
#
# COS build numbers are in the format x.y.z
#
a=$(echo "${cos_ver}" | cut -d. -f1)
b=$(echo "${cos_ver}" | cut -d. -f2)
c=$(echo "${cos_ver}" | cut -d. -f3)
d=$(echo "${base_ver}" | cut -d. -f1)
e=$(echo "${base_ver}" | cut -d. -f2)
f=$(echo "${base_ver}" | cut -d. -f3)
# Test the first component
if [[ $a -gt $d ]]; then
return 1
elif [[ $d -gt $a ]]; then
return 0
fi
# Test the second component
if [[ $b -gt $e ]]; then
return 1
elif [[ $e -gt $b ]]; then
return 0
fi
# Test the third component
if [[ $c -gt $f ]]; then
return 1
elif [[ $f -gt $c ]]; then
return 0
fi
# If we get here, probably malformatted version string?
return 0
}
get_kernel_config() {
if [ -f /proc/config.gz ]; then
echo "* Found kernel config at /proc/config.gz"
KERNEL_CONFIG_PATH=/proc/config.gz
elif [ -f "/boot/config-${KERNEL_RELEASE}" ]; then
echo "* Found kernel config at /boot/config-${KERNEL_RELEASE}"
KERNEL_CONFIG_PATH=/boot/config-${KERNEL_RELEASE}
elif [ -n "${HOST_ROOT}" ] && [ -f "${HOST_ROOT}/boot/config-${KERNEL_RELEASE}" ]; then
echo "* Found kernel config at ${HOST_ROOT}/boot/config-${KERNEL_RELEASE}"
KERNEL_CONFIG_PATH="${HOST_ROOT}/boot/config-${KERNEL_RELEASE}"
elif [ -f "/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" ]; then
echo "* Found kernel config at /usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
KERNEL_CONFIG_PATH="/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
elif [ -n "${HOST_ROOT}" ] && [ -f "${HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" ]; then
echo "* Found kernel config at ${HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
KERNEL_CONFIG_PATH="${HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
elif [ -f "/lib/modules/${KERNEL_RELEASE}/config" ]; then
# This code works both for native host and containers assuming that
# Dockerfile sets up the desired symlink /lib/modules -> $HOST_ROOT/lib/modules
echo "* Found kernel config at /lib/modules/${KERNEL_RELEASE}/config"
KERNEL_CONFIG_PATH="/lib/modules/${KERNEL_RELEASE}/config"
fi
if [ -z "${KERNEL_CONFIG_PATH}" ]; then
>&2 echo "Cannot find kernel config"
exit 1
fi
if [[ "${KERNEL_CONFIG_PATH}" == *.gz ]]; then
HASH=$(zcat "${KERNEL_CONFIG_PATH}" | md5sum - | cut -d' ' -f1)
else
HASH=$(md5sum "${KERNEL_CONFIG_PATH}" | cut -d' ' -f1)
fi
}
get_target_id() {
if [ -f "${HOST_ROOT}/etc/os-release" ]; then
# freedesktop.org and systemd
# shellcheck source=/dev/null
source "${HOST_ROOT}/etc/os-release"
OS_ID=$ID
elif [ -f "${HOST_ROOT}/etc/debian_version" ]; then
# Older debian distros
# fixme > Can this happen on older Ubuntu?
OS_ID=debian
elif [ -f "${HOST_ROOT}/etc/centos-release" ]; then
# Older CentOS distros
OS_ID=centos
elif [ -f "${HOST_ROOT}/etc/redhat-release" ]; then
# Older RHEL distros
OS_ID=rhel
else
# No target id can be determinand
TARGET_ID="undetermined"
return
fi
# Overwrite the OS_ID if /etc/VERSION file is present.
# Not sure if there is a better way to detect minikube.
if [ -f "${HOST_ROOT}/etc/VERSION" ]; then
OS_ID=minikube
fi
case "${OS_ID}" in
("amzn")
case "${VERSION_ID}" in
("2")
TARGET_ID="amazonlinux2"
;;
("2022")
TARGET_ID="amazonlinux2022"
;;
("2023")
TARGET_ID="amazonlinux2023"
;;
(*)
TARGET_ID="amazonlinux"
;;
esac
;;
("debian")
# Workaround: debian kernelreleases might now be actual kernel running;
# instead, they might be the Debian kernel package
# providing the compatible kernel ABI
# See https://lists.debian.org/debian-user/2017/03/msg00485.html
# Real kernel release is embedded inside the kernel version.
# Moreover, kernel arch, when present, is attached to the former,
# therefore make sure to properly take it and attach it to the latter.
# Moreover, we support 3 flavors for debian kernels: cloud, rt and normal.
# KERNEL-RELEASE will have a `-rt`, or `-cloud` if we are in one of these flavors.
# Manage it to download the correct driver.
#
# Example: KERNEL_RELEASE="5.10.0-0.deb10.22-rt-amd64" and `uname -v`="5.10.178-3"
# should lead to: KERNEL_RELEASE="5.10.178-3-rt-amd64"
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
local ARCH_extra=""
if [[ $KERNEL_RELEASE =~ -?(rt-|cloud-|)(amd64|arm64) ]];
then
ARCH_extra="-${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
fi
if [[ ${DRIVER_KERNEL_VERSION} =~ ([0-9]+\.[0-9]+\.[0-9]+\-[0-9]+) ]];
then
KERNEL_RELEASE="${BASH_REMATCH[1]}${ARCH_extra}"
fi
;;
("ubuntu")
# Extract the flavor from the kernelrelease
# Examples:
# 5.0.0-1028-aws-5.0 -> ubuntu-aws
# 5.15.0-1009-aws -> ubuntu-aws
if [[ $KERNEL_RELEASE =~ -([a-zA-Z]+)(-.*)?$ ]];
then
TARGET_ID="ubuntu-${BASH_REMATCH[1]}"
else
TARGET_ID="ubuntu-generic"
fi
# In the case that the kernelversion isn't just a number
# we keep also the remaining part excluding `-Ubuntu`.
# E.g.:
# from the following `uname -v` result
# `#26~22.04.1-Ubuntu SMP Mon Apr 24 01:58:15 UTC 2023`
# we obtain the kernelversion`26~22.04.1`
if [[ ${DRIVER_KERNEL_VERSION} =~ (^\#[0-9]+\~[^-]*-Ubuntu .*$) ]];
then
KERNEL_VERSION=$(echo "${DRIVER_KERNEL_VERSION}" | sed 's/#\([^-\\ ]*\).*/\1/g')
fi
;;
("flatcar")
KERNEL_RELEASE="${VERSION_ID}"
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
;;
("minikube")
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
# Extract the minikube version. Ex. With minikube version equal to "v1.26.0-1655407986-14197" the extracted version
# will be "1.26.0"
if [[ $(cat ${HOST_ROOT}/etc/VERSION) =~ ([0-9]+(\.[0-9]+){2}) ]]; then
# kernel version for minikube is always in "1_minikubeversion" format. Ex "1_1.26.0".
KERNEL_VERSION="1_${BASH_REMATCH[1]}"
else
echo "* Unable to extract minikube version from ${HOST_ROOT}/etc/VERSION"
exit 1
fi
;;
("bottlerocket")
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
# variant_id has been sourced from os-release. Get only the first variant part
if [[ -n ${VARIANT_ID} ]]; then
# take just first part (eg: VARIANT_ID=aws-k8s-1.15 -> aws)
VARIANT_ID_CUT=${VARIANT_ID%%-*}
fi
# version_id has been sourced from os-release. Build a kernel version like: 1_1.11.0-aws
KERNEL_VERSION="1_${VERSION_ID}-${VARIANT_ID_CUT}"
;;
("talos")
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
# version_id has been sourced from os-release. Build a kernel version like: 1_1.4.1
KERNEL_VERSION="1_${VERSION_ID}"
;;
(*)
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
;;
esac
}
flatcar_relocate_tools() {
local -a tools=(
scripts/basic/fixdep
scripts/mod/modpost
tools/objtool/objtool
)
local -r hostld=$(ls /host/usr/lib64/ld-linux-*.so.*)
local -r kdir=/lib/modules/$(ls /lib/modules/)/build
echo "** Found host dl interpreter: ${hostld}"
for host_tool in ${tools[@]}; do
t=${host_tool}
tool=$(basename $t)
tool_dir=$(dirname $t)
host_tool=${kdir}/${host_tool}
if [ ! -f ${host_tool} ]; then
continue
fi
umount ${host_tool} 2>/dev/null || true
mkdir -p /tmp/${tool_dir}/
cp -a ${host_tool} /tmp/${tool_dir}/
echo "** Setting host dl interpreter for $host_tool"
patchelf --set-interpreter ${hostld} --set-rpath /host/usr/lib64 /tmp/${tool_dir}/${tool}
mount -o bind /tmp/${tool_dir}/${tool} ${host_tool}
done
}
load_kernel_module_compile() {
# Skip dkms on UEK hosts because it will always fail
if [[ ${DRIVER_KERNEL_RELEASE} == *uek* ]]; then
>&2 echo "Skipping because the dkms install always fail (on UEK hosts)"
return
fi
if ! hash dkms >/dev/null 2>&1; then
>&2 echo "This program requires dkms"
return
fi
if [ "${TARGET_ID}" == "flatcar" ]; then
KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE}
echo "* Flatcar detected (version ${VERSION_ID}); relocating kernel tools"
flatcar_relocate_tools
fi
# Try to compile using all the available gcc versions
for CURRENT_GCC in $(ls "$(dirname "$(which gcc)")"/gcc*); do
# Filter away gcc-{ar,nm,...}
# Only gcc compiler has `-print-search-dirs` option.
${CURRENT_GCC} -print-search-dirs 2>&1 | grep "install:"
if [ "$?" -ne "0" ]; then
continue
fi
echo "* Trying to dkms install ${DRIVER_NAME} module with GCC ${CURRENT_GCC}"
echo "#!/usr/bin/env bash" > "${TMPDIR}/falco-dkms-make"
echo "make CC=${CURRENT_GCC} \$@" >> "${TMPDIR}/falco-dkms-make"
chmod +x "${TMPDIR}/falco-dkms-make"
if dkms install --directive="MAKE='${TMPDIR}/falco-dkms-make'" -m "${DRIVER_NAME}" -v "${DRIVER_VERSION}" -k "${KERNEL_RELEASE}" 2>/dev/null; then
echo "* ${DRIVER_NAME} module installed in dkms"
KO_FILE="/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}"
if [ -f "$KO_FILE.ko" ]; then
KO_FILE="$KO_FILE.ko"
elif [ -f "$KO_FILE.ko.gz" ]; then
KO_FILE="$KO_FILE.ko.gz"
elif [ -f "$KO_FILE.ko.xz" ]; then
KO_FILE="$KO_FILE.ko.xz"
elif [ -f "$KO_FILE.ko.zst" ]; then
KO_FILE="$KO_FILE.ko.zst"
else
>&2 echo "${DRIVER_NAME} module file not found"
return
fi
echo "* ${DRIVER_NAME} module found: ${KO_FILE}"
echo "* Trying to insmod"
chcon -t modules_object_t "$KO_FILE" > /dev/null 2>&1 || true
if insmod "$KO_FILE" > /dev/null 2>&1; then
echo "* Success: ${DRIVER_NAME} module found and loaded in dkms"
exit 0
fi
echo "* Unable to insmod ${DRIVER_NAME} module"
else
DKMS_LOG="/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/build/make.log"
if [ -f "${DKMS_LOG}" ]; then
echo "* Running dkms build failed, dumping ${DKMS_LOG} (with GCC ${CURRENT_GCC})"
cat "${DKMS_LOG}"
else
echo "* Running dkms build failed, couldn't find ${DKMS_LOG} (with GCC ${CURRENT_GCC})"
fi
fi
done
}
load_kernel_module_download() {
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
local URL=$(echo "${1}/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" | sed s/+/%2B/g)
echo "* Trying to download a prebuilt ${DRIVER_NAME} module from ${URL}"
if curl -L --create-dirs ${FALCO_DRIVER_CURL_OPTIONS} -o "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" "${URL}"; then
echo "* Download succeeded"
chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
if insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}"; then
echo "* Success: ${DRIVER_NAME} module found and inserted"
exit 0
fi
>&2 echo "Unable to insmod the prebuilt ${DRIVER_NAME} module"
else
>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} module"
return
fi
}
print_clean_termination() {
echo
echo "[SUCCESS] Cleaning phase correctly terminated."
echo
echo "================ Cleaning phase ================"
echo
}
print_filename_components() {
echo " - driver name: ${DRIVER_NAME}"
echo " - target identifier: ${TARGET_ID}"
echo " - kernel release: ${KERNEL_RELEASE}"
echo " - kernel version: ${KERNEL_VERSION}"
}
print_as_env_vars() {
echo "ARCH=\"${ARCH}\""
echo "KERNEL_RELEASE=\"${KERNEL_RELEASE}\""
echo "KERNEL_VERSION=\"${KERNEL_VERSION}\""
echo "ENABLE_COMPILE=\"${ENABLE_COMPILE}\""
echo "ENABLE_DOWNLOAD=\"${ENABLE_DOWNLOAD}\""
echo "TARGET_ID=\"${TARGET_ID}\""
echo "DRIVER=\"${DRIVER}\""
echo "DRIVERS_REPO=\"${DRIVERS_REPO}\""
echo "DRIVER_VERSION=\"${DRIVER_VERSION}\""
echo "DRIVER_NAME=\"${DRIVER_NAME}\""
echo "FALCO_VERSION=\"${FALCO_VERSION}\""
}
clean_kernel_module() {
echo
echo "================ Cleaning phase ================"
echo
if ! hash lsmod > /dev/null 2>&1; then
>&2 echo "This program requires lsmod."
exit 1
fi
if ! hash rmmod > /dev/null 2>&1; then
>&2 echo "This program requires rmmod."
exit 1
fi
KMOD_NAME=$(echo "${DRIVER_NAME}" | tr "-" "_")
echo "* 1. Check if kernel module '${KMOD_NAME}' is still loaded:"
if ! lsmod | cut -d' ' -f1 | grep -qx "${KMOD_NAME}"; then
echo "- OK! There is no '${KMOD_NAME}' module loaded."
echo
fi
# Wait 50s = MAX_RMMOD_WAIT * 5s
MAX_RMMOD_WAIT=10
# Remove kernel module if is still loaded.
while lsmod | cut -d' ' -f1 | grep -qx "${KMOD_NAME}" && [ $MAX_RMMOD_WAIT -gt 0 ]; do
echo "- Kernel module '${KMOD_NAME}' is still loaded."
echo "- Trying to unload it with 'rmmod ${KMOD_NAME}'..."
if rmmod ${KMOD_NAME}; then
echo "- OK! Unloading '${KMOD_NAME}' module succeeded."
echo
else
echo "- Nothing to do...'falco-driver-loader' will wait until you remove the kernel module to have a clean termination."
echo "- Check that no process is using the kernel module with 'lsmod | grep ${KMOD_NAME}'."
echo "- Sleep 5 seconds..."
echo
((--MAX_RMMOD_WAIT))
sleep 5
fi
done
if [ ${MAX_RMMOD_WAIT} -eq 0 ]; then
echo "[WARNING] '${KMOD_NAME}' module is still loaded, you could have incompatibility issues."
echo
fi
if ! hash dkms >/dev/null 2>&1; then
echo "- Skipping dkms remove (dkms not found)."
print_clean_termination
return
fi
# Remove all versions of this module from dkms.
echo "* 2. Check all versions of kernel module '${KMOD_NAME}' in dkms:"
DRIVER_VERSIONS=$(dkms status -m "${KMOD_NAME}" | tr -d "," | tr -d ":" | tr "/" " " | cut -d' ' -f2)
if [ -z "${DRIVER_VERSIONS}" ]; then
echo "- OK! There are no '${KMOD_NAME}' module versions in dkms."
else
echo "- There are some versions of '${KMOD_NAME}' module in dkms."
echo
echo "* 3. Removing all the following versions from dkms:"
echo "${DRIVER_VERSIONS}"
echo
fi
for CURRENT_VER in ${DRIVER_VERSIONS}; do
echo "- Removing ${CURRENT_VER}..."
if dkms remove -m ${KMOD_NAME} -v "${CURRENT_VER}" --all; then
echo
echo "- OK! Removing '${CURRENT_VER}' succeeded."
echo
else
echo "[WARNING] Removing '${KMOD_NAME}' version '${CURRENT_VER}' failed."
fi
done
print_clean_termination
}
load_kernel_module() {
clean_kernel_module
echo "* Looking for a ${DRIVER_NAME} module locally (kernel ${KERNEL_RELEASE})"
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
echo "* Filename '${FALCO_KERNEL_MODULE_FILENAME}' is composed of:"
print_filename_components
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" ]; then
echo "* Found a prebuilt ${DRIVER_NAME} module at ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}, loading it"
chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
exit $?
fi
if [ -n "$ENABLE_DOWNLOAD" ]; then
IFS=", " read -r -a urls <<< "${DRIVERS_REPO}"
for url in "${urls[@]}"; do
load_kernel_module_download $url
done
fi
if [ -n "$ENABLE_COMPILE" ]; then
load_kernel_module_compile
fi
# Last try (might load a previous driver version)
echo "* Trying to load a system ${DRIVER_NAME} module, if present"
if modprobe "${DRIVER_NAME}" > /dev/null 2>&1; then
echo "* Success: ${DRIVER_NAME} module found and loaded with modprobe"
exit 0
fi
# Not able to download a prebuilt module nor to compile one on-the-fly
>&2 echo "Consider compiling your own ${DRIVER_NAME} driver and loading it or getting in touch with the Falco community"
exit 1
}
load_bpf_probe_compile() {
local BPF_KERNEL_SOURCES_URL=""
local STRIP_COMPONENTS=1
customize_kernel_build() {
if [ -n "${KERNEL_EXTRA_VERSION}" ]; then
sed -i "s/LOCALVERSION=\"\"/LOCALVERSION=\"${KERNEL_EXTRA_VERSION}\"/" .config
fi
make olddefconfig > /dev/null
make modules_prepare > /dev/null
}
if [ "${TARGET_ID}" == "flatcar" ]; then
KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE}
echo "* Flatcar detected (version ${VERSION_ID}); relocating kernel tools"
flatcar_relocate_tools
fi
if [ "${TARGET_ID}" == "cos" ]; then
echo "* COS detected (build ${BUILD_ID}), using COS kernel headers"
BPF_KERNEL_SOURCES_URL="https://storage.googleapis.com/cos-tools/${BUILD_ID}/kernel-headers.tgz"
KERNEL_EXTRA_VERSION="+"
STRIP_COMPONENTS=0
customize_kernel_build() {
pushd usr/src/* > /dev/null || exit
# Note: this overrides the KERNELDIR set while untarring the tarball
KERNELDIR=$(pwd)
export KERNELDIR
sed -i '/^#define randomized_struct_fields_start struct {$/d' include/linux/compiler-clang.h
sed -i '/^#define randomized_struct_fields_end };$/d' include/linux/compiler-clang.h
popd > /dev/null || exit
# Might need to configure our own sources depending on COS version
cos_ver=${BUILD_ID}
base_ver=11553.0.0
cos_version_greater
greater_ret=$?
if [[ greater_ret -eq 1 ]]; then
export KBUILD_EXTRA_CPPFLAGS=-DCOS_73_WORKAROUND
fi
}
fi
if [ "${TARGET_ID}" == "minikube" ]; then
MINIKUBE_VERSION="$(cat "${HOST_ROOT}/etc/VERSION")"
echo "* Minikube detected (${MINIKUBE_VERSION}), using linux kernel sources for minikube kernel"
local kernel_version
kernel_version=${DRIVER_KERNEL_RELEASE}
local -r kernel_version_major=$(echo "${kernel_version}" | cut -d. -f1)
local -r kernel_version_minor=$(echo "${kernel_version}" | cut -d. -f2)
local -r kernel_version_patch=$(echo "${kernel_version}" | cut -d. -f3)
if [ "${kernel_version_patch}" == "0" ]; then
kernel_version="${kernel_version_major}.${kernel_version_minor}"
fi
BPF_KERNEL_SOURCES_URL="http://mirrors.edge.kernel.org/pub/linux/kernel/v${kernel_version_major}.x/linux-${kernel_version}.tar.gz"
fi
if [ -n "${BPF_USE_LOCAL_KERNEL_SOURCES}" ]; then
local -r kernel_version_major=$(echo "${DRIVER_KERNEL_RELEASE}" | cut -d. -f1)
local -r kernel_version=$(echo "${DRIVER_KERNEL_RELEASE}" | cut -d- -f1)
KERNEL_EXTRA_VERSION="-$(echo "${DRIVER_KERNEL_RELEASE}" | cut -d- -f2)"
echo "* Using downloaded kernel sources for kernel version ${kernel_version}..."
BPF_KERNEL_SOURCES_URL="http://mirrors.edge.kernel.org/pub/linux/kernel/v${kernel_version_major}.x/linux-${kernel_version}.tar.gz"
fi
if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then
get_kernel_config
echo "* Downloading ${BPF_KERNEL_SOURCES_URL}"
mkdir -p /tmp/kernel
cd /tmp/kernel || exit
cd "$(mktemp -d -p /tmp/kernel)" || exit
if ! curl -L -o kernel-sources.tgz --create-dirs ${FALCO_DRIVER_CURL_OPTIONS} "${BPF_KERNEL_SOURCES_URL}"; then
>&2 echo "Unable to download the kernel sources"
return
fi
echo "* Extracting kernel sources"
mkdir kernel-sources && tar xf kernel-sources.tgz -C kernel-sources --strip-components "${STRIP_COMPONENTS}"
cd kernel-sources || exit
KERNELDIR=$(pwd)
export KERNELDIR
if [[ "${KERNEL_CONFIG_PATH}" == *.gz ]]; then
zcat "${KERNEL_CONFIG_PATH}" > .config
else
cat "${KERNEL_CONFIG_PATH}" > .config
fi
echo "* Configuring kernel"
customize_kernel_build
fi
echo "* Trying to compile the eBPF probe (${BPF_PROBE_FILENAME})"
make -C "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf" > /dev/null
mkdir -p "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}"
mv "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf/probe.o" "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then
rm -r /tmp/kernel
fi
}
load_bpf_probe_download() {
local URL
URL=$(echo "${1}/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" | sed s/+/%2B/g)
echo "* Trying to download a prebuilt eBPF probe from ${URL}"
if ! curl -L --create-dirs ${FALCO_DRIVER_CURL_OPTIONS} -o "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" "${URL}"; then
>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} eBPF probe"
return 1
fi
return 0
}
load_bpf_probe() {
if [ ! -d /sys/kernel/debug/tracing ]; then
echo "* Mounting debugfs"
mount -t debugfs nodev /sys/kernel/debug
fi
BPF_PROBE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.o"
echo "* Filename '${BPF_PROBE_FILENAME}' is composed of:"
print_filename_components
if [ -n "$ENABLE_DOWNLOAD" ]; then
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then
echo "* Skipping download, eBPF probe is already present in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
else
IFS=", " read -r -a urls <<< "${DRIVERS_REPO}"
for url in "${urls[@]}"; do
load_bpf_probe_download $url
if [ $? -eq 0 ]; then
break
fi
done
fi
fi
if [ -n "$ENABLE_COMPILE" ]; then
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then
echo "* Skipping compilation, eBPF probe is already present in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
else
load_bpf_probe_compile
fi
fi
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then
echo "* eBPF probe located in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}"
ln -sf "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" "${HOME}/.falco/${DRIVER_NAME}-bpf.o" \
&& echo "* Success: eBPF probe symlinked to ${HOME}/.falco/${DRIVER_NAME}-bpf.o"
exit $?
else
>&2 echo "Unable to load the ${DRIVER_NAME} eBPF probe"
exit 1
fi
}
print_usage() {
echo ""
echo "Usage:"
echo " falco-driver-loader [driver] [options]"
echo ""
echo "Available drivers:"
echo " module kernel module (default)"
echo " bpf eBPF probe"
echo ""
echo "Options:"
echo " --help show brief help"
echo " --clean try to remove an already present driver installation"
echo " --compile try to compile the driver locally (default true)"
echo " --download try to download a prebuilt driver (default true)"
echo " --source-only skip execution and allow sourcing in another script using `. falco-driver-loader`"
echo " --print-env skip execution and print env variables for other tools to consume"
echo ""
echo "Environment variables:"
echo " DRIVERS_REPO specify different URL(s) where to look for prebuilt Falco drivers (comma separated)"
echo " DRIVER_NAME specify a different name for the driver"
echo " DRIVER_INSECURE_DOWNLOAD whether you want to allow insecure downloads or not"
echo " DRIVER_CURL_OPTIONS specify additional options to be passed to curl command used to download Falco drivers"
echo " DRIVER_KERNEL_RELEASE specify the kernel release for which to download/build the driver in the same format used by 'uname -r' (e.g. '6.1.0-10-cloud-amd64')"
echo " DRIVER_KERNEL_VERSION specify the kernel version for which to download/build the driver in the same format used by 'uname -v' (e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')"
echo ""
echo "Versions:"
echo " Falco version ${FALCO_VERSION}"
echo " Driver version ${DRIVER_VERSION}"
echo ""
}
ARCH=$(uname -m)
DRIVER_KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE:-$(uname -r)}
KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE}
if ! hash sed > /dev/null 2>&1; then
>&2 echo "This program requires sed"
exit 1
fi
DRIVER_KERNEL_VERSION=${DRIVER_KERNEL_VERSION:-$(uname -v)}
KERNEL_VERSION=$(echo "${DRIVER_KERNEL_VERSION}" | sed 's/#\([[:digit:]]\+\).*/\1/')
DRIVERS_REPO=${DRIVERS_REPO:-"@DRIVERS_REPO@"}
FALCO_DRIVER_CURL_OPTIONS="-fsS --connect-timeout 5 --max-time 60 --retry 3 --retry-max-time 120"
if [ -n "$DRIVER_INSECURE_DOWNLOAD" ]
then
FALCO_DRIVER_CURL_OPTIONS+=" -k"
fi
FALCO_DRIVER_CURL_OPTIONS+=" "${DRIVER_CURL_OPTIONS}
if [[ -z "$MAX_RMMOD_WAIT" ]]; then
MAX_RMMOD_WAIT=60
fi
DRIVER_VERSION=${DRIVER_VERSION:-"@DRIVER_VERSION@"}
DRIVER_NAME=${DRIVER_NAME:-"@DRIVER_NAME@"}
FALCO_VERSION="@FALCO_VERSION@"
TARGET_ID=
get_target_id
DRIVER="module"
if [ -v FALCO_BPF_PROBE ]; then
DRIVER="bpf"
fi
TMPDIR=${TMPDIR:-"/tmp"}
ENABLE_COMPILE=
ENABLE_DOWNLOAD=
clean=
has_args=
has_opts=
print_env=
source_only=
while test $# -gt 0; do
case "$1" in
module|bpf)
if [ -n "$has_args" ]; then
>&2 echo "Only one driver per invocation"
print_usage
exit 1
else
DRIVER="$1"
has_args="true"
shift
fi
;;
-h|--help)
print_usage
exit 0
;;
--clean)
clean="true"
shift
;;
--compile)
ENABLE_COMPILE="yes"
has_opts="true"
shift
;;
--download)
ENABLE_DOWNLOAD="yes"
has_opts="true"
shift
;;
--source-only)
source_only="true"
shift
;;
--print-env)
print_env="true"
shift
;;
--*)
>&2 echo "Unknown option: $1"
print_usage
exit 1
;;
*)
>&2 echo "Unknown driver: $1"
print_usage
exit 1
;;
esac
done
if [ -z "$has_opts" ]; then
ENABLE_COMPILE="yes"
ENABLE_DOWNLOAD="yes"
fi
if [ -n "$source_only" ]; then
# Return or exit, depending if we've been sourced.
(return 0 2>/dev/null) && return || exit 0
fi
if [ -n "$print_env" ]; then
print_as_env_vars
exit 0
fi
echo "* Running falco-driver-loader for: falco version=${FALCO_VERSION}, driver version=${DRIVER_VERSION}, arch=${ARCH}, kernel release=${KERNEL_RELEASE}, kernel version=${KERNEL_VERSION}"
if [ "$(id -u)" != 0 ]; then
>&2 echo "This program must be run as root (or with sudo)"
exit 1
fi
if [ "$TARGET_ID" = "undetermined" ]; then
if [ -n "$ENABLE_COMPILE" ]; then
ENABLE_DOWNLOAD=
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community. Trying to compile anyway."
else
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community."
exit 1
fi
fi
if [ -n "$clean" ]; then
if [ -n "$has_opts" ]; then
>&2 echo "Cannot use --clean with other options"
exit 1
fi
echo "* Running falco-driver-loader with: driver=$DRIVER, clean=yes"
case $DRIVER in
module)
clean_kernel_module
;;
bpf)
>&2 echo "--clean not supported for driver=bpf"
exit 1
esac
else
if ! hash curl > /dev/null 2>&1; then
>&2 echo "This program requires curl"
exit 1
fi
echo "* Running falco-driver-loader with: driver=$DRIVER, compile=${ENABLE_COMPILE:-"no"}, download=${ENABLE_DOWNLOAD:-"no"}"
case $DRIVER in
module)
load_kernel_module
;;
bpf)
load_bpf_probe
;;
esac
fi

View File

@@ -1,3 +1,10 @@
driver:
type: "kmod"
name: "@DRIVER_NAME@"
repos:
- "@DRIVERS_REPO@"
version: "@DRIVER_VERSION@"
hostroot: "/"
artifact:
follow:
every: 6h0m0s

View File

@@ -17,6 +17,8 @@
#
chosen_driver=
chosen_unit=
CHOICE=
# Every time we call this script we want to stat from a clean state.
echo "[POST-INSTALL] Disable all possible enabled 'falco' service:"
@@ -35,7 +37,18 @@ systemctl --system disable 'falcoctl-artifact-follow.service' || true
systemctl --system unmask falcoctl-artifact-follow.service || true
if [ $1 -ge 1 ]; then
if [ -x /usr/bin/dialog ] && [ "${FALCO_FRONTEND}" != "noninteractive" ]; then
case $FALCO_DRIVER_CHOICE in
kmod)
CHOICE=2
;;
ebpf)
CHOICE=3
;;
modern_ebpf)
CHOICE=4
;;
esac
if [ -z $CHOICE ] && [ -x /usr/bin/dialog ] && [ "${FALCO_FRONTEND}" != "noninteractive" ]; then
# If dialog is installed, create a dialog to let users choose the correct driver for them
CHOICE=$(dialog --clear --title "Falco drivers" --menu "Choose your preferred driver:" 12 55 4 \
1 "Manual configuration (no unit is started)" \
@@ -43,31 +56,44 @@ if [ $1 -ge 1 ]; then
3 "eBPF" \
4 "Modern eBPF" \
2>&1 >/dev/tty)
case $CHOICE in
2)
chosen_driver="kmod"
;;
3)
chosen_driver="bpf"
;;
4)
chosen_driver="modern-bpf"
fi
case $CHOICE in
2)
chosen_driver="kmod"
chosen_unit="kmod"
;;
3)
chosen_driver="ebpf"
chosen_unit="bpf"
;;
4)
chosen_driver="modern_ebpf"
chosen_unit="modern-bpf"
;;
esac
if [ -n "$CHOICE" ]; then
echo "[POST-INSTALL] Configure falcoctl driver type:"
falcoctl driver config --type $chosen_driver
CHOICE=
case $FALCOCTL_ENABLED in
no)
CHOICE=2
;;
esac
if [ -n "$chosen_driver" ]; then
CHOICE=$(dialog --clear --title "Falcoctl" --menu "Do you want to follow automatic ruleset updates?" 10 40 2 \
1 "Yes" \
2 "No" \
2>&1 >/dev/tty)
case $CHOICE in
2)
# we don't want falcoctl enabled, we mask it
systemctl --system mask falcoctl-artifact-follow.service || true
;;
esac
fi
clear
fi
if [ -z $CHOICE ] && [ -x /usr/bin/dialog ] && [ "${FALCO_FRONTEND}" != "noninteractive" ]; then
CHOICE=$(dialog --clear --title "Falcoctl" --menu "Do you want to follow automatic ruleset updates?" 10 40 2 \
1 "Yes" \
2 "No" \
2>&1 >/dev/tty)
fi
case $CHOICE in
2)
# we don't want falcoctl enabled, we mask it
systemctl --system mask falcoctl-artifact-follow.service || true
;;
esac
fi
clear
fi
set -e
@@ -75,16 +101,16 @@ set -e
echo "[POST-INSTALL] Trigger deamon-reload:"
systemctl --system daemon-reload || true
# If needed, try to load/compile the driver through falco-driver-loader
# If needed, try to load/compile the driver through falcoctl
case "$chosen_driver" in
"kmod")
# Only compile for kmod, in this way we use dkms
echo "[POST-INSTALL] Call 'falco-driver-loader --compile module':"
falco-driver-loader --compile module
echo "[POST-INSTALL] Call 'falcoctl driver install for kmod:"
falcoctl driver install --download=false
;;
"bpf")
echo "[POST-INSTALL] Call 'falco-driver-loader bpf':"
falco-driver-loader bpf
"ebpf")
echo "[POST-INSTALL] Call 'falcoctl driver install for ebpf':"
falcoctl driver install
;;
esac
@@ -95,14 +121,14 @@ esac
# systemd_post macro expands to
# if postinst:
# `systemd-update-helper install-system-units <service>`
%systemd_post "falco-$chosen_driver.service"
%systemd_post "falco-$chosen_unit.service"
# post install/upgrade mirrored from .deb
if [ $1 -ge 1 ]; then
if [ -n "$chosen_driver" ]; then
echo "[POST-INSTALL] Enable 'falco-$chosen_driver.service':"
systemctl --system enable "falco-$chosen_driver.service" || true
echo "[POST-INSTALL] Start 'falco-$chosen_driver.service':"
systemctl --system start "falco-$chosen_driver.service" || true
if [ -n "$chosen_unit" ]; then
echo "[POST-INSTALL] Enable 'falco-$chosen_unit.service':"
systemctl --system enable "falco-$chosen_unit.service" || true
echo "[POST-INSTALL] Start 'falco-$chosen_unit.service':"
systemctl --system start "falco-$chosen_unit.service" || true
fi
fi

View File

@@ -25,8 +25,8 @@ systemctl --system stop 'falco-modern-bpf.service' || true
systemctl --system stop 'falco-custom.service' || true
systemctl --system stop 'falcoctl-artifact-follow.service' || true
echo "[PRE-REMOVE] Call 'falco-driver-loader --clean:'"
falco-driver-loader --clean
echo "[PRE-REMOVE] Call 'falcoctl driver cleanup:'"
falcoctl driver cleanup
# validate rpm macros by `rpm -qp --scripts <rpm>`
# RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd

View File

@@ -7,8 +7,7 @@ Wants=falcoctl-artifact-follow.service
[Service]
Type=simple
User=root
Environment=FALCO_BPF_PROBE=
ExecStart=/usr/bin/falco
ExecStart=/usr/bin/falco -o engine.kind=ebpf
ExecReload=kill -1 $MAINPID
UMask=0077
TimeoutSec=30

View File

@@ -9,7 +9,7 @@ Wants=falcoctl-artifact-follow.service
[Service]
Type=simple
User=root
ExecStart=/usr/bin/falco
ExecStart=/usr/bin/falco -o engine.kind=kmod
ExecReload=kill -1 $MAINPID
UMask=0077
TimeoutSec=30

View File

@@ -7,7 +7,7 @@ Wants=falcoctl-artifact-follow.service
[Service]
Type=simple
User=root
ExecStart=/usr/bin/falco --modern-bpf
ExecStart=/usr/bin/falco -o engine.kind=modern_ebpf
ExecReload=kill -1 $MAINPID
UMask=0077
TimeoutSec=30

View File

@@ -0,0 +1,251 @@
// SPDX-License-Identifier: Apache-2.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 <string>
#include <gtest/gtest.h>
#include <sinsp.h>
#include <filter_check_list.h>
#include <filter.h>
#include <falco_engine.h>
static std::string single_rule = R"END(
- rule: test rule
desc: A test rule
condition: evt.type=execve
output: A test rule matched (evt.type=%evt.type)
priority: INFO
source: syscall
tags: [process]
- rule: disabled rule
desc: A disabled rule
condition: evt.type=execve
output: A disabled rule matched (evt.type=%evt.type)
priority: INFO
source: syscall
enabled: false
tags: [exec process]
)END";
// This must be kept in line with the (private) falco_engine::s_default_ruleset
static const std::string default_ruleset = "falco-default-ruleset";
static const std::string ruleset_1 = "ruleset-1";
static const std::string ruleset_2 = "ruleset-2";
static const std::string ruleset_3 = "ruleset-3";
static const std::string ruleset_4 = "ruleset-4";
static void load_rules(falco_engine& engine, sinsp& inspector, sinsp_filter_check_list& filterchecks)
{
std::unique_ptr<falco::load_result> res;
auto filter_factory = std::shared_ptr<gen_event_filter_factory>(
new sinsp_filter_factory(&inspector, filterchecks));
auto formatter_factory = std::shared_ptr<gen_event_formatter_factory>(
new sinsp_evt_formatter_factory(&inspector, filterchecks));
engine.add_source("syscall", filter_factory, formatter_factory);
res = engine.load_rules(single_rule, "single_rule.yaml");
EXPECT_TRUE(res->successful());
}
TEST(EnableRule, enable_rule_name)
{
falco_engine engine;
sinsp inspector;
sinsp_filter_check_list filterchecks;
load_rules(engine, inspector, filterchecks);
// No rules should be enabled yet for any custom rulesets
EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
// Enable for first ruleset, only that ruleset should have an
// enabled rule afterward
engine.enable_rule("test", true, ruleset_1);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
// Enable for second ruleset
engine.enable_rule("test", true, ruleset_2);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
// When the substring is blank, all rules are enabled
// (including the disabled rule)
engine.enable_rule("", true, ruleset_3);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_3));
// Now disable for second ruleset
engine.enable_rule("test", false, ruleset_2);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_3));
}
TEST(EnableRule, enable_rule_tags)
{
falco_engine engine;
sinsp inspector;
sinsp_filter_check_list filterchecks;
std::set<std::string> process_tags = {"process"};
load_rules(engine, inspector, filterchecks);
// No rules should be enabled yet for any custom rulesets
EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
// Enable for first ruleset, only that ruleset should have an
// enabled rule afterward
engine.enable_rule_by_tag(process_tags, true, ruleset_1);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
// Enable for second ruleset
engine.enable_rule_by_tag(process_tags, true, ruleset_2);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2));
// Now disable for second ruleset
engine.enable_rule_by_tag(process_tags, false, ruleset_2);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
}
TEST(EnableRule, enable_disabled_rule_by_tag)
{
falco_engine engine;
sinsp inspector;
sinsp_filter_check_list filterchecks;
std::set<std::string> exec_process_tags = {"exec process"};
load_rules(engine, inspector, filterchecks);
// Only the first rule should be enabled
EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset));
// Enable the disabled rule by tag
engine.enable_rule_by_tag(exec_process_tags, true);
// Both rules should be enabled now
EXPECT_EQ(2, engine.num_rules_for_ruleset(default_ruleset));
}
TEST(EnableRule, enable_rule_id)
{
falco_engine engine;
sinsp inspector;
sinsp_filter_check_list filterchecks;
uint16_t ruleset_1_id;
uint16_t ruleset_2_id;
uint16_t ruleset_3_id;
load_rules(engine, inspector, filterchecks);
// The cases are identical to above, just using ruleset ids
// instead of names.
ruleset_1_id = engine.find_ruleset_id(ruleset_1);
ruleset_2_id = engine.find_ruleset_id(ruleset_2);
ruleset_3_id = engine.find_ruleset_id(ruleset_3);
EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
engine.enable_rule("test rule", true, ruleset_1_id);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
engine.enable_rule("test rule", true, ruleset_2_id);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
engine.enable_rule("", true, ruleset_3_id);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_3));
engine.enable_rule("test", false, ruleset_2_id);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_3));
}
TEST(EnableRule, enable_rule_name_exact)
{
falco_engine engine;
sinsp inspector;
sinsp_filter_check_list filterchecks;
load_rules(engine, inspector, filterchecks);
EXPECT_EQ(1, engine.num_rules_for_ruleset(default_ruleset));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_4));
engine.enable_rule_exact("test rule", true, ruleset_1);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_4));
engine.enable_rule_exact("test rule", true, ruleset_2);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_4));
// This should **not** enable as this is a substring and not
// an exact match.
engine.enable_rule_exact("test", true, ruleset_3);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_4));
engine.enable_rule_exact("", true, ruleset_4);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_4));
engine.enable_rule("test rule", false, ruleset_2);
EXPECT_EQ(1, engine.num_rules_for_ruleset(ruleset_1));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_2));
EXPECT_EQ(0, engine.num_rules_for_ruleset(ruleset_3));
EXPECT_EQ(2, engine.num_rules_for_ruleset(ruleset_4));
}

View File

@@ -72,22 +72,3 @@ TEST(FalcoUtils, parse_prometheus_interval)
*/
ASSERT_EQ(falco::utils::parse_prometheus_interval("200"), 0UL);
}
TEST(FalcoUtils, decode_url)
{
ASSERT_EQ(
falco::utils::decode_uri("https://www.example.com?key1=value+1&key2=value%40%21%242&key3=value%253", true),
"https://www.example.com?key1=value 1&key2=value@!$2&key3=value%3");
ASSERT_EQ(
falco::utils::decode_uri("https://download.falco.org/?prefix=driver/3.0.1%2Bdriver/x86_64/", true),
"https://download.falco.org/?prefix=driver/3.0.1+driver/x86_64/");
ASSERT_EQ(
falco::utils::decode_uri("https://example.com/hello%20world", true),
"https://example.com/hello world");
ASSERT_EQ(
falco::utils::decode_uri("https://example.com/helloworld", true),
"https://example.com/helloworld");
}

View File

@@ -37,17 +37,17 @@ 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::shared_ptr<libsinsp::filter::ast::expr> macro = 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::shared_ptr<libsinsp::filter::ast::expr> filter = 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));
std::shared_ptr<libsinsp::filter::ast::expr> expected = libsinsp::filter::ast::and_expr::create(expected_and);
filter_macro_resolver resolver;
resolver.set_macro(MACRO_NAME, macro);
@@ -71,9 +71,9 @@ 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> macro = 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));
std::shared_ptr<libsinsp::filter::ast::expr> filter = libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos);
filter_macro_resolver resolver;
resolver.set_macro(MACRO_NAME, macro);
@@ -102,18 +102,18 @@ 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::shared_ptr<libsinsp::filter::ast::expr> a_macro = libsinsp::filter::ast::unary_check_expr::create("one.field", "", "exists");
std::shared_ptr<libsinsp::filter::ast::expr> b_macro = 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::shared_ptr<libsinsp::filter::ast::expr> filter = 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));
std::shared_ptr<libsinsp::filter::ast::expr> expected_filter = libsinsp::filter::ast::or_expr::create(expected_or);
filter_macro_resolver resolver;
resolver.set_macro(MACRO_A_NAME, a_macro);
@@ -149,17 +149,17 @@ TEST(MacroResolver, should_resolve_macros_on_a_filter_AST_nested_macros)
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> a_macro = 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> b_macro =
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::shared_ptr<libsinsp::filter::ast::expr> filter = 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));
std::shared_ptr<libsinsp::filter::ast::expr> expected_filter = libsinsp::filter::ast::and_expr::create(expected_and);
filter_macro_resolver resolver;
resolver.set_macro(MACRO_A_NAME, a_macro);
@@ -196,7 +196,7 @@ TEST(MacroResolver, should_find_unknown_macros)
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::shared_ptr<libsinsp::filter::ast::expr> filter = libsinsp::filter::ast::and_expr::create(filter_and);
filter_macro_resolver resolver;
ASSERT_FALSE(resolver.run(filter));
@@ -214,9 +214,9 @@ TEST(MacroResolver, should_find_unknown_nested_macros)
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> a_macro = 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));
std::shared_ptr<libsinsp::filter::ast::expr> filter = libsinsp::filter::ast::value_expr::create(MACRO_A_NAME, a_macro_pos);
auto expected_filter = clone(a_macro.get());
filter_macro_resolver resolver;
@@ -237,9 +237,9 @@ 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));
std::shared_ptr<libsinsp::filter::ast::expr> macro = libsinsp::filter::ast::unary_check_expr::create("test.field", "", "exists");
std::shared_ptr<libsinsp::filter::ast::expr> a_filter = libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos_1);
std::shared_ptr<libsinsp::filter::ast::expr> b_filter = libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos_2);
filter_macro_resolver resolver;
resolver.set_macro(MACRO_NAME, macro);
@@ -262,8 +262,8 @@ TEST(MacroResolver, should_undefine_macro)
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));
std::shared_ptr<libsinsp::filter::ast::unary_check_expr> macro = libsinsp::filter::ast::unary_check_expr::create("test.field", "", "exists");
std::shared_ptr<libsinsp::filter::ast::expr> filter = libsinsp::filter::ast::value_expr::create(MACRO_NAME, macro_pos);
filter_macro_resolver resolver;
resolver.set_macro(MACRO_NAME, macro);

View File

@@ -114,17 +114,35 @@ TEST(Configuration, configuration_environment_variables)
// Set an environment variable for testing purposes
std::string env_var_value = "envVarValue";
std::string env_var_name = "ENV_VAR";
std::string default_value = "default";
SET_ENV_VAR(env_var_name.c_str(), env_var_value.c_str());
yaml_helper conf;
std::string sample_yaml =
std::string embedded_env_var_value = "${ENV_VAR}";
std::string embedded_env_var_name = "ENV_VAR_EMBEDDED";
SET_ENV_VAR(embedded_env_var_name.c_str(), embedded_env_var_value.c_str());
std::string bool_env_var_value = "true";
std::string bool_env_var_name = "ENV_VAR_BOOL";
SET_ENV_VAR(bool_env_var_name.c_str(), bool_env_var_value.c_str());
std::string int_env_var_value = "12";
std::string int_env_var_name = "ENV_VAR_INT";
SET_ENV_VAR(int_env_var_name.c_str(), int_env_var_value.c_str());
std::string empty_env_var_value = "";
std::string empty_env_var_name = "ENV_VAR_EMPTY";
SET_ENV_VAR(empty_env_var_name.c_str(), empty_env_var_value.c_str());
std::string default_value = "default";
std::string env_var_sample_yaml =
"base_value:\n"
" id: $ENV_VAR\n"
" name: '${ENV_VAR}'\n"
" string: my_string\n"
" invalid: $${ENV_VAR}\n"
" invalid_env: $$ENV_VAR\n"
" invalid_env: $$ENV_VAR\n"
" invalid_double_env: $${ENV_VAR}$${ENV_VAR}\n"
" invalid_embedded_env: $${${ENV_VAR}}\n"
" invalid_valid_env: $${ENV_VAR}${ENV_VAR}\n"
" escaped: \"${ENV_VAR}\"\n"
" subvalue:\n"
" subvalue2:\n"
@@ -133,52 +151,135 @@ TEST(Configuration, configuration_environment_variables)
" sample_list:\n"
" - ${ENV_VAR}\n"
" - ' ${ENV_VAR}'\n"
" - $UNSED_XX_X_X_VAR\n";
conf.load_from_string(sample_yaml);
" - '${ENV_VAR} '\n"
" - $UNSED_XX_X_X_VAR\n"
"paths:\n"
" - ${ENV_VAR}/foo\n"
" - $ENV_VAR/foo\n"
" - /foo/${ENV_VAR}/\n"
" - /${ENV_VAR}/${ENV_VAR}${ENV_VAR}/foo\n"
" - ${ENV_VAR_EMBEDDED}/foo\n"
"is_test: ${ENV_VAR_BOOL}\n"
"num_test: ${ENV_VAR_INT}\n"
"empty_test: ${ENV_VAR_EMPTY}\n"
"plugins:\n"
" - name: k8saudit\n"
" library_path: /foo/${ENV_VAR}/libk8saudit.so\n"
" open_params: ${ENV_VAR_INT}\n";
yaml_helper conf;
conf.load_from_string(env_var_sample_yaml);
/* Check if the base values are defined */
ASSERT_TRUE(conf.is_defined("base_value"));
ASSERT_TRUE(conf.is_defined("base_value_2"));
ASSERT_TRUE(conf.is_defined("paths"));
ASSERT_FALSE(conf.is_defined("unknown_base_value"));
/* Test fetching of a regular string without any environment variable */
std::string base_value_string = conf.get_scalar<std::string>("base_value.string", default_value);
auto base_value_string = conf.get_scalar<std::string>("base_value.string", default_value);
ASSERT_EQ(base_value_string, "my_string");
/* Test fetching of escaped environment variable format. Should return the string as-is after stripping the leading `$` */
std::string base_value_invalid = conf.get_scalar<std::string>("base_value.invalid", default_value);
auto base_value_invalid = conf.get_scalar<std::string>("base_value.invalid", default_value);
ASSERT_EQ(base_value_invalid, "${ENV_VAR}");
/* Test fetching of invalid escaped environment variable format. Should return the string as-is */
std::string base_value_invalid_env = conf.get_scalar<std::string>("base_value.invalid_env", default_value);
/* Test fetching of invalid escaped environment variable format. Should return the string as-is */
auto base_value_invalid_env = conf.get_scalar<std::string>("base_value.invalid_env", default_value);
ASSERT_EQ(base_value_invalid_env, "$$ENV_VAR");
/* Test fetching of 2 escaped environment variables side by side. Should return the string as-is after stripping the leading `$` */
auto base_value_double_invalid = conf.get_scalar<std::string>("base_value.invalid_double_env", default_value);
ASSERT_EQ(base_value_double_invalid, "${ENV_VAR}${ENV_VAR}");
/*
* Test fetching of escaped environment variable format with inside an env variable.
* Should return the string as-is after stripping the leading `$` with the resolved env variable within
*/
auto base_value_embedded_invalid = conf.get_scalar<std::string>("base_value.invalid_embedded_env", default_value);
ASSERT_EQ(base_value_embedded_invalid, "${" + env_var_value + "}");
/*
* Test fetching of an escaped env variable plus an env variable side by side.
* Should return the escaped one trimming the leading `$` plus the second one resolved.
*/
auto base_value_valid_invalid = conf.get_scalar<std::string>("base_value.invalid_valid_env", default_value);
ASSERT_EQ(base_value_valid_invalid, "${ENV_VAR}" + env_var_value);
/* Test fetching of strings that contain environment variables */
std::string base_value_id = conf.get_scalar<std::string>("base_value.id", default_value);
auto base_value_id = conf.get_scalar<std::string>("base_value.id", default_value);
ASSERT_EQ(base_value_id, "$ENV_VAR"); // Does not follow the `${VAR}` format, so it should be treated as a regular string
std::string base_value_name = conf.get_scalar<std::string>("base_value.name", default_value);
auto base_value_name = conf.get_scalar<std::string>("base_value.name", default_value);
ASSERT_EQ(base_value_name, env_var_value); // Proper environment variable format
std::string base_value_escaped = conf.get_scalar<std::string>("base_value.escaped", default_value);
auto base_value_escaped = conf.get_scalar<std::string>("base_value.escaped", default_value);
ASSERT_EQ(base_value_escaped, env_var_value); // Environment variable within quotes
/* Test fetching of an undefined environment variable. Expected to return the default value.*/
std::string unknown_boolean = conf.get_scalar<std::string>("base_value.subvalue.subvalue2.boolean", default_value);
ASSERT_EQ(unknown_boolean, default_value);
/* Test fetching of an undefined environment variable. Resolves to empty string. */
auto unknown_boolean = conf.get_scalar<std::string>("base_value.subvalue.subvalue2.boolean", default_value);
ASSERT_EQ(unknown_boolean, "");
/* Test fetching of environment variables from a list */
std::string base_value_2_list_0 = conf.get_scalar<std::string>("base_value_2.sample_list[0]", default_value);
auto base_value_2_list_0 = conf.get_scalar<std::string>("base_value_2.sample_list[0]", default_value);
ASSERT_EQ(base_value_2_list_0, env_var_value); // Proper environment variable format
std::string base_value_2_list_1 = conf.get_scalar<std::string>("base_value_2.sample_list[1]", default_value);
ASSERT_EQ(base_value_2_list_1, " ${ENV_VAR}"); // Environment variable preceded by a space, hence treated as a regular string
auto base_value_2_list_1 = conf.get_scalar<std::string>("base_value_2.sample_list[1]", default_value);
ASSERT_EQ(base_value_2_list_1, " " + env_var_value); // Environment variable preceded by a space, still extracted env var with leading space
std::string base_value_2_list_2 = conf.get_scalar<std::string>("base_value_2.sample_list[2]", default_value);
ASSERT_EQ(base_value_2_list_2, "$UNSED_XX_X_X_VAR"); // Does not follow the `${VAR}` format, so should be treated as a regular string
auto base_value_2_list_2 = conf.get_scalar<std::string>("base_value_2.sample_list[2]", default_value);
ASSERT_EQ(base_value_2_list_2, env_var_value + " "); // Environment variable followed by a space, still extracted env var with trailing space
/* Clear the set environment variable after testing */
SET_ENV_VAR(env_var_name.c_str(), env_var_value.c_str());
auto base_value_2_list_3 = conf.get_scalar<std::string>("base_value_2.sample_list[3]", default_value);
ASSERT_EQ(base_value_2_list_3, "$UNSED_XX_X_X_VAR"); // Does not follow the `${VAR}` format, so should be treated as a regular string
/* Test expansion of environment variables within strings */
auto path_list_0 = conf.get_scalar<std::string>("paths[0]", default_value);
ASSERT_EQ(path_list_0, env_var_value + "/foo"); // Even if env var is part of bigger string, it gets expanded
auto path_list_1 = conf.get_scalar<std::string>("paths[1]", default_value);
ASSERT_EQ(path_list_1, "$ENV_VAR/foo"); // Does not follow the `${VAR}` format, so should be treated as a regular string
auto path_list_2 = conf.get_scalar<std::string>("paths[2]", default_value);
ASSERT_EQ(path_list_2, "/foo/" + env_var_value + "/"); // Even when env var is in the middle of a string. it gets expanded
auto path_list_3 = conf.get_scalar<std::string>("paths[3]", default_value);
ASSERT_EQ(path_list_3, "/" + env_var_value + "/" + env_var_value + env_var_value + "/foo"); // Even when the string contains multiple env vars they are correctly expanded
auto path_list_4 = conf.get_scalar<std::string>("paths[4]", default_value);
ASSERT_EQ(path_list_4, env_var_value + "/foo"); // Even when the env var contains another env var, it gets correctly double-expanded
/* Check that variable expansion is type-aware */
auto boolean = conf.get_scalar<bool>("is_test", false);
ASSERT_EQ(boolean, true); // `true` can be parsed to bool.
auto boolean_as_str = conf.get_scalar<std::string>("is_test", "false");
ASSERT_EQ(boolean_as_str, "true"); // `true` can be parsed to string.
auto boolean_as_int = conf.get_scalar<int32_t>("is_test", 0);
ASSERT_EQ(boolean_as_int, 0); // `true` cannot be parsed to integer.
auto integer = conf.get_scalar<int32_t>("num_test", -1);
ASSERT_EQ(integer, 12);
// An env var that resolves to an empty string returns ""
auto empty_default_str = conf.get_scalar<std::string>("empty_test", default_value);
ASSERT_EQ(empty_default_str, "");
std::list<falco_configuration::plugin_config> plugins;
conf.get_sequence<std::list<falco_configuration::plugin_config>>(plugins, std::string("plugins"));
std::vector<falco_configuration::plugin_config> m_plugins{ std::make_move_iterator(std::begin(plugins)),
std::make_move_iterator(std::end(plugins)) };
ASSERT_EQ(m_plugins[0].m_name, "k8saudit");
ASSERT_EQ(m_plugins[0].m_library_path, "/foo/" + env_var_value + "/libk8saudit.so");
ASSERT_EQ(m_plugins[0].m_open_params, "12");
/* Clear the set environment variables after testing */
SET_ENV_VAR(env_var_name.c_str(), "");
SET_ENV_VAR(embedded_env_var_name.c_str(), "");
SET_ENV_VAR(bool_env_var_name.c_str(), "");
SET_ENV_VAR(int_env_var_name.c_str(), "");
SET_ENV_VAR(empty_env_var_name.c_str(), "");
}
TEST(Configuration, configuration_webserver_ip)

View File

@@ -11,7 +11,7 @@
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
set(FALCO_ENGINE_SOURCE_FILES
add_library(falco_engine STATIC
falco_common.cpp
falco_engine.cpp
falco_load_result.cpp
@@ -25,9 +25,8 @@ set(FALCO_ENGINE_SOURCE_FILES
rule_loader.cpp
rule_loader_reader.cpp
rule_loader_collector.cpp
rule_loader_compiler.cpp)
add_library(falco_engine STATIC ${FALCO_ENGINE_SOURCE_FILES})
rule_loader_compiler.cpp
)
if (EMSCRIPTEN)
target_compile_options(falco_engine PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0")
@@ -35,14 +34,17 @@ endif()
add_dependencies(falco_engine yamlcpp njson)
target_include_directories(
falco_engine
PUBLIC
"${NJSON_INCLUDE}"
"${TBB_INCLUDE_DIR}"
"${LIBSCAP_INCLUDE_DIRS}"
"${LIBSINSP_INCLUDE_DIRS}"
"${YAMLCPP_INCLUDE_DIR}"
"${PROJECT_BINARY_DIR}/userspace/engine")
target_include_directories(falco_engine
PUBLIC
${LIBSCAP_INCLUDE_DIRS}
${LIBSINSP_INCLUDE_DIRS}
${PROJECT_BINARY_DIR}/userspace/engine
${nlohmann_json_DIR}
${TBB_INCLUDE_DIR}
${YAMLCPP_INCLUDE_DIR}
)
target_link_libraries(falco_engine "${FALCO_SINSP_LIBRARY}" "${YAMLCPP_LIB}")
target_link_libraries(falco_engine
${FALCO_SINSP_LIBRARY}
${YAMLCPP_LIB}
)

View File

@@ -195,7 +195,7 @@ std::unique_ptr<load_result> falco_engine::load_rules(const std::string &rules_c
// read rules YAML file and collect its definitions
rule_loader::reader reader;
if (reader.read(cfg, m_rule_collector))
{
{
// compile the definitions (resolve macro/list refs, exceptions, ...)
m_last_compile_output = std::make_unique<rule_loader::compiler::compile_output>();
rule_loader::compiler().compile(cfg, m_rule_collector, *m_last_compile_output.get());
@@ -298,6 +298,12 @@ std::unique_ptr<load_result> falco_engine::load_rules_file(const std::string &ru
void falco_engine::enable_rule(const std::string &substring, bool enabled, const std::string &ruleset)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
enable_rule(substring, enabled, ruleset_id);
}
void falco_engine::enable_rule(const std::string &substring, bool enabled, const uint16_t ruleset_id)
{
bool match_exact = false;
for(const auto &it : m_sources)
@@ -316,6 +322,12 @@ 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)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
enable_rule_exact(rule_name, enabled, ruleset_id);
}
void falco_engine::enable_rule_exact(const std::string &rule_name, bool enabled, const uint16_t ruleset_id)
{
bool match_exact = true;
for(const auto &it : m_sources)
@@ -335,6 +347,11 @@ void falco_engine::enable_rule_by_tag(const std::set<std::string> &tags, bool en
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
enable_rule_by_tag(tags, enabled, ruleset_id);
}
void falco_engine::enable_rule_by_tag(const std::set<std::string> &tags, bool enabled, const uint16_t ruleset_id)
{
for(const auto &it : m_sources)
{
if(enabled)
@@ -384,7 +401,7 @@ libsinsp::events::set<ppm_sc_code> falco_engine::sc_codes_for_ruleset(const std:
{
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));
@@ -505,17 +522,17 @@ std::size_t falco_engine::add_source(const std::string &source,
return m_sources.insert(src, source);
}
template <typename T> inline Json::Value sequence_to_json_array(const T& seq)
template <typename T> inline nlohmann::json sequence_to_json_array(const T& seq)
{
Json::Value ret = Json::arrayValue;
for (auto it = seq.begin(); it != seq.end(); it++)
nlohmann::json ret = nlohmann::json::array();
for (const auto& v : seq)
{
ret.append(*it);
ret.push_back(v);
}
return ret;
}
void falco_engine::describe_rule(std::string *rule, const std::vector<std::shared_ptr<sinsp_plugin>>& plugins, bool json) const
nlohmann::json falco_engine::describe_rule(std::string *rule, const std::vector<std::shared_ptr<sinsp_plugin>>& plugins) const
{
// use previously-loaded collector definitions and the compiled
// output of rules, macros, and lists.
@@ -524,103 +541,69 @@ void falco_engine::describe_rule(std::string *rule, const std::vector<std::share
throw falco_exception("rules most be loaded before describing them");
}
if(!json)
{
static const char *rule_fmt = "%-50s %s\n";
fprintf(stdout, rule_fmt, "Rule", "Description");
fprintf(stdout, rule_fmt, "----", "-----------");
if(!rule)
{
for(auto &r : m_rules)
{
auto str = falco::utils::wrap_text(r.description, 51, 110) + "\n";
fprintf(stdout, rule_fmt, r.name.c_str(), str.c_str());
}
}
else
{
auto r = m_rules.at(*rule);
if(r == nullptr)
{
return;
}
auto str = falco::utils::wrap_text(r->description, 51, 110) + "\n";
fprintf(stdout, rule_fmt, r->name.c_str(), str.c_str());
}
return;
}
// use collected and compiled info to print a json output
Json::FastWriter writer;
std::string json_str;
nlohmann::json output;
if(!rule)
{
// In this case we build json information about
// all rules, macros and lists
Json::Value output;
// Store required engine version
auto required_engine_version = m_rule_collector.required_engine_version();
output["required_engine_version"] = required_engine_version.version.as_string();
// Store required plugin versions
Json::Value plugin_versions = Json::arrayValue;
nlohmann::json plugin_versions = nlohmann::json::array();
auto required_plugin_versions = m_rule_collector.required_plugin_versions();
for(const auto& req : required_plugin_versions)
{
Json::Value r;
nlohmann::json r;
r["name"] = req.at(0).name;
r["version"] = req.at(0).version;
Json::Value alternatives = Json::arrayValue;
nlohmann::json alternatives = nlohmann::json::array();
for(size_t i = 1; i < req.size(); i++)
{
Json::Value alternative;
nlohmann::json alternative;
alternative["name"] = req[i].name;
alternative["version"] = req[i].version;
alternatives.append(alternative);
alternatives.push_back(std::move(alternative));
}
r["alternatives"] = alternatives;
plugin_versions.append(r);
r["alternatives"] = std::move(alternatives);
plugin_versions.push_back(std::move(r));
}
output["required_plugin_versions"] = plugin_versions;
output["required_plugin_versions"] = std::move(plugin_versions);
// Store information about rules
Json::Value rules_array = Json::arrayValue;
nlohmann::json rules_array = nlohmann::json::array();
for(const auto& r : m_last_compile_output->rules)
{
auto info = m_rule_collector.rules().at(r.name);
Json::Value rule;
nlohmann::json rule;
get_json_details(rule, r, *info, plugins);
rules_array.append(rule);
rules_array.push_back(std::move(rule));
}
output["rules"] = rules_array;
output["rules"] = std::move(rules_array);
// Store information about macros
Json::Value macros_array = Json::arrayValue;
nlohmann::json macros_array = nlohmann::json::array();
for(const auto &m : m_last_compile_output->macros)
{
auto info = m_rule_collector.macros().at(m.name);
Json::Value macro;
nlohmann::json macro;
get_json_details(macro, m, *info, plugins);
macros_array.append(macro);
macros_array.push_back(std::move(macro));
}
output["macros"] = macros_array;
output["macros"] = std::move(macros_array);
// Store information about lists
Json::Value lists_array = Json::arrayValue;
// Store information about lists
nlohmann::json lists_array = nlohmann::json::array();
for(const auto &l : m_last_compile_output->lists)
{
auto info = m_rule_collector.lists().at(l.name);
Json::Value list;
nlohmann::json list;
get_json_details(list, l, *info, plugins);
lists_array.append(list);
lists_array.push_back(std::move(list));
}
output["lists"] = lists_array;
json_str = writer.write(output);
output["lists"] = std::move(lists_array);
}
else
{
@@ -631,21 +614,24 @@ void falco_engine::describe_rule(std::string *rule, const std::vector<std::share
throw falco_exception("Rule \"" + *rule + "\" is not loaded");
}
auto r = m_rules.at(ri->name);
Json::Value rule;
nlohmann::json rule;
get_json_details(rule, *r, *ri, plugins);
json_str = writer.write(rule);
nlohmann::json rules_array = nlohmann::json::array();
rules_array.push_back(std::move(rule));
output["rules"] = std::move(rules_array);
}
fprintf(stdout, "%s", json_str.c_str());
return output;
}
void falco_engine::get_json_details(
Json::Value &out,
nlohmann::json &out,
const falco_rule &r,
const rule_loader::rule_info &info,
const std::vector<std::shared_ptr<sinsp_plugin>>& plugins) const
{
Json::Value rule_info;
nlohmann::json rule_info;
// Fill general rule information
rule_info["name"] = r.name;
@@ -656,7 +642,7 @@ void falco_engine::get_json_details(
rule_info["enabled"] = info.enabled;
rule_info["source"] = r.source;
rule_info["tags"] = sequence_to_json_array(info.tags);
out["info"] = rule_info;
out["info"] = std::move(rule_info);
// Parse rule condition and build the non-compiled AST
// Assumption: no error because rules have already been loaded.
@@ -665,7 +651,7 @@ void falco_engine::get_json_details(
// get details related to the condition's filter
filter_details details;
filter_details compiled_details;
Json::Value json_details;
nlohmann::json json_details;
for(const auto &m : m_rule_collector.macros())
{
details.known_macros.insert(m.name);
@@ -720,15 +706,15 @@ void falco_engine::get_json_details(
else
{
exception_operators.insert(e.comps.item);
}
}
}
out["details"]["exception_names"] = sequence_to_json_array(exception_names);
out["details"]["exception_operators"] = sequence_to_json_array(exception_operators);
// Store event types
Json::Value events;
nlohmann::json events;
get_json_evt_types(events, info.source, r.condition.get());
out["details"]["events"] = events;
out["details"]["events"] = std::move(events);
// Store compiled condition and output
out["details"]["condition_compiled"] = libsinsp::filter::ast::as_string(r.condition.get());
@@ -739,25 +725,25 @@ void falco_engine::get_json_details(
// - The fields used in the rule's condition, output, and exceptions
// - The evt types used in the rule's condition checks, that can potentially
// match plugin-provided async events
Json::Value used_plugins;
nlohmann::json used_plugins;
// note: making a union of conditions's and output's fields
// note: the condition's AST accounts for all the resolved refs and exceptions
compiled_details.fields.insert(out_fields.begin(), out_fields.end());
get_json_used_plugins(used_plugins, info.source, compiled_details.evtnames, compiled_details.fields, plugins);
out["details"]["plugins"] = used_plugins;
out["details"]["plugins"] = std::move(used_plugins);
}
void falco_engine::get_json_details(
Json::Value& out,
nlohmann::json& out,
const falco_macro& m,
const rule_loader::macro_info& info,
const std::vector<std::shared_ptr<sinsp_plugin>>& plugins) const
{
Json::Value macro_info;
nlohmann::json macro_info;
macro_info["name"] = m.name;
macro_info["condition"] = info.cond;
out["info"] = macro_info;
out["info"] = std::move(macro_info);
// Parse the macro condition and build the non-compiled AST
// Assumption: no exception because rules have already been loaded.
@@ -766,7 +752,7 @@ void falco_engine::get_json_details(
// get details related to the condition's filter
filter_details details;
filter_details compiled_details;
Json::Value json_details;
nlohmann::json json_details;
for(const auto &m : m_rule_collector.macros())
{
details.known_macros.insert(m.name);
@@ -787,9 +773,9 @@ void falco_engine::get_json_details(
out["details"]["condition_fields"] = sequence_to_json_array(compiled_details.fields);
// Store event types
Json::Value events;
nlohmann::json events;
get_json_evt_types(events, "", m.condition.get());
out["details"]["events"] = events;
out["details"]["events"] = std::move(events);
// Store compiled condition
out["details"]["condition_compiled"] = libsinsp::filter::ast::as_string(m.condition.get());
@@ -800,20 +786,20 @@ void falco_engine::get_json_details(
// if a macro uses a plugin's field, we can't be sure which plugin actually
// is used until we resolve the macro ref in a rule providing a source for
// disambiguation.
out["details"]["plugins"] = Json::arrayValue;
out["details"]["plugins"] = nlohmann::json::array();
}
void falco_engine::get_json_details(
Json::Value& out,
nlohmann::json& out,
const falco_list& l,
const rule_loader::list_info& info,
const std::vector<std::shared_ptr<sinsp_plugin>>& plugins) const
{
Json::Value list_info;
nlohmann::json list_info;
list_info["name"] = l.name;
// note: the syntactic definitions still has the list refs unresolved
Json::Value items = Json::arrayValue;
nlohmann::json items = nlohmann::json::array();
std::unordered_set<std::string> lists;
for(const auto &i : info.items)
{
@@ -825,19 +811,19 @@ void falco_engine::get_json_details(
lists.insert(i);
continue;
}
items.append(i);
items.push_back(std::move(i));
}
list_info["items"] = items;
out["info"] = list_info;
list_info["items"] = std::move(items);
out["info"] = std::move(list_info);
out["details"]["used"] = l.used;
out["details"]["lists"] = sequence_to_json_array(lists);
out["details"]["items_compiled"] = sequence_to_json_array(l.items);
out["details"]["plugins"] = Json::arrayValue; // always empty
out["details"]["plugins"] = nlohmann::json::array(); // always empty
}
void falco_engine::get_json_evt_types(
Json::Value& out,
nlohmann::json& out,
const std::string& source,
libsinsp::filter::ast::expr* ast) const
{
@@ -860,7 +846,7 @@ void falco_engine::get_json_evt_types(
}
void falco_engine::get_json_used_plugins(
Json::Value& out,
nlohmann::json& out,
const std::string& source,
const std::unordered_set<std::string>& evtnames,
const std::unordered_set<std::string>& fields,
@@ -869,14 +855,17 @@ void falco_engine::get_json_used_plugins(
// note: condition and output fields may have an argument, so
// we need to isolate the field names
std::unordered_set<std::string> fieldnames;
for (auto f: fields)
for (const auto &f: fields)
{
auto argpos = f.find('[');
if (argpos != std::string::npos)
{
f = f.substr(0, argpos);
fieldnames.insert(f.substr(0, argpos));
}
else
{
fieldnames.insert(f);
}
fieldnames.insert(f);
}
std::unordered_set<std::string> used_plugins;

View File

@@ -96,15 +96,23 @@ public:
//
void enable_rule(const std::string &substring, bool enabled, const std::string &ruleset = s_default_ruleset);
// Same as above but providing a ruleset id instead
void enable_rule(const std::string &substring, bool enabled, const uint16_t ruleset_id);
// Like enable_rule, but the rule name must be an exact match.
void enable_rule_exact(const std::string &rule_name, bool enabled, const std::string &ruleset = s_default_ruleset);
// Same as above but providing a ruleset id instead
void enable_rule_exact(const std::string &rule_name, bool enabled, const uint16_t ruleset_id);
//
// Enable/Disable any rules with any of the provided tags (set, exact matches only)
//
void enable_rule_by_tag(const std::set<std::string> &tags, bool enabled, const std::string &ruleset = s_default_ruleset);
// Same as above but providing a ruleset id instead
void enable_rule_by_tag(const std::set<std::string> &tags, bool enabled, const uint16_t ruleset_id);
//
// Must be called after the engine has been configured and all rulesets
// have been loaded and enabled/disabled.
@@ -136,7 +144,7 @@ public:
// Print details on the given rule. If rule is NULL, print
// details on all rules.
//
void describe_rule(std::string *rule, const std::vector<std::shared_ptr<sinsp_plugin>>& plugins, bool json) const;
nlohmann::json describe_rule(std::string *rule, const std::vector<std::shared_ptr<sinsp_plugin>>& plugins) const;
//
// Print statistics on how many events matched each rule.
@@ -315,26 +323,26 @@ private:
// Retrieve json details from rules, macros, lists
void get_json_details(
Json::Value& out,
nlohmann::json& out,
const falco_rule& r,
const rule_loader::rule_info& info,
const std::vector<std::shared_ptr<sinsp_plugin>>& plugins) const;
void get_json_details(
Json::Value& out,
nlohmann::json& out,
const falco_macro& m,
const rule_loader::macro_info& info,
const std::vector<std::shared_ptr<sinsp_plugin>>& plugins) const;
void get_json_details(
Json::Value& out,
nlohmann::json& out,
const falco_list& l,
const rule_loader::list_info& info,
const std::vector<std::shared_ptr<sinsp_plugin>>& plugins) const;
void get_json_evt_types(
Json::Value& out,
nlohmann::json& out,
const std::string& source,
libsinsp::filter::ast::expr* ast) const;
void get_json_used_plugins(
Json::Value& out,
nlohmann::json& out,
const std::string& source,
const std::unordered_set<std::string>& evttypes,
const std::unordered_set<std::string>& fields,

View File

@@ -20,7 +20,7 @@ limitations under the License.
// The version of this Falco engine
#define FALCO_ENGINE_VERSION_MAJOR 0
#define FALCO_ENGINE_VERSION_MINOR 27
#define FALCO_ENGINE_VERSION_MINOR 29
#define FALCO_ENGINE_VERSION_PATCH 0
#define FALCO_ENGINE_VERSION \
@@ -34,4 +34,4 @@ limitations under the License.
// It represents the fields supported by this version of Falco,
// the event types, and the underlying driverevent schema. It's used to
// detetect changes in engine version in our CI jobs.
#define FALCO_ENGINE_CHECKSUM "dbc34e88ab420320994d85f155dee6baff2dd018aacc00e249f897edc8b1e0f4"
#define FALCO_ENGINE_CHECKSUM "30bd8b1c09eab71e14416f04b617d4d20bc7c7358bce91748ce9bd8007786c4d"

View File

@@ -20,7 +20,6 @@ limitations under the License.
#include <cstring>
#include <iomanip>
#include "falco_common.h"
#include "falco_utils.h"
#include "utils.h"
@@ -161,80 +160,6 @@ void readfile(const std::string& filename, std::string& data)
return;
}
// URI-decodes the given string by replacing percent-encoded
// characters with the actual character. Returns the decoded string.
//
// When plus_as_space is true, non-encoded plus signs in the query are decoded as spaces.
// (http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1)
std::string decode_uri(const std::string& str, bool plus_as_space)
{
std::string decoded_str;
bool in_query = false;
std::string::const_iterator it = str.begin();
std::string::const_iterator end = str.end();
while(it != end)
{
char c = *it++;
if(c == '?')
{
in_query = true;
}
// spaces may be encoded as plus signs in the query
if(in_query && plus_as_space && c == '+')
{
c = ' ';
}
else if(c == '%')
{
if (it == end)
{
throw falco_exception("URI encoding: no hex digit following percent sign in " + str);
}
char hi = *it++;
if (it == end)
{
throw falco_exception("URI encoding: two hex digits must follow percent sign in " + str);
}
char lo = *it++;
if (hi >= '0' && hi <= '9')
{
c = hi - '0';
}
else if (hi >= 'A' && hi <= 'F')
{
c = hi - 'A' + 10;
}
else if (hi >= 'a' && hi <= 'f')
{
c = hi - 'a' + 10;
}
else
{
throw falco_exception("URI encoding: not a hex digit found in " + str);
}
c *= 16;
if (lo >= '0' && lo <= '9')
{
c += lo - '0';
}
else if (lo >= 'A' && lo <= 'F')
{
c += lo - 'A' + 10;
}
else if (lo >= 'a' && lo <= 'f')
{
c += lo - 'a' + 10;
}
else
{
throw falco_exception("URI encoding: not a hex digit");
}
}
decoded_str += c;
}
return decoded_str;
}
namespace network
{
bool is_unix_scheme(const std::string& url)

View File

@@ -52,8 +52,6 @@ void readfile(const std::string& filename, std::string& data);
uint32_t hardware_concurrency();
std::string decode_uri(const std::string& str, bool plus_as_space);
namespace network
{
static const std::string UNIX_SCHEME("unix://");

View File

@@ -22,12 +22,12 @@ limitations under the License.
#include <unordered_set>
#include <unordered_map>
struct filter_details
struct filter_details
{
// input macros and lists
std::unordered_set<std::string> known_macros;
std::unordered_set<std::string> known_lists;
// output details
std::unordered_set<std::string> fields;
std::unordered_set<std::string> macros;
@@ -47,25 +47,23 @@ public:
/*!
\brief Visits a filter AST and stores details about macros, lists,
fields and operators used.
\param filter The filter AST to be processed.
\param details Helper structure used to state known macros and
\param filter The filter AST to be processed.
\param details Helper structure used to state known macros and
lists on input, and to store all the retrieved details as output.
*/
void run(libsinsp::filter::ast::expr* filter,
filter_details& details);
private:
struct visitor : public libsinsp::filter::ast::expr_visitor
{
visitor(filter_details& details) :
visitor(filter_details& details) :
m_details(details),
m_expect_list(false),
m_expect_macro(false),
m_expect_evtname(false) {}
visitor(visitor&&) = default;
visitor& operator = (visitor&&) = default;
visitor(const visitor&) = delete;
visitor& operator = (const visitor&) = delete;
void visit(libsinsp::filter::ast::and_expr* e) override;
void visit(libsinsp::filter::ast::or_expr* e) override;

View File

@@ -61,7 +61,7 @@ class filter_macro_resolver
/*!
\brief used in get_{resolved,unknown}_macros and get_errors
to represent an identifier/string value along with an AST position.
to represent an identifier/string value along with an AST position.
*/
typedef std::pair<std::string,libsinsp::filter::ast::pos_info> value_info;
@@ -103,10 +103,6 @@ class filter_macro_resolver
m_unknown_macros(unknown_macros),
m_resolved_macros(resolved_macros),
m_macros(macros) {}
visitor(visitor&&) = default;
visitor& operator = (visitor&&) = default;
visitor(const visitor&) = delete;
visitor& operator = (const visitor&) = delete;
std::vector<std::string> m_macros_path;
std::unique_ptr<libsinsp::filter::ast::expr> m_node_substitute;

View File

@@ -562,10 +562,8 @@ rule_loader::rule_load_exception::~rule_load_exception()
{
}
const char* rule_loader::rule_load_exception::what()
const char* rule_loader::rule_load_exception::what() const noexcept
{
errstr = falco::load_result::error_code_str(ec) + ": "
+ msg.c_str();
return errstr.c_str();
// const + noexcept: can't use functions that change the object or throw
return msg.c_str();
}

View File

@@ -209,18 +209,12 @@ namespace rule_loader
public:
rule_load_exception(falco::load_result::error_code ec, const std::string& msg, const context& ctx);
virtual ~rule_load_exception();
rule_load_exception(rule_load_exception&&) = default;
rule_load_exception& operator = (rule_load_exception&&) = default;
rule_load_exception(const rule_load_exception&) = default;
rule_load_exception& operator = (const rule_load_exception&) = default;
const char* what();
const char* what() const noexcept override;
falco::load_result::error_code ec;
std::string msg;
context ctx;
std::string errstr;
};
/*!
@@ -278,10 +272,6 @@ namespace rule_loader
{
res.reset(new result(name));
}
configuration(configuration&&) = default;
configuration& operator = (configuration&&) = default;
configuration(const configuration&) = delete;
configuration& operator = (const configuration&) = delete;
// inputs
const std::string& content;

View File

@@ -50,12 +50,6 @@ static void paren_item(std::string& e)
}
}
static inline bool is_operator_defined(const std::string& op)
{
auto ops = libsinsp::filter::parser::supported_operators();
return find(ops.begin(), ops.end(), op) != ops.end();
}
static inline bool is_operator_for_list(const std::string& op)
{
auto ops = libsinsp::filter::parser::supported_operators(true);
@@ -83,12 +77,12 @@ static void build_rule_exception_infos(
std::string& condition)
{
std::string tmp;
for (auto &ex : exceptions)
for (const auto &ex : exceptions)
{
std::string icond;
if(!ex.fields.is_list)
{
for (auto &val : ex.values)
for (const auto &val : ex.values)
{
THROW(val.is_list,
"Expected values array to contain a list of strings",
@@ -107,7 +101,7 @@ static void build_rule_exception_infos(
else
{
icond = "(";
for (auto &values : ex.values)
for (const auto &values : ex.values)
{
THROW(ex.fields.items.size() != values.items.size(),
"Fields and values lists must have equal length",
@@ -116,13 +110,13 @@ static void build_rule_exception_infos(
icond += "(";
uint32_t k = 0;
std::string istr;
for (auto &field : ex.fields.items)
for (const auto &field : ex.fields.items)
{
icond += k == 0 ? "" : " and ";
if (values.items[k].is_list)
{
istr = "(";
for (auto &v : values.items[k].items)
for (const auto &v : values.items[k].items)
{
tmp = v.item;
quote_item(tmp);
@@ -212,7 +206,7 @@ static bool resolve_list(std::string& cnd, const falco_list& list)
}
// create substitution string by concatenating all values
std::string sub = "";
for (auto &v : list.items)
for (const auto &v : list.items)
{
if (!sub.empty())
{
@@ -262,7 +256,7 @@ static void resolve_macros(
const rule_loader::context &ctx)
{
filter_macro_resolver macro_resolver;
for (auto &m : infos)
for (const auto &m : infos)
{
if (m.index < visibility)
{
@@ -287,7 +281,7 @@ static void resolve_macros(
THROW(true, errmsg, cond_ctx);
}
for (auto &it : macro_resolver.get_resolved_macros())
for (const auto &it : macro_resolver.get_resolved_macros())
{
macros.at(it.first)->used = true;
}
@@ -346,13 +340,13 @@ void rule_loader::compiler::compile_list_infos(
indexed_vector<falco_list>& out) const
{
std::string tmp;
std::vector<std::string> used;
for (auto &list : col.lists())
std::list<std::string> used;
falco_list v;
for (const auto &list : col.lists())
{
falco_list v;
v.name = list.name;
v.items.clear();
for (auto &item : list.items)
for (const auto &item : list.items)
{
const auto ref = col.lists().at(item);
if (ref && ref->index < list.visibility)
@@ -375,7 +369,7 @@ void rule_loader::compiler::compile_list_infos(
auto list_id = out.insert(v, v.name);
out.at(list_id)->id = list_id;
}
for (auto &v : used)
for (const auto &v : used)
{
out.at(v)->used = true;
}
@@ -388,7 +382,7 @@ void rule_loader::compiler::compile_macros_infos(
indexed_vector<falco_list>& lists,
indexed_vector<falco_macro>& out) const
{
for (auto &m : col.macros())
for (const auto &m : col.macros())
{
falco_macro entry;
entry.name = m.name;
@@ -422,7 +416,7 @@ void rule_loader::compiler::compile_rule_infos(
std::string err, condition;
std::set<falco::load_result::load_result::warning_code> warn_codes;
filter_warning_resolver warn_resolver;
for (auto &r : col.rules())
for (const auto &r : col.rules())
{
// skip the rule if it has an unknown source
if (r.unknown_source)
@@ -453,7 +447,7 @@ void rule_loader::compiler::compile_rule_infos(
warn_codes.clear();
if (warn_resolver.run(rule.condition.get(), warn_codes))
{
for (auto &w : warn_codes)
for (const auto &w : warn_codes)
{
cfg.res->add_warning(w, "", r.ctx);
}
@@ -555,7 +549,7 @@ void rule_loader::compiler::compile(
}
// print info on any dangling lists or macros that were not used anywhere
for (auto &m : out.macros)
for (const auto &m : out.macros)
{
if (!m.used)
{
@@ -565,7 +559,7 @@ void rule_loader::compiler::compile(
macro_info_from_name(col, m.name)->ctx);
}
}
for (auto &l : out.lists)
for (const auto &l : out.lists)
{
if (!l.used)
{

View File

@@ -35,10 +35,6 @@ class stats_manager
public:
stats_manager();
virtual ~stats_manager();
stats_manager(stats_manager&&) = default;
stats_manager& operator = (stats_manager&&) = default;
stats_manager(const stats_manager&) = default;
stats_manager& operator = (const stats_manager&) = default;
/*!
\brief Erases the internal state and statistics data

View File

@@ -36,6 +36,7 @@ set(
app/actions/print_generated_gvisor_config.cpp
app/actions/print_help.cpp
app/actions/print_ignored_events.cpp
app/actions/print_kernel_version.cpp
app/actions/print_plugin_info.cpp
app/actions/print_support.cpp
app/actions/print_syscall_events.cpp

View File

@@ -41,6 +41,7 @@ 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_kernel_version(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);

View File

@@ -20,14 +20,6 @@ limitations under the License.
#include <sys/stat.h>
#include <filesystem>
#ifndef CPPPATH_SEP
#ifdef _MSC_VER
#define CPPPATH_SEP "\\"
#else
#define CPPPATH_SEP "/"
#endif
#endif
using namespace falco::app;
using namespace falco::app::actions;

View File

@@ -20,6 +20,8 @@ limitations under the License.
#include "../state.h"
#include "../run_result.h"
#include <nlohmann/json.hpp>
namespace falco {
namespace app {
namespace actions {
@@ -29,6 +31,8 @@ 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);
void format_described_rules_as_text(const nlohmann::json& v, 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,

View File

@@ -16,6 +16,7 @@ limitations under the License.
*/
#include "helpers.h"
#include "falco_utils.h"
#include <plugin_manager.h>
#include <unordered_set>
@@ -126,3 +127,22 @@ void falco::app::actions::format_plugin_info(std::shared_ptr<sinsp_plugin> p, st
os << " - Async Events" << std::endl;
}
}
static void format_two_columns(std::ostream& os, const std::string& l, const std::string& r)
{
static constexpr const int s_max_line_len = 4096;
char buf[s_max_line_len];
snprintf(buf, sizeof(buf) - 1, "%-50s %s", l.c_str(), r.c_str());
os << buf << std::endl;
}
void falco::app::actions::format_described_rules_as_text(const nlohmann::json& v, std::ostream& os)
{
format_two_columns(os, "Rule", "Description");
format_two_columns(os, "----", "-----------");
for(const auto &r : v["rules"])
{
auto str = falco::utils::wrap_text(r["info"]["description"], 51, 110) + "\n";
format_two_columns(os, r["info"]["name"], str);
}
}

View File

@@ -52,6 +52,11 @@ falco::app::run_result falco::app::actions::open_live_inspector(
{
try
{
if((s.config->m_metrics_flags & PPM_SCAP_STATS_STATE_COUNTERS))
{
inspector->set_sinsp_stats_v2_enabled();
}
if (source != falco_common::syscall_source) /* Plugin engine */
{
for (const auto& p: inspector->get_plugin_manager()->plugins())

View File

@@ -155,17 +155,22 @@ falco::app::run_result falco::app::actions::load_rules_files(falco::app::state&
s.engine->enable_rule_by_tag(s.options.enabled_rule_tags, true);
}
if (s.options.describe_all_rules)
// printout of `-L` option
if (s.options.describe_all_rules || !s.options.describe_rule.empty())
{
std::string* rptr = !s.options.describe_rule.empty() ? &(s.options.describe_rule) : nullptr;
const auto& plugins = s.offline_inspector->get_plugin_manager()->plugins();
s.engine->describe_rule(NULL, plugins, s.config->m_json_output);
return run_result::exit();
}
auto out = s.engine->describe_rule(rptr, plugins);
if (!s.config->m_json_output)
{
format_described_rules_as_text(out, std::cout);
}
else
{
std::cout << out.dump() << std::endl;
}
if (!s.options.describe_rule.empty())
{
const auto& plugins = s.offline_inspector->get_plugin_manager()->plugins();
s.engine->describe_rule(&(s.options.describe_rule), plugins, s.config->m_json_output);
return run_result::exit();
}

View File

@@ -0,0 +1,49 @@
// SPDX-License-Identifier: Apache-2.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 "../app.h"
#include <fstream>
#include <sstream>
#include <errno.h>
using namespace falco::app;
using namespace falco::app::actions;
falco::app::run_result falco::app::actions::print_kernel_version(falco::app::state& s)
{
#ifdef __linux__
// We print this info only when a kernel driver is injected
if(s.is_modern_ebpf() || s.is_ebpf() || s.is_kmod())
{
std::ifstream input_file("/proc/version");
if(!input_file.is_open())
{
// We don't want to fail, we just need to log something
falco_logger::log(falco_logger::level::INFO, "Cannot read under '/proc/version' (err_message: '" + std::string(strerror(errno)) + "', err_code: " + std::to_string(errno) + "). No info provided, go on.");
return run_result::ok();
}
std::stringstream buffer;
buffer << input_file.rdbuf();
std::string contents(buffer.str());
falco_logger::log(falco_logger::level::INFO, "System info: " + contents);
}
#endif
return run_result::ok();
}

View File

@@ -15,8 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include <nlohmann/json.hpp>
#include "actions.h"
#include "../../versions_info.h"

View File

@@ -45,16 +45,12 @@ class source_sync_context
public:
source_sync_context(falco::semaphore& s)
: m_finished(false), m_joined(false), m_semaphore(s) { }
source_sync_context(source_sync_context&&) = default;
source_sync_context& operator = (source_sync_context&&) = default;
source_sync_context(const source_sync_context&) = delete;
source_sync_context& operator = (const source_sync_context&) = delete;
inline void finish()
{
bool v = false;
while (!m_finished.compare_exchange_weak(
v, true,
v, true,
std::memory_order_seq_cst,
std::memory_order_seq_cst))
{
@@ -70,7 +66,7 @@ public:
{
bool v = false;
while (!m_joined.compare_exchange_weak(
v, true,
v, true,
std::memory_order_seq_cst,
std::memory_order_seq_cst))
{
@@ -90,7 +86,7 @@ public:
{
return m_finished.load(std::memory_order_seq_cst);
}
private:
// set to true when the event processing loop finishes
std::atomic<bool> m_finished;
@@ -102,12 +98,6 @@ private:
struct live_context
{
live_context() = default;
live_context(live_context&&) = default;
live_context& operator = (live_context&&) = default;
live_context(const live_context&) = default;
live_context& operator = (const live_context&) = default;
// the name of the source of which events are processed
std::string source;
// the result of the event processing loop
@@ -269,7 +259,7 @@ static falco::app::run_result do_inspect(
}
return run_result::fatal(msg);
}
// for capture mode, the source name can change at every event
stats_collector.collect(inspector, inspector->event_sources()[source_engine_idx], num_evts);
}
@@ -325,7 +315,7 @@ static falco::app::run_result do_inspect(
s.outputs->handle_event(rule_res.evt, rule_res.rule, rule_res.source, rule_res.priority_num, rule_res.format, rule_res.tags);
}
}
num_evts++;
}

View File

@@ -18,6 +18,8 @@ limitations under the License.
#include "actions.h"
#include "helpers.h"
#include <plugin_manager.h>
#include <string>
using namespace falco::app;
@@ -121,15 +123,33 @@ falco::app::run_result falco::app::actions::validate_rules_files(falco::app::sta
}
}
// printout of `-L` option
nlohmann::json describe_res;
if (successful && (s.options.describe_all_rules || !s.options.describe_rule.empty()))
{
std::string* rptr = !s.options.describe_rule.empty() ? &(s.options.describe_rule) : nullptr;
const auto& plugins = s.offline_inspector->get_plugin_manager()->plugins();
describe_res = s.engine->describe_rule(rptr, plugins);
}
if(s.config->m_json_output)
{
nlohmann::json res;
res["falco_load_results"] = results;
printf("%s\n", res.dump().c_str());
if (!describe_res.empty() && successful)
{
res["falco_describe_results"] = std::move(describe_res);
}
std::cout << res.dump() << std::endl;
}
else
{
printf("%s\n", summary.c_str());
std::cout << summary << std::endl;
if (!describe_res.empty() && successful)
{
std::cout << std::endl;
format_described_rules_as_text(describe_res, std::cout);
}
}
if(successful)

View File

@@ -62,6 +62,7 @@ bool falco::app::run(falco::app::state& s, bool& restart, std::string& errstr)
std::list<app_action> run_steps = {
falco::app::actions::load_config,
falco::app::actions::print_help,
falco::app::actions::print_kernel_version,
falco::app::actions::print_version,
falco::app::actions::print_page_size,
falco::app::actions::print_generated_gvisor_config,

View File

@@ -60,7 +60,7 @@ bool falco::app::restart_handler::start(std::string& err)
for (const auto& f : m_watched_files)
{
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CLOSE_WRITE);
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CLOSE_WRITE | IN_MOVE_SELF | IN_DELETE_SELF);
if (wd < 0)
{
err = "could not watch file: " + f;
@@ -71,7 +71,7 @@ bool falco::app::restart_handler::start(std::string& err)
for (const auto &f : m_watched_dirs)
{
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CREATE | IN_DELETE);
auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CREATE | IN_DELETE | IN_MOVE);
if (wd < 0)
{
err = "could not watch directory: " + f;

View File

@@ -57,15 +57,11 @@ public:
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;

View File

@@ -56,7 +56,7 @@ struct state
source_info& operator = (source_info&&) = default;
source_info(const source_info&) = default;
source_info& operator = (const source_info&) = default;
// 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;
@@ -93,10 +93,6 @@ struct state
}
~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;
@@ -145,7 +141,7 @@ struct state
falco_webserver webserver;
#endif
inline bool is_capture_mode() const
inline bool is_capture_mode() const
{
return config->m_engine_mode == engine_kind_t::REPLAY;
}
@@ -155,6 +151,11 @@ struct state
return config->m_engine_mode == engine_kind_t::GVISOR;
}
inline bool is_kmod() const
{
return config->m_engine_mode == engine_kind_t::KMOD;
}
inline bool is_ebpf() const
{
return config->m_engine_mode == engine_kind_t::EBPF;
@@ -170,7 +171,7 @@ struct state
return config->m_engine_mode == engine_kind_t::NONE;
}
inline bool is_source_enabled(const std::string& src) const
inline bool is_source_enabled(const std::string& src) const
{
return enabled_sources.find(falco_common::syscall_source) != enabled_sources.end();
}

View File

@@ -30,13 +30,6 @@ namespace falco
class atomic_signal_handler
{
public:
atomic_signal_handler(): m_triggered(false), m_handled(false) { }
atomic_signal_handler(atomic_signal_handler&&) = default;
atomic_signal_handler& operator = (atomic_signal_handler&&) = default;
atomic_signal_handler(const atomic_signal_handler&) = delete;
atomic_signal_handler& operator = (const atomic_signal_handler&) = delete;
~atomic_signal_handler() = default;
/**
* @brief Returns true if the underlying atomic implementation
* is lock-free as per C++ standard semantics.
@@ -95,7 +88,7 @@ namespace falco
* performed. After the first handler has been performed, every
* other invocation of handle() will be skipped and return false
* up until the next invocation of reset().
*
*
* @param f The action to perform.
* @return true If the action has been performed.
* @return false If the action has not been performed.
@@ -134,7 +127,7 @@ namespace falco
private:
std::mutex m_mtx;
std::atomic<bool> m_triggered;
std::atomic<bool> m_handled;
std::atomic<bool> m_triggered{false};
std::atomic<bool> m_handled{false};
};
};

View File

@@ -69,9 +69,7 @@ falco_configuration::falco_configuration():
m_metrics_interval(5000),
m_metrics_stats_rule_enabled(false),
m_metrics_output_file(""),
m_metrics_resource_utilization_enabled(true),
m_metrics_kernel_event_counters_enabled(true),
m_metrics_libbpf_stats_enabled(true),
m_metrics_flags((PPM_SCAP_STATS_KERNEL_COUNTERS | PPM_SCAP_STATS_LIBBPF_STATS | PPM_SCAP_STATS_RESOURCE_UTILIZATION | PPM_SCAP_STATS_STATE_COUNTERS)),
m_metrics_convert_memory_to_mb(true),
m_metrics_include_empty_values(false)
{
@@ -311,6 +309,14 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
client_key = config.get_scalar<std::string>("http_output.client_key", "/etc/ssl/certs/client.key");
http_output.options["client_key"] = client_key;
bool compress_uploads;
compress_uploads = config.get_scalar<bool>("http_output.compress_uploads", false);
http_output.options["compress_uploads"] = compress_uploads? std::string("true") : std::string("false");
bool keep_alive;
keep_alive = config.get_scalar<bool>("http_output.keep_alive", false);
http_output.options["keep_alive"] = keep_alive? std::string("true") : std::string("false");
m_outputs.push_back(http_output);
}
@@ -453,9 +459,29 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
m_metrics_interval = falco::utils::parse_prometheus_interval(m_metrics_interval_str);
m_metrics_stats_rule_enabled = config.get_scalar<bool>("metrics.output_rule", false);
m_metrics_output_file = config.get_scalar<std::string>("metrics.output_file", "");
m_metrics_resource_utilization_enabled = config.get_scalar<bool>("metrics.resource_utilization_enabled", true);
m_metrics_kernel_event_counters_enabled = config.get_scalar<bool>("metrics.kernel_event_counters_enabled", true);
m_metrics_libbpf_stats_enabled = config.get_scalar<bool>("metrics.libbpf_stats_enabled", true);
m_metrics_flags = 0;
if (config.get_scalar<bool>("metrics.resource_utilization_enabled", true))
{
m_metrics_flags |= PPM_SCAP_STATS_RESOURCE_UTILIZATION;
}
if (config.get_scalar<bool>("metrics.state_counters_enabled", true))
{
m_metrics_flags |= PPM_SCAP_STATS_STATE_COUNTERS;
}
if (config.get_scalar<bool>("metrics.kernel_event_counters_enabled", true))
{
m_metrics_flags |= PPM_SCAP_STATS_KERNEL_COUNTERS;
}
if (config.get_scalar<bool>("metrics.libbpf_stats_enabled", true))
{
m_metrics_flags |= PPM_SCAP_STATS_LIBBPF_STATS;
}
m_metrics_convert_memory_to_mb = config.get_scalar<bool>("metrics.convert_memory_to_mb", true);
m_metrics_include_empty_values = config.get_scalar<bool>("metrics.include_empty_values", false);

View File

@@ -55,45 +55,38 @@ enum class engine_kind_t : uint8_t
class falco_configuration
{
public:
typedef struct {
public:
struct plugin_config {
std::string m_name;
std::string m_library_path;
std::string m_init_config;
std::string m_open_params;
} plugin_config;
};
typedef struct {
public:
struct kmod_config {
int16_t m_buf_size_preset;
bool m_drop_failed_exit;
} kmod_config;
};
typedef struct {
public:
struct ebpf_config {
std::string m_probe_path;
int16_t m_buf_size_preset;
bool m_drop_failed_exit;
} ebpf_config;
};
typedef struct {
public:
struct modern_ebpf_config {
uint16_t m_cpus_for_each_buffer;
int16_t m_buf_size_preset;
bool m_drop_failed_exit;
} modern_ebpf_config;
};
typedef struct {
public:
struct replay_config {
std::string m_capture_file;
} replay_config;
};
typedef struct {
public:
struct gvisor_config {
std::string m_config;
std::string m_root;
} gvisor_config;
};
falco_configuration();
virtual ~falco_configuration() = default;
@@ -158,9 +151,7 @@ public:
uint64_t m_metrics_interval;
bool m_metrics_stats_rule_enabled;
std::string m_metrics_output_file;
bool m_metrics_resource_utilization_enabled;
bool m_metrics_kernel_event_counters_enabled;
bool m_metrics_libbpf_stats_enabled;
uint32_t m_metrics_flags;
bool m_metrics_convert_memory_to_mb;
bool m_metrics_include_empty_values;
std::vector<plugin_config> m_plugins;

View File

@@ -65,7 +65,7 @@ falco_outputs::falco_outputs(
{
add_output(output);
}
m_outputs_queue_num_drops = {0};
m_outputs_queue_num_drops = 0;
#ifndef __EMSCRIPTEN__
m_queue.set_capacity(outputs_queue_capacity);
m_worker_thread = std::thread(&falco_outputs::worker, this);

View File

@@ -32,11 +32,6 @@ namespace falco
* @brief Creates a semaphore with the given initial counter value
*/
semaphore(int c = 0): count(c) {}
semaphore(semaphore&&) = default;
semaphore& operator = (semaphore&&) = default;
semaphore(const semaphore&) = delete;
semaphore& operator = (const semaphore&) = delete;
~semaphore() = default;
/**
* @brief Increments the internal counter and unblocks acquirers

View File

@@ -97,6 +97,16 @@ bool falco::outputs::output_http::init(const config& oc, bool buffered, const st
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, noop_write_callback));
}
if(m_oc.options["compress_uploads"] == std::string("true"))
{
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_TRANSFER_ENCODING, 1L));
}
if(m_oc.options["keep_alive"] == std::string("true"))
{
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L));
}
if(res != CURLE_OK)
{
err = "libcurl error: " + std::string(curl_easy_strerror(res));

View File

@@ -20,7 +20,6 @@ limitations under the License.
#endif
#include <ctime>
#include <csignal>
#include <nlohmann/json.hpp>
#include <atomic>
#include <nlohmann/json.hpp>
@@ -40,8 +39,8 @@ static timer_t s_timerid;
#else
static uint16_t s_timerid;
#endif
// note: Workaround for older GLIBC versions (< 2.35), where calling timer_delete()
// with an invalid timer ID not returned by timer_create() causes a segfault because of
// note: Workaround for older GLIBC versions (< 2.35), where calling timer_delete()
// with an invalid timer ID not returned by timer_create() causes a segfault because of
// a bug in GLIBC (https://sourceware.org/bugzilla/show_bug.cgi?id=28257).
// Just performing a nullptr check is not enough as even after creating the timer, s_timerid
// remains a nullptr somehow.
@@ -132,7 +131,7 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string &err)
// delete any previously set timer
if (s_timerid_exists)
{
if (timer_delete(s_timerid) == -1)
if (timer_delete(s_timerid) == -1)
{
err = std::string("Could not delete previous timer: ") + strerror(errno);
return false;
@@ -140,7 +139,7 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string &err)
s_timerid_exists = false;
}
if (timer_create(CLOCK_MONOTONIC, &sev, &s_timerid) == -1)
if (timer_create(CLOCK_MONOTONIC, &sev, &s_timerid) == -1)
{
err = std::string("Could not create periodic timer: ") + strerror(errno);
return false;
@@ -151,7 +150,7 @@ bool stats_writer::init_ticker(uint32_t interval_msec, std::string &err)
timer.it_value.tv_nsec = (interval_msec % 1000) * 1000 * 1000;
timer.it_interval = timer.it_value;
if (timer_settime(s_timerid, 0, &timer, NULL) == -1)
if (timer_settime(s_timerid, 0, &timer, NULL) == -1)
{
err = std::string("Could not set up periodic timer: ") + strerror(errno);
return false;
@@ -265,7 +264,7 @@ void stats_writer::worker() noexcept
{
return;
}
// this helps waiting for the first tick
tick = stats_writer::get_ticker();
if (first_tick != tick)
@@ -302,7 +301,7 @@ void stats_writer::worker() noexcept
}
stats_writer::collector::collector(const std::shared_ptr<stats_writer>& writer)
: m_writer(writer), m_last_tick(0), m_samples(0),
: m_writer(writer), m_last_tick(0),
m_last_now(0), m_last_n_evts(0), m_last_n_drops(0), m_last_num_evts(0)
{
}
@@ -343,7 +342,7 @@ void stats_writer::collector::get_metrics_output_fields_wrapper(
if (m_last_num_evts != 0 && stats_snapshot_time_delta_sec > 0)
{
/* Successfully processed userspace event rate. */
output_fields["falco.evts_rate_sec"] = (double)((num_evts - m_last_num_evts) / (double)stats_snapshot_time_delta_sec);
output_fields["falco.evts_rate_sec"] = std::round((double)((num_evts - m_last_num_evts) / (double)stats_snapshot_time_delta_sec) * 10.0) / 10.0; // round to 1 decimal
}
output_fields["falco.num_evts"] = num_evts;
output_fields["falco.num_evts_prev"] = m_last_num_evts;
@@ -358,60 +357,74 @@ void stats_writer::collector::get_metrics_output_fields_additional(
const scap_agent_info* agent_info = inspector->get_agent_info();
#if !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__)
/* Resource utilization, CPU and memory usage etc. */
uint32_t nstats = 0;
int32_t rc = 0;
if (m_writer->m_config->m_metrics_resource_utilization_enabled)
uint32_t flags = m_writer->m_config->m_metrics_flags;
auto buffer = inspector->get_sinsp_stats_v2_buffer();
auto sinsp_stats_v2 = inspector->get_sinsp_stats_v2();
sinsp_thread_manager* thread_manager = inspector->m_thread_manager;
const scap_stats_v2* sinsp_stats_v2_snapshot = libsinsp::stats::get_sinsp_stats_v2(flags, agent_info, thread_manager, sinsp_stats_v2, buffer, &nstats, &rc);
if (sinsp_stats_v2_snapshot && rc == 0 && nstats > 0)
{
const scap_stats_v2* utilization;
auto buffer = inspector->get_sinsp_stats_v2_buffer();
utilization = libsinsp::resource_utilization::get_resource_utilization(agent_info, buffer, &nstats, &rc);
if (utilization && rc == 0 && nstats > 0)
for(uint32_t stat = 0; stat < nstats; stat++)
{
for(uint32_t stat = 0; stat < nstats; stat++)
if (sinsp_stats_v2_snapshot[stat].name[0] == '\0')
{
char metric_name[STATS_NAME_MAX] = "falco.";
strlcat(metric_name, utilization[stat].name, sizeof(metric_name));
switch(utilization[stat].type)
break;
}
char metric_name[STATS_NAME_MAX] = "falco.";
strlcat(metric_name, sinsp_stats_v2_snapshot[stat].name, sizeof(metric_name));
switch(sinsp_stats_v2_snapshot[stat].type)
{
case STATS_VALUE_TYPE_U64:
if (sinsp_stats_v2_snapshot[stat].value.u64 == 0 && !m_writer->m_config->m_metrics_include_empty_values)
{
case STATS_VALUE_TYPE_U64:
if (utilization[stat].value.u64 == 0 && !m_writer->m_config->m_metrics_include_empty_values)
{
break;
}
if (m_writer->m_config->m_metrics_convert_memory_to_mb && strncmp(utilization[stat].name, "container_memory_used", 22) == 0) // exact str match
{
output_fields[metric_name] = (uint64_t)(utilization[stat].value.u64 / (double)1024 / (double)1024);
}
else
{
output_fields[metric_name] = utilization[stat].value.u64;
}
break;
case STATS_VALUE_TYPE_U32:
if (utilization[stat].value.u32 == 0 && !m_writer->m_config->m_metrics_include_empty_values)
{
break;
}
if (m_writer->m_config->m_metrics_convert_memory_to_mb && strncmp(utilization[stat].name, "memory_", 7) == 0) // prefix match
{
output_fields[metric_name] = (uint32_t)(utilization[stat].value.u32 / (double)1024);
}
else
{
output_fields[metric_name] = utilization[stat].value.u32;
}
break;
case STATS_VALUE_TYPE_D:
if (utilization[stat].value.d == 0 && !m_writer->m_config->m_metrics_include_empty_values)
{
break;
}
output_fields[metric_name] = utilization[stat].value.d;
break;
default:
break;
}
if (m_writer->m_config->m_metrics_convert_memory_to_mb)
{
if (strncmp(sinsp_stats_v2_snapshot[stat].name, "container_memory_used", 22) == 0) // exact str match
{
output_fields[metric_name] = (uint64_t)(sinsp_stats_v2_snapshot[stat].value.u64 / (double)1024 / (double)1024);
} else if (strncmp(sinsp_stats_v2_snapshot[stat].name, "memory_", 7) == 0) // prefix match
{
output_fields[metric_name] = (uint64_t)(sinsp_stats_v2_snapshot[stat].value.u64 / (double)1024);
} else
{
output_fields[metric_name] = sinsp_stats_v2_snapshot[stat].value.u64;
}
}
else
{
output_fields[metric_name] = sinsp_stats_v2_snapshot[stat].value.u64;
}
break;
case STATS_VALUE_TYPE_U32:
if (sinsp_stats_v2_snapshot[stat].value.u32 == 0 && !m_writer->m_config->m_metrics_include_empty_values)
{
break;
}
if (m_writer->m_config->m_metrics_convert_memory_to_mb && strncmp(sinsp_stats_v2_snapshot[stat].name, "memory_", 7) == 0) // prefix match
{
output_fields[metric_name] = (uint32_t)(sinsp_stats_v2_snapshot[stat].value.u32 / (double)1024);
}
else
{
output_fields[metric_name] = sinsp_stats_v2_snapshot[stat].value.u32;
}
break;
case STATS_VALUE_TYPE_D:
if (sinsp_stats_v2_snapshot[stat].value.d == 0 && !m_writer->m_config->m_metrics_include_empty_values)
{
break;
}
output_fields[metric_name] = sinsp_stats_v2_snapshot[stat].value.d;
break;
default:
break;
}
}
}
@@ -424,18 +437,16 @@ void stats_writer::collector::get_metrics_output_fields_additional(
/* Kernel side stats counters and libbpf stats if applicable. */
nstats = 0;
rc = 0;
uint32_t flags = 0;
if (!(inspector->check_current_engine(BPF_ENGINE) || inspector->check_current_engine(MODERN_BPF_ENGINE)))
{
flags &= ~PPM_SCAP_STATS_LIBBPF_STATS;
}
if (m_writer->m_config->m_metrics_kernel_event_counters_enabled)
{
flags |= PPM_SCAP_STATS_KERNEL_COUNTERS;
}
if (m_writer->m_config->m_metrics_libbpf_stats_enabled && (inspector->check_current_engine(BPF_ENGINE) || inspector->check_current_engine(MODERN_BPF_ENGINE)))
{
flags |= PPM_SCAP_STATS_LIBBPF_STATS;
}
const scap_stats_v2* stats_v2 = inspector->get_capture_stats_v2(flags, &nstats, &rc);
if (stats_v2 && nstats > 0 && rc == 0)
// Note: ENGINE_FLAG_BPF_STATS_ENABLED check has been moved to libs, that is, when libbpf stats is not enabled
// in the kernel settings we won't collect them even if the end user enabled the libbpf stats option
const scap_stats_v2* scap_stats_v2_snapshot = inspector->get_capture_stats_v2(flags, &nstats, &rc);
if (scap_stats_v2_snapshot && nstats > 0 && rc == 0)
{
/* Cache n_evts and n_drops to derive n_drops_perc. */
uint64_t n_evts = 0;
@@ -444,24 +455,28 @@ void stats_writer::collector::get_metrics_output_fields_additional(
uint64_t n_drops_delta = 0;
for(uint32_t stat = 0; stat < nstats; stat++)
{
if (scap_stats_v2_snapshot[stat].name[0] == '\0')
{
break;
}
// todo: as we expand scap_stats_v2 prefix may be pushed to scap or we may need to expand
// functionality here for example if we add userspace syscall counters that should be prefixed w/ `falco.`
char metric_name[STATS_NAME_MAX] = "scap.";
strlcat(metric_name, stats_v2[stat].name, sizeof(metric_name));
switch(stats_v2[stat].type)
strlcat(metric_name, scap_stats_v2_snapshot[stat].name, sizeof(metric_name));
switch(scap_stats_v2_snapshot[stat].type)
{
case STATS_VALUE_TYPE_U64:
/* Always send high level n_evts related fields, even if zero. */
if (strncmp(stats_v2[stat].name, "n_evts", 7) == 0) // exact not prefix match here
if (strncmp(scap_stats_v2_snapshot[stat].name, "n_evts", 7) == 0) // exact not prefix match here
{
n_evts = stats_v2[stat].value.u64;
n_evts = scap_stats_v2_snapshot[stat].value.u64;
output_fields[metric_name] = n_evts;
output_fields["scap.n_evts_prev"] = m_last_n_evts;
n_evts_delta = n_evts - m_last_n_evts;
if (n_evts_delta != 0 && stats_snapshot_time_delta_sec > 0)
{
/* n_evts is total number of kernel side events. */
output_fields["scap.evts_rate_sec"] = (double)(n_evts_delta / stats_snapshot_time_delta_sec);
output_fields["scap.evts_rate_sec"] = std::round((double)(n_evts_delta / stats_snapshot_time_delta_sec) * 10.0) / 10.0; // round to 1 decimal
}
else
{
@@ -470,16 +485,16 @@ void stats_writer::collector::get_metrics_output_fields_additional(
m_last_n_evts = n_evts;
}
/* Always send high level n_drops related fields, even if zero. */
else if (strncmp(stats_v2[stat].name, "n_drops", 8) == 0) // exact not prefix match here
else if (strncmp(scap_stats_v2_snapshot[stat].name, "n_drops", 8) == 0) // exact not prefix match here
{
n_drops = stats_v2[stat].value.u64;
n_drops = scap_stats_v2_snapshot[stat].value.u64;
output_fields[metric_name] = n_drops;
output_fields["scap.n_drops_prev"] = m_last_n_drops;
n_drops_delta = n_drops - m_last_n_drops;
if (n_drops_delta != 0 && stats_snapshot_time_delta_sec > 0)
{
/* n_drops is total number of kernel side event drops. */
output_fields["scap.evts_drop_rate_sec"] = (double)(n_drops_delta / stats_snapshot_time_delta_sec);
output_fields["scap.evts_drop_rate_sec"] = std::round((double)(n_drops_delta / stats_snapshot_time_delta_sec) * 10.0) / 10.0; // round to 1 decimal
}
else
{
@@ -487,11 +502,11 @@ void stats_writer::collector::get_metrics_output_fields_additional(
}
m_last_n_drops = n_drops;
}
if (stats_v2[stat].value.u64 == 0 && !m_writer->m_config->m_metrics_include_empty_values)
if (scap_stats_v2_snapshot[stat].value.u64 == 0 && !m_writer->m_config->m_metrics_include_empty_values)
{
break;
}
output_fields[metric_name] = stats_v2[stat].value.u64;
output_fields[metric_name] = scap_stats_v2_snapshot[stat].value.u64;
break;
default:
break;

View File

@@ -50,7 +50,7 @@ public:
This class is not thread-safe.
*/
class collector
{
{
public:
/*!
\brief Initializes the collector with the given writer
@@ -74,11 +74,8 @@ public:
*/
void get_metrics_output_fields_additional(nlohmann::json& output_fields, const std::shared_ptr<sinsp>& inspector, double stats_snapshot_time_delta_sec, const std::string& src);
std::shared_ptr<stats_writer> m_writer;
stats_writer::ticker_t m_last_tick;
uint64_t m_samples;
scap_stats m_last_stats;
uint64_t m_last_now;
uint64_t m_last_n_evts;
uint64_t m_last_n_drops;

View File

@@ -37,6 +37,40 @@ limitations under the License.
#include "event_drops.h"
#include "falco_outputs.h"
class yaml_helper;
class yaml_visitor {
private:
using Callback = std::function<void(YAML::Node&)>;
yaml_visitor(Callback cb): seen(), cb(std::move(cb)) {}
void operator()(YAML::Node &cur) {
seen.push_back(cur);
if (cur.IsMap()) {
for (YAML::detail::iterator_value pair : cur) {
descend(pair.second);
}
} else if (cur.IsSequence()) {
for (YAML::detail::iterator_value child : cur) {
descend(child);
}
} else if (cur.IsScalar()) {
cb(cur);
}
}
void descend(YAML::Node &target) {
if (std::find(seen.begin(), seen.end(), target) == seen.end()) {
(*this)(target);
}
}
std::vector<YAML::Node> seen;
Callback cb;
friend class yaml_helper;
};
/**
* @brief An helper class for reading and editing YAML documents
*/
@@ -49,6 +83,7 @@ public:
void load_from_string(const std::string& input)
{
m_root = YAML::Load(input);
pre_process_env_vars();
}
/**
@@ -57,6 +92,7 @@ public:
void load_from_file(const std::string& path)
{
m_root = YAML::LoadFile(path);
pre_process_env_vars();
}
/**
@@ -77,44 +113,8 @@ public:
get_node(node, key);
if(node.IsDefined())
{
std::string value = node.as<std::string>();
// Helper function to convert string to the desired type T
auto convert_str_to_t = [&default_value](const std::string& str) -> T {
std::stringstream ss(str);
T result;
if (ss >> result) return result;
return default_value;
};
// If the value starts with `$$`, check for a subsequent `{...}`
if (value.size() >= 3 && value[0] == '$' && value[1] == '$')
{
// If after stripping the first `$`, the string format is like `${VAR}`, treat it as a plain string and don't resolve.
if (value[2] == '{' && value[value.size() - 1] == '}')
{
value = value.substr(1);
return convert_str_to_t(value);
}
else return convert_str_to_t(value);
}
// Check if the value is an environment variable reference
if(value.size() >= 2 && value[0] == '$' && value[1] == '{' && value[value.size() - 1] == '}')
{
// Format: ${ENV_VAR_NAME}
std::string env_var = value.substr(2, value.size() - 3);
const char* env_value = std::getenv(env_var.c_str()); // Get the environment variable value
if(env_value) return convert_str_to_t(env_value);
return default_value;
}
// If it's not an environment variable reference, return the value as is
return node.as<T>();
return node.as<T>(default_value);
}
return default_value;
}
@@ -153,6 +153,71 @@ public:
private:
YAML::Node m_root;
/*
* When loading a yaml file,
* we immediately pre process all scalar values through a visitor private API,
* and resolve any "${env_var}" to its value;
* moreover, any "$${str}" is resolved to simply "${str}".
*/
void pre_process_env_vars()
{
yaml_visitor([](YAML::Node &scalar) {
auto value = scalar.as<std::string>();
auto start_pos = value.find('$');
while (start_pos != std::string::npos)
{
auto substr = value.substr(start_pos);
// Case 1 -> ${}
if (substr.rfind("${", 0) == 0)
{
auto end_pos = substr.find('}');
if (end_pos != std::string::npos)
{
// Eat "${" and "}" when getting the env var name
auto env_str = substr.substr(2, end_pos - 2);
const char* env_value = std::getenv(env_str.c_str()); // Get the environment variable value
if(env_value)
{
// env variable name + "${}"
value.replace(start_pos, env_str.length() + 3, env_value);
}
else
{
value.erase(start_pos, env_str.length() + 3);
}
}
else
{
// There are no "}" chars anymore; just break leaving rest of value untouched.
break;
}
}
// Case 2 -> $${}
else if (substr.rfind("$${", 0) == 0)
{
auto end_pos = substr.find('}');
if (end_pos != std::string::npos)
{
// Consume first "$" token
value.erase(start_pos, 1);
}
else
{
// There are no "}" chars anymore; just break leaving rest of value untouched.
break;
}
start_pos++; // consume the second '$' token
}
else
{
start_pos += substr.length();
}
start_pos = value.find("$", start_pos);
}
scalar = value;
})(m_root);
}
/**
* Key is a string representing a node in the YAML document.
* The provided key string can navigate the document in its