Repository: falcosecurity/falco Branch: master Commit: 07d627a8e6a6 Files: 276 Total size: 1.7 MB Directory structure: gitextract_4qc7e9o1/ ├── .circleci/ │ └── OWNERS ├── .clang-format ├── .clang-format-ignore ├── .cmake-format.json ├── .codespellignore ├── .dockerignore ├── .git-blame-ignore-revs ├── .github/ │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ ├── release_template.md │ └── workflows/ │ ├── bump-libs.yaml │ ├── ci.yml │ ├── codeql.yaml │ ├── codespell.yml │ ├── engine-version-weakcheck.yaml │ ├── format.yaml │ ├── insecure-api.yaml │ ├── master.yaml │ ├── release.yaml │ ├── reusable_build_dev.yaml │ ├── reusable_build_docker.yaml │ ├── reusable_build_packages.yaml │ ├── reusable_fetch_version.yaml │ ├── reusable_publish_docker.yaml │ ├── reusable_publish_packages.yaml │ ├── reusable_test_packages.yaml │ ├── scorecard.yaml │ └── staticanalysis.yaml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── .yamllint.conf ├── ADOPTERS.md ├── CHANGELOG.md ├── CMakeLists.txt ├── COPYING ├── Contributing.md ├── LICENSE ├── Makefile ├── OWNERS ├── README.md ├── RELEASE.md ├── brand/ │ └── README.md ├── cmake/ │ ├── cpack/ │ │ ├── CMakeCPackOptions.cmake │ │ └── debian/ │ │ └── conffiles │ └── modules/ │ ├── CPackConfig.cmake │ ├── CompilerFlags.cmake │ ├── Coverage.cmake │ ├── cares.cmake │ ├── copy_files_to_build_dir.cmake │ ├── cpp-httplib.cmake │ ├── curl.cmake │ ├── cxxopts.cmake │ ├── driver-repo/ │ │ └── CMakeLists.txt │ ├── driver.cmake │ ├── falco-version.cmake │ ├── falcoctl.cmake │ ├── falcosecurity-libs-repo/ │ │ └── CMakeLists.txt │ ├── falcosecurity-libs.cmake │ ├── gperftools.cmake │ ├── jemalloc.cmake │ ├── mimalloc.cmake │ ├── njson.cmake │ ├── openssl.cmake │ ├── rules.cmake │ ├── static-analysis.cmake │ └── yaml-cpp.cmake ├── config/ │ ├── falco.container_plugin.yaml │ └── falco.iso8601_timeformat.yaml ├── docker/ │ ├── OWNERS │ ├── README.md │ ├── docker-compose/ │ │ ├── README.md │ │ ├── config/ │ │ │ └── http_output.yml │ │ └── docker-compose.yaml │ ├── driver-loader/ │ │ ├── Dockerfile │ │ └── docker-entrypoint.sh │ ├── driver-loader-buster/ │ │ ├── Dockerfile │ │ └── docker-entrypoint.sh │ ├── falco/ │ │ └── Dockerfile │ └── falco-debian/ │ └── Dockerfile ├── falco.yaml ├── proposals/ │ ├── 20190826-grpc-outputs.md │ ├── 20190909-psp-rules-support.md │ ├── 20191030-api.md │ ├── 20191217-rules-naming-convention.md │ ├── 20200506-artifacts-scope-part-1.md │ ├── 20200506-artifacts-scope-part-2.md │ ├── 20200818-artifacts-storage.md │ ├── 20200828-structured-exception-handling.md │ ├── 20200901-artifacts-cleanup.md │ ├── 20201025-drivers-storage-s3.md │ ├── 20210119-libraries-contribution.md │ ├── 20210501-plugin-system.md │ ├── 20221129-artifacts-distribution.md │ ├── 20230511-roadmap-management.md │ ├── 20230620-anomaly-detection-framework.md │ ├── 20231220-features-adoption-and-deprecation.md │ ├── 20251205-multi-thread-falco-design.md │ └── 20251215-legacy-bpf-grpc-output-gvisor-engine-deprecation.md ├── scripts/ │ ├── CMakeLists.txt │ ├── debian/ │ │ ├── postinst.in │ │ ├── postrm.in │ │ └── prerm.in │ ├── description.txt │ ├── falcoctl/ │ │ └── falcoctl.yaml.in │ ├── publish-bin │ ├── publish-deb │ ├── publish-rpm │ ├── publish-wasm │ ├── rpm/ │ │ ├── postinstall.in │ │ ├── postuninstall.in │ │ └── preuninstall.in │ └── systemd/ │ ├── falco-custom.service │ ├── falco-kmod-inject.service │ ├── falco-kmod.service │ ├── falco-modern-bpf.service │ └── falcoctl-artifact-follow.service ├── semgrep/ │ ├── insecure-api-gets.yaml │ ├── insecure-api-sprintf-vsprintf.yaml │ ├── insecure-api-strcpy-stpcpy-strcat.yaml │ └── insecure-api-strn.yaml ├── tools/ │ └── local_hooks/ │ └── dco-pre-commit-msg.sh ├── unit_tests/ │ ├── CMakeLists.txt │ ├── README.md │ ├── engine/ │ │ ├── test_add_source.cpp │ │ ├── test_alt_rule_loader.cpp │ │ ├── test_enable_rule.cpp │ │ ├── test_extra_output.cpp │ │ ├── test_falco_utils.cpp │ │ ├── test_filter_details_resolver.cpp │ │ ├── test_filter_macro_resolver.cpp │ │ ├── test_filter_warning_resolver.cpp │ │ ├── test_plugin_requirements.cpp │ │ ├── test_rule_loader.cpp │ │ └── test_rulesets.cpp │ ├── falco/ │ │ ├── app/ │ │ │ └── actions/ │ │ │ ├── app_action_helpers.h │ │ │ ├── test_configure_interesting_sets.cpp │ │ │ ├── test_configure_syscall_buffer_num.cpp │ │ │ ├── test_load_config.cpp │ │ │ └── test_select_event_sources.cpp │ │ ├── test_atomic_signal_handler.cpp │ │ ├── test_capture.cpp │ │ ├── test_configs/ │ │ │ ├── engine_kmod_config.yaml │ │ │ └── engine_modern_config.yaml │ │ ├── test_configuration.cpp │ │ ├── test_configuration_config_files.cpp │ │ ├── test_configuration_env_vars.cpp │ │ ├── test_configuration_output_options.cpp │ │ ├── test_configuration_rule_selection.cpp │ │ └── test_configuration_schema.cpp │ ├── falco_test_var.h.in │ ├── test_falco_engine.cpp │ └── test_falco_engine.h └── userspace/ ├── engine/ │ ├── CMakeLists.txt │ ├── OWNERS │ ├── evttype_index_ruleset.cpp │ ├── evttype_index_ruleset.h │ ├── falco_common.cpp │ ├── falco_common.h │ ├── falco_engine.cpp │ ├── falco_engine.h │ ├── falco_engine_version.h │ ├── falco_load_result.cpp │ ├── falco_load_result.h │ ├── falco_rule.h │ ├── falco_source.h │ ├── falco_utils.cpp │ ├── falco_utils.h │ ├── field_formatter.cpp │ ├── field_formatter.h │ ├── filter_details_resolver.cpp │ ├── filter_details_resolver.h │ ├── filter_macro_resolver.cpp │ ├── filter_macro_resolver.h │ ├── filter_ruleset.cpp │ ├── filter_ruleset.h │ ├── filter_warning_resolver.cpp │ ├── filter_warning_resolver.h │ ├── formats.cpp │ ├── formats.h │ ├── indexable_ruleset.h │ ├── indexed_vector.h │ ├── logger.cpp │ ├── logger.h │ ├── output_format.h │ ├── rule_json_schema.h │ ├── rule_loader.cpp │ ├── rule_loader.h │ ├── rule_loader_collector.cpp │ ├── rule_loader_collector.h │ ├── rule_loader_compile_output.h │ ├── rule_loader_compiler.cpp │ ├── rule_loader_compiler.h │ ├── rule_loader_reader.cpp │ ├── rule_loader_reader.h │ ├── rule_loading_messages.h │ ├── stats_manager.cpp │ ├── stats_manager.h │ └── yaml_helper.h └── falco/ ├── CMakeLists.txt ├── app/ │ ├── actions/ │ │ ├── actions.h │ │ ├── cleanup_outputs.cpp │ │ ├── close_inspectors.cpp │ │ ├── configure_interesting_sets.cpp │ │ ├── configure_syscall_buffer_num.cpp │ │ ├── configure_syscall_buffer_size.cpp │ │ ├── create_signal_handlers.cpp │ │ ├── event_formatter.cpp │ │ ├── event_formatter.h │ │ ├── helpers.h │ │ ├── helpers_generic.cpp │ │ ├── helpers_inspector.cpp │ │ ├── init_falco_engine.cpp │ │ ├── init_inspectors.cpp │ │ ├── init_outputs.cpp │ │ ├── list_fields.cpp │ │ ├── list_plugins.cpp │ │ ├── load_config.cpp │ │ ├── load_plugins.cpp │ │ ├── load_rules_files.cpp │ │ ├── pidfile.cpp │ │ ├── print_config_schema.cpp │ │ ├── print_generated_gvisor_config.cpp │ │ ├── print_help.cpp │ │ ├── print_ignored_events.cpp │ │ ├── print_kernel_version.cpp │ │ ├── print_page_size.cpp │ │ ├── print_plugin_info.cpp │ │ ├── print_rule_schema.cpp │ │ ├── print_support.cpp │ │ ├── print_syscall_events.cpp │ │ ├── print_version.cpp │ │ ├── process_events.cpp │ │ ├── select_event_sources.cpp │ │ ├── start_webserver.cpp │ │ └── validate_rules_files.cpp │ ├── app.cpp │ ├── app.h │ ├── options.cpp │ ├── options.h │ ├── restart_handler.cpp │ ├── restart_handler.h │ ├── run_result.h │ ├── signals.h │ └── state.h ├── atomic_signal_handler.h ├── config_falco.h.in ├── config_json_schema.h ├── configuration.cpp ├── configuration.h ├── event_drops.cpp ├── event_drops.h ├── falco.cpp ├── falco_metrics.cpp ├── falco_metrics.h ├── falco_outputs.cpp ├── falco_outputs.h ├── falco_semaphore.h ├── outputs.h ├── outputs_file.cpp ├── outputs_file.h ├── outputs_http.cpp ├── outputs_http.h ├── outputs_program.cpp ├── outputs_program.h ├── outputs_stdout.cpp ├── outputs_stdout.h ├── outputs_syslog.cpp ├── outputs_syslog.h ├── stats_writer.cpp ├── stats_writer.h ├── versions_info.cpp ├── versions_info.h ├── watchdog.h ├── webserver.cpp └── webserver.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/OWNERS ================================================ emeritus_approvers: - jonahjon ================================================ FILE: .clang-format ================================================ Language: Cpp BasedOnStyle: Google AccessModifierOffset: -4 BreakBeforeBraces: Attach AllowAllArgumentsOnNextLine: false AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false BinPackArguments: false BinPackParameters: false ColumnLimit: 100 DerivePointerBinding: false IndentCaseLabels: false IndentWidth: 4 SpaceAfterTemplateKeyword: false TabWidth: 4 UseTab: ForIndentation BreakConstructorInitializers: AfterColon ConstructorInitializerIndentWidth: 8 ContinuationIndentWidth: 8 DerivePointerAlignment: true SortIncludes: Never SpaceBeforeCtorInitializerColon: false SpaceBeforeParens: Never InsertNewlineAtEOF: true --- Language: Proto DisableFormat: true --- Language: JavaScript DisableFormat: true --- Language: Java DisableFormat: true ================================================ FILE: .clang-format-ignore ================================================ # These files contain some JSON schema definitions that are not C++ code userspace/falco/config_json_schema.h userspace/engine/rule_json_schema.h ================================================ FILE: .cmake-format.json ================================================ { "_help_format": "Options affecting formatting.", "format": { "_help_disable": [ "Disable formatting entirely, making cmake-format a no-op" ], "disable": false, "_help_line_width": [ "How wide to allow formatted cmake files" ], "line_width": 100, "_help_tab_size": [ "How many spaces to tab for indent" ], "tab_size": 4, "_help_use_tabchars": [ "If true, lines are indented using tab characters (utf-8", "0x09) instead of space characters (utf-8 0x20).", "In cases where the layout would require a fractional tab", "character, the behavior of the fractional indentation is", "governed by " ], "use_tabchars": true, "_help_fractional_tab_policy": [ "If is True, then the value of this variable", "indicates how fractional indentions are handled during", "whitespace replacement. If set to 'use-space', fractional", "indentation is left as spaces (utf-8 0x20). If set to", "`round-up` fractional indentation is replaced with a single", "tab character (utf-8 0x09) effectively shifting the column", "to the next tabstop" ], "fractional_tab_policy": "use-space", "_help_max_subgroups_hwrap": [ "If an argument group contains more than this many sub-groups", "(parg or kwarg groups) then force it to a vertical layout." ], "max_subgroups_hwrap": 2, "_help_max_pargs_hwrap": [ "If a positional argument group contains more than this many", "arguments, then force it to a vertical layout." ], "max_pargs_hwrap": 6, "_help_max_rows_cmdline": [ "If a cmdline positional group consumes more than this many", "lines without nesting, then invalidate the layout (and nest)" ], "max_rows_cmdline": 2, "_help_separate_ctrl_name_with_space": [ "If true, separate flow control names from their parentheses", "with a space" ], "separate_ctrl_name_with_space": false, "_help_separate_fn_name_with_space": [ "If true, separate function names from parentheses with a", "space" ], "separate_fn_name_with_space": false, "_help_dangle_parens": [ "If a statement is wrapped to more than one line, than dangle", "the closing parenthesis on its own line." ], "dangle_parens": true, "_help_dangle_align": [ "If the trailing parenthesis must be 'dangled' on its on", "line, then align it to this reference: `prefix`: the start", "of the statement, `prefix-indent`: the start of the", "statement, plus one indentation level, `child`: align to", "the column of the arguments" ], "dangle_align": "prefix", "_help_min_prefix_chars": [ "If the statement spelling length (including space and", "parenthesis) is smaller than this amount, then force reject", "nested layouts." ], "min_prefix_chars": 4, "_help_max_prefix_chars": [ "If the statement spelling length (including space and", "parenthesis) is larger than the tab width by more than this", "amount, then force reject un-nested layouts." ], "max_prefix_chars": 10, "_help_max_lines_hwrap": [ "If a candidate layout is wrapped horizontally but it exceeds", "this many lines, then reject the layout." ], "max_lines_hwrap": 2, "_help_line_ending": [ "What style line endings to use in the output." ], "line_ending": "unix", "_help_command_case": [ "Format command names consistently as 'lower' or 'upper' case" ], "command_case": "canonical", "_help_keyword_case": [ "Format keywords consistently as 'lower' or 'upper' case" ], "keyword_case": "unchanged", "_help_always_wrap": [ "A list of command names which should always be wrapped" ], "always_wrap": [], "_help_enable_sort": [ "If true, the argument lists which are known to be sortable", "will be sorted lexicographicall" ], "enable_sort": true, "_help_autosort": [ "If true, the parsers may infer whether or not an argument", "list is sortable (without annotation)." ], "autosort": false, "_help_require_valid_layout": [ "By default, if cmake-format cannot successfully fit", "everything into the desired linewidth it will apply the", "last, most agresive attempt that it made. If this flag is", "True, however, cmake-format will print error, exit with non-", "zero status code, and write-out nothing" ], "require_valid_layout": false, "_help_layout_passes": [ "A dictionary mapping layout nodes to a list of wrap", "decisions. See the documentation for more information." ], "layout_passes": {} }, "_help_markup": "Options affecting comment reflow and formatting.", "markup": { "_help_bullet_char": [ "What character to use for bulleted lists" ], "bullet_char": "*", "_help_enum_char": [ "What character to use as punctuation after numerals in an", "enumerated list" ], "enum_char": ".", "_help_first_comment_is_literal": [ "If comment markup is enabled, don't reflow the first comment", "block in each listfile. Use this to preserve formatting of", "your copyright/license statements." ], "first_comment_is_literal": false, "_help_literal_comment_pattern": [ "If comment markup is enabled, don't reflow any comment block", "which matches this (regex) pattern. Default is `None`", "(disabled)." ], "literal_comment_pattern": null, "_help_fence_pattern": [ "Regular expression to match preformat fences in comments", "default= ``r'^\\s*([`~]{3}[`~]*)(.*)$'``" ], "fence_pattern": "^\\s*([`~]{3}[`~]*)(.*)$", "_help_ruler_pattern": [ "Regular expression to match rulers in comments default=", "``r'^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$'``" ], "ruler_pattern": "^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$", "_help_explicit_trailing_pattern": [ "If a comment line matches starts with this pattern then it", "is explicitly a trailing comment for the preceding", "argument. Default is '#<'" ], "explicit_trailing_pattern": "#<", "_help_hashruler_min_length": [ "If a comment line starts with at least this many consecutive", "hash characters, then don't lstrip() them off. This allows", "for lazy hash rulers where the first hash char is not", "separated by space" ], "hashruler_min_length": 10, "_help_canonicalize_hashrulers": [ "If true, then insert a space between the first hash char and", "remaining hash chars in a hash ruler, and normalize its", "length to fill the column" ], "canonicalize_hashrulers": true, "_help_enable_markup": [ "enable comment markup parsing and reflow" ], "enable_markup": true }, "_help_lint": "Options affecting the linter", "lint": { "_help_disabled_codes": [ "a list of lint codes to disable" ], "disabled_codes": [], "_help_function_pattern": [ "regular expression pattern describing valid function names" ], "function_pattern": "[0-9a-z_]+", "_help_macro_pattern": [ "regular expression pattern describing valid macro names" ], "macro_pattern": "[0-9A-Z_]+", "_help_global_var_pattern": [ "regular expression pattern describing valid names for", "variables with global (cache) scope" ], "global_var_pattern": "[A-Z][0-9A-Z_]+", "_help_internal_var_pattern": [ "regular expression pattern describing valid names for", "variables with global scope (but internal semantic)" ], "internal_var_pattern": "_[A-Z][0-9A-Z_]+", "_help_local_var_pattern": [ "regular expression pattern describing valid names for", "variables with local scope" ], "local_var_pattern": "[a-z][a-z0-9_]+", "_help_private_var_pattern": [ "regular expression pattern describing valid names for", "privatedirectory variables" ], "private_var_pattern": "_[0-9a-z_]+", "_help_public_var_pattern": [ "regular expression pattern describing valid names for public", "directory variables" ], "public_var_pattern": "[A-Z][0-9A-Z_]+", "_help_argument_var_pattern": [ "regular expression pattern describing valid names for", "function/macro arguments and loop variables." ], "argument_var_pattern": "[a-z][a-z0-9_]+", "_help_keyword_pattern": [ "regular expression pattern describing valid names for", "keywords used in functions or macros" ], "keyword_pattern": "[A-Z][0-9A-Z_]+", "_help_max_conditionals_custom_parser": [ "In the heuristic for C0201, how many conditionals to match", "within a loop in before considering the loop a parser." ], "max_conditionals_custom_parser": 2, "_help_min_statement_spacing": [ "Require at least this many newlines between statements" ], "min_statement_spacing": 1, "_help_max_statement_spacing": [ "Require no more than this many newlines between statements" ], "max_statement_spacing": 2, "max_returns": 6, "max_branches": 12, "max_arguments": 5, "max_localvars": 15, "max_statements": 50 } } ================================================ FILE: .codespellignore ================================================ aks creat chage ro ================================================ FILE: .dockerignore ================================================ * !config/ !docker/ ================================================ FILE: .git-blame-ignore-revs ================================================ # This commit formatted the Falco code for the first time. 50b98b30e588eadce641136da85bc94a60eb6a3d ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ **What type of PR is this?** > Uncomment one (or more) `/kind <>` lines: > /kind bug > /kind cleanup > /kind design > /kind documentation > /kind failing-test > /kind feature > /kind release **Any specific area of the project related to this PR?** > Uncomment one (or more) `/area <>` lines: > /area build > /area engine > /area tests > /area proposals > /area CI **What this PR does / why we need it**: **Which issue(s) this PR fixes**: Fixes # **Special notes for your reviewer**: **Does this PR introduce a user-facing change?**: ```release-note ``` ================================================ FILE: .github/dependabot.yml ================================================ # # Copyright (C) 2023 The Falco Authors. # # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # version: 2 updates: - package-ecosystem: gitsubmodule schedule: interval: "daily" directory: / ================================================ FILE: .github/release_template.md ================================================ [![LIBS](https://img.shields.io/badge/LIBS-LIBSVER-yellow)](https://github.com/falcosecurity/libs/releases/tag/LIBSVER) [![DRIVER](https://img.shields.io/badge/DRIVER-DRIVERVER-yellow)](https://github.com/falcosecurity/libs/releases/tag/DRIVERVER) | Packages | Download | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | rpm-x86_64 | [![rpm](https://img.shields.io/badge/Falco-FALCOVER-%2300aec7?style=flat-square)](https://download.falco.org/packages/rpmFALCOBUCKET/falco-FALCOVER-x86_64.rpm) | | deb-x86_64 | [![deb](https://img.shields.io/badge/Falco-FALCOVER-%2300aec7?style=flat-square)](https://download.falco.org/packages/debFALCOBUCKET/stable/falco-FALCOVER-x86_64.deb) | | tgz-x86_64 | [![tgz](https://img.shields.io/badge/Falco-FALCOVER-%2300aec7?style=flat-square)](https://download.falco.org/packages/binFALCOBUCKET/x86_64/falco-FALCOVER-x86_64.tar.gz) | | tgz-static-x86_64 | [![tgz-static](https://img.shields.io/badge/Falco-FALCOVER-%2300aec7?style=flat-square)](https://download.falco.org/packages/binFALCOBUCKET/x86_64/falco-FALCOVER-static-x86_64.tar.gz) | | rpm-aarch64 | [![rpm](https://img.shields.io/badge/Falco-FALCOVER-%2300aec7?style=flat-square)](https://download.falco.org/packages/rpmFALCOBUCKET/falco-FALCOVER-aarch64.rpm) | | deb-aarch64 | [![deb](https://img.shields.io/badge/Falco-FALCOVER-%2300aec7?style=flat-square)](https://download.falco.org/packages/debFALCOBUCKET/stable/falco-FALCOVER-aarch64.deb) | | tgz-aarch64 | [![tgz](https://img.shields.io/badge/Falco-FALCOVER-%2300aec7?style=flat-square)](https://download.falco.org/packages/binFALCOBUCKET/aarch64/falco-FALCOVER-aarch64.tar.gz) | | Images | |---------------------------------------------------------------------------| | `docker pull docker.io/falcosecurity/falco:FALCOVER` | | `docker pull public.ecr.aws/falcosecurity/falco:FALCOVER` | | `docker pull docker.io/falcosecurity/falco-driver-loader:FALCOVER` | | `docker pull docker.io/falcosecurity/falco-driver-loader:FALCOVER-buster` | | `docker pull docker.io/falcosecurity/falco:FALCOVER-debian` | ================================================ FILE: .github/workflows/bump-libs.yaml ================================================ --- name: Bump Libs on: workflow_dispatch: schedule: - cron: '30 6 * * 1' # on each monday 6:30 permissions: contents: read # Checks if any concurrent jobs is running for kernels CI and eventually cancel it. concurrency: group: bump-libs-ci cancel-in-progress: true jobs: bump-libs: runs-on: ubuntu-latest permissions: contents: write pull-requests: write steps: - name: Download libs master tar.gz run: | wget https://github.com/falcosecurity/libs/archive/refs/heads/master.tar.gz - name: Store libs hash and shasum id: store run: | gunzip -c master.tar.gz > master.tar commit=$(cat master.tar | git get-tar-commit-id) echo "COMMIT=$commit" >> "$GITHUB_OUTPUT" wget https://github.com/falcosecurity/libs/archive/$commit.tar.gz echo "SHASUM=$(sha256sum $commit.tar.gz | awk '{print $1}')" >> "$GITHUB_OUTPUT" - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: path: falco - name: Bump libs version and hash run: | cd falco sed -i -E '45s/FALCOSECURITY_LIBS_VERSION "(.+)"/FALCOSECURITY_LIBS_VERSION "${{ steps.store.outputs.COMMIT }}"/' cmake/modules/falcosecurity-libs.cmake sed -i -E '47s/"SHA256=(.+)"/"SHA256=${{ steps.store.outputs.SHASUM }}"/' cmake/modules/falcosecurity-libs.cmake sed -i -E '38s/DRIVER_VERSION "(.+)"/DRIVER_VERSION "${{ steps.store.outputs.COMMIT }}"/' cmake/modules/driver.cmake sed -i -E '40s/"SHA256=(.+)"/"SHA256=${{ steps.store.outputs.SHASUM }}"/' cmake/modules/driver.cmake - name: Create Pull Request uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5 with: path: falco signoff: true base: master branch: update/libs title: 'update(cmake): update libs and driver to latest master' body: | This PR updates libs and driver to latest commit. /kind release /area build ```release-note NONE ``` commit-message: 'update(cmake): update libs and driver to latest master.' token: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI Build on: pull_request: branches: - master - release/* workflow_dispatch: # Checks if any concurrent jobs under the same pull request or branch are being executed # NOTE: this will cancel every workflow that is being ran against a PR as group is just the github ref (without the workflow name) concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true permissions: contents: read jobs: fetch-version: uses: ./.github/workflows/reusable_fetch_version.yaml build-dev-packages-x86_64: needs: [fetch-version] uses: ./.github/workflows/reusable_build_packages.yaml with: arch: x86_64 version: ${{ needs.fetch-version.outputs.version }} enable_debug: true enable_sanitizers: true use_mimalloc: true build-dev-packages-arm64: needs: [fetch-version] uses: ./.github/workflows/reusable_build_packages.yaml with: arch: aarch64 version: ${{ needs.fetch-version.outputs.version }} enable_debug: true use_mimalloc: true test-dev-packages: needs: [fetch-version, build-dev-packages-x86_64] uses: ./.github/workflows/reusable_test_packages.yaml # See https://github.com/falcosecurity/falco/pull/3482 # Since musl build does not support dynamically loaded plugins, # many tests would fail (the ones using `container.foo` fields). # Disable tests on static builds for now. # strategy: # fail-fast: false # matrix: # static: ["static", ""] with: arch: x86_64 # sanitizers: ${{ matrix.static == '' && true || false }} sanitizers: true # static: ${{ matrix.static != '' && true || false }} version: ${{ needs.fetch-version.outputs.version }} test-dev-packages-arm64: needs: [fetch-version, build-dev-packages-arm64] uses: ./.github/workflows/reusable_test_packages.yaml with: arch: aarch64 version: ${{ needs.fetch-version.outputs.version }} build-dev-minimal: uses: ./.github/workflows/reusable_build_dev.yaml with: arch: x86_64 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: " ", # of which is computed by hashing in order the following: # - Driver schema version supported by the built-in falcosecurity/libs # - The supported event types usable in Falco rules (evt.type=xxx) # - The supported rules fields with their name, type, and description build-dev: uses: ./.github/workflows/reusable_build_dev.yaml with: arch: x86_64 git_ref: ${{ github.event.pull_request.head.sha }} minimal: false sanitizers: true build_type: Debug cmd: "echo $(build/userspace/falco/falco -c ./falco.yaml --version | grep 'Engine:' | awk '{print $2}') $(echo $(build/userspace/falco/falco -c ./falco.yaml --version | grep 'Schema version:' | awk '{print $3}') $(build/userspace/falco/falco -c ./falco.yaml --list --markdown | grep '^`' | sort) $(build/userspace/falco/falco -c ./falco.yaml --list-events | sort) | sha256sum)" # checks the falco engine checksum for consistency check-engine-checksum: runs-on: ubuntu-latest needs: [build-dev] steps: - name: Checkout PR head ref uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - name: Check Engine checksum run: | prev_hash=$(grep CHECKSUM "./userspace/engine/falco_engine_version.h" | awk '{print $3}' | sed -e 's/"//g') cur_hash=$(echo "${{ needs.build-dev.outputs.cmdout }}" | cut -d ' ' -f 2) echo "encoded checksum: $prev_hash" echo "current checksum: $cur_hash" if [ $prev_hash != $cur_hash ]; then echo "current engine checksum differs from the one encoded in userspace/engine/falco_engine_version.h" exit 1 else echo "current and encoded engine checksum are matching" fi # checks the falco engine version and enforce bumping when necessary check-engine-version: runs-on: ubuntu-latest needs: [build-dev] steps: - name: Checkout base ref uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 ref: ${{ github.base_ref }} - name: Check Engine version run: | base_hash=$(grep CHECKSUM "./userspace/engine/falco_engine_version.h" | awk '{print $3}' | sed -e 's/"//g') base_engine_ver_major=$(grep ENGINE_VERSION_MAJOR "./userspace/engine/falco_engine_version.h" | head -n 1 | awk '{print $3}' | sed -e 's/(//g' -e 's/)//g') base_engine_ver_minor=$(grep ENGINE_VERSION_MINOR "./userspace/engine/falco_engine_version.h" | head -n 1 | awk '{print $3}' | sed -e 's/(//g' -e 's/)//g') base_engine_ver_patch=$(grep ENGINE_VERSION_PATCH "./userspace/engine/falco_engine_version.h" | head -n 1 | awk '{print $3}' | sed -e 's/(//g' -e 's/)//g') base_engine_ver="${base_engine_ver_major}.${base_engine_ver_minor}.${base_engine_ver_patch}" cur_hash=$(echo "${{ needs.build-dev.outputs.cmdout }}" | cut -d ' ' -f 2) cur_engine_ver=$(echo "${{ needs.build-dev.outputs.cmdout }}" | cut -d ' ' -f 1) echo "baseref checksum: $base_hash" echo "baseref engine version: $base_engine_ver" echo "headref checksum: $cur_hash" echo "headref engine version: $cur_engine_ver" if [ "$base_hash" != "$cur_hash" ]; then echo "engine checksum for baseref and headref differ" if [ "$base_engine_ver" == "$cur_engine_ver" ]; then echo "engine version must be bumped" exit 1 else echo "engine version for baseref and headref differ too, so no bump is required" fi fi ================================================ FILE: .github/workflows/codeql.yaml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ "master" ] pull_request: # The branches below must be a subset of the branches above branches: [ "master" ] permissions: contents: read jobs: analyze: name: Analyze runs-on: ubuntu-22.04 permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'cpp' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - name: Update base image run: sudo apt update -y - name: Install build dependencies run: sudo DEBIAN_FRONTEND=noninteractive apt install libssl-dev libc-ares-dev libyaml-cpp-dev rpm libelf-dev cmake build-essential libcurl4-openssl-dev linux-headers-$(uname -r) clang llvm git -y - name: Prepare project run: | cmake -B build -S . -DBUILD_FALCO_MODERN_BPF=Off -DUSE_BUNDLED_DEPS=Off -DUSE_BUNDLED_NLOHMANN_JSON=On -DUSE_BUNDLED_CXXOPTS=On -DUSE_BUNDLED_CPPHTTPLIB=On - name: Build run: | KERNELDIR=/lib/modules/$(uname -r)/build cmake --build build -j4 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 ================================================ FILE: .github/workflows/codespell.yml ================================================ name: Codespell on: pull_request: permissions: contents: read jobs: codespell: runs-on: ubuntu-latest steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: codespell-project/actions-codespell@94259cd8be02ad2903ba34a22d9c13de21a74461 # v2.0 with: skip: .git ignore_words_file: .codespellignore check_filenames: true check_hidden: false ================================================ FILE: .github/workflows/engine-version-weakcheck.yaml ================================================ # NOTE: it is UNSAFE to run ANY kind of script when using the pull_request_target trigger! # DO NOT TOUCH THIS FILE UNLESS THE TRIGGER IS CHANGED. # See warning in https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. name: Engine version checks (weak) on: pull_request_target: paths: - 'userspace/engine/*.cpp' - 'userspace/engine/*.h' permissions: contents: read jobs: paths-filter: runs-on: ubuntu-latest outputs: engine_version_changed: ${{ steps.filter.outputs.engine_version }} steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: filter with: filters: | engine_version: - 'userspace/engine/falco_engine_version.h' check-engine-version-weak: runs-on: ubuntu-22.04 permissions: pull-requests: write needs: paths-filter if: needs.paths-filter.outputs.engine_version_changed == 'false' steps: - name: Check driver Falco engine version uses: mshick/add-pr-comment@7c0890544fb33b0bdd2e59467fbacb62e028a096 # v2.8.1 with: message: | This PR may bring feature or behavior changes in the Falco engine and may require the engine version to be bumped. Please double check **userspace/engine/falco_engine_version.h** file. See [versioning for FALCO_ENGINE_VERSION](https://github.com/falcosecurity/falco/blob/master/RELEASE.md#falco-repo-this-repo). /hold ================================================ FILE: .github/workflows/format.yaml ================================================ name: Format code on: pull_request: push: branches: - master - "release/**" permissions: contents: read jobs: format: name: format code 🐲 runs-on: ubuntu-22.04 steps: - name: Checkout repository 🎉 uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Install deps ⛓️ run: | sudo apt update -y sudo apt install -y --no-install-recommends ca-certificates pip git pip install pre-commit - name: Run pre-commit ©️ run: | pre-commit run --all-files - name: Generate the git-diff 🚒 if: failure() run: git diff > format_diff.patch - name: Upload the git diff artifact 📦 if: failure() uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: format_diff.patch path: ./format_diff.patch ================================================ FILE: .github/workflows/insecure-api.yaml ================================================ name: Insecure API check on: pull_request: branches: - master - 'release/**' - 'maintainers/**' permissions: contents: read jobs: insecure-api: name: check-insecure-api runs-on: ubuntu-latest container: image: semgrep/semgrep:1.85.0@sha256:b4c2272e0a2e59ca551ff96d3bbae657bd2b7356e339af557b27a96d9e751544 steps: - name: Checkout Falco ⤵️ uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Scan PR for insecure API usage 🕵️ run: | semgrep scan \ --error \ --metrics=off \ --baseline-commit ${{ github.event.pull_request.base.sha }} \ --config=./semgrep ================================================ FILE: .github/workflows/master.yaml ================================================ name: Dev Packages and Docker images on: push: branches: [master] permissions: contents: read # Checks if any concurrent jobs is running for master CI and eventually cancel it concurrency: group: ci-master cancel-in-progress: true jobs: fetch-version: uses: ./.github/workflows/reusable_fetch_version.yaml build-dev-packages: needs: [fetch-version] uses: ./.github/workflows/reusable_build_packages.yaml with: arch: x86_64 version: ${{ needs.fetch-version.outputs.version }} secrets: inherit build-dev-packages-arm64: needs: [fetch-version] uses: ./.github/workflows/reusable_build_packages.yaml with: arch: aarch64 version: ${{ needs.fetch-version.outputs.version }} secrets: inherit test-dev-packages: needs: [fetch-version, build-dev-packages] uses: ./.github/workflows/reusable_test_packages.yaml # See https://github.com/falcosecurity/falco/pull/3482 # Since musl build does not support dynamically loaded plugins, # many tests would fail (the ones using `container.foo` fields). # Disable tests on static builds for now. # strategy: # fail-fast: false # matrix: # static: ["static", ""] with: arch: x86_64 # static: ${{ matrix.static != '' && true || false }} version: ${{ needs.fetch-version.outputs.version }} test-dev-packages-arm64: needs: [fetch-version, build-dev-packages-arm64] uses: ./.github/workflows/reusable_test_packages.yaml with: arch: aarch64 version: ${{ needs.fetch-version.outputs.version }} publish-dev-packages: needs: [fetch-version, test-dev-packages, test-dev-packages-arm64] uses: ./.github/workflows/reusable_publish_packages.yaml with: bucket_suffix: '-dev' version: ${{ needs.fetch-version.outputs.version }} secrets: inherit build-dev-docker: needs: [fetch-version, publish-dev-packages] uses: ./.github/workflows/reusable_build_docker.yaml with: arch: x86_64 bucket_suffix: '-dev' version: ${{ needs.fetch-version.outputs.version }} tag: master secrets: inherit build-dev-docker-arm64: needs: [fetch-version, publish-dev-packages] uses: ./.github/workflows/reusable_build_docker.yaml with: arch: aarch64 bucket_suffix: '-dev' version: ${{ needs.fetch-version.outputs.version }} tag: master secrets: inherit publish-dev-docker: needs: [fetch-version, build-dev-docker, build-dev-docker-arm64] uses: ./.github/workflows/reusable_publish_docker.yaml with: tag: master secrets: inherit ================================================ FILE: .github/workflows/release.yaml ================================================ name: Release Packages and Docker images on: release: types: [published] permissions: contents: read # Checks if any concurrent jobs is running for release CI and eventually cancel it. concurrency: group: ci-release cancel-in-progress: true jobs: release-settings: runs-on: ubuntu-latest outputs: is_latest: ${{ steps.get_settings.outputs.is_latest }} bucket_suffix: ${{ steps.get_settings.outputs.bucket_suffix }} steps: - name: Get latest release uses: rez0n/actions-github-release@27a57820ee808f8fd940c8a9d1f7188f854aa2b5 # v2.0 id: latest_release env: token: ${{ secrets.GITHUB_TOKEN }} repository: ${{ github.repository }} type: "stable" - name: Get settings for this release id: get_settings shell: python run: | import os import re import sys semver_no_meta = '''^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?$''' tag_name = '${{ github.event.release.tag_name }}' is_valid_version = re.match(semver_no_meta, tag_name) is not None if not is_valid_version: print(f'Release version {tag_name} is not a valid full or pre-release. See RELEASE.md for more information.') sys.exit(1) is_prerelease = '-' in tag_name # Safeguard: you need to both set "latest" in GH and not have suffixes to overwrite latest is_latest = '${{ steps.latest_release.outputs.release }}' == tag_name and not is_prerelease bucket_suffix = '-dev' if is_prerelease else '' with open(os.environ['GITHUB_OUTPUT'], 'a') as ofp: print(f'is_latest={is_latest}'.lower(), file=ofp) print(f'bucket_suffix={bucket_suffix}', file=ofp) build-packages: needs: [release-settings] uses: ./.github/workflows/reusable_build_packages.yaml with: arch: x86_64 version: ${{ github.event.release.tag_name }} use_jemalloc: true secrets: inherit build-packages-arm64: needs: [release-settings] uses: ./.github/workflows/reusable_build_packages.yaml with: arch: aarch64 version: ${{ github.event.release.tag_name }} use_jemalloc: true secrets: inherit test-packages: needs: [release-settings, build-packages] uses: ./.github/workflows/reusable_test_packages.yaml # See https://github.com/falcosecurity/falco/pull/3482 # Since musl build does not support dynamically loaded plugins, # many tests would fail (the ones using `container.foo` fields). # Disable tests on static builds for now. # strategy: # fail-fast: false # matrix: # static: ["static", ""] with: arch: x86_64 # static: ${{ matrix.static != '' && true || false }} version: ${{ github.event.release.tag_name }} test-packages-arm64: needs: [release-settings, build-packages-arm64] uses: ./.github/workflows/reusable_test_packages.yaml with: arch: aarch64 version: ${{ github.event.release.tag_name }} publish-packages: needs: [release-settings, test-packages, test-packages-arm64] uses: ./.github/workflows/reusable_publish_packages.yaml with: bucket_suffix: ${{ needs.release-settings.outputs.bucket_suffix }} version: ${{ github.event.release.tag_name }} secrets: inherit # Both build-docker and its arm64 counterpart require build-packages because they use its output build-docker: needs: [release-settings, build-packages, publish-packages] uses: ./.github/workflows/reusable_build_docker.yaml with: arch: x86_64 bucket_suffix: ${{ needs.release-settings.outputs.bucket_suffix }} version: ${{ github.event.release.tag_name }} tag: ${{ github.event.release.tag_name }} secrets: inherit build-docker-arm64: needs: [release-settings, build-packages, publish-packages] uses: ./.github/workflows/reusable_build_docker.yaml with: arch: aarch64 bucket_suffix: ${{ needs.release-settings.outputs.bucket_suffix }} version: ${{ github.event.release.tag_name }} tag: ${{ github.event.release.tag_name }} secrets: inherit publish-docker: needs: [release-settings, build-docker, build-docker-arm64] uses: ./.github/workflows/reusable_publish_docker.yaml secrets: inherit with: is_latest: ${{ needs.release-settings.outputs.is_latest == 'true' }} tag: ${{ github.event.release.tag_name }} sign: true release-body: needs: [release-settings, publish-docker] if: ${{ needs.release-settings.outputs.is_latest == 'true' }} # only for latest releases permissions: contents: write runs-on: ubuntu-latest steps: - name: Clone repo uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Extract LIBS and DRIVER versions run: | cp .github/release_template.md release-body.md LIBS_VERS=$(cat cmake/modules/falcosecurity-libs.cmake | grep 'set(FALCOSECURITY_LIBS_VERSION' | tail -n1 | grep -o '[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*') DRIVER_VERS=$(cat cmake/modules/driver.cmake | grep 'set(DRIVER_VERSION' | tail -n1 | grep -o '[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*+driver') sed -i s/LIBSVER/$LIBS_VERS/g release-body.md sed -i s/DRIVERVER/$DRIVER_VERS/g release-body.md - name: Append release matrixes run: | sed -i s/FALCOBUCKET/${{ needs.release-settings.outputs.bucket_suffix }}/g release-body.md sed -i s/FALCOVER/${{ github.event.release.tag_name }}/g release-body.md - name: Generate release notes uses: leodido/rn2md@9c351d81278644c0e17b1ca68edbdba305276c73 with: milestone: ${{ github.event.release.tag_name }} output: ./notes.md - name: Merge release notes to pre existent body run: cat notes.md >> release-body.md - name: Attach release creator to release body run: | echo "" >> release-body.md echo "#### Release Manager @${{ github.event.release.author.login }}" >> release-body.md - name: Download debug symbols for Falco x86_64 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ github.event.release.tag_name }}-x86_64.debug - name: Rename x86_64 debug symbols run: mv falco.debug falco-x86_64.debug - name: Download debug symbols for Falco aarch64 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ github.event.release.tag_name }}-aarch64.debug - name: Rename aarch64 debug symbols run: mv falco.debug falco-aarch64.debug - name: Release uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15 with: body_path: ./release-body.md tag_name: ${{ github.event.release.tag_name }} name: ${{ github.event.release.name }} files: | falco-x86_64.debug falco-aarch64.debug ================================================ FILE: .github/workflows/reusable_build_dev.yaml ================================================ # This is a reusable workflow used by the master CI on: workflow_call: outputs: cmdout: description: "Post-build command output" value: ${{ jobs.build-and-test.outputs.cmdout }} inputs: arch: description: x86_64 or aarch64 required: true type: string minimal: description: Minimal build required: true type: boolean sanitizers: description: Enable sanitizer support required: false default: false type: boolean build_type: description: One of 'Debug' or 'Release' required: true type: string git_ref: description: Git ref used for checking out the code required: true type: string cmd: description: If defined, this command is executed after a successful build and its output is set in the `cmdout` output required: false default: '' type: string permissions: contents: read jobs: build-and-test: # See https://github.com/actions/runner/issues/409#issuecomment-1158849936 runs-on: ${{ (inputs.arch == 'aarch64' && 'ubuntu-22.04-arm') || 'ubuntu-22.04' }} outputs: cmdout: ${{ steps.run_cmd.outputs.out }} steps: - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 ref: ${{ inputs.git_ref }} - name: Update base image run: sudo apt update -y - name: Install build dependencies 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 libc-ares-dev rpm libcurl4-openssl-dev linux-headers-$(uname -r) clang llvm -y - name: Prepare project run: | cmake -B build -S .\ -DBUILD_FALCO_UNIT_TESTS=On \ -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} \ -DBUILD_FALCO_MODERN_BPF=Off \ -DBUILD_DRIVER=${{ inputs.minimal == true && 'OFF' || 'ON' }} \ -DMINIMAL_BUILD=${{ inputs.minimal == true && 'ON' || 'OFF' }} \ -DUSE_ASAN=${{ inputs.sanitizers == true && 'ON' || 'OFF' }} \ -DUSE_UBSAN=${{ inputs.sanitizers == true && 'ON' || 'OFF' }} \ -DUSE_BUNDLED_DEPS=Off \ -DUSE_BUNDLED_NLOHMANN_JSON=On \ -DUSE_BUNDLED_CXXOPTS=On \ -DUSE_BUNDLED_CPPHTTPLIB=On \ - name: Build run: | KERNELDIR=/lib/modules/$(uname -r)/build cmake --build build -j4 - name: Run unit tests run: | pushd build sudo ./unit_tests/falco_unit_tests popd - name: Run command id: run_cmd if: inputs.cmd != '' run: | OUT=$(${{ inputs.cmd }}) echo "out=${OUT}" >> $GITHUB_OUTPUT ================================================ FILE: .github/workflows/reusable_build_docker.yaml ================================================ # This is a reusable workflow used by master and release CI on: workflow_call: inputs: arch: description: x86_64 or aarch64 required: true type: string bucket_suffix: description: bucket suffix for packages required: false default: '' type: string version: description: The Falco version to use when building images required: true type: string tag: description: The tag to use (e.g. "master" or "0.35.0") required: true type: string # Here we just build all docker images as tarballs, # then we upload all the tarballs to be later downloaded by reusable_publish_docker workflow. # In this way, we don't need to publish any arch specific image, # and this "build" workflow is actually only building images. permissions: contents: read jobs: build-docker: # See https://github.com/actions/runner/issues/409#issuecomment-1158849936 runs-on: ${{ (inputs.arch == 'aarch64' && 'ubuntu-22.04-arm') || 'ubuntu-latest' }} env: TARGETARCH: ${{ (inputs.arch == 'aarch64' && 'arm64') || 'amd64' }} steps: - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - name: Build falco image run: | docker build -f docker/falco/Dockerfile -t docker.io/falcosecurity/falco:${{ inputs.arch }}-${{ inputs.tag }} \ --build-arg VERSION_BUCKET=bin${{ inputs.bucket_suffix }} \ --build-arg FALCO_VERSION=${{ inputs.version }} \ --build-arg FALCO_COMMIT_SHA=${{ github.sha }} \ --build-arg TARGETARCH=${TARGETARCH} \ . docker save docker.io/falcosecurity/falco:${{ inputs.arch }}-${{ inputs.tag }} --output /tmp/falco-${{ inputs.arch }}.tar - name: Build falco-debian image run: | docker build -f docker/falco-debian/Dockerfile -t docker.io/falcosecurity/falco:${{ inputs.arch }}-${{ inputs.tag }}-debian \ --build-arg VERSION_BUCKET=deb${{ inputs.bucket_suffix }} \ --build-arg FALCO_VERSION=${{ inputs.version }} \ --build-arg FALCO_COMMIT_SHA=${{ github.sha }} \ --build-arg TARGETARCH=${TARGETARCH} \ . docker save docker.io/falcosecurity/falco:${{ inputs.arch }}-${{ inputs.tag }}-debian --output /tmp/falco-${{ inputs.arch }}-debian.tar - name: Build falco-driver-loader image run: | docker build -f docker/driver-loader/Dockerfile -t docker.io/falcosecurity/falco-driver-loader:${{ inputs.arch }}-${{ inputs.tag }} \ --build-arg FALCO_IMAGE_TAG=${{ inputs.arch }}-${{ inputs.tag }} \ --build-arg FALCO_VERSION=${{ inputs.version }} \ --build-arg FALCO_COMMIT_SHA=${{ github.sha }} \ --build-arg TARGETARCH=${TARGETARCH} \ . docker save docker.io/falcosecurity/falco-driver-loader:${{ inputs.arch }}-${{ inputs.tag }} --output /tmp/falco-driver-loader-${{ inputs.arch }}.tar - name: Build falco-driver-loader-buster image run: | docker build -f docker/driver-loader-buster/Dockerfile -t docker.io/falcosecurity/falco-driver-loader:${{ inputs.arch }}-${{ inputs.tag }}-buster \ --build-arg VERSION_BUCKET=deb${{ inputs.bucket_suffix }} \ --build-arg FALCO_VERSION=${{ inputs.version }} \ --build-arg FALCO_COMMIT_SHA=${{ github.sha }} \ --build-arg TARGETARCH=${TARGETARCH} \ . docker save docker.io/falcosecurity/falco-driver-loader:${{ inputs.arch }}-${{ inputs.tag }}-buster --output /tmp/falco-driver-loader-${{ inputs.arch }}-buster.tar - name: Upload images tarballs uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-images-${{ inputs.arch }} path: /tmp/falco-*.tar retention-days: 1 ================================================ FILE: .github/workflows/reusable_build_packages.yaml ================================================ # This is a reusable workflow used by master and release CI on: workflow_call: inputs: arch: description: x86_64 or aarch64 required: true type: string version: description: The Falco version to use when building packages required: true type: string enable_debug: description: Also create a debug build required: false type: boolean default: false enable_sanitizers: description: Also create a sanitizer build required: false type: boolean default: false use_jemalloc: description: Use jemalloc memory allocator required: false type: boolean default: false use_mimalloc: description: Use mimalloc memory allocator required: false type: boolean default: false permissions: contents: read jobs: build-modern-bpf-skeleton: # See https://github.com/actions/runner/issues/409#issuecomment-1158849936 runs-on: ${{ (inputs.arch == 'aarch64' && 'ubuntu-22.04-arm') || 'ubuntu-latest' }} container: fedora:41 steps: # Always install deps before invoking checkout action, to properly perform a full clone. - name: Install build dependencies run: | dnf install -y bpftool ca-certificates cmake make automake gcc gcc-c++ kernel-devel clang git pkg-config autoconf automake - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Build modern BPF skeleton run: | cmake -B skeleton-build -S . \ -DUSE_BUNDLED_DEPS=ON -DCREATE_TEST_TARGETS=Off -DFALCO_VERSION=${{ inputs.version }} cmake --build skeleton-build --target ProbeSkeleton -j6 - name: Upload skeleton uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: bpf_probe_${{ inputs.arch }}.skel.h path: skeleton-build/skel_dir/bpf_probe.skel.h retention-days: 1 build-packages-release: # See https://github.com/actions/runner/issues/409#issuecomment-1158849936 runs-on: ${{ (inputs.arch == 'aarch64' && 'ubuntu-22.04-arm') || 'ubuntu-latest' }} needs: [build-modern-bpf-skeleton] steps: # Always install deps before invoking checkout action, to properly perform a full clone. - name: Install build deps run: | sudo apt update && sudo apt install -y --no-install-recommends ca-certificates cmake curl wget build-essential git pkg-config autoconf automake libtool m4 rpm alien - name: Install systemd rpm macros run: | wget https://www.rpmfind.net/linux/centos-stream/9-stream/BaseOS/${{ inputs.arch }}/os/Packages/systemd-rpm-macros-252-59.el9.noarch.rpm sudo alien -d -i systemd-rpm-macros-252-59.el9.noarch.rpm - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Download skeleton uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: bpf_probe_${{ inputs.arch }}.skel.h path: /tmp - name: Install zig if: inputs.sanitizers == false uses: falcosecurity/libs/.github/actions/install-zig@master - name: Prepare project run: | cmake -B build -S . \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DUSE_BUNDLED_DEPS=On \ -DFALCO_ETC_DIR=/etc/falco \ -DMODERN_BPF_SKEL_DIR=/tmp \ -DBUILD_DRIVER=Off \ -DUSE_JEMALLOC=${{ inputs.use_jemalloc }} \ -DUSE_MIMALLOC=${{ inputs.use_mimalloc }} \ -DFALCO_VERSION=${{ inputs.version }} - name: Build project run: | cmake --build build --target falco -j6 - name: Build packages run: | cmake --build build --target package - name: Upload Falco tar.gz package uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-${{ inputs.version }}-${{ inputs.arch }}.tar.gz path: | ${{ github.workspace }}/build/falco-*.tar.gz - name: Upload Falco deb package uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-${{ inputs.version }}-${{ inputs.arch }}.deb path: | ${{ github.workspace }}/build/falco-*.deb - name: Upload Falco rpm package uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-${{ inputs.version }}-${{ inputs.arch }}.rpm path: | ${{ github.workspace }}/build/falco-*.rpm - name: Upload Falco debug symbols uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-${{ inputs.version }}-${{ inputs.arch }}.debug path: | ${{ github.workspace }}/build/userspace/falco/falco.debug build-packages-debug: # See https://github.com/actions/runner/issues/409#issuecomment-1158849936 runs-on: ${{ (inputs.arch == 'aarch64' && 'ubuntu-22.04-arm') || 'ubuntu-22.04' }} if: ${{ inputs.enable_debug == true }} needs: [build-modern-bpf-skeleton] steps: # Always install deps before invoking checkout action, to properly perform a full clone. - name: Install build deps run: | sudo apt update && sudo apt install -y --no-install-recommends ca-certificates cmake curl wget build-essential git pkg-config autoconf automake libtool m4 rpm - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Download skeleton uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: bpf_probe_${{ inputs.arch }}.skel.h path: /tmp - name: Install zig if: inputs.sanitizers == false uses: falcosecurity/libs/.github/actions/install-zig@master - name: Prepare project run: | cmake -B build -S . \ -DCMAKE_BUILD_TYPE=Debug \ -DUSE_BUNDLED_DEPS=On \ -DFALCO_ETC_DIR=/etc/falco \ -DMODERN_BPF_SKEL_DIR=/tmp \ -DBUILD_DRIVER=Off \ -DUSE_JEMALLOC=${{ inputs.use_jemalloc }} \ -DUSE_MIMALLOC=${{ inputs.use_mimalloc }} \ -DFALCO_VERSION=${{ inputs.version }} - name: Build project run: | cmake --build build --target falco -j6 - name: Build packages run: | cmake --build build --target package - name: Upload Falco tar.gz package uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-${{ inputs.version }}-${{ inputs.arch }}-debug.tar.gz path: | ${{ github.workspace }}/build/falco-*.tar.gz build-packages-sanitizers: # See https://github.com/actions/runner/issues/409#issuecomment-1158849936 runs-on: ${{ (inputs.arch == 'aarch64' && 'ubuntu-22.04-arm') || 'ubuntu-latest' }} if: ${{ inputs.enable_sanitizers == true }} needs: [build-modern-bpf-skeleton] steps: # Always install deps before invoking checkout action, to properly perform a full clone. - name: Install build deps run: | sudo apt update && sudo apt install -y --no-install-recommends ca-certificates cmake curl wget build-essential git pkg-config autoconf automake libtool m4 rpm - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Download skeleton uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: bpf_probe_${{ inputs.arch }}.skel.h path: /tmp - name: Prepare project # Jemalloc and ASAN don't play very well together. run: | cmake -B build -S . \ -DCMAKE_BUILD_TYPE=Debug \ -DUSE_BUNDLED_DEPS=On \ -DFALCO_ETC_DIR=/etc/falco \ -DMODERN_BPF_SKEL_DIR=/tmp \ -DBUILD_DRIVER=Off \ -DUSE_JEMALLOC=Off \ -DUSE_MIMALLOC=Off \ -DUSE_ASAN=On \ -DFALCO_VERSION=${{ inputs.version }} - name: Build project run: | cmake --build build --target falco -j6 - name: Build packages run: | cmake --build build --target package - name: Upload Falco tar.gz package uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-${{ inputs.version }}-${{ inputs.arch }}-sanitizers.tar.gz path: | ${{ github.workspace }}/build/falco-*.tar.gz build-musl-package: # x86_64 only for now if: ${{ inputs.arch == 'x86_64' }} runs-on: ubuntu-latest container: alpine:3.17 steps: # Always install deps before invoking checkout action, to properly perform a full clone. - name: Install build dependencies run: | apk add g++ gcc cmake make git bash perl linux-headers autoconf automake m4 libtool elfutils-dev libelf-static patch binutils clang llvm git clone https://github.com/libbpf/bpftool.git --branch v7.3.0 --single-branch cd bpftool git submodule update --init cd src && make install - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Prepare project run: | cmake -B build -S . \ -DCMAKE_BUILD_TYPE=Release \ -DCPACK_GENERATOR=TGZ \ -DBUILD_DRIVER=Off \ -DUSE_JEMALLOC=${{ inputs.use_jemalloc }} \ -DUSE_MIMALLOC=${{ inputs.use_mimalloc }} \ -DUSE_BUNDLED_DEPS=On \ -DMUSL_OPTIMIZED_BUILD=On \ -DFALCO_ETC_DIR=/etc/falco \ -DFALCO_VERSION=${{ inputs.version }} - name: Build project run: | cmake --build build -j6 - name: Build packages run: | cmake --build build -j6 --target package - name: Rename static package run: | cd build mv falco-${{ inputs.version }}-x86_64.tar.gz falco-${{ inputs.version }}-static-x86_64.tar.gz - name: Upload Falco static package uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-${{ inputs.version }}-static-x86_64.tar.gz path: | ${{ github.workspace }}/build/falco-${{ inputs.version }}-static-x86_64.tar.gz build-wasm-package: if: ${{ inputs.arch == 'x86_64' }} runs-on: ubuntu-22.04 steps: # Always install deps before invoking checkout action, to properly perform a full clone. - name: Install build dependencies run: | sudo apt update sudo DEBIAN_FRONTEND=noninteractive apt install cmake build-essential git emscripten -y - name: Select node version uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1 with: node-version: 14 - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Prepare project run: | emcmake cmake -B build -S . \ -DCMAKE_BUILD_TYPE=Release \ -DUSE_BUNDLED_DEPS=On \ -DFALCO_ETC_DIR=/etc/falco \ -DBUILD_FALCO_UNIT_TESTS=On \ -DFALCO_VERSION=${{ inputs.version }} - name: Build project run: | cd build emmake make -j6 all - name: Run unit Tests run: | cd build node ./unit_tests/falco_unit_tests.js - name: Build packages run: | cd build emmake make -j6 package - name: Upload Falco WASM package uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-${{ inputs.version }}-wasm.tar.gz path: | ${{ github.workspace }}/build/falco-${{ inputs.version }}-wasm.tar.gz build-win32-package: if: ${{ inputs.arch == 'x86_64' }} runs-on: windows-latest steps: - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Install NSIS run: choco install nsis -y # NOTE: Backslash doesn't work as line continuation on Windows. - name: Prepare project run: | cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DMINIMAL_BUILD=On -DUSE_BUNDLED_DEPS=On -DBUILD_FALCO_UNIT_TESTS=On -DFALCO_VERSION=${{ inputs.version }} - name: Build project run: | cmake --build build --target package --config Release - name: Run unit Tests run: | build/unit_tests/Release/falco_unit_tests.exe - name: Upload Falco win32 installer uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-installer-Release-win32.exe path: build/falco-*.exe - name: Upload Falco win32 package uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-Release-win32.exe path: | ${{ github.workspace }}/build/userspace/falco/Release/falco.exe build-macos-package: if: ${{ inputs.arch == 'x86_64' }} runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Prepare project run: | cmake -B build -S . \ -DMINIMAL_BUILD=On -DUSE_BUNDLED_DEPS=On -DBUILD_FALCO_UNIT_TESTS=On -DFALCO_VERSION=${{ inputs.version }} - name: Build project run: | cmake --build build --target package - name: Run unit Tests run: | sudo build/unit_tests/falco_unit_tests - name: Upload Falco macos package uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: falco-${{ inputs.version }}-macos path: | ${{ github.workspace }}/build/userspace/falco/falco ================================================ FILE: .github/workflows/reusable_fetch_version.yaml ================================================ # This is a reusable workflow used by master and release CI on: workflow_call: outputs: version: description: "Falco version" value: ${{ jobs.fetch-version.outputs.version }} permissions: contents: read jobs: # We need to use an ubuntu-latest to fetch Falco version because # Falco version is computed by some cmake scripts that do git sorceries # to get the current version. # But centos7 jobs have a git version too old and actions/checkout does not # fully clone the repo, but uses http rest api instead. fetch-version: runs-on: ubuntu-latest # Map the job outputs to step outputs outputs: version: ${{ steps.store_version.outputs.version }} steps: - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Install build dependencies run: | sudo apt update sudo apt install -y cmake build-essential - name: Configure project run: | cmake -B build -S . -DUSE_BUNDLED_DEPS=On -DUSE_DYNAMIC_LIBELF=Off - name: Load and store Falco version output id: store_version run: | FALCO_VERSION=$(cat build/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//') echo "version=${FALCO_VERSION}" >> $GITHUB_OUTPUT ================================================ FILE: .github/workflows/reusable_publish_docker.yaml ================================================ # This is a reusable workflow used by master and release CI on: workflow_call: inputs: tag: description: The tag to push required: true type: string is_latest: description: Update the latest tag with the new image required: false type: boolean default: false sign: description: Add signature with cosign required: false type: boolean default: false permissions: contents: read jobs: publish-docker: runs-on: ubuntu-latest permissions: attestations: write id-token: write contents: read steps: - name: Set up Docker Buildx uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - name: Download x86_64 images tarballs uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-images-x86_64 path: /tmp/falco-images - name: Download aarch64 images tarballs uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-images-aarch64 path: /tmp/falco-images - name: Load all images run: | for img in /tmp/falco-images/falco-*.tar; do docker load --input $img; done - name: Login to Docker Hub uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 with: username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_SECRET }} - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: "arn:aws:iam::292999226676:role/github_actions-falco-ecr" aws-region: us-east-1 # The region must be set to us-east-1 in order to access ECR Public. - name: Login to Amazon ECR id: login-ecr-public uses: aws-actions/amazon-ecr-login@2f9f10ea3fa2eed41ac443fee8bfbd059af2d0a4 # v1.6.0 with: registry-type: public - name: Setup Crane uses: imjasonh/setup-crane@00c9e93efa4e1138c9a7a5c594acd6c75a2fbf0c # v0.3 with: version: v0.15.1 # We're pushing the arch-specific manifests to Docker Hub so that we'll be able to easily create the index/multiarch later - name: Push arch-specific images to Docker Hub run: | docker push docker.io/falcosecurity/falco:aarch64-${{ inputs.tag }} docker push docker.io/falcosecurity/falco:x86_64-${{ inputs.tag }} docker push docker.io/falcosecurity/falco:aarch64-${{ inputs.tag }}-debian docker push docker.io/falcosecurity/falco:x86_64-${{ inputs.tag }}-debian docker push docker.io/falcosecurity/falco-driver-loader:aarch64-${{ inputs.tag }} docker push docker.io/falcosecurity/falco-driver-loader:x86_64-${{ inputs.tag }} docker push docker.io/falcosecurity/falco-driver-loader:aarch64-${{ inputs.tag }}-buster docker push docker.io/falcosecurity/falco-driver-loader:x86_64-${{ inputs.tag }}-buster - name: Create Falco manifest on Docker Hub uses: Noelware/docker-manifest-action@8e337e3cb9656abfcf20146b99706fd88716e942 # v0.4.0 with: inputs: docker.io/falcosecurity/falco:${{ inputs.tag }} images: docker.io/falcosecurity/falco:aarch64-${{ inputs.tag }},docker.io/falcosecurity/falco:x86_64-${{ inputs.tag }} push: true - name: Create falco-debian manifest on Docker Hub uses: Noelware/docker-manifest-action@8e337e3cb9656abfcf20146b99706fd88716e942 # v0.4.0 with: inputs: docker.io/falcosecurity/falco:${{ inputs.tag }}-debian images: docker.io/falcosecurity/falco:aarch64-${{ inputs.tag }}-debian,docker.io/falcosecurity/falco:x86_64-${{ inputs.tag }}-debian push: true - name: Create falco-driver-loader manifest on Docker Hub uses: Noelware/docker-manifest-action@8e337e3cb9656abfcf20146b99706fd88716e942 # v0.4.0 with: inputs: docker.io/falcosecurity/falco-driver-loader:${{ inputs.tag }} images: docker.io/falcosecurity/falco-driver-loader:aarch64-${{ inputs.tag }},docker.io/falcosecurity/falco-driver-loader:x86_64-${{ inputs.tag }} push: true - name: Create falco-driver-loader-buster manifest on Docker Hub uses: Noelware/docker-manifest-action@8e337e3cb9656abfcf20146b99706fd88716e942 # v0.4.0 with: inputs: docker.io/falcosecurity/falco-driver-loader:${{ inputs.tag }}-buster images: docker.io/falcosecurity/falco-driver-loader:aarch64-${{ inputs.tag }}-buster,docker.io/falcosecurity/falco-driver-loader:x86_64-${{ inputs.tag }}-buster push: true - name: Get Digests for images id: digests # We could probably use the docker-manifest-action output instead of recomputing those with crane run: | echo "falco=$(crane digest docker.io/falcosecurity/falco:${{ inputs.tag }})" >> $GITHUB_OUTPUT echo "falco-debian=$(crane digest docker.io/falcosecurity/falco:${{ inputs.tag }}-debian)" >> $GITHUB_OUTPUT echo "falco-driver-loader=$(crane digest docker.io/falcosecurity/falco-driver-loader:${{ inputs.tag }})" >> $GITHUB_OUTPUT echo "falco-driver-loader-buster=$(crane digest docker.io/falcosecurity/falco-driver-loader:${{ inputs.tag }}-buster)" >> $GITHUB_OUTPUT - name: Publish images to ECR run: | crane copy docker.io/falcosecurity/falco:${{ inputs.tag }} public.ecr.aws/falcosecurity/falco:${{ inputs.tag }} crane copy docker.io/falcosecurity/falco:${{ inputs.tag }}-debian public.ecr.aws/falcosecurity/falco:${{ inputs.tag }}-debian crane copy docker.io/falcosecurity/falco-driver-loader:${{ inputs.tag }} public.ecr.aws/falcosecurity/falco-driver-loader:${{ inputs.tag }} crane copy docker.io/falcosecurity/falco-driver-loader:${{ inputs.tag }}-buster public.ecr.aws/falcosecurity/falco-driver-loader:${{ inputs.tag }}-buster - name: Tag latest on Docker Hub and ECR if: inputs.is_latest run: | crane tag docker.io/falcosecurity/falco:${{ inputs.tag }} latest crane tag docker.io/falcosecurity/falco:${{ inputs.tag }}-debian latest-debian crane tag docker.io/falcosecurity/falco-driver-loader:${{ inputs.tag }} latest crane tag docker.io/falcosecurity/falco-driver-loader:${{ inputs.tag }}-buster latest-buster crane tag public.ecr.aws/falcosecurity/falco:${{ inputs.tag }} latest crane tag public.ecr.aws/falcosecurity/falco:${{ inputs.tag }}-debian latest-debian crane tag public.ecr.aws/falcosecurity/falco-driver-loader:${{ inputs.tag }} latest crane tag public.ecr.aws/falcosecurity/falco-driver-loader:${{ inputs.tag }}-buster latest-buster - name: Setup Cosign if: inputs.sign uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 - name: Sign images with cosign if: inputs.sign env: COSIGN_EXPERIMENTAL: "true" COSIGN_YES: "true" run: | cosign sign docker.io/falcosecurity/falco:latest@${{ steps.digests.outputs.falco }} cosign sign docker.io/falcosecurity/falco:latest-debian@${{ steps.digests.outputs.falco-debian }} cosign sign docker.io/falcosecurity/falco-driver-loader:latest@${{ steps.digests.outputs.falco-driver-loader }} cosign sign docker.io/falcosecurity/falco-driver-loader:latest-buster@${{ steps.digests.outputs.falco-driver-loader-buster }} cosign sign public.ecr.aws/falcosecurity/falco:latest@${{ steps.digests.outputs.falco }} cosign sign public.ecr.aws/falcosecurity/falco:latest-debian@${{ steps.digests.outputs.falco-debian }} cosign sign public.ecr.aws/falcosecurity/falco-driver-loader:latest@${{ steps.digests.outputs.falco-driver-loader }} cosign sign public.ecr.aws/falcosecurity/falco-driver-loader:latest-buster@${{ steps.digests.outputs.falco-driver-loader-buster }} - uses: actions/attest-build-provenance@173725a1209d09b31f9d30a3890cf2757ebbff0d # v1.1.2 with: subject-name: docker.io/falcosecurity/falco subject-digest: ${{ steps.digests.outputs.falco }} push-to-registry: true - uses: actions/attest-build-provenance@173725a1209d09b31f9d30a3890cf2757ebbff0d # v1.1.2 with: subject-name: docker.io/falcosecurity/falco-driver-loader subject-digest: ${{ steps.digests.outputs.falco-driver-loader }} push-to-registry: true ================================================ FILE: .github/workflows/reusable_publish_packages.yaml ================================================ # This is a reusable workflow used by master and release CI on: workflow_call: inputs: version: description: The Falco version to use when publishing packages required: true type: string bucket_suffix: description: bucket suffix for packages required: false default: '' type: string permissions: id-token: write contents: read env: AWS_S3_REGION: eu-west-1 AWS_CLOUDFRONT_DIST_ID: E1CQNPFWRXLGQD jobs: publish-packages: runs-on: ubuntu-latest container: docker.io/library/fedora:38 steps: - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install dependencies run: | dnf install rpm-sign expect which createrepo gpg python python-pip -y pip install awscli==1.29.60 # Configure AWS role; see https://github.com/falcosecurity/test-infra/pull/1102 # Note: master CI can only push dev packages as we have 2 different roles for master and release. - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: "arn:aws:iam::292999226676:role/github_actions-falco${{ inputs.bucket_suffix }}-s3" aws-region: ${{ env.AWS_S3_REGION }} - name: Download RPM x86_64 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ inputs.version }}-x86_64.rpm path: /tmp/falco-build-rpm - name: Download RPM aarch64 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ inputs.version }}-aarch64.rpm path: /tmp/falco-build-rpm - name: Download binary x86_64 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ inputs.version }}-x86_64.tar.gz path: /tmp/falco-build-bin - name: Download binary aarch64 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ inputs.version }}-aarch64.tar.gz path: /tmp/falco-build-bin - name: Download static binary x86_64 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ inputs.version }}-static-x86_64.tar.gz path: /tmp/falco-build-bin-static - name: Download WASM package uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ inputs.version }}-wasm.tar.gz path: /tmp/falco-wasm - name: Import gpg key env: GPG_KEY: ${{ secrets.GPG_KEY }} run: printenv GPG_KEY | gpg --import - - name: Publish wasm run: | ./scripts/publish-wasm -f /tmp/falco-wasm/falco-${{ inputs.version }}-wasm.tar.gz - name: Publish rpm run: | ./scripts/publish-rpm -f /tmp/falco-build-rpm/falco-${{ inputs.version }}-x86_64.rpm -f /tmp/falco-build-rpm/falco-${{ inputs.version }}-aarch64.rpm -r rpm${{ inputs.bucket_suffix }} - name: Publish bin run: | ./scripts/publish-bin -f /tmp/falco-build-bin/falco-${{ inputs.version }}-x86_64.tar.gz -r bin${{ inputs.bucket_suffix }} -a x86_64 ./scripts/publish-bin -f /tmp/falco-build-bin/falco-${{ inputs.version }}-aarch64.tar.gz -r bin${{ inputs.bucket_suffix }} -a aarch64 - name: Publish static run: | ./scripts/publish-bin -f /tmp/falco-build-bin-static/falco-${{ inputs.version }}-static-x86_64.tar.gz -r bin${{ inputs.bucket_suffix }} -a x86_64 publish-packages-deb: runs-on: ubuntu-latest container: docker.io/debian:stable steps: - name: Checkout uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install dependencies run: | apt update -y apt-get install apt-utils bzip2 gpg awscli -y # Configure AWS role; see https://github.com/falcosecurity/test-infra/pull/1102 # Note: master CI can only push dev packages as we have 2 different roles for master and release. - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: "arn:aws:iam::292999226676:role/github_actions-falco${{ inputs.bucket_suffix }}-s3" aws-region: ${{ env.AWS_S3_REGION }} - name: Download deb x86_64 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ inputs.version }}-x86_64.deb path: /tmp/falco-build-deb - name: Download deb aarch64 uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ inputs.version }}-aarch64.deb path: /tmp/falco-build-deb - name: Import gpg key env: GPG_KEY: ${{ secrets.GPG_KEY }} run: printenv GPG_KEY | gpg --import - - name: Publish deb run: | ./scripts/publish-deb -f /tmp/falco-build-deb/falco-${{ inputs.version }}-x86_64.deb -f /tmp/falco-build-deb/falco-${{ inputs.version }}-aarch64.deb -r deb${{ inputs.bucket_suffix }} ================================================ FILE: .github/workflows/reusable_test_packages.yaml ================================================ # This is a reusable workflow used by master and release CI on: workflow_call: inputs: arch: description: x86_64 or aarch64 required: true type: string static: description: Falco packages use a static build required: false type: boolean default: false version: description: The Falco version to use when testing packages required: true type: string sanitizers: description: Use sanitizer enabled build required: false default: false type: boolean permissions: contents: read jobs: test-packages: # See https://github.com/actions/runner/issues/409#issuecomment-1158849936 runs-on: ${{ (inputs.arch == 'aarch64' && 'ubuntu-22.04-arm') || 'ubuntu-latest' }} steps: - name: Download binary uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: falco-${{ inputs.version }}${{ inputs.static && '-static' || '' }}-${{ inputs.arch }}${{ inputs.sanitizers == true && '-sanitizers' || '' }}.tar.gz - name: Install Falco package run: | ls falco-*.tar.gz tar -xvf $(ls falco-*.tar.gz) cd falco-${{ inputs.version }}-${{ inputs.arch }} sudo cp -r * / - name: Install kernel headers for falco-driver-loader tests run: | sudo apt update -y sudo apt install -y --no-install-recommends linux-headers-$(uname -r) # Some builds use sanitizers, we always install support for them so they can run - name: Install sanitizer support if: inputs.sanitizers run: | sudo apt update -y sudo apt install -y libasan5 libubsan1 - name: Run tests env: LSAN_OPTIONS: "intercept_tls_get_addr=0" uses: falcosecurity/testing@main with: test-falco: 'true' test-falcoctl: 'true' test-k8saudit: 'true' test-dummy: 'true' static: ${{ inputs.static && 'true' || 'false' }} test-drivers: 'true' show-all: 'true' report-name-suffix: ${{ inputs.static && '-static' || '' }}${{ inputs.sanitizers && '-sanitizers' || '' }} ================================================ FILE: .github/workflows/scorecard.yaml ================================================ # This workflow uses actions that are not certified by GitHub. They are provided # by a third-party and are governed by separate terms of service, privacy # policy, and support documentation. name: Scorecard supply-chain security on: # To guarantee Maintained check is occasionally updated. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained schedule: # Weekly on Mondays at 00:00. - cron: '0 0 * * 1' # The OSSF recommendation encourages to enable branch protection rules trigger # to update the scorecard # (https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection) # but due to our GitHub org management this check is triggered too often and is # therefore disabled. # branch_protection_rule: push: branches: [ "master" ] # Declare default permissions as read only. permissions: read-all jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest permissions: # Needed to upload the results to code-scanning dashboard. security-events: write # Needed to publish results and get a badge (see publish_results below). id-token: write # Uncomment the permissions below if installing in a private repository. # contents: read # actions: read steps: - name: "Checkout code" uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 with: results_file: results.sarif results_format: sarif # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: # - you want to enable the Branch-Protection check on a *public* repository, or # - you are installing Scorecard on a *private* repository # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. # repo_token: ${{ secrets.SCORECARD_TOKEN }} # Public repositories: # - Publish results to OpenSSF REST API for easy access by consumers # - Allows the repository to include the Scorecard badge. # - See https://github.com/ossf/scorecard-action#publishing-results. # For private repositories: # - `publish_results` will always be set to `false`, regardless # of the value entered here. publish_results: true # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: SARIF file path: results.sarif retention-days: 5 # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4 with: sarif_file: results.sarif ================================================ FILE: .github/workflows/staticanalysis.yaml ================================================ name: StaticAnalysis on: pull_request: permissions: contents: read jobs: staticanalysis: runs-on: ubuntu-22.04 steps: - name: Checkout ⤵️ uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - name: Install build dependencies ⛓️ run: | sudo apt update -y sudo apt install build-essential git cppcheck cmake -y - name: Build and run cppcheck 🏎️ run: | cmake -B build -S . \ -DCMAKE_BUILD_TYPE="release" \ -DUSE_BUNDLED_DEPS=On -DUSE_DYNAMIC_LIBELF=Off -DBUILD_WARNINGS_AS_ERRORS=ON -DCREATE_TEST_TARGETS=Off -DBUILD_DRIVER=Off cmake --build build -j4 --target cppcheck cmake --build build -j4 --target cppcheck_htmlreport - name: Upload reports ⬆️ uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: static-analysis-reports path: ./build/static-analysis-reports ================================================ FILE: .gitignore ================================================ /build* *~ *.pyc .vscode/* *.idea* CMakeUserPresets.json ================================================ FILE: .gitmodules ================================================ [submodule "submodules/falcosecurity-rules"] path = submodules/falcosecurity-rules url = https://github.com/falcosecurity/rules.git branch = main ================================================ FILE: .pre-commit-config.yaml ================================================ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks fail_fast: false minimum_pre_commit_version: '0' repos: - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.13 hooks: - id: cmake-format stages: [commit] - repo: https://github.com/pre-commit/mirrors-clang-format rev: v18.1.8 hooks: - id: clang-format types_or: [c++, c] stages: [commit] - repo: local hooks: - id: dco-hook-local name: DCO hook local entry: ./tools/local_hooks/dco-pre-commit-msg.sh language: script stages: [prepare-commit-msg] ================================================ FILE: .yamllint.conf ================================================ extends: default rules: indentation: disable document-start: disable comments: disable line-length: disable new-line-at-end-of-file: disable ================================================ FILE: ADOPTERS.md ================================================ # Adopters Known end users with notable contributions to the project include: * AWS * IBM * Red Hat Falco is being used by numerous other companies, both large and small, to build higher layer products and services. The list includes but is not limited to: * Equinix Metal * IEEE * Lowes * Reckrut * Yellow Pepper * CTx * Utikal * Discrete Events * Agritech Infra This is a list of production adopters of Falco (in alphabetical order): * [ASAPP](https://www.asapp.com/) - ASAPP is a pushing the boundaries of fundamental artificial intelligence research. We apply our research into AI-Native® products that make organizations, in the customer experience industry, highly productive, efficient, and effective—by augmenting human activity and automating workflows. We constantly monitor our workloads against different hazards and FALCO helps us extend our threat monitoring boundaries. * [Booz Allen Hamilton](https://www.boozallen.com/) - BAH leverages Falco as part of their Kubernetes environment to verify that work loads behave as they did in their CD DevSecOps pipelines. BAH offers a solution to internal developers to easily build DevSecOps pipelines for projects. This makes it easy for developers to incorporate Security principles early on in the development cycle. In production, Falco is used to verify that the code the developer ships does not violate any of the production security requirements. BAH [are speaking at Kubecon NA 2019](https://kccncna19.sched.com/event/UaWr/building-reusable-devsecops-pipelines-on-a-secure-kubernetes-platform-steven-terrana-booz-allen-hamilton-michael-ducy-sysdig) on their use of Falco. * [Coveo](https://www.coveo.com/) - Coveo stitches together content and data, learning from every interaction, to tailor every experience using AI to drive growth, satisfy customers and develop employee proficiency. All Falco events are centralized in our SIEM for analysis. Understanding what is running on production servers, and the context around why things are running is even more tricky now that we have further abstractions with containers and orchestration systems. Falco is giving us a good visibility inside containers and complement other Host and Network Intrusion Detection Systems. In a near future, we expect to deploy serverless functions to take action when Falco identifies patterns worth taking action for. * [Deckhouse](https://deckhouse.io/) - Deckhouse Platform presents to you the opportunity to create homogeneous Kubernetes clusters anywhere and handles comprehensive, automagical management for them. It supplies all the add-ons you need for auto-scaling, observability, security, and service mesh. Falco is used as a part of the [runtime-audit-engine](https://deckhouse.io/documentation/latest/modules/650-runtime-audit-engine/) module to provide threats detection and enforce security compliance out of the box. By pairing with [shell-operator](https://github.com/flant/shell-operator) Falco can be configured by Kubernetes Custom Resources. * [Fairwinds](https://fairwinds.com/) - [Fairwinds Insights](https://fairwinds.com/insights), Kubernetes governance software, integrates Falco to offer a single pane of glass view into potential security incidents. Insights adds out-of-the-box integrations and rules filter to reduce alert fatigue and improve security response. The platform adds security prevention, detection, and response capabilities to your existing Kubernetes infrastructure. Security and DevOps teams benefit from a centralized view of container security vulnerability scanning and runtime container security. * [Frame.io](https://frame.io/) - Frame.io is a cloud-based (SaaS) video review and collaboration platform that enables users to securely upload source media, work-in-progress edits, dailies, and more into private workspaces where they can invite their team and clients to collaborate on projects. Understanding what is running on production servers, and the context around why things are running is even more tricky now that we have further abstractions like Docker and Kubernetes. To get this needed visibility into our system, we rely on Falco. Falco's ability to collect raw system calls such as open, connect, exec, along with their arguments offer key insights on what is happening on the production system and became the foundation of our intrusion detection and alerting system. * [Giant Swarm](https://www.giantswarm.io/) - Giant Swarm manages Kubernetes clusters and infrastructure for enterprises across multiple cloud providers as well as several flavors of on-premises data centers. Our platform provisions and monitors pure "vanilla" Kubernetes clusters which can be augmented with managed solutions to many common Kubernetes challenges, including security. We use Falco for anomaly detection as part of our collection of entirely open-source tools for securing our own clusters, and offer the same capabilities to our customers as part of our [managed security offering](https://docs.giantswarm.io/app-platform/apps/security/). * [GitLab](https://about.gitlab.com/direction/defend/container_host_security/) - GitLab is a complete DevOps platform, delivered as a single application, fundamentally changing the way Development, Security, and Ops teams collaborate. GitLab Ultimate provides the single tool teams need to find, triage, and fix vulnerabilities in applications, services, and cloud-native environments enabling them to manage their risk. This provides them with repeatable, defensible processes that automate security and compliance policies. GitLab includes a tight integration with Falco, allowing users to defend their containerized applications from attacks while running in production. * [gVisor](https://gvisor.dev/) - gVisor secures Kubernetes, containers, and workloads via an alternate execution environment that handles system calls in user space, blocking security issues before they reach the underlying host. gVisor provides defense-in-depth, protection against untrusted code execution, and a secure-by-default Kubernetes experience where containers are a security boundary. Falco can be used with gVisor to detect unusual or suspicious activity using its threat detection engine on top of gVisor runtime execution information. * [League](https://league.com/ca/) - League provides health benefits management services to help employees understand and get the most from their benefits, and employers to provide effective, efficient plans. Falco is used to monitor our deployed services on Kubernetes, protecting against malicious access to containers which could lead to leaks of PHI or other sensitive data. The Falco alerts are logged in Stackdriver for grouping and further analysis. In the future, we're hoping for integrations with Prometheus and AlertManager as well. * [Logz.io](https://logz.io/) - Logz.io is a cloud observability platform for modern engineering teams. The Logz.io platform consists of three products — Log Management, Infrastructure Monitoring, and Cloud SIEM — that work together to unify the jobs of monitoring, troubleshooting, and security. We empower engineers to deliver better software by offering the world's most popular open source observability tools — the ELK Stack, Grafana, and Jaeger — in a single, easy to use, and powerful platform purpose-built for monitoring distributed cloud environments. Cloud SIEM supports data from multiple sources, including Falco's alerts, and offers useful rules and dashboards content to visualize and manage incidents across your systems in a unified UI. * https://logz.io/blog/k8s-security-with-falco-and-cloud-siem/ * [MathWorks](https://mathworks.com) - MathWorks develops mathematical computing software for engineers and scientists. MathWorks uses Falco for Kubernetes threat detection, unexpected application behavior, and maps Falco rules to their cloud infrastructure's security kill chain model. MathWorks presented their Falco use case at [KubeCon + CloudNativeCon North America 2020](https://www.youtube.com/watch?v=L-5RYBTV010). * [NETWAYS Web Services](https://nws.netways.de/en/) - NETWAYS Web Services provides cloud and managed services tailored to their customers needs. From VPCs to managed databases and Kubernetes clusters, NETWAYS Web Services enables their customers to run infrastructure and applications without worries. Falco plays its part for NETWAYS Managed Services to ensure their platform conforms to ISO 27001 at all times and that their clients' workloads behave as expected by detecting anomalies in real-time. * [Pocteo](https://pocteo.co) - Pocteo helps with Kubernetes adoption in enterprises by providing a variety of services such as training, consulting, auditing and mentoring. We build CI/CD pipelines the GitOps way, as well as design and run k8s clusters. Pocteo uses Falco as a runtime monitoring system to secure clients' workloads against suspicious behavior and ensure k8s pods immutability. We also use Falco to collect, process and act on security events through a response engine and serverless functions. * [Preferral](https://www.preferral.com) - Preferral is a HIPAA-compliant platform for Referral Management and Online Referral Forms. Preferral streamlines the referral process for patients, specialists and their referral partners. By automating the referral process, referring practices spend less time on the phone, manual efforts are eliminated, and patients get the right care from the right specialist. Preferral leverages Falco to provide a Host Intrusion Detection System to meet their HIPAA compliance requirements. * https://hipaa.preferral.com/01-preferral_hipaa_compliance/ * [Qonto](https://qonto.com) - Qonto is a modern banking for SMEs and freelancers. Qonto provides a fully featured business account with a simplified accounting flow. Falco is used by our SecOps team to detect suspicious behaviors in our clusters. * [Raft](https://goraft.tech) - Raft is a government contractor that offers cloud-native solutions across many different agencies including DoD (Department of Defense), HHS (Health and Human Services), as well as within CFPB (Consumer Finance Protection Bureau). Raft leverages Falco to detect threats in our client's Kubernetes clusters and as a Host Intrusion Detection System. Raft proudly recommends Falco across all our different projects. * [Replicated](https://www.replicated.com/) - Replicated is the modern way to ship on-prem software. Replicated gives software vendors a container-based platform for easily deploying cloud native applications inside customers'​ environments to provide greater security and control. Replicated uses Falco as runtime security to detect threats in the Kubernetes clusters which host our critical SaaS services. * [Secureworks](https://www.secureworks.com/) - Secureworks is a leading worldwide cybersecurity company with a cloud-native security product that combines the power of human intellect with security analytics to unify detection and response across cloud, network, and endpoint environments for improved security operations and outcomes. Our Taegis XDR platform and detection system processes petabytes of security relevant data to expose active threats amongst the billions of daily events from our customers. We are proud to protect our platform’s Kubernetes deployments, as well as help our customers protect their own Linux and container environments, using Falco. * [Shopify](https://www.shopify.com) - Shopify is the leading multi-channel commerce platform. Merchants use Shopify to design, set up, and manage their stores across multiple sales channels, including mobile, web, social media, marketplaces, brick-and-mortar locations, and pop-up shops. The platform also provides merchants with a powerful back-office and a single view of their business, from payments to shipping. The Shopify platform was engineered for reliability and scale, making enterprise-level technology available to businesses of all sizes. Shopify uses Falco to complement its Host and Network Intrusion Detection Systems. * [SafeDep](https://safedep.io/) - SafeDep is a open source software supply chain security platform that helps organizations identify and mitigate risks in their dependencies. At its core, SafeDep offers [`vet`](https://github.com/safedep/vet) a free and open source tool for detecting vulnerabilities, malicious code, and quality issues in open source components, while the enterprise offering, SafeDep Cloud, provides centralized control, data aggregation, and advanced features like malware analysis for large-scale deployments across thousands of repositories. * [Sight Machine](https://www.sightmachine.com) - Sight Machine is the category leader for manufacturing analytics and used by Global 500 companies to make better, faster decisions about their operations. Sight Machine uses Falco to help enforce SOC2 compliance as well as a tool for real time security monitoring and alerting in Kubernetes. * [Skyscanner](https://www.skyscanner.net) - Skyscanner is the world's travel search engine for flights, hotels and car rentals. Most of our infrastructure is based on Kubernetes, and our Security team is using Falco to monitor anomalies at runtime, integrating Falco's findings with our internal ChatOps tooling to provide insight on the behavior of our machines in production. We also postprocess and store Falco's results to generate dashboards for auditing purposes. * [Sumo Logic](https://www.sumologic.com/) - Sumo Logic provides a SaaS based log aggregation service that provides dashboards and applications to easily identify and analyze problems in your application and infrastructure. Sumo Logic provides native integrations for many CNCF projects, such as Falco, that allows end users to easily collect Falco events and analyze Falco events on DecSecOps focused dashboards. * [Swissblock Technologies](https://swissblock.net/) At Swissblock we connect the dots by combining cutting-edge algorithmic trading strategies with in-depth market analysis. We route all Falco events to our control systems, both monitoring and logging. Being able to deeply analyse alerts, we can understand what is running on our Kubernetes clusters and check against security policies, specifically defined for each workload. A set of alarms notifies us in case of critical events, letting us react fast. In the near future we plan to build a little application to route Kubernetes internal events directly to Falco, fully leveraging Falco PodSecurityPolicies analyses. * [Shapesecurity/F5](https://www.shapesecurity.com/) Shapesecurity defends against application fraud attacks like Account Take Over, Credential Stuffing, Fake Accounts, etc. Required by FedRamp certification, we needed to find a FIM solution to help monitor and protect our Kubernetes clusters. Traditional FIM solutions were not scalable and not working for our environment, but with Falco we found the solution we needed. Falco's detection capabilities have helped us identify anomalous behaviour within our clusters. We leverage Sidekick (https://github.com/falcosecurity/charts/tree/master/falcosidekick) to send Falco alerts to a PubSub which in turn publishes those alerts to our SIEM (SumoLogic) * [Sysdig](https://www.sysdig.com/) Sysdig originally created Falco in 2016 to detect unexpected or suspicious activity using a rules engine on top of the data that comes from the sysdig kernel system call driver. Sysdig provides tooling to help with vulnerability management, compliance, detection, incident response and forensics in Cloud-native environments. Sysdig Secure has extended Falco to include: a rule library, the ability to update macros, lists & rules via the user interface and API, automated tuning of rules, and rule creation based on profiling known system behavior. On top of the basic Falco rules, Sysdig Secure implements the concept of a "Security policy" that can comprise several rules which are evaluated for a user-defined infrastructure scope like Kubernetes namespaces, OpenShift clusters, deployment workload, cloud regions etc. * [Thales Group](https://www.thalesgroup.com) Thales is a global technology leader with more than 81,000 employees on five continents. The Thales Group is investing in digital and “deep tech” innovations – Big Data, artificial intelligence, connectivity, cybersecurity and quantum technology – to build a future we can all trust. In the past few years, the Cloud-Native paradigms and its frameworks and tools have challenged the way applications and services are developed, delivered, and instantiated. All sorts of services are container-based workloads managed by higher level layers of orchestration such as the Kubernetes environment. Thales is committed to develop Cloud-Native services and to provide its customers with security features that ensure their applications and services are protected against cyber threats. Falco is a framework that can help Thales' products and services reach the level of trust, security and safety our clients need. * [Thought Machine](https://www.thoughtmachine.net) Thought Machine builds Vault Core and Vault Payments: cloud-native core and payments technology enabling banks and fintechs to remain competitive and flourish into the future. Vault Core and Vault Payments are the foundation layer of a bank's technology stack. They can run any bank, any product, and any payment set. Thought Machine uses Falco to perform cloud agnostic real time detections of suspicious container behaviour. * [Tulip Retail](https://tulip.com) Tulip Retail uses Falco to monitor container activity in our environments. It's numerous integration points, easy deployment and easily customizable rules were the main reasons we chose Falco. * [Vinted](https://vinted.com/) Vinted uses Falco to continuously monitor container activities, identifying security threats, and ensuring compliance. The container-native approach, rule-based real-time threat detection, community support, extensibility, and compliance capabilities are the main factors why we chose it to enhance Vinted Kubernetes security. Falco Sidekick is used to send critical and warning severity alerts to our incident management solution (RTIR). * [Xenit AB](https://xenit.se/contact/) Xenit is a growth company with services within cloud and digital transformation. We provide an open-source Kubernetes framework that we leverage to help our customers get their applications to production as quickly and as securely as possible. We use Falco's detection capabilities to identify anomalous behaviour within our clusters in both Azure and AWS. * [Yahoo! JAPAN](https://www.yahoo.co.jp/) Yahoo! JAPAN is a leading company of internet in Japan. We build an AI Platform in our private cloud and provide it to scientists in our company. AI Platform is a multi-tenant Kubernetes environment and more flexible, faster, more efficient Machine Learning environment. Falco is used to detect unauthorized commands and malicious access and our AI Platform is monitored and alerted by Falco. ## Projects that use Falco libs * [R6/Phoenix](https://r6security.com/) is an attack surface protection company that uses moving target defense to provide fully automated, proactive and devops friendly security to its customers. There are a set of policies you can add to enable the moving target defense capabilities. Some of them are triggered by a combination of Falco's findings. You can kill, restart and rename pods according to the ever changing policies. * [SysFlow](https://sysflow.io) SysFlow is a cloud-native system telemetry framework that focuses on data abstraction, behavioral analytics, and noise reduction. At its core, SysFlow exposes a compact open telemetry format that records workload behaviors by connecting event and flow representations of process control flows, file interactions, and network communications. The resulting abstraction encodes a graph structure that enables provenance reasoning on host and container environments, and fast retrieval of security-relevant information. * [StackRox](https://stackrox.io) is the industry’s first Kubernetes-native security platform enabling organizations to build, deploy, and run cloud-native applications securely. The platform works with Kubernetes environments and integrates with DevOps and security tools, enabling teams to operationalize and secure their supply chain, infrastructure, and workloads. StackRox aims to harness containerized applications’ development speed while giving operations and security teams greater context and risk profiling. StackRox leverages cloud-native principles and declarative artifacts to automate DevSecOps best practices. * [Wireshark](https://www.wireshark.org) is the world's most powerful and popular network protocol analyzer. The Wireshark team is combining Wireshark's features and Falco libs to create Logray, a cloud and system log analyzer with advanced filtering, capture, and scripting capabilities. ## Adding a name If you would like to add your name to this file, submit a pull request with your change. ================================================ FILE: CHANGELOG.md ================================================ # Change Log ## v0.43.0 Released on 2026-01-28 ### Breaking Changes :warning: * fix(userspace)!: show source config path only in debug builds [[#3787](https://github.com/falcosecurity/falco/pull/3787)] - [@leogr](https://github.com/leogr) ### Minor Changes * chore(userspace): deprecate `--gvisor-generate-config` CLI option [[#3784](https://github.com/falcosecurity/falco/pull/3784)] - [@ekoops](https://github.com/ekoops) * docs: add deprecation notice for legacy eBPF in pkg install dialog [[#3786](https://github.com/falcosecurity/falco/pull/3786)] - [@ekoops](https://github.com/ekoops) * chore: [NOTICE] The GPG key used to sign DEB/RPM packages has been rotated, and all existing packages have been re-signed. New key fingerprint: `478B2FBBC75F4237B731DA4365106822B35B1B1F` [[#3753](https://github.com/falcosecurity/falco/pull/3753)] - [@leogr](https://github.com/leogr) * chore(scripts/falcoctl): increase follow interval to 1 week [[#3757](https://github.com/falcosecurity/falco/pull/3757)] - [@leogr](https://github.com/leogr) * docs: add deprecation notice for legacy eBPF, gVisor and gRPC usage [[#3763](https://github.com/falcosecurity/falco/pull/3763)] - [@ekoops](https://github.com/ekoops) * chore(userspace): deprecate legacy eBPF probe, gVisor engine and gRPC [[#3763](https://github.com/falcosecurity/falco/pull/3763)] - [@ekoops](https://github.com/ekoops) * chore(engine): emit warning when the deprecated `evt.latency` field family is used in a rule condition or output [[#3744](https://github.com/falcosecurity/falco/pull/3744)] - [@irozzo-1A](https://github.com/irozzo-1A) ### Bug Fixes * fix: prevent null pointer crash on `popen()` failure in output_program [[#3722](https://github.com/falcosecurity/falco/pull/3722)] - [@vietcgi](https://github.com/vietcgi) * fix: correct falcoctl.yaml path in debian conffiles [[#3745](https://github.com/falcosecurity/falco/pull/3745)] - [@leogr](https://github.com/leogr) ### Non user-facing changes * chore(cmake): bump falcoctl dependency version to `0.12.2` [[#3790](https://github.com/falcosecurity/falco/pull/3790)] - [@ekoops](https://github.com/ekoops) * chore(cmake): bump falcoctl dependency version to `0.12.1` [[#3777](https://github.com/falcosecurity/falco/pull/3777)] - [@ekoops](https://github.com/ekoops) * chore(cmake): bump container plugin version to `0.6.1` [[#3780](https://github.com/falcosecurity/falco/pull/3780)] - [@ekoops](https://github.com/ekoops) * fix(userspace/engine): missing closing quote in deprecated field warning [[#3779](https://github.com/falcosecurity/falco/pull/3779)] - [@leogr](https://github.com/leogr) * chore(.github): Put back gpg key rotation workflow [[#3772](https://github.com/falcosecurity/falco/pull/3772)] - [@irozzo-1A](https://github.com/irozzo-1A) * chore(cmake): bump libs/drivers to `0.23.1`/`9.1.0+driver` [[#3769](https://github.com/falcosecurity/falco/pull/3769)] - [@ekoops](https://github.com/ekoops) * revert: chore(.github): temporary action for GPG key rotation [[#3766](https://github.com/falcosecurity/falco/pull/3766)] - [@leogr](https://github.com/leogr) * chore(cmake): bump container plugin version to 0.6.0 [[#3768](https://github.com/falcosecurity/falco/pull/3768)] - [@irozzo-1A](https://github.com/irozzo-1A) * docs(proposals): add proposal for legacy probe, gVisor engine and gRPC output deprecation [[#3755](https://github.com/falcosecurity/falco/pull/3755)] - [@ekoops](https://github.com/ekoops) * chore(cmake): bump libs/drivers to `0.23.0`/`9.1.0+driver` [[#3760](https://github.com/falcosecurity/falco/pull/3760)] - [@ekoops](https://github.com/ekoops) * update(cmake): update libs and driver to latest master [[#3754](https://github.com/falcosecurity/falco/pull/3754)] - [@github-actions[bot]](https://github.com/apps/github-actions) * fix(metrics): Add null check for state.outputs in metrics collection [[#3740](https://github.com/falcosecurity/falco/pull/3740)] - [@adduali1310](https://github.com/adduali1310) * chore(cmake): bump libs to `0.23.0-rc2` [[#3759](https://github.com/falcosecurity/falco/pull/3759)] - [@ekoops](https://github.com/ekoops) * chore(cmake): bump libs/drivers to `0.23.0-rc1`/`9.1.0-rc1+driver` [[#3758](https://github.com/falcosecurity/falco/pull/3758)] - [@ekoops](https://github.com/ekoops) * fix(ci): revert changes to mitigate rate-limitar change [[#3752](https://github.com/falcosecurity/falco/pull/3752)] - [@irozzo-1A](https://github.com/irozzo-1A) * update(cmake): update libs and driver to latest master [[#3723](https://github.com/falcosecurity/falco/pull/3723)] - [@github-actions[bot]](https://github.com/apps/github-actions) * Reduce image size [[#3746](https://github.com/falcosecurity/falco/pull/3746)] - [@jfcoz](https://github.com/jfcoz) * docs(RELEASE.md): specify target branch association upon release creation [[#3717](https://github.com/falcosecurity/falco/pull/3717)] - [@ekoops](https://github.com/ekoops) * docs(RELEASE.md): fix `rn2md` cmd generating changelogs [[#3709](https://github.com/falcosecurity/falco/pull/3709)] - [@ekoops](https://github.com/ekoops) * docs(RELEASE.md): fix PRs filtering expr for checking release notes [[#3708](https://github.com/falcosecurity/falco/pull/3708)] - [@ekoops](https://github.com/ekoops) * docs(RELEASE.md): fix PRs filtering expression text [[#3707](https://github.com/falcosecurity/falco/pull/3707)] - [@ekoops](https://github.com/ekoops) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 21 | | Release note | 11 | | Total | 32 | ## v0.42.0 Released on 2025-10-22 ### Major Changes * feat: add `falco_libs.thread_table_auto_purging_interval_s` and `thread_table_auto_purging_thread_timeout_s` configuration options [[#3670](https://github.com/falcosecurity/falco/pull/3670)] - [@ekoops](https://github.com/ekoops) * feat: log plugin version info at loading time [[#3657](https://github.com/falcosecurity/falco/pull/3657)] - [@FedeDP](https://github.com/FedeDP) * feat: ability to add statically defined fields via `static_fields` configuration [[#3557](https://github.com/falcosecurity/falco/pull/3557)] - [@FedeDP](https://github.com/FedeDP) * feat(engine): emit warning when a rule containing the `evt.dir` field in output is encountered [[#3697](https://github.com/falcosecurity/falco/pull/3697)] - [@irozzo-1A](https://github.com/irozzo-1A) * feat(engine): emit warning when a rule containing a condition on the deprecated `evt.dir` field is encountered [[#3690](https://github.com/falcosecurity/falco/pull/3690)] - [@irozzo-1A](https://github.com/irozzo-1A) * new: ability to record `.scap` files (capture feature) [[#3645](https://github.com/falcosecurity/falco/pull/3645)] - [@leogr](https://github.com/leogr) * new(docker): includes sha on the image labels [[#3658](https://github.com/falcosecurity/falco/pull/3658)] - [@jcchavezs](https://github.com/jcchavezs) * new(cmake,userspace,ci): add mimalloc support [[#3616](https://github.com/falcosecurity/falco/pull/3616)] - [@FedeDP](https://github.com/FedeDP) ### Minor Changes * docs(falco.yaml): refactor config documentation [[#3685](https://github.com/falcosecurity/falco/pull/3685)] - [@leogr](https://github.com/leogr) * build: fix `debian:buster` apt debian repo URL in `:driver-loader-buster` container image [[#3644](https://github.com/falcosecurity/falco/pull/3644)] - [@ekoops](https://github.com/ekoops) * build: updagrade libs to version 0.22.1 [[#3705](https://github.com/falcosecurity/falco/pull/3705)] - [@irozzo-1A](https://github.com/irozzo-1A) * build: upgrade drivers to v9.0.0+driver [[#3701](https://github.com/falcosecurity/falco/pull/3701)] - [@irozzo-1A](https://github.com/irozzo-1A) * build: upgrade cpp-httplib to v0.23.1 [[#3647](https://github.com/falcosecurity/falco/pull/3647)] - [@FedeDP](https://github.com/FedeDP) * update: upgrade default ruleset to v5.0.0 [[#3700](https://github.com/falcosecurity/falco/pull/3700)] - [@leogr](https://github.com/leogr) * build: upgrade `falcoctl` to v0.11.4 [[#3694](https://github.com/falcosecurity/falco/pull/3694)] - [@leogr](https://github.com/leogr) * chore(prometheus): deprecate enter events drop stats [[#3675](https://github.com/falcosecurity/falco/pull/3675)] - [@irozzo-1A](https://github.com/irozzo-1A) ### Bug Fixes * fix(cmake): correct abseil-cpp for alpine build [[#3598](https://github.com/falcosecurity/falco/pull/3598)] - [@RomanenkoDenys](https://github.com/RomanenkoDenys) * fix: enable handling of multiple actions configured with `syscall_event_drops.actions` [[#3676](https://github.com/falcosecurity/falco/pull/3676)] - [@terror96](https://github.com/terror96) * fix: disable dry-run restarts when Falco runs with config-watching disabled [[#3640](https://github.com/falcosecurity/falco/pull/3640)] - [@Proximyst](https://github.com/Proximyst) ### Non user-facing changes * fix(userspace/falco): correct default duration calculation [[#3715](https://github.com/falcosecurity/falco/pull/3715)] - [@leogr](https://github.com/leogr) * chore(falcoctl): update falco rules to version 5 [[#3712](https://github.com/falcosecurity/falco/pull/3712)] - [@irozzo-1A](https://github.com/irozzo-1A) * doc(OWNERS): move incertum (Melissa Kilby) to emeritus_approvers [[#3605](https://github.com/falcosecurity/falco/pull/3605)] - [@incertum](https://github.com/incertum) * update(cmake): update libs and driver to latest master [[#3689](https://github.com/falcosecurity/falco/pull/3689)] - [@github-actions[bot]](https://github.com/apps/github-actions) * chore(docker): use new `ENV` syntax in place of deprecated one [[#3696](https://github.com/falcosecurity/falco/pull/3696)] - [@ekoops](https://github.com/ekoops) * chore(cmake/modules): update rules to 5.0.0-rc1 [[#3698](https://github.com/falcosecurity/falco/pull/3698)] - [@leogr](https://github.com/leogr) * fix(userspace/engine): fix logger date format [[#3672](https://github.com/falcosecurity/falco/pull/3672)] - [@ekoops](https://github.com/ekoops) * docs(OWNERS): add `ekoops`(Leonardo Di Giovanna) as approver [[#3688](https://github.com/falcosecurity/falco/pull/3688)] - [@ekoops](https://github.com/ekoops) * update(cmake): update libs and driver to latest master [[#3665](https://github.com/falcosecurity/falco/pull/3665)] - [@github-actions[bot]](https://github.com/apps/github-actions) * Refactor: cppcheck cleanups [[#3649](https://github.com/falcosecurity/falco/pull/3649)] - [@sgaist](https://github.com/sgaist) * update(userspace/engine): update falco engine version and checksum [[#3648](https://github.com/falcosecurity/falco/pull/3648)] - [@ekoops](https://github.com/ekoops) * update(cmake): update libs and driver to latest master [[#3662](https://github.com/falcosecurity/falco/pull/3662)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3661](https://github.com/falcosecurity/falco/pull/3661)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3653](https://github.com/falcosecurity/falco/pull/3653)] - [@github-actions[bot]](https://github.com/apps/github-actions) * chore(ci): disable mimalloc for master builds. [[#3655](https://github.com/falcosecurity/falco/pull/3655)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `1208816` to `be38001` [[#3651](https://github.com/falcosecurity/falco/pull/3651)] - [@dependabot[bot]](https://github.com/apps/dependabot) * docs(falco.yaml): avoid out-of-sync config options for `container` pl… [[#3650](https://github.com/falcosecurity/falco/pull/3650)] - [@leogr](https://github.com/leogr) * update(cmake): update libs and driver to latest master [[#3636](https://github.com/falcosecurity/falco/pull/3636)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(CHANGELOG.md): release 0.41.3 (cherry-pick) [[#3634](https://github.com/falcosecurity/falco/pull/3634)] - [@ekoops](https://github.com/ekoops) * update(cmake): update libs and driver to latest master [[#3628](https://github.com/falcosecurity/falco/pull/3628)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(CHANGELOG.md): release 0.41.2 (cherry-pick) [[#3623](https://github.com/falcosecurity/falco/pull/3623)] - [@ekoops](https://github.com/ekoops) * update(cmake): update libs and driver to latest master [[#3618](https://github.com/falcosecurity/falco/pull/3618)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3602](https://github.com/falcosecurity/falco/pull/3602)] - [@github-actions[bot]](https://github.com/apps/github-actions) * chore(falco.yaml): clean up plugins config leftover [[#3596](https://github.com/falcosecurity/falco/pull/3596)] - [@leogr](https://github.com/leogr) * chore(deps): Bump submodules/falcosecurity-rules from `b4437c4` to `4d51b18` [[#3607](https://github.com/falcosecurity/falco/pull/3607)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(docs): cherry pick CHANGELOG. [[#3600](https://github.com/falcosecurity/falco/pull/3600)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): update libs and driver to latest master [[#3592](https://github.com/falcosecurity/falco/pull/3592)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(docs): bumped changelog for release 0.41.0, master sync [[#3586](https://github.com/falcosecurity/falco/pull/3586)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `cb17833` to `b4437c4` [[#3578](https://github.com/falcosecurity/falco/pull/3578)] - [@dependabot[bot]](https://github.com/apps/dependabot) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 29 | | Release note | 23 | | Total | 52 | ## v0.41.3 Released on 2025-07-01 ### Minor Changes * update: bump container plugin to v0.3.1 [[#3629](https://github.com/falcosecurity/falco/pull/3629)] - [@FedeDP](https://github.com/FedeDP) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 0 | | Release note | 1 | | Total | 1 | ## v0.41.2 Released on 2025-06-17 ### Minor Changes * update(build): update container plugin to 0.3.0 [[#3619](https://github.com/falcosecurity/falco/pull/3619)] - [@ekoops](https://github.com/ekoops) ### Non user-facing changes * update(build): update container plugin to 0.2.6 [[#3611](https://github.com/falcosecurity/falco/pull/3611)] - [@leogr](https://github.com/leogr) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 1 | | Release note | 1 | | Total | 2 | ## v0.41.1 Released on 2025-06-05 ### Bug Fixes * fix(userspace/falco): when collecting metrics for stats_writer, create a `libs_metrics_collector` for each source [[#3585](https://github.com/falcosecurity/falco/pull/3585)] - [@FedeDP](https://github.com/FedeDP) * fix(userspace/falco): only enable prometheus metrics once all inspectors have been opened [[#3588](https://github.com/falcosecurity/falco/pull/3588)] - [@FedeDP](https://github.com/FedeDP) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 0 | | Release note | 2 | | Total | 2 | ## v0.41.0 Released on 2025-05-29 ### Breaking Changes :warning: * cleanup(engine)!: only consider .yaml/.yml rule files [[#3551](https://github.com/falcosecurity/falco/pull/3551)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup(userspace)!: deprecate print of `container.info` [[#3543](https://github.com/falcosecurity/falco/pull/3543)] - [@FedeDP](https://github.com/FedeDP) * cleanup(userspace/falco)!: drop deprecated in 0.40.0 CLI flags. [[#3496](https://github.com/falcosecurity/falco/pull/3496)] - [@FedeDP](https://github.com/FedeDP) ### Major Changes * new(falco): add json_include_output_fields option [[#3527](https://github.com/falcosecurity/falco/pull/3527)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(build,userspace): switch to use container plugin [[#3482](https://github.com/falcosecurity/falco/pull/3482)] - [@FedeDP](https://github.com/FedeDP) * new(docker,scripts,ci): use an override config file to enable ISO 8601 output timeformat on docker images [[#3488](https://github.com/falcosecurity/falco/pull/3488)] - [@FedeDP](https://github.com/FedeDP) ### Minor Changes * chore(build): update falcoctl to v0.11.2, rules for artifact follow to v4 [[#3580](https://github.com/falcosecurity/falco/pull/3580)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(cmake): bumped falcoctl to 0.11.1 and rules to 4.0.0. [[#3577](https://github.com/falcosecurity/falco/pull/3577)] - [@FedeDP](https://github.com/FedeDP) * update(containers): update opencontainers labels [[#3575](https://github.com/falcosecurity/falco/pull/3575)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(metrics): improve restart/hot_reload conditions inspection [[#3562](https://github.com/falcosecurity/falco/pull/3562)] - [@incertum](https://github.com/incertum) * update: empty `values` in `exceptions` won't emit a warning anymore [[#3529](https://github.com/falcosecurity/falco/pull/3529)] - [@leogr](https://github.com/leogr) * chore(falco.yaml): enable libs_logger by default with info level [[#3507](https://github.com/falcosecurity/falco/pull/3507)] - [@FedeDP](https://github.com/FedeDP) ### Bug Fixes * fix(metrics/prometheus): gracefully handle multiple event sources, avoid erroneous duplicate metrics [[#3563](https://github.com/falcosecurity/falco/pull/3563)] - [@incertum](https://github.com/incertum) * fix(ci): properly install rpm systemd-rpm-macro package on building packages pipeline [[#3521](https://github.com/falcosecurity/falco/pull/3521)] - [@FedeDP](https://github.com/FedeDP) * fix(userspace/falco): init cmdline options after loading all config files [[#3493](https://github.com/falcosecurity/falco/pull/3493)] - [@FedeDP](https://github.com/FedeDP) * fix(cmake): add support for 16K kernel page to jemalloc [[#3490](https://github.com/falcosecurity/falco/pull/3490)] - [@Darkness4](https://github.com/Darkness4) * fix(userspace/falco): fix jemalloc enabled in minimal build. [[#3478](https://github.com/falcosecurity/falco/pull/3478)] - [@FedeDP](https://github.com/FedeDP) ### Non user-facing changes * chore(deps): Bump submodules/falcosecurity-rules from `4ccf111` to `cb17833` [[#3572](https://github.com/falcosecurity/falco/pull/3572)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake/rules): bump to falco-rules-4.0.0-rc1 [[#3567](https://github.com/falcosecurity/falco/pull/3567)] - [@leogr](https://github.com/leogr) * cleanup(userspace/falco): drop unused `libs_metrics_collector` variable. [[#3566](https://github.com/falcosecurity/falco/pull/3566)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): update libs and driver to latest master [[#3564](https://github.com/falcosecurity/falco/pull/3564)] - [@github-actions[bot]](https://github.com/apps/github-actions) * fix(build): fixed container custom_target `sed` command. [[#3556](https://github.com/falcosecurity/falco/pull/3556)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `ae6ed41` to `4ccf111` [[#3555](https://github.com/falcosecurity/falco/pull/3555)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(cmake): fix bundled c-ares cmake issue with e.g. SLES [[#3559](https://github.com/falcosecurity/falco/pull/3559)] - [@terror96](https://github.com/terror96) * chore(deps): Bump submodules/falcosecurity-rules from `1d2c6b1` to `ae6ed41` [[#3553](https://github.com/falcosecurity/falco/pull/3553)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore: revert "chore(deps): Bump submodules/falcosecurity-rules from `1d2c6b1` to `371e431`" [[#3552](https://github.com/falcosecurity/falco/pull/3552)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): update libs and driver to latest master [[#3550](https://github.com/falcosecurity/falco/pull/3550)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3549](https://github.com/falcosecurity/falco/pull/3549)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(adopters): added SafeDep as adopter [[#3548](https://github.com/falcosecurity/falco/pull/3548)] - [@KunalSin9h](https://github.com/KunalSin9h) * update(cmake): update libs and driver to latest master [[#3547](https://github.com/falcosecurity/falco/pull/3547)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3541](https://github.com/falcosecurity/falco/pull/3541)] - [@github-actions[bot]](https://github.com/apps/github-actions) * fix(userspace): fixed engine `openssl` dep. [[#3535](https://github.com/falcosecurity/falco/pull/3535)] - [@FedeDP](https://github.com/FedeDP) * fix(userspace/falco): fix outputs_http timeout [[#3523](https://github.com/falcosecurity/falco/pull/3523)] - [@benierc](https://github.com/benierc) * fix(ci): use clang-19 to build modern_ebpf skeleton. [[#3537](https://github.com/falcosecurity/falco/pull/3537)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): update libs and driver to latest master [[#3531](https://github.com/falcosecurity/falco/pull/3531)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3530](https://github.com/falcosecurity/falco/pull/3530)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3525](https://github.com/falcosecurity/falco/pull/3525)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3520](https://github.com/falcosecurity/falco/pull/3520)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3516](https://github.com/falcosecurity/falco/pull/3516)] - [@github-actions[bot]](https://github.com/apps/github-actions) * docs(README.md): cleanups and enhancements [[#3514](https://github.com/falcosecurity/falco/pull/3514)] - [@leogr](https://github.com/leogr) * update(cmake): update libs and driver to latest master [[#3511](https://github.com/falcosecurity/falco/pull/3511)] - [@github-actions[bot]](https://github.com/apps/github-actions) * chore(deps): Bump submodules/falcosecurity-rules from `1d2c6b1` to `371e431` [[#3510](https://github.com/falcosecurity/falco/pull/3510)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake): update libs and driver to latest master [[#3508](https://github.com/falcosecurity/falco/pull/3508)] - [@github-actions[bot]](https://github.com/apps/github-actions) * update(cmake): update libs and driver to latest master [[#3506](https://github.com/falcosecurity/falco/pull/3506)] - [@github-actions[bot]](https://github.com/apps/github-actions) * fix(userspace/falco): when counting `-M` timeout, do not account for async events [[#3505](https://github.com/falcosecurity/falco/pull/3505)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `d8415c1` to `1d2c6b1` [[#3504](https://github.com/falcosecurity/falco/pull/3504)] - [@dependabot[bot]](https://github.com/apps/dependabot) * docs(proposals): correct typo in example [[#3499](https://github.com/falcosecurity/falco/pull/3499)] - [@leogr](https://github.com/leogr) * fix(docker): fixed entrypoints paths with new docker context. [[#3492](https://github.com/falcosecurity/falco/pull/3492)] - [@FedeDP](https://github.com/FedeDP) * feat(falco/app): move actions not using config before `load_config` [[#3483](https://github.com/falcosecurity/falco/pull/3483)] - [@ekoops](https://github.com/ekoops) * refactor(falco/app): apply early return pattern in actions code [[#3484](https://github.com/falcosecurity/falco/pull/3484)] - [@ekoops](https://github.com/ekoops) * chore(deps): Bump submodules/falcosecurity-rules from `abf6637` to `d8415c1` [[#3489](https://github.com/falcosecurity/falco/pull/3489)] - [@dependabot[bot]](https://github.com/apps/dependabot) * Add NETWAYS Web Services to ADOPTERS.md [[#3487](https://github.com/falcosecurity/falco/pull/3487)] - [@mocdaniel](https://github.com/mocdaniel) * chore: add back Falco static package to the release template. [[#3472](https://github.com/falcosecurity/falco/pull/3472)] - [@FedeDP](https://github.com/FedeDP) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 36 | | Release note | 17 | | Total | 53 | ## v0.40.0 Released on 2025-01-28 ### Breaking Changes :warning: * cleanup(userspac/falco)!: drop deprecated options. [[#3361](https://github.com/falcosecurity/falco/pull/3361)] - [@FedeDP](https://github.com/FedeDP) ### Major Changes * new(docker): streamline docker images [[#3273](https://github.com/falcosecurity/falco/pull/3273)] - [@FedeDP](https://github.com/FedeDP) * new(build): reintroduce static build [[#3428](https://github.com/falcosecurity/falco/pull/3428)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(cmake,ci): added support for using jemalloc allocator instead of glibc one and use it by default for release artifacts [[#3406](https://github.com/falcosecurity/falco/pull/3406)] - [@FedeDP](https://github.com/FedeDP) * new(userspace,cmake): honor new plugins exposed suggested output formats [[#3388](https://github.com/falcosecurity/falco/pull/3388)] - [@FedeDP](https://github.com/FedeDP) * new(userspace/falco): allow entirely disabling plugin hostinfo support. [[#3412](https://github.com/falcosecurity/falco/pull/3412)] - [@FedeDP](https://github.com/FedeDP) * new(ci): use `zig` compiler instead of relying on centos7. [[#3307](https://github.com/falcosecurity/falco/pull/3307)] - [@FedeDP](https://github.com/FedeDP) * new(falco): add buffer_format_base64 option, deprecate -b [[#3358](https://github.com/falcosecurity/falco/pull/3358)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(falco): add base_syscalls.all option to falco.yaml, deprecate -A [[#3352](https://github.com/falcosecurity/falco/pull/3352)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(falco): add falco_libs.snaplen option, deprecate -S / --snaplen [[#3362](https://github.com/falcosecurity/falco/pull/3362)] - [@LucaGuerra](https://github.com/LucaGuerra) ### Minor Changes * update(cmake): bump falcoctl to v0.11.0 [[#3467](https://github.com/falcosecurity/falco/pull/3467)] - [@alacuku](https://github.com/alacuku) * chore(ci): add attestation for falco [[#3216](https://github.com/falcosecurity/falco/pull/3216)] - [@cpanato](https://github.com/cpanato) * chore(ci): build Falco in RelWithDebInfo, and upload Falco debug symbols as github artifacts [[#3452](https://github.com/falcosecurity/falco/pull/3452)] - [@FedeDP](https://github.com/FedeDP) * update(build): DEB and RPM package requirements for dkms and kernel-devel are now suggestions [[#3450](https://github.com/falcosecurity/falco/pull/3450)] - [@jthiltges](https://github.com/jthiltges) ### Bug Fixes * fix(userspace/falco): fix container_engines.cri.sockets not loading from config file [[#3453](https://github.com/falcosecurity/falco/pull/3453)] - [@zayaanmoez](https://github.com/zayaanmoez) * fix(docker): /usr/src/'*' no longer created if $HOST_PATH/usr/src didn't exist at startup [[#3434](https://github.com/falcosecurity/falco/pull/3434)] - [@shane-lawrence](https://github.com/shane-lawrence) * fix(docker): add brotli to the Falco image [[#3399](https://github.com/falcosecurity/falco/pull/3399)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(userspace/engine): explicitly disallow appending/modifying a rule with different sources [[#3383](https://github.com/falcosecurity/falco/pull/3383)] - [@mstemm](https://github.com/mstemm) ### Non user-facing changes * chore(falco.yaml): remove comments about cri cli arguments [[#3458](https://github.com/falcosecurity/falco/pull/3458)] - [@alacuku](https://github.com/alacuku) * fix(ci): fixed reusable_build/publish_docker workflows. [[#3459](https://github.com/falcosecurity/falco/pull/3459)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): update libs and driver to latest master [[#3455](https://github.com/falcosecurity/falco/pull/3455)] - [@github-actions[bot]](https://github.com/apps/github-actions) * chore(ci): bumped actions/upload-download-artifact. [[#3454](https://github.com/falcosecurity/falco/pull/3454)] - [@FedeDP](https://github.com/FedeDP) * chore(docker): drop unused libelf dep from container images [[#3451](https://github.com/falcosecurity/falco/pull/3451)] - [@leogr](https://github.com/leogr) * chore(docs): update `plugins_hostinfo` config file comment. [[#3449](https://github.com/falcosecurity/falco/pull/3449)] - [@FedeDP](https://github.com/FedeDP) * new(build): add RelWithDebInfo target [[#3440](https://github.com/falcosecurity/falco/pull/3440)] - [@shane-lawrence](https://github.com/shane-lawrence) * chore(deps): Bump submodules/falcosecurity-rules from `283a62f` to `abf6637` [[#3448](https://github.com/falcosecurity/falco/pull/3448)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(ci): use 4cpu-16gb arm runners [[#3447](https://github.com/falcosecurity/falco/pull/3447)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(cmake): update libs and driver to latest master [[#3439](https://github.com/falcosecurity/falco/pull/3439)] - [@github-actions[bot]](https://github.com/apps/github-actions) * chore: avoid deprecated funcs to calculate sha256 [[#3442](https://github.com/falcosecurity/falco/pull/3442)] - [@federico-sysdig](https://github.com/federico-sysdig) * chore(ci): enable jemalloc in musl build. [[#3436](https://github.com/falcosecurity/falco/pull/3436)] - [@FedeDP](https://github.com/FedeDP) * docs(falco.yaml): correct `buffered_outputs` description [[#3427](https://github.com/falcosecurity/falco/pull/3427)] - [@leogr](https://github.com/leogr) * fix(userspace/falco): use correct filtercheck_field_info. [[#3426](https://github.com/falcosecurity/falco/pull/3426)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): update libs and driver to latest master [[#3421](https://github.com/falcosecurity/falco/pull/3421)] - [@github-actions[bot]](https://github.com/apps/github-actions) * fix: update the url for the docs about the concurrent queue classes [[#3415](https://github.com/falcosecurity/falco/pull/3415)] - [@Issif](https://github.com/Issif) * update(changelog): updated changelog for 0.39.2. [[#3410](https://github.com/falcosecurity/falco/pull/3410)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): update libs and driver to latest master [[#3392](https://github.com/falcosecurity/falco/pull/3392)] - [@github-actions[bot]](https://github.com/apps/github-actions) * fix(cmake,docker): avoid cpp-httplib requiring brotli. [[#3400](https://github.com/falcosecurity/falco/pull/3400)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `407e997` to `283a62f` [[#3391](https://github.com/falcosecurity/falco/pull/3391)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake): bump libs to latest master. [[#3389](https://github.com/falcosecurity/falco/pull/3389)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): update libs and driver to latest master [[#3385](https://github.com/falcosecurity/falco/pull/3385)] - [@github-actions[bot]](https://github.com/apps/github-actions) * Make enable()/disable() virtual so they can be overridden [[#3375](https://github.com/falcosecurity/falco/pull/3375)] - [@mstemm](https://github.com/mstemm) * fix(ci): fixed shasum computation for bump-libs CI. [[#3379](https://github.com/falcosecurity/falco/pull/3379)] - [@FedeDP](https://github.com/FedeDP) * chore(ci): use redhat advised method to check rpmsign success. [[#3376](https://github.com/falcosecurity/falco/pull/3376)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `e38fb3f` to `407e997` [[#3374](https://github.com/falcosecurity/falco/pull/3374)] - [@dependabot[bot]](https://github.com/apps/dependabot) * Compile output clone [[#3364](https://github.com/falcosecurity/falco/pull/3364)] - [@mstemm](https://github.com/mstemm) * fix(ci): fixed bump-libs workflow syntax. [[#3369](https://github.com/falcosecurity/falco/pull/3369)] - [@FedeDP](https://github.com/FedeDP) * new(ci): add a workflow to automatically bump libs on each monday. [[#3360](https://github.com/falcosecurity/falco/pull/3360)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `b6ad373` to `e38fb3f` [[#3365](https://github.com/falcosecurity/falco/pull/3365)] - [@dependabot[bot]](https://github.com/apps/dependabot) * cleanup(falco): reformat options::define [[#3356](https://github.com/falcosecurity/falco/pull/3356)] - [@LucaGuerra](https://github.com/LucaGuerra) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 31 | | Release note | 18 | | Total | 49 | ## v0.39.2 Released on 2024-11-21 ### Minor Changes * update(cmake): bumped falcoctl to v0.10.1. [[#3408](https://github.com/falcosecurity/falco/pull/3408)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): bump yaml-cpp to latest master. [[#3394](https://github.com/falcosecurity/falco/pull/3394)] - [@FedeDP](https://github.com/FedeDP) ### Non user-facing changes * update(ci): use arm64 CNCF runners for GH actions [[#3386](https://github.com/falcosecurity/falco/pull/3386)] - [@LucaGuerra](https://github.com/LucaGuerra) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 1 | | Release note | 2 | | Total | 3 | ## v0.39.1 Released on 2024-10-09 ### Bug Fixes * fix(engine): allow null init_config for plugin info [[#3372](https://github.com/falcosecurity/falco/pull/3372)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(engine): fix parsing issues in -o key={object} when the object definition contains a comma [[#3363](https://github.com/falcosecurity/falco/pull/3363)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(userspace/falco): fix event set selection for plugin with parsing capability [[#3368](https://github.com/falcosecurity/falco/pull/3368)] - [@FedeDP](https://github.com/FedeDP) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 0 | | Release note | 3 | | Total | 3 | ## v0.39.0 Released on 2024-10-01 ### Breaking Changes :warning: * fix(falco_metrics)!: split tags label into multiple `tag_`-prefixed labels [[#3337](https://github.com/falcosecurity/falco/pull/3337)] - [@ekoops](https://github.com/ekoops) * fix(falco_metrics)!: use full name for configs and rules files [[#3337](https://github.com/falcosecurity/falco/pull/3337)] - [@ekoops](https://github.com/ekoops) * update(falco_metrics)!: rearrange `n_evts_cpu` and `n_drops_cpu` Prometheus metrics to follow best practices [[#3319](https://github.com/falcosecurity/falco/pull/3319)] - [@incertum](https://github.com/incertum) * cleanup(userspace/falco)!: drop deprecated -t,-T,-D options. [[#3311](https://github.com/falcosecurity/falco/pull/3311)] - [@FedeDP](https://github.com/FedeDP) ### Major Changes * feat(stats): add host_netinfo networking information stats family [[#3344](https://github.com/falcosecurity/falco/pull/3344)] - [@ekoops](https://github.com/ekoops) * new(falco): add json_include_message_property to have a message field without date and priority [[#3314](https://github.com/falcosecurity/falco/pull/3314)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(userspace/falco,userspace/engine): rule json schema validation [[#3313](https://github.com/falcosecurity/falco/pull/3313)] - [@FedeDP](https://github.com/FedeDP) * new(falco): introduce append_output configuration [[#3308](https://github.com/falcosecurity/falco/pull/3308)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(userspace/falco): added --config-schema action to print config schema [[#3312](https://github.com/falcosecurity/falco/pull/3312)] - [@FedeDP](https://github.com/FedeDP) * new(falco): enable CLI options with -o key={object} [[#3310](https://github.com/falcosecurity/falco/pull/3310)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(config): add `container_engines` config to falco.yaml [[#3266](https://github.com/falcosecurity/falco/pull/3266)] - [@incertum](https://github.com/incertum) * new(metrics): add host_ifinfo metric [[#3253](https://github.com/falcosecurity/falco/pull/3253)] - [@incertum](https://github.com/incertum) * new(userspace,unit_tests): validate configs against schema [[#3302](https://github.com/falcosecurity/falco/pull/3302)] - [@FedeDP](https://github.com/FedeDP) ### Minor Changes * update(falco): upgrade libs to 0.18.1 [[#3349](https://github.com/falcosecurity/falco/pull/3349)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(systemd): users can refer to systemd falco services with a consistent unique alias falco.service [[#3332](https://github.com/falcosecurity/falco/pull/3332)] - [@ekoops](https://github.com/ekoops) * update(cmake): bump libs to 0.18.0 and driver to 7.3.0+driver. [[#3330](https://github.com/falcosecurity/falco/pull/3330)] - [@FedeDP](https://github.com/FedeDP) * chore(userspace/falco): deprecate `cri` related CLI options. [[#3329](https://github.com/falcosecurity/falco/pull/3329)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): bumped falcoctl to v0.10.0 and rules to 3.2.0 [[#3327](https://github.com/falcosecurity/falco/pull/3327)] - [@FedeDP](https://github.com/FedeDP) * update(falco_metrics): change prometheus rules metric naming [[#3324](https://github.com/falcosecurity/falco/pull/3324)] - [@incertum](https://github.com/incertum) ### Bug Fixes * fix(falco): allow disable_cri_async from both CLI and config [[#3353](https://github.com/falcosecurity/falco/pull/3353)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(engine): sync outputs before printing stats at shutdown [[#3338](https://github.com/falcosecurity/falco/pull/3338)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(falco): allow plugin init_config map in json schema [[#3335](https://github.com/falcosecurity/falco/pull/3335)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(userspace/falco): properly account for plugin with CAP_PARSING when computing interesting sc set [[#3334](https://github.com/falcosecurity/falco/pull/3334)] - [@FedeDP](https://github.com/FedeDP) ### Non user-facing changes * feat(cmake): add conditional builds for falcoctl and rules paths [[#3305](https://github.com/falcosecurity/falco/pull/3305)] - [@tembleking](https://github.com/tembleking) * cleanup(falco): ignore lint commit [[#3354](https://github.com/falcosecurity/falco/pull/3354)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(falco): apply code formatting [[#3350](https://github.com/falcosecurity/falco/pull/3350)] - [@poiana](https://github.com/poiana) * chore: ignore_some_files for clang format [[#3351](https://github.com/falcosecurity/falco/pull/3351)] - [@Andreagit97](https://github.com/Andreagit97) * sync: release 0.39.x [[#3340](https://github.com/falcosecurity/falco/pull/3340)] - [@FedeDP](https://github.com/FedeDP) * fix(userspace/engine): improve rule json schema to account for `source` and `required_plugin_versions` [[#3328](https://github.com/falcosecurity/falco/pull/3328)] - [@FedeDP](https://github.com/FedeDP) * cleanup(falco): use header file for json schema [[#3325](https://github.com/falcosecurity/falco/pull/3325)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(engine): modify append_output format [[#3322](https://github.com/falcosecurity/falco/pull/3322)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore: scaffolding for enabling code formatting [[#3321](https://github.com/falcosecurity/falco/pull/3321)] - [@Andreagit97](https://github.com/Andreagit97) * update(cmake): bump libs and driver to 0.18.0-rc1. [[#3320](https://github.com/falcosecurity/falco/pull/3320)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): restore master and release CI workflow permissions. [[#3317](https://github.com/falcosecurity/falco/pull/3317)] - [@FedeDP](https://github.com/FedeDP) * fixed the token-permission and pinned-dependencies issue [[#3299](https://github.com/falcosecurity/falco/pull/3299)] - [@harshitasao](https://github.com/harshitasao) * update(cmake): bump falcoctl to v0.10.0-rc1 [[#3316](https://github.com/falcosecurity/falco/pull/3316)] - [@alacuku](https://github.com/alacuku) * ci(insecure-api): update semgrep docker image [[#3315](https://github.com/falcosecurity/falco/pull/3315)] - [@francesco-furlan](https://github.com/francesco-furlan) * Add demo environment instructions and docker-config files [[#3295](https://github.com/falcosecurity/falco/pull/3295)] - [@bbl232](https://github.com/bbl232) * chore(deps): Bump submodules/falcosecurity-rules from `baecf18` to `b6ad373` [[#3301](https://github.com/falcosecurity/falco/pull/3301)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake): bump libs and driver to latest master [[#3283](https://github.com/falcosecurity/falco/pull/3283)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore(deps): Bump submodules/falcosecurity-rules from `342b20d` to `baecf18` [[#3298](https://github.com/falcosecurity/falco/pull/3298)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore(deps): Bump submodules/falcosecurity-rules from `068f0f2` to `342b20d` [[#3288](https://github.com/falcosecurity/falco/pull/3288)] - [@dependabot[bot]](https://github.com/apps/dependabot) * vote: add sgaist to OWNERS [[#3264](https://github.com/falcosecurity/falco/pull/3264)] - [@sgaist](https://github.com/sgaist) * Add Tulip Retail to adopters list [[#3291](https://github.com/falcosecurity/falco/pull/3291)] - [@bbl232](https://github.com/bbl232) * chore(deps): Bump submodules/falcosecurity-rules from `28b98b6` to `068f0f2` [[#3282](https://github.com/falcosecurity/falco/pull/3282)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore(deps): Bump submodules/falcosecurity-rules from `c0a9bf1` to `28b98b6` [[#3267](https://github.com/falcosecurity/falco/pull/3267)] - [@dependabot[bot]](https://github.com/apps/dependabot) * Added the OpenSSF Scorecard Badge [[#3250](https://github.com/falcosecurity/falco/pull/3250)] - [@harshitasao](https://github.com/harshitasao) * chore(deps): Bump submodules/falcosecurity-rules from `ea57e78` to `c0a9bf1` [[#3247](https://github.com/falcosecurity/falco/pull/3247)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake,userspace): bump libs and driver to latest master. [[#3263](https://github.com/falcosecurity/falco/pull/3263)] - [@FedeDP](https://github.com/FedeDP) * If rule compilation fails, return immediately [[#3260](https://github.com/falcosecurity/falco/pull/3260)] - [@mstemm](https://github.com/mstemm) * new(userspace/engine): generalize indexable ruleset [[#3251](https://github.com/falcosecurity/falco/pull/3251)] - [@mstemm](https://github.com/mstemm) * update(cmake): bump libs to master. [[#3249](https://github.com/falcosecurity/falco/pull/3249)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `df963b6` to `ea57e78` [[#3240](https://github.com/falcosecurity/falco/pull/3240)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore(ci): enable dummy tests on the testing framework. [[#3233](https://github.com/falcosecurity/falco/pull/3233)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `679a50a` to `df963b6` [[#3231](https://github.com/falcosecurity/falco/pull/3231)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake): bump libs and driver to master. [[#3225](https://github.com/falcosecurity/falco/pull/3225)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump submodules/falcosecurity-rules from `9e56293` to `679a50a` [[#3222](https://github.com/falcosecurity/falco/pull/3222)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(docs): update CHANGELOG for 0.38.0 (master branch) [[#3224](https://github.com/falcosecurity/falco/pull/3224)] - [@LucaGuerra](https://github.com/LucaGuerra) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 35 | | Release note | 22 | | Total | 57 | ## v0.38.2 Released on 2024-08-19 ### Bug Fixes * fix(engine): fix metrics names to better adhere to best practices [[#3272](https://github.com/falcosecurity/falco/pull/3272)] - [@incertum](https://github.com/incertum) * fix(ci): use vault.centos.org for centos:7 CI build. [[#3274](https://github.com/falcosecurity/falco/pull/3274)] - [@FedeDP](https://github.com/FedeDP) ## v0.38.1 Released on 2024-06-19 ### Major Changes * new(metrics): enable plugins metrics [[#3228](https://github.com/falcosecurity/falco/pull/3228)] - [@mrgian](https://github.com/mrgian) ### Minor Changes * cleanup(falco): clarify that --print variants only affect syscalls [[#3238](https://github.com/falcosecurity/falco/pull/3238)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(engine): enable -p option for all sources, -pk, -pc etc only for syscall sources [[#3239](https://github.com/falcosecurity/falco/pull/3239)] - [@LucaGuerra](https://github.com/LucaGuerra) ### Bug Fixes * fix(engine): enable output substitution only for syscall rules, prevent engine from exiting with validation errors when a plugin is loaded and -pc/pk is specified [[#3236](https://github.com/falcosecurity/falco/pull/3236)] - [@mrgian](https://github.com/mrgian) * fix(metrics): allow each metric output channel to be selected independently [[#3232](https://github.com/falcosecurity/falco/pull/3232)] - [@incertum](https://github.com/incertum) * fix(userspace/falco): fixed `falco_metrics::to_text` implementation when running with plugins [[#3230](https://github.com/falcosecurity/falco/pull/3230)] - [@FedeDP](https://github.com/FedeDP) ### Statistics | MERGED PRS | NUMBER | |-----------------|--------| | Not user-facing | 0 | | Release note | 6 | | Total | 6 | ## v0.38.0 Released on 2024-05-30 ### Breaking Changes :warning: * new(scripts,docker)!: enable automatic driver selection logic in packages and docker images. Modern eBPF is now also the default driver and the highest priority one in the new driver selection logic. [[#3154](https://github.com/falcosecurity/falco/pull/3154)] - [@FedeDP](https://github.com/FedeDP) * cleanup(falco.yaml)!: remove some deprecated configs [[#3087](https://github.com/falcosecurity/falco/pull/3087)] - [@Andreagit97](https://github.com/Andreagit97) * cleanup(docker)!: remove unused builder dockerfile [[#3088](https://github.com/falcosecurity/falco/pull/3088)] - [@Andreagit97](https://github.com/Andreagit97) More details: https://falco.org/blog/falco-0-38-0/#breaking-changes-and-deprecations ### Major Changes * new(webserver): a metrics endpoint has been added providing prometheus metrics. It can be optionally enabled using the new `metrics.prometheus_enabled` configuration option. It will only be activated if the `metrics.enabled` is true as well. [[#3140](https://github.com/falcosecurity/falco/pull/3140)] - [@sgaist](https://github.com/sgaist) * new(metrics): add `rules_counters_enabled` option [[#3192](https://github.com/falcosecurity/falco/pull/3192)] - [@incertum](https://github.com/incertum) * new(build): provide signatures for .tar.gz packages [[#3201](https://github.com/falcosecurity/falco/pull/3201)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(engine): add print_enabled_rules_falco_logger when log_level debug [[#3189](https://github.com/falcosecurity/falco/pull/3189)] - [@incertum](https://github.com/incertum) * new(falco): allow selecting which rules to load from the configuration file or command line [[#3178](https://github.com/falcosecurity/falco/pull/3178)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(metrics): add file sha256sum metrics for loaded config and rules files [[#3187](https://github.com/falcosecurity/falco/pull/3187)] - [@incertum](https://github.com/incertum) * new(engine): throw an error when an invalid macro/list name is used [[#3116](https://github.com/falcosecurity/falco/pull/3116)] - [@mrgian](https://github.com/mrgian) * new(engine): raise warning instead of error on invalid macro/list name [[#3167](https://github.com/falcosecurity/falco/pull/3167)] - [@mrgian](https://github.com/mrgian) * new(userspace): support split config files [[#3024](https://github.com/falcosecurity/falco/pull/3024)] - [@FedeDP](https://github.com/FedeDP) * new(engine): enforce unique exceptions names [[#3134](https://github.com/falcosecurity/falco/pull/3134)] - [@mrgian](https://github.com/mrgian) * new(engine): add warning when appending an exception with no values [[#3133](https://github.com/falcosecurity/falco/pull/3133)] - [@mrgian](https://github.com/mrgian) * feat(metrics): coherent metrics stats model including few metrics naming changes [[#3129](https://github.com/falcosecurity/falco/pull/3129)] - [@incertum](https://github.com/incertum) * new(config): add `falco_libs.thread_table_size` [[#3071](https://github.com/falcosecurity/falco/pull/3071)] - [@incertum](https://github.com/incertum) * new(proposals): introduce on host anomaly detection framework [[#2655](https://github.com/falcosecurity/falco/pull/2655)] - [@incertum](https://github.com/incertum) ### Minor Changes * update(cmake): bump falcoctl to v0.8.0. [[#3219](https://github.com/falcosecurity/falco/pull/3219)] - [@FedeDP](https://github.com/FedeDP) * update(rules): update falco-rules to 3.1.0 [[#3217](https://github.com/falcosecurity/falco/pull/3217)] - [@LucaGuerra](https://github.com/LucaGuerra) * refactor(userspace): move falco logger under falco engine [[#3208](https://github.com/falcosecurity/falco/pull/3208)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore(docs): apply features adoption and deprecation proposal to config file keys [[#3206](https://github.com/falcosecurity/falco/pull/3206)] - [@FedeDP](https://github.com/FedeDP) * cleanup(metrics): add original rule name as label [[#3205](https://github.com/falcosecurity/falco/pull/3205)] - [@incertum](https://github.com/incertum) * update(falco): deprecate options -T, -t and -D [[#3193](https://github.com/falcosecurity/falco/pull/3193)] - [@LucaGuerra](https://github.com/LucaGuerra) * refactor: bump libs and driver, support field modifiers [[#3186](https://github.com/falcosecurity/falco/pull/3186)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore(userspace/falco): deprecated old 'rules_file' config key [[#3162](https://github.com/falcosecurity/falco/pull/3162)] - [@FedeDP](https://github.com/FedeDP) * chore(falco): update falco libs and driver to master (Apr 8th 2024) [[#3158](https://github.com/falcosecurity/falco/pull/3158)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(build): update libs to 026ffe1d8f1b25c6ccdc09afa2c02afdd3e3f672 [[#3151](https://github.com/falcosecurity/falco/pull/3151)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup: minor adjustments to readme, add new testing section [[#3072](https://github.com/falcosecurity/falco/pull/3072)] - [@incertum](https://github.com/incertum) * refactor(userspace/engine): reduce allocations during rules loading [[#3065](https://github.com/falcosecurity/falco/pull/3065)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(CI): publish wasm package as dev-wasm [[#3017](https://github.com/falcosecurity/falco/pull/3017)] - [@Rohith-Raju](https://github.com/Rohith-Raju) ### Bug Fixes * fix(userspace/falco): fix state initialization avoid a crash during hot reload [[#3190](https://github.com/falcosecurity/falco/pull/3190)] - [@FedeDP](https://github.com/FedeDP) * fix(userspace/engine): make sure exception fields are not optional in replace mode [[#3108](https://github.com/falcosecurity/falco/pull/3108)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(docker): added zstd to driver loader images [[#3203](https://github.com/falcosecurity/falco/pull/3203)] - [@FedeDP](https://github.com/FedeDP) * fix(engine): raise warning instead of error on not-unique exceptions names [[#3159](https://github.com/falcosecurity/falco/pull/3159)] - [@mrgian](https://github.com/mrgian) * fix(engine): apply output substitutions for all sources [[#3135](https://github.com/falcosecurity/falco/pull/3135)] - [@mrgian](https://github.com/mrgian) * fix(userspace/configuration): make sure that folders that would trigger permission denied are not traversed [[#3127](https://github.com/falcosecurity/falco/pull/3127)] - [@sgaist](https://github.com/sgaist) * fix(engine): logical issue in exceptions condition [[#3115](https://github.com/falcosecurity/falco/pull/3115)] - [@mrgian](https://github.com/mrgian) * fix(cmake): properly let falcoctl cmake module create /usr/share/falco/plugins/ folder. [[#3105](https://github.com/falcosecurity/falco/pull/3105)] - [@FedeDP](https://github.com/FedeDP) ### Non user-facing changes * update(scripts/falcoctl): bump falco-rules version to 3 [[#3128](https://github.com/falcosecurity/falco/pull/3128)] - [@alacuku](https://github.com/alacuku) * build(deps): Bump submodules/falcosecurity-rules from `59bf03b` to `9e56293` [[#3212](https://github.com/falcosecurity/falco/pull/3212)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore(gha): update cosign to v3.5.0 [[#3209](https://github.com/falcosecurity/falco/pull/3209)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `29c41c4` to `59bf03b` [[#3207](https://github.com/falcosecurity/falco/pull/3207)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake): bumped libs to 0.17.0-rc1 and falcoctl to v0.8.0-rc6. [[#3204](https://github.com/falcosecurity/falco/pull/3204)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-rules from `3f668d0` to `3cac61c` [[#3044](https://github.com/falcosecurity/falco/pull/3044)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-testing from `ae3950a` to `7abf76f` [[#3094](https://github.com/falcosecurity/falco/pull/3094)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(ci): enforce bundled deps OFF in build-dev CI [[#3118](https://github.com/falcosecurity/falco/pull/3118)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-rules from `88a40c8` to `869c9a7` [[#3156](https://github.com/falcosecurity/falco/pull/3156)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake): bumped falcoctl to v0.8.0-rc5. [[#3199](https://github.com/falcosecurity/falco/pull/3199)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-rules from `4f153f5` to `29c41c4` [[#3198](https://github.com/falcosecurity/falco/pull/3198)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake): bump falcoctl to v0.8.0-rc4 [[#3191](https://github.com/falcosecurity/falco/pull/3191)] - [@FedeDP](https://github.com/FedeDP) * refactor: smart pointer usage [[#3184](https://github.com/falcosecurity/falco/pull/3184)] - [@federico-sysdig](https://github.com/federico-sysdig) * build(deps): Bump submodules/falcosecurity-rules from `ec255e6` to `4f153f5` [[#3182](https://github.com/falcosecurity/falco/pull/3182)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake): bumped libs and driver to latest master. [[#3177](https://github.com/falcosecurity/falco/pull/3177)] - [@FedeDP](https://github.com/FedeDP) * chore(cmake): enable modern bpf build by default. [[#3180](https://github.com/falcosecurity/falco/pull/3180)] - [@FedeDP](https://github.com/FedeDP) * cleanup(docs): fix typo in license blocks [[#3175](https://github.com/falcosecurity/falco/pull/3175)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(docker,scripts): set old eBPF probe as lowest priority driver. [[#3173](https://github.com/falcosecurity/falco/pull/3173)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-rules from `869c9a7` to `ec255e6` [[#3170](https://github.com/falcosecurity/falco/pull/3170)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(app): close inspectors at teardown time [[#3169](https://github.com/falcosecurity/falco/pull/3169)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(docker): fixed docker entrypoints for driver loading. [[#3168](https://github.com/falcosecurity/falco/pull/3168)] - [@FedeDP](https://github.com/FedeDP) * fix(docker,scripts): do not load falcoctl driver loader when installing Falco deb package in docker images [[#3166](https://github.com/falcosecurity/falco/pull/3166)] - [@FedeDP](https://github.com/FedeDP) * update(ci): build both release and debug versions [[#3161](https://github.com/falcosecurity/falco/pull/3161)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(userspace/falco): watch all configs files. [[#3160](https://github.com/falcosecurity/falco/pull/3160)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): update scorecard-action to v2.3.1 [[#3153](https://github.com/falcosecurity/falco/pull/3153)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup(falco): consolidate falco::grpc::server in one class [[#3150](https://github.com/falcosecurity/falco/pull/3150)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(build): enable ASan and UBSan builds with options and in CI [[#3147](https://github.com/falcosecurity/falco/pull/3147)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(userspace): variable / function shadowing [[#3123](https://github.com/falcosecurity/falco/pull/3123)] - [@sgaist](https://github.com/sgaist) * build(deps): Bump submodules/falcosecurity-rules from `fbf0a4e` to `88a40c8` [[#3145](https://github.com/falcosecurity/falco/pull/3145)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(cmake): fix USE_BUNDLED_DEPS=ON and BUILD_FALCO_UNIT_TESTS=ON [[#3146](https://github.com/falcosecurity/falco/pull/3146)] - [@LucaGuerra](https://github.com/LucaGuerra) * Add --kernelversion and --kernelrelease options to falco driver loader entrypoint [[#3143](https://github.com/falcosecurity/falco/pull/3143)] - [@Sryther](https://github.com/Sryther) * build(deps): Bump submodules/falcosecurity-rules from `44addef` to `fbf0a4e` [[#3139](https://github.com/falcosecurity/falco/pull/3139)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore: bump to latest libs commit [[#3137](https://github.com/falcosecurity/falco/pull/3137)] - [@Andreagit97](https://github.com/Andreagit97) * refactor: Use FetchContent for integrating three bundled libs [[#3107](https://github.com/falcosecurity/falco/pull/3107)] - [@federico-sysdig](https://github.com/federico-sysdig) * build(deps): Bump submodules/falcosecurity-rules from `dc7970d` to `44addef` [[#3136](https://github.com/falcosecurity/falco/pull/3136)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `f88b991` to `dc7970d` [[#3126](https://github.com/falcosecurity/falco/pull/3126)] - [@dependabot[bot]](https://github.com/apps/dependabot) * refactor(ci): Avoid using command make directly [[#3101](https://github.com/falcosecurity/falco/pull/3101)] - [@federico-sysdig](https://github.com/federico-sysdig) * docs(proposal): 20231220-features-adoption-and-deprecation.md [[#2986](https://github.com/falcosecurity/falco/pull/2986)] - [@leogr](https://github.com/leogr) * build(deps): Bump submodules/falcosecurity-rules from `b499a1d` to `f88b991` [[#3125](https://github.com/falcosecurity/falco/pull/3125)] - [@dependabot[bot]](https://github.com/apps/dependabot) * docs(README.md): Falco Graduates within the CNCF [[#3124](https://github.com/falcosecurity/falco/pull/3124)] - [@leogr](https://github.com/leogr) * build(deps): Bump submodules/falcosecurity-rules from `497e011` to `b499a1d` [[#3111](https://github.com/falcosecurity/falco/pull/3111)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore(ci): bumped codeql actions. [[#3114](https://github.com/falcosecurity/falco/pull/3114)] - [@FedeDP](https://github.com/FedeDP) * Cleanup warnings and smart ptrs [[#3112](https://github.com/falcosecurity/falco/pull/3112)] - [@federico-sysdig](https://github.com/federico-sysdig) * new(build): add options to use bundled dependencies [[#3092](https://github.com/falcosecurity/falco/pull/3092)] - [@mrgian](https://github.com/mrgian) * fix(ci): test-dev-packages-arm64 needs build-dev-packages-arm64. [[#3110](https://github.com/falcosecurity/falco/pull/3110)] - [@FedeDP](https://github.com/FedeDP) * refactor: bump libs and driver, and adopt unique pointers wherever possible [[#3109](https://github.com/falcosecurity/falco/pull/3109)] - [@jasondellaluce](https://github.com/jasondellaluce) * cleanup: falco_engine test fixture [[#3099](https://github.com/falcosecurity/falco/pull/3099)] - [@federico-sysdig](https://github.com/federico-sysdig) * refactor: test AtomicSignalHandler.handle_once_wait_consistency [[#3100](https://github.com/falcosecurity/falco/pull/3100)] - [@federico-sysdig](https://github.com/federico-sysdig) * Cleanup variable use [[#3097](https://github.com/falcosecurity/falco/pull/3097)] - [@sgaist](https://github.com/sgaist) * cleanup(submodules): dropped testing submodule. [[#3098](https://github.com/falcosecurity/falco/pull/3098)] - [@FedeDP](https://github.com/FedeDP) * cleanup(ci): make use of falcosecurity/testing provided composite action [[#3093](https://github.com/falcosecurity/falco/pull/3093)] - [@FedeDP](https://github.com/FedeDP) * Improve const correctness [[#3083](https://github.com/falcosecurity/falco/pull/3083)] - [@sgaist](https://github.com/sgaist) * Improve exception throwing [[#3085](https://github.com/falcosecurity/falco/pull/3085)] - [@sgaist](https://github.com/sgaist) * fix(ci): update sync in deb and rpm scripts with acl [[#3062](https://github.com/falcosecurity/falco/pull/3062)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup(tests): consolidate Falco engine and rule loader tests [[#3066](https://github.com/falcosecurity/falco/pull/3066)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup: falco_engine deps and include paths [[#3090](https://github.com/falcosecurity/falco/pull/3090)] - [@federico-sysdig](https://github.com/federico-sysdig) * fix: Some compiler warnings [[#3089](https://github.com/falcosecurity/falco/pull/3089)] - [@federico-sysdig](https://github.com/federico-sysdig) * build(deps): Bump submodules/falcosecurity-rules from `0f60976` to `497e011` [[#3081](https://github.com/falcosecurity/falco/pull/3081)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(c++): add missing explicit to single argument constructors [[#3069](https://github.com/falcosecurity/falco/pull/3069)] - [@sgaist](https://github.com/sgaist) * Improve class initialization [[#3074](https://github.com/falcosecurity/falco/pull/3074)] - [@sgaist](https://github.com/sgaist) * build(deps): Bump submodules/falcosecurity-rules from `6ed2036` to `0f60976` [[#3078](https://github.com/falcosecurity/falco/pull/3078)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `1053b2d` to `6ed2036` [[#3067](https://github.com/falcosecurity/falco/pull/3067)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(c++): add missing overrides [[#3064](https://github.com/falcosecurity/falco/pull/3064)] - [@sgaist](https://github.com/sgaist) * new(build): prune deb-dev and rpm-dev directories [[#3056](https://github.com/falcosecurity/falco/pull/3056)] - [@LucaGuerra](https://github.com/LucaGuerra) * refactor(userspace): align falco to gen-event class family deprecation [[#3051](https://github.com/falcosecurity/falco/pull/3051)] - [@jasondellaluce](https://github.com/jasondellaluce) * build(deps): Bump submodules/falcosecurity-rules from `3cac61c` to `1053b2d` [[#3047](https://github.com/falcosecurity/falco/pull/3047)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix: adopt new libsinsp logger [[#3026](https://github.com/falcosecurity/falco/pull/3026)] - [@therealbobo](https://github.com/therealbobo) * refactor: cleanup libs relative include paths [[#2936](https://github.com/falcosecurity/falco/pull/2936)] - [@therealbobo](https://github.com/therealbobo) * chore(ci): bumped rn2md to latest master. [[#3046](https://github.com/falcosecurity/falco/pull/3046)] - [@FedeDP](https://github.com/FedeDP) * Support alternate rules loader [[#3008](https://github.com/falcosecurity/falco/pull/3008)] - [@mstemm](https://github.com/mstemm) * fix(ci): fixed release body driver version. [[#3042](https://github.com/falcosecurity/falco/pull/3042)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-rules from `c39d31a` to `3f668d0` [[#3039](https://github.com/falcosecurity/falco/pull/3039)] - [@dependabot[bot]](https://github.com/apps/dependabot) ## v0.37.1 Released on 2024-02-13 ### Major Changes * new(docker): added option for insecure http driver download to falco and driver-loader images [[#3058](https://github.com/falcosecurity/falco/pull/3058)] - [@toamto94](https://github.com/toamto94) ### Minor Changes * update(cmake): bumped falcoctl to v0.7.2 [[#3076](https://github.com/falcosecurity/falco/pull/3076)] - [@FedeDP](https://github.com/FedeDP) * update(build): link libelf dynamically [[#3048](https://github.com/falcosecurity/falco/pull/3048)] - [@LucaGuerra](https://github.com/LucaGuerra) ### Bug Fixes * fix(userspace/engine): always consider all rules (even the ones below min_prio) in m_rule_stats_manager [[#3060](https://github.com/falcosecurity/falco/pull/3060)] - [@FedeDP](https://github.com/FedeDP) ### Non user-facing changes * Added http headers option for driver download in docker images [[#3075](https://github.com/falcosecurity/falco/pull/3075)] - [@toamto94](https://github.com/toamto94) * fix(build): install libstdc++ in the Wolfi image [[#3053](https://github.com/falcosecurity/falco/pull/3053)] - [@LucaGuerra](https://github.com/LucaGuerra) ## v0.37.0 Released on 2024-01-30 ### Breaking Changes - The deprecated `rate-limiter` mechanism is removed as it is no longer used. - the deprecated `outputs.rate` Falco config is removed. - the deprecated `outputs.max_burst` Falco config is removed. - The deprecated `--userspace` CLI option is removed as it is no longer used. - The `falco-driver-loader` script will be removed and embedded into falcoctl. The new falcoctl driven implementation will drop: - `--source-only` CLI option. - `BPF_USE_LOCAL_KERNEL_SOURCES` environment variable. - `DRIVER_CURL_OPTIONS` environment variable. - `FALCO_BPF_PROBE` environment variable is not used by the new falcoctl driver loader, since it is already deprecated and will be removed in the next major version. Some env vars were renamed: - `DRIVERS_REPO` env variable has been replaced by `FALCOCTL_DRIVER_NAME` or `--name` command line argument for `falcoctl driver` command - `DRIVERS_NAME` env variable has been replaced by `FALCOCTL_DRIVER_REPOS`, or `--repo` command line argument for `falcoctl driver` command - `DRIVER_KERNEL_RELEASE` env variable has been replaced by `--kernelrelease` command line argument for `falcoctl driver install` command - `DRIVER_KERNEL_VERSION` env variable has been replaced by `--kernelversion` command line argument for `falcoctl driver install` command - `DRIVER_INSECURE_DOWNLOAD` env variable has been replaced by `--http-insecure` command line argument for `falcoctl driver install` command - Remove `-K/-k` options from Falco in favor of the new `k8smeta` plugin. - Drop plugins shipped with Falco since plugins are now be managed by falcoctl. - Falco 0.37.0 allows environment variables to be expanded even if they are part of a string. This introduces small breaking changes: - Previously, environment variables used in YAML that were empty or defined as `“”` would be expanded to the default value. This was not consistent with the way YAML was handled in other cases, where we only returned the default values if the node was not defined. Now expanded env vars retain the same behavior of all other variables. - Falco 0.37.0 will return default value for nodes that cannot be parsed to chosen type. - `program_output` command will be env-expanded at init time, instead of letting `popen` and thus the `sh` shell expand it. This is technically a breaking change even if no behavioral change is expected. Also, you can avoid env var expansion by using `${{FOO}}` instead of `${FOO}`. It will resolve to `${FOO}` and won't be resolved to the env var value. ### Major Changes * new!: dropped falco-driver-loader script in favor of new falcoctl driver command [[#2905](https://github.com/falcosecurity/falco/pull/2905)] - [@FedeDP](https://github.com/FedeDP) * update!: bump libs to latest and deprecation of k8s metadata options and configs [[#2914](https://github.com/falcosecurity/falco/pull/2914)] - [@jasondellaluce](https://github.com/jasondellaluce) * cleanup(falco)!: remove `outputs.rate` and `outputs.max_burst` from Falco config [[#2841](https://github.com/falcosecurity/falco/pull/2841)] - [@Andreagit97](https://github.com/Andreagit97) * cleanup(falco)!: remove `--userspace` support [[#2839](https://github.com/falcosecurity/falco/pull/2839)] - [@Andreagit97](https://github.com/Andreagit97) * new(engine): add selective overrides for Falco rules [[#2981](https://github.com/falcosecurity/falco/pull/2981)] - [@LucaGuerra](https://github.com/LucaGuerra) * feat(userspace/falco): falco administrators can now configure the http output to compress the data sent as well as enable keep alive for the connection. Two new fields (compress_uploads and keep_alive) in the http_output block of the `falco.yaml` file can be used for that purpose. Both are disabled by default. [[#2974](https://github.com/falcosecurity/falco/pull/2974)] - [@sgaist](https://github.com/sgaist) * new(userspace): support env variable expansion in all yaml, even inside strings. [[#2918](https://github.com/falcosecurity/falco/pull/2918)] - [@FedeDP](https://github.com/FedeDP) * new(scripts): add a way to enforce driver kind and falcoctl enablement when installing Falco from packages and dialog is not present. [[#2773](https://github.com/falcosecurity/falco/pull/2773)] - [@vjjmiras](https://github.com/vjjmiras) * new(falco): print system info when Falco starts [[#2927](https://github.com/falcosecurity/falco/pull/2927)] - [@Andreagit97](https://github.com/Andreagit97) * new: driver selection in falco.yaml [[#2413](https://github.com/falcosecurity/falco/pull/2413)] - [@therealbobo](https://github.com/therealbobo) * new(build): enable compilation on win32 and macOS. [[#2889](https://github.com/falcosecurity/falco/pull/2889)] - [@therealbobo](https://github.com/therealbobo) * feat(userspace/falco): falco administrators can now configure the address on which the webserver listen using the new listen_address field in the webserver block of the `falco.yaml` file. [[#2890](https://github.com/falcosecurity/falco/pull/2890)] - [@sgaist](https://github.com/sgaist) ### Minor Changes * update(userspace/falco): add `engine_version_semver` key in `/versions` endpoint [[#2899](https://github.com/falcosecurity/falco/pull/2899)] - [@loresuso](https://github.com/loresuso) * update: default ruleset upgrade to version 3.0 [[#3034](https://github.com/falcosecurity/falco/pull/3034)] - [@leogr](https://github.com/leogr) * update!(config): soft deprecation of drop stats counters in `syscall_event_drops` [[#3015](https://github.com/falcosecurity/falco/pull/3015)] - [@incertum](https://github.com/incertum) * update(cmake): bumped falcoctl tool to v0.7.1. [[#3030](https://github.com/falcosecurity/falco/pull/3030)] - [@FedeDP](https://github.com/FedeDP) * update(rule_loader): deprecate the `append` flag in Falco rules [[#2992](https://github.com/falcosecurity/falco/pull/2992)] - [@Andreagit97](https://github.com/Andreagit97) * cleanup!(cmake): drop bundled plugins in Falco [[#2997](https://github.com/falcosecurity/falco/pull/2997)] - [@FedeDP](https://github.com/FedeDP) * update(config): clarify deprecation notices + list all env vars [[#2988](https://github.com/falcosecurity/falco/pull/2988)] - [@incertum](https://github.com/incertum) * update: now the `watch_config_files` config option monitors file/directory moving and deletion, too [[#2965](https://github.com/falcosecurity/falco/pull/2965)] - [@NitroCao](https://github.com/NitroCao) * update(userspace): enhancements in rule description feature [[#2934](https://github.com/falcosecurity/falco/pull/2934)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(userspace/falco): add libsinsp state metrics option [[#2883](https://github.com/falcosecurity/falco/pull/2883)] - [@incertum](https://github.com/incertum) * update(doc): Add Thought Machine as adopters [[#2919](https://github.com/falcosecurity/falco/pull/2919)] - [@RichardoC](https://github.com/RichardoC) * update(docs): add Wireshark/Logray as adopter [[#2867](https://github.com/falcosecurity/falco/pull/2867)] - [@geraldcombs](https://github.com/geraldcombs) * update: engine_version in semver representation [[#2838](https://github.com/falcosecurity/falco/pull/2838)] - [@loresuso](https://github.com/loresuso) * update(userspace/engine): modularize rule compiler, fix and enrich rule descriptions [[#2817](https://github.com/falcosecurity/falco/pull/2817)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Bug Fixes * fix(userspace/metric): minor fixes in new libsinsp state metrics handling [[#3033](https://github.com/falcosecurity/falco/pull/3033)] - [@incertum](https://github.com/incertum) * fix(userspace/engine): avoid storing escaped strings in engine defs [[#3028](https://github.com/falcosecurity/falco/pull/3028)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/engine): cache latest rules compilation output [[#2900](https://github.com/falcosecurity/falco/pull/2900)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/engine): solve description of macro-only rules [[#2898](https://github.com/falcosecurity/falco/pull/2898)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/engine): fix memory leak [[#2877](https://github.com/falcosecurity/falco/pull/2877)] - [@therealbobo](https://github.com/therealbobo) ### Non user-facing changes * fix: nlohmann_json lib include path [[#3032](https://github.com/falcosecurity/falco/pull/3032)] - [@federico-sysdig](https://github.com/federico-sysdig) * chore: bump falco rules [[#3021](https://github.com/falcosecurity/falco/pull/3021)] - [@Andreagit97](https://github.com/Andreagit97) * chore: bump Falco to libs 0.14.1 [[#3020](https://github.com/falcosecurity/falco/pull/3020)] - [@Andreagit97](https://github.com/Andreagit97) * chore(build): remove outdated development libs [[#2946](https://github.com/falcosecurity/falco/pull/2946)] - [@federico-sysdig](https://github.com/federico-sysdig) * chore(falco): bump Falco to `000d576` libs commit [[#2944](https://github.com/falcosecurity/falco/pull/2944)] - [@Andreagit97](https://github.com/Andreagit97) * fix(gha): update rpmsign [[#2856](https://github.com/falcosecurity/falco/pull/2856)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `424b258` to `1221b9e` [[#3000](https://github.com/falcosecurity/falco/pull/3000)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `2ac430b` to `c39d31a` [[#3019](https://github.com/falcosecurity/falco/pull/3019)] - [@dependabot[bot]](https://github.com/apps/dependabot) * cleanup(falco.yaml): rename `none` in `nodriver` [[#3012](https://github.com/falcosecurity/falco/pull/3012)] - [@Andreagit97](https://github.com/Andreagit97) * update(config): graduate outputs_queue to stable [[#3016](https://github.com/falcosecurity/falco/pull/3016)] - [@incertum](https://github.com/incertum) * update(cmake): bump falcoctl to v0.7.0. [[#3009](https://github.com/falcosecurity/falco/pull/3009)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-rules from `1221b9e` to `2ac430b` [[#3007](https://github.com/falcosecurity/falco/pull/3007)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore(ci): bumped rn2md to latest master. [[#3006](https://github.com/falcosecurity/falco/pull/3006)] - [@FedeDP](https://github.com/FedeDP) * chore: bump Falco to latest libs [[#3002](https://github.com/falcosecurity/falco/pull/3002)] - [@Andreagit97](https://github.com/Andreagit97) * chore: bump driver version [[#2998](https://github.com/falcosecurity/falco/pull/2998)] - [@Andreagit97](https://github.com/Andreagit97) * Add addl source related methods [[#2939](https://github.com/falcosecurity/falco/pull/2939)] - [@mstemm](https://github.com/mstemm) * build(deps): Bump submodules/falcosecurity-rules from `cd33bc3` to `424b258` [[#2993](https://github.com/falcosecurity/falco/pull/2993)] - [@dependabot[bot]](https://github.com/apps/dependabot) * cleanup(engine): clarify deprecation notice for engines [[#2987](https://github.com/falcosecurity/falco/pull/2987)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(cmake): bumped falcoctl to v0.7.0-rc1. [[#2983](https://github.com/falcosecurity/falco/pull/2983)] - [@FedeDP](https://github.com/FedeDP) * chore(ci): revert #2961. [[#2984](https://github.com/falcosecurity/falco/pull/2984)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-testing from `930170b` to `9b9630e` [[#2980](https://github.com/falcosecurity/falco/pull/2980)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore: bump Falco to latest libs [[#2977](https://github.com/falcosecurity/falco/pull/2977)] - [@Andreagit97](https://github.com/Andreagit97) * build(deps): Bump submodules/falcosecurity-rules from `262f569` to `cd33bc3` [[#2976](https://github.com/falcosecurity/falco/pull/2976)] - [@dependabot[bot]](https://github.com/apps/dependabot) * Allow enabling rules by ruleset id in addition to name [[#2920](https://github.com/falcosecurity/falco/pull/2920)] - [@mstemm](https://github.com/mstemm) * chore(ci): enable aarch64 falco driver loader tests. [[#2961](https://github.com/falcosecurity/falco/pull/2961)] - [@FedeDP](https://github.com/FedeDP) * chore(unit_tests): added more tests for yaml env vars expansion. [[#2972](https://github.com/falcosecurity/falco/pull/2972)] - [@FedeDP](https://github.com/FedeDP) * chore(falco.yaml): use HOME env var for ebpf probe path. [[#2971](https://github.com/falcosecurity/falco/pull/2971)] - [@FedeDP](https://github.com/FedeDP) * chore: bump falco to latest libs [[#2970](https://github.com/falcosecurity/falco/pull/2970)] - [@Andreagit97](https://github.com/Andreagit97) * build(deps): Bump submodules/falcosecurity-rules from `dd38952` to `262f569` [[#2969](https://github.com/falcosecurity/falco/pull/2969)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(readme): add actuated.dev badge [[#2967](https://github.com/falcosecurity/falco/pull/2967)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(cmake,docker): bumped falcoctl to v0.7.0-beta5. [[#2968](https://github.com/falcosecurity/falco/pull/2968)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-rules from `64e2adb` to `dd38952` [[#2959](https://github.com/falcosecurity/falco/pull/2959)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(docker): small fixes in docker entrypoints for new driver loader. [[#2966](https://github.com/falcosecurity/falco/pull/2966)] - [@FedeDP](https://github.com/FedeDP) * chore(build): allow usage of non-bundled nlohmann-json [[#2947](https://github.com/falcosecurity/falco/pull/2947)] - [@federico-sysdig](https://github.com/federico-sysdig) * update(ci): enable actuated.dev [[#2945](https://github.com/falcosecurity/falco/pull/2945)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup: fix several warnings from a Clang build [[#2948](https://github.com/falcosecurity/falco/pull/2948)] - [@federico-sysdig](https://github.com/federico-sysdig) * chore(docker/falco): add back some deps to falco docker image. [[#2932](https://github.com/falcosecurity/falco/pull/2932)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-testing from `92c313f` to `5248e6d` [[#2937](https://github.com/falcosecurity/falco/pull/2937)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `e206c1a` to `8f0520f` [[#2904](https://github.com/falcosecurity/falco/pull/2904)] - [@dependabot[bot]](https://github.com/apps/dependabot) * cleanup(falco): remove decode_uri as it is no longer used [[#2933](https://github.com/falcosecurity/falco/pull/2933)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(engine): port decode_uri in falco engine [[#2912](https://github.com/falcosecurity/falco/pull/2912)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(falco): update to libs on nov 28th [[#2929](https://github.com/falcosecurity/falco/pull/2929)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup(falco): remove `init` in the configuration constructor [[#2917](https://github.com/falcosecurity/falco/pull/2917)] - [@Andreagit97](https://github.com/Andreagit97) * build(deps): Bump submodules/falcosecurity-rules from `8f0520f` to `64e2adb` [[#2908](https://github.com/falcosecurity/falco/pull/2908)] - [@dependabot[bot]](https://github.com/apps/dependabot) * cleanup(userspace/engine): remove legacy k8saudit implementation [[#2913](https://github.com/falcosecurity/falco/pull/2913)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(gha): disable branch protection rule trigger for scorecard [[#2911](https://github.com/falcosecurity/falco/pull/2911)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(gha): set cosign-installer to v3.1.2 [[#2901](https://github.com/falcosecurity/falco/pull/2901)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(docs): sync changelog for 0.36.2. [[#2894](https://github.com/falcosecurity/falco/pull/2894)] - [@FedeDP](https://github.com/FedeDP) * Run OpenSSF Scorecard in pipeline [[#2888](https://github.com/falcosecurity/falco/pull/2888)] - [@maxgio92](https://github.com/maxgio92) * cleanup: replace banned.h with semgrep [[#2881](https://github.com/falcosecurity/falco/pull/2881)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(gha): upgrade GitHub actions [[#2876](https://github.com/falcosecurity/falco/pull/2876)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `a22d0d7` to `e206c1a` [[#2865](https://github.com/falcosecurity/falco/pull/2865)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `d119706` to `a22d0d7` [[#2860](https://github.com/falcosecurity/falco/pull/2860)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(gha): use fedora instead of centos 7 for package publishing [[#2854](https://github.com/falcosecurity/falco/pull/2854)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(gha): pin versions to hashes [[#2849](https://github.com/falcosecurity/falco/pull/2849)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `c366d5b` to `d119706` [[#2847](https://github.com/falcosecurity/falco/pull/2847)] - [@dependabot[bot]](https://github.com/apps/dependabot) * new(ci): properly link libs and driver releases linked to a Falco release [[#2846](https://github.com/falcosecurity/falco/pull/2846)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-rules from `7a7cf24` to `c366d5b` [[#2842](https://github.com/falcosecurity/falco/pull/2842)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `77ba57a` to `7a7cf24` [[#2836](https://github.com/falcosecurity/falco/pull/2836)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore(ci): bumped rn2md to latest master. [[#2844](https://github.com/falcosecurity/falco/pull/2844)] - [@FedeDP](https://github.com/FedeDP) ## v0.36.2 Released on 2023-10-27 NO CHANGES IN FALCO, ALL CHANGES IN LIBS. ## v0.36.1 Released on 2023-10-16 ### Major Changes ### Minor Changes * feat(userspace): remove experimental outputs queue recovery strategies [[#2863](https://github.com/falcosecurity/falco/pull/2863)] - [@incertum](https://github.com/incertum) ### Bug Fixes * fix(userspace/falco): timer_delete() workaround due to bug in older GLIBC [[#2851](https://github.com/falcosecurity/falco/pull/2851)] - [@incertum](https://github.com/incertum) ## v0.36.0 Released on 2023-09-26 ### Breaking Changes - The default rules file that is shipped in the Falco image and/or can be downloaded via falcoctl as `falco-rules` is now a _stable_ rule file. This file **contains a much smaller number of rules** that are less noisy and have been vetted by the community. This serves as a much requested "starter" Falco rule set that covers many common use case. The rest of that file has been expanded and split into `falco-incubating-rules` and `falco-sandbox-rules`. For more information, see the [rules repository](https://github.com/falcosecurity/rules) - The main `falcosecurity/falco` container image and its `falco-driver-loader` counterpart have been upgraded. Now they are able to compile the kernel module or classic eBPF probe for relatively newer version of the kernel (5.x and above) while we no longer ship toolchains to compile the kernel module for older versions in the default images. Downloading of prebuilt drivers and the modern eBPF will work exactly like before. The older image, meant for compatibility with older kernels (4.x and below), is currently retained as `falcosecurity/falco-driver-loader-legacy`. - The Falco HTTP output no longer logs to stdout by default for performance reasons. You can set stdout logging preferences and restore the previous behavior with the configuration option `http_output.echo` in `falco.yaml`. - The `--list-syscall-events` command line option has been replaced by `--list-events` which prints all supported system events (syscall, tracepoints, metaevents, internal plugin events) in addition to extra information about flags. - The semantics of `proc.exepath` have changed. Now that field contains the executable path on disk even if the binary was launched from a symbolic link. - The `-d` daemonize option has been removed. - The `-p` option is now changed: - when only `-pc` is set Falco will print `container_id=%container.id container_image=%container.image.repository container_image_tag=%container.image.tag container_name=%container.name` - when `-pk` is set it will print as above, but with `k8s_ns=%k8s.ns.name k8s_pod_name=%k8s.pod.name` appended ### Major Changes * new(falco-driver-loader): --source-only now prints the values as env vars [[#2353](https://github.com/falcosecurity/falco/pull/2353)] - [@steakunderscore](https://github.com/steakunderscore) * new(docker): allow passing options to falco-driver-loader from the driver loader container [[#2781](https://github.com/falcosecurity/falco/pull/2781)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(docker): add experimental falco-distroless image based on Wolfi [[#2768](https://github.com/falcosecurity/falco/pull/2768)] - [@LucaGuerra](https://github.com/LucaGuerra) * new: the legacy falco image is available as driver-loader-legacy [[#2718](https://github.com/falcosecurity/falco/pull/2718)] - [@LucaGuerra](https://github.com/LucaGuerra) * new: added option to enable/disable echoing of server answer to stdout (disabled by default) when using HTTP output [[#2602](https://github.com/falcosecurity/falco/pull/2602)] - [@FedeDP](https://github.com/FedeDP) * new: support systemctl reload for Falco services [[#2588](https://github.com/falcosecurity/falco/pull/2588)] - [@jabdr](https://github.com/jabdr) * new(falco/config): add new configurations for http_output that allow mTLS [[#2633](https://github.com/falcosecurity/falco/pull/2633)] - [@annadorottya](https://github.com/annadorottya) * new: allow falco to match multiple rules on same event [[#2705](https://github.com/falcosecurity/falco/pull/2705)] - [@loresuso](https://github.com/loresuso) ### Minor Changes * update(cmake): bumped bundled falcoctl to 0.6.2 [[#2829](https://github.com/falcosecurity/falco/pull/2829)] - [@FedeDP](https://github.com/FedeDP) * update(rules)!: major rule update to version 2.0.0 [[#2823](https://github.com/falcosecurity/falco/pull/2823)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(cmake): bumped plugins to latest stable versions [[#2820](https://github.com/falcosecurity/falco/pull/2820)] - [@FedeDP](https://github.com/FedeDP) * update(cmake): bumped libs to 0.13.0-rc2 and driver to 6.0.1+driver [[#2806](https://github.com/falcosecurity/falco/pull/2806)] - [@FedeDP](https://github.com/FedeDP) * update!: default substitution for `%container.info` is now equal `container_id=%container.id container_name=%container.name` [[#2793](https://github.com/falcosecurity/falco/pull/2793)] - [@leogr](https://github.com/leogr) * update!: the --list-syscall-events flag is now called --list-events and lists all events [[#2771](https://github.com/falcosecurity/falco/pull/2771)] - [@LucaGuerra](https://github.com/LucaGuerra) * update!: the Falco base image is now based on Debian 12 with gcc 11-12 [[#2718](https://github.com/falcosecurity/falco/pull/2718)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(docker): the Falco no-driver image is now based on Debian 12 [[#2782](https://github.com/falcosecurity/falco/pull/2782)] - [@LucaGuerra](https://github.com/LucaGuerra) * feat(userspace)!: remove `-d` daemonize option [[#2677](https://github.com/falcosecurity/falco/pull/2677)] - [@incertum](https://github.com/incertum) * build(deps): Bump submodules/falcosecurity-rules from 3f52480 to 0d0e333 [[#2693](https://github.com/falcosecurity/falco/pull/2693)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from 3f52480 to b42893a [[#2756](https://github.com/falcosecurity/falco/pull/2756)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from b42893a to 6ed73fe [[#2780](https://github.com/falcosecurity/falco/pull/2780)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake): bumped libs to 0.13.0-rc1 and driver to 6.0.0+driver. [[#2783](https://github.com/falcosecurity/falco/pull/2783)] - [@FedeDP](https://github.com/FedeDP) * feat: support parsing of system environment variables in yaml [[#2562](https://github.com/falcosecurity/falco/pull/2562)] - [@therealdwright](https://github.com/therealdwright) * feat(userspace)!: deprecate stats command args option in favor of metrics configs in falco.yaml [[#2739](https://github.com/falcosecurity/falco/pull/2739)] - [@incertum](https://github.com/incertum) * update: upgrade `falcoctl` to version 0.6.0 [[#2764](https://github.com/falcosecurity/falco/pull/2764)] - [@leogr](https://github.com/leogr) * cleanup: deprecate rate limiter mechanism [[#2762](https://github.com/falcosecurity/falco/pull/2762)] - [@Andreagit97](https://github.com/Andreagit97) * cleanup(config): add more info [[#2758](https://github.com/falcosecurity/falco/pull/2758)] - [@incertum](https://github.com/incertum) * update(userspace/engine): improve skip-if-unknown-filter YAML field [[#2749](https://github.com/falcosecurity/falco/pull/2749)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore: improved HTTP output performance [[#2602](https://github.com/falcosecurity/falco/pull/2602)] - [@FedeDP](https://github.com/FedeDP) * update!: HTTP output will no more echo to stdout by default [[#2602](https://github.com/falcosecurity/falco/pull/2602)] - [@FedeDP](https://github.com/FedeDP) * chore: remove b64 from falco dependencies [[#2746](https://github.com/falcosecurity/falco/pull/2746)] - [@Andreagit97](https://github.com/Andreagit97) * update(cmake): support building libs and driver from forks [[#2747](https://github.com/falcosecurity/falco/pull/2747)] - [@jasondellaluce](https://github.com/jasondellaluce) * update: `-p` presets have been updated to reflect the new rules style guide [[#2737](https://github.com/falcosecurity/falco/pull/2737)] - [@leogr](https://github.com/leogr) * feat: Allow specifying explicit kernel release and version for falco-driver-loader [[#2728](https://github.com/falcosecurity/falco/pull/2728)] - [@johananl](https://github.com/johananl) * cleanup(config): assign Stable to `base_syscalls` config [[#2740](https://github.com/falcosecurity/falco/pull/2740)] - [@incertum](https://github.com/incertum) * update : support build for wasm [[#2663](https://github.com/falcosecurity/falco/pull/2663)] - [@Rohith-Raju](https://github.com/Rohith-Raju) * docs(config.yaml): fix wrong severity levels for sinsp logger [[#2736](https://github.com/falcosecurity/falco/pull/2736)] - [@Andreagit97](https://github.com/Andreagit97) * update(cmake): bump libs and driver to 0.12.0 [[#2721](https://github.com/falcosecurity/falco/pull/2721)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(docker): remove experimental image based on RedHat UBI [[#2720](https://github.com/falcosecurity/falco/pull/2720)] - [@leogr](https://github.com/leogr) ### Bug Fixes * fix(outputs): expose queue_capacity_outputs config for memory control [[#2711](https://github.com/falcosecurity/falco/pull/2711)] - [@incertum](https://github.com/incertum) * fix(userspace/falco): cleanup metrics timer upon leaving. [[#2759](https://github.com/falcosecurity/falco/pull/2759)] - [@FedeDP](https://github.com/FedeDP) * fix: restore Falco MINIMAL_BUILD and deprecate `userspace` option [[#2761](https://github.com/falcosecurity/falco/pull/2761)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace/engine): support appending to unknown sources [[#2753](https://github.com/falcosecurity/falco/pull/2753)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Non user-facing changes * build(deps): Bump submodules/falcosecurity-rules from `69c9be8` to `77ba57a` [[#2833](https://github.com/falcosecurity/falco/pull/2833)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore: bump submodule testing to 62edc65 [[#2831](https://github.com/falcosecurity/falco/pull/2831)] - [@Andreagit97](https://github.com/Andreagit97) * update(gha): add version for rn2md [[#2830](https://github.com/falcosecurity/falco/pull/2830)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore: automatically attach release author to release body. [[#2828](https://github.com/falcosecurity/falco/pull/2828)] - [@FedeDP](https://github.com/FedeDP) * new(ci): autogenerate release body. [[#2812](https://github.com/falcosecurity/falco/pull/2812)] - [@FedeDP](https://github.com/FedeDP) * fix(dockerfile): remove useless CMD [[#2824](https://github.com/falcosecurity/falco/pull/2824)] - [@Andreagit97](https://github.com/Andreagit97) * chore: bump to the latest libs [[#2822](https://github.com/falcosecurity/falco/pull/2822)] - [@Andreagit97](https://github.com/Andreagit97) * update: add SPDX license identifier [[#2809](https://github.com/falcosecurity/falco/pull/2809)] - [@leogr](https://github.com/leogr) * chore: bump to latest libs [[#2815](https://github.com/falcosecurity/falco/pull/2815)] - [@Andreagit97](https://github.com/Andreagit97) * build(deps): Bump submodules/falcosecurity-rules from `ee5fb38` to `bea364e` [[#2814](https://github.com/falcosecurity/falco/pull/2814)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(build): set the right bucket and version for driver legacy [[#2800](https://github.com/falcosecurity/falco/pull/2800)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `43580b4` to `ee5fb38` [[#2810](https://github.com/falcosecurity/falco/pull/2810)] - [@dependabot[bot]](https://github.com/apps/dependabot) * cleanup(userspace): thrown exceptions and avoid multiple logs [[#2803](https://github.com/falcosecurity/falco/pull/2803)] - [@Andreagit97](https://github.com/Andreagit97) * build(deps): Bump submodules/falcosecurity-rules from `c6e01fa` to `43580b4` [[#2801](https://github.com/falcosecurity/falco/pull/2801)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-testing from `76d1743` to `30c3643` [[#2802](https://github.com/falcosecurity/falco/pull/2802)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(userspace/falco): clearing full output queue [[#2798](https://github.com/falcosecurity/falco/pull/2798)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(docs): add driver-loader-legacy to readme and fix bad c&p [[#2799](https://github.com/falcosecurity/falco/pull/2799)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `d31dbc2` to `c6e01fa` [[#2797](https://github.com/falcosecurity/falco/pull/2797)] - [@dependabot[bot]](https://github.com/apps/dependabot) * docs: add LICENSE file [[#2796](https://github.com/falcosecurity/falco/pull/2796)] - [@leogr](https://github.com/leogr) * build(deps): Bump submodules/falcosecurity-rules from `b6372d2` to `d31dbc2` [[#2794](https://github.com/falcosecurity/falco/pull/2794)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(stats): always initialize m_output field [[#2789](https://github.com/falcosecurity/falco/pull/2789)] - [@Andreagit97](https://github.com/Andreagit97) * build(deps): Bump submodules/falcosecurity-rules from `6ed73fe` to `b6372d2` [[#2786](https://github.com/falcosecurity/falco/pull/2786)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(cmake/modules): bump rules to falco-rules-2.0.0-rc1 [[#2775](https://github.com/falcosecurity/falco/pull/2775)] - [@leogr](https://github.com/leogr) * update(OWNERS): add LucaGuerra to owners [[#2650](https://github.com/falcosecurity/falco/pull/2650)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `9126bef` to `0328c59` [[#2709](https://github.com/falcosecurity/falco/pull/2709)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `0d0e333` to `64ce419` [[#2731](https://github.com/falcosecurity/falco/pull/2731)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `3ceea88` to `40a9817` [[#2745](https://github.com/falcosecurity/falco/pull/2745)] - [@dependabot[bot]](https://github.com/apps/dependabot) * docs(README.md): correct URL [[#2772](https://github.com/falcosecurity/falco/pull/2772)] - [@vjjmiras](https://github.com/vjjmiras) * #2393 Document why Falco is written in C++ rather than anything else [[#2410](https://github.com/falcosecurity/falco/pull/2410)] - [@RichardoC](https://github.com/RichardoC) * chore: bump Falco to latest libs [[#2769](https://github.com/falcosecurity/falco/pull/2769)] - [@Andreagit97](https://github.com/Andreagit97) * ci: disable falco-driver-loader tests on ARM64 [[#2770](https://github.com/falcosecurity/falco/pull/2770)] - [@Andreagit97](https://github.com/Andreagit97) * update(userspace/falco): revised CLI help messages [[#2755](https://github.com/falcosecurity/falco/pull/2755)] - [@leogr](https://github.com/leogr) * fix(engine): fix reorder warning for m_watch_config_files / m_rule_matching [[#2767](https://github.com/falcosecurity/falco/pull/2767)] - [@LucaGuerra](https://github.com/LucaGuerra) * update: introduce new stats updated to the latest libs version [[#2766](https://github.com/falcosecurity/falco/pull/2766)] - [@Andreagit97](https://github.com/Andreagit97) * ci: support tests on amazon-linux [[#2765](https://github.com/falcosecurity/falco/pull/2765)] - [@Andreagit97](https://github.com/Andreagit97) * chore: bump Falco to latest libs master [[#2754](https://github.com/falcosecurity/falco/pull/2754)] - [@Andreagit97](https://github.com/Andreagit97) * build(deps): Bump submodules/falcosecurity-testing from `b39c807` to `9110022` [[#2760](https://github.com/falcosecurity/falco/pull/2760)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix: fix "ebpf_enabled" output stat [[#2751](https://github.com/falcosecurity/falco/pull/2751)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace/engine): support both old and new gcc + std::move [[#2748](https://github.com/falcosecurity/falco/pull/2748)] - [@jasondellaluce](https://github.com/jasondellaluce) * cleanup: turn some warnings into errors [[#2744](https://github.com/falcosecurity/falco/pull/2744)] - [@Andreagit97](https://github.com/Andreagit97) * update(ci): minimize retention days for build-only CI artifacts [[#2743](https://github.com/falcosecurity/falco/pull/2743)] - [@jasondellaluce](https://github.com/jasondellaluce) * cleanup: remove unused `--pidfile` option from systemd units [[#2742](https://github.com/falcosecurity/falco/pull/2742)] - [@Andreagit97](https://github.com/Andreagit97) * build(deps): Bump submodules/falcosecurity-rules from `bf1639a` to `3ceea88` [[#2741](https://github.com/falcosecurity/falco/pull/2741)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `64ce419` to `bf1639a` [[#2738](https://github.com/falcosecurity/falco/pull/2738)] - [@dependabot[bot]](https://github.com/apps/dependabot) * Relocate tools on Flatcar in BPF mode [[#2729](https://github.com/falcosecurity/falco/pull/2729)] - [@johananl](https://github.com/johananl) * build: update versioning with cmake [[#2727](https://github.com/falcosecurity/falco/pull/2727)] - [@leogr](https://github.com/leogr) * update(userspace/engine): make rule_matching strategy stateless [[#2726](https://github.com/falcosecurity/falco/pull/2726)] - [@loresuso](https://github.com/loresuso) * chore: bump Falco to latest libs version [[#2722](https://github.com/falcosecurity/falco/pull/2722)] - [@Andreagit97](https://github.com/Andreagit97) * update: enforce bumping engine version whenever appropriate [[#2719](https://github.com/falcosecurity/falco/pull/2719)] - [@jasondellaluce](https://github.com/jasondellaluce) ## v0.35.1 Released on 2023-06-29 ### Major Changes ### Minor Changes * update(userspace): change description of snaplen option stating only performance implications [[#2634](https://github.com/falcosecurity/falco/pull/2634)] - [@loresuso](https://github.com/loresuso) * update(cmake): bump libs to 0.11.3 [[#2662](https://github.com/falcosecurity/falco/pull/2662)] - [@jasondellaluce](https://github.com/jasondellaluce) * cleanup(config): minor config clarifications [[#2651](https://github.com/falcosecurity/falco/pull/2651)] - [@incertum](https://github.com/incertum) * update(cmake): bump falco rules to v1.0.1 [[#2648](https://github.com/falcosecurity/falco/pull/2648)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore(userspace/falco): make source matching error more expressive [[#2623](https://github.com/falcosecurity/falco/pull/2623)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(.github): integrate Go regression tests [[#2437](https://github.com/falcosecurity/falco/pull/2437)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Bug Fixes * fix(scripts): fixed falco-driver-loader to manage debian kernel rt and cloud flavors. [[#2627](https://github.com/falcosecurity/falco/pull/2627)] - [@FedeDP](https://github.com/FedeDP) * fix(userspace/falco): solve live multi-source issues when loading more than two sources [[#2653](https://github.com/falcosecurity/falco/pull/2653)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(driver-loader): fix ubuntu kernel version parsing [[#2635](https://github.com/falcosecurity/falco/pull/2635)] - [@therealbobo](https://github.com/therealbobo) * fix(userspace): switch to timer_settime API for stats writer. [[#2646](https://github.com/falcosecurity/falco/pull/2646)] - [@FedeDP](https://github.com/FedeDP) ### Non user-facing changes * CI: bump ubuntu version for tests-driver-loader-integration job [[#2661](https://github.com/falcosecurity/falco/pull/2661)] - [@Andreagit97](https://github.com/Andreagit97) ## v0.35.0 Released on 2023-06-07 ### Major Changes * BREAKING CHANGE: support for metadata enrichment from Mesos has been removed. [[#2465](https://github.com/falcosecurity/falco/pull/2465)] - [@leogr](https://github.com/leogr) * new(falco): introduce new metrics w/ Falco internal: metrics snapshot option and new metrics config [[#2333](https://github.com/falcosecurity/falco/pull/2333)] - [@incertum](https://github.com/incertum) * new(scripts): properly manage talos prebuilt drivers [[#2537](https://github.com/falcosecurity/falco/pull/2537)] - [@FedeDP](https://github.com/FedeDP) * new(release): released container images are now signed with cosign [[#2546](https://github.com/falcosecurity/falco/pull/2546)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(ci): ported master and release artifacts publishing CI to gha [[#2501](https://github.com/falcosecurity/falco/pull/2501)] - [@FedeDP](https://github.com/FedeDP) * new(app_actions): introduce base_syscalls user option [[#2428](https://github.com/falcosecurity/falco/pull/2428)] - [@incertum](https://github.com/incertum) * new(falco/config): add new configurations for http_output that allow custom CA certificates and stores. [[#2458](https://github.com/falcosecurity/falco/pull/2458)] - [@alacuku](https://github.com/alacuku) * new(userspace): add a new `syscall_drop_failed` config option to drop failed syscalls exit events [[#2456](https://github.com/falcosecurity/falco/pull/2456)] - [@FedeDP](https://github.com/FedeDP) ### Minor Changes * update(cmake): bump Falco rules to 1.0.0 [[#2618](https://github.com/falcosecurity/falco/pull/2618)] - [@loresuso](https://github.com/loresuso) * update(cmake): bump libs to 0.11.1 [[#2614](https://github.com/falcosecurity/falco/pull/2614)] - [@loresuso](https://github.com/loresuso) * update(cmake): bump plugins to latest versions [[#2610](https://github.com/falcosecurity/falco/pull/2610)] - [@loresuso](https://github.com/loresuso) * update(cmake): bump falco rules to 1.0.0-rc1 [[#2609](https://github.com/falcosecurity/falco/pull/2609)] - [@loresuso](https://github.com/loresuso) * update(cmake): bump libs to 0.11.0 [[#2608](https://github.com/falcosecurity/falco/pull/2608)] - [@loresuso](https://github.com/loresuso) * cleanup(docs): update release.md [[#2599](https://github.com/falcosecurity/falco/pull/2599)] - [@incertum](https://github.com/incertum) * update(cmake): bump libs to 0.11.0-rc5 and driver to 5.0.1. [[#2600](https://github.com/falcosecurity/falco/pull/2600)] - [@FedeDP](https://github.com/FedeDP) * cleanup(docs): adjust falco readme style and content [[#2594](https://github.com/falcosecurity/falco/pull/2594)] - [@incertum](https://github.com/incertum) * cleanup(userspace, config): improve metrics UX, add include_empty_values option [[#2593](https://github.com/falcosecurity/falco/pull/2593)] - [@incertum](https://github.com/incertum) * feat: add the curl and jq packages to the falco-no-driver docker image [[#2581](https://github.com/falcosecurity/falco/pull/2581)] - [@therealdwright](https://github.com/therealdwright) * update: add missing exception, required_engine_version, required_plugin_version to -L json output [[#2584](https://github.com/falcosecurity/falco/pull/2584)] - [@loresuso](https://github.com/loresuso) * feat: add image source OCI label to docker images [[#2592](https://github.com/falcosecurity/falco/pull/2592)] - [@therealdwright](https://github.com/therealdwright) * cleanup(config): improve falco config [[#2571](https://github.com/falcosecurity/falco/pull/2571)] - [@incertum](https://github.com/incertum) * update(cmake): bump libs and plugins to latest dev versions [[#2586](https://github.com/falcosecurity/falco/pull/2586)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore(userspace/falco): always print invalid syscalls from custom set [[#2578](https://github.com/falcosecurity/falco/pull/2578)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(build): upgrade falcoctl to 0.5.0 [[#2572](https://github.com/falcosecurity/falco/pull/2572)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(userspace/falco/app): print all supported plugin caps [[#2564](https://github.com/falcosecurity/falco/pull/2564)] - [@jasondellaluce](https://github.com/jasondellaluce) * update: get rules details with `-l` or `-L` flags when json output format is specified [[#2544](https://github.com/falcosecurity/falco/pull/2544)] - [@loresuso](https://github.com/loresuso) * update!: bump libs version, and support latest plugin features, add --nodriver option [[#2552](https://github.com/falcosecurity/falco/pull/2552)] - [@jasondellaluce](https://github.com/jasondellaluce) * cleanup(actions): now modern bpf support `-A` flag [[#2551](https://github.com/falcosecurity/falco/pull/2551)] - [@Andreagit97](https://github.com/Andreagit97) * update: `falco-driver-loader` now uses now uses $TMPDIR if set [[#2518](https://github.com/falcosecurity/falco/pull/2518)] - [@jabdr](https://github.com/jabdr) * update: improve control and UX of ignored events [[#2509](https://github.com/falcosecurity/falco/pull/2509)] - [@jasondellaluce](https://github.com/jasondellaluce) * update: bump libs and adapt Falco to new libsinsp event source management [[#2507](https://github.com/falcosecurity/falco/pull/2507)] - [@jasondellaluce](https://github.com/jasondellaluce) * new(app_actions)!: adjust base_syscalls option, add base_syscalls.repair [[#2457](https://github.com/falcosecurity/falco/pull/2457)] - [@incertum](https://github.com/incertum) * update(scripts): support al2022 and al2023 in falco-driver-loader. [[#2494](https://github.com/falcosecurity/falco/pull/2494)] - [@FedeDP](https://github.com/FedeDP) * update: sync libs with newest event name APIs [[#2471](https://github.com/falcosecurity/falco/pull/2471)] - [@jasondellaluce](https://github.com/jasondellaluce) * update!: remove `--mesos-api`, `-pmesos`, and `-pm` command-line flags [[#2465](https://github.com/falcosecurity/falco/pull/2465)] - [@leogr](https://github.com/leogr) * cleanup(unit_tests): try making test_configure_interesting_sets more robust [[#2464](https://github.com/falcosecurity/falco/pull/2464)] - [@incertum](https://github.com/incertum) ### Bug Fixes * fix: unquote quoted URL's to avoid libcurl errors [[#2596](https://github.com/falcosecurity/falco/pull/2596)] - [@therealdwright](https://github.com/therealdwright) * fix(userspace/engine): store alternatives as array in -L json output [[#2597](https://github.com/falcosecurity/falco/pull/2597)] - [@loresuso](https://github.com/loresuso) * fix(userspace/engine): store required engine version as string in -L json output [[#2595](https://github.com/falcosecurity/falco/pull/2595)] - [@loresuso](https://github.com/loresuso) * fix(userspace/falco): report plugin deps rules issues in any case [[#2589](https://github.com/falcosecurity/falco/pull/2589)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace): hotreload on wrong metrics [[#2582](https://github.com/falcosecurity/falco/pull/2582)] - [@therealbobo](https://github.com/therealbobo) * fix(userspace): check the supported number of online CPUs with modern bpf [[#2575](https://github.com/falcosecurity/falco/pull/2575)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace/falco): don't hang on terminating error when multi sourcing [[#2576](https://github.com/falcosecurity/falco/pull/2576)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/falco): properly format numeric values in metrics [[#2569](https://github.com/falcosecurity/falco/pull/2569)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(scripts): properly support debian kernel releases embedded in kernel version [[#2377](https://github.com/falcosecurity/falco/pull/2377)] - [@FedeDP](https://github.com/FedeDP) ### Non user-facing changes * docs(README.md): add scope/status badge and simply doc structure [[#2611](https://github.com/falcosecurity/falco/pull/2611)] - [@leogr](https://github.com/leogr) * build(deps): Bump submodules/falcosecurity-rules from `3471984` to `16fb709` [[#2598](https://github.com/falcosecurity/falco/pull/2598)] - [@dependabot[bot]](https://github.com/apps/dependabot) * docs(proposals): Falco roadmap management [[#2547](https://github.com/falcosecurity/falco/pull/2547)] - [@leogr](https://github.com/leogr) * build(deps): Bump submodules/falcosecurity-rules from `b2290ad` to `3471984` [[#2577](https://github.com/falcosecurity/falco/pull/2577)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(build): libs 0.11.0-rc2 [[#2573](https://github.com/falcosecurity/falco/pull/2573)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `3f52480` to `b2290ad` [[#2570](https://github.com/falcosecurity/falco/pull/2570)] - [@dependabot[bot]](https://github.com/apps/dependabot) * update(ci): use repo instead of master branch for reusable workflows [[#2568](https://github.com/falcosecurity/falco/pull/2568)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup(ci): cleaned up circleci workflow. [[#2566](https://github.com/falcosecurity/falco/pull/2566)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump requests from 2.26.0 to 2.31.0 in /test [[#2567](https://github.com/falcosecurity/falco/pull/2567)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(ci): simplify and fix multi-arch image publishing process [[#2542](https://github.com/falcosecurity/falco/pull/2542)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): get the manifest for the correct tag [[#2563](https://github.com/falcosecurity/falco/pull/2563)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `3f52480` to `6da15ae` [[#2559](https://github.com/falcosecurity/falco/pull/2559)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(ci): properly use `docker save` to store images. [[#2560](https://github.com/falcosecurity/falco/pull/2560)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): docker arg is named `TARGETARCH`. [[#2558](https://github.com/falcosecurity/falco/pull/2558)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): set docker TARGET_ARCH [[#2557](https://github.com/falcosecurity/falco/pull/2557)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): use normal docker to build docker images, instead of buildx. [[#2556](https://github.com/falcosecurity/falco/pull/2556)] - [@FedeDP](https://github.com/FedeDP) * docs: improve documentation and description of base_syscalls option [[#2515](https://github.com/falcosecurity/falco/pull/2515)] - [@Happy-Dude](https://github.com/Happy-Dude) * Updating Falco branding guidelines [[#2493](https://github.com/falcosecurity/falco/pull/2493)] - [@aijamalnk](https://github.com/aijamalnk) * build(deps): Bump submodules/falcosecurity-rules from `f773578` to `6da15ae` [[#2553](https://github.com/falcosecurity/falco/pull/2553)] - [@dependabot[bot]](https://github.com/apps/dependabot) * fix(cmake): properly exclude prereleases when fetching latest tag from cmake [[#2550](https://github.com/falcosecurity/falco/pull/2550)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): load falco image before building falco-driver-loader [[#2549](https://github.com/falcosecurity/falco/pull/2549)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): correctly tag slim manifest [[#2545](https://github.com/falcosecurity/falco/pull/2545)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup(config): modern bpf is no more experimental [[#2538](https://github.com/falcosecurity/falco/pull/2538)] - [@Andreagit97](https://github.com/Andreagit97) * new(ci): add RC/prerelease support [[#2533](https://github.com/falcosecurity/falco/pull/2533)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): configure ECR public region [[#2531](https://github.com/falcosecurity/falco/pull/2531)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): falco images directory, ecr login [[#2528](https://github.com/falcosecurity/falco/pull/2528)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): separate rpm/bin/bin-static/deb packages before publication, rename bin-static [[#2527](https://github.com/falcosecurity/falco/pull/2527)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): add Cloudfront Distribution ID [[#2525](https://github.com/falcosecurity/falco/pull/2525)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): escape heredoc [[#2521](https://github.com/falcosecurity/falco/pull/2521)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(ci): build-musl-package does not need to wait for build-packages anymore [[#2520](https://github.com/falcosecurity/falco/pull/2520)] - [@FedeDP](https://github.com/FedeDP) * fix: ci Falco version [[#2516](https://github.com/falcosecurity/falco/pull/2516)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): fetch version step, download rpms/debs, minor change [[#2519](https://github.com/falcosecurity/falco/pull/2519)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(ci): properly install recent version of git (needed >= 2.18 by checkout action) [[#2514](https://github.com/falcosecurity/falco/pull/2514)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): enable toolset before every make command [[#2513](https://github.com/falcosecurity/falco/pull/2513)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): remove unnecessary mv [[#2512](https://github.com/falcosecurity/falco/pull/2512)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): bucket -> bucket_suffix [[#2511](https://github.com/falcosecurity/falco/pull/2511)] - [@LucaGuerra](https://github.com/LucaGuerra) * build(deps): Bump submodules/falcosecurity-rules from `5857874` to `1bd7e4a` [[#2478](https://github.com/falcosecurity/falco/pull/2478)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `694adf5` to `5857874` [[#2473](https://github.com/falcosecurity/falco/pull/2473)] - [@dependabot[bot]](https://github.com/apps/dependabot) * cleanup(ci): properly set a concurrency for CI workflows. [[#2470](https://github.com/falcosecurity/falco/pull/2470)] - [@FedeDP](https://github.com/FedeDP) * build(deps): Bump submodules/falcosecurity-rules from `e0646a0` to `694adf5` [[#2466](https://github.com/falcosecurity/falco/pull/2466)] - [@dependabot[bot]](https://github.com/apps/dependabot) * build(deps): Bump submodules/falcosecurity-rules from `0b0f50f` to `e0646a0` [[#2460](https://github.com/falcosecurity/falco/pull/2460)] - [@dependabot[bot]](https://github.com/apps/dependabot) ## v0.34.1 Released on 2023-02-20 ### Minor Changes * fix(userspace/engine): correctly bump FALCO_ENGINE_VERSION after introduction of new fields [[#2418](https://github.com/falcosecurity/falco/pull/2418)] - [@loresuso](https://github.com/loresuso/) ### Non user-facing changes * fix(dockerfile/no-driver): install ca-certificates [[#2412](https://github.com/falcosecurity/falco/pull/2412)] - [@alacuku](https://github.com/alacuku) ## v0.34.0 Released on 2023-02-07 ### Major Changes * BREAKING CHANGE: if you relied upon `application_rules.yaml` you can download it from https://github.com/falcosecurity/rules/tree/main/rules and manually install it. [[#2389](https://github.com/falcosecurity/falco/pull/2389)] - [@leogr](https://github.com/leogr) * new(rules): New rule to detect attempts to inject code into a process using PTRACE [[#2226](https://github.com/falcosecurity/falco/pull/2226)] - [@Brucedh](https://github.com/Brucedh) * new(engine): Also include exact locations for rule condition compile errors (missing macros, etc). [[#2216](https://github.com/falcosecurity/falco/pull/2216)] - [@mstemm](https://github.com/mstemm) * new(scripts): Support older RHEL distros in falco-driver-loader script [[#2312](https://github.com/falcosecurity/falco/pull/2312)] - [@gentooise](https://github.com/gentooise) * new(scripts): add `falcoctl` config into Falco package [[#2390](https://github.com/falcosecurity/falco/pull/2390)] - [@Andreagit97](https://github.com/Andreagit97) * new(userspace/falco): [EXPERIMENTAL] allow modern bpf probe to assign more than one CPU to a single ring buffer [[#2363](https://github.com/falcosecurity/falco/pull/2363)] - [@Andreagit97](https://github.com/Andreagit97) * new(userspace/falco): add webserver endpoint for retrieving internal version numbers [[#2356](https://github.com/falcosecurity/falco/pull/2356)] - [@jasondellaluce](https://github.com/jasondellaluce) * new(falco): add --version-json to print version information in json format [[#2331](https://github.com/falcosecurity/falco/pull/2331)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(scripts): support multiple drivers in systemd units [[#2242](https://github.com/falcosecurity/falco/pull/2242)] - [@FedeDP](https://github.com/FedeDP) * new(scripts): add bottlerocket support in falco-driver-loader [[#2318](https://github.com/falcosecurity/falco/pull/2318)] - [@FedeDP](https://github.com/FedeDP) * new(falco): add more version fields to --support and --version [[#2325](https://github.com/falcosecurity/falco/pull/2325)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(config): explicitly add the `simulate_drops` config [[#2260](https://github.com/falcosecurity/falco/pull/2260)] - [@Andreagit97](https://github.com/Andreagit97) ### Minor Changes * build: upgrade to `falcoctl` v0.4.0 [[#2406](https://github.com/falcosecurity/falco/pull/2406)] - [@loresuso](https://github.com/loresuso) * update(userspace): change `modern_bpf.cpus_for_each_syscall_buffer` default value [[#2404](https://github.com/falcosecurity/falco/pull/2404)] - [@Andreagit97](https://github.com/Andreagit97) * update(build): update falcoctl to 0.3.0 [[#2401](https://github.com/falcosecurity/falco/pull/2401)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(build): update falcoctl to 0.3.0-rc7 [[#2396](https://github.com/falcosecurity/falco/pull/2396)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(cmake): bump libs to 0.10.3 [[#2392](https://github.com/falcosecurity/falco/pull/2392)] - [@FedeDP](https://github.com/FedeDP) * build: `/etc/falco/rules.available` has been deprecated [[#2389](https://github.com/falcosecurity/falco/pull/2389)] - [@leogr](https://github.com/leogr) * build: `application_rules.yaml` is not shipped anymore with Falco [[#2389](https://github.com/falcosecurity/falco/pull/2389)] - [@leogr](https://github.com/leogr) * build: upgrade k8saudit plugin to v0.5.0 [[#2381](https://github.com/falcosecurity/falco/pull/2381)] - [@leogr](https://github.com/leogr) * build: upgrade cloudtrail plugin to v0.6.0 [[#2381](https://github.com/falcosecurity/falco/pull/2381)] - [@leogr](https://github.com/leogr) * new!: ship falcoctl inside Falco [[#2345](https://github.com/falcosecurity/falco/pull/2345)] - [@FedeDP](https://github.com/FedeDP) * refactor: remove rules and add submodule to falcosecurity/rules [[#2359](https://github.com/falcosecurity/falco/pull/2359)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(scripts): add option for regenerating signatures of all dev and release packages [[#2364](https://github.com/falcosecurity/falco/pull/2364)] - [@jasondellaluce](https://github.com/jasondellaluce) * update: print JSON version output when json_output is enabled [[#2351](https://github.com/falcosecurity/falco/pull/2351)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(cmake): updated libs to 0.10.1 tag. [[#2362](https://github.com/falcosecurity/falco/pull/2362)] - [@FedeDP](https://github.com/FedeDP) * Install the certificates of authorities in falco:no-driver docker image [[#2355](https://github.com/falcosecurity/falco/pull/2355)] - [@Issif](https://github.com/Issif) * update: Mesos support is now deprecated and will be removed in the next version. [[#2328](https://github.com/falcosecurity/falco/pull/2328)] - [@leogr](https://github.com/leogr) * update(scripts/falco-driver-loader): optimize the resiliency of module download script for air-gapped environments [[#2336](https://github.com/falcosecurity/falco/pull/2336)] - [@Dentrax](https://github.com/Dentrax) * doc(userspace): provide users with a correct message when some syscalls are not defined [[#2329](https://github.com/falcosecurity/falco/pull/2329)] - [@Andreagit97](https://github.com/Andreagit97) * update(ci): update ci jobs to generate Falco images with the modern BPF probe [[#2320](https://github.com/falcosecurity/falco/pull/2320)] - [@Andreagit97](https://github.com/Andreagit97) * rules: add Falco container lists [[#2290](https://github.com/falcosecurity/falco/pull/2290)] - [@oscr](https://github.com/oscr) * rules(macro: private_key_or_password): now also check for OpenSSH private keys [[#2284](https://github.com/falcosecurity/falco/pull/2284)] - [@oscr](https://github.com/oscr) * update(cmake): bump libs and driver to latest RC. [[#2302](https://github.com/falcosecurity/falco/pull/2302)] - [@FedeDP](https://github.com/FedeDP) * Ensure that a ruleset object is copied properly in falco_engine::add_source(). [[#2271](https://github.com/falcosecurity/falco/pull/2271)] - [@mstemm](https://github.com/mstemm) * update(userspace/falco): enable using zlib with webserver [[#2125](https://github.com/falcosecurity/falco/pull/2125)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(falco): add container-gvisor and kubernetes-gvisor print options [[#2288](https://github.com/falcosecurity/falco/pull/2288)] - [@LucaGuerra](https://github.com/LucaGuerra) * cleanup: always use bundled libz and libelf in BUNDLED_DEPS mode. [[#2277](https://github.com/falcosecurity/falco/pull/2277)] - [@FedeDP](https://github.com/FedeDP) * update: updated libs and driver to version dd443b67c6b04464cb8ee2771af8ada8777e7fac [[#2277](https://github.com/falcosecurity/falco/pull/2277)] - [@FedeDP](https://github.com/FedeDP) * update(falco.yaml): `open_params` under plugins configuration is now trimmed from surrounding whitespace [[#2267](https://github.com/falcosecurity/falco/pull/2267)] - [@yardenshoham](https://github.com/yardenshoham) ### Bug Fixes * fix(engine): Avoid crash related to caching syscall source when the falco engine uses multiple sources at the same time. [[#2272](https://github.com/falcosecurity/falco/pull/2272)] - [@mstemm](https://github.com/mstemm) * fix(scripts): use falco-driver-loader only into install scripts [[#2391](https://github.com/falcosecurity/falco/pull/2391)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace/falco): fix grpc server shutdown [[#2350](https://github.com/falcosecurity/falco/pull/2350)] - [@FedeDP](https://github.com/FedeDP) * fix(docker/falco): trust latest GPG key [[#2365](https://github.com/falcosecurity/falco/pull/2365)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/engine): improve rule loading validation results [[#2344](https://github.com/falcosecurity/falco/pull/2344)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix: graceful error handling for macros/lists reference loops [[#2311](https://github.com/falcosecurity/falco/pull/2311)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Rule Changes * rules(tagging): enhanced rules tagging for inventory / threat modeling [[#2167](https://github.com/falcosecurity/falco/pull/2167)] - [@incertum](https://github.com/incertum) * rule(Outbound Connection to C2 Server): Update the "Outbound connection to C2 server" rule to match both FQDN and IP addresses. Prior to this change, the rule only matched IP addresses and not FQDN. [[#2241](https://github.com/falcosecurity/falco/pull/2241)] - [@Nicolas-Peiffer](https://github.com/Nicolas-Peiffer) * rule(Execution from /dev/shm): new rule to detect execution from /dev/shm [[#2225](https://github.com/falcosecurity/falco/pull/2225)] - [@AlbertoPellitteri](https://github.com/AlbertoPellitteri) * rule(Find AWS Credentials): new rule to detect executions looking for AWS credentials [[#2224](https://github.com/falcosecurity/falco/pull/2224)] - [@AlbertoPellitteri](https://github.com/AlbertoPellitteri) * rule(Linux Kernel Module Injection Detected): improve insmod detection within container using CAP_SYS_MODULE [[#2305](https://github.com/falcosecurity/falco/pull/2305)] - [@loresuso](https://github.com/loresuso) * rule(Read sensitive file untrusted): let salt-call read sensitive files [[#2291](https://github.com/falcosecurity/falco/pull/2291)] - [@vin01](https://github.com/vin01) * rule(macro: rpm_procs): let salt-call write to rpm database [[#2291](https://github.com/falcosecurity/falco/pull/2291)] - [@vin01](https://github.com/vin01) ### Non user-facing changes * fix(ci): fix rpm sign job dependencies [[#2324](https://github.com/falcosecurity/falco/pull/2324)] - [@cappellinsamuele](https://github.com/cappellinsamuele) * chore(userspace): add `njson` lib as a dependency for `falco_engine` [[#2316](https://github.com/falcosecurity/falco/pull/2316)] - [@Andreagit97](https://github.com/Andreagit97) * fix(scripts): force rpm postinstall script to always show dialog, even on upgrade [[#2405](https://github.com/falcosecurity/falco/pull/2405)] - [@FedeDP](https://github.com/FedeDP) * fix(scripts): fixed falcoctl config install dir. [[#2399](https://github.com/falcosecurity/falco/pull/2399)] - [@FedeDP](https://github.com/FedeDP) * fix(scripts): make /usr writable [[#2398](https://github.com/falcosecurity/falco/pull/2398)] - [@therealbobo](https://github.com/therealbobo) * fix(scripts): driver loader insmod [[#2388](https://github.com/falcosecurity/falco/pull/2388)] - [@FedeDP](https://github.com/FedeDP) * update(systemd): solve some issues with systemd unit [[#2385](https://github.com/falcosecurity/falco/pull/2385)] - [@Andreagit97](https://github.com/Andreagit97) * build(cmake): upgrade falcoctl to v0.3.0-rc6 [[#2383](https://github.com/falcosecurity/falco/pull/2383)] - [@leogr](https://github.com/leogr) * docs(.github): rules are no longer in this repo [[#2382](https://github.com/falcosecurity/falco/pull/2382)] - [@leogr](https://github.com/leogr) * update(CI): mitigate frequent failure in CircleCI jobs [[#2375](https://github.com/falcosecurity/falco/pull/2375)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace): use the right path for the `cpus_for_each_syscall_buffer` config [[#2378](https://github.com/falcosecurity/falco/pull/2378)] - [@Andreagit97](https://github.com/Andreagit97) * fix(scripts): fixed incorrect bash var expansion [[#2367](https://github.com/falcosecurity/falco/pull/2367)] - [@therealbobo](https://github.com/therealbobo) * update(CI): upgrade toolchain in modern falco builder dockerfile [[#2337](https://github.com/falcosecurity/falco/pull/2337)] - [@Andreagit97](https://github.com/Andreagit97) * cleanup(ci): move static analysis job from circle CI to GHA [[#2332](https://github.com/falcosecurity/falco/pull/2332)] - [@Andreagit97](https://github.com/Andreagit97) * update(falco): update cpp-httplib to 0.11.3 [[#2327](https://github.com/falcosecurity/falco/pull/2327)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(script): makes user able to pass custom option to driver-loade… [[#1901](https://github.com/falcosecurity/falco/pull/1901)] - [@andreabonanno](https://github.com/andreabonanno) * cleanup(ci): remove some unused jobs and remove some `falco-builder` reference where possible [[#2322](https://github.com/falcosecurity/falco/pull/2322)] - [@Andreagit97](https://github.com/Andreagit97) * docs(proposal): new artifacts distribution proposal [[#2304](https://github.com/falcosecurity/falco/pull/2304)] - [@leogr](https://github.com/leogr) * fix(cmake): properly fetch dev version by appending latest Falco tag, delta between master and tag, and hash [[#2292](https://github.com/falcosecurity/falco/pull/2292)] - [@FedeDP](https://github.com/FedeDP) * chore(deps): Bump certifi from 2020.4.5.1 to 2022.12.7 in /test [[#2313](https://github.com/falcosecurity/falco/pull/2313)] - [@dependabot[bot]](https://github.com/apps/dependabot) * chore: remove string view lite [[#2307](https://github.com/falcosecurity/falco/pull/2307)] - [@leogr](https://github.com/leogr) * new(CHANGELOG): add entry for 0.33.1 (in master branch this time) [[#2303](https://github.com/falcosecurity/falco/pull/2303)] - [@LucaGuerra](https://github.com/LucaGuerra) * update(docs): add overview and versioning sections to falco release.md [[#2205](https://github.com/falcosecurity/falco/pull/2205)] - [@incertum](https://github.com/incertum) * Add Xenit AB to adopters [[#2285](https://github.com/falcosecurity/falco/pull/2285)] - [@NissesSenap](https://github.com/NissesSenap) * fix(userspace/falco): verify engine fields only for syscalls [[#2281](https://github.com/falcosecurity/falco/pull/2281)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(output): do not print syscall_buffer_size when gvisor is enabled [[#2283](https://github.com/falcosecurity/falco/pull/2283)] - [@alacuku](https://github.com/alacuku) * fix(engine): fix warning about redundant std::move [[#2286](https://github.com/falcosecurity/falco/pull/2286)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(scripts): force falco-driver-loader script to try to compile the driver anyway even on unsupported platforms [[#2219](https://github.com/falcosecurity/falco/pull/2219)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): fixed version bucket for release jobs. [[#2266](https://github.com/falcosecurity/falco/pull/2266)] - [@FedeDP](https://github.com/FedeDP) ## v0.33.1 Released on 2022-11-24 ### Minor Changes * update(falco): fix container-gvisor and kubernetes-gvisor print options [[#2288](https://github.com/falcosecurity/falco/pull/2288)] * Update libs to 0.9.2, fixing potential CLBO on gVisor+Kubernetes and crash with eBPF when some CPUs are offline [[#2299](https://github.com/falcosecurity/falco/pull/2299)] - [@LucaGuerra](https://github.com/LucaGuerra) ## v0.33.0 Released on 2022-10-19 ### Major Changes * new: add a `drop_pct` referred to the global number of events [[#2130](https://github.com/falcosecurity/falco/pull/2130)] - [@Andreagit97](https://github.com/Andreagit97) * new: print some info about eBPF and enabled sources when Falco starts [[#2133](https://github.com/falcosecurity/falco/pull/2133)] - [@Andreagit97](https://github.com/Andreagit97) * new(userspace): print architecture information [[#2147](https://github.com/falcosecurity/falco/pull/2147)] - [@Andreagit97](https://github.com/Andreagit97) * new(CI): add CodeQL security scanning to Falco. [[#2171](https://github.com/falcosecurity/falco/pull/2171)] - [@Andreagit97](https://github.com/Andreagit97) * new: configure syscall buffer dimension from Falco [[#2214](https://github.com/falcosecurity/falco/pull/2214)] - [@Andreagit97](https://github.com/Andreagit97) * new(cmdline): add development support for modern BPF probe [[#2221](https://github.com/falcosecurity/falco/pull/2221)] - [@Andreagit97](https://github.com/Andreagit97) * new(falco-driver-loader): `DRIVERS_REPO` now supports the use of multiple download URLs (comma separated) [[#2165](https://github.com/falcosecurity/falco/pull/2165)] - [@IanRobertson-wpe](https://github.com/IanRobertson-wpe) * new(userspace/engine): support alternative plugin version requirements in checks [[#2190](https://github.com/falcosecurity/falco/pull/2190)] - [@jasondellaluce](https://github.com/jasondellaluce) * new: support running multiple event sources in parallel [[#2182](https://github.com/falcosecurity/falco/pull/2182)] - [@jasondellaluce](https://github.com/jasondellaluce) * new(userspace/falco): automatically create paths for grpc unix socket and gvisor endpoint. [[#2189](https://github.com/falcosecurity/falco/pull/2189)] - [@FedeDP](https://github.com/FedeDP) * new(scripts): allow falco-driver-loader to properly distinguish any ubuntu flavor [[#2178](https://github.com/falcosecurity/falco/pull/2178)] - [@FedeDP](https://github.com/FedeDP) * new: add option to enable event sources selectively [[#2085](https://github.com/falcosecurity/falco/pull/2085)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Minor Changes * docs(falco-driver-loader): add some comments in `falco-driver-loader` [[#2153](https://github.com/falcosecurity/falco/pull/2153)] - [@Andreagit97](https://github.com/Andreagit97) * update(cmake): use latest libs tag `0.9.0` [[#2257](https://github.com/falcosecurity/falco/pull/2257)] - [@Andreagit97](https://github.com/Andreagit97) * update(.circleci): re-enabled cppcheck [[#2186](https://github.com/falcosecurity/falco/pull/2186)] - [@leogr](https://github.com/leogr) * update(userspace/engine): improve falco files loading performance [[#2151](https://github.com/falcosecurity/falco/pull/2151)] - [@VadimZy](https://github.com/VadimZy) * update(cmake): use latest driver tag 3.0.1+driver [[#2251](https://github.com/falcosecurity/falco/pull/2251)] - [@Andreagit97](https://github.com/Andreagit97) * update(userspace/falco)!: adapt stats writer for multiple parallel event sources [[#2182](https://github.com/falcosecurity/falco/pull/2182)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor(userspace/engine): remove falco engine APIs that returned a required_engine_version [[#2096](https://github.com/falcosecurity/falco/pull/2096)] - [@mstemm](https://github.com/mstemm) * update(userspace/engine): add some small changes to rules matching that reduce cpu usage with high event volumes (> 1M syscalls/sec) [[#2210](https://github.com/falcosecurity/falco/pull/2210)] - [@mstemm](https://github.com/mstemm) * rules: added process IDs to default rules [[#2211](https://github.com/falcosecurity/falco/pull/2211)] - [@spyder-kyle](https://github.com/spyder-kyle) * update(scripts/debian): falco.service systemd unit is now cleaned-up during (re)install and removal via the DEB and RPM packages [[#2138](https://github.com/falcosecurity/falco/pull/2138)] - [@Happy-Dude](https://github.com/Happy-Dude) * update(userspace/falco): move on from deprecated libs API for printing event list [[#2253](https://github.com/falcosecurity/falco/pull/2253)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore(userspace/falco): improve cli helper and log options with debug level [[#2252](https://github.com/falcosecurity/falco/pull/2252)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(userspace): minor pre-release improvements [[#2236](https://github.com/falcosecurity/falco/pull/2236)] - [@jasondellaluce](https://github.com/jasondellaluce) * update: bumped libs to fd46dd139a8e35692a7d40ab2f0ed2016df827cf. [[#2201](https://github.com/falcosecurity/falco/pull/2201)] - [@FedeDP](https://github.com/FedeDP) * update!: gVisor sock default path changed from `/tmp/gvisor.sock` to `/run/falco/gvisor.sock` [[#2163](https://github.com/falcosecurity/falco/pull/2163)] - [@vjjmiras](https://github.com/vjjmiras) * update!: gRPC server sock default path changed from `/run/falco.sock.sock` to `/run/falco/falco.sock` [[#2163](https://github.com/falcosecurity/falco/pull/2163)] - [@vjjmiras](https://github.com/vjjmiras) * update(scripts/falco-driver-loader): minikube environment is now correctly detected [[#2191](https://github.com/falcosecurity/falco/pull/2191)] - [@alacuku](https://github.com/alacuku) * update(rules/falco_rules.yaml): `required_engine_version` changed to 13 [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum) * refactor(userspace/falco): re-design stats writer and make it thread-safe [[#2109](https://github.com/falcosecurity/falco/pull/2109)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor(userspace/falco): make signal handlers thread safe [[#2091](https://github.com/falcosecurity/falco/pull/2091)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor(userspace/engine): strengthen and document thread-safety guarantees of falco_engine::process_event [[#2082](https://github.com/falcosecurity/falco/pull/2082)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(userspace/falco): make webserver threadiness configurable [[#2090](https://github.com/falcosecurity/falco/pull/2090)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor(userspace/falco): reduce app actions dependency on app state and inspector [[#2097](https://github.com/falcosecurity/falco/pull/2097)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(userspace/falco): use move semantics in falco logger [[#2095](https://github.com/falcosecurity/falco/pull/2095)] - [@jasondellaluce](https://github.com/jasondellaluce) * update: use `FALCO_HOSTNAME` env var to override the hostname value [[#2174](https://github.com/falcosecurity/falco/pull/2174)] - [@leogr](https://github.com/leogr) * update: bump libs and driver versions to 6599e2efebce30a95f27739d655d53f0d5f686e4 [[#2177](https://github.com/falcosecurity/falco/pull/2177)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor(userspace/falco): make output rate limiter optional and output engine explicitly thread-safe [[#2139](https://github.com/falcosecurity/falco/pull/2139)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(falco.yaml)!: notification rate limiter disabled by default. [[#2139](https://github.com/falcosecurity/falco/pull/2139)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Bug Fixes * fix: compute the `drop ratio` in the right way [[#2128](https://github.com/falcosecurity/falco/pull/2128)] - [@Andreagit97](https://github.com/Andreagit97) * fix(falco_service): falco service needs to write under /sys/module/falco [[#2238](https://github.com/falcosecurity/falco/pull/2238)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace): cleanup output of ruleset validation result [[#2248](https://github.com/falcosecurity/falco/pull/2248)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace): properly print ignored syscalls messages when not in `-A` mode [[#2243](https://github.com/falcosecurity/falco/pull/2243)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(falco): clarify pid/tid and container info in gvisor [[#2223](https://github.com/falcosecurity/falco/pull/2223)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(userspace/engine): avoid reading duplicate exception values [[#2200](https://github.com/falcosecurity/falco/pull/2200)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix: hostname was not present when `json_output: true` [[#2174](https://github.com/falcosecurity/falco/pull/2174)] - [@leogr](https://github.com/leogr) ### Rule Changes * rule(macro: known_gke_mount_in_privileged_containers): add new macro [[#2198](https://github.com/falcosecurity/falco/pull/2198)] - [@hi120ki](https://github.com/hi120ki) * rule(Mount Launched in Privileged Container): add GKE default pod into allowlist in Mount Launched of Privileged Container rule [[#2198](https://github.com/falcosecurity/falco/pull/2198)] - [@hi120ki](https://github.com/hi120ki) * rule(list: known_binaries_to_read_environment_variables_from_proc_files): add new list [[#2193](https://github.com/falcosecurity/falco/pull/2193)] - [@hi120ki](https://github.com/hi120ki) * rule(Read environment variable from /proc files): add rule to detect an attempt to read process environment variables from /proc files [[#2193](https://github.com/falcosecurity/falco/pull/2193)] - [@hi120ki](https://github.com/hi120ki) * rule(macro: k8s_containers): add falco no-driver images [[#2234](https://github.com/falcosecurity/falco/pull/2234)] - [@jasondellaluce](https://github.com/jasondellaluce) * rule(macro: open_file_failed): add new macro [[#2118](https://github.com/falcosecurity/falco/pull/2118)] - [@incertum](https://github.com/incertum) * rule(macro: directory_traversal): add new macro [[#2118](https://github.com/falcosecurity/falco/pull/2118)] - [@incertum](https://github.com/incertum) * rule(Directory traversal monitored file read): add new rule [[#2118](https://github.com/falcosecurity/falco/pull/2118)] - [@incertum](https://github.com/incertum) * rule(Modify Container Entrypoint): new rule created to detect CVE-2019-5736 [[#2188](https://github.com/falcosecurity/falco/pull/2188)] - [@darryk10](https://github.com/darryk10) * rule(Program run with disallowed http proxy env)!: disabled by default [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum) * rule(Container Drift Detected (chmod))!: disabled by default [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum) * rule(Container Drift Detected (open+create))!: disabled by default [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum) * rule(Packet socket created in container)!: removed consider_packet_socket_communication macro [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum) * rule(macro: consider_packet_socket_communication)!: remove unused macro [[#2179](https://github.com/falcosecurity/falco/pull/2179)] - [@incertum](https://github.com/incertum) * rule(Interpreted procs outbound network activity)!: disabled by default [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum) * rule(Interpreted procs inbound network activity)!: disabled by default [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum) * rule(Contact cloud metadata service from container)!: disabled by default [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum) * rule(macro: consider_interpreted_outbound)!: remove unused macro [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum) * rule(macro: consider_interpreted_inbound)!: remove unused macro [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum) * rule(macro: consider_metadata_access)!: remove unused macro [[#2166](https://github.com/falcosecurity/falco/pull/2166)] - [@incertum](https://github.com/incertum) * rule(Unexpected outbound connection destination)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Unexpected inbound connection source)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Read Shell Configuration File)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Schedule Cron Jobs)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Launch Suspicious Network Tool on Host)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Create Hidden Files or Directories)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Outbound or Inbound Traffic not to Authorized Server Process and Port)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Network Connection outside Local Subnet)!: disabled by default [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: consider_all_outbound_conns)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: consider_all_inbound_conns)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: consider_shell_config_reads)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: consider_all_cron_jobs)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: consider_all_inbound_conns)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: consider_hidden_file_creation)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: allowed_port)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: enabled_rule_network_only_subnet)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: consider_userfaultfd_activities)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(macro: consider_all_chmods)!: remove unused macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Set Setuid or Setgid bit)!: removed consider_all_chmods macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Container Drift Detected (chmod))!: removed consider_all_chmods macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) * rule(Unprivileged Delegation of Page Faults Handling to a Userspace Process)!: removed consider_userfaultfd_activities macro [[#2168](https://github.com/falcosecurity/falco/pull/2168)] - [@incertum](https://github.com/incertum) ### Non user-facing changes * new(userspace): support `SCAP_FILTERED_EVENT` return code [[#2148](https://github.com/falcosecurity/falco/pull/2148)] - [@Andreagit97](https://github.com/Andreagit97) * chore(test/utils): remove unused script [[#2157](https://github.com/falcosecurity/falco/pull/2157)] - [@Andreagit97](https://github.com/Andreagit97) * Enrich pull request template [[#2162](https://github.com/falcosecurity/falco/pull/2162)] - [@Andreagit97](https://github.com/Andreagit97) * vote: update(OWNERS): add Andrea Terzolo to owners [[#2185](https://github.com/falcosecurity/falco/pull/2185)] - [@Andreagit97](https://github.com/Andreagit97) * fix(CI): codespell should ignore `ro` word [[#2173](https://github.com/falcosecurity/falco/pull/2173)] - [@Andreagit97](https://github.com/Andreagit97) * chore: bump plugin version [[#2256](https://github.com/falcosecurity/falco/pull/2256)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace/falco): avoid using CPU when main thread waits for parallel event sources [[#2255](https://github.com/falcosecurity/falco/pull/2255)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(scripts): inject kmod script fails with some systemd versions [[#2250](https://github.com/falcosecurity/falco/pull/2250)] - [@Andreagit97](https://github.com/Andreagit97) * chore(userspace/falco): make logging optional when terminating, restarting, and reopening outputs [[#2249](https://github.com/falcosecurity/falco/pull/2249)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore: bump libs version [[#2244](https://github.com/falcosecurity/falco/pull/2244)] - [@Andreagit97](https://github.com/Andreagit97) * update(userspace): solve warnings and performance tips from cppcheck [[#2247](https://github.com/falcosecurity/falco/pull/2247)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/falco): make signal termination more robust with multi-threading [[#2235](https://github.com/falcosecurity/falco/pull/2235)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/falco): make termination and signal handlers more stable [[#2239](https://github.com/falcosecurity/falco/pull/2239)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace): safely check string bounded access [[#2237](https://github.com/falcosecurity/falco/pull/2237)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore: bump libs/driver to the latest release branch commit [[#2232](https://github.com/falcosecurity/falco/pull/2232)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace/falco): check plugin requirements when validating rule files [[#2233](https://github.com/falcosecurity/falco/pull/2233)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace): add explicit constructors and initializations [[#2229](https://github.com/falcosecurity/falco/pull/2229)] - [@jasondellaluce](https://github.com/jasondellaluce) * Add StackRox to adopters [[#2187](https://github.com/falcosecurity/falco/pull/2187)] - [@Molter73](https://github.com/Molter73) * fix(process_events): check the return value of `open_live_inspector` [[#2215](https://github.com/falcosecurity/falco/pull/2215)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace/engine): properly include stdexcept header to fix build. [[#2197](https://github.com/falcosecurity/falco/pull/2197)] - [@FedeDP](https://github.com/FedeDP) * refactor(userspace/engine): split rule loader classes for a more testable design [[#2206](https://github.com/falcosecurity/falco/pull/2206)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore(OWNERS): cleanup inactive reviewer [[#2204](https://github.com/falcosecurity/falco/pull/2204)] - [@leogr](https://github.com/leogr) * fix(circleci): falco-driver-loader image build must be done starting from just-pushed falco master image. [[#2194](https://github.com/falcosecurity/falco/pull/2194)] - [@FedeDP](https://github.com/FedeDP) * Support condition parse errors in rule loading results [[#2155](https://github.com/falcosecurity/falco/pull/2155)] - [@mstemm](https://github.com/mstemm) * docs: readme update [[#2183](https://github.com/falcosecurity/falco/pull/2183)] - [@leogr](https://github.com/leogr) * cleanup: rename legacy references [[#2180](https://github.com/falcosecurity/falco/pull/2180)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor(userspace/engine): increase const coherence in falco engine [[#2081](https://github.com/falcosecurity/falco/pull/2081)] - [@jasondellaluce](https://github.com/jasondellaluce) * Rules result handle multiple files [[#2158](https://github.com/falcosecurity/falco/pull/2158)] - [@mstemm](https://github.com/mstemm) * fix: print full rule load errors/warnings without verbose/-v [[#2156](https://github.com/falcosecurity/falco/pull/2156)] - [@mstemm](https://github.com/mstemm) ## v0.32.2 Released on 2022-08-09 ### Major Changes ### Bug Fixes * fix: Added ARCH to bpf download URL [[#2142](https://github.com/falcosecurity/falco/pull/2142)] - [@eric-engberg](https://github.com/eric-engberg) ## v0.32.1 Released on 2022-07-11 ### Major Changes * new(falco): add gVisor support [[#2078](https://github.com/falcosecurity/falco/pull/2078)] - [@LucaGuerra](https://github.com/LucaGuerra) * new(docker,scripts): add multiarch images and ARM64 packages [[#1990](https://github.com/falcosecurity/falco/pull/1990)] - [@FedeDP](https://github.com/FedeDP) ### Minor Changes * update(build): Switch from RSA/SHA1 to RSA/SHA256 signature in the RPM package [[#2044](https://github.com/falcosecurity/falco/pull/2044)] - [@vjjmiras](https://github.com/vjjmiras) * refactor(userspace/engine): drop macro source field in rules and rule loader [[#2094](https://github.com/falcosecurity/falco/pull/2094)] - [@jasondellaluce](https://github.com/jasondellaluce) * build: introduce `DRIVER_VERSION` that allows setting a driver version (which may differ from the falcosecurity/libs version) [[#2086](https://github.com/falcosecurity/falco/pull/2086)] - [@leogr](https://github.com/leogr) * update: add more info to `--version` output [[#2086](https://github.com/falcosecurity/falco/pull/2086)] - [@leogr](https://github.com/leogr) * build(scripts): publish deb repo has now a InRelease file [[#2060](https://github.com/falcosecurity/falco/pull/2060)] - [@FedeDP](https://github.com/FedeDP) * update(userspace/falco): make plugin init config optional and add --plugin-info CLI option [[#2059](https://github.com/falcosecurity/falco/pull/2059)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(userspace/falco): support libs logging [[#2093](https://github.com/falcosecurity/falco/pull/2093)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(falco): update libs to 0.7.0 [[#2119](https://github.com/falcosecurity/falco/pull/2119)] - [@LucaGuerra](https://github.com/LucaGuerra) ### Bug Fixes * fix(userspace/falco): ensure that only rules files named with `-V` are loaded when validating rules files. [[#2088](https://github.com/falcosecurity/falco/pull/2088)] - [@mstemm](https://github.com/mstemm) * fix(rules): use exit event in reverse shell detection rule [[#2076](https://github.com/falcosecurity/falco/pull/2076)] - [@alacuku](https://github.com/alacuku) * fix(scripts): falco-driver-loader script will now seek for drivers in driver/${ARCH}/ for x86_64 too. [[#2057](https://github.com/falcosecurity/falco/pull/2057)] - [@FedeDP](https://github.com/FedeDP) * fix(falco-driver-loader): building falco module with DKMS on Flatcar and supporting fetching pre-built module/eBPF probe [[#2043](https://github.com/falcosecurity/falco/pull/2043)] - [@jepio](https://github.com/jepio) ### Rule Changes * rule(Redirect STDOUT/STDIN to Network Connection in Container): changed priority to NOTICE [[#2092](https://github.com/falcosecurity/falco/pull/2092)] - [@leogr](https://github.com/leogr) * rule(Java Process Class Download): detect potential log4shell exploitation [[#2041](https://github.com/falcosecurity/falco/pull/2041)] - [@pirxthepilot](https://github.com/pirxthepilot) ### Non user-facing changes * remove kaizhe from falco rule owner [[#2050](https://github.com/falcosecurity/falco/pull/2050)] - [@Kaizhe](https://github.com/Kaizhe) * docs(readme): added arm64 mention + packages + badge. [[#2101](https://github.com/falcosecurity/falco/pull/2101)] - [@FedeDP](https://github.com/FedeDP) * new(circleci): enable integration tests for arm64. [[#2099](https://github.com/falcosecurity/falco/pull/2099)] - [@FedeDP](https://github.com/FedeDP) * chore(cmake): bump plugins versions [[#2102](https://github.com/falcosecurity/falco/pull/2102)] - [@Andreagit97](https://github.com/Andreagit97) * fix(docker): fixed deb tester sub image. [[#2100](https://github.com/falcosecurity/falco/pull/2100)] - [@FedeDP](https://github.com/FedeDP) * fix(ci): fix sign script - avoid interpreting '{*}$argv' too soon [[#2075](https://github.com/falcosecurity/falco/pull/2075)] - [@vjjmiras](https://github.com/vjjmiras) * fix(tests): make tests run locally (take 2) [[#2089](https://github.com/falcosecurity/falco/pull/2089)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(ci): creates ~/sign instead of ./sign [[#2072](https://github.com/falcosecurity/falco/pull/2072)] - [@vjjmiras](https://github.com/vjjmiras) * fix(ci): sign arm64 rpm packages. [[#2069](https://github.com/falcosecurity/falco/pull/2069)] - [@FedeDP](https://github.com/FedeDP) * update(falco_scripts): Change Flatcar dynlinker path [[#2066](https://github.com/falcosecurity/falco/pull/2066)] - [@jepio](https://github.com/jepio) * fix(scripts): fixed path in publish-deb script. [[#2062](https://github.com/falcosecurity/falco/pull/2062)] - [@FedeDP](https://github.com/FedeDP) * fix(build): docker-container buildx engine does not support retagging images. Tag all images together. [[#2058](https://github.com/falcosecurity/falco/pull/2058)] - [@FedeDP](https://github.com/FedeDP) * fix(build): fixed publish-docker-dev job context. [[#2056](https://github.com/falcosecurity/falco/pull/2056)] - [@FedeDP](https://github.com/FedeDP) * Correct linting issue in rules [[#2055](https://github.com/falcosecurity/falco/pull/2055)] - [@stephanmiehe](https://github.com/stephanmiehe) * Fix falco compilation issues with new libs [[#2053](https://github.com/falcosecurity/falco/pull/2053)] - [@alacuku](https://github.com/alacuku) * fix(scripts): forcefully create packages dir for debian packages. [[#2054](https://github.com/falcosecurity/falco/pull/2054)] - [@FedeDP](https://github.com/FedeDP) * fix(build): removed leftover line in circleci config. [[#2052](https://github.com/falcosecurity/falco/pull/2052)] - [@FedeDP](https://github.com/FedeDP) * fix(build): fixed circleCI artifacts publish for arm64. [[#2051](https://github.com/falcosecurity/falco/pull/2051)] - [@FedeDP](https://github.com/FedeDP) * update(docker): updated falco-builder to fix multiarch support. [[#2049](https://github.com/falcosecurity/falco/pull/2049)] - [@FedeDP](https://github.com/FedeDP) * fix(build): use apt instead of apk when installing deps for aws ecr publish [[#2047](https://github.com/falcosecurity/falco/pull/2047)] - [@FedeDP](https://github.com/FedeDP) * fix(build): try to use root user for cimg/base [[#2045](https://github.com/falcosecurity/falco/pull/2045)] - [@FedeDP](https://github.com/FedeDP) * update(build): avoid double build of docker images when pushing to aws ecr [[#2046](https://github.com/falcosecurity/falco/pull/2046)] - [@FedeDP](https://github.com/FedeDP) * chore(k8s_audit_plugin): bump k8s audit plugin version [[#2042](https://github.com/falcosecurity/falco/pull/2042)] - [@Andreagit97](https://github.com/Andreagit97) * fix(tests): make run_regression_tests.sh work locally [[#2020](https://github.com/falcosecurity/falco/pull/2020)] - [@LucaGuerra](https://github.com/LucaGuerra) * Circle CI build job for ARM64 [[#1997](https://github.com/falcosecurity/falco/pull/1997)] - [@odidev](https://github.com/odidev) ## v0.32.0 Released on 2022-06-03 ### Major Changes * new: added new `watch_config_files` config option, to trigger a Falco restart whenever a change is detected in the rules or config files [[#1991](https://github.com/falcosecurity/falco/pull/1991)] - [@FedeDP](https://github.com/FedeDP) * new(rules): add rule to detect excessively capable container [[#1963](https://github.com/falcosecurity/falco/pull/1963)] - [@loresuso](https://github.com/loresuso) * new(rules): add rules to detect pods sharing host pid and IPC namespaces [[#1951](https://github.com/falcosecurity/falco/pull/1951)] - [@loresuso](https://github.com/loresuso) * new(image): add Falco image based on RedHat UBI [[#1943](https://github.com/falcosecurity/falco/pull/1943)] - [@araujof](https://github.com/araujof) * new(falco): add --markdown and --list-syscall-events [[#1939](https://github.com/falcosecurity/falco/pull/1939)] - [@LucaGuerra](https://github.com/LucaGuerra) ### Minor Changes * update(build): updated plugins to latest versions. [[#2033](https://github.com/falcosecurity/falco/pull/2033)] - [@FedeDP](https://github.com/FedeDP) * refactor(userspace/falco): split the currently monolithic falco_init into smaller "actions", managed by the falco application's action manager. [[#1953](https://github.com/falcosecurity/falco/pull/1953)] - [@mstemm](https://github.com/mstemm) * rules: out of the box ruleset for OKTA Falco Plugin [[#1955](https://github.com/falcosecurity/falco/pull/1955)] - [@darryk10](https://github.com/darryk10) * update(build): updated libs to 39ae7d40496793cf3d3e7890c9bbdc202263836b [[#2031](https://github.com/falcosecurity/falco/pull/2031)] - [@FedeDP](https://github.com/FedeDP) * update!: moving out plugins ruleset files [[#1995](https://github.com/falcosecurity/falco/pull/1995)] - [@leogr](https://github.com/leogr) * update: added `hostname` as a field in JSON output [[#1989](https://github.com/falcosecurity/falco/pull/1989)] - [@Milkshak3s](https://github.com/Milkshak3s) * refactor!: remove K8S audit logs from Falco [[#1952](https://github.com/falcosecurity/falco/pull/1952)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor(userspace/engine): use supported_operators helper from libsinsp filter parser [[#1975](https://github.com/falcosecurity/falco/pull/1975)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor!: deprecate PSP regression tests and warn for unsafe usage of in k8s audit filters [[#1976](https://github.com/falcosecurity/falco/pull/1976)] - [@jasondellaluce](https://github.com/jasondellaluce) * build(cmake): upgrade catch2 to 2.13.9 [[#1977](https://github.com/falcosecurity/falco/pull/1977)] - [@leogr](https://github.com/leogr) * refactor(userspace/engine): reduce memory usage for resolving evttypes [[#1965](https://github.com/falcosecurity/falco/pull/1965)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor(userspace/engine): remove Lua from Falco and re-implement the rule loader [[#1966](https://github.com/falcosecurity/falco/pull/1966)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor(userspace/engine): decoupling ruleset reading, parsing, and compilation steps [[#1970](https://github.com/falcosecurity/falco/pull/1970)] - [@jasondellaluce](https://github.com/jasondellaluce) * refactor: update definitions of falco_common [[#1967](https://github.com/falcosecurity/falco/pull/1967)] - [@jasondellaluce](https://github.com/jasondellaluce) * update: improved Falco engine event processing performance [[#1944](https://github.com/falcosecurity/falco/pull/1944)] - [@deepskyblue86](https://github.com/deepskyblue86) * refactor(userspace/engine): use libsinsp filter parser and compiler inside rule loader [[#1947](https://github.com/falcosecurity/falco/pull/1947)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Bug Fixes * fix(userspace/engine): skip rules with unknown sources that also have exceptions, and skip macros with unknown sources. [[#1920](https://github.com/falcosecurity/falco/pull/1920)] - [@mstemm](https://github.com/mstemm) * fix(userspace/falco): enable k8s and mesos clients only when syscall source is enabled [[#2019](https://github.com/falcosecurity/falco/pull/2019)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Rule Changes * rule(Launch Excessively Capable Container): fix typo in description [[#1996](https://github.com/falcosecurity/falco/pull/1996)] - [@mmonitz](https://github.com/mmonitz) * rule(macro: known_shell_spawn_cmdlines): add `sh -c /usr/share/lighttpd/create-mime.conf.pl` to macro [[#1996](https://github.com/falcosecurity/falco/pull/1996)] - [@mmonitz](https://github.com/mmonitz) * rule(macro net_miner_pool): additional syscall for detection [[#2011](https://github.com/falcosecurity/falco/pull/2011)] - [@beryxz](https://github.com/beryxz) * rule(macro truncate_shell_history): include .ash_history [[#1956](https://github.com/falcosecurity/falco/pull/1956)] - [@bdashrad](https://github.com/bdashrad) * rule(macro modify_shell_history): include .ash_history [[#1956](https://github.com/falcosecurity/falco/pull/1956)] - [@bdashrad](https://github.com/bdashrad) * rule(Detect release_agent File Container Escapes): new rule created to detect an attempt to exploit a container escape using release_agent file [[#1969](https://github.com/falcosecurity/falco/pull/1969)] - [@darryk10](https://github.com/darryk10) * rule(k8s: secret): detect `get` attempts for both successful and unsuccessful attempts [[#1949](https://github.com/falcosecurity/falco/pull/1949)] - [@Dentrax](https://github.com/Dentrax) * rule(K8s Serviceaccount Created/Deleted): Fixed output for the rules [[#1973](https://github.com/falcosecurity/falco/pull/1973)] - [@darryk10](https://github.com/darryk10) * rule(Disallowed K8s User): exclude allowed EKS users [[#1960](https://github.com/falcosecurity/falco/pull/1960)] - [@darryk10](https://github.com/darryk10) * rule(Launch Ingress Remote File Copy Tools in Container): Removed use cases not triggering the rule [[#1968](https://github.com/falcosecurity/falco/pull/1968)] - [@darryk10](https://github.com/darryk10) * rule(Mount Launched in Privileged Container): added allowlist macro user_known_mount_in_privileged_containers. [[#1930](https://github.com/falcosecurity/falco/pull/1930)] - [@mmoyerfigma](https://github.com/mmoyerfigma) * rule(macro user_known_shell_config_modifiers): allow to allowlist shell config modifiers [[#1938](https://github.com/falcosecurity/falco/pull/1938)] - [@claudio-vellage](https://github.com/claudio-vellage) ### Non user-facing changes * new: update plugins [[#2023](https://github.com/falcosecurity/falco/pull/2023)] - [@FedeDP](https://github.com/FedeDP) * update(build): updated libs version for Falco 0.32.0 release. [[#2022](https://github.com/falcosecurity/falco/pull/2022)] - [@FedeDP](https://github.com/FedeDP) * update(build): updated libs to 1be924900a09cf2e4db4b4ae13d03d838959f350 [[#2024](https://github.com/falcosecurity/falco/pull/2024)] - [@FedeDP](https://github.com/FedeDP) * chore(userspace/falco): do not print error code in process_events.cpp [[#2030](https://github.com/falcosecurity/falco/pull/2030)] - [@alacuku](https://github.com/alacuku) * fix(falco-scripts): remove driver versions with `dkms-3.0.3` [[#2027](https://github.com/falcosecurity/falco/pull/2027)] - [@Andreagit97](https://github.com/Andreagit97) * chore(userspace/falco): fix punctuation typo in output message when loading plugins [[#2026](https://github.com/falcosecurity/falco/pull/2026)] - [@alacuku](https://github.com/alacuku) * refactor(userspace): change falco engine design to properly support multiple sources [[#2017](https://github.com/falcosecurity/falco/pull/2017)] - [@jasondellaluce](https://github.com/jasondellaluce) * update(userspace/falco): improve falco termination [[#2012](https://github.com/falcosecurity/falco/pull/2012)] - [@Andreagit97](https://github.com/Andreagit97) * update(userspace/engine): introduce new `check_plugin_requirements` API [[#2009](https://github.com/falcosecurity/falco/pull/2009)] - [@Andreagit97](https://github.com/Andreagit97) * fix(userspace/engine): improve rule loader source checks [[#2010](https://github.com/falcosecurity/falco/pull/2010)] - [@Andreagit97](https://github.com/Andreagit97) * fix: split filterchecks per source-idx [[#1999](https://github.com/falcosecurity/falco/pull/1999)] - [@FedeDP](https://github.com/FedeDP) * new: port CI builds to github actions [[#2000](https://github.com/falcosecurity/falco/pull/2000)] - [@FedeDP](https://github.com/FedeDP) * build(userspace/engine): cleanup unused include dir [[#1987](https://github.com/falcosecurity/falco/pull/1987)] - [@leogr](https://github.com/leogr) * rule(Anonymous Request Allowed): exclude {/livez, /readyz} [[#1954](https://github.com/falcosecurity/falco/pull/1954)] - [@sledigabel](https://github.com/sledigabel) * chore(falco_scripts): Update `falco-driver-loader` cleaning phase [[#1950](https://github.com/falcosecurity/falco/pull/1950)] - [@Andreagit97](https://github.com/Andreagit97) * new(userspace/falco): use new plugin caps API [[#1982](https://github.com/falcosecurity/falco/pull/1982)] - [@FedeDP](https://github.com/FedeDP) * build: correct conffiles for DEB packages [[#1980](https://github.com/falcosecurity/falco/pull/1980)] - [@leogr](https://github.com/leogr) * Fix exception parsing regressions [[#1985](https://github.com/falcosecurity/falco/pull/1985)] - [@mstemm](https://github.com/mstemm) * Add codespell GitHub Action [[#1962](https://github.com/falcosecurity/falco/pull/1962)] - [@invidian](https://github.com/invidian) * build: components opt-in mechanism for packages [[#1979](https://github.com/falcosecurity/falco/pull/1979)] - [@leogr](https://github.com/leogr) * add gVisor to ADOPTERS.md [[#1974](https://github.com/falcosecurity/falco/pull/1974)] - [@kevinGC](https://github.com/kevinGC) * rules: whitelist GCP's container threat detection image [[#1959](https://github.com/falcosecurity/falco/pull/1959)] - [@clmssz](https://github.com/clmssz) * Fix some typos [[#1961](https://github.com/falcosecurity/falco/pull/1961)] - [@invidian](https://github.com/invidian) * chore(rules): remove leftover [[#1958](https://github.com/falcosecurity/falco/pull/1958)] - [@leogr](https://github.com/leogr) * docs: readme update and plugins [[#1940](https://github.com/falcosecurity/falco/pull/1940)] - [@leogr](https://github.com/leogr) ## v0.31.1 Released on 2022-03-09 ### Major Changes * new: add a new drop category `n_drops_scratch_map` [[#1916](https://github.com/falcosecurity/falco/pull/1916)] - [@Andreagit97](https://github.com/Andreagit97) * new: allow to specify multiple --cri options [[#1893](https://github.com/falcosecurity/falco/pull/1893)] - [@FedeDP](https://github.com/FedeDP) ### Minor Changes * refactor(userspace/falco): replace direct getopt_long() cmdline option parsing with third-party cxxopts library. [[#1886](https://github.com/falcosecurity/falco/pull/1886)] - [@mstemm](https://github.com/mstemm) * update: driver version is b7eb0dd [[#1923](https://github.com/falcosecurity/falco/pull/1923)] - [@LucaGuerra](https://github.com/LucaGuerra) ### Bug Fixes * fix(userspace/falco): correct plugins init config conversion from YAML to JSON [[#1907](https://github.com/falcosecurity/falco/pull/1907)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/engine): for rules at the informational level being loaded at the notice level [[#1885](https://github.com/falcosecurity/falco/pull/1885)] - [@mike-stewart](https://github.com/mike-stewart) * chore(userspace/falco): fixes truncated -b option description. [[#1915](https://github.com/falcosecurity/falco/pull/1915)] - [@andreabonanno](https://github.com/andreabonanno) * update(falco): updates usage description for -o, --option [[#1903](https://github.com/falcosecurity/falco/pull/1903)] - [@andreabonanno](https://github.com/andreabonanno) ### Security Fixes * Fix for a TOCTOU issue that could lead to rule bypass (CVE-2022-26316). For more information, see the [advisory](https://github.com/falcosecurity/falco/security/advisories/GHSA-6v9j-2vm2-ghf7). ### Rule Changes * rule(Detect outbound connections to common miner pool ports): fix url in rule output [[#1918](https://github.com/falcosecurity/falco/pull/1918)] - [@jsoref](https://github.com/jsoref) * rule(macro somebody_becoming_themself): renaming macro to somebody_becoming_themselves [[#1918](https://github.com/falcosecurity/falco/pull/1918)] - [@jsoref](https://github.com/jsoref) * rule(list package_mgmt_binaries): `npm` added [[#1866](https://github.com/falcosecurity/falco/pull/1866)] - [@rileydakota](https://github.com/rileydakota) * rule(Launch Package Management Process in Container): support for detecting `npm` usage [[#1866](https://github.com/falcosecurity/falco/pull/1866)] - [@rileydakota](https://github.com/rileydakota) * rule(Polkit Local Privilege Escalation Vulnerability): new rule created to detect CVE-2021-4034 [[#1877](https://github.com/falcosecurity/falco/pull/1877)] - [@darryk10](https://github.com/darryk10) * rule(macro: modify_shell_history): avoid false-positive alerts triggered by modifications to .zsh_history.new and .zsh_history.LOCK files [[#1832](https://github.com/falcosecurity/falco/pull/1832)] - [@m4wh6k](https://github.com/m4wh6k) * rule(macro: truncate_shell_history): avoid false-positive alerts triggered by modifications to .zsh_history.new and .zsh_history.LOCK files [[#1832](https://github.com/falcosecurity/falco/pull/1832)] - [@m4wh6k](https://github.com/m4wh6k) * rule(macro sssd_writing_krb): fixed a false-positive alert that was being generated when SSSD updates /etc/krb5.keytab [[#1825](https://github.com/falcosecurity/falco/pull/1825)] - [@mac-chaffee](https://github.com/mac-chaffee) * rule(macro write_etc_common): fixed a false-positive alert that was being generated when SSSD updates /etc/krb5.keytab [[#1825](https://github.com/falcosecurity/falco/pull/1825)] - [@mac-chaffee](https://github.com/mac-chaffee) * upgrade macro(keepalived_writing_conf) [[#1742](https://github.com/falcosecurity/falco/pull/1742)] - [@pabloopez](https://github.com/pabloopez) * rule_output(Delete Bucket Public Access Block) typo [[#1888](https://github.com/falcosecurity/falco/pull/1888)] - [@pabloopez](https://github.com/pabloopez) ### Non user-facing changes * fix(build): fix civetweb linking in cmake module [[#1919](https://github.com/falcosecurity/falco/pull/1919)] - [@LucaGuerra](https://github.com/LucaGuerra) * chore(userspace/engine): remove unused lua functions and state vars [[#1908](https://github.com/falcosecurity/falco/pull/1908)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/falco): applies FALCO_INSTALL_CONF_FILE as the default … [[#1900](https://github.com/falcosecurity/falco/pull/1900)] - [@andreabonanno](https://github.com/andreabonanno) * fix(scripts): correct typo in `falco-driver-loader` help message [[#1899](https://github.com/falcosecurity/falco/pull/1899)] - [@leogr](https://github.com/leogr) * update(build)!: replaced various `PROBE` with `DRIVER` where necessary. [[#1887](https://github.com/falcosecurity/falco/pull/1887)] - [@FedeDP](https://github.com/FedeDP) * Add [Fairwinds](https://fairwinds.com) to the adopters list [[#1917](https://github.com/falcosecurity/falco/pull/1917)] - [@sudermanjr](https://github.com/sudermanjr) * build(cmake): several cmake changes to speed up/simplify builds for external projects and copying files from source-to-build directories [[#1905](https://github.com/falcosecurity/falco/pull/1905)] - [@mstemm](https://github.com/mstemm) ## v0.31.0 Released on 2022-01-31 ### Major Changes * new: add support for plugins to extend Falco functionality to new event sources and custom fields [[#1753](https://github.com/falcosecurity/falco/pull/1753)] - [@mstemm](https://github.com/mstemm) * new: add ability to set User-Agent http header when sending http output. Provide default value of 'falcosecurity/falco'. [[#1850](https://github.com/falcosecurity/falco/pull/1850)] - [@yoshi314](https://github.com/yoshi314) * new(configuration): support defining plugin init config as a YAML [[#1852](https://github.com/falcosecurity/falco/pull/1852)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Minor Changes * rules: add the official Falco ECR repository to rules [[#1817](https://github.com/falcosecurity/falco/pull/1817)] - [@calvinbui](https://github.com/calvinbui) * build: update CircleCI machine image for eBPF tests to a newer version of ubuntu [[#1764](https://github.com/falcosecurity/falco/pull/1764)] - [@mstemm](https://github.com/mstemm) * update(engine): refactor Falco engine to be agnostic to specific event sources [[#1715](https://github.com/falcosecurity/falco/pull/1715)] - [@mstemm](https://github.com/mstemm) * build: upgrade civetweb to v1.15 [[#1782](https://github.com/falcosecurity/falco/pull/1782)] - [@FedeDP](https://github.com/FedeDP) * update: driver version is 319368f1ad778691164d33d59945e00c5752cd27 now [[#1861](https://github.com/falcosecurity/falco/pull/1861)] - [@FedeDP](https://github.com/FedeDP) * build: allow using local libs source dir by setting `FALCOSECURITY_LIBS_SOURCE_DIR` in cmake [[#1791](https://github.com/falcosecurity/falco/pull/1791)] - [@jasondellaluce](https://github.com/jasondellaluce) * build: the statically linked binary package is now published with the `-static` suffix [[#1873](https://github.com/falcosecurity/falco/pull/1873)] - [@LucaGuerra](https://github.com/LucaGuerra) * update!: removed "--alternate-lua-dir" cmdline option as lua scripts are now embedded in Falco executable. [[#1872](https://github.com/falcosecurity/falco/pull/1872)] - [@FedeDP](https://github.com/FedeDP) * build: switch to dynamic build for the binary package (`.tar.gz`) [[#1853](https://github.com/falcosecurity/falco/pull/1853)] - [@LucaGuerra](https://github.com/LucaGuerra) * update: simpleconsumer filtering is now being done at kernel level [[#1846](https://github.com/falcosecurity/falco/pull/1846)] - [@FedeDP](https://github.com/FedeDP) * update(scripts/falco-driver-loader): first try to load the latest kmod version, then fallback to an already installed if any [[#1863](https://github.com/falcosecurity/falco/pull/1863)] - [@leogr](https://github.com/leogr) * refactor: clean up --list output with better formatting and no duplicate sections across event sources. [[#1816](https://github.com/falcosecurity/falco/pull/1816)] - [@mstemm](https://github.com/mstemm) * update: embed .lua files used to load/compile rules into the main falco executable, for simplicity and to avoid tampering. [[#1843](https://github.com/falcosecurity/falco/pull/1843)] - [@mstemm](https://github.com/mstemm) * update: support non-enumerable event sources in gRPC outputs service [[#1840](https://github.com/falcosecurity/falco/pull/1840)] - [@jasondellaluce](https://github.com/jasondellaluce) * docs: add jasondellaluce to OWNERS [[#1818](https://github.com/falcosecurity/falco/pull/1818)] - [@jasondellaluce](https://github.com/jasondellaluce) * chore: --list option can be used to selectively list fields related to new sources that are introduced by plugins [[#1839](https://github.com/falcosecurity/falco/pull/1839)] - [@loresuso](https://github.com/loresuso) * update(userspace/falco): support arbitrary-depth nested values in YAML configuration [[#1792](https://github.com/falcosecurity/falco/pull/1792)] - [@jasondellaluce](https://github.com/jasondellaluce) * build: bump FakeIt version to 2.0.9 [[#1797](https://github.com/falcosecurity/falco/pull/1797)] - [@jasondellaluce](https://github.com/jasondellaluce) * update: allow append of new exceptions to rules [[#1780](https://github.com/falcosecurity/falco/pull/1780)] - [@sai-arigeli](https://github.com/sai-arigeli) * update: Linux packages are now signed with SHA256 [[#1758](https://github.com/falcosecurity/falco/pull/1758)] - [@twa16](https://github.com/twa16) ### Bug Fixes * fix(scripts/falco-driver-loader): fix for SELinux insmod denials [[#1756](https://github.com/falcosecurity/falco/pull/1756)] - [@dwindsor](https://github.com/dwindsor) * fix(scripts/falco-driver-loader): correctly clean loaded drivers when using `--clean` [[#1795](https://github.com/falcosecurity/falco/pull/1795)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(userspace/falco): in case output_file cannot be opened, throw a falco exception [[#1773](https://github.com/falcosecurity/falco/pull/1773)] - [@FedeDP](https://github.com/FedeDP) * fix(userspace/engine): support jsonpointer escaping in rule parser [[#1777](https://github.com/falcosecurity/falco/pull/1777)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(scripts/falco-driver-loader): support kernel object files in `.zst` and `.gz` compression formats [[#1863](https://github.com/falcosecurity/falco/pull/1863)] - [@leogr](https://github.com/leogr) * fix(engine): correctly format json output in json_event [[#1847](https://github.com/falcosecurity/falco/pull/1847)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix: set http output content type to text/plain when json output is disabled [[#1829](https://github.com/falcosecurity/falco/pull/1829)] - [@FedeDP](https://github.com/FedeDP) * fix(userspace/falco): accept 'Content-Type' header that contains "application/json", but it is not strictly equal to it [[#1800](https://github.com/falcosecurity/falco/pull/1800)] - [@FedeDP](https://github.com/FedeDP) * fix(userspace/engine): supporting enabled-only overwritten rules [[#1775](https://github.com/falcosecurity/falco/pull/1775)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Rule Changes * rule(Create Symlink Over Sensitive File): corrected typo in rule output [[#1820](https://github.com/falcosecurity/falco/pull/1820)] - [@deepskyblue86](https://github.com/deepskyblue86) * rule(macro open_write): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce) * rule(macro open_read): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce) * rule(macro open_directory): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce) * rule(Create files below dev): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce) * rule(Container Drift Detected (open+create)): add support to openat2 [[#1796](https://github.com/falcosecurity/falco/pull/1796)] - [@jasondellaluce](https://github.com/jasondellaluce) * rule(macro sensitive_mount): add containerd socket [[#1815](https://github.com/falcosecurity/falco/pull/1815)] - [@loresuso](https://github.com/loresuso) * rule(macro spawned_process): monitor also processes spawned by `execveat` [[#1868](https://github.com/falcosecurity/falco/pull/1868)] - [@Andreagit97](https://github.com/Andreagit97) * rule(Create Hardlink Over Sensitive Files): new rule to detect hard links created over sensitive files [[#1810](https://github.com/falcosecurity/falco/pull/1810)] - [@sberkovich](https://github.com/sberkovich) * rule(Detect crypto miners using the Stratum protocol): add `stratum2+tcp` and `stratum+ssl` protocols detection [[#1810](https://github.com/falcosecurity/falco/pull/1810)] - [@sberkovich](https://github.com/sberkovich) * rule(Sudo Potential Privilege Escalation): correct special case for the CVE-2021-3156 exploit [[#1810](https://github.com/falcosecurity/falco/pull/1810)] - [@sberkovich](https://github.com/sberkovich) * rule(list falco_hostnetwork_images): moved to k8s_audit_rules.yaml to avoid a warning when using falco_rules.yaml only [[#1681](https://github.com/falcosecurity/falco/pull/1681)] - [@leodido](https://github.com/leodido) * rule(list deb_binaries): remove `apt-config` [[#1860](https://github.com/falcosecurity/falco/pull/1860)] - [@Andreagit97](https://github.com/Andreagit97) * rule(Launch Remote File Copy Tools in Container): add additional binaries: curl and wget. [[#1771](https://github.com/falcosecurity/falco/pull/1771)] - [@ec4n6](https://github.com/ec4n6) * rule(list known_sa_list): add coredns, coredns-autoscaler, endpointslicemirroring-controller, horizontal-pod-autoscaler, job-controller, node-controller (nodelifecycle), persistent-volume-binder, pv-protection-controller, pvc-protection-controller, root-ca-cert-publisher and service-account-controller as allowed service accounts in the kube-system namespace [[#1760](https://github.com/falcosecurity/falco/pull/1760)] - [@sboschman](https://github.com/sboschman) ### Non user-facing changes * fix: force-set evt.type for plugin source events [[#1878](https://github.com/falcosecurity/falco/pull/1878)] - [@FedeDP](https://github.com/FedeDP) * fix: updated some warning strings; properly refresh lua files embedded in falco [[#1864](https://github.com/falcosecurity/falco/pull/1864)] - [@FedeDP](https://github.com/FedeDP) * style(userspace/engine): avoid creating multiple versions of methods only to assume default ruleset. Use a default argument instead. [[#1754](https://github.com/falcosecurity/falco/pull/1754)] - [@FedeDP](https://github.com/FedeDP) * add raft in the adopters list [[#1776](https://github.com/falcosecurity/falco/pull/1776)] - [@teshsharma](https://github.com/teshsharma) * build: always populate partial version variables [[#1778](https://github.com/falcosecurity/falco/pull/1778)] - [@dnwe](https://github.com/dnwe) * build: updated cloudtrail plugin to latest version [[#1865](https://github.com/falcosecurity/falco/pull/1865)] - [@FedeDP](https://github.com/FedeDP) * replace ".." concatenation with table.concat [[#1834](https://github.com/falcosecurity/falco/pull/1834)] - [@VadimZy](https://github.com/VadimZy) * fix(userspace/engine): actually make m_filter_all_event_types useful by properly using it as fallback when no filter event types is provided [[#1875](https://github.com/falcosecurity/falco/pull/1875)] - [@FedeDP](https://github.com/FedeDP) * fix(build): do not show plugin options in musl optimized builds [[#1871](https://github.com/falcosecurity/falco/pull/1871)] - [@LucaGuerra](https://github.com/LucaGuerra) * fix(aws_cloudtrail_rules.yaml): correct required plugin versions [[#1867](https://github.com/falcosecurity/falco/pull/1867)] - [@FedeDP](https://github.com/FedeDP) * docs: fix priority level "info" to "informational" [[#1858](https://github.com/falcosecurity/falco/pull/1858)] - [@Andreagit97](https://github.com/Andreagit97) * Field properties changes [[#1838](https://github.com/falcosecurity/falco/pull/1838)] - [@mstemm](https://github.com/mstemm) * update(build): updated libs to latest master version; updated plugins versions [[#1856](https://github.com/falcosecurity/falco/pull/1856)] - [@FedeDP](https://github.com/FedeDP) * Add Giant Swarm to Adopters list [[#1842](https://github.com/falcosecurity/falco/pull/1842)] - [@stone-z](https://github.com/stone-z) * update(tests): remove `token_bucket` unit tests [[#1798](https://github.com/falcosecurity/falco/pull/1798)] - [@jasondellaluce](https://github.com/jasondellaluce) * fix(build): use consistent 7-character build abbrev sha [[#1830](https://github.com/falcosecurity/falco/pull/1830)] - [@LucaGuerra](https://github.com/LucaGuerra) * add Phoenix to adopters list [[#1806](https://github.com/falcosecurity/falco/pull/1806)] - [@kaldyka](https://github.com/kaldyka) * remove unused files in test directory [[#1801](https://github.com/falcosecurity/falco/pull/1801)] - [@jasondellaluce](https://github.com/jasondellaluce) * drop Falco luajit module, use the one provided by libs [[#1788](https://github.com/falcosecurity/falco/pull/1788)] - [@FedeDP](https://github.com/FedeDP) * chore(build): update libs version to 7906f7e [[#1790](https://github.com/falcosecurity/falco/pull/1790)] - [@LucaGuerra](https://github.com/LucaGuerra) * Add SysFlow to list of libs adopters [[#1747](https://github.com/falcosecurity/falco/pull/1747)] - [@araujof](https://github.com/araujof) * build: dropped centos8 circleci build because it is useless [[#1882](https://github.com/falcosecurity/falco/pull/1882)] - [@FedeDP](https://github.com/FedeDP) ## v0.30.0 Released on 2021-10-01 ### Major Changes * new: add `--k8s-node` command-line options, which allows filtering by a node when requesting metadata of pods to the K8s API server [[#1671](https://github.com/falcosecurity/falco/pull/1671)] - [@leogr](https://github.com/leogr) * new(outputs): expose rule tags and event source in gRPC and json outputs [[#1714](https://github.com/falcosecurity/falco/pull/1714)] - [@jasondellaluce](https://github.com/jasondellaluce) * new(userspace/falco): add customizable metadata fetching params [[#1667](https://github.com/falcosecurity/falco/pull/1667)] - [@zuc](https://github.com/zuc) ### Minor Changes * update: bump driver version to 3aa7a83bf7b9e6229a3824e3fd1f4452d1e95cb4 [[#1744](https://github.com/falcosecurity/falco/pull/1744)] - [@zuc](https://github.com/zuc) * docs: clarify that previous Falco drivers will remain available at https://download.falco.org and no automated cleanup is run anymore [[#1738](https://github.com/falcosecurity/falco/pull/1738)] - [@leodido](https://github.com/leodido) * update(outputs): add configuration option for tags in json outputs [[#1733](https://github.com/falcosecurity/falco/pull/1733)] - [@jasondellaluce](https://github.com/jasondellaluce) ### Bug Fixes * fix(scripts): correct standard output redirection in systemd config (DEB and RPM packages) [[#1697](https://github.com/falcosecurity/falco/pull/1697)] - [@chirabino](https://github.com/chirabino) * fix(scripts): correct lookup order when trying multiple `gcc` versions in the `falco-driver-loader` script [[#1716](https://github.com/falcosecurity/falco/pull/1716)] - [@Spartan-65](https://github.com/Spartan-65) ### Rule Changes * rule(list miner_domains): add new miner domains [[#1729](https://github.com/falcosecurity/falco/pull/1729)] - [@AlbertoPellitteri](https://github.com/AlbertoPellitteri) * rule(list https_miner_domains): add new miner domains [[#1729](https://github.com/falcosecurity/falco/pull/1729)] - [@AlbertoPellitteri](https://github.com/AlbertoPellitteri) ### Non user-facing changes * add Qonto as adopter [[#1717](https://github.com/falcosecurity/falco/pull/1717)] - [@Issif](https://github.com/Issif) * docs(proposals): proposal for a libs plugin system [[#1637](https://github.com/falcosecurity/falco/pull/1637)] - [@ldegio](https://github.com/ldegio) * build: remove unused `ncurses` dependency [[#1658](https://github.com/falcosecurity/falco/pull/1658)] - [@leogr](https://github.com/leogr) * build(.circleci): use new Debian 11 package names for python-pip [[#1712](https://github.com/falcosecurity/falco/pull/1712)] - [@zuc](https://github.com/zuc) * build(docker): adding libssl-dev, upstream image reference pinned to `debian:buster` [[#1719](https://github.com/falcosecurity/falco/pull/1719)] - [@michalschott](https://github.com/michalschott) * fix(test): avoid output_strictly_contains failures [[#1724](https://github.com/falcosecurity/falco/pull/1724)] - [@jasondellaluce](https://github.com/jasondellaluce) * Remove duplicate allowed ecr registry rule [[#1725](https://github.com/falcosecurity/falco/pull/1725)] - [@TomKeyte](https://github.com/TomKeyte) * docs(RELEASE.md): switch to 3 releases per year [[#1711](https://github.com/falcosecurity/falco/pull/1711)] - [@leogr](https://github.com/leogr) ## v0.29.1 Released on 2021-06-29 ### Minor Changes * update: bump the Falco engine version to version 9 [[#1675](https://github.com/falcosecurity/falco/pull/1675)] - [@leodido](https://github.com/leodido) ### Rule Changes * rule(list user_known_userfaultfd_processes): list to exclude processes known to use userfaultfd syscall [[#1675](https://github.com/falcosecurity/falco/pull/1675)] - [@leodido](https://github.com/leodido) * rule(macro consider_userfaultfd_activities): macro to gate the "Unprivileged Delegation of Page Faults Handling to a Userspace Process" rule [[#1675](https://github.com/falcosecurity/falco/pull/1675)] - [@leodido](https://github.com/leodido) * rule(Unprivileged Delegation of Page Faults Handling to a Userspace Process): new rule to detect successful unprivileged userfaultfd syscalls [[#1675](https://github.com/falcosecurity/falco/pull/1675)] - [@leodido](https://github.com/leodido) * rule(Linux Kernel Module Injection Detected): adding container info to the output of the rule [[#1675](https://github.com/falcosecurity/falco/pull/1675)] - [@leodido](https://github.com/leodido) ### Non user-facing changes * docs(release.md): update steps [[#1684](https://github.com/falcosecurity/falco/pull/1684)] - [@maxgio92](https://github.com/maxgio92) ## v0.29.0 Released on 2021-06-21 ### Minor Changes * update: driver version is 17f5df52a7d9ed6bb12d3b1768460def8439936d now [[#1669](https://github.com/falcosecurity/falco/pull/1669)] - [@leogr](https://github.com/leogr) ### Rule Changes * rule(list miner_domains): add rx.unmineable.com for anti-miner detection [[#1676](https://github.com/falcosecurity/falco/pull/1676)] - [@fntlnz](https://github.com/fntlnz) * rule(Change thread namespace and Set Setuid or Setgid bit): disable by default [[#1632](https://github.com/falcosecurity/falco/pull/1632)] - [@Kaizhe](https://github.com/Kaizhe) * rule(list known_sa_list): add namespace-controller, statefulset-controller, disruption-controller, job-controller, horizontal-pod-autoscaler and persistent-volume-binder as allowed service accounts in the kube-system namespace [[#1659](https://github.com/falcosecurity/falco/pull/1659)] - [@sboschman](https://github.com/sboschman) * rule(Non sudo setuid): check user id as well in case user name info is not available [[#1665](https://github.com/falcosecurity/falco/pull/1665)] - [@Kaizhe](https://github.com/Kaizhe) * rule(Debugfs Launched in Privileged Container): fix typo in description [[#1657](https://github.com/falcosecurity/falco/pull/1657)] - [@Kaizhe](https://github.com/Kaizhe) ### Non user-facing changes * Fix link to CONTRIBUTING.md in the Pull Request Template [[#1679](https://github.com/falcosecurity/falco/pull/1679)] - [@tspearconquest](https://github.com/tspearconquest) * fetch libs and drivers from the new repo [[#1552](https://github.com/falcosecurity/falco/pull/1552)] - [@leogr](https://github.com/leogr) * build(test): upgrade urllib3 to 1.26.5 [[#1666](https://github.com/falcosecurity/falco/pull/1666)] - [@leogr](https://github.com/leogr) * revert: add notes for 0.28.2 release [[#1663](https://github.com/falcosecurity/falco/pull/1663)] - [@maxgio92](https://github.com/maxgio92) * changelog: add notes for 0.28.2 release [[#1661](https://github.com/falcosecurity/falco/pull/1661)] - [@maxgio92](https://github.com/maxgio92) * docs(release.md): add blog announcement to post-release tasks [[#1652](https://github.com/falcosecurity/falco/pull/1652)] - [@maxgio92](https://github.com/maxgio92) * add Yahoo!Japan as an adopter [[#1651](https://github.com/falcosecurity/falco/pull/1651)] - [@ukitazume](https://github.com/ukitazume) * Add Replicated to adopters [[#1649](https://github.com/falcosecurity/falco/pull/1649)] - [@diamonwiggins](https://github.com/diamonwiggins) * docs(proposals): fix libs contribution name [[#1641](https://github.com/falcosecurity/falco/pull/1641)] - [@leodido](https://github.com/leodido) ## v0.28.1 Released on 2021-05-07 ### Major Changes * new: `--support` output now includes info about the Falco engine version [[#1581](https://github.com/falcosecurity/falco/pull/1581)] - [@mstemm](https://github.com/mstemm) * new: Falco outputs an alert in the unlikely situation it's receiving too many consecutive timeouts without an event [[#1622](https://github.com/falcosecurity/falco/pull/1622)] - [@leodido](https://github.com/leodido) * new: configuration field `syscall_event_timeouts.max_consecutive` to configure after how many consecutive timeouts without an event Falco must alert [[#1622](https://github.com/falcosecurity/falco/pull/1622)] - [@leodido](https://github.com/leodido) ### Minor Changes * build: enforcing hardening flags by default [[#1604](https://github.com/falcosecurity/falco/pull/1604)] - [@leogr](https://github.com/leogr) ### Bug Fixes * fix: do not stop the webserver for k8s audit logs when invalid data is coming in the event to be processed [[#1617](https://github.com/falcosecurity/falco/pull/1617)] - [@fntlnz](https://github.com/fntlnz) ### Rule Changes * rule(macro: allowed_aws_ecr_registry_root_for_eks): new macro for AWS EKS images hosted on ECR to use in rule: Launch Privileged Container [[#1640](https://github.com/falcosecurity/falco/pull/1640)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(macro: aws_eks_core_images): new macro for AWS EKS images hosted on ECR to use in rule: Launch Privileged Container [[#1640](https://github.com/falcosecurity/falco/pull/1640)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(macro: aws_eks_image_sensitive_mount): new macro for AWS EKS images hosted on ECR to use in rule: Launch Privileged Container [[#1640](https://github.com/falcosecurity/falco/pull/1640)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(list `falco_privileged_images`): remove deprecated Falco's OCI image repositories [[#1634](https://github.com/falcosecurity/falco/pull/1634)] - [@maxgio92](https://github.com/maxgio92) * rule(list `falco_sensitive_mount_images`): remove deprecated Falco's OCI image repositories [[#1634](https://github.com/falcosecurity/falco/pull/1634)] - [@maxgio92](https://github.com/maxgio92) * rule(macro `k8s_containers`): remove deprecated Falco's OCI image repositories [[#1634](https://github.com/falcosecurity/falco/pull/1634)] - [@maxgio92](https://github.com/maxgio92) * rule(macro: python_running_sdchecks): macro removed [[#1620](https://github.com/falcosecurity/falco/pull/1620)] - [@leogr](https://github.com/leogr) * rule(Change thread namespace): remove python_running_sdchecks exception [[#1620](https://github.com/falcosecurity/falco/pull/1620)] - [@leogr](https://github.com/leogr) ### Non user-facing changes * urelease/docs: fix link and small refactor in the text [[#1636](https://github.com/falcosecurity/falco/pull/1636)] - [@cpanato](https://github.com/cpanato) * Add Secureworks to adopters [[#1629](https://github.com/falcosecurity/falco/pull/1629)] - [@dwindsor-scwx](https://github.com/dwindsor-scwx) * regression test for malformed k8s audit input (FAL-01-003) [[#1624](https://github.com/falcosecurity/falco/pull/1624)] - [@leodido](https://github.com/leodido) * Add mathworks to adopterlist [[#1621](https://github.com/falcosecurity/falco/pull/1621)] - [@natchaphon-r](https://github.com/natchaphon-r) * adding known users [[#1623](https://github.com/falcosecurity/falco/pull/1623)] - [@danpopSD](https://github.com/danpopSD) * docs: update link for HackMD community call notes [[#1614](https://github.com/falcosecurity/falco/pull/1614)] - [@leodido](https://github.com/leodido) ## v0.28.0 Released on 2021-04-12 ### Major Changes * BREAKING CHANGE: Bintray is deprecated, no new packages will be published at https://dl.bintray.com/falcosecurity/ [[#1577](https://github.com/falcosecurity/falco/pull/1577)] - [@leogr](https://github.com/leogr) * BREAKING CHANGE: SKIP_MODULE_LOAD env variable no more disables the driver loading (use SKIP_DRIVER_LOADER env variable introduced in Falco 0.24) [[#1599](https://github.com/falcosecurity/falco/pull/1599)] - [@leodido](https://github.com/leodido) * BREAKING CHANGE: the init.d service unit is not shipped anymore in deb/rpm packages in favor of a systemd service file [[#1448](https://github.com/falcosecurity/falco/pull/1448)] - [@jenting](https://github.com/jenting) * new: add support for exceptions as rule attributes to provide a compact way to add exceptions to Falco rules [[#1427](https://github.com/falcosecurity/falco/pull/1427)] - [@mstemm](https://github.com/mstemm) * new: falco-no-driver container images on AWS ECR gallery (https://gallery.ecr.aws/falcosecurity/falco-no-driver) [[#1519](https://github.com/falcosecurity/falco/pull/1519)] - [@jonahjon](https://github.com/jonahjon) * new: falco-driver-loader container images on AWS ECR gallery (https://gallery.ecr.aws/falcosecurity/falco-driver-loader) [[#1519](https://github.com/falcosecurity/falco/pull/1519)] - [@jonahjon](https://github.com/jonahjon) * new: add healthz endpoint to the webserver [[#1546](https://github.com/falcosecurity/falco/pull/1546)] - [@cpanato](https://github.com/cpanato) * new: introduce a new configuration field `syscall_event_drops.threshold` to tune the drop noisiness [[#1586](https://github.com/falcosecurity/falco/pull/1586)] - [@leodido](https://github.com/leodido) * new: falco-driver-loader script can get a custom driver name from DRIVER_NAME env variable [[#1488](https://github.com/falcosecurity/falco/pull/1488)] - [@leodido](https://github.com/leodido) * new: falco-driver-loader know the Falco version [[#1488](https://github.com/falcosecurity/falco/pull/1488)] - [@leodido](https://github.com/leodido) ### Minor Changes * docs(proposals): libraries and drivers donation [[#1530](https://github.com/falcosecurity/falco/pull/1530)] - [@leodido](https://github.com/leodido) * docs(docker): update links to the new Falco website URLs [[#1545](https://github.com/falcosecurity/falco/pull/1545)] - [@cpanato](https://github.com/cpanato) * docs(test): update links to new Falco website URLs [[#1563](https://github.com/falcosecurity/falco/pull/1563)] - [@shane-lawrence](https://github.com/shane-lawrence) * build: now Falco packages are published at https://download.falco.org [[#1577](https://github.com/falcosecurity/falco/pull/1577)] - [@leogr](https://github.com/leogr) * update: lower the `syscall_event_drops.max_burst` default value to 1 [[#1586](https://github.com/falcosecurity/falco/pull/1586)] - [@leodido](https://github.com/leodido) * update: falco-driver-loader tries to download a Falco driver before then compiling it on the fly for the host [[#1599](https://github.com/falcosecurity/falco/pull/1599)] - [@leodido](https://github.com/leodido) * docs(test): document the prerequisites for running the integration test suite locally [[#1609](https://github.com/falcosecurity/falco/pull/1609)] - [@fntlnz](https://github.com/fntlnz) * update: Debian/RPM package migrated from init to systemd [[#1448](https://github.com/falcosecurity/falco/pull/1448)] - [@jenting](https://github.com/jenting) ### Bug Fixes * fix(userspace/engine): properly handle field extraction over lists of containers when not all containers match the specified sub-properties [[#1601](https://github.com/falcosecurity/falco/pull/1601)] - [@mstemm](https://github.com/mstemm) * fix(docker/falco): add flex and bison dependency to container image [[#1562](https://github.com/falcosecurity/falco/pull/1562)] - [@schans](https://github.com/schans) * fix: ignore action can not be used with log and alert ones (`syscall_event_drops` config) [[#1586](https://github.com/falcosecurity/falco/pull/1586)] - [@leodido](https://github.com/leodido) * fix(userspace/engine): allows fields starting with numbers to be parsed properly [[#1598](https://github.com/falcosecurity/falco/pull/1598)] - [@mstemm](https://github.com/mstemm) ### Rule Changes * rule(Write below monitored dir): improve rule description [[#1588](https://github.com/falcosecurity/falco/pull/1588)] - [@stevenshuang](https://github.com/stevenshuang) * rule(macro allowed_aws_eks_registry_root): macro to match the official eks registry [[#1555](https://github.com/falcosecurity/falco/pull/1555)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(macro aws_eks_image): match aws image repository for eks [[#1555](https://github.com/falcosecurity/falco/pull/1555)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(macro aws_eks_image_sensitive_mount): match aws cni images [[#1555](https://github.com/falcosecurity/falco/pull/1555)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(macro k8s_containers): include fluent/fluentd-kubernetes-daemonset and prom/prometheus [[#1555](https://github.com/falcosecurity/falco/pull/1555)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(Launch Privileged Container): exclude aws_eks_image [[#1555](https://github.com/falcosecurity/falco/pull/1555)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(Launch Sensitive Mount Container): exclude aws_eks_image_sensitive_mount [[#1555](https://github.com/falcosecurity/falco/pull/1555)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(Debugfs Launched in Privileged Container): new rule [[#1583](https://github.com/falcosecurity/falco/pull/1583)] - [@Kaizhe](https://github.com/Kaizhe) * rule(Mount Launched in Privileged Container): new rule [[#1583](https://github.com/falcosecurity/falco/pull/1583)] - [@Kaizhe](https://github.com/Kaizhe) * rule(Set Setuid or Setgid bit): add k3s-agent in the whitelist [[#1583](https://github.com/falcosecurity/falco/pull/1583)] - [@Kaizhe](https://github.com/Kaizhe) * rule(macro user_ssh_directory): using glob operator [[#1560](https://github.com/falcosecurity/falco/pull/1560)] - [@shane-lawrence](https://github.com/shane-lawrence) * rule(list falco_sensitive_mount_containers): added image exceptions for IBM cloud [[#1337](https://github.com/falcosecurity/falco/pull/1337)] - [@nibalizer](https://github.com/nibalizer) * rule(list rpm_binaries): add rhsmcertd [[#1385](https://github.com/falcosecurity/falco/pull/1385)] - [@epcim](https://github.com/epcim) * rule(list deb_binaries): add apt.systemd.daily [[#1385](https://github.com/falcosecurity/falco/pull/1385)] - [@epcim](https://github.com/epcim) * rule(Sudo Potential Privilege Escalation): new rule created to detect CVE-2021-3156 [[#1543](https://github.com/falcosecurity/falco/pull/1543)] - [@darryk10](https://github.com/darryk10) * rule(list allowed_k8s_users): add `eks:node-manager` [[#1536](https://github.com/falcosecurity/falco/pull/1536)] - [@ismailyenigul](https://github.com/ismailyenigul) * rule(list mysql_mgmt_binaries): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(list db_mgmt_binaries): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_ansible_running_python): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_bro_running_python): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_python_running_denyhosts): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_linux_image_upgrade_script): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_java_running_echo): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_scripting_running_builds): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_Xvfb_running_xkbcomp): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_nginx_running_serf): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_node_running_npm): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_java_running_sbt): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(list known_container_shell_spawn_cmdlines): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(list known_shell_spawn_binaries): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro run_by_puppet): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro user_privileged_containers): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(list rancher_images): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(list images_allow_network_outside_subnet): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro parent_python_running_sdchecks): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(macro trusted_containers): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) * rule(list authorized_server_binaries): removed [[#1602](https://github.com/falcosecurity/falco/pull/1602)] - [@fntlnz](https://github.com/fntlnz) ### Non user-facing changes * chore(test): replace bucket url with official distribution url [[#1608](https://github.com/falcosecurity/falco/pull/1608)] - [@fntlnz](https://github.com/fntlnz) * adding asapp as an adopter [[#1611](https://github.com/falcosecurity/falco/pull/1611)] - [@Stuxend](https://github.com/Stuxend) * update: fixtures URLs [[#1603](https://github.com/falcosecurity/falco/pull/1603)] - [@leogr](https://github.com/leogr) * cleanup publishing jobs [[#1596](https://github.com/falcosecurity/falco/pull/1596)] - [@leogr](https://github.com/leogr) * fix(falco/test): bump pyyaml from 5.3.1 to 5.4 [[#1595](https://github.com/falcosecurity/falco/pull/1595)] - [@leodido](https://github.com/leodido) * fix(.circleci): tar must be present in the image [[#1594](https://github.com/falcosecurity/falco/pull/1594)] - [@leogr](https://github.com/leogr) * fix: publishing jobs [[#1591](https://github.com/falcosecurity/falco/pull/1591)] - [@leogr](https://github.com/leogr) * Pocteo as an adopter [[#1574](https://github.com/falcosecurity/falco/pull/1574)] - [@pocteo-labs](https://github.com/pocteo-labs) * build: fetch build deps from download.falco.org [[#1572](https://github.com/falcosecurity/falco/pull/1572)] - [@leogr](https://github.com/leogr) * adding shapesecurity to adopters [[#1566](https://github.com/falcosecurity/falco/pull/1566)] - [@irivera007](https://github.com/irivera007) * Use default pip version to get avocado version [[#1565](https://github.com/falcosecurity/falco/pull/1565)] - [@shane-lawrence](https://github.com/shane-lawrence) * Added Swissblock to list of adopters [[#1551](https://github.com/falcosecurity/falco/pull/1551)] - [@bygui86](https://github.com/bygui86) * Fix various typos in markdown files. [[#1514](https://github.com/falcosecurity/falco/pull/1514)] - [@didier-durand](https://github.com/didier-durand) * docs: move governance to falcosecurity/.github [[#1524](https://github.com/falcosecurity/falco/pull/1524)] - [@leogr](https://github.com/leogr) * ci: fix missing infra context to publish stable Falco packages [[#1615](https://github.com/falcosecurity/falco/pull/1615)] - [@leodido](https://github.com/leodido) ## v0.27.0 Released on 2021-01-18 ### Major Changes * new: Added falco engine version to grpc version service [[#1507](https://github.com/falcosecurity/falco/pull/1507)] - [@nibalizer](https://github.com/nibalizer) * BREAKING CHANGE: Users who run Falco without a config file will be unable to do that any more, Falco now expects a configuration file to be passed all the times. Developers may need to adjust their processes. [[#1494](https://github.com/falcosecurity/falco/pull/1494)] - [@nibalizer](https://github.com/nibalizer) * new: asynchronous outputs implementation, outputs channels will not block event processing anymore [[#1451](https://github.com/falcosecurity/falco/pull/1451)] - [@leogr](https://github.com/leogr) * new: slow outputs detection [[#1451](https://github.com/falcosecurity/falco/pull/1451)] - [@leogr](https://github.com/leogr) * new: `output_timeout` config option for slow outputs detection [[#1451](https://github.com/falcosecurity/falco/pull/1451)] - [@leogr](https://github.com/leogr) ### Minor Changes * build: bump b64 to v2.0.0.1 [[#1441](https://github.com/falcosecurity/falco/pull/1441)] - [@fntlnz](https://github.com/fntlnz) * rules(macro container_started): reuse `spawned_process` macro inside `container_started` macro [[#1449](https://github.com/falcosecurity/falco/pull/1449)] - [@leodido](https://github.com/leodido) * docs: reach out documentation [[#1472](https://github.com/falcosecurity/falco/pull/1472)] - [@fntlnz](https://github.com/fntlnz) * docs: Broken outputs.proto link [[#1493](https://github.com/falcosecurity/falco/pull/1493)] - [@deepskyblue86](https://github.com/deepskyblue86) * docs(README.md): correct broken links [[#1506](https://github.com/falcosecurity/falco/pull/1506)] - [@leogr](https://github.com/leogr) * docs(proposals): Exceptions handling proposal [[#1376](https://github.com/falcosecurity/falco/pull/1376)] - [@mstemm](https://github.com/mstemm) * docs: fix a broken link of README [[#1516](https://github.com/falcosecurity/falco/pull/1516)] - [@oke-py](https://github.com/oke-py) * docs: adding the kubernetes privileged use case to use cases [[#1484](https://github.com/falcosecurity/falco/pull/1484)] - [@fntlnz](https://github.com/fntlnz) * rules(Mkdir binary dirs): Adds exe_running_docker_save as an exception as this rules can be triggered when a container is created. [[#1386](https://github.com/falcosecurity/falco/pull/1386)] - [@jhwbarlow](https://github.com/jhwbarlow) * rules(Create Hidden Files): Adds exe_running_docker_save as an exception as this rules can be triggered when a container is created. [[#1386](https://github.com/falcosecurity/falco/pull/1386)] - [@jhwbarlow](https://github.com/jhwbarlow) * docs(.circleci): welcome Jonah (Amazon) as a new Falco CI maintainer [[#1518](https://github.com/falcosecurity/falco/pull/1518)] - [@leodido](https://github.com/leodido) * build: falcosecurity/falco:master also available on the AWS ECR Public registry [[#1512](https://github.com/falcosecurity/falco/pull/1512)] - [@leodido](https://github.com/leodido) * build: falcosecurity/falco:latest also available on the AWS ECR Public registry [[#1512](https://github.com/falcosecurity/falco/pull/1512)] - [@leodido](https://github.com/leodido) * update: gRPC clients can now subscribe to drop alerts via gRCP API [[#1451](https://github.com/falcosecurity/falco/pull/1451)] - [@leogr](https://github.com/leogr) * macro(allowed_k8s_users): exclude cloud-controller-manage to avoid false positives on k3s [[#1444](https://github.com/falcosecurity/falco/pull/1444)] - [@fntlnz](https://github.com/fntlnz) ### Bug Fixes * fix(userspace/falco): use given priority in falco_outputs::handle_msg() [[#1450](https://github.com/falcosecurity/falco/pull/1450)] - [@leogr](https://github.com/leogr) * fix(userspace/engine): free formatters, if any [[#1447](https://github.com/falcosecurity/falco/pull/1447)] - [@leogr](https://github.com/leogr) * fix(scripts/falco-driver-loader): lsmod usage [[#1474](https://github.com/falcosecurity/falco/pull/1474)] - [@dnwe](https://github.com/dnwe) * fix: a bug that prevents Falco driver to be consumed by many Falco instances in some circumstances [[#1485](https://github.com/falcosecurity/falco/pull/1485)] - [@leodido](https://github.com/leodido) * fix: set `HOST_ROOT=/host` environment variable for the `falcosecurity/falco-no-driver` container image by default [[#1492](https://github.com/falcosecurity/falco/pull/1492)] - [@leogr](https://github.com/leogr) ### Rule Changes * rule(list user_known_change_thread_namespace_binaries): add crio and multus to the list [[#1501](https://github.com/falcosecurity/falco/pull/1501)] - [@Kaizhe](https://github.com/Kaizhe) * rule(Container Run as Root User): new rule created [[#1500](https://github.com/falcosecurity/falco/pull/1500)] - [@Kaizhe](https://github.com/Kaizhe) * rule(Linux Kernel Module injection detected): adds a new rule that detects when an LKM module is injected using `insmod` from a container (typically used by rootkits looking to obfuscate their behavior via kernel hooking). [[#1478](https://github.com/falcosecurity/falco/pull/1478)] - [@d1vious](https://github.com/d1vious) * rule(macro multipath_writing_conf): create and use the macro [[#1475](https://github.com/falcosecurity/falco/pull/1475)] - [@nmarier-coveo](https://github.com/nmarier-coveo) * rule(list falco_privileged_images): add calico/node without registry prefix to prevent false positive alerts [[#1457](https://github.com/falcosecurity/falco/pull/1457)] - [@czunker](https://github.com/czunker) * rule(Full K8s Administrative Access): use the right list of admin users (fix) [[#1454](https://github.com/falcosecurity/falco/pull/1454)] - [@mstemm](https://github.com/mstemm) ### Non user-facing changes * chore(cmake): remove unnecessary whitespace patch [[#1522](https://github.com/falcosecurity/falco/pull/1522)] - [@leogr](https://github.com/leogr) * remove stale bot in favor of the new lifecycle bot [[#1490](https://github.com/falcosecurity/falco/pull/1490)] - [@leodido](https://github.com/leodido) * chore(cmake): mark some variables as advanced [[#1496](https://github.com/falcosecurity/falco/pull/1496)] - [@deepskyblue86](https://github.com/deepskyblue86) * chore(cmake/modules): avoid useless rebuild [[#1495](https://github.com/falcosecurity/falco/pull/1495)] - [@deepskyblue86](https://github.com/deepskyblue86) * build: BUILD_BYPRODUCTS for civetweb [[#1489](https://github.com/falcosecurity/falco/pull/1489)] - [@fntlnz](https://github.com/fntlnz) * build: remove duplicate item from FALCO_SOURCES [[#1480](https://github.com/falcosecurity/falco/pull/1480)] - [@leodido](https://github.com/leodido) * build: make our integration tests report clear steps for CircleCI UI [[#1473](https://github.com/falcosecurity/falco/pull/1473)] - [@fntlnz](https://github.com/fntlnz) * further improvements outputs impl. [[#1443](https://github.com/falcosecurity/falco/pull/1443)] - [@leogr](https://github.com/leogr) * fix(test): make integration tests properly fail [[#1439](https://github.com/falcosecurity/falco/pull/1439)] - [@leogr](https://github.com/leogr) * Falco outputs refactoring [[#1412](https://github.com/falcosecurity/falco/pull/1412)] - [@leogr](https://github.com/leogr) ## v0.26.2 Released on 2020-11-10 ### Major Changes * update: DRIVERS_REPO now defaults to https://download.falco.org/driver [[#1460](https://github.com/falcosecurity/falco/pull/1460)] - [@leodido](https://github.com/leodido) ## v0.26.1 Released on 2020-10-01 ### Major Changes * new: CLI flag `--alternate-lua-dir` to load Lua files from arbitrary paths [[#1419](https://github.com/falcosecurity/falco/pull/1419)] - [@admiral0](https://github.com/admiral0) ### Rule Changes * rule(Delete or rename shell history): fix warnings/FPs + container teardown [[#1423](https://github.com/falcosecurity/falco/pull/1423)] - [@mstemm](https://github.com/mstemm) * rule(Write below root): ensure proc_name_exists too [[#1423](https://github.com/falcosecurity/falco/pull/1423)] - [@mstemm](https://github.com/mstemm) ## v0.26.0 Released on 2020-24-09 ### Major Changes * new: address several sources of FPs, primarily from GKE environments. [[#1372](https://github.com/falcosecurity/falco/pull/1372)] - [@mstemm](https://github.com/mstemm) * new: driver updated to 2aa88dcf6243982697811df4c1b484bcbe9488a2 [[#1410](https://github.com/falcosecurity/falco/pull/1410)] - [@leogr](https://github.com/leogr) * new(scripts/falco-driver-loader): detect and try to build the Falco kernel module driver using different GCC versions available in the current environment. [[#1408](https://github.com/falcosecurity/falco/pull/1408)] - [@fntlnz](https://github.com/fntlnz) * new: tgz (tarball) containing the statically-linked (musl) binary of Falco is now automatically built and published on bintray [[#1377](https://github.com/falcosecurity/falco/pull/1377)] - [@leogr](https://github.com/leogr) ### Minor Changes * update: bump Falco engine version to 7 [[#1381](https://github.com/falcosecurity/falco/pull/1381)] - [@leogr](https://github.com/leogr) * update: the required_engine_version is now on by default [[#1381](https://github.com/falcosecurity/falco/pull/1381)] - [@leogr](https://github.com/leogr) * update: falcosecurity/falco-no-driver image now uses the statically-linked Falco [[#1377](https://github.com/falcosecurity/falco/pull/1377)] - [@leogr](https://github.com/leogr) * docs(proposals): artifacts storage [[#1375](https://github.com/falcosecurity/falco/pull/1375)] - [@leodido](https://github.com/leodido) * docs(proposals): artifacts cleanup [[#1375](https://github.com/falcosecurity/falco/pull/1375)] - [@leodido](https://github.com/leodido) ### Rule Changes * rule(macro inbound_outbound): add brackets to disambiguate operator precedence [[#1373](https://github.com/falcosecurity/falco/pull/1373)] - [@ldegio](https://github.com/ldegio) * rule(macro redis_writing_conf): add brackets to disambiguate operator precedence [[#1373](https://github.com/falcosecurity/falco/pull/1373)] - [@ldegio](https://github.com/ldegio) * rule(macro run_by_foreman): add brackets to disambiguate operator precedence [[#1373](https://github.com/falcosecurity/falco/pull/1373)] - [@ldegio](https://github.com/ldegio) * rule(macro consider_packet_socket_communication): enable "Packet socket created in container" rule by default. [[#1402](https://github.com/falcosecurity/falco/pull/1402)] - [@rung](https://github.com/rung) * rule(Delete or rename shell history): skip docker overlay filesystems when considering bash history [[#1393](https://github.com/falcosecurity/falco/pull/1393)] - [@mstemm](https://github.com/mstemm) * rule(Disallowed K8s User): quote colons in user names [[#1393](https://github.com/falcosecurity/falco/pull/1393)] - [@mstemm](https://github.com/mstemm) * rule(macro falco_sensitive_mount_containers): Adds a trailing slash to avoid repo naming issues [[#1394](https://github.com/falcosecurity/falco/pull/1394)] - [@bgeesaman](https://github.com/bgeesaman) * rule: adds user.loginuid to the default Falco rules that also contain user.name [[#1369](https://github.com/falcosecurity/falco/pull/1369)] - [@csschwe](https://github.com/csschwe) ## v0.25.0 Released on 2020-08-25 ### Major Changes * new(userspace/falco): print the Falco and driver versions at the very beginning of the output. [[#1303](https://github.com/falcosecurity/falco/pull/1303)] - [@leogr](https://github.com/leogr) * new: libyaml is now bundled in the release process. Users can now avoid installing libyaml directly when getting Falco from the official release. [[#1252](https://github.com/falcosecurity/falco/pull/1252)] - [@fntlnz](https://github.com/fntlnz) ### Minor Changes * docs(test): step-by-step instructions to run integration tests locally [[#1313](https://github.com/falcosecurity/falco/pull/1313)] - [@leodido](https://github.com/leodido) * update: renameat2 syscall support [[#1355](https://github.com/falcosecurity/falco/pull/1355)] - [@fntlnz](https://github.com/fntlnz) * update: support for 5.8.x kernels [[#1355](https://github.com/falcosecurity/falco/pull/1355)] - [@fntlnz](https://github.com/fntlnz) ### Bug Fixes * fix(userspace/falco): correct the fallback mechanism for loading the kernel module [[#1366](https://github.com/falcosecurity/falco/pull/1366)] - [@leogr](https://github.com/leogr) * fix(falco-driver-loader): script crashing when using arguments [[#1330](https://github.com/falcosecurity/falco/pull/1330)] - [@antoinedeschenes](https://github.com/antoinedeschenes) ### Rule Changes * rule(macro user_trusted_containers): add `sysdig/node-image-analyzer` and `sysdig/agent-slim` [[#1321](https://github.com/falcosecurity/falco/pull/1321)] - [@Kaizhe](https://github.com/Kaizhe) * rule(macro falco_privileged_images): add `docker.io/falcosecurity/falco` [[#1326](https://github.com/falcosecurity/falco/pull/1326)] - [@nvanheuverzwijn](https://github.com/nvanheuverzwijn) * rule(EphemeralContainers Created): add new rule to detect ephemeral container created [[#1339](https://github.com/falcosecurity/falco/pull/1339)] - [@Kaizhe](https://github.com/Kaizhe) * rule(macro user_read_sensitive_file_containers): replace endswiths with exact image repo name [[#1349](https://github.com/falcosecurity/falco/pull/1349)] - [@Kaizhe](https://github.com/Kaizhe) * rule(macro user_trusted_containers): replace endswiths with exact image repo name [[#1349](https://github.com/falcosecurity/falco/pull/1349)] - [@Kaizhe](https://github.com/Kaizhe) * rule(macro user_privileged_containers): replace endswiths with exact image repo name [[#1349](https://github.com/falcosecurity/falco/pull/1349)] - [@Kaizhe](https://github.com/Kaizhe) * rule(macro trusted_images_query_miner_domain_dns): replace endswiths with exact image repo name [[#1349](https://github.com/falcosecurity/falco/pull/1349)] - [@Kaizhe](https://github.com/Kaizhe) * rule(macro falco_privileged_containers): append "/" to quay.io/sysdig [[#1349](https://github.com/falcosecurity/falco/pull/1349)] - [@Kaizhe](https://github.com/Kaizhe) * rule(list falco_privileged_images): add images docker.io/sysdig/agent-slim and docker.io/sysdig/node-image-analyzer [[#1349](https://github.com/falcosecurity/falco/pull/1349)] - [@Kaizhe](https://github.com/Kaizhe) * rule(list falco_sensitive_mount_images): add image docker.io/sysdig/agent-slim [[#1349](https://github.com/falcosecurity/falco/pull/1349)] - [@Kaizhe](https://github.com/Kaizhe) * rule(list k8s_containers): prepend docker.io to images [[#1349](https://github.com/falcosecurity/falco/pull/1349)] - [@Kaizhe](https://github.com/Kaizhe) * rule(macro exe_running_docker_save): add better support for centos [[#1350](https://github.com/falcosecurity/falco/pull/1350)] - [@admiral0](https://github.com/admiral0) * rule(macro rename): add `renameat2` syscall [[#1359](https://github.com/falcosecurity/falco/pull/1359)] - [@leogr](https://github.com/leogr) * rule(Read sensitive file untrusted): add trusted images into whitelist [[#1327](https://github.com/falcosecurity/falco/pull/1327)] - [@Kaizhe](https://github.com/Kaizhe) * rule(Pod Created in Kube Namespace): add new list k8s_image_list as white list [[#1336](https://github.com/falcosecurity/falco/pull/1336)] - [@Kaizhe](https://github.com/Kaizhe) * rule(list allowed_k8s_users): add "kubernetes-admin" user [[#1323](https://github.com/falcosecurity/falco/pull/1323)] - [@leogr](https://github.com/leogr) ## v0.24.0 Released on 2020-07-16 ### Major Changes * new: Falco now supports userspace instrumentation with the -u flag [[#1195](https://github.com/falcosecurity/falco/pull/1195)] * BREAKING CHANGE: --stats_interval is now --stats-interval [[#1308](https://github.com/falcosecurity/falco/pull/1308)] * new: auto threadiness for gRPC server [[#1271](https://github.com/falcosecurity/falco/pull/1271)] * BREAKING CHANGE: server streaming gRPC outputs method is now `falco.outputs.service/get` [[#1241](https://github.com/falcosecurity/falco/pull/1241)] * new: new bi-directional async streaming gRPC outputs (`falco.outputs.service/sub`) [[#1241](https://github.com/falcosecurity/falco/pull/1241)] * new: unix socket for the gRPC server [[#1217](https://github.com/falcosecurity/falco/pull/1217)] ### Minor Changes * update: driver version is 85c88952b018fdbce2464222c3303229f5bfcfad now [[#1305](https://github.com/falcosecurity/falco/pull/1305)] * update: `SKIP_MODULE_LOAD` renamed to `SKIP_DRIVER_LOADER` [[#1297](https://github.com/falcosecurity/falco/pull/1297)] * docs: add leogr to OWNERS [[#1300](https://github.com/falcosecurity/falco/pull/1300)] * update: default threadiness to 0 ("auto" behavior) [[#1271](https://github.com/falcosecurity/falco/pull/1271)] * update: k8s audit endpoint now defaults to /k8s-audit everywhere [[#1292](https://github.com/falcosecurity/falco/pull/1292)] * update(falco.yaml): `webserver.k8s_audit_endpoint` default value changed from `/k8s_audit` to `/k8s-audit` [[#1261](https://github.com/falcosecurity/falco/pull/1261)] * docs(test): instructions to run regression test suites locally [[#1234](https://github.com/falcosecurity/falco/pull/1234)] ### Bug Fixes * fix: --stats-interval correctly accepts values >= 999 (ms) [[#1308](https://github.com/falcosecurity/falco/pull/1308)] * fix: make the eBPF driver build work on CentOS 8 [[#1301](https://github.com/falcosecurity/falco/pull/1301)] * fix(userspace/falco): correct options handling for `buffered_output: false` which was not honored for the `stdout` output [[#1296](https://github.com/falcosecurity/falco/pull/1296)] * fix(userspace/falco): honor -M also when using a trace file [[#1245](https://github.com/falcosecurity/falco/pull/1245)] * fix: high CPU usage when using server streaming gRPC outputs [[#1241](https://github.com/falcosecurity/falco/pull/1241)] * fix: missing newline from some log messages (eg., token bucket depleted) [[#1257](https://github.com/falcosecurity/falco/pull/1257)] ### Rule Changes * rule(Container Drift Detected (chmod)): disabled by default [[#1316](https://github.com/falcosecurity/falco/pull/1316)] * rule(Container Drift Detected (open+create)): disabled by default [[#1316](https://github.com/falcosecurity/falco/pull/1316)] * rule(Write below etc): allow snapd to write its unit files [[#1289](https://github.com/falcosecurity/falco/pull/1289)] * rule(macro remote_file_copy_procs): fix reference to remote_file_copy_binaries [[#1224](https://github.com/falcosecurity/falco/pull/1224)] * rule(list allowed_k8s_users): whitelisted kube-apiserver-healthcheck user created by kops >= 1.17.0 for the kube-apiserver-healthcheck sidecar [[#1286](https://github.com/falcosecurity/falco/pull/1286)] * rule(Change thread namespace): Allow `protokube`, `dockerd`, `tini` and `aws` binaries to change thread namespace. [[#1222](https://github.com/falcosecurity/falco/pull/1222)] * rule(macro exe_running_docker_save): to filter out cmdlines containing `/var/run/docker`. [[#1222](https://github.com/falcosecurity/falco/pull/1222)] * rule(macro user_known_cron_jobs): new macro to be overridden to list known cron jobs [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Schedule Cron Jobs): exclude known cron jobs [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_update_package_registry): new macro to be overridden to list known package registry update [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Update Package Registry): exclude known package registry update [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_read_ssh_information_activities): new macro to be overridden to list known activities that read SSH info [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Read ssh information): do not throw for activities known to read SSH info [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_read_sensitive_files_activities): new macro to be overridden to list activities known to read sensitive files [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Read sensitive file trusted after startup): do not throw for activities known to read sensitive files [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Read sensitive file untrusted): do not throw for activities known to read sensitive files [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_write_rpm_database_activities): new macro to be overridden to list activities known to write RPM database [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Write below rpm database): do not throw for activities known to write RPM database [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_db_spawned_processes): new macro to be overridden to list processes known to spawn DB [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(DB program spawned process): do not throw for processes known to spawn DB [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_modify_bin_dir_activities): new macro to be overridden to list activities known to modify bin directories [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Modify binary dirs): do not throw for activities known to modify bin directories [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_mkdir_bin_dir_activities): new macro to be overridden to list activities known to create directories below bin directories [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Mkdir binary dirs): do not throw for activities known to create directories below bin directories [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_system_user_login): new macro to exclude known system user logins [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(System user interactive): do not throw for known system user logins [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_user_management_activities): new macro to be overridden to list activities known to do user managements activities [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(User mgmt binaries): do not throw for activities known to do user managements activities [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_create_files_below_dev_activities): new macro to be overridden to list activities known to create files below dev [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Create files below dev): do not throw for activities known to create files below dev [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_contact_k8s_api_server_activities): new macro to be overridden to list activities known to contact Kubernetes API server [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Contact K8S API Server From Container): do not throw for activities known to contact Kubernetes API server [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_network_tool_activities): new macro to be overridden to list activities known to spawn/use network tools [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Launch Suspicious Network Tool in Container): do not throw for activities known to spawn/use network tools [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_remove_data_activities): new macro to be overridden to list activities known to perform data remove commands [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Remove Bulk Data from Disk): do not throw for activities known to perform data remove commands [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_create_hidden_file_activities): new macro to be overridden to list activities known to create hidden files [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Create Hidden Files or Directories): do not throw for activities known to create hidden files [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_stand_streams_redirect_activities): new macro to be overridden to list activities known to redirect stream to network connection (in containers) [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Redirect STDOUT/STDIN to Network Connection in Container): do not throw for activities known to redirect stream to network connection (in containers) [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_container_drift_activities): new macro to be overridden to list activities known to create executables in containers [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Container Drift Detected (chmod)): do not throw for activities known to give execution permissions to files in containers [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Container Drift Detected (open+create)): do not throw for activities known to create executables in containers [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_node_port_service): do not throw for services known to start with a NopePort service type (k8s) [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Create NodePort Service): do not throw for services known to start with a NopePort service type (k8s) [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro user_known_exec_pod_activities): do not throw for activities known to attach/exec to a pod (k8s) [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Attach/Exec Pod): do not throw for activities known to attach/exec to a pod (k8s) [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro trusted_pod): defines trusted pods by an image list [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Pod Created in Kube Namespace): do not throw for trusted pods [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(macro trusted_sa): define trusted ServiceAccount [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(Service Account Created in Kube Namespace): do not throw for trusted ServiceAccount [[#1294](https://github.com/falcosecurity/falco/pull/1294)] * rule(list network_tool_binaries): add zmap to the list [[#1284](https://github.com/falcosecurity/falco/pull/1284)] * rule(macro root_dir): correct macro to exactly match the `/root` dir and not other with just `/root` as a prefix [[#1279](https://github.com/falcosecurity/falco/pull/1279)] * rule(macro user_expected_terminal_shell_in_container_conditions): allow whitelisting terminals in containers under specific conditions [[#1154](https://github.com/falcosecurity/falco/pull/1154)] * rule(macro user_known_write_below_binary_dir_activities): allow writing to a binary dir in some conditions [[#1260](https://github.com/falcosecurity/falco/pull/1260)] * rule(macro trusted_logging_images): Add addl fluentd image [[#1230](https://github.com/falcosecurity/falco/pull/1230)] * rule(macro trusted_logging_images): Let azure-npm image write to /var/log [[#1230](https://github.com/falcosecurity/falco/pull/1230)] * rule(macro lvprogs_writing_conf): Add lvs as a lvm program [[#1230](https://github.com/falcosecurity/falco/pull/1230)] * rule(macro user_known_k8s_client_container): Allow hcp-tunnelfront to run kubectl in containers [[#1230](https://github.com/falcosecurity/falco/pull/1230)] * rule(list allowed_k8s_users): Add vertical pod autoscaler as known k8s users [[#1230](https://github.com/falcosecurity/falco/pull/1230)] * rule(Anonymous Request Allowed): update to checking auth decision equals to allow [[#1267](https://github.com/falcosecurity/falco/pull/1267)] * rule(Container Drift Detected (chmod)): new rule to detect if an existing file get exec permissions in a container [[#1254](https://github.com/falcosecurity/falco/pull/1254)] * rule(Container Drift Detected (open+create)): new rule to detect if a new file with execution permission is created in a container [[#1254](https://github.com/falcosecurity/falco/pull/1254)] * rule(Mkdir binary dirs): correct condition in macro `bin_dir_mkdir` to catch `mkdirat` syscall [[#1250](https://github.com/falcosecurity/falco/pull/1250)] * rule(Modify binary dirs): correct condition in macro `bin_dir_rename` to catch `rename`, `renameat`, and `unlinkat` syscalls [[#1250](https://github.com/falcosecurity/falco/pull/1250)] * rule(Create files below dev): correct condition to catch `openat` syscall [[#1250](https://github.com/falcosecurity/falco/pull/1250)] * rule(macro user_known_set_setuid_or_setgid_bit_conditions): create macro [[#1213](https://github.com/falcosecurity/falco/pull/1213)] ## v0.23.0 Released on 2020-05-18 ### Major Changes * BREAKING CHANGE: the falco-driver-loader script now references `falco-probe.o` and `falco-probe.ko` as `falco.o` and `falco.ko` [[#1158](https://github.com/falcosecurity/falco/pull/1158)] * BREAKING CHANGE: the `falco-driver-loader` script environment variable to use a custom repository to download drivers now uses the `DRIVERS_REPO` environment variable instead of `DRIVER_LOOKUP_URL`. This variable must contain the parent URI containing the following directory structure `/$driver_version$/falco_$target$_$kernelrelease$_$kernelversion$.[ko|o]`. e.g: [[#1160](https://github.com/falcosecurity/falco/pull/1160)] * new(scripts): options and command-line usage for `falco-driver-loader` [[#1200](https://github.com/falcosecurity/falco/pull/1200)] * new: ability to specify exact matches when adding rules to Falco engine (only API) [[#1185](https://github.com/falcosecurity/falco/pull/1185)] * new(docker): add an image that wraps the `falco-driver-loader` with the toolchain [[#1192](https://github.com/falcosecurity/falco/pull/1192)] * new(docker): add `falcosecurity/falco-no-driver` image [[#1205](https://github.com/falcosecurity/falco/pull/1205)] ### Minor Changes * update(scripts): improve `falco-driver-loader` output messages [[#1200](https://github.com/falcosecurity/falco/pull/1200)] * update: containers look for prebuilt drivers on the Drivers Build Grid [[#1158](https://github.com/falcosecurity/falco/pull/1158)] * update: driver version bump to 96bd9bc560f67742738eb7255aeb4d03046b8045 [[#1190](https://github.com/falcosecurity/falco/pull/1190)] * update(docker): now `falcosecurity/falco:slim-*` alias to `falcosecurity/falco-no-driver:*` [[#1205](https://github.com/falcosecurity/falco/pull/1205)] * docs: instructions to run unit tests [[#1199](https://github.com/falcosecurity/falco/pull/1199)] * docs(examples): move `/examples` to `contrib` repo [[#1191](https://github.com/falcosecurity/falco/pull/1191)] * update(docker): remove `minimal` image [[#1196](https://github.com/falcosecurity/falco/pull/1196)] * update(integration): move `/integrations` to `contrib` repo [[#1157](https://github.com/falcosecurity/falco/pull/1157)] * https://dl.bintray.com/driver/$driver_version$/falco_$target$_$kernelrelease$_$kernelversion$.[ko|o]` [[#1160](https://github.com/falcosecurity/falco/pull/1160)] * update(docker/event-generator): remove the event-generator from Falco repository [[#1156](https://github.com/falcosecurity/falco/pull/1156)] * docs(examples): set audit level to metadata for object secrets [[#1153](https://github.com/falcosecurity/falco/pull/1153)] ### Bug Fixes * fix(scripts): upstream files (prebuilt drivers) for the generic Ubuntu kernel contains "ubuntu-generic" [[#1212](https://github.com/falcosecurity/falco/pull/1212)] * fix: support Falco driver on Linux kernels 5.6.y [[#1174](https://github.com/falcosecurity/falco/pull/1174)] ### Rule Changes * rule(Redirect STDOUT/STDIN to Network Connection in Container): correct rule name as per rules naming convention [[#1164](https://github.com/falcosecurity/falco/pull/1164)] * rule(Redirect STDOUT/STDIN to Network Connection in Container): new rule to detect Redirect stdout/stdin to network connection in container [[#1152](https://github.com/falcosecurity/falco/pull/1152)] * rule(K8s Secret Created): new rule to track the creation of Kubernetes secrets (excluding kube-system and service account secrets) [[#1151](https://github.com/falcosecurity/falco/pull/1151)] * rule(K8s Secret Deleted): new rule to track the deletion of Kubernetes secrets (excluding kube-system and service account secrets) [[#1151](https://github.com/falcosecurity/falco/pull/1151)] ## v0.22.1 Released on 2020-04-17 ### Major Changes * Same as v0.22.0 ### Minor Changes * Same as v0.22.0 ### Bug Fixes * fix: correct driver path (/usr/src/falco-%driver_version%) for RPM package [[#1148](https://github.com/falcosecurity/falco/pull/1148)] ### Rule Changes * Same as v0.22.0 ## v0.22.0 Released on 2020-04-16 ### Major Changes * new: falco version and driver version are distinct and not coupled anymore [[#1111](https://github.com/falcosecurity/falco/pull/1111)] * new: flag to disable asynchronous container metadata (CRI) fetch `--disable-cri-async` [[#1099](https://github.com/falcosecurity/falco/pull/1099)] ### Minor Changes * docs(integrations): update API resource versions to Kubernetes 1.16 [[#1044](https://github.com/falcosecurity/falco/pull/1044)] * docs: add new release archive to the `README.md` [[#1098](https://github.com/falcosecurity/falco/pull/1098)] * update: driver version a259b4bf49c3 [[#1138](https://github.com/falcosecurity/falco/pull/1138)] * docs(integrations/k8s-using-daemonset): --cri flag correct socket path [[#1140](https://github.com/falcosecurity/falco/pull/1140)] * update: bump driver version to cd3d10123e [[#1131](https://github.com/falcosecurity/falco/pull/1131)] * update(docker): remove RHEL, kernel/linuxkit, and kernel/probeloader images [[#1124](https://github.com/falcosecurity/falco/pull/1124)] * update: falco-probe-loader script is falco-driver-loader now [[#1111](https://github.com/falcosecurity/falco/pull/1111)] * update: using only sha256 hashes when pulling build dependencies [[#1118](https://github.com/falcosecurity/falco/pull/1118)] ### Bug Fixes * fix(integrations/k8s-using-daemonset): added missing privileges for the apps Kubernetes API group in the falco-cluster-role when using RBAC [[#1136](https://github.com/falcosecurity/falco/pull/1136)] * fix: connect to docker works also with libcurl >= 7.69.0 [[#1138](https://github.com/falcosecurity/falco/pull/1138)] * fix: HOST_ROOT environment variable detection [[#1133](https://github.com/falcosecurity/falco/pull/1133)] * fix(driver/bpf): stricter conditionals while dealing with strings [[#1131](https://github.com/falcosecurity/falco/pull/1131)] * fix: `/usr/bin/falco-${DRIVER_VERSION}` driver directory [[#1111](https://github.com/falcosecurity/falco/pull/1111)] * fix: FALCO_VERSION env variable inside Falco containers contains the Falco version now (not the docker image tag) [[#1111](https://github.com/falcosecurity/falco/pull/1111)] ### Rule Changes * rule(macro user_expected_system_procs_network_activity_conditions): allow whitelisting system binaries using the network under specific conditions [[#1070](https://github.com/falcosecurity/falco/pull/1070)] * rule(Full K8s Administrative Access): detect any k8s operation by an administrator with full access [[#1122](https://github.com/falcosecurity/falco/pull/1122)] * rule(Ingress Object without TLS Certificate Created): detect any attempt to create an ingress without TLS certification (rule enabled by default) [[#1122](https://github.com/falcosecurity/falco/pull/1122)] * rule(Untrusted Node Successfully Joined the Cluster): detect a node successfully joined the cluster outside of the list of allowed nodes [[#1122](https://github.com/falcosecurity/falco/pull/1122)] * rule(Untrusted Node Unsuccessfully Tried to Join the Cluster): detect an unsuccessful attempt to join the cluster for a node not in the list of allowed nodes [[#1122](https://github.com/falcosecurity/falco/pull/1122)] * rule(Network Connection outside Local Subnet): detect traffic to image outside local subnet [[#1122](https://github.com/falcosecurity/falco/pull/1122)] * rule(Outbound or Inbound Traffic not to Authorized Server Process and Port): detect traffic that is not to authorized server process and port [[#1122](https://github.com/falcosecurity/falco/pull/1122)] * rule(Delete or rename shell history): "mitre_defense_evation" tag corrected to "mitre_defense_evasion" [[#1143](https://github.com/falcosecurity/falco/pull/1143)] * rule(Delete Bash History): "mitre_defense_evation" tag corrected to "mitre_defense_evasion" [[#1143](https://github.com/falcosecurity/falco/pull/1143)] * rule(Write below root): use pmatch to check against known root directories [[#1137](https://github.com/falcosecurity/falco/pull/1137)] * rule(Detect outbound connections to common miner pool ports): whitelist sysdig/agent and falcosecurity/falco for query miner domain dns [[#1115](https://github.com/falcosecurity/falco/pull/1115)] * rule(Service Account Created in Kube Namespace): only detect sa created in kube namespace with success [[#1117](https://github.com/falcosecurity/falco/pull/1117)] ## v0.21.0 Released on 2020-03-17 ### Major Changes * BREAKING CHANGE: the SYSDIG_BPF_PROBE environment variable is now just FALCO_BPF_PROBE (please update your systemd scripts or kubernetes deployments. [[#1050](https://github.com/falcosecurity/falco/pull/1050)] * new: automatically publish deb packages (from git master branch) to public dev repository [[#1059](https://github.com/falcosecurity/falco/pull/1059)] * new: automatically publish rpm packages (from git master branch) to public dev repository [[#1059](https://github.com/falcosecurity/falco/pull/1059)] * new: automatically release deb packages (from git tags) to public repository [[#1059](https://github.com/falcosecurity/falco/pull/1059)] * new: automatically release rpm packages (from git tags) to public repository [[#1059](https://github.com/falcosecurity/falco/pull/1059)] * new: automatically publish docker images from master (master, master-slim, master-minimal) [[#1059](https://github.com/falcosecurity/falco/pull/1059)] * new: automatically publish docker images from git tag (tag, tag-slim, tag-master, latest, latest-slim, latest-minimal) [[#1059](https://github.com/falcosecurity/falco/pull/1059)] * new: sign packages with falcosecurity gpg key [[#1059](https://github.com/falcosecurity/falco/pull/1059)] ### Minor Changes * new: falco_version_prerelease contains the number of commits since last tag on the master [[#1086](https://github.com/falcosecurity/falco/pull/1086)] * docs: update branding [[#1074](https://github.com/falcosecurity/falco/pull/1074)] * new(docker/event-generator): add example k8s resource files that allow running the event generator in a k8s cluster. [[#1088](https://github.com/falcosecurity/falco/pull/1088)] * update: creating *-dev docker images using build arguments at build time [[#1059](https://github.com/falcosecurity/falco/pull/1059)] * update: docker images use packages from the new repositories [[#1059](https://github.com/falcosecurity/falco/pull/1059)] * update: docker image downloads old deb dependencies (gcc-6, gcc-5, binutils-2.30) from a new open repository [[#1059](https://github.com/falcosecurity/falco/pull/1059)] ### Bug Fixes * fix(docker): updating `stable` and `local` images to run from `debian:stable` [[#1018](https://github.com/falcosecurity/falco/pull/1018)] * fix(event-generator): the image used by the event generator deployment to `latest`. [[#1091](https://github.com/falcosecurity/falco/pull/1091)] * fix: -t (to disable rules by certain tag) or -t (to only run rules with a certain tag) work now [[#1081](https://github.com/falcosecurity/falco/pull/1081)] * fix: the falco driver now compiles on >= 5.4 kernels [[#1080](https://github.com/falcosecurity/falco/pull/1080)] * fix: download falco packages which url contains character to encode - eg, `+` [[#1059](https://github.com/falcosecurity/falco/pull/1059)] * fix(docker): use base name in docker-entrypoint.sh [[#981](https://github.com/falcosecurity/falco/pull/981)] ### Rule Changes * rule(detect outbound connections to common miner pool ports): disabled by default [[#1061](https://github.com/falcosecurity/falco/pull/1061)] * rule(macro net_miner_pool): add localhost and rfc1918 addresses as exception in the rule. [[#1061](https://github.com/falcosecurity/falco/pull/1061)] * rule(change thread namespace): modify condition to detect suspicious container activity [[#974](https://github.com/falcosecurity/falco/pull/974)] ## v0.20.0 Released on 2020-02-24 ### Major Changes * fix: memory leak introduced in 0.18.0 happening while using json events and the kubernetes audit endpoint [[#1041](https://github.com/falcosecurity/falco/pull/1041)] * new: grpc version api [[#872](https://github.com/falcosecurity/falco/pull/872)] ### Bug Fixes * fix: the base64 output format (-b) now works with both json and normal output. [[#1033](https://github.com/falcosecurity/falco/pull/1033)] * fix: version follows semver 2 bnf [[#872](https://github.com/falcosecurity/falco/pull/872)] ### Rule Changes * rule(write below etc): add "dsc_host" as a ms oms program [[#1028](https://github.com/falcosecurity/falco/pull/1028)] * rule(write below etc): let mcafee write to /etc/cma.d [[#1028](https://github.com/falcosecurity/falco/pull/1028)] * rule(write below etc): let avinetworks supervisor write some ssh cfg [[#1028](https://github.com/falcosecurity/falco/pull/1028)] * rule(write below etc): allow writes to /etc/pki from openshift secrets dir [[#1028](https://github.com/falcosecurity/falco/pull/1028)] * rule(write below root): let runc write to /exec.fifo [[#1028](https://github.com/falcosecurity/falco/pull/1028)] * rule(change thread namespace): let cilium-cni change namespaces [[#1028](https://github.com/falcosecurity/falco/pull/1028)] * rule(run shell untrusted): let puma reactor spawn shells [[#1028](https://github.com/falcosecurity/falco/pull/1028)] ## v0.19.0 Released on 2020-01-23 ### Major Changes * new: security audit [[#977](https://github.com/falcosecurity/falco/pull/977)] * instead of crashing, now falco will report the error when an internal error occurs while handling an event to be inspected. the log line will be of type error and will contain the string `error handling inspector event` [[#746](https://github.com/falcosecurity/falco/pull/746)] * build: bump grpc to 1.25.0 [[#939](https://github.com/falcosecurity/falco/pull/939)] * build: (most of) dependencies are bundled dynamically (by default) [[#968](https://github.com/falcosecurity/falco/pull/968)] * test: integration tests now can run on different distributions via docker containers, for now CentOS 7 and Ubuntu 18.04 with respective rpm and deb packages [[#1012](https://github.com/falcosecurity/falco/pull/1012)] ### Minor Changes * proposal: rules naming convention [[#980](https://github.com/falcosecurity/falco/pull/980)] * update: also allow posting json arrays containing k8s audit events to the k8s_audit endpoint. [[#967](https://github.com/falcosecurity/falco/pull/967)] * update: add support for k8s audit events to the falco-event-generator container. [[#997](https://github.com/falcosecurity/falco/pull/997)] * update: falco-tester base image is fedora:31 now [[#968](https://github.com/falcosecurity/falco/pull/968)] * build: switch to circleci [[#968](https://github.com/falcosecurity/falco/pull/968)] * build: bundle openssl into falco-builder docker image [[#1004](https://github.com/falcosecurity/falco/pull/1004)] * build: falco-builder docker image revamp (centos:7 base image) [[#1004](https://github.com/falcosecurity/falco/pull/1004)] * update: puppet module had been renamed from "sysdig-falco" to "falco" [[#922](https://github.com/falcosecurity/falco/pull/922)] * update: adds a hostname field to grpc output [[#927](https://github.com/falcosecurity/falco/pull/927)] * build: download grpc from their github repo [[#933](https://github.com/falcosecurity/falco/pull/933)] * update: ef_drop_falco is now ef_drop_simple_cons [[#922](https://github.com/falcosecurity/falco/pull/922)] * update(docker): use host_root environment variable rather than sysdig_host_root [[#922](https://github.com/falcosecurity/falco/pull/922)] * update: ef_drop_falco is now ef_drop_simple_cons [[#922](https://github.com/falcosecurity/falco/pull/922)] ### Bug Fixes * fix: providing clang into docker-builder [[#972](https://github.com/falcosecurity/falco/pull/972)] * fix: prevent throwing json type error c++ exceptions outside of the falco engine when processing k8s audit events. [[#928](https://github.com/falcosecurity/falco/pull/928)] * fix(docker/kernel/linuxkit): correct from for falco minimal image [[#913](https://github.com/falcosecurity/falco/pull/913)] ### Rule Changes * rules(list network_tool_binaries): add some network tools to detect suspicious network activity. [[#973](https://github.com/falcosecurity/falco/pull/973)] * rules(write below etc): allow automount to write to /etc/mtab [[#957](https://github.com/falcosecurity/falco/pull/957)] * rules(macro user_known_k8s_client_container): when executing the docker client, exclude fluentd-gcp-scaler container running in the `kube-system` namespace to avoid false positives [[#962](https://github.com/falcosecurity/falco/pull/962)] * rules(the docker client is executed in a container): detect the execution of the docker client in a container and logs it with warning priority. [[#915](https://github.com/falcosecurity/falco/pull/915)] * rules(list k8s_client_binaries): create and add docker, kubectl, crictl [[#915](https://github.com/falcosecurity/falco/pull/915)] * rules(macro container_entrypoint): add docker-runc-cur [[#914](https://github.com/falcosecurity/falco/pull/914)] * rules(list user_known_chmod_applications): add hyperkube [[#914](https://github.com/falcosecurity/falco/pull/914)] * rules(list network_tool_binaries): add some network tools to detect suspicious network activity. [[#975](https://github.com/falcosecurity/falco/pull/975)] * rules(macro user_known_k8s_client_container): macro to match kube-system namespace [[#955](https://github.com/falcosecurity/falco/pull/955)] * rules(contact k8s api server from container): now it can automatically resolve the cluster ip address [[#952](https://github.com/falcosecurity/falco/pull/952)] * rules(macro k8s_api_server): new macro to match the default k8s api server [[#952](https://github.com/falcosecurity/falco/pull/952)] * rules(macro sensitive_vol_mount): add more sensitive host paths [[#929](https://github.com/falcosecurity/falco/pull/929)] * rules(macro sensitive_mount): add more sensitive paths [[#929](https://github.com/falcosecurity/falco/pull/929)] * rules(macro consider_metadata_access): macro to decide whether to consider metadata or not (off by default) [[#943](https://github.com/falcosecurity/falco/pull/943)] * rules(contact cloud metadata service from container): add rules to detect access to gce instance metadata [[#943](https://github.com/falcosecurity/falco/pull/943)] * rules(macro sensitive_vol_mount): align sensitive mounts macro between k8s audit rules and syscall rules [[#950](https://github.com/falcosecurity/falco/pull/950)] * rules(macro consider_packet_socket_communication): macro to consider or not packet socket communication (off by default) [[#945](https://github.com/falcosecurity/falco/pull/945)] * rules(packet socket created in container): rule to detect raw packets creation [[#945](https://github.com/falcosecurity/falco/pull/945)] * rules(macro exe_running_docker_save): fixed false positives in multiple rules that were caused by the use of docker in docker [[#951](https://github.com/falcosecurity/falco/pull/951)] * rules(modify shell configuration file): fixed a false positive by excluding "exe_running_docker_save" [[#949](https://github.com/falcosecurity/falco/pull/949)] * rules(update package repository): fixed a false positive by excluding "exe_running_docker_save". [[#948](https://github.com/falcosecurity/falco/pull/948)] * rules(the docker client is executed in a container): when executing the docker client, exclude containers running in the `kube-system` namespace to avoid false positives [[#955](https://github.com/falcosecurity/falco/pull/955)] * rules(list user_known_chmod_applications): add kubelet [[#944](https://github.com/falcosecurity/falco/pull/944)] * rules(set setuid or setgid bit): fixed a false positive by excluding "exe_running_docker_save" [[#946](https://github.com/falcosecurity/falco/pull/946)] * rules(macro user_known_package_manager_in_container): allow users to specify conditions that match a legitimate use case for using a package management process in a container. [[#941](https://github.com/falcosecurity/falco/pull/941)] ## v0.18.0 Released 2019-10-28 ### Major Changes * falco grpc api server implementation, contains a subscribe method to subscribe to outputs from any grpc capable language [[#822](https://github.com/falcosecurity/falco/pull/822)] * add support for converting k8s pod security policies (psps) into set of falco rules that can be used to evaluate the conditions specified in the psp. [[#826](https://github.com/falcosecurity/falco/pull/826)] * initial redesign container images to remove build tools and leverage init containers for kernel module delivery. [[#776](https://github.com/falcosecurity/falco/pull/776)] * add flags to disable `syscall` event source or `k8s_audit` event source [[#779](https://github.com/falcosecurity/falco/pull/779)] ### Minor Changes * allow for unique names for psp converted rules/macros/lists/rule names as generated by falcoctl 0.0.3 [[#895](https://github.com/falcosecurity/falco/pull/895)] * make it easier to run regression tests without necessarily using the falco-tester docker image. [[#808](https://github.com/falcosecurity/falco/pull/808)] * fix falco engine compatibility with older k8s audit rules files. [[#893](https://github.com/falcosecurity/falco/pull/893)] * add tests for psp conversions with names containing spaces/dashes. [[#899](https://github.com/falcosecurity/falco/pull/899)] ### Bug Fixes * handle multi-document yaml files when reading rules files. [[#760](https://github.com/falcosecurity/falco/pull/760)] * improvements to how the webserver handles incoming invalid inputs [[#759](https://github.com/falcosecurity/falco/pull/759)] * fix: make lua state access thread-safe [[#867](https://github.com/falcosecurity/falco/pull/867)] * fix compilation on gcc 5.4 by working around gcc bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 [[#873](https://github.com/falcosecurity/falco/pull/873)] * add explicit dependency between tests and catch2 header file. [[#879](https://github.com/falcosecurity/falco/pull/879)] * fix: stable dockerfile libgcc-6-dev dependencies [[#830](https://github.com/falcosecurity/falco/pull/830)] * fix: build dependencies for the local dockerfile [[#782](https://github.com/falcosecurity/falco/pull/782)] * fix: a crash bug that could result from reading more than ~6 rules files [[#906](https://github.com/falcosecurity/falco/issues/906)] [[#907](https://github.com/falcosecurity/falco/pull/907)] ### Rule Changes * rules: add calico/node to trusted privileged container list [[#902](https://github.com/falcosecurity/falco/pull/902)] * rules: add macro `calico_node_write_envvars` to exception list of write below etc [[#902](https://github.com/falcosecurity/falco/pull/902)] * rules: add exception for rule write below rpm, this is a fp caused by amazon linux 2 yum. [[#755](https://github.com/falcosecurity/falco/pull/755)] * rules: ignore sensitive mounts from the ecs-agent [[#881](https://github.com/falcosecurity/falco/pull/881)] * rules: add rules to detect crypto mining activities [[#763](https://github.com/falcosecurity/falco/pull/763)] * rules: add back rule delete bash history for backport compatibility [[#864](https://github.com/falcosecurity/falco/pull/864)] * rule: syscalls are used to detect suid and sgid [[#765](https://github.com/falcosecurity/falco/pull/765)] * rules: delete bash history is renamed to delete or rename shell history [[#762](https://github.com/falcosecurity/falco/pull/762)] * rules: add image fluent/fluentd-kubernetes-daemonset to clear log trusted images [[#852](https://github.com/falcosecurity/falco/pull/852)] * rules: include default users created by `kops`. [[#898](https://github.com/falcosecurity/falco/pull/898)] * rules: delete or rename shell history: when deleting a shell history file now the syscalls are taken into account rather than just the commands deleting the files [[#762](https://github.com/falcosecurity/falco/pull/762)] * rules: delete or rename shell history: history deletion now supports fish and zsh in addition to bash [[#762](https://github.com/falcosecurity/falco/pull/762)] * rules: "create hidden files or directories" and "update package repository" now trigger also if the files are moved and not just if modified or created. [[#766](https://github.com/falcosecurity/falco/pull/766)] ## v0.17.1 Released 2019-09-26 ### Major Changes * Same as v0.17.0 ### Minor Changes * Same as v0.17.0 ### Bug Fixes * All in v0.17.0 * Fix a build problem for pre-built kernel probes. [[draios/sysdig#1471](https://github.com/draios/sysdig/pull/1471)] ### Rule Changes * Same as v0.17.0 ## v0.17.0 Released 2019-07-31 ### Major Changes * **The set of supported platforms has changed**. Switch to a reorganized builder image that uses Centos 7 as a base. As a result, falco is no longer supported on Centos 6. The other supported platforms should remain the same [[#719](https://github.com/falcosecurity/falco/pull/719)] ### Minor Changes * When enabling rules within the falco engine, use rule substrings instead of regexes. [[#743](https://github.com/falcosecurity/falco/pull/743)] * Additional improvements to the handling and display of rules validation errors [[#744](https://github.com/falcosecurity/falco/pull/744)] [[#747](https://github.com/falcosecurity/falco/pull/747)] ### Bug Fixes * Fix a problem that would cause prevent container metadata lookups when falco was daemonized [[#731](https://github.com/falcosecurity/falco/pull/731)] * Allow rule priorities to be expressed as lowercase and a mix of lower/uppercase [[#737](https://github.com/falcosecurity/falco/pull/737)] ### Rule Changes * Fix a parentheses bug with the `shell_procs` macro [[#728](https://github.com/falcosecurity/falco/pull/728)] * Allow additional containers to mount sensitive host paths [[#733](https://github.com/falcosecurity/falco/pull/733)] [[#736](https://github.com/falcosecurity/falco/pull/736)] * Allow additional containers to truncate log files [[#733](https://github.com/falcosecurity/falco/pull/733)] * Fix false positives with the `Write below root` rule on GKE [[#739](https://github.com/falcosecurity/falco/pull/739)] ## v0.16.0 Released 2019-07-12 ### Major Changes * Clean up error reporting to provide more meaningful error messages along with context when loading rules files. When run with -V, the results of the validation ("OK" or error message) are sent to standard output. [[#708](https://github.com/falcosecurity/falco/pull/708)] * Improve rule loading performance by optimizing lua parsing paths to avoid expensive pattern matches. [[#694](https://github.com/falcosecurity/falco/pull/694)] * Bump falco engine version to 4 to reflect new fields `ka.useragent`, others. [[#710](https://github.com/falcosecurity/falco/pull/710)] [[#681](https://github.com/falcosecurity/falco/pull/681)] * Add Catch2 as a unit testing framework. This will add additional coverage on top of the regression tests using Avocado. [[#687](https://github.com/falcosecurity/falco/pull/687)] ### Minor Changes * Add SYSDIG_DIR Cmake option to specify location for sysdig source code when building falco. [[#677](https://github.com/falcosecurity/falco/pull/677)] [[#679](https://github.com/falcosecurity/falco/pull/679)] [[#702](https://github.com/falcosecurity/falco/pull/702)] * New field `ka.useragent` reports the useragent from k8s audit events. [[#709](https://github.com/falcosecurity/falco/pull/709)] * Add clang formatter for C++ syntax formatting. [[#701](https://github.com/falcosecurity/falco/pull/701)] [[#689](https://github.com/falcosecurity/falco/pull/689)] * Partial changes towards lua syntax formatting. No particular formatting enforced yet, though. [[#718](https://github.com/falcosecurity/falco/pull/718)] * Partial changes towards yaml syntax formatting. No particular formatting enforced yet, though. [[#714](https://github.com/falcosecurity/falco/pull/714)] * Add cmake syntax formatting. [[#703](https://github.com/falcosecurity/falco/pull/703)] * Token bucket unit tests and redesign. [[#692](https://github.com/falcosecurity/falco/pull/692)] * Update github PR template. [[#699](https://github.com/falcosecurity/falco/pull/699)] * Fix PR template for kind/rule-*. [[#697](https://github.com/falcosecurity/falco/pull/697)] ### Bug Fixes * Remove an unused cmake file. [[#700](https://github.com/falcosecurity/falco/pull/700)] * Misc Cmake cleanups. [[#673](https://github.com/falcosecurity/falco/pull/673)] * Misc k8s install docs improvements. [[#671](https://github.com/falcosecurity/falco/pull/671)] ### Rule Changes * Allow k8s.gcr.io/kube-proxy image to run privileged. [[#717](https://github.com/falcosecurity/falco/pull/717)] * Add runc to the list of possible container entrypoint parents. [[#712](https://github.com/falcosecurity/falco/pull/712)] * Skip Source RFC 1918 addresses when considering outbound connections. [[#685](https://github.com/falcosecurity/falco/pull/685)] * Add additional `user_XXX` placeholder macros to allow for easy customization of rule exceptions. [[#685](https://github.com/falcosecurity/falco/pull/685)] * Let weaveworks programs change namespaces. [[#685](https://github.com/falcosecurity/falco/pull/685)] * Add additional openshift images. [[#685](https://github.com/falcosecurity/falco/pull/685)] * Add openshift as a k8s binary. [[#678](https://github.com/falcosecurity/falco/pull/678)] * Add dzdo as a binary that can change users. [[#678](https://github.com/falcosecurity/falco/pull/678)] * Allow azure/calico binaries to change namespaces. [[#678](https://github.com/falcosecurity/falco/pull/678)] * Add back trusted_containers list for backport compatibility [[#675](https://github.com/falcosecurity/falco/pull/675)] * Add mkdirat as a syscall for mkdir operations. [[#667](https://github.com/falcosecurity/falco/pull/667)] * Add container id/repository to rules that can work with containers. [[#667](https://github.com/falcosecurity/falco/pull/667)] ## v0.15.3 Released 2019-06-12 ### Major Changes * None. ### Minor Changes * None. ### Bug Fixes * Fix kernel module compilation for kernels < 3.11 [[#sysdig/1436](https://github.com/draios/sysdig/pull/1436)] ### Rule Changes * None. ## v0.15.2 Released 2019-06-12 ### Major Changes * New documentation and process handling around issues and pull requests. [[#644](https://github.com/falcosecurity/falco/pull/644)] [[#659](https://github.com/falcosecurity/falco/pull/659)] [[#664](https://github.com/falcosecurity/falco/pull/664)] [[#665](https://github.com/falcosecurity/falco/pull/665)] ### Minor Changes * None. ### Bug Fixes * Fix compilation of eBPF programs on COS (used by GKE) [[#sysdig/1431](https://github.com/draios/sysdig/pull/1431)] ### Rule Changes * Rework exceptions lists for `Create Privileged Pod`, `Create Sensitive Mount Pod`, `Launch Sensitive Mount Container`, `Launch Privileged Container` rules to use separate specific lists rather than a single "Trusted Containers" list. [[#651](https://github.com/falcosecurity/falco/pull/651)] ## v0.15.1 Released 2019-06-07 ### Major Changes * Drop unnecessary events at the kernel level instead of userspace, which should improve performance [[#635](https://github.com/falcosecurity/falco/pull/635)] ### Minor Changes * Add instructions for k8s audit support in >= 1.13 [[#608](https://github.com/falcosecurity/falco/pull/608)] * Fix security issues reported by GitHub on Anchore integration [[#592](https://github.com/falcosecurity/falco/pull/592)] * Several docs/readme improvements [[#620](https://github.com/falcosecurity/falco/pull/620)] [[#616](https://github.com/falcosecurity/falco/pull/616)] [[#631](https://github.com/falcosecurity/falco/pull/631)] [[#639](https://github.com/falcosecurity/falco/pull/639)] [[#642](https://github.com/falcosecurity/falco/pull/642)] * Better tracking of rule counts per ruleset [[#645](https://github.com/falcosecurity/falco/pull/645)] ### Bug Fixes * Handle rule patterns that are invalid regexes [[#636](https://github.com/falcosecurity/falco/pull/636)] * Fix kernel module builds on newer kernels [[#646](https://github.com/falcosecurity/falco/pull/646)] [[#sysdig/1413](https://github.com/draios/sysdig/pull/1413)] ### Rule Changes * New rule `Launch Remote File Copy Tools in Container` could be used to identify exfiltration attacks [[#600](https://github.com/falcosecurity/falco/pull/600)] * New rule `Create Symlink Over Sensitive Files` can help detect attacks like [[CVE-2018-15664](https://nvd.nist.gov/vuln/detail/CVE-2018-15664)] [[#613](https://github.com/falcosecurity/falco/pull/613)] [[#637](https://github.com/falcosecurity/falco/pull/637)] * Let etcd-manager write to /etc/hosts. [[#613](https://github.com/falcosecurity/falco/pull/613)] * Let additional processes spawned by google-accounts-daemon access sensitive files [[#593](https://github.com/falcosecurity/falco/pull/593)] * Add Sematext Monitoring & Logging agents to trusted k8s containers [[#594](https://github.com/falcosecurity/falco/pull/594/)] * Add additional coverage for `Netcat Remote Code Execution in Container` rule. [[#617](https://github.com/falcosecurity/falco/pull/617/)] * Fix `egrep` typo. [[#617](https://github.com/falcosecurity/falco/pull/617/)] * Allow Ansible to run using Python 3 [[#625](https://github.com/falcosecurity/falco/pull/625/)] * Additional `Write below etc` exceptions for nginx, rancher [[#637](https://github.com/falcosecurity/falco/pull/637)] [[#648](https://github.com/falcosecurity/falco/pull/648)] [[#652](https://github.com/falcosecurity/falco/pull/652)] * Add rules for running with IBM Cloud Kubernetes Service [[#634](https://github.com/falcosecurity/falco/pull/634)] ## v0.15.0 Released 2019-05-13 ### Major Changes * **Actions and alerts for dropped events**: Falco can now take actions, including sending alerts/logging messages, and/or even exiting Falco, when it detects dropped system call events. Fixes CVE 2019-8339. [[#561](https://github.com/falcosecurity/falco/pull/561)] [[#571](https://github.com/falcosecurity/falco/pull/571)] * **Support for Containerd/CRI-O**: Falco now supports containerd/cri-o containers. [[#585](https://github.com/falcosecurity/falco/pull/585)] [[#591](https://github.com/falcosecurity/falco/pull/591)] [[#599](https://github.com/falcosecurity/falco/pull/599)] [[#sysdig/1376](https://github.com/draios/sysdig/pull/1376)] [[#sysdig/1310](https://github.com/draios/sysdig/pull/1310)] [[#sysdig/1399](https://github.com/draios/sysdig/pull/1399)] * **Perform docker metadata fetches asynchronously**: When new containers are discovered, fetch metadata about the container asynchronously, which should significantly reduce the likelihood of dropped system call events. [[#sysdig/1326](https://github.com/draios/sysdig/pull/1326)] [[#550](https://github.com/falcosecurity/falco/pull/550)] [[#570](https://github.com/falcosecurity/falco/pull/570)] * Better syscall event performance: improve algorithm for reading system call events from kernel module to handle busy event streams [[#sysdig/1372](https://github.com/draios/sysdig/pull/1372)] * HTTP Output: Falco can now send alerts to http endpoints directly without having to use curl. [[#523](https://github.com/falcosecurity/falco/pull/523)] * Move Kubernetes Response Engine to own repo: The Kubernetes Response Engine is now in its [own github repository](https://github.com/falcosecurity/kubernetes-response-engine). [[#539](https://github.com/falcosecurity/falco/pull/539)] * Updated Puppet Module: An all-new puppet module compatible with puppet 4 with a smoother installation process and updated package links. [[#537](https://github.com/falcosecurity/falco/pull/537)] [[#543](https://github.com/falcosecurity/falco/pull/543)] [[#546](https://github.com/falcosecurity/falco/pull/546)] * RHEL-based falco image: Provide dockerfiles that use RHEL 7 as the base image instead of debian:unstable. [[#544](https://github.com/falcosecurity/falco/pull/544)] ### Minor Changes * ISO-8601 Timestamps: Add the ability to write timestamps in ISO-8601 w/ UTC, and use this format by default when running falco in a container [[#518](https://github.com/falcosecurity/falco/pull/518)] * Docker-based builder/tester: You can now build Falco using the [falco-builder](https://falco.org/docs/source/#build-using-falco-builder-container) docker image, and run regression tests using the [falco-tester](https://falco.org/docs/source/#test-using-falco-tester-container) docker image. [[#522](https://github.com/falcosecurity/falco/pull/522)] [[#584](https://github.com/falcosecurity/falco/pull/584)] * Several small docs changes to improve clarity and readability [[#524](https://github.com/falcosecurity/falco/pull/524)] [[#540](https://github.com/falcosecurity/falco/pull/540)] [[#541](https://github.com/falcosecurity/falco/pull/541)] [[#542](https://github.com/falcosecurity/falco/pull/542)] * Add instructions on how to enable K8s Audit Logging for kops [[#535](https://github.com/falcosecurity/falco/pull/535)] * Add a "stale issue" bot that marks and eventually closes old issues with no activity [[#548](https://github.com/falcosecurity/falco/pull/548)] * Improvements to sample K8s daemonset/service/etc files [[#562](https://github.com/falcosecurity/falco/pull/562)] ### Bug Fixes * Fix regression that broke json output [[#581](https://github.com/falcosecurity/falco/pull/581)] * Fix errors when building via docker from MacOS [[#582](https://github.com/falcosecurity/falco/pull/582)] ### Rule Changes * **Tag rules using Mitre Attack Framework**: Add tags for all relevant rules linking them to the [MITRE Attack Framework](https://attack.mitre.org). We have an associated [blog post](https://sysdig.com/blog/mitre-attck-framework-for-container-runtime-security-with-sysdig-falco/). [[#575](https://github.com/falcosecurity/falco/pull/575)] [[#578](https://github.com/falcosecurity/falco/pull/578)] * New rules for additional use cases: New rules `Schedule Cron Jobs`, `Update Package Repository`, `Remove Bulk Data from Disk`, `Set Setuid or Setgid bit`, `Detect bash history deletion`, `Create Hidden Files or Directories` look for additional common follow-on activity you might see from an attacker. [[#578](https://github.com/falcosecurity/falco/pull/578)] [[#580](https://github.com/falcosecurity/falco/pull/580)] * Allow docker's "exe" (usually part of docker save/load) to write to many filesystem locations [[#552](https://github.com/falcosecurity/falco/pull/552)] * Let puppet write below /etc [[#563](https://github.com/falcosecurity/falco/pull/563) * Add new `user_known_write_root_conditions`, `user_known_non_sudo_setuid_conditions`, and `user_known_write_monitored_dir_conditions` macros to allow those rules to be easily customized in user rules files [[#563](https://github.com/falcosecurity/falco/pull/563)] [[#566](https://github.com/falcosecurity/falco/pull/566)] * Better coverage and exceptions for rancher [[#559](https://github.com/falcosecurity/falco/pull/559)] * Allow prometheus to write to its conf directory under etc [[#564](https://github.com/falcosecurity/falco/pull/564)] * Better coverage and exceptions for openshift/related tools [[#567](https://github.com/falcosecurity/falco/pull/567)] [[#573](https://github.com/falcosecurity/falco/pull/573)] * Better coverage for cassandra/kubelet/kops to reduce FPs [[#551](https://github.com/falcosecurity/falco/pull/551)] * Better coverage for docker, openscap to reduce FPs [[#573](https://github.com/falcosecurity/falco/pull/573)] * Better coverage for fluentd/jboss to reduce FPs [[#590](https://github.com/falcosecurity/falco/pull/590)] * Add `ash` (Alpine Linux-related shell) as a shell binary [[#597](https://github.com/falcosecurity/falco/pull/597)] ## v0.14.0 Released 2019-02-06 ### Major Changes * Rules versioning support: The falco engine and executable now have an *engine version* that represents the fields they support. Similarly, rules files have an optional *required_engine_version: NNN* object that names the minimum engine version required to read that rules file. Any time the engine adds new fields, event sources, etc, the engine version will be incremented, and any time a rules file starts using new fields, event sources, etc, the required engine version will be incremented. [[#492](https://github.com/falcosecurity/falco/pull/492)] * Allow SSL for K8s audit endpoint/embedded webserver [[#471](https://github.com/falcosecurity/falco/pull/471)] * Add stale issues bot that automatically flags old github issues as stale after 60 days of inactivity and closes issues after 67 days of inactivity. [[#500](https://github.com/falcosecurity/falco/pull/500)] * Support bundle: When run with `--support`, falco will print a json object containing necessary information like falco version, command line, operating system information, and falco rules files contents. This could be useful when reporting issues. [[#517](https://github.com/falcosecurity/falco/pull/517)] ### Minor Changes * Support new third-party library dependencies from open source sysdig. [[#498](https://github.com/falcosecurity/falco/pull/498)] * Add CII best practices badge. [[#499](https://github.com/falcosecurity/falco/pull/499)] * Fix kernel module builds when running on centos as a container by installing gcc 5 by hand instead of directly from debian/unstable. [[#501](https://github.com/falcosecurity/falco/pull/501)] * Mount `/etc` when running as a container, which allows container to build kernel module/ebpf program on COS/Minikube. [[#475](https://github.com/falcosecurity/falco/pull/475)] * Improved way to specify the source of generic event objects [[#480](https://github.com/falcosecurity/falco/pull/480)] * Readability/clarity improvements to K8s Audit/K8s Daemonset READMEs. [[#503](https://github.com/falcosecurity/falco/pull/503)] * Add additional RBAC permissions to track deployments/daemonsets/replicasets. [[#514](https://github.com/falcosecurity/falco/pull/514)] ### Bug Fixes * Fix formatting of nodejs examples README [[#502](https://github.com/falcosecurity/falco/pull/502)] ### Rule Changes * Remove FPs for `Launch Sensitive Mount Container` rule [[#509](https://github.com/falcosecurity/falco/pull/509/files)] * Update Container rules/macros to use the more reliable `container.image.{repository,tag}` that always return the repository/tag of an image instead of `container.image`, which may not for some docker daemon versions. [[#513](https://github.com/falcosecurity/falco/pull/513)] ## v0.13.1 Released 2019-01-16 ### Major Changes ### Minor Changes * Unbuffer outputs by default. This helps make output readable when used in environments like K8s. [[#494](https://github.com/falcosecurity/falco/pull/494)] * Improved documentation for running Falco within K8s and getting K8s Audit Logging to work with Minikube and Falco as a Daemonset within K8s. [[#496](https://github.com/falcosecurity/falco/pull/496)] * Fix AWS Permissions for Kubernetes Response Engine [[#465](https://github.com/falcosecurity/falco/pull/465)] * Tighten compilation flags to include `-Wextra` and `-Werror` [[#479](https://github.com/falcosecurity/falco/pull/479)] * Add `k8s.ns.name` to outputs when `-pk` argument is used [[#472](https://github.com/falcosecurity/falco/pull/472)] * Remove kubernetes-response-engine from system:masters [[#488](https://github.com/falcosecurity/falco/pull/488)] ### Bug Fixes * Ensure `-pc`/`-pk` only apply to syscall rules and not k8s_audit rules [[#495](https://github.com/falcosecurity/falco/pull/495)] * Fix a potential crash that could occur when using the falco engine and rulesets [[#468](https://github.com/falcosecurity/falco/pull/468)] * Fix a regression where format output options were mistakenly removed [[#485](https://github.com/falcosecurity/falco/pull/485)] ### Rule Changes * Fix FPs related to calico and writing files below etc [[#481](https://github.com/falcosecurity/falco/pull/481)] * Fix FPs related to `apt-config`/`apt-cache`, `apk` [[#490](https://github.com/falcosecurity/falco/pull/490)] * New rules `Launch Package Management Process in Container`, `Netcat Remote Code Execution in Container`, `Launch Suspicious Network Tool in Container` look for host-level network tools like `netcat`, package management tools like `apt-get`, or network tool binaries being run in a container. [[#490](https://github.com/falcosecurity/falco/pull/490)] * Fix the `inbound` and `outbound` macros so they work with sendto/recvfrom/sendmsg/recvmsg. [[#470](https://github.com/falcosecurity/falco/pull/470)] * Fix FPs related to prometheus/openshift writing config below /etc. [[#470](https://github.com/falcosecurity/falco/pull/470)] ## v0.13.0 Released 2018-11-09 ### Major Changes * **Support for K8s Audit Events** : Falco now supports [K8s Audit Events](https://kubernetes.io/docs/tasks/debug-application-cluster/audit/#audit-backends) as a second stream of events in addition to syscalls. For full details on the feature, see the [wiki](https://github.com/falcosecurity/falco/wiki/K8s-Audit-Event-Support). * Transparent Config/Rule Reloading: On SIGHUP, Falco will now reload all config files/rules files and start processing new events. Allows rules changes without having to restart falco [[#457](https://github.com/falcosecurity/falco/pull/457)] [[#432](https://github.com/falcosecurity/falco/issues/432)] ### Minor Changes * The reference integration of falco into a action engine now supports aws actions like lambda, etc. [[#460](https://github.com/falcosecurity/falco/pull/460)] * Add netcat to falco docker images, which allows easier integration of program outputs to external servers [[#456](https://github.com/falcosecurity/falco/pull/456)] [[#433](https://github.com/falcosecurity/falco/issues/433)] ### Bug Fixes * Links cleanup related to the draios/falco -> falcosecurity/falco move [[#447](https://github.com/falcosecurity/falco/pull/447)] * Properly load/unload kernel module when the falco service is started/stopped [[#459](https://github.com/falcosecurity/falco/pull/459)] [[#418](https://github.com/falcosecurity/falco/issues/418)] ### Rule Changes * Better coverage (e.g. reduced FPs) for critical stack, hids systems, ufw, cloud-init, etc. [[#445](https://github.com/falcosecurity/falco/pull/445)] * New rules `Launch Package Management Process in Container`, `Netcat Remote Code Execution in Container`, and `Launch Suspicious Network Tool in Container` look for running various suspicious programs in a container. [[#461](https://github.com/falcosecurity/falco/pull/461)] * Misc changes to address false positives in GKE, Istio, etc. [[#455](https://github.com/falcosecurity/falco/pull/455)] [[#439](https://github.com/falcosecurity/falco/issues/439)] ## v0.12.1 Released 2018-09-11 ### Bug Fixes * Fig regression in libcurl configure script [[#416](https://github.com/draios/falco/pull/416)] ## v0.12.0 Released 2018-09-11 ### Major Changes * Improved IPv6 Support to fully support use of IPv6 addresses in events, connections and filters [[#sysdig/1204](https://github.com/draios/sysdig/pull/1204)] * Ability to associate connections with dns names: new filterchecks `fd.*ip.name` allow looking up the DNS name for a connection's IP address. This can be used to identify or restrict connections by dns names e.g. `evt.type=connect and fd.sip.name=github.com`. [[#412](https://github.com/draios/falco/pull/412)] [[#sysdig/1213](https://github.com/draios/sysdig/pull/1213)] * New filterchecks `user.loginuid` and `user.loginname` can be used to match the login uid, which stays consistent across sudo/su. This can be used to find the actual user running a given process [[#sysdig/1189](https://github.com/draios/sysdig/pull/1189)] ### Minor Changes * Upgrade zlib to 1.2.11, openssl to 1.0.2n, and libcurl to 7.60.0 to address software vulnerabilities [[#402](https://github.com/draios/falco/pull/402)] * New `endswith` operator can be used for suffix matching on strings [[#sysdig/1209](https://github.com/draios/sysdig/pull/1209)] ### Bug Fixes * Better control of specifying location of lua source code [[#406](https://github.com/draios/falco/pull/406)] ### Rule Changes * None for this release. ## v0.11.1 Released 2018-07-31 ### Bug Fixes * Fix a problem that caused the kernel module to not load on certain kernel versions [[#397](https://github.com/draios/falco/pull/397)] [[#394](https://github.com/draios/falco/issues/394)] ## v0.11.0 Released 2018-07-24 ### Major Changes * **EBPF Support** (Beta): Falco can now read events via an ebpf program loaded into the kernel instead of the `falco-probe` kernel module. Full docs [here](https://github.com/draios/sysdig/wiki/eBPF-(beta)). [[#365](https://github.com/draios/falco/pull/365)] ### Minor Changes * Rules may now have an `skip-if-unknown-filter` property. If set to true, a rule will be skipped if its condition/output property refers to a filtercheck (e.g. `fd.some-new-attribute`) that is not present in the current falco version. [[#364](https://github.com/draios/falco/pull/364)] [[#345](https://github.com/draios/falco/issues/345)] * Small changes to Falco `COPYING` file so github automatically recognizes license [[#380](https://github.com/draios/falco/pull/380)] * New example integration showing how to connect Falco with Anchore to dynamically create falco rules based on negative scan results [[#390](https://github.com/draios/falco/pull/390)] * New example integration showing how to connect Falco, [nats](https://nats.io/), and K8s to run flexible "playbooks" based on Falco events [[#389](https://github.com/draios/falco/pull/389)] ### Bug Fixes * Ensure all rules are enabled by default [[#379](https://github.com/draios/falco/pull/379)] * Fix libcurl compilation problems [[#374](https://github.com/draios/falco/pull/374)] * Add gcc-6 to docker container, which improves compatibility when building kernel module [[#382](https://github.com/draios/falco/pull/382)] [[#371](https://github.com/draios/falco/issues/371)] * Ensure the /lib/modules symlink to /host/lib/modules is set correctly [[#392](https://github.com/draios/falco/issues/392)] ### Rule Changes * Add additional binary writing programs [[#366](https://github.com/draios/falco/pull/366)] * Add additional package management programs [[#388](https://github.com/draios/falco/pull/388)] [[#366](https://github.com/draios/falco/pull/366)] * Expand write_below_etc handling for additional programs [[#388](https://github.com/draios/falco/pull/388)] [[#366](https://github.com/draios/falco/pull/366)] * Expand set of programs allowed to write to `/etc/pki` [[#388](https://github.com/draios/falco/pull/388)] * Expand set of root written directories/files [[#388](https://github.com/draios/falco/pull/388)] [[#366](https://github.com/draios/falco/pull/366)] * Let pam-config read sensitive files [[#388](https://github.com/draios/falco/pull/388)] * Add additional trusted containers: openshift, datadog, docker ucp agent, gliderlabs logspout [[#388](https://github.com/draios/falco/pull/388)] * Let coreos update-ssh-keys write to /home/core/.ssh [[#388](https://github.com/draios/falco/pull/388)] * Expand coverage for MS OMS [[#388](https://github.com/draios/falco/issues/388)] [[#387](https://github.com/draios/falco/issues/387)] * Expand the set of shell spawning programs [[#366](https://github.com/draios/falco/pull/366)] * Add additional mysql programs/directories [[#366](https://github.com/draios/falco/pull/366)] * Let program `id` open network connections [[#366](https://github.com/draios/falco/pull/366)] * Opt-in rule for protecting tomcat shell spawns [[#366](https://github.com/draios/falco/pull/366)] * New rule `Write below monitored directory` [[#366](https://github.com/draios/falco/pull/366)] ## v0.10.0 Released 2018-04-24 ### Major Changes * **Rules Directory Support**: Falco will read rules files from `/etc/falco/rules.d` in addition to `/etc/falco/falco_rules.yaml` and `/etc/falco/falco_rules.local.yaml`. Also, when the argument to `-r`/falco.yaml `rules_file` is a directory, falco will read rules files from that directory. [[#348](https://github.com/draios/falco/pull/348)] [[#187](https://github.com/draios/falco/issues/187)] * Properly support all syscalls (e.g. those without parameter extraction by the kernel module) in falco conditions, so they can be included in `evt.type=` conditions. [[#352](https://github.com/draios/falco/pull/352)] * When packaged as a container, start building kernel module with gcc 5.0 instead of gcc 4.9. [[#331](https://github.com/draios/falco/pull/331)] * New example puppet module for falco. [[#341](https://github.com/draios/falco/pull/341)] [[#115](https://github.com/draios/falco/issues/115)] * When signaled with `USR1`, falco will close/reopen log files. Include a [logrotate](https://github.com/logrotate/logrotate) example that shows how to use this feature for log rotation. [[#347](https://github.com/draios/falco/pull/347)] [[#266](https://github.com/draios/falco/issues/266)] * To improve resource usage, further restrict the set of system calls available to falco [[#351](https://github.com/draios/falco/pull/351)] [[draios/sysdig#1105](https://github.com/draios/sysdig/pull/1105)] ### Minor Changes * Add gdb to the development Docker image (sysdig/falco:dev) to aid in debugging. [[#323](https://github.com/draios/falco/pull/323)] * You can now specify -V multiple times on the command line to validate multiple rules files at once. [[#329](https://github.com/draios/falco/pull/329)] * When run with `-v`, falco will print *dangling* macros/lists that are not used by any rules. [[#329](https://github.com/draios/falco/pull/329)] * Add an example demonstrating cryptomining attack that exploits an open docker daemon using host mounts. [[#336](https://github.com/draios/falco/pull/336)] * New falco.yaml option `json_include_output_property` controls whether the formatted string "output" is included in the json object when json output is enabled. [[#342](https://github.com/draios/falco/pull/342)] * Centralize testing event types for consideration by falco into a single function [[draios/sysdig#1105](https://github.com/draios/sysdig/pull/1105)) [[#356](https://github.com/draios/falco/pull/356)] * If a rule has an attribute `warn_evttypes`, falco will not complain about `evt.type` restrictions on that rule [[#355](https://github.com/draios/falco/pull/355)] * When run with `-i`, print all ignored events/syscalls and exit. [[#359](https://github.com/draios/falco/pull/359)] ### Bug Fixes * Minor bug fixes to k8s daemonset configuration. [[#325](https://github.com/draios/falco/pull/325)] [[#296](https://github.com/draios/falco/pull/296)] [[#295](https://github.com/draios/falco/pull/295)] * Ensure `--validate` can be used interchangeably with `-V`. [[#334](https://github.com/draios/falco/pull/334)] [[#322](https://github.com/draios/falco/issues/322)] * Rule conditions like `fd.net` can now be used with the `in` operator e.g. `evt.type=connect and fd.net in ("127.0.0.1/24")`. [[draios/sysdig#1091](https://github.com/draios/sysdig/pull/1091)] [[#343](https://github.com/draios/falco/pull/343)] * Ensure that `keep_alive` can be used both with file and program output at the same time. [[#335](https://github.com/draios/falco/pull/335)] * Make it possible to append to a skipped macro/rule without falco complaining [[#346](https://github.com/draios/falco/pull/346)] [[#305](https://github.com/draios/falco/issues/305)] * Ensure rule order is preserved even when rules do not contain any `evt.type` restriction. [[#354](https://github.com/draios/falco/issues/354)] [[#355](https://github.com/draios/falco/pull/355)] ### Rule Changes * Make it easier to extend the `Change thread namespace` rule via a `user_known_change_thread_namespace_binaries` list. [[#324](https://github.com/draios/falco/pull/324)] * Various FP fixes from users. [[#321](https://github.com/draios/falco/pull/321)] [[#326](https://github.com/draios/falco/pull/326)] [[#344](https://github.com/draios/falco/pull/344)] [[#350](https://github.com/draios/falco/pull/350)] * New rule `Disallowed SSH Connection` detects attempts ssh connection attempts to hosts outside of an expected set. In order to be effective, you need to override the macro `allowed_ssh_hosts` in a user rules file. [[#321](https://github.com/draios/falco/pull/321)] * New rule `Unexpected K8s NodePort Connection` detects attempts to contact the K8s NodePort range from a program running inside a container. In order to be effective, you need to override the macro `nodeport_containers` in a user rules file. [[#321](https://github.com/draios/falco/pull/321)] * Improve `Modify binary dirs` rule to work with new syscalls [[#353](https://github.com/draios/falco/pull/353)] * New rule `Unexpected UDP Traffic` checks for udp traffic not on a list of expected ports. Somewhat FP-prone, so it must be explicitly enabled by overriding the macro `do_unexpected_udp_check` in a user rules file. [[#320](https://github.com/draios/falco/pull/320)] [[#357](https://github.com/draios/falco/pull/357)] ## v0.9.0 Released 2018-01-18 ### Bug Fixes * Fix driver incompatibility problems with some linux kernel versions that can disable pagefault tracepoints [[#sysdig/1034](https://github.com/draios/sysdig/pull/1034)] * Fix OSX Build incompatibility with latest version of libcurl [[#291](https://github.com/draios/falco/pull/291)] ### Minor Changes * Updated the Kubernetes example to provide an additional example: Daemon Set using RBAC and a ConfigMap for configuration. Also expanded the documentation for both the RBAC and non-RBAC examples. [[#309](https://github.com/draios/falco/pull/309)] ### Rule Changes * Refactor the shell-related rules to reduce false positives. These changes significantly decrease the scope of the rules so they trigger only for shells spawned below specific processes instead of anywhere. [[#301](https://github.com/draios/falco/pull/301)] [[#304](https://github.com/draios/falco/pull/304)] * Lots of rule changes based on feedback from Sysdig Secure community [[#293](https://github.com/draios/falco/pull/293)] [[#298](https://github.com/draios/falco/pull/298)] [[#300](https://github.com/draios/falco/pull/300)] [[#307](https://github.com/draios/falco/pull/307)] [[#315](https://github.com/draios/falco/pull/315)] ## v0.8.1 Released 2017-10-10 ### Bug Fixes * Fix packaging to specify correct built-in config file [[#288](https://github.com/draios/falco/pull/288)] ## v0.8.0 Released 2017-10-10 **Important**: the location for falco's configuration file has moved from `/etc/falco.yaml` to `/etc/falco/falco.yaml`. The default rules file has moved from `/etc/falco_rules.yaml` to `/etc/falco/falco_rules.yaml`. In addition, 0.8.0 has added a _local_ rules file to `/etc/falco/falco_rules.local.yaml`. See [the documentation](https://github.com/draios/falco/wiki/Falco-Default-and-Local-Rules-Files) for more details. ### Major Changes * Add the ability to append one list to another list by setting an `append: true` attribute. [[#264](https://github.com/draios/falco/pull/264)] * Add the ability to append one macro/rule to another list by setting an `append: true` attribute. [[#277](https://github.com/draios/falco/pull/277)] * Ensure that falco rules/config files are preserved across package upgrades/removes if modified. [[#278](https://github.com/draios/falco/pull/278)] * Add the notion of a "local" rules file that should contain modifications to the default falco rules file. [[#278](https://github.com/draios/falco/pull/278)] * When using json output, separately include the individual templated fields in the json object. [[#282](https://github.com/draios/falco/pull/282)] * Add the ability to keep a file/program pipe handle open across rule notifications. [[#283](https://github.com/draios/falco/pull/283)] * New argument `-V` validates rules file and immediately exits. [[#286](https://github.com/draios/falco/pull/286)] ### Minor Changes * Minor updates to falco example programs [[#248](https://github.com/draios/falco/pull/248)] [[#275](https://github.com/draios/falco/pull/275)] * Also validate macros at rule parse time. [[#257](https://github.com/draios/falco/pull/257)] * Minor README typo fixes [[#276](https://github.com/draios/falco/pull/276)] * Add a government CLA (contributor license agreement). [[#263](https://github.com/draios/falco/pull/263)] * Add ability to only run rules with a priority >= some threshold [[#281](https://github.com/draios/falco/pull/281)] * Add ability to make output channels unbuffered [[#285](https://github.com/draios/falco/pull/285)] ### Bug Fixes * Fix installation of falco on OSX [[#252](https://github.com/draios/falco/pull/252)] * Fix a bug that caused the trailing whitespace of a quoted string to be accidentally removed [[#254](https://github.com/draios/falco/pull/254)] * When multiple sets of kernel headers are installed, find the one for the running kernel [[#260](https://github.com/draios/falco/pull/260)] * Allow pathnames in rule/macro conditions to contain '.' characters [[#262](https://github.com/draios/falco/pull/262)] * Fix a bug where a list named "foo" would be substituted even if it were a substring of a longer word like "my_foo" [[#258](https://github.com/draios/falco/pull/258)] * Remove extra trailing newlines from rule output strings [[#265](https://github.com/draios/falco/pull/265)] * Improve build pathnames to avoid relative paths when possible [[#284](https://github.com/draios/falco/pull/284)] ### Rule Changes * Significant changes to default ruleset to address FPs. These changes resulted from hundreds of hours of use in actual customer environments. [[#247](https://github.com/draios/falco/pull/247)] [[#259](https://github.com/draios/falco/pull/259)] * Add official gitlab EE docker image to list of known shell spawning images. Thanks @dkerwin! [[#270](https://github.com/draios/falco/pull/270)] * Add keepalived to list of shell spawning binaries. Thanks @dkerwin! [[#269](https://github.com/draios/falco/pull/269)] ## v0.7.0 Released 2017-05-30 ### Major Changes * Update the priorities of falco rules to use a wider range of priorities rather than just ERROR/WARNING. More info on the use of priorities in the ruleset can be found [here](https://github.com/draios/falco/wiki/Falco-Rules#rule-priorities). [[#244](https://github.com/draios/falco/pull/244)] ### Minor Changes None. ### Bug Fixes * Fix typos in various markdown files. Thanks @sublimino! [[#241](https://github.com/draios/falco/pull/241)] ### Rule Changes * Add gitlab-mon as a gitlab binary, which allows it to run shells, etc. Thanks @dkerwin! [[#237](https://github.com/draios/falco/pull/237)] * A new rule Terminal shell in container" that looks for shells spawned in a container with an attached terminal. [[#242](https://github.com/draios/falco/pull/242)] * Fix some FPs related to the sysdig monitor agent. [[#243](https://github.com/draios/falco/pull/243)] * Fix some FPs related to stating containers combined with missed events [[#243](https://github.com/draios/falco/pull/243)] ## v0.6.1 Released 2017-05-15 ### Major Changes None ### Minor Changes * Small changes to token bucket used to throttle falco events [[#234](https://github.com/draios/falco/pull/234)] [[#235](https://github.com/draios/falco/pull/235)] [[#236](https://github.com/draios/falco/pull/236)] [[#238](https://github.com/draios/falco/pull/238)] ### Bug Fixes * Update the falco driver to work with kernel 4.11 [[#829](https://github.com/draios/sysdig/pull/829)] ### Rule Changes * Don't allow apache2 to spawn shells in containers [[#231](https://github.com/draios/falco/issues/231)] [[#232](https://github.com/draios/falco/pull/232)] ## v0.6.0 Released 2017-03-29 ### Major Changes * Add the notion of tagged falco rules. Full documentation for this feature is available on the [wiki](https://github.com/draios/falco/wiki/Falco-Rules#rule-tags). [[#58](https://github.com/draios/falco/issues/58)] [[#59](https://github.com/draios/falco/issues/59)] [[#60](https://github.com/draios/falco/issues/60)] [[#206](https://github.com/draios/falco/pull/206)] * Falco now has its own dedicated kernel module. Previously, it would depend on sysdig being installed and would use sysdig's `sysdig-probe` kernel module. This ensures you can upgrade sysdig and falco without kernel driver compatibility problems. More details on the kernel module and its installation are on the [wiki](https://github.com/draios/falco/wiki/Falco-Kernel-Module). [[#215](https://github.com/draios/falco/issues/215)] [[#223](https://github.com/draios/falco/issues/223)] [[#224](https://github.com/draios/falco/pull/224)] * When providing multiple rules files by specifying `-r' multiple times, make sure that you can override rules/lists/macros. Previously, a list/macro/rule specified in an earlier file could not be overridden in a later file. [[#176](https://github.com/draios/falco/issues/176)] [[#177](https://github.com/draios/falco/pull/177)] * Add example k8s yaml files that show how to run falco as a k8s DaemonSet, and how to run falco-event-generator as a deployment running on one node. [[#222](https://github.com/draios/falco/pull/222)] [[#225](https://github.com/draios/falco/issues/225)] [[#226](https://github.com/draios/falco/pull/226)] * Update third party libraries to address security vulnerabilities. [[#182](https://github.com/draios/falco/pull/182)] * Falco can now be built on OSX. Like sysdig, on OSX it is limited to reading existing trace files. [[#210](https://github.com/draios/falco/pull/210)] ### Minor Changes * Several changes to [falco-event-generator](https://github.com/draios/falco/wiki/Generating-Sample-Events) to improve usability. [[#205](https://github.com/draios/falco/pull/205)] * Switch to a formatter cache provided by sysdig code instead of using our own. [[#212](https://github.com/draios/falco/pull/212)] * Add automated tests that use locally-built docker images. [[#188](https://github.com/draios/falco/issues/188)] ### Bug Fixes * Make sure output strings are not truncated when a given %field expression has a NULL value. [[#180](https://github.com/draios/falco/issues/180)] [[#181](https://github.com/draios/falco/pull/181)] * Allow ASSERTs when running travisci tests. [[#199](https://github.com/draios/falco/pull/199)] * Fix make dependencies for lyaml. [[#204](https://github.com/draios/falco/pull/204)] [[#130](https://github.com/draios/falco/issues/130)] * (This was a change in sysdig, but affected falco). Prevent hangs when traversing malformed parent thread state. [[#208](https://github.com/draios/falco/issues/208)] ### Rule Changes * Add confd as a program that can write files below /etc and fleetctl as a program that can spawn shells. [[#175](https://github.com/draios/falco/pull/175)] * Add [exechealthz](https://github.com/kubernetes/contrib/tree/master/exec-healthz), a k8s liveness checking utility, to the list of shell spawners. [[#190](https://github.com/draios/falco/pull/190)] * Eliminate FPs related to weekly ubuntu cron jobs. [[#192](https://github.com/draios/falco/pull/192)] * Allow shells spawned by ansible, and eliminate FPs when managing machines via ansible. [[#193](https://github.com/draios/falco/pull/193)] [[#196](https://github.com/draios/falco/pull/196)] [[#202](https://github.com/draios/falco/pull/202)] * Eliminate FPs related to use of other security products. Thanks to @juju4 for the useful rule updates. [[#200](https://github.com/draios/falco/pull/200)] * Add additional possible locations for denyhosts, add [PM2](http://pm2.keymetrics.io/) as a shell spawner. [[#202](https://github.com/draios/falco/pull/202)] * Add flanneld as a privileged container, improve grouping for the "x running y" macros, allow denyhosts to spawn shells. [[#207](https://github.com/draios/falco/pull/207)] * Handle systemd changing its name to "(systemd)", add sv (part of [runit](http://smarden.org/runit/)) as a program that can write below /etc, allow writing to all `/dev/tty*` files. [[#209](https://github.com/draios/falco/pull/209)] * Add erl_child_setup as a shell spawner. Thanks to @dkerwin for the useful rule updates. [[#218](https://github.com/draios/falco/pull/218)] [[#221](https://github.com/draios/falco/pull/221)] * Add support for gitlab omnibus containers/pods. Thanks to @dkerwin for the useful rule updates. [[#220](https://github.com/draios/falco/pull/220)] ## v0.5.0 Released 2016-12-22 Starting with this release, we're adding a new section "Rule Changes" devoted to changes to the default ruleset `falco_rules.yaml`. ### Major Changes * Cache event formatting objects so they are not re-created for every falco notification. This can result in significant speedups when the ruleset results in lots of notifications. [[#158](https://github.com/draios/falco/pull/158)] * Falco notifications are now throttled by a token bucket, preventing a flood of notifications when many events match a rule. Controlled by the `outputs, rate` and `outputs, max_burst` options. [[#161](https://github.com/draios/falco/pull/161)] ### Minor Changes * When run from a container, you can provide the environment variable `SYSDIG_SKIP_LOAD` to skip the process of building/loading the kernel module. Thanks @carlsverre for the fix. [[#145](https://github.com/draios/falco/pull/145)] * Fully implement `USE_BUNDLED_DEPS` within CMakeFiles so you can build with external third-party libraries. [[#147](https://github.com/draios/falco/pull/147)] * Improve error messages that result when trying to load a rule with a malformed `output:` attribute [[#150](https://github.com/draios/falco/pull/150)] [[#151](https://github.com/draios/falco/pull/151)] * Add the ability to write event capture statistics to a file via the `-s ` option. [[#155](https://github.com/draios/falco/pull/155)] * New configuration option `log_level` controls the verbosity of falco's logging. [[#160](https://github.com/draios/falco/pull/160)] ### Bug Fixes * Improve compatibility with Sysdig Cloud Agent build [[#148](https://github.com/draios/falco/pull/148)] ### Rule Changes * Add DNF as non-alerting for RPM and package management. Thanks @djcross for the fix. [[#153](https://github.com/draios/falco/pull/153)] * Make `google_containers/kube-proxy` a trusted image, affecting the File Open by Privileged Container/Sensitive Mount by Container rules. [[#159](https://github.com/draios/falco/pull/159)] * Add fail2ban-server as a program that can spawn shells. Thanks @jcoetzee for the fix. [[#168](https://github.com/draios/falco/pull/168)] * Add systemd as a program that can access sensitive files. Thanks @jcoetzee for the fix. [[#169](https://github.com/draios/falco/pull/169)] * Add apt/apt-get as programs that can spawn shells. Thanks @jcoetzee for the fix. [[#170](https://github.com/draios/falco/pull/170)] ## v0.4.0 Released 2016-10-25 As falco depends heavily on sysdig, many changes here were actually made to sysdig and pulled in as a part of the build process. Issues/PRs starting with `sysdig/#XXX` are sysdig changes. ### Major Changes * Improved visibility into containers: ** New filter `container.privileged` to match containers running in privileged mode [[sysdig/#655](https://github.com/draios/sysdig/pull/655)] [[sysdig/#658](https://github.com/draios/sysdig/pull/658)] ** New rules utilizing privileged state [[#121](https://github.com/draios/falco/pull/121)] ** New filters `container.mount*` to match container mount points [[sysdig/#655](https://github.com/draios/sysdig/pull/655)] ** New rules utilizing container mount points [[#120](https://github.com/draios/falco/pull/120)] ** New filter `container.image.id` to match container image id [[sysdig/#661](https://github.com/draios/sysdig/pull/661)] * Improved visibility into orchestration environments: ** New k8s.deployment.* and k8s.rs.* filters to support latest kubernetes features [[sysdg/#dbf9b5c](https://github.com/draios/sysdig/commit/dbf9b5c893d49f945c59684b4effe5700d730973)] ** Rule changes to avoid FPs when monitoring k8s environments [[#138](https://github.com/draios/falco/pull/138)] ** Add new options `-pc`/`-pk`/`-pm`/`-k`/`-m` analogous to sysdig command line options. These options pull metadata information from k8s/mesos servers and adjust default falco notification outputs to contain container/orchestration information when applicable. [[#131](https://github.com/draios/falco/pull/131)] [[#134](https://github.com/draios/falco/pull/134)] * Improved ability to work with file pathnames: ** Added `glob` operator for strings, works as classic shell glob path matcher [[sysdig/#653](https://github.com/draios/sysdig/pull/653)] ** Added `pmatch` operator to efficiently test a subject pathname against a set of target pathnames, to see if the subject is a prefix of any target [[sysdig/#660](https://github.com/draios/sysdig/pull/660)] [[#125](https://github.com/draios/falco/pull/125)] ### Minor Changes * Add an event generator program that simulates suspicious activity that can be detected by falco. This is also available as a docker image [[sysdig/falco-event-generator](https://hub.docker.com/r/sysdig/falco-event-generator/)]. [[#113](https://github.com/draios/falco/pull/113)] [[#132](https://github.com/draios/falco/pull/132)] * Changed rule names to be human readable [[#116](https://github.com/draios/falco/pull/116)] * Add Copyright notice to all source files [[#126](https://github.com/draios/falco/pull/126)] * Changes to docker images to make it easier to massage JSON output for webhooks [[#133](https://github.com/draios/falco/pull/133)] * When run with `-v`, print statistics on the number of events processed and dropped [[#139](https://github.com/draios/falco/pull/139)] * Add ability to write trace files with `-w`. This can be useful to write a trace file in parallel with live event monitoring so you can reproduce it later. [[#140](https://github.com/draios/falco/pull/140)] * All rules can now take an optional `enabled` flag. With `enabled: false`, a rule will not be loaded or run against events. By default all rules are enabled [[#119](https://github.com/draios/falco/pull/119)] ### Bug Fixes * Fixed rule FPs related to docker's `docker`/`dockerd` split in 1.12 [[#112](https://github.com/draios/falco/pull/112)] * Fixed rule FPs related to sysdigcloud agent software [[#141](https://github.com/draios/falco/pull/141)] * Minor changes to node.js example to avoid falco false positives [[#111](https://github.com/draios/falco/pull/111/)] * Fixed regression that broke configurable outputs [[#117](https://github.com/draios/falco/pull/117)]. This was not broken in 0.3.0, just between 0.3.0 and 0.4.0. * Fixed a lua stack leak that could cause problems when matching millions of events against a large set of rules [[#123](https://github.com/draios/falco/pull/123)] * Update docker files to reflect changes to `debian:unstable` docker image [[#124](https://github.com/draios/falco/pull/124)] * Fixed logic for detecting config files to ensure config files in `/etc/falco.yaml` are properly detected [[#135](https://github.com/draios/falco/pull/135)] [[#136](https://github.com/draios/falco/pull/136)] * Don't alert on falco spawning a shell for program output notifications [[#137](https://github.com/draios/falco/pull/137)] ## v0.3.0 Released 2016-08-05 ### Major Changes Significantly improved performance, involving changes in the falco and sysdig repositories: * Reordering a rule condition's operators to put likely-to-fail operators at the beginning and expensive operators at the end. [[#95](https://github.com/draios/falco/pull/95/)] [[#104](https://github.com/draios/falco/pull/104/)] * Adding the ability to perform x in (a, b, c, ...) as a single set membership test instead of individual comparisons between x=a, x=b, etc. [[#624](https://github.com/draios/sysdig/pull/624)] [[#98](https://github.com/draios/falco/pull/98/)] * Avoid unnecessary string manipulations. [[#625](https://github.com/draios/sysdig/pull/625)] * Using `startswith` as a string comparison operator when possible. [[#623](https://github.com/draios/sysdig/pull/623)] * Use `is_open_read`/`is_open_write` when possible instead of searching through open flags. [[#610](https://github.com/draios/sysdig/pull/610)] * Group rules by event type, which allows for an initial filter using event type before going through each rule's condition. [[#627](https://github.com/draios/sysdig/pull/627)] [[#101](https://github.com/draios/falco/pull/101/)] All of these changes result in dramatically reduced CPU usage. Here are some comparisons between 0.2.0 and 0.3.0 for the following workloads: * [Phoronix](http://www.phoronix-test-suite.com/)'s `pts/apache` and `pts/dbench` tests. * Sysdig Cloud Kubernetes Demo: Starts a kubernetes environment using docker with apache and wordpress instances + synthetic workloads. * [Juttle-engine examples](https://github.com/juttle/juttle-engine/blob/master/examples/README.md) : Several elasticsearch, node.js, logstash, mysql, postgres, influxdb instances run under docker-compose. | Workload | 0.2.0 CPU Usage | 0.3.0 CPU Usage | | --------------------------------- | --------------- | --------------- | | pts/apache | 24% | 7% | | pts/dbench | 70% | 5% | | Kubernetes-Demo (Running) | 6% | 2% | | Kubernetes-Demo (During Teardown) | 15% | 3% | | Juttle-examples | 3% | 1% | As a part of these changes, falco now prefers rule conditions that have at least one `evt.type=` operator, at the beginning of the condition, before any negative operators (i.e. `not` or `!=`). If a condition does not have any `evt.type=` operator, falco will log a warning like: ``` Rule no_evttype: warning (no-evttype): proc.name=foo did not contain any evt.type restriction, meaning it will run for all event types. This has a significant performance penalty. Consider adding an evt.type restriction if possible. ``` If a rule has a `evt.type` operator in the later portion of the condition, falco will log a warning like: ``` Rule evttype_not_equals: warning (trailing-evttype): evt.type!=execve does not have all evt.type restrictions at the beginning of the condition, or uses a negative match (i.e. "not"/"!=") for some evt.type restriction. This has a performance penalty, as the rule can not be limited to specific event types. Consider moving all evt.type restrictions to the beginning of the rule and/or replacing negative matches with positive matches if possible. ``` ### Minor Changes * Several sets of rule cleanups to reduce false positives. [[#95](https://github.com/draios/falco/pull/95/)] * Add example of how falco can detect abuse of a badly designed REST API. [[#97](https://github.com/draios/falco/pull/97/)] * Add a new output type "program" that writes a formatted event to a configurable program. Each notification results in one invocation of the program. A common use of this output type would be to send an email for every falco notification. [[#105](https://github.com/draios/falco/pull/105/)] [[#99](https://github.com/draios/falco/issues/99)] * Add the ability to run falco on all events, including events that are flagged with `EF_DROP_FALCO`. (These events are high-volume, low-value events that are ignored by default to improve performance). [[#107](https://github.com/draios/falco/pull/107/)] [[#102](https://github.com/draios/falco/issues/102)] ### Bug Fixes * Add third-party jq library now that sysdig requires it. [[#96](https://github.com/draios/falco/pull/96/)] ## v0.2.0 Released 2016-06-09 For full handling of setsid system calls and session id tracking using `proc.sname`, falco requires a sysdig version >= 0.10.0. ### Major Changes - Add TravisCI regression tests. Testing involves a variety of positive, negative, and informational trace files with both plain and json output. [[#76](https://github.com/draios/falco/pull/76)] [[#83](https://github.com/draios/falco/pull/83)] - Fairly big rework of ruleset to improve coverage, reduce false positives, and handle installation environments effectively [[#83](https://github.com/draios/falco/pull/83)] [[#87](https://github.com/draios/falco/pull/87)] - Not directly a code change, but mentioning it here--the Wiki has now been populated with an initial set of articles, migrating content from the README and adding detail when necessary. [[#90](https://github.com/draios/falco/pull/90)] ### Minor Changes - Improve JSON output to include the rule name, full output string, time, and severity [[#89](https://github.com/draios/falco/pull/89)] ### Bug Fixes - Improve CMake quote handling [[#84](https://github.com/draios/falco/pull/84)] - Remove unnecessary NULL check of a delete [[#85](https://github.com/draios/falco/pull/85)] ## v0.1.0 Released 2016-05-17 ### Major Changes - Initial release. Subsequent releases will have "Major Changes", "Minor Changes", and "Bug Fixes" sections, with links to github issues/pull requests as appropriate. ================================================ FILE: CMakeLists.txt ================================================ # 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. # cmake_minimum_required(VERSION 3.5.1) project(falco) option(USE_BUNDLED_DEPS "Bundle hard to find dependencies into the Falco binary" ON) option(USE_DYNAMIC_LIBELF "Dynamically link libelf" OFF) option(BUILD_WARNINGS_AS_ERRORS "Enable building with -Wextra -Werror flags" OFF) option( MINIMAL_BUILD "Build a minimal version of Falco, containing only the engine and basic input/output (EXPERIMENTAL)" OFF ) option(MUSL_OPTIMIZED_BUILD "Enable if you want a musl optimized build" OFF) option(BUILD_FALCO_UNIT_TESTS "Build falco unit tests" OFF) option(USE_ASAN "Build with AddressSanitizer" OFF) option(USE_UBSAN "Build with UndefinedBehaviorSanitizer" OFF) option(UBSAN_HALT_ON_ERROR "Halt on error when building with UBSan" ON) option(USE_GPERFTOOLS "Build with gperftools CPU profiler support" OFF) option(USE_FRAME_POINTER "Build with frame pointers for accurate profiling" OFF) # Enable frame pointers by default when using gperftools for accurate stack traces if(USE_GPERFTOOLS AND NOT USE_FRAME_POINTER) set(USE_FRAME_POINTER ON CACHE BOOL "Build with frame pointers for accurate profiling" FORCE ) message(STATUS "Enabling USE_FRAME_POINTER since USE_GPERFTOOLS is enabled") endif() # Mem allocators - linux only for now if(NOT WIN32 AND NOT APPLE AND NOT MINIMAL_BUILD AND NOT EMSCRIPTEN ) # If one enables multiple allocators, cmake will fail since all of the allocators cmake modules # create a `malloc` target. option(USE_JEMALLOC "Use jemalloc allocator, linux only" OFF) option(USE_MIMALLOC "Use mimalloc (microsoft) allocator, linux only" OFF) endif() if(WIN32) if(POLICY CMP0091) # Needed for CMAKE_MSVC_RUNTIME_LIBRARY # https://cmake.org/cmake/help/latest/policy/CMP0091.html cmake_policy(SET CMP0091 NEW) endif() set(CPACK_GENERATOR "NSIS") # this needs NSIS installed, and available elseif(APPLE) set(CPACK_GENERATOR "DragNDrop") elseif(EMSCRIPTEN) set(USE_BUNDLED_DEPS ON CACHE BOOL "" FORCE ) set(BUILD_DRIVER OFF CACHE BOOL "" FORCE ) set(ENABLE_DKMS OFF CACHE BOOL "" FORCE ) set(CPACK_GENERATOR TGZ CACHE BOOL "" FORCE ) endif() # Modern BPF is not supported on not Linux systems and in MINIMAL_BUILD if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD) option(BUILD_FALCO_MODERN_BPF "Build modern BPF support for Falco" ON) if(BUILD_FALCO_MODERN_BPF) add_definitions(-DHAS_MODERN_BPF) endif() endif() # We shouldn't need to set this, see https://gitlab.kitware.com/cmake/cmake/-/issues/16419 option(EP_UPDATE_DISCONNECTED "ExternalProject update disconnected" OFF) if(${EP_UPDATE_DISCONNECTED}) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY EP_UPDATE_DISCONNECTED TRUE) endif() # Elapsed time set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") # # TODO(fntlnz, leodido): add a flag to enable this # Make flag for parallel processing include(ProcessorCount) ProcessorCount(PROCESSOR_COUNT) if(NOT PROCESSOR_COUNT EQUAL 0) set(PROCESSOUR_COUNT_MAKE_FLAG -j${PROCESSOR_COUNT}) endif() # Custom CMake modules list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") # GNU standard installation directories' definitions include(GNUInstallDirs) if(NOT DEFINED FALCO_ETC_DIR) set(FALCO_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/falco") endif() # This will be used to print the architecture for which Falco is compiled. if(EMSCRIPTEN) set(FALCO_TARGET_ARCH "wasm") else() set(FALCO_TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR}) endif() include(CompilerFlags) set(PACKAGE_NAME "falco") set(DRIVER_NAME "falco") set(DRIVER_DEVICE_NAME "falco") set(DRIVERS_REPO "https://download.falco.org/driver") if(NOT DEFINED FALCO_COMPONENT_NAME) set(FALCO_COMPONENT_NAME "${CMAKE_PROJECT_NAME}") endif() if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set(CMAKE_INSTALL_PREFIX /usr CACHE PATH "Default install path" FORCE ) endif() set(CMD_MAKE make) include(ExternalProject) include(cxxopts) # libs include(falcosecurity-libs) # compute FALCO_VERSION (depends on libs) include(falco-version) # Mem allocators - linux only for now if(NOT WIN32 AND NOT APPLE AND NOT MINIMAL_BUILD AND NOT EMSCRIPTEN ) if(USE_JEMALLOC) include(jemalloc) endif() if(USE_MIMALLOC) include(mimalloc) endif() message(STATUS "Will use mem allocator library: ${MALLOC_LIB}") endif() # nlohmann-json include(njson) # yaml-cpp include(yaml-cpp) if(NOT WIN32 AND NOT APPLE AND NOT MINIMAL_BUILD AND NOT EMSCRIPTEN ) # OpenSSL include(openssl) # libcurl include(curl) # todo(jasondellaluce,rohith-raju): support webserver for non-linux builds too cpp-httlib include(cpp-httplib) endif() # One TBB if(NOT EMSCRIPTEN) include(tbb) endif() include(zlib) include(valijson) # CPU Profiling with gperftools if(USE_GPERFTOOLS) include(gperftools) endif() if(NOT MINIMAL_BUILD) if(NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN ) include(cares) endif() endif() # Installation if(WIN32) set(FALCO_INSTALL_CONF_FILE "%PROGRAMFILES%/${PACKAGE_NAME}-${FALCO_VERSION}/etc/falco/falco.yaml" ) install( FILES falco.yaml DESTINATION etc/falco/ COMPONENT "${FALCO_COMPONENT_NAME}" ) install( DIRECTORY DESTINATION etc/falco/config.d COMPONENT "${FALCO_COMPONENT_NAME}" ) elseif(APPLE) set(FALCO_INSTALL_CONF_FILE "/etc/falco/falco.yaml") install( FILES falco.yaml DESTINATION etc/falco/ COMPONENT "${FALCO_COMPONENT_NAME}" ) install( DIRECTORY DESTINATION etc/falco/config.d COMPONENT "${FALCO_COMPONENT_NAME}" ) else() set(FALCO_INSTALL_CONF_FILE "/etc/falco/falco.yaml") install( FILES falco.yaml DESTINATION "${FALCO_ETC_DIR}" COMPONENT "${FALCO_COMPONENT_NAME}" ) install( DIRECTORY DESTINATION "${FALCO_ETC_DIR}/config.d" COMPONENT "${FALCO_COMPONENT_NAME}" ) endif() if(NOT MINIMAL_BUILD) # Coverage include(Coverage) endif() # Rules include(rules) # Clang format add_custom_target(format COMMAND clang-format --style=file -i # $ COMMENT "Formatting ..." VERBATIM) # Static analysis include(static-analysis) # Shared build variables set(FALCO_SHARE_DIR share/falco) set(FALCO_ABSOLUTE_SHARE_DIR "${CMAKE_INSTALL_PREFIX}/${FALCO_SHARE_DIR}") set(FALCO_BIN_DIR bin) add_subdirectory(scripts) add_subdirectory(userspace/engine) add_subdirectory(userspace/falco) if(NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT MUSL_OPTIMIZED_BUILD ) include(falcoctl) set(CONTAINER_VERSION "0.6.1") if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64") set(CONTAINER_HASH "008989992ed1f31b3ffb94ba6b64ca5a8e2f91611a10c9d6213c5c0a499d0679") else() # arm64 set(CONTAINER_HASH "f90a700b4c2b411b23e7cc461b61a316b242994aad853c3e6baf12481fb6f6c9") endif() include(container_plugin) # Generate a binary_dir/falco.yaml that automatically enables the plugin to be used for local # testing. configure_file(${CMAKE_CURRENT_SOURCE_DIR}/falco.yaml ${CMAKE_BINARY_DIR} COPYONLY) # The custom target configures the plugin and set its path add_custom_target( container COMMAND sed -i 's,^load_plugins: .*,load_plugins: [container],g' ${CMAKE_BINARY_DIR}/falco.yaml COMMAND sed -i 's,library_path: libcontainer.so,library_path: ${CONTAINER_LIBRARY},g' ${CMAKE_BINARY_DIR}/falco.yaml DEPENDS container_plugin ) # Let `make falco` also download container plugin add_dependencies(falco container) # Install the plugin install( FILES "${CONTAINER_LIBRARY}" DESTINATION "${FALCO_ABSOLUTE_SHARE_DIR}/plugins" COMPONENT "${FALCO_COMPONENT_NAME}" ) # Install additional config override file to enable the container plugin install( FILES "${PROJECT_SOURCE_DIR}/config/falco.container_plugin.yaml" DESTINATION "${FALCO_ETC_DIR}/config.d" COMPONENT "${FALCO_COMPONENT_NAME}" ) endif() # Packages configuration include(CPackConfig) if(BUILD_FALCO_UNIT_TESTS) add_subdirectory(unit_tests) endif() ================================================ FILE: COPYING ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2019 The Falco Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Contributing.md ================================================ # How to contribute ## Enforce coding style 💻 ### Introduction This document introduces the coding style that will be applied in this repository. This coding style involves all the following files: `.c`, `.h`, `.cpp`, `.cmake`, `CMakeLists.txt`. To enforce it we rely on two main tools: 1. `clang-format` version `18.1.8`. 2. `cmake-format` version `0.6.13`. > __Please note__: tools versions are important! Different versions will enforce slightly different changes on the code. For example `clang-format-18` will produce a slightly different output respect to `clang-format-17` always respecting the imposed style. The coding style is expressed through the 2 configuration file that you find in this repo: `.clang-format`, `.cmake-format.json`. ### Enforce the style locally There are many ways to enforce the style locally, here we will describe two of them: 1. Use `pre-commit` framework. 2. Use the repo `Makefile`. #### 1.Pre-commit framework (suggested if you don't have the 2 tools already installed on your machine) The `pre-commit` framework allows you to automatically install different `git-hooks` that will run at every new commit. More precisely, if you use the `.pre-commit-config.yaml` in this repo you will install 3 different hooks: 1. The `clang-format` hook: this is a `pre-commit` git hook that runs `clang-format` on your staged changes. 2. The `cmake-format` hook: this is a `pre-commit` git hook that runs `cmake-format` on your staged changes. 3. The `DCO signed-off` hook: this is a `pre-commit-msg` git hook that adds the `DCO` on your commit if not present. This hook is not strictly related to the coding style so we will talk about it in a separate section: [Add DCO signed-off to your commits](#add-dco-signed-off-to-your-commits-). Now let's see what we need to use `pre-commit` framework. ##### Step 1 Install `pre-commit` framework following the [official documentation](https://pre-commit.com/#installation). > __Please note__: you have to follow only the "Installation" section. ##### Step 2 Once you have installed `pre-commit`, you don't need to install anything else! This is the good point of using a framework like `pre-commit`, all the tools necessary to format your code will be directly managed by the framework. But in order to be ready, you need to install the git hooks in your local repo. This simple command allows you to install the two `pre-commit` git hooks, `clang-format` and `cmake-format`. ```bash pre-commit install --install-hooks --hook-type pre-commit --overwrite ``` If you want to install also the `pre-commit-msg` git hook for the DCO you have to type the following command, but be sure to have configured all you need as said in the [dedicated section](#add-dco-signed-off-to-your-commits-) ```bash pre-commit install --install-hooks --hook-type prepare-commit-msg --overwrite ``` You have done, at every new commit, this hook will check that your patch respects the coding style of this repo! If you want to detach the git hooks, you can simply type: ```bash pre-commit uninstall --hook-type prepare-commit-msg pre-commit uninstall --hook-type pre-commit ``` #### 2.Makefile ##### Step 1 In order to use the repo `Makefile`, you need to install on your local machine the two aforementioned tools: __clang-format v18.1.8__ One of the easiest ways to install `clang-format` could be directly downloading its static binary from [here](https://github.com/muttleyxd/clang-tools-static-binaries). There are other ways for example you can download the package for your distro or you can also build it from sources. __cmake-format v0.6.13__ To install `cmake-format` you can follow the official documentation [here](https://cmake-format.readthedocs.io/en/latest/installation.html). > __NOTE__: Please check the versions of the two tool with `clang-format --version` and `cmake-format --version`. ##### Step 2 Once you have installed the __right__ versions of the 2 tools, you can simply type `make format-all` from the root directory of the project to format all your code according to the coding style. Remember to do that before submitting a new patch upstream! 😁 #### Other solutions Obviously, you can also install the 2 tools locally and enable some extension of your favorite IDE (like `VScode`) to format your code every time you save your files! ## Add DCO signed-off to your commits 🔏 ### Introduction Another requirement for contributing to the `falco` repository, is applying the [DCO](https://cert-manager.io/docs/contributing/sign-off/) to every commit you want to push upstream. Before doing this you have to configure your git user `name` and `email` if you haven't already done it. To check your actual `name` and `email` type: ```bash git config --get user.name git config --get user.email ``` If they are correct you have done, otherwise, you have to set them: ```bash git config user.name git config user.email ``` >__Please note__: If you have problems in doing this you can read the full documentation [here](https://docs.github.com/en/get-started/getting-started-with-git/setting-your-username-in-git). ### Enforce the DCO locally Now you are ready to sign your commits! You have two main ways to do this: 1. Manually with `git` tool. 2. Use the `pre-commit-msg` hook quoted before. ### Manually To do this you just need to remember the `-s` while performing your commits: ```bash git commit -s ``` or with the inline message: ```bash git commit -s -m "my first commit" ``` ### Use `pre-commit` hook Here if you have already added the hook in the [previous section](#step-2), you have to do nothing otherwise you have to simply install the DCO hook with: ```bash pre-commit install --install-hooks --hook-type prepare-commit-msg --overwrite ``` And you have done! Now you don't have to remember the `-s` option every time you commit something, the DCO hook will automatically add the DCO if you forget it! 😄 ## Some best practices 📏 ### Class variables To know whether a variable belongs to a `class` or a `function`, we start member variables with `m_`. Example: ```c public int32_t m_counter; ``` ### Global variables To know whether the variable is global or not, we start globals with `g_`. Example: ```c int g_nplugins; ``` ### Capitalization The naming convention is camel-cased "Unix" style, i.e. always lower case. Words are separated by underscores. Example: ```c int32_t g_global_bean_counter; int32_t count_beans(); ``` and not, ```c int32_t GlobalBeanCounter; int32_t CountBeans(); ``` ### Packed Structures Packed structures should use the GCC and MSVC-style supported `pragma`: Example: ```c #pragma pack(push,1) struct frame_control { struct fields.... }; #pragma pack(pop) ``` ### 64-bit constants Put an `LL` at the end of your `64-bit` constants. Without the `LL`, some platform compilers try to interpret the constant on the right-hand side as a `long integer` instead of a `long long` and this could lead to an error at building time. Example: ```c x=0X00FF00000000000LL ``` ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2019 The Falco Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Makefile ================================================ # # Copyright (C) 2024 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. # # mofidy the following variables to match your paths CLANG_FORMAT_EXE ?= clang-format CLANG_FORMAT_VERSION = "$(shell ${CLANG_FORMAT_EXE} --version | grep -o '[0-9]*\.[0-9]*\.[0-9]*')" CLANG_FORMAT_DESIRED_VERSION ="18.1.8" CMAKE_FORMAT_EXE ?= cmake-format CMAKE_FORMAT_VERSION = "$(shell ${CMAKE_FORMAT_EXE} --version | grep -o '[0-9]*\.[0-9]*\.[0-9]*')" CMAKE_FORMAT_DESIRED_VERSION = "0.6.13" PROJECT_ROOT_DIR = $(shell git rev-parse --show-toplevel) ###################### # Clang-format # ###################### .PHONY: clang-format-install clang-format-install: ifeq (, $(shell ${CLANG_FORMAT_EXE} --version)) @echo "${CLANG_FORMAT_EXE} is not installed. Please read the 'coding style' doc to get more info." @exit 1 endif ifneq ($(CLANG_FORMAT_VERSION), $(CLANG_FORMAT_DESIRED_VERSION)) @echo "${CLANG_FORMAT_EXE} version is not '${CLANG_FORMAT_DESIRED_VERSION}'. Actual version is '${CLANG_FORMAT_VERSION}'" @exit 1 endif .PHONY: format-clang format-clang: clang-format-install git ls-files --directory ${PROJECT_ROOT_DIR} | grep -E '\.(cpp|h|c)$$' | xargs ${CLANG_FORMAT_EXE} -Werror --style=file:${PROJECT_ROOT_DIR}/.clang-format -i .PHONY: check-clang check-clang: clang-format-install git ls-files --directory ${PROJECT_ROOT_DIR} | grep -E '\.(cpp|h|c)$$' | xargs ${CLANG_FORMAT_EXE} -Werror --style=file:${PROJECT_ROOT_DIR}/.clang-format -n ###################### # Cmake-format # ###################### .PHONY: cmake-format-install cmake-format-install: ifeq (, $(shell ${CMAKE_FORMAT_EXE} --version)) @echo "${CMAKE_FORMAT_EXE} is not installed. Please read the 'coding style' doc to get more info." @exit 1 endif ifneq ($(CMAKE_FORMAT_VERSION), $(CMAKE_FORMAT_DESIRED_VERSION)) @echo "${CMAKE_FORMAT_EXE} version is not '${CMAKE_FORMAT_DESIRED_VERSION}'. Actual version is '${CMAKE_FORMAT_VERSION}'" @exit 1 endif .PHONY: format-cmake format-cmake: cmake-format-install git ls-files --directory ${PROJECT_ROOT_DIR} | grep -E '\.(cmake)$$|CMakeLists.txt$$' | xargs ${CMAKE_FORMAT_EXE} --config-files ${PROJECT_ROOT_DIR}/.cmake-format.json -i .PHONY: check-cmake check-cmake: cmake-format-install git ls-files --directory ${PROJECT_ROOT_DIR} | grep -E '\.(cmake)$$|CMakeLists.txt$$' | xargs ${CMAKE_FORMAT_EXE} --config-files ${PROJECT_ROOT_DIR}/.cmake-format.json --check # Add new formatters here... .PHONY: format-all format-all: format-clang format-cmake .PHONY: check-all check-all: check-clang check-cmake ================================================ FILE: OWNERS ================================================ approvers: - mstemm - leogr - jasondellaluce - fededp - andreagit97 - LucaGuerra - sgaist - ekoops reviewers: - kaizhe - irozzo-1A emeritus_approvers: - fntlnz - kris-nova - leodido - incertum ================================================ FILE: README.md ================================================ # Falco [![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 Scorecard](https://img.shields.io/ossf-scorecard/github.com/falcosecurity/falco?label=openssf%20scorecard&style=for-the-badge)](https://scorecard.dev/viewer/?uri=github.com/falcosecurity/falco) [![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](https://falco.org/img/brand/falco-horizontal-color.svg)](https://falco.org) [Falco](https://falco.org/) is a cloud native runtime security tool for Linux operating systems. It is designed to detect and alert on abnormal behavior and potential security threats in real-time. At its core, Falco is a kernel monitoring and detection agent that observes events, such as syscalls, based on custom rules. Falco can enhance these events by integrating metadata from the container runtime and Kubernetes. The collected events can be analyzed off-host in SIEM or data lake systems. Falco, originally created by [Sysdig](https://sysdig.com), is a **graduated project** under the [Cloud Native Computing Foundation](https://cncf.io) (CNCF) used in production by various [organisations](https://github.com/falcosecurity/falco/blob/master/ADOPTERS.md). For detailed technical information and insights into the cyber threats that Falco can detect, visit the official [Falco](https://falco.org/) website. For comprehensive information on the latest updates and changes to the project, please refer to the [Change Log](CHANGELOG.md). ## The Falco Project The Falco Project codebase is maintained under the [falcosecurity GitHub organization](https://github.com/falcosecurity). The primary repository, [falcosecurity/falco](https://github.com/falcosecurity/falco), holds the source code for the Falco binary, while other sub-projects are hosted in dedicated repositories. This approach of isolating components into specialized repositories enhances modularity and focused development. Notable [core repositories](https://github.com/falcosecurity/evolution?tab=readme-ov-file#core) include: - [falcosecurity/libs](https://github.com/falcosecurity/libs): This repository hosts Falco's core libraries, which constitute the majority of the binary’s source code and provide essential features, such as kernel drivers. - [falcosecurity/rules](https://github.com/falcosecurity/rules): It contains the official ruleset for Falco, offering pre-defined detection rules for various security threats and abnormal behaviors. - [falcosecurity/plugins](https://github.com/falcosecurity/plugins): This repository supports integration with external services through plugins that extend Falco's capabilities beyond syscalls and container events, with plans for evolving specialized functionalities in future releases. - [falcosecurity/falcoctl](https://github.com/falcosecurity/falcoctl): A command-line utility designed for managing and interacting with Falco. - [falcosecurity/charts](https://github.com/falcosecurity/charts): This repository provides Helm charts for deploying Falco and its ecosystem, simplifying the installation and management process. For further insights into our repositories and additional details about our governance model, please visit the official hub of The Falco Project: [falcosecurity/evolution](https://github.com/falcosecurity/evolution). ## Getting Started with Falco If you're new to Falco, begin your journey with our [Getting Started](https://falco.org/docs/getting-started/) guide. For production deployments, please refer to our comprehensive [Setup](https://falco.org/docs/setup/) documentation. As final recommendations before deploying Falco, verify environment compatibility, define your detection goals, optimize performance, choose the appropriate build, and plan for SIEM or data lake integration to ensure effective incident response. ### Demo Environment A demo environment is provided via a docker-compose file that can be started on a docker host which includes falco, falcosidekick, falcosidekick-ui and its required redis database. For more information see the [docker-compose section](docker/docker-compose/) ## Join the Community To get involved with the Falco Project please visit the [Community](https://github.com/falcosecurity/community) repository to find more information and ways to get involved. If you have any questions about Falco or contributing, do not hesitate to file an issue or contact the Falco maintainers and community members for assistance. How to reach out? - Join the [#falco](https://kubernetes.slack.com/messages/falco) channel on the [Kubernetes Slack](https://slack.k8s.io). - Join the [Falco mailing list](https://lists.cncf.io/g/cncf-falco-dev). - File an [issue](https://github.com/falcosecurity/falco/issues) or make feature requests. ## Commitment to Falco's Own Security Full reports of various security audits can be found [here](./audits/). In addition, you can refer to the [falco](https://github.com/falcosecurity/falco/security) and [libs](https://github.com/falcosecurity/libs/security) security sections for detailed updates on security advisories and policies. To report security vulnerabilities, please follow the community process outlined in the documentation found [here](https://github.com/falcosecurity/.github/blob/main/SECURITY.md). ## Building For comprehensive, step-by-step instructions on building Falco from source, please refer to the [official documentation](https://falco.org/docs/developer-guide/source/). ## Testing
Expand Testing Instructions Falco's [Build Falco from source](https://falco.org/docs/developer-guide/source/) is the go-to resource to understand how to build Falco from source. In addition, the [falcosecurity/libs](https://github.com/falcosecurity/libs) repository offers additional valuable information about tests and debugging of Falco's underlying libraries and kernel drivers. Here's an example of a `cmake` command that will enable everything you need for all unit tests of this repository: ```bash cmake \ -DUSE_BUNDLED_DEPS=ON \ -DBUILD_DRIVER=ON \ -DBUILD_FALCO_MODERN_BPF=ON \ -DCREATE_TEST_TARGETS=ON \ -DBUILD_FALCO_UNIT_TESTS=ON ..; ``` Build and run the unit test suite: ```bash nproc=$(grep processor /proc/cpuinfo | tail -n 1 | awk '{print $3}'); make -j$(($nproc-1)) falco_unit_tests; # Run the tests sudo ./unit_tests/falco_unit_tests; ``` Optionally, build the driver of your choice and test run the Falco binary to perform manual tests. Lastly, The Falco Project has moved its Falco regression tests to [falcosecurity/testing](https://github.com/falcosecurity/testing).

## How to Contribute Please refer to the [Contributing](https://github.com/falcosecurity/.github/blob/main/CONTRIBUTING.md) guide and the [Code of Conduct](https://github.com/falcosecurity/evolution/blob/main/CODE_OF_CONDUCT.md) for more information on how to contribute. ## FAQs ### Why is Falco in C++ rather than Go or {language}?
Expand Information 1. The first lines of code at the base of Falco were written some time ago, where Go didn't yet have the same level of maturity and adoption as today. 2. The Falco execution model is sequential and mono-thread due to the statefulness requirements of the tool, and so most of the concurrency-related selling points of the Go runtime would not be leveraged at all. 3. The Falco code deals with very low-level programming in many places, and we all know that interfacing Go with C is possible but brings tons of complexity and tradeoffs to the table. 4. As a security tool meant to consume a crazy high throughput of events per second, Falco needs to squeeze performance in all hot paths at runtime and requires deep control on memory allocation, which the Go runtime can't provide (there's also garbage collection involved). 5. Although Go didn't suit the engineering requirements of the core of Falco, we still thought that it could be a good candidate for writing Falco extensions through the plugin system. This is the main reason we gave special attention and high priority to the development of the plugin-sdk-go. 6. Go is not a requirement for having statically-linked binaries. In fact, we provide fully-static Falco builds since few years. The only issue with those is that the plugin system can't be supported with the current dynamic library model we currently have. 7. The plugin system has been envisioned to support multiple languages, so on our end maintaining a C-compatible codebase is the best strategy to ensure maximum cross-language compatibility. 8. In general, plugins have GLIBC requirements/dependencies because they have low-level C bindings required for dynamic loading. A potential solution for the future could be to also support plugin to be statically-linked at compilation time and so released as bundled in the Falco binary. Although no work started yet in this direction, this would solve most issues you reported and would provide a totally-static binary too. Of course, this would not be compatible with dynamic loading anymore, but it may be a viable solution for our static-build flavor of Falco. 9. Memory safety is definitely a concern and we try our best to keep an high level of quality even though C++ is quite error prone. For instance, we try to use smart pointers whenever possible, we build the libraries with an address sanitizer in our CI, we run Falco through Valgrind before each release, and have ways to stress-test it to detect performance regressions or weird memory usage (e.g. https://github.com/falcosecurity/event-generator). On top of that, we also have third parties auditing the codebase by time to time. None of this make a perfect safety standpoint of course, but we try to maximize our odds. Go would definitely make our life easier from this perspective, however the tradeoffs never made it worth it so far due to the points above. 10. The C++ codebase of falcosecurity/libs, which is at the core of Falco, is quite large and complex. Porting all that code to another language would be a major effort requiring lots of development resource and with an high chance of failure and regression. As such, our approach so far has been to choose refactors and code polishing instead, up until we'll reach an optimal level of stability, quality, and modularity, on that portion of code. This would allow further developments to be smoother and more feasibile in the future.

### What's next for Falco? Stay updated with Falco's evolving capabilities by exploring the [Falco Roadmap](https://github.com/orgs/falcosecurity/projects/5), which provides insights into the features currently under development and planned for future releases. ## License Falco is licensed to you under the [Apache 2.0](./COPYING) open source license. ## Resources - [Governance](https://github.com/falcosecurity/evolution/blob/main/GOVERNANCE.md) - [Code Of Conduct](https://github.com/falcosecurity/evolution/blob/main/CODE_OF_CONDUCT.md) - [Maintainers Guidelines](https://github.com/falcosecurity/evolution/blob/main/MAINTAINERS_GUIDELINES.md) - [Maintainers List](https://github.com/falcosecurity/evolution/blob/main/MAINTAINERS.md) - [Repositories Guidelines](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md) - [Repositories List](https://github.com/falcosecurity/evolution/blob/main/README.md#repositories) - [Adopters List](https://github.com/falcosecurity/falco/blob/master/ADOPTERS.md) - [Release Process](RELEASE.md) - [Setup documentation](https://falco.org/docs/setup/) - [Troubleshooting](https://falco.org/docs/troubleshooting/) ================================================ FILE: RELEASE.md ================================================ # Falco Release Process ## Overview This document provides the process to create a new Falco release. In addition, it provides information about the versioning of the Falco components. At a high level each Falco release consists of the following main components: - Falco binary (userspace), includes `modern_bpf` driver object code (kernel space) starting with Falco 0.34.x releases - Falco kernel driver object files, separate artifacts for `kmod` and `bpf` drivers, not applicable for `modern_bpf` driver (kernel space) - Option 1: Kernel module (`.ko` files) - Option 2: eBPF (`.o` files) - Falco config and rules `.yaml` files (userspace) - Falco plugins (userspace - optional) > Note: Starting with Falco 0.34.x releases, the Falco userspace binary includes the `modern_bpf` driver object code during the linking process. This integration is made possible by the CO-RE (Compile Once - Run Everywhere) feature of the modern BPF driver. CO-RE allows the driver to function on kernels that have backported BTF (BPF Type Format) support or have a kernel version >= 5.8. For the older `kmod` and `bpf` drivers, separate artifacts are released for the kernel space. This is because these drivers need to be explicitly compiled for the specific kernel release, using the exact kernel headers. This approach ensures that Falco can support a wide range of environments, including multiple kernel versions, distributions, and architectures. (see `libs` [driver - kernel version support matrix](https://github.com/falcosecurity/libs#drivers-officially-supported-architectures)). The Falco Project manages the release of both the Falco userspace binary and pre-compiled Falco kernel drivers for the most popular kernel versions and distros. The build and publish process is managed by the [test-infra](https://github.com/falcosecurity/test-infra) repo. The Falco userspace executable includes bundled dependencies, so that it can be run from anywhere. Falco publishes all sources, enabling users to audit the project's integrity and build kernel drivers for custom or unsupported kernels/distributions, specifically for non-modern BPF drivers (see [driverkit](https://github.com/falcosecurity/driverkit) for more information). Finally, the release process follows a transparent process described in more detail in the following sections and the official [Falco guide and documentation](https://falco.org/) provide rich information around building, installing and using Falco. ### Falco Binaries, Rules and Sources Artifacts - Quick Links The Falco project publishes all sources and the Falco userspace binaries as GitHub releases. - [Falco Releases](https://github.com/falcosecurity/falco/releases) - `tgz`, `rpm` and `deb` Falco binary packages (contains sources, including driver sources, Falco rules as well as k8saudit and cloudtrail plugins) - `tgz`, `zip` source code - [Libs Releases](https://github.com/falcosecurity/libs/releases) - `tgz`, `zip` source code - [Driver Releases](https://github.com/falcosecurity/libs/releases), marked with `+driver` [build metadata](https://semver.org/). - `tgz`, `zip` source code - [Falco Rules Releases](https://github.com/falcosecurity/rules/releases) - `tgz`, `zip` source code, each ruleset is tagged separately in a mono-repo fashion, see the [rules release guidelines](https://github.com/falcosecurity/rules/blob/main/RELEASE.md) Alternatively Falco binaries or plugins can be downloaded from the Falco Artifacts repo. - [Falco Artifacts Repo Packages Root](https://download.falco.org/?prefix=packages/) - [Falco Artifacts Repo Plugins Root](https://download.falco.org/?prefix=plugins/) ### Falco Drivers Artifacts Repo - Quick Links > Note: This section specifically applies to non-modern BPF drivers. The Falco Project publishes all drivers for each release for popular kernel versions / distros and `x86_64` and `aarch64` architectures to the Falco project's managed Artifacts repo. The Artifacts repo follows standard directory level conventions. The respective driver object file is prefixed by distro and named / versioned by kernel release - `$(uname -r)`. Pre-compiled drivers are released with a [best effort](https://github.com/falcosecurity/falco/blob/master/proposals/20200818-artifacts-storage.md#notice) notice. This is because gcc (`kmod`) sometimes fails to build the artifacts for a specific kernel version. More details around driver versioning and driver compatibility are provided in the [Falco Components Versioning](#falco-components-versioning) section. Short preview: If you use the standard Falco setup leveraging driver-loader, [driver-loader script](https://github.com/falcosecurity/falco/blob/master/scripts/falco-driver-loader) will fetch the kernel space artifact (object file) corresponding to the default `DRIVER_VERSION` Falco was shipped with. - [Falco Artifacts Repo Drivers Root](https://download.falco.org/?prefix=driver/) - Kernel module (`.ko` files) - all under same driver version directory ### Timeline Falco follows a release schedule of three times per year, with releases expected at the end of January, May, and September. Hotfix releases are issued as needed. Changes and new features are organized into [milestones](https://github.com/falcosecurity/falco/milestones). The milestone corresponding to the next version represents the content that will be included in the upcoming release. ### Procedures The release process is mostly automated, requiring only a few manual steps to initiate and complete. Moreover, we assign owners for each release (typically pairing a new person with an experienced one). Assignees and due dates for releases are proposed during the [community call](https://github.com/falcosecurity/community). At a high level each Falco release needs to follow a pre-determined sequencing of releases and build order: - [1 - 3] `libs` (+ `driver`) and `plugins` components releases - [4] Falco driver pre-compiled object files push to Falco's Artifacts repo - [5] Falco userspace binary release Assignees are responsible for creating a Falco GitHub issue to track the release tasks and monitor the progress of the release. This issue serves as a central point for communication and provides updates on the release dates. You can refer to the [Falco v0.35 release](https://github.com/falcosecurity/falco/issues/2554) or [Libs Release (0.11.0+5.0.1+driver)](https://github.com/falcosecurity/libs/issues/1092) issues as examples/templates for creating the release issue. Finally, on the proposed due date, the assignees for the upcoming release proceed with the processes described below. ## Pre-Release Checklist Before proceeding with the release, make sure to complete the following preparatory steps, which can be easily done using the GitHub UI: ### 1. Release notes - Find the previous release date (`YYYY-MM-DD`) by looking at the [Falco releases](https://github.com/falcosecurity/falco/releases) - Double-check, by using the following filters, if there is any closed issue/merge PR with no milestone assigned: - `is:issue state:closed no:milestone closed:>YYYY-MM-DD` [filter](https://github.com/falcosecurity/falco/issues?q=is%3Aissue%20state%3Aclosed%20no%3Amilestone%20closed%3A%3EYYYY-MM-DD) - `is:pr is:merged no:milestone closed:>YYYY-MM-DD` [filter](https://github.com/falcosecurity/falco/pulls?q=is%3Apr+is%3Amerged+no%3Amilestone+closed%3A%3EYYYY-MM-DD) - Assign any issue/PR identified in the previous point to the milestone corresponding to the currently undergoing release - Check the release note block of every PR matching the `is:pr is:merged milestone:M.m.p` [filter](https://github.com/falcosecurity/falco/pulls?q=is%3Apr+is%3Amerged+milestone%3AM.m.p) - Ensure the release note block follows the [commit convention](https://github.com/falcosecurity/.github/blob/master/CONTRIBUTING.md#commit-convention), otherwise fix its content - If the PR has no milestone, assign it to the milestone currently undergoing release ### 2. Milestones - Move the [tasks not completed](https://github.com/falcosecurity/falco/pulls?q=is%3Apr+is%3Aopen) to a new minor milestone ### 3. Release branch Assuming we are releasing a non-patch version (like: Falco 0.34.0), a new release branch needs to be created. Its naming will be `release/M.m.x`; for example: `release/0.34.x`. The same branch will then be used for any eventual cherry pick for patch releases. For patch releases, instead, the `release/M.m.x` branch should already be in place; no more steps are needed. Double-check that any PR that should be part of the tag has been cherry-picked from master! ### 4. Release PR The release PR is meant to be made against the respective `release/M.m.x` branch, **then cherry-picked on master**. - Double-check if any hard-coded version number is present in the code, it should be not present anywhere: - If any, manually correct it then open an issue to automate version number bumping later - Versions table in the `README.md` updates itself automatically - Generate the change log using [rn2md](https://github.com/leodido/rn2md): - Execute `rn2md -r falcosecurity/falco -m M.m.p` - In case `rn2md` emits error try to generate an GitHub OAuth access token and provide it with the `-t` flag - Add the latest changes on top the previous `CHANGELOG.md` - Submit a PR with the above modifications - Await PR approval - Close the completed milestone as soon as the PR is merged into the release branch - Cherry pick the PR on master too ## Publishing Pre-Releases (RCs and tagged development versions) Core maintainers and/or the release manager can decide to publish pre-releases at any time before the final release is live for development and testing purposes. The pre-release must be associated with a newly created tag. The tag is intended to be created while drafting the new pre-release through the GitHub form (this is indeed the only way to correctly associate the tag with a target branch; more on this below). The pre-release tag must be formatted as `M.m.p-r`, where `r` is the pre-release version information (e.g. `0.35.0-rc1`). To create both pre-release tag and pre-release, do the following: - [Draft a new release](https://github.com/falcosecurity/falco/releases/new) - Use `M.m.p-r` both as tag version and release title - Associate `release/M.m.x` as "target branch" for the new tag - Check the "Set as a pre-release" checkbox and make sure "Set as the latest release" is unchecked - It is recommended to add a brief description so that other contributors will understand the reason why the prerelease is published - Publish the prerelease! - The release pipeline will start automatically. Packages will be uploaded to the `-dev` bucket and container images will be tagged with the specified tag In order to check the status of the release pipeline click on the [GitHub Actions tab](https://github.com/falcosecurity/falco/actions?query=event%3Arelease) in the Falco repository and filter by release. ## Release Assume `M.m.p` is the new version. ### 1. Create the release with GitHub - [Draft a new release](https://github.com/falcosecurity/falco/releases/new) - Use `M.m.p` both as tag version and release title - Associate `release/M.m.x` as "target branch" for the new tag - Do NOT fill body, since it will be autogenerated by the [github release workflow](.github/workflows/release.yaml) - Publish the release! - The release pipeline will start automatically upon publication and all packages and container images will be uploaded to the stable repositories. In order to check the status of the release pipeline click on the [GitHub Actions tab](https://github.com/falcosecurity/falco/actions?query=event%3Arelease) in the Falco repository and filter by release. ### 2. Update the meeting notes For each release we archive the meeting notes in git for historical purposes. - The notes from the Falco meetings can be [found here](https://hackmd.io/3qYPnZPUQLGKCzR14va_qg). - Note: There may be other notes from working groups that can optionally be added as well as needed. - Add the entire content of the document to a new file in [github.com/falcosecurity/community/tree/main/meeting-notes](https://github.com/falcosecurity/community/tree/main/meeting-notes) as a new file labeled `release-M.m.p.md` - Open up a pull request with the new change. ## Post-Release tasks Announce the new release to the world! - IFF the ongoing release introduces a **new minor version**, [archive a snapshot of the Falco website](https://github.com/falcosecurity/falco-website/blob/master/release.md#documentation-versioning) - Publish a blog on [Falco website](https://github.com/falcosecurity/falco-website) ([example](https://github.com/falcosecurity/falco-website/blob/master/content/en/blog/falco-0-28-1.md)) - Send an announcement to cncf-falco-dev@lists.cncf.io (plain text, please) - Let folks in the slack #falco channel know about a new release came out ## Falco Components Versioning This section provides more details around the versioning of the components that make up Falco's core. It can also be a useful guide for the uninitiated to be more informed about Falco's source. Because `libs` makes up the greater portion of the source code of the Falco binary and is the home of each of the kernel drivers and engines, the [libs release doc](https://github.com/falcosecurity/libs/blob/master/release.md) is an excellent additional resource. In addition, the [plugins release doc](https://github.com/falcosecurity/plugins/blob/master/release.md) provides similar details around Falco's plugins. `SHA256` checksums are provided throughout Falco's source code to empower the end user to perform integrity checks. All Falco releases also contain the sources as part of the packages. ### Falco repo (this repo) - Falco version is a git tag (`x.y.z`), see [Procedures](#procedures) section. Note that the Falco version is a sem-ver-like schema, but not fully compatible with sem-ver. - [FALCO_ENGINE_VERSION](https://github.com/falcosecurity/falco/blob/master/userspace/engine/falco_engine_version.h) is not sem-ver and must be bumped either when a backward incompatible change has been introduced to the rules files syntax and loading logic, and/or when `FALCO_ENGINE_CHECKSUM` has changed. The checksum is computed by considering the available rules fields (see currently supported [Falco fields](https://falco.org/docs/reference/rules/supported-fields/)), the event types (see currently supported [Falco events](https://falco.org/docs/reference/rules/supported-events/)), and the supported driver schema version. A checksum indicates that something was not available in previous engine versions. See the [rules release guidelines](https://github.com/falcosecurity/rules/blob/main/RELEASE.md#versioning-a-ruleset) to understand how this affects the versioning of Falco rules. Breaking changes introduced in the Falco engine are not necessarily tied to the drivers or libs versions. The version number must be incremented every time and only when a single change or an atomic group of changes - which meet the criteria described above - is included in the `master` branch. Thus, a version bump can occur multiple times during the development and testing phases of a given release cycle. A given version bump must not group multiple changes that occurred sporadically during the release cycle. - During development and release preparation, libs and driver reference commits are often bumped in Falco's cmake setup ([falcosecurity-libs cmake](https://github.com/falcosecurity/falco/blob/master/cmake/modules/falcosecurity-libs.cmake#L30) and [driver cmake](https://github.com/falcosecurity/falco/blob/master/cmake/modules/driver.cmake#L29)) in order to merge new Falco features. In practice, they are mostly bumped at the same time referencing the same `libs` commit. However, for the official Falco build `FALCOSECURITY_LIBS_VERSION` flag that references the stable libs version is used (read below). - At release time Plugin, Libs and Driver versions are compatible with Falco. ``` Falco version: x.y.z (sem-ver like) Libs version: x.y.z (sem-ver like) Plugin API: x.y.z (sem-ver like) Engine: x Driver: API version: x.y.z (sem-ver) Schema version: x.y.z (sem-ver) Default driver: x.y.z+driver (sem-ver like, indirectly encodes compatibility range in addition to default version Falco is shipped with) ``` ### Libs repo - Libs version is a git tag (`x.y.z`) and when building Falco the libs version is set via the `FALCOSECURITY_LIBS_VERSION` flag (see above). - The driver version is not directly linked to the userspace components of the Falco binary. This is because of the clear separation between userspace and kernel space, which adds an additional layer of complexity. To address this, the concept of a `Default driver` has been introduced, allowing for implicit declaration of compatible driver versions. For example, if the default driver version is `5.0.1+driver`, Falco works with all driver versions >= 5.0.1 and < 6.0.0. This is a consequence of how the driver version is constructed starting from the `Driver API version` and `Driver Schema version`. Driver API and Schema versions are explained in the respective [libs driver doc](https://github.com/falcosecurity/libs/blob/master/driver/README.VERSION.md) -> Falco's `driver-loader` will always fetch the default driver, therefore a Falco release is always "shipped" with the driver version corresponding to the default driver. - See [libs release doc](https://github.com/falcosecurity/libs/blob/master/release.md) for more information. ### Plugins repo - Plugins version is a git tag (`x.y.z`) - See [plugins release doc](https://github.com/falcosecurity/plugins/blob/master/release.md) for more information. ### Rules repo - Rulesets are versioned individually through git tags - See [rules release doc](https://github.com/falcosecurity/rules/blob/main/RELEASE.md) for more information. - See [plugins release doc](https://github.com/falcosecurity/plugins/blob/master/release.md) for more information about plugins rulesets. ================================================ FILE: brand/README.md ================================================

Cloud Native Runtime Security.

# Falco Branding Guidelines Falco is an open source security project whose brand and identity are governed by the [Cloud Native Computing Foundation](https://www.linuxfoundation.org/legal/trademark-usage). This document describes the official branding guidelines of The Falco Project. Please see the [Falco Branding](https://falco.org/community/falco-brand/) page on our website for further details. ### Logo There are 3 logos available for use in this directory. Use the primary logo unless required otherwise due to background issues or printing. The Falco logo is Apache 2 licensed and free to use in media and publication for the CNCF Falco project. ### Colors | Name | PMS | RGB | |-----------|------|-------------| | Teal | 3125 | 0 174 199 | | Cool Gray | 11 | 83 86 90 | | Black | | 0 0 0 | | Blue-Gray | 7700 | 22 92 125 | | Gold | 1375 | 255 158 27 | | Orange | 171 | 255 92 57 | | Emerald | 3278 | 0 155 119 | | Green | 360 | 108 194 74 | The primary colors are those in the first two rows. ### Slogan > Cloud Native Runtime Security ### Writing about Falco ##### Yes Notice the capitalization of the following terms. - The Falco Project - Falco ##### No - falco - the falco project - the Falco project --- # Glossary This section contains key terms specifically used within the context of The Falco Project. For a more comprehensive list of Falco-related terminology, we invite you to visit the [Glossary](https://falco.org/docs/reference/glossary/) page on our official website. #### Modern eBPF Probe Robust eBPF probe, which brings the CO-RE paradigm, better performances, and maintainability. The modern eBPF probe is not shipped as a separate artifact but bundled into the Falco binary itself. This is one option used to pass kernel events up to userspace for Falco to consume. #### Kernel Module Used to describe the `.ko` object that would be loaded into the kernel as a potentially risky kernel module. This is one option used to pass kernel events up to userspace for Falco to consume. #### Driver The global term for the software that sends events from the kernel. Such as the [Modern eBPF probe](#modern-ebpf-probe), or the [Kernel Module](#kernel-module). #### Plugin Used to describe a dynamic shared library (`.so` files in Unix, `.dll` files in Windows) that conforms to a documented API and allows to extend Falco's capabilities. #### Falco The name of the project and also the name of [the main engine](https://github.com/falcosecurity/falco) that the rest of the project is built on. ================================================ FILE: cmake/cpack/CMakeCPackOptions.cmake ================================================ # 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. # if(CPACK_GENERATOR MATCHES "DEB" OR CPACK_GENERATOR MATCHES "RPM") list( APPEND CPACK_INSTALL_COMMANDS "mkdir -p _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system" ) list( APPEND CPACK_INSTALL_COMMANDS "cp scripts/systemd/falco-kmod-inject.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system" ) list( APPEND CPACK_INSTALL_COMMANDS "cp scripts/systemd/falco-kmod.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system" ) list( APPEND CPACK_INSTALL_COMMANDS "cp scripts/systemd/falco-modern-bpf.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system" ) list( APPEND CPACK_INSTALL_COMMANDS "cp scripts/systemd/falco-custom.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system" ) list( APPEND CPACK_INSTALL_COMMANDS "cp scripts/systemd/falcoctl-artifact-follow.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system" ) endif() if(CPACK_GENERATOR MATCHES "TGZ") set(CPACK_SET_DESTDIR "ON") endif() ================================================ FILE: cmake/cpack/debian/conffiles ================================================ /etc/falco/falco.yaml /etc/falco/falco_rules.local.yaml /etc/falcoctl/falcoctl.yaml ================================================ FILE: cmake/modules/CPackConfig.cmake ================================================ # 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(CPACK_PACKAGE_NAME "${PACKAGE_NAME}") set(CPACK_PACKAGE_VENDOR "Cloud Native Computing Foundation (CNCF) cncf.io.") set(CPACK_PACKAGE_CONTACT "cncf-falco-dev@lists.cncf.io") # todo: change this once we've got # @falco.org addresses set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Falco - Container Native Runtime Security") set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/scripts/description.txt") set(CPACK_PACKAGE_VERSION "${FALCO_VERSION}") set(CPACK_PACKAGE_VERSION_MAJOR "${FALCO_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${FALCO_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${FALCO_VERSION_PATCH}") set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_SOURCE_DIR}/cmake/cpack/CMakeCPackOptions.cmake") if(CMAKE_BUILD_TYPE STREQUAL "debug") set(CPACK_STRIP_FILES "OFF") else() set(CPACK_STRIP_FILES "ON") endif() set(CPACK_PACKAGE_RELOCATABLE "OFF") if(EMSCRIPTEN) set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-wasm") else() set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}" ) endif() if(WIN32) set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") endif() # Built packages will include only the following components set(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_CURRENT_BINARY_DIR};${FALCO_COMPONENT_NAME};${FALCO_COMPONENT_NAME};/" ) if(CMAKE_SYSTEM_NAME MATCHES "Linux") # only Linux has drivers list(APPEND CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_CURRENT_BINARY_DIR};${DRIVER_COMPONENT_NAME};${DRIVER_COMPONENT_NAME};/" ) endif() if(NOT CPACK_GENERATOR) if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(CPACK_GENERATOR DEB RPM TGZ) else() set(CPACK_GENERATOR TGZ) endif() endif() message(STATUS "Using package generators: ${CPACK_GENERATOR}") message(STATUS "Package architecture: ${CMAKE_SYSTEM_PROCESSOR}") set(CPACK_DEBIAN_PACKAGE_SECTION "utils") if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") endif() if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64") set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "arm64") endif() set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://www.falco.org") set(CPACK_DEBIAN_PACKAGE_SUGGESTS "dkms (>= 2.1.0.0)") set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/scripts/debian/postinst;${CMAKE_BINARY_DIR}/scripts/debian/prerm;${CMAKE_BINARY_DIR}/scripts/debian/postrm;${PROJECT_SOURCE_DIR}/cmake/cpack/debian/conffiles" ) set(CPACK_RPM_PACKAGE_LICENSE "Apache v2.0") set(CPACK_RPM_PACKAGE_ARCHITECTURE, "amd64") set(CPACK_RPM_PACKAGE_URL "https://www.falco.org") set(CPACK_RPM_PACKAGE_REQUIRES "systemd") set(CPACK_RPM_PACKAGE_SUGGESTS "dkms, kernel-devel") set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_BINARY_DIR}/scripts/rpm/postinstall") set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_BINARY_DIR}/scripts/rpm/preuninstall") set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${CMAKE_BINARY_DIR}/scripts/rpm/postuninstall") set(CPACK_RPM_PACKAGE_VERSION "${FALCO_VERSION}") set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/src /usr/share/man /usr/share/man/man8 /etc /usr /usr/bin /usr/share ) set(CPACK_RPM_PACKAGE_RELOCATABLE "OFF") include(CPack) ================================================ FILE: cmake/modules/CompilerFlags.cmake ================================================ # 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(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS OFF) if(NOT FALCO_EXTRA_DEBUG_FLAGS) set(FALCO_EXTRA_DEBUG_FLAGS "-D_DEBUG") endif() string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE) if(CMAKE_BUILD_TYPE STREQUAL "debug") set(KBUILD_FLAGS "${FALCO_EXTRA_DEBUG_FLAGS} ${FALCO_EXTRA_FEATURE_FLAGS}") add_definitions(-DBUILD_TYPE_DEBUG) elseif(CMAKE_BUILD_TYPE STREQUAL "relwithdebinfo") set(KBUILD_FLAGS "${FALCO_EXTRA_FEATURE_FLAGS}") add_definitions(-DBUILD_TYPE_RELWITHDEBINFO) else() set(CMAKE_BUILD_TYPE "release") set(KBUILD_FLAGS "${FALCO_EXTRA_FEATURE_FLAGS}") add_definitions(-DBUILD_TYPE_RELEASE) endif() message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") if(MINIMAL_BUILD) set(MINIMAL_BUILD_FLAGS "-DMINIMAL_BUILD") endif() if(MUSL_OPTIMIZED_BUILD) set(MUSL_FLAGS "-static -Os -fPIE -pie") add_definitions(-DMUSL_OPTIMIZED) endif() # explicitly set hardening flags set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(FALCO_SECURITY_FLAGS "") if(LINUX) set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -fstack-protector-strong") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now") endif() if(NOT MSVC) if(CMAKE_BUILD_TYPE STREQUAL "release") set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -D_FORTIFY_SOURCE=2") endif() if(USE_ASAN) set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -fsanitize=address") endif() if(USE_UBSAN) set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -fsanitize=undefined") if(UBSAN_HALT_ON_ERROR) set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -fno-sanitize-recover=undefined") endif() endif() if(USE_FRAME_POINTER) set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -fno-omit-frame-pointer") endif() set(CMAKE_COMMON_FLAGS "${FALCO_SECURITY_FLAGS} -Wall -ggdb ${FALCO_EXTRA_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}" ) if(BUILD_WARNINGS_AS_ERRORS) set(CMAKE_SUPPRESSED_WARNINGS "-Wno-unused-parameter -Wno-unused-variable -Wno-unused-but-set-variable -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -Wno-implicit-fallthrough -Wno-format-truncation -Wno-stringop-truncation -Wno-stringop-overflow -Wno-restrict -Wno-deprecated-declarations" ) set(CMAKE_COMPILE_WARNING_AS_ERROR ON) set(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} -Wextra ${CMAKE_SUPPRESSED_WARNINGS}") endif() set(CMAKE_C_FLAGS "${CMAKE_COMMON_FLAGS}") set(CMAKE_CXX_FLAGS "-std=c++17 ${CMAKE_COMMON_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}") set(CMAKE_C_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELEASE} -g") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -g") # Add linker flags to generate separate debug files set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} -Wl,--build-id" ) set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} -Wl,--build-id" ) else() # MSVC set(MINIMAL_BUILD ON) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") # The WIN32_LEAN_AND_MEAN define avoids possible macro pollution when a libsinsp consumer # includes the windows.h header: https://stackoverflow.com/a/28380820 Same goes for NOMINMAX: # https://stackoverflow.com/questions/5004858/why-is-stdmin-failing-when-windows-h-is-included add_compile_definitions( _HAS_STD_BYTE=0 _CRT_SECURE_NO_WARNINGS WIN32 MINIMAL_BUILD WIN32_LEAN_AND_MEAN NOMINMAX ) set(FALCOSECURITY_LIBS_COMMON_FLAGS "/EHsc /W3 /Zi /std:c++17") set(FALCOSECURITY_LIBS_DEBUG_FLAGS "/MTd /Od") set(FALCOSECURITY_LIBS_RELEASE_FLAGS "/MT") set(FALCOSECURITY_LIBS_RELWITHDEBINFO_FLAGS "/MT /Zi") # Ensure linker generates PDB files for MSVC set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG") set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG" ) set(CMAKE_C_FLAGS "${FALCOSECURITY_LIBS_COMMON_FLAGS}") set(CMAKE_CXX_FLAGS "${FALCOSECURITY_LIBS_COMMON_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${FALCOSECURITY_LIBS_DEBUG_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${FALCOSECURITY_LIBS_DEBUG_FLAGS}") set(CMAKE_C_FLAGS_RELEASE "${FALCOSECURITY_LIBS_RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE "${FALCOSECURITY_LIBS_RELEASE_FLAGS}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${FALCOSECURITY_LIBS_RELWITHDEBINFO_FLAGS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${FALCOSECURITY_LIBS_RELWITHDEBINFO_FLAGS}") endif() ================================================ FILE: cmake/modules/Coverage.cmake ================================================ # 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. # # Tests coverage option(FALCO_COVERAGE "Build test suite with coverage information" OFF) if(FALCO_COVERAGE) if(NOT (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) ) message(FATAL_ERROR "FALCO_COVERAGE requires GCC or Clang.") endif() message(STATUS "Building with coverage information") add_compile_options(-g --coverage) set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS}") endif() ================================================ FILE: cmake/modules/cares.cmake ================================================ # 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. # option(USE_BUNDLED_CARES "Enable building of the bundled c-ares" ${USE_BUNDLED_DEPS}) if(CARES_INCLUDE) # we already have c-ares elseif(NOT USE_BUNDLED_CARES) find_path(CARES_INCLUDE NAMES cares/ares.h ares.h) find_library(CARES_LIB NAMES cares) if(CARES_INCLUDE AND CARES_LIB) message(STATUS "Found c-ares: include: ${CARES_INCLUDE}, lib: ${CARES_LIB}") else() message(FATAL_ERROR "Couldn't find system c-ares") endif() else() if(BUILD_SHARED_LIBS) set(CARES_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) set(CARES_STATIC_OPTION "Off") else() set(CARES_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) set(CARES_STATIC_OPTION "On") endif() set(CARES_SRC "${PROJECT_BINARY_DIR}/c-ares-prefix/src/c-ares") set(CARES_INCLUDE "${CARES_SRC}/include/") set(CARES_LIB "${CARES_SRC}/lib/libcares${CARES_LIB_SUFFIX}") if(NOT TARGET c-ares) message(STATUS "Using bundled c-ares in '${CARES_SRC}'") ExternalProject_Add( c-ares PREFIX "${PROJECT_BINARY_DIR}/c-ares-prefix" URL "https://github.com/c-ares/c-ares/releases/download/v1.33.1/c-ares-1.33.1.tar.gz" URL_HASH "SHA256=06869824094745872fa26efd4c48e622b9bd82a89ef0ce693dc682a23604f415" BUILD_IN_SOURCE 1 CMAKE_ARGS -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=NEW -DCMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY} -DCMAKE_INSTALL_LIBDIR=lib -DCARES_SHARED=${BUILD_SHARED_LIBS} -DCARES_STATIC=${CARES_STATIC_OPTION} -DCARES_STATIC_PIC=${ENABLE_PIC} -DCARES_BUILD_TOOLS=Off -DCARES_INSTALL=Off -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} BUILD_BYPRODUCTS ${CARES_INCLUDE} ${CARES_LIB} INSTALL_COMMAND "" ) install( FILES "${CARES_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" ) install( DIRECTORY "${CARES_INCLUDE}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" ) endif() endif() if(NOT TARGET c-ares) add_custom_target(c-ares) endif() include_directories("${CARES_INCLUDE}") ================================================ FILE: cmake/modules/copy_files_to_build_dir.cmake ================================================ # 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. # function(copy_files_to_build_dir source_files targetsuffix) set(build_files) foreach(file_path ${source_files}) get_filename_component(trace_file ${file_path} NAME) list(APPEND build_files ${CMAKE_CURRENT_BINARY_DIR}/${trace_file}) endforeach() add_custom_target(copy-files-${targetsuffix} ALL DEPENDS ${build_files}) add_custom_command( OUTPUT ${build_files} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${source_files} ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${source_files} ) endfunction() ================================================ FILE: cmake/modules/cpp-httplib.cmake ================================================ # 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. # option(USE_BUNDLED_CPPHTTPLIB "Enable building of the bundled cpp-httplib" ${USE_BUNDLED_DEPS}) if(USE_BUNDLED_CPPHTTPLIB) set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF) set(HTTPLIB_REQUIRE_BROTLI OFF) set(HTTPLIB_USE_ZLIB_IF_AVAILABLE OFF) set(HTTPLIB_REQUIRE_ZLIB OFF) set(HTTPLIB_USE_ZSTD_IF_AVAILABLE OFF) set(HTTPLIB_REQUIRE_ZSTD OFF) set(HTTPLIB_USE_NON_BLOCKING_GETADDRINFO OFF) include(FetchContent) FetchContent_Declare( cpp-httplib URL https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.23.1.tar.gz URL_HASH SHA256=410a1347ed6bcbcc4a19af8ed8ad3873fe9fa97731d52db845c4c78f3f9c31e6 ) FetchContent_MakeAvailable(cpp-httplib) else() find_package(httplib CONFIG REQUIRED) endif() ================================================ FILE: cmake/modules/curl.cmake ================================================ # 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. # option(USE_BUNDLED_CURL "Enable building of the bundled curl" ${USE_BUNDLED_DEPS}) include(openssl) include(zlib) if(CURL_INCLUDE_DIRS) # we already have curl elseif(NOT USE_BUNDLED_CURL) find_package(CURL REQUIRED) message(STATUS "Found CURL: include: ${CURL_INCLUDE_DIRS}, lib: ${CURL_LIBRARIES}") else() if(BUILD_SHARED_LIBS) set(CURL_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) set(CURL_STATIC_OPTION) else() set(CURL_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) set(CURL_STATIC_OPTION --disable-shared) endif() set(CURL_BUNDLE_DIR "${PROJECT_BINARY_DIR}/curl-prefix/src/curl") set(CURL_INCLUDE_DIRS "${CURL_BUNDLE_DIR}/include/") set(CURL_LIBRARIES "${CURL_BUNDLE_DIR}/lib/.libs/libcurl${CURL_LIB_SUFFIX}") if(NOT USE_BUNDLED_OPENSSL) set(CURL_SSL_OPTION "--with-ssl") else() set(CURL_SSL_OPTION "--with-ssl=${OPENSSL_INSTALL_DIR}") message(STATUS "Using SSL for curl in '${OPENSSL_INSTALL_DIR}'") endif() if(NOT USE_BUNDLED_ZLIB) set(CURL_ZLIB_OPTION "--with-zlib") else() set(CURL_ZLIB_OPTION "--with-zlib=${ZLIB_SRC}") message(STATUS "Using zlib for curl in '${ZLIB_SRC}'") endif() message(STATUS "Using bundled curl in '${CURL_BUNDLE_DIR}'") if(NOT ENABLE_PIC) set(CURL_PIC_OPTION) else() set(CURL_PIC_OPTION "--with-pic") endif() if(NOT TARGET curl) ExternalProject_Add( curl PREFIX "${PROJECT_BINARY_DIR}/curl-prefix" DEPENDS openssl zlib URL "https://github.com/curl/curl/releases/download/curl-8_7_1/curl-8.7.1.tar.bz2" URL_HASH "SHA256=05bbd2b698e9cfbab477c33aa5e99b4975501835a41b7ca6ca71de03d8849e76" CONFIGURE_COMMAND ./configure ${CURL_SSL_OPTION} ${CURL_ZLIB_OPTION} ${CURL_STATIC_OPTION} ${CURL_PIC_OPTION} --enable-optimize --disable-curldebug --disable-rt --enable-http --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-sspi --disable-ntlm-wb --disable-tls-srp --without-winssl --without-polarssl --without-cyassl --without-nss --without-axtls --without-librtmp --without-winidn --without-libidn2 --without-libpsl --without-nghttp2 --without-libssh2 --with-ca-path=/etc/ssl/certs/ --disable-threaded-resolver --without-brotli --without-zstd BUILD_COMMAND make BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${CURL_LIBRARIES} INSTALL_COMMAND "" ) install( FILES "${CURL_LIBRARIES}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" ) install( DIRECTORY "${CURL_INCLUDE_DIRS}curl" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" FILES_MATCHING PATTERN "*.h" ) endif() endif() if(NOT TARGET curl) add_custom_target(curl) endif() include_directories("${CURL_INCLUDE_DIRS}") ================================================ FILE: cmake/modules/cxxopts.cmake ================================================ # 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. # # # cxxopts (https://github.com/jarro2783/cxxopts) # option(USE_BUNDLED_CXXOPTS "Enable building of the bundled cxxopts" ${USE_BUNDLED_DEPS}) if(CXXOPTS_INCLUDE_DIR) # we already have cxxopts elseif(NOT USE_BUNDLED_CXXOPTS) find_package(cxxopts CONFIG REQUIRED) get_target_property(CXXOPTS_INCLUDE_DIR cxxopts::cxxopts INTERFACE_INCLUDE_DIRECTORIES) elseif(NOT TARGET cxxopts) set(CXXOPTS_SRC "${PROJECT_BINARY_DIR}/cxxopts-prefix/src/cxxopts/") set(CXXOPTS_INCLUDE_DIR "${CXXOPTS_SRC}/include") message(STATUS "Using bundled cxxopts in ${CXXOPTS_SRC}") ExternalProject_Add( cxxopts URL "https://github.com/jarro2783/cxxopts/archive/refs/tags/v3.0.0.tar.gz" URL_HASH "SHA256=36f41fa2a46b3c1466613b63f3fa73dc24d912bc90d667147f1e43215a8c6d00" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" ) endif() if(NOT TARGET cxxopts) add_custom_target(cxxopts) endif() ================================================ FILE: cmake/modules/driver-repo/CMakeLists.txt ================================================ # 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. # cmake_minimum_required(VERSION 3.5.1) project(driver-repo NONE) include(ExternalProject) message(STATUS "Driver repository: ${DRIVER_REPO}") message(STATUS "Driver version: ${DRIVER_VERSION}") ExternalProject_Add( driver URL "https://github.com/${DRIVER_REPO}/archive/${DRIVER_VERSION}.tar.gz" URL_HASH "${DRIVER_CHECKSUM}" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" PATCH_COMMAND sh -c "mv ./driver ../driver.tmp && rm -rf ./* && mv ../driver.tmp/* ." ) ================================================ FILE: cmake/modules/driver.cmake ================================================ # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2026 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(DRIVER_CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/driver-repo") set(DRIVER_CMAKE_WORKING_DIR "${CMAKE_BINARY_DIR}/driver-repo") file(MAKE_DIRECTORY ${DRIVER_CMAKE_WORKING_DIR}) if(DRIVER_SOURCE_DIR) set(DRIVER_VERSION "0.0.0-local") message(STATUS "Using local version for driver: '${DRIVER_SOURCE_DIR}'") else() # DRIVER_REPO accepts a repository name (/) alternative to the # falcosecurity/libs repository. In case you want to test against a fork of falcosecurity/libs # just pass the variable - ie., `cmake -DDRIVER_REPO=/libs ..` if(NOT DRIVER_REPO) set(DRIVER_REPO "falcosecurity/libs") endif() # DRIVER_VERSION accepts a git reference (branch name, commit hash, or tag) to the # falcosecurity/libs repository which contains the driver source code under the `/driver` # directory. The chosen driver version must be compatible with the given # FALCOSECURITY_LIBS_VERSION. In case you want to test against another driver version (or # branch, or commit) just pass the variable - ie., `cmake -DDRIVER_VERSION=dev ..` if(NOT DRIVER_VERSION) set(DRIVER_VERSION "7b08f8a0a12b56d59eab73052e637ca123623f61") set(DRIVER_CHECKSUM "SHA256=43c72a98e48d04177c8223ccdfe88de6f09958f2330b6b9ee26882f1a77e369f" ) endif() # cd /path/to/build && cmake /path/to/source execute_process( COMMAND "${CMAKE_COMMAND}" -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" -DDRIVER_REPO=${DRIVER_REPO} -DDRIVER_VERSION=${DRIVER_VERSION} -DDRIVER_CHECKSUM=${DRIVER_CHECKSUM} ${DRIVER_CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${DRIVER_CMAKE_WORKING_DIR} ) # cmake --build . execute_process( COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${DRIVER_CMAKE_WORKING_DIR}" ) set(DRIVER_SOURCE_DIR "${DRIVER_CMAKE_WORKING_DIR}/driver-prefix/src/driver") endif() add_definitions(-D_GNU_SOURCE) set(DRIVER_NAME "falco") set(DRIVER_PACKAGE_NAME "falco") set(DRIVER_COMPONENT_NAME "falco-driver") add_subdirectory(${DRIVER_SOURCE_DIR} ${PROJECT_BINARY_DIR}/driver) ================================================ FILE: cmake/modules/falco-version.cmake ================================================ # 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. # # Retrieve git ref and commit hash include(GetVersionFromGit) # Get Falco version variable according to git index if(NOT FALCO_VERSION) set(FALCO_VERSION "0.0.0") get_version_from_git(FALCO_VERSION "" "") endif() # Remove the starting "v" in case there is one string(REGEX REPLACE "^v(.*)" "\\1" FALCO_VERSION "${FALCO_VERSION}") string(REGEX MATCH "^(0|[1-9][0-9]*)" FALCO_VERSION_MAJOR "${FALCO_VERSION}") string(REGEX REPLACE "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\..*" "\\2" FALCO_VERSION_MINOR "${FALCO_VERSION}" ) string(REGEX REPLACE "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*).*" "\\3" FALCO_VERSION_PATCH "${FALCO_VERSION}" ) string( REGEX REPLACE "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*).*" "\\5" FALCO_VERSION_PRERELEASE "${FALCO_VERSION}" ) if(FALCO_VERSION_PRERELEASE STREQUAL "${FALCO_VERSION}") set(FALCO_VERSION_PRERELEASE "") endif() if(NOT FALCO_VERSION_BUILD) string(REGEX REPLACE ".*\\+([0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)" "\\1" FALCO_VERSION_BUILD "${FALCO_VERSION}" ) endif() if(FALCO_VERSION_BUILD STREQUAL "${FALCO_VERSION}") set(FALCO_VERSION_BUILD "") endif() message(STATUS "Falco version: ${FALCO_VERSION}") ================================================ FILE: cmake/modules/falcoctl.cmake ================================================ # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2025 The Falco Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except # in compliance with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software distributed under the License # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express # or implied. See the License for the specific language governing permissions and limitations under # the License. # include(ExternalProject) option(ADD_FALCOCTL_DEPENDENCY "Add falcoctl dependency while building falco" ON) if(ADD_FALCOCTL_DEPENDENCY) string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} FALCOCTL_SYSTEM_NAME) set(FALCOCTL_VERSION "0.12.2") message(STATUS "Building with falcoctl: ${FALCOCTL_VERSION}") if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64") set(FALCOCTL_SYSTEM_PROC_GO "amd64") set(FALCOCTL_HASH "7e0e232aa73825383d3382b3af8a38466289a768f9c1c7f25bd7e11a3ed6980a") else() # aarch64 set(FALCOCTL_SYSTEM_PROC_GO "arm64") set(FALCOCTL_HASH "9b7dd75189f997da6423bcdb5dfe68840f20c56f95d30d323d26d0c4bd75a8e3") endif() ExternalProject_Add( falcoctl URL "https://github.com/falcosecurity/falcoctl/releases/download/v${FALCOCTL_VERSION}/falcoctl_${FALCOCTL_VERSION}_${FALCOCTL_SYSTEM_NAME}_${FALCOCTL_SYSTEM_PROC_GO}.tar.gz" URL_HASH "SHA256=${FALCOCTL_HASH}" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" ) install( PROGRAMS "${PROJECT_BINARY_DIR}/falcoctl-prefix/src/falcoctl/falcoctl" DESTINATION "${FALCO_BIN_DIR}" COMPONENT "${FALCO_COMPONENT_NAME}" ) install( DIRECTORY DESTINATION "${FALCO_ABSOLUTE_SHARE_DIR}/plugins" COMPONENT "${FALCO_COMPONENT_NAME}" ) else() message(STATUS "Won't build with falcoctl") endif() ================================================ FILE: cmake/modules/falcosecurity-libs-repo/CMakeLists.txt ================================================ # 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. # cmake_minimum_required(VERSION 3.5.1) project(falcosecurity-libs-repo NONE) include(ExternalProject) message(STATUS "Libs repository: ${FALCOSECURITY_LIBS_REPO}") message(STATUS "Libs version: ${FALCOSECURITY_LIBS_VERSION}") ExternalProject_Add( falcosecurity-libs URL "https://github.com/${FALCOSECURITY_LIBS_REPO}/archive/${FALCOSECURITY_LIBS_VERSION}.tar.gz" URL_HASH "${FALCOSECURITY_LIBS_CHECKSUM}" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" ) ================================================ FILE: cmake/modules/falcosecurity-libs.cmake ================================================ # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2026 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(FALCOSECURITY_LIBS_CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/falcosecurity-libs-repo" ) set(FALCOSECURITY_LIBS_CMAKE_WORKING_DIR "${CMAKE_BINARY_DIR}/falcosecurity-libs-repo") file(MAKE_DIRECTORY ${FALCOSECURITY_LIBS_CMAKE_WORKING_DIR}) # explicitly disable the bundled driver, since we pull it separately set(USE_BUNDLED_DRIVER OFF CACHE BOOL "" ) if(FALCOSECURITY_LIBS_SOURCE_DIR) set(FALCOSECURITY_LIBS_VERSION "0.0.0-local") message(STATUS "Using local version of falcosecurity/libs: '${FALCOSECURITY_LIBS_SOURCE_DIR}'") else() # FALCOSECURITY_LIBS_REPO accepts a repository name (/) 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=/libs ..` if(NOT FALCOSECURITY_LIBS_REPO) set(FALCOSECURITY_LIBS_REPO "falcosecurity/libs") endif() # FALCOSECURITY_LIBS_VERSION accepts a git reference (branch name, commit hash, or tag) to the # falcosecurity/libs repository. In case you want to test against another falcosecurity/libs # version (or branch, or commit) just pass the variable - ie., `cmake # -DFALCOSECURITY_LIBS_VERSION=dev ..` if(NOT FALCOSECURITY_LIBS_VERSION) set(FALCOSECURITY_LIBS_VERSION "7b08f8a0a12b56d59eab73052e637ca123623f61") set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=43c72a98e48d04177c8223ccdfe88de6f09958f2330b6b9ee26882f1a77e369f" ) endif() # cd /path/to/build && cmake /path/to/source execute_process( COMMAND "${CMAKE_COMMAND}" -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" -DFALCOSECURITY_LIBS_REPO=${FALCOSECURITY_LIBS_REPO} -DFALCOSECURITY_LIBS_VERSION=${FALCOSECURITY_LIBS_VERSION} -DFALCOSECURITY_LIBS_CHECKSUM=${FALCOSECURITY_LIBS_CHECKSUM} ${FALCOSECURITY_LIBS_CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${FALCOSECURITY_LIBS_CMAKE_WORKING_DIR} ) # cmake --build . execute_process( COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${FALCOSECURITY_LIBS_CMAKE_WORKING_DIR}" ) set(FALCOSECURITY_LIBS_SOURCE_DIR "${FALCOSECURITY_LIBS_CMAKE_WORKING_DIR}/falcosecurity-libs-prefix/src/falcosecurity-libs" ) endif() set(LIBS_PACKAGE_NAME "falcosecurity") if(CMAKE_SYSTEM_NAME MATCHES "Linux") add_definitions(-D_GNU_SOURCE) endif() if(MUSL_OPTIMIZED_BUILD) add_definitions(-DMUSL_OPTIMIZED) endif() set(SCAP_HOST_ROOT_ENV_VAR_NAME "HOST_ROOT") set(SCAP_HOSTNAME_ENV_VAR "FALCO_HOSTNAME") set(SINSP_AGENT_CGROUP_MEM_PATH_ENV_VAR "FALCO_CGROUP_MEM_PATH") if(NOT LIBS_DIR) set(LIBS_DIR "${FALCOSECURITY_LIBS_SOURCE_DIR}") endif() # todo(ekoops): remove this once we remove gvisor from libs option(BUILD_LIBSCAP_GVISOR OFF) # configure modern BPF support set(BUILD_LIBSCAP_MODERN_BPF ${BUILD_FALCO_MODERN_BPF} CACHE BOOL "" ) # explicitly disable the tests/examples of this dependency set(CREATE_TEST_TARGETS OFF CACHE BOOL "" ) set(BUILD_LIBSCAP_EXAMPLES OFF CACHE BOOL "" ) set(BUILD_LIBSINSP_EXAMPLES OFF CACHE BOOL "" ) set(USE_BUNDLED_TBB ON CACHE BOOL "" ) set(USE_BUNDLED_JSONCPP ON CACHE BOOL "" ) set(USE_BUNDLED_VALIJSON ON CACHE BOOL "" ) set(USE_BUNDLED_RE2 ON CACHE BOOL "" ) set(USE_BUNDLED_UTHASH ON CACHE BOOL "" ) if(USE_DYNAMIC_LIBELF) set(USE_BUNDLED_LIBELF OFF CACHE BOOL "" ) set(USE_SHARED_LIBELF ON CACHE BOOL "" ) endif() list(APPEND CMAKE_MODULE_PATH "${FALCOSECURITY_LIBS_SOURCE_DIR}/cmake/modules") include(CheckSymbolExists) check_symbol_exists(strlcpy "string.h" HAVE_STRLCPY) if(HAVE_STRLCPY) message( STATUS "Existing strlcpy and strlcat found, will *not* use local definition by setting -DHAVE_STRLCPY and -DHAVE_STRLCAT." ) add_definitions(-DHAVE_STRLCPY) add_definitions(-DHAVE_STRLCAT) else() message(STATUS "No strlcpy and strlcat found, will use local definition") endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux") include(driver) endif() include(libscap) include(libsinsp) ================================================ FILE: cmake/modules/gperftools.cmake ================================================ # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2026 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. # # gperftools CPU profiler support This module provides: GPERFTOOLS_INCLUDE_DIR (include directory) # and GPERFTOOLS_PROFILER_LIB (the profiler library path) option(USE_BUNDLED_GPERFTOOLS "Build gperftools from source" ${USE_BUNDLED_DEPS}) if(GPERFTOOLS_INCLUDE_DIR) # Already have gperftools configured elseif(NOT USE_BUNDLED_GPERFTOOLS) # Use system gperftools find_path( GPERFTOOLS_INCLUDE_DIR NAMES gperftools/profiler.h PATHS /usr/include /usr/local/include ) find_library( GPERFTOOLS_PROFILER_LIB NAMES profiler PATHS /usr/lib /usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/aarch64-linux-gnu ) if(GPERFTOOLS_INCLUDE_DIR AND GPERFTOOLS_PROFILER_LIB) message( STATUS "Found system gperftools: include: ${GPERFTOOLS_INCLUDE_DIR}, lib: ${GPERFTOOLS_PROFILER_LIB}" ) else() message( FATAL_ERROR "Couldn't find system gperftools. Install it or use -DUSE_BUNDLED_GPERFTOOLS=ON\n" " Ubuntu/Debian: sudo apt-get install libgoogle-perftools-dev\n" " Fedora/RHEL: sudo dnf install gperftools-devel\n" " macOS: brew install gperftools" ) endif() else() # Build gperftools from source set(GPERFTOOLS_SRC "${PROJECT_BINARY_DIR}/gperftools-prefix/src/gperftools") set(GPERFTOOLS_INCLUDE_DIR "${GPERFTOOLS_SRC}/src") if(BUILD_SHARED_LIBS) set(GPERFTOOLS_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) else() set(GPERFTOOLS_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() # The library is built in .libs subdirectory set(GPERFTOOLS_PROFILER_LIB "${GPERFTOOLS_SRC}/.libs/libprofiler${GPERFTOOLS_LIB_SUFFIX}") # gperftools version 2.15 (latest stable as of 2024) set(GPERFTOOLS_VERSION "2.15") set(GPERFTOOLS_URL "https://github.com/gperftools/gperftools/releases/download/gperftools-${GPERFTOOLS_VERSION}/gperftools-${GPERFTOOLS_VERSION}.tar.gz" ) set(GPERFTOOLS_URL_HASH "SHA256=c69fef855628c81ef56f12e3c58f2b7ce1f326c0a1fe783e5cae0b88cbbe9a80" ) message(STATUS "Building gperftools ${GPERFTOOLS_VERSION} from source") # Configure options for gperftools set(GPERFTOOLS_CONFIGURE_ARGS --enable-cpu-profiler --disable-heap-profiler --disable-heap-checker --disable-debugalloc ) # Check if libunwind is available for better stack traces find_library(LIBUNWIND_LIBRARY NAMES unwind) if(LIBUNWIND_LIBRARY) list(APPEND GPERFTOOLS_CONFIGURE_ARGS --enable-libunwind) message(STATUS "gperftools: libunwind found, enabling for better stack traces") else() list(APPEND GPERFTOOLS_CONFIGURE_ARGS --disable-libunwind) message(STATUS "gperftools: libunwind not found, using frame pointers for stack traces") endif() ExternalProject_Add( gperftools PREFIX "${PROJECT_BINARY_DIR}/gperftools-prefix" URL "${GPERFTOOLS_URL}" URL_HASH "${GPERFTOOLS_URL_HASH}" CONFIGURE_COMMAND /configure ${GPERFTOOLS_CONFIGURE_ARGS} BUILD_COMMAND ${CMD_MAKE} ${PROCESSOUR_COUNT_MAKE_FLAG} BUILD_IN_SOURCE 1 INSTALL_COMMAND "" UPDATE_COMMAND "" BUILD_BYPRODUCTS ${GPERFTOOLS_PROFILER_LIB} ) install( FILES "${GPERFTOOLS_PROFILER_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" OPTIONAL ) endif() # Create a custom target so we can always depend on 'gperftools' regardless of bundled/system if(NOT TARGET gperftools) add_custom_target(gperftools) endif() # Add include directory globally include_directories(${GPERFTOOLS_INCLUDE_DIR}) # Add compile definition so code can detect profiling support add_compile_definitions(HAS_GPERFTOOLS) # Wrap the profiler library with --whole-archive to ensure the profiler's initialization code is # linked even though we don't call ProfilerStart() directly. This is required for the CPUPROFILE # environment variable to work. set(GPERFTOOLS_PROFILER_LIB "-Wl,--whole-archive" "${GPERFTOOLS_PROFILER_LIB}" "-Wl,--no-whole-archive" ) message(STATUS "gperftools CPU profiler enabled") message(STATUS " Include dir: ${GPERFTOOLS_INCLUDE_DIR}") message(STATUS " Library: ${GPERFTOOLS_PROFILER_LIB}") ================================================ FILE: cmake/modules/jemalloc.cmake ================================================ # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2025 The Falco Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except # in compliance with the License. You may obtain a copy of the License at # # 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. # option(USE_BUNDLED_JEMALLOC "Use bundled jemalloc allocator" ${USE_BUNDLED_DEPS}) if(JEMALLOC_INCLUDE) # we already have JEMALLOC elseif(NOT USE_BUNDLED_JEMALLOC) find_path(JEMALLOC_INCLUDE jemalloc/jemalloc.h) set(JEMALLOC_INCLUDE ${JEMALLOC_INCLUDE}/jemalloc) if(BUILD_SHARED_LIBS) set(JEMALLOC_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) else() set(JEMALLOC_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() find_library(MALLOC_LIB NAMES libjemalloc${JEMALLOC_LIB_SUFFIX}) if(MALLOC_LIB) message(STATUS "Found system jemalloc: include: ${JEMALLOC_INCLUDE}, lib: ${MALLOC_LIB}") else() message(FATAL_ERROR "Couldn't find system jemalloc") endif() else() if(BUILD_SHARED_LIBS) set(JEMALLOC_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) else() set(JEMALLOC_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() set(JEMALLOC_SRC "${PROJECT_BINARY_DIR}/jemalloc-prefix/src") set(MALLOC_LIB "${JEMALLOC_SRC}/malloc/lib/libjemalloc${JEMALLOC_LIB_SUFFIX}") set(JEMALLOC_INCLUDE "${JEMALLOC_SRC}/malloc/include/jemalloc") if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") set(JEMALLOC_ARCH_SPECIFIC_CONFIGURE_ARGS --with-lg-page=14) else() set(JEMALLOC_ARCH_SPECIFIC_CONFIGURE_ARGS "") endif() ExternalProject_Add( malloc PREFIX "${PROJECT_BINARY_DIR}/jemalloc-prefix" URL "https://github.com/jemalloc/jemalloc/archive/refs/tags/5.3.0.tar.gz" URL_HASH "SHA256=ef6f74fd45e95ee4ef7f9e19ebe5b075ca6b7fbe0140612b2a161abafb7ee179" CONFIGURE_COMMAND ./autogen.sh --enable-prof --disable-libdl ${JEMALLOC_ARCH_SPECIFIC_CONFIGURE_ARGS} BUILD_IN_SOURCE 1 BUILD_COMMAND make build_lib_static INSTALL_COMMAND "" UPDATE_COMMAND "" BUILD_BYPRODUCTS ${MALLOC_LIB} ) install( FILES "${MALLOC_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" ) endif() # We add a custom target, in this way we can always depend on `jemalloc` without distinguishing # between "bundled" and "not-bundled" case if(NOT TARGET malloc) add_custom_target(malloc) endif() include_directories(${JEMALLOC_INCLUDE}) add_compile_definitions(HAS_JEMALLOC) ================================================ FILE: cmake/modules/mimalloc.cmake ================================================ # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2025 The Falco Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except # in compliance with the License. You may obtain a copy of the License at # # 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. # option(USE_BUNDLED_MIMALLOC "Use bundled mimalloc (microsoft) allocator" ${USE_BUNDLED_DEPS}) if(MIMALLOC_INCLUDE) # we already have MIMALLOC elseif(NOT USE_BUNDLED_MIMALLOC) find_path(MIMALLOC_INCLUDE mimalloc/mimalloc.h) set(MIMALLOC_INCLUDE ${MIMALLOC_INCLUDE}/mimalloc) if(BUILD_SHARED_LIBS) set(MIMALLOC_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) else() set(MIMALLOC_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() find_library(MALLOC_LIB NAMES libmimalloc${MIMALLOC_LIB_SUFFIX}) if(MALLOC_LIB) message(STATUS "Found system mimalloc: include: ${MIMALLOC_INCLUDE}, lib: ${MALLOC_LIB}") else() message(FATAL_ERROR "Couldn't find system mimalloc") endif() else() if(BUILD_SHARED_LIBS) set(BUILD_STATIC Off) set(MIMALLOC_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) else() set(BUILD_STATIC On) set(MIMALLOC_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() set(MIMALLOC_SRC "${PROJECT_BINARY_DIR}/mimalloc-prefix/src") string(TOLOWER "${CMAKE_BUILD_TYPE}" _build_type) if(_build_type STREQUAL "debug") set(MIMALLOC_LIB_BASENAME "libmimalloc-debug") else() set(MIMALLOC_LIB_BASENAME "libmimalloc") endif() set(MALLOC_LIB "${MIMALLOC_SRC}/malloc-build/${MIMALLOC_LIB_BASENAME}${MIMALLOC_LIB_SUFFIX}") set(MIMALLOC_INCLUDE ${MIMALLOC_SRC}/malloc/include/) # To avoid recent clang versions complaining with "error: expansion of date or time macro is not # reproducible" while building mimalloc, we force-set both variables. string(TIMESTAMP DATE "%Y%m%d") string(TIMESTAMP TIME "%H:%M") set(MIMALLOC_EXTRA_CPPDEFS __DATE__="${DATE}",__TIME__="${TIME}") # We disable arch specific optimization because of issues with building with zig. Optimizations # would be only effective on arm64. See MI_NO_OPT_ARCH=On. ExternalProject_Add( malloc PREFIX "${PROJECT_BINARY_DIR}/mimalloc-prefix" URL "https://github.com/microsoft/mimalloc/archive/refs/tags/v3.1.5.tar.gz" URL_HASH "SHA256=1c6949032069d5ebea438ec5cedd602d06f40a92ddf0f0d9dcff0993e5f6635c" LIST_SEPARATOR "," # to pass MIMALLOC_EXTRA_CPPDEFS as list CMAKE_ARGS -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DMI_BUILD_SHARED=${BUILD_SHARED_LIBS} -DMI_BUILD_STATIC=${BUILD_STATIC} -DMI_BUILD_TESTS=Off -DMI_BUILD_OBJECT=Off -DMI_NO_OPT_ARCH=On -DMI_EXTRA_CPPDEFS=${MIMALLOC_EXTRA_CPPDEFS} INSTALL_COMMAND "" UPDATE_COMMAND "" BUILD_BYPRODUCTS ${MALLOC_LIB} ) install( FILES "${MALLOC_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" ) endif() # We add a custom target, in this way we can always depend on `mimalloc` without distinguishing # between "bundled" and "not-bundled" case if(NOT TARGET malloc) add_custom_target(malloc) endif() include_directories(${MIMALLOC_INCLUDE}) add_compile_definitions(HAS_MIMALLOC) ================================================ FILE: cmake/modules/njson.cmake ================================================ # 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. # option(USE_BUNDLED_NLOHMANN_JSON "Enable building of the bundled nlohmann-json" ${USE_BUNDLED_DEPS}) if(USE_BUNDLED_NLOHMANN_JSON) include(FetchContent) FetchContent_Declare( nlohmann_json URL https://github.com/nlohmann/json/archive/v3.11.3.tar.gz URL_HASH SHA256=0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406 ) FetchContent_MakeAvailable(nlohmann_json) else() find_package(nlohmann_json CONFIG REQUIRED) endif() ================================================ FILE: cmake/modules/openssl.cmake ================================================ # 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. # option(USE_BUNDLED_OPENSSL "Enable building of the bundled OpenSSL" ${USE_BUNDLED_DEPS}) if(OPENSSL_INCLUDE_DIR) # we already have openssl elseif(NOT USE_BUNDLED_OPENSSL) find_package(OpenSSL REQUIRED) message(STATUS "Found OpenSSL: include: ${OPENSSL_INCLUDE_DIR}, lib: ${OPENSSL_LIBRARIES}") else() if(BUILD_SHARED_LIBS) set(OPENSSL_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) set(OPENSSL_SHARED_OPTION shared) else() set(OPENSSL_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) set(OPENSSL_SHARED_OPTION no-shared) endif() set(OPENSSL_BUNDLE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl") set(OPENSSL_INSTALL_DIR "${OPENSSL_BUNDLE_DIR}/target") set(OPENSSL_INCLUDE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl/include/") set(OPENSSL_LIBRARY_SSL "${OPENSSL_INSTALL_DIR}/lib/libssl${OPENSSL_LIB_SUFFIX}") set(OPENSSL_LIBRARY_CRYPTO "${OPENSSL_INSTALL_DIR}/lib/libcrypto${OPENSSL_LIB_SUFFIX}") set(OPENSSL_LIBRARIES ${OPENSSL_LIBRARY_SSL} ${OPENSSL_LIBRARY_CRYPTO}) if(NOT TARGET openssl) if(NOT ENABLE_PIC) set(OPENSSL_PIC_OPTION) else() set(OPENSSL_PIC_OPTION "-fPIC") endif() message(STATUS "Using bundled openssl in '${OPENSSL_BUNDLE_DIR}'") ExternalProject_Add( openssl PREFIX "${PROJECT_BINARY_DIR}/openssl-prefix" URL "https://github.com/openssl/openssl/releases/download/openssl-3.1.4/openssl-3.1.4.tar.gz" URL_HASH "SHA256=840af5366ab9b522bde525826be3ef0fb0af81c6a9ebd84caa600fea1731eee3" CONFIGURE_COMMAND ./config ${OPENSSL_SHARED_OPTION} ${OPENSSL_PIC_OPTION} --prefix=${OPENSSL_INSTALL_DIR} --libdir=lib BUILD_COMMAND make BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${OPENSSL_LIBRARY_SSL} ${OPENSSL_LIBRARY_CRYPTO} INSTALL_COMMAND make install_sw ) install( FILES "${OPENSSL_LIBRARY_SSL}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" ) install( FILES "${OPENSSL_LIBRARY_CRYPTO}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" ) install( DIRECTORY "${OPENSSL_INCLUDE_DIR}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIBS_PACKAGE_NAME}" COMPONENT "libs-deps" ) endif() endif() if(NOT TARGET openssl) add_custom_target(openssl) endif() include_directories("${OPENSSL_INCLUDE_DIR}") ================================================ FILE: cmake/modules/rules.cmake ================================================ # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2025 The Falco Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except # in compliance with the License. You may obtain a copy of the License at # # 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(GNUInstallDirs) include(ExternalProject) if(NOT DEFINED FALCOSECURITY_RULES_FALCO_PATH) # falco_rules.yaml set(FALCOSECURITY_RULES_FALCO_VERSION "falco-rules-5.0.0") set(FALCOSECURITY_RULES_FALCO_CHECKSUM "SHA256=ca87d972e102a9f960fed41f90d2736a73079fcc7e787187028f455ad58b1637" ) set(FALCOSECURITY_RULES_FALCO_PATH "${PROJECT_BINARY_DIR}/falcosecurity-rules-falco-prefix/src/falcosecurity-rules-falco/falco_rules.yaml" ) ExternalProject_Add( falcosecurity-rules-falco URL "https://download.falco.org/rules/${FALCOSECURITY_RULES_FALCO_VERSION}.tar.gz" URL_HASH "${FALCOSECURITY_RULES_FALCO_CHECKSUM}" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" ) endif() if(NOT DEFINED FALCOSECURITY_RULES_LOCAL_PATH) # falco_rules.local.yaml set(FALCOSECURITY_RULES_LOCAL_PATH "${PROJECT_BINARY_DIR}/falcosecurity-rules-local-prefix/falco_rules.local.yaml" ) file(WRITE "${FALCOSECURITY_RULES_LOCAL_PATH}" "# Your custom rules!\n") endif() if(NOT DEFINED FALCO_ETC_DIR) set(FALCO_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/falco") endif() if(WIN32 OR APPLE) set(FALCO_ETC_DIR "etc/falco") endif() if(NOT DEFINED FALCO_RULES_DEST_FILENAME) set(FALCO_RULES_DEST_FILENAME "falco_rules.yaml") set(FALCO_LOCAL_RULES_DEST_FILENAME "falco_rules.local.yaml") endif() if(DEFINED FALCO_COMPONENT) # Allow a slim version of Falco to be embedded in other projects, # intentionally *not* installing all rulesets. install( FILES "${FALCOSECURITY_RULES_FALCO_PATH}" COMPONENT "${FALCO_COMPONENT}" DESTINATION "${FALCO_ETC_DIR}" RENAME "${FALCO_RULES_DEST_FILENAME}" ) install( FILES "${FALCOSECURITY_RULES_LOCAL_PATH}" COMPONENT "${FALCO_COMPONENT}" DESTINATION "${FALCO_ETC_DIR}" RENAME "${FALCO_LOCAL_RULES_DEST_FILENAME}" ) else() # Default Falco installation install( FILES "${FALCOSECURITY_RULES_FALCO_PATH}" DESTINATION "${FALCO_ETC_DIR}" RENAME "${FALCO_RULES_DEST_FILENAME}" COMPONENT "${FALCO_COMPONENT_NAME}" ) install( FILES "${FALCOSECURITY_RULES_LOCAL_PATH}" DESTINATION "${FALCO_ETC_DIR}" RENAME "${FALCO_LOCAL_RULES_DEST_FILENAME}" COMPONENT "${FALCO_COMPONENT_NAME}" ) install( DIRECTORY DESTINATION "${FALCO_ETC_DIR}/rules.d" COMPONENT "${FALCO_COMPONENT_NAME}" ) endif() ================================================ FILE: cmake/modules/static-analysis.cmake ================================================ # 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. # # create the reports folder file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/static-analysis-reports) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/static-analysis-reports/cppcheck) # cppcheck mark_as_advanced(CPPCHECK CPPCHECK_HTMLREPORT) find_program(CPPCHECK cppcheck) find_program(CPPCHECK_HTMLREPORT cppcheck-htmlreport) if(NOT CPPCHECK) message( STATUS "cppcheck command not found, static code analysis using cppcheck will not be available." ) else() message(STATUS "cppcheck found at: ${CPPCHECK}") # we are aware that cppcheck can be run along with the software compilation in a single step # using the CMAKE_CXX_CPPCHECK variables. However, for practical needs we want to keep the two # things separated and have a specific target for it. Our cppcheck target reads the compilation # database produced by CMake set(CMAKE_EXPORT_COMPILE_COMMANDS On) add_custom_target( cppcheck COMMAND ${CPPCHECK} "--enable=all" "--force" "--inconclusive" "--inline-suppr" # allows to # specify suppressions directly in source code "--xml" # we want to generate a report "--output-file=${CMAKE_CURRENT_BINARY_DIR}/static-analysis-reports/cppcheck/cppcheck.xml" # generate # the report under the reports folder in the build folder "-i${CMAKE_CURRENT_BINARY_DIR}" # exclude the build folder "${CMAKE_SOURCE_DIR}" ) endif() # CPPCHECK if(NOT CPPCHECK_HTMLREPORT) message( STATUS "cppcheck-htmlreport command not found, will not be able to produce html reports for cppcheck results" ) else() message(STATUS "cppcheck-htmlreport found at: ${CPPCHECK_HTMLREPORT}") add_custom_target( cppcheck_htmlreport COMMAND ${CPPCHECK_HTMLREPORT} --title=${CMAKE_PROJECT_NAME} --report-dir=${CMAKE_CURRENT_BINARY_DIR}/static-analysis-reports/cppcheck --file=static-analysis-reports/cppcheck/cppcheck.xml ) endif() # CPPCHECK_HTMLREPORT ================================================ FILE: cmake/modules/yaml-cpp.cmake ================================================ # 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. # option(USE_BUNDLED_YAMLCPP "Enable building of the bundled yamlcpp" ${USE_BUNDLED_DEPS}) if(USE_BUNDLED_YAMLCPP) include(FetchContent) FetchContent_Declare( yamlcpp URL https://github.com/jbeder/yaml-cpp/archive/c2bec4c755c67ad86185a2a264996137904fb712.tar.gz URL_HASH SHA256=faea1ffdbad81b958b3b45a63ba667f4db53a3fffb983ca5df4745cf90044797 ) FetchContent_MakeAvailable(yamlcpp) else() find_package(yaml-cpp CONFIG REQUIRED) endif() ================================================ FILE: config/falco.container_plugin.yaml ================================================ # Enable container plugin for linux non musl installation. load_plugins: [container] ================================================ FILE: config/falco.iso8601_timeformat.yaml ================================================ # Enable iso 8601 time format on docker time_format_iso_8601: true ================================================ FILE: docker/OWNERS ================================================ labels: - area/integration approvers: - leogr ================================================ FILE: docker/README.md ================================================ # Falco Dockerfiles This directory contains various ways to package Falco as a container and related tools. ## Currently Supported Images | Name | Directory | Description | |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [falcosecurity/falco:latest](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/falco | Distroless image based on the latest released tar.gz of Falco. No tools are included in the image. | | [falcosecurity/falco:latest-debian](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_-debian](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master-debian](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/falco-debian | Debian-based image. Include some tools (i.e. jq, curl). No driver-building toolchain support. | | [falcosecurity/falco-driver-loader:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:master](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader) | docker/driver-loader | Based on falcosecurity/falco:x.y.z-debian (see above) plus the driver building toolchain support and falcoctl. This is intended to be used as an installer or an init container when modern eBPF cannot be used. | | [falcosecurity/falco-driver-loader:latest-buster](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:_tag_-buster](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:master-debian](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader) | docker/driver-loader-buster | Similar to falcosecurity/falco-driver-loader (see above) but based on a legacy Debian image (i.e. buster ). Recommended only for old kernel versions. | ================================================ FILE: docker/docker-compose/README.md ================================================ # Warning This environment is provided for demonstration purposes only and does not represent a production ready deployment of Falco. # Components The components that this docker-compose file spins up are [Falco](https://falco.org/), [falcosidekick](https://github.com/falcosecurity/falcosidekick), [falcosidekick-ui](https://github.com/falcosecurity/falcosidekick-ui) and a [redis](https://redis.io/) database. # Running To start this environment run `docker-compose up`. Note: You may need to use sudo for Falco to start correctly. # Cleaning up To clean up run `docker-compose rm`. # Generating events If you'd like to generate events that will trigger rules and show up in the UI you can run `docker run -it --rm falcosecurity/event-generator run syscall --loop` ================================================ FILE: docker/docker-compose/config/http_output.yml ================================================ # [Stable] `http_output` # # Send logs to an HTTP endpoint or webhook. # # When using falcosidekick, it is necessary to set `json_output` to true. json_output: true json_include_output_property: true http_output: enabled: true url: "http://falco-sidekick:2801/" ================================================ FILE: docker/docker-compose/docker-compose.yaml ================================================ version: "3" services: falco: container_name: falco cap_drop: - all cap_add: - sys_admin - sys_resource - sys_ptrace volumes: - /var/run/docker.sock:/host/var/run/docker.sock - /proc:/host/proc:ro - /etc:/host/etc:ro - ./config/http_output.yml:/etc/falco/config.d/http_output.yml image: falcosecurity/falco:latest sidekick: container_name: falco-sidekick image: falcosecurity/falcosidekick environment: WEBUI_URL: http://falco-webui:2802 webui: container_name: falco-webui image: falcosecurity/falcosidekick-ui:2.2.0 ports: - 2802:2802 depends_on: - redis command: ['-r', 'redis:6379', '-d'] redis: image: redis/redis-stack:7.2.0-v11 ================================================ FILE: docker/driver-loader/Dockerfile ================================================ ARG FALCO_IMAGE_TAG=latest FROM docker.io/falcosecurity/falco:${FALCO_IMAGE_TAG}-debian ARG FALCO_COMMIT_SHA ARG FALCO_VERSION LABEL org.opencontainers.image.authors='The Falco Authors https://falco.org' \ org.opencontainers.image.url='https://falco.org' \ org.opencontainers.image.source='https://github.com/falcosecurity/falco' \ org.opencontainers.image.vendor='Falco Organization' \ org.opencontainers.image.licenses='Apache-2.0' \ org.opencontainers.image.revision=${FALCO_COMMIT_SHA} \ org.opencontainers.image.version=${FALCO_VERSION} \ maintainer="cncf-falco-dev@lists.cncf.io" LABEL usage="docker run -i -t --privileged -v /root/.falco:/root/.falco -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro -v /etc:/host/etc:ro falcosecurity/falco-driver-loader:latest [driver] [options]" ENV HOST_ROOT=/host ENV HOME=/root 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 \ libc6-dev \ libssl-dev \ llvm \ make \ netcat-openbsd \ patchelf \ xz-utils \ zstd \ && rm -rf /var/lib/apt/lists/* # Some base images have an empty /lib/modules by default # If it's not empty, docker build will fail instead of # silently overwriting the existing directory RUN rm -df /lib/modules \ && ln -s $HOST_ROOT/lib/modules /lib/modules COPY ./docker/driver-loader/docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] ================================================ FILE: docker/driver-loader/docker-entrypoint.sh ================================================ #!/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. # 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 " auto leverage automatic driver selection logic (default)" echo " modern_ebpf modern eBPF CORE probe" echo " kmod kernel module" 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 " --kernel-release set the kernel release" echo " --kernel-version set the kernel version" echo " --http-insecure enable insecure downloads" 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 " FALCOCTL_DRIVER_HTTP_HEADERS specify comma separated list of http headers for driver download (e.g. 'x-emc-namespace: default,Proxy-Authenticate: Basic')" echo "" } echo "* Setting up /usr/src links from host" for i in "$HOST_ROOT/usr/src"/* do [[ -e $i ]] || continue base=$(basename "$i") ln -s "$i" "/usr/src/$base" done ENABLE_COMPILE="false" ENABLE_DOWNLOAD="false" HTTP_INSECURE="false" driver= has_opts= extra_args= while test $# -gt 0; do case "$1" in auto|kmod|modern_ebpf) if [ -n "$driver" ]; then >&2 echo "Only one driver per invocation" print_usage exit 1 else driver=$1 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" ;; --http-insecure) HTTP_INSECURE="true" ;; --kernel-release) extra_args+="--kernelrelease=$2 " shift ;; --kernel-version) extra_args+="--kernelversion=$2 " shift ;; --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 # No opts passed, enable both compile and download if [ -z "$has_opts" ]; then ENABLE_COMPILE="true" ENABLE_DOWNLOAD="true" fi # Default value: auto if [ -z "$driver" ]; then driver="auto" fi if [ "$driver" != "auto" ]; then /usr/bin/falcoctl driver config --type $driver else # Needed because we need to configure Falco to start with correct driver /usr/bin/falcoctl driver config --type modern_ebpf --type kmod fi /usr/bin/falcoctl driver install --compile=$ENABLE_COMPILE --download=$ENABLE_DOWNLOAD --http-insecure=$HTTP_INSECURE --http-headers="$FALCOCTL_DRIVER_HTTP_HEADERS" $extra_args ================================================ FILE: docker/driver-loader-buster/Dockerfile ================================================ FROM debian:buster ARG FALCO_COMMIT_SHA ARG FALCO_VERSION=latest LABEL org.opencontainers.image.authors='The Falco Authors https://falco.org' \ org.opencontainers.image.url='https://falco.org' \ org.opencontainers.image.source='https://github.com/falcosecurity/falco' \ org.opencontainers.image.vendor='Falco Organization' \ org.opencontainers.image.licenses='Apache-2.0' \ org.opencontainers.image.revision=${FALCO_COMMIT_SHA} \ org.opencontainers.image.version=${FALCO_VERSION} \ maintainer="cncf-falco-dev@lists.cncf.io" LABEL usage="docker run -i -t --privileged -v /root/.falco:/root/.falco -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro -v /etc:/host/etc:ro falcosecurity/falco-driver-loader:latest-buster [driver] [options]" ARG TARGETARCH ARG VERSION_BUCKET=deb ARG HOST_ROOT=/host ARG HOME=/root ENV FALCO_VERSION="${FALCO_VERSION}" \ VERSION_BUCKET="${VERSION_BUCKET}" \ HOST_ROOT="${HOST_ROOT}" \ HOME="${HOME}" RUN cp /etc/skel/.bashrc /root && cp /etc/skel/.profile /root # Use 20250630T203427Z debian apt snapshot as it still contains support for buster. RUN cat < /etc/apt/sources.list deb http://snapshot.debian.org/archive/debian/20250630T203427Z buster main deb http://snapshot.debian.org/archive/debian-security/20250630T203427Z buster/updates main deb http://snapshot.debian.org/archive/debian/20250630T203427Z buster-updates main EOF RUN apt-get update \ && apt-get install -y --no-install-recommends \ bash-completion \ bc \ bison \ clang-7 \ ca-certificates \ curl \ dkms \ flex \ gnupg2 \ gcc \ jq \ libc6-dev \ libssl-dev \ llvm-7 \ netcat \ patchelf \ xz-utils \ zstd \ && rm -rf /var/lib/apt/lists/* RUN if [ "$TARGETARCH" = "amd64" ]; \ then apt-get install -y --no-install-recommends libmpx2; \ fi # gcc 6 is no longer included in debian stable, but we need it to # build kernel modules on the default debian-based ami used by # kops. So grab copies we've saved from debian snapshots with the # prefix https://snapshot.debian.org/archive/debian/20170517T033514Z # or so. RUN if [ "$TARGETARCH" = "amd64" ]; then curl -L -o libcilkrts5_6.3.0-18_${TARGETARCH}.deb https://download.falco.org/dependencies/libcilkrts5_6.3.0-18_${TARGETARCH}.deb; fi; \ curl -L -o cpp-6_6.3.0-18_${TARGETARCH}.deb https://download.falco.org/dependencies/cpp-6_6.3.0-18_${TARGETARCH}.deb \ && curl -L -o gcc-6-base_6.3.0-18_${TARGETARCH}.deb https://download.falco.org/dependencies/gcc-6-base_6.3.0-18_${TARGETARCH}.deb \ && curl -L -o gcc-6_6.3.0-18_${TARGETARCH}.deb https://download.falco.org/dependencies/gcc-6_6.3.0-18_${TARGETARCH}.deb \ && curl -L -o libasan3_6.3.0-18_${TARGETARCH}.deb https://download.falco.org/dependencies/libasan3_6.3.0-18_${TARGETARCH}.deb \ && curl -L -o libgcc-6-dev_6.3.0-18_${TARGETARCH}.deb https://download.falco.org/dependencies/libgcc-6-dev_6.3.0-18_${TARGETARCH}.deb \ && curl -L -o libubsan0_6.3.0-18_${TARGETARCH}.deb https://download.falco.org/dependencies/libubsan0_6.3.0-18_${TARGETARCH}.deb \ && curl -L -o libmpfr4_3.1.3-2_${TARGETARCH}.deb https://download.falco.org/dependencies/libmpfr4_3.1.3-2_${TARGETARCH}.deb \ && curl -L -o libisl15_0.18-1_${TARGETARCH}.deb https://download.falco.org/dependencies/libisl15_0.18-1_${TARGETARCH}.deb \ && dpkg -i cpp-6_6.3.0-18_${TARGETARCH}.deb gcc-6-base_6.3.0-18_${TARGETARCH}.deb gcc-6_6.3.0-18_${TARGETARCH}.deb libasan3_6.3.0-18_${TARGETARCH}.deb; \ if [ "$TARGETARCH" = "amd64" ]; then dpkg -i libcilkrts5_6.3.0-18_${TARGETARCH}.deb; fi; \ dpkg -i libgcc-6-dev_6.3.0-18_${TARGETARCH}.deb libubsan0_6.3.0-18_${TARGETARCH}.deb libmpfr4_3.1.3-2_${TARGETARCH}.deb libisl15_0.18-1_${TARGETARCH}.deb \ && rm -f cpp-6_6.3.0-18_${TARGETARCH}.deb gcc-6-base_6.3.0-18_${TARGETARCH}.deb gcc-6_6.3.0-18_${TARGETARCH}.deb libasan3_6.3.0-18_${TARGETARCH}.deb libcilkrts5_6.3.0-18_${TARGETARCH}.deb libgcc-6-dev_6.3.0-18_${TARGETARCH}.deb libubsan0_6.3.0-18_${TARGETARCH}.deb libmpfr4_3.1.3-2_${TARGETARCH}.deb libisl15_0.18-1_${TARGETARCH}.deb # gcc 5 is no longer included in debian stable, but we need it to # build centos kernels, which are 3.x based and explicitly want a gcc # version 3, 4, or 5 compiler. So grab copies we've saved from debian # snapshots with the prefix https://snapshot.debian.org/archive/debian/20190122T000000Z. RUN if [ "$TARGETARCH" = "amd64" ]; then curl -L -o libmpx0_5.5.0-12_${TARGETARCH}.deb https://download.falco.org/dependencies/libmpx0_5.5.0-12_${TARGETARCH}.deb; fi; \ curl -L -o cpp-5_5.5.0-12_${TARGETARCH}.deb https://download.falco.org/dependencies/cpp-5_5.5.0-12_${TARGETARCH}.deb \ && curl -L -o gcc-5-base_5.5.0-12_${TARGETARCH}.deb https://download.falco.org/dependencies/gcc-5-base_5.5.0-12_${TARGETARCH}.deb \ && curl -L -o gcc-5_5.5.0-12_${TARGETARCH}.deb https://download.falco.org/dependencies/gcc-5_5.5.0-12_${TARGETARCH}.deb \ && curl -L -o libasan2_5.5.0-12_${TARGETARCH}.deb https://download.falco.org/dependencies/libasan2_5.5.0-12_${TARGETARCH}.deb \ && curl -L -o libgcc-5-dev_5.5.0-12_${TARGETARCH}.deb https://download.falco.org/dependencies/libgcc-5-dev_5.5.0-12_${TARGETARCH}.deb \ && curl -L -o libisl15_0.18-4_${TARGETARCH}.deb https://download.falco.org/dependencies/libisl15_0.18-4_${TARGETARCH}.deb \ && dpkg -i cpp-5_5.5.0-12_${TARGETARCH}.deb gcc-5-base_5.5.0-12_${TARGETARCH}.deb gcc-5_5.5.0-12_${TARGETARCH}.deb libasan2_5.5.0-12_${TARGETARCH}.deb; \ if [ "$TARGETARCH" = "amd64" ]; then dpkg -i libmpx0_5.5.0-12_${TARGETARCH}.deb; fi; \ dpkg -i libgcc-5-dev_5.5.0-12_${TARGETARCH}.deb libisl15_0.18-4_${TARGETARCH}.deb \ && rm -f cpp-5_5.5.0-12_${TARGETARCH}.deb gcc-5-base_5.5.0-12_${TARGETARCH}.deb gcc-5_5.5.0-12_${TARGETARCH}.deb libasan2_5.5.0-12_${TARGETARCH}.deb libgcc-5-dev_5.5.0-12_${TARGETARCH}.deb libisl15_0.18-4_${TARGETARCH}.deb libmpx0_5.5.0-12_${TARGETARCH}.deb # Since our base Debian image ships with GCC 7 which breaks older kernels, revert the # default to gcc-5. RUN rm -rf /usr/bin/gcc && ln -s /usr/bin/gcc-5 /usr/bin/gcc RUN rm -rf /usr/bin/clang \ && rm -rf /usr/bin/llc \ && ln -s /usr/bin/clang-7 /usr/bin/clang \ && ln -s /usr/bin/llc-7 /usr/bin/llc RUN curl -s https://falco.org/repo/falcosecurity-packages.asc | apt-key add - \ && echo "deb https://download.falco.org/packages/${VERSION_BUCKET} stable main" | tee -a /etc/apt/sources.list.d/falcosecurity.list \ && apt-get update -y \ && if [ "$FALCO_VERSION" = "latest" ]; then FALCO_DRIVER_CHOICE=none apt-get install -y --no-install-recommends falco; else FALCO_DRIVER_CHOICE=none apt-get install -y --no-install-recommends falco=${FALCO_VERSION}; fi \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Change the falco config within the container to enable ISO 8601 output. ADD ./config/falco.iso8601_timeformat.yaml /etc/falco/config.d/ # Some base images have an empty /lib/modules by default # If it's not empty, docker build will fail instead of # silently overwriting the existing directory RUN rm -df /lib/modules \ && ln -s $HOST_ROOT/lib/modules /lib/modules # debian:stable head contains binutils 2.31, which generates # binaries that are incompatible with kernels < 4.16. So manually # forcibly install binutils 2.30-22 instead. RUN if [ "$TARGETARCH" = "amd64" ] ; then \ curl -L -o binutils-x86-64-linux-gnu_2.30-22_${TARGETARCH}.deb https://download.falco.org/dependencies/binutils-x86-64-linux-gnu_2.30-22_${TARGETARCH}.deb; \ else \ curl -L -o binutils-aarch64-linux-gnu_2.30-22_${TARGETARCH}.deb https://download.falco.org/dependencies/binutils-aarch64-linux-gnu_2.30-22_${TARGETARCH}.deb; \ fi RUN curl -L -o binutils_2.30-22_${TARGETARCH}.deb https://download.falco.org/dependencies/binutils_2.30-22_${TARGETARCH}.deb \ && curl -L -o libbinutils_2.30-22_${TARGETARCH}.deb https://download.falco.org/dependencies/libbinutils_2.30-22_${TARGETARCH}.deb \ && curl -L -o binutils-common_2.30-22_${TARGETARCH}.deb https://download.falco.org/dependencies/binutils-common_2.30-22_${TARGETARCH}.deb \ && dpkg -i *binutils*.deb \ && rm -f *binutils*.deb COPY docker/driver-loader-buster/docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] ================================================ FILE: docker/driver-loader-buster/docker-entrypoint.sh ================================================ #!/usr/bin/env bash # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2026 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. # 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-buster [driver] [options]" echo "" echo "Available drivers:" echo " auto leverage automatic driver selection logic (default)" echo " modern_ebpf modern eBPF CORE probe" echo " kmod kernel module" 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 " --http-insecure enable insecure downloads" 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 " FALCOCTL_DRIVER_HTTP_HEADERS specify comma separated list of http headers for driver download (e.g. 'x-emc-namespace: default,Proxy-Authenticate: Basic')" echo "" } echo "* Setting up /usr/src links from host" for i in "$HOST_ROOT/usr/src"/* do base=$(basename "$i") ln -s "$i" "/usr/src/$base" done ENABLE_COMPILE="false" ENABLE_DOWNLOAD="false" HTTP_INSECURE="false" driver= has_opts= while test $# -gt 0; do case "$1" in auto|kmod|modern_ebpf) if [ -n "$driver" ]; then >&2 echo "Only one driver per invocation" print_usage exit 1 else driver=$1 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" ;; --http-insecure) HTTP_INSECURE="true" ;; --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 # No opts passed, enable both compile and download if [ -z "$has_opts" ]; then ENABLE_COMPILE="true" ENABLE_DOWNLOAD="true" fi # Default value: auto if [ -z "$driver" ]; then driver="auto" fi if [ "$driver" != "auto" ]; then /usr/bin/falcoctl driver config --type $driver else # Needed because we need to configure Falco to start with correct driver /usr/bin/falcoctl driver config --type modern_ebpf --type kmod fi /usr/bin/falcoctl driver install --compile=$ENABLE_COMPILE --download=$ENABLE_DOWNLOAD --http-insecure=$HTTP_INSECURE --http-headers="$FALCOCTL_DRIVER_HTTP_HEADERS" ================================================ FILE: docker/falco/Dockerfile ================================================ FROM cgr.dev/chainguard/wolfi-base ARG FALCO_COMMIT_SHA ARG FALCO_VERSION LABEL org.opencontainers.image.authors='The Falco Authors https://falco.org' \ org.opencontainers.image.url='https://falco.org' \ org.opencontainers.image.source='https://github.com/falcosecurity/falco' \ org.opencontainers.image.vendor='Falco Organization' \ org.opencontainers.image.licenses='Apache-2.0' \ org.opencontainers.image.revision=${FALCO_COMMIT_SHA} \ org.opencontainers.image.version=${FALCO_VERSION} \ maintainer="cncf-falco-dev@lists.cncf.io" LABEL usage="docker run -i -t --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v /proc:/host/proc:ro -v /etc:/host/etc:ro falcosecurity/falco:latest" # NOTE: for the "least privileged" use case, please refer to the official documentation ARG VERSION_BUCKET=bin ARG HOST_ROOT=/host ARG HOME=/root ENV FALCO_VERSION="${FALCO_VERSION}" \ VERSION_BUCKET="${VERSION_BUCKET}" \ HOST_ROOT="${HOST_ROOT}" \ HOME="${HOME}" RUN apk update && apk add curl ca-certificates jq libstdc++ WORKDIR / RUN ARCH=$(uname -m) && \ FALCO_VERSION_URLENCODED=$(echo -n "${FALCO_VERSION}" | jq -sRr @uri) && \ echo "Downloading Falco ${FALCO_VERSION} for ${ARCH}" && \ curl -L -o falco.tar.gz \ https://download.falco.org/packages/${VERSION_BUCKET}/${ARCH}/falco-${FALCO_VERSION_URLENCODED}-${ARCH}.tar.gz && \ tar -xvf falco.tar.gz && \ rm -f falco.tar.gz && \ mv falco-${FALCO_VERSION}-${ARCH} falco && \ rm -rf /falco/usr/src/falco-* && \ cp -r /falco/* / && \ rm -rf /falco && \ rm -rf /usr/bin/falcoctl /etc/falcoctl/ # Change the falco config within the container to enable ISO 8601 output. ADD config/falco.iso8601_timeformat.yaml /etc/falco/config.d/ CMD ["/usr/bin/falco"] ================================================ FILE: docker/falco-debian/Dockerfile ================================================ FROM debian:12-slim ARG FALCO_COMMIT_SHA ARG FALCO_VERSION LABEL org.opencontainers.image.authors='The Falco Authors https://falco.org' \ org.opencontainers.image.url='https://falco.org' \ org.opencontainers.image.source='https://github.com/falcosecurity/falco' \ org.opencontainers.image.vendor='Falco Organization' \ org.opencontainers.image.licenses='Apache-2.0' \ org.opencontainers.image.revision=${FALCO_COMMIT_SHA} \ org.opencontainers.image.version=${FALCO_VERSION} \ maintainer="cncf-falco-dev@lists.cncf.io" LABEL usage="docker run -i -t --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v /proc:/host/proc:ro -v /etc:/host/etc:ro falcosecurity/falco:latest-debian" ARG VERSION_BUCKET=deb ARG HOST_ROOT=/host ARG HOME=/root ENV FALCO_VERSION="${FALCO_VERSION}" \ VERSION_BUCKET="${VERSION_BUCKET}" \ HOST_ROOT="${HOST_ROOT}" \ HOME="${HOME}" RUN apt-get -y update && apt-get -y install curl jq ca-certificates gnupg2 \ && apt clean -y && rm -rf /var/lib/apt/lists/* WORKDIR / RUN curl -s https://falco.org/repo/falcosecurity-packages.asc | apt-key add - \ && echo "deb https://download.falco.org/packages/${VERSION_BUCKET} stable main" | tee -a /etc/apt/sources.list.d/falcosecurity.list \ && apt-get update -y \ && if [ "$FALCO_VERSION" = "latest" ]; then FALCO_DRIVER_CHOICE=none apt-get install -y --no-install-recommends falco; else FALCO_DRIVER_CHOICE=none apt-get install -y --no-install-recommends falco=${FALCO_VERSION}; fi \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Change the falco config within the container to enable ISO 8601 output. ADD config/falco.iso8601_timeformat.yaml /etc/falco/config.d/ CMD ["/usr/bin/falco"] ================================================ FILE: falco.yaml ================================================ # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2025 The Falco Authors. # # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # 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. # ############################# # Documentation style guide # ############################# # This file uses a structured, helm-docs–friendly comment style: # - Macro sections use banner headers to group topics. # - Feature titles use "# [Maturity] `key`" and leaf docs use "# -- ..." lines. # - Subsections use "################### `subkey`" and may include "--- [Usage]/[Suggestions]". # - For booleans, add a "# -- Enable ..." line above "enabled:" flags. # - Empty string values indicate "unset"; comments may clarify semantics. # Note: This style is a superset of Helm-docs and is fully compatible with helm-docs, # because this file is also consumed by the Falco Helm chart. ################ # Config index # ################ # Here is an index of the configuration categories to help you navigate # the Falco configuration file: # # (Falco command-line arguments) # (Falco environment variables) # Falco config files settings # config_files [Stable] # watch_config_files [Stable] # Falco rules # rules_files [Stable] # rules [Incubating] # Falco engine # engine [Stable] # Falco captures # capture [Sandbox] # Falco plugins # load_plugins [Stable] # plugins [Stable] # Falco outputs settings # time_format_iso_8601 [Stable] # buffer_format_base64 [Stable] # priority [Stable] # json_output [Stable] # json_include_output_property [Stable] # json_include_message_property [Stable] # json_include_output_fields_property [Stable] # json_include_tags_property [Stable] # buffered_outputs [Stable] # rule_matching [Incubating] # outputs_queue [Stable] # append_output [Stable] # Falco outputs channels # stdout_output [Stable] # syslog_output [Stable] # file_output [Stable] # http_output [Stable] # program_output [Stable] # Falco exposed services # webserver [Stable] # Falco logging / alerting / metrics related to software functioning (basic) # log_stderr [Stable] # log_syslog [Stable] # log_level [Stable] # libs_logger [Stable] # Falco logging / alerting / metrics related to software functioning (advanced) # output_timeout [Stable] # syscall_event_timeouts [Stable] # syscall_event_drops [Stable] -> [CHANGE NOTICE] Automatic notifications will be simplified in Falco 0.38! If you depend on the detailed drop counters payload, use 'metrics.output_rule' along with 'metrics.kernel_event_counters_enabled' instead # metrics [Stable] # Falco performance tuning (advanced) # base_syscalls [Stable] # Falco libs # falco_libs [Incubating] ######################## # Config maturity tags # ######################## # As per features adoption and deprecation proposal we support 4 levels of configuration keys maturity: # * Sandbox -> Experimental/alpha features, not recommended for production use, can be removed at any time without further notice. # * Incubating -> Beta features, long-term support is not guaranteed. # * Stable -> General Availability (GA) features for which long-term support is expected. # * Deprecated -> Deprecated keys, soon to be removed. # # For more info, please take a look at the proposal: https://github.com/falcosecurity/falco/blob/master/proposals/20231220-features-adoption-and-deprecation.md. ################################ # Falco command-line arguments # ################################ # To explore the latest command-line arguments supported by Falco for additional # configuration, you can run `falco --help` in your terminal. You can also pass # configuration options from this config file as command-line arguments by using # the `-o` flag followed by the option name and value. In the following example, # three config options (`json_output`, `log_level`, and # `engine.kind`) are passed as command-line # arguments with their corresponding values: falco -o "json_output=true" # -o "log_level=debug" -o "engine.kind=kmod" # Please note that command-line arguments take precedence over the options # specified in this config file. ############################### # Falco environment variables # ############################### # Customize Falco settings using environment variables: # # - HOST_ROOT: Specifies the prefix to the underlying host `/proc` filesystem # when deploying Falco over a container with read-only host mounts instead of # directly on the host. Defaults to "/host". # # - FALCO_HOSTNAME: Customize the hostname output field logged by Falco by # setting the "FALCO_HOSTNAME" environment variable. # # - FALCO_CGROUP_MEM_PATH: Specifies the file path holding the container # memory usage metric for the `metrics` feature. Defaults to # "/sys/fs/cgroup/memory/memory.usage_in_bytes" (Kubernetes). # # - SKIP_DRIVER_LOADER is used by the Falco fat image to skip the driver loading part. # # - FALCO_FRONTEND is useful when set to noninteractive to skip the dialog choice during # the installation of Falco deb/rpm packages. This setting is somewhat similar to DEBIAN_FRONTEND. # # - FALCO_DRIVER_CHOICE is useful when set to kmod, ebpf, or modern_ebpf (matching the names # used in engine.kind in the Falco config) during the installation of Falco deb/rpm packages. # It skips the dialog choice but retains the driver configuration. # # - FALCOCTL_ENABLED is useful when set to 'no' during the installation of Falco deb/rpm packages, # disabling the automatic artifacts followed by falcoctl. ############################### # Falco config files settings # ############################### # [Stable] `config_files` # # -- Allow to load additional configs files, beside the main one. # # Their loading is assumed to be made *after* main config file has been processed, # exactly in the order they are specified. # Therefore, loaded config files *can* override values from main config file. # Also, nested include is not allowed, ie: included config files won't be able to include other config files. # # Like for 'rules_files', specifying a folder will load all the configs files present in it in a lexicographical order. # # 3 merge-strategies are available: # `append` (default): # * existing sequence keys will be appended # * existing scalar keys will be overridden # * non-existing keys will be added # `override`: # * existing keys will be overridden # * non-existing keys will be added # `add-only`: # * existing keys will be ignored # * non-existing keys will be added # # Each item on the list can be either a yaml map or a simple string. # The simple string will be interpreted as the config file path, and the `append` merge-strategy will be enforced. # When the item is a yaml map instead, it will be of the form: ` path: foo\n strategy: X`. # When `strategy` is omitted, once again `append` is used. # # When a merge-strategy is enabled for a folder entry, all the included config files will use that merge-strategy. config_files: - /etc/falco/config.d # Example of config file specified as yaml map with strategy made explicit. # - path: $HOME/falco_local_configs/ # strategy: add-only # [Stable] `watch_config_files` # # -- Falco monitors configuration and rules files for changes and automatically # reloads itself to apply the updated configuration when any modifications are # detected. This feature is particularly useful when you want to make real-time # changes to the configuration or rules of Falco without interrupting its # operation or losing its state. For more information about Falco's state # engine, please refer to the `base_syscalls` section. watch_config_files: true ############### # Falco rules # ############### # [Stable] `rules_files` # # -- The locations of rules files (or directories) to load. # # If the entry is a yaml file, it will be read directly. If the entry is a directory, # all yaml files within that directory will be read in alphabetical order. # # The falco_rules.yaml file ships with the Falco package and is overridden with # every new software version. falco_rules.local.yaml is only created if it # doesn't already exist. # # To customize the set of rules, you can add your modifications to any file. # It's important to note that the files or directories are read in the order # specified here. In addition, rules are loaded by Falco in the order they # appear within each rule file. # # If you have any customizations intended to override a previous configuration, # make sure they appear in later files to take precedence. On the other hand, if # the conditions of rules with the same event type(s) have the potential to # overshadow each other, ensure that the more important rule appears first. This # is because rules are evaluated on a "first match wins" basis, where the first # rule that matches the conditions will be applied, and subsequent rules will # not be evaluated for the same event type. # # By arranging the order of files and rules thoughtfully, you can ensure that # desired customizations and rule behaviors are prioritized and applied as # intended. # # With Falco 0.36 and beyond, it's now possible to apply multiple rules that match # the same event type, eliminating concerns about rule prioritization based on the # "first match wins" principle. However, enabling the `all` matching option may result # in a performance penalty. We recommend carefully testing this alternative setting # before deploying it in production. Read more under the `rule_matching` configuration. # # Since Falco 0.41 only files with .yml and .yaml extensions are considered, # including directory contents. This means that you may specify directories that # contain yaml files for rules and other files which will be ignored. # # NOTICE: Before Falco 0.38, this config key was `rules_file` (singular form), # which is now deprecated in favor of `rules_files` (plural form). rules_files: - /etc/falco/falco_rules.yaml - /etc/falco/falco_rules.local.yaml - /etc/falco/rules.d # [Incubating] `rules` # # -- Falco rules can be enabled or disabled by name (with wildcards *) and/or by tag. # # This configuration is applied after all rules files have been loaded, including # their overrides, and will take precedence over the enabled/disabled configuration # specified or overridden in the rules files. # # The ordering matters and selections are evaluated in order. For instance, if you # need to only enable a rule you would first disable all of them and then only # enable what you need, regardless of the enabled status in the files. # # --- [Examples] # # Only enable two rules: # # rules: # - disable: # rule: "*" # - enable: # rule: Netcat Remote Code Execution in Container # - enable: # rule: Delete or rename shell history # # Disable all rules with a specific tag: # # rules: # - disable: # tag: network # ################ # Falco engine # ################ # [Stable] `engine` # # -- Falco supports different engines to generate events. # Choose the appropriate engine kind based on your system's configuration and requirements. # # Available engines: # - `kmod`: Kernel Module # - `modern_ebpf`: Modern eBPF (CO-RE eBPF probe) # - `replay`: Replay a scap trace file # - `nodriver`: No driver is injected into the system. # This is useful to debug and to run plugins with 'syscall' source. # # Only one engine can be specified in the `kind` key. # Moreover, for each engine multiple options might be available, # grouped under engine-specific configuration keys. # Some of them deserve an in-depth description: # ################### `buf_size_preset` # # The syscall buffer index determines the size of the shared space between Falco # and its drivers. This shared space serves as a temporary storage for syscall # events, allowing them to be transferred from the kernel to the userspace # efficiently. The buffer size for each online CPU is determined by the buffer # index, and each CPU has its own dedicated buffer. Adjusting this index allows # you to control the overall size of the syscall buffers. # # --- [Usage] # # The index 0 is reserved, and each subsequent index corresponds to an # increasing size in bytes. For example, index 1 corresponds to a size of 1 MB, # index 2 corresponds to 2 MB, and so on: # # [(*), 1 MB, 2 MB, 4 MB, 8 MB, 16 MB, 32 MB, 64 MB, 128 MB, 256 MB, 512 MB] # ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ # | | | | | | | | | | | # 0 1 2 3 4 5 6 7 8 9 10 # # # The buffer dimensions in bytes are determined by the following requirements: # (1) a power of 2. # (2) a multiple of your system_page_dimension. # (3) greater than `2 * (system_page_dimension). # # The buffer size constraints may limit the usability of certain indexes. Let's # consider an example to illustrate this: # # If your system has a page size of 1 MB, the first available buffer size would # be 4 MB because 2 MB is exactly equal to 2 * (system_page_size), which is not # sufficient as we require more than 2 * (system_page_size). In this example, it # is evident that if the page size is 1 MB, the first index that can be used is 3. # # However, in most cases, these constraints do not pose a limitation, and all # indexes from 1 to 10 can be used. You can check your system's page size using # the Falco `--page-size` command-line option. # # --- [Suggestions] # # The buffer size was previously fixed at 8 MB (index 4). You now have the # option to adjust the size based on your needs. Increasing the size, such as to # 16 MB (index 5), can reduce syscall drops in heavy production systems, but may # impact performance. Decreasing the size can speed up the system but may # increase syscall drops. It's important to note that the buffer size is mapped # twice in the process' virtual memory, so a buffer of 8 MB will result in a 16 # MB area in virtual memory. Use this parameter with caution and only modify it # if the default size is not suitable for your use case. # ################### `drop_failed_exit` # # Enabling this option in Falco allows it to drop failed system call exit events # in the kernel drivers before pushing them onto the ring buffer. This # optimization can result in lower CPU usage and more efficient utilization of # the ring buffer, potentially reducing the number of event losses. However, it # is important to note that enabling this option also means sacrificing some # visibility into the system. # ################### `cpus_for_each_buffer` (modern_ebpf only) # # The modern_ebpf driver in Falco utilizes the new BPF ring buffer. The Falco # core maintainers have discussed the differences and their implications, # particularly in Kubernetes environments where limits need to be carefully set # to avoid interference with the Falco daemonset deployment from the OOM # killer. Based on guidance received from the kernel mailing list, it is # recommended to assign multiple CPUs to one buffer instead of allocating a # buffer for each CPU individually. This helps optimize resource allocation and # prevent potential issues related to memory usage. # # This is an index that controls how many CPUs you want to assign to a single # syscall buffer (ring buffer). By default, for modern_ebpf every syscall buffer # is associated to 2 CPUs, so the mapping is 1:2. The modern BPF probe allows # you to choose different mappings, for example, changing the value to `1` # results in a 1:1 mapping and would mean one syscall buffer for each CPU (this # is the default for the `bpf` driver). # # --- [Usage] # # You can choose an index from 0 to MAX_NUMBER_ONLINE_CPUs to set the dimension # of the syscall buffers. The value 0 represents a single buffer shared among # all online CPUs. It serves as a flexible option when the exact number of # online CPUs is unknown. Here's an example to illustrate this: # # Consider a system with 7 online CPUs: # # CPUs 0 X 2 3 X X 6 7 8 9 (X means offline CPU) # # - `1` means a syscall buffer for each CPU so 7 buffers # # CPUs 0 X 2 3 X X 6 7 8 9 (X means offline CPU) # | | | | | | | # BUFFERs 0 1 2 3 4 5 6 # # - `2` (Default value) means a syscall buffer for each CPU pair, so 4 buffers # # CPUs 0 X 2 3 X X 6 7 8 9 (X means offline CPU) # | | | | | | | # BUFFERs 0 0 1 1 2 2 3 # # Please note that in this example, there are 4 buffers in total. Three of the # buffers are associated with pairs of CPUs, while the last buffer is mapped to # a single CPU. This arrangement is necessary because we have an odd number of # CPUs. # # - `0` or `MAX_NUMBER_ONLINE_CPUs` mean a syscall buffer shared between all # CPUs, so 1 buffer # # CPUs 0 X 2 3 X X 6 7 8 9 (X means offline CPU) # | | | | | | | # BUFFERs 0 0 0 0 0 0 0 # # Moreover, you have the option to combine this parameter with # `buf_size_preset` index. For instance, you can create a large shared # syscall buffer of 512 MB (using buf_size_preset=10) that is # allocated among all the online CPUs. # # --- [Suggestions] # # The default choice of index 2 (one syscall buffer for each CPU pair) was made # because the modern bpf probe utilizes a different memory allocation strategy # compared to the other two drivers (bpf and kernel module). However, you have # the flexibility to experiment and find the optimal configuration for your # system. # # When considering a fixed buf_size_preset and a fixed buffer dimension: # - Increasing this configs value results in lower number of buffers and you can # speed up your system and reduce memory usage # - However, using too few buffers may increase contention in the kernel, # leading to a slowdown. # # If you have low event throughputs and minimal drops, reducing the number of # buffers (higher `cpus_for_each_buffer`) can lower the memory footprint. # engine: # -- The kind of engine to use. kind: modern_ebpf # -- Engine-specific configuration for Kernel Module (kmod) engine. kmod: buf_size_preset: 4 drop_failed_exit: false # -- Engine-specific configuration for Modern eBPF (modern_ebpf) engine. modern_ebpf: cpus_for_each_buffer: 2 buf_size_preset: 4 drop_failed_exit: false # -- Engine-specific configuration for replay engine which replays a capture file. replay: # -- Path to the capture file to replay (eg: /path/to/file.scap) capture_file: "" ################## # Falco captures # ################## # [Sandbox] `capture` # # -- Falco captures allow you to record events and their associated data for # later analysis. This feature is particularly useful for debugging and # forensics purposes. # # Captures operate in two modes: # # 1. `rules`: Captures events only when specific rules are triggered. # Enable capturing for individual rules by adding `capture: true` to the rule. # # 2. `all_rules`: Captures events when any enabled rule is triggered. # # When a capture starts, Falco records events from the moment the triggering rule # fires until the deadline is reached. The deadline is determined by the rule's # `capture_duration` if specified, otherwise the `default_duration` is used. # If additional rules trigger during an active capture, the deadline is extended # accordingly. Once the deadline expires, the capture stops and data is written # to a file. Subsequent captures create new files with unique names. # # Captured data is stored in files with a `.scap` extension, which can be # analyzed later using: # falco -o engine.kind=replay -o replay.capture_file=/path/to/file.scap # # --- [Usage] # # Enable captures by setting `capture.enabled` to `true`. # # Configure `capture.path_prefix` to specify where capture files are stored. # Falco generates unique filenames based on timestamp and event number for # proper ordering. For example, with `path_prefix: /tmp/falco`, files are # named like `/tmp/falco_00000001234567890_00000000000000042.scap`. # # Use `capture.mode` to choose between `rules` and `all_rules` modes. # # Set `capture.default_duration` to define the default capture duration # in milliseconds. # # --- [Suggestions] # # When using `mode: rules`, configure individual rules to enable capture by # adding `capture: true` and optionally `capture_duration` to specific rules. # For example: # # - rule: Suspicious File Access # desc: Detect suspicious file access patterns # condition: > # open_read and fd.name startswith "/etc/" # output: > # Suspicious file access (user=%user.name command=%proc.cmdline file=%fd.name) # priority: WARNING # capture: true # capture_duration: 10000 # Capture for 10 seconds when this rule triggers # # This configuration will capture events for 10 seconds whenever the # "Suspicious File Access" rule is triggered, overriding the default duration. capture: # -- Set to true to enable event capturing. enabled: false # -- Prefix for capture files. Falco appends a timestamp and event number to ensure unique filenames. path_prefix: /tmp/falco # -- Capture mode. Can be "rules" or "all_rules". mode: rules # -- Default capture duration in milliseconds if not specified in the rule. default_duration: 5000 ################# # Falco plugins # ################# # Plugins allow Falco to extend its functionality and leverage data sources such as # Kubernetes audit logs or enable integration with other services in your ecosystem. # This enables Falco to perform fast on-host detections beyond syscalls. # Furthermore, plugins allow enriching existing data sources with additional metadata, # and add other advanced capabilities to Falco. # # The plugin system will continue to evolve with more specialized functionality in future # releases. # # Please refer to the plugins repo at https://github.com/falcosecurity/plugins for detailed # documentation on the available plugins. This repository provides comprehensive # information about each plugin and how to utilize them with Falco. # # Please note that if your intention is to enrich Falco syscall logs with fields # such as `k8s.ns.name`, `k8s.pod.name`, and `k8s.pod.*`, you do not need to use # the `k8saudit` plugin. This information is automatically extracted from # the container runtime socket by the 'container' plugin. # The `k8saudit` plugin is specifically designed to integrate with Kubernetes audit logs # and is not required for basic enrichment of syscall logs with Kubernetes-related fields. # # --- [Usage] # # Disabled by default, indicated by an empty `load_plugins` list. Each plugin meant # to be enabled needs to be listed as explicit list item. # # For example, if you want to use the `k8saudit` plugin, # ensure it is configured appropriately and then change this to: # load_plugins: [k8saudit, json] # [Stable] `load_plugins` # # -- List of plugins to load. load_plugins: [] # [Stable] `plugins` # # -- Customize subsettings for each enabled plugin. These settings will only be # applied when the corresponding plugin is enabled using the `load_plugins` # option. plugins: - name: container # For a summary of config option, see https://github.com/falcosecurity/plugins/tree/main/plugins/container#configuration library_path: libcontainer.so init_config: label_max_len: 100 with_size: false # We use default config values for "engines" key. # Configuration example for `k8saudit` plugin. # - name: k8saudit # library_path: libk8saudit.so # init_config: "" # # maxEventSize: 262144 # # webhookMaxBatchSize: 12582912 # # sslCertificate: /etc/falco/falco.pem # open_params: "http://:9765/k8s-audit" # - name: json # library_path: libjson.so # [Sandbox] `plugins_hostinfo` # # -- Control host info support for plugins. # When `false`, it disables host info support for source plugins # that DO NOT generate raw events from the libscap event table # or for plugins that DO NOT parse raw events generated by drivers, # effectively dropping the `proc-fs` hostPath volume requirement for them: # https://github.com/falcosecurity/charts/blob/bd57711e7c8e00919ea288716e0d9d5fdad8867e/charts/falco/templates/pod-template.tpl#L302-L304 plugins_hostinfo: true ########################## # Falco outputs settings # ########################## # [Stable] `time_format_iso_8601` # # -- When enabled, Falco will display log and output messages with times in the ISO # 8601 format. By default, times are shown in the local time zone determined by # the /etc/localtime configuration. time_format_iso_8601: false # [Incubating] `buffer_format_base64` # # -- When enabled, Falco will output data buffer with base64 encoding. This is useful # for encoding binary data that needs to be used over media designed to consume # this format. buffer_format_base64: false # [Stable] `priority` # # -- Any rule with a priority level more severe than or equal to the specified # minimum level will be loaded and run by Falco. This allows you to filter and # control the rules based on their severity, ensuring that only rules of a # certain priority or higher are active and evaluated by Falco. Supported # levels: "emergency", "alert", "critical", "error", "warning", "notice", # "info", "debug" priority: debug # [Stable] `json_output` # # -- When enabled, Falco will output alert messages and rules file # loading/validation results in JSON format, making it easier for downstream # programs to process and consume the data. By default, this option is disabled. json_output: false # [Stable] `json_include_output_property` # # -- When using JSON output in Falco, you have the option to include the "output" # property itself in the generated JSON output. The "output" property provides # additional information about the purpose of the rule. To reduce the logging # volume, it is recommended to turn it off if it's not necessary for your use # case. json_include_output_property: true # [Incubating] `json_include_message_property` # # -- When using JSON output in Falco, you have the option to include the formatted # rule output without timestamp or priority. For instance, if a rule specifies # an "output" property like "Opened process %proc.name" the "message" field will # only contain "Opened process bash" whereas the "output" field will contain more # information. json_include_message_property: false # [Incubating] `json_include_output_fields_property` # # -- When using JSON output in Falco, you have the option to include the individual # output fields for easier access. To reduce the logging volume, it is recommended # to turn it off if it's not necessary for your use case. json_include_output_fields_property: true # [Stable] `json_include_tags_property` # # -- When using JSON output in Falco, you have the option to include the "tags" # field of the rules in the generated JSON output. The "tags" field provides # additional metadata associated with the rule. To reduce the logging volume, # if the tags associated with the rule are not needed for your use case or can # be added at a later stage, it is recommended to turn it off. json_include_tags_property: true # [Stable] `buffered_outputs` # # -- Global buffering option for output channels. When disabled, the output channel # that supports buffering flushes the output buffer on every alert. This can lead to # increased CPU usage but is useful when piping outputs to another process or script. # Buffering is currently supported by `file_output`, `program_output`, and `stdout_output`. # Some output channels may implement buffering strategies you cannot control. # Additionally, this setting is separate from the `output_queue` option. The output queue # sits between the rule engine and the output channels, while output buffering occurs # afterward once the specific channel implementation outputs the formatted message. buffered_outputs: false # [Incubating] `rule_matching` # # The `rule_matching` configuration key's values are: # - `first`: Falco stops checking conditions of rules against upcoming event # at the first matching rule # - `all`: Falco will continue checking conditions of rules even if a matching # one was already found # # Rules conditions are evaluated in the order they are defined in the rules files. # For this reason, when using `first` as value, only the first defined rule will # trigger, possibly shadowing other rules. # In case `all` is used as value, rules still trigger in the order they were # defined. # # Effectively, with this setting, it is now possible to apply multiple rules that match # the same event type. This eliminates concerns about rule prioritization based on the # "first match wins" principle. However, enabling the `all` matching option may result in # a performance penalty. We recommend carefully testing this alternative setting before # deploying it in production. # -- Default value is `first`, which stops checking conditions of rules against upcoming event # at the first matching rule. rule_matching: first # [Stable] `outputs_queue` # # -- Configure the output queue capacity. # # Falco utilizes tbb::concurrent_bounded_queue for handling outputs, and this parameter # allows you to customize the queue capacity. Please refer to the official documentation: # https://uxlfoundation.github.io/oneTBB/main/tbb_userguide/Concurrent_Queue_Classes.html. # On a healthy system with optimized Falco rules, the queue should not fill up. # If it does, it is most likely happening due to the entire event flow being too slow, # indicating that the server is under heavy load. # # In the case of an unbounded queue, if the available memory on the system is consumed, # the Falco process would be OOM killed. When using this option and setting the capacity, # the current event would be dropped, and the event loop would continue. This behavior mirrors # kernel-side event drops when the buffer between kernel space and user space is full. outputs_queue: # -- The maximum number of items allowed in the queue is determined by this value. # Setting the value to 0 (which is the default) is equivalent to keeping the queue unbounded. # In other words, when this configuration is set to 0, the number of allowed items is # effectively set to the largest possible long value, disabling this setting. capacity: 0 # [Sandbox] `append_output` # # -- Add information to the Falco output. # With this setting you can add more information to the Falco output message, customizable by # rule, tag or source. # You can also add additional data that will appear in the output_fields property # of JSON formatted messages but will not be part of the regular output message. # This allows you to add custom fields that can help you filter your Falco events without # polluting the message text. # # Each append_output entry has an optional `match` map which specifies which rules will be # affected. # `match`: # `rule`: append output only to a specific rule # `source`: append output only to a specific source # `tags`: append output only to rules that have all of the specified tags # If none of the above are specified (or `match` is omitted) # output is appended to all events. # If more than one match condition is specified output will be appended to events # that match all conditions. # And several options to add output: # `extra_output`: add output to the Falco message # `extra_fields`: add new fields to the JSON output and structured output, which will not # affect the regular Falco message in any way. These can be specified as a # custom name with a custom format or as any supported field # (see: https://falco.org/docs/reference/rules/supported-fields/) # `suggested_output`: automatically append fields that are suggested to rules output # # Example: # # append_output: # - match: # source: syscall # extra_output: "on CPU %evt.cpu" # extra_fields: # - home_directory: "${HOME}" # - evt.hostname # # In the example above every event coming from the syscall source will get an extra message # at the end telling the CPU number. In addition, if `json_output` is true, in the "output_fields" # property you will find three new ones: "evt.cpu", "home_directory" which will contain the value of the # environment variable $HOME, and "evt.hostname" which will contain the hostname. # # By default, we enable suggested_output for any source. # This means that any extractor plugin that indicates some of its fields # as suggested output formats, will see these fields in the output # in the form "foo_bar=$foo.bar" append_output: # -- Automatically append fields that are suggested to rules output. - suggested_output: true # [Sandbox] `static_fields` # # Add statically defined fields to the Falco engine. # Then, they can be used as normal rule conditions, by prepending `static.` prefix, # eg: evt.type=open and static.foo=bar # Also, if `append_output.suggested_output` is true, # they'll be automatically appended to each rule output, # in the form "static_foo=bar" # static_fields: # foo: bar # foo2: ${env} ########################## # Falco outputs channels # ########################## # Falco supports various output channels, such as syslog, stdout, file, # webhook, and more. You can enable or disable these channels as needed to # control where Falco alerts and log messages are directed. This flexibility # allows seamless integration with your preferred logging and alerting systems. # Multiple outputs can be enabled simultaneously. # [Stable] `stdout_output` # # -- Send alerts to standard output. stdout_output: # -- Enable sending alerts to standard output. enabled: true # [Stable] `syslog_output` # # -- Send alerts to syslog. syslog_output: # -- Enable sending alerts to syslog. enabled: true # [Stable] `file_output` # # -- Send alerts to a file. # Each new alert will be added to a new line. # It's important to note that Falco does not perform log rotation for this # file. Furthermore, the file will be closed and reopened if Falco receives # the SIGUSR1 signal. file_output: # -- Enable sending alerts to a file. enabled: false # -- If true, the file will be opened once and continuously written to. # If false, the file will be reopened for each output message. keep_alive: false # -- Path to the file where alerts will be appended. filename: ./events.txt # [Stable] `http_output` # # -- Send alerts to an HTTP endpoint or webhook. # # When using falcosidekick, it is necessary to set `json_output` to true, which is # conveniently done automatically for you when using `falcosidekick.enabled=true`. http_output: # -- Enable sending alerts to an HTTP endpoint or webhook. enabled: false # -- URL of the remote server to send the alerts to. url: "" # -- User agent string to be used in the HTTP request. user_agent: "falcosecurity/falco" # -- Tell Falco to not verify the remote server. insecure: false # -- Path to the CA certificate that can verify the remote server. ca_cert: "" # -- Path to a specific file that will be used as the CA certificate store. ca_bundle: "" # -- Path to a folder that will be used as the CA certificate store. CA certificate need to be # stored as individual PEM files in this directory. ca_path: "/etc/ssl/certs" # -- Tell Falco to use mTLS. mtls: false # -- Path to the client cert. client_cert: "/etc/ssl/certs/client.crt" # -- Path to the client key. client_key: "/etc/ssl/certs/client.key" # -- Whether to echo server answers to stdout. echo: false # -- Whether to compress the payload sent to the http server. compress_uploads: false # -- If true, the HTTP connection will be kept alive and reused. keep_alive: false # -- Maximum consecutive timeouts of libcurl to ignore. max_consecutive_timeouts: 5 # [Stable] `program_output` # # -- Send alerts to another program or command. # # Possible additional things you might want to do with program output: # - send to a slack webhook: # program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/XXX" # - logging (alternate method than syslog): # program: logger -t falco-test # - send over a network connection: # program: nc host.example.com 80 # # The program will be re-spawned if Falco receives the SIGUSR1 signal. program_output: # -- Enable sending alerts to another program or command. enabled: false # -- If true, the program will be started once and continuously written to, # with each output message on its own line. # If false, the program will be re-spawned for each output message. keep_alive: false # -- The program to execute. program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/XXX" ########################## # Falco exposed services # ########################## # [Stable] `webserver` # # -- Falco supports an embedded webserver that runs within the Falco process, # providing a lightweight and efficient way to expose web-based functionalities # without the need for an external web server. The following endpoints are # exposed: # - /healthz: designed to be used for checking the health and availability of # the Falco application (the name of the endpoint is configurable). # - /versions: responds with a JSON object containing the version numbers of the # internal Falco components (similar output as `falco --version -o # json_output=true`). # # Please note that the /versions endpoint is particularly useful for other Falco # services, such as `falcoctl`, to retrieve information about a running Falco # instance. If you plan to use `falcoctl` locally or with Kubernetes, make sure # the Falco webserver is enabled. # webserver: # -- Enable the embedded webserver. enabled: true # -- When the `threadiness` value is set to 0, Falco will automatically determine # the appropriate number of threads based on the number of online cores in the system. threadiness: 0 # -- Port to listen on. listen_port: 8765 # -- Address to listen on. # Can be an IPV4 or IPV6 address, defaults to IPV4. listen_address: 0.0.0.0 # -- Endpoint to use for the health check. k8s_healthz_endpoint: /healthz # [Incubating] `webserver.prometheus_metrics_enabled` # -- Enable the metrics endpoint providing Prometheus values. # It is effective only if metrics.enabled is set to true. prometheus_metrics_enabled: false # -- Enable SSL. ssl_enabled: false # -- Path to the combined SSL certificate and key file. # You can generate a key/cert as follows: # ``` # $ openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem # $ cat certificate.pem key.pem > falco.pem $ sudo cp falco.pem /etc/falco/falco.pem # ``` ssl_certificate: /etc/falco/falco.pem ############################################################################## # Falco logging / alerting / metrics related to software functioning (basic) # ############################################################################## # [Stable] `log_stderr` # # Falco's logs related to the functioning of the software, which are not related # to Falco alert outputs but rather its lifecycle, settings and potential # errors, can be directed to stderr and/or syslog. # # -- Send information logs to stderr. # Note that these are just Falco lifecycle (and possibly error) logs. # These are *not* alerts related to Falco outputs, so must not be considered # as security notification logs! log_stderr: true # [Stable] `log_syslog` # # -- Send information logs to syslog. # Note that these are just Falco lifecycle (and possibly error) logs. # These are *not* alerts related to Falco outputs, so must not be considered # as security notification logs! log_syslog: true # [Stable] `log_level` # # -- The `log_level` setting determines the minimum log level to include in Falco's # logs related to the functioning of the software. This setting is separate from # the `priority` field of rules and specifically controls the log level of # Falco's operational logging. By specifying a log level, you can control the # verbosity of Falco's operational logs. Only logs of a certain severity level # or higher will be emitted. Supported levels: "emergency", "alert", "critical", # "error", "warning", "notice", "info", "debug". log_level: info # [Stable] `libs_logger` # # -- The `libs_logger` setting in Falco determines the minimum log level to include # in the logs related to the functioning of the software of the underlying # `libs` library, which Falco utilizes. This setting is independent of the # `priority` field of rules and the `log_level` setting that controls Falco's # operational logs. It allows you to specify the desired log level for the `libs` # library specifically, providing more granular control over the logging # behavior of the underlying components used by Falco. Only logs of a certain # severity level or higher will be emitted. Supported levels: "fatal", # "critical", "error", "warning", "notice", "info", "debug", "trace". # It is not recommended to use "debug" and "trace" for production use. libs_logger: # -- Enable logging from the underlying `libs` library. enabled: true # -- The minimum log level to include in the `libs` logs. Only logs of a certain # severity level or higher will be emitted. Supported levels: "fatal", "critical", # "error", "warning", "notice", "info", "debug", "trace". severity: info ################################################################################# # Falco logging / alerting / metrics related to software functioning (advanced) # ################################################################################# # [Stable] `output_timeout` # # Generates Falco operational logs when `log_level=notice` at minimum # # A timeout error occurs when a process or operation takes longer to complete # than the allowed or expected time limit. In the context of Falco, an output # timeout error refers to the situation where an output channel fails to deliver # an alert within a specified deadline. Various reasons, such as network issues, # resource constraints, or performance bottlenecks can cause timeouts. # # -- The `output_timeout` parameter specifies the duration, in milliseconds, to # wait before considering the deadline exceeded. By default, the timeout is set # to 2000ms (2 seconds), meaning that the consumer of Falco outputs can block # the Falco output channel for up to 2 seconds without triggering a timeout # error. # # Falco actively monitors the performance of output channels. With this setting # the timeout error can be logged, but please note that this requires setting # Falco's operational logs `log_level` to a minimum of `notice`. # # It's important to note that Falco outputs will not be discarded from the # output queue. This means that if an output channel becomes blocked # indefinitely, it indicates a potential issue that needs to be addressed by the # user. output_timeout: 2000 # [Stable] `syscall_event_timeouts` # # Generates Falco operational logs when `log_level=notice` at minimum # # -- Falco utilizes a shared buffer between the kernel and userspace to receive # events, such as system call information, in userspace. However, there may be # cases where timeouts occur in the underlying libraries due to issues in # reading events or the need to skip a particular event. While it is uncommon # for Falco to experience consecutive event timeouts, it has the capability to # detect such situations. You can configure the maximum number of consecutive # timeouts without an event after which Falco will generate an alert, but please # note that this requires setting Falco's operational logs `log_level` to a # minimum of `notice`. The default value is set to 1000 consecutive timeouts # without receiving any events. The mapping of this value to a time interval # depends on the CPU frequency. syscall_event_timeouts: # -- The maximum number of consecutive timeouts without an event after which Falco will generate an alert. max_consecutives: 1000 # [Stable] `syscall_event_drops` # # Take actions when syscall events are dropped. # # -- Falco uses a shared buffer between the kernel and userspace to pass system # call information. When Falco detects that this buffer is full and system calls # have been dropped, it can take one or more of the following actions: # - ignore: do nothing (default when list of actions is empty) # - log: log a DEBUG message noting that the buffer was full # - alert: emit a Falco alert noting that the buffer was full # - exit: exit Falco with a non-zero rc # # Notice it is not possible to ignore and log/alert messages at the same time. # # The rate at which log/alert messages are emitted is governed by a token # bucket. The rate corresponds to one message every 30 seconds with a burst of # one message (by default). # # --- [Usage] # # Enabled by default, but requires Falco rules config `priority` set to `debug`. # Emits a Falco rule named "Falco internal: syscall event drop" as many times in # a given time period as dictated by the settings. Statistics here reflect the # delta in a 1s time period. # # If instead you prefer periodic metrics of monotonic counters at a regular # interval, which include syscall drop statistics and additional metrics, # explore the `metrics` configuration option. # # --- [Notice] # # Automatic notifications will be simplified in future Falco versions! # If you depend on the detailed drop counters payload, use 'metrics.output_rule' # along with 'metrics.kernel_event_counters_enabled' instead. syscall_event_drops: # -- The percentage of dropped syscall events with respect to the number of events in the last second # that will trigger the actions. A value in the range [0, 1]. If you want to be alerted on any drops, # set the threshold to 0. threshold: .1 # -- The list of actions to take when the threshold is exceeded. Possible values are: # `ignore`, `log`, `alert`, `exit`. If the list is empty, no action is taken. actions: - log - alert # -- The rate at which log/alert messages are emitted is governed by a token bucket. The rate # corresponds to one message every `1/rate` seconds. # The default rate corresponds to one message every 30 seconds. rate: .03333 # -- The maximum number of messages that can be emitted in a burst. max_burst: 1 # -- For debugging/testing it is possible to simulate the drops. In this case the threshold does not apply. # This is useful to test the actions taken when drops are detected. simulate_drops: false # [Stable] `metrics` # # Falco's `metrics` feature provides a comprehensive and flexible framework for # capturing and exporting metrics. # # Consider these key points about the `metrics` feature in Falco: # # - It introduces a redesigned stats/metrics system. # - Native support for resource utilization metrics and specialized performance # metrics. # - Metrics are emitted as monotonic counters at predefined intervals # (snapshots). # - All metrics are consolidated into a single log message, adhering to the # established rules schema and naming conventions. # - Additional info fields complement the metrics and facilitate customized # statistical analyses and correlations. # - The metrics framework is designed for easy future extension. # # The `metrics` feature follows a specific schema and field naming convention. # All metrics are collected as subfields under the `output_fields` key, similar # to regular Falco rules. Each metric field name adheres to the grammar used in # Falco rules. There are two new field classes introduced: `falco.` and `scap.`. # The `falco.` class represents userspace counters, statistics, resource # utilization, or useful information fields. The `scap.` class represents # counters and statistics mostly obtained from Falco's kernel instrumentation # before events are sent to userspace, but can include scap userspace stats as # well. # # It's important to note that the output fields and their names can be subject # to change until the metrics feature reaches a stable release. # In addition, the majority of fields represent an instant snapshot, with the # exception of event rates per second and drop percentage stats. These values # are computed based on the delta between two snapshots. # # To customize the hostname in Falco, you can set the environment variable # `FALCO_HOSTNAME` to your desired hostname. This is particularly useful in # Kubernetes deployments where the hostname can be set to the pod name. # # If metrics are enabled, the web server can be configured to activate the # corresponding Prometheus endpoint using `webserver.prometheus_metrics_enabled`. # Prometheus output can be used in combination with the other output options. # # --- [Warning] # # Due to a regression (https://github.com/falcosecurity/falco/issues/2821) some metrics # like `falco.host_num_cpus` or `falco.start_ts` will not be available when you use # source plugins (like k8saudit). metrics: # -- Enable metrics collection and export. enabled: false # -- Interval at which stats are collected and emitted. This follows the time duration definitions # used by Prometheus (https://prometheus.io/docs/prometheus/latest/querying/basics/#time-durations). # Time durations are specified as a number, followed immediately by one of the following units: # ms - millisecond # s - second # m - minute # h - hour # d - day - assuming a day has always 24h # w - week - assuming a week has always 7d # y - year - assuming a year has always 365d # A minimum interval of 100ms is enforced for metric collection. However, for # production environments, we recommend selecting one of the following intervals # for optimal monitoring: 15m, 30m, 1h, 4h, 6h. interval: 1h # Set `webserver.prometheus_metrics_enabled` for Prometheus output. # -- Emit stats as an output alert "Falco internal: metrics snapshot". # This option is particularly useful when Falco logs are preserved in a data # lake. Please note that to use this option, the Falco rules config `priority` # must be set to `info` at a minimum. output_rule: true # -- Append stats to a `jsonl` file. If empty, no file is written. # Use with caution in production as Falco does not automatically rotate the file. # It can be used in combination with `output_rule`, however enabling both is not typical. # e.g. output_file: /tmp/falco_stats.jsonl output_file: "" # -- Add counts for each rule to metrics output. rules_counters_enabled: true # -- Add CPU and memory usage metrics to metrics output. # Usage is reported as a percentage of one CPU and can be normalized to the total # 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 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. resource_utilization_enabled: true # -- Add Falco's internal state counters to metrics output. # Including added, removed threads or file descriptors (fds), and failed lookup, # store, or retrieve actions in relation to Falco's underlying process cache table # (a.k.a. the threadtable). state_counters_enabled: true # -- Add kernel side event and drop counters to metrics output. # This isan alternative to `syscall_event_drops`, but with some differences. # These counters reflect monotonic values since Falco's start and are exported at a # constant stats interval. kernel_event_counters_enabled: true # -- Add kernel side event and drop counters per CPU to metrics output. # Enabling this option automatically enables `kernel_event_counters_enabled`. kernel_event_counters_per_cpu_enabled: false # -- Add statistics similar to `bpftool prog show` to metrics output. # This provides information such as the number of invocations of each BPF program # attached by Falco and the time spent in each program measured in nanoseconds. # To enable this feature, the kernel must be >= 5.1, and the kernel # configuration `/proc/sys/kernel/bpf_stats_enabled` must be set. This option, # or an equivalent statistics feature, is not available for non `*bpf*` drivers. # Additionally, please be aware that the current implementation of `libbpf` does # not support granularity of statistics at the bpf tail call level. libbpf_stats_enabled: true # -- Add custom plugins metrics to metrics output. # Please note that if the respective plugin has no metrics implemented, # there will be no metrics available. plugins_metrics_enabled: true # -- Add jemalloc stats to metrics output. # This option requires that Falco is built with jemalloc support, otherwise # it will have no effect. jemalloc_stats_enabled: false # -- Convert memory metrics to megabytes. convert_memory_to_mb: true # -- Include fields with empty values in the metrics output. # Still, this option does not apply to high-level fields such as `n_evts` or `n_drops`; # they will always be included in the metrics output even if their value is empty. include_empty_values: false ####################################### # Falco performance tuning (advanced) # ####################################### # [Stable] `base_syscalls`, use with caution, read carefully # # -- This option configures the set of syscalls that Falco traces. # # --- [Falco's State Engine] # # Falco requires a set of syscalls to build up state in userspace. For example, # when spawning a new process or network connection, multiple syscalls are # involved. Furthermore, properties of a process during its lifetime can be # modified by syscalls. Falco accounts for this by enabling the collection of # additional syscalls than the ones defined in the rules and by managing a smart # process cache table in userspace. Processes are purged from this table when a # process exits. # # By default, with # ``` # base_syscalls.custom_set = [] # base_syscalls.repair = false # ``` # Falco enables tracing for a syscall set gathered: (1) from (enabled) Falco # rules (2) from a static, more verbose set defined in # `libsinsp::events::sinsp_state_sc_set` in # libs/userspace/libsinsp/events/sinsp_events_ppm_sc.cpp This allows Falco to # successfully build up it's state engine and life-cycle management. # # If the default behavior described above does not fit the user's use case for # Falco, the `base_syscalls` option allows for finer end-user control of # syscalls traced by Falco. # # --- [Usage] # # List of system calls names (), negative ("!") # notation supported. # # Example: base_syscalls.custom_set: [, , # "!"] base_syscalls.repair: # # We recommend to only exclude syscalls, e.g. "!mprotect" if you need a fast # deployment update (overriding rules), else remove unwanted syscalls from the # Falco rules. # # Passing `-o "log_level=debug" -o "log_stderr=true" --dry-run` to Falco's cmd # args will print the final set of syscalls to STDOUT. # # --- [Suggestions] # # NOTE: setting `base_syscalls.repair: true` automates the following suggestions # for you. # # These suggestions are subject to change as Falco and its state engine evolve. # # For execve* events: Some Falco fields for an execve* syscall are retrieved # from the associated `clone`, `clone3`, `fork`, `vfork` syscalls when spawning # a new process. The `close` syscall is used to purge file descriptors from # Falco's internal thread / process cache table and is necessary for rules # relating to file descriptors (e.g. open, openat, openat2, socket, connect, # accept, accept4 ... and many more) # # Consider enabling the following syscalls in `base_syscalls.custom_set` for # process rules: [clone, clone3, fork, vfork, execve, execveat, close] # # For networking related events: While you can log `connect` or `accept*` # syscalls without the socket syscall, the log will not contain the ip tuples. # Additionally, for `listen` and `accept*` syscalls, the `bind` syscall is also # necessary. # # We recommend the following as the minimum set for networking-related rules: # [clone, clone3, fork, vfork, execve, execveat, close, socket, bind, # getsockopt] # # Lastly, for tracking the correct `uid`, `gid` or `sid`, `pgid` of a process # when the running process opens a file or makes a network connection, consider # adding the following to the above recommended syscall sets: ... setresuid, # setsid, setuid, setgid, setpgid, setresgid, setsid, capset, chdir, chroot, # fchdir ... base_syscalls: # -- Custom set of syscalls to trace in addition to the ones required by enabled rules. # This is useful in lowering CPU utilization and further tailoring Falco to # specific environments according to your threat model and budget constraints. # In the `base_syscalls.custom_set` list, it is possible either to use syscall names # (e.g. "mprotect") to be included or the negative notation ("!") # for those to be excluded. # CAUTION: Misconfiguration of this setting may result in incomplete Falco event # logs, Falco being unable to trace events entirely, or Falco being unable to properly # garbage collect its internal process cache table. custom_set: [] # -- When true, Falco will automatically add the minimal set of additional # syscalls needed to properly build up (e.g. "repair") its state engine and life-cycle # management. The repair mode can be enabled with an empty custom set. # This is an alternative to Falco's default state engine enforcement and is designed to be # the most system resource-friendly by activating the least number of additional syscalls # (outside of those enabled for enabled rules). repair: false # -- Enable monitoring for all events supported by Falco and used in rules and configs. # By default some events, such as `write`, are ignored (run `falco -i` to get # the full list) unless this option is true. # Enabling this option may negatively impact performance. all: false ############## # Falco libs # ############## # [Incubating] `falco_libs` # # -- Falco's performance and resource utilization can be fine-tuned by adjusting `falco_libs` # parameters. For advanced users and specific use cases only. # falco_libs: # -- Maximum number of entries for Falco's internal threadtable (process cache). # The absolute maximum value can only be MAX UINT32. # Please note that Falco operates at a granular level, focusing on individual threads. # Falco rules reference the thread leader (i.e. the thread which spawned the thread) # as the process. The size of the threadtable should typically be much higher than the # number of currently alive processes. The default value should work well on modern # infrastructures and be sufficient to absorb bursts. # Reducing its size can help in better memory management, but as a consequence, your # process tree may be more frequently disrupted due to missing threads. You can explore # `metrics.state_counters_enabled` to measure how the internal state handling is performing, # and the fields called `n_drops_full_threadtable` or `n_store_evts_drops` will inform you # if you should increase this value for optimal performance. thread_table_size: 262144 # -- Interval at which the automatic threads purging routine runs, in seconds. # Theautomatic threads purging is essential for Falco to remove stale inactive/dead threads # from its internal threadtable. The presence of this kind of threads could be the # results of multiple conditions, process exit events dropping or event re-ordering being # the most probable ones. # Reducing the interval can help in better memory management, but as a consequence, could # impact the CPU usage. thread_table_auto_purging_interval_s: 300 # -- Amount of time after which a thread which has seen no events can be purged # through the automatic threads purging routine, in seconds. # As the purging happens only every `thread_table_auto_purging_interval_s`, # the max time a thread may linger is actually # `thread_table_auto_purging_interval_s` + `thread_table_auto_purging_thread_timeout_s`. # Reducing the interval can help in better memory management, but as a consequence, could # impact the CPU usage. thread_table_auto_purging_thread_timeout_s: 300 # -- Set how many bytes are collected of each I/O buffer for 'syscall' events. # Use this option with caution since it can have a strong performance impact. snaplen: 80 ================================================ FILE: proposals/20190826-grpc-outputs.md ================================================ # Falco gRPC Outputs - [Summary](#summary) - [Motivation](#motivation) * [Goals](#goals) * [Non-Goals](#non-goals) - [Proposal](#proposal) * [Use cases](#use-cases) * [Diagrams](#diagrams) * [Design Details](#design-details) ## Summary We intend to build a simple gRPC server and SDKs - eg., [falco#785](https://github.com/falcosecurity/falco/issues/785) - to allow users receive and consume the alerts regarding the violated rules. ## Motivation The most valuable information that Falco can give to its users are the alerts. An alert is an "output" when it goes over a transport, and it is emitted by Falco every time a rule is matched. At the current moment, however, Falco can deliver alerts in a very basic way, for example by dumping them to standard output. For this reason, many Falco users asked, with issues - eg., [falco#528](https://github.com/falcosecurity/falco/issues/528) - or in the [slack channel](https://slack.k8s.io) if we can find a more consumable way to implement Falco outputs in an extensible way. The motivation behind this proposal is to design a new output implementation that can meet our user's needs. ### Goals - To decouple the outputs from the Falco code base - To design and implement an additional output mode by mean of a gRPC **streaming** server - To keep it as simple as possible - To have a simple contract interface - To only have the responsibility to route Falco output requests and responses - To continue supporting the old output formats by implementing their same interface - To be secure by default (**mutual TLS** authentication) - To be **asynchronous** and **non-blocking** - To provide a connection over unix socket (no authentication) - To implement a Go client - To implement a Rust client - To implement a Python client ### Non-Goals - To substitute existing outputs (stdout, syslog, etc.) - To support different queuing systems than the default (round-robin) one - To support queuing mechanisms for message retransmission - Users can have a local gRPC relay server along with Falco that multiplexes connections and handles retires and backoff - To change the output format - To make the message context (text, fields, etc.) and format configurable - Users can already override rules changing their output messages - To act as an orchestrator for Falco instances ## Proposal ### Use cases - Receive Falco events with a well-defined contract over wire - Integrate Falco events with existing alerting/paging mechanisms - Integrate Falco events with existing monitoring infrastructures/tools - Falco outputs SDKs for different languages ### Diagrams The following sequence diagram illustrates the flow happening for a single rule being matched and the consequent alert through the gRPC output client. ![Single alert sequence diagram](20190826-grpc-output-single-alert-sequence.png) ### Design Details Here is the proto3 contracts definitions for the client and the server SDK. ```proto3 syntax = "proto3"; import "google/protobuf/timestamp.proto"; import "schema.proto"; package falco.outputs; option go_package = "github.com/falcosecurity/client-go/pkg/api/outputs"; // This service defines the RPC methods // to `request` a stream of output `response`s. service service { // Subscribe to a stream of Falco outputs by sending a stream of requests. rpc sub(stream request) returns (stream response); // Get all the Falco outputs present in the system up to this call. rpc get(request) returns (stream response); } // The `request` message is the logical representation of the request model. // It is the input of the `output.service` service. message request { } // The `response` message is the representation of the output model. // It contains all the elements that Falco emits in an output along with the // definitions for priorities and source. message response { google.protobuf.Timestamp time = 1; falco.schema.priority priority = 2; falco.schema.source source = 3; string rule = 4; string output = 5; map output_fields = 6; string hostname = 7; } ``` --- ================================================ FILE: proposals/20190909-psp-rules-support.md ================================================ # Support for K8s Pod Security Policies (PSPs) in Falco - [Summary](#summary) - [Motivation](#motivation) * [Goals](#goals) * [Non-Goals](#non-goals) - [Proposal](#proposal) * [Use cases](#use-cases) * [Diagrams](#diagrams) * [Design Details](#design-details) ## Summary We want to make it easier for K8s Cluster Operators to Author Pod Security Policies by providing a way to read a PSP, convert it to a set of Falco rules, and then run Falco with those rules. ## Motivation PSPs provide a rich powerful framework to restrict the behavior of pods and apply consistent security policies across a cluster, but it’s difficult to know the gap between what you want your security policy to be and what your cluster is actually doing. Additionally, since PSPs enforce once applied, they might prevent pods from running, and the process of tuning a PSP live on a cluster can be disruptive and painful. That's where Falco comes in. We want to make it possible for Falco to perform a "dry run" evaluation of a PSP, translating it to Falco rules that observe the behaviour of deployed pods and sending alerts for violations, *without* blocking. This helps accelerate the authoring cycle, providing a complete authoring framework for PSPs without deploying straight to the cluster. ### Goals Transparently read a candidate PSP into an equivalent set of Falco rules that can look for the conditions in the PSP. The PSP is converted into a set of Falco rules which can be either saved as a file for later use/inspection, or loaded directly so that they can monitor system calls and k8s audit activity. ### Non-Goals Falco will not automatically read PSPs from a cluster, will not install PSPs, and will not provide guidance on the parts of your infrastructure that are already covered by PSPs. This feature only helps with the testing part of a candidate PSP. For coming up with an initial PSP, you can use tools like [https://github.com/sysdiglabs/kube-psp-advisor](Kube PSP Advisor). The use case here is for cluster operators who want to author PSPs, but don't want to just put it in a cluster and see what breaks. For example, if your PSP sets privileged to false, but it turns out some of your pods are running privileged, they won't be able to start. With this feature, they could iterate without enforcement until they have a PSP that matches the actual behaviour of their cluster. Some of that will come from changing the PSP, some of that will come from changing the behaviour of the cluster. The important part is that it's not mistakenly preventing things from running while you're figuring it out. ## Proposal ### Use cases You'll be able to run falco with a `--psp` argument that provides a single PSP yaml file. Falco will automatically convert the PSP into an equivalent set of Falco rules, load the rules, and then run with the loaded rules. You can optionally provide a `--psp_save=` command line option to save the converted rules to a file. ### Diagrams No diagrams yet. ### Design Details * We'll use [inja](https://github.com/pantor/inja) as the templating engine. * For the most part, we can rely on the existing framework of rules, filter expressions, and output expressions that already exist in Falco. One significant change will be that filter fields can extract more than one "value" per event, and we'll need to define new operators to perform set comparisons between values in an event and values in the comparison right-hand-side. * This will rely heavily on existing support for [K8s Audit Events](https://falco.org/docs/event-sources/kubernetes-audit/) in Falco. ================================================ FILE: proposals/20191030-api.md ================================================ # Falco APIs ## Summary This is a proposal to better structure the Falco API. The Falco API is a set of contracts describing how users can interacts with Falco. By defining a set of interfaces the Falco Authors intend to decouple Falco from other software and data (eg., from the input sources) and, at the same time, make it more extensible. Thus, this document intent is to propose a list of services that constitute the Falco API (targeting the first stable version of Falco, v1.0.0). ## Motivation We want to enable users to use third-party clients to interface with Falco outputs, inputs, rules, and configurations. Such ability would enable the community to create a whole set of OSS tools, built on top of Falco. Some examples, already in place, are: - [falcoctl](https://github.com/falcosecurity/falcoctl) - [falco-exporter](https://github.com/falcosecurity/falco-exporter) ## Goals - To design and implement an API to make Falco more configurable - To design and implement an API to make Falco more consumable - To have an API so that users are able to interface with Falco - To design a set of simple contract interfaces - To have an API which is secure by default - To implement this API in C++ using gRPC as a first implementation ## Non-Goals - To replace the existing inputs: the **inputs** will be only for new source events users may want to add, initially - To document the API: to be approached as separate task - To create/update the API client: to be approached as a separate task ## Proposal ### Use cases - Receive Falco events with a well-defined contract --> **outputs** - Receive Falco drops with a well-defined contract --> **drops** - Receive current Falco version and related meta information (commit hash, built type, etc.) --> **version** - Configure Falco via API (CRUD) -> **configs** - Inject and/or modify Falco rules via API (CRUD) --> **rules** - Send input events to Falco --> **inputs** ### Unary services 1. Version 2. Rules 3. Configs ### Streaming (server or client) services 1. Outputs 2. Drops ### Bidirectional streaming services 1. Inputs ### Architecture We propose to have: - 1 gRPC server - 1 service grouping services This translates in having the following set of `proto` files. - a `.proto` containing the **request** and **response messages** for each service - eg., `version.proto`, `outputs.proto` etc. For example, ```proto3 # version.proto message request { ... } // The `response` message contains the version of Falco. // It provides the whole version as a string and also // its parts as per semver 2.0 specification (https://semver.org). message response { string version = 1; uint32 major = 2; uint32 minor = 3; uint32 patch = 4; string prerelease = 5; string build = 6; } ``` - one or more `.proto` containing the command models - ie., the already existing `schema.proto` containing source enum, etc. ```proto3 # schema.proto enum priority { .. } enum source { option allow_alias = true; SYSCALL = 0; syscall = 0; Syscall = 0; K8S_AUDIT = 1; k8s_audit = 1; K8s_audit = 1; K8S_audit = 1; } ``` - a `.proto` for service definitions For example, ```proto3 service api { rpc version(request) returns (response); rpc outputs(request) returns (stream response); rpc drops(request) returns (stream response); ... } ``` ================================================ FILE: proposals/20191217-rules-naming-convention.md ================================================ # Falco rule naming convention - [Summary](#summary) - [Motivation](#motivation) * [Goals](#goals) * [Non-Goals](#non-goals) - [Proposal](#proposal) * [Use cases](#use-cases) * [Diagrams](#diagrams) * [Design Details](#design-details) + [Rule](#rule) + [Macro](#macro) + [List](#list) ## Summary Propose some basic naming conventions when new lists, macros, rules are introduced. ## Motivation We want to help people from the community to contribute to falco rules. It will help improving the security content provided by Falco out of the box. Since people have different preference of naming things, it's necessary to set forth some basic naming convention for people to follow when creating new rules, macros and lists. ### Goals People will have to follow the naming conventions rules when introducing new Falco rules, macros and lists. ### Non-Goals There will be no intention to cover Falco rule syntax in this proposal. ## Proposal ### Use cases When new PRs are created in the area of rules, reviewers need to examine whether there are new rules, macros or lists are introduced. If yes, check whether follow the naming convention. ### Diagrams N/A ### Design Details #### Rule - Rule Name: Use phrases with capitalizing every word except preposition (e.g. `Search Private Keys or Passwords`) - Description: Use sentence always starting with "Detect" and ending with period. (e.g. `Detect grep private keys or passwords activity.`) - Output: Use sentence. Must at least include output fields (user=%user.name command=%proc.cmdline container_id=%container.id) - Tags: Use at least one of the following: [network, process, filesystem]. Also encourage to use mitre_* tags if applicable #### Macro - Macro Name: Use lowercase_separated_by_underscores (e.g. `parent_java_running_zookeeper`) #### List - List Name: Use lowercase_separated_by_underscores (e.g. `protected_shell_spawning_binaries`) ================================================ FILE: proposals/20200506-artifacts-scope-part-1.md ================================================ # Falco Artifacts Scope - Part 1 The **Falco Artifact Scope** proposal is divided in two parts: 1. the Part 1 - *this document*: the State of Art of Falco artifacts 2. the [Part 2](./20200506-artifacts-scope-part-2.md): the intended state moving forward ## Summary As a project we would like to support the following artifacts. Everything else will be moved to [contrib](https://github.com/falcosecurity/contrib). As a project we will build, change, rename, and move files, documents, scripts, configurations according to the new state of the art described into [Part 2](./20200506-artifacts-scope-part-2.md). Inspired by many previous issues and many of the weekly community calls. ## Terms **falco** *The Falco binary* **driver** *System call provider from the Linux kernel. Either (`bpf`, `module`)* **falco-driver-loader** *The bash script found [here](https://github.com/falcosecurity/falco/blob/master/scripts/falco-driver-loader) that tries to compile else download the driver (kernel module or eBPF probe).* **package** *An installable artifact that is operating system specific. All packages MUST be hosted on [bintray](https://bintray.com/falcosecurity).* **image** *OCI compliant container image hosted on dockerhub with tags for every release and the current master branch.* # Packages List of currently official packages (for x86 64bits only): - `falco-x.y.z-x86_64.deb` for debian like systems, it installs the kernel module by default - `falco-x.y.z-x86_64.rpm` for rpm like systems, it installs the kernel module by default - `falco-x.y.z-x86_64.tar.gz` for binary installation, it contains `falco` binary, `falco-driver-loader` script, drivers source, and related dependencies # Images List of currently official container images (for X86 64bits only): | Name | Directory | Description | |---|---|---| | [falcosecurity/falco:latest](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/stable | Falco (DEB built from git tag or from the master) with all the building toolchain. | | [falcosecurity/falco:latest-slim](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_-slim](https://hub.docker.com/repository/docker/falcosecurity/falco),[falcosecurity/falco:master-slim](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/slim | Falco (DEB build from git tag or from the master) without the building toolchain. | | [falcosecurity/falco-driver-loader:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:master](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader) | docker/driver-loader | `falco-driver-loader` as entrypoint with the building toolchain. | | [falcosecurity/falco-builder:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-builder) | docker/builder | The complete build tool chain for compiling Falco from source. See [the documentation](https://falco.org/docs/source/) for more details on building from source. Used to build Falco (CI). | | [falcosecurity/falco-tester:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-tester) | docker/tester | Container image for running the Falco test suite. Used to run Falco integration tests (CI). | | _to not be published_ | docker/local | Built on-the-fly and used by falco-tester. | **Note**: `falco-builder`, `falco-tester` (and the `docker/local` image which it's built on the fly by the `falco-tester` one) are not integrated into the release process because they are development and CI tools that need to be manually pushed only when updated. # Falco Project Evolution We will modeling a loosely defined adoption of the Kubernetes and CNCF incubator efforts. The criteria will remain loose, and tighten as needed at the discretion of the Falco open source community. ### contrib "_Sandbox level_" This new [contrib](https://github.com/falcosecurity/contrib) repository will be equivalent to the `Falco Sandbox` and serves as a place for the community to `test-drive` ideas/projects/code. ### repository "_Incubating level_" projects such as [falco-exporter](https://github.com/falco-exporter) can be promoted from `contrib` to their own repository. This is done as needed, and can best be measured by the need to cut a release and use the GitHub release features. Again, this is at the discretion of the Falco open source community. ### official support As the need for a project grows, it can ultimately achieve the highest and most coveted status within The Falco Project. "_Official support_." The artifacts listed above are part of the official Falco release process. These artifact will be refined and amended by the [Part 2](./20200506-artifacts-scope-part-2.md). # Action The *Part 1* is mainly intended as a cleanup process. For each item not listed above, ask if it needs to be moved or deleted. After the cleanup process, all items will match the *Part 1* of this proposal. ### Action Items Here are SOME of the items that would need to be done, for example: - Remove `minimal` from `falco` repository (it's almost similar to `slim`, we don't need two images for the same purpose) - Rename `driverloader` image to `falco-driver-loader` (since it has not been release yet, we can rename it without breaking things) - Move everything else to contrib - Move [/integrations](https://github.com/falcosecurity/falco/tree/master/integrations) to contrib - Move [/examples](https://github.com/falcosecurity/falco/tree/master/examples) to contrib - Old documentation ### Documentation Update documentation in [falco-website#184](https://github.com/falcosecurity/falco-website/pull/184). ### Adjusting projects - YAML manifest documentation to be moved to `contrib` - Minikube, Kind, Puppet, Ansible, etc documentation to be moved to `contrib` ================================================ FILE: proposals/20200506-artifacts-scope-part-2.md ================================================ # Falco Artifacts Scope - Part 2 The **Falco Artifact Scope** proposal is divided in two parts: 1. the [Part 1](./20200506-artifacts-scope-part-1.md): the State of Art of Falco artifacts 2. the Part 2 - *this document*: the intended state moving forward ## Summary See [Part 1](./20200506-artifacts-scope-part-1.md). ## Terms See [Part 1](./20200506-artifacts-scope-part-1.md). ## Packages Official packages for x86 64bits only. The following convention MUST be used for all packages. _All package names MUST contain a version._ _If a package installs the Falco kernel module it MUST contain `module`._ _If a package installs the Falco BPF probe it MUST contain `bpf`._ _In general, if a package installs a Falco driver it MUST contain the driver name._ ### .deb Falco running in debian like systems that will default to the kernel module. - falco-*x.y.z*-amd64.deb - alias to ` falco-*x.y.z*-module-amd64.deb` - falco-*x.y.z*-module-amd64.deb - `falco` and `module` - falco-*x.y.z*-bpf-amd64.deb - `falco` and `bpf` We reserve the right to change the naming convention of deb packages accordingly to deb conventions. ### .rpm Falco running in rpm like systems that will default to the kernel module. - falco-*x.y.z*-x86_64.rpm - alias to ` falco-*x.y.z*-module-x86_64.rpm` - falco-*x.y.z*-module-x86_64.rpm - `falco` and `module` - falco-*x.y.z*-bpf-x86_64.rpm - `falco` and `bpf` We reserve the right to change the naming convention of rpm packages accordingly to rpm conventions. ### .tar.gz - falco-bin-x86.tar.gz - `falco` binary, `falco-loader-script`, drivers source, and related dependencies - `INSTALL` file - `Makefile` file - falco-src-x86.tar.gz - No binaries - `INSTALL` file - falco-module-src-x86.tar.gz - `module` sources with `Makefile` - `INSTALL` file - falco-bpf-src-x86.tar.gz - `bpf` sources with `Makefile` - `INSTALL` file ## Images The following convention MUST be used for all container images. - falcosecurity/falco:TAG - First runs `falco-driver-loader` and then runs `falco` - Can be run with `--privileged` - Can be run with `-e SKIP_DRIVER_LOADER=true` to skip the execution of `falco-driver-loader` - TAG can be `latest` to refer to the latest release - TAG can be `master` to refer to the latest master - TAG can be `x.y.z` to refer to a specific release - falcosecurity/falco-driver-loader:TAG - Runs `falco-driver-loader` and exit - Needs to be run with `--privileged` - falcosecurity/falco-no-driver - Runs `falco` (only userspace) - falcosecurity/falco-tester:TAG - Runs the Falco integration test suite - falcosecurity/falco-builder:TAG - Contains the Falco tool chain for development The image usage MUST be documented in the Dockerfile and in the [website](https://falco.org/docs/). If an image does not take any action by default, a command usage MUST printed out. We reserve the right to add image aliases if it was needed. ## Official support These artifacts will be amended to the ones listed above, and will become a part of the official Falco release process. ## Action For each item, ask if this already exists. If so we need to rename, and update it to match this new convention. If does not exist, add it. ### Action Items Here are SOME of the items that would need to be done for example: - Rename package accordingly - Rename docker images accordingly - Evaluate how to call what's currently called `falcosecurity/falco:latest-slim` - Documentation in all packages with `INSTALL` file - Add `Makefile` where needed - Implement missing packages - Rename `SKIP_MODULE_LOAD` environment variable of docker images to `SKIP_DRIVER_LOADER` - Create `usage` commands for every docker image ### Documentation Update documentation in [falco-website](https://github.com/falcosecurity/falco-website/) #### Note: This could break the current helm chart, and maybe other dependencies. We owe existing users of the Falco project some courtesy if we will break their usage of how Falco has traditionally been advertised. Some things we owe the community. - Announcement on Falco mailing list - Issues/Pull Request to Helm chart - Note: At the very least open an issue and document how to make the existing helm chart work with the new changes if needed. [Nova Volunteers] - We should at least open a PR and update the helm chart with these new expectations if needed. [Nova Volunteers] - We should revisit the helm chart OWNERS - Twitter - Documentation ================================================ FILE: proposals/20200818-artifacts-storage.md ================================================ # Falco Artifacts Storage This document reflects the way we store the Falco artifacts. ## Terms & Definitions - [Falco artifacts](./20200506-artifacts-scope-part-1.md) - Bintray: artifacts distribution platform ## Packages The Falco packages are **automatically** built and sent to [bintray](https://bintray.com/falcosecurity) in the following cases: - a pull request gets merged into the master branch (**Falco development releases**) - a new Falco release (git tag) happens on the master branch (**Falco stable releases**) The only prerequisite is that the specific Falco source code builds successfully and that the tests pass. As per [Falco Artifacts Scope (#1)](./20200506-artifacts-scope-part-1.md) proposal we provide three kind of Falco packages: - DEB - RPM - Tarball Thus, we have three repositories for the Falco stable releases: - https://bintray.com/falcosecurity/deb - https://bintray.com/falcosecurity/rpm - https://bintray.com/falcosecurity/bin And three repositories for the Falco development releases: - https://bintray.com/falcosecurity/deb-dev - https://bintray.com/falcosecurity/rpm-dev - https://bintray.com/falcosecurity/bin-dev ## Drivers The process of publishing a set of prebuilt Falco drivers is implemented by the **Drivers Build Grid (DBG)** in the [test-infra](https://github.com/falcosecurity/test-infra/tree/master/driverkit) repository (`driverkit` directory). This process is driven by the configuration files (YAML) present in the `driverkit/config` directory in the [test-infra](https://github.com/falcosecurity/test-infra/tree/master/driverkit) repository. Each of these files represents a prebuilt driver (eventually two: kernel module and eBPF probe, when possible) that will be published on [bintray](https://bintray.com/falcosecurity) if it builds correctly. Every time the `driverkit/config` directory on the master branch has some changes from the previous commit the CI system, which you can find defined in the [.circleci/config.yml](https://github.com/falcosecurity/test-infra/blob/master/.circleci/config.yml) file, takes care of building and publishing all the drivers. The driver versions we ship prebuilt drivers for are: - the driver version associated with the last Falco stable version ([see here](https://github.com/falcosecurity/falco/blob/c4b7f17271d1a4ca533b2e672ecaaea5289ccdc5/cmake/modules/sysdig.cmake#L29)) - the driver version associated with the penultimate Falco stable version The prebuilt drivers get published into [this](https://bintray.com/falcosecurity/driver) generic artifacts repository. You can also visualize the full list of prebuilt drivers by driver version visiting this [URL](https://dl.bintray.com/falcosecurity/driver). ### Notice The generation of new prebuilt drivers takes usually place with a frequency of 1-2 weeks, on a **best-effort** basis. Thus, it can happen the list of available prebuilt drivers does not yet contain the driver version currently on Falco master. Nevertheless, this process is an open, auditable, and transparent one. So, by sending a pull-request towards [test-infra](https://github.com/falcosecurity/test-infra) repository containing the configuration YAML files you can help the Falco community stay on track. Some pull-requests you can look at to create your own are: - https://github.com/falcosecurity/test-infra/pull/165 - https://github.com/falcosecurity/test-infra/pull/163 - https://github.com/falcosecurity/test-infra/pull/162 While, the documentation of the YAML configuration files can be found [here](https://github.com/falcosecurity/driverkit/blob/master/README.md). ## Container images As per Falco packages, also the Falco official container images are **automatically** published to the [dockerhub](https://hub.docker.com/r/falcosecurity/falco). These images are built and published in two cases: - a pull request gets merged into the master branch (**Falco development releases**) - a new Falco release (git tag) happens (**Falco stable releases**) For a detailed explanation of the container images we build and ship look at the following [documentation](https://github.com/falcosecurity/falco/blob/master/docker/README.md). ================================================ FILE: proposals/20200828-structured-exception-handling.md ================================================ # Proposal for First Class Structured Exceptions in Falco Rules ## Summary ## Motivation Almost all Falco Rules have cases where the behavior detected by the rule should be allowed. For example, The rule Write Below Binary Dir has exceptions for specific programs that are known to write below these directories as a part of software installation/management: ```yaml - rule: Write below binary dir desc: an attempt to write to any file below a set of binary directories condition: > bin_dir and evt.dir = < and open_write and not package_mgmt_procs and not exe_running_docker_save and not python_running_get_pip and not python_running_ms_oms and not user_known_write_below_binary_dir_activities ... ``` In most cases, these exceptions are expressed as concatenations to the original rule's condition. For example, looking at the macro package_mgmt_procs: ```yaml - macro: package_mgmt_procs condition: proc.name in (package_mgmt_binaries) ``` The result is appending `and not proc.name in (package_mgmt_binaries)` to the condition of the rule. A more extreme case of this is the write_below_etc macro used by Write below etc rule. It has tens of exceptions: ``` ... and not sed_temporary_file and not exe_running_docker_save and not ansible_running_python and not python_running_denyhosts and not fluentd_writing_conf_files and not user_known_write_etc_conditions and not run_by_centrify and not run_by_adclient and not qualys_writing_conf_files and not git_writing_nssdb ... ``` The exceptions all generally follow the same structure--naming a program and a directory prefix below /etc where that program is allowed to write files. ### Using Appends/Overwrites to Customize Rules An important way to customize rules and macros is to use `append: true` to add to them, or `append: false` to define a new rule/macro, overwriting the original rule/macro. Here's an example from Update Package Repository: ```yaml - list: package_mgmt_binaries items: [rpm_binaries, deb_binaries, update-alternat, gem, pip, pip3, sane-utils.post, alternatives, chef-client, apk, snapd] - macro: package_mgmt_procs condition: proc.name in (package_mgmt_binaries) - macro: user_known_update_package_registry condition: (never_true) - rule: Update Package Repository desc: Detect package repositories get updated condition: > ((open_write and access_repositories) or (modify and modify_repositories)) and not package_mgmt_procs and not exe_running_docker_save and not user_known_update_package_registry ``` If someone wanted to add additional exceptions to this rule, they could add the following to the user_rules file: ```yaml - list: package_mgmt_binaries items: [puppet] append: true - macro: package_mgmt_procs condition: and not proc.pname=chef append: true - macro: user_known_update_package_registry condition: (proc.name in (npm)) append: false ``` This adds an 3 different exceptions: * an additional binary to package_mgmt_binaries (because append is true), * adds to package_mgmt_procs, adding an exception for programs spawned by chef (because append is true) * overrides the macro user_known_update_package_registry to add an exception for npm (because append is false). ### Problems with Appends/Overrides to Define Exceptions Although the concepts of macros and lists in condition fields, combined with appending to lists/conditions in macros/rules, is very general purpose, it can be unwieldy: * Appending to conditions can result in incorrect behavior, unless the original condition has its logical operators set up properly with parentheses. For example: ```yaml rule: my_rule condition: (evt.type=open and (fd.name=/tmp/foo or fd.name=/tmp/bar)) rule: my_rule condition: or fd.name=/tmp/baz append: true ``` Results in unintended behavior. It will match any fd related event where the name is /tmp/baz, when the intent was probably to add /tmp/baz as an additional opened file. * A good convention many rules use is to have a clause "and not user_known_xxxx" built into the condition field. However, it's not in all rules and its use is a bit haphazard. * Appends and overrides can get confusing if you try to apply them multiple times. For example: ```yaml macro: allowed_files condition: fd.name=/tmp/foo ... macro: allowed_files condition: and fd.name=/tmp/bar append: true ``` If someone wanted to override the original behavior of allowed_files, they would have to use `append: false` in a third definition of allowed_files, but this would result in losing the append: true override. ## Solution: Exceptions as first class objects To address some of these problems, we will add the notion of Exceptions as top level objects alongside Rules, Macros, and Lists. A rule that supports exceptions must define a new key `exceptions` in the rule. The exceptions key is a list of identifier plus list of tuples of filtercheck fields. Here's an example: ```yaml - rule: Write below binary dir desc: an attempt to write to any file below a set of binary directories condition: > bin_dir and evt.dir = < and open_write and not package_mgmt_procs and not exe_running_docker_save and not python_running_get_pip and not python_running_ms_oms and not user_known_write_below_binary_dir_activities exceptions: - name: proc_writer fields: [proc.name, fd.directory] - name: container_writer fields: [container.image.repository, fd.directory] comps: [=, startswith] - name: proc_filenames fields: [proc.name, fd.name] comps: [=, in] - name: filenames fields: fd.filename comps: in ``` This rule defines four kinds of exceptions: * proc_writer: uses a combination of proc.name and fd.directory * container_writer: uses a combination of container.image.repository and fd.directory * proc_filenames: uses a combination of process and list of filenames. * filenames: uses a list of filenames The specific strings "proc_writer"/"container_writer"/"proc_filenames"/"filenames" are arbitrary strings and don't have a special meaning to the rules file parser. They're only used to link together the list of field names with the list of field values that exist in the exception object. proc_writer does not have any comps property, so the fields are directly compared to values using the = operator. container_writer does have a comps property, so each field will be compared to the corresponding exception items using the corresponding comparison operator. proc_filenames uses the in comparison operator, so the corresponding values entry should be a list of filenames. filenames differs from the others in that it names a single field and single comp operator. This changes how the exception condition snippet is constructed (see below). Notice that exceptions are defined as a part of the rule. This is important because the author of the rule defines what construes a valid exception to the rule. In this case, an exception can consist of a process and file directory (actor and target), but not a process name only (too broad). Exception values will most commonly be defined in rules with append: true. Here's an example: ```yaml - list: apt_files items: [/bin/ls, /bin/rm] - rule: Write below binary dir exceptions: - name: proc_writer values: - [apk, /usr/lib/alpine] - [npm, /usr/node/bin] - name: container_writer values: - [docker.io/alpine, /usr/libexec/alpine] - name: proc_filenames values: - [apt, apt_files] - [rpm, [/bin/cp, /bin/pwd]] - name: filenames values: [python, go] ``` A rule exception applies if for a given event, the fields in a rule.exception match all of the values in some exception.item. For example, if a program `apk` writes to a file below `/usr/lib/alpine`, the rule will not trigger, even if the condition is met. Notice that an item in a values list can be a list. This allows building exceptions with operators like "in", "pmatch", etc. that work on a list of items. The item can also be a name of an existing list. If not present surrounding parentheses will be added. Finally, note that the structure of the values property differs between the items where fields is a list of fields (proc_writer/container_writer/proc_filenames) and when it is a single field (procs_only). This changes how the condition snippet is constructed. ### Implementation For exception items where the fields property is a list of field names, each exception can be thought of as an implicit "and not (field1 cmp1 val1 and field2 cmp2 val2 and...)" appended to the rule's condition. For exception items where the fields property is a single field name, the exception can be thought of as an implicit "and not field cmp (val1, val2, ...)". In practice, that's how exceptions will be implemented. When a rule is parsed, the original condition will be wrapped in an extra layer of parentheses and all exception values will be appended to the condition. For example, using the example above, the resulting condition will be: ``` () and not ( (proc.name = apk and fd.directory = /usr/lib/alpine) or (proc.name = npm and fd.directory = /usr/node/bin) or (container.image.repository = docker.io/alpine and fd.directory startswith /usr/libexec/alpine) or (proc.name=apt and fd.name in (apt_files))) or (fd.filename in (python, go)))) ``` The exceptions are effectively syntactic sugar that allows expressing sets of exceptions in a concise way. ### Advantages Adding Exception objects as described here has several advantages: * All rules will implicitly support exceptions. A rule writer doesn't need to define a user_known_xxx macro and add it to the condition. * The rule writer has some controls on what defines a valid exception. The rule author knows best what is a good exception, and can define the fields that make up the exception. * With this approach, it's much easier to add and manage multiple sets of exceptions from multiple sources. You're just combining lists of tuples of filtercheck field values. ## Backwards compatibility To take advantage of these new features, users will need to upgrade Falco to a version that supports exception objects and exception keys in rules. For the most part, however, the rules file structure is unchanged. This approach does not remove the ability to append to exceptions nor the existing use of user_xxx macros to define exceptions to rules. It only provides an additional way to express exceptions. Hopefully, we can migrate existing exceptions to use this approach, but there isn't any plan to make wholesale rules changes as a part of this. This approach is for the most part backwards compatible with older Falco releases. To implement exceptions, we'll add a preprocessing element to rule parsing. The main Falco engine is unchanged. However, there are a few changes we'll have to make to Falco rules file parsing: * Currently, Falco will reject files containing anything other than rule/macro/list top-level objects. As a result, `exception` objects would be rejected. We'll probably want to make a one-time change to Falco to allow arbitrary top level objects. * Similarly, Falco will reject rule objects with exception keys. We'll also probably want to change Falco to allow unknown keys inside rule/macro/list/exception objects. ================================================ FILE: proposals/20200901-artifacts-cleanup.md ================================================ # Falco Artifacts Cleanup This document reflects when and how we clean up the Falco artifacts from their storage location. **Superseded by**: [drivers-storage-s3 proposal](https://github.com/falcosecurity/falco/blob/master/proposals/20201025-drivers-storage-s3.md). ## Motivation The [bintray](https://bintray.com/falcosecurity) open-source plan offers 10GB free space for storing artifacts. They also kindly granted us an additional 5GB of free space. ## Goal Keep the storage space usage under 15GB by cleaning up the [Falco artifacts](./20200506-artifacts-scope-part-1.md) from the [storage](./20200818-artifacts-storage). ## Status To be implemented. ## Packages ### Tarballs from Falco master At the moment of writing this document, this kind of Falco package requires approx. 50MB (maximum detected size) of storage space. Since, historically, the [bin-dev](https://bintray.com/falcosecurity/bin-dev) repository is the less used one, this document proposes to keep only the last 10 **Falco development releases** it contains. This means that the [bin-dev](https://bintray.com/falcosecurity/bin-dev) repository will take at maximum 500MB of storage space. ### DEB from Falco master At the moment of writing this document, this kind of Falco package requires approx. 5.1MB (maximum detected size) of storage space. Historically, every Falco release is composed by less than 50 merges (upper limit). So, to theoretically retain all the **Falco development releases** that led to a Falco stable release, this document proposes to keep the last 50 Falco DEB packages. This means that the [deb-dev](https://bintray.com/falcosecurity/deb-dev) repository will take at maximum 255MB of storage space. ### RPM from Falco master At the moment of writing this document, this kind of Falco package requires approx. 4.3MB (maximum detected size) of storage space. For the same exact reasons explained above this document proposes to keep the last 50 Falco RPM packages. This means that the [rpm-dev](https://bintray.com/falcosecurity/rpm-dev) repository will take at maximum 215MB of storage space. ### Stable releases This document proposes to retain all the stable releases. This means that all the Falco packages present in the Falco stable release repositories will be kept. The [bin](https://bintray.com/falcosecurity/bin) repository contains a Falco tarball package for every release. This means it grows in space of ~50MB each month. The [deb](https://bintray.com/falcosecurity/deb) repository contains a Falco DEB package for every release. This means it grows in space of ~5MB each month. The [rpm](https://bintray.com/falcosecurity/rpm) repository contains a Falco RPM package for every release. This means it grows in space of ~4.3MB each month. ### Considerations Assuming the size of the packages does not surpass the numbers listed in the above sections, the **Falco development releases** will always take less that 1GB of artifacts storage space. Assuming 12 stable releases at year, at the current size of packages, the **Falco stable releases** will take approx. 720MB of storage space every year. ### Implementation The Falco CI will have a new CI job - called `cleanup/packages-dev` - responsible for removing the **Falco development releases** depending on the above plan. This job will be triggered after the `publish/packages-dev` completed successfully. ## Drivers As explained in the [Artifacts Storage](./20200818-artifacts-storage) proposal, we build the drivers for the **last two driver versions** associated with **latest Falco stable releases**. Then, we store those drivers into a [generic bintray repository](https://bintray.com/falcosecurity/driver) from which the installation process automatically downloads them, if suitable. This document proposes to implement a cleanup mechanism that deletes all the other driver versions available. At the moment of writing, considering only the last two driver versions (**ae104eb**, **85c8895**) associated with the latest Falco stable releases, we ship ~340 eBPF drivers, each accounting for ~3.1MB of storage space, and 1512 kernel modules (~3.1MB size each, too). Thus, we obtain an estimate of approx. 2.875GB for **each** driver version. This document proposes to only store the last two driver versions associates with the latest Falco stable releases. And deleting the other ones. This way, assuming the number of prebuilt drivers does not skyrocket, we can reasonably estimate the storage space used by prebuilt drivers to be around 6GB. Notice that, in case a Falco stable release will not depend on a new driver version, this means the last two driver versions will, in this case, cover more than the two Falco stable releases. ### Archiving Since the process of building drivers is time and resource consuming, this document also proposes to move the driver versions in other storage facilities. The candidate is an AWS S3 bucket responsible for holding the deleted driver version files. #### Notice The current mechanism the Falco community uses to store the Falco drivers is explained by the [drivers-storage-s3](https://github.com/falcosecurity/falco/blob/master/proposals/20201025-drivers-storage-s3.md) proposal. ### Implementation The [test-infra](https://github.com/falcosecurity/test-infra) CI, specifically its part dedicated to run the **Drivers Build Grid** that runs every time it detects changes into the `driverkit` directory of the [test-infra](https://github.com/falcosecurity/test-infra) repository, will have a new job - called `drivers/cleanup` - responsible for removing all the Falco driver versions except the last two. This job will be triggered after the `drivers/publish` completed successfully on the master branch. #### Notice At the moment of writing (2021 09 28) the `drivers/cleanup` job is no more in place. Pragmatically, this means that the older Falco drivers will remain available in their [S3 bucket](https://download.falco.org/?prefix=driver/). ================================================ FILE: proposals/20201025-drivers-storage-s3.md ================================================ # Falco Drivers Storage S3 Supersedes: [20200818-artifacts-storage.md#drivers](20200818-artifacts-storage.md#drivers) Supersedes: [20200901-artifacts-cleanup.md#drivers](20200901-artifacts-cleanup.md#drivers) ## Introduction In the past days, as many people probably noticed, Bintray started rate-limiting our users, effectively preventing them from downloading any kernel module, rpm/deb package or any pre-built dependency we host there. This does not only interrupt the workflow of our users but also the workflow of the contributors, since without bintray most of our container images and CMake files can’t download the dependencies we mirror. ### What is the cause? We had a spike in adoption apparently, either a user with many nodes or an increased number of users. We don’t know this detail specifically yet because bintray does not give us very fine-grained statistics on this. This is the 30-days history: ![A spike on driver downloads the last ten days](20201025-drivers-storage-s3_downloads.png) As you can see, we can only see that they downloaded the latest kernel module driver version, however we can’t see if: * It’s a single source or many different users * What is the kernel/OS they are using ### What do we host on Bintray? * RPM packages: high traffic but very manageable ~90k downloads a month * Deb packages:low traffic ~5k downloads a month * Pre-built image Dependencies: low traffic, will eventually disappear in the future * Kernel modules: very high traffic, 700k downloads in 10 days, this is what is causing the current problems. They are primarily used by users of our container images. * eBPF probes: low traffic ~5k downloads a month ### Motivations to go to S3 instead of Bintray for the Drivers Bintray does an excellent service at building the rpm/deb structures for us, however we also use them for S3-like storage for the drivers. We have ten thousand files hosted there and the combinations are infinite. Before today, we had many issues with storage even without the spike in users we are seeing since the last ten days. ## Context on AWS Amazon AWS, recently gave credits to the Falco project to operate some parts of the infrastructure on AWS. The CNCF is providing a sub-account we are already using for the migration of the other pieces (like Prow). ## Interactions with other teams and the CNCF * The setup on the AWS account side already done, this is all technical work. * We need to open a CNCF service account ticket for the download.falco.org subdomain to point to the S3 bucket we want to use ## The Plan We want to propose to move the drivers and the container dependencies to S3. #### Moving means: * We create a public S3 bucket with [stats enabled](https://docs.aws.amazon.com/AmazonS3/latest/dev/analytics-storage-class.html) * We attach the bucket to a cloudfront distribution behind the download.falco.org subdomain * We move the current content keeping the same web server directory structure * We change the Falco Dockerfiles and driver loader script accordingly * We update test-infra to push the drivers to S3 * Once we have the drivers in S3, we can ask bintray to relax the limits for this month so that our users are able to download the other packages we keep there. Otherwise they will have to wait until November 1st. We only want to do that after the moving because otherwise we will hit the limits pretty quickly. #### The repositories we want to move are: * [https://bintray.com/falcosecurity/driver](https://bintray.com/falcosecurity/driver) will become https://download.falco.org/driver * [https://bintray.com/falcosecurity/dependencies](https://bintray.com/falcosecurity/dependencies) will become https://download.falco.org/dependencies #### Changes in Falco * [Search for bintray ](https://github.com/falcosecurity/falco/search?p=2&q=bintray)on the Falco repo and replace the URL for the CMake and Docker files. * It’s very important to change the DRIVERS_REPO environment variable [here](https://github.com/falcosecurity/falco/blob/0a33f555eb8e019806b46fea8b80a6302a935421/CMakeLists.txt#L86) - this is what updates the falco-driver-loader scripts that the users and container images use to fetch the module #### Changes in Test Infra * We need to use the S3 cli instead of jfrog cli to upload to the s3 bucket after building [here](https://github.com/falcosecurity/test-infra/blob/master/.circleci/config.yml) * We can probably remove jfrog from that repo since it only deals with drivers and drivers are being put on S3 now * Instructions on how to setup the S3 directory structure [here](https://falco.org/docs/installation/#install-driver) * `/$driver_version$/falco_$target$_$kernelrelease$_$kernelversion$.[ko|o]` #### Changes to Falco website * Changes should not be necessary, we are not updating the way people install Falco but only the driver. The driver is managed by a script we can change. ## Mitigation and next steps for the users * **The average users should be good to go now, Bintray raised our limits and we have some room to do this without requiring manual steps on your end** * **Users that can’t wait for us to have the S3 setup done: **can setup an S3 as driver repo themselves, push the drivers they need to it after compiling them (they can use [Driverkit](https://github.com/falcosecurity/driverkit) for that) Instructions on how to setup the S3 directory structure [here](https://falco.org/docs/installation/#install-driver). * **Users that can’t wait but don’t want to setup a webserver themselves**: the falco-driver-loader script can also compile the module for you. Make sure to install the kernel-headers on your nodes. * **Users that can wait** we will approve this document and act on the plan described here by providing the DRIVERS_REPO at [https://download.falco.org/driver](https://download.falco.org/driver) that then you can use ### How to use an alternative DRIVERS_REPO ? **On bash:** export DRIVERS_REPO=https://your-url-here **Docker** Pass it as environment variable using the docker run flag -e - for example: docker run -e DRIVERS_REPO=[https://your-url-here](https://your-url-here) **Kubernetes** spec: containers: - env: - name: DRIVERS_REPO value: https://your-url-here ## Release Next release is on December 1st, we want to rollout a hotfix 0.26.2 release that only contains the updated script before that date so that users don’t get confused and we can just tell them "update Falco" to get the thing working again. ================================================ FILE: proposals/20210119-libraries-contribution.md ================================================ # OSS Libraries Contribution Plan ## Summary Sysdig Inc. intends to donate **libsinsp**, **libscap**, the **kernel module driver** and the **eBPF driver sources** by moving them to the Falco project. This means that some parts of the [draios/sysdig](https://github.com/draios/sysdig) repository will be moved to a new GitHub repository called [falcosecurity/libs](https://github.com/falcosecurity/libs). This plan aims to describe and clarify the terms and goals to get the contribution done. ## Motivation There are two main OSS projects using the libraries and drivers that we are aware of: - [sysdig](https://github.com/draios/sysdig) the command line tool - [Falco](https:/github.com/falcosecurity/falco), the CNCF project. Since the Falco project is a heavy user of the libraries, a lot more than the sysdig cli tool, Sysdig (the company) decided to donate the libraries and the driver to the Falco community. Sysdig (the command line tool) will continue to use the libraries now provided by the Falco community underneath. This change is win-win for both parties because of the following reasons: - The Falco community owns the source code of the three most important parts of the software it distributes. - Right now it is "only" an engine on top of the libraries. This **contribution** helps in making the scope of the Falco project broader. Having the majority of the source code under an **open governance** in the same organization gives the Falco project more contribution opportunities, helps it in **evolving independently** and makes the whole Falco community a strong owner of the processes and decision making regarding those crucial parts. - Given the previous point, Sysdig (the command line tool) will benefit from the now **extended contributors base** - Sysdig (the company) can now focus on the user experience and user space features - **Contributions** to the libraries and drivers will be **easier** to spread across the Falco community - By being donated, with their own **release process**, **release artifacts**, and **documentation**, the libraries can now live on their own and possibly be used directly in other projects by becoming fundamental pieces for their success. ## Goals There are many sub-projects and each of them interacts in a different way in this contribution. Let's see the goals per sub-project. ### libsinsp 1. Extract libsinsp from `draios/sysdig/userspace/libsinsp` (keeping the commit history) into [falcosecurity/libs](https://github.com/falcosecurity/libs) 2. The migration comes first, then we can do additional PRs for the points below so that we do only one thing at a time and keep the history linear 3. Keep the same code, refactorings will need to be done in subsequent PRs and approved separately 4. Adapt the CMake and build files 5. Install [poiana](https://github.com/poiana) and its workflows on it 6. Define the `OWNERS` - Owners are chosen from the current major contributors (considering the past two years) to this project, given their availability, commitment is key 7. When possible, migrate issues and PRs to the new repository 8. Distribute the `libsinsp.so` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process 9. Distribute the `libsinsp.a` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process 10. Creation of the CI scripts using the Falco CI and Falco Infra 11. The CI scripts will need to publish the artifacts in the current falcosecurity artifacts repository 12. Artifacts will be pushed for every tag (release) and for every master merge (development release) 13. Falco follows a [multi-stage model for adopting new projects](https://github.com/falcosecurity/evolution#falco-project-evolution), in this case we will do an exception since the library is foundational for Falco and it has a very good track record already 14. This project will go already "Official support" once the contribution is completed 15. Contributing, Code of Conduct, Governance, Security, and Support will be the same as the rest of the organization, find them [here](https://github.com/falcosecurity/.github) 16. Every other additional change will need to have its own process with a proposal 17. Implement the release process as described above 18. Propose a change to Falco repository to use the artifacts produced by the libsinsp release process for the build 19. Document the API ### libscap 1. Extract libscap from `draios/sysdig/userspace/libscap` (keeping the commit history) into [falcosecurity/libs](https://github.com/falcosecurity/libs) 2. The migration comes first, then we can do additional PRs for the points below so that we do only one thing at a time and keep the history linear 3. Keep the same code, refactorings will need to be done in subsequent PRs and approved separately 4. Adapt the CMake and build files 5. Install [poiana](https://github.com/poiana) and its workflows on it 6. Define the `OWNERS` - Owners are chosen from the current major contributors (considering the past two years) to this project, given their availability, commitment is key 7. When possible, migrate issues and PRs to the new repository 8. Distribute the `libscap.so` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process 9. Distribute the `libscap.a` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process 10. Creation of the CI scripts using the Falco CI and Falco Infra 11. The CI scripts will need to publish the artifacts in the current falcosecurity artifacts repository 12. Artifacts will be pushed for every tag (release) and for every master merge (development release) 13. Falco follows a [multi-stage model for adopting new projects](https://github.com/falcosecurity/evolution#falco-project-evolution), in this case we will do an exception since the library is foundational for Falco and it has a very good track record already 14. This project will go already "Official support" once the contribution is completed 15. Contributing, Code of Conduct, Governance, Security, and Support will be the same as the rest of the organization, find them [here](https://github.com/falcosecurity/.github) 16. Every other additional change will need to have its own process with a proposal 17. Implement the release process as described above 18. Propose a change to Falco repository to use the artifacts produced by the libscap release process for the build 19. Document the API ### Drivers: Kernel module and eBPF probe 1. Extract them from `draios/sysdig/driver` (keeping the commit history) into [falcosecurity/libs](https://github.com/falcosecurity/libs) 2. The migration comes first, then we can do additional PRs for the point below so that we do only one thing at a time and keep the history linear 3. Keep the same code, refactorings will need to be done in subsequent PRs and approved separately 4. Adapt the Makefiles and build files 5. Install [poiana](https://github.com/poiana) and its workflows on it 6. Define the `OWNERS` - Owners are chosen from the current major contributors (considering the past two years) to this project, given their availability, commitment is key 7. When possible, migrate issues and PRs to the new repository 8. Falco follows a [multi-stage model for adopting new projects](https://github.com/falcosecurity/evolution#falco-project-evolution), in this case we will do an exception since the library is foundational for Falco and it has a very good track record already. We are just changing maintenance ownership 9. Contributing, Code of Conduct, Governance, Security, and Support will be the same as the rest of the organization, find them [here](https://github.com/falcosecurity/.github) 10. Every other additional change will need to have its own process with a proposal 11. The Falco community already ships driver artifacts using [driverkit](https://github.com/falcosecurity/driverkit) and the [test-infra repository](https://github.com/falcosecurity/test-infra) - Adapt the place from which [driverkit](https://github.com/falcosecurity/driverkit) grabs the drivers source 12. This project will go already "Official support" once the migration is completed. ### Falco 1. Adapt the CMake files to point to the new homes for libscap, libsinsp and the drivers 2. When distributing the deb and rpm, libscap and libsinsp will need to be install dependencies and not anymore compiled into Falco ### Driverkit 1. Change the source location for the drivers to point to the new driver repository ### pdig 1. The project will need to be adapted to use libscap and libsinsp and the fillers from their new location ================================================ FILE: proposals/20210501-plugin-system.md ================================================ # Plugin System ## Summary This is a proposal to create an infrastructure to extend the functionality of the Falco libraries via plugins. Plugins will allow users to easily extend the functionality of the libraries and, as a consequence, of Falco and any other tool based on the libraries. This proposal, in particular, focuses on two types of plugins: source plugins and extractor plugins. ## Motivation [libscap](https://github.com/falcosecurity/libs/tree/master/userspace/libscap) and [libsinsp](https://github.com/falcosecurity/libs/tree/master/userspace/libsinsp) provide a powerful data capture framework, with a rich set of features that includes: - data capture - trace files management - enrichment - filtering - formatting and screen rendering - Lua scripting (chisels) These features have been designed with one specific input in mind: system calls. However, they are generically adaptable to a broad set of inputs, such as cloud logs. With this proposal, we want to dramatically extend the scope of what the libraries, Falco and other tools can be applied to. We want to do it in a way that is easy, efficient and empowers anyone in the community to write a plugin. ## Goals - To design and implement a plugin framework that makes the libraries more modular and extensible - To have a framework that is easy to use - To support dynamic loading of plugins, so that the libraries can be extended without having to be recompiled and relinked - To enable users to write plugins in any language, with a particular focus on Go, C and C++ - To have an efficient plugin framework so that, performance-wise, writing a plugin is as close as possible as extending the libraries internal source code - To make it possible to write plugins for Linux, MacOS and Windows ## Non-Goals - To implement plugins other than source and extractor: to be approached as separate task - To document the plugin framework and interface: to be approached as separate task ## Proposal ### Plugin Common Information Both source and extractor plugins have the following: - A required api version, to ensure compatibility with the plugin framework. - A name - A description - A version - A contact field for the plugin authors (website, github repo, twitter, etc). - Functions to initialize and destroy the plugin internal state. ### Plugin types Initially, we will implement support for two types of plugins: source plugins and extractor plugins. #### Source Plugin A source plugin implements a new sinsp/scap event source. It has the ability to "open" and "close" a session that provides events. It also has the ability to return an event to the plugin framework via a next() method. Events returned by source plugins have an "event source", which describes the information in the event. This is distinct from the plugin name to allow for multiple kinds of plugins to generate the same kind of events. For example, there might be plugins gke-audit-bridge, eks-audit-bridge, ibmcloud-audit-bridge, etc. that all fetch [K8s Audit](https://kubernetes.io/docs/tasks/debug-application-cluster/audit/) information. The plugins would have different names but would have the same event source "k8s_audit". Source plugins also have the ability to extract information from events based on fields. For example, a field proc.name extracts a process name from a syscall event. The plugin returns a set of supported fields, and there are functions to extract a value given an event and field. The plugin framework can then build filtering expressions/Falco rule conditions based on these fields combined with relational and/or logical operators. For example, given an expression "ct.name=root and ct.region=us-east-1", the plugin framework handles parsing the expression, calling the plugin to extract values for a given event, and determining the result of the expression. In a Falco output string like "An EC2 Node was created (name=%ec2.name region=%ct.region)", the plugin framework handles parsing the output string, calling the plugin to extract values for a given event, and building the resolved string. Source plugins also provide an "id", which is globally unique and is used in capture files (see below). #### Extractor Plugin An extractor plugin focuses only on field extraction from events generated by other plugins, or by the core libraries. It does *not* provide an event source, but can extract fields from other event sources. An example is json field extraction, where a plugin might be able to extract fields from arbitrary json payloads. An extractor plugin provides an optional set of event sources. When the framework receives an event with an event source in the plugin's set of event sources, fields in expressions/Falco outputs will be extracted from events using the plugin. An extractor plugin can also *not* name a set of event sources. In this case, fields will be extracted from *all* events, regardless of source. In this case, the extractor plugin must detect the format of arbitrary payloads and be able to return NULL/no value when the payload is not supported. ### Support for Plugin Events in Capture Files. libscap will define a new event type called "pluginevent" that contains two fields: * "plugin ID": This uniquely identifies the plugin that generated this event. * "event_data": This is a variable-length data buffer containing the event data, as returned by the plugin. Defining an event for plugins allows creating capture files from plugins. These capture files can be saved, read, filtered, etc, like any other capture file, allowing for later analysis/display/etc. ### Plugins format Plugins are dynamic libraries (.so files in Unix, .dll files in windows) that export a minimum set of functions that the libraries will recognize. Plugins are versioned using semantic versioning to minimize regressions and compatibility issues. Plugins can be written in any language, as long as they export the required functions. Go, however, is the preferred language to write plugins, followed by C/C++. ### Protecting from plugin issues The libraries will do everything possible to validate the data coming from the plugins and protect Falco and the other consumers from corrupted data. However, for performance reasons, plugins will be "trusted": they will run in the same thread and address space as Falco and they could crash the program. We assume that the user will be in control of plugin loading and will make sure only trusted plugins are loaded/packaged with Falco. ### Plugin/Event Source registries Every source plugin requires its own, unique plugin ID to interoperate with Falco and the other plugins. The plugin ID will be used by the libs to properly process incoming events (for example, when saving events to file and loading them back), and by plugins to unambiguously recognize their dependencies. To facilitate the allocation and distribution of plugin IDs, we will require that plugin developers request IDs for their plugins to the Falco organization. The mechanism used for plugin allocation is not determined yet and will be discussed in the future. Similarly, plugin developers must register event sources with the Falco organization. This allows coordination between plugins that wish to provide compatible payloads, and to allow extractor plugins to know what data format is associated with a given event source. ### golang plugin SDK To facilitate the development of plugins written in go, an SDK has been developed. We intend this SDK (and future SDKs for other languages) to be part of the Falco organization. For this reason, we submitted the following incubation request: https://github.com/falcosecurity/evolution/issues/62 ### Proposed API (subject to change) ```c // This struct represents an event returned by the plugin, and is used // below in next()/next_batch(). // - data: pointer to a memory buffer pointer. The plugin will set it // to point to the memory containing the next event. Once returned, // the memory is owned by the plugin framework and will be freed via // a call to free(). // - datalen: pointer to a 32bit integer. The plugin will set it the size of the // buffer pointed by data. // - ts: the event timestamp. Can be (uint64_t)-1, in which case the engine will // automatically fill the event time with the current time. typedef struct ss_plugin_event { uint8_t *data; uint32_t datalen; uint64_t ts; } ss_plugin_event; // // This is the opaque pointer to the state of a plugin. // It points to any data that might be needed plugin-wise. It is // allocated by init() and must be destroyed by destroy(). // It is defined as void because the engine doesn't care what it is // and it treats is as opaque. // typedef void ss_plugin_t; // // This is the opaque pointer to the state of an open instance of the source // plugin. // It points to any data that is needed while a capture is running. It is // allocated by open() and must be destroyed by close(). // It is defined as void because the engine doesn't care what it is // and it treats is as opaque. // typedef void ss_instance_t; // // Interface for a sinsp/scap source plugin // // // NOTE: For all functions below that return a char *, the memory // pointed to by the char * must be allocated by the plugin using // malloc() and should be freed by the caller using free(). // // For each function below, the exported symbol from the dynamic // library should have a prefix of "plugin_" // (e.g. plugin_get_required_api_version, plugin_init, etc.) // typedef struct { // // Return the version of the plugin API used by this plugin. // Required: yes // Return value: the API version string, in the following format: // "..", e.g. "1.2.3". // NOTE: to ensure correct interoperability between the engine and the plugins, // we use a semver approach. Plugins are required to specify the version // of the API they run against, and the engine will take care of checking // and enforcing compatibility. // char* (*get_required_api_version)(); // // Return the plugin type. // Required: yes // Should return TYPE_SOURCE_PLUGIN. It still makes sense to // have a function get_type() as the plugin interface will // often dlsym() functions from shared libraries, and can't // inspect any C struct type. // uint32_t (*get_type)(); // // Initialize the plugin and, if needed, allocate its state. // Required: yes // Arguments: // - config: a string with the plugin configuration. The format of the // string is chosen by the plugin itself. // - rc: pointer to an integer that will contain the initialization result, // as a SCAP_* value (e.g. SCAP_SUCCESS=0, SCAP_FAILURE=1) // Return value: pointer to the plugin state that will be treated as opaque // by the engine and passed to the other plugin functions. // If rc is SCAP_FAILURE, this function should return NULL. // ss_plugin_t* (*init)(char* config, int32_t* rc); // // Destroy the plugin and, if plugin state was allocated, free it. // Required: yes // void (*destroy)(ss_plugin_t* s); // // Return a string with the error that was last generated by // the plugin. // Required: yes // // In cases where any other api function returns an error, the // plugin should be prepared to return a human-readable error // string with more context for the error. The plugin manager // calls get_last_error() to access that string. // char* (*get_last_error)(ss_plugin_t* s); // // Return the unique ID of the plugin. // Required: yes // EVERY SOURCE PLUGIN (see get_type()) MUST OBTAIN AN OFFICIAL ID FROM THE // FALCOSECURITY ORGANIZATION, OTHERWISE IT WON'T PROPERLY COEXIST WITH OTHER PLUGINS. // uint32_t (*get_id)(); // // Return the name of the plugin, which will be printed when displaying // information about the plugin. // Required: yes // char* (*get_name)(); // // Return the descriptions of the plugin, which will be printed when displaying // information about the plugin or its events. // Required: yes // char* (*get_description)(); // // Return a string containing contact info (url, email, twitter, etc) for // the plugin authors. // Required: yes // char* (*get_contact)(); // // Return the version of this plugin itself // Required: yes // Return value: a string with a version identifier, in the following format: // "..", e.g. "1.2.3". // This differs from the api version in that this versions the // plugin itself, as compared to the plugin interface. When // reading capture files, the major version of the plugin that // generated events must match the major version of the plugin // used to read events. // char* (*get_version)(); // // Return a string describing the events generated by this source plugin. // Required: yes // Example event sources would be strings like "syscall", // "k8s_audit", etc. The source can be used by extractor // plugins to filter the events they receive. // char* (*get_event_source)(); // // Return the list of extractor fields exported by this plugin. Extractor // fields can be used in Falco rule conditions and sysdig filters. // Required: no // Return value: a string with the list of fields encoded as a json // array. // Each field entry is a json object with the following properties: // "type": one of "string", "uint64" // "name": a string with a name for the field // "desc": a string with a description of the field // Example return value: // [ // {"type": "string", "name": "field1", "desc": "Describing field 1"}, // {"type": "uint64", "name": "field2", "desc": "Describing field 2"} // ] char* (*get_fields)(); // // Open the source and start a capture. // Required: yes // Arguments: // - s: the plugin state returned by init() // - params: the open parameters, as a string. The format is defined by the plugin // itself // - rc: pointer to an integer that will contain the open result, as a SCAP_* value // (e.g. SCAP_SUCCESS=0, SCAP_FAILURE=1) // Return value: a pointer to the open context that will be passed to next(), // close(), event_to_string() and extract_*. // ss_instance_t* (*open)(ss_plugin_t* s, char* params, int32_t* rc); // // Close a capture. // Required: yes // Arguments: // - s: the plugin context, returned by init(). Can be NULL. // - h: the capture context, returned by open(). Can be NULL. // void (*close)(ss_plugin_t* s, ss_instance_t* h); // // Return the next event. // Required: yes // Arguments: // - s: the plugin context, returned by init(). Can be NULL. // - h: the capture context, returned by open(). Can be NULL. // // - evt: pointer to a ss_plugin_event pointer. The plugin should // allocate a ss_plugin_event struct using malloc(), as well as // allocate the data buffer within the ss_plugin_event struct. // Both the struct and data buffer are owned by the plugin framework // and will free them using free(). // // Return value: the status of the operation (e.g. SCAP_SUCCESS=0, SCAP_FAILURE=1, // SCAP_TIMEOUT=-1) // int32_t (*next)(ss_plugin_t* s, ss_instance_t* h, ss_plugin_event **evt); // // Return the read progress. // Required: no // Arguments: // - progress_pct: the read progress, as a number between 0 (no data has been read) // and 10000 (100% of the data has been read). This encoding allows the engine to // print progress decimals without requiring to deal with floating point numbers // (which could cause incompatibility problems with some languages). // Return value: a string representation of the read // progress. This might include the progress percentage // combined with additional context added by the plugin. If // NULL, progress_pct should be used. // NOTE: reporting progress is optional and in some case could be impossible. However, // when possible, it's recommended as it provides valuable information to the // user. // char* (*get_progress)(ss_plugin_t* s, ss_instance_t* h, uint32_t* progress_pct); // // Return a text representation of an event generated by this source plugin. // Required: yes // Arguments: // - data: the buffer from an event produced by next(). // - datalen: the length of the buffer from an event produced by next(). // Return value: the text representation of the event. This is used, for example, // by sysdig to print a line for the given event. // char *(*event_to_string)(ss_plugin_t *s, const uint8_t *data, uint32_t datalen); // // Extract a filter field value from an event. // We offer multiple versions of extract(), differing from each other only in // the type of the value they return (string, integer...). // Required: no // Arguments: // - evtnum: the number of the event that is being processed // - id: the numeric identifier of the field to extract. It corresponds to the // position of the field in the array returned by get_fields(). // - arg: the field argument, if an argument has been specified for the field, // otherwise it's NULL. For example: // * if the field specified by the user is foo.bar[pippo], arg will be the // string "pippo" // * if the field specified by the user is foo.bar, arg will be NULL // - data: the buffer produced by next(). // - datalen: the length of the buffer produced by next(). // - field_present: nonzero if the field is present for the given event. // Return value: the produced value of the filter field. For extract_str(), a // NULL return value means that the field is missing for the given event. // char *(*extract_str)(ss_plugin_t *s, uint64_t evtnum, const char * field, const char *arg, uint8_t *data, uint32_t datalen); uint64_t (*extract_u64)(ss_plugin_t *s, uint64_t evtnum, const char *field, const char *arg, uint8_t *data, uint32_t datalen, uint32_t *field_present); // // This is an optional, internal, function used to speed up event capture by // batching the calls to next(). // On success: // - nevts will be filled in with the number of events. // - evts: pointer to an ss_plugin_event pointer. The plugin should // allocate an array of contiguous ss_plugin_event structs using malloc(), // as well as allocate each data buffer within each ss_plugin_event // struct using malloc(). Both the array of structs and each data buffer are // owned by the plugin framework and will free them using free(). // Required: no // int32_t (*next_batch)(ss_plugin_t* s, ss_instance_t* h, uint32_t *nevts, ss_plugin_event **evts); // // This is an optional, internal, function used to speed up value extraction // Required: no // int32_t (*register_async_extractor)(ss_plugin_t *s, async_extractor_info *info); // // The following members are PRIVATE for the engine and should not be touched. // ss_plugin_t* state; ss_instance_t* handle; uint32_t id; char *name; } source_plugin_info; // // Interface for a sinsp/scap extractor plugin // // // NOTE: For all functions below that return a char *, the memory // pointed to by the char * must be allocated by the plugin using // malloc() and should be freed by the caller using free(). // typedef struct { // // Return the version of the plugin API used by this plugin. // Required: yes // Return value: the API version string, in the following format: // "..", e.g. "1.2.3". // NOTE: to ensure correct interoperability between the engine and the plugins, // we use a semver approach. Plugins are required to specify the version // of the API they run against, and the engine will take care of checking // and enforcing compatibility. // char* (*get_required_api_version)(); // // Return the plugin type. // Required: yes // Should return TYPE_EXTRACTOR_PLUGIN. It still makes sense to // have a function get_type() as the plugin interface will // often dlsym() functions from shared libraries, and can't // inspect any C struct type. // uint32_t (*get_type)(); // // Initialize the plugin and, if needed, allocate its state. // Required: yes // Arguments: // - config: a string with the plugin configuration. The format of the // string is chosen by the plugin itself. // - rc: pointer to an integer that will contain the initialization result, // as a SCAP_* value (e.g. SCAP_SUCCESS=0, SCAP_FAILURE=1) // Return value: pointer to the plugin state that will be treated as opaque // by the engine and passed to the other plugin functions. // ss_plugin_t* (*init)(char* config, int32_t* rc); // // Destroy the plugin and, if plugin state was allocated, free it. // Required: yes // void (*destroy)(ss_plugin_t* s); // // Return a string with the error that was last generated by // the plugin. // Required: yes // // In cases where any other api function returns an error, the // plugin should be prepared to return a human-readable error // string with more context for the error. The plugin manager // calls get_last_error() to access that string. // char* (*get_last_error)(ss_plugin_t* s); // // Return the name of the plugin, which will be printed when displaying // information about the plugin. // Required: yes // char* (*get_name)(); // // Return the descriptions of the plugin, which will be printed when displaying // information about the plugin or its events. // Required: yes // char* (*get_description)(); // // Return a string containing contact info (url, email, twitter, etc) for // the plugin author. // Required: yes // char* (*get_contact)(); // // Return the version of this plugin itself // Required: yes // Return value: a string with a version identifier, in the following format: // "..", e.g. "1.2.3". // This differs from the api version in that this versions the // plugin itself, as compared to the plugin interface. When // reading capture files, the major version of the plugin that // generated events must match the major version of the plugin // used to read events. // char* (*get_version)(); // // Return a string describing the event sources that this // extractor plugin can consume. // Required: no // Return value: a json array of strings containing event // sources returned by a source plugin's get_event_source() // function. // This function is optional--if NULL then the extractor // plugin will receive every event. // char* (*get_extract_event_sources)(); // // Return the list of extractor fields exported by this plugin. Extractor // fields can be used in Falco rules and sysdig filters. // Required: yes // Return value: a string with the list of fields encoded as a json // array. // char* (*get_fields)(); // // Extract a filter field value from an event. // We offer multiple versions of extract(), differing from each other only in // the type of the value they return (string, integer...). // Required: for plugins of type TYPE_EXTRACTOR_PLUGIN only // Arguments: // - evtnum: the number of the event that is being processed // - id: the numeric identifier of the field to extract. It corresponds to the // position of the field in the array returned by get_fields(). // - arg: the field argument, if an argument has been specified for the field, // otherwise it's NULL. For example: // * if the field specified by the user is foo.bar[pippo], arg will be the // string "pippo" // * if the field specified by the user is foo.bar, arg will be NULL // - data: the buffer produced by next(). // - datalen: the length of the buffer produced by next(). // - field_present: nonzero if the field is present for the given event. // Return value: the produced value of the filter field. For extract_str(), a // NULL return value means that the field is missing for the given event. // char *(*extract_str)(ss_plugin_t *s, uint64_t evtnum, const char *field, const char *arg, uint8_t *data, uint32_t datalen); uint64_t (*extract_u64)(ss_plugin_t *s, uint64_t evtnum, const char *field, const char *arg, uint8_t *data, uint32_t datalen, uint32_t *field_present); } extractor_plugin_info; ``` ### Event Sources and Falco Rules Falco rules already have the notion of a "source", using the source property in rules objects, and there are currently two kinds of event sources: "syscall" and "k8s_audit". We will use the source property in Falco rules to map a given rule to the event source on which the rule runs. For example, given a plugin with source "aws_cloudtrail", and a Falco rule with source "aws_cloudtrail", the rule will be evaluated for any events generated by the plugin. Similarly, an extractor plugin that includes "aws_cloudtrail" in its set of event sources will have the opportunity to extract information from aws_cloudtrail events if a matching field is found in the rule's condition, exception, or output properties. This, combined with the restrictions below, allows a set of loaded rules files to contain a mix of rules for plugins as well as "core" syscall/k8s_audit events. We will also make a change to compile rules/macros/lists selectively based on the set of loaded plugins (specifically, their event sources), instead of unconditionally as Falco is started. This is especially important for macros, which do not contain a source property, but might contain fields that are only implemented by a given plugin. ### Handling Duplicate/Overlapping Fields in Plugins/Libraries Core At an initial glance, adding plugins introduces the possibility of tens/hundreds of new filtercheck fields that could potentially overlap/conflict. For example, what happens if a plugin defines a "proc.name" field? However, the notion of "event source" makes these potential conflicts manageable. Remember that field extraction is always done in the context of an event, and each event can be mapped back to an event source. So we only need to ensure that filtercheck fields are distinct for a given event source. For example, it's perfectly valid for an AWS Cloudtrail plugin to define a proc.name field, as the events generated by that plugin are wholly separate from syscall events. For syscall events, the AWS Cloudtrail plugin is not involved and the core libraries extract the process name for the tid performing a syscall. For AWS Cloudtrail events, the core libraries are not involved in field extraction and is performed by the AWS Cloudtrail plugin instead. We only need to ensure the following: * That only one plugin is loaded at a time that exports a given event source. For example, the libraries can load either a gke-audit-bridge plugin with event source k8s_audit, or eks-audit-bridge with event source k8s_audit, but not both. * That for a mix of source and extractor plugins having the same event source, that the fields are distinct. For example, a source plugin with source k8s_audit can export ka.* fields, and an extractor plugin with event source k8s_audit can export a jevt.value[/...] field, and the appropriate plugin will be used to extract fields from k8s_audit events as fields are parsed from condition expressions/output format strings. ### Plugin Versions and Falco Rules To allow rules files to document the plugin versions they are compatible with, we will add a new top-level field `required_plugin_versions` to the Falco rules file format. The field is optional, and if not provided no plugin compatibility checks will be performed. The syntax of `required_plugin_versions` will be the following: ```yaml - required_plugin_versions: - name: version: x.y.z ... ``` Below required_plugin_versions is a list of objects, where each object has `name` and `version` properties. If a plugin is loaded, and if an entry in `required_plugin_versions` has a matching name, then the loaded plugin version must be semver compatible with the version property. Falco can load multiple rules files, and each file may contain its own `required_plugin_versions` property. In this case, name+version pairs across all files will be merged, and in the case of duplicate names all provided versions must be compatible. ### Loading the plugins The mechanics of loading a plugin are implemented in the libraries and leverage the dynamic library functionality of the operating system (dlopen/dlsym in unix, LoadLibrary/GetProcAddress in Windows). The plugin loading code also ensures that: - the plugin is valid, i.e. that it exports the set of expected symbols - the plugin has an api version number that is compatible with the libraries instance - that only one source plugin is loaded at a time for a given event source - if a mix of source and extractor plugins are loaded for a given event source, that the exported fields have unique names that don't overlap across plugins #### Loading plugins in falcosecurity/libs At the libraries level, loading plugins is handled via the static method: ```c++ void sinsp_plugin::register_plugin(sinsp* inspector, string filepath, char* config, ...) ``` filepath points to a dynamic library containing code that exports plugin API functions. config contains arbitrary config content which is passed to init(). Note that the code using the libraries is responsible for determining the location of plugin libraries and their configuration. #### Loading plugins in falcosecurity/falco Falco will control/configure loading plugins via the new "plugins" property in falco.yaml. Here's an example: ```yaml plugins: - name: aws_cloudtrail library_path: aws_cloudtrail/plugin.so init_config: "..." open_params: "..." - name: http_json library_path: http_json/plugin.so init_config_file: http_json/config.txt open_params_file: http_json/params.txt # Optional load_plugins: [aws_cloudtrail] ``` A new "plugins" property in falco.yaml will define the set of plugins that can be loaded by Falco. The property contains a list of objects, with the following properties: * name: Only used for load_plugins, but by convention should be the same as the value returned by the name() api function. * library_path: a path to the shared library. The path can be relative, in which case it is relative to Falco's "share" directory under a "plugins" subdirectory e.g. /usr/share/falco/plugins. * init_config: If present, the exact configuration text that will be provided as an argument to the init() function. * init_config_file: If present, the provided file will be read and the contents will be provided as an argument to the init() function. * open_params: If present, the exact params text that will be provided as an argument to the open() function. * open_params_file: If present, the provided file will be read and the contents will be provided as an argument to the open() function. For a given yaml object in the plugins list, only one of init_config/init_config_file and one of open_params/open_params_file can be provided at a time. A new "load_plugins" property in falco.yaml will allow for loading a subset of the plugins defined in plugins. If present, only the plugins with the provided names will be loaded. ### Examples We have an initial version working, consisting of: * A version of falcosecurity/libs that supports the [plugin framework](https://github.com/falcosecurity/libs/tree/new/plugin-system-api-additions) * Support code and examples for [writing plugins in go](https://github.com/mstemm/libsinsp-plugin-sdk-go/tree/new/plugin-system-api-additions) * A [cloudtrail](https://github.com/mstemm/plugins/tree/new/plugin-system-api-additions) plugin that can generate events from cloudtrail logs and extract fields from those events. * A version of Falco that uses all of the above to [load and evaluate rules with plugins](https://github.com/leogr/falco/tree/new/plugin-system-api-additions) ================================================ FILE: proposals/20221129-artifacts-distribution.md ================================================ # Artifacts distribution This proposal aims to define guidelines for the official distribution of artifacts published by Falcosecurity. Therefore, to create a unified management of the distribution of artifacts, this document supersedes (for the parts concerning the distributions of artifacts) proposals [Falco Artifacts Scope - Part 1](https://github.com/falcosecurity/falco/blob/master/proposals/20200506-artifacts-scope-part-1.md), [Falco Artifacts Scope - Part 2](https://github.com/falcosecurity/falco/blob/master/proposals/20200506-artifacts-scope-part-2.md), and [Falco Drivers Storage S3](https://github.com/falcosecurity/falco/blob/master/proposals/20201025-drivers-storage-s3.md) and also extends and generalizes the proposal [Falco Rules and Plugin distribution](https://github.com/falcosecurity/falcoctl/blob/main/proposals/20220916-rules-and-plugin-distribution.md) for [falcoctl](https://github.com/falcosecurity/falcoctl). ## Goals - Allow users to consume artifacts in a consistent way - Define official artifacts - Unify distribution mechanism, infrastructure and tooling - Provide generic guidelines applicable to any artifact to be distributed ## Non-Goals - Infra/CI implementation details - Supply chain security topics ## Proposal With officially supported artifacts, we mean that set of artifacts published by Falcosecurity as part of Falco or its ecosystem. At the time of writing, the Falcosecurity organization distributes several kinds of artifacts in the form of files or container images. They include: - Installation packages - Helm charts - Drivers (eg, kmod, eBPF) - Rule files - Plugins - Other kinds may be added in the future. Features shipped with **official artifacts are intended for general availability(GA)**, unless otherwise specified (eg. if experimental or non-production ready features are present, they must be indicated in the release notes). The same artifacts can be distributed via multiple distribution channels, and each channel can be mirrored. **The [falco.org](https://falco.org/) website must list all official distribution channels and mirrors**. Any distribution channel not listed on our official website must not be considered part of the official distribution. However, maintainers can still use other channels for experimentation or incubating projects eventually. ### Distribution channels #### HTTP Distribution Distributing artifacts as plain files via HTTP is mostly intended for **humans, simple and legacy clients** (e.g., a shell script that downloads a file). The allowed publishing channels are: - **[download.falco.org](https://download.falco.org/)** where most of the file artifacts lives - **endpoints made available by GitHub** for the Falcosecurity organization (e.g., release download URL, GitHub pages, etc.). Typically, all official artifacts that can be shipped as plain files should be published at [download.falco.org](https://download.falco.org/) and available for download. Using the GitHub platform is allowed as an alternative assuming that artifacts are published under the Falcosecurity organization and the GitHub platform usage limitations are being respected (a notable example is publishing a [Helm chart index file using GitHub pages](https://falcosecurity.github.io/charts/)). It is allowed to publish other non-official artifacts (for example, [development builds](https://download.falco.org/?prefix=packages/bin-dev/)), taking that those are correctly denoted. Introducing other HTTP channels is discouraged. Providing mirrors is discouraged unless required for technical reasons. #### OCI Distribution Some artifacts are in the form of Open Container Initiative (OCI) images and require OCI registries to be distributed. Nevertheless, since the [OCI Distribution Spec](https://specs.opencontainers.org/distribution-spec/?v=v1.0.0) allows any content, even regular files can be stored in OCI registries and distributed likewise. Notably, the [Helm project in early 2022 started storing charts in OCI](https://helm.sh/blog/storing-charts-in-oci/) registries. One our tool [falcoctl did the same](https://github.com/falcosecurity/falcoctl/blob/main/proposals/20220916-rules-and-plugin-distribution.md) later. Distributing artifacts via OCI registries is intended for all compatible consumers (i.e., [falcoctl](https://github.com/falcosecurity/falcoctl)). It is **allowed and encouraged for any artifacts**. All official artifacts should be published so. The allowed publishing channels are: | Registry | Name | Account URL | | -------- | -------- | -------- | | `docker.io` | Docker Hub | https://hub.docker.com/u/falcosecurity | | `ghcr.io` | Github Packages Container registry | https://github.com/orgs/falcosecurity/packages | Both channels are equivalent and may publish the same artifacts. However, for historical reasons and to avoid confusion, the **`docker.io` registry should only be used for container images** and not for other kinds of artifacts (e.g., plugins, rules, etc.). Mirrors are allowed and encouraged if they facilitate artifacts consumption by our users. This proposal recommends to enable mirrors on the major public OCI registry, such as [Amazon ECR](https://gallery.ecr.aws/) (which is already implentend in our infra at the time of writing). Official **channels and mirrors must be listed at [falco.org](https://falco.org/)**. It is allowed to publish other non-official artifacts, even using image tags, taking that those are correctly denoted. #### Other channels At the time of writing, no other distribution channels are present or needed. However, in case a new kind of artifact will require a particular distribution mechanism (for example, in case an existing package manager system need to consume the artifact using its protocol), the rule of thumb is first to use the available GitHub features for the Falcosecurity organization, if possible. Users will quickly recognize the association between the artifact and the publisher (i.e., falcosecurity), and for that reason is usually preferable. In all other cases, introducing a new distribution channel must require extensive discussion among maintainers. Nevertheless, **introducing too many distribution channels is discouraged** because it disperses the effort and can mislead users. ### Publishing #### Source repository Artifacts must always be built starting from the originating source code and thru an auditable and reproducible process that runs on our infra. It's recommended that the naming and versioning of the published artifact consistently match the originating repository's naming and versioning. For example, the package `falco-0.33.0-x86_64.tar.gz` must match the source code of the git tag [0.33.0](https://github.com/falcosecurity/falco/tree/0.33.0) of the [falco](https://github.com/falcosecurity/falco) repository. It's recommended that **each repository publish only one kind of artifact** associated with it. Exceptions are allowed for: - mono repos (notably [charts](https://github.com/falcosecurity/charts) and [plugins](https://github.com/falcosecurity/plugins)), - or whenever technical constraints impose a different approach (notably, our Driver Build Grid lives on [test-infra](https://github.com/falcosecurity/test-infra), but the source code is in [libs](https://github.com/falcosecurity/libs)). Exceptions should be documented to avoid the users and contributors might be confused. #### Namespacing As a general rule, to avoid name clashing among different projects under the Falcosecurity organization, all **published artifacts should reflect the originating repository name** in their publishing URL. For example, all artifacts generated by the [falcosecurity/plugins](https://github.com/falcosecurity/plugins) repository should have `falcosecurity/plugins` as the URL's base path. Exceptions are allowed for: - legacy and already published artifacts (to avoid disruption); - justified technical reasons. #### Versioning All published artifacts must be labeled with version numbers following the **[Semantic Versioning 2 specification](https://semver.org/)**. For the [HTTP Distribution](#http-distribution), the version number must be reflected in the file name (including build metadata like the targeted arch and platform). For the [OCI Distribution](#oci-distribution), the version number must be reflected in the image tag (build metadata may be avoided if included in the manifest). ### Tooling Tooling is essential to deliver a consistent and straightforward UX to our users since the limited set of distribution channels is acceptable to provide just one (or a limited set of) tool(s) capable of working with various artifacts published by the Falcosecurity organization. In this regard, this proposal follows up the [Falco Rules and Plugin distribution](https://github.com/falcosecurity/falcoctl/blob/main/proposals/20220916-rules-and-plugin-distribution.md) proposal and recommends to use of **[falcoctl](https://github.com/falcosecurity/falcoctl) as the tool to managing artifacts specifically intended for Falco**. The tool's design should consider that other kinds of artifacts may be added in the future. Likewise, relying on existing **third-party tools for generic or well-known kinds of artifacts** (for example, Helm charts) is recommended. ### Ecosystem Compatibility with other tools on the broader cloud native ecosystem should be considered when dealing with artifacts and their distribution. It is also recommended to use third-party solutions and projects that facilitate our users' discovery of published artifacts (for example, https://artifacthub.io/). ## Action items The following subsections indicate major action items to be executed in order to transition from the current to the desiderate state of the art, as noted in this proposal. ### Move [Falco rules](https://github.com/falcosecurity/falco/tree/master/rules) to their own repo Falco rules files (i.e., the ruleset for the data source syscall) are currently only distributed in bundles with Falco. However, now falcoctl can manage rules artifacts so that we can ship them separately. The benefits of having rules living in their repository are: - dedicated versioning - rules release will not be tied anymore to a Falco release (e.g., no need to wait for the scheduled Falco release to publish a new rule aiming to detect the latest published CVE) - consistent installation/update mechanism with other rulesets (plugins rules are already published in their repository and can be consumed by falcoctl) Note that this change will not introduce a breaking change: Falco will continue shipping the default ruleset by including the published ruleset package. ### Make `falcoctl` official Considering the centrality of falcoctl for managing official artifacts for Falco, the falcoctl project must be promoted to "Official" status, and its repository assumed to be [core](https://github.com/falcosecurity/evolution/blob/main/GOVERNANCE.md#core-repositories). ### Deprecate `falco-driver-loader` At the time of writing, `falco-driver-loader` is a shell script shipped in a bundle with Falco that has the responsibility of installing a driver by either downloading it from our distribution channels or trying to build it on-the-fly. Our experience showed all the limitations of this approach, and it's now clear that such as script is hard to maintain. Furthermore, its responsibility overlaps with our aim to use `falcoctl` as the tool for managing artifacts. Thus, this proposal mandates to deprecate of `falco-driver-loader` in favor of `falcoctl.` However, to avoid user disruption and breaking legacy use case, it's recommended to provide still a faced script that exposes the same command line usage of `falco-driver-loader` but forward its execution to the new tool `falcoctl`. This implicitly requires that `falcoctl` be shipped in a bundle with Falco. ### Update the documentation This proposal mandates making use of official documentation (i.e., falco.org) to state official items, such as artifacts, distribution channels, and mirrors. For that reason, it becomes imperative to update the documentation periodically concerning the list of officially supported distribution channels and mirrors. ### Usage of GitHub Packages Since GitHub is the primary platform where the Falcosecurity organization hosts its code and infrastructure, its provided features should be preferred whenever possible. This proposal recommends using the GitHub Packages feature when the need to distribute a new kind of artifact arises. Such as convention should be adopted among all repositories of the organization. ================================================ FILE: proposals/20230511-roadmap-management.md ================================================ # Falco Roadmap Management Proposal ## Summary This document proposes the introduction of a structured process for managing Falco's roadmap and implementing related changes in our development process. The goal is to ensure the efficient execution of our roadmap objectives. ### Goals The pillars of this proposal are: - Define processes for release cycles and development iterations - Provide guidelines for planning and prioritizing efforts - Introduce regular meetings for core maintainers - Using *GitHub Project* as the primary tool for managing *The Falco Project* roadmap ### Non-Goals - Providing an exact set of criteria for task prioritization - Detailing testing procedures - Providing detailed instructions for GitHub Project usage - Addressing hotfix releases ### Scope of this Proposal Primarily, the roadmap targets the planning of Falco development and releases. However, given Falco's dependence on numerous components, it's inevitable that scheduling and planning activities span across multiple repositories. We anticipate that all [core repositories](https://github.com/falcosecurity/evolution#official) will be interconnected with the roadmap, making it comprehensive enough to incorporate items from all related [Falcosecurity repositories](https://github.com/falcosecurity) as necessary. This proposal does **not apply to hotfix releases** that may happen whenever needed at the maintainers' discretion. ## Release Cycles and Development Iterations Falco releases happen 3 times per year. Each release cycle completes, respectively, by the end of January, May, and September. A **release cycle is a 16-week time frame** between two subsequent releases. Using this schema, in a 52-week calendar year, we allocate 48 weeks for scheduled activities (16 weeks *x* 3 releases), leaving 4 weeks for breaks. The 16-week release cycle is further divided into three distinct iterations: | Iteration Name | Duration | Description | |---------------|----------|-------------| | Development | 8 weeks | Development phase | | Stabilization | 4 weeks | Feature completion and bug fixing | | Release Preparation | 4 weeks | Release preparation, testing, bug fixing, no new feature | ### Targeted Release Date The final week of the *Release Preparation* should conclude before the *last Monday of the release month* (ie. January/May/September). This *last Monday* is designated as the **targeted release date** (when the release is being published), and the remaining part of the week is considered a break period. ### Milestones For each release, we create a [GitHub Milestone](https://github.com/falcosecurity/falco/milestones) (whose due date must be equal to the target release date). We use the milestone to collect all items to be tentatively completed within the release. ### Alignment of Falco Components The release schedule of the [components Falco depends on](https://github.com/falcosecurity/falco/blob/master/RELEASE.md#falco-components-versioning) needs to be synchronized to conform to these stipulations. For instance, a [falcosecurity/libs](https://github.com/falcosecurity/libs) release may be required at least one week prior to the termination of each iteration. The maintainers are responsible for adapting those components' release schedules and procedures to release cycles and development iterations of Falco. Furthermore, all release processes must be documented and provide clear expectations regarding release dates. ## Project Roadmap We use the [GitHub Project called *Falco Roadmap*](https://github.com/orgs/falcosecurity/projects/5) to plan and track the progress of each release cycle. The GitHub Project needs to be configured with the above mentioned iterations and break periods, compiled with actual dates. It's recommended to preconfigure the GitHub Project to accommodate the current plus the following three release cycles. ### Roadmap Planning The roadmap serves as a strategic planning tool that outlines the goals and objectives for Falco. Its purpose is to visually represent the overall direction and timeline, enhance transparency and engage the community. The onus is on the [Core Maintainers](https://github.com/falcosecurity/evolution/blob/main/GOVERNANCE.md#core-maintainers) to manage the roadmap. In this regard, Core Maintainers meet in **planning sessions on the first week of each calendar month**. During these planning sessions, tasks are allocated to the current iteration or postponed to one of the following iterations. The assigned iteration indicates the projected completion date for a particular workstream. When a session matches with the commencement of an iteration, maintainers convene to assess the planning and prioritize tasks for the iteration. The first planning session of a release cycle must define top priorities for the related release. ## Testing and Quality Assurance (QA) Each iteration's output must include at least one Falco pre-release (or a viable development build) designated for testing and QA activities. While it's acceptable for these builds to contain unfinished features or known bugs, they must enable any community member to contribute to the testing and QA efforts. The targeted schedule for these Testing/QA activities should be the **last week of each iteration** (or earlier during the *Release Preparation*). Testing and Quality Assurance criteria and procedures must be defined and documented across relevant repositories. Furthermore, given the strong reliance of Falco on [falcosecurity/libs](https://github.com/falcosecurity/libs), the above-mentioned pre-release/build for Testing/QA purposes must be based on the most recent *libs* development for the intended iteration. This means that during each interaction, a *libs* release (either pre or stable) must happen early enough to be used for this purpose. ## Next Steps and Conclusions The Falco 0.36 release cycle, running from June to September 2023, will mark the initiation of the new process. This cycle will also serve as an experimental phase for refining the process. Furthermore, as soon as possible, we will kick off a Working Group specifically to ensure smooth execution. This group will involve community members in assisting maintainers with roadmap management. It will provide curated feature suggestions for the roadmap, informed by community needs. This approach would facilitate the core maintainers' decisions, as they would mostly need just to review and adopt these pre-vetted recommendations, enhancing efficiency. The Working Group's responsibilities will include (non-exhaustive list): - Address input from the [2023-04-27 Core Maintainers meeting](https://github.com/falcosecurity/community/blob/main/meeting-notes/2023-04-27-Falco-Roadmap-Discussion.md) - Sorting and reviewing pending issues to identify key topics for discussion and potential inclusion in the roadmap - Establishing protocols not explicitly covered in this document - Updating the documentation accordingly - Supporting Core Maintainers in managing the [Falco Roadmap GitHub project](https://github.com/orgs/falcosecurity/projects/5) - Gathering suggestions from all involved stakeholders to put forward potential enhancements Finally, we anticipate the need for minor adjustments, which will become apparent only after an initial period of experimentation. Thus we have to intend this process to be flexible enough to adapt to emerging needs and improvements as long as the fundamental spirit of this proposal is upheld. ================================================ FILE: proposals/20230620-anomaly-detection-framework.md ================================================ # On Host Anomaly Detection Framework - New `anomalydetection` Plugin ## Motivation **A Wind of Change for Threat Detection** Feel that light breeze? That is the continued advancement of cloud native security blowing steady. But despite our progress, threat actors are outpacing our innovation constantly finding new ways to thwart and tornado past our achievements — rule-based detections focus on what we *think* attackers will do, not on what they *are* doing and generate enough alerts to bury security analysts in a sandstorm of poor signal-to-noise. Can this dynamic be blown back to shift the information asymmetry in favor of defenders? This framework lays the foundation on how to create high-value, kernel signals that are difficult to bypass - but not in the traditional way. Advanced data analytics is an emerging crosswind that enables us to soar past attackers by detecting deviations in current behavior from past behavior. ## Benefits to the Ecosystem Advanced data analytics enables us to combine the intricacies of the Linux kernel with on-host anomaly detection in cloud native and cloud environments to determine patterns of past behavior in running applications. By detecting deviations in current behavior from past behavior, we can shift the focus away from relying solely on signatures and rule matching to catch attackers. Threat detection in open source and more importantly cloud native is constrained by the amount of rules we can write and the signatures we know to look for in our environments. But these have the same problem: they assume our attackers don't change what they're doing. The reality is attackers are not limited to the ways, means, and methods they employ to expose, manipulate, or even destroy our data, systems, and organizations. This framework leverages an attacker's mindset applied to detection engineering: observing and learning about our targets to create more rich and actionable alerts so we can catch them earlier and more often - regardless if it's behavior we know about, or something we haven't seen yet. ## Elevator Pitch When Falco processes events in userspace, its rules engine filters the events while the parsers simultaneously update and maintain an internal state. This state includes a process tree cache that enhances Falco alerts by providing contextual information derived from previous events. The goal is to enhance the "state engine" even further and provide an option for monitoring the behavior of applications over time. To achieve this, end users define a "behavior profile" in the configuration by combining existing event fields such as process name, file descriptor (fd), executable path, parent lineage, cmdline, and others. During event parsing on the hot path, Falco compresses and stores this information in a "filter" - an efficient probabilistic data structure that optimizes space, time, robustness and accuracy. As time progresses, Falco provides more accurate estimates of application behavior counts and identifies events as rare or heavy hitters. Instead of analyzing the original event stream, you can write Falco rules based on pre-filtered data. This approach introduces a novel threat detection framework that analyzes abnormal application behavior in real-time, derived and observed in a data-driven fashion, without requiring operator reconfiguration of Falco. It complements the operator's expertise and extends capabilities similar to our current practices. The new capability draws inspiration from big data stream and database query optimizations, ensuring that Falco maintains a streamlined real-time one-pass stream with zero allocations. Similar to Falco rules, the analysis of events may require multiple behavior profiles of different dimensions based on sets of events. These profiles can either vote in parallel or in a cascading fashion, a common practice in established algorithms. This is just the beginning and and paves the way for more sophisticated approaches, such as running Falco in a DAST-like capacity to build a pre-state pattern file on a workload with test data and soften the cold-start via distributing it to production. ## Challenges and Considerations First, The Falco Project is committed to continuously ensuring access to the most accurate data possible for on-host threat detection. As an example, recent efforts involved expanding kernel signal logging, such as verifying if an execve call is linked to a file descriptor existing exclusively in memory or improving the efficient and reliable resolution of symlinks for executable paths. Therefore, the proposed anomaly detection framework operates under the assumption of having the *correct* data, thereby complementing the ongoing efforts to expand logging coverage and improve its quality. In summary, the primary focus of the framework is to derive increased value from the existing *right* data that is currently available. There is a common perception that attacks on running cloud applications, as well as their indicators of compromise, are typically rare when the appropriate data or combination of signals is considered. While this holds true, there are inherent challenges in applying this concept of rarity to robust data analytics approaches. On the one hand, this is due to the diverse range of attacks and attack vectors. An attacker may introduce a new malicious binary (which is comparatively easier to detect using traditional rules and high-value kernel signals) after gaining initial access. Alternatively, they may exploit existing binaries, shell built-ins, and employ obfuscation techniques to "live off the land". The Turing completeness of the latter scenario, in particular, leads to an infinite number of attack possibilities. However, what poses even more challenges in anomaly detection lies not necessarily in the nature of attacks but rather in identifying the right signals and their appropriate combinations for robust analytics to distinguish between normal and anomalous behavior. This challenge becomes particularly evident when considering the natural fluctuations in application behavior over time and the occurrence of ad-hoc legitimate debugging activities. Such fluctuations can arise from various factors, including routine deployment updates. Moreover, certain applications may produce random file names or execute arbitrary executable paths as part of their regular operations, adding to the challenge of anomaly detection. This is compounded by the inherent "cold start" issue when initially observing an application. In such cases, the algorithms must demonstrate flexibility and robustness by recognizing and encoding consistent patterns, similar to how humans can identify the sameness by examining combinations of file names, command arguments, parent process lineage, and other attributes. Furthermore, factors like data inconsistency and the diverse forms of data representations (comprising a mix of numeric data and strings with varying meanings) further complicate the task. We believe it is important to incorporate operator heuristics or domain knowledge into the algorithm's definition of rarity. For example, while current algorithms are capable of generating human faces, they used to frequently produce images with different eye colors. However, if we were to inform the machine that humans typically have matching eye colors, it could easily correct this discrepancy. This highlights the role of the security engineer as a guiding hand to the algorithms, both in terms of handling noise tolerance and choosing the appropriate data to be ingested into the algorithm. This is crucial as machines are currently limited in their ability to draw meaningful observations from limited data and constrained memory. In summary, this is where the fusion of data-driven anomaly detection and rules matching will come into play. Lastly, the value proposition of conducting real-time anomaly analysis on the host lies in the unique options it offers, which cannot be achieved through alternative methods. On the host, we can observe anomalies based on all relevant and observed kernel events. In contrast, sending a large volume of kernel events to a centralized system would be impractical, resulting in significant costs for data pipeline management and data lake compute expenses. ## Initial Scope The initial scope is to implement the Count Min Sketch algorithm using n shared sketches and expose its count estimates as new filterchecks for use in Falco rules. An MVP can be explored in this libs draft PR [wip: new(userspace/libsinsp): MVP CountMinSketch Powered Probabilistic Counting and Filtering](https://github.com/falcosecurity/libs/pull/1453). Moreover, the initial anomaly detection framework will include a transparent `plugin` user interface for defining application behavior profiles and utilizing sketch count estimates in Falco rules. The primary direct benefit lies in establishing a safety boundary for Falco rules in production environments, allowing for broader rule monitoring while preventing Falco rules from blowing up in production. Furthermore, The Falco Project will provide adopters with valuable initial use cases, recommended thresholds, and callouts for known issues. One important consideration is the identification of SRE anti-patterns. Another consideration is to provide *very clear* guidance to adopters for setting and configuring parameters, including recommended minimums. Additionally, guidance should be provided on indicators to look for in order to determine if adjustments need to be made and in which direction, particularly when defining application behavior profiles. ## High-Level Technical Design of a New `anomalydetection` Plugin This document provides a high-level proposal with limited technical details. *Probabilistic Data Structures* One option for implementing the probabilistic filter is by utilizing a robust two-dimensional probabilistic data structure known as the Count Min Sketch. This data structure is widely employed in distributed stream processing frameworks such as Apache Spark, Apache Storm, Apache Flink, and others, as well as databases like Redis and PostgreSQL. Technical details and implications are extensively covered in numerous research papers and textbooks. Therefore, here are some key points to consider in order to make informed choices: - The challenges posed by both hard and soft collisions can be mitigated by using multiple non-cryptographic hash functions, which has been mathematically proven to be effective. - Despite providing one-sided error bounds and preventing undercounting, the sketchy data structure requires adopters to define a tolerance level for overcounting. This tolerance level determines what qualifies as rare or noteworthy. - To enhance accuracy and reduce estimation errors, consider debiasing data (e.g. Count Min Sketch with Conservative Updates) or applying a logarithmic scale to address kernel event data skew. The logarithmic scale may suit threat detection, targeting low-frequency or long-tail attack-related items. However, only use if performance overhead is acceptable. - Use larger shared sketches and incorporate container IDs as part of the behavior profiles to differentiate between workloads / applications. Conversely, use separate sketches for distinct behavior profiles, also known as the "what we are counting". - ... and numerous other aspects that will be discussed in subsequent implementation PRs. *Plumbing and Interface* The ultimate goal is to introduce these new capabilities as plugin. A significant amount of work will be dedicated to addressing the necessary plumbing required to support the new framework and integrate it with the existing rules filtering, `libsinsp` and `plugin` mechanisms. This integration aims to provide a user-friendly interface that allows users to easily configure and utilize the opt-in framework for different use cases. For instance, the interface should empower end users to define error tolerances and, consequently, sketch dimensions, along with other tuning parameters, bounds, and settings. Ultimately, it should enable the definition of n behavior profiles to facilitate the use of count estimates in Falco rules. ## What this Framework is Not - This framework is not intended to function as an event aggregator or enhancer, such as netflow data. Its sole purpose is to serve as an anomaly filter for individual events, utilizing the existing sinsp state, the newly built state through sketches, and the current rules engine. - The development of this framework will not be swayed by overly specific use cases that limit its broader adoption and coverage. - While it may not offer flawless attack threat detection from the beginning, it serves as an initial step towards comprehensive event logging and analysis, capturing all events that exhibit any form of new or changing behavior we observe. Therefore, initially, the greatest value lies in combining it with regular Falco rules based on the anomaly-filtered event stream. ## Why now? In case you haven't noticed, advanced data analytics is quite the big deal these days, and we can leverage robust established algorithms used in real production settings across various industries. The novelty lies in addressing the specific data encoding challenges unique to the field of cybersecurity, not re-inventing already established algorithms. Furthermore, over the past several Falco releases, we have significantly improved stability, configurability, and capabilities. Notably, the plugins system has been refined over the past year to efficiently access the complete `libsinsp` state, now also featuring an improved CPP SDK. Additionally, it now seamlessly collaborates with the existing primary syscalls event source, deviating from its original purpose of processing new data sources. This improvement allows for more intuitive functionality, as demonstrated by the new `k8smeta` plugin. Now is the opportune time to further enhance proven threat detection capabilities and expand the plugins system even more. *Initial community feedback concerning the KubeCon NA 2023 Full Talk* - Overall, the feedback for [A Wind of Change for Threat Detection](https://kccncna2023.sched.com/event/1R2mX/a-wind-of-change-for-threat-detection-melissa-kilby-apple) was very positive and appreciative, particularly regarding the direct real-life benefits (a safety boundary for Falco rules enabling broader monitoring that won't blow up in production). Suggestions for future development included integrating the sketch directly into the kernel driver (which would be a remarkable achievement if feasible). Lastly, people have inquired about the timeline for the availability of this feature. - Refer to the [KubeCon NA 2023 Slides](https://static.sched.com/hosted_files/kccncna2023/c5/A%20Wind%20of%20Change%20for%20Threat%20Detection%20-%20Melissa%20Kilby%20-%20KubeCon%20NA%202023.pdf) or [attached PDF](kubeconna23-anomaly-detection-slides.pdf) for more information. Here's the [Talk Recording](https://www.youtube.com/watch?v=1y1m9Vz93Yo) (please note that the first four minutes of the video are missing, but the slides and audio recordings are complete). *Falco Community Call - January 17, 2024* See dedicated [HackMD](https://hackmd.io/Ss0_1avySUuxArBQm-oaGQ?view): - While not blocking the start of the plugin or an alpha dev version, there's feedback from @jasondellaluce that plugins cannot access the existing `libsinsp` filtercheck. It would be advantageous to enable this access to avoid reimplementing them and the constant risk of falling out of sync with `libs`. @leogr mentioned that supporting this over time should be possible. - We have discussed the plugins config and are currently undecided on whether the definition of the behavior profile per sketch, meaning the fields that are string concatenated together and counted, should reside in the plugins config or in the rules files. The latter would potentially require a new rules component. Final decisions will be deferred to a later stage to ensure the config is intuitive, and we want to guarantee proper sketch definition when attempting to run Falco rules using the `anomalydetection` plugin. - One use case, namely determining if a rule has previously occurred in a container, could be addressed by this framework as well. However, we are currently unsure how to expose the rule names, as `libsinsp` is not aware of them. This may be an optimization we can address later and does not block the development of an initial version. - Future use cases might involve counting distinct values, utilizing the hyper log log algorithm. However, there will be additional technical challenges to overcome. - Finally, just to reiterate some feedback from the KubeCon talk, there's a suggestion that, perhaps in the future, we could pass intelligence back and forth between the drivers and userspace. This idea has been discussed independently, especially in the context of kernel-side filtering. However, such capabilities would be a long-term consideration. ## Proposed Timelines - Falco 0.37.0: Design details and scaffolding - Falco 0.38.0: Experimental release - Falco 0.39.0: First release ## Resources / References - [Probabilistic Data Structures and Algorithms for Big Data Applications](https://www.gakhov.com/books/pdsa.html) book - [Count Min Sketch blog 1](https://towardsdatascience.com/big-data-with-sketchy-structures-part-1-the-count-min-sketch-b73fb3a33e2a) - [Count Min Sketch blog 2](https://www.synnada.ai/blog/probabilistic-data-structures-in-streaming-count-min-sketch) - [Count Min Log Sketch](https://arxiv.org/pdf/1502.04885.pdf) paper - [Count Min Sketch with Conservative Updates](https://hal.science/hal-03613957/document#:~:text=Count%2DMin%20Sketch%20with%20Conservative%20Updates%20(CMS%2DCU),because%20of%20its%20inherent%20difficulty) paper - [xxHash](https://github.com/Cyan4973/xxHash) as new dependency for fast and reliable hashing (using xxh3) ================================================ FILE: proposals/20231220-features-adoption-and-deprecation.md ================================================ # Features Adoption and Deprecation Policies Proposal This proposal aims to introduce a balance between maintaining adopter trust and the need for The Falco Project to evolve. Historically, Falco has favored rapid evolution over providing long-term support for features and interfaces. However, some project subsystems have been implicitly assumed not to allow backward-incompatible changes (e.g., we have almost never removed a condition syntax field). These implicit conventions have never been formalized, and decisions in this regard have been left unspecified. ## Goals - Establish adopter expectations on the operational cost of using Falco. - Provide a clear path for features to be adopted and dismissed. - Allow quick evolution and experimentation without disrupting our adopters' deployments. - Detail the process for introducing new features, following a "sandbox" to "incubating" to "stable" progression. - Define the scope of the policy, including which aspects of Falco are covered (e.g., command line flags, configuration files, rules syntax). - Establish stages for deprecating features, aligning with the project's current status (pre- and post-1.0 stages). - Adopt a semantic versioning (semver) approach. ## Non-Goals - Define the number of previous releases that will receive patches or security updates and the duration of this support. - Define the criteria for Falco 1.0. ## Scope The proposed policies apply to Falco, its subsystems (e.g., rules, the plugin system), and all [core projects](https://github.com/falcosecurity/evolution#core) which are deemed [stable](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#stable), thus officially supported by The Falco Project. ## Definitions ### Feature Changes A feature is a distinct and specific functionality or characteristic of Falco and its core components that provides value to the user by enabling them to perform particular tasks. Features encompass aspects such as functionality, user value, usability, integrability, scalability, configurability, and discoverability. Features can range from essential user interface elements to complex, multifunctional operations. A feature change refers to any modification or update to an existing feature or the addition of a new feature. This does not include documentation, Falco compatibility across different environments, platforms, systems, or other software or hardware, bug fixing (stated it does not require a feature change to overcome the problem), and performance (unless a change produces a measurable effect on usability). ### Behavior Changes A behavior change refers to alterations in how Falco, or a specific feature within it, operates or responds under certain conditions. Unlike feature changes, behavior changes are more about tweaking the underlying logic or the way existing features interact or perform, particularly the expected behavior of Falco when run with the default configuration. Behaviors are generally documented. Any modification that does not meet the conditions and expectations of an already documented feature is assumed to be a behavior change. Undocumented behaviors may be included in this definition if there's strong evidence or suspicion that users rely on those undocumented behaviors. ### User-Facing Changes User-facing changes refer to any feature changes, behavior changes, modifications, or additions that are directly noticeable and interactable by the end users. These changes affect how Falco operates from the user's perspective (notably any change that can lead to user disruption). Unlike internal changes (i.e., code refactoring, CI, maintenance-related changes), which are under-the-hood improvements not directly visible to the user, user-facing changes are evident in the Falco and its core components interface and functionality. ### CLI/Config Area Falco is comprised of the Falco binary and other programs and tools cooperating (notably [falcoctl](https://github.com/falcosecurity/falcoctl)). These programs are the primary user interface for Falco. Any feature or behavior changes to the following elements of these programs are assumed to be user-facing changes to the CLI/Config area: - Program name. - Distribution mechanism and packaging (e.g., a container image). - Command line flags and options. - Environment variables. - Configurations. - Elements that affect the program's lifecycle (e.g., the effect of sending a SIGINT to the program). - Elements that allow scripting, automation, or interaction with other programs (e.g., piping and redirection). - Program inputs, excluding elements explicitly governed by other areas (e.g., [Falco rules](#rules-area)). - Program outputs excluding elements explicitly governed by other areas (e.g., [Falco outputs/alerts](#outputs-alerts-area)). ### Rules System Area Rules are the primary input for Falco. Any feature or behavior changes to the following aspects or elements are assumed to be user-facing changes to the rules system area: - Syntax. - File format. - Schema (i.e., supported fields). - Elements that affect the way users can implement rules. - Elements that affect the way rules are triggered. However, any change related to the rule's output when triggered (i.e., the alert) is out of scope for this area (see next section). Note that this area does not include changes related to the ruleset files. Ruleset distributions follow their own [Rules Maturity Framework](https://github.com/falcosecurity/rules/blob/main/CONTRIBUTING.md#rules-maturity-framework) policies. ### Outputs/Alerts Area Alerts, delivered through Falco output channels, are Falco's primary output. The way and the format in which alerts are produced can have a significant impact on adopters. For example, removing a supported rule field also impacts this area, as adopters may have relied on that field when consuming Falco output. Any feature or behavior changes to the following aspects or elements are assumed to be user-facing changes to the Outputs/Alerts area: - Output and logging formats. - Schema of outputted data (i.e., supported fields). - Falco output channels. - Any element that might be consumed from the output. ### Subsystem APIs (Plugins, gRPC Output, Metrics, etc.) Area Falco is also comprised of several subsystems providing specific APIs. These subsystems notably include plugin system API, gRPC output API, and metrics API. In the context of this proposal, only changes to **public APIs** are assumed to be user-facing changes to this area. Public APIs are defined as those supporting Falco functioning and explicitly intended for user usage. Internal APIs consumed by Falco or other tools are out of scope for this area. For instance, the driver APIs or libs APIs are intended to be mainly consumed by Falco and not by users. ### Platform Support Area Platform support for Falco encompasses the range of platforms, systems, and environments it is designed to operate in. Platform support may significantly vary by Falco's data sources and use cases. For example, its compatibility differs when utilized for Kubernetes audit events versus system call events. Additionally, platform support can be influenced by deployment methods (e.g., directly on a host versus within Kubernetes) or configurations (e.g., running in privileged versus least privileged mode). Given the diversity of potential platforms and setups, only those explicitly listed in Falco's documentation are considered officially supported. While Falco may function on other platforms, official support is guaranteed solely for documented ones. Therefore, changes in platform compatibility or behavior that are documented explicitly assumed to be user-facing changes to the Platform Support area. ### Release Cycle In the context of this proposal, a release cycle is the period between two consecutive major or minor releases of Falco. Hotfix/Patch releases must not be counted. The actual duration of a release cycle can vary. Still, it's assumed to be about 16 weeks (as per our current defined [Release Cycles and Development Iterations](https://github.com/falcosecurity/falco/blob/master/proposals/20230511-roadmap-management.md#release-cycles-and-development-iterations)). In case of future modification to the Falco release schedule, a period of minimum 3 months must be assumed. ## Proposal ### Maturation Levels Maturation levels (inspired by those we already have in place for [repositories](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#status)) are used to characterize the maturity of a feature. Each feature will have an assigned level at any specific time (i.e., a Falco release). Levels are shown in the table below. | Maturity Level | Intended for | | --- | --- | | Sandbox | Experimental/alpha features, not recommended for production use, can be removed at any time without further notice. | | Incubating | Beta features, long-term support is not guaranteed. | | Stable | General Availability (GA) features for which long-term support is expected. | | Deprecated | See the [deprecation policy](#Deprecation-policy) section below. | ### Adoption Policy The adoption policy applies to any backward compatible user-facing changes which add functionalities. For non-backward compatible changes see the [deprecation policy](#Deprecation-policy) below. **Adoption rules**: 1. A feature can be introduced at only one of the following levels: - Sandbox: The feature must be opt-in (e.g., not enabled by default), labeled as *Sandbox* and the user must be proactively informed by the experimental nature of the feature (i.e. emitting a notice when the feature is being enabled). - Incubating: The feature must be labeled as *Incubating*. 2. Any functionality additions to an existing feature are inherently introduced at the same level as the feature itself unless logically separable (for instance, a sub-feature that may be enabled separately). 3. A feature can be promoted *from Sandbox to Incubating* or *from Incubating to Stable* only after at least one release cycle has passed without user-facing changes to the feature. 4. A feature cannot be demoted to a previous level. _Note about behaviors_: This policy indirectly applies to behaviors, too. Behavior changes are assumed to be a consequence of a feature change. The adoption level of a documented behavior is considered to be the same as the related feature. Furthermore, behavior changes are particularly relevant in the context of deprecation (see the next section). ### Deprecation Policy The deprecation policy applies to any non-backward compatible user-facing changes. Any other changes introduced in a backward-compatible manner does not fall under the scope of this deprecation policy. **Deprecation rules**: 1. Sandbox features can be removed or changed at any time without notice. No deprecation period is required. 2. Incubating or Stable features and documented behaviors must enter a deprecation period and function for no less than the indicated release cycles (see tables below) after their announced deprecation. - If the change affects the feature partially, the deprecation applies only to that feature part. - If the change removes the feature entirely, the deprecation applies to the entire feature. 3. At least for the entire deprecation period, the feature must be labeled as *Deprecated* in all relevant documentation, and: - for deprecated configurations or CLI elements, a warning must be emitted warnings when the feature is being enabled or used; - for deprecated APIs, when technically feasible, the API should be signal the deprecation status (this may vary depending on the specific subsystem); - for deprecated behaviors the documentation must highlight the _before_ and _after_ behavior, alongside with a prominent deprecation notice. 4. Any Pull Request introducing a deprecation notice must be labeled and include a note in the format `DEPRECATION NOTICE: ...`. 5. Any Pull Request introducing a breaking change due to the end of the deprecation notice period must be labeled and include a note in the format `BREAKING CHANGE: ...`. - It is also recommended for code commits that introduce a breaking change to follow the related [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/#specification). The minimum deprecation period length depends on the affected area. If a single change spans multiple areas, the area with the most extended deprecation period is assumed. Longer deprecation periods are allowed if the feature is deemed to be particularly critical or widely used. #### Deprecation Period Lengths _The units represent the number of releases._ ##### Before Falco 1.0 | Area | Stable | Incubating | | -------------- | ------ | ---------- | | *all areas* | 1 | 0 | ##### Since Falco 1.0 onward | Area | Stable | Incubating | | -------------- | ------ | ---------- | | Behaviors | 2 | 1 | | Rules System | 2 | 1 | | Output/Alerts | 2 | 1 | | Platform | 2 | 1 | | CLI/Config | 1 | 1 | | Subsystem APIs | 1 | 0 | ### Examples **Example 1** Let's consider a feature _foo_ in the Output/Alerts Area introduced in Falco 1.0.0 and labeled as *Incubating*. The feature is promoted to *Stable* in Falco 1.1.0 (because the feature did not get any user-facing change). Subsequently, maintainers decide that backward-incompatible changes must be introduced in _foo_ to improve its functionality. The part of the feature to be changed is labeled as *Deprecated* in Falco 1.2.0, and the deprecation period starts. The non-backward compatible change is then introduced in Falco 1.4.0. **Example 2** The `--bar` flag in the CLI/Config Area has been introduced since Falco 1.1.0 and is labeled as *Stable*. Before releasing Falco 1.5.0, maintainers realize `--bar` is redundant and should be removed. The flag is labeled as *Deprecated* in Falco 1.5.0, and the deprecation period starts. The flag is removed in Falco 1.6.0. ### Exceptions - Ruleset in the official distributions follow the [Rules Maturity Framework](https://github.com/falcosecurity/rules/blob/main/CONTRIBUTING.md#rules-maturity-framework) policies. - Subsystems or subcomponents may have additional criteria and exceptions. Stated other criteria and exceptions must not directly affect the main Falco distribution (e.g., *falcoctl* can have a different release cycle and different policies; however, if Falco relies on a specific *falcoctl* feature, that *falcoctl* feature adoption and deprecation must be strictly compatible with the rules described in this proposal). - Internal APIs are out of scope for this policy. Their adoption models and deprecation policies might be regulated separately. - Different parties may provide plugins, and each plugin may have a different maturity level. Only those plugins officially maintained by The Falco Project and identified as "core" for Falco are in scope for this policy; all others are excluded. - Any other exceptions to the rules provided by this policy require a formal core maintainer majority vote. ### Versioning Regarding the above policies, component versioning must adhere to [Semantic Versioning 2.0.0](https://semver.org/). However, in the context of Falco core components, the scope extends beyond the strict API definition and includes any user-facing changes. Thus, given a version number `MAJOR.MINOR.PATCH` increment the: - *MAJOR* version when the deprecation period of one or more _stable_ features ends, thus introducing incompatible user-facing or API changes. - *MINOR* version when adding functionality in a backward-compatible manner. - *PATCH* version when making backward-compatible bug fixes. Moreover, *MAJOR* version zero (0.y.z) is for versioning stabilization (i.e., before defining the public set of user-facing features and APIs). At this stage, the *MINOR* version is allowed to be incremented instead of the *MAJOR* version. ### Documentation Documentation must be tied to a specific release and reflect the adoption level status of a feature at that specific release. In particular: - Deprecated items must be labeled `DEPRECATED` in all relevant documentation. - Stable items must be sufficiently documented. Explicitly labeling the Stable status is not required or recommended. - Incubating items must be sufficiently documented and labeled `INCUBATING` in all relevant documentation. - Sandbox items may be partially documented and labeled `SANDBOX` in all relevant documentation, if any. The relevant documentation must also explicitly state the experimental nature of the item. ## Transition Phases Since software components may need to adapt to implement the requirements this proposal mandates, we assume the following stages are required to transition from the current state to the desired state fully: - Within Falco 0.38, at least stable features must be identified, and the adoption policy and relevant documentation should be implemented in Falco. Exceptions may be made temporarily for the deprecation policy. - Within subsequent releases and no later than Falco 1.0.0 (still not scheduled to date), all the policies must be strictly implemented in Falco and documented in [falco.org](falco.org). The [Rules Maturity Framework](https://github.com/falcosecurity/rules/blob/main/CONTRIBUTING.md#rules-maturity-framework) must be adapted to ensure it aligns with the spirit of this proposal. Exceptions may be made temporarily for other [core projects](https://github.com/falcosecurity/evolution#core) with [stable](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#stable) status, assuming exceptions don't severely affect the main Falco distribution. - Within Falco 1.1.0, all the policies must be strictly implemented in Falco and in all [core projects](https://github.com/falcosecurity/evolution#core) with [stable](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md#stable) status. During the transition phases, maintainers can fine-tune these policies and add further exceptions, eventually. After this initial transition phases, the policy is assumed to be established. From then on, any policy modifications, updates, and exceptions must be subject to a core maintainer majority vote to ensure the policy remains relevant and practical. ================================================ FILE: proposals/20251205-multi-thread-falco-design.md ================================================ # Multi-Threaded Falco High-Level Design (Working draft) ## Summary This document outlines a high-level design for implementing multi-threading in Falco. The goal of this proposal is to overcome Falco's single-threaded architecture to improve scalability in scenarios where the amount of events produced cannot be processed in a single thread. This is achieved by leveraging multiple threads for event processing, rule evaluation, and output handling, enabling Falco to better utilize multi-core systems and reduce event drops under high event rates. ## Goals * Address the problems related to single CPU core saturation, leading to dropped events. * Minimize the performance impact on the single threaded usage, that remains the default. ## Non-Goals * This document does not cover low-level implementation details that will be addressed in specific design documents for each component or directly in the implementation phase. * This document does not focus on performance optimization, the primary goal is scalability improvements to handle higher event rates that exceed single-thread processing capacity. ## Success Metrics The success of this multi-threading initiative will be measured by the following key metrics: * **Event Drop Rate Reduction**: The primary success metric is the reduction in event drops under high event rates. A successful implementation should significantly reduce or eliminate event drops that occur when a single thread cannot keep up with the event rate. * **Throughput Scaling**: The system should demonstrate improved throughput (events processed per second) that scales with the number of worker threads, up to a reasonable limit based on available CPU cores and workload characteristics. * **CPU Utilization**: Multi-threaded Falco should better utilize available CPU cores, with worker threads distributing load across multiple cores instead of saturating a single core. * **Single-Threaded Performance Preservation**: The single-threaded mode (default) should maintain its current performance characteristics, with minimal or no performance regression when multi-threading is disabled. These metrics will be evaluated through benchmarking and real-world deployment scenarios to validate that the multi-threaded architecture achieves its scalability goals without compromising correctness or introducing significant overhead. ## High-Level Design ### Current Architecture ![Current Falco Architecture](images/falco-architecture.png) * The kernel driver (via kmod or eBPF) writes events into per-CPU ring buffers. Each CPU has its own buffer to avoid lock contention. We have a ring buffer per CPU core, and a single userspace. * Userspace (libscap) performs an `O(n_cpus)` scan on every next() call, it peeks at the head event from each ring buffer, finds the event with the minimum timestamp across all buffers and returns that event to Falco for processing. The consumer position is only advanced after the event has been consumed (on the next call), ensuring the caller can safely read the event data and avoiding the need to perform copies of the event data. * Libsinsp processes the events sequentially as they are received from libscap, building a stateful representation of the system and providing the necessary context for rule evaluation. * Falco evaluates the rules against the processed events and generates alerts based on the defined security policies. ### Proposed Architecture Overview ![Multi-Threaded Falco Architecture](images/falco-multi-thread-architecture.png) * The kernel driver (modern eBPF probe) routes events into per-partition ring buffers based on TGID. The routing logic executes in kernel space (within the eBPF program), where each event's TGID is hashed and used to select the target ring buffer. Only the modern eBPF probe is supported, as it relies on [BPF_MAP_TYPE_RINGBUF](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_RINGBUF/) which does not have a per-CPU design as opposed to the `BPF_MAP_TYPE_PERF_EVENT_ARRAY` used by the legacy eBPF probe. * Each ring buffer is associated with an event loop worker thread that processes events from its assigned ring buffer. * The `libsinsp` state (e.g., the thread state) is maintained in a shared data structure, allowing all workers to access data pushed by other workers. This is crucial for handling events like clone() that rely on data written by other partitions. This requires designing lightweight synchronization mechanisms to ensure efficient access to shared state without introducing significant contention. A dedicated proposal document will address the design of the shared state and synchronization mechanisms, and data consistency. * Falco's rule evaluation is performed in parallel by multiple worker threads, each evaluating rules against the events they process. Current Falco plugins are not supposed to be thread-safe. A dedicated proposal document will address the design of a thread-safe plugin architecture. * **Output handling** is already designed for multi-threaded access. The `falco_outputs` class implements a thread-safe, queue-based architecture using Intel TBB's `concurrent_bounded_queue`, which is specifically designed for multi-producer, single-consumer scenarios. Multiple worker threads can concurrently call `handle_event()` to enqueue alert messages using the thread-safe `try_push()` operation. A dedicated output worker thread consumes messages from the queue using `pop()` and sends them to all configured outputs (stdout, file, syslog, gRPC, HTTP, etc.). This design is already proven in production, as Falco's multi-source support (where different event sources run in separate threads) already uses this same queue concurrently. The existing implementation requires no changes to support multi-threaded event processing. Note that while outputs are processed in order within the queue, alerts from different worker threads may be interleaved, meaning strict temporal ordering of alerts across different processes is not guaranteed. This is acceptable for security monitoring use cases where the primary concern is detecting and reporting security events rather than maintaining precise event ordering. ### Work Partitioning Strategies A crucial and challenging design aspect is partitioning the work to achieve a good trade-off among the following properties: 1. **Even load balancing** between threads 2. **Low contention** on shared data (or no shared data) 3. **Avoiding temporal inconsistencies and causality violations** (e.g., processing a file-opening event before the related process-forking event) The first two properties are primarily focused on performance, while the third is essential for the correctness of the solution. These aspects are intrinsically linked. Based on the analysis below, **Static Partitioning by TGID** is the proposed approach for the initial implementation. #### Static Partitioning by TGID (Thread Group ID / Process ID) Events are routed based on the TGID in kernel space (within the eBPF program) to a ring buffer dedicated to a specific partition. The routing logic executes at the point where events are captured, before they are written to any ring buffer. This partition is then consumed by a dedicated worker thread in userspace. The routing in the eBPF program can be accomplished with a simple hash and modulo operation, depending on the desired number of worker threads: ``` ring_buffer_index = hash(event->tgid) % num_workers ``` The hash function and number of workers are configured at eBPF program initialization time, allowing the kernel to route events directly to the appropriate ring buffer without userspace intervention. **Pros:** * **Reduced need for thread synchronization**: While structures keeping thread group data are shared across all worker threads and require synchronization, TGID partitioning minimizes cross-partition access. For data stored per thread-group (such as file descriptors), TGID partitioning guarantees a single writer (the worker thread assigned to that TGID), resulting in low contention since the data is mostly accessed by the same partition. Synchronization is needed only in two specific cases: 1. **Clone/fork events**: When handling clone/fork events, the worker thread needs to access thread information from the parent thread, which may reside in a different partition. This requires synchronization to read the parent's state (e.g., file descriptors, environment variables) that will be inherited by the child. 2. **Proc exit events**: When a process exits, reparenting logic may need to access thread information from other partitions to handle child processes that are being reparented to a different thread group. * Guarantee of sequential order processing of events related to the same thread group/process, as they are handled by the same worker thread. This limits the chance of temporal inconsistencies. **Cons:** * **Load Imbalance / "Hot" Process Vulnerability**: This static partitioning is susceptible to uneven worker load distribution, as a small number of high-activity ("hot") processes can overload the specific worker thread assigned to their TGID, creating a bottleneck. * **Cross-Partition Temporal Inconsistency**: Events that require information from a parent thread (e.g., fork/clone events) can still lead to causality issues. If the parent's related event is handled by a different, lagging partition, the required context might be incomplete or arrive out of order. Note that load imbalance amplifies this issue. Missing thread information is easy to detect, but there are also cases where information is present but not up-to-date or ahead of the time the clone event happened. **Ancestor information during rule evaluation**: When evaluating rules that require ancestor information, the worker thread may need to access thread data from other partitions. Falco rules commonly check ancestor process attributes using fields that traverse the process hierarchy. Based on actual usage in Falco rules, commonly used ancestor fields include: - `proc.aname` / `proc.aname[N]` - ancestor process name (where N is the generation level: 1=parent, 2=grandparent, 3=great-grandparent, etc., up to at least level 7) - `proc.aexepath[N]` - ancestor executable path (e.g., `proc.aexepath[2]` for grandparent) - `proc.aexe[N]` - ancestor executable (e.g., `proc.aexe[2]` for grandparent) Accessing stale or "ahead" ancestor data (where the ancestor's state may be out of date or from events processed by other partitions with different timestamps) could lead to false positives or false negatives in rule evaluation. We acknowledge this potential issue and plan to assess its impact and determine appropriate mitigations once we have a running prototype. **Mitigations:** * **Last-Resort Fetching**: Fetching the thread information from a different channel to resolve the drift (e.g., proc scan, eBPF iterator). This solution is considered as a last resort because it risks slowing down the event processing loop, potentially negating the performance benefits of multi-threading. * **Context Synchronization**: Wait for the required thread information to become available. This can be decomposed into two orthogonal concerns: **How to handle the wait:** * **Wait/Sleep (Blocking)**: The worker thread blocks (sleeping or spinning) until the required data becomes available. Simple to implement, but the worker is idle during the wait, reducing throughput. * **Deferring (Non-blocking)**: The event is copied/buffered for later processing; the worker continues with other events from its ring buffer. More complex (requires event copying, a pending queue, and a retry mechanism), but keeps the worker productive. **How to detect data readiness:** * **Polling**: Periodically check if the required data is available (spin-check for Wait/Sleep, or periodic retry for Deferring). Simple but wastes CPU cycles. * **Signaling**: Partitions proactively notify each other when data is ready. More efficient but requires coordination infrastructure (e.g., condition variables, eventfd, or message queues). These combine into four possible approaches: | | Polling | Signaling | |---|---------|-----------| | **Wait/Sleep** | Spin-check until ready | Sleep on condition variable, wake on signal | | **Deferring** | Periodically retry deferred events | Process deferred events when signaled | **Synchronization point**: A natural synchronization point is the **clone exit parent event**. At this point, the parent process has completed setting up the child's initial state (inherited file descriptors, environment, etc.), making it safe to start processing events for the newly created thread group. **Special case — `vfork()` / `CLONE_VFORK`**: When `vfork()` is used, the parent thread is blocked until the child calls `exec()` or exits, delaying the clone exit parent event. An alternative synchronization point may be needed (e.g., adding back clone enter parent). ### Other Considered Approaches #### Static Partitioning by TID (Thread ID) Similar to the previous approach, but events are routed by TID instead of TGID. **Pros:** * Guarantee of sequential order processing of events related to the same thread, as they are handled by the same worker thread. This limits the chance of temporal inconsistencies. * Good enough load balancing between partitions. **Cons:** * **Cross-Partition Temporal Inconsistency**: This approach can lead to temporal inconsistencies when accessing/writing information from/to other processes or from the Thread Group Leader (e.g., environment, file descriptor information is stored in the thread group leader). #### Static Partitioning by CPU Core This approach routes events based on the CPU core where the event was captured. Each CPU core has its own ring buffer (per-CPU buffers), and multiple CPU buffers are assigned to the same partition. Each partition is consumed by a dedicated worker thread that reads from all the per-CPU buffers assigned to it. The number of partitions does not necessarily match the number of CPU cores—a single partition can read from multiple per-CPU buffers, allowing flexibility in choosing the number of worker threads independently from the number of CPU cores. This leverages the existing per-CPU ring buffer infrastructure used by the kernel module (kmod) and legacy eBPF probe, where events are written to per-CPU buffers that are then grouped into partitions consumed by worker threads. **Pros:** * **Natural Load Distribution**: Events are naturally distributed across CPUs based on where processes execute, providing inherent load balancing that reflects actual system activity. * **No Routing Logic Required**: Uses the existing per-CPU ring buffer design, eliminating the need for custom routing logic in kernel or userspace. CPU cores are simply mapped to partitions (e.g., via modulo operation: `partition = cpu_id % num_workers`), and each worker thread reads from all per-CPU buffers assigned to its partition. * **Low Synchronization Overhead**: Events from per-CPU buffers assigned to the same partition are processed sequentially by the same worker thread, reducing cross-thread synchronization needs. * **Flexible Partitioning**: The number of partitions (and thus worker threads) can be chosen independently from the number of CPU cores, allowing optimization based on workload characteristics rather than hardware topology. **Cons:** * **Cross-CPU Temporal Inconsistency**: Events from the same process or thread group can be processed by different worker threads if the process migrates between CPUs, leading to potential temporal inconsistencies and causality violations. This is particularly problematic for multi-threaded applications that may execute on different CPUs. * **Process Migration Effects**: CPU migration can cause events from the same process to be processed out of order, as events captured on different CPUs are handled by different worker threads. * **Load Imbalance with CPU Grouping**: When multiple per-CPU buffers are assigned to the same partition, the worker thread must process events from all assigned buffers. If the activity levels across these CPUs are uneven, the worker thread may experience load imbalance, with some partitions handling more active CPUs than others. The worker thread must also coordinate reading from multiple buffers, potentially using techniques similar to the current `O(n_cpus)` scan to maintain event ordering. * **Modern eBPF Probe Limitation**: The modern eBPF probe uses `BPF_MAP_TYPE_RINGBUF`, which does not have a per-CPU design. This approach would only be viable with the kernel module (kmod) or legacy eBPF probe that use `BPF_MAP_TYPE_PERF_EVENT_ARRAY` with per-CPU buffers. #### Functional Partitioning (Pipelining) Instead of partitioning the data, this approach partitions the work by splitting processing into phases: 1. **Parsing**: Runs in a single thread, the state is updated in this phase. 2. **Rules evaluation**: Runs in a thread chosen from a worker thread pool, the state is accessed but not modified. **Pros:** * The state handling remains single-threaded, avoiding any synchronization issue on the write side. * The load balancing of the Rules evaluation phase is good as it does not require any form of stickiness. Every worker can take whatever event, and a simple round-robin policy can be applied. **Cons:** * The "Parsing" stage is likely to become the bottleneck; a single thread here limits total throughput regardless of how many cores you have. * As we are parallelizing parsing and rules evaluation phases, we need an MVCC (multi-version concurrency control) technique to maintain multiple levels of state to use the state at the right point in time during rules evaluation. * Processing multiple events in parallel involves changes at the driver and libscap level. At the moment we are processing one event at a time from the driver memory without copying. To be able to process multiple events in parallel, we need to adapt the ring-buffer to make sure that `next()` does not consume the event. We would also need some flow control (e.g., backpressure) to avoid processing too many events in parallel. This problem would arise only if the rules evaluation phase is slower than the parsing phase. #### Comparison Summary | Approach | Load Balancing | Contention | Temporal Consistency | |----------|----------------|------------|----------------------| | TGID | Moderate (hot process risk) | Low | Good (within process) | | TID | Good | Higher | Partial (thread-level only) | | CPU Core | Good | Low | Poor (process migration issues) | | Pipelining | Good (rules evaluation phase) | Low (writes) | Requires MVCC | #### Rationale for TGID Partitioning TGID partitioning was chosen because it offers the best balance between synchronization complexity and correctness guarantees. TID partitioning increases cross-partition access for thread group leader data (e.g., file descriptor table, working directory, environment variables), increasing the coordination cost. Per-CPU partitioning, while leveraging existing infrastructure, suffers from process migration issues that can cause significant temporal inconsistencies when processes move between CPUs. Functional partitioning, while elegant in its separation of concerns, introduces a single-threaded bottleneck in the parsing phase that limits scalability regardless of available cores, and requires complex MVCC mechanisms for data consistency and mechanisms for handling multiple events in parallel. ### Risks and Mitigations - **Increased Complexity**: Multi-threading introduces complexity in terms of synchronization and state management. Mitigation: Careful design of shared state and synchronization mechanisms, along with thorough testing. - **Synchronization Overhead vs Performance Gains**: The overhead of synchronization might negate the performance benefits of multi-threading. Mitigation: Use lightweight synchronization techniques and minimize shared state access. - **Synchronization Overhead vs Data Consistency**: In order to keep the synchronization overhead low with the shared state, we might need to relax some data consistency guarantees. Mitigation: Analyze the trade-offs and ensure that any relaxed guarantees do not compromise security. ================================================ FILE: proposals/20251215-legacy-bpf-grpc-output-gvisor-engine-deprecation.md ================================================ # Legacy eBPF probe, gVisor libscap engine and gRPC output deprecations ## Summary This proposal aims to formalize motivations and procedures for deprecating the legacy eBPF probe, the gRPC output and the gVisor libscap engine. One of the key objectives of Falco is to maintain a seamless user experience, regardless of the system call event source actually used. This objective imposes strong requirements among all drivers and engines acting as system call source (i.e.: gVisor libscap engine), feature parity, among each other, above all. Feature parity raises challenges from both technical and maintainability perspectives, and these challenges are not justified if the driver/engine is no/little used. For these reasons, this document aims for raising consensus regarding the legacy eBPF probe and gRPC output deprecation. Similar arguments could be raised in favor of the gRPC output deprecation: this output requires dependency on the gRPC framework, that introduces a non-negligible build time overhead and maintainability burden (especially in a C++ codebase), not justified by the little usage of the output. Upcoming evidences of non-negligible use of the gVisor engine and the gRPC output could be addressed by providing a separate source plugin in case of gVisor, and a Falco Sidekick output as a replacement of the gRPC output. ## Motivation ### Legacy eBPF probe deprecation The following matrix details the current minimum kernel version officially supported by each driver, for each architecture: | | Kernel module | legacy eBPF probe | Modern eBPF probe | Status | | ----------- |----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| ----------------- | ------ | | **x86_64** | >= 3.10 | >= 4.14 | >= 5.8 | _STABLE_ | | **aarch64** | >= [3.16](https://github.com/torvalds/linux/commit/055b1212d141f1f398fca548f8147787c0b6253f) | >= 4.17 | >= 5.8 | _STABLE_ | | **s390x** | >= 3.10 | >= [5.5](https://github.com/torvalds/linux/commit/6ae08ae3dea) | >= 5.8 | _EXPERIMENTAL_ | | **riscv64** | >= [5.0](https://github.com/torvalds/linux/commit/5aeb1b36cedd3a1dfdbfe368629fed52dee34103) | N/A | N/A | _EXPERIMENTAL_ | | **ppc64le** | >= 3.10 | >= [5.1](https://github.com/torvalds/linux/commit/ed1cd6deb013a11959d17a94e35ce159197632da) | >= 5.8 | _STABLE_ | The legacy eBPF probe strives to provide a little more coverage than the modern eBPF one. This increased coverage comes at cost of flexibility and maintainability. Indeed: 1. it cannot leverage CORE eBPF features - as a result, falcosecurity must maintain a great number of officially supported eBPF objects, each one built for a specific officially-supported kernel flavor; this increases the maintainability burden and makes the system less flexible to kernel configurations/structures changes 2. old kernel versions support is difficult to retain - the verifier imposes huge limitations on old kernel versions, and any tiny change easily result in the verifier rejecting the code 3. it is difficult to keep it up to date with other drivers - some desired features cannot be implemented in any way using eBPF on old kernel flavors, due to lack of eBPF helpers/program types or verifier limitations (e.g.: there is no way of implementing a synchronous data harvesting mechanism like the one provided by BPF iterators). As falcosecurity strives for feature parity among drivers, this imposes a big limitation on the other drivers. Please notice that: 1. the kernel module is unconstrained on the nature of feature it can support 2. the modern eBPF probe can easily rely on CORE features to probe for kernel features and use them if available Besides the above, the legacy eBPF probe provides support for a range of versions that is entirely contained by the kernel module supported range. Additionally, different distro kernel flavors already back-port features required by the modern eBPF, enabling its usage on kernel older than `5.8`. The above considerations, together with the evidence of its little usage, make the legacy eBPF probe a good candidate for deprecation. ### gVisor libscap engine deprecation gVisor libscap engine implements a system call event source by leveraging events coming from gVisor itself through gRPC. There is evidence that this engine is little used. Moreover, gVisor doesn't provide all information required to build all supported event types, indeed resulting in a system call source not completely equivalent to the ones provided by drivers. Finally, it requires `falcosecurity/libs` being dependent on protobuf, this latter introducing a non-negligible build time overhead and maintainability burden. Deprecating it would allow to streamline system call event sources alignment, maintainability, and reduce build time for both `falcosecurity/falco` and `falcosecurity/libs`. ### gRPC output deprecation The gRPC output provides a mechanism through which a gRPC client can subscribe to the Falco alerts stream. This output leverages a gRPC server embedded into Falco. As for the legacy eBPF probe and the gVisor libscap engine, there is evidence that this output is little used. Also, similarly to the gVisor libscap engine, this requires Falco being dependent on the protobuf, and additionally, on the entire C++ gRPC framework. Finally, the little amount of data that is sent through the gRPC stream, and the communication model (only involving a one-way communication from the server to the client) doesn't justify the need of using gRPC. Deprecating it would allow to reduce the build system, streamline maintainability, and reduce build time for `falcosecurity/falco`. ## Goals * Deprecate the legacy eBPF probe, the gVisor libscap engine, and the gRPC output * Detail a plan to follow during the deprecation period, before completely remove any of the aforementioned components ## Non-goals * Implement a gVisor source plugin as gVisor libscap engine alternative * Implement the gRPC output as Falco Sidekick output * Detail a plan to follow after taking the decision to completely remove any of the aforementioned components ## The plan This section aims to detail the plan to follow contextually and after the deprecation mark, but before taking any definitive removal decision about the legacy eBPF probe, the gVisor libscap engine, and the gRPC output (collectively referred to hereinafter as "the components" or simply "components"). The deprecation of these components introduces user-facing changes that must be addressed as prescribed by the current deprecation policy for "non-backward compatible user-facing changes" (see [20231220-features-adoption-and-deprecation.md#deprecation-policy](./20231220-features-adoption-and-deprecation.md#deprecation-policy)). All components are stable, and considering that deprecations will first be enforced in the stable Falco version `0.43.0` (ante `1.0.0`), the minimum deprecation period length is 1 release: this means that components cannot be removed before Falco `0.44.0`. At high level, the action plan is to inform users, during the deprecation period, about the deprecation: this is achieved by emitting a deprecation notice if the user try to leverage any of the feature exposed by any component, and by updating the website in any of the relevant areas. During the deprecation period, but before taking decision to remove the components, projects belonging to the `falcosecurity` organization will be updated to not use/rely on any of these. Specifically: - on `falcosecurity/libs`, any CI job building and testing the legacy eBPF probe will be removed - on `falcosecurity/kernel-testing`, playbooks will not build and test the legacy eBPF probe anymore - on `falcosecurity/event-generator`, the internal gRPC alert retriever will be replaced with an HTTP alert retriever, leveraging the existing HTTP output. ## The non-plan This proposal does not address any design or implementation aspect of the gVisor engine and gRPC output replacement, nor formalizes in any way the conditions under which a replacement should be delivered. Upcoming evidences of non-negligible use of the gVisor engine and the gRPC output may be addressed by providing a separate source plugin in case of gVisor, and a Falco Sidekick output as a replacement of the gRPC output, but these latter possibilities should be intended as suggestions, and will not constraint in any way any related future choice. Finally, this proposal doesn't detail any aspect of the eventual removal. ================================================ FILE: scripts/CMakeLists.txt ================================================ # 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. # if(CMAKE_SYSTEM_NAME MATCHES "Linux") # Systemd file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/scripts/systemd) configure_file( "${PROJECT_SOURCE_DIR}/scripts/systemd/falco-kmod-inject.service" "${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY ) configure_file( "${PROJECT_SOURCE_DIR}/scripts/systemd/falco-kmod.service" "${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY ) configure_file( "${PROJECT_SOURCE_DIR}/scripts/systemd/falco-modern-bpf.service" "${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY ) configure_file( "${PROJECT_SOURCE_DIR}/scripts/systemd/falco-custom.service" "${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY ) configure_file( "${PROJECT_SOURCE_DIR}/scripts/systemd/falcoctl-artifact-follow.service" "${PROJECT_BINARY_DIR}/scripts/systemd" COPYONLY ) # Debian configure_file(debian/postinst.in debian/postinst COPYONLY) configure_file(debian/postrm.in debian/postrm COPYONLY) configure_file(debian/prerm.in debian/prerm COPYONLY) # Rpm configure_file(rpm/postinstall.in rpm/postinstall COPYONLY) configure_file(rpm/postuninstall.in rpm/postuninstall COPYONLY) configure_file(rpm/preuninstall.in rpm/preuninstall COPYONLY) endif() # Install Falcoctl config file if(NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT MUSL_OPTIMIZED_BUILD ) if(NOT DEFINED FALCOCTL_ETC_DIR) set(FALCOCTL_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/falcoctl") endif() set(FALCOCTL_DRIVER_TYPES_LIST "") if(BUILD_FALCO_MODERN_BPF) list(APPEND FALCOCTL_DRIVER_TYPES_LIST "modern_ebpf") endif() if(BUILD_DRIVER) list(APPEND FALCOCTL_DRIVER_TYPES_LIST "kmod") endif() string(REPLACE ";" ", " FALCOCTL_DRIVER_TYPES "${FALCOCTL_DRIVER_TYPES_LIST}") 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() ================================================ FILE: scripts/debian/postinst.in ================================================ #!/bin/sh # 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. # # By default, we use the automatic selection for drivers chosen_driver="auto" 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:" systemctl --system stop 'falco-kmod.service' || true systemctl --system stop 'falco-modern-bpf.service' || true systemctl --system stop 'falco-custom.service' || true systemctl --system stop 'falcoctl-artifact-follow.service' || true systemctl --system disable 'falco-kmod.service' || true systemctl --system disable 'falco-modern-bpf.service' || true systemctl --system disable 'falco-custom.service' || true systemctl --system disable 'falcoctl-artifact-follow.service' || true # unmask falcoctl if it was masked systemctl --system unmask falcoctl-artifact-follow.service || true if [ "$1" = "configure" ]; then # "auto" case is not managed here since it is already the default, so no CHOICE=2 case $FALCO_DRIVER_CHOICE in none) CHOICE=1 ;; kmod) 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 "Automatic selection" \ 3 "Kmod" \ 4 "Modern eBPF" \ 2>&1 >/dev/tty) fi # "auto" case is not managed here since it is already the default, so no CHOICE=2 case $CHOICE in 1) chosen_driver="" ;; 3) chosen_driver="kmod" ;; 4) chosen_driver="modern_ebpf" ;; esac if [ -n "$chosen_driver" ]; then echo "[POST-INSTALL] Configure falcoctl '$chosen_driver' driver type:" if [ "$chosen_driver" = "auto" ]; then # Configure falcoctl to enable all drivers falcoctl driver config --type "modern_ebpf" --type "kmod" # Load the actually automatic chosen driver chosen_driver=$(falcoctl driver printenv | grep DRIVER= | cut -d'"' -f2) else falcoctl driver config --type "$chosen_driver" fi 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 ;; esac fi clear fi set -e echo "[POST-INSTALL] Trigger deamon-reload:" systemctl --system daemon-reload || true # 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 'falcoctl driver install for kmod:" falcoctl driver install --download=false chosen_unit="kmod" ;; "modern_ebpf") chosen_unit="modern-bpf" ;; esac if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; 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_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 ================================================ FILE: scripts/debian/postrm.in ================================================ #!/bin/sh # 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. # # Based off what debhelper dh_systemd_enable/13.3.4 would have added # ref: https://www.debian.org/doc/manuals/debmake-doc/ch05.en.html#debhelper set -e if [ -d /run/systemd/system ] && [ "$1" = remove ]; then echo "[POST-REMOVE] Disable all Falco services:" systemctl --system disable 'falco-kmod.service' || true systemctl --system disable 'falco-modern-bpf.service' || true systemctl --system disable 'falco-custom.service' || true systemctl --system disable 'falcoctl-artifact-follow.service' || true echo "[POST-REMOVE] Trigger deamon-reload:" systemctl --system daemon-reload || true fi ================================================ FILE: scripts/debian/prerm.in ================================================ #!/bin/sh # 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 -e # Based off what debhelper dh_systemd_enable/13.3.4 would have added # ref: https://www.debian.org/doc/manuals/debmake-doc/ch05.en.html#debhelper # Currently running falco service uses the driver, so stop it before driver cleanup case "$1" in remove|upgrade|deconfigure) echo "[PRE-REMOVE] Stop all Falco services:" systemctl --system stop 'falco-kmod.service' || true 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 'falcoctl driver cleanup:'" falcoctl driver cleanup ;; esac ================================================ FILE: scripts/description.txt ================================================ Falco, the open source cloud-native runtime security project, is the defacto Kubernetes threat detection engine. Falco detects unexpected application behavior and alerts on threats at runtime. ================================================ FILE: scripts/falcoctl/falcoctl.yaml.in ================================================ driver: type: [@FALCOCTL_DRIVER_TYPES@] name: "@DRIVER_NAME@" repos: - "@DRIVERS_REPO@" version: "@DRIVER_VERSION@" hostroot: "/" artifact: follow: every: 168h0m0s falcoVersions: http://localhost:8765/versions refs: - falco-rules:5 indexes: - name: falcosecurity url: https://falcosecurity.github.io/falcoctl/index.yaml ================================================ FILE: scripts/publish-bin ================================================ #!/usr/bin/env bash set -e usage() { echo "usage: $0 -f -r -a " exit 1 } # parse options while getopts ":f::r::a:" opt; do case "${opt}" in f ) file=${OPTARG} ;; r ) repo="${OPTARG}" [[ "${repo}" == "bin" || "${repo}" == "bin-dev" ]] || usage ;; a ) arch=${OPTARG} ;; : ) echo "invalid option: ${OPTARG} requires an argument" 1>&2 exit 1 ;; \?) echo "invalid option: ${OPTARG}" 1>&2 exit 1 ;; esac done shift $((OPTIND-1)) if [ -z "${file}" ] || [ -z "${repo}" ] || [ -z "${arch}" ]; then usage fi # settings s3_bucket_repo="s3://falco-distribution/packages/${repo}/${arch}" cloudfront_path="/packages/${repo}/${arch}" # sign gpg --detach-sign --digest-algo SHA256 --armor ${file} # publish package=$(basename -- ${file}) echo "Publishing ${package} to ${s3_bucket_repo}..." aws s3 cp ${file} ${s3_bucket_repo}/${package} --acl public-read aws s3 cp ${file}.asc ${s3_bucket_repo}/${package}.asc --acl public-read aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/${package} aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/${package}.asc ================================================ FILE: scripts/publish-deb ================================================ #!/usr/bin/env bash set -e usage() { echo "usage: $0 -f -f -r [-s]" exit 1 } check_program() { if ! command -v $1 &> /dev/null then echo "$1 is required and could not be found" exit fi } # Used to get comma separated list of architectures join_arr() { local IFS="$1" shift echo "$*" } # Updates the signature of a DEB package in the local repository # # $1: path of the repository. # $2: suite (eg. "stable") # $3: path of the DEB file. sign_deb() { pushd $1/$2 > /dev/null rm -f $(basename -- $3).asc gpg --detach-sign --digest-algo SHA256 --armor $(basename -- $3) popd > /dev/null } # Add a package to the local DEB repository # # $1: path of the repository. # $2: suite (eg. "stable") # $3: path of the DEB file. add_deb() { cp -f $3 $1/$2 sign_deb $1 $2 $3 # Get package architecture from dpkg local arch=$(dpkg --info $3 | awk '/Architecture/ {printf "%s", $2}') # Store architecture in array architectures+=("${arch}") } falco_arch_from_deb_arch() { case "$1" in "amd64") echo -n "x86_64" ;; "arm64") echo -n "aarch64" ;; *) echo "Wrong arch." exit 1 ;; esac } # Sign the local DEB repository # # $1: path of the repository # $2: suite (eg. "stable") sign_repo() { local release_dir=dists/$2 pushd $1 > /dev/null # release signature - Release.gpg file gpg --detach-sign --digest-algo SHA256 --armor ${release_dir}/Release rm -f ${release_dir}/Release.gpg mv ${release_dir}/Release.asc ${release_dir}/Release.gpg # release signature - InRelease file gpg --armor --sign --clearsign --digest-algo SHA256 ${release_dir}/Release rm -f ${release_dir}/InRelease mv ${release_dir}/Release.asc ${release_dir}/InRelease popd > /dev/null } # Update the local DEB repository # # $1: path of the repository # $2: suite (eg. "stable") update_repo() { local component=main local debs_dir=$2 local release_dir=dists/$2 pushd $1 > /dev/null # packages metadata for arch in "${architectures[@]}"; do local packages_dir=${release_dir}/${component}/binary-${arch} mkdir -p ${packages_dir} truncate -s 0 ${packages_dir}/Packages # Find all ${arch} deb files. # Note that debian uses {arm64,amd64}, while # Falco packages use {x86_64,aarch64}. find ${debs_dir} -name "falco-*-$(falco_arch_from_deb_arch ${arch}).deb" -exec apt-ftparchive packages {} \; >> ${packages_dir}/Packages gzip -c ${packages_dir}/Packages > ${packages_dir}/Packages.gz bzip2 -z -c ${packages_dir}/Packages > ${packages_dir}/Packages.bz2 done # release metadata apt-ftparchive release \ -o APT::FTPArchive::Release::Origin=Falco \ -o APT::FTPArchive::Release::Label=Falco \ -o APT::FTPArchive::Release::Suite=$2 \ -o APT::FTPArchive::Release::Codename=$2 \ -o APT::FTPArchive::Release::Components=${component} \ -o APT::FTPArchive::Release::Architectures="$(join_arr , "${architectures[@]}")" \ ${release_dir} > ${release_dir}/Release popd > /dev/null } reduce_dir_size() { local DIR=$1 local MAX_SIZE_GB=$2 local EXTENSION=$3 local MAX_SIZE=$((MAX_SIZE_GB*1024*1024)) # Convert GB to KB for du command # Check if directory exists if [[ ! -d "$DIR" ]]; then echo "The directory $DIR does not exist." return 1 fi # Calculate current directory size in KB local CUR_SIZE=$(du -sk "$DIR" | cut -f1) # Check if we need to delete any files if ((CUR_SIZE <= MAX_SIZE)); then return 0 fi # Calculate size to delete in bytes local DEL_SIZE=$(( (CUR_SIZE - MAX_SIZE) * 1024 )) local ACC_SIZE=0 find "$DIR" -maxdepth 1 -type f -name "*.$EXTENSION" -printf "%T+ %s %p\n" | sort | while read -r date size file; do if ((ACC_SIZE + size < DEL_SIZE)); then rm "$file" ACC_SIZE=$((ACC_SIZE + size)) local asc_file="$file.asc" if [[ -e "$asc_file" ]]; then local asc_size=$(stat --format="%s" "$asc_file") rm "$asc_file" ACC_SIZE=$((ACC_SIZE + asc_size)) fi else break fi done } # parse options while getopts ":f::r::s" opt; do case "${opt}" in f ) files+=("${OPTARG}") ;; r ) repo="${OPTARG}" [[ "${repo}" == "deb" || "${repo}" == "deb-dev" ]] || usage ;; s ) sign_all="true" ;; : ) echo "invalid option: ${OPTARG} requires an argument" 1>&2 exit 1 ;; \?) echo "invalid option: ${OPTARG}" 1>&2 exit 1 ;; esac done shift $((OPTIND-1)) # check options if ([ ${#files[@]} -eq 0 ] && [ -z "${sign_all}" ]) || [ -z "${repo}" ]; then usage fi # check prerequisites check_program apt-ftparchive check_program gzip check_program bzip2 check_program gpg check_program aws check_program dpkg # settings debSuite=stable s3_bucket_repo="s3://falco-distribution/packages/${repo}" cloudfront_path="/packages/${repo}" tmp_repo_path=/tmp/falco-$repo # prepare repository local copy echo "Fetching ${s3_bucket_repo}..." mkdir -p ${tmp_repo_path} aws s3 cp ${s3_bucket_repo} ${tmp_repo_path} --recursive # update signatures for all existing packages if [ "${sign_all}" ]; then for file in ${tmp_repo_path}/${debSuite}/*; do if [ -f "$file" ]; then # exclude directories, symlinks, etc... if [[ ! $file == *.asc ]]; then # exclude signature files package=$(basename -- ${file}) echo "Signing ${package}..." sign_deb ${tmp_repo_path} ${debSuite} ${file} echo "Syncing ${package}.asc to ${s3_bucket_repo}..." aws s3 cp ${tmp_repo_path}/${debSuite}/${package}.asc ${s3_bucket_repo}/${debSuite}/${package}.asc --acl public-read fi fi done aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/${debSuite}/*.asc sign_repo ${tmp_repo_path} ${debSuite} fi # remove old dev packages if necessary if [[ ${repo} == "deb-dev" ]]; then reduce_dir_size "${tmp_repo_path}/${debSuite}" 10 deb fi # update the repo by adding new packages if ! [ ${#files[@]} -eq 0 ]; then for file in "${files[@]}"; do echo "Adding ${file}..." add_deb ${tmp_repo_path} ${debSuite} ${file} done update_repo ${tmp_repo_path} ${debSuite} sign_repo ${tmp_repo_path} ${debSuite} # publish for file in "${files[@]}"; do package=$(basename -- ${file}) echo "Publishing ${package} to ${s3_bucket_repo}..." aws s3 cp ${tmp_repo_path}/${debSuite}/${package} ${s3_bucket_repo}/${debSuite}/${package} --acl public-read aws s3 cp ${tmp_repo_path}/${debSuite}/${package}.asc ${s3_bucket_repo}/${debSuite}/${package}.asc --acl public-read aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/${debSuite}/${package} aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/${debSuite}/${package}.asc done fi # sync dists aws s3 sync ${tmp_repo_path}/dists ${s3_bucket_repo}/dists --delete --acl public-read aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/dists/* # delete packages that have been pruned # the dryrun option is there so we can check that we're doing the right thing, can be removed after testing if [[ ${repo} == "deb-dev" ]]; then aws s3 sync "${tmp_repo_path}/${debSuite}" ${s3_bucket_repo} --dryrun --delete --acl public-read fi ================================================ FILE: scripts/publish-rpm ================================================ #!/usr/bin/env bash set -e usage() { echo "usage: $0 -f -f -r [-s]" exit 1 } check_program() { if ! command -v $1 &> /dev/null then echo "$1 is required and could not be found" exit fi } # Sign RPM packages with embedded GPG signature using rpmsign # # $@: paths of RPM files to sign. rpmsign_packages() { echo "Signing RPM packages with rpmsign..." rpmsign --define '_gpg_name Falcosecurity Package Signing' --resign "$@" echo "Verifying RPM signatures..." rpm -qp --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}: %|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{(none)}|}|\n' "$@" } # Updates the signature of a RPM package in the local repository # # $1: path of the repository. # $2: path of the RPM file. sign_rpm() { pushd $1 > /dev/null rm -f $(basename -- $2).asc gpg --detach-sign --digest-algo SHA256 --armor $(basename -- $2) popd > /dev/null } # Add a package to the local RPM repository # # $1: path of the repository. # $2: path of the RPM file. add_rpm() { cp -f $2 $1 sign_rpm $1 $2 } # Sign the local RPM repository # # $1: path of the repository. sign_repo() { pushd $1 > /dev/null rm -f repodata/repomd.xml.asc gpg --detach-sign --digest-algo SHA256 --armor repodata/repomd.xml popd > /dev/null } # Update the local RPM repository # # $1: path of the repository. update_repo() { pushd $1 > /dev/null createrepo --update --no-database . popd > /dev/null } reduce_dir_size() { local DIR=$1 local MAX_SIZE_GB=$2 local EXTENSION=$3 local MAX_SIZE=$((MAX_SIZE_GB*1024*1024)) # Convert GB to KB for du command # Check if directory exists if [[ ! -d "$DIR" ]]; then echo "The directory $DIR does not exist." return 1 fi # Calculate current directory size in KB local CUR_SIZE=$(du -sk "$DIR" | cut -f1) # Check if we need to delete any files if ((CUR_SIZE <= MAX_SIZE)); then return 0 fi # Calculate size to delete in bytes local DEL_SIZE=$(( (CUR_SIZE - MAX_SIZE) * 1024 )) local ACC_SIZE=0 find "$DIR" -maxdepth 1 -type f -name "*.$EXTENSION" -printf "%T+ %s %p\n" | sort | while read -r date size file; do if ((ACC_SIZE + size < DEL_SIZE)); then rm "$file" ACC_SIZE=$((ACC_SIZE + size)) local asc_file="$file.asc" if [[ -e "$asc_file" ]]; then local asc_size=$(stat --format="%s" "$asc_file") rm "$asc_file" ACC_SIZE=$((ACC_SIZE + asc_size)) fi else break fi done } # parse options while getopts ":f::r::s" opt; do case "${opt}" in f ) files+=("${OPTARG}") ;; r ) repo="${OPTARG}" [[ "${repo}" == "rpm" || "${repo}" == "rpm-dev" ]] || usage ;; s ) sign_all="true" ;; : ) echo "invalid option: ${OPTARG} requires an argument" 1>&2 exit 1 ;; \?) echo "invalid option: ${OPTARG}" 1>&2 exit 1 ;; esac done shift $((OPTIND-1)) if ([ ${#files[@]} -eq 0 ] && [ -z "${sign_all}" ]) || [ -z "${repo}" ]; then usage fi # check prerequisites check_program createrepo check_program gpg check_program aws check_program rpmsign check_program rpm # settings s3_bucket_repo="s3://falco-distribution/packages/${repo}" cloudfront_path="/packages/${repo}" tmp_repo_path=/tmp/falco-$repo # prepare repository local copy echo "Fetching ${s3_bucket_repo}..." mkdir -p ${tmp_repo_path} aws s3 cp ${s3_bucket_repo} ${tmp_repo_path} --recursive # update signatures for all existing packages if [ "${sign_all}" ]; then # collect all RPM files rpm_files=() for file in ${tmp_repo_path}/*; do if [ -f "$file" ] && [[ $file == *.rpm ]]; then rpm_files+=("$file") fi done # sign all RPM packages with embedded GPG signature if [ ${#rpm_files[@]} -gt 0 ]; then rpmsign_packages "${rpm_files[@]}" fi # create detached signatures and upload for file in "${rpm_files[@]}"; do package=$(basename -- ${file}) echo "Creating detached signature for ${package}..." sign_rpm ${tmp_repo_path} ${file} echo "Syncing ${package} and ${package}.asc to ${s3_bucket_repo}..." aws s3 cp ${tmp_repo_path}/${package} ${s3_bucket_repo}/${package} --acl public-read aws s3 cp ${tmp_repo_path}/${package}.asc ${s3_bucket_repo}/${package}.asc --acl public-read done aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/*.rpm aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/*.asc update_repo ${tmp_repo_path} sign_repo ${tmp_repo_path} fi # remove old dev packages if necessary if [[ ${repo} == "rpm-dev" ]]; then reduce_dir_size ${tmp_repo_path} 10 rpm fi # sign and add new packages to the repo if ! [ ${#files[@]} -eq 0 ]; then rpmsign_packages "${files[@]}" for file in "${files[@]}"; do echo "Adding ${file}..." add_rpm ${tmp_repo_path} ${file} done update_repo ${tmp_repo_path} sign_repo ${tmp_repo_path} # publish for file in "${files[@]}"; do package=$(basename -- ${file}) echo "Publishing ${package} to ${s3_bucket_repo}..." aws s3 cp ${tmp_repo_path}/${package} ${s3_bucket_repo}/${package} --acl public-read aws s3 cp ${tmp_repo_path}/${package}.asc ${s3_bucket_repo}/${package}.asc --acl public-read aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/${package} aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/${package}.asc done fi # sync repodata aws s3 sync ${tmp_repo_path}/repodata ${s3_bucket_repo}/repodata --delete --acl public-read aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/repodata/* # delete packages that have been pruned # the dryrun option is there so we can check that we're doing the right thing, can be removed after testing if [[ ${repo} == "rpm-dev" ]]; then aws s3 sync ${tmp_repo_path} ${s3_bucket_repo} --dryrun --delete --acl public-read fi ================================================ FILE: scripts/publish-wasm ================================================ #!/usr/bin/env bash set -e usage() { echo "usage: $0 -f " exit 1 } # parse options while getopts ":f:" opt; do case "${opt}" in f ) file=${OPTARG} ;; \?) echo "invalid option: ${OPTARG}" >&2 exit 1 ;; esac done shift $((OPTIND-1)) if [ -z "${file}" ]; then usage fi repo="wasm-dev" # settings s3_bucket_repo="s3://falco-distribution/packages/${repo}" cloudfront_path="/packages/${repo}" # publish package=$(basename -- ${file}) echo "Publishing ${package} to ${s3_bucket_repo}..." aws s3 cp ${file} ${s3_bucket_repo}/${package} --acl public-read aws cloudfront create-invalidation --distribution-id ${AWS_CLOUDFRONT_DIST_ID} --paths ${cloudfront_path}/${package} ================================================ FILE: scripts/rpm/postinstall.in ================================================ # 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. # # By default, we use the automatic selection for drivers chosen_driver="auto" 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:" systemctl --system stop 'falco-kmod.service' || true systemctl --system stop 'falco-modern-bpf.service' || true systemctl --system stop 'falco-custom.service' || true systemctl --system stop 'falcoctl-artifact-follow.service' || true systemctl --system disable 'falco-kmod.service' || true systemctl --system disable 'falco-modern-bpf.service' || true systemctl --system disable 'falco-custom.service' || true systemctl --system disable 'falcoctl-artifact-follow.service' || true # unmask falcoctl if it was masked systemctl --system unmask falcoctl-artifact-follow.service || true if [ $1 -ge 1 ]; then # "auto" case is not managed here since it is already the default, so no CHOICE=2 case $FALCO_DRIVER_CHOICE in none) CHOICE=1 ;; kmod) 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 "Automatic selection" \ 3 "Kmod" \ 4 "Modern eBPF" \ 2>&1 >/dev/tty) fi # "auto" case is not managed here since it is already the default, so no CHOICE=2 case $CHOICE in 1) chosen_driver="" ;; 3) chosen_driver="kmod" ;; 4) chosen_driver="modern_ebpf" ;; esac if [ -n "$chosen_driver" ]; then echo "[POST-INSTALL] Configure falcoctl '$chosen_driver' driver type:" if [ "$chosen_driver" = "auto" ]; then # Configure falcoctl to enable all drivers falcoctl driver config --type "modern_ebpf" --type "kmod" # Load the actually automatic chosen driver chosen_driver=$(falcoctl driver printenv | grep DRIVER= | cut -d'"' -f2) else falcoctl driver config --type "$chosen_driver" fi 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 ;; esac fi clear fi set -e echo "[POST-INSTALL] Trigger deamon-reload:" systemctl --system daemon-reload || true # 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 'falcoctl driver install for kmod:" falcoctl driver install --download=false chosen_unit="kmod" ;; "modern_ebpf") chosen_unit="modern-bpf" ;; esac # validate rpm macros by `rpm -qp --scripts ` # RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd # https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_syntax # systemd_post macro expands to # if postinst: # `systemd-update-helper install-system-units ` %systemd_post "falco-$chosen_unit.service" # post install/upgrade mirrored from .deb if [ $1 -ge 1 ]; then 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 ================================================ FILE: scripts/rpm/postuninstall.in ================================================ # 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 -e if [ -d /run/systemd/system ] && [ $1 -eq 0 ]; then echo "[POST-REMOVE] Disable all Falco services:" systemctl --system disable 'falco-kmod.service'|| true systemctl --system disable 'falco-modern-bpf.service' || true systemctl --system disable 'falco-custom.service' || true systemctl --system disable 'falcoctl-artifact-follow.service' || true echo "[POST-REMOVE] Trigger deamon-reload:" systemctl --system daemon-reload || true fi ================================================ FILE: scripts/rpm/preuninstall.in ================================================ # 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 -e # Currently running falco service uses the driver, so stop it before driver cleanup echo "[PRE-REMOVE] Stop all Falco services:" systemctl --system stop 'falco-kmod.service' || true 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 'falcoctl driver cleanup:'" falcoctl driver cleanup # validate rpm macros by `rpm -qp --scripts ` # RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd # https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_syntax # systemd_preun macro expands to # if preuninstall: # `systemd-update-helper remove-system-units ` %systemd_preun 'falco-kmod.service' %systemd_preun 'falco-modern-bpf.service' %systemd_preun 'falco-custom.service' %systemd_preun 'falcoctl-artifact-follow.service' ================================================ FILE: scripts/systemd/falco-custom.service ================================================ [Unit] Description=Falco: Container Native Runtime Security with custom configuration Documentation=https://falco.org/docs/ Before=falcoctl-artifact-follow.service Wants=falcoctl-artifact-follow.service [Service] Type=simple User=%u ExecStart=/usr/bin/falco ExecReload=kill -1 $MAINPID UMask=0077 TimeoutSec=30 RestartSec=15s Restart=on-failure PrivateTmp=true NoNewPrivileges=yes ProtectHome=read-only ProtectSystem=full ProtectKernelTunables=true RestrictRealtime=true RestrictAddressFamilies=~AF_PACKET StandardOutput=null [Install] WantedBy=multi-user.target Alias=falco.service ================================================ FILE: scripts/systemd/falco-kmod-inject.service ================================================ [Unit] Description=Falco: Container Native Runtime Security with kmod, inject. Documentation=https://falco.org/docs/ PartOf=falco-kmod.service [Service] Type=oneshot RemainAfterExit=yes User=root ExecStart=/sbin/modprobe falco ExecStop=/sbin/rmmod falco ================================================ FILE: scripts/systemd/falco-kmod.service ================================================ [Unit] Description=Falco: Container Native Runtime Security with kmod Documentation=https://falco.org/docs/ After=falco-kmod-inject.service Requires=falco-kmod-inject.service Before=falcoctl-artifact-follow.service Wants=falcoctl-artifact-follow.service [Service] Type=simple User=root ExecStart=/usr/bin/falco -o engine.kind=kmod ExecReload=kill -1 $MAINPID UMask=0077 TimeoutSec=30 RestartSec=15s Restart=on-failure PrivateTmp=true NoNewPrivileges=yes ProtectHome=read-only ProtectSystem=full ProtectKernelTunables=true ReadWriteDirectories=/sys/module/falco RestrictRealtime=true RestrictAddressFamilies=~AF_PACKET StandardOutput=null [Install] WantedBy=multi-user.target Alias=falco.service ================================================ FILE: scripts/systemd/falco-modern-bpf.service ================================================ [Unit] Description=Falco: Container Native Runtime Security with modern ebpf Documentation=https://falco.org/docs/ Before=falcoctl-artifact-follow.service Wants=falcoctl-artifact-follow.service [Service] Type=simple User=root ExecStart=/usr/bin/falco -o engine.kind=modern_ebpf ExecReload=kill -1 $MAINPID UMask=0077 TimeoutSec=30 RestartSec=15s Restart=on-failure PrivateTmp=true NoNewPrivileges=yes ProtectHome=read-only ProtectSystem=full ProtectKernelTunables=true RestrictRealtime=true RestrictAddressFamilies=~AF_PACKET StandardOutput=null [Install] WantedBy=multi-user.target Alias=falco.service ================================================ FILE: scripts/systemd/falcoctl-artifact-follow.service ================================================ [Unit] Description=Falcoctl Artifact Follow: automatic artifacts update service Documentation=https://falco.org/docs/ PartOf=falco-kmod.service falco-modern-bpf.service falco-custom.service [Service] Type=simple User=root ExecStart=/usr/bin/falcoctl artifact follow --allowed-types=rulesfile UMask=0077 TimeoutSec=30 RestartSec=15s Restart=on-failure PrivateTmp=true NoNewPrivileges=yes ProtectSystem=true ReadWriteDirectories=/usr/share/falco ProtectKernelTunables=true RestrictRealtime=true [Install] WantedBy=multi-user.target ================================================ FILE: semgrep/insecure-api-gets.yaml ================================================ # MIT License # # Copyright (c) 2022 raptor # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. rules: - id: raptor-insecure-api-gets metadata: author: Marco Ivaldi references: - https://cwe.mitre.org/data/definitions/242 - https://cwe.mitre.org/data/definitions/120 confidence: HIGH message: >- The program calls a function that can never be guaranteed to work safely. Certain functions behave in dangerous ways regardless of how they are used. Functions in this category were often implemented without taking security concerns into account. The gets() function is unsafe because it does not perform bounds checking on the size of its input. An attacker can easily send arbitrarily-sized input to gets() and overflow the destination buffer. severity: ERROR languages: - c - cpp pattern: gets(...) ================================================ FILE: semgrep/insecure-api-sprintf-vsprintf.yaml ================================================ # MIT License # # Copyright (c) 2022 raptor # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. rules: - id: raptor-insecure-api-sprintf-vsprintf metadata: author: Marco Ivaldi references: - https://cwe.mitre.org/data/definitions/676 - https://cwe.mitre.org/data/definitions/120 - https://cwe.mitre.org/data/definitions/787 - https://g.co/kgs/PCHQjJ confidence: HIGH message: >- The program invokes a potentially dangerous function that could introduce a vulnerability if it is used incorrectly, but the function can also be used safely. A buffer overflow condition exists when a program attempts to put more data in a buffer than it can hold, or when a program attempts to put data in a memory area outside of the boundaries of a buffer. The simplest type of error, and the most common cause of buffer overflows, is the classic case in which the program copies the buffer without restricting how much is copied. Other variants exist, but the existence of a classic overflow strongly suggests that the programmer is not considering even the most basic of security protections. severity: ERROR languages: - c - cpp patterns: - pattern-either: - pattern: sprintf($BUF, $FMT, ...) - pattern: vsprintf($BUF, $FMT, ...) # swprintf() and vswprintf() should have a size parameter - metavariable-regex: metavariable: $FMT # NOTE: some format string modifiers are not handled regex: '(".*%l?s.*"|".*%S.*"|[a-zA-Z_][a-zA-Z0-9_]*)' ================================================ FILE: semgrep/insecure-api-strcpy-stpcpy-strcat.yaml ================================================ # MIT License # # Copyright (c) 2022 raptor # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. rules: - id: raptor-insecure-api-strcpy-stpcpy-strcat metadata: author: Marco Ivaldi references: - https://cwe.mitre.org/data/definitions/676 - https://cwe.mitre.org/data/definitions/120 - https://cwe.mitre.org/data/definitions/787 - https://g.co/kgs/PCHQjJ confidence: HIGH message: >- The program invokes a potentially dangerous function that could introduce a vulnerability if it is used incorrectly, but the function can also be used safely. A buffer overflow condition exists when a program attempts to put more data in a buffer than it can hold, or when a program attempts to put data in a memory area outside of the boundaries of a buffer. The simplest type of error, and the most common cause of buffer overflows, is the classic case in which the program copies the buffer without restricting how much is copied. Other variants exist, but the existence of a classic overflow strongly suggests that the programmer is not considering even the most basic of security protections. In the Falco codebase you can use the safer alternative strlcpy(). severity: ERROR languages: - c - cpp patterns: - pattern-either: - pattern: strcpy(...) - pattern: stpcpy(...) - pattern: strcat(...) - pattern: wcscpy(...) - pattern: wcpcpy(...) - pattern: wcscat(...) - pattern-not: $FUN($BUF, "...", ...) ================================================ FILE: semgrep/insecure-api-strn.yaml ================================================ rules: - id: falco-insecure-api-strn metadata: references: - https://cwe.mitre.org/data/definitions/120 confidence: HIGH message: >- The libc function strncpy and strncat are not used in the Falco codebase as they are error prone. Read more: https://www.cisa.gov/uscert/bsi/articles/knowledge/coding-practices/strncpy-and-strncat . In the Falco codebase you can use the safer alternatives strlcpy() and strlcat(). severity: ERROR languages: - c - cpp patterns: - pattern-either: - pattern: strncpy(...) - pattern: strncat(...) ================================================ FILE: tools/local_hooks/dco-pre-commit-msg.sh ================================================ #!/bin/bash # # This is a git pre-commit-msg hook which automatically add a # DCO signed-off message if one is missing. # MESSAGE_FILE="$1" GIT_AUTHOR=$(git var GIT_AUTHOR_IDENT) SIGNOFF_BY=$(echo $GIT_AUTHOR | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') # Verify if a DCO signoff message exists. # Append a DCO signoff message if one doesn't exist. if ! $(grep -qs "^$SIGNOFF_BY" "$MESSAGE_FILE") ; then echo -e "\n$SIGNOFF_BY" >> "$MESSAGE_FILE" fi exit 0 ================================================ FILE: unit_tests/CMakeLists.txt ================================================ # 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. # message(STATUS "Falco unit tests build enabled") include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.14.0 ) FetchContent_MakeAvailable(googletest) # Create a libscap_test_var.h file with some variables used by our tests (e.g: the kmod path). configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/falco_test_var.h.in ${CMAKE_CURRENT_BINARY_DIR}/falco_test_var.h ) add_executable( falco_unit_tests test_falco_engine.cpp engine/test_add_source.cpp engine/test_alt_rule_loader.cpp engine/test_enable_rule.cpp engine/test_extra_output.cpp engine/test_falco_utils.cpp engine/test_filter_details_resolver.cpp engine/test_filter_macro_resolver.cpp engine/test_filter_warning_resolver.cpp engine/test_plugin_requirements.cpp engine/test_rule_loader.cpp engine/test_rulesets.cpp falco/test_capture.cpp falco/test_configuration.cpp falco/test_configuration_rule_selection.cpp falco/test_configuration_config_files.cpp falco/test_configuration_env_vars.cpp falco/test_configuration_output_options.cpp falco/test_configuration_schema.cpp falco/app/actions/test_select_event_sources.cpp falco/app/actions/test_load_config.cpp ) if(CMAKE_SYSTEM_NAME MATCHES "Linux") target_sources( falco_unit_tests PRIVATE falco/test_atomic_signal_handler.cpp falco/app/actions/test_configure_interesting_sets.cpp falco/app/actions/test_configure_syscall_buffer_num.cpp ) endif() target_include_directories( falco_unit_tests PRIVATE ${CMAKE_SOURCE_DIR}/userspace ${CMAKE_BINARY_DIR}/userspace/falco # we need it to include indirectly `config_falco.h` # file ${CMAKE_SOURCE_DIR}/userspace/engine # we need it to include indirectly `falco_common.h` # file ${CMAKE_CURRENT_BINARY_DIR} # we need it to include `falco_test_var.h` ) get_target_property(FALCO_APPLICATION_LIBRARIES falco_application LINK_LIBRARIES) target_link_libraries( falco_unit_tests falco_application GTest::gtest GTest::gtest_main ${FALCO_APPLICATION_LIBRARIES} ) if(EMSCRIPTEN) target_compile_options(falco_unit_tests PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0") target_link_options(falco_unit_tests PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0") target_link_options(falco_unit_tests PRIVATE "-sALLOW_MEMORY_GROWTH=1") target_link_options(falco_unit_tests PRIVATE "-sEXPORTED_FUNCTIONS=['_main','_htons','_ntohs']") endif() ================================================ FILE: unit_tests/README.md ================================================ # Falco unit tests ## Intro Under `unit_tests/engine` and `unit_tests/falco` directories, we have different test suites that could be a single file or an entire directory according to the number and the complexity of tests. ## Build and Run ```bash cmake -DMINIMAL_BUILD=On -DBUILD_DRIVER=Off -DBUILD_FALCO_UNIT_TESTS=On .. make falco_unit_tests sudo ./unit_tests/falco_unit_tests ``` ================================================ FILE: unit_tests/engine/test_add_source.cpp ================================================ // 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 #include #include static std::string syscall_source_name = "syscall"; // A variant of evttype_index_ruleset_factory that uses a singleton // for the underlying ruleset. This allows testing of // ruleset_for_source namespace { class test_ruleset_factory : public evttype_index_ruleset_factory { public: explicit test_ruleset_factory(std::shared_ptr factory): evttype_index_ruleset_factory(factory) { ruleset = evttype_index_ruleset_factory::new_ruleset(); } virtual ~test_ruleset_factory() = default; inline std::shared_ptr new_ruleset() override { return ruleset; } std::shared_ptr ruleset; }; }; // namespace TEST(AddSource, basic) { falco_engine engine; sinsp inspector; sinsp_filter_check_list filterchecks; auto filter_factory = std::make_shared(&inspector, filterchecks); auto formatter_factory = std::make_shared(&inspector, filterchecks); auto ruleset_factory = std::make_shared(filter_factory); falco_source syscall_source; syscall_source.name = syscall_source_name; syscall_source.ruleset = ruleset_factory->new_ruleset(); syscall_source.ruleset_factory = ruleset_factory; syscall_source.filter_factory = filter_factory; syscall_source.formatter_factory = formatter_factory; size_t source_idx = engine.add_source(syscall_source_name, filter_factory, formatter_factory, ruleset_factory); ASSERT_TRUE(engine.is_source_valid(syscall_source_name)); ASSERT_EQ(engine.filter_factory_for_source(syscall_source_name), filter_factory); ASSERT_EQ(engine.filter_factory_for_source(source_idx), filter_factory); ASSERT_EQ(engine.formatter_factory_for_source(syscall_source_name), formatter_factory); ASSERT_EQ(engine.formatter_factory_for_source(source_idx), formatter_factory); ASSERT_EQ(engine.ruleset_factory_for_source(syscall_source_name), ruleset_factory); ASSERT_EQ(engine.ruleset_factory_for_source(source_idx), ruleset_factory); ASSERT_EQ(engine.ruleset_for_source(syscall_source_name), ruleset_factory->ruleset); ASSERT_EQ(engine.ruleset_for_source(source_idx), ruleset_factory->ruleset); } ================================================ FILE: unit_tests/engine/test_alt_rule_loader.cpp ================================================ // 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 #include #include #include #include #include #include #include #include "indexed_vector.h" #include "evttype_index_ruleset.h" #include "rule_loader_reader.h" #include "rule_loader_collector.h" #include "rule_loader_compiler.h" namespace { struct test_object_info { std::string name; std::string property; }; struct test_compile_output : public rule_loader::compile_output { test_compile_output() = default; virtual ~test_compile_output() = default; virtual std::unique_ptr clone() const override { return std::make_unique(*this); } std::set defined_test_properties; }; class test_compiler : public rule_loader::compiler { public: test_compiler() = default; virtual ~test_compiler() = default; std::unique_ptr new_compile_output() override { return std::make_unique(); } void compile(rule_loader::configuration& cfg, const rule_loader::collector& col, rule_loader::compile_output& out) const override; }; class test_collector : public rule_loader::collector { public: test_collector() = default; virtual ~test_collector() = default; indexed_vector test_object_infos; }; class test_reader : public rule_loader::reader { public: test_reader() = default; virtual ~test_reader() = default; protected: rule_loader::context create_context(const YAML::Node& item, const rule_loader::context& parent) { return rule_loader::context(item, rule_loader::context::EXTENSION_ITEM, "test object", parent); }; void read_item(rule_loader::configuration& cfg, rule_loader::collector& collector, const YAML::Node& item, const rule_loader::context& parent) override { test_collector& test_col = dynamic_cast(collector); if(item["test_object"].IsDefined()) { rule_loader::context tmp = create_context(item, parent); test_object_info obj; std::string name; std::string property; decode_val(item, "test_object", name, tmp); decode_val(item, "property", property, tmp); obj.name = name; obj.property = property; test_col.test_object_infos.insert(obj, obj.name); } else { rule_loader::reader::read_item(cfg, collector, item, parent); } }; }; class test_ruleset : public evttype_index_ruleset { public: explicit test_ruleset(std::shared_ptr factory): evttype_index_ruleset(factory) {}; virtual ~test_ruleset() = default; void add_compile_output(const rule_loader::compile_output& compile_output, falco_common::priority_type min_priority, const std::string& source) { evttype_index_ruleset::add_compile_output(compile_output, min_priority, source); std::shared_ptr ruleset; get_engine_state().get_ruleset(source, ruleset); EXPECT_EQ(this, ruleset.get()); const test_compile_output& test_output = dynamic_cast(compile_output); defined_properties = test_output.defined_test_properties; }; std::set defined_properties; }; class test_ruleset_factory : public filter_ruleset_factory { public: explicit test_ruleset_factory(std::shared_ptr factory): m_filter_factory(factory) {} virtual ~test_ruleset_factory() = default; inline std::shared_ptr new_ruleset() override { return std::make_shared(m_filter_factory); } std::shared_ptr m_filter_factory; }; }; // namespace void test_compiler::compile(rule_loader::configuration& cfg, const rule_loader::collector& col, rule_loader::compile_output& out) const { rule_loader::compiler::compile(cfg, col, out); const test_collector& test_col = dynamic_cast(col); test_compile_output& test_output = dynamic_cast(out); for(auto& test_obj : test_col.test_object_infos) { test_output.defined_test_properties.insert(test_obj.property); } } static std::string content = R"END( - test_object: test property: my-value - test_object: test2 property: other-value - list: shell_binaries items: [sh, bash] - macro: spawned_process condition: evt.type=execve and proc.name in (shell_binaries) - rule: test info rule desc: A test info rule condition: spawned_process output: A test info rule matched (evt.type=%evt.type proc.name=%proc.name) priority: INFO source: syscall tags: [process] - rule: test k8s_audit rule desc: A k8s audit test rule condition: ka.target.resource=deployments output: A k8s audit rule matched (ka.verb=%ka.verb resource=%ka.target.resource) priority: INFO source: k8s_audit tags: [process] - rule: test debug rule desc: A test debug rule condition: spawned_process and proc.name="bash" output: A test debug rule matched (evt.type=%evt.type proc.name=%proc.name) priority: DEBUG source: syscall tags: [process] )END"; static std::string syscall_source_name = "syscall"; static std::shared_ptr create_configuration( sinsp& inspector, sinsp_filter_check_list& filterchecks, indexed_vector& sources) { auto filter_factory = std::make_shared(&inspector, filterchecks); auto formatter_factory = std::make_shared(&inspector, filterchecks); auto ruleset_factory = std::make_shared(filter_factory); falco_source syscall_source; syscall_source.name = syscall_source_name; syscall_source.ruleset = ruleset_factory->new_ruleset(); syscall_source.ruleset_factory = ruleset_factory; syscall_source.filter_factory = filter_factory; syscall_source.formatter_factory = formatter_factory; sources.insert(syscall_source, syscall_source_name); return std::make_shared(content, sources, "test configuration"); } static void load_rules(sinsp& inspector, sinsp_filter_check_list& filterchecks, std::unique_ptr& compile_output, indexed_vector& sources) { std::shared_ptr cfg = create_configuration(inspector, filterchecks, sources); rule_loader::reader reader; rule_loader::collector collector; rule_loader::compiler compiler; EXPECT_TRUE(reader.read(*cfg, collector)); compile_output = compiler.new_compile_output(); compiler.compile(*cfg, collector, *compile_output); } TEST(engine_loader_alt_loader, load_rules) { sinsp inspector; sinsp_filter_check_list filterchecks; std::unique_ptr compile_output; indexed_vector sources; load_rules(inspector, filterchecks, compile_output, sources); // Note that the k8s_audit rule will be skipped as load_rules // only adds a syscall source. EXPECT_EQ(compile_output->lists.size(), 1); EXPECT_TRUE(compile_output->lists.at("shell_binaries") != nullptr); EXPECT_EQ(compile_output->macros.size(), 1); EXPECT_TRUE(compile_output->macros.at("spawned_process") != nullptr); EXPECT_EQ(compile_output->rules.size(), 2); EXPECT_TRUE(compile_output->rules.at("test info rule") != nullptr); EXPECT_TRUE(compile_output->rules.at("test debug rule") != nullptr); } TEST(engine_loader_alt_loader, pass_compile_output_to_ruleset) { sinsp inspector; sinsp_filter_check_list filterchecks; std::unique_ptr compile_output; indexed_vector sources; load_rules(inspector, filterchecks, compile_output, sources); std::shared_ptr ruleset = sources.at(syscall_source_name)->ruleset; ruleset->add_compile_output(*compile_output, falco_common::PRIORITY_INFORMATIONAL, syscall_source_name); // Enable all rules for a ruleset id. Because the compile // output contained one rule with priority >= INFO, that rule // should be enabled. uint16_t ruleset_id = 0; ruleset->enable("", filter_ruleset::match_type::substring, ruleset_id); EXPECT_EQ(ruleset->enabled_count(ruleset_id), 1); } TEST(engine_loader_alt_loader, falco_engine_alternate_loader) { falco_engine engine; sinsp inspector; sinsp_filter_check_list filterchecks; auto filter_factory = std::make_shared(&inspector, filterchecks); auto formatter_factory = std::make_shared(&inspector, filterchecks); auto ruleset_factory = std::make_shared(filter_factory); engine.add_source(syscall_source_name, filter_factory, formatter_factory, ruleset_factory); auto reader = std::make_shared(); auto collector = std::make_shared(); auto compiler = std::make_shared(); engine.set_rule_reader(reader); engine.set_rule_collector(collector); engine.set_rule_compiler(compiler); EXPECT_EQ(reader, engine.get_rule_reader()); EXPECT_EQ(collector, engine.get_rule_collector()); EXPECT_EQ(compiler, engine.get_rule_compiler()); engine.load_rules(content, "test_rules.yaml"); EXPECT_EQ(collector->test_object_infos.size(), 2); std::shared_ptr ruleset = engine.ruleset_for_source(syscall_source_name); std::set& defined_properties = std::dynamic_pointer_cast(ruleset)->defined_properties; EXPECT_TRUE(defined_properties.find("my-value") != defined_properties.end()); EXPECT_TRUE(defined_properties.find("other-value") != defined_properties.end()); EXPECT_TRUE(defined_properties.find("not-exists-value") == defined_properties.end()); }; TEST(engine_loader_alt_loader, clone_compile_output) { sinsp inspector; sinsp_filter_check_list filterchecks; indexed_vector sources; std::shared_ptr cfg = create_configuration(inspector, filterchecks, sources); test_reader reader; test_collector collector; test_compiler compiler; EXPECT_TRUE(reader.read(*cfg, collector)); std::unique_ptr compile_output = compiler.new_compile_output(); compiler.compile(*cfg, collector, *compile_output); const test_compile_output& original_ref = dynamic_cast(*(compile_output.get())); std::unique_ptr copy = compile_output->clone(); const test_compile_output& copy_ref = dynamic_cast(*(copy.get())); EXPECT_EQ(copy_ref.lists, original_ref.lists); EXPECT_EQ(copy_ref.macros, original_ref.macros); EXPECT_EQ(copy_ref.rules, original_ref.rules); EXPECT_EQ(copy_ref.defined_test_properties, original_ref.defined_test_properties); } ================================================ FILE: unit_tests/engine/test_enable_rule.cpp ================================================ // 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 #include #include #include #include #include "../test_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"; static std::string multi_rule = R"END( - rule: first actual 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: second disabled rule desc: A disabled rule condition: evt.type=execve output: A disabled 2 rule matched (evt.type=%evt.type) priority: INFO source: syscall enabled: false tags: [exec process] - rule: third disabled rule desc: A disabled rule condition: evt.type=execve output: A disabled 3 rule matched (evt.type=%evt.type) priority: INFO source: syscall enabled: false tags: [exec] )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"; TEST_F(test_falco_engine, enable_rule_name) { load_rules(single_rule, "single_rule.yaml"); // No rules should be enabled yet for any custom rulesets EXPECT_EQ(1, m_engine->num_rules_for_ruleset(default_ruleset)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); // Enable for first ruleset, only that ruleset should have an // enabled rule afterward m_engine->enable_rule("test", true, ruleset_1); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); // Enable for second ruleset m_engine->enable_rule("test", true, ruleset_2); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); // When the substring is blank, all rules are enabled // (including the disabled rule) m_engine->enable_rule("", true, ruleset_3); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(2, m_engine->num_rules_for_ruleset(ruleset_3)); // Now disable for second ruleset m_engine->enable_rule("test", false, ruleset_2); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(2, m_engine->num_rules_for_ruleset(ruleset_3)); } TEST_F(test_falco_engine, enable_rule_tags) { std::set process_tags = {"process"}; load_rules(single_rule, "single_rule.yaml"); // No rules should be enabled yet for any custom rulesets EXPECT_EQ(1, m_engine->num_rules_for_ruleset(default_ruleset)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); // Enable for first ruleset, only that ruleset should have an // enabled rule afterward m_engine->enable_rule_by_tag(process_tags, true, ruleset_1); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); // Enable for second ruleset m_engine->enable_rule_by_tag(process_tags, true, ruleset_2); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_2)); // Now disable for second ruleset m_engine->enable_rule_by_tag(process_tags, false, ruleset_2); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); } TEST_F(test_falco_engine, enable_disabled_rule_by_tag) { std::set exec_process_tags = {"exec process"}; load_rules(single_rule, "single_rule.yaml"); // Only the first rule should be enabled EXPECT_EQ(1, m_engine->num_rules_for_ruleset(default_ruleset)); // Enable the disabled rule by tag m_engine->enable_rule_by_tag(exec_process_tags, true); // Both rules should be enabled now EXPECT_EQ(2, m_engine->num_rules_for_ruleset(default_ruleset)); } TEST_F(test_falco_engine, enable_rule_id) { uint16_t ruleset_1_id; uint16_t ruleset_2_id; uint16_t ruleset_3_id; load_rules(single_rule, "single_rule.yaml"); // The cases are identical to above, just using ruleset ids // instead of names. ruleset_1_id = m_engine->find_ruleset_id(ruleset_1); ruleset_2_id = m_engine->find_ruleset_id(ruleset_2); ruleset_3_id = m_engine->find_ruleset_id(ruleset_3); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(default_ruleset)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); m_engine->enable_rule("test rule", true, ruleset_1_id); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); m_engine->enable_rule("test rule", true, ruleset_2_id); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); m_engine->enable_rule("", true, ruleset_3_id); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(2, m_engine->num_rules_for_ruleset(ruleset_3)); m_engine->enable_rule("test", false, ruleset_2_id); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(2, m_engine->num_rules_for_ruleset(ruleset_3)); } TEST_F(test_falco_engine, enable_rule_name_exact) { load_rules(single_rule, "single_rule.yaml"); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(default_ruleset)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4)); m_engine->enable_rule_exact("test rule", true, ruleset_1); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4)); m_engine->enable_rule_exact("test rule", true, ruleset_2); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4)); // This should **not** enable as this is a substring and not // an exact match. m_engine->enable_rule_exact("test", true, ruleset_3); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4)); m_engine->enable_rule_exact("", true, ruleset_4); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(2, m_engine->num_rules_for_ruleset(ruleset_4)); m_engine->enable_rule("test rule", false, ruleset_2); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(2, m_engine->num_rules_for_ruleset(ruleset_4)); } TEST_F(test_falco_engine, enable_rule_name_wildcard) { load_rules(multi_rule, "multi_rule.yaml"); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(default_ruleset)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4)); // As long as there are no *, exact matches work m_engine->enable_rule_wildcard("first actual rule", true, ruleset_1); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4)); m_engine->enable_rule_wildcard("*rule", true, ruleset_2); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(3, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4)); // This should enable the second rule m_engine->enable_rule_wildcard("*second*r*", true, ruleset_3); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(3, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(0, m_engine->num_rules_for_ruleset(ruleset_4)); m_engine->enable_rule_wildcard("*", true, ruleset_4); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_1)); EXPECT_EQ(3, m_engine->num_rules_for_ruleset(ruleset_2)); EXPECT_EQ(1, m_engine->num_rules_for_ruleset(ruleset_3)); EXPECT_EQ(3, m_engine->num_rules_for_ruleset(ruleset_4)); } ================================================ FILE: unit_tests/engine/test_extra_output.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 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 #include "../test_falco_engine.h" TEST_F(test_falco_engine, extra_format_all) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO )END"; m_engine->add_extra_output_format("evt.type=%evt.type", "", {}, ""); ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; EXPECT_EQ(get_compiled_rule_output("legit_rule"), "user=%user.name command=%proc.cmdline file=%fd.name evt.type=%evt.type"); } TEST_F(test_falco_engine, extra_format_by_rule) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: out 1 priority: INFO - rule: another_rule desc: legit rule description condition: evt.type=open output: out 2 priority: INFO )END"; m_engine->add_extra_output_format("evt.type=%evt.type", "", {}, "legit_rule"); ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; EXPECT_EQ(get_compiled_rule_output("legit_rule"), "out 1 evt.type=%evt.type"); EXPECT_EQ(get_compiled_rule_output("another_rule"), "out 2"); } TEST_F(test_falco_engine, extra_format_by_tag_rule) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: out 1 priority: INFO tags: [tag1] - rule: another_rule desc: legit rule description condition: evt.type=open output: out 2 priority: INFO tags: [tag1] - rule: a_third_rule desc: legit rule description condition: evt.type=open output: out 3 priority: INFO tags: [tag1, tag2] )END"; m_engine->add_extra_output_format("extra 1", "", {"tag1"}, ""); m_engine->add_extra_output_format("extra 2", "", {}, "another_rule"); m_engine->add_extra_output_format("extra 3", "", {"tag1", "tag2"}, ""); ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; EXPECT_EQ(get_compiled_rule_output("legit_rule"), "out 1 extra 1"); EXPECT_EQ(get_compiled_rule_output("another_rule"), "out 2 extra 1 extra 2"); EXPECT_EQ(get_compiled_rule_output("a_third_rule"), "out 3 extra 1 extra 3"); } TEST_F(test_falco_engine, extra_format_empty_container_info) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: out 1 (%container.info) priority: INFO tags: [tag1] )END"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; auto output = get_compiled_rule_output("legit_rule"); EXPECT_TRUE(output.find("%container.info") == output.npos); } TEST_F(test_falco_engine, extra_fields_all) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO )END"; std::unordered_map extra_formatted_fields = { {"my_field", "hello %evt.num"}}; for(auto const& f : extra_formatted_fields) { m_engine->add_extra_output_formatted_field(f.first, f.second, "", {}, ""); } ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; EXPECT_EQ(get_compiled_rule_formatted_fields("legit_rule"), extra_formatted_fields); } ================================================ FILE: unit_tests/engine/test_falco_utils.cpp ================================================ // 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 #include TEST(FalcoUtils, is_unix_scheme) { /* Wrong prefix */ ASSERT_EQ(falco::utils::network::is_unix_scheme("something:///run/falco/falco.sock"), false); /* Similar prefix, but wrong */ ASSERT_EQ(falco::utils::network::is_unix_scheme("unix///falco.sock"), false); /* Right prefix, passed as an `rvalue` */ ASSERT_EQ(falco::utils::network::is_unix_scheme("unix:///falco.sock"), true); /* Right prefix, passed as a `std::string` */ std::string url_string("unix:///falco.sock"); ASSERT_EQ(falco::utils::network::is_unix_scheme(url_string), true); /* Right prefix, passed as a `char[]` */ char url_char[] = "unix:///falco.sock"; ASSERT_EQ(falco::utils::network::is_unix_scheme(url_char), true); } TEST(FalcoUtils, parse_prometheus_interval) { /* Test matrix around correct time conversions. */ ASSERT_EQ(falco::utils::parse_prometheus_interval("1ms"), 1UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("1s"), 1000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("1m"), 60000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("1h"), 3600000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("1d"), 86400000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("1w"), 604800000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("1y"), (unsigned long)31536000000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("300ms"), 300UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("255s"), 255000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("5m"), 300000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("15m"), 900000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("30m"), 1800000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("60m"), 3600000UL); /* Test matrix for concatenated time interval examples. */ ASSERT_EQ(falco::utils::parse_prometheus_interval("1h3m2s1ms"), 3600000UL + 3 * 60000UL + 2 * 1000UL + 1UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("1y1w1d1h1m1s1ms"), (unsigned long)31536000000UL + 604800000UL + 86400000UL + 3600000UL + 60000UL + 1000UL + 1UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("2h5m"), 2 * 3600000UL + 5 * 60000UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("2h 5m"), 2 * 3600000UL + 5 * 60000UL); /* Invalid, non prometheus compliant time ordering will result in 0ms. */ ASSERT_EQ(falco::utils::parse_prometheus_interval("1ms1y"), 0UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("1t1y"), 0UL); ASSERT_EQ(falco::utils::parse_prometheus_interval("1t"), 0UL); /* Deprecated option to pass a numeric value in ms without prometheus compliant time unit, * will result in 0ms and as a result the end user will receive an error warning. */ ASSERT_EQ(falco::utils::parse_prometheus_interval("200"), 0UL); } TEST(FalcoUtils, sanitize_rule_name) { ASSERT_EQ(falco::utils::sanitize_rule_name("Testing rule 2 (CVE-2244)"), "Testing_rule_2_CVE_2244"); ASSERT_EQ(falco::utils::sanitize_rule_name("Testing rule__:2)"), "Testing_rule_:2"); ASSERT_EQ(falco::utils::sanitize_rule_name("This@is_a$test rule123"), "This_is_a_test_rule123"); ASSERT_EQ(falco::utils::sanitize_rule_name("RULEwith:special#characters"), "RULEwith:special_characters"); } TEST(FalcoUtils, matches_wildcard) { ASSERT_TRUE(falco::utils::matches_wildcard("*", "anything")); ASSERT_TRUE(falco::utils::matches_wildcard("**", "anything")); ASSERT_TRUE(falco::utils::matches_wildcard("*", "")); ASSERT_TRUE(falco::utils::matches_wildcard("no star", "no star")); ASSERT_TRUE(falco::utils::matches_wildcard("", "")); ASSERT_TRUE(falco::utils::matches_wildcard("hello*world", "hello new world")); ASSERT_TRUE(falco::utils::matches_wildcard("hello*world*", "hello new world yes")); ASSERT_TRUE(falco::utils::matches_wildcard("*hello*world", "come on hello this world")); ASSERT_TRUE(falco::utils::matches_wildcard("*hello*****world", "come on hello this world")); ASSERT_FALSE(falco::utils::matches_wildcard("no star", "")); ASSERT_FALSE(falco::utils::matches_wildcard("", "no star")); ASSERT_FALSE(falco::utils::matches_wildcard("star", "no star")); ASSERT_FALSE(falco::utils::matches_wildcard("hello*world", "hello new thing")); ASSERT_FALSE(falco::utils::matches_wildcard("hello*world", "hello new world yes")); ASSERT_FALSE(falco::utils::matches_wildcard("*hello*world", "come on hello this world yes")); ASSERT_FALSE(falco::utils::matches_wildcard("*hello*world*", "come on hello this yes")); } ================================================ FILE: unit_tests/engine/test_filter_details_resolver.cpp ================================================ // 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 ASSERT_EQd by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include TEST(DetailsResolver, resolve_ast) { std::string cond = "(spawned_process or evt.type = open) and (proc.name icontains cat or proc.name in " "(known_procs, ps))"; auto ast = libsinsp::filter::parser(cond).parse(); filter_details details; details.known_macros.insert("spawned_process"); details.known_lists.insert("known_procs"); filter_details_resolver resolver; resolver.run(ast.get(), details); // Assert fields ASSERT_EQ(details.fields.size(), 2); ASSERT_NE(details.fields.find("evt.type"), details.fields.end()); ASSERT_NE(details.fields.find("proc.name"), details.fields.end()); // Assert macros ASSERT_EQ(details.macros.size(), 1); ASSERT_NE(details.macros.find("spawned_process"), details.macros.end()); // Assert operators ASSERT_EQ(details.operators.size(), 3); ASSERT_NE(details.operators.find("="), details.operators.end()); ASSERT_NE(details.operators.find("icontains"), details.operators.end()); ASSERT_NE(details.operators.find("in"), details.operators.end()); // Assert lists ASSERT_EQ(details.lists.size(), 1); ASSERT_NE(details.lists.find("known_procs"), details.lists.end()); } ================================================ FILE: unit_tests/engine/test_filter_macro_resolver.cpp ================================================ // 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 #include namespace filter_ast = libsinsp::filter::ast; static std::vector::const_iterator find_value( const std::vector& values, const std::string& ref) { return std::find_if( values.begin(), values.end(), [&ref](const filter_macro_resolver::value_info& v) { return v.first == ref; }); } #define MACRO_NAME "test_macro" #define MACRO_A_NAME "test_macro_1" #define MACRO_B_NAME "test_macro_2" TEST(MacroResolver, should_resolve_macros_on_a_filter_AST) { filter_ast::pos_info macro_pos(12, 85, 27); std::shared_ptr macro = filter_ast::unary_check_expr::create(filter_ast::field_expr::create("test.field", ""), "exists"); std::vector> filter_and; filter_and.push_back( filter_ast::unary_check_expr::create(filter_ast::field_expr::create("evt.name", ""), "exists")); filter_and.push_back(filter_ast::not_expr::create( filter_ast::identifier_expr::create(MACRO_NAME, macro_pos))); std::shared_ptr filter = filter_ast::and_expr::create(filter_and); std::vector> expected_and; expected_and.push_back( filter_ast::unary_check_expr::create(filter_ast::field_expr::create("evt.name", ""), "exists")); expected_and.push_back(filter_ast::not_expr::create(clone(macro.get()))); std::shared_ptr expected = filter_ast::and_expr::create(expected_and); filter_macro_resolver resolver; resolver.set_macro(MACRO_NAME, macro); // first run ASSERT_TRUE(resolver.run(filter)); ASSERT_EQ(resolver.get_resolved_macros().size(), 1); ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_NAME); ASSERT_EQ(resolver.get_resolved_macros().begin()->second, macro_pos); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_TRUE(filter->is_equal(expected.get())); // second run ASSERT_FALSE(resolver.run(filter)); ASSERT_TRUE(resolver.get_resolved_macros().empty()); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_TRUE(filter->is_equal(expected.get())); } TEST(MacroResolver, should_resolve_macros_on_a_filter_AST_single_node) { filter_ast::pos_info macro_pos(12, 85, 27); std::shared_ptr macro = filter_ast::unary_check_expr::create(filter_ast::field_expr::create("test.field", ""), "exists"); std::shared_ptr filter = filter_ast::identifier_expr::create(MACRO_NAME, macro_pos); filter_macro_resolver resolver; resolver.set_macro(MACRO_NAME, macro); // first run filter_ast::expr* old_filter_ptr = filter.get(); ASSERT_TRUE(resolver.run(filter)); ASSERT_NE(filter.get(), old_filter_ptr); ASSERT_EQ(resolver.get_resolved_macros().size(), 1); ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_NAME); ASSERT_EQ(resolver.get_resolved_macros().begin()->second, macro_pos); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_TRUE(filter->is_equal(macro.get())); // second run old_filter_ptr = filter.get(); ASSERT_FALSE(resolver.run(filter)); ASSERT_EQ(filter.get(), old_filter_ptr); ASSERT_TRUE(resolver.get_resolved_macros().empty()); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_TRUE(filter->is_equal(macro.get())); } TEST(MacroResolver, should_resolve_macros_on_a_filter_AST_multiple_macros) { filter_ast::pos_info a_macro_pos(11, 75, 43); filter_ast::pos_info b_macro_pos(91, 21, 9); std::shared_ptr a_macro = filter_ast::unary_check_expr::create(filter_ast::field_expr::create("one.field", ""), "exists"); std::shared_ptr b_macro = filter_ast::unary_check_expr::create( filter_ast::field_expr::create("another.field", ""), "exists"); std::vector> filter_or; filter_or.push_back(filter_ast::identifier_expr::create(MACRO_A_NAME, a_macro_pos)); filter_or.push_back(filter_ast::identifier_expr::create(MACRO_B_NAME, b_macro_pos)); std::shared_ptr filter = filter_ast::or_expr::create(filter_or); std::vector> expected_or; expected_or.push_back(clone(a_macro.get())); expected_or.push_back(clone(b_macro.get())); std::shared_ptr expected_filter = filter_ast::or_expr::create(expected_or); filter_macro_resolver resolver; resolver.set_macro(MACRO_A_NAME, a_macro); resolver.set_macro(MACRO_B_NAME, b_macro); // first run ASSERT_TRUE(resolver.run(filter)); ASSERT_EQ(resolver.get_resolved_macros().size(), 2); auto a_resolved_itr = find_value(resolver.get_resolved_macros(), MACRO_A_NAME); ASSERT_NE(a_resolved_itr, resolver.get_resolved_macros().end()); ASSERT_STREQ(a_resolved_itr->first.c_str(), MACRO_A_NAME); ASSERT_EQ(a_resolved_itr->second, a_macro_pos); auto b_resolved_itr = find_value(resolver.get_resolved_macros(), MACRO_B_NAME); ASSERT_NE(b_resolved_itr, resolver.get_resolved_macros().end()); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_STREQ(b_resolved_itr->first.c_str(), MACRO_B_NAME); ASSERT_EQ(b_resolved_itr->second, b_macro_pos); ASSERT_TRUE(filter->is_equal(expected_filter.get())); // second run ASSERT_FALSE(resolver.run(filter)); ASSERT_TRUE(resolver.get_resolved_macros().empty()); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_TRUE(filter->is_equal(expected_filter.get())); } TEST(MacroResolver, should_resolve_macros_on_a_filter_AST_nested_macros) { filter_ast::pos_info a_macro_pos(47, 1, 76); filter_ast::pos_info b_macro_pos(111, 65, 2); std::vector> a_macro_and; a_macro_and.push_back( filter_ast::unary_check_expr::create(filter_ast::field_expr::create("one.field", ""), "exists")); a_macro_and.push_back(filter_ast::identifier_expr::create(MACRO_B_NAME, b_macro_pos)); std::shared_ptr a_macro = filter_ast::and_expr::create(a_macro_and); std::shared_ptr b_macro = filter_ast::unary_check_expr::create( filter_ast::field_expr::create("another.field", ""), "exists"); std::shared_ptr filter = filter_ast::identifier_expr::create(MACRO_A_NAME, a_macro_pos); std::vector> expected_and; expected_and.push_back( filter_ast::unary_check_expr::create(filter_ast::field_expr::create("one.field", ""), "exists")); expected_and.push_back(filter_ast::unary_check_expr::create( filter_ast::field_expr::create("another.field", ""), "exists")); std::shared_ptr expected_filter = filter_ast::and_expr::create(expected_and); filter_macro_resolver resolver; resolver.set_macro(MACRO_A_NAME, a_macro); resolver.set_macro(MACRO_B_NAME, b_macro); // first run ASSERT_TRUE(resolver.run(filter)); ASSERT_EQ(resolver.get_resolved_macros().size(), 2); auto a_resolved_itr = find_value(resolver.get_resolved_macros(), MACRO_A_NAME); ASSERT_NE(a_resolved_itr, resolver.get_resolved_macros().end()); ASSERT_STREQ(a_resolved_itr->first.c_str(), MACRO_A_NAME); ASSERT_EQ(a_resolved_itr->second, a_macro_pos); auto b_resolved_itr = find_value(resolver.get_resolved_macros(), MACRO_B_NAME); ASSERT_NE(b_resolved_itr, resolver.get_resolved_macros().end()); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_STREQ(b_resolved_itr->first.c_str(), MACRO_B_NAME); ASSERT_EQ(b_resolved_itr->second, b_macro_pos); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_TRUE(filter->is_equal(expected_filter.get())); // second run ASSERT_FALSE(resolver.run(filter)); ASSERT_TRUE(resolver.get_resolved_macros().empty()); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_TRUE(filter->is_equal(expected_filter.get())); } TEST(MacroResolver, should_find_unknown_macros) { filter_ast::pos_info macro_pos(9, 4, 2); std::vector> filter_and; filter_and.push_back( filter_ast::unary_check_expr::create(filter_ast::field_expr::create("evt.name", ""), "exists")); filter_and.push_back(filter_ast::not_expr::create( filter_ast::identifier_expr::create(MACRO_NAME, macro_pos))); std::shared_ptr filter = filter_ast::and_expr::create(filter_and); filter_macro_resolver resolver; ASSERT_FALSE(resolver.run(filter)); ASSERT_EQ(resolver.get_unknown_macros().size(), 1); ASSERT_STREQ(resolver.get_unknown_macros().begin()->first.c_str(), MACRO_NAME); ASSERT_EQ(resolver.get_unknown_macros().begin()->second, macro_pos); ASSERT_TRUE(resolver.get_resolved_macros().empty()); } TEST(MacroResolver, should_find_unknown_nested_macros) { filter_ast::pos_info a_macro_pos(32, 84, 9); filter_ast::pos_info b_macro_pos(1, 0, 5); std::vector> a_macro_and; a_macro_and.push_back( filter_ast::unary_check_expr::create(filter_ast::field_expr::create("one.field", ""), "exists")); a_macro_and.push_back(filter_ast::identifier_expr::create(MACRO_B_NAME, b_macro_pos)); std::shared_ptr a_macro = filter_ast::and_expr::create(a_macro_and); std::shared_ptr filter = filter_ast::identifier_expr::create(MACRO_A_NAME, a_macro_pos); auto expected_filter = clone(a_macro.get()); filter_macro_resolver resolver; resolver.set_macro(MACRO_A_NAME, a_macro); ASSERT_TRUE(resolver.run(filter)); ASSERT_EQ(resolver.get_resolved_macros().size(), 1); ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_A_NAME); ASSERT_EQ(resolver.get_resolved_macros().begin()->second, a_macro_pos); ASSERT_EQ(resolver.get_unknown_macros().size(), 1); ASSERT_STREQ(resolver.get_unknown_macros().begin()->first.c_str(), MACRO_B_NAME); ASSERT_EQ(resolver.get_unknown_macros().begin()->second, b_macro_pos); ASSERT_TRUE(filter->is_equal(expected_filter.get())); } TEST(MacroResolver, should_undefine_macro) { filter_ast::pos_info macro_pos_1(12, 9, 3); filter_ast::pos_info macro_pos_2(9, 6, 3); std::shared_ptr macro = filter_ast::unary_check_expr::create(filter_ast::field_expr::create("test.field", ""), "exists"); std::shared_ptr a_filter = filter_ast::identifier_expr::create(MACRO_NAME, macro_pos_1); std::shared_ptr b_filter = filter_ast::identifier_expr::create(MACRO_NAME, macro_pos_2); filter_macro_resolver resolver; resolver.set_macro(MACRO_NAME, macro); ASSERT_TRUE(resolver.run(a_filter)); ASSERT_EQ(resolver.get_resolved_macros().size(), 1); ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_NAME); ASSERT_EQ(resolver.get_resolved_macros().begin()->second, macro_pos_1); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_TRUE(a_filter->is_equal(macro.get())); resolver.set_macro(MACRO_NAME, NULL); ASSERT_FALSE(resolver.run(b_filter)); ASSERT_TRUE(resolver.get_resolved_macros().empty()); ASSERT_EQ(resolver.get_unknown_macros().size(), 1); ASSERT_STREQ(resolver.get_unknown_macros().begin()->first.c_str(), MACRO_NAME); ASSERT_EQ(resolver.get_unknown_macros().begin()->second, macro_pos_2); } /* checks that the macro AST is cloned and not shared across resolved filters */ TEST(MacroResolver, should_clone_macro_AST) { filter_ast::pos_info macro_pos(5, 2, 8888); std::shared_ptr macro = filter_ast::unary_check_expr::create(filter_ast::field_expr::create("test.field", ""), "exists"); std::shared_ptr filter = filter_ast::identifier_expr::create(MACRO_NAME, macro_pos); filter_macro_resolver resolver; resolver.set_macro(MACRO_NAME, macro); ASSERT_TRUE(resolver.run(filter)); ASSERT_EQ(resolver.get_resolved_macros().size(), 1); ASSERT_STREQ(resolver.get_resolved_macros().begin()->first.c_str(), MACRO_NAME); ASSERT_EQ(resolver.get_resolved_macros().begin()->second, macro_pos); ASSERT_TRUE(resolver.get_unknown_macros().empty()); ASSERT_TRUE(filter->is_equal(macro.get())); macro->left = filter_ast::field_expr::create("another.field", ""); ASSERT_FALSE(filter->is_equal(macro.get())); } ================================================ FILE: unit_tests/engine/test_filter_warning_resolver.cpp ================================================ // 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 #include #include static bool warns(const std::string& condition) { auto ast = libsinsp::filter::parser(condition).parse(); rule_loader::context ctx("test"); rule_loader::result res("test"); filter_warning_resolver().run(ctx, res, *ast.get()); return res.has_warnings(); } TEST(WarningResolver, warnings_in_filtering_conditions) { ASSERT_FALSE(warns("ka.field exists")); ASSERT_FALSE(warns("some.field = ")); ASSERT_TRUE(warns("jevt.field = ")); ASSERT_TRUE(warns("ka.field = ")); ASSERT_TRUE(warns("ka.field == ")); ASSERT_TRUE(warns("ka.field != ")); ASSERT_TRUE(warns("ka.field in ()")); ASSERT_TRUE(warns("ka.field in (otherval, )")); ASSERT_TRUE(warns("ka.field intersects ()")); ASSERT_TRUE(warns("ka.field intersects (otherval, )")); ASSERT_TRUE(warns("ka.field pmatch ()")); ASSERT_TRUE(warns("ka.field pmatch (otherval, )")); ASSERT_TRUE(warns("evt.dir = <")); ASSERT_TRUE(warns("evt.dir = >")); ASSERT_TRUE(warns("proc.name=test and evt.dir = <")); ASSERT_TRUE(warns("evt.dir = < and proc.name=test")); } ================================================ FILE: unit_tests/engine/test_plugin_requirements.cpp ================================================ // 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 #include #include static bool check_requirements(std::string& err, const std::vector& plugins, const std::string& ruleset_content) { falco_engine e; falco::load_result::rules_contents_t c = {{"test", ruleset_content}}; auto res = e.load_rules(c.begin()->second, c.begin()->first); if(!res->successful()) { return false; } return e.check_plugin_requirements(plugins, err); } TEST(PluginRequirements, check_plugin_requirements_success) { std::string error; /* No requirement */ ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.1.0"}}, "")) << error << std::endl; /* Single plugin */ ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 )")) << error << std::endl; /* Single plugin newer version */ ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.2.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 )")) << error << std::endl; /* Multiple plugins */ ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.1.0"}, {"json", "0.3.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 - name: json version: 0.3.0 )")) << error << std::endl; /* Single plugin multiple versions */ ASSERT_TRUE(check_requirements(error, {{"k8saudit", "0.2.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 - required_plugin_versions: - name: k8saudit version: 0.2.0 )")) << error << std::endl; /* Single plugin with alternatives */ ASSERT_TRUE(check_requirements(error, {{"k8saudit-other", "0.5.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 alternatives: - name: k8saudit-other version: 0.4.0 )")) << error << std::endl; /* Multiple plugins with alternatives */ ASSERT_TRUE(check_requirements(error, {{"k8saudit-other", "0.5.0"}, {"json2", "0.5.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 alternatives: - name: k8saudit-other version: 0.4.0 - name: json version: 0.3.0 alternatives: - name: json2 version: 0.1.0 )")) << error << std::endl; /* Multiple plugins with alternatives with multiple versions */ ASSERT_TRUE(check_requirements(error, {{"k8saudit-other", "0.7.0"}, {"json2", "0.5.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 alternatives: - name: k8saudit-other version: 0.4.0 - name: json version: 0.3.0 alternatives: - name: json2 version: 0.1.0 - required_plugin_versions: - name: k8saudit version: 1.0.0 alternatives: - name: k8saudit-other version: 0.7.0 )")) << error << std::endl; } TEST(PluginRequirements, check_plugin_requirements_reject) { std::string error; /* No plugin loaded */ ASSERT_FALSE(check_requirements(error, {}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 )")) << error << std::endl; /* Single plugin wrong name */ ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"( - required_plugin_versions: - name: k8saudit2 version: 0.1.0 )")) << error << std::endl; /* Single plugin wrong version */ ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.2.0 )")) << error << std::endl; /* Multiple plugins */ ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 - name: json version: 0.3.0 )")) << error << std::endl; /* Single plugin multiple versions */ ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.1.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 - required_plugin_versions: - name: k8saudit version: 0.2.0 )")) << error << std::endl; /* Single plugin with alternatives */ ASSERT_FALSE(check_requirements(error, {{"k8saudit2", "0.5.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 alternatives: - name: k8saudit-other version: 0.4.0 )")) << error << std::endl; /* Single plugin with overlapping alternatives */ ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.5.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 alternatives: - name: k8saudit version: 0.4.0 )")) << error << std::endl; /* Multiple plugins with alternatives */ ASSERT_FALSE(check_requirements(error, {{"k8saudit-other", "0.5.0"}, {"json3", "0.5.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.1.0 alternatives: - name: k8saudit-other version: 0.4.0 - name: json version: 0.3.0 alternatives: - name: json2 version: 0.1.0 )")) << error << std::endl; /* Multiple plugins with alternatives with multiple versions */ ASSERT_FALSE(check_requirements(error, {{"k8saudit", "0.7.0"}, {"json2", "0.5.0"}}, R"( - required_plugin_versions: - name: k8saudit version: 0.4.0 alternatives: - name: k8saudit-other version: 0.4.0 - name: json version: 0.3.0 alternatives: - name: json2 version: 0.1.0 - required_plugin_versions: - name: k8saudit version: 1.0.0 alternatives: - name: k8saudit-other version: 0.7.0 )")) << error << std::endl; } ================================================ FILE: unit_tests/engine/test_rule_loader.cpp ================================================ #include #include "../test_falco_engine.h" #include "yaml_helper.h" #define ASSERT_VALIDATION_STATUS(status) \ ASSERT_TRUE(sinsp_utils::startswith(m_load_result->schema_validation(), status)) std::string s_sample_ruleset = "sample-ruleset"; std::string s_sample_source = falco_common::syscall_source; TEST_F(test_falco_engine, list_append) { std::string rules_content = R"END( - list: shell_binaries items: [ash, bash, csh, ksh, sh, tcsh, zsh, dash] - rule: legit_rule desc: legit rule description condition: evt.type=open and proc.name in (shell_binaries) output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - list: shell_binaries items: [pwsh] override: items: append )END"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ( get_compiled_rule_condition("legit_rule"), "(evt.type = open and proc.name in (ash, bash, csh, ksh, sh, tcsh, zsh, dash, pwsh))"); } TEST_F(test_falco_engine, condition_append) { std::string rules_content = R"END( - macro: interactive condition: > ((proc.aname=sshd and proc.name != sshd) or proc.name=systemd-logind or proc.name=login) - rule: legit_rule desc: legit rule description condition: evt.type=open and interactive output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - macro: interactive condition: or proc.name = ssh override: condition: append )END"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("legit_rule"), "(evt.type = open and (((proc.aname = sshd and proc.name != sshd) or proc.name = " "systemd-logind or proc.name = login) or proc.name = ssh))"); } TEST_F(test_falco_engine, rule_override_append) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: legit_rule desc: with append condition: and proc.name = cat output: proc=%proc.name override: desc: append condition: append output: append )END"; std::string rule_name = "legit_rule"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); // Here we don't use the deprecated `append` flag, so we don't expect the warning. ASSERT_FALSE(check_warning_message(WARNING_APPEND)); auto rule_description = m_engine->describe_rule(&rule_name, {}); ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get(), "evt.type=open and proc.name = cat"); ASSERT_EQ(rule_description["rules"][0]["info"]["output"].template get(), "user=%user.name command=%proc.cmdline file=%fd.name proc=%proc.name"); ASSERT_EQ(rule_description["rules"][0]["info"]["description"].template get(), "legit rule description with append"); } TEST_F(test_falco_engine, rule_append) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: legit_rule condition: and proc.name = cat append: true )END"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); // We should have at least one warning because the 'append' flag is deprecated. ASSERT_TRUE(check_warning_message(WARNING_APPEND)); ASSERT_EQ(get_compiled_rule_condition("legit_rule"), "(evt.type = open and proc.name = cat)"); } TEST_F(test_falco_engine, rule_override_replace) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type=open output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: legit_rule desc: a replaced legit description condition: evt.type = close override: desc: replace condition: replace )END"; std::string rule_name = "legit_rule"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); auto rule_description = m_engine->describe_rule(&rule_name, {}); ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get(), "evt.type = close"); ASSERT_EQ(rule_description["rules"][0]["info"]["output"].template get(), "user=%user.name command=%proc.cmdline file=%fd.name"); ASSERT_EQ(rule_description["rules"][0]["info"]["description"].template get(), "a replaced legit description"); } TEST_F(test_falco_engine, rule_override_append_replace) { std::string rules_content = R"END( - rule: legit_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: legit_rule desc: a replaced legit description condition: and proc.name = cat priority: WARNING override: desc: replace condition: append priority: replace )END"; std::string rule_name = "legit_rule"; ASSERT_TRUE(load_rules(rules_content, "legit_rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); auto rule_description = m_engine->describe_rule(&rule_name, {}); ASSERT_EQ(rule_description["rules"][0]["info"]["condition"].template get(), "evt.type = close and proc.name = cat"); ASSERT_EQ(rule_description["rules"][0]["info"]["output"].template get(), "user=%user.name command=%proc.cmdline file=%fd.name"); ASSERT_EQ(rule_description["rules"][0]["info"]["description"].template get(), "a replaced legit description"); ASSERT_EQ(rule_description["rules"][0]["info"]["priority"].template get(), "Warning"); } TEST_F(test_falco_engine, rule_incorrect_override_type) { std::string rules_content = R"END( - rule: failing_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: failing_rule desc: an appended incorrect field condition: and proc.name = cat priority: WARNING override: desc: replace condition: append priority: append )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message("Key 'priority' cannot be appended to, use 'replace' instead")); ASSERT_TRUE(std::string(m_load_result_json["errors"][0]["context"]["snippet"]) .find("priority: append") != std::string::npos); } TEST_F(test_falco_engine, rule_incorrect_append_override) { std::string rules_content = R"END( - rule: failing_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: failing_rule desc: an appended incorrect field condition: and proc.name = cat append: true override: desc: replace condition: append )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); // We should have at least one warning because the 'append' flag is deprecated. ASSERT_TRUE(check_warning_message(WARNING_APPEND)); ASSERT_TRUE(check_error_message(ERROR_OVERRIDE_APPEND)); } TEST_F(test_falco_engine, macro_override_append_before_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: or evt.type = openat2 override: condition: append - macro: open_simple condition: evt.type in (open,openat) - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // We cannot define a macro override before the macro definition. ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_MACRO)); } TEST_F(test_falco_engine, macro_override_replace_before_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: or evt.type = openat2 override: condition: replace - macro: open_simple condition: evt.type in (open,openat) - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // The first override defines a macro that is overridden by the second macro definition ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "evt.type in (open, openat)"); } TEST_F(test_falco_engine, macro_append_before_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: or evt.type = openat2 append: true - macro: open_simple condition: evt.type in (open,openat) - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // We cannot define a macro override before the macro definition. ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_MACRO)); } TEST_F(test_falco_engine, macro_override_append_after_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: evt.type in (open,openat) - macro: open_simple condition: or evt.type = openat2 override: condition: append - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // We cannot define a macro override before the macro definition. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type in (open, openat) or evt.type = openat2)"); } TEST_F(test_falco_engine, macro_append_after_macro_definition) { std::string rules_content = R"END( - macro: open_simple condition: evt.type in (open,openat) - macro: open_simple condition: or evt.type = openat2 append: true - rule: test_rule desc: simple rule condition: open_simple output: command=%proc.cmdline priority: INFO )END"; // We cannot define a macro override before the macro definition. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type in (open, openat) or evt.type = openat2)"); } TEST_F(test_falco_engine, rule_override_append_before_rule_definition) { std::string rules_content = R"END( - rule: test_rule condition: and proc.name = cat override: condition: append - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_RULE_APPEND)); } TEST_F(test_falco_engine, rule_override_replace_before_rule_definition) { std::string rules_content = R"END( - rule: test_rule condition: and proc.name = cat override: condition: replace - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_RULE_REPLACE)); } TEST_F(test_falco_engine, rule_append_before_rule_definition) { std::string rules_content = R"END( - rule: test_rule condition: and proc.name = cat append: true - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_RULE_APPEND)); } TEST_F(test_falco_engine, rule_override_append_after_rule_definition) { std::string rules_content = R"END( - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO - rule: test_rule condition: and proc.name = cat override: condition: append )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type in (open, openat) and proc.name = cat)"); } TEST_F(test_falco_engine, rule_append_after_rule_definition) { std::string rules_content = R"END( - rule: test_rule desc: simple rule condition: evt.type in (open,openat) output: command=%proc.cmdline priority: INFO - rule: test_rule condition: and proc.name = cat append: true )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type in (open, openat) and proc.name = cat)"); } TEST_F(test_falco_engine, list_override_append_wrong_key) { // todo: maybe we want to manage some non-existent keys // Please note how the non-existent key 'non-existent keys' is ignored. std::string rules_content = R"END( - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] override_written_wrong: items: append - list: dev_creation_binaries items: [blkid] - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; // Since there is a wrong key in the first list definition the `override` is not // considered. so in this situation, we are defining the list 2 times. The // second one overrides the first one. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_failed) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = execve and proc.name in (blkid))"); } TEST_F(test_falco_engine, list_override_append_before_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] override: items: append - list: dev_creation_binaries items: [blkid] - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; // We cannot define a list override before the list definition. ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_LIST)); } TEST_F(test_falco_engine, list_override_replace_before_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] override: items: replace - list: dev_creation_binaries items: [blkid] - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; // With override replace we define a first list that then is overridden by the second one. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = execve and proc.name in (blkid))"); } TEST_F(test_falco_engine, list_append_before_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] append: true - list: dev_creation_binaries items: [blkid] - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; // We cannot define a list append before the list definition. ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message(ERROR_NO_PREVIOUS_LIST)); } TEST_F(test_falco_engine, list_override_append_after_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: [blkid] - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] override: items: append - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = execve and proc.name in (blkid, csi-provisioner, csi-attacher))"); } TEST_F(test_falco_engine, list_append_after_list_definition) { std::string rules_content = R"END( - list: dev_creation_binaries items: [blkid] - list: dev_creation_binaries items: ["csi-provisioner", "csi-attacher"] append: true - rule: test_rule desc: simple rule condition: evt.type = execve and proc.name in (dev_creation_binaries) output: command=%proc.cmdline priority: INFO )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = execve and proc.name in (blkid, csi-provisioner, csi-attacher))"); } TEST_F(test_falco_engine, rule_override_without_field) { std::string rules_content = R"END( - rule: failing_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: failing_rule desc: an appended incorrect field override: desc: replace condition: append )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message( "An append override for 'condition' was specified but 'condition' is not defined")); } TEST_F(test_falco_engine, rule_override_extra_field) { std::string rules_content = R"END( - rule: failing_rule desc: legit rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: failing_rule desc: an appended incorrect field condition: and proc.name = cat priority: WARNING override: desc: replace condition: append )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message("Unexpected key 'priority'")); } TEST_F(test_falco_engine, missing_enabled_key_with_override) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule desc: missing enabled key condition: and proc.name = cat override: desc: replace condition: append enabled: replace )END"; // In the rule override we miss `enabled: true` ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message("'enabled' was specified but 'enabled' is not defined")); } TEST_F(test_falco_engine, rule_override_with_enabled) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule desc: correct override condition: and proc.name = cat enabled: true override: desc: replace condition: append enabled: replace )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); // The rule should be enabled at the end. EXPECT_EQ(num_rules_for_ruleset(), 1); } TEST_F(test_falco_engine, rule_override_exceptions_required_fields) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO exceptions: - name: test_exception fields: proc.name comps: in values: ["cat"] # when appending, it's fine to provide partial exception definitions - rule: test_rule exceptions: - name: test_exception values: [echo] override: exceptions: append # when replacing, we don't allow partial exception definitions - rule: test_rule exceptions: - name: test_exception values: [id] override: exceptions: replace )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); ASSERT_TRUE(check_error_message("Item has no mapping for key 'fields'")) << m_load_result_json.dump(); } TEST_F(test_falco_engine, rule_not_enabled) { std::string rules_content = R"END( - rule: test_rule desc: rule not enabled condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); EXPECT_EQ(num_rules_for_ruleset(), 0); } TEST_F(test_falco_engine, rule_enabled_warning) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule enabled: true )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message(WARNING_ENABLED)); // The rule should be enabled at the end. EXPECT_EQ(num_rules_for_ruleset(), 1); } // todo!: Probably we shouldn't allow this syntax TEST_F(test_falco_engine, rule_enabled_is_ignored_by_append) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule condition: and proc.name = cat append: true enabled: true )END"; // 'enabled' is ignored by the append, this syntax is not supported // so the rule is not enabled. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(num_rules_for_ruleset(), 0); } // todo!: Probably we shouldn't allow this syntax TEST_F(test_falco_engine, rewrite_rule) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false - rule: test_rule desc: redefined rule syntax condition: proc.name = cat output: user=%user.name command=%proc.cmdline file=%fd.name priority: WARNING enabled: true )END"; // The above syntax is not supported, we cannot override the content // of a rule in this way. ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); // In this case the rule is completely overridden but this syntax is not supported. EXPECT_EQ(num_rules_for_ruleset(), 1); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "proc.name = cat"); } TEST_F(test_falco_engine, required_engine_version_semver) { std::string rules_content = R"END( - required_engine_version: 0.26.0 - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); } TEST_F(test_falco_engine, required_engine_version_not_semver) { std::string rules_content = R"END( - required_engine_version: 26 - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); } TEST_F(test_falco_engine, required_engine_version_invalid) { std::string rules_content = R"END( - required_engine_version: seven - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_error_message("Unable to parse engine version")); } // checks for issue described in https://github.com/falcosecurity/falco/pull/3028 TEST_F(test_falco_engine, list_value_with_escaping) { std::string rules_content = R"END( - list: my_list items: [non_escaped_val, "escaped val"] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(m_load_result->successful()); ASSERT_TRUE(m_load_result->has_warnings()); // a warning for the unused list auto rule_description = m_engine->describe_rule(nullptr, {}); ASSERT_TRUE(m_load_result->successful()); ASSERT_EQ(rule_description["rules"].size(), 0); ASSERT_EQ(rule_description["macros"].size(), 0); ASSERT_EQ(rule_description["lists"].size(), 1); // escaped values must not be interpreted as list refs by mistake ASSERT_EQ(rule_description["lists"][0]["details"]["lists"].size(), 0); // values should be escaped correctly ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"].size(), 2); ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"][0] .template get(), "non_escaped_val"); ASSERT_EQ(rule_description["lists"][0]["details"]["items_compiled"][1] .template get(), "escaped val"); } TEST_F(test_falco_engine, exceptions_condition) { std::string rules_content = R"END( - rule: test_rule desc: test rule condition: proc.cmdline contains curl or proc.cmdline contains wget output: command=%proc.cmdline priority: INFO exceptions: - name: test_exception fields: [proc.cmdline] comps: [contains] values: - [curl 127.0.0.1] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_EQ(get_compiled_rule_condition("test_rule"), "((proc.cmdline contains curl or proc.cmdline contains wget) and not proc.cmdline " "contains \"curl 127.0.0.1\")"); } TEST_F(test_falco_engine, macro_name_invalid) { std::string rules_content = R"END( - macro: test-macro condition: evt.type = close )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message( "Macro has an invalid name. Macro names should match a regular expression")); } TEST_F(test_falco_engine, list_name_invalid) { std::string rules_content = R"END( - list: test list items: [open, openat, openat2] - rule: test_rule desc: test rule description condition: evt.type in (test list) output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO enabled: false )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message( "List has an invalid name. List names should match a regular expression")); } // The appended exception has a purposely miswritten field (value), // simulating a typo or an incorrect usage. TEST_F(test_falco_engine, exceptions_append_no_values) { std::string rules_content = R"END( - rule: test_rule desc: test rule condition: proc.cmdline contains curl output: command=%proc.cmdline priority: INFO exceptions: - name: test_exception fields: [proc.cmdline] comps: [contains] values: - [curl 127.0.0.1] - rule: test_rule exceptions: - name: test_exception value: curl 1.1.1.1 append: true )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_failed) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message("Overriding/appending exception with no values")); } TEST_F(test_falco_engine, exceptions_override_no_values) { std::string rules_content = R"END( - rule: test_rule desc: test rule condition: proc.cmdline contains curl output: command=%proc.cmdline priority: INFO exceptions: - name: test_exception fields: [proc.cmdline] comps: [contains] values: - [curl 127.0.0.1] - rule: test_rule exceptions: - name: test_exception value: curl 1.1.1.1 override: exceptions: append )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_failed) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message("Overriding/appending exception with no values")); } TEST_F(test_falco_engine, exceptions_names_not_unique) { std::string rules_content = R"END( - rule: test_rule desc: test rule condition: proc.cmdline contains curl output: command=%proc.cmdline priority: INFO exceptions: - name: test_exception fields: [proc.cmdline] comps: [contains] values: - [curl 127.0.0.1] - name: test_exception fields: [proc.cmdline] comps: [endswith] values: - [curl 127.0.0.1] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message("Multiple definitions of exception")); } static std::string s_exception_values_rule_base = R"END( - rule: test_rule desc: test rule condition: evt.type = open output: command=%proc.cmdline priority: INFO )END"; TEST_F(test_falco_engine, exceptions_values_rhs_field_ambiguous) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - [proc.pname] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = proc.pname)"); EXPECT_TRUE(check_warning_message( "'proc.pname' may be a valid field misused as a const string value")); } TEST_F(test_falco_engine, exceptions_values_rhs_field_ambiguous_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - ["proc.pname"] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = proc.pname)"); EXPECT_TRUE(check_warning_message( "'proc.pname' may be a valid field misused as a const string value")); } TEST_F(test_falco_engine, exceptions_values_rhs_field_ambiguous_space_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - ["proc.pname "] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = \"proc.pname \")"); EXPECT_TRUE(check_warning_message( "'proc.pname ' may be a valid field misused as a const string value")); } TEST_F(test_falco_engine, exceptions_values_rhs_transformer) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - [toupper(proc.pname)] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = toupper(proc.pname))"); } TEST_F(test_falco_engine, exceptions_values_transformer_value_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - ["toupper(proc.pname)"] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = toupper(proc.pname))"); } TEST_F(test_falco_engine, exceptions_values_transformer_space) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - [toupper( proc.pname)] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = \"toupper( proc.pname)\")"); EXPECT_TRUE( check_warning_message("'toupper( proc.pname)' may be a valid field transformer misused " "as a const string value")); } TEST_F(test_falco_engine, exceptions_values_transformer_space_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [proc.name] comps: [=] values: - ["toupper( proc.pname)"] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not proc.name = \"toupper( proc.pname)\")"); EXPECT_TRUE( check_warning_message("'toupper( proc.pname)' may be a valid field transformer misused " "as a const string value")); } TEST_F(test_falco_engine, exceptions_fields_transformer) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: [tolower(proc.name)] comps: [=] values: - [test] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); EXPECT_FALSE(has_warnings()); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)"); } TEST_F(test_falco_engine, exceptions_fields_transformer_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: ["tolower(proc.name)"] comps: [=] values: - [test] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)"); } TEST_F(test_falco_engine, exceptions_fields_transformer_space_quoted) { auto rules_content = s_exception_values_rule_base + R"END( exceptions: - name: test_exception fields: ["tolower( proc.name)"] comps: [=] values: - [test] )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(has_warnings()); EXPECT_EQ(get_compiled_rule_condition("test_rule"), "(evt.type = open and not tolower(proc.name) = test)"); } TEST_F(test_falco_engine, redefine_rule_different_source) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: ka.verb = GET output: some output priority: INFO source: k8s_audit - rule: LD_PRELOAD trick desc: Some desc condition: and 1 = 2 output: Some output priority: INFO source: syscall )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(check_error_message("Rule has been re-defined with a different source")); } TEST_F(test_falco_engine, append_across_sources) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: ka.verb = GET output: some output priority: INFO source: k8s_audit - rule: LD_PRELOAD trick desc: Some desc condition: and 1 = 2 output: Some output priority: INFO source: syscall append: true )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(check_error_message("Rule has been re-defined with a different source")); } TEST_F(test_falco_engine, selective_replace_across_sources) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: ka.verb = GET output: some output priority: INFO source: k8s_audit - rule: LD_PRELOAD trick condition: 1 = 2 override: condition: replace source: syscall )END"; ASSERT_FALSE(load_rules(rules_content, "rules.yaml")); ASSERT_TRUE(check_error_message("Rule has been re-defined with a different source")); } TEST_F(test_falco_engine, empty_source_addl_rule) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: evt.type=execve output: some output priority: INFO source: syscall - rule: LD_PRELOAD trick desc: Some desc condition: and proc.name=apache output: Some output priority: INFO source: append: true )END"; EXPECT_TRUE(load_rules(rules_content, "rules.yaml")); } TEST_F(test_falco_engine, empty_string_source_addl_rule) { auto rules_content = R"END( - rule: LD_PRELOAD trick desc: Some desc condition: evt.type=execve output: some output priority: INFO source: syscall - rule: LD_PRELOAD trick desc: Some desc condition: and proc.name=apache output: Some output priority: INFO source: "" append: true )END"; EXPECT_TRUE(load_rules(rules_content, "rules.yaml")); } // Phase 1: Schema correctness — no false positives for valid rule properties TEST_F(test_falco_engine, rule_with_warn_evttypes) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO warn_evttypes: false )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); } TEST_F(test_falco_engine, rule_with_skip_if_unknown_filter) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO skip-if-unknown-filter: true )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); } TEST_F(test_falco_engine, override_replace_warn_evttypes) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO warn_evttypes: true - rule: test_rule warn_evttypes: false override: warn_evttypes: replace )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); } TEST_F(test_falco_engine, override_replace_capture) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO - rule: test_rule capture: true override: capture: replace )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); } TEST_F(test_falco_engine, override_replace_tags) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO tags: [filesystem] - rule: test_rule tags: [network] override: tags: replace )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")) << m_load_result_string; ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); } // Phase 2: Unknown key detection TEST_F(test_falco_engine, rule_unknown_key) { std::string rules_content = R"END( - rule: test_rule desc: test rule description condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO typo_field: some_value )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")) << m_load_result_string; ASSERT_TRUE(check_warning_message("Unknown key 'typo_field'")); } TEST_F(test_falco_engine, list_unknown_key) { std::string rules_content = R"END( - list: my_list items: [cat, bash] typo_field: some_value - rule: test_rule desc: test rule description condition: evt.type = close and proc.name in (my_list) output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")) << m_load_result_string; ASSERT_TRUE(check_warning_message("Unknown key 'typo_field'")); } TEST_F(test_falco_engine, macro_unknown_key) { std::string rules_content = R"END( - macro: my_macro condition: evt.type = close typo_field: some_value - rule: test_rule desc: test rule description condition: my_macro output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")) << m_load_result_string; ASSERT_TRUE(check_warning_message("Unknown key 'typo_field'")); } TEST_F(test_falco_engine, list_cross_type_key_priority) { std::string rules_content = R"END( - list: my_list items: [cat, bash] priority: INFO - rule: test_rule desc: test rule description condition: evt.type = close and proc.name in (my_list) output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")) << m_load_result_string; // The flat-union schema accepts 'priority' on a list (validation_ok), // but procedural detection catches the cross-type misuse. ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(check_warning_message("Unknown key 'priority'")); } TEST_F(test_falco_engine, deprecated_field_in_output) { std::string rules_content = R"END( - rule: test_rule_with_evt_dir_in_output desc: test rule with evt.dir in output condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name evt.dir=%evt.dir priority: INFO )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_TRUE(has_warnings()); ASSERT_TRUE(check_warning_message( "usage of deprecated field 'evt.dir' has been detected in the rule output")) << m_load_result_string; EXPECT_EQ(num_rules_for_ruleset(), 1); } TEST_F(test_falco_engine, no_deprecated_field_warning_in_output) { std::string rules_content = R"END( - rule: test_rule_without_evt_dir desc: test rule without evt.dir in output condition: evt.type = close output: user=%user.name command=%proc.cmdline file=%fd.name priority: INFO )END"; ASSERT_TRUE(load_rules(rules_content, "rules.yaml")); ASSERT_VALIDATION_STATUS(yaml_helper::validation_ok) << m_load_result->schema_validation(); ASSERT_FALSE(check_warning_message("evt.dir")) << m_load_result_string; EXPECT_EQ(num_rules_for_ruleset(), 1); } ================================================ FILE: unit_tests/engine/test_rulesets.cpp ================================================ // 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 #include #define RULESET_0 0 #define RULESET_1 1 #define RULESET_2 2 /* Helpers methods */ static std::shared_ptr create_factory(sinsp* inspector, filter_check_list& list) { return std::make_shared(inspector, list); } static std::shared_ptr create_ruleset(std::shared_ptr f) { return std::make_shared(f); } static std::shared_ptr create_ast( std::shared_ptr f) { libsinsp::filter::parser parser("evt.type=open"); return parser.parse(); } static std::shared_ptr create_filter(std::shared_ptr f, libsinsp::filter::ast::expr* ast) { sinsp_filter_compiler compiler(f, ast); return std::shared_ptr(compiler.compile()); } TEST(Ruleset, enable_disable_rules_using_names) { sinsp inspector; sinsp_filter_check_list filterlist; auto f = create_factory(&inspector, filterlist); auto r = create_ruleset(f); auto ast = create_ast(f); auto filter = create_filter(f, ast.get()); falco_rule rule_A = {}; rule_A.name = "rule_A"; rule_A.source = falco_common::syscall_source; falco_rule rule_B = {}; rule_B.name = "rule_B"; rule_B.source = falco_common::syscall_source; falco_rule rule_C = {}; rule_C.name = "rule_C"; rule_C.source = falco_common::syscall_source; r->add(rule_A, filter, ast); r->add(rule_B, filter, ast); r->add(rule_C, filter, ast); /* Enable `rule_A` for RULESET_0 */ r->enable(rule_A.name, filter_ruleset::match_type::exact, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 1); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Disable `rule_A` for RULESET_1, this should have no effect */ r->disable(rule_A.name, filter_ruleset::match_type::exact, RULESET_1); ASSERT_EQ(r->enabled_count(RULESET_0), 1); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Enable a not existing rule for RULESET_2, this should have no effect */ r->disable("", filter_ruleset::match_type::exact, RULESET_2); ASSERT_EQ(r->enabled_count(RULESET_0), 1); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Enable all rules for RULESET_0 */ r->enable("rule_", filter_ruleset::match_type::substring, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 3); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Try to disable all rules with exact match for RULESET_0, this should have no effect */ r->disable("rule_", filter_ruleset::match_type::exact, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 3); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Disable all rules for RULESET_0 */ r->disable("rule_", filter_ruleset::match_type::substring, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 0); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Enable rule_C for RULESET_2 without exact_match */ r->enable("_C", filter_ruleset::match_type::substring, RULESET_2); ASSERT_EQ(r->enabled_count(RULESET_0), 0); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 1); /* Disable rule_C for RULESET_2 without exact_match */ r->disable("_C", filter_ruleset::match_type::substring, RULESET_2); ASSERT_EQ(r->enabled_count(RULESET_0), 0); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Enable all rules for RULESET_0 with wildcard */ r->enable("*", filter_ruleset::match_type::wildcard, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 3); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Disable rule C for RULESET_0 with wildcard */ r->disable("*C*", filter_ruleset::match_type::wildcard, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 2); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Disable all rules for RULESET_0 with wildcard */ r->disable("*_*", filter_ruleset::match_type::wildcard, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 0); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); } TEST(Ruleset, enable_disable_rules_using_tags) { sinsp inspector; sinsp_filter_check_list filterlist; auto f = create_factory(&inspector, filterlist); auto r = create_ruleset(f); auto ast = create_ast(f); auto filter = create_filter(f, ast.get()); falco_rule rule_A = {}; rule_A.name = "rule_A"; rule_A.source = falco_common::syscall_source; rule_A.tags = {"first_rule_A_tag", "second_rule_A_tag", "common_tag"}; falco_rule rule_B = {}; rule_B.name = "rule_B"; rule_B.source = falco_common::syscall_source; rule_B.tags = {"first_rule_B_tag", "second_rule_B_tag", "common_tag"}; r->add(rule_A, filter, ast); r->add(rule_B, filter, ast); /* Enable `rule_A` for RULESET_0 using its first tag */ r->enable_tags({"first_rule_A_tag"}, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 1); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Disable `rule_A` for RULESET_1 using its first tag, this should have no effect */ r->disable_tags({"first_rule_A_tag"}, RULESET_1); ASSERT_EQ(r->enabled_count(RULESET_0), 1); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Enable a not existing rule for RULESET_0, this should have no effect */ r->enable_tags({""}, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 1); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); /* Enable all rules for RULESET_2 */ r->enable_tags({"common_tag"}, RULESET_2); ASSERT_EQ(r->enabled_count(RULESET_0), 1); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 2); /* Disable `rule_A` for RULESET_0 using its second tag * Note that we have previously enabled it using the first tag, * so here we are using a different tag of the rule t disable it! */ r->disable_tags({"second_rule_A_tag"}, RULESET_0); ASSERT_EQ(r->enabled_count(RULESET_0), 0); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 2); /* Disable all rules for RULESET_2 */ r->disable_tags({"common_tag"}, RULESET_2); ASSERT_EQ(r->enabled_count(RULESET_0), 0); ASSERT_EQ(r->enabled_count(RULESET_1), 0); ASSERT_EQ(r->enabled_count(RULESET_2), 0); } ================================================ FILE: unit_tests/falco/app/actions/app_action_helpers.h ================================================ // 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. */ #pragma once #include #include #include #define EXPECT_ACTION_OK(r) \ { \ auto result = r; \ EXPECT_TRUE(result.success); \ EXPECT_TRUE(result.proceed); \ EXPECT_EQ(result.errstr, ""); \ } #define EXPECT_ACTION_FAIL(r) \ { \ auto result = r; \ EXPECT_FALSE(result.success); \ EXPECT_FALSE(result.proceed); \ EXPECT_NE(result.errstr, ""); \ } ================================================ FILE: unit_tests/falco/app/actions/test_configure_interesting_sets.cpp ================================================ // 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 "../../../test_falco_engine.h" #include #include #include "app_action_helpers.h" #define ASSERT_NAMES_EQ(a, b) \ { \ EXPECT_EQ(_order(a).size(), _order(b).size()); \ ASSERT_EQ(_order(a), _order(b)); \ } #define ASSERT_NAMES_CONTAIN(a, b) \ { ASSERT_NAMES_EQ(unordered_set_intersection(a, b), b); } #define ASSERT_NAMES_NOCONTAIN(a, b) \ { ASSERT_NAMES_EQ(unordered_set_intersection(a, b), strset_t({})); } using strset_t = std::unordered_set; static std::set _order(const strset_t& s) { return std::set(s.begin(), s.end()); } static std::string s_sample_ruleset = "sample-ruleset"; static std::string s_sample_source = falco_common::syscall_source; static strset_t s_sample_filters = { "evt.type=connect or evt.type=accept or evt.type=accept4 or evt.type=umount2", "evt.type in (open, ptrace, mmap, execve, read, container)", "evt.type in (open, execve, mprotect) and not evt.type=mprotect"}; static strset_t s_sample_generic_filters = {"evt.type=syncfs or evt.type=fanotify_init"}; static strset_t s_sample_nonsyscall_filters = { "evt.type in (procexit, switch, pluginevent, container)"}; static std::string ruleset_from_filters(const strset_t& filters) { std::string dummy_rules; falco::load_result::rules_contents_t content = {{"dummy_rules.yaml", dummy_rules}}; int n_rules = 0; for(const auto& f : filters) { n_rules++; dummy_rules += "- rule: Dummy Rule " + std::to_string(n_rules) + "\n" + " output: Dummy Output " + std::to_string(n_rules) + "\n" + " condition: " + f + "\n" + " desc: Dummy Desc " + std::to_string(n_rules) + "\n" + " priority: CRITICAL\n\n"; } return dummy_rules; } TEST_F(test_falco_engine, engine_codes_syscalls_set) { load_rules(ruleset_from_filters(s_sample_filters), "dummy_ruleset.yaml"); auto enabled_count = m_engine->num_rules_for_ruleset(s_sample_ruleset); ASSERT_EQ(enabled_count, s_sample_filters.size()); // test if event code names were extracted from each rule in test ruleset. auto rules_event_set = m_engine->event_codes_for_ruleset(s_sample_source); auto rules_event_names = libsinsp::events::event_set_to_names(rules_event_set); ASSERT_NAMES_EQ(rules_event_names, strset_t({"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read", "container", "asyncevent"})); // test if sc code names were extracted from each rule in test ruleset. // note, this is not supposed to contain "container", as that's an event // not mapped through the ppm_sc_code enumerative. auto rules_sc_set = m_engine->sc_codes_for_ruleset(s_sample_source); auto rules_sc_names = libsinsp::events::sc_set_to_event_names(rules_sc_set); ASSERT_NAMES_EQ(rules_sc_names, strset_t({"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read"})); } TEST_F(test_falco_engine, preconditions_postconditions) { load_rules(ruleset_from_filters(s_sample_filters), "dummy_ruleset.yaml"); falco::app::state s1; s1.engine = nullptr; s1.config = std::make_shared(); auto result = falco::app::actions::configure_interesting_sets(s1); ASSERT_FALSE(result.success); ASSERT_NE(result.errstr, ""); s1.engine = m_engine; s1.config = nullptr; result = falco::app::actions::configure_interesting_sets(s1); ASSERT_FALSE(result.success); ASSERT_NE(result.errstr, ""); s1.config = std::make_shared(); result = falco::app::actions::configure_interesting_sets(s1); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); auto prev_selection_size = s1.selected_sc_set.size(); result = falco::app::actions::configure_interesting_sets(s1); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); ASSERT_EQ(prev_selection_size, s1.selected_sc_set.size()); } TEST_F(test_falco_engine, engine_codes_nonsyscalls_set) { auto filters = s_sample_filters; filters.insert(s_sample_generic_filters.begin(), s_sample_generic_filters.end()); filters.insert(s_sample_nonsyscall_filters.begin(), s_sample_nonsyscall_filters.end()); load_rules(ruleset_from_filters(filters), "dummy_ruleset.yaml"); auto enabled_count = m_engine->num_rules_for_ruleset(s_sample_ruleset); ASSERT_EQ(enabled_count, filters.size()); auto rules_event_set = m_engine->event_codes_for_ruleset(s_sample_source); auto rules_event_names = libsinsp::events::event_set_to_names(rules_event_set); // note: including even one generic event will cause PPME_GENERIC_E to be // included in the ruleset's event codes. As such, when translating to names, // PPME_GENERIC_E will cause all names of generic events to be added! // This is a good example of information loss from ppm_event_code <-> ppm_sc_code. auto generic_names = libsinsp::events::event_set_to_names({ppm_event_code::PPME_GENERIC_E}); auto expected_names = strset_t({"connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read", "container", // ruleset "procexit", "switch", "pluginevent", "asyncevent"}); // from non-syscall event filters expected_names.insert(generic_names.begin(), generic_names.end()); ASSERT_NAMES_EQ(rules_event_names, expected_names); auto rules_sc_set = m_engine->sc_codes_for_ruleset(s_sample_source); auto rules_sc_names = libsinsp::events::sc_set_to_event_names(rules_sc_set); ASSERT_NAMES_EQ(rules_sc_names, strset_t({ "connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read", "procexit", "switch", "syncfs", "fanotify_init", // from generic event filters })); } TEST_F(test_falco_engine, selection_not_allevents) { load_rules(ruleset_from_filters(s_sample_filters), "dummy_ruleset.yaml"); falco::app::state s2; // run app action with fake engine and without the `-A` option s2.engine = m_engine; s2.config->m_base_syscalls_all = false; auto result = falco::app::actions::configure_interesting_sets(s2); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); // todo(jasondellaluce): once we have deeper control on falco's outputs, // also check if a warning has been printed in stderr // check that the final selected set is the one expected ASSERT_GT(s2.selected_sc_set.size(), 1); auto selected_sc_names = libsinsp::events::sc_set_to_event_names(s2.selected_sc_set); auto expected_sc_names = strset_t({ // note: we expect the "read" syscall to have been erased "connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", // from ruleset "clone", "clone3", "fork", "vfork", // from sinsp state set (spawned_process) "socket", "bind", "close" // from sinsp state set (network, files) }); ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names); // check that all IO syscalls have been erased from the selection auto ignored_set = falco::app::ignored_sc_set(); auto erased_sc_names = libsinsp::events::sc_set_to_event_names(ignored_set); ASSERT_NAMES_NOCONTAIN(selected_sc_names, erased_sc_names); // check that final selected set is exactly sinsp state + ruleset auto rule_set = s2.engine->sc_codes_for_ruleset(s_sample_source, s_sample_ruleset); auto state_set = libsinsp::events::sinsp_state_sc_set(); for(const auto& erased : ignored_set) { rule_set.remove(erased); state_set.remove(erased); } auto union_set = state_set.merge(rule_set); auto inter_set = state_set.intersect(rule_set); EXPECT_EQ(s2.selected_sc_set.size(), state_set.size() + rule_set.size() - inter_set.size()); ASSERT_EQ(s2.selected_sc_set, union_set); } TEST_F(test_falco_engine, selection_allevents) { load_rules(ruleset_from_filters(s_sample_filters), "dummy_ruleset.yaml"); falco::app::state s3; // run app action with fake engine and with the `-A` option s3.engine = m_engine; s3.config->m_base_syscalls_all = true; auto result = falco::app::actions::configure_interesting_sets(s3); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); // todo(jasondellaluce): once we have deeper control on falco's outputs, // also check if a warning has not been printed in stderr // check that the final selected set is the one expected ASSERT_GT(s3.selected_sc_set.size(), 1); auto selected_sc_names = libsinsp::events::sc_set_to_event_names(s3.selected_sc_set); auto expected_sc_names = strset_t({ // note: we expect the "read" syscall to not be erased "connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read", // from ruleset "clone", "clone3", "fork", "vfork", // from sinsp state set (spawned_process) "socket", "bind", "close" // from sinsp state set (network, files) }); ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names); // check that final selected set is exactly sinsp state + ruleset auto rule_set = s3.engine->sc_codes_for_ruleset(s_sample_source, s_sample_ruleset); auto state_set = libsinsp::events::sinsp_state_sc_set(); auto union_set = state_set.merge(rule_set); auto inter_set = state_set.intersect(rule_set); EXPECT_EQ(s3.selected_sc_set.size(), state_set.size() + rule_set.size() - inter_set.size()); ASSERT_EQ(s3.selected_sc_set, union_set); } TEST_F(test_falco_engine, selection_generic_evts) { falco::app::state s4; // run app action with fake engine and without the `m_base_syscalls_all` option auto filters = s_sample_filters; filters.insert(s_sample_generic_filters.begin(), s_sample_generic_filters.end()); load_rules(ruleset_from_filters(filters), "dummy_ruleset.yaml"); s4.engine = m_engine; auto result = falco::app::actions::configure_interesting_sets(s4); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); // check that the final selected set is the one expected ASSERT_GT(s4.selected_sc_set.size(), 1); auto selected_sc_names = libsinsp::events::sc_set_to_event_names(s4.selected_sc_set); auto expected_sc_names = strset_t({ // note: we expect the "read" syscall to not be erased "connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", // from ruleset "syncfs", "fanotify_init", // from ruleset (generic events) "clone", "clone3", "fork", "vfork", // from sinsp state set (spawned_process) "socket", "bind", "close" // from sinsp state set (network, files) }); ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names); auto unexpected_sc_names = libsinsp::events::sc_set_to_event_names(falco::app::ignored_sc_set()); ASSERT_NAMES_NOCONTAIN(selected_sc_names, unexpected_sc_names); } // expected combinations precedence: // - final selected set is the union of rules events and base events // (either default or custom positive set) // - events in the custom negative set are removed from the selected set // - if `-A` is not set, events from the IO set are removed from the selected set TEST_F(test_falco_engine, selection_custom_base_set) { load_rules(ruleset_from_filters(s_sample_filters), "dummy_ruleset.yaml"); falco::app::state s5; s5.config->m_base_syscalls_all = true; s5.engine = m_engine; auto default_base_set = libsinsp::events::sinsp_state_sc_set(); // non-empty custom base set (both positive and negative) s5.config->m_base_syscalls_repair = false; s5.config->m_base_syscalls_custom_set = {"syncfs", "!accept"}; auto result = falco::app::actions::configure_interesting_sets(s5); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); auto selected_sc_names = libsinsp::events::sc_set_to_event_names(s5.selected_sc_set); auto expected_sc_names = strset_t({// note: `syncfs` has been added due to the custom base set, and `accept` // has been remove due to the negative base set. // note: `read` is not ignored due to the "-A" option being set. // note: `accept` is not included even though it is matched by the rules, // which means that the custom negation base set has precedence over the // final selection set as a whole // note(jasondellaluce): "accept4" should be added, however old versions // of the ACCEPT4 event are actually named "accept" in the event table "connect", "umount2", "open", "ptrace", "mmap", "execve", "read", "syncfs", "procexit"}); ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names); // non-empty custom base set (both positive and negative with collision) s5.config->m_base_syscalls_repair = false; s5.config->m_base_syscalls_custom_set = {"syncfs", "accept", "!accept"}; result = falco::app::actions::configure_interesting_sets(s5); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); selected_sc_names = libsinsp::events::sc_set_to_event_names(s5.selected_sc_set); // note: in case of collision, negation has priority, so the expected // names are the same as the case above ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names); // non-empty custom base set (only positive) s5.config->m_base_syscalls_custom_set = {"syncfs"}; result = falco::app::actions::configure_interesting_sets(s5); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); selected_sc_names = libsinsp::events::sc_set_to_event_names(s5.selected_sc_set); expected_sc_names = strset_t({// note: accept is not negated anymore "connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "read", "syncfs", "procexit"}); ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names); // non-empty custom base set (only negative) s5.config->m_base_syscalls_custom_set = {"!accept"}; result = falco::app::actions::configure_interesting_sets(s5); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); selected_sc_names = libsinsp::events::sc_set_to_event_names(s5.selected_sc_set); expected_sc_names = unordered_set_union( libsinsp::events::sc_set_to_event_names(default_base_set), strset_t({"connect", "umount2", "open", "ptrace", "mmap", "execve", "read"})); expected_sc_names.erase("accept"); // note(jasondellaluce): "accept4" should be included, however old versions // of the ACCEPT4 event are actually named "accept" in the event table expected_sc_names.erase("accept4"); ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names); // non-empty custom base set (positive, disable all syscalls) s5.config->m_base_syscalls_all = false; s5.config->m_base_syscalls_custom_set = {"read"}; result = falco::app::actions::configure_interesting_sets(s5); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); selected_sc_names = libsinsp::events::sc_set_to_event_names(s5.selected_sc_set); expected_sc_names = strset_t({// note: read is both part of the custom base set and the rules // set, but we expect the unset -A option to take precedence "connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "procexit"}); ASSERT_NAMES_EQ(selected_sc_names, expected_sc_names); auto unexpected_sc_names = libsinsp::events::sc_set_to_event_names(falco::app::ignored_sc_set()); ASSERT_NAMES_NOCONTAIN(selected_sc_names, unexpected_sc_names); } TEST_F(test_falco_engine, selection_custom_base_set_repair) { load_rules(ruleset_from_filters(s_sample_filters), "dummy_ruleset.yaml"); falco::app::state s6; // run app action with fake engine and without the `all syscalls` option s6.config->m_base_syscalls_all = false; s6.engine = m_engine; // note: here we use file syscalls (e.g. open, openat) and have a custom // positive set, so we expect syscalls such as "close" to be selected as // repaired. Also, given that we use some network syscalls, we expect "bind" // to be selected event if we negate it, because repairment should have // take precedence. s6.config->m_base_syscalls_custom_set = {"openat", "!bind"}; s6.config->m_base_syscalls_repair = true; auto result = falco::app::actions::configure_interesting_sets(s6); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); auto selected_sc_names = libsinsp::events::sc_set_to_event_names(s6.selected_sc_set); auto expected_sc_names = strset_t({// note: expecting syscalls from mock rules and // `sinsp_repair_state_sc_set` enforced syscalls "connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "procexit", "bind", "socket", "clone3", "close", "setuid"}); ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names); auto unexpected_sc_names = libsinsp::events::sc_set_to_event_names(falco::app::ignored_sc_set()); ASSERT_NAMES_NOCONTAIN(selected_sc_names, unexpected_sc_names); } TEST_F(test_falco_engine, selection_empty_custom_base_set_repair) { load_rules(ruleset_from_filters(s_sample_filters), "dummy_ruleset.yaml"); falco::app::state s7; // run app action with fake engine and with the `all syscalls` option s7.config->m_base_syscalls_all = true; s7.engine = m_engine; // simulate empty custom set but repair option set. s7.config->m_base_syscalls_custom_set = {}; s7.config->m_base_syscalls_repair = true; auto result = falco::app::actions::configure_interesting_sets(s7); auto s7_rules_set = s7.engine->sc_codes_for_ruleset(s_sample_source, s_sample_ruleset); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); auto selected_sc_names = libsinsp::events::sc_set_to_event_names(s7.selected_sc_set); auto expected_sc_names = strset_t({// note: expecting syscalls from mock rules and // `sinsp_repair_state_sc_set` enforced syscalls "connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "procexit", "bind", "socket", "clone3", "close", "setuid"}); ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names); auto s7_state_set = libsinsp::events::sinsp_repair_state_sc_set(s7_rules_set); ASSERT_EQ(s7.selected_sc_set, s7_state_set); ASSERT_EQ(s7.selected_sc_set.size(), s7_state_set.size()); } TEST_F(test_falco_engine, selection_base_syscalls_all) { load_rules(ruleset_from_filters(s_sample_filters), "dummy_ruleset.yaml"); falco::app::state s7; s7.engine = m_engine; // simulate empty custom set but repair option set. s7.config->m_base_syscalls_custom_set = {}; s7.config->m_base_syscalls_repair = true; s7.config->m_base_syscalls_all = true; auto result = falco::app::actions::configure_interesting_sets(s7); auto s7_rules_set = s7.engine->sc_codes_for_ruleset(s_sample_source, s_sample_ruleset); ASSERT_TRUE(result.success); ASSERT_EQ(result.errstr, ""); auto selected_sc_names = libsinsp::events::sc_set_to_event_names(s7.selected_sc_set); auto expected_sc_names = strset_t({// note: expecting syscalls from mock rules and // `sinsp_repair_state_sc_set` enforced syscalls "connect", "accept", "accept4", "umount2", "open", "ptrace", "mmap", "execve", "procexit", "bind", "socket", "clone3", "close", "setuid"}); ASSERT_NAMES_CONTAIN(selected_sc_names, expected_sc_names); auto s7_state_set = libsinsp::events::sinsp_repair_state_sc_set(s7_rules_set); ASSERT_EQ(s7.selected_sc_set, s7_state_set); ASSERT_EQ(s7.selected_sc_set.size(), s7_state_set.size()); } TEST(ConfigureInterestingSets, ignored_set_expected_size) { // unit test fence to make sure we don't have unexpected regressions // in the ignored set, to be updated in the future ASSERT_EQ(falco::app::ignored_sc_set().size(), 12); // we don't expect to ignore any syscall in the default base set ASSERT_EQ(falco::app::ignored_sc_set().intersect(libsinsp::events::sinsp_state_sc_set()).size(), 0); } ================================================ FILE: unit_tests/falco/app/actions/test_configure_syscall_buffer_num.cpp ================================================ // 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 "app_action_helpers.h" TEST(ActionConfigureSyscallBufferNum, variable_number_of_CPUs) { auto action = falco::app::actions::configure_syscall_buffer_num; ssize_t online_cpus = sysconf(_SC_NPROCESSORS_ONLN); if(online_cpus <= 0) { FAIL() << "cannot get the number of online CPUs from the system\n"; } // not modern ebpf engine, we do nothing { falco::app::state s; s.config->m_engine_mode = engine_kind_t::MODERN_EBPF; EXPECT_ACTION_OK(action(s)); } // modern ebpf engine, with an invalid number of CPUs // default `m_cpus_for_each_syscall_buffer` to online CPU number { falco::app::state s; s.config->m_engine_mode = engine_kind_t::MODERN_EBPF; s.config->m_modern_ebpf.m_cpus_for_each_buffer = online_cpus + 1; EXPECT_ACTION_OK(action(s)); EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, online_cpus); } // modern ebpf engine, with a valid number of CPUs // we don't modify `m_cpus_for_each_syscall_buffer` { falco::app::state s; s.config->m_engine_mode = engine_kind_t::MODERN_EBPF; s.config->m_modern_ebpf.m_cpus_for_each_buffer = online_cpus - 1; EXPECT_ACTION_OK(action(s)); EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, online_cpus - 1); } } ================================================ FILE: unit_tests/falco/app/actions/test_load_config.cpp ================================================ // 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 "app_action_helpers.h" #include "falco_test_var.h" #ifndef __EMSCRIPTEN__ TEST(ActionLoadConfig, check_kmod_engine_config) { falco::app::state s = {}; s.options.conf_filename = TEST_ENGINE_KMOD_CONFIG; EXPECT_ACTION_OK(falco::app::actions::load_config(s)); // Check that the engine is the kmod EXPECT_TRUE(s.config->m_engine_mode == engine_kind_t::KMOD); // Check that kmod params are the ones specified in the config EXPECT_EQ(s.config->m_kmod.m_buf_size_preset, 2); EXPECT_FALSE(s.config->m_kmod.m_drop_failed_exit); EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, 0); EXPECT_EQ(s.config->m_modern_ebpf.m_buf_size_preset, 0); EXPECT_FALSE(s.config->m_modern_ebpf.m_drop_failed_exit); EXPECT_TRUE(s.config->m_replay.m_capture_file.empty()); } TEST(ActionLoadConfig, check_modern_engine_config) { falco::app::state s = {}; s.options.conf_filename = TEST_ENGINE_MODERN_CONFIG; EXPECT_ACTION_OK(falco::app::actions::load_config(s)); // Check that the engine is the modern ebpf EXPECT_TRUE(s.config->m_engine_mode == engine_kind_t::MODERN_EBPF); // Check that modern ebpf params are the ones specified in the config EXPECT_EQ(s.config->m_modern_ebpf.m_cpus_for_each_buffer, 1); EXPECT_EQ(s.config->m_modern_ebpf.m_buf_size_preset, 4); EXPECT_TRUE(s.config->m_modern_ebpf.m_drop_failed_exit); // Check that all other engine params are empty EXPECT_EQ(s.config->m_kmod.m_buf_size_preset, 0); EXPECT_FALSE(s.config->m_kmod.m_drop_failed_exit); EXPECT_TRUE(s.config->m_replay.m_capture_file.empty()); } #endif ================================================ FILE: unit_tests/falco/app/actions/test_select_event_sources.cpp ================================================ // 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 "app_action_helpers.h" TEST(ActionSelectEventSources, pre_post_conditions) { auto action = falco::app::actions::select_event_sources; // requires sources to be already loaded { falco::app::state s; EXPECT_ACTION_FAIL(action(s)); } // ignore source selection in capture mode { falco::app::state s; s.config->m_engine_mode = engine_kind_t::REPLAY; EXPECT_TRUE(s.is_capture_mode()); EXPECT_ACTION_OK(action(s)); } // enable all loaded sources by default, even with multiple calls { falco::app::state s; s.loaded_sources = {"syscall", "some_source"}; EXPECT_ACTION_OK(action(s)); EXPECT_EQ(s.loaded_sources.size(), s.enabled_sources.size()); for(const auto& v : s.loaded_sources) { ASSERT_TRUE(s.enabled_sources.find(v) != s.enabled_sources.end()); } s.loaded_sources.push_back("another_source"); EXPECT_ACTION_OK(action(s)); EXPECT_EQ(s.loaded_sources.size(), s.enabled_sources.size()); for(const auto& v : s.loaded_sources) { ASSERT_TRUE(s.enabled_sources.find(v) != s.enabled_sources.end()); } } // enable only selected sources { falco::app::state s; s.loaded_sources = {"syscall", "some_source"}; s.options.enable_sources = {"syscall"}; EXPECT_ACTION_OK(action(s)); EXPECT_EQ(s.enabled_sources.size(), 1); EXPECT_EQ(*s.enabled_sources.begin(), "syscall"); } // enable all loaded sources expect the disabled ones { falco::app::state s; s.loaded_sources = {"syscall", "some_source"}; s.options.disable_sources = {"syscall"}; EXPECT_ACTION_OK(action(s)); EXPECT_EQ(s.enabled_sources.size(), 1); EXPECT_EQ(*s.enabled_sources.begin(), "some_source"); } // enable unknown sources { falco::app::state s; s.loaded_sources = {"syscall", "some_source"}; s.options.enable_sources = {"some_other_source"}; EXPECT_ACTION_FAIL(action(s)); } // disable unknown sources { falco::app::state s; s.loaded_sources = {"syscall", "some_source"}; s.options.disable_sources = {"some_other_source"}; EXPECT_ACTION_FAIL(action(s)); } // mix enable and disable sources options { falco::app::state s; s.loaded_sources = {"syscall", "some_source"}; s.options.disable_sources = {"syscall"}; s.options.enable_sources = {"syscall"}; EXPECT_ACTION_FAIL(action(s)); } } ================================================ FILE: unit_tests/falco/test_atomic_signal_handler.cpp ================================================ // 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 #include #include #include #include #include #include #include TEST(AtomicSignalHandler, lock_free_implementation) { ASSERT_TRUE(falco::atomic_signal_handler().is_lock_free()); } TEST(AtomicSignalHandler, handle_once_wait_consistency) { constexpr const auto thread_num = 10; constexpr const std::chrono::seconds thread_wait_sec{2}; constexpr const std::chrono::seconds handler_wait_sec{1}; // have a shared signal handler falco::atomic_signal_handler handler; // launch a bunch of threads all syncing on the same handler struct task_result_t { bool handled; std::chrono::seconds duration_secs; }; std::vector> futures; for(int i = 0; i < thread_num; i++) { futures.emplace_back(std::async(std::launch::async, [&handler, thread_wait_sec]() { auto start = std::chrono::high_resolution_clock::now(); task_result_t res; res.handled = false; while(!handler.handled()) { if(handler.triggered()) { res.handled = handler.handle( [thread_wait_sec]() { std::this_thread::sleep_for(thread_wait_sec); }); } } auto diff = std::chrono::high_resolution_clock::now() - start; res.duration_secs = std::chrono::duration_cast(diff); return res; })); } // wait a bit, then trigger the signal handler from the main thread auto total_handled = 0; auto start = std::chrono::high_resolution_clock::now(); std::this_thread::sleep_for(handler_wait_sec); handler.trigger(); for(int i = 0; i < thread_num; i++) { // wait for all threads to finish and get the results from the futures auto res = futures[i].get(); if(res.handled) { total_handled++; } ASSERT_GE(res.duration_secs, thread_wait_sec); } // check that the total time is consistent with the expectations auto diff = std::chrono::high_resolution_clock::now() - start; auto secs = std::chrono::duration_cast(diff); ASSERT_GE(secs, thread_wait_sec + handler_wait_sec); // check that only one thread handled the signal ASSERT_EQ(total_handled, 1); } TEST(AtomicSignalHandler, handle_and_reset) { auto do_nothing = [] {}; falco::atomic_signal_handler handler; ASSERT_FALSE(handler.triggered()); ASSERT_FALSE(handler.handled()); ASSERT_FALSE(handler.handle(do_nothing)); handler.trigger(); ASSERT_TRUE(handler.triggered()); ASSERT_FALSE(handler.handled()); ASSERT_TRUE(handler.handle(do_nothing)); ASSERT_TRUE(handler.triggered()); ASSERT_TRUE(handler.handled()); ASSERT_FALSE(handler.handle(do_nothing)); handler.trigger(); ASSERT_TRUE(handler.triggered()); ASSERT_FALSE(handler.handled()); ASSERT_TRUE(handler.handle(do_nothing)); ASSERT_TRUE(handler.triggered()); ASSERT_TRUE(handler.handled()); ASSERT_FALSE(handler.handle(do_nothing)); handler.reset(); ASSERT_FALSE(handler.triggered()); ASSERT_FALSE(handler.handled()); ASSERT_FALSE(handler.handle(do_nothing)); } ================================================ FILE: unit_tests/falco/test_capture.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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 #include #include TEST(Capture, generate_scap_file_path_realistic_scenario) { // Simulate a realistic timestamp (nanoseconds since epoch) uint64_t timestamp = 1648178040000000000ULL; // 2022-03-25 04:14:00 CET (03:14:00 UTC) in ns, // birth date of my son Michelangelo :) uint64_t evt_num = 1011; std::string prefix = "/var/log/falco/captures/security_event"; std::string result = falco::app::actions::generate_scap_file_path(prefix, timestamp, evt_num); std::string expected = "/var/log/falco/captures/security_event_01648178040000000000_00000000000000001011.scap"; EXPECT_EQ(result, expected); } TEST(Capture, generate_scap_file_path_lexicographic_ordering) { std::string prefix = "/tmp/test"; // Generate multiple file paths with different timestamps std::string path1 = falco::app::actions::generate_scap_file_path(prefix, 1000, 1); std::string path2 = falco::app::actions::generate_scap_file_path(prefix, 2000, 1); std::string path3 = falco::app::actions::generate_scap_file_path(prefix, 10000, 1); // Verify lexicographic ordering (important for file sorting) EXPECT_LT(path1, path2); EXPECT_LT(path2, path3); // Also test with same timestamp but different event numbers std::string path4 = falco::app::actions::generate_scap_file_path(prefix, 1000, 1); std::string path5 = falco::app::actions::generate_scap_file_path(prefix, 1000, 2); std::string path6 = falco::app::actions::generate_scap_file_path(prefix, 1000, 100); EXPECT_LT(path4, path5); EXPECT_LT(path5, path6); } TEST(Capture, generate_scap_file_path_empty_prefix) { std::string prefix = ""; uint64_t timestamp = 123; uint64_t evt_num = 456; std::string result = falco::app::actions::generate_scap_file_path(prefix, timestamp, evt_num); std::string expected = "_00000000000000000123_00000000000000000456.scap"; EXPECT_EQ(result, expected); } TEST(Capture, capture_config_disabled_by_default) { std::string config_content = R"( plugins: )"; falco_configuration config; config_loaded_res res; ASSERT_NO_THROW(res = config.init_from_content(config_content, {})); // Capture should be disabled by default EXPECT_FALSE(config.m_capture_enabled); EXPECT_EQ(config.m_capture_path_prefix, "/tmp/falco"); EXPECT_EQ(config.m_capture_mode, capture_mode_t::RULES); EXPECT_EQ(config.m_capture_default_duration_ns, 5000 * 1000000LL); // 5 seconds in ns } TEST(Capture, capture_config_enabled_rules_mode) { std::string config_content = R"( capture: enabled: true path_prefix: /var/log/captures/falco mode: rules default_duration: 10000 )"; falco_configuration config; config_loaded_res res; ASSERT_NO_THROW(res = config.init_from_content(config_content, {})); EXPECT_TRUE(config.m_capture_enabled); EXPECT_EQ(config.m_capture_path_prefix, "/var/log/captures/falco"); EXPECT_EQ(config.m_capture_mode, capture_mode_t::RULES); EXPECT_EQ(config.m_capture_default_duration_ns, 10000 * 1000000LL); // 10 seconds in ns } TEST(Capture, capture_config_enabled_all_rules_mode) { std::string config_content = R"( capture: enabled: true path_prefix: /tmp/debug/falco mode: all_rules default_duration: 30000 )"; falco_configuration config; config_loaded_res res; ASSERT_NO_THROW(res = config.init_from_content(config_content, {})); EXPECT_TRUE(config.m_capture_enabled); EXPECT_EQ(config.m_capture_path_prefix, "/tmp/debug/falco"); EXPECT_EQ(config.m_capture_mode, capture_mode_t::ALL_RULES); EXPECT_EQ(config.m_capture_default_duration_ns, 30000 * 1000000LL); // 30 seconds in ns } TEST(Capture, capture_config_invalid_mode) { std::string config_content = R"( capture: enabled: true mode: invalid_mode )"; falco_configuration config; config_loaded_res res; // Should throw an exception for invalid mode EXPECT_THROW(res = config.init_from_content(config_content, {}), std::logic_error); } ================================================ FILE: unit_tests/falco/test_configs/engine_kmod_config.yaml ================================================ # 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. # ################ # Falco engine # ################ engine: kind: kmod kmod: buf_size_preset: 2 drop_failed_exit: false modern_ebpf: cpus_for_each_buffer: 2 buf_size_preset: 4 drop_failed_exit: false replay: capture_file: /path/to/file.scap ================================================ FILE: unit_tests/falco/test_configs/engine_modern_config.yaml ================================================ # 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. # ################ # Falco engine # ################ engine: kind: modern_ebpf kmod: buf_size_preset: 1 drop_failed_exit: true modern_ebpf: cpus_for_each_buffer: 1 # missing `buf_size_preset` should be defaulted drop_failed_exit: true replay: capture_file: /path/to/file.scap ================================================ FILE: unit_tests/falco/test_configuration.cpp ================================================ // 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 #include static std::string sample_yaml = "base_value:\n" " id: 1\n" " name: 'sample_name'\n" " subvalue:\n" " subvalue2:\n" " boolean: true\n" "base_value_2:\n" " sample_list:\n" " - elem1\n" " - elem2\n" " - elem3\n"; TEST(Configuration, configuration_exceptions) { yaml_helper conf; /* Broken YAML */ std::string sample_broken_yaml = sample_yaml + " / bad_symbol"; EXPECT_ANY_THROW(conf.load_from_string(sample_broken_yaml)); /* Right YAML */ EXPECT_NO_THROW(conf.load_from_string(sample_yaml)); } TEST(Configuration, configuration_reload) { yaml_helper conf; /* Clear and reload config */ conf.load_from_string(sample_yaml); ASSERT_TRUE(conf.is_defined("base_value")); conf.clear(); ASSERT_FALSE(conf.is_defined("base_value")); conf.load_from_string(sample_yaml); ASSERT_TRUE(conf.is_defined("base_value")); } TEST(Configuration, read_yaml_fields) { yaml_helper conf; conf.load_from_string(sample_yaml); /* is_defined */ ASSERT_TRUE(conf.is_defined("base_value")); ASSERT_TRUE(conf.is_defined("base_value_2")); ASSERT_FALSE(conf.is_defined("unknown_base_value")); /* get some fields */ ASSERT_EQ(conf.get_scalar("base_value.id", -1), 1); ASSERT_STREQ(conf.get_scalar("base_value.name", "none").c_str(), "sample_name"); ASSERT_EQ(conf.get_scalar("base_value.subvalue.subvalue2.boolean", false), true); /* get list field elements */ ASSERT_STREQ(conf.get_scalar("base_value_2.sample_list[0]", "none").c_str(), "elem1"); ASSERT_STREQ(conf.get_scalar("base_value_2.sample_list[1]", "none").c_str(), "elem2"); ASSERT_STREQ(conf.get_scalar("base_value_2.sample_list[2]", "none").c_str(), "elem3"); /* get sequence */ std::vector seq; conf.get_sequence(seq, "base_value_2.sample_list"); ASSERT_EQ(seq.size(), 3); ASSERT_STREQ(seq[0].c_str(), "elem1"); ASSERT_STREQ(seq[1].c_str(), "elem2"); ASSERT_STREQ(seq[2].c_str(), "elem3"); } TEST(Configuration, modify_yaml_fields) { std::string key = "base_value.subvalue.subvalue2.boolean"; yaml_helper conf; /* Get original value */ conf.load_from_string(sample_yaml); ASSERT_EQ(conf.get_scalar(key, false), true); /* Modify the original value */ conf.set_scalar(key, false); ASSERT_EQ(conf.get_scalar(key, true), false); /* Modify it again */ conf.set_scalar(key, true); ASSERT_EQ(conf.get_scalar(key, false), true); } TEST(Configuration, configuration_webserver_ip) { falco_configuration falco_config; std::vector valid_addresses = {"127.0.0.1", "1.127.0.1", "1.1.127.1", "1.1.1.127", "::", "::1", "1200:0000:AB00:1234:0000:2552:7777:1313", "1200::AB00:1234:0000:2552:7777:1313", "1200:0000:AB00:1234::2552:7777:1313", "21DA:D3:0:2F3B:2AA:FF:FE28:9C5A", "FE80:0000:0000:0000:0202:B3FF:FE1E:8329", "0.0.0.0", "9.255.255.255", "11.0.0.0", "126.255.255.255", "129.0.0.0", "169.253.255.255", "169.255.0.0", "172.15.255.255", "172.32.0.0", "191.0.1.255", "192.88.98.255", "192.88.100.0", "192.167.255.255", "192.169.0.0", "198.17.255.255", "223.255.255.255"}; for(const std::string &address : valid_addresses) { std::string option = "webserver.listen_address="; option.append(address); std::vector cmdline_config_options; cmdline_config_options.push_back(option); EXPECT_NO_THROW(falco_config.init_from_content("", cmdline_config_options)); ASSERT_EQ(falco_config.m_webserver_config.m_listen_address, address); } std::vector invalid_addresses = { "327.0.0.1", "1.327.0.1", "1.1.327.1", "1.1.1.327", "12 7.0.0.1", "127. 0.0.1", "127.0. 0.1", "127.0.0. 1", "!27.0.0.1", "1200: 0000:AB00:1234:0000:2552:7777:1313", "1200:0000: AB00:1234:0000:2552:7777:1313", "1200:0000:AB00: 1234:0000:2552:7777:1313", "1200:0000:AB00:1234: 0000:2552:7777:1313", "1200:0000:AB00:1234:0000: 2552:7777:1313", "1200:0000:AB00:1234:0000:2552: 7777:1313", "1200:0000:AB00:1234:0000:2552:7777: 1313", "1200:0000:AB00:1234:0000:2552:7777:131G", "1200:0000:AB00:1234:0000:2552:77Z7:1313", "1200:0000:AB00:1234:0000:2G52:7777:1313", "1200:0000:AB00:1234:0O00:2552:7777:1313", "1200:0000:AB00:H234:0000:2552:7777:1313", "1200:0000:IB00:1234:0000:2552:7777:1313", "1200:0O00:AB00:1234:0000:2552:7777:1313", "12O0:0000:AB00:1234:0000:2552:7777:1313", }; for(const std::string &address : invalid_addresses) { std::string option = "webserver.listen_address="; option.append(address); std::vector cmdline_config_options; cmdline_config_options.push_back(option); EXPECT_ANY_THROW(falco_config.init_from_content("", cmdline_config_options)); } } ================================================ FILE: unit_tests/falco/test_configuration_config_files.cpp ================================================ // 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 #include TEST(Configuration, configuration_config_files_secondary_fail) { /* Test that a secondary config file is not able to include anything, triggering an exception. */ const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - conf_2.yaml\n" " - conf_3.yaml\n" "foo: bar\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = yaml_helper::configs_key + ":\n" " - conf_4.yaml\n" "foo2: bar2\n" "base_value_2:\n" " id: 2\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; ASSERT_ANY_THROW(falco_config.init_from_file("main.yaml", cmdline_config_options)); std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); } TEST(Configuration, configuration_config_files_ok) { /* Test that every included config file was correctly parsed */ const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - conf_2.yaml\n" " - conf_3.yaml\n" "foo: bar\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo2: bar2\n" "base_value_2:\n" " id: 2\n"; const std::string conf_yaml_3 = "foo3: bar3\n" "base_value_3:\n" " id: 3\n" " name: foo3\n"; const std::string conf_yaml_4 = "base_value_4:\n" " id: 4\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); outfile.open("conf_3.yaml"); outfile << conf_yaml_3; outfile.close(); outfile.open("conf_4.yaml"); outfile << conf_yaml_4; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3 ASSERT_EQ(res.size(), 3); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar2"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); ASSERT_TRUE(falco_config.m_config.is_defined("foo3")); ASSERT_EQ(falco_config.m_config.get_scalar("foo3", ""), "bar3"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_3.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_3.id", 0), 3); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_3.name")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_3.name", ""), "foo3"); ASSERT_FALSE(falco_config.m_config.is_defined("base_value_4.id")); // conf_4 is not included std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); std::filesystem::remove("conf_3.yaml"); std::filesystem::remove("conf_4.yaml"); } TEST(Configuration, configuration_config_files_relative_main) { /* * Test that relative path are treated as relative to cwd and not to main config folder, * and that absolute includes are ok too. */ const auto temp_main = std::filesystem::temp_directory_path() / "main.yaml"; // So, conf_2 will be looked up in the same folder as main config file, // while conf_3, since is absolute, will be looked up in the absolute path (and found!). const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - conf_2.yaml\n" " - " + std::filesystem::current_path().string() + "/conf_3.yaml\n" "foo: bar\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo2: bar2\n" "base_value_2:\n" " id: 2\n"; const std::string conf_yaml_3 = "foo3: bar3\n" "base_value_3:\n" " id: 3\n" " name: foo3\n"; std::ofstream outfile(temp_main.string()); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); outfile.open("conf_3.yaml"); outfile << conf_yaml_3; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file(temp_main.string(), cmdline_config_options)); // main + conf_2 + conf_3 ASSERT_EQ(res.size(), 3); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar2"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_3.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_3.id", 0), 3); std::filesystem::remove(temp_main.string()); std::filesystem::remove("conf_2.yaml"); std::filesystem::remove("conf_3.yaml"); } TEST(Configuration, configuration_config_files_override) { /* Test that included config files are able to override configs from main file */ const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - conf_2.yaml\n" " - conf_3.yaml\n" "foo: bar\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo2: bar2\n" "base_value_2:\n" " id: 2\n"; const std::string conf_yaml_3 = "base_value:\n" " id: 3\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); outfile.open("conf_3.yaml"); outfile << conf_yaml_3; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3 ASSERT_EQ(res.size(), 3); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 3); // overridden! ASSERT_FALSE(falco_config.m_config.is_defined( "base_value.name")); // no more present since entire `base_value` block was overridden ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar2"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); std::filesystem::remove("conf_3.yaml"); } TEST(Configuration, configuration_config_files_sequence_strategy_default) { const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - conf_2.yaml\n" // default merge-strategy: append " - conf_3.yaml\n" "foo: [ bar ]\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo: [ bar2 ]\n" // append to foo sequence "base_value_2:\n" " id: 2\n"; const std::string conf_yaml_3 = "base_value:\n" // override base_value " id: 3\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); outfile.open("conf_3.yaml"); outfile << conf_yaml_3; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3 ASSERT_EQ(res.size(), 3); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); std::vector foos; auto expected_foos = std::vector{"bar", "bar2"}; ASSERT_NO_THROW(falco_config.m_config.get_sequence>(foos, "foo")); ASSERT_EQ(foos.size(), 2); // 2 elements in `foo` sequence because we appended to it for(size_t i = 0; i < foos.size(); ++i) { EXPECT_EQ(foos[i], expected_foos[i]) << "Vectors foo's and expected_foo's differ at index " << i; } ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 3); // overridden! ASSERT_FALSE(falco_config.m_config.is_defined( "base_value.name")); // no more present since entire `base_value` block was overridden ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); std::filesystem::remove("conf_3.yaml"); } TEST(Configuration, configuration_config_files_sequence_strategy_append) { const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - path: conf_2.yaml\n" " strategy: append\n" " - conf_3.yaml\n" "foo: [ bar ]\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo: [ bar2 ]\n" // append to foo sequence "base_value_2:\n" " id: 2\n"; const std::string conf_yaml_3 = "base_value:\n" // override base_value " id: 3\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); outfile.open("conf_3.yaml"); outfile << conf_yaml_3; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3 ASSERT_EQ(res.size(), 3); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); std::vector foos; auto expected_foos = std::vector{"bar", "bar2"}; ASSERT_NO_THROW(falco_config.m_config.get_sequence>(foos, "foo")); ASSERT_EQ(foos.size(), 2); // 2 elements in `foo` sequence because we appended to it for(size_t i = 0; i < foos.size(); ++i) { EXPECT_EQ(foos[i], expected_foos[i]) << "Vectors foo's and expected_foo's differ at index " << i; } ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 3); // overridden! ASSERT_FALSE(falco_config.m_config.is_defined( "base_value.name")); // no more present since entire `base_value` block was overridden ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); std::filesystem::remove("conf_3.yaml"); } TEST(Configuration, configuration_config_files_sequence_strategy_override) { const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - path: conf_2.yaml\n" " strategy: override\n" " - conf_3.yaml\n" "foo: [ bar ]\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo: [ bar2 ]\n" // override foo sequence "base_value_2:\n" " id: 2\n"; const std::string conf_yaml_3 = "base_value:\n" // override base_value " id: 3\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); outfile.open("conf_3.yaml"); outfile << conf_yaml_3; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3 ASSERT_EQ(res.size(), 3); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); std::vector foos; auto expected_foos = std::vector{"bar2"}; ASSERT_NO_THROW(falco_config.m_config.get_sequence>(foos, "foo")); ASSERT_EQ(foos.size(), 1); // one element in `foo` sequence because we overrode it for(size_t i = 0; i < foos.size(); ++i) { EXPECT_EQ(foos[i], expected_foos[i]) << "Vectors foo's and expected_foo's differ at index " << i; } ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 3); // overridden! ASSERT_FALSE(falco_config.m_config.is_defined( "base_value.name")); // no more present since entire `base_value` block was overridden ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); std::filesystem::remove("conf_3.yaml"); } TEST(Configuration, configuration_config_files_sequence_strategy_addonly) { /* Test that included config files are able to override configs from main file */ const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - path: conf_2.yaml\n" " strategy: add-only\n" " - conf_3.yaml\n" "foo: [ bar ]\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo: [ bar2 ]\n" // ignored: add-only strategy "base_value_2:\n" " id: 2\n"; const std::string conf_yaml_3 = "base_value:\n" // override base_value " id: 3\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); outfile.open("conf_3.yaml"); outfile << conf_yaml_3; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3 ASSERT_EQ(res.size(), 3); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); std::vector foos; auto expected_foos = std::vector{"bar"}; // bar2 is ignored because of merge-strategy: add-only ASSERT_NO_THROW(falco_config.m_config.get_sequence>(foos, "foo")); ASSERT_EQ(foos.size(), 1); // one element in `foo` sequence because we overrode it for(size_t i = 0; i < foos.size(); ++i) { EXPECT_EQ(foos[i], expected_foos[i]) << "Vectors foo's and expected_foo's differ at index " << i; } ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 3); // overridden! ASSERT_FALSE(falco_config.m_config.is_defined( "base_value.name")); // no more present since entire `base_value` block was overridden ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); std::filesystem::remove("conf_3.yaml"); } TEST(Configuration, configuration_config_files_sequence_wrong_strategy) { const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - path: conf_2.yaml\n" " strategy: wrong\n" " - conf_3.yaml\n" "foo: [ bar ]\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo: [ bar2 ]\n" // append to foo sequence "base_value_2:\n" " id: 2\n"; const std::string conf_yaml_3 = "base_value:\n" // override base_value " id: 3\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); outfile.open("conf_3.yaml"); outfile << conf_yaml_3; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main ASSERT_EQ(res.size(), 3); auto validation = res["main.yaml"]; // Since we are using a wrong strategy, the validation should fail // but the enforced strategy should be "append" ASSERT_NE(validation, yaml_helper::validation_ok); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); std::vector foos; auto expected_foos = std::vector{"bar", "bar2"}; ASSERT_NO_THROW(falco_config.m_config.get_sequence>(foos, "foo")); ASSERT_EQ(foos.size(), 2); // 2 elements in `foo` sequence because we appended to it for(size_t i = 0; i < foos.size(); ++i) { EXPECT_EQ(foos[i], expected_foos[i]) << "Vectors foo's and expected_foo's differ at index " << i; } ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 3); // overridden! ASSERT_FALSE(falco_config.m_config.is_defined( "base_value.name")); // no more present since entire `base_value` block was overridden ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); ASSERT_FALSE(falco_config.m_config.is_defined("base_value_3.id")); // not defined std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); std::filesystem::remove("conf_3.yaml"); } TEST(Configuration, configuration_config_files_unexistent) { /* Test that including an unexistent file just skips it */ const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" " - conf_5.yaml\n" "base_value:\n" " id: 1\n" " name: foo\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main ASSERT_EQ(res.size(), 1); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); std::filesystem::remove("main.yaml"); } TEST(Configuration, configuration_config_files_scalar_config_files) { /* Test that a single file can be included as a scalar (thanks to get_sequence_from_node magic) */ const std::string main_conf_yaml = yaml_helper::configs_key + ": conf_2.yaml\n" "foo: bar\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo2: bar2\n" "base_value_2:\n" " id: 2\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 ASSERT_EQ(res.size(), 2); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar2"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); } TEST(Configuration, configuration_config_files_empty_config_files) { /* Test that empty includes list is accepted */ const std::string main_conf_yaml = yaml_helper::configs_key + ":\n" "foo: bar\n" "base_value:\n" " id: 1\n" " name: foo\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main ASSERT_EQ(res.size(), 1); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); std::filesystem::remove("main.yaml"); } TEST(Configuration, configuration_config_files_self) { /* Test that main config file cannot include itself */ const std::string main_conf_yaml = yaml_helper::configs_key + ": main.yaml\n" "foo: bar\n" "base_value:\n" " id: 1\n" " name: foo\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; ASSERT_ANY_THROW(falco_config.init_from_file("main.yaml", cmdline_config_options)); std::filesystem::remove("main.yaml"); } TEST(Configuration, configuration_config_files_directory) { /* * Test that when main config file includes a config directory, * the config directory is parsed in lexicographic order, * and only regular files are parsed. */ // Main config includes whole temp directory const std::string main_conf_yaml = yaml_helper::configs_key + ": " + std::filesystem::temp_directory_path().string() + "/test\n" "foo: bar\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo2: bar2\n" "base_value_2:\n" " id: 2\n"; const std::string conf_yaml_3 = "foo2: bar3\n" "base_value_3:\n" " id: 3\n" " name: foo3\n"; const std::string conf_yaml_4 = "foo4: bar4\n"; std::filesystem::create_directory(std::filesystem::temp_directory_path() / "test"); std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open(std::filesystem::temp_directory_path() / "test/conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); outfile.open(std::filesystem::temp_directory_path() / "test/conf_3.yaml"); outfile << conf_yaml_3; outfile.close(); // Create a directory and create a config inside it. We will later check that it was not parsed std::filesystem::create_directory(std::filesystem::temp_directory_path() / "test" / "foo"); outfile.open(std::filesystem::temp_directory_path() / "test/foo/conf_4.yaml"); outfile << conf_yaml_4; outfile.close(); std::vector cmdline_config_options; falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 + conf_3. // test/foo is not parsed. ASSERT_EQ(res.size(), 3); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_3.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_3.id", 0), 3); ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar3"); ASSERT_FALSE(falco_config.m_config.is_defined("foo4")); std::filesystem::remove("main"); std::filesystem::remove_all(std::filesystem::temp_directory_path() / "test"); } TEST(Configuration, configuration_config_files_cmdline) { /* Test that we support including configs files from cmdline option */ const std::string main_conf_yaml = "foo: bar\n" "base_value:\n" " id: 1\n" " name: foo\n"; const std::string conf_yaml_2 = "foo2: bar2\n" "base_value_2:\n" " id: 2\n"; std::ofstream outfile("main.yaml"); outfile << main_conf_yaml; outfile.close(); outfile.open("conf_2.yaml"); outfile << conf_yaml_2; outfile.close(); // Pass "config_files=..." cmdline option std::vector cmdline_config_options; cmdline_config_options.push_back((yaml_helper::configs_key + "=conf_2.yaml")); // Override foo2 value from cli cmdline_config_options.push_back(("foo2=bar22")); falco_configuration falco_config; config_loaded_res res; ASSERT_NO_THROW(res = falco_config.init_from_file("main.yaml", cmdline_config_options)); // main + conf_2 ASSERT_EQ(res.size(), 2); ASSERT_TRUE(falco_config.m_config.is_defined("foo")); ASSERT_EQ(falco_config.m_config.get_scalar("foo", ""), "bar"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.id", 0), 1); ASSERT_TRUE(falco_config.m_config.is_defined("base_value.name")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value.name", ""), "foo"); ASSERT_TRUE(falco_config.m_config.is_defined("foo2")); ASSERT_EQ(falco_config.m_config.get_scalar("foo2", ""), "bar22"); ASSERT_TRUE(falco_config.m_config.is_defined("base_value_2.id")); ASSERT_EQ(falco_config.m_config.get_scalar("base_value_2.id", 0), 2); std::filesystem::remove("main.yaml"); std::filesystem::remove("conf_2.yaml"); } ================================================ FILE: unit_tests/falco/test_configuration_env_vars.cpp ================================================ // 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 #include #ifdef _WIN32 #define SET_ENV_VAR(env_var_name, env_var_value) _putenv_s(env_var_name, env_var_value) #else #define SET_ENV_VAR(env_var_name, env_var_value) setenv(env_var_name, env_var_value, 1) #endif 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"; SET_ENV_VAR(env_var_name.c_str(), env_var_value.c_str()); 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_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" " boolean: ${UNSED_XX_X_X_VAR}\n" "base_value_2:\n" " sample_list:\n" " - ${ENV_VAR}\n" " - ' ${ENV_VAR}'\n" " - '${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 */ auto base_value_string = conf.get_scalar("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 `$` */ auto base_value_invalid = conf.get_scalar("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 */ auto base_value_invalid_env = conf.get_scalar("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("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("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("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 */ auto base_value_id = conf.get_scalar("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 auto base_value_name = conf.get_scalar("base_value.name", default_value); ASSERT_EQ(base_value_name, env_var_value); // Proper environment variable format auto base_value_escaped = conf.get_scalar("base_value.escaped", default_value); ASSERT_EQ(base_value_escaped, env_var_value); // Environment variable within quotes /* Test fetching of an undefined environment variable. Resolves to empty string. */ auto unknown_boolean = conf.get_scalar("base_value.subvalue.subvalue2.boolean", default_value); ASSERT_EQ(unknown_boolean, ""); /* Test fetching of environment variables from a list */ auto base_value_2_list_0 = conf.get_scalar("base_value_2.sample_list[0]", default_value); ASSERT_EQ(base_value_2_list_0, env_var_value); // Proper environment variable format auto base_value_2_list_1 = conf.get_scalar("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 auto base_value_2_list_2 = conf.get_scalar("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 auto base_value_2_list_3 = conf.get_scalar("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("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("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("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("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("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("is_test", false); ASSERT_EQ(boolean, true); // `true` can be parsed to bool. auto boolean_as_str = conf.get_scalar("is_test", "false"); ASSERT_EQ(boolean_as_str, "true"); // `true` can be parsed to string. auto boolean_as_int = conf.get_scalar("is_test", 0); ASSERT_EQ(boolean_as_int, 0); // `true` cannot be parsed to integer. auto integer = conf.get_scalar("num_test", -1); ASSERT_EQ(integer, 12); // An env var that resolves to an empty string returns "" auto empty_default_str = conf.get_scalar("empty_test", default_value); ASSERT_EQ(empty_default_str, ""); std::list plugins; conf.get_sequence>(plugins, std::string("plugins")); std::vector 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(), ""); } ================================================ FILE: unit_tests/falco/test_configuration_output_options.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 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 #include TEST(ConfigurationRuleOutputOptions, parse_yaml) { falco_configuration falco_config; ASSERT_NO_THROW(falco_config.init_from_content(R"( append_output: - match: source: syscall tags: ["persistence"] rule: some rule name extra_output: "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]" - match: tags: ["persistence", "execution"] extra_fields: - proc.aname[2]: "%proc.aname[2]" - proc.aname[3]: "%proc.aname[3]" - proc.aname[4]: "%proc.aname[4]" extra_output: "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]" - match: source: k8s_audit extra_fields: - ka.verb - static_field: "static content" )", {})); EXPECT_EQ(falco_config.m_append_output.size(), 3); EXPECT_EQ(falco_config.m_append_output[0].m_source, "syscall"); EXPECT_EQ(falco_config.m_append_output[0].m_tags.size(), 1); EXPECT_EQ(falco_config.m_append_output[0].m_tags.count("persistence"), 1); EXPECT_EQ(falco_config.m_append_output[0].m_rule, "some rule name"); EXPECT_EQ(falco_config.m_append_output[0].m_formatted_fields.size(), 0); EXPECT_EQ(falco_config.m_append_output[0].m_format, "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"); EXPECT_EQ(falco_config.m_append_output[1].m_tags.size(), 2); EXPECT_EQ(falco_config.m_append_output[1].m_tags.count("persistence"), 1); EXPECT_EQ(falco_config.m_append_output[1].m_tags.count("execution"), 1); EXPECT_EQ(falco_config.m_append_output[1].m_format, "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"); EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields.size(), 3); EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields["proc.aname[2]"], "%proc.aname[2]"); EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields["proc.aname[3]"], "%proc.aname[3]"); EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields["proc.aname[4]"], "%proc.aname[4]"); EXPECT_EQ(falco_config.m_append_output[2].m_source, "k8s_audit"); EXPECT_EQ(falco_config.m_append_output[2].m_formatted_fields.size(), 1); EXPECT_EQ(falco_config.m_append_output[2].m_formatted_fields["static_field"], "static content"); EXPECT_EQ(falco_config.m_append_output[2].m_raw_fields.size(), 1); EXPECT_EQ(falco_config.m_append_output[2].m_raw_fields.count("ka.verb"), 1); } TEST(ConfigurationRuleOutputOptions, cli_options) { falco_configuration falco_config; ASSERT_NO_THROW(falco_config.init_from_content( "", std::vector{ R"(append_output[]={"match": {"source": "syscall", "tags": ["persistence"], "rule": "some rule name"}, "extra_output": "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"})", R"(append_output[]={"match": {"tags": ["persistence", "execution"]}, "extra_fields": [{"proc.aname[2]": "%proc.aname[2]"}, {"proc.aname[3]": "%proc.aname[3]"}, {"proc.aname[4]": "%proc.aname[4]"}], "extra_output": "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"})", R"(append_output[]={"match": {"source": "k8s_audit"}, "extra_fields": ["ka.verb", {"static_field": "static content"}]})"})); EXPECT_EQ(falco_config.m_append_output.size(), 3); EXPECT_EQ(falco_config.m_append_output[0].m_source, "syscall"); EXPECT_EQ(falco_config.m_append_output[0].m_tags.size(), 1); EXPECT_EQ(falco_config.m_append_output[0].m_tags.count("persistence"), 1); EXPECT_EQ(falco_config.m_append_output[0].m_rule, "some rule name"); EXPECT_EQ(falco_config.m_append_output[0].m_formatted_fields.size(), 0); EXPECT_EQ(falco_config.m_append_output[0].m_format, "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"); EXPECT_EQ(falco_config.m_append_output[1].m_tags.size(), 2); EXPECT_EQ(falco_config.m_append_output[1].m_tags.count("persistence"), 1); EXPECT_EQ(falco_config.m_append_output[1].m_tags.count("execution"), 1); EXPECT_EQ(falco_config.m_append_output[1].m_format, "gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4]"); EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields.size(), 3); EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields["proc.aname[2]"], "%proc.aname[2]"); EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields["proc.aname[3]"], "%proc.aname[3]"); EXPECT_EQ(falco_config.m_append_output[1].m_formatted_fields["proc.aname[4]"], "%proc.aname[4]"); EXPECT_EQ(falco_config.m_append_output[2].m_source, "k8s_audit"); EXPECT_EQ(falco_config.m_append_output[2].m_formatted_fields.size(), 1); EXPECT_EQ(falco_config.m_append_output[2].m_formatted_fields["static_field"], "static content"); EXPECT_EQ(falco_config.m_append_output[2].m_raw_fields.size(), 1); EXPECT_EQ(falco_config.m_append_output[2].m_raw_fields.count("ka.verb"), 1); } ================================================ FILE: unit_tests/falco/test_configuration_rule_selection.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 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 #include TEST(ConfigurationRuleSelection, parse_yaml) { falco_configuration falco_config; ASSERT_NO_THROW(falco_config.init_from_content(R"( rules: - enable: rule: 'Terminal Shell in Container' - disable: tag: experimental - enable: rule: 'hello*' )", {})); EXPECT_EQ(falco_config.m_rules_selection.size(), 3); EXPECT_EQ(falco_config.m_rules_selection[0].m_op, falco_configuration::rule_selection_operation::enable); EXPECT_EQ(falco_config.m_rules_selection[0].m_rule, "Terminal Shell in Container"); EXPECT_EQ(falco_config.m_rules_selection[1].m_op, falco_configuration::rule_selection_operation::disable); EXPECT_EQ(falco_config.m_rules_selection[1].m_tag, "experimental"); EXPECT_EQ(falco_config.m_rules_selection[2].m_op, falco_configuration::rule_selection_operation::enable); EXPECT_EQ(falco_config.m_rules_selection[2].m_rule, "hello*"); } TEST(ConfigurationRuleSelection, cli_options) { falco_configuration falco_config; ASSERT_NO_THROW(falco_config.init_from_content( "", std::vector{"rules[].disable.tag=maturity_incubating", "rules[].enable.rule=Adding ssh keys to authorized_keys"})); EXPECT_EQ(falco_config.m_rules_selection.size(), 2); EXPECT_EQ(falco_config.m_rules_selection[0].m_op, falco_configuration::rule_selection_operation::disable); EXPECT_EQ(falco_config.m_rules_selection[0].m_tag, "maturity_incubating"); EXPECT_EQ(falco_config.m_rules_selection[1].m_op, falco_configuration::rule_selection_operation::enable); EXPECT_EQ(falco_config.m_rules_selection[1].m_rule, "Adding ssh keys to authorized_keys"); } TEST(ConfigurationRuleSelection, cli_options_object) { falco_configuration falco_config; ASSERT_NO_THROW(falco_config.init_from_content( "", std::vector{ R"(rules[]={"disable": {"tag": "maturity_incubating"}})", R"(rules[]={"enable": {"rule": "Adding ssh keys to authorized_keys"}})"})); EXPECT_EQ(falco_config.m_rules_selection.size(), 2); EXPECT_EQ(falco_config.m_rules_selection[0].m_op, falco_configuration::rule_selection_operation::disable); EXPECT_EQ(falco_config.m_rules_selection[0].m_tag, "maturity_incubating"); EXPECT_EQ(falco_config.m_rules_selection[1].m_op, falco_configuration::rule_selection_operation::enable); EXPECT_EQ(falco_config.m_rules_selection[1].m_rule, "Adding ssh keys to authorized_keys"); } ================================================ FILE: unit_tests/falco/test_configuration_schema.cpp ================================================ // 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 #include #include #include #define EXPECT_VALIDATION_STATUS(res, status) \ do { \ for(const auto& pair : res) { \ auto validation_status = pair.second; \ EXPECT_TRUE(sinsp_utils::startswith(validation_status, status)) << validation_status; \ } \ } while(0) // Read Falco config from current repo-path TEST(Configuration, schema_validate_config) { falco_configuration falco_config; config_loaded_res res; if(!std::filesystem::exists(TEST_FALCO_CONFIG)) { GTEST_SKIP() << "Falco config not present under " << TEST_FALCO_CONFIG; } EXPECT_NO_THROW(res = falco_config.init_from_file(TEST_FALCO_CONFIG, {})); EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok); } TEST(Configuration, schema_ok) { falco_configuration falco_config; config_loaded_res res; /* OK YAML */ std::string config = "falco_libs:\n" " thread_table_size: 50\n"; EXPECT_NO_THROW(res = falco_config.init_from_content(config, {})); EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok); } TEST(Configuration, schema_wrong_key) { falco_configuration falco_config; config_loaded_res res; /* Miss-typed key YAML */ std::string config = "falco_libss:\n" " thread_table_size: 50\n"; EXPECT_NO_THROW(res = falco_config.init_from_content(config, {})); EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_failed); } TEST(Configuration, schema_wrong_type) { falco_configuration falco_config; /* Wrong value type YAML */ std::string config = "falco_libs: 512\n"; // We expect an exception since `falco_configuration::load_yaml()` // will fail to parse `falco_libs` node. ASSERT_ANY_THROW(falco_config.init_from_content(config, {})); } TEST(Configuration, schema_wrong_embedded_key) { falco_configuration falco_config; config_loaded_res res; /* Miss-typed sub-key YAML */ std::string config = "falco_libs:\n" " thread_table_sizeee: 50\n"; EXPECT_NO_THROW(res = falco_config.init_from_content(config, {})); EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_failed); } TEST(Configuration, plugin_init_config) { falco_configuration falco_config; config_loaded_res res; std::string config = R"( plugins: - name: k8saudit library_path: libk8saudit.so init_config: maxEventSize: 262144 sslCertificate: /etc/falco/falco.pem )"; auto plugin_config_json = nlohmann::json::parse( R"({"maxEventSize": 262144, "sslCertificate": "/etc/falco/falco.pem"})"); EXPECT_NO_THROW(res = falco_config.init_from_content(config, {})); EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok); auto parsed_init_config = nlohmann::json::parse(falco_config.m_plugins[0].m_init_config); EXPECT_EQ(parsed_init_config, plugin_config_json); config = R"( plugins: - name: k8saudit library_path: libk8saudit.so init_config: '{"maxEventSize": 262144, "sslCertificate": "/etc/falco/falco.pem"}' )"; EXPECT_NO_THROW(res = falco_config.init_from_content(config, {})); EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok); parsed_init_config = nlohmann::json::parse(falco_config.m_plugins[0].m_init_config); EXPECT_EQ(parsed_init_config, plugin_config_json); config = R"( plugins: - name: k8saudit library_path: libk8saudit.so init_config: "" )"; EXPECT_NO_THROW(res = falco_config.init_from_content(config, {})); EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok); EXPECT_EQ(falco_config.m_plugins[0].m_init_config, ""); config = R"( plugins: - name: k8saudit library_path: libk8saudit.so init_config: null )"; EXPECT_NO_THROW(res = falco_config.init_from_content(config, {})); EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok); EXPECT_EQ(falco_config.m_plugins[0].m_init_config, ""); } TEST(Configuration, schema_yaml_helper_validator) { yaml_helper conf; falco_configuration falco_config; /* Broken YAML */ std::string sample_yaml = "falco_libs:\n" " thread_table_size: 50\n"; // Ok, we don't ask for any validation EXPECT_NO_THROW(conf.load_from_string(sample_yaml)); // We pass a string variable but not a schema std::vector validation; EXPECT_NO_THROW(conf.load_from_string(sample_yaml, nlohmann::json{}, &validation)); EXPECT_EQ(validation[0], yaml_helper::validation_none); // We pass a schema but not a string storage for the validation; no validation takes place EXPECT_NO_THROW(conf.load_from_string(sample_yaml, falco_config.m_config_schema, nullptr)); // We pass everything EXPECT_NO_THROW(conf.load_from_string(sample_yaml, falco_config.m_config_schema, &validation)); EXPECT_EQ(validation[0], yaml_helper::validation_ok); } ================================================ FILE: unit_tests/falco_test_var.h.in ================================================ #pragma once #define TEST_ENGINE_KMOD_CONFIG "${CMAKE_SOURCE_DIR}/unit_tests/falco/test_configs/engine_kmod_config.yaml" #define TEST_ENGINE_MODERN_CONFIG "${CMAKE_SOURCE_DIR}/unit_tests/falco/test_configs/engine_modern_config.yaml" #define TEST_FALCO_CONFIG "${CMAKE_SOURCE_DIR}/falco.yaml" ================================================ FILE: unit_tests/test_falco_engine.cpp ================================================ #include "test_falco_engine.h" test_falco_engine::test_falco_engine(): m_engine(std::make_shared()) { // create a falco engine ready to load the ruleset m_filter_factory = std::make_shared(&m_inspector, m_filterlist); m_formatter_factory = std::make_shared(&m_inspector, m_filterlist); m_engine->add_source(m_sample_source, m_filter_factory, m_formatter_factory); } bool test_falco_engine::load_rules(const std::string& rules_content, const std::string& rules_filename) { bool ret = false; falco::load_result::rules_contents_t rc = {{rules_filename, rules_content}}; m_load_result = m_engine->load_rules(rules_content, rules_filename); m_load_result_string = m_load_result->as_string(true, rc); m_load_result_json = m_load_result->as_json(rc); ret = m_load_result->successful(); if(ret) { m_engine->enable_rule("", true, m_sample_ruleset); } return ret; } // This must be kept in line with the (private) falco_engine::s_default_ruleset uint64_t test_falco_engine::num_rules_for_ruleset(const std::string& ruleset) { return m_engine->num_rules_for_ruleset(ruleset); } bool test_falco_engine::has_warnings() const { return m_load_result->has_warnings(); } bool test_falco_engine::check_warning_message(const std::string& warning_msg) const { if(!m_load_result->has_warnings()) { return false; } for(const auto& warn : m_load_result_json["warnings"]) { std::string msg = warn["message"]; // Debug: // printf("msg: %s\n", msg.c_str()); if(msg.find(warning_msg) != std::string::npos) { return true; } } return false; } bool test_falco_engine::check_error_message(const std::string& error_msg) const { // if the loading is successful there are no errors if(m_load_result->successful()) { return false; } for(const auto& err : m_load_result_json["errors"]) { std::string msg = err["message"]; // Debug: // printf("msg: %s\n", msg.c_str()); if(msg.find(error_msg) != std::string::npos) { return true; } } return false; } std::string test_falco_engine::get_compiled_rule_condition(std::string rule_name) const { auto rule_description = m_engine->describe_rule(&rule_name, {}); return rule_description["rules"][0]["details"]["condition_compiled"] .template get(); } std::string test_falco_engine::get_compiled_rule_output(std::string rule_name) const { auto rule_description = m_engine->describe_rule(&rule_name, {}); return rule_description["rules"][0]["details"]["output_compiled"].template get(); } std::unordered_map test_falco_engine::get_compiled_rule_formatted_fields( std::string rule_name) const { auto rule_description = m_engine->describe_rule(&rule_name, {}); return rule_description["rules"][0]["details"]["extra_output_formatted_fields"] .template get>(); } ================================================ FILE: unit_tests/test_falco_engine.h ================================================ #pragma once #include "falco_engine.h" #include "rule_loader_reader.h" #include "rule_loader_compiler.h" #include "rule_loading_messages.h" #include #include class test_falco_engine : public testing::Test { protected: test_falco_engine(); bool load_rules(const std::string& rules_content, const std::string& rules_filename); // This must be kept in line with the (private) falco_engine::s_default_ruleset uint64_t num_rules_for_ruleset(const std::string& ruleset = "falco-default-ruleset"); bool has_warnings() const; bool check_warning_message(const std::string& warning_msg) const; bool check_error_message(const std::string& error_msg) const; std::string get_compiled_rule_condition(std::string rule_name = "") const; std::string get_compiled_rule_output(std::string rule_name = "") const; std::unordered_map get_compiled_rule_formatted_fields( std::string rule_name) const; std::string m_sample_ruleset = "sample-ruleset"; std::string m_sample_source = falco_common::syscall_source; sinsp m_inspector; sinsp_filter_check_list m_filterlist; std::shared_ptr m_filter_factory; std::shared_ptr m_formatter_factory; std::shared_ptr m_engine; std::unique_ptr m_load_result; std::string m_load_result_string; nlohmann::json m_load_result_json; }; ================================================ FILE: userspace/engine/CMakeLists.txt ================================================ # 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. add_library( falco_engine STATIC falco_common.cpp falco_engine.cpp falco_load_result.cpp falco_utils.cpp filter_ruleset.cpp evttype_index_ruleset.cpp formats.cpp field_formatter.cpp filter_details_resolver.cpp filter_macro_resolver.cpp filter_warning_resolver.cpp logger.cpp stats_manager.cpp rule_loader.cpp rule_loader_reader.cpp rule_loader_collector.cpp rule_loader_compiler.cpp ) if(EMSCRIPTEN) target_compile_options(falco_engine PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0") endif() set(ENGINE_LIBRARIES sinsp nlohmann_json::nlohmann_json yaml-cpp) if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD) # Used by falco_utils.cpp add_dependencies(falco_engine openssl) list(APPEND ENGINE_LIBRARIES "${OPENSSL_LIBRARIES}") endif() target_include_directories(falco_engine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${TBB_INCLUDE_DIR}) target_link_libraries(falco_engine PUBLIC ${ENGINE_LIBRARIES}) ================================================ FILE: userspace/engine/OWNERS ================================================ labels: - area/engine ================================================ FILE: userspace/engine/evttype_index_ruleset.cpp ================================================ // 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 "evttype_index_ruleset.h" #include "logger.h" #include evttype_index_ruleset::evttype_index_ruleset(std::shared_ptr f): m_filter_factory(f) {} evttype_index_ruleset::~evttype_index_ruleset() {} void evttype_index_ruleset::add(const falco_rule &rule, std::shared_ptr filter, std::shared_ptr condition) { try { auto wrap = std::make_shared(); wrap->m_rule = rule; wrap->m_filter = filter; if(rule.source == falco_common::syscall_source) { wrap->m_sc_codes = libsinsp::filter::ast::ppm_sc_codes(condition.get()); wrap->m_event_codes = libsinsp::filter::ast::ppm_event_codes(condition.get()); } else { wrap->m_sc_codes = {}; wrap->m_event_codes = {ppm_event_code::PPME_PLUGINEVENT_E}; } wrap->m_event_codes.insert(ppm_event_code::PPME_ASYNCEVENT_E); add_wrapper(wrap); } catch(const sinsp_exception &e) { throw falco_exception(std::string(e.what())); } } void evttype_index_ruleset::on_loading_complete() { print_enabled_rules_falco_logger(); } bool evttype_index_ruleset::run_wrappers(sinsp_evt *evt, filter_wrapper_list &wrappers, uint16_t ruleset_id, falco_rule &match) { for(const auto &wrap : wrappers) { if(wrap->m_filter->run(evt)) { match = wrap->m_rule; return true; } } return false; } bool evttype_index_ruleset::run_wrappers(sinsp_evt *evt, filter_wrapper_list &wrappers, uint16_t ruleset_id, std::vector &matches) { bool match_found = false; for(const auto &wrap : wrappers) { if(wrap->m_filter->run(evt)) { matches.push_back(wrap->m_rule); match_found = true; } } return match_found; } void evttype_index_ruleset::print_enabled_rules_falco_logger() { falco_logger::log(falco_logger::level::DEBUG, "Enabled rules:\n"); auto logger = [](std::shared_ptr wrap) { falco_logger::log(falco_logger::level::DEBUG, std::string(" ") + wrap->name() + "\n"); }; uint64_t num_filters = iterate(logger); falco_logger::log(falco_logger::level::DEBUG, "(" + std::to_string(num_filters) + ") enabled rules in total\n"); } ================================================ FILE: userspace/engine/evttype_index_ruleset.h ================================================ // 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. */ #pragma once #include "indexable_ruleset.h" #include #include #include /*! \brief A filter_ruleset that indexes enabled rules by event type, and performs linear search on each event type bucket */ struct evttype_index_wrapper { const std::string &name() { return m_rule.name; } const std::set &tags() { return m_rule.tags; } const libsinsp::events::set &sc_codes() { return m_sc_codes; } const libsinsp::events::set &event_codes() { return m_event_codes; } falco_rule m_rule; libsinsp::events::set m_sc_codes; libsinsp::events::set m_event_codes; std::shared_ptr m_filter; }; class evttype_index_ruleset : public indexable_ruleset { public: explicit evttype_index_ruleset(std::shared_ptr factory); virtual ~evttype_index_ruleset() override; // From filter_ruleset void add(const falco_rule &rule, std::shared_ptr filter, std::shared_ptr condition) override; void on_loading_complete() override; // From indexable_ruleset bool run_wrappers(sinsp_evt *evt, filter_wrapper_list &wrappers, uint16_t ruleset_id, falco_rule &match) override; bool run_wrappers(sinsp_evt *evt, filter_wrapper_list &wrappers, uint16_t ruleset_id, std::vector &matches) override; // Print each enabled rule when running Falco with falco logger // log_level=debug; invoked within on_loading_complete() void print_enabled_rules_falco_logger(); private: std::shared_ptr m_filter_factory; }; class evttype_index_ruleset_factory : public filter_ruleset_factory { public: inline explicit evttype_index_ruleset_factory(std::shared_ptr factory): m_filter_factory(factory) {} inline std::shared_ptr new_ruleset() override { return std::make_shared(m_filter_factory); } private: std::shared_ptr m_filter_factory; }; ================================================ FILE: userspace/engine/falco_common.cpp ================================================ // 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 "falco_common.h" static std::vector priority_names = {"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Informational", "Debug"}; static std::vector rule_matching_names = {"first", "all"}; bool falco_common::parse_priority(const std::string& v, priority_type& out) { for(size_t i = 0; i < priority_names.size(); i++) { // note: for legacy reasons, "Info" and "Informational" has been used // interchangeably and ambiguously, so this is the only edge case for // which we can't apply strict equality check if(!strcasecmp(v.c_str(), priority_names[i].c_str()) || (i == PRIORITY_INFORMATIONAL && !strcasecmp(v.c_str(), "info"))) { out = (priority_type)i; return true; } } return false; } falco_common::priority_type falco_common::parse_priority(const std::string& v) { falco_common::priority_type out; if(!parse_priority(v, out)) { throw falco_exception("Unknown priority value: " + v); } return out; } bool falco_common::format_priority(priority_type v, std::string& out, bool shortfmt) { if((size_t)v < priority_names.size()) { if(v == PRIORITY_INFORMATIONAL && shortfmt) { out = "Info"; } else { out = priority_names[(size_t)v]; } return true; } return false; } std::string falco_common::format_priority(priority_type v, bool shortfmt) { std::string out; if(!format_priority(v, out, shortfmt)) { throw falco_exception("Unknown priority enum value: " + std::to_string(v)); } return out; } bool falco_common::parse_rule_matching(const std::string& v, rule_matching& out) { for(size_t i = 0; i < rule_matching_names.size(); i++) { if(!strcasecmp(v.c_str(), rule_matching_names[i].c_str())) { out = (rule_matching)i; return true; } } return false; } ================================================ FILE: userspace/engine/falco_common.h ================================================ // 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. */ #pragma once #include #include #include #include // // equivalent to an "unbounded queue" in TBB terms or largest long value // https://github.com/oneapi-src/oneTBB/blob/b2474bfc636937052d05daf8b3f4d6b76e20273a/include/oneapi/tbb/concurrent_queue.h#L554 // #define DEFAULT_OUTPUTS_QUEUE_CAPACITY_UNBOUNDED_MAX_LONG_VALUE std::ptrdiff_t(~size_t(0) / 2) #define DEFAULT_FALCO_LIBS_THREAD_TABLE_SIZE 262144 #define DEFAULT_FALCO_LIBS_THREAD_TABLE_AUTO_PURGING_INTERVAL_S (5 * 60) // 5 minutes. #define DEFAULT_FALCO_LIBS_THREAD_TABLE_AUTO_PURGING_THREAD_TIMEOUT_S (5 * 60) // 5 minutes. // // Most falco_* classes can throw exceptions. Unless directly related // to low-level failures like inability to open file, etc, they will // be of this type. // struct falco_exception : std::runtime_error { using std::runtime_error::runtime_error; }; namespace falco_common { const std::string syscall_source = sinsp_syscall_event_source_name; // Same as numbers/indices into the above vector enum priority_type { PRIORITY_EMERGENCY = 0, PRIORITY_ALERT = 1, PRIORITY_CRITICAL = 2, PRIORITY_ERROR = 3, PRIORITY_WARNING = 4, PRIORITY_NOTICE = 5, PRIORITY_INFORMATIONAL = 6, PRIORITY_DEBUG = 7 }; bool parse_priority(const std::string& v, priority_type& out); priority_type parse_priority(const std::string& v); bool format_priority(priority_type v, std::string& out, bool shortfmt = false); std::string format_priority(priority_type v, bool shortfmt = false); enum rule_matching { FIRST = 0, ALL = 1 }; bool parse_rule_matching(const std::string& v, rule_matching& out); }; // namespace falco_common typedef std::unordered_map> extra_output_field_t; ================================================ FILE: userspace/engine/falco_engine.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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 #ifndef _WIN32 #include #else #include #include #define srandom srand #define random rand #endif #include #include #include #include #include #include #include #include #include #include #include "falco_engine.h" #include "falco_utils.h" #include "falco_engine_version.h" #include "formats.h" #include "field_formatter.h" #include "evttype_index_ruleset.h" #include "rule_json_schema.h" const std::string falco_engine::s_default_ruleset = "falco-default-ruleset"; using namespace falco; falco_engine::falco_engine(bool seed_rng): m_syscall_source(NULL), m_syscall_source_idx(SIZE_MAX), m_rule_reader(std::make_shared()), m_rule_collector(std::make_shared()), m_rule_compiler(std::make_shared()), m_next_ruleset_id(0), m_min_priority(falco_common::PRIORITY_DEBUG), m_sampling_ratio(1), m_sampling_multiplier(0) { if(seed_rng) { srandom((unsigned)getpid()); } m_default_ruleset_id = find_ruleset_id(s_default_ruleset); fill_engine_state_funcs(m_engine_state); m_rule_schema = nlohmann::json::parse(rule_schema_string); } falco_engine::~falco_engine() { m_rules.clear(); m_rule_collector->clear(); m_rule_stats_manager.clear(); m_sources.clear(); m_extra_output_format.clear(); } sinsp_version falco_engine::engine_version() { return sinsp_version(FALCO_ENGINE_VERSION); } void falco_engine::set_rule_reader(std::shared_ptr reader) { m_rule_reader = reader; } std::shared_ptr falco_engine::get_rule_reader() { return m_rule_reader; } void falco_engine::set_rule_collector(std::shared_ptr collector) { m_rule_collector = collector; } std::shared_ptr falco_engine::get_rule_collector() { return m_rule_collector; } void falco_engine::set_rule_compiler(std::shared_ptr compiler) { m_rule_compiler = compiler; } std::shared_ptr falco_engine::get_rule_compiler() { return m_rule_compiler; } // Return a key that uniquely represents a field class. // For now, we assume name + shortdesc is unique. static std::string fieldclass_key(const sinsp_filter_factory::filter_fieldclass_info &fld_info) { return fld_info.name + fld_info.shortdesc; } void falco_engine::list_fields(const std::string &source, bool verbose, bool names_only, output_format format) const { // Maps from field class name + short desc to list of event // sources for which this field class can be used. std::map> fieldclass_event_sources; // Do a first pass to group together classes that are // applicable to multiple event sources. for(const auto &it : m_sources) { if(source != "" && source != it.name) { continue; } for(const auto &fld_class : it.filter_factory->get_fields()) { fieldclass_event_sources[fieldclass_key(fld_class)].insert(it.name); } } // The set of field classes already printed. Used to avoid // printing field classes multiple times for different sources std::set seen_fieldclasses; // Create the appropriate formatter and use it auto formatter = FieldFormatter::create(format, verbose); formatter->begin(); // In the second pass, actually print info, skipping duplicate // field classes and also printing info on supported sources. for(const auto &it : m_sources) { if(source != "" && source != it.name) { continue; } for(auto &fld_class : it.filter_factory->get_fields()) { std::string key = fieldclass_key(fld_class); if(seen_fieldclasses.find(key) != seen_fieldclasses.end()) { continue; } seen_fieldclasses.insert(key); if(names_only) { for(auto &field : fld_class.fields) { if(field.is_skippable() || field.is_deprecated()) { continue; } formatter->print_field_name(field.name); } } else { formatter->print_fieldclass(fld_class, fieldclass_event_sources[key]); } } } formatter->end(); } std::unique_ptr falco_engine::load_rules(const std::string &rules_content, const std::string &name) { rule_loader::configuration cfg(rules_content, m_sources, name); cfg.extra_output_format = m_extra_output_format; cfg.extra_output_fields = m_extra_output_fields; // read rules YAML file and collect its definitions if(m_rule_reader->read(cfg, *m_rule_collector, m_rule_schema)) { // compile the definitions (resolve macro/list refs, exceptions, ...) m_last_compile_output = m_rule_compiler->new_compile_output(); m_rule_compiler->compile(cfg, *m_rule_collector, *m_last_compile_output); if(!cfg.res->successful()) { return std::move(cfg.res); } // clear the rules known by the engine and each ruleset m_rules.clear(); for(auto &src : m_sources) // add rules to each ruleset { src.ruleset = create_ruleset(src.ruleset_factory); src.ruleset->add_compile_output(*m_last_compile_output, m_min_priority, src.name); } // add rules to the engine and the rulesets for(const auto &rule : m_last_compile_output->rules) { auto info = m_rule_collector->rules().at(rule.name); if(!info) { // this is just defensive, it should never happen throw falco_exception("can't find internal rule info at name: " + name); } auto source = find_source(rule.source); auto rule_id = m_rules.insert(rule, rule.name); if(rule_id != rule.id) { throw falco_exception("Incompatible ID for rule: " + rule.name + " | compiled ID: " + std::to_string(rule.id) + " | stats_mgr ID: " + std::to_string(rule_id)); } // By default rules are enabled/disabled for the default ruleset // skip the rule if below the minimum priority if(rule.priority > m_min_priority) { continue; } if(info->enabled) { source->ruleset->enable(rule.name, filter_ruleset::match_type::exact, m_default_ruleset_id); } else { source->ruleset->disable(rule.name, filter_ruleset::match_type::exact, m_default_ruleset_id); } } } if(cfg.res->successful()) { m_rule_stats_manager.clear(); for(const auto &r : m_rules) { m_rule_stats_manager.on_rule_loaded(r); } } return std::move(cfg.res); } 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) { for(const auto &it : m_sources) { if(enabled) { it.ruleset->enable(substring, filter_ruleset::match_type::substring, ruleset_id); } else { it.ruleset->disable(substring, filter_ruleset::match_type::substring, ruleset_id); } } } 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) { for(const auto &it : m_sources) { if(enabled) { it.ruleset->enable(rule_name, filter_ruleset::match_type::exact, ruleset_id); } else { it.ruleset->disable(rule_name, filter_ruleset::match_type::exact, ruleset_id); } } } void falco_engine::enable_rule_wildcard(const std::string &rule_name, bool enabled, const std::string &ruleset) { uint16_t ruleset_id = find_ruleset_id(ruleset); enable_rule_wildcard(rule_name, enabled, ruleset_id); } void falco_engine::enable_rule_wildcard(const std::string &rule_name, bool enabled, const uint16_t ruleset_id) { for(const auto &it : m_sources) { if(enabled) { it.ruleset->enable(rule_name, filter_ruleset::match_type::wildcard, ruleset_id); } else { it.ruleset->disable(rule_name, filter_ruleset::match_type::wildcard, ruleset_id); } } } void falco_engine::enable_rule_by_tag(const std::set &tags, bool enabled, const std::string &ruleset) { 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 &tags, bool enabled, const uint16_t ruleset_id) { for(const auto &it : m_sources) { if(enabled) { it.ruleset->enable_tags(tags, ruleset_id); } else { it.ruleset->disable_tags(tags, ruleset_id); } } } void falco_engine::set_min_priority(falco_common::priority_type priority) { m_min_priority = priority; } uint16_t falco_engine::find_ruleset_id(const std::string &ruleset) { auto it = m_known_rulesets.lower_bound(ruleset); if(it == m_known_rulesets.end() || it->first != ruleset) { it = m_known_rulesets.emplace_hint(it, std::make_pair(ruleset, m_next_ruleset_id++)); } return it->second; } uint64_t falco_engine::num_rules_for_ruleset(const std::string &ruleset) { uint16_t ruleset_id = find_ruleset_id(ruleset); uint64_t ret = 0; for(const auto &src : m_sources) { ret += src.ruleset->enabled_count(ruleset_id); } return ret; } void falco_engine::evttypes_for_ruleset(const std::string &source, std::set &evttypes, const std::string &ruleset) { find_source(source)->ruleset->enabled_evttypes(evttypes, find_ruleset_id(ruleset)); } libsinsp::events::set falco_engine::sc_codes_for_ruleset(const std::string &source, const std::string &ruleset) { return find_source(source)->ruleset->enabled_sc_codes(find_ruleset_id(ruleset)); } libsinsp::events::set falco_engine::event_codes_for_ruleset( const std::string &source, const std::string &ruleset) { return find_source(source)->ruleset->enabled_event_codes(find_ruleset_id(ruleset)); } std::shared_ptr falco_engine::create_formatter( const std::string &source, const std::string &output) const { return find_source(source)->formatter_factory->create_formatter(output); } std::unique_ptr> falco_engine::process_event( std::size_t source_idx, sinsp_evt *ev, uint16_t ruleset_id, falco_common::rule_matching strategy) { // note: there are no thread-safety guarantees on the filter_ruleset::run() // method, but the thread-safety assumptions of falco_engine::process_event() // imply that concurrent invokers use different and non-switchable values of // source_idx, which means that at any time each filter_ruleset will only // be accessed by a single thread. const falco_source *source = find_source(source_idx); if(should_drop_evt() || !source) { return nullptr; } switch(strategy) { case falco_common::rule_matching::ALL: if(source->m_rules.size() > 0) { source->m_rules.clear(); } if(!source->ruleset->run(ev, source->m_rules, ruleset_id)) { return nullptr; } break; case falco_common::rule_matching::FIRST: if(source->m_rules.size() != 1) { source->m_rules.resize(1); } if(!source->ruleset->run(ev, source->m_rules[0], ruleset_id)) { return nullptr; } break; } auto res = std::make_unique>(); for(const auto &rule : source->m_rules) { rule_result rule_result; rule_result.evt = ev; rule_result.rule = rule.name; rule_result.source = rule.source; rule_result.format = rule.output; rule_result.priority_num = rule.priority; rule_result.capture = rule.capture; rule_result.capture_duration_ns = uint64_t(rule.capture_duration) * 1000000LL; rule_result.tags = rule.tags; rule_result.exception_fields = rule.exception_fields; rule_result.extra_output_fields = rule.extra_output_fields; m_rule_stats_manager.on_event(rule); res->push_back(rule_result); } return res; } std::unique_ptr> falco_engine::process_event( std::size_t source_idx, sinsp_evt *ev, falco_common::rule_matching strategy) { return process_event(source_idx, ev, m_default_ruleset_id, strategy); } std::size_t falco_engine::add_source( const std::string &source, std::shared_ptr filter_factory, std::shared_ptr formatter_factory) { // evttype_index_ruleset is the default ruleset implementation size_t idx = add_source(source, filter_factory, formatter_factory, std::make_shared(filter_factory)); if(source == falco_common::syscall_source) { m_syscall_source_idx = idx; } return idx; } std::size_t falco_engine::add_source(const std::string &source, std::shared_ptr filter_factory, std::shared_ptr formatter_factory, std::shared_ptr ruleset_factory) { falco_source src; src.name = source; src.filter_factory = filter_factory; src.formatter_factory = formatter_factory; src.ruleset_factory = ruleset_factory; src.ruleset = create_ruleset(src.ruleset_factory); return m_sources.insert(src, source); } template inline nlohmann::json sequence_to_json_array(const T &seq) { nlohmann::json ret = nlohmann::json::array(); for(const auto &v : seq) { ret.push_back(v); } return ret; } nlohmann::json falco_engine::describe_rule( std::string *rule_name, const std::vector> &plugins) const { // use previously-loaded collector definitions and the compiled // output of rules, macros, and lists. if(m_last_compile_output == nullptr) { throw falco_exception("rules must be loaded before describing them"); } // use collected and compiled info to print a json output nlohmann::json output; if(!rule_name) { // 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 nlohmann::json plugin_versions = nlohmann::json::array(); auto required_plugin_versions = m_rule_collector->required_plugin_versions(); for(const auto &req : required_plugin_versions) { nlohmann::json r; r["name"] = req.at(0).name; r["version"] = req.at(0).version; nlohmann::json alternatives = nlohmann::json::array(); for(size_t i = 1; i < req.size(); i++) { nlohmann::json alternative; alternative["name"] = req[i].name; alternative["version"] = req[i].version; alternatives.push_back(std::move(alternative)); } r["alternatives"] = std::move(alternatives); plugin_versions.push_back(std::move(r)); } output["required_plugin_versions"] = std::move(plugin_versions); // Store information about rules nlohmann::json rules_array = nlohmann::json::array(); for(const auto &rule : m_last_compile_output->rules) { auto info = m_rule_collector->rules().at(rule.name); nlohmann::json details; get_json_details(details, rule, *info, plugins); rules_array.push_back(std::move(details)); } output["rules"] = std::move(rules_array); // Store information about macros nlohmann::json macros_array = nlohmann::json::array(); for(const auto ¯o : m_last_compile_output->macros) { auto info = m_rule_collector->macros().at(macro.name); nlohmann::json details; get_json_details(details, macro, *info, plugins); macros_array.push_back(std::move(details)); } output["macros"] = std::move(macros_array); // Store information about lists nlohmann::json lists_array = nlohmann::json::array(); for(const auto &list : m_last_compile_output->lists) { auto info = m_rule_collector->lists().at(list.name); nlohmann::json details; get_json_details(details, list, *info, plugins); lists_array.push_back(std::move(details)); } output["lists"] = std::move(lists_array); } else { // build json information for just the specified rule auto ri = m_rule_collector->rules().at(*rule_name); if(ri == nullptr || ri->unknown_source) { throw falco_exception("Rule \"" + *rule_name + "\" is not loaded"); } auto rule = m_rules.at(ri->name); nlohmann::json details; get_json_details(details, *rule, *ri, plugins); nlohmann::json rules_array = nlohmann::json::array(); rules_array.push_back(std::move(details)); output["rules"] = std::move(rules_array); } return output; } void falco_engine::get_json_details( nlohmann::json &out, const falco_rule &r, const rule_loader::rule_info &info, const std::vector> &plugins) const { nlohmann::json rule_info; // Fill general rule information rule_info["name"] = r.name; rule_info["condition"] = info.cond; rule_info["priority"] = format_priority(r.priority, false); rule_info["output"] = info.output; rule_info["description"] = r.description; rule_info["enabled"] = info.enabled; rule_info["source"] = r.source; rule_info["capture"] = r.capture; rule_info["capture_duration"] = r.capture_duration; rule_info["tags"] = sequence_to_json_array(info.tags); out["info"] = std::move(rule_info); // Parse rule condition and build the non-compiled AST // Assumption: no error because rules have already been loaded. auto ast = libsinsp::filter::parser(info.cond).parse(); // get details related to the condition's filter filter_details details; filter_details compiled_details; nlohmann::json json_details; for(const auto &m : m_rule_collector->macros()) { details.known_macros.insert(m.name); compiled_details.known_macros.insert(m.name); } for(const auto &l : m_rule_collector->lists()) { details.known_lists.insert(l.name); compiled_details.known_lists.insert(l.name); } filter_details_resolver().run(ast.get(), details); filter_details_resolver().run(r.condition.get(), compiled_details); out["details"]["macros"] = sequence_to_json_array(details.macros); out["details"]["lists"] = sequence_to_json_array(details.lists); out["details"]["condition_operators"] = sequence_to_json_array(compiled_details.operators); out["details"]["condition_fields"] = sequence_to_json_array(compiled_details.fields); // Get extra requested fields std::vector out_fields; for(auto const &f : r.extra_output_fields) { // add all the field keys out_fields.emplace_back(f.second.first); if(!f.second.second) // formatted field { out["details"]["extra_output_formatted_fields"][f.first] = f.second.first; } } // Get fields from output string auto fmt = create_formatter(r.source, r.output); fmt->get_field_names(out_fields); out["details"]["output_fields"] = sequence_to_json_array(out_fields); // Get fields from exceptions out["details"]["exception_fields"] = sequence_to_json_array(r.exception_fields); // Get names and operators from exceptions std::unordered_set exception_names; std::unordered_set exception_operators; for(const auto &e : info.exceptions) { exception_names.insert(e.name); if(e.comps.is_list) { for(const auto &c : e.comps.items) { if(c.is_list) { // considering max two levels of lists for(const auto &i : c.items) { exception_operators.insert(i.item); } } else { exception_operators.insert(c.item); } } } 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 nlohmann::json events; get_json_evt_types(events, info.source, r.condition.get()); out["details"]["events"] = std::move(events); // Store compiled condition and output out["details"]["condition_compiled"] = libsinsp::filter::ast::as_string(r.condition.get()); out["details"]["output_compiled"] = r.output; // Compute the plugins that are actually used by this rule. This is involves: // - The rule's event source, that can be implemented by a plugin // - 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 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"] = std::move(used_plugins); } void falco_engine::get_json_details( nlohmann::json &out, const falco_macro ¯o, const rule_loader::macro_info &info, const std::vector> &plugins) const { nlohmann::json macro_info; macro_info["name"] = macro.name; macro_info["condition"] = info.cond; 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. auto ast = libsinsp::filter::parser(info.cond).parse(); // get details related to the condition's filter filter_details details; filter_details compiled_details; nlohmann::json json_details; for(const auto &m : m_rule_collector->macros()) { details.known_macros.insert(m.name); compiled_details.known_macros.insert(m.name); } for(const auto &l : m_rule_collector->lists()) { details.known_lists.insert(l.name); compiled_details.known_lists.insert(l.name); } filter_details_resolver().run(ast.get(), details); filter_details_resolver().run(macro.condition.get(), compiled_details); out["details"]["used"] = macro.used; out["details"]["macros"] = sequence_to_json_array(details.macros); out["details"]["lists"] = sequence_to_json_array(details.lists); out["details"]["condition_operators"] = sequence_to_json_array(compiled_details.operators); out["details"]["condition_fields"] = sequence_to_json_array(compiled_details.fields); // Store event types nlohmann::json events; get_json_evt_types(events, "", macro.condition.get()); out["details"]["events"] = std::move(events); // Store compiled condition out["details"]["condition_compiled"] = libsinsp::filter::ast::as_string(macro.condition.get()); // Compute the plugins that are actually used by this macro. // Note: macros have no specific source, we need to set an empty list of used // plugins because we can't be certain about their actual usage. For example, // 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"] = nlohmann::json::array(); } void falco_engine::get_json_details( nlohmann::json &out, const falco_list &l, const rule_loader::list_info &info, const std::vector> &plugins) const { nlohmann::json list_info; list_info["name"] = l.name; // note: the syntactic definitions still has the list refs unresolved nlohmann::json items = nlohmann::json::array(); std::unordered_set lists; for(const auto &i : info.items) { // if an item is present in the syntactic def of a list, but not // on the compiled_items of the same list, then we can assume it // being a resolved list ref if(std::find(l.items.begin(), l.items.end(), i) == l.items.end()) { lists.insert(i); continue; } items.push_back(std::move(i)); } 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"] = nlohmann::json::array(); // always empty } void falco_engine::get_json_evt_types(nlohmann::json &out, const std::string &source, libsinsp::filter::ast::expr *ast) const { // note: this duplicates part of the logic of evttype_index_ruleset, // not good but it's our best option for now if(source.empty() || source == falco_common::syscall_source) { auto evtcodes = libsinsp::filter::ast::ppm_event_codes(ast); evtcodes.insert(ppm_event_code::PPME_ASYNCEVENT_E); auto syscodes = libsinsp::filter::ast::ppm_sc_codes(ast); auto syscodes_to_evt_names = libsinsp::events::sc_set_to_event_names(syscodes); auto evtcodes_to_evt_names = libsinsp::events::event_set_to_names(evtcodes, false); out = sequence_to_json_array( unordered_set_union(syscodes_to_evt_names, evtcodes_to_evt_names)); } else { out = sequence_to_json_array(libsinsp::events::event_set_to_names( {ppm_event_code::PPME_PLUGINEVENT_E, ppm_event_code::PPME_ASYNCEVENT_E})); } } void falco_engine::get_json_used_plugins( nlohmann::json &out, const std::string &source, const std::unordered_set &evtnames, const std::unordered_set &fields, const std::vector> &plugins) const { // note: condition and output fields may have an argument, so // we need to isolate the field names std::unordered_set fieldnames; for(const auto &f : fields) { auto argpos = f.find('['); if(argpos != std::string::npos) { fieldnames.insert(f.substr(0, argpos)); } else { fieldnames.insert(f); } } std::unordered_set used_plugins; for(const auto &p : plugins) { bool used = false; if(p->caps() & CAP_SOURCING) { // The rule's source is implemented by a plugin with event // sourcing capability. // Note: if Falco loads two plugins implementing the same source, // they will both be included in the list. if(!used && p->event_source() == source) { used_plugins.insert(p->name()); used = true; } } if(!used && p->caps() & CAP_EXTRACTION) { // The rule uses a field implemented by a plugin with field // extraction capability that is compatible with the rule's source. // Note: here we're assuming that Falco will prevent loading // plugins implementing fields with the same name for the same // event source (implemented in init_inspectors app action). if(sinsp_plugin::is_source_compatible(p->extract_event_sources(), source)) { for(const auto &f : p->fields()) { if(!used && fieldnames.find(f.m_name) != fieldnames.end()) { used_plugins.insert(p->name()); used = true; break; } } } } if(!used && p->caps() & CAP_ASYNC) { // The rule matches an event type implemented by a plugin with // async events capability that is compatible with the rule's source. // Note: if Falco loads two plugins implementing async events with // the same name, they will both be included in the list. if(sinsp_plugin::is_source_compatible(p->async_event_sources(), source)) { for(const auto &n : p->async_event_names()) { if(!used && evtnames.find(n) != evtnames.end()) { used_plugins.insert(p->name()); used = true; break; } } } } } out = sequence_to_json_array(used_plugins); } void falco_engine::print_stats() const { std::string out; m_rule_stats_manager.format(m_rules, out); // todo(jasondellaluce): introduce a logging callback in Falco fprintf(stdout, "%s", out.c_str()); } const stats_manager &falco_engine::get_rule_stats_manager() const { return m_rule_stats_manager; } bool falco_engine::is_source_valid(const std::string &source) const { return m_sources.at(source) != nullptr; } std::shared_ptr falco_engine::filter_factory_for_source( const std::string &source) { return find_source(source)->filter_factory; } std::shared_ptr falco_engine::filter_factory_for_source( std::size_t source_idx) { return find_source(source_idx)->filter_factory; } std::shared_ptr falco_engine::formatter_factory_for_source( const std::string &source) { return find_source(source)->formatter_factory; } std::shared_ptr falco_engine::formatter_factory_for_source( std::size_t source_idx) { return find_source(source_idx)->formatter_factory; } std::shared_ptr falco_engine::ruleset_factory_for_source( const std::string &source) { return find_source(source)->ruleset_factory; } std::shared_ptr falco_engine::ruleset_factory_for_source( std::size_t source_idx) { return find_source(source_idx)->ruleset_factory; } std::shared_ptr falco_engine::ruleset_for_source(const std::string &source_name) { const falco_source *source = find_source(source_name); return source->ruleset; } std::shared_ptr falco_engine::ruleset_for_source(std::size_t source_idx) { const falco_source *source = find_source(source_idx); return source->ruleset; } static bool check_plugin_requirement_alternatives( const std::vector &plugins, const rule_loader::plugin_version_info::requirement_alternatives &alternatives, std::string &err) { for(const auto &req : alternatives) { for(const auto &plugin : plugins) { if(req.name == plugin.name) { sinsp_version req_version(req.version); sinsp_version plugin_version(plugin.version); if(!plugin_version.is_valid()) { err = "Plugin '" + plugin.name + "' has invalid version string '" + plugin.version + "'"; return false; } if(!plugin_version.compatible_with(req_version)) { err = "Plugin '" + plugin.name + "' version '" + plugin.version + "' is not compatible with required plugin version '" + req.version + "'"; return false; } return true; } } } return false; } bool falco_engine::check_plugin_requirements(const std::vector &plugins, std::string &err) const { err = ""; for(const auto &alternatives : m_rule_collector->required_plugin_versions()) { if(!check_plugin_requirement_alternatives(plugins, alternatives, err)) { if(err.empty()) { for(const auto &req : alternatives) { err += err.empty() ? "" : ", "; err += req.name + " (>= " + req.version + ")"; } err = "Plugin requirement not satisfied, must load one of: " + err; } return false; } } return true; } std::shared_ptr falco_engine::create_ruleset( std::shared_ptr &ruleset_factory) { auto ret = ruleset_factory->new_ruleset(); ret->set_engine_state(m_engine_state); return ret; } void falco_engine::fill_engine_state_funcs(filter_ruleset::engine_state_funcs &engine_state) { engine_state.get_ruleset = [this](const std::string &source_name, std::shared_ptr &ruleset) -> bool { const falco_source *src = m_sources.at(source_name); if(src == nullptr) { return false; } ruleset = src->ruleset; return true; }; }; void falco_engine::complete_rule_loading() const { for(const auto &src : m_sources) { src.ruleset->on_loading_complete(); } } void falco_engine::set_sampling_ratio(uint32_t sampling_ratio) { m_sampling_ratio = sampling_ratio; } void falco_engine::set_sampling_multiplier(double sampling_multiplier) { m_sampling_multiplier = sampling_multiplier; } void falco_engine::add_extra_output_format(const std::string &format, const std::string &source, const std::set &tags, const std::string &rule) { m_extra_output_format.push_back({format, source, tags, rule}); } void falco_engine::add_extra_output_formatted_field(const std::string &key, const std::string &format, const std::string &source, const std::set &tags, const std::string &rule) { m_extra_output_fields.push_back({key, format, source, tags, rule, false}); } void falco_engine::add_extra_output_raw_field(const std::string &key, const std::string &source, const std::set &tags, const std::string &rule) { std::string format = "%" + key; m_extra_output_fields.push_back({key, format, source, tags, rule, true}); } inline bool falco_engine::should_drop_evt() const { if(m_sampling_multiplier == 0) { return false; } if(m_sampling_ratio == 1) { return false; } double coin = (random() * (1.0 / RAND_MAX)); return (coin >= (1.0 / (m_sampling_multiplier * m_sampling_ratio))); } ================================================ FILE: userspace/engine/falco_engine.h ================================================ // 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. */ #pragma once #include #include #include #include #include #include "filter_ruleset.h" #include "rule_loader.h" #include "rule_loader_reader.h" #include "rule_loader_collector.h" #include "rule_loader_compiler.h" #include "stats_manager.h" #include "falco_common.h" #include "falco_source.h" #include "falco_load_result.h" #include "filter_details_resolver.h" #include "output_format.h" // // This class acts as the primary interface between a program and the // falco rules engine. Falco outputs (writing to files/syslog/etc) are // handled in a separate class falco_outputs. // class falco_engine { public: explicit falco_engine(bool seed_rng = true); virtual ~falco_engine(); // A given engine has a version which identifies the fields // and rules file format it supports. This version will change // any time the code that handles rules files, expression // fields, etc, changes. static sinsp_version engine_version(); // Engine version used to be represented as a simple progressive // number. With the new semver schema, the number now represents // the semver minor number. This function converts the legacy version // number to the new semver schema. static inline sinsp_version get_implicit_version(uint32_t minor) { return rule_loader::reader::get_implicit_engine_version(minor); } // Print to stdout (using printf) a description of each field supported by this engine. // If source is non-empty, only fields for the provided source are printed. void list_fields(const std::string &source, bool verbose, bool names_only, output_format format) const; // Provide an alternate rule reader, collector, and compiler // to compile any rules provided via load_rules* void set_rule_reader(std::shared_ptr reader); std::shared_ptr get_rule_reader(); void set_rule_collector(std::shared_ptr collector); std::shared_ptr get_rule_collector(); void set_rule_compiler(std::shared_ptr compiler); std::shared_ptr get_rule_compiler(); // // Load rules and returns a result object. // std::unique_ptr load_rules(const std::string &rules_content, const std::string &name); // // Enable/Disable any rules matching the provided substring. // If the substring is "", all rules are enabled/disabled. // When provided, enable/disable these rules in the // context of the provided ruleset. The ruleset (id) can later // be passed as an argument to process_event(). This allows // for different sets of rules being active at once. // The rules are matched against the rulesets of all the defined sources. // 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); // Like enable_rule, but wildcards are supported and substrings are not matched void enable_rule_wildcard(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_wildcard(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 &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 &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. // This does not change the engine configuration nor the loaded/enabled rule // setup, and does not affect the functional behavior. // Internally, this can be used to release unused resources before starting // processing events with process_event(). // void complete_rule_loading() const; // Only load rules having this priority or more severe. void set_min_priority(falco_common::priority_type priority); // // Return the ruleset id corresponding to this ruleset name, // creating a new one if necessary. If you provide any ruleset // to enable_rule/enable_rule_by_tag(), you should look up the // ruleset id and pass it to process_event(). // uint16_t find_ruleset_id(const std::string &ruleset); // // Return the number of falco rules enabled for the provided ruleset // across all sources. // uint64_t num_rules_for_ruleset(const std::string &ruleset); // // Print details on the given rule. If rule is NULL, print // details on all rules. // nlohmann::json describe_rule(std::string *rule_name, const std::vector> &plugins) const; // // Return const /ref to rules stored in the Falco engine. // inline const indexed_vector &get_rules() const { return m_rules; } // // Print statistics on how many events matched each rule. // void print_stats() const; // // Return const /ref to stats_manager to access current rules stats (how many events matched // each rule so far). // const stats_manager &get_rule_stats_manager() const; // // Set the sampling ratio, which can affect which events are // matched against the set of rules. // void set_sampling_ratio(uint32_t sampling_ratio); // // Set the sampling ratio multiplier, which can affect which // events are matched against the set of rules. // void set_sampling_multiplier(double sampling_multiplier); // You can optionally add "extra" output to the end // of all output expressions. You can also choose to replace // %container.info with the extra information or add it to the // end of the expression. This is used in open source falco to // add k8s/container information to outputs when // available. // void add_extra_output_format(const std::string &format, const std::string &source, const std::set &tags, const std::string &rule); // You can optionally add fields that will only show up in the object output (e.g. json) // alongside other output_fields and not in the text message output. You can add two types of // fields: formatted which will act like an additional output format that appears in the output // field void add_extra_output_formatted_field(const std::string &key, const std::string &format, const std::string &source, const std::set &tags, const std::string &rule); void add_extra_output_raw_field(const std::string &key, const std::string &source, const std::set &tags, const std::string &rule); // Represents the result of matching an event against a set of // rules. struct rule_result { sinsp_evt *evt; std::string rule; std::string source; falco_common::priority_type priority_num; std::string format; std::set exception_fields; std::set tags; extra_output_field_t extra_output_fields; bool capture; uint64_t capture_duration_ns; }; // // Given an event, check it against the set of rules in the // engine and if a matching rule is found, return details on // the rule that matched. If no rule matched, returns nullptr. // // This method should be invoked only after having initialized and // configured the engine. In particular, invoking this with a source_idx // not previosly-returned by a call to add_source() would cause a // falco_exception to be thrown. // // This method is thread-safe only with the assumption that every invoker // uses a different source_idx. Moreover, each invoker must not switch // source_idx in subsequent invocations of this method. // Considering that each invoker is related to a unique event source, it // is safe to assume that each invoker will pass a different event // to this method too, since two distinct sources cannot possibly produce // the same event. Lastly, filterchecks and formatters (and their factories) // that are used to populate the conditions for a given event-source // ruleset must not be reused across rulesets of other event sources. // These assumptions guarantee thread-safety because internally the engine // is partitioned by event sources. However, each ruleset assigned to each // event source is not thread-safe of its own, so invoking this method // concurrently with the same source_idx would inherently cause data races // and lead to undefined behavior. std::unique_ptr> process_event(std::size_t source_idx, sinsp_evt *ev, uint16_t ruleset_id, falco_common::rule_matching strategy); // // Wrapper assuming the default ruleset. // // This inherits the same thread-safety guarantees. // std::unique_ptr> process_event(std::size_t source_idx, sinsp_evt *ev, falco_common::rule_matching strategy); // // Configure the engine to support events with the provided // source, with the provided filter factory and formatter factory. // Return source index for fast lookup. // std::size_t add_source(const std::string &source, std::shared_ptr filter_factory, std::shared_ptr formatter_factory); // // Equivalent to above, but allows specifying a ruleset factory // for the newly added source. // std::size_t add_source(const std::string &source, std::shared_ptr filter_factory, std::shared_ptr formatter_factory, std::shared_ptr ruleset_factory); // Return whether or not there is a valid filter/formatter // factory for this source. bool is_source_valid(const std::string &source) const; // // Given a source, return a formatter factory that can create // filters for events of that source. // std::shared_ptr filter_factory_for_source(const std::string &source); std::shared_ptr filter_factory_for_source(std::size_t source_idx); // // Given a source, return a formatter factory that can create // formatters for an event. // std::shared_ptr formatter_factory_for_source( const std::string &source); std::shared_ptr formatter_factory_for_source( std::size_t source_idx); // // Given a source, return a ruleset factory that can create // rulesets for that source. // std::shared_ptr ruleset_factory_for_source(const std::string &source); std::shared_ptr ruleset_factory_for_source(std::size_t source_idx); // Return the filter_ruleset used for a given source. std::shared_ptr ruleset_for_source(const std::string &source); std::shared_ptr ruleset_for_source(std::size_t source_idx); // // Given an event source and ruleset, fill in a bitset // containing the event types for which this ruleset can run. // note(jasondellaluce): this is deprecated, must use the new // typing-improved `enabled_event_codes` and `enabled_sc_codes` instead // todo(jasondellaluce): remove this in future code refactors // void evttypes_for_ruleset(const std::string &source, std::set &evttypes, const std::string &ruleset = s_default_ruleset); // // Given an event source and ruleset, return the set of ppm_sc_codes // for which this ruleset can run and match events. // libsinsp::events::set sc_codes_for_ruleset( const std::string &source, const std::string &ruleset = s_default_ruleset); // // Given an event source and ruleset, return the set of ppm_event_codes // for which this ruleset can run and match events. // libsinsp::events::set event_codes_for_ruleset( const std::string &source, const std::string &ruleset = s_default_ruleset); // // Given a source and output string, return an // sinsp_evt_formatter that can format output strings for an // event. // std::shared_ptr create_formatter(const std::string &source, const std::string &output) const; // The rule loader definition is aliased as it is exactly what we need typedef rule_loader::plugin_version_info::requirement plugin_version_requirement; // // Returns true if the provided list of plugins satisfies all the // version requirements of the internal definitions. The list is represented // as a vectors of pair of strings. In each pair, the first element is // the name of the plugin and the second element is its version. // If false is returned, err is filled with error causing the check failure. // bool check_plugin_requirements(const std::vector &plugins, std::string &err) const; nlohmann::json m_rule_schema; private: // Create a ruleset using the provided factory and set the // engine state funcs for it. std::shared_ptr create_ruleset( std::shared_ptr &ruleset_factory); // Functions to retrieve state from this engine void fill_engine_state_funcs(filter_ruleset::engine_state_funcs &engine_state); filter_ruleset::engine_state_funcs m_engine_state; indexed_vector m_sources; inline const falco_source *find_source(std::size_t index) { const falco_source *source; if(index == m_syscall_source_idx) { if(m_syscall_source == NULL) { m_syscall_source = m_sources.at(m_syscall_source_idx); if(!m_syscall_source) { throw falco_exception("Unknown event source index " + std::to_string(index)); } } source = m_syscall_source; } else { source = m_sources.at(index); if(!source) { throw falco_exception("Unknown event source index " + std::to_string(index)); } } return source; } inline const falco_source *find_source(const std::string &name) const { auto ret = m_sources.at(name); if(!ret) { throw falco_exception("Unknown event source " + name); } return ret; } // To allow the engine to be extremely fast for syscalls (can // be > 1M events/sec), we save the syscall source/source_idx // separately and check it explicitly in process_event() const falco_source *m_syscall_source; std::atomic m_syscall_source_idx; // // Determine whether the given event should be matched at all // against the set of rules, given the current sampling // ratio/multiplier. // inline bool should_drop_evt() const; // Retrieve json details from rules, macros, lists void get_json_details(nlohmann::json &out, const falco_rule &r, const rule_loader::rule_info &info, const std::vector> &plugins) const; void get_json_details(nlohmann::json &out, const falco_macro &m, const rule_loader::macro_info &info, const std::vector> &plugins) const; void get_json_details(nlohmann::json &out, const falco_list &l, const rule_loader::list_info &info, const std::vector> &plugins) const; void get_json_evt_types(nlohmann::json &out, const std::string &source, libsinsp::filter::ast::expr *ast) const; void get_json_used_plugins(nlohmann::json &out, const std::string &source, const std::unordered_set &evttypes, const std::unordered_set &fields, const std::vector> &plugins) const; indexed_vector m_rules; std::shared_ptr m_rule_reader; std::shared_ptr m_rule_collector; std::shared_ptr m_rule_compiler; stats_manager m_rule_stats_manager; uint16_t m_next_ruleset_id; std::map m_known_rulesets; falco_common::priority_type m_min_priority; std::unique_ptr m_last_compile_output; // // Here's how the sampling ratio and multiplier influence // whether or not an event is dropped in // should_drop_evt(). The intent is that m_sampling_ratio is // generally changing external to the engine e.g. in the main // inspector class based on how busy the inspector is. A // sampling ratio implies no dropping. Values > 1 imply // increasing levels of dropping. External to the engine, the // sampling ratio results in events being dropped at the // kernel/inspector interface. // // The sampling multiplier is an amplification to the sampling // factor in m_sampling_ratio. If 0, no additional events are // dropped other than those that might be dropped by the // kernel/inspector interface. If 1, events that make it past // the kernel module are subject to an additional level of // dropping at the falco engine, scaling with the sampling // ratio in m_sampling_ratio. // uint32_t m_sampling_ratio; double m_sampling_multiplier; static const std::string s_default_ruleset; uint32_t m_default_ruleset_id; std::vector m_extra_output_format; std::vector m_extra_output_fields; }; ================================================ FILE: userspace/engine/falco_engine_version.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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. */ #define __FALCO_ENGINE_STRINGIFY1(str) #str #define __FALCO_ENGINE_STRINGIFY(str) __FALCO_ENGINE_STRINGIFY1(str) // The version of this Falco engine #define FALCO_ENGINE_VERSION_MAJOR 0 #define FALCO_ENGINE_VERSION_MINOR 60 #define FALCO_ENGINE_VERSION_PATCH 0 #define FALCO_ENGINE_VERSION \ __FALCO_ENGINE_STRINGIFY(FALCO_ENGINE_VERSION_MAJOR) \ "." __FALCO_ENGINE_STRINGIFY(FALCO_ENGINE_VERSION_MINOR) "." __FALCO_ENGINE_STRINGIFY( \ FALCO_ENGINE_VERSION_PATCH) // This is the result of running the following command: // FALCO="falco -c ./falco.yaml" // echo $($FALCO --version | grep 'Engine:' | awk '{print $2}') $(echo $($FALCO --version | grep // 'Schema version:' | awk '{print $3}') $($FALCO --list --markdown | grep '^`' | sort) $($FALCO // --list-events | sort) | sha256sum) // 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 "17c1ac99576c032a58895a10f7091cf777008a1059b7f1bff3c78a6451b17fdf" ================================================ FILE: userspace/engine/falco_load_result.cpp ================================================ // 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 "falco_load_result.h" static const std::string error_codes[] = {"LOAD_ERR_FILE_READ", "LOAD_ERR_YAML_PARSE", "LOAD_ERR_YAML_VALIDATE", "LOAD_ERR_COMPILE_CONDITION", "LOAD_ERR_COMPILE_OUTPUT", "LOAD_ERR_VALIDATE", "LOAD_ERR_EXTENSION"}; const std::string& falco::load_result::error_code_str(error_code ec) { return error_codes[ec]; } static const std::string error_strings[] = {"File read error", "YAML parse error", "Error validating internal structure of YAML file", "Error compiling condition", "Error compiling output", "Error validating rule/macro/list/exception objects", "Error in extension item"}; const std::string& falco::load_result::error_str(error_code ec) { return error_strings[ec]; } static const std::string error_descs[] = { "This occurs when falco can not read a given file. Check permissions and whether the file " "exists.", "This occurs when the rules content is not valid YAML.", "This occurs when the internal structure of the YAML file is incorrect. Examples include " "not consisting of a sequence of maps, a given rule/macro/list item not having required " "keys, values not having the right type (e.g. the items property of a list not being a " "sequence), etc.", "This occurs when a condition string can not be compiled to a filter object.", "This occurs when an output string can not be compiled to an output object.", "This occurs when a rule/macro/list item is incorrect. Examples include a condition field " "referring to an undefined macro, falco engine/plugin version mismatches, items with " "append without any existing item, exception fields/comps having different lengths, etc.", "This occurs when there is an error in an extension item"}; const std::string& falco::load_result::error_desc(error_code ec) { return error_strings[ec]; } static const std::string warning_codes[] = {"LOAD_UNKNOWN_SOURCE", "LOAD_UNSAFE_NA_CHECK", "LOAD_NO_EVTTYPE", "LOAD_UNKNOWN_FILTER", "LOAD_UNUSED_MACRO", "LOAD_UNUSED_LIST", "LOAD_UNKNOWN_ITEM", "LOAD_DEPRECATED_ITEM", "LOAD_WARNING_EXTENSION", "LOAD_APPEND_NO_VALUES", "LOAD_EXCEPTION_NAME_NOT_UNIQUE", "LOAD_INVALID_MACRO_NAME", "LOAD_INVALID_LIST_NAME", "LOAD_COMPILE_CONDITION", "LOAD_UNKNOWN_KEY"}; // Compile-time check to ensure warning_codes array has the correct size static_assert(std::size(warning_codes) == static_cast(falco::load_result::warning_code::LOAD_UNKNOWN_KEY) + 1, "warning_codes array size must match the last warning_code enum value + 1"); const std::string& falco::load_result::warning_code_str(warning_code wc) { return warning_codes[static_cast(wc)]; } static const std::string warning_strings[] = {"Unknown event source", "Unsafe comparison in condition", "Condition has no event-type restriction", "Unknown field or event-type in condition or output", "Unused macro", "Unused list", "Unknown rules file item", "Used deprecated item", "Warning in extension item", "Overriding/appending with no values", "Multiple exceptions defined with the same name", "Invalid macro name", "Invalid list name", "Warning in rule condition", "Unknown key in item definition"}; // Compile-time check to ensure warning_strings array has the correct size static_assert(std::size(warning_strings) == static_cast(falco::load_result::warning_code::LOAD_UNKNOWN_KEY) + 1, "warning_strings array size must match the last warning_code enum value + 1"); const std::string& falco::load_result::warning_str(warning_code wc) { return warning_strings[static_cast(wc)]; } static const std::string warning_descs[] = { "A rule has a unknown event source. This can occur when reading rules content without " "having a corresponding plugin loaded, etc. The rule will be silently ignored.", "Comparing a field value with is unsafe and can lead to unpredictable behavior of the " "rule condition. If you need to check for the existence of a field, consider using the " "'exists' operator instead.", "A rule condition matches too many evt.type values. This has a significant performance " "penalty. Make the condition more specific by adding an evt.type field or further " "restricting the number of evt.type values in the condition.", "A rule condition or output refers to a field or evt.type that does not exist. This is " "normally an error, but if a rule has a skip-if-unknown-filter property, the error is " "downgraded to a warning.", "A macro is defined in the rules content but is not used by any other macro or rule.", "A list is defined in the rules content but is not used by any other list, macro, or rule.", "An unknown top-level object is in the rules content. It will be ignored.", "A deprecated item is employed by lists, macros, or rules.", "An extension item has a warning", "A rule exception is overriding/appending with no values", "A rule is defining multiple exceptions with the same name", "A macro is defined with an invalid name", "A list is defined with an invalid name", "A rule condition or output have been parsed with a warning", "An item in the rules content contains an unrecognized key. The key will be ignored. " "This may indicate a typo or a property placed on the wrong item type."}; // Compile-time check to ensure warning_descs array has the correct size static_assert(std::size(warning_descs) == static_cast(falco::load_result::warning_code::LOAD_UNKNOWN_KEY) + 1, "warning_descs array size must match the last warning_code enum value + 1"); const std::string& falco::load_result::warning_desc(warning_code wc) { return warning_descs[static_cast(wc)]; } static const std::string deprecated_fields[] = {"evt.dir", "evt.latency", "evt.latency.s", "evt.latency.ns", "evt.latency.human", "evt.wait_latency"}; // Compile-time check to ensure deprecated_fields array has the correct size static_assert( std::size(deprecated_fields) == static_cast(falco::load_result::deprecated_field::DEPRECATED_FIELD_NOT_FOUND), "deprecated_fields array size must match DEPRECATED_FIELD_NOT_FOUND enum value"); const std::string& falco::load_result::deprecated_field_str(deprecated_field df) { return deprecated_fields[static_cast(df)]; } // Shared description suffix for latency fields static const std::string latency_field_desc_suffix = "field is not available due to the drop of enter events."; static const std::string deprecated_field_descs[] = { "due to the drop of enter events, 'evt.dir = <' always evaluates to true, and 'evt.dir = " ">' always evaluates to false. The rule expression can be simplified by removing the " "condition on 'evt.dir'", latency_field_desc_suffix, latency_field_desc_suffix, latency_field_desc_suffix, latency_field_desc_suffix, latency_field_desc_suffix}; // Compile-time check to ensure deprecated_field_descs array has the correct size static_assert( std::size(deprecated_field_descs) == static_cast(falco::load_result::deprecated_field::DEPRECATED_FIELD_NOT_FOUND), "deprecated_field_descs array size must match DEPRECATED_FIELD_NOT_FOUND enum value"); const std::string& falco::load_result::deprecated_field_desc(deprecated_field df) { return deprecated_field_descs[static_cast(df)]; } falco::load_result::deprecated_field falco::load_result::deprecated_field_from_str( const std::string& f) { return falco::load_result::deprecated_field( std::find(std::begin(deprecated_fields), std::end(deprecated_fields), f) - std::begin(deprecated_fields)); } ================================================ FILE: userspace/engine/falco_load_result.h ================================================ // 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. */ #pragma once #include #include #include namespace falco { // Represents the result of loading a rules file. class load_result { public: enum error_code { LOAD_ERR_FILE_READ = 0, LOAD_ERR_YAML_PARSE, LOAD_ERR_YAML_VALIDATE, LOAD_ERR_COMPILE_CONDITION, LOAD_ERR_COMPILE_OUTPUT, LOAD_ERR_VALIDATE, LOAD_ERR_EXTENSION }; // The error code as a string static const std::string& error_code_str(error_code ec); // A short string representation of the error static const std::string& error_str(error_code ec); // A longer description of what the error represents and the // impact. static const std::string& error_desc(error_code ec); virtual ~load_result() = default; enum class warning_code { LOAD_UNKNOWN_SOURCE = 0, LOAD_UNSAFE_NA_CHECK, LOAD_NO_EVTTYPE, LOAD_UNKNOWN_FILTER, LOAD_UNUSED_MACRO, LOAD_UNUSED_LIST, LOAD_UNKNOWN_ITEM, LOAD_DEPRECATED_ITEM, LOAD_WARNING_EXTENSION, LOAD_APPEND_NO_VALUES, LOAD_EXCEPTION_NAME_NOT_UNIQUE, LOAD_INVALID_MACRO_NAME, LOAD_INVALID_LIST_NAME, LOAD_COMPILE_CONDITION, LOAD_UNKNOWN_KEY }; // The warning code as a string static const std::string& warning_code_str(warning_code ec); // A short string representation of the warning static const std::string& warning_str(warning_code ec); // A longer description of what the warning represents and the // impact. static const std::string& warning_desc(warning_code ec); enum class deprecated_field { DEPRECATED_FIELD_EVT_DIR, DEPRECATED_FIELD_EVT_LATENCY, DEPRECATED_FIELD_EVT_LATENCY_S, DEPRECATED_FIELD_EVT_LATENCY_NS, DEPRECATED_FIELD_EVT_LATENCY_HUMAN, DEPRECATED_FIELD_EVT_WAIT_LATENCY, DEPRECATED_FIELD_NOT_FOUND }; // The deprecated field as a string static const std::string& deprecated_field_str(deprecated_field df); // A longer description of what the deprecated field represents and the // impact. static const std::string& deprecated_field_desc(deprecated_field df); // Return the deprecated field from a field string name, or DEPRECATED_FIELD_NOT_FOUND if the // field is not deprecated static deprecated_field deprecated_field_from_str(const std::string& f); // If true, the rules were loaded successfully and can be used // against events. If false, there were one or more // errors--use one of the as_xxx methods to return information // about why the rules could not be loaded. virtual bool successful() = 0; // If true, there were one or more warnings. successful() and // has_warnings() can both be true if there were only warnings. virtual bool has_warnings() = 0; // Return json schema validation status. virtual std::string schema_validation() = 0; // This represents a set of rules contents as a mapping from // rules content name (usually filename) to rules content. The // rules content is actually a reference to the actual string // to avoid copies. Using reference_wrapper allows the // reference to be held in the stl map (bare references can't // be copied/assigned, but reference_wrappers can). // // It's used in the as_string/as_json() methods below. typedef std::map> rules_contents_t; // This contains a human-readable version of the result, // suitable for display to end users. // // The provided rules_contents_t should map from content name // to rules content (reference) for each rules_content that has // been passed to rule_loader::compile() or // rule_reader::load(). // // When verbose is true, the returned value has full details // on the result including document locations/context. // // When verbose is false, the returned value is a short string // with the success value and a list of // errors/warnings. Suitable for simple one-line display. virtual const std::string& as_string(bool verbose, const rules_contents_t& contents) = 0; // This contains the full result structure as json, suitable // for automated parsing/interpretation downstream. virtual const nlohmann::json& as_json(const rules_contents_t& contents) = 0; }; } // namespace falco ================================================ FILE: userspace/engine/falco_rule.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #include #include #include "falco_common.h" #include /*! \brief Represents a list in the Falco Engine. The rule ID must be unique across all the lists loaded in the engine. */ struct falco_list { falco_list(): used(false), id(0) {} falco_list(falco_list&&) = default; falco_list& operator=(falco_list&&) = default; falco_list(const falco_list&) = default; falco_list& operator=(const falco_list&) = default; ~falco_list() = default; bool operator==(const falco_list& rhs) const { return (this->used == rhs.used && this->id == rhs.id && this->name == rhs.name && this->items == rhs.items); } bool used; std::size_t id; std::string name; std::vector items; }; /*! \brief Represents a macro in the Falco Engine. The rule ID must be unique across all the macros loaded in the engine. */ struct falco_macro { falco_macro(): used(false), id(0) {} falco_macro(falco_macro&&) = default; falco_macro& operator=(falco_macro&&) = default; falco_macro(const falco_macro&) = default; falco_macro& operator=(const falco_macro&) = default; ~falco_macro() = default; bool operator==(const falco_macro& rhs) const { // Note this only ensures that the shared_ptrs are // pointing to the same underlying memory, not that // they are logically equal. return (this->used == rhs.used && this->id == rhs.id && this->name == rhs.name && this->condition.get() == rhs.condition.get()); } bool used; std::size_t id; std::string name; std::shared_ptr condition; }; /*! \brief Represents a rule in the Falco Engine. The rule ID must be unique across all the rules loaded in the engine. */ struct falco_rule { falco_rule(): id(0), priority(falco_common::PRIORITY_DEBUG), capture(false), capture_duration(0) {} falco_rule(falco_rule&&) = default; falco_rule& operator=(falco_rule&&) = default; falco_rule(const falco_rule&) = default; falco_rule& operator=(const falco_rule&) = default; ~falco_rule() = default; bool operator==(const falco_rule& rhs) const { // Note this only ensures that the shared_ptrs are // pointing to the same underlying memory, not that // they are logically equal. return (this->id == rhs.id && this->source == rhs.source && this->name == rhs.name && this->description == rhs.description && this->output == rhs.output && this->tags == rhs.tags && this->exception_fields == rhs.exception_fields && this->priority == rhs.priority && this->capture == rhs.capture && this->capture_duration == rhs.capture_duration && this->condition.get() == rhs.condition.get() && this->filter.get() == rhs.filter.get()); } std::size_t id; std::string source; std::string name; std::string description; std::string output; extra_output_field_t extra_output_fields; std::set tags; std::set exception_fields; falco_common::priority_type priority; bool capture; uint32_t capture_duration; std::shared_ptr condition; std::shared_ptr filter; }; ================================================ FILE: userspace/engine/falco_source.h ================================================ // 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. */ #pragma once #include #include "filter_ruleset.h" /*! \brief Represents a given data source used by the engine. The ruleset of a source should be created through the ruleset factory of the same data source. */ struct falco_source { falco_source() = default; falco_source(falco_source&&) = default; falco_source& operator=(falco_source&&) = default; falco_source(const falco_source& s): name(s.name), ruleset(s.ruleset), ruleset_factory(s.ruleset_factory), filter_factory(s.filter_factory), formatter_factory(s.formatter_factory) {}; falco_source& operator=(const falco_source& s) { name = s.name; ruleset = s.ruleset; ruleset_factory = s.ruleset_factory; filter_factory = s.filter_factory; formatter_factory = s.formatter_factory; return *this; }; std::string name; std::shared_ptr ruleset; std::shared_ptr ruleset_factory; std::shared_ptr filter_factory; std::shared_ptr formatter_factory; // Used by the filter_ruleset interface. Filled in when a rule // matches an event. mutable std::vector m_rules; inline bool is_valid_lhs_field(const std::string& field) const { // if there's at least one parenthesis we may be parsing a field // wrapped inside one or more transformers. In those cases, the most // rigorous analysis we can do is compiling a simple filter using // the field as left-hand side of a comparison, and see if any error // occurs. if(field.find('(') != std::string::npos) { try { auto filter = field; filter.append(" exists"); sinsp_filter_compiler(filter_factory, filter).compile(); return true; } catch(...) { return false; } } // otherwise, simply attempt creating a filtercheck with the given // field name and see if we succeed return filter_factory->new_filtercheck(field.c_str()) != nullptr; } }; ================================================ FILE: userspace/engine/falco_utils.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2016-2018 The Falco Authors. This file is part of falco. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "falco_utils.h" #include #include #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) #include #endif #include #include #include #include #define RGX_PROMETHEUS_TIME_DURATION \ "^((?P[0-9]+)y)?((?P[0-9]+)w)?((?P[0-9]+)d)?((?P[0-9]+)h)?((?P[0-9]+)m)?((?P<" \ "s>[0-9]+)s)?((?P[0-9]+)ms)?$" // using pre-compiled regex static re2::RE2 s_rgx_prometheus_time_duration(RGX_PROMETHEUS_TIME_DURATION); // Prometheus time durations: // https://prometheus.io/docs/prometheus/latest/querying/basics/#time-durations #define PROMETHEUS_UNIT_Y "y" ///> assuming a year has always 365d #define PROMETHEUS_UNIT_W "w" ///> assuming a week has always 7d #define PROMETHEUS_UNIT_D "d" ///> assuming a day has always 24h #define PROMETHEUS_UNIT_H "h" ///> hour #define PROMETHEUS_UNIT_M "m" ///> minute #define PROMETHEUS_UNIT_S "s" ///> second #define PROMETHEUS_UNIT_MS "ms" ///> millisecond // standard time unit conversions to milliseconds #define ONE_MS_TO_MS 1UL #define ONE_SECOND_TO_MS 1000UL #define ONE_MINUTE_TO_MS ONE_SECOND_TO_MS * 60UL #define ONE_HOUR_TO_MS ONE_MINUTE_TO_MS * 60UL #define ONE_DAY_TO_MS ONE_HOUR_TO_MS * 24UL #define ONE_WEEK_TO_MS ONE_DAY_TO_MS * 7UL #define ONE_YEAR_TO_MS ONE_DAY_TO_MS * 365UL namespace falco { namespace utils { uint64_t parse_prometheus_interval(std::string interval_str) { uint64_t interval = 0; /* Sanitize user input, remove possible whitespaces. */ interval_str.erase(remove_if(interval_str.begin(), interval_str.end(), isspace), interval_str.end()); if(!interval_str.empty()) { re2::StringPiece input(interval_str); std::string args[14]; re2::RE2::Arg arg0(&args[0]); re2::RE2::Arg arg1(&args[1]); re2::RE2::Arg arg2(&args[2]); re2::RE2::Arg arg3(&args[3]); re2::RE2::Arg arg4(&args[4]); re2::RE2::Arg arg5(&args[5]); re2::RE2::Arg arg6(&args[6]); re2::RE2::Arg arg7(&args[7]); re2::RE2::Arg arg8(&args[8]); re2::RE2::Arg arg9(&args[9]); re2::RE2::Arg arg10(&args[10]); re2::RE2::Arg arg11(&args[11]); re2::RE2::Arg arg12(&args[12]); re2::RE2::Arg arg13(&args[13]); const re2::RE2::Arg* const matches[14] = {&arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, &arg10, &arg11, &arg12, &arg13}; const std::map& named_groups = s_rgx_prometheus_time_duration.NamedCapturingGroups(); int num_groups = s_rgx_prometheus_time_duration.NumberOfCapturingGroups(); re2::RE2::FullMatchN(input, s_rgx_prometheus_time_duration, matches, num_groups); static const char* all_prometheus_units[7] = {PROMETHEUS_UNIT_Y, PROMETHEUS_UNIT_W, PROMETHEUS_UNIT_D, PROMETHEUS_UNIT_H, PROMETHEUS_UNIT_M, PROMETHEUS_UNIT_S, PROMETHEUS_UNIT_MS}; static const uint64_t all_prometheus_time_conversions[7] = {ONE_YEAR_TO_MS, ONE_WEEK_TO_MS, ONE_DAY_TO_MS, ONE_HOUR_TO_MS, ONE_MINUTE_TO_MS, ONE_SECOND_TO_MS, ONE_MS_TO_MS}; for(size_t i = 0; i < sizeof(all_prometheus_units) / sizeof(const char*); i++) { const auto& group_it = named_groups.find(all_prometheus_units[i]); if(group_it != named_groups.end()) { uint64_t cur_interval = 0; std::string cur_interval_str = args[group_it->second - 1]; if(!cur_interval_str.empty()) { cur_interval = std::stoull(cur_interval_str, nullptr, 0); } if(cur_interval > 0) { interval += cur_interval * all_prometheus_time_conversions[i]; } } } } return interval; } #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) std::string calculate_file_sha256sum(const std::string& filename) { std::ifstream file(filename, std::ios::binary); if(!file.is_open()) { return ""; } std::unique_ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr); constexpr size_t buffer_size = 4096; char buffer[buffer_size]; while(file.read(buffer, buffer_size)) { EVP_DigestUpdate(ctx.get(), buffer, buffer_size); } EVP_DigestUpdate(ctx.get(), buffer, file.gcount()); std::vector digest(EVP_MD_size(EVP_sha256())); EVP_DigestFinal_ex(ctx.get(), digest.data(), nullptr); std::ostringstream ss; for(auto& c : digest) { ss << std::hex << std::setw(2) << std::setfill('0') << (int)c; } return ss.str(); } #endif std::string sanitize_rule_name(const std::string& name) { std::string sanitized_name = name; RE2::GlobalReplace(&sanitized_name, "[^a-zA-Z0-9_:]", "_"); RE2::GlobalReplace(&sanitized_name, "_+", "_"); if(!sanitized_name.empty() && sanitized_name.back() == '_') { sanitized_name.pop_back(); } return sanitized_name; } std::string wrap_text(const std::string& in, uint32_t indent, uint32_t line_len) { std::istringstream is(in); std::ostringstream os; std::string word; uint32_t len = 0; while(is >> word) { if((len + word.length() + 1) <= (line_len - indent)) { len += word.length() + 1; } else { os << std::endl; os << std::left << std::setw(indent) << " "; len = word.length() + 1; } os << word << " "; } return os.str(); } uint32_t hardware_concurrency() { auto hc = std::thread::hardware_concurrency(); return hc ? hc : 1; } void readfile(const std::string& filename, std::string& data) { std::ifstream file(filename, std::ios::in); if(file.is_open()) { std::stringstream ss; ss << file.rdbuf(); file.close(); data = ss.str(); } return; } bool matches_wildcard(const std::string& pattern, const std::string& s) { std::string::size_type star_pos = pattern.find("*"); if(star_pos == std::string::npos) { // regular match (no wildcards) return pattern == s; } if(star_pos == 0) { // wildcard at the beginning "*something*..." std::string::size_type next_pattern_start = pattern.find_first_not_of("*"); if(next_pattern_start == std::string::npos) { // pattern was just a sequence of stars *, **, ***, ... . This always matches. return true; } std::string next_pattern = pattern.substr(next_pattern_start); std::string to_find = next_pattern.substr(0, next_pattern.find("*")); std::string::size_type lit_pos = s.find(to_find); if(lit_pos == std::string::npos) { return false; } return matches_wildcard(next_pattern.substr(to_find.size()), s.substr(lit_pos + to_find.size())); } else { // wildcard at the end or in the middle "something*else*..." if(pattern.substr(0, star_pos) != s.substr(0, star_pos)) { return false; } return matches_wildcard(pattern.substr(star_pos), s.substr(star_pos)); } } namespace network { bool is_unix_scheme(const std::string& url) { return sinsp_utils::startswith(url, UNIX_SCHEME); } } // namespace network } // namespace utils } // namespace falco ================================================ FILE: userspace/engine/falco_utils.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2016-2018 The Falco Authors. This file is part of falco. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #include #include namespace falco::utils { uint64_t parse_prometheus_interval(std::string interval_str); #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) std::string calculate_file_sha256sum(const std::string& filename); #endif std::string sanitize_rule_name(const std::string& name); std::string wrap_text(const std::string& in, uint32_t indent, uint32_t linelen); void readfile(const std::string& filename, std::string& data); uint32_t hardware_concurrency(); bool matches_wildcard(const std::string& pattern, const std::string& s); namespace network { static const std::string UNIX_SCHEME("unix://"); bool is_unix_scheme(const std::string& url); } // namespace network } // namespace falco::utils ================================================ FILE: userspace/engine/field_formatter.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2026 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 "field_formatter.h" #include "formats.h" using namespace falco; // Factory method std::unique_ptr FieldFormatter::create(output_format format, bool verbose) { switch(format) { case output_format::JSON: return std::make_unique(verbose); case output_format::MARKDOWN: return std::make_unique(verbose); case output_format::TEXT: default: return std::make_unique(verbose); } } // ============================================================================ // TextFieldFormatter implementation // ============================================================================ TextFieldFormatter::TextFieldFormatter(bool verbose): m_verbose(verbose) {} void TextFieldFormatter::begin() { // Nothing to do for text format } void TextFieldFormatter::print_fieldclass( const sinsp_filter_factory::filter_fieldclass_info& fld_class, const std::set& event_sources) { printf("%s\n", fld_class.as_string(m_verbose, event_sources).c_str()); } void TextFieldFormatter::print_field_name(const std::string& name) { printf("%s\n", name.c_str()); } void TextFieldFormatter::end() { // Nothing to do for text format } // ============================================================================ // MarkdownFieldFormatter implementation // ============================================================================ MarkdownFieldFormatter::MarkdownFieldFormatter(bool verbose): m_verbose(verbose) {} void MarkdownFieldFormatter::begin() { // Nothing to do for markdown format } void MarkdownFieldFormatter::print_fieldclass( const sinsp_filter_factory::filter_fieldclass_info& fld_class, const std::set& event_sources) { printf("%s\n", fld_class.as_markdown(event_sources).c_str()); } void MarkdownFieldFormatter::print_field_name(const std::string& name) { printf("%s\n", name.c_str()); } void MarkdownFieldFormatter::end() { // Nothing to do for markdown format } // ============================================================================ // JsonFieldFormatter implementation // ============================================================================ JsonFieldFormatter::JsonFieldFormatter(bool verbose): m_verbose(verbose) {} void JsonFieldFormatter::begin() { m_fieldclasses_array = nlohmann::json::array(); m_fieldnames_array = nlohmann::json::array(); m_has_fieldclasses = false; m_has_fieldnames = false; } void JsonFieldFormatter::print_fieldclass( const sinsp_filter_factory::filter_fieldclass_info& fld_class, const std::set& event_sources) { std::string json_str = fld_class.as_json(event_sources); if(!json_str.empty()) { m_fieldclasses_array.push_back(nlohmann::json::parse(json_str)); m_has_fieldclasses = true; } } void JsonFieldFormatter::print_field_name(const std::string& name) { m_fieldnames_array.push_back(name); m_has_fieldnames = true; } void JsonFieldFormatter::end() { nlohmann::json root; if(m_has_fieldclasses) { root["fieldclasses"] = m_fieldclasses_array; printf("%s\n", root.dump(2).c_str()); } else if(m_has_fieldnames) { root["fieldnames"] = m_fieldnames_array; printf("%s\n", root.dump(2).c_str()); } } ================================================ FILE: userspace/engine/field_formatter.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2026 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #include #include #include #include #include enum class output_format; namespace falco { // Abstract formatter interface for field listing class FieldFormatter { public: virtual ~FieldFormatter() = default; // Initialize formatter virtual void begin() = 0; // Print a field class with its event sources virtual void print_fieldclass(const sinsp_filter_factory::filter_fieldclass_info& fld_class, const std::set& event_sources) = 0; // Print a single field name (for names_only mode) virtual void print_field_name(const std::string& name) = 0; // Finalize and output virtual void end() = 0; // Factory method static std::unique_ptr create(output_format format, bool verbose); }; // Text formatter (default) class TextFieldFormatter : public FieldFormatter { public: explicit TextFieldFormatter(bool verbose); void begin() override; void print_fieldclass(const sinsp_filter_factory::filter_fieldclass_info& fld_class, const std::set& event_sources) override; void print_field_name(const std::string& name) override; void end() override; private: bool m_verbose; }; // Markdown formatter class MarkdownFieldFormatter : public FieldFormatter { public: explicit MarkdownFieldFormatter(bool verbose); void begin() override; void print_fieldclass(const sinsp_filter_factory::filter_fieldclass_info& fld_class, const std::set& event_sources) override; void print_field_name(const std::string& name) override; void end() override; private: bool m_verbose; }; // JSON formatter class JsonFieldFormatter : public FieldFormatter { public: explicit JsonFieldFormatter(bool verbose); void begin() override; void print_fieldclass(const sinsp_filter_factory::filter_fieldclass_info& fld_class, const std::set& event_sources) override; void print_field_name(const std::string& name) override; void end() override; private: bool m_verbose; nlohmann::json m_fieldclasses_array; nlohmann::json m_fieldnames_array; bool m_has_fieldclasses{false}; bool m_has_fieldnames{false}; }; } // namespace falco ================================================ FILE: userspace/engine/filter_details_resolver.cpp ================================================ // 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 "filter_details_resolver.h" #include using namespace libsinsp::filter; static inline std::string get_field_name(const std::string& name, const std::string& arg) { std::string fld = name; if(!arg.empty()) { fld += "[" + arg + "]"; } return fld; } void filter_details::reset() { fields.clear(); macros.clear(); operators.clear(); lists.clear(); evtnames.clear(); transformers.clear(); } void filter_details_resolver::run(ast::expr* filter, filter_details& details) { visitor v(details); filter->accept(&v); } void filter_details_resolver::visitor::visit(ast::and_expr* e) { for(size_t i = 0; i < e->children.size(); i++) { e->children[i]->accept(this); } } void filter_details_resolver::visitor::visit(ast::or_expr* e) { for(size_t i = 0; i < e->children.size(); i++) { e->children[i]->accept(this); } } void filter_details_resolver::visitor::visit(ast::not_expr* e) { e->child->accept(this); } void filter_details_resolver::visitor::visit(ast::list_expr* e) { if(m_expect_list) { for(const auto& item : e->values) { if(m_details.known_lists.find(item) != m_details.known_lists.end()) { m_details.lists.insert(item); } } } if(m_expect_evtname) { for(const auto& item : e->values) { if(m_details.known_lists.find(item) == m_details.known_lists.end()) { m_details.evtnames.insert(item); } } } } void filter_details_resolver::visitor::visit(ast::binary_check_expr* e) { m_last_node_field_name.clear(); m_expect_evtname = false; m_expect_list = false; e->left->accept(this); if(m_last_node_field_name.empty()) { throw std::runtime_error("can't find field info in binary check expression"); } m_details.operators.insert(e->op); m_expect_list = true; m_expect_evtname = m_last_node_field_name == "evt.type" || m_last_node_field_name == "evt.asynctype"; e->right->accept(this); m_expect_evtname = false; m_expect_list = false; } void filter_details_resolver::visitor::visit(ast::unary_check_expr* e) { m_last_node_field_name.clear(); e->left->accept(this); if(m_last_node_field_name.empty()) { throw std::runtime_error("can't find field info in unary check expression"); } m_details.fields.insert(m_last_node_field_name); m_details.operators.insert(e->op); } void filter_details_resolver::visitor::visit(ast::identifier_expr* e) { // todo(jasondellaluce): maybe throw an error if we encounter an unknown macro? if(m_details.known_macros.find(e->identifier) != m_details.known_macros.end()) { m_details.macros.insert(e->identifier); } } void filter_details_resolver::visitor::visit(ast::value_expr* e) { if(m_expect_evtname) { m_details.evtnames.insert(e->value); } } void filter_details_resolver::visitor::visit(ast::field_expr* e) { m_last_node_field_name = get_field_name(e->field, e->arg); m_details.fields.insert(m_last_node_field_name); } void filter_details_resolver::visitor::visit(ast::field_transformer_expr* e) { m_details.transformers.insert(e->transformer); for(auto& value : e->values) { value->accept(this); } } void filter_details_resolver::visitor::visit(ast::transformer_list_expr* e) { for(auto& child : e->children) { child->accept(this); } } ================================================ FILE: userspace/engine/filter_details_resolver.h ================================================ // 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. */ #pragma once #include #include #include #include struct filter_details { // input macros and lists std::unordered_set known_macros; std::unordered_set known_lists; // output details std::unordered_set fields; std::unordered_set macros; std::unordered_set operators; std::unordered_set lists; std::unordered_set evtnames; std::unordered_set transformers; void reset(); }; /*! \brief Helper class for getting details about rules' filters. */ class filter_details_resolver { 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 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 { explicit visitor(filter_details& details): m_details(details), m_expect_list(false), m_expect_evtname(false), m_last_node_field_name() {} visitor(visitor&&) = default; visitor(const visitor&) = delete; void visit(libsinsp::filter::ast::and_expr* e) override; void visit(libsinsp::filter::ast::or_expr* e) override; void visit(libsinsp::filter::ast::not_expr* e) override; void visit(libsinsp::filter::ast::identifier_expr* e) override; void visit(libsinsp::filter::ast::value_expr* e) override; void visit(libsinsp::filter::ast::list_expr* e) override; void visit(libsinsp::filter::ast::unary_check_expr* e) override; void visit(libsinsp::filter::ast::binary_check_expr* e) override; void visit(libsinsp::filter::ast::field_expr* e) override; void visit(libsinsp::filter::ast::field_transformer_expr* e) override; void visit(libsinsp::filter::ast::transformer_list_expr* e) override; filter_details& m_details; bool m_expect_list; bool m_expect_evtname; std::string m_last_node_field_name; }; }; ================================================ FILE: userspace/engine/filter_macro_resolver.cpp ================================================ // 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 "filter_macro_resolver.h" #include "falco_common.h" using namespace libsinsp::filter; bool filter_macro_resolver::run(std::shared_ptr& filter) { m_unknown_macros.clear(); m_resolved_macros.clear(); m_errors.clear(); visitor v(m_errors, m_unknown_macros, m_resolved_macros, m_macros); v.m_node_substitute = nullptr; filter->accept(&v); if(v.m_node_substitute) { filter = std::move(v.m_node_substitute); } return !m_resolved_macros.empty(); } void filter_macro_resolver::set_macro(const std::string& name, const std::shared_ptr& macro) { m_macros[name] = macro; } const std::vector& filter_macro_resolver::get_unknown_macros() const { return m_unknown_macros; } const std::vector& filter_macro_resolver::get_errors() const { return m_errors; } const std::vector& filter_macro_resolver::get_resolved_macros() const { return m_resolved_macros; } void filter_macro_resolver::visitor::visit(ast::and_expr* e) { for(size_t i = 0; i < e->children.size(); i++) { e->children[i]->accept(this); if(m_node_substitute) { e->children[i] = std::move(m_node_substitute); } } m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::or_expr* e) { for(size_t i = 0; i < e->children.size(); i++) { e->children[i]->accept(this); if(m_node_substitute) { e->children[i] = std::move(m_node_substitute); } } m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::not_expr* e) { e->child->accept(this); if(m_node_substitute) { e->child = std::move(m_node_substitute); } m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::list_expr* e) { m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::binary_check_expr* e) { m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::unary_check_expr* e) { m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::value_expr* e) { m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::field_expr* e) { m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::field_transformer_expr* e) { m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::transformer_list_expr* e) { m_node_substitute = nullptr; } void filter_macro_resolver::visitor::visit(ast::identifier_expr* e) { const auto& macro = m_macros.find(e->identifier); if(macro != m_macros.end() && macro->second) // skip null-ptr macros { // note: checks for loop detection const auto& prevref = std::find(m_macros_path.begin(), m_macros_path.end(), macro->first); if(prevref != m_macros_path.end()) { auto msg = "reference loop in macro '" + macro->first + "'"; m_errors.push_back({msg, e->get_pos()}); m_node_substitute = nullptr; m_unknown_macros.push_back({e->identifier, e->get_pos()}); return; } m_macros_path.push_back(macro->first); m_node_substitute = nullptr; auto new_node = ast::clone(macro->second.get()); new_node->accept(this); // new_node might already have set a non-NULL m_node_substitute. // if not, the right substituted is the newly-cloned node. if(!m_node_substitute) { m_node_substitute = std::move(new_node); } m_resolved_macros.push_back({e->identifier, e->get_pos()}); m_macros_path.pop_back(); } else { m_node_substitute = nullptr; m_unknown_macros.push_back({e->identifier, e->get_pos()}); } } ================================================ FILE: userspace/engine/filter_macro_resolver.h ================================================ // 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. */ #pragma once #include #include #include #include #include /*! \brief Helper class for substituting and resolving macro references in parsed filters. */ class filter_macro_resolver { public: /*! \brief Visits a filter AST and substitutes macro references according with all the definitions added through set_macro(), by replacing the reference with a clone of the macro AST. \param filter The filter AST to be processed. Note that the pointer is passed by reference and be transformed in order to apply the substutions. In that case, the old pointer is owned by this class and is deleted automatically. \return true if at least one of the defined macros is resolved */ bool run(std::shared_ptr& filter); /*! \brief Defines a new macro to be substituted in filters. If called multiple times for the same macro name, the previous definition gets overridden. A macro can be undefined by setting a null AST pointer. \param name The name of the macro. \param macro The AST of the macro. */ void set_macro(const std::string& name, const std::shared_ptr& macro); /*! \brief used in get_{resolved,unknown}_macros and get_errors to represent an identifier/string value along with an AST position. */ typedef std::pair value_info; /*! \brief Returns a set containing the names of all the macros substituted during the last invocation of run(). Should be non-empty if the last invocation of run() returned true. */ const std::vector& get_resolved_macros() const; /*! \brief Returns a set containing the names of all the macros that remained unresolved during the last invocation of run(). A macro remains unresolved if it is found inside the processed filter but it was not defined with set_macro(); */ const std::vector& get_unknown_macros() const; /*! \brief Returns a list of errors occurred during the latest invocation of run(). */ const std::vector& get_errors() const; /*! \brief Clears the resolver by resetting all state related to known macros and everything related to the previous resolution run. */ inline void clear() { m_errors.clear(); m_unknown_macros.clear(); m_resolved_macros.clear(); m_macros.clear(); } private: typedef std::unordered_map > macro_defs; struct visitor : public libsinsp::filter::ast::expr_visitor { visitor(std::vector& errors, std::vector& unknown_macros, std::vector& resolved_macros, macro_defs& macros): m_errors(errors), m_unknown_macros(unknown_macros), m_resolved_macros(resolved_macros), m_macros(macros) {} std::vector m_macros_path; std::unique_ptr m_node_substitute; std::vector& m_errors; std::vector& m_unknown_macros; std::vector& m_resolved_macros; macro_defs& m_macros; void visit(libsinsp::filter::ast::and_expr* e) override; void visit(libsinsp::filter::ast::or_expr* e) override; void visit(libsinsp::filter::ast::not_expr* e) override; void visit(libsinsp::filter::ast::identifier_expr* e) override; void visit(libsinsp::filter::ast::value_expr* e) override; void visit(libsinsp::filter::ast::list_expr* e) override; void visit(libsinsp::filter::ast::unary_check_expr* e) override; void visit(libsinsp::filter::ast::binary_check_expr* e) override; void visit(libsinsp::filter::ast::field_expr* e) override; void visit(libsinsp::filter::ast::field_transformer_expr* e) override; void visit(libsinsp::filter::ast::transformer_list_expr* e) override; }; std::vector m_errors; std::vector m_unknown_macros; std::vector m_resolved_macros; macro_defs m_macros; }; ================================================ FILE: userspace/engine/filter_ruleset.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "filter_ruleset.h" void filter_ruleset::set_engine_state(const filter_ruleset::engine_state_funcs& engine_state) { m_engine_state = engine_state; } filter_ruleset::engine_state_funcs& filter_ruleset::get_engine_state() { return m_engine_state; } ================================================ FILE: userspace/engine/filter_ruleset.h ================================================ // 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. */ #pragma once #include "falco_rule.h" #include "rule_loader_compile_output.h" #include #include #include #include /*! \brief Manages a set of rulesets. A ruleset is a set of enabled rules that is able to process events and find matches for those rules. */ class filter_ruleset { public: // A set of functions that can be used to retrieve state from // the falco engine that created this ruleset. struct engine_state_funcs { using ruleset_retriever_func_t = std::function &ruleset)>; ruleset_retriever_func_t get_ruleset; }; enum class match_type { exact, substring, wildcard }; virtual ~filter_ruleset() = default; void set_engine_state(const engine_state_funcs &engine_state); engine_state_funcs &get_engine_state(); /*! \brief Adds a rule and its filtering filter + condition inside the manager. This method only adds the rule inside the internal collection, but does not enable it for any ruleset. The rule must be enabled for one or more rulesets with the enable() or enable_tags() methods. The ast representation of the rule's condition is provided to allow the filter_ruleset object to parse the ast to obtain event types or do other analysis/indexing of the condition. \param rule The rule to be added \param the filter representing the rule's filtering condition. \param condition The AST representing the rule's filtering condition */ virtual void add(const falco_rule &rule, std::shared_ptr filter, std::shared_ptr condition) = 0; /*! \brief Adds all rules contained in the provided rule_loader::compile_output struct. Only those rules with the provided source and those rules with priority >= min_priority should be added. The intent is that this replaces add(). However, we retain add() for backwards compatibility. Any rules added via add() are also added to this ruleset. The default implementation iterates over rules and calls add(), but can be overridden. \param rule The compile output. \param min_priority Only add rules with priority above this priority. \param source Only add rules with source equal to this source. */ virtual void add_compile_output(const rule_loader::compile_output &compile_output, falco_common::priority_type min_priority, const std::string &source) { for(const auto &rule : compile_output.rules) { if(rule.priority <= min_priority && rule.source == source) { add(rule, rule.filter, rule.condition); } } }; /*! \brief Erases the internal state. All rules are disabled in each ruleset, and all the rules defined with add() are removed. */ virtual void clear() = 0; /*! \brief This is meant to be called after all rules have been added with add() and enabled on the given ruleset with enable()/enable_tags(). */ virtual void on_loading_complete() = 0; /*! \brief Processes an event and tries to find a match in a given ruleset. \return true if a match is found, false otherwise \param evt The event to be processed \param match If true is returned, this is filled-out with the first rule that matched the event \param ruleset_id The id of the ruleset to be used */ virtual bool run(sinsp_evt *evt, falco_rule &match, uint16_t ruleset_id) = 0; /*! \brief Processes an event and tries to find a match in a given ruleset. \return true if a match is found, false otherwise \param evt The event to be processed \param matches If true is returned, this is filled-out with all the rules that matched the event \param ruleset_id The id of the ruleset to be used */ virtual bool run(sinsp_evt *evt, std::vector &matches, uint16_t ruleset_id) = 0; /*! \brief Returns the number of rules enabled in a given ruleset \param ruleset_id The id of the ruleset to be used */ virtual uint64_t enabled_count(uint16_t ruleset_id) = 0; /*! \brief Returns the union of the evttypes of all the rules enabled in a given ruleset \param ruleset_id The id of the ruleset to be used \deprecated Must use the new typing-improved `enabled_event_codes` and `enabled_sc_codes` instead \note todo(jasondellaluce): remove this in future refactors */ virtual void enabled_evttypes(std::set &evttypes, uint16_t ruleset) = 0; /*! \brief Returns the all the ppm_sc_codes matching the rules enabled in a given ruleset. \param ruleset_id The id of the ruleset to be used */ virtual libsinsp::events::set enabled_sc_codes(uint16_t ruleset) = 0; /*! \brief Returns the all the ppm_event_codes matching the rules enabled in a given ruleset. \param ruleset_id The id of the ruleset to be used */ virtual libsinsp::events::set enabled_event_codes(uint16_t ruleset) = 0; /*! \brief Find those rules matching the provided substring and enable them in the provided ruleset. \param pattern Pattern used to match rule names. \param match How to match the pattern against rules: - exact: rules that has the same exact name as the pattern are matched - substring: rules having the pattern as a substring in the rule are matched. An empty pattern matches all rules. - wildcard: rules with names that satisfies a wildcard (*) pattern are matched. A "*" pattern matches all rules. Wildcards can appear anywhere in the pattern (e.g. "*hello*world*") \param ruleset_id The id of the ruleset to be used */ virtual void enable(const std::string &pattern, match_type match, uint16_t ruleset_id) = 0; /*! \brief Find those rules matching the provided substring and disable them in the provided ruleset. \param pattern Pattern used to match rule names. \param match How to match the pattern against rules: - exact: rules that has the same exact name as the pattern are matched - substring: rules having the pattern as a substring in the rule are matched. An empty pattern matches all rules. - wildcard: rules with names that satisfies a wildcard (*) pattern are matched. A "*" pattern matches all rules. Wildcards can appear anywhere in the pattern (e.g. "*hello*world*") \param ruleset_id The id of the ruleset to be used */ virtual void disable(const std::string &pattern, match_type match, uint16_t ruleset_id) = 0; /*! \brief Find those rules that have a tag in the set of tags and enable them for the provided ruleset. Note that the enabled status is on the rules, and not the tags--if a rule R has tags (a, b), and you call enable_tags([a]) and then disable_tags([b]), R will be disabled despite the fact it has tag a and was enabled by the first call to enable_tags. \param substring Tags used to match ruless \param ruleset_id The id of the ruleset to be used */ virtual void enable_tags(const std::set &tags, uint16_t ruleset_id) = 0; /*! \brief Find those rules that have a tag in the set of tags and disable them for the provided ruleset. Note that the disabled status is on the rules, and not the tags--if a rule R has tags (a, b), and you call enable_tags([a]) and then disable_tags([b]), R will be disabled despite the fact it has tag a and was enabled by the first call to enable_tags. \param substring Tags used to match ruless \param ruleset_id The id of the ruleset to be used */ virtual void disable_tags(const std::set &tags, uint16_t ruleset_id) = 0; private: engine_state_funcs m_engine_state; }; /*! \brief Represents a factory that creates filter_ruleset objects */ class filter_ruleset_factory { public: virtual ~filter_ruleset_factory() = default; virtual std::shared_ptr new_ruleset() = 0; }; ================================================ FILE: userspace/engine/filter_warning_resolver.cpp ================================================ // 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 #include #include "filter_warning_resolver.h" using namespace falco; static const char* no_value = ""; static inline bool is_unsafe_field(const std::string& f) { return !strncmp(f.c_str(), "ka.", strlen("ka.")) || !strncmp(f.c_str(), "jevt.", strlen("jevt.")); } static inline bool is_equality_operator(const std::string& op) { return op == "==" || op == "=" || op == "!=" || op == "in" || op == "intersects" || op == "pmatch"; } bool filter_warning_resolver::run(const rule_loader::context& ctx, rule_loader::result& res, libsinsp::filter::ast::expr& filter) const { std::set warnings; std::set deprecated_fields; visitor v(warnings, deprecated_fields); v.m_is_equality_check = false; filter.accept(&v); for(auto& w : warnings) { switch(w) { case falco::load_result::warning_code::LOAD_DEPRECATED_ITEM: // add a warning for each deprecated field for(auto& deprecated_field : deprecated_fields) { res.add_deprecated_field_warning( deprecated_field, falco::load_result::deprecated_field_desc(deprecated_field), ctx); } break; default: res.add_warning(w, "", ctx); } } return !warnings.empty(); } void filter_warning_resolver::visitor::visit(libsinsp::filter::ast::binary_check_expr* e) { m_last_node_is_unsafe_field = false; e->left->accept(this); if(m_last_node_is_unsafe_field && is_equality_operator(e->op)) { m_is_equality_check = true; e->right->accept(this); m_is_equality_check = false; } } void filter_warning_resolver::visitor::visit(libsinsp::filter::ast::field_expr* e) { m_last_node_is_unsafe_field = is_unsafe_field(e->field); // Check for deprecated dir field usage if(auto df = falco::load_result::deprecated_field_from_str(e->field); df != falco::load_result::deprecated_field::DEPRECATED_FIELD_NOT_FOUND) { m_deprecated_fields->insert(df); m_warnings->insert(falco::load_result::warning_code::LOAD_DEPRECATED_ITEM); } } void filter_warning_resolver::visitor::visit(libsinsp::filter::ast::value_expr* e) { if(m_is_equality_check && e->value == no_value) { m_warnings->insert(falco::load_result::warning_code::LOAD_UNSAFE_NA_CHECK); } } void filter_warning_resolver::visitor::visit(libsinsp::filter::ast::list_expr* e) { if(m_is_equality_check && std::find(e->values.begin(), e->values.end(), no_value) != e->values.end()) { m_warnings->insert(falco::load_result::warning_code::LOAD_UNSAFE_NA_CHECK); } } ================================================ FILE: userspace/engine/filter_warning_resolver.h ================================================ // 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. */ #pragma once #include #include #include #include #include "falco_common.h" #include "falco_load_result.h" #include "rule_loader.h" /*! \brief Searches for bad practices in filter conditions and generates warning messages */ class filter_warning_resolver { public: /*! \brief Runs the filter warning resolver on a filter AST and adds the warnings to the result object \param ctx The context of the warning \param res The result to add the warnings to \param filter The filter AST to be visited \return true if at least one warning is generated */ bool run(const rule_loader::context& ctx, rule_loader::result& res, libsinsp::filter::ast::expr& filter) const; private: struct visitor : public libsinsp::filter::ast::base_expr_visitor { visitor(std::set& warnings, std::set& deprecated_fields): m_is_equality_check(false), m_last_node_is_unsafe_field(false), m_warnings(&warnings), m_deprecated_fields(&deprecated_fields) {} visitor(visitor&&) = default; visitor& operator=(visitor&&) = default; visitor(const visitor&) = delete; visitor& operator=(const visitor&) = delete; bool m_is_equality_check; bool m_last_node_is_unsafe_field; std::set* m_warnings; std::set* m_deprecated_fields; void visit(libsinsp::filter::ast::value_expr* e) override; void visit(libsinsp::filter::ast::list_expr* e) override; void visit(libsinsp::filter::ast::binary_check_expr* e) override; void visit(libsinsp::filter::ast::field_expr* e) override; }; }; ================================================ FILE: userspace/engine/formats.cpp ================================================ // 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 #include "formats.h" #include "falco_engine.h" falco_formats::falco_formats(std::shared_ptr engine, bool json_include_output_property, bool json_include_tags_property, bool json_include_message_property, bool json_include_output_fields_property, bool time_format_iso_8601): m_falco_engine(engine), m_json_include_output_property(json_include_output_property), m_json_include_tags_property(json_include_tags_property), m_json_include_message_property(json_include_message_property), m_json_include_output_fields_property(json_include_output_fields_property), m_time_format_iso_8601(time_format_iso_8601) {} falco_formats::~falco_formats() {} std::string falco_formats::format_event(sinsp_evt *evt, const std::string &rule, const std::string &source, const std::string &level, const std::string &format, const std::set &tags, const std::string &hostname, const extra_output_field_t &extra_fields) const { std::string prefix_format; std::string message_format = format; if(m_time_format_iso_8601) { prefix_format = "*%evt.time.iso8601: "; } else { prefix_format = "*%evt.time: "; } prefix_format += level; if(message_format[0] != '*') { message_format = "*" + message_format; } auto prefix_formatter = m_falco_engine->create_formatter(source, prefix_format); auto message_formatter = m_falco_engine->create_formatter(source, message_format); // The classic Falco output prefix with time and priority e.g. "13:53:31.726060287: Critical" std::string prefix; prefix_formatter->tostring_withformat(evt, prefix, sinsp_evt_formatter::OF_NORMAL); // The formatted rule message/output std::string message; message_formatter->tostring_withformat(evt, message, sinsp_evt_formatter::OF_NORMAL); // The complete Falco output, e.g. "13:53:31.726060287: Critical Some Event Description // (proc_exe=bash)..." std::string output = prefix + " " + message; if(message_formatter->get_output_format() == sinsp_evt_formatter::OF_NORMAL) { return output; } else if(message_formatter->get_output_format() == sinsp_evt_formatter::OF_JSON) { std::string json_fields_message; std::string json_fields_prefix; // Resolve message fields if(m_json_include_output_fields_property) { message_formatter->tostring(evt, json_fields_message); } // Resolve prefix (e.g. time) fields prefix_formatter->tostring(evt, json_fields_prefix); // For JSON output, the formatter returned a json-as-text // object containing all the fields in the original format // message as well as the event time in ns. Use this to build // a more detailed object containing the event time, rule, // severity, full output, and fields. nlohmann::json event; // Convert the time-as-nanoseconds to a more json-friendly ISO8601. time_t evttime = evt->get_ts() / 1000000000; char time_sec[20]; // sizeof "YYYY-MM-DDTHH:MM:SS" char time_ns[12]; // sizeof ".sssssssssZ" std::string iso8601evttime; strftime(time_sec, sizeof(time_sec), "%FT%T", gmtime(&evttime)); snprintf(time_ns, sizeof(time_ns), ".%09luZ", evt->get_ts() % 1000000000); iso8601evttime = time_sec; iso8601evttime += time_ns; event["time"] = iso8601evttime; event["rule"] = rule; event["priority"] = level; event["source"] = source; event["hostname"] = hostname; if(m_json_include_output_property) { event["output"] = output; } if(m_json_include_tags_property) { event["tags"] = tags; } if(m_json_include_message_property) { event["message"] = message; } if(m_json_include_output_fields_property) { event["output_fields"] = nlohmann::json::parse(json_fields_message); auto prefix_fields = nlohmann::json::parse(json_fields_prefix); if(prefix_fields.is_object()) { for(auto const &el : prefix_fields.items()) { event["output_fields"][el.key()] = el.value(); } } for(auto const &ef : extra_fields) { std::string fformat = ef.second.first; if(fformat.size() == 0) { continue; } if(!(fformat[0] == '*')) { fformat = "*" + fformat; } if(ef.second.second) // raw field { std::string json_field_map; auto field_formatter = m_falco_engine->create_formatter(source, fformat); field_formatter->tostring_withformat(evt, json_field_map, sinsp_evt_formatter::OF_JSON); auto json_obj = nlohmann::json::parse(json_field_map); event["output_fields"][ef.first] = json_obj[ef.first]; } else { event["output_fields"][ef.first] = format_string(evt, fformat, source); } } } return event.dump(); } // should never get here until we only have OF_NORMAL and OF_JSON return "INVALID_OUTPUT_FORMAT"; } std::string falco_formats::format_string(sinsp_evt *evt, const std::string &format, const std::string &source) const { std::string line; std::shared_ptr formatter; formatter = m_falco_engine->create_formatter(source, format); formatter->tostring_withformat(evt, line, sinsp_evt_formatter::OF_NORMAL); return line; } std::map falco_formats::get_field_values( sinsp_evt *evt, const std::string &source, const std::string &format) const { std::shared_ptr formatter; std::string fformat = format; if(fformat[0] != '*') { fformat = "*" + fformat; } formatter = m_falco_engine->create_formatter(source, fformat); std::map ret; if(!formatter->get_field_values(evt, ret)) { throw falco_exception("Could not extract all field values from event"); } return ret; } ================================================ FILE: userspace/engine/formats.h ================================================ // 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. */ #pragma once #include #include #include "falco_engine.h" class falco_formats { public: falco_formats(std::shared_ptr engine, bool json_include_output_property, bool json_include_tags_property, bool json_include_message_property, bool json_include_output_fields_property, bool time_format_iso_8601); virtual ~falco_formats(); std::string format_event(sinsp_evt *evt, const std::string &rule, const std::string &source, const std::string &level, const std::string &format, const std::set &tags, const std::string &hostname, const extra_output_field_t &extra_fields) const; std::string format_string(sinsp_evt *evt, const std::string &format, const std::string &source) const; std::map get_field_values(sinsp_evt *evt, const std::string &source, const std::string &format) const; protected: std::shared_ptr m_falco_engine; bool m_json_include_output_property; bool m_json_include_tags_property; bool m_json_include_message_property; bool m_json_include_output_fields_property; bool m_time_format_iso_8601; }; ================================================ FILE: userspace/engine/indexable_ruleset.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* This describes the interface for an "indexable" ruleset, that is, a * ruleset that can enable/disable abstract filters for various * ruleset ids. * * It's used by evttype_index_ruleset as well as other rulesets that * need the same functionality but don't want to copy the same code. */ #pragma once #include "falco_utils.h" #include "filter_ruleset.h" #include #include #include #include #include #include // A filter_wrapper should implement these methods: // const std::string &filter_wrapper::name(); // const std::set &filter_wrapper::tags(); // const libsinsp::events::set &filter_wrapper::sc_codes(); // const libsinsp::events::set &filter_wrapper::event_codes(); template class indexable_ruleset : public filter_ruleset { public: indexable_ruleset() = default; virtual ~indexable_ruleset() override = default; // Required to implement filter_ruleset void clear() override { for(size_t i = 0; i < m_rulesets.size(); i++) { m_rulesets[i] = std::make_shared(i); } m_filters.clear(); } uint64_t enabled_count(uint16_t ruleset_id) override { while(m_rulesets.size() < (size_t)ruleset_id + 1) { m_rulesets.emplace_back(std::make_shared(m_rulesets.size())); } return m_rulesets[ruleset_id]->num_filters(); } void enabled_evttypes(std::set &evttypes, uint16_t ruleset_id) override { evttypes.clear(); for(const auto &e : enabled_event_codes(ruleset_id)) { evttypes.insert((uint16_t)e); } } libsinsp::events::set enabled_sc_codes(uint16_t ruleset_id) override { if(m_rulesets.size() < (size_t)ruleset_id + 1) { return {}; } return m_rulesets[ruleset_id]->sc_codes(); } libsinsp::events::set enabled_event_codes(uint16_t ruleset_id) override { if(m_rulesets.size() < (size_t)ruleset_id + 1) { return {}; } return m_rulesets[ruleset_id]->event_codes(); } virtual void enable(const std::string &pattern, match_type match, uint16_t ruleset_id) override { enable_disable(pattern, match, true, ruleset_id); } virtual void disable(const std::string &pattern, match_type match, uint16_t ruleset_id) override { enable_disable(pattern, match, false, ruleset_id); } void enable_tags(const std::set &tags, uint16_t ruleset_id) override { enable_disable_tags(tags, true, ruleset_id); } void disable_tags(const std::set &tags, uint16_t ruleset_id) override { enable_disable_tags(tags, false, ruleset_id); } // Note that subclasses do *not* implement run. Instead, they // implement run_wrappers. bool run(sinsp_evt *evt, falco_rule &match, uint16_t ruleset_id) override { if(m_rulesets.size() < (size_t)ruleset_id + 1) { return false; } return m_rulesets[ruleset_id]->run(*this, evt, match); } bool run(sinsp_evt *evt, std::vector &matches, uint16_t ruleset_id) override { if(m_rulesets.size() < (size_t)ruleset_id + 1) { return false; } return m_rulesets[ruleset_id]->run(*this, evt, matches); } typedef std::list> filter_wrapper_list; // Subclasses should call add_wrapper (most likely from // filter_ruleset::add or ::add_compile_output) to add filters. void add_wrapper(std::shared_ptr wrap) { m_filters.insert(wrap); } // If a subclass needs to iterate over all filters, they can // call iterate with this function, which will be called for // all filters. typedef std::function &wrap)> filter_wrapper_func; uint64_t iterate(filter_wrapper_func func) { uint64_t num_filters = 0; for(const auto &ruleset_ptr : m_rulesets) { if(ruleset_ptr) { for(const auto &wrap : ruleset_ptr->get_filters()) { num_filters++; func(wrap); } } } return num_filters; } // A subclass must implement these methods. They are analogous // to run() but take care of selecting filters that match a // ruleset and possibly an event type. virtual bool run_wrappers(sinsp_evt *evt, filter_wrapper_list &wrappers, uint16_t ruleset_id, std::vector &matches) = 0; virtual bool run_wrappers(sinsp_evt *evt, filter_wrapper_list &wrappers, uint16_t ruleset_id, falco_rule &match) = 0; private: // Helper used by enable()/disable() void enable_disable(const std::string &pattern, match_type match, bool enabled, uint16_t ruleset_id) { while(m_rulesets.size() < (size_t)ruleset_id + 1) { m_rulesets.emplace_back(std::make_shared(m_rulesets.size())); } for(const auto &wrap : m_filters) { bool matches; std::string::size_type pos; switch(match) { case match_type::exact: pos = wrap->name().find(pattern); matches = (pattern == "" || (pos == 0 && pattern.size() == wrap->name().size())); break; case match_type::substring: matches = (pattern == "" || (wrap->name().find(pattern) != std::string::npos)); break; case match_type::wildcard: matches = falco::utils::matches_wildcard(pattern, wrap->name()); break; default: // should never happen matches = false; } if(matches) { if(enabled) { m_rulesets[ruleset_id]->add_filter(wrap); } else { m_rulesets[ruleset_id]->remove_filter(wrap); } } } } // Helper used by enable_tags()/disable_tags() void enable_disable_tags(const std::set &tags, bool enabled, uint16_t ruleset_id) { while(m_rulesets.size() < (size_t)ruleset_id + 1) { m_rulesets.emplace_back(std::make_shared(m_rulesets.size())); } for(const auto &wrap : m_filters) { std::set intersect; set_intersection(tags.begin(), tags.end(), wrap->tags().begin(), wrap->tags().end(), inserter(intersect, intersect.begin())); if(!intersect.empty()) { if(enabled) { m_rulesets[ruleset_id]->add_filter(wrap); } else { m_rulesets[ruleset_id]->remove_filter(wrap); } } } } // A group of filters all having the same ruleset class ruleset_filters { public: explicit ruleset_filters(uint16_t ruleset_id): m_ruleset_id(ruleset_id) {} virtual ~ruleset_filters() {}; void add_filter(std::shared_ptr wrap) { if(wrap->event_codes().empty()) { // Should run for all event types add_wrapper_to_list(m_filter_all_event_types, wrap); } else { for(auto &etype : wrap->event_codes()) { if(m_filter_by_event_type.size() <= etype) { m_filter_by_event_type.resize(etype + 1); } add_wrapper_to_list(m_filter_by_event_type[etype], wrap); } } m_filters.insert(wrap); } void remove_filter(std::shared_ptr wrap) { if(wrap->event_codes().empty()) { remove_wrapper_from_list(m_filter_all_event_types, wrap); } else { for(auto &etype : wrap->event_codes()) { if(etype < m_filter_by_event_type.size()) { remove_wrapper_from_list(m_filter_by_event_type[etype], wrap); } } } m_filters.erase(wrap); } uint64_t num_filters() { return m_filters.size(); } inline const std::set> &get_filters() const { return m_filters; } // Evaluate an event against the ruleset and return the first rule // that matched. bool run(indexable_ruleset &ruleset, sinsp_evt *evt, falco_rule &match) { if(evt->get_type() < m_filter_by_event_type.size() && m_filter_by_event_type[evt->get_type()].size() > 0) { if(ruleset.run_wrappers(evt, m_filter_by_event_type[evt->get_type()], m_ruleset_id, match)) { return true; } } // Finally, try filters that are not specific to an event type. if(m_filter_all_event_types.size() > 0) { if(ruleset.run_wrappers(evt, m_filter_all_event_types, m_ruleset_id, match)) { return true; } } return false; } // Evaluate an event against the ruleset and return all the // matching rules. bool run(indexable_ruleset &ruleset, sinsp_evt *evt, std::vector &matches) { if(evt->get_type() < m_filter_by_event_type.size() && m_filter_by_event_type[evt->get_type()].size() > 0) { if(ruleset.run_wrappers(evt, m_filter_by_event_type[evt->get_type()], m_ruleset_id, matches)) { return true; } } // Finally, try filters that are not specific to an event type. if(m_filter_all_event_types.size() > 0) { if(ruleset.run_wrappers(evt, m_filter_all_event_types, m_ruleset_id, matches)) { return true; } } return false; } libsinsp::events::set sc_codes() { libsinsp::events::set res; for(const auto &wrap : m_filters) { res.insert(wrap->sc_codes().begin(), wrap->sc_codes().end()); } return res; } libsinsp::events::set event_codes() { libsinsp::events::set res; for(const auto &wrap : m_filters) { res.insert(wrap->event_codes().begin(), wrap->event_codes().end()); } return res; } private: void add_wrapper_to_list(filter_wrapper_list &wrappers, std::shared_ptr wrap) { // This is O(n) but it's also uncommon // (when loading rules only). auto pos = std::find(wrappers.begin(), wrappers.end(), wrap); if(pos == wrappers.end()) { wrappers.push_back(wrap); } } void remove_wrapper_from_list(filter_wrapper_list &wrappers, std::shared_ptr wrap) { // This is O(n) but it's also uncommon // (when loading rules only). auto pos = std::find(wrappers.begin(), wrappers.end(), wrap); if(pos != wrappers.end()) { wrappers.erase(pos); } } uint16_t m_ruleset_id; // Vector indexes from event type to a set of filters. There can // be multiple filters for a given event type. // NOTE: This is used only when the event sub-type is 0. std::vector m_filter_by_event_type; filter_wrapper_list m_filter_all_event_types; // All filters added. Used to make num_filters() fast. std::set> m_filters; }; // Vector indexes from ruleset id to set of rules. std::vector> m_rulesets; // All filters added. The set of enabled filters is held in m_rulesets std::set> m_filters; }; ================================================ FILE: userspace/engine/indexed_vector.h ================================================ // 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. */ #pragma once #include #include #include /*! \brief Simple wrapper of std::vector that allows random access through both numeric and string indexes with O(1) complexity */ template class indexed_vector { public: indexed_vector() = default; virtual ~indexed_vector() = default; indexed_vector(indexed_vector&&) = default; indexed_vector& operator=(indexed_vector&&) = default; indexed_vector(const indexed_vector&) = default; indexed_vector& operator=(const indexed_vector&) = default; bool operator==(const indexed_vector& rhs) const { return (this->m_entries == rhs.m_entries && this->m_index == rhs.m_index); } /*! \brief Returns the number of elements */ virtual inline size_t size() const { return m_entries.size(); } /*! \brief Returns true if the vector is empty */ virtual inline bool empty() const { return m_entries.empty(); } /*! \brief Removes all the elements */ virtual inline void clear() { m_entries.clear(); m_index.clear(); } /*! \brief Inserts a new element in the vector with a given string index and returns its numeric index. String indexes are unique in the vector. If no element is already present with the given string index, then the provided element is added to the vector and its numeric index is assigned as the next free slot in the vector. Otherwise, the existing element gets overwritten with the contents of the provided one and the numeric index of the existing element is returned. \param entry Element to add in the vector \param index String index of the element to be added in the vector \return The numeric index assigned to the element */ virtual inline size_t insert(const T& entry, const std::string& index) { size_t id; auto prev = m_index.find(index); if(prev != m_index.end()) { id = prev->second; m_entries[id] = entry; return id; } id = m_entries.size(); m_entries.push_back(entry); m_index[index] = id; return id; } /*! \brief Returns a pointer to the element at the given numeric index, or nullptr if no element exists at the given index. */ virtual inline T* at(size_t id) const { if(id < m_entries.size()) { return (T* const)&m_entries[id]; } return nullptr; } /*! \brief Returns a pointer to the element at the given string index, or nullptr if no element exists at the given index. */ virtual inline T* at(const std::string& index) const { auto it = m_index.find(index); if(it != m_index.end()) { return at(it->second); } return nullptr; } virtual inline typename std::vector::iterator begin() { return m_entries.begin(); } virtual inline typename std::vector::iterator end() { return m_entries.end(); } virtual inline typename std::vector::const_iterator begin() const { return m_entries.begin(); } virtual inline typename std::vector::const_iterator end() const { return m_entries.end(); } private: std::vector m_entries; std::unordered_map m_index; }; ================================================ FILE: userspace/engine/logger.cpp ================================================ // 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 #include "logger.h" #include "falco_common.h" falco_logger::level falco_logger::current_level = falco_logger::level::INFO; bool falco_logger::time_format_iso_8601 = false; static sinsp_logger::severity decode_sinsp_severity(const std::string& s) { if(s == "trace") { return sinsp_logger::SEV_TRACE; } else if(s == "debug") { return sinsp_logger::SEV_DEBUG; } else if(s == "info") { return sinsp_logger::SEV_INFO; } else if(s == "notice") { return sinsp_logger::SEV_NOTICE; } else if(s == "warning") { return sinsp_logger::SEV_WARNING; } else if(s == "error") { return sinsp_logger::SEV_ERROR; } else if(s == "critical") { return sinsp_logger::SEV_CRITICAL; } else if(s == "fatal") { return sinsp_logger::SEV_FATAL; } throw falco_exception("Unknown sinsp log severity " + s); } void falco_logger::set_time_format_iso_8601(bool val) { falco_logger::time_format_iso_8601 = val; } void falco_logger::set_level(const std::string& level) { if(level == "emergency") { falco_logger::current_level = falco_logger::level::EMERG; } else if(level == "alert") { falco_logger::current_level = falco_logger::level::ALERT; } else if(level == "critical") { falco_logger::current_level = falco_logger::level::CRIT; } else if(level == "error") { falco_logger::current_level = falco_logger::level::ERR; } else if(level == "warning") { falco_logger::current_level = falco_logger::level::WARNING; } else if(level == "notice") { falco_logger::current_level = falco_logger::level::NOTICE; } else if(level == "info") { falco_logger::current_level = falco_logger::level::INFO; } else if(level == "debug") { falco_logger::current_level = falco_logger::level::DEBUG; } else { throw falco_exception("Unknown log level " + level); } } static std::string s_sinsp_logger_prefix = ""; void falco_logger::set_sinsp_logging(bool enable, const std::string& severity, const std::string& prefix) { if(enable) { s_sinsp_logger_prefix = prefix; libsinsp_logger()->set_severity(decode_sinsp_severity(severity)); libsinsp_logger()->disable_timestamps(); libsinsp_logger()->add_callback_log( [](std::string&& str, const sinsp_logger::severity sev) { // note: using falco_logger::level ensures that the sinsp // logs are always printed by the Falco logger. These // logs are pre-filtered at the sinsp level depending // on the configured severity falco_logger::log(falco_logger::current_level, s_sinsp_logger_prefix + str); }); } else { libsinsp_logger()->remove_callback_log(); } } bool falco_logger::log_stderr = true; bool falco_logger::log_syslog = true; void falco_logger::log(falco_logger::level priority, const std::string&& msg) { if(priority > falco_logger::current_level) { return; } std::string copy = msg; #ifndef _WIN32 if(falco_logger::log_syslog) { // Syslog output should not have any trailing newline if(copy.back() == '\n') { copy.pop_back(); } ::syslog(static_cast(priority), "%s", copy.c_str()); } #endif if(falco_logger::log_stderr) { // log output should always have a trailing newline if(copy.back() != '\n') { copy.push_back('\n'); } std::time_t result = std::time(nullptr); if(falco_logger::time_format_iso_8601) { char buf[sizeof "YYYY-MM-DDTHH:MM:SS-0000"]; const struct tm* gtm = std::gmtime(&result); if(gtm != NULL && (strftime(buf, sizeof(buf), "%FT%T%z", gtm) != 0)) { fprintf(stderr, "%s: %s", buf, copy.c_str()); } } else { const struct tm* ltm = std::localtime(&result); char tstr[std::size("WWW MMM DD HH:mm:ss YYYY")]; std::strftime(std::data(tstr), std::size(tstr), "%a %b %d %H:%M:%S %Y", ltm); fprintf(stderr, "%s: %s", tstr, copy.c_str()); } } } ================================================ FILE: userspace/engine/logger.h ================================================ // 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. */ #pragma once #include #ifndef _WIN32 #include #endif class falco_logger { public: enum class level : int { EMERG = 0, ALERT, CRIT, ERR, WARNING, NOTICE, INFO, DEBUG }; static void set_time_format_iso_8601(bool val); // Will throw exception if level is unknown. static void set_level(const std::string& level); static void set_sinsp_logging(bool enable, const std::string& severity, const std::string& prefix); static void log(falco_logger::level priority, const std::string&& msg); static level current_level; static bool log_stderr; static bool log_syslog; static bool time_format_iso_8601; }; ================================================ FILE: userspace/engine/output_format.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2026 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once enum class output_format { TEXT, MARKDOWN, JSON }; ================================================ FILE: userspace/engine/rule_json_schema.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #define LONG_STRING_CONST(...) #__VA_ARGS__ const char rule_schema_string[] = LONG_STRING_CONST( { "$schema": "http://json-schema.org/draft-06/schema#", "type": "array", "items": { "$ref": "#/definitions/FalcoRule" }, "definitions": { "FalcoRule": { "type": "object", "additionalProperties": false, "properties": { "required_engine_version": { "type": "string" }, "required_plugin_versions": { "type": "array", "items": { "$ref": "#/definitions/RequiredPluginVersion" } }, "macro": { "type": "string" }, "condition": { "type": "string" }, "list": { "type": "string" }, "items": { "type": "array", "items": { "$ref": "#/definitions/Item" } }, "rule": { "type": "string" }, "desc": { "type": "string" }, "enabled": { "type": "boolean" }, "output": { "type": "string" }, "append": { "type": "boolean" }, "priority": { "$ref": "#/definitions/Priority" }, "capture": { "type": "boolean" }, "capture_duration": { "type": "integer" }, "source": { "type": "string" }, "exceptions": { "type": "array", "items": { "$ref": "#/definitions/Exception" } }, "override": { "$ref": "#/definitions/Override" }, "tags": { "type": "array", "items": { "type": "string" } }, "warn_evttypes": { "type": "boolean" }, "skip-if-unknown-filter": { "type": "boolean" } }, "required": [], "title": "FalcoRule" }, "Item": { "anyOf": [ { "type": "integer" }, { "type": "string" } ], "title": "Item" }, "Exception": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string" }, "fields": {}, "comps": {}, "values": {} }, "required": [ "name" ], "title": "Exception" }, "Priority": { "type": "string", "enum": [ "EMERGENCY", "ALERT", "CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "INFORMATIONAL", "DEBUG" ], "title": "Priority" }, "OverriddenItem": { "type": "string", "enum": [ "append", "replace" ], "title": "OverriddenItem" }, "Override": { "type": "object", "additionalProperties": false, "properties": { "items": { "$ref": "#/definitions/OverriddenItem" }, "desc": { "$ref": "#/definitions/OverriddenItem" }, "condition": { "$ref": "#/definitions/OverriddenItem" }, "output": { "$ref": "#/definitions/OverriddenItem" }, "priority": { "$ref": "#/definitions/OverriddenItem" }, "enabled": { "$ref": "#/definitions/OverriddenItem" }, "exceptions": { "$ref": "#/definitions/OverriddenItem" }, "capture": { "$ref": "#/definitions/OverriddenItem" }, "capture_duration": { "$ref": "#/definitions/OverriddenItem" }, "tags": { "$ref": "#/definitions/OverriddenItem" }, "warn_evttypes": { "$ref": "#/definitions/OverriddenItem" }, "skip-if-unknown-filter": { "$ref": "#/definitions/OverriddenItem" } }, "minProperties": 1, "title": "Override" }, "RequiredPluginVersion": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string" }, "version": { "type": "string" }, "alternatives": { "type": "array", "items": { "$ref": "#/definitions/Alternative" } } }, "required": [ "name", "version" ], "title": "RequiredPluginVersion" }, "Alternative": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string" }, "version": { "type": "string" } }, "required": [ "name", "version" ], "title": "Alternative" } } } ); // LONG_STRING_CONST macro ================================================ FILE: userspace/engine/rule_loader.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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 #include "rule_loader.h" #include "yaml_helper.h" static const std::string item_type_strings[] = {"value for", "exceptions", "exception", "exception values", "exception value", "rules content", "rules content item", "required_engine_version", "required plugin versions", "required plugin versions entry", "required plugin versions alternative", "list", "list item", "macro", "macro condition", "rule", "rule condition", "condition expression", "rule output", "rule output expression", "rule priority", "overrides", "extension item"}; const std::string& rule_loader::context::item_type_as_string(enum item_type it) { return item_type_strings[it]; } rule_loader::context::context(const std::string& name) { // This ensures that every context has one location, even if // that location is effectively the whole document. location loc = {name, position(), rule_loader::context::RULES_CONTENT, ""}; m_locs.push_back(loc); } rule_loader::context::context(const YAML::Node& item, const item_type item_type, const std::string& item_name, const context& parent) { init(parent.name(), position(item.Mark()), item_type, item_name, parent); } rule_loader::context::context(const YAML::Mark& mark, const context& parent) { init(parent.name(), position(mark), item_type::VALUE_FOR, "", parent); } rule_loader::context::context(const libsinsp::filter::ast::pos_info& pos, const std::string& condition, const context& parent): alt_content(condition) { // Contexts based on conditions don't use the // filename. Instead the "name" is just the condition, and // uses a short prefix of the condition. std::string condition_name = "\"" + (condition.length() > 20 ? condition.substr(0, 20 - 3) + "...\"" : condition + "\""); std::replace(condition_name.begin(), condition_name.end(), '\n', ' '); std::replace(condition_name.begin(), condition_name.end(), '\r', ' '); std::string item_name = ""; // Convert the parser position to a context location. Both // they have the same basic info (position, line, column). // parser line/columns are 1-indexed while yaml marks are // 0-indexed, though. position condpos; auto& lastpos = parent.m_locs.back(); condpos.pos = pos.idx + lastpos.pos.pos; condpos.line = pos.line + lastpos.pos.line; condpos.column = pos.col + lastpos.pos.column; init(condition_name, condpos, rule_loader::context::CONDITION_EXPRESSION, item_name, parent); } const std::string& rule_loader::context::name() const { // All valid contexts should have at least one location. if(m_locs.empty()) { throw falco_exception("rule_loader::context without location?"); } return m_locs.front().name; } void rule_loader::context::init(const std::string& name, const position& pos, const item_type item_type, const std::string& item_name, const context& parent) { // Copy parent locations m_locs = parent.m_locs; // Add current item to back location loc = {name, pos, item_type, item_name}; m_locs.push_back(loc); } std::string rule_loader::context::as_string() const { std::ostringstream os; // All valid contexts should have at least one location. if(m_locs.empty()) { throw falco_exception("rule_loader::context without location?"); } bool first = true; for(const auto& loc : m_locs) { os << (first ? "In " : " "); first = false; os << item_type_as_string(loc.item_type); if(!loc.item_name.empty()) { os << " '" << loc.item_name << "'"; } os << ": "; os << "(" << loc.name << ":" << loc.pos.line << ":" << loc.pos.column << ")" << std::endl; } return os.str(); } nlohmann::json rule_loader::context::as_json() const { nlohmann::json ret; ret["locations"] = nlohmann::json::array(); // All valid contexts should have at least one location. if(m_locs.empty()) { throw falco_exception("rule_loader::context without location?"); } for(const auto& loc : m_locs) { nlohmann::json jloc, jpos; jloc["item_type"] = item_type_as_string(loc.item_type); jloc["item_name"] = loc.item_name; jpos["name"] = loc.name; jpos["line"] = loc.pos.line; jpos["column"] = loc.pos.column; jpos["offset"] = loc.pos.pos; jloc["position"] = jpos; ret["locations"].push_back(jloc); } return ret; } std::string rule_loader::context::snippet( const falco::load_result::rules_contents_t& rules_contents, size_t snippet_width) const { // All valid contexts should have at least one location. if(m_locs.empty()) { throw falco_exception("rule_loader::context without location?"); } rule_loader::context::location loc = m_locs.back(); auto it = rules_contents.find(loc.name); if(alt_content.empty() && it == rules_contents.end()) { return "\n"; } // If not using alt content, the last location's name must be found in rules_contents const std::string& snip_content = (!alt_content.empty() ? alt_content : it->second.get()); if(snip_content.empty()) { return "\n"; } // In some cases like this, where the content ends with a // dangling property value: // tags: // The YAML::Mark position can be past the end of the file. size_t pos = loc.pos.pos; for(; pos > 0 && (pos >= snip_content.size() || snip_content.at(pos) == '\n'); pos--) ; // The snippet is generally the line that contains the // position. So walk backwards from pos to the preceding // newline, and walk forwards from pos to the following // newline. // // However, some lines can be very very long, so the walk // forwards/walk backwards is capped at a maximum of // snippet_width/2 characters in either direction. size_t from = pos; for(; from > 0 && snip_content.at(from) != '\n' && (pos - from) < (snippet_width / 2); from--) ; size_t to = pos; for(; to < snip_content.size() - 1 && snip_content.at(to) != '\n' && (to - pos) < (snippet_width / 2); to++) ; // Don't include the newlines if(from < snip_content.size() && snip_content.at(from) == '\n') { from++; } if(to < snip_content.size() && snip_content.at(to) == '\n') { to--; } std::string ret = snip_content.substr(from, to - from + 1); if(ret.empty()) { return "\n"; } // Replace the initial/end characters with '...' if the walk // forwards/backwards was incomplete if(pos - from >= (snippet_width / 2)) { ret.replace(0, 3, "..."); } if(to - pos >= (snippet_width / 2)) { ret.replace(ret.size() - 3, 3, "..."); } ret += "\n"; // Add a blank line with a marker at the position within the snippet if(pos - from <= ret.size() - 1) { ret += std::string(pos - from, ' ') + '^' + "\n"; } return ret; } rule_loader::result::result(const std::string& name): name(name), success(true) {} bool rule_loader::result::successful() { return success; } bool rule_loader::result::has_warnings() { return (warnings.size() > 0); } std::string rule_loader::result::schema_validation() { if(schema_validation_status.empty()) { return yaml_helper::validation_none; } return schema_validation_status[0]; } void rule_loader::result::add_error(load_result::error_code ec, const std::string& msg, const context& ctx) { error err = {ec, msg, ctx}; success = false; errors.push_back(err); } void rule_loader::result::add_warning(load_result::warning_code wc, const std::string& msg, const context& ctx) { warnings.emplace_back(std::make_unique(wc, msg, ctx)); } void rule_loader::result::add_deprecated_field_warning(load_result::deprecated_field df, const std::string& msg, const context& ctx) { warnings.emplace_back(std::make_unique(df, msg, ctx)); } void rule_loader::result::set_schema_validation_status(const std::vector& status) { schema_validation_status = status; } const std::string& rule_loader::result::as_string(bool verbose, const rules_contents_t& contents) { if(verbose) { return as_verbose_string(contents); } else { return as_summary_string(); } } const std::string& rule_loader::result::as_summary_string() { std::ostringstream os; if(!res_summary_string.empty()) { return res_summary_string; } if(!name.empty()) { os << name << ": "; } if(success) { os << "Ok"; if(!warnings.empty()) { os << ", with warnings"; } } else { os << "Invalid"; } // Only print schema validation info if any validation was requested if(!schema_validation_status.empty()) { bool schema_valid = schema_validation() == yaml_helper::validation_ok; // Only print info when there are validation warnings if(!schema_valid) { os << std::endl; os << " " << schema_validation_status.size() << " schema warnings: ["; bool first = true; for(const auto& status : schema_validation_status) { if(!first) { os << " "; } first = false; os << status; } os << "]"; } } if(!errors.empty()) { os << std::endl; os << " " << errors.size() << " errors: ["; bool first = true; for(auto& err : errors) { if(!first) { os << " "; } first = false; os << load_result::error_code_str(err.ec) << " (" << load_result::error_str(err.ec) << ")"; } os << "]"; } if(!warnings.empty()) { os << std::endl; os << " " << warnings.size() << " warnings: ["; bool first = true; for(auto& warn : warnings) { if(!first) { os << " "; } first = false; os << warn->code_string() << " (" << warn->as_string() << ")"; } os << "]"; } res_summary_string = os.str(); return res_summary_string; } const std::string& rule_loader::result::as_verbose_string(const rules_contents_t& contents) { std::ostringstream os; if(!res_verbose_string.empty()) { return res_verbose_string; } if(!name.empty()) { os << name << ": "; } if(success) { os << "Ok"; if(!warnings.empty()) { os << ", with warnings"; } } else { os << "Invalid"; } // Only print schema validation info if any validation was requested if(!schema_validation_status.empty()) { bool schema_valid = schema_validation() == yaml_helper::validation_ok; // Only print info when there are validation warnings if(!schema_valid) { os << std::endl; os << schema_validation_status.size() << " Schema warnings:" << std::endl; for(const auto& status : schema_validation_status) { os << "------" << std::endl; os << status << std::endl; } os << "------" << std::endl; } } if(!errors.empty()) { os << std::endl; os << errors.size() << " Errors:" << std::endl; for(auto& err : errors) { os << err.ctx.as_string(); os << "------" << std::endl; os << err.ctx.snippet(contents); os << "------" << std::endl; os << load_result::error_code_str(err.ec) << " (" << load_result::error_str(err.ec) << "): " << err.msg << std::endl; } } if(!warnings.empty()) { os << std::endl; os << warnings.size() << " Warnings:" << std::endl; for(auto& warn : warnings) { os << warn->ctx.as_string(); os << "------" << std::endl; os << warn->ctx.snippet(contents); os << "------" << std::endl; os << warn->code_string() << " (" << warn->as_string() << "): " << warn->msg; os << std::endl; } } res_verbose_string = os.str(); return res_verbose_string; } const nlohmann::json& rule_loader::result::as_json(const rules_contents_t& contents) { nlohmann::json j; if(!res_json.empty()) { return res_json; } j["name"] = name; j["successful"] = success; // Only print schema validation info if any validation was requested if(!schema_validation_status.empty()) { bool schema_valid = schema_validation() == yaml_helper::validation_ok; j["schema_valid"] = schema_valid; j["schema_warnings"] = nlohmann::json::array(); if(!schema_valid) { for(const auto& schema_warning : schema_validation_status) { j["schema_warnings"].push_back(schema_warning); } } } j["errors"] = nlohmann::json::array(); for(auto& err : errors) { nlohmann::json jerr; jerr["context"] = err.ctx.as_json(); jerr["context"]["snippet"] = err.ctx.snippet(contents); jerr["code"] = load_result::error_code_str(err.ec); jerr["codedesc"] = load_result::error_desc(err.ec); jerr["message"] = err.msg; j["errors"].push_back(jerr); } j["warnings"] = nlohmann::json::array(); for(auto& warn : warnings) { j["warnings"].push_back(warn->as_json(contents)); } res_json = j; return res_json; } rule_loader::engine_version_info::engine_version_info(context& ctx): ctx(ctx) {} rule_loader::plugin_version_info::plugin_version_info(): ctx("no-filename-given") {} rule_loader::plugin_version_info::plugin_version_info(context& ctx): ctx(ctx) {} rule_loader::list_info::list_info(context& ctx): ctx(ctx), index(0), visibility(0) {} rule_loader::macro_info::macro_info(context& ctx): ctx(ctx), cond_ctx(ctx), index(0), visibility(0) {} rule_loader::rule_exception_info::rule_exception_info(context& ctx): ctx(ctx) {} rule_loader::rule_info::rule_info(context& ctx): ctx(ctx), cond_ctx(ctx), output_ctx(ctx), index(0), visibility(0), unknown_source(false), priority(falco_common::PRIORITY_DEBUG), capture(false), capture_duration(0), enabled(true), warn_evttypes(true), skip_if_unknown_filter(false) {} rule_loader::rule_update_info::rule_update_info(context& ctx): ctx(ctx), cond_ctx(ctx) {} rule_loader::rule_load_exception::rule_load_exception(falco::load_result::error_code ec, const std::string& msg, const context& ctx): ec(ec), msg(msg), ctx(ctx) {} rule_loader::rule_load_exception::~rule_load_exception() {} const char* rule_loader::rule_load_exception::what() const noexcept { // const + noexcept: can't use functions that change the object or throw return msg.c_str(); } ================================================ FILE: userspace/engine/rule_loader.h ================================================ // 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. */ #pragma once #include #include #include #include #include #include #include "falco_source.h" #include "falco_load_result.h" #include "indexed_vector.h" #include namespace rule_loader { class context { public: // The kinds of items that can be in rules // content. These generally map to yaml items but a // few are more specific (e.g. "within condition // expression", "value for yaml node", etc.) enum item_type { VALUE_FOR = 0, EXCEPTIONS, EXCEPTION, EXCEPTION_VALUES, EXCEPTION_VALUE, RULES_CONTENT, RULES_CONTENT_ITEM, REQUIRED_ENGINE_VERSION, REQUIRED_PLUGIN_VERSIONS, REQUIRED_PLUGIN_VERSIONS_ENTRY, REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, LIST, LIST_ITEM, MACRO, MACRO_CONDITION, RULE, RULE_CONDITION, CONDITION_EXPRESSION, RULE_OUTPUT, RULE_OUTPUT_EXPRESSION, RULE_PRIORITY, OVERRIDE, EXTENSION_ITEM }; static const std::string& item_type_as_string(enum item_type it); static const size_t default_snippet_width = 160; struct position { position(): pos(0), line(0), column(0) {}; explicit position(const YAML::Mark& mark): pos(mark.pos), line(mark.line), column(mark.column) {}; ~position() = default; position(position&&) = default; position& operator=(position&&) = default; position(const position&) = default; position& operator=(const position&) = default; int pos; int line; int column; }; struct location { location(): item_type(context::item_type::VALUE_FOR) {} location(const std::string& n, const position& p, context::item_type i, const std::string& in): name(n), pos(p), item_type(i), item_name(in) {} location(location&&) = default; location& operator=(location&&) = default; location(const location&) = default; location& operator=(const location&) = default; // A name for the content this location refers // to. Will generally be a filename, can also // refer to a rule/macro condition when the // location points into a condition string. std::string name; // The original location in the document position pos; // The kind of item at this location // (e.g. "list", "macro", "rule", "exception", etc) context::item_type item_type; // The name of this item (e.g. "Write Below Etc", // etc). std::string item_name; }; explicit context(const std::string& name); context(const YAML::Node& item, item_type item_type, const std::string& item_name, const context& parent); context(const YAML::Mark& mark, const context& parent); // Build a context from a condition expression + // parser position. This does not use the original // yaml content because: // - YAML block indicators will remove whitespace/newlines/wrapping // from the YAML node containing the condition expression. // - When compiling, the condition expression has expanded // macro and list references with their values. context(const libsinsp::filter::ast::pos_info& pos, const std::string& condition, const context& parent); virtual ~context() = default; context(context&&) = default; context& operator=(context&&) = default; context(const context&) = default; context& operator=(const context&) = default; // Return the content name (generally filename) for // this context const std::string& name() const; // Return a snippet of the provided rules content // corresponding to this context. // Uses the provided rules_contents to look up the original // rules content for a given location name. // (If this context has a non-empty alt_content, it // will be used to create the snippet, ignoring the // provided rules_contents). std::string snippet(const falco::load_result::rules_contents_t& rules_contents, size_t snippet_width = default_snippet_width) const; std::string as_string() const; nlohmann::json as_json() const; private: void init(const std::string& name, const position& pos, const item_type item_type, const std::string& item_name, const context& parent); // A chain of locations from the current item, its // parent, possibly older ancestors. std::vector m_locs; // If non-empty, this content will be used when // creating snippets. Used for contexts involving // condition expressions. std::string alt_content; }; struct warning { warning(): wc(falco::load_result::warning_code::LOAD_UNKNOWN_SOURCE), ctx("no-filename-given") {} warning(falco::load_result::warning_code w, const std::string& m, const context& c): wc(w), msg(m), ctx(c) {} warning(warning&&) = default; virtual ~warning() = default; virtual std::string code_string() const { return falco::load_result::warning_code_str(wc); }; virtual std::string as_string() const { return falco::load_result::warning_str(wc); }; virtual std::string description() const { return falco::load_result::warning_desc(wc); }; virtual nlohmann::json as_json(const falco::load_result::rules_contents_t& contents) const { nlohmann::json jwarn; jwarn["context"] = ctx.as_json(); jwarn["context"]["snippet"] = ctx.snippet(contents); jwarn["code"] = falco::load_result::warning_code_str(wc); jwarn["codedesc"] = falco::load_result::warning_desc(wc); jwarn["message"] = msg; return jwarn; }; falco::load_result::warning_code wc; std::string msg; context ctx; }; struct deprecated_field_warning : warning { deprecated_field_warning(): warning() {} deprecated_field_warning(falco::load_result::deprecated_field df, const std::string& m, const context& c): warning(falco::load_result::warning_code::LOAD_DEPRECATED_ITEM, m, c), df(df) {} std::string as_string() const override { return warning::as_string() + ": field '" + falco::load_result::deprecated_field_str(df) + "'"; }; std::string description() const override { return warning::description() + ": " + falco::load_result::deprecated_field_desc(df); }; nlohmann::json as_json(const falco::load_result::rules_contents_t& contents) const override { auto jwarn = warning::as_json(contents); jwarn["deprecated_field"] = falco::load_result::deprecated_field_str(df); return jwarn; }; falco::load_result::deprecated_field df; }; struct error { error(): ec(falco::load_result::error_code::LOAD_ERR_FILE_READ), ctx("no-filename-given") {} error(falco::load_result::error_code e, const std::string& m, const context& c): ec(e), msg(m), ctx(c) {} error(error&&) = default; error& operator=(error&&) = default; error(const error&) = default; error& operator=(const error&) = default; falco::load_result::error_code ec; std::string msg; context ctx; }; class rule_load_exception : public std::exception { public: rule_load_exception(falco::load_result::error_code ec, const std::string& msg, const context& ctx); virtual ~rule_load_exception(); const char* what() const noexcept override; falco::load_result::error_code ec; std::string msg; context ctx; }; /*! \brief Contains the result of loading rule definitions */ class result : public falco::load_result { public: explicit result(const std::string& name); virtual ~result() override = default; result(result&&) = default; result& operator=(result&&) = default; result(const result&) = default; result& operator=(const result&) = default; virtual bool successful() override; virtual bool has_warnings() override; virtual const std::string& as_string( bool verbose, const falco::load_result::rules_contents_t& contents) override; virtual const nlohmann::json& as_json( const falco::load_result::rules_contents_t& contents) override; void add_error(falco::load_result::error_code ec, const std::string& msg, const context& ctx); void add_warning(falco::load_result::warning_code ec, const std::string& msg, const context& ctx); void add_deprecated_field_warning(falco::load_result::deprecated_field df, const std::string& msg, const context& ctx); void set_schema_validation_status(const std::vector& status); std::string schema_validation() override; protected: const std::string& as_summary_string(); const std::string& as_verbose_string(const falco::load_result::rules_contents_t& contents); std::string name; bool success; std::vector schema_validation_status; std::vector errors; std::vector> warnings; std::string res_summary_string; std::string res_verbose_string; nlohmann::json res_json; }; struct extra_output_format_conf { std::string m_format; std::string m_source; std::set m_tags; std::string m_rule; }; struct extra_output_field_conf { std::string m_key; std::string m_format; std::string m_source; std::set m_tags; std::string m_rule; bool m_raw; }; /*! \brief Contains the info required to load rule definitions */ struct configuration { explicit configuration(const std::string& cont, const indexed_vector& srcs, const std::string& name): content(cont), sources(srcs), name(name), res(std::make_unique(name)) {} // inputs const std::string& content; const indexed_vector& sources; std::string name; std::vector extra_output_format; std::vector extra_output_fields; // outputs std::unique_ptr res; }; /*! \brief Represents infos about an engine version requirement */ struct engine_version_info { engine_version_info(): ctx("no-filename-given"), version("0.0.0") {}; explicit engine_version_info(context& ctx); ~engine_version_info() = default; engine_version_info(engine_version_info&&) = default; engine_version_info& operator=(engine_version_info&&) = default; engine_version_info(const engine_version_info&) = default; engine_version_info& operator=(const engine_version_info&) = default; context ctx; sinsp_version version; }; /*! \brief Represents infos about a plugin version requirement */ struct plugin_version_info { struct requirement { requirement() = default; requirement(const std::string& n, const std::string& v): name(n), version(v) {} requirement(requirement&&) = default; requirement& operator=(requirement&&) = default; requirement(const requirement&) = default; requirement& operator=(const requirement&) = default; std::string name; std::string version; }; typedef std::vector requirement_alternatives; // This differs from the other _info structs by having // a default constructor. This allows it to be used // by falco_engine, which aliases the type. plugin_version_info(); explicit plugin_version_info(context& ctx); ~plugin_version_info() = default; plugin_version_info(plugin_version_info&&) = default; plugin_version_info& operator=(plugin_version_info&&) = default; plugin_version_info(const plugin_version_info&) = default; plugin_version_info& operator=(const plugin_version_info&) = default; context ctx; requirement_alternatives alternatives; }; /*! \brief Represents infos about a list */ struct list_info { explicit list_info(context& ctx); ~list_info() = default; list_info(list_info&&) = default; list_info& operator=(list_info&&) = default; list_info(const list_info&) = default; list_info& operator=(const list_info&) = default; context ctx; size_t index; size_t visibility; std::string name; std::vector items; }; /*! \brief Represents infos about a macro */ struct macro_info { explicit macro_info(context& ctx); ~macro_info() = default; macro_info(macro_info&&) = default; macro_info& operator=(macro_info&&) = default; macro_info(const macro_info&) = default; macro_info& operator=(const macro_info&) = default; context ctx; context cond_ctx; size_t index; size_t visibility; std::string name; std::string cond; }; /*! \brief Represents infos about a single rule exception */ struct rule_exception_info { explicit rule_exception_info(context& ctx); ~rule_exception_info() = default; rule_exception_info(rule_exception_info&&) = default; rule_exception_info& operator=(rule_exception_info&&) = default; rule_exception_info(const rule_exception_info&) = default; rule_exception_info& operator=(const rule_exception_info&) = default; /*! \brief This is necessary due to the dynamic-typed nature of exceptions. Each of fields, comps, and values, can either be a single value or a list of values. This is a simple hack to make this easier to implement in C++, that is not non-dynamic-typed. */ struct entry { entry(): is_list(false) {} explicit entry(const std::string& i): is_list(false), item(i) {} explicit entry(const std::vector& v): is_list(true), items(v) {} entry(entry&&) = default; entry& operator=(entry&&) = default; entry(const entry&) = default; entry& operator=(const entry&) = default; bool is_list; std::string item; std::vector items; inline bool is_valid() const { return (is_list && !items.empty()) || (!is_list && !item.empty()); } }; context ctx; std::string name; entry fields; entry comps; std::vector values; }; /*! \brief Represents infos about a rule */ struct rule_info { explicit rule_info(context& ctx); ~rule_info() = default; rule_info(rule_info&&) = default; rule_info& operator=(rule_info&&) = default; rule_info(const rule_info&) = default; rule_info& operator=(const rule_info&) = default; context ctx; context cond_ctx; context output_ctx; size_t index; size_t visibility; bool unknown_source; std::string name; std::string cond; std::string source; std::string desc; std::string output; std::set tags; std::vector exceptions; falco_common::priority_type priority; bool capture; uint32_t capture_duration; bool enabled; bool warn_evttypes; bool skip_if_unknown_filter; }; /*! \brief Represents infos about a rule update (append or replace) request */ struct rule_update_info { explicit rule_update_info(context& ctx); ~rule_update_info() = default; rule_update_info(rule_update_info&&) = default; rule_update_info& operator=(rule_update_info&&) = default; rule_update_info(const rule_update_info&) = default; rule_update_info& operator=(const rule_update_info&) = default; bool has_any_value() { return cond.has_value() || output.has_value() || desc.has_value() || tags.has_value() || exceptions.has_value() || priority.has_value() || enabled.has_value() || capture.has_value() || capture_duration.has_value() || warn_evttypes.has_value() || skip_if_unknown_filter.has_value(); } context ctx; context cond_ctx; std::string name; std::optional cond; std::string source; std::optional output; std::optional desc; std::optional> tags; std::optional> exceptions; std::optional priority; std::optional capture; std::optional capture_duration; std::optional enabled; std::optional warn_evttypes; std::optional skip_if_unknown_filter; }; }; // namespace rule_loader ================================================ FILE: userspace/engine/rule_loader_collector.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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 #include #include "falco_engine.h" #include "rule_loader_collector.h" #include "rule_loading_messages.h" #define THROW(cond, err, ctx) \ { \ if((cond)) { \ throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_VALIDATE, \ (err), \ (ctx)); \ } \ } 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(); } template static inline void define_info(indexed_vector& infos, T& info, uint32_t id) { auto prev = infos.at(info.name); if(prev) { info.index = prev->index; info.visibility = id; *prev = info; } else { info.index = id; info.visibility = id; infos.insert(info, info.name); } } template static inline void append_info(T* prev, U& info, uint32_t id) { prev->visibility = id; } template static inline void replace_info(T* prev, U& info, uint32_t id) { prev->visibility = id; } static void validate_exception_info(const falco_source* source, rule_loader::rule_exception_info& ex) { if(ex.fields.is_list) { if(!ex.comps.is_valid()) { ex.comps.is_list = true; for(size_t i = 0; i < ex.fields.items.size(); i++) { ex.comps.items.push_back(rule_loader::rule_exception_info::entry("=")); } } THROW(ex.fields.items.size() != ex.comps.items.size(), "Fields and comps lists must have equal length", ex.ctx); for(const auto& v : ex.comps.items) { THROW(!is_operator_defined(v.item), std::string("'") + v.item + "' is not a supported comparison operator", ex.ctx); } if(source) { for(const auto& v : ex.fields.items) { THROW(!source->is_valid_lhs_field(v.item), std::string("'") + v.item + "' is not a supported filter field", ex.ctx); } } } else { if(!ex.comps.is_valid()) { ex.comps.is_list = false; ex.comps.item = "in"; } THROW(ex.comps.is_list, "Fields and comps must both be strings", ex.ctx); THROW((ex.comps.item != "in" && ex.comps.item != "pmatch" && ex.comps.item != "intersects"), "When fields is a single value, comps must be one of (in, pmatch, intersects)", ex.ctx); if(source) { THROW(!source->is_valid_lhs_field(ex.fields.item), std::string("'") + ex.fields.item + "' is not a supported filter field", ex.ctx); } } } void rule_loader::collector::clear() { m_cur_index = 0; m_rule_infos.clear(); m_list_infos.clear(); m_macro_infos.clear(); m_required_plugin_versions.clear(); } const std::vector& rule_loader::collector::required_plugin_versions() const { return m_required_plugin_versions; } const rule_loader::engine_version_info& rule_loader::collector::required_engine_version() const { return m_required_engine_version; } const indexed_vector& rule_loader::collector::lists() const { return m_list_infos; } const indexed_vector& rule_loader::collector::macros() const { return m_macro_infos; } const indexed_vector& rule_loader::collector::rules() const { return m_rule_infos; } void rule_loader::collector::define(configuration& cfg, engine_version_info& info) { auto v = falco_engine::engine_version(); THROW(!v.compatible_with(info.version), "Rules require engine version " + info.version.as_string() + ", but engine version is " + v.as_string(), info.ctx); // Store max required_engine_version if(m_required_engine_version.version < info.version) { m_required_engine_version = info; } } void rule_loader::collector::define(configuration& cfg, plugin_version_info& info) { std::unordered_set plugin_names; for(const auto& req : info.alternatives) { sinsp_version plugin_version(req.version); THROW(!plugin_version.is_valid(), "Invalid required version '" + req.version + "' for plugin '" + req.name + "'", info.ctx); THROW(plugin_names.find(req.name) != plugin_names.end(), "Defined multiple alternative version requirements for plugin '" + req.name + "'", info.ctx); plugin_names.insert(req.name); } m_required_plugin_versions.push_back(info.alternatives); } void rule_loader::collector::define(configuration& cfg, list_info& info) { define_info(m_list_infos, info, m_cur_index++); } void rule_loader::collector::append(configuration& cfg, list_info& info) { auto prev = m_list_infos.at(info.name); THROW(!prev, ERROR_NO_PREVIOUS_LIST, info.ctx); prev->items.insert(prev->items.end(), info.items.begin(), info.items.end()); append_info(prev, info, m_cur_index++); } void rule_loader::collector::define(configuration& cfg, macro_info& info) { define_info(m_macro_infos, info, m_cur_index++); } void rule_loader::collector::append(configuration& cfg, macro_info& info) { auto prev = m_macro_infos.at(info.name); THROW(!prev, ERROR_NO_PREVIOUS_MACRO, info.ctx); prev->cond += " "; prev->cond += info.cond; append_info(prev, info, m_cur_index++); } void rule_loader::collector::define(configuration& cfg, rule_info& info) { auto prev = find_prev_rule(info); (void)prev; const auto* source = cfg.sources.at(info.source); if(!source) { info.unknown_source = true; cfg.res->add_warning(falco::load_result::warning_code::LOAD_UNKNOWN_SOURCE, "Unknown source " + info.source + ", skipping", info.ctx); } for(auto& ex : info.exceptions) { THROW(!ex.fields.is_valid(), "Rule exception item must have fields property with a list of fields", ex.ctx); validate_exception_info(source, ex); } define_info(m_rule_infos, info, m_cur_index++); } void rule_loader::collector::append(configuration& cfg, rule_update_info& info) { auto prev = find_prev_rule(info); THROW(!prev, ERROR_NO_PREVIOUS_RULE_APPEND, info.ctx); THROW(!info.has_any_value(), "Appended rule must have exceptions or condition property", // "Appended rule must have at least one field that can be appended to", // TODO replace // with this and update testing info.ctx); // note: source can be nullptr in case we've collected a // rule for which the source is unknown const falco_source* source = nullptr; if(!prev->unknown_source) { // note: if the source is not unknown, this should not return nullptr source = cfg.sources.at(prev->source); THROW(!source, std::string("Unknown source ") + prev->source, info.ctx); } if(info.cond.has_value() && !info.cond->empty()) { prev->cond += " "; prev->cond += *info.cond; } if(info.output.has_value() && !info.output->empty()) { prev->output += " "; prev->output += *info.output; } if(info.desc.has_value() && !info.desc->empty()) { prev->desc += " "; prev->desc += *info.desc; } if(info.tags.has_value()) { for(auto& tag : *info.tags) { prev->tags.insert(tag); } } if(info.exceptions.has_value()) { for(auto& ex : *info.exceptions) { auto prev_ex = find_if( prev->exceptions.begin(), prev->exceptions.end(), [&ex](const rule_loader::rule_exception_info& i) { return i.name == ex.name; }); if(prev_ex == prev->exceptions.end()) { THROW(!ex.fields.is_valid(), "Rule exception must have fields property with a list of fields", ex.ctx); THROW(ex.values.empty(), "Rule exception must have values property with a list of values", ex.ctx); validate_exception_info(source, ex); prev->exceptions.push_back(ex); } else { THROW(ex.fields.is_valid(), "Can not append exception fields to existing exception, only values", ex.ctx); THROW(ex.comps.is_valid(), "Can not append exception comps to existing exception, only values", ex.ctx); prev_ex->values.insert(prev_ex->values.end(), ex.values.begin(), ex.values.end()); } } } append_info(prev, info, m_cur_index++); } void rule_loader::collector::selective_replace(configuration& cfg, rule_update_info& info) { auto prev = find_prev_rule(info); THROW(!prev, ERROR_NO_PREVIOUS_RULE_REPLACE, info.ctx); THROW(!info.has_any_value(), "The rule must have at least one field that can be replaced", info.ctx); // note: source can be nullptr in case we've collected a // rule for which the source is unknown const falco_source* source = nullptr; if(!prev->unknown_source) { // note: if the source is not unknown, this should not return nullptr source = cfg.sources.at(prev->source); THROW(!source, std::string("Unknown source ") + prev->source, info.ctx); } if(info.cond.has_value()) { prev->cond = *info.cond; } if(info.output.has_value()) { prev->output = *info.output; } if(info.desc.has_value()) { prev->desc = *info.desc; } if(info.tags.has_value()) { prev->tags = *info.tags; } if(info.exceptions.has_value()) { prev->exceptions = *info.exceptions; } if(info.priority.has_value()) { prev->priority = *info.priority; } if(info.capture.has_value()) { prev->capture = *info.capture; } if(info.capture_duration.has_value()) { prev->capture_duration = *info.capture_duration; } if(info.enabled.has_value()) { prev->enabled = *info.enabled; } if(info.warn_evttypes.has_value()) { prev->warn_evttypes = *info.warn_evttypes; } if(info.skip_if_unknown_filter.has_value()) { prev->skip_if_unknown_filter = *info.skip_if_unknown_filter; } replace_info(prev, info, m_cur_index++); } template rule_loader::rule_info* rule_loader::collector::find_prev_rule(ruleInfo& info) { auto ret = m_rule_infos.at(info.name); // Throw an error if both the original rule and current rule // have the same name and explicitly have different sources. THROW(ret && (ret->source != "" && info.source != "" && ret->source != info.source), "Rule has been re-defined with a different source", info.ctx); return ret; } void rule_loader::collector::enable(configuration& cfg, rule_info& info) { auto prev = m_rule_infos.at(info.name); THROW(!prev, "Rule has 'enabled' key but no rule by that name already exists", info.ctx); prev->enabled = info.enabled; } ================================================ FILE: userspace/engine/rule_loader_collector.h ================================================ // 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. */ #pragma once #include #include "rule_loader.h" #include "indexed_vector.h" namespace rule_loader { /*! \brief Collector for the ruleset loader of the falco engine */ class collector { public: collector(): m_cur_index(0) {} virtual ~collector() = default; collector(collector&&) = default; collector& operator=(collector&&) = default; collector(const collector&) = delete; collector& operator=(const collector&) = delete; /*! \brief Erases all the internal state and definitions */ virtual void clear(); /*! \brief Returns the set of all defined required plugin versions */ virtual const std::vector& required_plugin_versions() const; /*! \brief Returns the required engine versions */ virtual const engine_version_info& required_engine_version() const; /*! \brief Returns the list of defined lists */ virtual const indexed_vector& lists() const; /*! \brief Returns the list of defined macros */ virtual const indexed_vector& macros() const; /*! \brief Returns the list of defined rules */ virtual const indexed_vector& rules() const; /*! \brief Defines an info block. If a similar info block is found in the internal state (e.g. another rule with same name), then the previous definition gets overwritten */ virtual void define(configuration& cfg, engine_version_info& info); virtual void define(configuration& cfg, plugin_version_info& info); virtual void define(configuration& cfg, list_info& info); virtual void define(configuration& cfg, macro_info& info); virtual void define(configuration& cfg, rule_info& info); /*! \brief Appends an info block to an existing one. An exception is thrown if no existing definition can be matched with the appended one */ virtual void append(configuration& cfg, list_info& info); virtual void append(configuration& cfg, macro_info& info); virtual void append(configuration& cfg, rule_update_info& info); /*! \brief Updates the 'enabled' flag of an existing definition */ virtual void enable(configuration& cfg, rule_info& info); /*! \brief Selectively replaces some fields of an existing definition */ virtual void selective_replace(configuration& cfg, rule_update_info& info); private: template rule_info* find_prev_rule(ruleInfo& info); uint32_t m_cur_index; indexed_vector m_rule_infos; indexed_vector m_macro_infos; indexed_vector m_list_infos; std::vector m_required_plugin_versions; engine_version_info m_required_engine_version; }; }; // namespace rule_loader ================================================ FILE: userspace/engine/rule_loader_compile_output.h ================================================ // 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. */ #pragma once #include "indexed_vector.h" #include "falco_rule.h" #include namespace rule_loader { struct compile_output { compile_output() = default; virtual ~compile_output() = default; compile_output(compile_output&&) = default; compile_output& operator=(compile_output&&) = default; compile_output(const compile_output&) = default; compile_output& operator=(const compile_output&) = default; virtual std::unique_ptr clone() const { return std::make_unique(*this); }; indexed_vector lists; indexed_vector macros; indexed_vector rules; }; }; // namespace rule_loader ================================================ FILE: userspace/engine/rule_loader_compiler.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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 #include #include #include #include "rule_loader_compiler.h" #include "filter_warning_resolver.h" #define MAX_VISIBILITY ((uint32_t) - 1) #define THROW(cond, err, ctx) \ { \ if((cond)) { \ throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_VALIDATE, \ (err), \ (ctx)); \ } \ } static std::string s_container_info_fmt = "%container.info"; // We were previously expanding %container.info to "container_id=%container.id // container_name=%container.name". Since the container plugin is now in use, and it exposes // container.id and container.name as suggested output fields, we don't need to expand // container.info anymore. We kept container.info in the ruleset to avoid a major breaking change. // TODO: drop `container.info` magic once we make a major breaking change in the ruleset. static std::string s_default_extra_fmt = ""; using namespace libsinsp::filter; // todo(jasondellaluce): this breaks string escaping in lists and exceptions static void quote_item(std::string& e) { if(e.find(" ") != std::string::npos && e[0] != '"' && e[0] != '\'') { e = '"' + e + '"'; } } static void paren_item(std::string& e) { if(e[0] != '(') { e = '(' + e + ')'; } } static inline bool is_operator_for_list(const std::string& op) { auto ops = libsinsp::filter::parser::supported_operators(true); return find(ops.begin(), ops.end(), op) != ops.end(); } static bool is_format_valid(const falco_source& source, std::string fmt, std::string& err) { try { std::shared_ptr formatter; formatter = source.formatter_factory->create_formatter(fmt); return true; } catch(std::exception& e) { err = e.what(); return false; } } static void check_deprecated_fields_in_output(const std::string& fmt, const rule_loader::context& ctx, rule_loader::result& res) { // Check for evt.dir field usage in output format for(int i = 0; i < static_cast(falco::load_result::deprecated_field::DEPRECATED_FIELD_NOT_FOUND); i++) { auto df = falco::load_result::deprecated_field(i); if(fmt.find(falco::load_result::deprecated_field_str(df)) != std::string::npos) { res.add_deprecated_field_warning(df, "usage of deprecated field '" + falco::load_result::deprecated_field_str(df) + "' has been detected in the rule output", ctx); } } } static void build_rule_exception_infos( const std::vector& exceptions, std::set& exception_fields, std::string& condition) { std::string tmp; condition = "(" + condition + ")"; for(const auto& ex : exceptions) { std::string icond; if(!ex.fields.is_list) { for(const auto& val : ex.values) { THROW(val.is_list, "Expected values array to contain a list of strings", ex.ctx) icond += icond.empty() ? ("(" + ex.fields.item + " " + ex.comps.item + " (") : ", "; exception_fields.insert(ex.fields.item); tmp = val.item; quote_item(tmp); icond += tmp; } icond += icond.empty() ? "" : "))"; } else { icond = "("; for(const auto& values : ex.values) { THROW(ex.fields.items.size() != values.items.size(), "Fields and values lists must have equal length", ex.ctx); icond += icond == "(" ? "" : " or "; icond += "("; uint32_t k = 0; std::string istr; for(const auto& field : ex.fields.items) { icond += k == 0 ? "" : " and "; if(values.items[k].is_list) { istr = "("; for(const auto& v : values.items[k].items) { tmp = v.item; quote_item(tmp); istr += istr == "(" ? "" : ", "; istr += tmp; } istr += ")"; } else { istr = values.items[k].item; if(is_operator_for_list(ex.comps.items[k].item)) { paren_item(istr); } else { quote_item(istr); } } icond += " " + field.item; icond += " " + ex.comps.items[k].item + " " + istr; exception_fields.insert(field.item); k++; } icond += ")"; } icond += ")"; if(icond == "()") { icond = ""; } } condition += icond.empty() ? "" : " and not " + icond; } } static inline rule_loader::list_info* list_info_from_name(const rule_loader::collector& c, const std::string& name) { auto ret = c.lists().at(name); if(!ret) { throw falco_exception("can't find internal list info at name: " + name); } return ret; } static inline rule_loader::macro_info* macro_info_from_name(const rule_loader::collector& c, const std::string& name) { auto ret = c.macros().at(name); if(!ret) { throw falco_exception("can't find internal macro info at name: " + name); } return ret; } // todo(jasondellaluce): this breaks string escaping in lists static bool resolve_list(std::string& cnd, const falco_list& list) { static std::string blanks = " \t\n\r"; static std::string delims = blanks + "(),="; std::string tmp; std::string new_cnd; size_t start; bool used = false; start = cnd.find(list.name); while(start != std::string::npos) { // the characters surrounding the name must // be delims of beginning/end of string size_t end = start + list.name.length(); if((start == 0 || delims.find(cnd[start - 1]) != std::string::npos) && (end >= cnd.length() || delims.find(cnd[end]) != std::string::npos)) { // shift pointers to consume all whitespaces while(start > 0 && blanks.find(cnd[start - 1]) != std::string::npos) { start--; } while(end < cnd.length() && blanks.find(cnd[end]) != std::string::npos) { end++; } // create substitution string by concatenating all values std::string sub = ""; for(const auto& v : list.items) { if(!sub.empty()) { sub += ", "; } tmp = v; quote_item(tmp); sub += tmp; } // if substituted list is empty, we need to // remove a comma from the left or the right if(sub.empty()) { if(start > 0 && cnd[start - 1] == ',') { start--; } else if(end < cnd.length() && cnd[end] == ',') { end++; } } // compose new string with substitution new_cnd = ""; if(start > 0) { new_cnd += cnd.substr(0, start) + " "; } new_cnd += sub + " "; if(end <= cnd.length()) { new_cnd += cnd.substr(end); } cnd = new_cnd; start += sub.length() + 1; used = true; } start = cnd.find(list.name, start + 1); } return used; } static inline void resolve_macros(filter_macro_resolver& macro_resolver, const indexed_vector& infos, indexed_vector& macros, std::shared_ptr& ast, const std::string& condition, uint32_t visibility, const rule_loader::context& ctx) { macro_resolver.clear(); for(const auto& m : infos) { if(m.index < visibility) { auto macro = macros.at(m.name); macro_resolver.set_macro(m.name, macro->condition); } } macro_resolver.run(ast); // Note: only complaining about the first error or unknown macro const auto& errors_macros = macro_resolver.get_errors(); const auto& unresolved_macros = macro_resolver.get_unknown_macros(); if(!errors_macros.empty() || !unresolved_macros.empty()) { auto errpos = !errors_macros.empty() ? errors_macros.begin()->second : unresolved_macros.begin()->second; std::string errmsg = !errors_macros.empty() ? errors_macros.begin()->first : ("Undefined macro '" + unresolved_macros.begin()->first + "' used in filter."); const rule_loader::context cond_ctx(errpos, condition, ctx); THROW(true, errmsg, cond_ctx); } for(const auto& it : macro_resolver.get_resolved_macros()) { macros.at(it.first)->used = true; } } // note: there is no visibility order between filter conditions and lists static std::shared_ptr parse_condition(std::string condition, indexed_vector& lists, const rule_loader::context& ctx) { for(auto& l : lists) { if(resolve_list(condition, l)) { l.used = true; } } libsinsp::filter::parser p(condition); p.set_max_depth(1000); try { std::shared_ptr res_ptr(p.parse()); return res_ptr; } catch(const sinsp_exception& e) { rule_loader::context parsectx(p.get_pos(), condition, ctx); throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_COMPILE_CONDITION, e.what(), parsectx); } } void rule_loader::compiler::compile_list_infos(const configuration& cfg, const collector& col, indexed_vector& out) const { std::list used_names; falco_list infos; for(const auto& list : col.lists()) { infos.name = list.name; infos.items.clear(); for(const auto& item : list.items) { const auto ref = col.lists().at(item); if(ref && ref->index < list.visibility) { used_names.push_back(ref->name); for(const auto& val : ref->items) { infos.items.push_back(val); } } else { infos.items.push_back(item); } } infos.used = false; auto list_id = out.insert(infos, infos.name); out.at(list_id)->id = list_id; } for(const auto& name : used_names) { out.at(name)->used = true; } } // note: there is a visibility ordering between macros void rule_loader::compiler::compile_macros_infos(const configuration& cfg, const collector& col, indexed_vector& lists, indexed_vector& out) const { for(const auto& m : col.macros()) { falco_macro entry; entry.name = m.name; entry.condition = parse_condition(m.cond, lists, m.cond_ctx); entry.used = false; auto macro_id = out.insert(entry, m.name); out.at(macro_id)->id = macro_id; } filter_macro_resolver macro_resolver; for(auto& m : out) { const auto* info = macro_info_from_name(col, m.name); resolve_macros(macro_resolver, col.macros(), out, m.condition, info->cond, info->visibility, info->ctx); } } static bool err_is_unknown_type_or_field(const std::string& err) { return err.find("nonexistent field") != std::string::npos || err.find("invalid formatting token") != std::string::npos || err.find("unknown event type") != std::string::npos || err.find("unknown filter:") != std::string::npos; } bool rule_loader::compiler::compile_condition(const configuration& cfg, filter_macro_resolver& macro_resolver, indexed_vector& lists, const indexed_vector& macros, const std::string& condition, std::shared_ptr filter_factory, const rule_loader::context& cond_ctx, const rule_loader::context& parent_ctx, bool allow_unknown_fields, indexed_vector& macros_out, std::shared_ptr& ast_out, std::shared_ptr& filter_out) const { std::set warn_codes; filter_warning_resolver warn_resolver; ast_out = parse_condition(condition, lists, cond_ctx); resolve_macros(macro_resolver, macros, macros_out, ast_out, condition, MAX_VISIBILITY, parent_ctx); // check for warnings in the filtering condition warn_resolver.run(cond_ctx, *cfg.res, *ast_out.get()); // validate the rule's condition: we compile it into a sinsp filter // on-the-fly and we throw an exception with details on failure sinsp_filter_compiler compiler(filter_factory, ast_out.get()); try { filter_out = compiler.compile(); } catch(const sinsp_exception& e) { // skip the rule silently if skip_if_unknown_filter is true and // we encountered some specific kind of errors std::string err = e.what(); rule_loader::context ctx(compiler.get_pos(), condition, cond_ctx); if(err_is_unknown_type_or_field(err) && allow_unknown_fields) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_UNKNOWN_FILTER, err, ctx); return false; } throw rule_loader::rule_load_exception( falco::load_result::error_code::LOAD_ERR_COMPILE_CONDITION, err, ctx); } for(const auto& w : compiler.get_warnings()) { rule_loader::context ctx(w.pos, condition, cond_ctx); cfg.res->add_warning(falco::load_result::warning_code::LOAD_COMPILE_CONDITION, w.msg, ctx); } return true; } void rule_loader::compiler::compile_rule_infos(const configuration& cfg, const collector& col, indexed_vector& lists, indexed_vector& macros, indexed_vector& out) const { std::string err, condition; filter_macro_resolver macro_resolver; for(const auto& r : col.rules()) { // skip the rule if it has an unknown source if(r.unknown_source) { continue; } // note: this should not be nullptr if the source is not unknown auto source = cfg.sources.at(r.source); THROW(!source, std::string("Unknown source at compile-time") + r.source, r.ctx); // build filter AST by parsing the condition, building exceptions, // and resolving lists and macros falco_rule rule; condition = r.cond; if(!r.exceptions.empty()) { build_rule_exception_infos(r.exceptions, rule.exception_fields, condition); } // build rule output message rule.output = r.output; for(auto& extra : cfg.extra_output_format) { if(extra.m_source != "" && r.source != extra.m_source) { continue; } if(!std::includes(r.tags.begin(), r.tags.end(), extra.m_tags.begin(), extra.m_tags.end())) { continue; } if(extra.m_rule != "" && r.name != extra.m_rule) { continue; } rule.output = rule.output + " " + extra.m_format; } if(rule.output.find(s_container_info_fmt) != std::string::npos) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_DEPRECATED_ITEM, "%container.info is deprecated and no more useful, and will be " "dropped by Falco 1.0.0. " "The container plugin will automatically add required fields to " "the output message.", r.ctx); rule.output = replace(rule.output, s_container_info_fmt, s_default_extra_fmt); } // build extra output fields if required for(auto const& extra : cfg.extra_output_fields) { if(extra.m_source != "" && r.source != extra.m_source) { continue; } if(!std::includes(r.tags.begin(), r.tags.end(), extra.m_tags.begin(), extra.m_tags.end())) { continue; } if(extra.m_rule != "" && r.name != extra.m_rule) { continue; } rule.extra_output_fields[extra.m_key] = {extra.m_format, extra.m_raw}; } // validate the rule's output if(!is_format_valid(*cfg.sources.at(r.source), rule.output, err)) { // skip the rule silently if skip_if_unknown_filter is true and // we encountered some specific kind of errors if(err_is_unknown_type_or_field(err) && r.skip_if_unknown_filter) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_UNKNOWN_FILTER, err, r.output_ctx); continue; } throw rule_load_exception(falco::load_result::error_code::LOAD_ERR_COMPILE_OUTPUT, err, r.output_ctx); } // check for deprecated fields in output format check_deprecated_fields_in_output(rule.output, r.output_ctx, *cfg.res); // validate the rule's extra fields if any for(auto const& ef : rule.extra_output_fields) { if(!is_format_valid(*cfg.sources.at(r.source), ef.second.first, err)) { throw rule_load_exception(falco::load_result::error_code::LOAD_ERR_COMPILE_OUTPUT, err, r.output_ctx); } // check for deprecated fields in extra output fields check_deprecated_fields_in_output(ef.second.first, r.output_ctx, *cfg.res); } if(!compile_condition(cfg, macro_resolver, lists, col.macros(), condition, cfg.sources.at(r.source)->filter_factory, r.cond_ctx, r.ctx, r.skip_if_unknown_filter, macros, rule.condition, rule.filter)) { continue; } // populate set of event types and emit an special warning if(r.source == falco_common::syscall_source) { auto evttypes = libsinsp::filter::ast::ppm_event_codes(rule.condition.get()); if((evttypes.empty() || evttypes.size() > 100) && r.warn_evttypes) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_NO_EVTTYPE, "Rule matches too many evt.type values. This has a " "significant performance penalty.", r.ctx); } } // finalize the rule definition and add it to output rule.name = r.name; rule.source = r.source; rule.description = r.desc; rule.priority = r.priority; rule.capture = r.capture; rule.capture_duration = r.capture_duration; rule.tags = r.tags; auto rule_id = out.insert(rule, rule.name); out.at(rule_id)->id = rule_id; } } std::unique_ptr rule_loader::compiler::new_compile_output() { return std::make_unique(); } void rule_loader::compiler::compile(configuration& cfg, const collector& col, compile_output& out) const { // expand all lists, macros, and rules try { compile_list_infos(cfg, col, out.lists); compile_macros_infos(cfg, col, out.lists, out.macros); compile_rule_infos(cfg, col, out.lists, out.macros, out.rules); } catch(rule_load_exception& e) { cfg.res->add_error(e.ec, e.msg, e.ctx); return; } // print info on any dangling lists or macros that were not used anywhere for(const auto& m : out.macros) { if(!m.used) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_UNUSED_MACRO, "Macro not referred to by any other rule/macro", macro_info_from_name(col, m.name)->ctx); } } for(const auto& l : out.lists) { if(!l.used) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_UNUSED_LIST, "List not referred to by any other rule/macro", list_info_from_name(col, l.name)->ctx); } } } ================================================ FILE: userspace/engine/rule_loader_compiler.h ================================================ // 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. */ #pragma once #include "rule_loader.h" #include "rule_loader_compile_output.h" #include "rule_loader_collector.h" #include "filter_macro_resolver.h" #include "indexed_vector.h" #include "falco_rule.h" namespace rule_loader { /*! \brief Compiler for the ruleset loader of the falco engine */ class compiler { public: compiler() = default; virtual ~compiler() = default; compiler(compiler&&) = default; compiler& operator=(compiler&&) = default; compiler(const compiler&) = default; compiler& operator=(const compiler&) = default; // Return a new result object, suitable for passing to // compile(). virtual std::unique_ptr new_compile_output(); /*! \brief Compiles a list of falco rules */ virtual void compile(configuration& cfg, const collector& col, compile_output& out) const; protected: /*! \brief Compile a single condition expression, including expanding macro and list references. returns true if the condition could be compiled, and sets ast_out/filter_out with the compiled filter + ast. Returns false if the condition could not be compiled and should be skipped. */ bool compile_condition(const configuration& cfg, filter_macro_resolver& macro_resolver, indexed_vector& lists, const indexed_vector& macros, const std::string& condition, std::shared_ptr filter_factory, const rule_loader::context& cond_ctx, const rule_loader::context& parent_ctx, bool allow_unknown_fields, indexed_vector& macros_out, std::shared_ptr& ast_out, std::shared_ptr& filter_out) const; private: void compile_list_infos(const configuration& cfg, const collector& col, indexed_vector& out) const; void compile_macros_infos(const configuration& cfg, const collector& col, indexed_vector& lists, indexed_vector& out) const; void compile_rule_infos(const configuration& cfg, const collector& col, indexed_vector& lists, indexed_vector& macros, indexed_vector& out) const; }; }; // namespace rule_loader ================================================ FILE: userspace/engine/rule_loader_reader.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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 #include #include #include #include "rule_loader_reader.h" #include "falco_engine_version.h" #include "rule_loading_messages.h" #include "yaml_helper.h" #include #include #define THROW(cond, err, ctx) \ { \ if((cond)) { \ throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_YAML_VALIDATE, \ (err), \ (ctx)); \ } \ } // Sinsp Filter grammar tokens taken from "libsinsp/filter/parser.h" // These regular expressions are used here to check for invalid macro/list names // todo(mrgian): to avoid code duplication we can move regex definitions in libsinsp/filter/parser.h // and include it here instead of redefining them. #define RGX_IDENTIFIER "([a-zA-Z]+[a-zA-Z0-9_]*)" #define RGX_BARESTR "([^()\"'[:space:]=,]+)" static re2::RE2 s_rgx_identifier(RGX_IDENTIFIER, re2::RE2::POSIX); static re2::RE2 s_rgx_barestr(RGX_BARESTR, re2::RE2::POSIX); // Don't call this directly, call decode_val/decode_optional_val instead. template static void decode_val_generic(const YAML::Node& item, const char* key, T& out, const rule_loader::context& ctx, bool optional, bool can_be_empty) { const YAML::Node& val = item[key]; if(!val.IsDefined() && optional) { return; } THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx); if(val.IsNull() && can_be_empty) { return; } THROW(val.IsNull(), std::string("Mapping for key '") + key + "' is empty", ctx); rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx); THROW(!val.IsScalar(), "Value is not a scalar value", valctx); if(val.Scalar().empty() && can_be_empty) { return; } THROW(val.Scalar().empty(), "Value must be non-empty", valctx); THROW(!YAML::convert::decode(val, out), "Can't decode YAML scalar value", valctx); } template static void decode_val_generic(const YAML::Node& item, const char* key, std::optional& out, const rule_loader::context& ctx, bool optional, bool can_be_empty) { T decoded; decode_val_generic(item, key, decoded, ctx, optional, can_be_empty); out = decoded; } template void rule_loader::reader::decode_val(const YAML::Node& item, const char* key, T& out, const rule_loader::context& ctx) { bool optional = false; bool can_be_empty = false; decode_val_generic(item, key, out, ctx, optional, can_be_empty); } template void rule_loader::reader::decode_val(const YAML::Node& item, const char* key, std::string& out, const rule_loader::context& ctx); template void rule_loader::reader::decode_optional_val(const YAML::Node& item, const char* key, T& out, const rule_loader::context& ctx) { bool optional = true; bool can_be_empty = false; decode_val_generic(item, key, out, ctx, optional, can_be_empty); } template void rule_loader::reader::decode_optional_empty_val(const YAML::Node& item, const char* key, T& out, const rule_loader::context& ctx) { bool optional = true; bool can_be_empty = true; decode_val_generic(item, key, out, ctx, optional, can_be_empty); } template void rule_loader::reader::decode_optional_val( const YAML::Node& item, const char* key, std::string& out, const rule_loader::context& ctx); template void rule_loader::reader::decode_optional_val(const YAML::Node& item, const char* key, bool& out, const rule_loader::context& ctx); // Don't call this directly, call decode_items/decode_tags instead. template static void decode_seq(const YAML::Node& item, const char* key, std::function inserter, const rule_loader::context& ctx, bool optional) { const YAML::Node& val = item[key]; if(!val.IsDefined() && optional) { return; } THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx); rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx); THROW(!val.IsSequence(), "Value is not a sequence", valctx); T value; for(const YAML::Node& v : val) { rule_loader::context ictx(v, rule_loader::context::LIST_ITEM, "", valctx); THROW(!v.IsScalar(), "sequence value is not scalar", ictx); THROW(!YAML::convert::decode(v, value), "Can't decode YAML sequence value", ictx); inserter(value); } } template static void decode_items(const YAML::Node& item, std::vector& out, const rule_loader::context& ctx) { bool optional = false; std::function inserter = [&out](T value) { out.push_back(value); }; decode_seq(item, "items", inserter, ctx, optional); } template static void decode_tags(const YAML::Node& item, std::set& out, const rule_loader::context& ctx) { bool optional = true; std::function inserter = [&out](T value) { out.insert(value); }; decode_seq(item, "tags", inserter, ctx, optional); } template static void decode_tags(const YAML::Node& item, std::optional>& out, const rule_loader::context& ctx) { std::set decoded; decode_tags(item, decoded, ctx); out = decoded; } static void decode_overrides(const YAML::Node& item, std::set& overridable_append, std::set& overridable_replace, std::set& out_append, std::set& out_replace, const rule_loader::context& ctx) { const YAML::Node& val = item["override"]; if(!val.IsDefined()) { return; } rule_loader::context overridectx(item, rule_loader::context::OVERRIDE, "", ctx); for(YAML::const_iterator it = val.begin(); it != val.end(); ++it) { std::string key = it->first.as(); std::string operation = it->second.as(); bool is_overridable_append = overridable_append.find(it->first.as()) != overridable_append.end(); bool is_overridable_replace = overridable_replace.find(it->first.as()) != overridable_replace.end(); if(operation == "append") { rule_loader::context keyctx(it->first, rule_loader::context::OVERRIDE, key, overridectx); THROW(!is_overridable_append, std::string("Key '") + key + std::string("' cannot be appended to, use 'replace' instead"), keyctx); out_append.insert(key); } else if(operation == "replace") { rule_loader::context keyctx(it->first, rule_loader::context::OVERRIDE, key, overridectx); THROW(!is_overridable_replace, std::string("Key '") + key + std::string("' cannot be replaced"), keyctx); out_replace.insert(key); } else { rule_loader::context operationctx(it->second, rule_loader::context::VALUE_FOR, key, overridectx); std::stringstream err_ss; err_ss << "Invalid override operation for key '" << key << "': '" << operation << "'. " << "Allowed values are: "; if(is_overridable_append) { err_ss << "append "; } if(is_overridable_replace) { err_ss << "replace "; } THROW(true, err_ss.str(), operationctx); } } } // Don't call this directly, call decode_exception_{fields,comps,values} instead static void decode_exception_info_entry(const YAML::Node& item, const char* key, rule_loader::rule_exception_info::entry& out, const rule_loader::context& ctx, bool optional) { const YAML::Node& val = (key == NULL ? item : item[key]); if(!val.IsDefined() && optional) { return; } THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx); rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, (key == NULL ? "" : key), ctx); if(val.IsScalar()) { THROW(val.Scalar().empty(), "Value must be non-empty", valctx); out.is_list = false; THROW(!YAML::convert::decode(val, out.item), "Could not decode scalar value", valctx); } if(val.IsSequence()) { out.is_list = true; for(const YAML::Node& v : val) { rule_loader::rule_exception_info::entry tmp; rule_loader::context lctx(v, rule_loader::context::EXCEPTION, "", valctx); // Optional is always false once you get past the outer values optional = false; decode_exception_info_entry(v, NULL, tmp, lctx, optional); out.items.push_back(tmp); } } } static void decode_exception_fields(const YAML::Node& item, rule_loader::rule_exception_info::entry& out, const rule_loader::context& ctx, bool optional) { decode_exception_info_entry(item, "fields", out, ctx, optional); } static void decode_exception_comps(const YAML::Node& item, rule_loader::rule_exception_info::entry& out, const rule_loader::context& ctx) { bool optional = true; decode_exception_info_entry(item, "comps", out, ctx, optional); } static void decode_exception_values(const YAML::Node& item, rule_loader::rule_exception_info::entry& out, const rule_loader::context& ctx) { bool optional = false; decode_exception_info_entry(item, NULL, out, ctx, optional); } static void read_rule_exceptions(rule_loader::configuration& cfg, const YAML::Node& item, std::vector& exceptions, const rule_loader::context& parent, bool append) { const YAML::Node& exs = item["exceptions"]; // No exceptions property, or an exceptions property with // nothing in it, are allowed if(!exs.IsDefined() || exs.IsNull()) { return; } rule_loader::context exes_ctx(exs, rule_loader::context::EXCEPTIONS, "", parent); THROW(!exs.IsSequence(), "Rule exceptions must be a sequence", exes_ctx); for(auto& ex : exs) { // Make a temp context to verify simple properties // about the exception. std::string name; rule_loader::context tmp(ex, rule_loader::context::EXCEPTION, "", exes_ctx); THROW(!ex.IsMap(), "Rule exception must be a mapping", tmp); rule_loader::reader::decode_val(ex, "name", name, tmp); // Now use a real context including the exception name. rule_loader::context ex_ctx(ex, rule_loader::context::EXCEPTION, name, parent); rule_loader::rule_exception_info v_ex(ex_ctx); v_ex.name = name; // Check if an exception with the same name has already been defined for(const auto& exception : exceptions) { if(v_ex.name == exception.name) { cfg.res->add_warning( falco::load_result::warning_code::LOAD_EXCEPTION_NAME_NOT_UNIQUE, "Multiple definitions of exception '" + v_ex.name + "' in the same rule", ex_ctx); } } // note: the legacy lua loader used to throw a "xxx must strings" error // fields are optional when append is true decode_exception_fields(ex, v_ex.fields, ex_ctx, append); decode_exception_comps(ex, v_ex.comps, ex_ctx); const YAML::Node& exvals = ex["values"]; if(exvals.IsDefined()) { rule_loader::context vals_ctx(exvals, rule_loader::context::EXCEPTION_VALUES, "", ex_ctx); THROW(!exvals.IsSequence(), "Rule exception values must be a sequence", vals_ctx); for(const auto& val : exvals) { rule_loader::context vctx(val, rule_loader::context::EXCEPTION_VALUE, "", vals_ctx); rule_loader::rule_exception_info::entry v_ex_val; decode_exception_values(val, v_ex_val, vctx); v_ex.values.push_back(v_ex_val); } } else if(append) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_APPEND_NO_VALUES, "Overriding/appending exception with no values", ex_ctx); } exceptions.push_back(v_ex); } } static void read_rule_exceptions( rule_loader::configuration& cfg, const YAML::Node& item, std::optional>& exceptions, const rule_loader::context& parent, bool append) { std::vector decoded; read_rule_exceptions(cfg, item, decoded, parent, append); exceptions = decoded; } static void warn_unknown_keys(const YAML::Node& item, const std::set& expected_keys, rule_loader::configuration& cfg, const rule_loader::context& ctx) { if(!item.IsMap()) { return; } for(auto it = item.begin(); it != item.end(); ++it) { std::string key = it->first.as(); if(expected_keys.find(key) == expected_keys.end()) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_UNKNOWN_KEY, "Unknown key '" + key + "'. The key will be ignored.", ctx); } } } inline static bool check_update_expected(std::set& expected_keys, const std::set& overrides, const std::string& override_type, const std::string& key, const rule_loader::context& ctx) { if(overrides.find(key) == overrides.end()) { return false; } THROW(expected_keys.find(key) == expected_keys.end(), std::string("An ") + override_type + " override for '" + key + "' was specified but '" + key + "' is not defined", ctx); expected_keys.erase(key); return true; } void rule_loader::reader::read_item(rule_loader::configuration& cfg, rule_loader::collector& collector, const YAML::Node& item, const rule_loader::context& parent) { { rule_loader::context tmp(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent); THROW(!item.IsMap(), "Unexpected element type. " "Each element should be a yaml associative array.", tmp); } if(item["required_engine_version"].IsDefined()) { rule_loader::context ctx(item, rule_loader::context::REQUIRED_ENGINE_VERSION, "", parent); rule_loader::engine_version_info v(ctx); try { // Convert convert to an uint (more restrictive than converting to a string) uint32_t ver; decode_val(item, "required_engine_version", ver, ctx); // Build proper semver representation v.version = rule_loader::reader::get_implicit_engine_version(ver); } catch(std::exception& e) { // Convert to string std::string ver; decode_val(item, "required_engine_version", ver, ctx); v.version = sinsp_version(ver); THROW(!v.version.is_valid(), "Unable to parse engine version '" + ver + "' as a semver string. Expected \"x.y.z\" semver format.", ctx); } collector.define(cfg, v); static const std::set expected_required_engine_version_keys{ "required_engine_version"}; warn_unknown_keys(item, expected_required_engine_version_keys, cfg, ctx); } else if(item["required_plugin_versions"].IsDefined()) { const YAML::Node& req_plugin_vers = item["required_plugin_versions"]; rule_loader::context ctx(req_plugin_vers, rule_loader::context::REQUIRED_PLUGIN_VERSIONS, "", parent); THROW(!req_plugin_vers.IsSequence(), "Value of required_plugin_versions must be a sequence", ctx); for(const YAML::Node& plugin : req_plugin_vers) { rule_loader::plugin_version_info::requirement r; // Use a temp context until we can get a name rule_loader::context tmp(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, "", ctx); THROW(!plugin.IsMap(), "Plugin version must be a mapping", tmp); decode_val(plugin, "name", r.name, tmp); rule_loader::context pctx(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, r.name, ctx); rule_loader::plugin_version_info v(pctx); decode_val(plugin, "version", r.version, pctx); v.alternatives.push_back(r); const YAML::Node& alternatives = plugin["alternatives"]; if(alternatives.IsDefined()) { THROW(!alternatives.IsSequence(), "Value of plugin version alternatives must be a sequence", pctx); for(const auto& req : alternatives) { tmp = rule_loader::context( req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, "", pctx); THROW(!req.IsMap(), "Plugin version alternative must be a mapping", tmp); decode_val(req, "name", r.name, tmp); tmp = rule_loader::context( req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, r.name, pctx); decode_val(req, "version", r.version, tmp); v.alternatives.push_back(r); } } collector.define(cfg, v); } static const std::set expected_required_plugin_versions_keys{ "required_plugin_versions"}; rule_loader::context rpv_ctx(item, rule_loader::context::REQUIRED_PLUGIN_VERSIONS, "", parent); warn_unknown_keys(item, expected_required_plugin_versions_keys, cfg, rpv_ctx); } else if(item["list"].IsDefined()) { std::string name; // Using tmp context until name is decoded rule_loader::context tmp(item, rule_loader::context::LIST, "", parent); decode_val(item, "list", name, tmp); rule_loader::context ctx(item, rule_loader::context::LIST, name, parent); bool invalid_name = !re2::RE2::FullMatch(name, s_rgx_barestr); if(invalid_name) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_INVALID_LIST_NAME, "List has an invalid name. List names should match a regular " "expression: " RGX_BARESTR, ctx); } rule_loader::list_info v(ctx); bool append = false; decode_val(item, "list", v.name, ctx); decode_items(item, v.items, ctx); decode_optional_val(item, "append", append, ctx); if(append) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_DEPRECATED_ITEM, WARNING_APPEND, ctx); } std::set override_append, override_replace; std::set overridable{"items"}; decode_overrides(item, overridable, overridable, override_append, override_replace, ctx); bool has_overrides = !override_append.empty() || !override_replace.empty(); THROW(append && has_overrides, ERROR_OVERRIDE_APPEND, ctx); // Since a list only has items, if we have chosen to append them we can append the entire // object otherwise we just want to redefine the list. append |= override_append.find("items") != override_append.end(); if(append) { collector.append(cfg, v); } else { collector.define(cfg, v); } static const std::set expected_list_keys{"list", "items", "append", "override"}; warn_unknown_keys(item, expected_list_keys, cfg, ctx); } else if(item["macro"].IsDefined()) { std::string name; // Using tmp context until name is decoded rule_loader::context tmp(item, rule_loader::context::MACRO, "", parent); decode_val(item, "macro", name, tmp); rule_loader::context ctx(item, rule_loader::context::MACRO, name, parent); bool invalid_name = !re2::RE2::FullMatch(name, s_rgx_identifier); if(invalid_name) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_INVALID_MACRO_NAME, "Macro has an invalid name. Macro names should match a regular " "expression: " RGX_IDENTIFIER, ctx); } rule_loader::macro_info v(ctx); v.name = name; bool append = false; decode_val(item, "condition", v.cond, ctx); // Now set the proper context for the condition now that we know it exists v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::MACRO_CONDITION, "", ctx); decode_optional_val(item, "append", append, ctx); if(append) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_DEPRECATED_ITEM, WARNING_APPEND, ctx); } std::set override_append, override_replace; std::set overridable{"condition"}; decode_overrides(item, overridable, overridable, override_append, override_replace, ctx); bool has_overrides = !override_append.empty() || !override_replace.empty(); THROW((append && has_overrides), ERROR_OVERRIDE_APPEND, ctx); // Since a macro only has a condition, if we have chosen to append to it we can append the // entire object otherwise we just want to redefine the macro. append |= override_append.find("condition") != override_append.end(); if(append) { collector.append(cfg, v); } else { collector.define(cfg, v); } static const std::set expected_macro_keys{"macro", "condition", "append", "override"}; warn_unknown_keys(item, expected_macro_keys, cfg, ctx); } else if(item["rule"].IsDefined()) { std::string name; // Using tmp context until name is decoded rule_loader::context tmp(item, rule_loader::context::RULE, "", parent); decode_val(item, "rule", name, tmp); rule_loader::context ctx(item, rule_loader::context::RULE, name, parent); std::string source = ""; decode_optional_empty_val(item, "source", source, ctx); bool has_append_flag = false; decode_optional_val(item, "append", has_append_flag, ctx); if(has_append_flag) { cfg.res->add_warning(falco::load_result::warning_code::LOAD_DEPRECATED_ITEM, WARNING_APPEND, ctx); } std::set override_append, override_replace; std::set overridable_append{"condition", "output", "desc", "tags", "exceptions"}; std::set overridable_replace{"condition", "output", "desc", "priority", "capture", "capture_duration", "tags", "exceptions", "enabled", "warn_evttypes", "skip-if-unknown-filter"}; decode_overrides(item, overridable_append, overridable_replace, override_append, override_replace, ctx); bool has_overrides_append = !override_append.empty(); bool has_overrides_replace = !override_replace.empty(); bool has_overrides = has_overrides_append || has_overrides_replace; THROW((has_append_flag && has_overrides), ERROR_OVERRIDE_APPEND, ctx); if(has_overrides) { std::set expected_keys; for(auto& key : overridable_append) { if(item[key].IsDefined()) { expected_keys.insert(key); } } for(auto& key : overridable_replace) { if(item[key].IsDefined()) { expected_keys.insert(key); } } // expected_keys is (appendable U replaceable) ^ (defined) if(has_overrides_append) { rule_loader::rule_update_info v(ctx); v.name = name; if(check_update_expected(expected_keys, override_append, "append", "condition", ctx)) { v.source = source; decode_val(item, "condition", v.cond, ctx); } if(check_update_expected(expected_keys, override_append, "append", "exceptions", ctx)) { read_rule_exceptions(cfg, item, v.exceptions, ctx, true); } if(check_update_expected(expected_keys, override_append, "append", "output", ctx)) { decode_val(item, "output", v.output, ctx); } if(check_update_expected(expected_keys, override_append, "append", "desc", ctx)) { decode_val(item, "desc", v.desc, ctx); } if(check_update_expected(expected_keys, override_append, "append", "tags", ctx)) { decode_tags(item, v.tags, ctx); } collector.append(cfg, v); } if(has_overrides_replace) { rule_loader::rule_update_info v(ctx); v.name = name; if(check_update_expected(expected_keys, override_replace, "replace", "condition", ctx)) { v.source = source; decode_val(item, "condition", v.cond, ctx); } if(check_update_expected(expected_keys, override_replace, "replace", "exceptions", ctx)) { read_rule_exceptions(cfg, item, v.exceptions, ctx, false); } if(check_update_expected(expected_keys, override_replace, "replace", "output", ctx)) { decode_val(item, "output", v.output, ctx); } if(check_update_expected(expected_keys, override_replace, "replace", "desc", ctx)) { decode_val(item, "desc", v.desc, ctx); } if(check_update_expected(expected_keys, override_replace, "replace", "tags", ctx)) { decode_tags(item, v.tags, ctx); } if(check_update_expected(expected_keys, override_replace, "replace", "priority", ctx)) { std::string priority; decode_val(item, "priority", priority, ctx); rule_loader::context prictx(item["priority"], rule_loader::context::RULE_PRIORITY, "", ctx); falco_common::priority_type parsed_priority; THROW(!falco_common::parse_priority(priority, parsed_priority), "Invalid priority", prictx); v.priority = parsed_priority; } if(check_update_expected(expected_keys, override_replace, "replace", "capture", ctx)) { decode_val(item, "capture", v.capture, ctx); } if(check_update_expected(expected_keys, override_replace, "replace", "capture_duration", ctx)) { decode_val(item, "capture_duration", v.capture_duration, ctx); } if(check_update_expected(expected_keys, override_replace, "replace", "enabled", ctx)) { decode_val(item, "enabled", v.enabled, ctx); } if(check_update_expected(expected_keys, override_replace, "replace", "warn_evttypes", ctx)) { decode_val(item, "warn_evttypes", v.warn_evttypes, ctx); } if(check_update_expected(expected_keys, override_replace, "replace", "skip-if-unknown-filter", ctx)) { decode_val(item, "skip-if-unknown-filter", v.skip_if_unknown_filter, ctx); } collector.selective_replace(cfg, v); } // if any expected key has not been defined throw an error for(const auto& key : expected_keys) { rule_loader::context keyctx(item[key], rule_loader::context::OVERRIDE, key, ctx); THROW(true, "Unexpected key '" + key + "': no corresponding entry under 'override' is defined.", keyctx); } } else if(has_append_flag) { rule_loader::rule_update_info v(ctx); v.name = name; v.source = source; if(item["condition"].IsDefined()) { v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx); decode_val(item, "condition", v.cond, ctx); } if(item["exceptions"].IsDefined()) { read_rule_exceptions(cfg, item, v.exceptions, ctx, true); } // TODO restore this error and update testing // THROW((!v.cond.has_value() && !v.exceptions.has_value()), // "Appended rule must have exceptions or condition property", // v.ctx); collector.append(cfg, v); } else { rule_loader::rule_info v(ctx); v.name = name; v.enabled = true; v.capture = false; v.capture_duration = 0; v.warn_evttypes = true; v.skip_if_unknown_filter = false; // If the rule does *not* have any of // condition/output/desc/priority, it *must* // have an enabled property. Use the enabled // property to set the enabled status of an // earlier rule. if(!item["condition"].IsDefined() && !item["output"].IsDefined() && !item["desc"].IsDefined() && !item["priority"].IsDefined()) { decode_val(item, "enabled", v.enabled, ctx); cfg.res->add_warning(falco::load_result::warning_code::LOAD_DEPRECATED_ITEM, WARNING_ENABLED, ctx); collector.enable(cfg, v); } else { std::string priority; // All of these are required decode_val(item, "condition", v.cond, ctx); v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx); decode_val(item, "output", v.output, ctx); v.output_ctx = rule_loader::context(item["output"], rule_loader::context::RULE_OUTPUT, "", ctx); decode_val(item, "desc", v.desc, ctx); decode_val(item, "priority", priority, ctx); v.output = trim(v.output); v.source = falco_common::syscall_source; rule_loader::context prictx(item["priority"], rule_loader::context::RULE_PRIORITY, "", ctx); THROW(!falco_common::parse_priority(priority, v.priority), "Invalid priority", prictx); decode_optional_val(item, "source", v.source, ctx); decode_optional_val(item, "enabled", v.enabled, ctx); decode_optional_val(item, "capture", v.capture, ctx); decode_optional_val(item, "capture_duration", v.capture_duration, ctx); decode_optional_val(item, "warn_evttypes", v.warn_evttypes, ctx); decode_optional_val(item, "skip-if-unknown-filter", v.skip_if_unknown_filter, ctx); decode_tags(item, v.tags, ctx); read_rule_exceptions(cfg, item, v.exceptions, ctx, false); collector.define(cfg, v); } } static const std::set expected_rule_keys{"rule", "condition", "output", "desc", "priority", "source", "enabled", "capture", "capture_duration", "warn_evttypes", "skip-if-unknown-filter", "tags", "exceptions", "override", "append"}; warn_unknown_keys(item, expected_rule_keys, cfg, ctx); } else { rule_loader::context ctx(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent); cfg.res->add_warning(falco::load_result::warning_code::LOAD_UNKNOWN_ITEM, "Unknown top level item", ctx); } } bool rule_loader::reader::read(rule_loader::configuration& cfg, collector& collector, const nlohmann::json& schema) { std::vector docs; yaml_helper reader; std::vector schema_warnings; rule_loader::context ctx(cfg.name); try { docs = reader.loadall_from_string(cfg.content, schema, &schema_warnings); } catch(YAML::ParserException& e) { rule_loader::context ictx(e.mark, ctx); cfg.res->add_error(falco::load_result::LOAD_ERR_YAML_PARSE, e.what(), ictx); return false; } catch(std::exception& e) { cfg.res->add_error(falco::load_result::LOAD_ERR_YAML_PARSE, e.what(), ctx); return false; } catch(...) { cfg.res->add_error(falco::load_result::LOAD_ERR_YAML_PARSE, "unknown YAML parsing error", ctx); return false; } cfg.res->set_schema_validation_status(schema_warnings); for(auto doc = docs.begin(); doc != docs.end(); doc++) { if(doc->IsDefined() && !doc->IsNull()) { try { THROW(!doc->IsMap() && !doc->IsSequence(), "Rules content is not yaml", ctx); THROW(!doc->IsSequence(), "Rules content is not yaml array of objects", ctx); for(auto it = doc->begin(); it != doc->end(); it++) { if(!it->IsNull()) { read_item(cfg, collector, *it, ctx); } } } catch(rule_loader::rule_load_exception& e) { cfg.res->add_error(e.ec, e.msg, e.ctx); // Although we *could* continue on to the next doc, // as it's effectively a new rules file, for // consistency we stop at the first error. return false; } catch(YAML::ParserException& e) { rule_loader::context ictx(e.mark, ctx); cfg.res->add_error(falco::load_result::LOAD_ERR_YAML_VALIDATE, e.what(), ictx); return false; } catch(std::exception& e) { cfg.res->add_error(falco::load_result::LOAD_ERR_VALIDATE, e.what(), ctx); return false; } catch(...) { cfg.res->add_error(falco::load_result::LOAD_ERR_VALIDATE, "unknown validation error", ctx); return false; } } } return true; } ================================================ FILE: userspace/engine/rule_loader_reader.h ================================================ // 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. */ #pragma once #include "rule_loader.h" #include "rule_loader_collector.h" #include #include #include "falco_engine_version.h" namespace rule_loader { /*! \brief Reads the contents of a ruleset */ class reader { public: reader() = default; virtual ~reader() = default; reader(reader&&) = default; reader& operator=(reader&&) = default; reader(const reader&) = default; reader& operator=(const reader&) = default; /*! \brief Reads the contents of a ruleset and uses a collector to store thew new definitions */ virtual bool read(configuration& cfg, collector& loader, const nlohmann::json& schema = {}); /*! \brief Engine version used to be represented as a simple progressive number. With the new semver schema, the number now represents the semver minor number. This function converts the legacy version number to the new semver schema. */ static inline sinsp_version get_implicit_engine_version(uint32_t minor) { return sinsp_version(std::to_string(FALCO_ENGINE_VERSION_MAJOR) + "." + std::to_string(minor) + "." + std::to_string(FALCO_ENGINE_VERSION_PATCH)); } template static void decode_val(const YAML::Node& item, const char* key, T& out, const rule_loader::context& ctx); template static void decode_optional_val(const YAML::Node& item, const char* key, T& out, const rule_loader::context& ctx); template static void decode_optional_empty_val(const YAML::Node& item, const char* key, T& out, const rule_loader::context& ctx); protected: virtual void read_item(rule_loader::configuration& cfg, rule_loader::collector& collector, const YAML::Node& item, const rule_loader::context& parent); }; }; // namespace rule_loader ================================================ FILE: userspace/engine/rule_loading_messages.h ================================================ #pragma once //////////////// // Warnings //////////////// #define WARNING_APPEND \ "'append' key is deprecated. Add an 'append' entry (e.g. 'condition: append') under " \ "'override' instead." #define WARNING_ENABLED \ "The standalone 'enabled' key usage is deprecated. The correct approach requires also a " \ "'replace' entry under the 'override' key (i.e. 'enabled: replace')." //////////////// // Errors //////////////// #define ERROR_OVERRIDE_APPEND \ "Keys 'override' and 'append: true' cannot be used together. Add an 'append' entry (e.g. " \ "'condition: append') under 'override' instead." #define ERROR_NO_PREVIOUS_MACRO \ "Macro uses 'append' or 'override.condition: append' but no macro by that name already exists" #define ERROR_NO_PREVIOUS_LIST \ "List uses 'append' or 'override.items: append' but no list by that name already exists" #define ERROR_NO_PREVIOUS_RULE_APPEND \ "Rule uses 'append' or 'override.: append' but no rule by that name already exists" #define ERROR_NO_PREVIOUS_RULE_REPLACE \ "An 'override.: replace' to a rule was requested but no rule by that name already exists" ================================================ FILE: userspace/engine/stats_manager.cpp ================================================ // 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 "stats_manager.h" #include "falco_common.h" stats_manager::stats_manager(): m_total(0) {} stats_manager::~stats_manager() { clear(); } void stats_manager::clear() { m_total = 0; m_by_rule_id.clear(); m_by_priority.clear(); } void stats_manager::format(const indexed_vector& rules, std::string& out) const { std::string fmt; out = "Events detected: " + to_string(m_total) + "\n"; out += "Rule counts by severity:\n"; for(size_t i = 0; i < m_by_priority.size(); i++) { auto val = m_by_priority[i]->load(); if(val > 0) { falco_common::format_priority((falco_common::priority_type)i, fmt, true); transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper); out += " " + fmt + ": " + std::to_string(val) + "\n"; } } out += "Triggered rules by rule name:\n"; for(size_t i = 0; i < m_by_rule_id.size(); i++) { auto val = m_by_rule_id[i]->load(); if(val > 0) { out += " " + rules.at(i)->name + ": " + std::to_string(val) + "\n"; } } } void stats_manager::on_rule_loaded(const falco_rule& rule) { while(m_by_rule_id.size() <= rule.id) { m_by_rule_id.emplace_back(std::make_unique>(0)); } while(m_by_priority.size() <= (size_t)rule.priority) { m_by_priority.emplace_back(std::make_unique>(0)); } } void stats_manager::on_event(const falco_rule& rule) { if(m_by_rule_id.size() <= rule.id || m_by_priority.size() <= (size_t)rule.priority) { throw falco_exception("rule id or priority out of bounds"); } m_total.fetch_add(1, std::memory_order_relaxed); m_by_rule_id[rule.id]->fetch_add(1, std::memory_order_relaxed); m_by_priority[(size_t)rule.priority]->fetch_add(1, std::memory_order_relaxed); } ================================================ FILE: userspace/engine/stats_manager.h ================================================ // 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. */ #pragma once #include #include #include #include #include "falco_rule.h" #include "indexed_vector.h" /*! \brief Manager for the internal statistics of the rule engine. The on_event() is thread-safe and non-blocking, and it can be used concurrently across many callers in parallel. All the other methods are not thread safe. */ class stats_manager { public: stats_manager(); virtual ~stats_manager(); /*! \brief Erases the internal state and statistics data */ virtual void clear(); /*! \brief Callback for when a new rule is loaded in the engine. Rules must be passed through this method before submitting them as an argument of on_event(). */ virtual void on_rule_loaded(const falco_rule& rule); /*! \brief Callback for when a given rule matches an event. This method is thread-safe. \throws falco_exception if rule has not been passed to on_rule_loaded() first */ virtual void on_event(const falco_rule& rule); /*! \brief Formats the internal statistics into the out string. */ virtual void format(const indexed_vector& rules, std::string& out) const; // Getter functions inline const std::atomic& get_total() const { return m_total; } inline const std::vector>>& get_by_priority() const { return m_by_priority; } inline const std::vector>>& get_by_rule_id() const { return m_by_rule_id; } private: std::atomic m_total; std::vector>> m_by_priority; std::vector>> m_by_rule_id; }; ================================================ FILE: userspace/engine/yaml_helper.h ================================================ // 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. */ #pragma once #include #include #ifdef _WIN32 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class yaml_helper; class yaml_visitor { private: using Callback = std::function; explicit 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 seen; Callback cb; friend class yaml_helper; }; /** * @brief An helper class for reading and editing YAML documents */ class yaml_helper { public: inline static const std::string configs_key = "config_files"; inline static const std::string validation_ok = "ok"; inline static const std::string validation_failed = "failed"; inline static const std::string validation_none = "none"; enum config_files_strategy { STRATEGY_APPEND, // append to existing sequence keys, override scalar keys and add new ones STRATEGY_OVERRIDE, // override existing keys (sequences too) and add new ones STRATEGY_ADDONLY // only add new keys and ignore existing ones }; static enum config_files_strategy strategy_from_string(const std::string& strategy) { if(strategy == "override") { return yaml_helper::STRATEGY_OVERRIDE; } if(strategy == "add-only") { return yaml_helper::STRATEGY_ADDONLY; } return yaml_helper::STRATEGY_APPEND; } static std::string strategy_to_string(const enum config_files_strategy strategy) { switch(strategy) { case yaml_helper::STRATEGY_OVERRIDE: return "override"; case yaml_helper::STRATEGY_ADDONLY: return "add-only"; default: return "append"; } } /** * Load all the YAML document represented by the input string. * Since this is used by rule loader, does not process env vars. */ std::vector loadall_from_string( const std::string& input, const nlohmann::json& schema = {}, std::vector* schema_warnings = nullptr) { auto nodes = YAML::LoadAll(input); if(schema_warnings) { schema_warnings->clear(); if(!schema.empty()) { // Validate each node. for(const auto& node : nodes) { validate_node(node, schema, schema_warnings); } } else { schema_warnings->push_back(validation_none); } } return nodes; } /** * Load the YAML document represented by the input string. */ void load_from_string(const std::string& input, const nlohmann::json& schema = {}, std::vector* schema_warnings = nullptr) { m_root = YAML::Load(input); pre_process_env_vars(m_root); if(schema_warnings) { schema_warnings->clear(); if(!schema.empty()) { validate_node(m_root, schema, schema_warnings); } else { schema_warnings->push_back(validation_none); } } } /** * Load the YAML document from the given file path. */ void load_from_file(const std::string& path, const nlohmann::json& schema = {}, std::vector* schema_warnings = nullptr) { m_root = load_from_file_int(path, schema, schema_warnings); } void include_config_file(const std::string& include_file_path, enum config_files_strategy strategy = STRATEGY_APPEND, const nlohmann::json& schema = {}, std::vector* schema_warnings = nullptr) { auto loaded_nodes = load_from_file_int(include_file_path, schema, schema_warnings); for(auto n : loaded_nodes) { /* * To avoid recursion hell, * we don't support `config_files` directives from included config files * (that use load_from_file_int recursively). */ const auto& key = n.first.Scalar(); if(key == configs_key) { throw std::runtime_error("Config error: '" + configs_key + "' directive in included config file " + include_file_path + "."); } switch(strategy) { case STRATEGY_APPEND: if(n.second.IsSequence()) { for(const auto& item : n.second) { m_root[key].push_back(item); } break; } // fallthrough case STRATEGY_OVERRIDE: m_root[key] = n.second; break; case STRATEGY_ADDONLY: if(!m_root[key].IsDefined()) { m_root[key] = n.second; } break; } } } /** * Clears the internal loaded document. */ void clear() { m_root = YAML::Node(); } /** * Get a scalar value from the node identified by key. */ template const T get_scalar(const std::string& key, const T& default_value) const { YAML::Node node; get_node(node, key); if(node.IsDefined()) { return node.as(default_value); } return default_value; } /** * Set the node identified by key to value. */ template void set_scalar(const std::string& key, const T& value) { YAML::Node node; get_node(node, key, true); node = value; } /** * Set the node identified by key to an object value */ void set_object(const std::string& key, const YAML::Node& value) { YAML::Node node; get_node(node, key, true); node = value; } /** * Get the sequence value from the node identified by key. */ template void get_sequence(T& ret, const std::string& key) const { YAML::Node node; get_node(node, key); return get_sequence_from_node(ret, node); } /** * Return true if the node identified by key is defined. */ bool is_defined(const std::string& key) const { YAML::Node node; get_node(node, key); return node.IsDefined(); } std::string dump() const { YAML::Emitter emitter; emitter << YAML::DoubleQuoted << YAML::Flow << YAML::LowerNull << YAML::BeginSeq << m_root; return emitter.c_str() + 1; // drop initial '[' char } private: YAML::Node m_root; YAML::Node load_from_file_int(const std::string& path, const nlohmann::json& schema, std::vector* schema_warnings) { auto root = YAML::LoadFile(path); pre_process_env_vars(root); if(schema_warnings) { schema_warnings->clear(); if(!schema.empty()) { validate_node(root, schema, schema_warnings); } else { schema_warnings->push_back(validation_none); } } return root; } void validate_node(const YAML::Node& node, const nlohmann::json& schema, std::vector* schema_warnings) { // Validate the yaml against our json schema valijson::Schema schemaDef; valijson::SchemaParser schemaParser; valijson::Validator validator(valijson::Validator::kWeakTypes); valijson::ValidationResults validationResults; valijson::adapters::YamlCppAdapter configAdapter(node); valijson::adapters::NlohmannJsonAdapter schemaAdapter(schema); schemaParser.populateSchema(schemaAdapter, schemaDef); if(!validator.validate(schemaDef, configAdapter, &validationResults)) { valijson::ValidationResults::Error error; // report only the top-most error while(validationResults.popError(error)) { schema_warnings->push_back(std::string(validation_failed + " for ") + std::accumulate(error.context.begin(), error.context.end(), std::string("")) + ": " + error.description); } } else { schema_warnings->push_back(validation_ok); } } /* * 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::Node& root) { yaml_visitor([](YAML::Node& scalar) { auto value = scalar.as(); 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; })(root); } /** * Key is a string representing a node in the YAML document. * The provided key string can navigate the document in its * nested nodes, with arbitrary depth. The key string follows * this regular language: * * Key := NodeKey ('.' NodeKey)* * NodeKey := (any)+ ('[' (integer)+? ']')* * * If can_append is true, an empty NodeKey will append a new entry * to the sequence, it is rejected otherwise. * * Some examples of accepted key strings: * - NodeName * - ListValue[3].subvalue * - MatrixValue[1][3] * - value1.subvalue2.subvalue3 */ void get_node(YAML::Node& ret, const std::string& key, bool can_append = false) const { try { std::string nodeKey; ret.reset(m_root); for(std::string::size_type i = 0; i < key.size(); ++i) { char c = key[i]; bool should_shift = c == '.' || c == '[' || i == key.size() - 1; if(c != '.' && c != '[') { if(i > 0 && nodeKey.empty() && key[i - 1] != '.') { throw std::runtime_error("Parsing error: expected '.' character at pos " + std::to_string(i - 1)); } nodeKey += c; } if(should_shift) { if(nodeKey.empty()) { throw std::runtime_error("Parsing error: unexpected character at pos " + std::to_string(i)); } ret.reset(ret[nodeKey]); nodeKey.clear(); } if(c == '[') { auto close_param_idx = key.find(']', i); std::string idx_str = key.substr(i + 1, close_param_idx - i - 1); int nodeIdx; bool ret_appendable = !ret.IsDefined() || ret.IsSequence(); if(idx_str.empty() && ret_appendable && can_append) { YAML::Node newNode; ret.push_back(newNode); nodeIdx = ret.size() - 1; } else { try { nodeIdx = std::stoi(idx_str); } catch(const std::exception& e) { throw std::runtime_error( "Parsing error: expected a numeric index, found '" + idx_str + "'"); } } ret.reset(ret[nodeIdx]); i = close_param_idx; if(i < key.size() - 1 && key[i + 1] == '.') { i++; } } } } catch(const std::exception& e) { throw std::runtime_error("Config error at key \"" + key + "\": " + std::string(e.what())); } } template void get_sequence_from_node(T& ret, const YAML::Node& node) const { if(node.IsDefined()) { if(node.IsSequence()) { for(const YAML::Node& item : node) { ret.insert(ret.end(), item.as()); } } else if(node.IsScalar()) { ret.insert(ret.end(), node.as()); } } } }; // define a yaml-cpp conversion function for nlohmann json objects namespace YAML { template<> struct convert { static bool decode(const Node& node, nlohmann::json& res) { switch(node.Type()) { case YAML::NodeType::Map: for(auto&& it : node) { nlohmann::json sub{}; YAML::convert::decode(it.second, sub); res[it.first.as()] = sub; } break; case YAML::NodeType::Sequence: for(auto&& it : node) { nlohmann::json sub{}; YAML::convert::decode(it, sub); res.emplace_back(sub); } break; case YAML::NodeType::Scalar: { int int_val; double double_val; bool bool_val; std::string str_val; if(YAML::convert::decode(node, int_val)) { res = int_val; } else if(YAML::convert::decode(node, double_val)) { res = double_val; } else if(YAML::convert::decode(node, bool_val)) { res = bool_val; } else if(YAML::convert::decode(node, str_val)) { res = str_val; } } default: break; } return true; } // The "encode" function is not needed here, in fact you can simply YAML::load any json string. }; } // namespace YAML ================================================ FILE: userspace/falco/CMakeLists.txt ================================================ # 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. # configure_file(config_falco.h.in config_falco.h) add_library( falco_application STATIC app/app.cpp app/options.cpp app/restart_handler.cpp app/actions/helpers_generic.cpp app/actions/helpers_inspector.cpp app/actions/configure_interesting_sets.cpp app/actions/create_signal_handlers.cpp app/actions/pidfile.cpp app/actions/init_falco_engine.cpp app/actions/init_inspectors.cpp app/actions/init_outputs.cpp app/actions/list_fields.cpp app/actions/list_plugins.cpp app/actions/load_config.cpp app/actions/load_plugins.cpp app/actions/load_rules_files.cpp app/actions/process_events.cpp app/actions/print_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 app/actions/event_formatter.cpp app/actions/print_version.cpp app/actions/print_page_size.cpp app/actions/configure_syscall_buffer_size.cpp app/actions/configure_syscall_buffer_num.cpp app/actions/select_event_sources.cpp app/actions/start_webserver.cpp app/actions/validate_rules_files.cpp app/actions/close_inspectors.cpp app/actions/cleanup_outputs.cpp app/actions/print_config_schema.cpp app/actions/print_rule_schema.cpp configuration.cpp falco_outputs.cpp outputs_file.cpp outputs_stdout.cpp event_drops.cpp stats_writer.cpp versions_info.cpp ) set(FALCO_INCLUDE_DIRECTORIES "${PROJECT_SOURCE_DIR}/userspace/engine" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" "${PROJECT_BINARY_DIR}/driver/src" "${CXXOPTS_INCLUDE_DIR}" ) set(FALCO_DEPENDENCIES cxxopts) set(FALCO_LIBRARIES falco_engine) if(USE_JEMALLOC OR USE_MIMALLOC) list(APPEND FALCO_DEPENDENCIES malloc) list(APPEND FALCO_LIBRARIES ${MALLOC_LIB}) endif() if(USE_GPERFTOOLS) list(APPEND FALCO_DEPENDENCIES gperftools) list(APPEND FALCO_LIBRARIES "${GPERFTOOLS_PROFILER_LIB}") endif() if(NOT WIN32) target_sources(falco_application PRIVATE outputs_program.cpp outputs_syslog.cpp) endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT MINIMAL_BUILD) target_sources(falco_application PRIVATE outputs_http.cpp falco_metrics.cpp webserver.cpp) list(APPEND FALCO_INCLUDE_DIRECTORIES FALCO_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}" "${CARES_INCLUDE}" ) if(TARGET c-ares) list(APPEND FALCO_DEPENDENCIES c-ares) endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND USE_BUNDLED_CURL) list(APPEND FALCO_DEPENDENCIES curl) endif() list(APPEND FALCO_LIBRARIES httplib::httplib "${CURL_LIBRARIES}" "${CARES_LIB}") endif() if(EMSCRIPTEN) target_compile_options(falco_application PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0") endif() target_compile_definitions(falco_application PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT) add_dependencies(falco_application ${FALCO_DEPENDENCIES}) target_link_libraries(falco_application ${FALCO_LIBRARIES}) target_include_directories(falco_application PUBLIC ${FALCO_INCLUDE_DIRECTORIES}) add_executable(falco falco.cpp) add_dependencies(falco falco_application ${FALCO_DEPENDENCIES}) target_link_libraries(falco falco_application ${FALCO_LIBRARIES}) target_include_directories(falco PUBLIC ${FALCO_INCLUDE_DIRECTORIES}) if(EMSCRIPTEN) target_compile_options(falco PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0") target_link_options(falco PRIVATE "-sALLOW_MEMORY_GROWTH=1") target_link_options(falco PRIVATE "-sDISABLE_EXCEPTION_CATCHING=0") target_link_options(falco PRIVATE "-sMODULARIZE=1") target_link_options(falco PRIVATE "-sEXPORT_ES6=1") target_link_options(falco PRIVATE "-sEXPORTED_RUNTIME_METHODS=['FS', 'callMain']") target_link_options(falco PRIVATE "-sEXPORTED_FUNCTIONS=['_main','_htons','_ntohs']") endif() # strip the Falco binary when releasing using musl if(MUSL_OPTIMIZED_BUILD AND CMAKE_BUILD_TYPE STREQUAL "release") add_custom_command( TARGET falco POST_BUILD COMMAND ${CMAKE_STRIP} --strip-unneeded falco COMMENT "Strip the Falco binary when releasing the musl build" ) endif() # TODO: Add win32 support. https://github.com/falcosecurity/falco/issues/3445 if(CMAKE_BUILD_TYPE STREQUAL "relwithdebinfo" AND NOT WIN32) find_program(OBJCOPY_EXECUTABLE NAMES objcopy) if(OBJCOPY_EXECUTABLE) add_custom_command( TARGET falco POST_BUILD COMMAND ${OBJCOPY_EXECUTABLE} --only-keep-debug $ $.debug COMMAND ${OBJCOPY_EXECUTABLE} --strip-debug --strip-unneeded $ COMMAND ${OBJCOPY_EXECUTABLE} --add-gnu-debuglink=$.debug $ COMMENT "Generating separate debug file for falco" ) else() message(WARNING "objcopy not found; separate debug files will not be generated.") endif() endif() if(EMSCRIPTEN) install( FILES "$/falco.js" "$/falco.wasm" DESTINATION ${FALCO_BIN_DIR} COMPONENT "${FALCO_COMPONENT_NAME}" ) elseif(WIN32) install( TARGETS falco DESTINATION bin COMPONENT "${FALCO_COMPONENT_NAME}" ) else() install(TARGETS falco RUNTIME DESTINATION ${FALCO_BIN_DIR} COMPONENT "${FALCO_COMPONENT_NAME}") endif() ================================================ FILE: userspace/falco/app/actions/actions.h ================================================ // 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. */ #pragma once #include "../state.h" #include "../run_result.h" namespace falco { namespace app { namespace actions { falco::app::run_result configure_interesting_sets(falco::app::state& s); falco::app::run_result configure_syscall_buffer_size(falco::app::state& s); falco::app::run_result configure_syscall_buffer_num(const falco::app::state& s); falco::app::run_result create_signal_handlers(falco::app::state& s); falco::app::run_result pidfile(const falco::app::state& s); falco::app::run_result init_falco_engine(falco::app::state& s); falco::app::run_result init_inspectors(falco::app::state& s); falco::app::run_result init_outputs(falco::app::state& s); falco::app::run_result list_fields(falco::app::state& s); falco::app::run_result list_plugins(const falco::app::state& s); falco::app::run_result load_config(const falco::app::state& s); falco::app::run_result load_plugins(falco::app::state& s); falco::app::run_result load_rules_files(falco::app::state& s); falco::app::run_result print_config_schema(falco::app::state& s); falco::app::run_result print_help(falco::app::state& s); falco::app::run_result print_ignored_events(const falco::app::state& s); falco::app::run_result print_kernel_version(const falco::app::state& s); falco::app::run_result print_page_size(const falco::app::state& s); falco::app::run_result print_plugin_info(const falco::app::state& s); falco::app::run_result print_rule_schema(falco::app::state& s); falco::app::run_result print_support(falco::app::state& s); falco::app::run_result print_syscall_events(falco::app::state& s); falco::app::run_result print_version(falco::app::state& s); falco::app::run_result process_events(falco::app::state& s); falco::app::run_result require_config_file(const falco::app::state& s); falco::app::run_result select_event_sources(falco::app::state& s); falco::app::run_result start_webserver(falco::app::state& s); falco::app::run_result stop_webserver(falco::app::state& s); falco::app::run_result unregister_signal_handlers(falco::app::state& s); falco::app::run_result validate_rules_files(falco::app::state& s); falco::app::run_result close_inspectors(falco::app::state& s); falco::app::run_result cleanup_outputs(falco::app::state& s); }; // namespace actions }; // namespace app }; // namespace falco ================================================ FILE: userspace/falco/app/actions/cleanup_outputs.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "actions.h" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::cleanup_outputs(falco::app::state& s) { if(s.outputs) { s.outputs.reset(); s.engine->print_stats(); } return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/close_inspectors.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "actions.h" #include "helpers.h" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::close_inspectors(falco::app::state& s) { falco_logger::log(falco_logger::level::DEBUG, "closing inspectors"); if(s.offline_inspector != nullptr) { s.offline_inspector->close(); } for(const auto& src : s.loaded_sources) { auto src_info = s.source_infos.at(src); if(src_info->inspector != nullptr) { src_info->inspector->close(); } } return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/configure_interesting_sets.cpp ================================================ // 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 using namespace falco::app; using namespace falco::app::actions; static void extract_base_syscalls_names(const std::unordered_set& base_syscalls_names, std::unordered_set& user_positive_names, std::unordered_set& user_negative_names) { for(const std::string& ev : base_syscalls_names) { if(!ev.empty()) { if(ev.at(0) == '!') { user_negative_names.insert(ev.substr(1, ev.size())); } else { user_positive_names.insert(ev); } } } } static void check_for_rules_unsupported_events( falco::app::state& s, const libsinsp::events::set& rules_sc_set) { /* Unsupported events are those events that are used in the rules * but that are not part of the selected event set. For now, this * is expected to happen only for high volume syscalls for * performance reasons. */ auto unsupported_sc_set = rules_sc_set.diff(s.selected_sc_set); if(unsupported_sc_set.empty()) { return; } /* Get the names of the events (syscall and non syscall events) that were not activated and * print them. */ auto names = libsinsp::events::sc_set_to_event_names(unsupported_sc_set); std::cerr << "Loaded rules match syscalls that are not activated (e.g. were removed via config " "settings such as no -A flag or negative base_syscalls elements) or unsupported " "with current configuration: warning (unsupported-evttype): " + concat_set_in_order(names) << std::endl; std::cerr << "If syscalls in rules include high volume syscalls (-> activate via `-A` flag), " "else syscalls may have been removed via base_syscalls option or might be " "associated with syscalls undefined on your architecture " "(https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html)" << std::endl; } static void select_event_set(falco::app::state& s, const libsinsp::events::set& rules_sc_set) { /* PPM syscall codes (sc) can be viewed as condensed libsinsp lookup table * to map a system call name to it's actual system syscall id (as defined * by the Linux kernel). Hence here we don't need syscall enter and exit distinction. */ auto rules_names = libsinsp::events::sc_set_to_event_names(rules_sc_set); if(!rules_sc_set.empty()) { falco_logger::log(falco_logger::level::DEBUG, "(" + std::to_string(rules_names.size()) + ") syscalls in rules: " + concat_set_in_order(rules_names) + "\n"); } /* Load PPM event codes needed by plugins with parsing capability */ libsinsp::events::set plugin_ev_codes; if(s.is_capture_mode()) { // In capture mode, we need to use the offline inspector // because plugins are inited under it; see init_inspectors action. for(const auto& p : s.offline_inspector->get_plugin_manager()->plugins()) { if(!(p->caps() & CAP_PARSING)) { continue; } plugin_ev_codes.merge(p->parse_event_codes()); } } else { // In live mode, we need to use inspectors from the loaded sources, // because plugins are inited under them; see init_inspectors action. for(const auto& src : s.loaded_sources) { auto src_info = s.source_infos.at(src); for(const auto& p : src_info->inspector->get_plugin_manager()->plugins()) { if(!(p->caps() & CAP_PARSING)) { continue; } plugin_ev_codes.merge(p->parse_event_codes()); } } } const auto plugin_sc_set = libsinsp::events::event_set_to_sc_set(plugin_ev_codes); const auto plugin_names = libsinsp::events::sc_set_to_event_names(plugin_sc_set); if(!plugin_sc_set.empty()) { falco_logger::log(falco_logger::level::DEBUG, "(" + std::to_string(plugin_names.size()) + ") syscalls required by plugins: " + concat_set_in_order(plugin_names) + "\n"); } /* DEFAULT OPTION: * Current `sinsp_state_sc_set()` approach includes multiple steps: * (1) Enforce all positive syscalls from each Falco rule * (2) Enforce static Falco state set (non-adaptive, not conditioned by rules, * but based on PPME event table flags indicating generic sinsp state modifications) * -> Final set is union of (1) and (2) * * Fall-back if no valid positive syscalls in `base_syscalls.custom_set`, * e.g. when using `base_syscalls.custom_set` only for negative syscalls. */ auto base_sc_set = libsinsp::events::sinsp_state_sc_set(); /* USER OVERRIDE INPUT OPTION `base_syscalls.custom_set` etc. */ std::unordered_set user_positive_names = {}; std::unordered_set user_negative_names = {}; extract_base_syscalls_names(s.config->m_base_syscalls_custom_set, user_positive_names, user_negative_names); auto user_positive_sc_set = libsinsp::events::event_names_to_sc_set(user_positive_names); auto user_negative_sc_set = libsinsp::events::event_names_to_sc_set(user_negative_names); auto user_positive_sc_set_names = libsinsp::events::sc_set_to_event_names(user_positive_sc_set); if(!user_positive_sc_set.empty()) { // user overrides base event set base_sc_set = user_positive_sc_set; // we re-transform from sc_set to names to make // sure that bad user inputs are ignored falco_logger::log(falco_logger::level::DEBUG, "+(" + std::to_string(user_positive_sc_set_names.size()) + ") syscalls added (base_syscalls override): " + concat_set_in_order(user_positive_sc_set_names) + "\n"); } auto invalid_positive_sc_set_names = unordered_set_difference(user_positive_names, user_positive_sc_set_names); if(!invalid_positive_sc_set_names.empty()) { falco_logger::log(falco_logger::level::WARNING, "Invalid (positive) syscall names: warning (base_syscalls override): " + concat_set_in_order(invalid_positive_sc_set_names)); } // selected events are the union of the rules events set plus // the parsing capability plugins events set and the // base events set (either the default or the user-defined one) s.selected_sc_set = rules_sc_set.merge(plugin_sc_set).merge(base_sc_set); /* REPLACE DEFAULT STATE, nothing else. Need to override s.selected_sc_set and have a separate * logic block. */ if(s.config->m_base_syscalls_repair && user_positive_sc_set.empty()) { /* If `base_syscalls.repair` is specified, but `base_syscalls.custom_set` is empty we are * replacing the default `sinsp_state_sc_set()` enforcement with the alternative * `sinsp_repair_state_sc_set`. This approach only activates additional syscalls Falco needs * beyond the syscalls defined in each Falco rule that are absolutely necessary based on the * current rules configuration. */ // returned set already has rules_sc_set merged s.selected_sc_set = libsinsp::events::sinsp_repair_state_sc_set(rules_sc_set); } auto user_negative_sc_set_names = libsinsp::events::sc_set_to_event_names(user_negative_sc_set); if(!user_negative_sc_set.empty()) { /* Remove negative base_syscalls events. */ s.selected_sc_set = s.selected_sc_set.diff(user_negative_sc_set); // we re-transform from sc_set to names to make // sure that bad user inputs are ignored falco_logger::log(falco_logger::level::DEBUG, "-(" + std::to_string(user_negative_sc_set_names.size()) + ") syscalls removed (base_syscalls override): " + concat_set_in_order(user_negative_sc_set_names) + "\n"); } auto invalid_negative_sc_set_names = unordered_set_difference(user_negative_names, user_negative_sc_set_names); if(!invalid_negative_sc_set_names.empty()) { falco_logger::log(falco_logger::level::WARNING, "Invalid (negative) syscall names: warning (base_syscalls override): " + concat_set_in_order(invalid_negative_sc_set_names)); } /* Derive the diff between the additional syscalls added via libsinsp state enforcement and the syscalls from each Falco rule. We avoid printing this in case the user specified a custom set of base syscalls */ auto non_rules_sc_set = s.selected_sc_set.diff(rules_sc_set); if(!non_rules_sc_set.empty() && user_positive_sc_set.empty()) { auto non_rules_sc_set_names = libsinsp::events::sc_set_to_event_names(non_rules_sc_set); falco_logger::log(falco_logger::level::DEBUG, "+(" + std::to_string(non_rules_sc_set_names.size()) + ") syscalls (Falco's state engine set of syscalls): " + concat_set_in_order(non_rules_sc_set_names) + "\n"); } /* base_syscall.all behavior: * (1) default: all syscalls in rules included, sinsp state enforcement without high volume syscalls * (2) set: all syscalls in rules included, sinsp state enforcement and allowing high volume syscalls */ if(!s.config->m_base_syscalls_all) { auto ignored_sc_set = falco::app::ignored_sc_set(); auto erased_sc_set = s.selected_sc_set.intersect(ignored_sc_set); s.selected_sc_set = s.selected_sc_set.diff(ignored_sc_set); if(!erased_sc_set.empty()) { auto erased_sc_set_names = libsinsp::events::sc_set_to_event_names(erased_sc_set); falco_logger::log(falco_logger::level::DEBUG, "-(" + std::to_string(erased_sc_set_names.size()) + ") ignored syscalls (-> activate via `-A` flag): " + concat_set_in_order(erased_sc_set_names) + "\n"); } } /* If a custom set is specified (positive, negative, or both), we attempt * to repair it if configured to do so. */ if(s.config->m_base_syscalls_repair && !s.config->m_base_syscalls_custom_set.empty()) { /* If base_syscalls.repair is specified enforce state using `sinsp_repair_state_sc_set`. * This approach is an alternative to the default `sinsp_state_sc_set()` state enforcement * and only activates additional syscalls Falco needs beyond the syscalls defined in the * Falco rules that are absolutely necessary based on the current rules configuration. */ auto selected_sc_set = s.selected_sc_set; s.selected_sc_set = libsinsp::events::sinsp_repair_state_sc_set(s.selected_sc_set); auto repaired_sc_set = s.selected_sc_set.diff(selected_sc_set); if(!repaired_sc_set.empty()) { auto repaired_sc_set_names = libsinsp::events::sc_set_to_event_names(repaired_sc_set); falco_logger::log(falco_logger::level::INFO, "+(" + std::to_string(repaired_sc_set_names.size()) + ") repaired syscalls: " + concat_set_in_order(repaired_sc_set_names) + "\n"); } } /* Hidden safety enforcement for `base_syscalls.custom_set` user * input override option (but keep as general safety enforcement) * -> sched_process_exit trace point activation (procexit event) * is necessary for continuous state engine cleanup, * else memory would grow rapidly and linearly over time. */ s.selected_sc_set.insert(ppm_sc_code::PPM_SC_SCHED_PROCESS_EXIT); if(!s.selected_sc_set.empty()) { auto selected_sc_set_names = libsinsp::events::sc_set_to_event_names(s.selected_sc_set); falco_logger::log(falco_logger::level::DEBUG, "(" + std::to_string(selected_sc_set_names.size()) + ") syscalls selected in total (final set): " + concat_set_in_order(selected_sc_set_names) + "\n"); } } falco::app::run_result falco::app::actions::configure_interesting_sets(falco::app::state& s) { #ifdef __linux__ if(s.engine == nullptr || s.config == nullptr) { return run_result::fatal( "Broken 'configure_interesting_sets' preconditions: engine and config must be " "non-null"); } s.selected_sc_set.clear(); /* note: the set of events is the richest source of truth about * the events generable by an inspector, because they also carry information * about events that are old, unused, internal, and so on. As such, the * strategy is to first craft the actual set of selected events, and * then use it to obtain a set of enabled kernel tracepoints and a set * of syscall codes. Those last two sets will be passed down to the * inspector to instruct the kernel drivers on which kernel event should * be collected at runtime. */ auto rules_sc_set = s.engine->sc_codes_for_ruleset(falco_common::syscall_source); select_event_set(s, rules_sc_set); check_for_rules_unsupported_events(s, rules_sc_set); #endif return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/configure_syscall_buffer_num.cpp ================================================ // 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" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::configure_syscall_buffer_num( const falco::app::state& s) { #ifdef __linux__ if(!s.is_modern_ebpf()) { return run_result::ok(); } ssize_t online_cpus = sysconf(_SC_NPROCESSORS_ONLN); if(online_cpus <= 0) { return run_result::fatal("cannot get the number of online CPUs from the system\n"); } if(s.config->m_modern_ebpf.m_cpus_for_each_buffer > online_cpus) { falco_logger::log(falco_logger::level::WARNING, "you required a buffer every '" + std::to_string(s.config->m_modern_ebpf.m_cpus_for_each_buffer) + "' CPUs but there are only '" + std::to_string(online_cpus) + "' online CPUs. Falco changed the config to: one buffer every '" + std::to_string(online_cpus) + "' CPUs\n"); s.config->m_modern_ebpf.m_cpus_for_each_buffer = online_cpus; } #endif return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/configure_syscall_buffer_size.cpp ================================================ // 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" using namespace falco::app; using namespace falco::app::actions; /* These indexes could change over the Falco releases. */ #define MIN_INDEX 1 #define MAX_INDEX 10 #define DEFAULT_BYTE_SIZE 1 << 23 falco::app::run_result falco::app::actions::configure_syscall_buffer_size(falco::app::state& s) { #ifdef __linux__ auto index = s.driver_buf_size_preset(); if(index == -1) { // Chosen driver kind does not support this option. return run_result::ok(); } if(index < MIN_INDEX || index > MAX_INDEX) { return run_result::fatal("The 'buf_size_preset' value must be between '" + std::to_string(MIN_INDEX) + "' and '" + std::to_string(MAX_INDEX) + "'\n"); } /* Sizes from `1 MB` to `512 MB`. The index `0` is reserved, users cannot use it! */ std::vector vect{0, 1 << 20, 1 << 21, 1 << 22, DEFAULT_BYTE_SIZE, 1 << 24, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29}; uint64_t chosen_size = vect[index]; /* If the page size is not valid we return here. */ long page_size = getpagesize(); if(page_size <= 0) { s.syscall_buffer_bytes_size = DEFAULT_BYTE_SIZE; falco_logger::log(falco_logger::level::WARNING, "Unable to get the system page size through 'getpagesize()'. Try to use " "the default syscall buffer dimension: " + std::to_string(DEFAULT_BYTE_SIZE) + " bytes\n"); return run_result::ok(); } /* Check if the chosen size is a multiple of the page size. */ if(chosen_size % page_size != 0) { return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not a multiple of your system page size '" + std::to_string(page_size) + "'. Please configure a greater 'buf_size_preset' value in the " "Falco configuration file\n"); } /* Check if the chosen size is greater than `2 * page_size`. */ if((chosen_size / page_size) <= 2) { return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not greater than '2 * " + std::to_string(page_size) + "' where '" + std::to_string(page_size) + "' is your system page size. Please configure a greater " "'buf_size_preset' value in the Falco configuration file\n"); } s.syscall_buffer_bytes_size = chosen_size; falco_logger::log(falco_logger::level::INFO, "The chosen syscall buffer dimension is: " + std::to_string(chosen_size) + " bytes (" + std::to_string(chosen_size / (uint64_t)(1024 * 1024)) + " MBs)\n"); #endif // __linux__ return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/create_signal_handlers.cpp ================================================ // 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 #include "actions.h" #include "../app.h" #include "../signals.h" #ifdef __linux__ #include #endif // __linux__ using namespace falco::app; using namespace falco::app::actions; static std::shared_ptr s_restarter; static void terminate_signal_handler(int signal) { falco::app::g_terminate_signal.trigger(); } static void reopen_outputs_signal_handler(int signal) { falco::app::g_reopen_outputs_signal.trigger(); } static void restart_signal_handler(int signal) { if(s_restarter != nullptr) { s_restarter->trigger(); } } bool create_handler(int sig, void (*func)(int), run_result& ret) { ret = run_result::ok(); #ifdef __linux__ if(signal(sig, func) == SIG_ERR) { char errbuf[1024]; if(strerror_r(errno, errbuf, sizeof(errbuf)) != 0) { snprintf(errbuf, sizeof(errbuf) - 1, "Errno %d", errno); } ret = run_result::fatal(std::string("Could not create signal handler for ") + strsignal(sig) + ": " + errbuf); } #endif return ret.success; } falco::app::run_result falco::app::actions::create_signal_handlers(falco::app::state& s) { auto ret = run_result::ok(); #ifdef __linux__ if(s.options.dry_run) { falco_logger::log(falco_logger::level::DEBUG, "Skipping signal handlers creation in dry-run\n"); return run_result::ok(); } falco::app::g_terminate_signal.reset(); falco::app::g_restart_signal.reset(); falco::app::g_reopen_outputs_signal.reset(); if(!g_terminate_signal.is_lock_free() || !g_restart_signal.is_lock_free() || !g_reopen_outputs_signal.is_lock_free()) { falco_logger::log(falco_logger::level::WARNING, "Bundled atomics implementation is not lock-free, signal handlers may be " "unstable\n"); } if(!create_handler(SIGINT, ::terminate_signal_handler, ret) || !create_handler(SIGTERM, ::terminate_signal_handler, ret) || !create_handler(SIGUSR1, ::reopen_outputs_signal_handler, ret) || !create_handler(SIGHUP, ::restart_signal_handler, ret)) { return ret; } falco::app::restart_handler::watch_list_t files_to_watch; falco::app::restart_handler::watch_list_t dirs_to_watch; if(s.config->m_watch_config_files) { files_to_watch.insert(files_to_watch.end(), s.config->m_loaded_configs_filenames.begin(), s.config->m_loaded_configs_filenames.end()); dirs_to_watch.insert(dirs_to_watch.end(), s.config->m_loaded_configs_folders.begin(), s.config->m_loaded_configs_folders.end()); files_to_watch.insert(files_to_watch.end(), s.config->m_loaded_rules_filenames.begin(), s.config->m_loaded_rules_filenames.end()); dirs_to_watch.insert(dirs_to_watch.end(), s.config->m_loaded_rules_folders.begin(), s.config->m_loaded_rules_folders.end()); } s.restarter = std::make_shared( [&s] { bool tmp = false; bool success = false; std::string err; falco::app::state tmp_state(s.cmdline, s.options); tmp_state.options.dry_run = true; try { success = falco::app::run(tmp_state, tmp, err); } catch(std::exception& e) { err = e.what(); } catch(...) { err = "unknown error"; } if(!success && s.outputs != nullptr) { std::string rule = "Falco internal: hot restart failure"; std::string msg = rule + ": " + err; auto fields = nlohmann::json::object(); auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); s.outputs->handle_msg(now, falco_common::PRIORITY_CRITICAL, msg, rule, fields); } return success; }, files_to_watch, dirs_to_watch); ret = run_result::ok(); ret.success = s.restarter->start(ret.errstr); ret.proceed = ret.success; if(ret.success) { s_restarter = s.restarter; } #endif return ret; } falco::app::run_result falco::app::actions::unregister_signal_handlers(falco::app::state& s) { #ifdef __linux__ if(s.options.dry_run) { falco_logger::log(falco_logger::level::DEBUG, "Skipping unregistering signal handlers in dry-run\n"); return run_result::ok(); } s_restarter = nullptr; if(s.restarter != nullptr) { s.restarter->stop(); } run_result ret; if(!create_handler(SIGINT, SIG_DFL, ret) || !create_handler(SIGTERM, SIG_DFL, ret) || !create_handler(SIGUSR1, SIG_DFL, ret) || !create_handler(SIGHUP, SIG_DFL, ret)) { return ret; } #endif // __linux__ return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/event_formatter.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2026 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 "event_formatter.h" #include #include using namespace falco::app::actions; static bool is_flag_type(ppm_param_type type) { return (type == PT_FLAGS8 || type == PT_FLAGS16 || type == PT_FLAGS32 || type == PT_ENUMFLAGS8 || type == PT_ENUMFLAGS16 || type == PT_ENUMFLAGS32); } // Factory method std::unique_ptr EventFormatter::create(output_format format) { switch(format) { case output_format::JSON: return std::make_unique(); case output_format::MARKDOWN: return std::make_unique(); case output_format::TEXT: default: return std::make_unique(); } } // ============================================================================ // TextFormatter implementation // ============================================================================ void TextFormatter::begin(const std::string& schema_version) { printf("The events below are valid for Falco *Schema Version*: %s\n", schema_version.c_str()); } void TextFormatter::begin_category(const std::string& category) { printf("## %s\n\n", category.c_str()); } void TextFormatter::print_event(const event_entry& e) { char dir = e.is_enter ? '>' : '<'; printf("%c %s(", dir, e.name.c_str()); for(uint32_t k = 0; k < e.info->nparams; k++) { if(k != 0) { printf(", "); } print_param(&e.info->params[k]); } printf(")\n"); } void TextFormatter::end_category() { printf("\n"); } void TextFormatter::end() { // Nothing to do for text format } void TextFormatter::print_param(const struct ppm_param_info* param) { printf("%s **%s**", param_type_to_string(param->type), param->name); if(is_flag_type(param->type) && param->info) { auto flag_info = static_cast(param->info); printf(": "); for(size_t i = 0; flag_info[i].name != NULL; i++) { if(i != 0) { printf(", "); } printf("%s", flag_info[i].name); } } } // ============================================================================ // MarkdownFormatter implementation // ============================================================================ void MarkdownFormatter::begin(const std::string& schema_version) { printf("The events below are valid for Falco *Schema Version*: %s\n", schema_version.c_str()); } void MarkdownFormatter::begin_category(const std::string& category) { printf("## %s\n\n", category.c_str()); printf("Default | Dir | Name | Params \n"); printf(":-------|:----|:-----|:-----\n"); } void MarkdownFormatter::print_event(const event_entry& e) { char dir = e.is_enter ? '>' : '<'; printf(e.available ? "Yes" : "No"); printf(" | `%c` | `%s` | ", dir, e.name.c_str()); for(uint32_t k = 0; k < e.info->nparams; k++) { if(k != 0) { printf(", "); } print_param(&e.info->params[k]); } printf("\n"); } void MarkdownFormatter::end_category() { printf("\n"); } void MarkdownFormatter::end() { // Nothing to do for markdown format } void MarkdownFormatter::print_param(const struct ppm_param_info* param) { printf("%s **%s**", param_type_to_string(param->type), param->name); if(is_flag_type(param->type) && param->info) { auto flag_info = static_cast(param->info); printf(": "); for(size_t i = 0; flag_info[i].name != NULL; i++) { if(i != 0) { printf(", "); } printf("*%s*", flag_info[i].name); } } } // ============================================================================ // JsonFormatter implementation // ============================================================================ void JsonFormatter::begin(const std::string& schema_version) { m_root = nlohmann::json::object(); m_root["schema_version"] = schema_version; } void JsonFormatter::begin_category(const std::string& category) { m_current_category = nlohmann::json::array(); m_current_category_name = category; } void JsonFormatter::print_event(const event_entry& e) { m_current_category.push_back(event_to_json(e)); } void JsonFormatter::end_category() { m_root[m_current_category_name] = m_current_category; } void JsonFormatter::end() { printf("%s\n", m_root.dump(2).c_str()); } nlohmann::json JsonFormatter::event_to_json(const event_entry& e) { nlohmann::json event; event["name"] = e.name; event["dir"] = e.is_enter ? ">" : "<"; event["available"] = e.available; nlohmann::json params = nlohmann::json::array(); for(uint32_t k = 0; k < e.info->nparams; k++) { nlohmann::json param; param["type"] = param_type_to_string(e.info->params[k].type); param["name"] = e.info->params[k].name; if(is_flag_type(e.info->params[k].type) && e.info->params[k].info) { auto flag_info = static_cast(e.info->params[k].info); nlohmann::json flags = nlohmann::json::array(); for(size_t i = 0; flag_info[i].name != NULL; i++) { flags.push_back(flag_info[i].name); } param["flags"] = flags; } params.push_back(param); } event["params"] = params; return event; } ================================================ FILE: userspace/falco/app/actions/event_formatter.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2026 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #include #include #include #include #include #include "../../../engine/output_format.h" struct ppm_param_info; struct ppm_event_info; namespace falco { namespace app { namespace actions { struct event_entry { bool is_enter; bool available; std::string name; const ppm_event_info* info; }; // Abstract formatter interface class EventFormatter { public: virtual ~EventFormatter() = default; // Initialize formatter with schema version virtual void begin(const std::string& schema_version) = 0; // Print category header virtual void begin_category(const std::string& category) = 0; // Print a single event virtual void print_event(const event_entry& e) = 0; // End category virtual void end_category() = 0; // Finalize and output virtual void end() = 0; // Factory method static std::unique_ptr create(output_format format); }; // Text formatter (default) class TextFormatter : public EventFormatter { public: void begin(const std::string& schema_version) override; void begin_category(const std::string& category) override; void print_event(const event_entry& e) override; void end_category() override; void end() override; private: void print_param(const struct ppm_param_info* param); }; // Markdown formatter class MarkdownFormatter : public EventFormatter { public: void begin(const std::string& schema_version) override; void begin_category(const std::string& category) override; void print_event(const event_entry& e) override; void end_category() override; void end() override; private: void print_param(const struct ppm_param_info* param); }; // JSON formatter class JsonFormatter : public EventFormatter { public: void begin(const std::string& schema_version) override; void begin_category(const std::string& category) override; void print_event(const event_entry& e) override; void end_category() override; void end() override; private: nlohmann::json m_root; nlohmann::json m_current_category; std::string m_current_category_name; nlohmann::json event_to_json(const event_entry& e); }; } // namespace actions } // namespace app } // namespace falco ================================================ FILE: userspace/falco/app/actions/helpers.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #include "../state.h" #include "../run_result.h" #include #include namespace falco { namespace app { namespace actions { bool check_rules_plugin_requirements(falco::app::state& s, std::string& err); void print_enabled_event_sources(falco::app::state& s); void activate_interesting_kernel_tracepoints(falco::app::state& s, std::unique_ptr& inspector); void check_for_ignored_events(falco::app::state& s); void format_plugin_info(std::shared_ptr p, std::ostream& os); void format_described_rules_as_text(const nlohmann::json& v, std::ostream& os); inline std::string generate_scap_file_path(const std::string& prefix, uint64_t timestamp, uint64_t evt_num) { // File path in format: __.scap // Example: "/tmp/falco_00000001234567890_00000000000000042.scap" // Add underscore separator between prefix and timestamp std::string path = prefix + "_"; // Zero-pad timestamp to 20 digits for proper lexicographic sorting // Build digits from right to left in a buffer, then append to path char digits[21]; // 20 digits + null terminator digits[20] = '\0'; uint64_t t = timestamp; for(int i = 19; i >= 0; --i) { digits[i] = '0' + (t % 10); t /= 10; } path += digits; // Add underscore separator between timestamp and evt_num path += "_"; // Zero-pad evt_num to 20 digits for proper lexicographic sorting // Build digits from right to left in a buffer, then append to path t = evt_num; for(int i = 19; i >= 0; --i) { digits[i] = '0' + (t % 10); t /= 10; } path += digits; // Add file extension path += ".scap"; return path; } falco::app::run_result open_offline_inspector(falco::app::state& s); falco::app::run_result open_live_inspector(falco::app::state& s, std::shared_ptr inspector, const std::string& source); template void read_files(InputIterator begin, InputIterator end, std::vector& rules_contents, falco::load_result::rules_contents_t& rc) { // Read the contents in a first pass for(auto it = begin; it != end; it++) { const std::string& filename = *it; std::ifstream is; is.open(filename); if(!is.is_open()) { throw falco_exception("Could not open file " + filename + " for reading"); } std::string rules_content((std::istreambuf_iterator(is)), std::istreambuf_iterator()); rules_contents.emplace_back(std::move(rules_content)); } // Populate the map in a second pass to avoid // references becoming invalid. auto it = begin; auto rit = rules_contents.begin(); for(; it != end && rit != rules_contents.end(); it++, rit++) { rc.emplace(*it, *rit); } // Both it and rit must be at the end, otherwise // there's a bug in the above if(it != end || rit != rules_contents.end()) { throw falco_exception("Unexpected mismatch in rules content name/rules content sets?"); } } }; // namespace actions }; // namespace app }; // namespace falco ================================================ FILE: userspace/falco/app/actions/helpers_generic.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "helpers.h" #include "falco_utils.h" #include #include #include using namespace falco::app; using namespace falco::app::actions; bool falco::app::actions::check_rules_plugin_requirements(falco::app::state& s, std::string& err) { // Ensure that all plugins are compatible with the loaded set of rules // note: offline inspector contains all the loaded plugins std::vector plugin_reqs; for(const auto& plugin : s.offline_inspector->get_plugin_manager()->plugins()) { falco_engine::plugin_version_requirement req; req.name = plugin->name(); req.version = plugin->plugin_version().as_string(); plugin_reqs.push_back(req); } return s.engine->check_plugin_requirements(plugin_reqs, err); } void falco::app::actions::print_enabled_event_sources(falco::app::state& s) { /* Print all loaded sources. */ std::string str; for(const auto& src : s.loaded_sources) { str += str.empty() ? "" : ", "; str += src; } falco_logger::log(falco_logger::level::INFO, "Loaded event sources: " + str); /* Print all enabled sources. */ str.clear(); for(const auto& src : s.enabled_sources) { str += str.empty() ? "" : ", "; str += src; } falco_logger::log(falco_logger::level::INFO, "Enabled event sources: " + str); // print some warnings to the user for(const auto& src : s.enabled_sources) { std::shared_ptr first_plugin = nullptr; const auto& plugins = s.offline_inspector->get_plugin_manager()->plugins(); for(const auto& p : plugins) { if((p->caps() & CAP_SOURCING) && ((p->id() != 0 && src == p->event_source()) || (p->id() == 0 && src == falco_common::syscall_source))) { if(first_plugin == nullptr) { first_plugin = p; } else { if(src != falco_common::syscall_source || s.is_nodriver()) { falco_logger::log(falco_logger::level::WARNING, "Enabled event source '" + src + "' can be opened with multiple loaded plugins, " "will use only '" + first_plugin->name() + "'"); } } } } if(!first_plugin && s.is_nodriver()) { falco_logger::log(falco_logger::level::WARNING, "Enabled event source '" + src + "' will be opened with no driver, no event will be produced"); } } } void falco::app::actions::format_plugin_info(std::shared_ptr p, std::ostream& os) { os << "Name: " << p->name() << std::endl; os << "Description: " << p->description() << std::endl; os << "Contact: " << p->contact() << std::endl; os << "Version: " << p->plugin_version().as_string() << std::endl; os << "Capabilities: " << std::endl; if(p->caps() & CAP_SOURCING) { os << " - Event Sourcing"; if(p->id() != 0) { os << " (ID=" << p->id(); os << ", source='" << p->event_source() << "')"; } else { os << " (system events)"; } os << std::endl; } if(p->caps() & CAP_EXTRACTION) { os << " - Field Extraction" << std::endl; } if(p->caps() & CAP_PARSING) { os << " - Event Parsing" << std::endl; } if(p->caps() & CAP_ASYNC) { 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); } } ================================================ FILE: userspace/falco/app/actions/helpers_inspector.cpp ================================================ // 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 #include #include #include #include #include "helpers.h" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::open_offline_inspector(falco::app::state& s) { try { s.offline_inspector->open_savefile(s.config->m_replay.m_capture_file); falco_logger::log(falco_logger::level::INFO, "Replaying events from the capture file: " + s.config->m_replay.m_capture_file + "\n"); return run_result::ok(); } catch(sinsp_exception& e) { return run_result::fatal("Could not open trace filename " + s.config->m_replay.m_capture_file + " for reading: " + e.what()); } } falco::app::run_result falco::app::actions::open_live_inspector(falco::app::state& s, std::shared_ptr inspector, const std::string& source) { try { if(s.config->m_falco_libs_thread_table_size > 0) { // Default value is set in libs as part of the sinsp_thread_manager setup inspector->m_thread_manager->set_max_thread_table_size( s.config->m_falco_libs_thread_table_size); } inspector->set_auto_threads_purging(true); inspector->set_auto_threads_purging_interval_s( s.config->m_falco_libs_thread_table_auto_purging_interval_s); inspector->set_thread_timeout_s( s.config->m_falco_libs_thread_table_auto_purging_thread_timeout_s); if(source != falco_common::syscall_source) /* Plugin engine */ { for(const auto& p : inspector->get_plugin_manager()->plugins()) { // note: if more than one loaded plugin supports the given // event source, only the first one will be opened, following // the loading order specified in the Falco config. if(p->caps() & CAP_SOURCING && p->id() != 0 && p->event_source() == source) { auto cfg = s.plugin_configs.at(p->name()); falco_logger::log( falco_logger::level::INFO, "Opening '" + source + "' source with plugin '" + cfg->m_name + "'"); inspector->open_plugin(cfg->m_name, cfg->m_open_params, s.config->m_plugins_hostinfo ? sinsp_plugin_platform::SINSP_PLATFORM_HOSTINFO : sinsp_plugin_platform::SINSP_PLATFORM_GENERIC); return run_result::ok(); } } return run_result::fatal("Can't find plugin for event source: " + source); } else if(s.is_nodriver()) /* nodriver engine. */ { // when opening a capture with no driver, Falco will first check // if a plugin is capable of generating raw events from the libscap // event table (including system events), and if none is found it // will use the nodriver engine. for(const auto& p : inspector->get_plugin_manager()->plugins()) { if(p->caps() & CAP_SOURCING && p->id() == 0) { auto cfg = s.plugin_configs.at(p->name()); falco_logger::log( falco_logger::level::INFO, "Opening '" + source + "' source with plugin '" + cfg->m_name + "'"); inspector->open_plugin(cfg->m_name, cfg->m_open_params, sinsp_plugin_platform::SINSP_PLATFORM_FULL); return run_result::ok(); } } falco_logger::log(falco_logger::level::INFO, "Opening '" + source + "' source with no driver\n"); inspector->open_nodriver(); } else if(s.is_modern_ebpf()) /* modern BPF engine. */ { falco_logger::log(falco_logger::level::INFO, "Opening '" + source + "' source with modern BPF probe."); falco_logger::log( falco_logger::level::INFO, "One ring buffer every '" + std::to_string(s.config->m_modern_ebpf.m_cpus_for_each_buffer) + "' CPUs."); inspector->open_modern_bpf(s.syscall_buffer_bytes_size, s.config->m_modern_ebpf.m_cpus_for_each_buffer, true, s.selected_sc_set); } else /* Kernel module (default). */ { try { falco_logger::log(falco_logger::level::INFO, "Opening '" + source + "' source with Kernel module"); inspector->open_kmod(s.syscall_buffer_bytes_size, s.selected_sc_set); } catch(sinsp_exception& e) { // Try to insert the Falco kernel module falco_logger::log( falco_logger::level::INFO, "Trying to inject the Kernel module and opening the capture again..."); if(system("modprobe " DRIVER_NAME " > /dev/null 2> /dev/null")) { falco_logger::log(falco_logger::level::ERR, "Unable to load the driver\n"); } inspector->open_kmod(s.syscall_buffer_bytes_size, s.selected_sc_set); } } } catch(sinsp_exception& e) { return run_result::fatal(e.what()); } return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/init_falco_engine.cpp ================================================ // 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 #include #include using namespace falco::app; using namespace falco::app::actions; static inline std::string format_suggested_field(const filtercheck_field_info* info) { std::ostringstream out; // Replace "foo.bar" with "foo_bar" auto name = info->m_name; std::replace(name.begin(), name.end(), '.', '_'); // foo_bar=%foo.bar out << name << "=%" << info->m_name; return out.str(); } static void add_suggested_output(const falco::app::state& s, const std::string& src, const falco_configuration::append_output_config& eo) { auto src_info = s.source_infos.at(src); if(!src_info) { return; } auto& filterchecks = *src_info->filterchecks; std::vector fields; filterchecks.get_all_fields(fields); for(const auto& fld : fields) { for(int i = 0; i < fld->m_nfields; i++) { const auto* fldinfo = &fld->m_fields[i]; if(fldinfo->is_format_suggested()) { s.engine->add_extra_output_format(format_suggested_field(fldinfo), src, eo.m_tags, eo.m_rule); } } } } void configure_output_format(falco::app::state& s) { for(auto& eo : s.config->m_append_output) { if(eo.m_format != "") { s.engine->add_extra_output_format(eo.m_format, eo.m_source, eo.m_tags, eo.m_rule); } // Add suggested filtercheck formats to each source output if(eo.m_suggested_output) { if(eo.m_source.empty()) { for(auto& src : s.loaded_sources) { add_suggested_output(s, src, eo); } } else { add_suggested_output(s, eo.m_source, eo); } } for(auto const& ff : eo.m_formatted_fields) { s.engine->add_extra_output_formatted_field(ff.first, ff.second, eo.m_source, eo.m_tags, eo.m_rule); } for(auto const& rf : eo.m_raw_fields) { s.engine->add_extra_output_raw_field(rf, eo.m_source, eo.m_tags, eo.m_rule); } } if(!s.options.print_additional.empty()) { falco_logger::log(falco_logger::level::WARNING, "The -p/--print option is deprecated and will be removed. Use -o " "append_output=... instead.\n"); if(s.options.print_additional == "c" || s.options.print_additional == "container" || s.options.print_additional == "k" || s.options.print_additional == "kubernetes") { // Don't do anything, we don't need these anymore // since container plugin takes care of suggesting the output format fields itself. } else { s.engine->add_extra_output_format(s.options.print_additional, "", {}, ""); } } } void add_source_to_engine(falco::app::state& s, const std::string& src) { auto src_info = s.source_infos.at(src); auto& filterchecks = *src_info->filterchecks; auto* inspector = src_info->inspector.get(); auto filter_factory = std::make_shared(inspector, filterchecks); auto formatter_factory = std::make_shared(inspector, filterchecks); if(s.config->m_json_output) { formatter_factory->set_output_format(sinsp_evt_formatter::OF_JSON); } src_info->engine_idx = s.engine->add_source(src, filter_factory, formatter_factory); } falco::app::run_result falco::app::actions::init_falco_engine(falco::app::state& s) { // add syscall as first source, this is also what each inspector do // in their own list of registered event sources add_source_to_engine(s, falco_common::syscall_source); // add all non-syscall event sources in engine for(const auto& src : s.loaded_sources) { // we skip the syscall source because we already added it if(src != falco_common::syscall_source) { add_source_to_engine(s, src); } } // note: in capture mode, we can assume that the plugin source index will // be the same in both the falco engine and the sinsp plugin manager. // This assumption stands because the plugin manager stores sources in a // vector, and the syscall source is appended in the engine *after* the sources // coming from plugins. The reason why this can't work with live mode, // is because in that case event sources are scattered across different // inspectors. Since this is an implementation-based assumption, we // check this and return an error to spot regressions in the future. if(s.is_capture_mode()) { auto manager = s.offline_inspector->get_plugin_manager(); for(const auto& p : manager->plugins()) { if((p->caps() & CAP_SOURCING) == 0 || p->id() == 0) { continue; } bool added = false; auto source_idx = manager->source_idx_by_plugin_id(p->id(), added); auto engine_idx = s.source_infos.at(p->event_source())->engine_idx; if(!added || source_idx != engine_idx) { return run_result::fatal("Could not add event source in the engine: " + p->event_source()); } } } configure_output_format(s); s.engine->set_min_priority(s.config->m_min_priority); return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/init_inspectors.cpp ================================================ // 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 #include #include using namespace falco::app; using namespace falco::app::actions; static void init_syscall_inspector(falco::app::state& s, std::shared_ptr inspector) { sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL; if(s.config->m_buffer_format_base64) { event_buffer_format = sinsp_evt::PF_BASE64; } inspector->set_buffer_format(event_buffer_format); // // If required, set the snaplen. // if(s.config->m_falco_libs_snaplen != 0) { inspector->set_snaplen(s.config->m_falco_libs_snaplen); } if(s.is_driver_drop_failed_exit_enabled()) { falco_logger::log(falco_logger::level::INFO, "Failed syscall exit events are dropped in the kernel driver\n"); inspector->set_dropfailed(true); } inspector->set_hostname_and_port_resolution_mode(false); } static bool populate_filterchecks(const std::shared_ptr& inspector, const std::string& source, filter_check_list& filterchecks, std::unordered_set& used_plugins, std::map static_fields, std::string& err) { // Add static filterchecks loaded from config if(!static_fields.empty()) { filterchecks.add_filter_check(std::make_unique(static_fields)); } // Add plugin-defined filterchecks, checking that they do not overlap any internal filtercheck std::vector infos; for(const auto& plugin : inspector->get_plugin_manager()->plugins()) { if(!(plugin->caps() & CAP_EXTRACTION)) { continue; } // check if some fields are overlapping on this event sources infos.clear(); filterchecks.get_all_fields(infos); for(const auto& info : infos) { for(int32_t i = 0; i < info->m_nfields; i++) { // check if one of the fields extractable by the plugin // is already provided by another filtercheck for this source std::string fname = info->m_fields[i].m_name; for(const auto& field : plugin->fields()) { if(field.m_name == fname) { err = "Plugin '" + plugin->name() + "' supports extraction of field '" + fname + "' that is overlapping for source '" + source + "'"; return false; } } } } // add plugin filterchecks to the event source filterchecks.add_filter_check(sinsp_plugin::new_filtercheck(plugin)); used_plugins.insert(plugin->name()); } return true; } falco::app::run_result falco::app::actions::init_inspectors(falco::app::state& s) { std::string err; std::unordered_set used_plugins; const auto& all_plugins = s.offline_inspector->get_plugin_manager()->plugins(); const bool is_capture_mode = s.is_capture_mode(); for(const auto& src : s.loaded_sources) { auto src_info = s.source_infos.at(src); // in capture mode, every event source uses the offline inspector. // in live mode, we create a new inspector for each event source if(is_capture_mode) { src_info->inspector = s.offline_inspector; } else { src_info->inspector = std::make_shared(s.config->m_metrics_flags & METRICS_V2_STATE_COUNTERS); } // do extra preparation for the syscall source if(src == falco_common::syscall_source) { init_syscall_inspector(s, src_info->inspector); } // load and init all plugins compatible with this event source // (if in capture mode, all plugins will be inited on the same inspector) for(const auto& p : all_plugins) { std::shared_ptr plugin = nullptr; auto config = s.plugin_configs.at(p->name()); auto is_input = (p->caps() & CAP_SOURCING) && ((p->id() != 0 && src == p->event_source()) || (p->id() == 0 && src == falco_common::syscall_source)); if(is_capture_mode) { // in capture mode, every plugin is already registered // in the offline inspector by the load_plugins action plugin = p; } else { // in live mode, for the inspector assigned to the given event source, we must // register a plugin if one of the following condition applies to it: // - it has event sourcing capability for the given event source // - it has one among field extraction, event parsing and async events capabilities // and is compatible (with respect to that capability) with the given event source if(is_input || (p->caps() & CAP_EXTRACTION && sinsp_plugin::is_source_compatible(p->extract_event_sources(), src)) || (p->caps() & CAP_PARSING && sinsp_plugin::is_source_compatible(p->parse_event_sources(), src)) || (p->caps() & CAP_ASYNC && sinsp_plugin::is_source_compatible(p->async_event_sources(), src))) { plugin = src_info->inspector->register_plugin(config->m_library_path); } } if(!plugin) { continue; } // init the plugin only if we registered it into an inspector (in capture mode, this is // true for every plugin). Avoid initializing the same plugin twice in the same // inspector if we're in capture mode if(!is_capture_mode || used_plugins.find(p->name()) == used_plugins.end()) { if(!plugin->init(config->m_init_config, err)) { return run_result::fatal(err); } } if(is_input) { auto gen_check = src_info->inspector->new_generic_filtercheck(); src_info->filterchecks->add_filter_check(std::move(gen_check)); } used_plugins.insert(plugin->name()); } // populate filtercheck list for this inspector if(!populate_filterchecks(src_info->inspector, src, *src_info->filterchecks, used_plugins, s.config->m_static_fields, err)) { return run_result::fatal(err); } if(is_capture_mode) { continue; } // in live mode, each inspector should have registered at most two event sources: the // "syscall" on, loaded at default at index 0, and optionally another one defined by a // plugin, at index 1 const auto& sources = src_info->inspector->event_sources(); if(sources.size() == 0 || sources.size() > 2 || sources[0] != falco_common::syscall_source) { err.clear(); for(const auto& source : sources) { err += (err.empty() ? "" : ", ") + source; } return run_result::fatal("Illegal sources setup in live inspector for source '" + src + "': " + err); } } // check if some plugin remains unused for(const auto& p : all_plugins) { if(used_plugins.find(p->name()) == used_plugins.end()) { return run_result::fatal( "Plugin '" + p->name() + "' is loaded but unused as not compatible with any known event source"); } } return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/init_outputs.cpp ================================================ // 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 #ifdef _WIN32 #include #else #include #endif #include "actions.h" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::init_outputs(falco::app::state& s) { if(s.config->m_outputs.empty()) { return run_result::fatal( "No output configured, please make sure at least one output is configured and " "enabled."); } // read hostname std::string hostname; char* env_hostname = getenv("FALCO_HOSTNAME"); if(env_hostname) { hostname = env_hostname; falco_logger::log(falco_logger::level::INFO, "Hostname value has been overridden via environment variable to: " + hostname + "\n"); } else { char c_hostname[256]; int err = gethostname(c_hostname, 256); if(err != 0) { return run_result::fatal("Failed to get hostname"); } hostname = c_hostname; } if(s.options.dry_run) { falco_logger::log(falco_logger::level::DEBUG, "Skipping outputs initialization in dry-run\n"); return run_result::ok(); } s.outputs = std::make_shared(s.engine, s.config->m_outputs, s.config->m_json_output, s.config->m_json_include_output_property, s.config->m_json_include_tags_property, s.config->m_json_include_message_property, s.config->m_json_include_output_fields_property, s.config->m_output_timeout, s.config->m_buffered_outputs, s.config->m_outputs_queue_capacity, s.config->m_time_format_iso_8601, hostname); return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/list_fields.cpp ================================================ // 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" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::list_fields(falco::app::state& s) { if(!s.options.list_fields) { return run_result::ok(); } if(s.options.list_source_fields != "" && !s.engine->is_source_valid(s.options.list_source_fields)) { return run_result::fatal("Value for --list must be a valid source type"); } s.engine->list_fields(s.options.list_source_fields, s.options.verbose, s.options.names_only, s.options.output_fmt); return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/list_plugins.cpp ================================================ // 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 using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::list_plugins(const falco::app::state& s) { if(!s.options.list_plugins) { return run_result::ok(); } std::ostringstream os; sinsp inspector; const auto& configs = s.config->m_plugins; for(auto& c : configs) { // load the plugin (no need to initialize it) auto plugin = inspector.register_plugin(c.m_library_path); format_plugin_info(plugin, os); os << std::endl; } printf("%zu Plugins Loaded:\n\n%s\n", configs.size(), os.str().c_str()); return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/load_config.cpp ================================================ // 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 "falco_utils.h" using namespace falco::app; using namespace falco::app::actions; // applies legacy/in-deprecation options to the current state static falco::app::run_result apply_deprecated_options(const falco::app::state& s) { return run_result::ok(); } falco::app::run_result falco::app::actions::load_config(const falco::app::state& s) { // List of loaded conf files, ie: s.options.conf_filename // plus all the `config_files` expanded list of configs. config_loaded_res res; try { if(!s.options.conf_filename.empty()) { res = s.config->init_from_file(s.options.conf_filename, s.options.cmdline_config_options); } else { // Is possible to have an empty config file when we want to use some command line // options like `--help`, `--version`, ... // The configs used in `load_yaml` will be initialized to the default values. res = s.config->init_from_content("", s.options.cmdline_config_options); } } catch(std::exception& e) { return run_result::fatal(e.what()); } // log after config init because config determines where logs go falco_logger::set_time_format_iso_8601(s.config->m_time_format_iso_8601); falco_logger::log(falco_logger::level::INFO, "Falco version: " + std::string(FALCO_VERSION) + " (" + std::string(FALCO_TARGET_ARCH) + ")\n"); if(!s.cmdline.empty()) { falco_logger::log(falco_logger::level::DEBUG, "CLI args: " + s.cmdline); } if(!s.options.conf_filename.empty()) { falco_logger::log(falco_logger::level::INFO, "Falco initialized with configuration files:\n"); for(const auto& pair : res) { auto config_path = pair.first; auto validation = pair.second; auto priority = validation == yaml_helper::validation_ok ? falco_logger::level::INFO : falco_logger::level::WARNING; falco_logger::log(priority, std::string(" ") + config_path + " | schema validation: " + validation + "\n"); } } s.config->m_falco_reload_ts = (int64_t)std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); s.config->m_buffered_outputs = !s.options.unbuffered_outputs; return apply_deprecated_options(s); } falco::app::run_result falco::app::actions::require_config_file(const falco::app::state& s) { #ifndef __EMSCRIPTEN__ if(s.options.conf_filename.empty()) { #ifdef BUILD_TYPE_DEBUG return run_result::fatal(std::string("You must create a config file at ") + FALCO_SOURCE_CONF_FILE + ", " + FALCO_INSTALL_CONF_FILE + " or by passing -c"); #else return run_result::fatal(std::string("You must create a config file at ") + FALCO_INSTALL_CONF_FILE + " or by passing -c"); #endif } #endif // __EMSCRIPTEN__ return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/load_plugins.cpp ================================================ // 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 using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::load_plugins(falco::app::state& s) { #if defined(MUSL_OPTIMIZED) or defined(__EMSCRIPTEN__) if(!s.config->m_plugins.empty()) { return run_result::fatal( "Loading plugins dynamic libraries is not supported by this Falco build"); } #endif // Initialize the set of loaded event sources. // By default, the set includes the 'syscall' event source state::source_info syscall_src_info; syscall_src_info.filterchecks = std::make_shared(); s.source_infos.clear(); s.source_infos.insert(syscall_src_info, falco_common::syscall_source); s.loaded_sources = {falco_common::syscall_source}; // Initialize map of plugin configs s.plugin_configs.clear(); // Initialize the offline inspector. This is used to load all the configured // plugins in order to have them available every time we need to access // their static info. If Falco is in capture mode, this inspector is also // used to open and read the trace file s.offline_inspector = std::make_shared(); // Load all the configured plugins for(auto& p : s.config->m_plugins) { auto plugin = s.offline_inspector->register_plugin(p.m_library_path); s.plugin_configs.insert(p, plugin->name()); falco_logger::log(falco_logger::level::INFO, "Loaded plugin '" + p.m_name + "@" + plugin->plugin_version().as_string() + "' from file " + p.m_library_path + "\n"); if((plugin->caps() & CAP_SOURCING) == 0 || plugin->id() == 0) { continue; } // Account the plugin event source state::source_info src_info; src_info.filterchecks = std::make_shared(); auto src_name = plugin->event_source(); s.source_infos.insert(src_info, src_name); // note: this avoids duplicate values if(std::find(s.loaded_sources.begin(), s.loaded_sources.end(), src_name) == s.loaded_sources.end()) { s.loaded_sources.push_back(src_name); } } return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/load_rules_files.cpp ================================================ // 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 "falco_utils.h" #include #include using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::load_rules_files(falco::app::state& s) { if(!s.options.rules_filenames.empty()) { s.config->m_rules_filenames = s.options.rules_filenames; } if(s.config->m_rules_filenames.empty()) { return run_result::fatal( "You must specify at least one rules file/directory via -r or a rules_file entry " "in falco.yaml"); } falco_logger::log(falco_logger::level::DEBUG, "Configured rules filenames:\n"); for(const auto& path : s.config->m_rules_filenames) { falco_logger::log(falco_logger::level::DEBUG, std::string(" ") + path + "\n"); } for(const auto& path : s.config->m_rules_filenames) { falco_configuration::read_rules_file_directory(path, s.config->m_loaded_rules_filenames, s.config->m_loaded_rules_folders); } std::vector rules_contents; falco::load_result::rules_contents_t rc; try { read_files(s.config->m_loaded_rules_filenames.begin(), s.config->m_loaded_rules_filenames.end(), rules_contents, rc); } catch(falco_exception& e) { return run_result::fatal(e.what()); } std::string err = ""; falco_logger::log(falco_logger::level::INFO, "Loading rules from:\n"); for(auto& filename : s.config->m_loaded_rules_filenames) { std::unique_ptr res; res = s.engine->load_rules(rc.at(filename), filename); auto priority = res->schema_validation() == yaml_helper::validation_ok ? falco_logger::level::INFO : falco_logger::level::WARNING; falco_logger::log(priority, std::string(" ") + filename + " | schema validation: " + res->schema_validation() + "\n"); if(!res->successful()) { // Return the summary version as the error err = res->as_string(true, rc); break; } if(res->has_warnings()) { falco_logger::log(falco_logger::level::WARNING, res->as_string(true, rc) + "\n"); } #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) s.config->m_loaded_rules_filenames_sha256sum.insert( {filename, falco::utils::calculate_file_sha256sum(filename)}); #endif } // note: we have an egg-and-chicken problem here. We would like to check // plugin requirements before loading any rule, so that we avoid having // all the "unknown field XXX" errors caused when a plugin is required but // not loaded. On the other hand, we can't check the requirements before // loading the rules file, because that's where the plugin dependencies // are specified. This issue is visible only for dependencies over extractor // plugins, due to the fact that if a source plugin is not loaded, its // source will be unknown for the engine and so it will skip loading all of // the rules to that source, to finally end up here and return a fatal error // due to plugin dependency not satisfied being the actual problem. // // The long-term solution would be to pass information about all the loaded // plugins to the falco engine before or when loading a rules file, so that // plugin version checks can be performed properly by the engine, just // like it does for the engine version requirement. On the other hand, // This also requires refactoring a big chunk of the API and code of the // engine responsible of loading rules. // // Since we're close to releasing Falco v0.35, the chosen workaround is // to first collect any error from the engine, then checking if there is // also a version dependency not being satisfied, and give that failure // cause priority in case we encounter it. This is indeed not perfect, but // suits us for the time being. The non-covered corner case is when // the `required_plugin_versions` YAML block is defined after the first // rule definition (which is wrong anyways but currently allowed by the // engine), in which case Falco would stop at the first error (which // behavior we'll still want to change in the near future), not collect the // plugin deps info, and the checks below will pass with success wrongly. // // todo(jasondellaluce): perform plugin deps checks inside the // falco engine in the middle of the loading procedure of a rules file std::string req_err = ""; if(!check_rules_plugin_requirements(s, req_err)) { err = req_err; } if(!err.empty()) { return run_result::fatal(err); } for(const auto& sel : s.config->m_rules_selection) { bool enable = sel.m_op == falco_configuration::rule_selection_operation::enable; if(sel.m_rule != "") { falco_logger::log(falco_logger::level::INFO, (enable ? "Enabling" : "Disabling") + std::string(" rules with name: ") + sel.m_rule + "\n"); s.engine->enable_rule_wildcard(sel.m_rule, enable); } if(sel.m_tag != "") { falco_logger::log(falco_logger::level::INFO, (enable ? "Enabling" : "Disabling") + std::string(" rules with tag: ") + sel.m_tag + "\n"); s.engine->enable_rule_by_tag(std::set{sel.m_tag}, enable); // TODO wildcard support } } // 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(); 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; } return run_result::exit(); } return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/pidfile.cpp ================================================ // 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 #include #include #include "actions.h" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::pidfile(const falco::app::state& state) { if(state.options.dry_run) { falco_logger::log(falco_logger::level::DEBUG, "Skipping pidfile creation in dry-run\n"); return run_result::ok(); } if(state.options.pidfilename.empty()) { return run_result::ok(); } int64_t self_pid = getpid(); std::ofstream stream; stream.open(state.options.pidfilename); if(!stream.good()) { falco_logger::log( falco_logger::level::ERR, "Could not write pid to pidfile " + state.options.pidfilename + ". Exiting.\n"); exit(-1); } stream << self_pid; stream.close(); return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/print_config_schema.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "actions.h" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::print_config_schema(falco::app::state &s) { if(!s.options.print_config_schema) { return run_result::ok(); } printf("%s", s.config->m_config_schema.dump(2).c_str()); return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/print_generated_gvisor_config.cpp ================================================ ================================================ FILE: userspace/falco/app/actions/print_help.cpp ================================================ // 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" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::print_help(falco::app::state& s) { if(!s.options.help) { return run_result::ok(); } printf("%s", s.options.usage().c_str()); return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/print_ignored_events.cpp ================================================ // 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" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::print_ignored_events(const falco::app::state& s) { if(!s.options.print_ignored_events) { return run_result::ok(); } std::cout << "Ignored syscall(s):" << std::endl; for(const auto& it : libsinsp::events::sc_set_to_event_names(falco::app::ignored_sc_set())) { std::cout << "- " << it.c_str() << std::endl; } std::cout << std::endl; return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/print_kernel_version.cpp ================================================ // 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 #include #include using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::print_kernel_version(const falco::app::state& s) { #ifdef __linux__ // We print this info only when a kernel driver is injected bool const is_kernel_driver_injected = s.is_modern_ebpf() || s.is_kmod(); if(!is_kernel_driver_injected) { return run_result::ok(); } 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(); } ================================================ FILE: userspace/falco/app/actions/print_page_size.cpp ================================================ // 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" #ifdef _WIN32 #include #endif using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::print_page_size(const falco::app::state& s) { if(!s.options.print_page_size) { return run_result::ok(); } #ifndef _WIN32 long page_size = getpagesize(); #else SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); long page_size = sysInfo.dwPageSize; #endif if(page_size <= 0) { return run_result::fatal("\nUnable to get the system page size through 'getpagesize()'\n"); } falco_logger::log(falco_logger::level::INFO, "Your system page size is: " + std::to_string(page_size) + " bytes\n"); return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/print_plugin_info.cpp ================================================ // 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 using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::print_plugin_info(const falco::app::state &s) { if(s.options.print_plugin_info.empty()) { return run_result::ok(); } sinsp inspector; for(auto &pc : s.config->m_plugins) { if(pc.m_name != s.options.print_plugin_info && pc.m_library_path != s.options.print_plugin_info) { continue; } // found matching plugin; load it auto p = inspector.register_plugin(pc.m_library_path); // print plugin descriptive info std::ostringstream os; format_plugin_info(p, os); os << std::endl; printf("%s", os.str().c_str()); // print plugin init schema os.str(""); os.clear(); ss_plugin_schema_type type; auto schema = p->get_init_schema(type); os << "Init config schema type: "; switch(type) { case SS_PLUGIN_SCHEMA_JSON: os << "JSON" << std::endl; break; case SS_PLUGIN_SCHEMA_NONE: default: os << "Not available, plugin does not implement the init config schema " "functionality" << std::endl; break; } os << schema << std::endl; os << std::endl; printf("%s", os.str().c_str()); // init the plugin std::string err; if(!p->init(pc.m_init_config, err)) { return run_result::fatal(err); } // print plugin suggested open parameters if(p->caps() & CAP_SOURCING) { os.str(""); os.clear(); auto params = p->list_open_params(); if(params.empty()) { os << "No suggested open params available: "; os << "plugin has not been configured, or it does not implement the open " "params suggestion functionality" << std::endl; } else { os << "Suggested open params:" << std::endl; for(const auto &oparam : p->list_open_params()) { if(oparam.desc == "") { os << oparam.value << std::endl; } else { os << oparam.value << ": " << oparam.desc << std::endl; } } } os << std::endl; printf("%s", os.str().c_str()); } // exit return run_result::exit(); } return run_result::fatal("can't find plugin and print its info: " + s.options.print_plugin_info); } ================================================ FILE: userspace/falco/app/actions/print_rule_schema.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "actions.h" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::print_rule_schema(falco::app::state &s) { if(!s.options.print_rule_schema) { return run_result::ok(); } printf("%s", s.engine->m_rule_schema.dump(2).c_str()); return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/print_support.cpp ================================================ // 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. */ #ifndef _WIN32 #include #else #include #endif #include #include "actions.h" #include "../../versions_info.h" using namespace falco::app; using namespace falco::app::actions; static std::string read_file(const std::string& filename) { std::ifstream t(filename); std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); return str; } #ifndef _WIN32 static int get_sysinfo(nlohmann::json& support) { struct utsname sysinfo; if(uname(&sysinfo) != 0) { return -1; } support["system_info"]["sysname"] = sysinfo.sysname; support["system_info"]["nodename"] = sysinfo.nodename; support["system_info"]["release"] = sysinfo.release; support["system_info"]["version"] = sysinfo.version; support["system_info"]["machine"] = sysinfo.machine; return 0; } #else static int get_sysinfo(nlohmann::json& support) { OSVERSIONINFO osvi; SYSTEM_INFO sysInfo; TCHAR computerName[256]; DWORD size = sizeof(computerName); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetSystemInfo(&sysInfo); if(!GetVersionEx(&osvi) || !GetComputerName(computerName, &size)) { return -1; } support["system_info"]["sysname"] = "Windows"; support["system_info"]["nodename"] = computerName; support["system_info"]["release"] = osvi.dwMajorVersion; support["system_info"]["version"] = osvi.dwMinorVersion; switch(sysInfo.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_AMD64: support["system_info"]["machine"] = "x86_64"; break; case PROCESSOR_ARCHITECTURE_ARM: support["system_info"]["machine"] = "ARM"; break; case PROCESSOR_ARCHITECTURE_ARM64: support["system_info"]["machine"] = "ARM64"; break; case PROCESSOR_ARCHITECTURE_INTEL: support["system_info"]["machine"] = "i386"; break; default: support["system_info"]["machine"] = "unknown"; } return 0; } #endif falco::app::run_result falco::app::actions::print_support(falco::app::state& s) { if(!s.options.print_support) { return run_result::ok(); } nlohmann::json support; if(get_sysinfo(support) != 0) { return run_result::fatal(std::string("Could not get system info: ") + strerror(errno)); } const falco::versions_info infos(s.offline_inspector); support["version"] = infos.falco_version; support["engine_info"] = infos.as_json(); support["cmdline"] = s.cmdline; support["config"] = s.config->dump(); support["rules_files"] = nlohmann::json::array(); for(const auto& filename : s.config->m_loaded_rules_filenames) { nlohmann::json finfo; finfo["name"] = filename; nlohmann::json variant; variant["content"] = read_file(filename); finfo["variants"].push_back(variant); support["rules_files"].push_back(finfo); } printf("%s\n", support.dump().c_str()); return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/print_syscall_events.cpp ================================================ // 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 "event_formatter.h" #include "../app.h" #include "../../versions_info.h" using namespace falco::app; using namespace falco::app::actions; struct events_by_category { std::vector syscalls; std::vector tracepoints; std::vector pluginevents; std::vector metaevents; void add_event(ppm_event_code e, bool available, const std::string& name = "") { event_entry entry; entry.is_enter = PPME_IS_ENTER(e); entry.info = libsinsp::events::info(e); entry.available = available; if(name == "") { entry.name = entry.info->name; } else { entry.name = name; } if(libsinsp::events::is_syscall_event(e)) { syscalls.push_back(entry); return; } if(libsinsp::events::is_tracepoint_event(e)) { tracepoints.push_back(entry); return; } if(libsinsp::events::is_plugin_event(e)) { pluginevents.push_back(entry); return; } if(libsinsp::events::is_metaevent(e)) { metaevents.push_back(entry); return; } } void print_all(EventFormatter& formatter) { formatter.begin_category("Syscall events"); for(const auto& e : syscalls) { formatter.print_event(e); } formatter.end_category(); formatter.begin_category("Tracepoint events"); for(const auto& e : tracepoints) { formatter.print_event(e); } formatter.end_category(); formatter.begin_category("Plugin events"); for(const auto& e : pluginevents) { formatter.print_event(e); } formatter.end_category(); formatter.begin_category("Metaevents"); for(const auto& e : metaevents) { formatter.print_event(e); } formatter.end_category(); } }; static struct events_by_category get_event_entries_by_category( bool include_generics, const libsinsp::events::set& available) { events_by_category result; // skip generic events for(const auto& e : libsinsp::events::all_event_set()) { if(!libsinsp::events::is_generic(e) && !libsinsp::events::is_old_version_event(e) && !libsinsp::events::is_unused_event(e) && !libsinsp::events::is_unknown_event(e)) { result.add_event(e, available.contains(e)); } } if(include_generics) { // append generic events const auto names = libsinsp::events::event_set_to_names({ppm_event_code::PPME_GENERIC_E}); for(const auto& name : names) { result.add_event(ppm_event_code::PPME_GENERIC_E, available.contains(ppm_event_code::PPME_GENERIC_E), name); result.add_event(ppm_event_code::PPME_GENERIC_X, available.contains(ppm_event_code::PPME_GENERIC_X), name); } } return result; } falco::app::run_result falco::app::actions::print_syscall_events(falco::app::state& s) { if(!s.options.list_syscall_events) { return run_result::ok(); } const falco::versions_info info(s.offline_inspector); const libsinsp::events::set available = libsinsp::events::all_event_set().diff( sc_set_to_event_set(falco::app::ignored_sc_set())); struct events_by_category events_bc = get_event_entries_by_category(true, available); // Create the appropriate formatter and use it auto formatter = EventFormatter::create(s.options.output_fmt); formatter->begin(info.driver_schema_version); events_bc.print_all(*formatter); formatter->end(); return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/print_version.cpp ================================================ // 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 "../../versions_info.h" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::print_version(falco::app::state& s) { if(!s.options.print_version_info) { return run_result::ok(); } const falco::versions_info info(s.offline_inspector); if(s.config->m_json_output) { printf("%s\n", info.as_json().dump().c_str()); } else { printf("Falco version: %s\n", info.falco_version.c_str()); printf("Libs version: %s\n", info.libs_version.c_str()); printf("Plugin API: %s\n", info.plugin_api_version.c_str()); printf("Engine: %s\n", info.engine_version.c_str()); printf("Driver:\n"); printf(" API version: %s\n", info.driver_api_version.c_str()); printf(" Schema version: %s\n", info.driver_schema_version.c_str()); printf(" Default driver: %s\n", info.default_driver_version.c_str()); } return run_result::exit(); } ================================================ FILE: userspace/falco/app/actions/process_events.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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. */ #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include "falco_utils.h" #include "actions.h" #include "helpers.h" #include "../options.h" #include "../signals.h" #include "../../falco_semaphore.h" #include "../../stats_writer.h" #include "../../falco_outputs.h" #include "../../event_drops.h" #include #include using namespace falco::app; using namespace falco::app::actions; class source_sync_context { public: explicit source_sync_context(falco::semaphore& s): m_finished(false), m_joined(false), m_semaphore(s) {} inline void finish() { bool v = false; while(!m_finished.compare_exchange_weak(v, true, std::memory_order_seq_cst, std::memory_order_seq_cst)) { if(v) { throw falco_exception("source_sync_context has been finished twice"); } } m_semaphore.release(); } inline void join() { bool v = false; while(!m_joined.compare_exchange_weak(v, true, std::memory_order_seq_cst, std::memory_order_seq_cst)) { if(v) { throw falco_exception("source_sync_context has been joined twice"); } } } inline bool joined() { return m_joined.load(std::memory_order_seq_cst); } inline bool finished() { return m_finished.load(std::memory_order_seq_cst); } private: // set to true when the event processing loop finishes std::atomic m_finished; // set to true when the result has been collected after finishing std::atomic m_joined; // used to notify the waiting thread when finished gets set to true falco::semaphore& m_semaphore; }; struct live_context { // the name of the source of which events are processed std::string source; // the result of the event processing loop run_result res; // if non-null, the thread on which events are processed std::unique_ptr thread; // used for thread synchronization purposes std::unique_ptr sync; }; // // Event processing loop // static falco::app::run_result do_inspect( falco::app::state& s, std::shared_ptr inspector, const std::string& source, // an empty source represents capture mode std::shared_ptr statsw, syscall_evt_drop_mgr& sdropmgr, bool check_drops_and_timeouts, uint64_t duration_to_tot_ns, uint64_t& num_evts) { int32_t rc = 0; sinsp_evt* ev = NULL; stats_writer::collector stats_collector(statsw); uint64_t duration_start = 0; uint32_t timeouts_since_last_success_or_msg = 0; const bool is_capture_mode = source.empty(); size_t source_engine_idx = 0; // note(jasondellaluce): The "syscall" event source will always be loaded // by default in an inspector, and at index 0. As such, in live mode we would // expect the event source index to always be 0 in case of "syscall" source, // and 1 in case of any other plugin event source, because it would be // the only other source loaded in its relative live inspector. size_t expected_live_evt_src_idx = source == falco_common::syscall_source ? 0 : 1; if(!is_capture_mode) { // note: in live mode, each inspector gets assigned a distinct event // source that does not change for the whole capture. source_engine_idx = s.source_infos.at(source)->engine_idx; } // reset event counter num_evts = 0; // init drop manager if we are inspecting syscalls if(check_drops_and_timeouts) { sdropmgr.init(inspector, s.outputs, // drop manager has its own rate limiting logic s.config->m_syscall_evt_drop_actions, s.config->m_syscall_evt_drop_threshold, s.config->m_syscall_evt_drop_rate, s.config->m_syscall_evt_drop_max_burst, s.config->m_syscall_evt_simulate_drops); } // init dumper for captures auto dumper = std::make_unique(); uint64_t dump_started_ts = 0; uint64_t dump_deadline_ts = 0; // // Start capture // inspector->start_capture(); // // Loop through the events // while(1) { rc = inspector->next(&ev); if(falco::app::g_reopen_outputs_signal.triggered()) { falco::app::g_reopen_outputs_signal.handle([&s]() { falco_logger::log(falco_logger::level::INFO, "SIGUSR1 received, reopening outputs...\n"); if(s.outputs != nullptr) { s.outputs->reopen_outputs(); } falco::app::g_reopen_outputs_signal.reset(); }); } if(falco::app::g_terminate_signal.triggered()) { falco::app::g_terminate_signal.handle([&]() { falco_logger::log(falco_logger::level::INFO, "SIGINT received, exiting...\n"); if(dump_started_ts != 0) { dump_started_ts = 0; dump_deadline_ts = 0; dumper->close(); } }); break; } else if(falco::app::g_restart_signal.triggered()) { falco::app::g_restart_signal.handle([&]() { falco_logger::log(falco_logger::level::INFO, "SIGHUP received, restarting...\n"); if(dump_started_ts != 0) { dump_started_ts = 0; dump_deadline_ts = 0; dumper->close(); } s.restart.store(true); }); break; } else if(rc == SCAP_TIMEOUT) { if(ev == nullptr) [[unlikely]] { timeouts_since_last_success_or_msg++; if(timeouts_since_last_success_or_msg > s.config->m_syscall_evt_timeout_max_consecutives && check_drops_and_timeouts) { std::string rule = "Falco internal: timeouts notification"; std::string msg = rule + ". " + std::to_string(s.config->m_syscall_evt_timeout_max_consecutives) + " consecutive timeouts without event."; std::string last_event_time_str = "none"; if(duration_start > 0) { sinsp_utils::ts_to_string(duration_start, &last_event_time_str, false, true); } nlohmann::json fields; fields["last_event_time"] = last_event_time_str; auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); s.outputs->handle_msg(now, falco_common::PRIORITY_DEBUG, msg, rule, fields); // Reset the timeouts counter, Falco alerted timeouts_since_last_success_or_msg = 0; } } continue; } else if(rc == SCAP_FILTERED_EVENT) { continue; } else if(rc == SCAP_EOF) { break; } else if(rc != SCAP_SUCCESS) { // // Event read error. // return run_result::fatal(inspector->getlasterr()); } // if we are in live mode, we already have the right source engine idx if(is_capture_mode) { // note: here we can assume that the source index will be the same // in both the falco engine and the inspector. See the // comment in init_falco_engine.cpp for more details. source_engine_idx = ev->get_source_idx(); if(source_engine_idx == sinsp_no_event_source_idx) { std::string msg = "Unknown event source for inspector's event"; if(ev->get_type() == PPME_PLUGINEVENT_E || ev->get_type() == PPME_ASYNCEVENT_E) { auto pluginID = ev->get_param(0)->as(); if(pluginID != 0) { msg += " (plugin ID: " + std::to_string(pluginID) + ")"; } } 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); } else { // in live mode, each inspector gets assigned a distinct event source, // so we report an error if we fetch an event of a different source. if(expected_live_evt_src_idx != ev->get_source_idx()) { std::string actual = (ev->get_source_name() != NULL) ? ("'" + std::string(ev->get_source_name()) + "'") : (""); std::string msg = "Unexpected event source for inspector's event:"; msg += " type=" + std::to_string(ev->get_type()); msg += ", expected='" + source + " (idx=" + std::to_string(expected_live_evt_src_idx) + ")"; msg += "', actual=" + actual + " (idx=" + std::to_string(ev->get_source_idx()) + ")"; return run_result::fatal(msg); } // for live mode, the source name is constant stats_collector.collect(inspector, source, num_evts); } // Reset the timeouts counter, Falco successfully got an event to process timeouts_since_last_success_or_msg = 0; if(duration_start == 0) { duration_start = ev->get_ts(); } else if(duration_to_tot_ns > 0) { // Highest priority async events (whose timestamp is -1 and get set by sinsp to current // ts) are processed **before** other events, event if already enqueued. This means that // we might find ourself in a situation where we have duration_start whose ts is > then // next ev->get_ts(), leading t ev->get_ts() - duration_start being <0 (and, since we // are unsigned here, huge). The diff should never need to be that large anyway, use a // signed. const int64_t diff = ev->get_ts() - duration_start; if(diff >= (int64_t)duration_to_tot_ns) { break; } } if(check_drops_and_timeouts && !sdropmgr.process_event(inspector, ev)) { return run_result::fatal("Drop manager internal error"); } // As the inspector has no filter at its level, all // events are returned here. Pass them to the falco // engine, which will match the event against the set // of rules. If a match is found, pass the event to // the outputs. auto res = s.engine->process_event(source_engine_idx, ev, s.config->m_rule_matching); if(res != nullptr) { auto capture = s.config->m_capture_enabled && capture_mode_t::ALL_RULES == s.config->m_capture_mode; for(auto& rule_res : *res) { // Process output s.outputs->handle_event(rule_res.evt, rule_res.rule, rule_res.source, rule_res.priority_num, rule_res.format, rule_res.tags, rule_res.extra_output_fields); // Compute capture params, if enabled if(s.config->m_capture_enabled) { if(capture_mode_t::RULES == s.config->m_capture_mode && rule_res.capture) { capture = true; } // Compute the capture deadline for this event, // based on the rule’s duration or the default one if unspecified auto evt_deadline_ts = ev->get_ts() + (rule_res.capture_duration_ns > 0 ? rule_res.capture_duration_ns : s.config->m_capture_default_duration_ns); // Update the capture deadline if this event needs to extend it beyond the // current deadline or if no deadline is currently set if(evt_deadline_ts > dump_deadline_ts) { dump_deadline_ts = evt_deadline_ts; } } } // When a rule matches or we are in all_rules mode, we start a dump (if not in progress // yet) if(capture && dump_started_ts == 0) { dumper->open(inspector.get(), generate_scap_file_path(s.config->m_capture_path_prefix, ev->get_ts(), ev->get_num()), true); // Enable compression dump_started_ts = ev->get_ts(); } } // Save events when a dump is in progress. // If the deadline is reached, close the dump. if(dump_started_ts != 0) { dumper->dump(ev); if(ev->get_ts() > dump_deadline_ts) { dumper->flush(); dumper->close(); dump_started_ts = 0; dump_deadline_ts = 0; } } num_evts++; } return run_result::ok(); } static void process_inspector_events( falco::app::state& s, std::shared_ptr inspector, std::shared_ptr statsw, const std::string& source, // an empty source represents capture mode source_sync_context* sync, run_result* res) noexcept { run_result result; try { double duration; scap_stats cstats; uint64_t num_evts = 0; syscall_evt_drop_mgr sdropmgr; bool is_capture_mode = source.empty(); bool check_drops_timeouts = is_capture_mode || source == falco_common::syscall_source; duration = ((double)clock()) / CLOCKS_PER_SEC; result = do_inspect(s, inspector, source, statsw, sdropmgr, check_drops_timeouts, uint64_t(s.options.duration_to_tot * ONE_SECOND_IN_NS), num_evts); duration = ((double)clock()) / CLOCKS_PER_SEC - duration; inspector->get_capture_stats(&cstats); if(s.options.verbose) { if(source == falco_common::syscall_source) { fprintf(stderr, "Driver Events:%" PRIu64 "\nDriver Drops:%" PRIu64 "\n", cstats.n_evts, cstats.n_drops); } fprintf(stderr, "%sElapsed time: %.3lf, Captured Events: %" PRIu64 ", %.2lf eps\n", (is_capture_mode ? "" : ("(" + source + ") ").c_str()), duration, num_evts, num_evts / duration); } if(check_drops_timeouts) { sdropmgr.print_stats(); } } catch(const std::exception& e) { result = run_result::fatal(e.what()); } if(sync) { try { sync->finish(); } catch(const std::exception& e) { result = run_result::merge(result, run_result::fatal(e.what())); } } *res = result; } static falco::app::run_result init_stats_writer( const std::shared_ptr& sw, const std::shared_ptr& config, bool is_dry_run) { if(!config->m_metrics_enabled) { return falco::app::run_result::ok(); } /* Enforce minimum bound of 100ms. */ if(config->m_metrics_interval < 100) { return falco::app::run_result::fatal( "Metrics interval must have a minimum value of 100ms and reflect a Prometheus " "compliant time duration format: " "https://prometheus.io/docs/prometheus/latest/querying/basics/#time-durations. "); } if(std::all_of(config->m_metrics_interval_str.begin(), config->m_metrics_interval_str.end(), ::isdigit)) { return falco::app::run_result::fatal( "Metrics interval was passed as numeric value without Prometheus time unit. Please " "specify a time unit"); } if(config->m_metrics_enabled && !(sw->has_output() || config->m_webserver_config.m_prometheus_metrics_enabled)) { return falco::app::run_result::fatal( "Metrics are enabled with no output configured. Please enable at least one output " "channel ('metrics.output_rule', 'metrics.output_file' or " "'webserver.prometheus_metrics_enabled')"); } falco_logger::log(falco_logger::level::INFO, "Setting metrics interval to " + config->m_metrics_interval_str + ", equivalent to " + std::to_string(config->m_metrics_interval) + " (ms)\n"); auto res = falco::app::run_result::ok(); if(is_dry_run) { return res; } res.success = stats_writer::init_ticker(config->m_metrics_interval, res.errstr); res.proceed = res.success; return res; } falco::app::run_result falco::app::actions::process_events(falco::app::state& s) { // Notify engine that we finished loading and enabling all rules s.engine->complete_rule_loading(); // Initialize stats writer auto statsw = std::make_shared(s.outputs, s.config, s.engine); auto res = init_stats_writer(statsw, s.config, s.options.dry_run); if(s.options.dry_run) { falco_logger::log(falco_logger::level::DEBUG, "Skipping event processing in dry-run\n"); return res; } if(!res.success) { return res; } // Print capture mode info, if enabled if(s.config->m_capture_enabled) { std::string capture_mode; switch(s.config->m_capture_mode) { case capture_mode_t::RULES: capture_mode = "'rules'"; break; case capture_mode_t::ALL_RULES: capture_mode = "'all_rules'"; break; default: ASSERT(false); } falco_logger::log(falco_logger::level::INFO, "Capture is enabled in mode " + capture_mode + ". Capturing events to " + s.config->m_capture_path_prefix + "\n"); } // Start processing events bool termination_forced = false; if(s.is_capture_mode()) { res = open_offline_inspector(s); if(!res.success) { return res; } process_inspector_events(s, s.offline_inspector, statsw, "", nullptr, &res); s.offline_inspector->close(); // Honor -M also when using a trace file. // Since inspection stops as soon as all events have been consumed // just await the given duration is reached, if needed. if(s.options.duration_to_tot > 0) { std::this_thread::sleep_for(std::chrono::seconds(s.options.duration_to_tot)); } } else { print_enabled_event_sources(s); #ifdef __EMSCRIPTEN__ if(s.enabled_sources.size() > 1) { return run_result::fatal( "enabling multiple event sources is not supported by this Falco build"); } #endif // start event processing for all enabled sources falco::semaphore termination_sem(s.enabled_sources.size()); std::vector ctxs; ctxs.reserve(s.enabled_sources.size()); for(const auto& source : s.enabled_sources) { auto& ctx = ctxs.emplace_back(); ctx.source = source; ctx.sync = std::make_unique(termination_sem); auto src_info = s.source_infos.at(source); try { falco_logger::log(falco_logger::level::DEBUG, "Opening event source '" + source + "'\n"); termination_sem.acquire(); res = open_live_inspector(s, src_info->inspector, source); if(!res.success) { // note: we don't return here because we need to reach // the thread termination loop below to make sure all // already-spawned threads get terminated gracefully ctx.sync->finish(); break; } if(s.enabled_sources.size() == 1) { if(s.on_inspectors_opened != nullptr) { s.on_inspectors_opened(); } // optimization: with only one source we don't spawn additional threads process_inspector_events(s, src_info->inspector, statsw, source, ctx.sync.get(), &ctx.res); } else { auto res_ptr = &ctx.res; auto sync_ptr = ctx.sync.get(); ctx.thread = std::make_unique( [&s, src_info, &statsw, source, sync_ptr, res_ptr]() { process_inspector_events(s, src_info->inspector, statsw, source, sync_ptr, res_ptr); }); } } catch(std::exception& e) { // note: we don't return here because we need to reach // the thread termination loop below to make sure all // already-spawned threads get terminated gracefully ctx.res = run_result::fatal(e.what()); ctx.sync->finish(); break; } } if(s.enabled_sources.size() > 1 && s.on_inspectors_opened != nullptr) { s.on_inspectors_opened(); } // wait for event processing to terminate for all sources // if a thread terminates with an error, we trigger the app termination // to force all other event streams to terminate too. // We accumulate the errors in a single run_result. size_t stopped_count = 0; while(stopped_count < ctxs.size()) { if(!res.success && !termination_forced) { falco_logger::log(falco_logger::level::INFO, "An error occurred in an event source, forcing termination...\n"); falco::app::g_terminate_signal.trigger(); falco::app::g_terminate_signal.handle([&]() {}); termination_forced = true; } // This is shared across all running event source threads an // keeps the main thread sleepy until one of the parallel // threads terminates and invokes release(). At that point, // we know that at least one thread finished running and we can // attempt joining it. Not that this also works when only one // event source is enabled, in which we have no additional threads. termination_sem.acquire(); for(auto& ctx : ctxs) { if(ctx.sync->finished() && !ctx.sync->joined()) { if(ctx.thread) { if(!ctx.thread->joinable()) { // thread has finished executing but // we already joined it, so we skip to the next one. // technically, we should never get here because // ctx.joined should already be true at this point continue; } ctx.thread->join(); } falco_logger::log(falco_logger::level::DEBUG, "Stopping capture for event source '" + ctx.source + "'\n"); s.source_infos.at(ctx.source)->inspector->stop_capture(); res = run_result::merge(res, ctx.res); ctx.sync->join(); stopped_count++; } } } } return res; } ================================================ FILE: userspace/falco/app/actions/select_event_sources.cpp ================================================ // 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" using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::select_event_sources(falco::app::state &s) { s.enabled_sources = {s.loaded_sources.begin(), s.loaded_sources.end()}; // event sources selection is meaningless when reading trace files if(s.is_capture_mode()) { return run_result::ok(); } if(!s.options.enable_sources.empty() && !s.options.disable_sources.empty()) { return run_result::fatal("You can not mix --enable-source and --disable-source"); } if(!s.options.enable_sources.empty()) { s.enabled_sources.clear(); for(const auto &src : s.options.enable_sources) { if(std::find(s.loaded_sources.begin(), s.loaded_sources.end(), src) == s.loaded_sources.end()) { return run_result::fatal("Attempted enabling an unknown event source: " + src); } s.enabled_sources.insert(src); } } else if(!s.options.disable_sources.empty()) { for(const auto &src : s.options.disable_sources) { if(std::find(s.loaded_sources.begin(), s.loaded_sources.end(), src) == s.loaded_sources.end()) { return run_result::fatal("Attempted disabling an unknown event source: " + src); } s.enabled_sources.erase(src); } } if(s.enabled_sources.empty()) { return run_result::fatal("Must enable at least one event source"); } return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/start_webserver.cpp ================================================ // 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" #if defined(__linux__) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD) #include "webserver.h" #endif using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::start_webserver(falco::app::state& state) { #if defined(__linux__) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD) if(state.is_capture_mode() || !state.config->m_webserver_enabled) { return run_result::ok(); } if(state.options.dry_run) { falco_logger::log(falco_logger::level::DEBUG, "Skipping starting webserver in dry-run\n"); return run_result::ok(); } falco_configuration::webserver_config webserver_config = state.config->m_webserver_config; std::string ssl_option = (webserver_config.m_ssl_enabled ? " (SSL)" : ""); falco_logger::log(falco_logger::level::INFO, "Starting health webserver with threadiness " + std::to_string(webserver_config.m_threadiness) + ", listening on " + webserver_config.m_listen_address + ":" + std::to_string(webserver_config.m_listen_port) + ssl_option + "\n"); state.webserver.start(state, webserver_config); state.on_inspectors_opened = [&state]() { state.webserver.enable_prometheus_metrics(state); }; #endif return run_result::ok(); } falco::app::run_result falco::app::actions::stop_webserver(falco::app::state& state) { #if defined(__linux__) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD) if(state.is_capture_mode() || !state.config->m_webserver_enabled) { return run_result::ok(); } if(state.options.dry_run) { falco_logger::log(falco_logger::level::DEBUG, "Skipping stopping webserver in dry-run\n"); return run_result::ok(); } state.webserver.stop(); #endif return run_result::ok(); } ================================================ FILE: userspace/falco/app/actions/validate_rules_files.cpp ================================================ // 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 #include using namespace falco::app; using namespace falco::app::actions; falco::app::run_result falco::app::actions::validate_rules_files(falco::app::state& s) { if(s.options.validate_rules_filenames.size() == 0) { return run_result::ok(); } std::vector rules_contents; falco::load_result::rules_contents_t rc; try { read_files(s.options.validate_rules_filenames.begin(), s.options.validate_rules_filenames.end(), rules_contents, rc); } catch(falco_exception& e) { return run_result::fatal(e.what()); } bool successful = true; // The validation result is *always* printed to // stdout. When json_output is true, the output is in // json format and contains all errors/warnings for // all files. // // When json_output is false, it contains a summary of // each file and whether it was valid or not, along // with any errors. To match older falco behavior, // this *only* contains errors. // // So for each file stdout will contain: // // : Ok // or // : Invalid // [All Validation Errors] // // Warnings are only printed to stderr, and only // printed when verbose is true. std::string summary; falco_logger::log(falco_logger::level::INFO, "Validating rules file(s):\n"); for(const auto& file : s.options.validate_rules_filenames) { falco_logger::log(falco_logger::level::INFO, " " + file + "\n"); } // The json output encompasses all files so the // validation result is a single json object. std::string err = ""; nlohmann::json results = nlohmann::json::array(); for(auto& filename : s.options.validate_rules_filenames) { std::unique_ptr res; res = s.engine->load_rules(rc.at(filename), filename); if(!check_rules_plugin_requirements(s, err)) { return run_result::fatal(err); } successful &= res->successful(); if(s.config->m_json_output) { results.push_back(res->as_json(rc)); } if(summary != "") { summary += "\n"; } // Add to the summary if not successful, or successful // with no warnings. if(!res->successful() || (res->successful() && !res->has_warnings())) { summary += res->as_string(true, rc); } else { // If here, there must be only warnings. // Add a line to the summary noting that the // file was ok with warnings, without actually // printing the warnings. summary += filename + ": Ok, with warnings"; falco_logger::log(falco_logger::level::WARNING, res->as_string(true, rc) + "\n"); } } // 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; if(!describe_res.empty() && successful) { res["falco_describe_results"] = std::move(describe_res); } std::cout << res.dump() << std::endl; } else { 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) { return run_result::fatal(summary); } return run_result::exit(); } ================================================ FILE: userspace/falco/app/app.cpp ================================================ // 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 "app.h" #include "state.h" #include "signals.h" #include "actions/actions.h" falco::atomic_signal_handler falco::app::g_terminate_signal; falco::atomic_signal_handler falco::app::g_restart_signal; falco::atomic_signal_handler falco::app::g_reopen_outputs_signal; using app_action = std::function; libsinsp::events::set falco::app::ignored_sc_set() { // we ignore all the I/O syscalls that can have very high throughput and // that can badly impact performance. Of those, we avoid ignoring the // ones that are part of the base set used by libsinsp for maintaining // its internal state. return libsinsp::events::io_sc_set().diff(libsinsp::events::sinsp_state_sc_set()); } bool falco::app::run(int argc, char** argv, bool& restart, std::string& errstr) { falco::app::state s; if(!s.options.parse(argc, argv, errstr)) { return false; } for(char** arg = argv; *arg; arg++) { if(s.cmdline.size() > 0) { s.cmdline += " "; } s.cmdline += *arg; } return falco::app::run(s, restart, errstr); } bool falco::app::run(falco::app::state& s, bool& restart, std::string& errstr) { // The order here is the order in which the methods will be // called. Before changing the order, ensure that all // dependencies are honored (e.g. don't process events before // loading plugins, opening inspector, etc.). std::list const run_steps = { falco::app::actions::print_help, falco::app::actions::print_config_schema, falco::app::actions::print_rule_schema, falco::app::actions::print_ignored_events, falco::app::actions::print_syscall_events, falco::app::actions::load_config, falco::app::actions::print_kernel_version, falco::app::actions::print_version, falco::app::actions::print_page_size, falco::app::actions::require_config_file, falco::app::actions::print_plugin_info, falco::app::actions::list_plugins, falco::app::actions::load_plugins, falco::app::actions::init_inspectors, falco::app::actions::init_falco_engine, falco::app::actions::list_fields, falco::app::actions::select_event_sources, falco::app::actions::validate_rules_files, falco::app::actions::load_rules_files, falco::app::actions::print_support, falco::app::actions::init_outputs, falco::app::actions::create_signal_handlers, falco::app::actions::pidfile, falco::app::actions::configure_interesting_sets, falco::app::actions::configure_syscall_buffer_size, falco::app::actions::configure_syscall_buffer_num, falco::app::actions::start_webserver, falco::app::actions::process_events, }; std::list const teardown_steps = { falco::app::actions::unregister_signal_handlers, falco::app::actions::stop_webserver, // Note: calls print_stats internally after resetting outputs. falco::app::actions::cleanup_outputs, falco::app::actions::close_inspectors, }; falco::app::run_result res = falco::app::run_result::ok(); for(const auto& func : run_steps) { res = falco::app::run_result::merge(res, func(s)); if(!res.proceed) { break; } } for(const auto& func : teardown_steps) { res = falco::app::run_result::merge(res, func(s)); // note: we always proceed because we don't want to miss teardown steps } if(!res.success) { errstr = res.errstr; } restart = s.restart; return res.success; } ================================================ FILE: userspace/falco/app/app.h ================================================ // 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. */ #pragma once #include "state.h" #include namespace falco { namespace app { libsinsp::events::set ignored_sc_set(); bool run(int argc, char** argv, bool& restart, std::string& errstr); bool run(falco::app::state& s, bool& restart, std::string& errstr); }; // namespace app }; // namespace falco ================================================ FILE: userspace/falco/app/options.cpp ================================================ // 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 "options.h" #include "../configuration.h" #include "config_falco.h" // disable cxxopts vector delimiter, meaning that // -o test1,test2,test3 won't be treated like -o test1 -o test2 -o test3 #define CXXOPTS_VECTOR_DELIMITER '\0' #include #include #include namespace falco { namespace app { static bool parse_output_format(const std::string &format_str, output_format &out, std::string &errstr) { if(format_str.empty()) { return true; } std::string lower_format = format_str; std::transform(lower_format.begin(), lower_format.end(), lower_format.begin(), ::tolower); if(lower_format == "text") { out = output_format::TEXT; } else if(lower_format == "markdown") { out = output_format::MARKDOWN; } else if(lower_format == "json") { out = output_format::JSON; } else { errstr = "Invalid format '" + format_str + "'. Valid values are: text, markdown, json"; return false; } return true; } bool options::parse(int argc, char **argv, std::string &errstr) { cxxopts::Options opts("falco", "Falco - Cloud Native Runtime Security"); define(opts); m_usage_str = opts.help(); cxxopts::ParseResult m_cmdline_parsed; try { m_cmdline_parsed = opts.parse(argc, argv); } catch(std::exception &e) { errstr = e.what(); return false; } // Some options require additional processing/validation std::ifstream conf_stream; if(!conf_filename.empty()) { conf_stream.open(conf_filename); if(!conf_stream.is_open()) { errstr = std::string("Could not find configuration file at ") + conf_filename; return false; } } else { #ifdef BUILD_TYPE_DEBUG conf_stream.open(FALCO_SOURCE_CONF_FILE); if(conf_stream.is_open()) { conf_filename = FALCO_SOURCE_CONF_FILE; } else #endif { conf_stream.open(FALCO_INSTALL_CONF_FILE); if(conf_stream.is_open()) { conf_filename = FALCO_INSTALL_CONF_FILE; } else { // Note we do not return false here. Although there is // no valid config file, some ways of running falco // (e.g. --help, --list) do not need a config file. // // Later, when it comes time to read a config file, if // the filename is empty we exit with an error. conf_filename = ""; } } } if(m_cmdline_parsed.count("r") > 0) { for(auto &path : m_cmdline_parsed["r"].as>()) { rules_filenames.push_back(path); } } list_fields = m_cmdline_parsed.count("list") > 0; // Validate that both markdown and format are not specified together if(m_cmdline_parsed.count("markdown") > 0 && m_cmdline_parsed.count("format") > 0) { errstr = "Cannot specify both --markdown and --format options together"; return false; } // Parse and validate the format option if(!format.empty()) { if(!parse_output_format(format, output_fmt, errstr)) { return false; } } else if(markdown) { // If markdown flag is set and format is not specified, use MARKDOWN format fprintf(stderr, "WARNING: --markdown is deprecated, use --format markdown instead.\n"); output_fmt = output_format::MARKDOWN; } return true; } const std::string &options::usage() { return m_usage_str; } // clang-format off void options::define(cxxopts::Options& opts) { opts.add_options() ("h,help", "Print this help list and exit.", cxxopts::value(help)->default_value("false")) #ifndef BUILD_TYPE_DEBUG ("c", "Configuration file. If not specified uses " FALCO_INSTALL_CONF_FILE ".", cxxopts::value(conf_filename), "") #else ("c", "Configuration file. If not specified tries " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ".", cxxopts::value(conf_filename), "") #endif ("config-schema", "Print the config json schema and exit.", cxxopts::value(print_config_schema)->default_value("false")) ("rule-schema", "Print the rule json schema and exit.", cxxopts::value(print_rule_schema)->default_value("false")) ("disable-source", "Turn off a specific . By default, all loaded sources get enabled. Available sources are 'syscall' plus all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times, but turning off all event sources simultaneously is not permitted. This option can not be mixed with --enable-source. This option has no effect when reproducing events from a capture file.", cxxopts::value(disable_sources), "") ("dry-run", "Run Falco without processing events. It can help check that the configuration and rules do not have any errors.", cxxopts::value(dry_run)->default_value("false")) ("enable-source", "Enable a specific . By default, all loaded sources get enabled. Available sources are 'syscall' plus all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. When using this option, only the event sources specified by it will be enabled. This option can not be mixed with --disable-source. This option has no effect when reproducing events from a capture file.", cxxopts::value(enable_sources), "") ("i", "Print those events that are ignored by default for performance reasons and exit.", cxxopts::value(print_ignored_events)->default_value("false")) ("L", "Show the name and description of all rules and exit. If json_output is set to true, it prints details about all rules, macros, and lists in JSON format.", cxxopts::value(describe_all_rules)->default_value("false")) ("l", "Show the name and description of the rule specified and exit. If json_output is set to true, it prints details about the rule in JSON format.", cxxopts::value(describe_rule), "") ("list", "List all defined fields and exit. If is provided, only list those fields for the source . Current values for are \"syscall\" or any source from a configured plugin with event sourcing capability.", cxxopts::value(list_source_fields)->implicit_value(""), "") ("list-events", "List all defined syscall events, metaevents, tracepoint events and exit.", cxxopts::value(list_syscall_events)) ("list-plugins", "Print info on all loaded plugins and exit.", cxxopts::value(list_plugins)->default_value("false")) ("M", "Stop Falco execution after are passed.", cxxopts::value(duration_to_tot)->default_value("0"), "") ("markdown", "DEPRECATED: use --format markdown instead. Print output in Markdown format when used in conjunction with --list or --list-events options. It has no effect when used with other options.", cxxopts::value(markdown)) ("format", "Print output in the specified when used in conjunction with --list or --list-events options. Valid values are 'text', 'markdown', or 'json'. It has no effect when used with other options. Cannot be used together with --markdown.", cxxopts::value(format), "") ("N", "Only print field names when used in conjunction with the --list option. It has no effect when used with other options.", cxxopts::value(names_only)->default_value("false")) ("o,option", "Set the value of option to . Overrides values in the configuration file. can be identified using its location in the configuration file using dot notation. Elements of list entries can be accessed via square brackets [].\n E.g. base.id = val\n base.subvalue.subvalue2 = val\n base.list[1]=val", cxxopts::value(cmdline_config_options), "=") ("plugin-info", "Print info for the plugin specified by and exit.\nThis includes all descriptive information like name and author, along with the\nschema format for the init configuration and a list of suggested open parameters.\n can be the plugin's name or its configured 'library_path'.", cxxopts::value(print_plugin_info), "") ("p,print", "DEPRECATED: use -o append_output... instead. Print additional information in the rule's output.\nUse -pc or -pcontainer to append container details to syscall events.\nUse -pk or -pkubernetes to add both container and Kubernetes details to syscall events.\nThe details will be directly appended to the rule's output.\nAlternatively, use -p for a custom format. In this case, the given will be appended to the rule's output without any replacement to all events, including plugin events.", cxxopts::value(print_additional), "") ("P,pidfile", "Write PID to specified path. By default, no PID file is created.", cxxopts::value(pidfilename)->default_value(""), "") ("r", "Rules file or directory to be loaded. This option can be passed multiple times. Falco defaults to the values in the configuration file when this option is not specified. Only files with .yml or .yaml extension are considered.", cxxopts::value>(), "") ("support", "Print support information, including version, rules files used, loaded configuration, etc., and exit. The output is in JSON format.", cxxopts::value(print_support)->default_value("false")) ("U,unbuffered", "Turn off output buffering for configured outputs. This causes every single line emitted by Falco to be flushed, which generates higher CPU usage but is useful when piping those outputs into another process or a script.", cxxopts::value(unbuffered_outputs)->default_value("false")) ("V,validate", "Read the contents of the specified file(s), validate the loaded rules, and exit. This option can be passed multiple times to validate multiple files.", cxxopts::value(validate_rules_filenames), "") ("v", "Enable verbose output.", cxxopts::value(verbose)->default_value("false")) ("version", "Print version information and exit.", cxxopts::value(print_version_info)->default_value("false")) ("page-size", "Print the system page size and exit. This utility may help choose the right syscall ring buffer size.", cxxopts::value(print_page_size)->default_value("false")); opts.set_width(140); } // clang-format on }; // namespace app }; // namespace falco ================================================ FILE: userspace/falco/app/options.h ================================================ // 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. */ #pragma once #include #include "../../engine/output_format.h" #include #include #include #include namespace cxxopts { class Options; }; namespace falco { namespace app { class options { public: options() = default; ~options() = default; options(options&&) = default; options& operator=(options&&) = default; options(const options&) = default; options& operator=(const options&) = default; // Each of these maps directly to a command line option. bool help = false; bool print_config_schema = false; bool print_rule_schema = false; std::string conf_filename; sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL; std::vector disable_sources; std::vector enable_sources; bool describe_all_rules = false; std::string describe_rule; bool print_ignored_events = false; bool list_fields = false; std::string list_source_fields; bool list_plugins = false; std::string print_plugin_info; bool list_syscall_events = false; bool markdown = false; std::string format; output_format output_fmt = output_format::TEXT; int duration_to_tot = 0; bool names_only = false; std::vector cmdline_config_options; std::string print_additional; std::string pidfilename; // Rules list as passed by the user, via cmdline option '-r' std::list rules_filenames; bool print_support = false; bool unbuffered_outputs = false; std::vector validate_rules_filenames; bool verbose = false; bool print_version_info = false; bool print_page_size = false; bool dry_run = false; bool parse(int argc, char** argv, std::string& errstr); const std::string& usage(); private: void define(cxxopts::Options& opts); std::string m_usage_str; }; }; // namespace app }; // namespace falco ================================================ FILE: userspace/falco/app/restart_handler.cpp ================================================ // 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 "restart_handler.h" #include "signals.h" #include "logger.h" #include #include #ifdef _WIN32 #include #else #include #endif #ifdef __linux__ #include #include #endif #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30 #include #define gettid() syscall(SYS_gettid) #endif falco::app::restart_handler::~restart_handler() { stop(); if(m_inotify_fd != -1) { close(m_inotify_fd); } m_inotify_fd = -1; } void falco::app::restart_handler::trigger() { m_forced.store(true, std::memory_order_release); } bool falco::app::restart_handler::start(std::string& err) { #ifdef __linux__ if(m_watched_files.empty() && m_watched_dirs.empty()) { falco_logger::log(falco_logger::level::DEBUG, "Refusing to start restart handler due to nothing to watch\n"); return true; } m_inotify_fd = inotify_init(); if(m_inotify_fd < 0) { err = "could not initialize inotify handler"; return false; } for(const auto& f : m_watched_files) { auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CLOSE_WRITE | IN_MOVE_SELF | IN_DELETE_SELF); if(wd < 0) { err = "could not watch file: " + f; return false; } falco_logger::log(falco_logger::level::DEBUG, "Watching file '" + f + "'\n"); } for(const auto& f : m_watched_dirs) { auto wd = inotify_add_watch(m_inotify_fd, f.c_str(), IN_CREATE | IN_DELETE | IN_MOVE); if(wd < 0) { err = "could not watch directory: " + f; return false; } falco_logger::log(falco_logger::level::DEBUG, "Watching directory '" + f + "'\n"); } // launch the watcher thread m_watcher = std::thread(&falco::app::restart_handler::watcher_loop, this); #endif return true; } void falco::app::restart_handler::stop() { #ifdef __linux__ m_stop.store(true, std::memory_order_release); if(m_watcher.joinable()) { m_watcher.join(); } #endif } void falco::app::restart_handler::watcher_loop() noexcept { #ifdef __linux__ if(fcntl(m_inotify_fd, F_SETOWN, gettid()) < 0) { // an error occurred, we can't recover // todo(jasondellaluce): should we terminate the process? falco_logger::log(falco_logger::level::ERR, "Failed owning inotify handler, shutting down watcher..."); return; } fd_set set; bool should_check = false; bool should_restart = false; struct timeval timeout; uint8_t buf[(10 * (sizeof(struct inotify_event) + NAME_MAX + 1))]; while(!m_stop.load(std::memory_order_acquire)) { // wait for inotify events with a certain timeout. // Note, we'll run through select even before performing a dry-run, // so that we can dismiss in case we have to debounce rapid // subsequent events. timeout.tv_sec = 0; timeout.tv_usec = 100000; FD_ZERO(&set); FD_SET(m_inotify_fd, &set); auto rv = select(m_inotify_fd + 1, &set, NULL, NULL, &timeout); if(rv < 0) { // an error occurred, we can't recover // todo(jasondellaluce): should we terminate the process? falco_logger::log(falco_logger::level::ERR, "Failed select with inotify handler, shutting down watcher..."); return; } // check if there's been a forced restart request bool forced = m_forced.load(std::memory_order_acquire); m_forced.store(false, std::memory_order_release); // no new watch event is received during the timeout if(rv == 0 && !forced) { // perform a dry run. In case no error occurs, we loop back // to the select in order to debounce new inotify events before // actually triggering a restart. if(should_check) { should_check = false; should_restart = m_on_check(); continue; } // if the previous dry run was successful, and no new // inotify events have been received during the dry run, // then we trigger the restarting signal and quit. // note: quitting is a time optimization, the thread // will be forced to quit anyways later by the Falco app, but // at least we don't make users wait for the timeout. if(should_restart) { // todo(jasondellaluce): make this a callback too maybe? g_restart_signal.trigger(); return; } // let's go back to the select continue; } // at this point, we either received a new inotify event or a forced // restart. If this happened during a dry run (even if the dry run // was successful), or during a timeout wait since the last successful // dry run before a restart, we dismiss the restart attempt and // perform an additional dry-run for safety purposes (the new inotify // events may be related to bad config/rules files changes). should_restart = false; should_check = false; // if there's date on the inotify fd, consume it // (even if there is a forced request too) if(rv > 0) { // note: if available data is less than buffer size, this should // return n > 0 but not filling the buffer. If available data is // more than buffer size, we will loop back to select and behave // like we debounced an event. auto n = read(m_inotify_fd, buf, sizeof(buf)); if(n < 0) { // an error occurred, we can't recover // todo(jasondellaluce): should we terminate the process? falco_logger::log(falco_logger::level::ERR, "Failed read with inotify handler, shutting down watcher..."); return; } // this is an odd case, but if we got here with // no read data, and no forced request, we get back // looping in the select. This can likely happen if // there's data in the inotify fd but the first read // returned no bytes. Likely we'll get back here at the // next select call. else if(n == 0) { // we still proceed in case the request was forced if(!forced) { continue; } } } // we consumed the new inotify events or we received a forced // restart request, so we'll perform a dry run after the // next timeout. should_check = true; } #endif } ================================================ FILE: userspace/falco/app/restart_handler.h ================================================ // 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. */ #pragma once #include #include #include #include #include namespace falco { namespace app { /** * @brief A thread-safe helper for handling hot-reload application restarts. */ class restart_handler { public: /** * @brief A function that performs safety checks before confirming * a triggered application restart. Returns true if the application * can safely be restarted. */ using on_check_t = std::function; /** * @brief A list of files or directories paths to watch. */ using watch_list_t = std::vector; explicit restart_handler(on_check_t on_check, const watch_list_t& watch_files = {}, const watch_list_t& watch_dirs = {}): m_inotify_fd(-1), m_stop(false), m_forced(false), m_on_check(on_check), m_watched_dirs(watch_dirs), m_watched_files(watch_files) {} virtual ~restart_handler(); bool start(std::string& err); void stop(); void trigger(); private: void watcher_loop() noexcept; int m_inotify_fd = -1; std::thread m_watcher; std::atomic m_stop; std::atomic m_forced; on_check_t m_on_check; watch_list_t m_watched_dirs; watch_list_t m_watched_files; }; }; // namespace app }; // namespace falco ================================================ FILE: userspace/falco/app/run_result.h ================================================ // 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. */ #pragma once #include namespace falco { namespace app { struct run_result { // Successful result inline static run_result ok() { run_result r; r.success = true; r.errstr = ""; r.proceed = true; return r; } // Successful result that causes the program to stop inline static run_result exit() { run_result r = ok(); r.proceed = false; return r; } // Failure result that causes the program to stop with an error inline static run_result fatal(const std::string& err) { run_result r; r.success = false; r.errstr = err; r.proceed = false; return r; } // Merges two run results into one inline static run_result merge(const run_result& a, const run_result& b) { auto res = ok(); res.proceed = a.proceed && b.proceed; res.success = a.success && b.success; res.errstr = a.errstr; if(!b.errstr.empty()) { res.errstr += res.errstr.empty() ? "" : "\n"; res.errstr += b.errstr; } return res; } run_result(): success(true), errstr(""), proceed(true) {} virtual ~run_result() = default; run_result(run_result&&) = default; run_result& operator=(run_result&&) = default; run_result(const run_result&) = default; run_result& operator=(const run_result&) = default; // If true, the method completed successfully. bool success; // If success==false, details on the error. std::string errstr; // If true, subsequent methods should be performed. If // false, subsequent methods should *not* be performed // and falco should tear down/exit/restart. bool proceed; }; }; // namespace app }; // namespace falco ================================================ FILE: userspace/falco/app/signals.h ================================================ // 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. */ #pragma once #include "../atomic_signal_handler.h" namespace falco { namespace app { extern atomic_signal_handler g_terminate_signal; extern atomic_signal_handler g_restart_signal; extern atomic_signal_handler g_reopen_outputs_signal; }; // namespace app }; // namespace falco ================================================ FILE: userspace/falco/app/state.h ================================================ // 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. */ #pragma once #include "indexed_vector.h" #include "options.h" #include "restart_handler.h" #include "../configuration.h" #include "../stats_writer.h" #if defined(__linux__) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD) #include "../webserver.h" #endif #include #include #include #include #include namespace falco { namespace app { // Holds the state used and shared by the below methods that // actually implement the application. Declared as a // standalone class to allow for a bit of separation between // application state and instance variables, and to also defer // initializing this state until application::init. struct state { // Holds the info mapped for each loaded event source struct source_info { source_info(): filterchecks(std::make_shared()) {} // 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 = -1; // The filtercheck list containing all fields compatible // with the given event source std::shared_ptr filterchecks; // The inspector assigned to this event source. If in capture mode, // all event source will share the same inspector. If the event // source is a plugin one, the assigned inspector must have that // plugin registered in its plugin manager std::shared_ptr inspector; }; state(): config(std::make_shared()), engine(std::make_shared()), offline_inspector(std::make_shared()) {} state(const std::string& cmd, const falco::app::options& opts): state() { cmdline = cmd; options = opts; } std::string cmdline; falco::app::options options; std::atomic restart = false; std::shared_ptr config; std::shared_ptr outputs; std::shared_ptr engine; // The set of loaded event sources (by default, the syscall event // source plus all event sources coming from the loaded plugins). // note: this has to be a vector to preserve the loading order, // however it's not supposed to contain duplicate values. std::vector loaded_sources; // The set of enabled event sources (can be altered by using // the --enable-source and --disable-source options) std::unordered_set enabled_sources; // Used to load all plugins to get their info. In capture mode, // this is also used to open the capture file and read its events std::shared_ptr offline_inspector; // List of all the information mapped to each event source // indexed by event source name indexed_vector source_infos; // List of all plugin configurations indexed by plugin name as returned // by their sinsp_plugin::name method indexed_vector plugin_configs; // Set of syscalls we want the driver to capture libsinsp::events::set selected_sc_set; // Dimension of the syscall buffer in bytes. uint64_t syscall_buffer_bytes_size = DEFAULT_DRIVER_BUFFER_BYTES_DIM; // Helper responsible for watching of handling hot application restarts std::shared_ptr restarter; #if defined(__linux__) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD) falco_webserver webserver; #endif // Set by start_webserver to start prometheus metrics // once all inspectors are opened. std::function on_inspectors_opened = nullptr; inline bool is_capture_mode() const { return config->m_engine_mode == engine_kind_t::REPLAY; } inline bool is_kmod() const { return config->m_engine_mode == engine_kind_t::KMOD; } inline bool is_modern_ebpf() const { return config->m_engine_mode == engine_kind_t::MODERN_EBPF; } inline bool is_nodriver() const { return config->m_engine_mode == engine_kind_t::NODRIVER; } inline bool is_source_enabled(const std::string& src) const { return enabled_sources.find(falco_common::syscall_source) != enabled_sources.end(); } inline bool is_driver_drop_failed_exit_enabled() const { bool drop_failed; switch(config->m_engine_mode) { case engine_kind_t::KMOD: drop_failed = config->m_kmod.m_drop_failed_exit; break; case engine_kind_t::MODERN_EBPF: drop_failed = config->m_modern_ebpf.m_drop_failed_exit; break; default: drop_failed = false; break; } return drop_failed; } inline int16_t driver_buf_size_preset() const { int16_t index; switch(config->m_engine_mode) { case engine_kind_t::KMOD: index = config->m_kmod.m_buf_size_preset; break; case engine_kind_t::MODERN_EBPF: index = config->m_modern_ebpf.m_buf_size_preset; break; default: // unsupported index = -1; break; } return index; } }; }; // namespace app }; // namespace falco ================================================ FILE: userspace/falco/atomic_signal_handler.h ================================================ // 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. */ #pragma once #include #include #include namespace falco { /** * @brief A concurrent object that helps properly handling * system signals from multiple threads. */ class atomic_signal_handler { public: /** * @brief Returns true if the underlying atomic implementation * is lock-free as per C++ standard semantics. */ inline bool is_lock_free() const { return m_handled.is_lock_free() && m_triggered.is_lock_free(); } /** * @brief Resets the handler to its initial state, which is * non-triggered and non-handled. */ inline void reset() { m_handled.store(false, std::memory_order_seq_cst); m_triggered.store(false, std::memory_order_seq_cst); } /** * @brief Returns true if the signal has been triggered. */ inline bool triggered() const { return m_triggered.load(std::memory_order_seq_cst); } /** * @brief Returns true if the signal has been handled. */ inline bool handled() const { return m_handled.load(std::memory_order_seq_cst); } /** * @brief Triggers the signal. Must generally be invoked from * within an actual signal handler (created with the `signal` * system call). Can eventually be invoked for "faking" * the triggering of a signal programmatically. */ inline void trigger() { m_triggered.store(true, std::memory_order_seq_cst); m_handled.store(false, std::memory_order_seq_cst); } /** * @brief If a signal is triggered, performs an handler action. * The action function will be invoked exactly once among all the * simultaneous calls. The action will not be performed if the * signal is not triggered, or if the triggered has already been * handled. When an action is being performed, all the simultaneous * callers will wait and be blocked up until its execution is finished. * If the handler action throws an exception, it will be considered * 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. */ inline bool handle(std::function f) { if(triggered() && !handled()) { std::unique_lock lock(m_mtx); if(!handled()) { try { f(); // note: the action may have forcely reset // the signal handler, so we don't want to create // an inconsistent state if(triggered()) { m_handled.store(true, std::memory_order_seq_cst); } } catch(std::exception&) { if(triggered()) { m_handled.store(true, std::memory_order_seq_cst); } throw; } return true; } } return false; } private: std::mutex m_mtx; std::atomic m_triggered{false}; std::atomic m_handled{false}; }; }; // namespace falco ================================================ FILE: userspace/falco/config_falco.h.in ================================================ // 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. */ #pragma once #define FALCO_VERSION "@FALCO_VERSION@" #define FALCO_VERSION_MAJOR @FALCO_VERSION_MAJOR@ #define FALCO_VERSION_MINOR @FALCO_VERSION_MINOR@ #define FALCO_VERSION_PATCH @FALCO_VERSION_PATCH@ #define FALCO_VERSION_PRERELEASE "@FALCO_VERSION_PRERELEASE@" #define FALCO_VERSION_BUILD "@FALCO_VERSION_BUILD@" #define FALCO_TARGET_ARCH "@FALCO_TARGET_ARCH@" #define FALCO_SOURCE_DIR "${PROJECT_SOURCE_DIR}" #define FALCO_SOURCE_CONF_FILE "${PROJECT_SOURCE_DIR}/falco.yaml" #define FALCO_INSTALL_CONF_FILE "/etc/falco/falco.yaml" #define FALCO_ENGINE_PLUGINS_DIR "${FALCO_ABSOLUTE_SHARE_DIR}/plugins/" #define FALCOSECURITY_LIBS_VERSION "${FALCOSECURITY_LIBS_VERSION}" #define DRIVER_NAME "@DRIVER_NAME@" #define DRIVER_VERSION "@DRIVER_VERSION@" ================================================ FILE: userspace/falco/config_json_schema.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #define LONG_STRING_CONST(...) #__VA_ARGS__ const char config_schema_string[] = LONG_STRING_CONST( { "$schema": "http://json-schema.org/draft-06/schema#", "$ref": "#/definitions/FalcoConfig", "definitions": { "FalcoConfig": { "type": "object", "additionalProperties": false, "properties": { "append_output": { "type": "array", "items": { "$ref": "#/definitions/AppendOutput" } }, "static_fields": { "type": "object" }, "config_files": { "type": "array", "items": { "oneOf": [ { "type": "string" }, { "type": "object", "properties": { "path": { "type": "string" }, "strategy": { "type": "string", "enum": [ "append", "override", "add-only" ] } }, "required": [ "path" ] } ] } }, "watch_config_files": { "type": "boolean" }, "plugins_hostinfo": { "type": "boolean" }, "rules_files": { "type": "array", "items": { "type": "string" } }, "rule_files": { "type": "array", "items": { "type": "string" } }, "rules": { "type": "array", "items": { "$ref": "#/definitions/Rule" } }, "engine": { "$ref": "#/definitions/Engine" }, "capture": { "$ref": "#/definitions/Capture" }, "load_plugins": { "type": "array", "items": { "type": "string" } }, "plugins": { "type": "array", "items": { "$ref": "#/definitions/Plugin" } }, "time_format_iso_8601": { "type": "boolean" }, "buffer_format_base64": { "type": "boolean" }, "priority": { "type": "string" }, "json_output": { "type": "boolean" }, "json_include_output_property": { "type": "boolean" }, "json_include_message_property": { "type": "boolean" }, "json_include_output_fields_property": { "type": "boolean" }, "json_include_tags_property": { "type": "boolean" }, "buffered_outputs": { "type": "boolean" }, "rule_matching": { "type": "string" }, "outputs_queue": { "$ref": "#/definitions/OutputsQueue" }, "stdout_output": { "$ref": "#/definitions/Output" }, "syslog_output": { "$ref": "#/definitions/Output" }, "file_output": { "$ref": "#/definitions/FileOutput" }, "http_output": { "$ref": "#/definitions/HTTPOutput" }, "program_output": { "$ref": "#/definitions/ProgramOutput" }, "webserver": { "$ref": "#/definitions/Webserver" }, "log_stderr": { "type": "boolean" }, "log_syslog": { "type": "boolean" }, "log_level": { "type": "string" }, "libs_logger": { "$ref": "#/definitions/LibsLogger" }, "output_timeout": { "type": "integer" }, "syscall_event_timeouts": { "$ref": "#/definitions/SyscallEventTimeouts" }, "syscall_event_drops": { "$ref": "#/definitions/SyscallEventDrops" }, "metrics": { "$ref": "#/definitions/Metrics" }, "base_syscalls": { "$ref": "#/definitions/BaseSyscalls" }, "falco_libs": { "$ref": "#/definitions/FalcoLibs" }, "container_engines": { "type": "object", "additionalProperties": false, "properties": { "docker": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" } } }, "cri": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" }, "sockets": { "type": "array", "items": { "type": "string" } }, "disable_async": { "type": "boolean" } } }, "podman": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" } } }, "lxc": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" } } }, "libvirt_lxc": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" } } }, "bpm": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" } } } } } }, "title": "FalcoConfig" }, "AppendOutput": { "type": "object", "additionalProperties": false, "properties": { "match": { "type": "object", "additionalProperties": false, "properties": { "source": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } }, "rule": { "type": "string" } } }, "extra_output": { "type": "string" }, "extra_fields": { "type": "array", "items": { "anyOf": [ { "type": "object", "additionalProperties": { "type": "string" } }, { "type": "string" } ] } }, "suggested_output": { "type": "boolean" } } }, "Capture": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" }, "path_prefix": { "type": "string" }, "mode": { "type": "string", "enum": [ "rules", "all_rules" ] }, "default_duration": { "type": "integer" } }, "title": "Capture" }, "BaseSyscalls": { "type": "object", "additionalProperties": false, "properties": { "all": { "type": "boolean" }, "custom_set": { "type": "array", "items": { "type": "string" } }, "repair": { "type": "boolean" } }, "minProperties": 1, "title": "BaseSyscalls" }, "Engine": { "type": "object", "additionalProperties": false, "properties": { "kind": { "type": "string" }, "kmod": { "$ref": "#/definitions/Kmod" }, "modern_ebpf": { "$ref": "#/definitions/ModernEbpf" }, "replay": { "$ref": "#/definitions/Replay" } }, "required": [ "kind" ], "title": "Engine" }, "Kmod": { "type": "object", "additionalProperties": false, "properties": { "buf_size_preset": { "type": "integer" }, "drop_failed_exit": { "type": "boolean" } }, "minProperties": 1, "title": "Kmod" }, "ModernEbpf": { "type": "object", "additionalProperties": false, "properties": { "cpus_for_each_buffer": { "type": "integer" }, "buf_size_preset": { "type": "integer" }, "drop_failed_exit": { "type": "boolean" } }, "title": "ModernEbpf" }, "Replay": { "type": "object", "additionalProperties": false, "properties": { "capture_file": { "type": "string" } }, "required": [ "capture_file" ], "title": "Replay" }, "FalcoLibs": { "type": "object", "additionalProperties": false, "properties": { "thread_table_size": { "type": "integer" }, "thread_table_auto_purging_interval_s": { "type": "integer" }, "thread_table_auto_purging_thread_timeout_s": { "type": "integer" }, "snaplen": { "type": "integer" } }, "minProperties": 1, "title": "FalcoLibs" }, "FileOutput": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" }, "keep_alive": { "type": "boolean" }, "filename": { "type": "string" } }, "minProperties": 1, "title": "FileOutput" }, "Output": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" } }, "minProperties": 1, "title": "Output" }, "HTTPOutput": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" }, "url": { "type": "string", "format": "uri", "qt-uri-protocols": [ "http" ] }, "user_agent": { "type": "string" }, "insecure": { "type": "boolean" }, "ca_cert": { "type": "string" }, "ca_bundle": { "type": "string" }, "ca_path": { "type": "string" }, "mtls": { "type": "boolean" }, "client_cert": { "type": "string" }, "client_key": { "type": "string" }, "echo": { "type": "boolean" }, "compress_uploads": { "type": "boolean" }, "keep_alive": { "type": "boolean" }, "max_consecutive_timeouts": { "type": "integer" } }, "minProperties": 1, "title": "HTTPOutput" }, "LibsLogger": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" }, "severity": { "type": "string" } }, "minProperties": 1, "title": "LibsLogger" }, "Metrics": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" }, "interval": { "type": "string" }, "output_rule": { "type": "boolean" }, "output_file": { "type": "string" }, "rules_counters_enabled": { "type": "boolean" }, "resource_utilization_enabled": { "type": "boolean" }, "state_counters_enabled": { "type": "boolean" }, "kernel_event_counters_enabled": { "type": "boolean" }, "kernel_event_counters_per_cpu_enabled": { "type": "boolean" }, "libbpf_stats_enabled": { "type": "boolean" }, "plugins_metrics_enabled": { "type": "boolean" }, "convert_memory_to_mb": { "type": "boolean" }, "include_empty_values": { "type": "boolean" }, "jemalloc_stats_enabled": { "type": "boolean" } }, "minProperties": 1, "title": "Metrics" }, "OutputsQueue": { "type": "object", "additionalProperties": false, "properties": { "capacity": { "type": "integer" } }, "minProperties": 1, "title": "OutputsQueue" }, "Plugin": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string" }, "library_path": { "type": "string" }, "init_config": { "anyOf": [ { "type": "object" }, { "type": "string" }, { "type": "null" } ] }, "open_params": { "type": "string" } }, "required": [ "library_path", "name" ], "title": "Plugin" }, "ProgramOutput": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" }, "keep_alive": { "type": "boolean" }, "program": { "type": "string" } }, "required": [ "program" ], "title": "ProgramOutput" }, "Rule": { "type": "object", "additionalProperties": false, "properties": { "disable": { "$ref": "#/definitions/Able" }, "enable": { "$ref": "#/definitions/Able" } }, "minProperties": 1, "title": "Rule" }, "Able": { "type": "object", "additionalProperties": false, "properties": { "rule": { "type": "string" }, "tag": { "type": "string" } }, "minProperties": 1, "title": "Able" }, "SyscallEventDrops": { "type": "object", "additionalProperties": false, "properties": { "threshold": { "type": "number" }, "actions": { "type": "array", "items": { "type": "string" } }, "rate": { "type": "number" }, "max_burst": { "type": "integer" }, "simulate_drops": { "type": "boolean" } }, "minProperties": 1, "title": "SyscallEventDrops" }, "SyscallEventTimeouts": { "type": "object", "additionalProperties": false, "properties": { "max_consecutives": { "type": "integer" } }, "minProperties": 1, "title": "SyscallEventTimeouts" }, "Webserver": { "type": "object", "additionalProperties": false, "properties": { "enabled": { "type": "boolean" }, "threadiness": { "type": "integer" }, "listen_port": { "type": "integer" }, "listen_address": { "type": "string" }, "k8s_healthz_endpoint": { "type": "string" }, "prometheus_metrics_enabled": { "type": "boolean" }, "ssl_enabled": { "type": "boolean" }, "ssl_certificate": { "type": "string" } }, "minProperties": 1, "title": "Webserver" } } } ); // LONG_STRING_CONST macro ================================================ FILE: userspace/falco/configuration.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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 #include #include #include #include #include #include #include #ifndef _WIN32 #include #endif #include "falco_utils.h" #include "configuration.h" #include "logger.h" #include "config_json_schema.h" #include namespace fs = std::filesystem; // Reference: https://digitalfortress.tech/tips/top-15-commonly-used-regex/ static re2::RE2 ip_address_re( "((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{" "2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-" "9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[" "0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((" "25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([" "0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|" "1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:)" "{3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]" "?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-" "Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[" "0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){" "1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]" "\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:(" "(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(" "%.+)?\\s*$))"); #define DEFAULT_BUF_SIZE_PRESET 4 #define DEFAULT_CPUS_FOR_EACH_SYSCALL_BUFFER 2 #define DEFAULT_DROP_FAILED_EXIT false falco_configuration::falco_configuration(): m_json_output(false), m_json_include_output_property(true), m_json_include_tags_property(true), m_json_include_message_property(false), m_json_include_output_fields_property(true), m_rule_matching(falco_common::rule_matching::FIRST), m_watch_config_files(true), m_buffered_outputs(false), m_outputs_queue_capacity(DEFAULT_OUTPUTS_QUEUE_CAPACITY_UNBOUNDED_MAX_LONG_VALUE), m_time_format_iso_8601(false), m_buffer_format_base64(false), m_output_timeout(2000), m_webserver_enabled(false), m_syscall_evt_drop_threshold(.1), m_syscall_evt_drop_rate(.03333), m_syscall_evt_drop_max_burst(1), m_syscall_evt_simulate_drops(false), m_syscall_evt_timeout_max_consecutives(1000), m_falco_libs_thread_table_size(DEFAULT_FALCO_LIBS_THREAD_TABLE_SIZE), m_falco_libs_thread_table_auto_purging_interval_s( DEFAULT_FALCO_LIBS_THREAD_TABLE_AUTO_PURGING_INTERVAL_S), m_falco_libs_thread_table_auto_purging_thread_timeout_s( DEFAULT_FALCO_LIBS_THREAD_TABLE_AUTO_PURGING_THREAD_TIMEOUT_S), m_falco_libs_snaplen(0), m_base_syscalls_all(false), m_base_syscalls_repair(false), m_metrics_enabled(false), m_metrics_interval_str("5000"), m_metrics_interval(5000), m_metrics_stats_rule_enabled(false), m_metrics_output_file(""), m_metrics_flags(0), m_metrics_convert_memory_to_mb(true), m_metrics_include_empty_values(false), m_plugins_hostinfo(true), m_capture_enabled(false), m_capture_path_prefix("/tmp/falco"), m_capture_mode(capture_mode_t::RULES), m_capture_default_duration_ns(5000 * 1000000LL) { m_config_schema = nlohmann::json::parse(config_schema_string); } config_loaded_res falco_configuration::init_from_content( const std::string &config_content, const std::vector &cmdline_options, const std::string &filename) { config_loaded_res res; std::vector validation_status; m_config.load_from_string(config_content, m_config_schema, &validation_status); init_cmdline_options(cmdline_options); // Only report top most schema validation status res[filename] = validation_status[0]; load_yaml(filename); return res; } config_loaded_res falco_configuration::init_from_file( const std::string &conf_filename, const std::vector &cmdline_options) { config_loaded_res res; std::vector validation_status; try { m_config.load_from_file(conf_filename, m_config_schema, &validation_status); } catch(const std::exception &e) { std::cerr << "Cannot read config file (" + conf_filename + "): " + e.what() + "\n"; throw e; } // Only report top most schema validation status res[conf_filename] = validation_status[0]; // Load any `-o config_files=foo.yaml` cmdline additional option load_cmdline_config_files(cmdline_options); // Merge all config files (both from main falco.yaml and `-o config_files=foo.yaml`) merge_config_files(conf_filename, res); // Load all other `-o` cmdline options to override any config key init_cmdline_options(cmdline_options); // Finally load the parsed config to our structure load_yaml(conf_filename); return res; } std::string falco_configuration::dump() { return m_config.dump(); } // Load configs files to be included and merge them into current config // NOTE: loaded_config_files will resolve to the filepaths list of loaded config. // m_loaded_configs_filenames and m_loaded_configs_folders instead will hold the list of // filenames and folders specified in config (minus the skipped ones). void falco_configuration::merge_config_files(const std::string &config_name, config_loaded_res &res) { std::vector validation_status; m_loaded_configs_filenames.push_back(config_name); const auto ppath = std::filesystem::path(config_name); // Parse files to be included std::list include_files; m_config.get_sequence>( include_files, yaml_helper::configs_key); for(const auto &include_file : include_files) { auto include_file_path = std::filesystem::path(include_file.m_path); if(include_file_path == ppath) { throw std::logic_error("Config error: '" + yaml_helper::configs_key + "' directive tried to recursively include main config file: " + config_name + "."); } if(!std::filesystem::exists(include_file_path)) { // Same we do for rules_file: just skip the entry. continue; } if(std::filesystem::is_regular_file(include_file_path)) { m_loaded_configs_filenames.push_back(include_file.m_path); m_config.include_config_file(include_file.m_path, include_file.m_strategy, m_config_schema, &validation_status); // Only report top most schema validation status res[include_file.m_path] = validation_status[0]; } else if(std::filesystem::is_directory(include_file_path)) { m_loaded_configs_folders.push_back(include_file.m_path); std::vector v; const auto it_options = std::filesystem::directory_options::follow_directory_symlink | std::filesystem::directory_options::skip_permission_denied; for(auto const &dir_entry : std::filesystem::directory_iterator(include_file_path, it_options)) { if(std::filesystem::is_regular_file(dir_entry.path())) { v.push_back(dir_entry.path().string()); } } std::sort(v.begin(), v.end()); for(const auto &f : v) { m_config.include_config_file(f, include_file.m_strategy, m_config_schema, &validation_status); // Only report top most schema validation status res[f] = validation_status[0]; } } } #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) for(auto &filename : m_loaded_configs_filenames) { m_loaded_configs_filenames_sha256sum.insert( {filename, falco::utils::calculate_file_sha256sum(filename)}); } #endif } void falco_configuration::init_logger() { m_log_level = m_config.get_scalar("log_level", "info"); falco_logger::set_level(m_log_level); falco_logger::set_sinsp_logging( m_config.get_scalar("libs_logger.enabled", true), m_config.get_scalar("libs_logger.severity", "info"), "[libs]: "); falco_logger::log_stderr = m_config.get_scalar("log_stderr", false); falco_logger::log_syslog = m_config.get_scalar("log_syslog", true); } void falco_configuration::load_engine_config(const std::string &config_name) { // Set driver mode if not already set. const std::unordered_map engine_mode_lut = { {"kmod", engine_kind_t::KMOD}, {"modern_ebpf", engine_kind_t::MODERN_EBPF}, {"replay", engine_kind_t::REPLAY}, {"nodriver", engine_kind_t::NODRIVER}, }; auto driver_mode_str = m_config.get_scalar("engine.kind", "kmod"); if(engine_mode_lut.find(driver_mode_str) != engine_mode_lut.end()) { m_engine_mode = engine_mode_lut.at(driver_mode_str); } else { throw std::logic_error("Error reading config file (" + config_name + "): engine.kind '" + driver_mode_str + "' is not a valid kind."); } switch(m_engine_mode) { case engine_kind_t::KMOD: m_kmod.m_buf_size_preset = m_config.get_scalar("engine.kmod.buf_size_preset", DEFAULT_BUF_SIZE_PRESET); m_kmod.m_drop_failed_exit = m_config.get_scalar("engine.kmod.drop_failed_exit", DEFAULT_DROP_FAILED_EXIT); break; case engine_kind_t::MODERN_EBPF: m_modern_ebpf.m_cpus_for_each_buffer = m_config.get_scalar("engine.modern_ebpf.cpus_for_each_buffer", DEFAULT_CPUS_FOR_EACH_SYSCALL_BUFFER); m_modern_ebpf.m_buf_size_preset = m_config.get_scalar("engine.modern_ebpf.buf_size_preset", DEFAULT_BUF_SIZE_PRESET); m_modern_ebpf.m_drop_failed_exit = m_config.get_scalar("engine.modern_ebpf.drop_failed_exit", DEFAULT_DROP_FAILED_EXIT); break; case engine_kind_t::REPLAY: m_replay.m_capture_file = m_config.get_scalar("engine.replay.capture_file", ""); if(m_replay.m_capture_file.empty()) { throw std::logic_error( "Error reading config file (" + config_name + "): engine.kind is 'replay' but no engine.replay.capture_file specified."); } break; case engine_kind_t::NODRIVER: default: break; } } void falco_configuration::load_yaml(const std::string &config_name) { init_logger(); load_engine_config(config_name); std::list rules_files; // Small glue code to support old deprecated 'rules_file' config key. int num_rules_files_opts = 0; if(m_config.is_defined("rules_files")) { num_rules_files_opts++; m_config.get_sequence>(rules_files, std::string("rules_files")); } if(m_config.is_defined("rules_file")) { num_rules_files_opts++; m_config.get_sequence>(rules_files, std::string("rules_file")); falco_logger::log(falco_logger::level::WARNING, "Using deprecated config key 'rules_file' (singular form). Please use " "new 'rules_files' config key (plural form)."); } if(num_rules_files_opts == 2) { throw std::logic_error("Error reading config file (" + config_name + "): both 'rules_files' and 'rules_file' keys set"); } m_rules_filenames.clear(); m_loaded_rules_filenames.clear(); m_loaded_rules_filenames_sha256sum.clear(); m_loaded_rules_folders.clear(); for(const auto &file : rules_files) { // Here, we only include files that exist struct stat buffer; if(stat(file.c_str(), &buffer) == 0) { m_rules_filenames.push_back(file); } } m_json_output = m_config.get_scalar("json_output", false); m_json_include_output_property = m_config.get_scalar("json_include_output_property", true); m_json_include_tags_property = m_config.get_scalar("json_include_tags_property", true); m_json_include_message_property = m_config.get_scalar("json_include_message_property", false); m_json_include_output_fields_property = m_config.get_scalar("json_include_output_fields_property", true); m_outputs.clear(); falco::outputs::config file_output; file_output.name = "file"; if(m_config.get_scalar("file_output.enabled", false)) { std::string filename, keep_alive; filename = m_config.get_scalar("file_output.filename", ""); if(filename == std::string("")) { throw std::logic_error("Error reading config file (" + config_name + "): file output enabled but no filename in configuration block"); } file_output.options["filename"] = filename; keep_alive = m_config.get_scalar("file_output.keep_alive", ""); file_output.options["keep_alive"] = keep_alive; m_outputs.push_back(file_output); } falco::outputs::config stdout_output; stdout_output.name = "stdout"; if(m_config.get_scalar("stdout_output.enabled", false)) { m_outputs.push_back(stdout_output); } falco::outputs::config syslog_output; syslog_output.name = "syslog"; if(m_config.get_scalar("syslog_output.enabled", false)) { m_outputs.push_back(syslog_output); } falco::outputs::config program_output; program_output.name = "program"; if(m_config.get_scalar("program_output.enabled", false)) { std::string program, keep_alive; program = m_config.get_scalar("program_output.program", ""); if(program == std::string("")) { throw std::logic_error( "Error reading config file (" + config_name + "): program output enabled but no program in configuration block"); } program_output.options["program"] = program; keep_alive = m_config.get_scalar("program_output.keep_alive", ""); program_output.options["keep_alive"] = keep_alive; m_outputs.push_back(program_output); } falco::outputs::config http_output; http_output.name = "http"; if(m_config.get_scalar("http_output.enabled", false)) { std::string url; url = m_config.get_scalar("http_output.url", ""); if(url == std::string("")) { throw std::logic_error("Error reading config file (" + config_name + "): http output enabled but no url in configuration block"); } http_output.options["url"] = url; std::string user_agent; user_agent = m_config.get_scalar("http_output.user_agent", "falcosecurity/falco"); http_output.options["user_agent"] = user_agent; bool insecure; insecure = m_config.get_scalar("http_output.insecure", false); http_output.options["insecure"] = insecure ? std::string("true") : std::string("false"); bool echo; echo = m_config.get_scalar("http_output.echo", false); http_output.options["echo"] = echo ? std::string("true") : std::string("false"); std::string ca_cert; ca_cert = m_config.get_scalar("http_output.ca_cert", ""); http_output.options["ca_cert"] = ca_cert; std::string ca_bundle; ca_bundle = m_config.get_scalar("http_output.ca_bundle", ""); http_output.options["ca_bundle"] = ca_bundle; std::string ca_path; ca_path = m_config.get_scalar("http_output.ca_path", "/etc/ssl/certs"); http_output.options["ca_path"] = ca_path; bool mtls; mtls = m_config.get_scalar("http_output.mtls", false); http_output.options["mtls"] = mtls ? std::string("true") : std::string("false"); std::string client_cert; client_cert = m_config.get_scalar("http_output.client_cert", "/etc/ssl/certs/client.crt"); http_output.options["client_cert"] = client_cert; std::string client_key; client_key = m_config.get_scalar("http_output.client_key", "/etc/ssl/certs/client.key"); http_output.options["client_key"] = client_key; bool compress_uploads; compress_uploads = m_config.get_scalar("http_output.compress_uploads", false); http_output.options["compress_uploads"] = compress_uploads ? std::string("true") : std::string("false"); bool keep_alive; keep_alive = m_config.get_scalar("http_output.keep_alive", false); http_output.options["keep_alive"] = keep_alive ? std::string("true") : std::string("false"); uint8_t max_consecutive_timeouts; max_consecutive_timeouts = m_config.get_scalar("http_output.max_consecutive_timeouts", 5); http_output.options["max_consecutive_timeouts"] = std::to_string(max_consecutive_timeouts); m_outputs.push_back(http_output); } m_output_timeout = m_config.get_scalar("output_timeout", 2000); std::string rule_matching = m_config.get_scalar("rule_matching", "first"); if(!falco_common::parse_rule_matching(rule_matching, m_rule_matching)) { throw std::logic_error("Unknown rule matching strategy \"" + rule_matching + "\"--must be one of first, all"); } std::string priority = m_config.get_scalar("priority", "debug"); if(!falco_common::parse_priority(priority, m_min_priority)) { throw std::logic_error("Unknown priority \"" + priority + "\"--must be one of emergency, alert, critical, error, warning, " "notice, informational, debug"); } m_buffered_outputs = m_config.get_scalar("buffered_outputs", false); m_outputs_queue_capacity = m_config.get_scalar("outputs_queue.capacity", DEFAULT_OUTPUTS_QUEUE_CAPACITY_UNBOUNDED_MAX_LONG_VALUE); // We use 0 in falco.yaml to indicate an unbounded queue; equivalent to the largest long value if(m_outputs_queue_capacity == 0) { m_outputs_queue_capacity = DEFAULT_OUTPUTS_QUEUE_CAPACITY_UNBOUNDED_MAX_LONG_VALUE; } m_time_format_iso_8601 = m_config.get_scalar("time_format_iso_8601", false); m_buffer_format_base64 = m_config.get_scalar("buffer_format_base64", false); m_webserver_enabled = m_config.get_scalar("webserver.enabled", false); m_webserver_config.m_threadiness = m_config.get_scalar("webserver.threadiness", 0); m_webserver_config.m_listen_port = m_config.get_scalar("webserver.listen_port", 8765); m_webserver_config.m_listen_address = m_config.get_scalar("webserver.listen_address", "0.0.0.0"); if(!re2::RE2::FullMatch(m_webserver_config.m_listen_address, ip_address_re)) { throw std::logic_error( "Error reading config file (" + config_name + "): webserver listen address \"" + m_webserver_config.m_listen_address + "\" is not a valid IP address"); } m_webserver_config.m_k8s_healthz_endpoint = m_config.get_scalar("webserver.k8s_healthz_endpoint", "/healthz"); m_webserver_config.m_ssl_enabled = m_config.get_scalar("webserver.ssl_enabled", false); m_webserver_config.m_ssl_certificate = m_config.get_scalar("webserver.ssl_certificate", "/etc/falco/falco.pem"); if(m_webserver_config.m_threadiness == 0) { m_webserver_config.m_threadiness = falco::utils::hardware_concurrency(); } m_webserver_config.m_prometheus_metrics_enabled = m_config.get_scalar("webserver.prometheus_metrics_enabled", false); std::list syscall_event_drop_acts; m_config.get_sequence(syscall_event_drop_acts, "syscall_event_drops.actions"); m_syscall_evt_drop_actions.clear(); for(const std::string &act : syscall_event_drop_acts) { if(act == "ignore") { m_syscall_evt_drop_actions.insert(syscall_evt_drop_action::DISREGARD); } else if(act == "log") { if(m_syscall_evt_drop_actions.count(syscall_evt_drop_action::DISREGARD)) { throw std::logic_error("Error reading config file (" + config_name + "): syscall event drop action \"" + act + "\" does not make sense with the \"ignore\" action"); } m_syscall_evt_drop_actions.insert(syscall_evt_drop_action::LOG); } else if(act == "alert") { if(m_syscall_evt_drop_actions.count(syscall_evt_drop_action::DISREGARD)) { throw std::logic_error("Error reading config file (" + config_name + "): syscall event drop action \"" + act + "\" does not make sense with the \"ignore\" action"); } m_syscall_evt_drop_actions.insert(syscall_evt_drop_action::ALERT); } else if(act == "exit") { m_syscall_evt_drop_actions.insert(syscall_evt_drop_action::EXIT); } else { throw std::logic_error("Error reading config file (" + config_name + "): available actions for syscall event drops are \"ignore\", " "\"log\", \"alert\", and \"exit\""); } } if(m_syscall_evt_drop_actions.empty()) { m_syscall_evt_drop_actions.insert(syscall_evt_drop_action::DISREGARD); } m_syscall_evt_drop_threshold = m_config.get_scalar("syscall_event_drops.threshold", .1); if(m_syscall_evt_drop_threshold < 0 || m_syscall_evt_drop_threshold > 1) { throw std::logic_error( "Error reading config file (" + config_name + "): syscall event drops threshold must be a double in the range [0, 1]"); } m_syscall_evt_drop_rate = m_config.get_scalar("syscall_event_drops.rate", .03333); m_syscall_evt_drop_max_burst = m_config.get_scalar("syscall_event_drops.max_burst", 1); m_syscall_evt_simulate_drops = m_config.get_scalar("syscall_event_drops.simulate_drops", false); m_syscall_evt_timeout_max_consecutives = m_config.get_scalar("syscall_event_timeouts.max_consecutives", 1000); if(m_syscall_evt_timeout_max_consecutives == 0) { throw std::logic_error("Error reading config file(" + config_name + "): the maximum consecutive timeouts without an event must be an " "unsigned integer > 0"); } m_falco_libs_thread_table_size = m_config.get_scalar("falco_libs.thread_table_size", DEFAULT_FALCO_LIBS_THREAD_TABLE_SIZE); m_falco_libs_thread_table_auto_purging_interval_s = m_config.get_scalar( "falco_libs.thread_table_auto_purging_interval_s", DEFAULT_FALCO_LIBS_THREAD_TABLE_AUTO_PURGING_INTERVAL_S); m_falco_libs_thread_table_auto_purging_thread_timeout_s = m_config.get_scalar( "falco_libs.thread_table_auto_purging_thread_timeout_s", DEFAULT_FALCO_LIBS_THREAD_TABLE_AUTO_PURGING_THREAD_TIMEOUT_S); // if falco_libs.snaplen is not set we'll let libs configure it m_falco_libs_snaplen = m_config.get_scalar("falco_libs.snaplen", 0); m_base_syscalls_custom_set.clear(); m_config.get_sequence>(m_base_syscalls_custom_set, std::string("base_syscalls.custom_set")); m_base_syscalls_repair = m_config.get_scalar("base_syscalls.repair", false); m_base_syscalls_all = m_config.get_scalar("base_syscalls.all", false); m_metrics_enabled = m_config.get_scalar("metrics.enabled", false); m_metrics_interval_str = m_config.get_scalar("metrics.interval", "5000"); m_metrics_interval = falco::utils::parse_prometheus_interval(m_metrics_interval_str); m_metrics_stats_rule_enabled = m_config.get_scalar("metrics.output_rule", false); m_metrics_output_file = m_config.get_scalar("metrics.output_file", ""); m_metrics_flags = 0; if(m_config.get_scalar("metrics.rules_counters_enabled", true)) { m_metrics_flags |= METRICS_V2_RULE_COUNTERS; } if(m_config.get_scalar("metrics.resource_utilization_enabled", true)) { m_metrics_flags |= METRICS_V2_RESOURCE_UTILIZATION; } if(m_config.get_scalar("metrics.state_counters_enabled", true)) { m_metrics_flags |= METRICS_V2_STATE_COUNTERS; } if(m_config.get_scalar("metrics.kernel_event_counters_enabled", true)) { m_metrics_flags |= METRICS_V2_KERNEL_COUNTERS; } if(m_config.get_scalar("metrics.kernel_event_counters_per_cpu_enabled", true)) { m_metrics_flags |= METRICS_V2_KERNEL_COUNTERS_PER_CPU; } if(m_config.get_scalar("metrics.libbpf_stats_enabled", true)) { m_metrics_flags |= METRICS_V2_LIBBPF_STATS; } if(m_config.get_scalar("metrics.plugins_metrics_enabled", true)) { m_metrics_flags |= METRICS_V2_PLUGINS; } if(m_config.get_scalar("metrics.jemalloc_stats_enabled", true)) { m_metrics_flags |= METRICS_V2_JEMALLOC_STATS; } m_metrics_convert_memory_to_mb = m_config.get_scalar("metrics.convert_memory_to_mb", true); m_metrics_include_empty_values = m_config.get_scalar("metrics.include_empty_values", false); m_capture_enabled = m_config.get_scalar("capture.enabled", false); m_capture_path_prefix = m_config.get_scalar("capture.path_prefix", "/tmp/falco"); // Set capture mode if not already set. const std::unordered_map capture_mode_lut = { {"rules", capture_mode_t::RULES}, {"all_rules", capture_mode_t::ALL_RULES}, }; auto capture_mode_str = m_config.get_scalar("capture.mode", "rules"); if(capture_mode_lut.find(capture_mode_str) != capture_mode_lut.end()) { m_capture_mode = capture_mode_lut.at(capture_mode_str); } else { throw std::logic_error("Error reading config file (" + config_name + "): capture.mode '" + capture_mode_str + "' is not a valid mode."); } // Convert to nanoseconds m_capture_default_duration_ns = m_config.get_scalar("capture.default_duration", 5000) * 1000000LL; m_plugins_hostinfo = m_config.get_scalar("plugins_hostinfo", true); m_config.get_sequence>(m_rules_selection, "rules"); m_config.get_sequence>(m_append_output, "append_output"); // check if append_output matching conditions are sane, if not emit a warning for(auto const &entry : m_append_output) { if(entry.m_rule != "" && entry.m_tags.size() > 0) { std::string tag_list; for(auto const &tag : entry.m_tags) { tag_list += tag; tag_list += ", "; } tag_list.pop_back(); falco_logger::log(falco_logger::level::WARNING, "An append_ouptut entry specifies both a rule (" + entry.m_rule + ") and a list of tags (" + tag_list + std::string("). ") + "This means that output will be appended only to the " + entry.m_rule + " rule and only if it has " + "all the tags: " + tag_list + "."); } } m_static_fields = m_config.get_scalar>("static_fields", {}); std::vector load_plugins; bool load_plugins_node_defined = m_config.is_defined("load_plugins"); m_config.get_sequence>(load_plugins, "load_plugins"); std::list plugins; try { if(m_config.is_defined("plugins")) { m_config.get_sequence>( plugins, std::string("plugins")); } } catch(std::exception &e) { // Might be thrown due to not being able to open files throw std::logic_error("Error reading config file(" + config_name + "): could not load plugins config: " + e.what()); } // If load_plugins was specified, only save plugins matching those in values m_plugins.clear(); if(!load_plugins_node_defined) { // If load_plugins was not specified at all, every plugin is added. // The loading order is the same as the sequence in the YAML m_config. m_plugins = {plugins.begin(), plugins.end()}; } else { // If load_plugins is specified, only plugins contained in its list // are added, with the same order as in the list. for(const auto &pname : load_plugins) { bool found = false; for(const auto &p : plugins) { if(pname == p.m_name) { m_plugins.push_back(p); found = true; break; } } if(!found) { throw std::logic_error("Cannot load plugin '" + pname + "': plugin config not found for given name"); } } } m_watch_config_files = m_config.get_scalar("watch_config_files", true); } void falco_configuration::read_rules_file_directory(const std::string &path, std::list &rules_filenames, std::list &rules_folders) { fs::path rules_path = std::string(path); if(fs::is_directory(rules_path)) { rules_folders.push_back(path); // It's a directory. Read the contents, sort // alphabetically, and add every path to // rules_filenames std::vector dir_filenames; const auto it_options = fs::directory_options::follow_directory_symlink | fs::directory_options::skip_permission_denied; for(auto const &dir_entry : fs::directory_iterator(rules_path, it_options)) { if(std::filesystem::is_regular_file(dir_entry.path())) { dir_filenames.push_back(dir_entry.path().string()); } } std::sort(dir_filenames.begin(), dir_filenames.end()); for(const std::string &ent : dir_filenames) { // only consider yaml files if(falco::utils::matches_wildcard("*.yaml", ent) || falco::utils::matches_wildcard("*.yml", ent)) { rules_filenames.push_back(ent); } } } else { // Assume it's a file and just add to // rules_filenames. If it can't be opened/etc that // will be reported later.. // also, only consider yaml files if(falco::utils::matches_wildcard("*.yaml", path) || falco::utils::matches_wildcard("*.yml", path)) { rules_filenames.push_back(path); } } } static bool split(const std::string &str, char delim, std::pair &parts) { size_t pos; if((pos = str.find_first_of(delim)) == std::string::npos) { return false; } parts.first = str.substr(0, pos); parts.second = str.substr(pos + 1); return true; } void falco_configuration::load_cmdline_config_files( const std::vector &cmdline_options) { for(const std::string &option : cmdline_options) { // Set all config_files options if(option.rfind(yaml_helper::configs_key, 0) == 0) { set_cmdline_option(option); } } } void falco_configuration::init_cmdline_options(const std::vector &cmdline_options) { for(const std::string &option : cmdline_options) { set_cmdline_option(option); } } void falco_configuration::set_cmdline_option(const std::string &opt) { std::pair keyval; if(!split(opt, '=', keyval)) { throw std::logic_error("Error parsing config option \"" + opt + "\". Must be of the form key=val or key.subkey=val"); } if(keyval.second[0] == '{' && keyval.second[keyval.second.size() - 1] == '}') { YAML::Node node = YAML::Load(keyval.second); m_config.set_object(keyval.first, node); } else { m_config.set_scalar(keyval.first, keyval.second); } } ================================================ FILE: userspace/falco/configuration.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2025 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #include #include #ifdef _WIN32 #include #else #include #endif #include #include #include #include #include #include #include #include "config_falco.h" #include "yaml_helper.h" #include "event_drops.h" #include "falco_outputs.h" // Falco only metric #define METRICS_V2_JEMALLOC_STATS 1 << 31 enum class engine_kind_t : uint8_t { KMOD, MODERN_EBPF, REPLAY, NODRIVER }; enum class capture_mode_t : uint8_t { RULES, ALL_RULES }; // Map that holds { config filename | validation status } for each loaded config file. typedef std::map config_loaded_res; class falco_configuration { public: struct plugin_config { std::string m_name; std::string m_library_path; std::string m_init_config; std::string m_open_params; }; struct config_files_config { std::string m_path; yaml_helper::config_files_strategy m_strategy; }; struct kmod_config { int16_t m_buf_size_preset; bool m_drop_failed_exit; }; struct modern_ebpf_config { uint16_t m_cpus_for_each_buffer; int16_t m_buf_size_preset; bool m_drop_failed_exit; }; struct replay_config { std::string m_capture_file; }; struct webserver_config { uint32_t m_threadiness = 0; uint32_t m_listen_port = 8765; std::string m_listen_address = "0.0.0.0"; std::string m_k8s_healthz_endpoint = "/healthz"; bool m_ssl_enabled = false; std::string m_ssl_certificate; bool m_prometheus_metrics_enabled = false; }; enum class rule_selection_operation { enable, disable }; struct rule_selection_config { rule_selection_operation m_op; std::string m_tag; std::string m_rule; }; struct append_output_config { std::string m_source; std::set m_tags; std::string m_rule; std::string m_format; bool m_suggested_output = false; std::unordered_map m_formatted_fields; std::set m_raw_fields; }; falco_configuration(); virtual ~falco_configuration() = default; config_loaded_res init_from_file(const std::string& conf_filename, const std::vector& cmdline_options); config_loaded_res init_from_content(const std::string& config_content, const std::vector& cmdline_options, const std::string& filename = "default"); std::string dump(); static void read_rules_file_directory(const std::string& path, std::list& rules_filenames, std::list& rules_folders); // Config list as passed by the user. Filenames. std::list m_loaded_configs_filenames; // Map with filenames and their sha256 of the loaded configs files std::unordered_map m_loaded_configs_filenames_sha256sum; // Config list as passed by the user. Folders. std::list m_loaded_configs_folders; // Rules list as passed by the user std::list m_rules_filenames; // Actually loaded rules, with folders inspected std::list m_loaded_rules_filenames; // Map with filenames and their sha256 of the loaded rules files std::unordered_map m_loaded_rules_filenames_sha256sum; // List of loaded rule folders std::list m_loaded_rules_folders; // Rule selection options passed by the user std::vector m_rules_selection; // Append output configuration passed by the user std::vector m_append_output; // Static fields configuration passed by the user std::map m_static_fields; bool m_json_output; bool m_json_include_output_property; bool m_json_include_tags_property; bool m_json_include_message_property; bool m_json_include_output_fields_property; std::string m_log_level; std::vector m_outputs; falco_common::priority_type m_min_priority; falco_common::rule_matching m_rule_matching; bool m_watch_config_files; bool m_buffered_outputs; size_t m_outputs_queue_capacity; bool m_time_format_iso_8601; bool m_buffer_format_base64; uint32_t m_output_timeout; bool m_webserver_enabled; webserver_config m_webserver_config; syscall_evt_drop_actions m_syscall_evt_drop_actions; double m_syscall_evt_drop_threshold; double m_syscall_evt_drop_rate; double m_syscall_evt_drop_max_burst; // Only used for testing bool m_syscall_evt_simulate_drops; uint32_t m_syscall_evt_timeout_max_consecutives; uint32_t m_falco_libs_thread_table_size; uint32_t m_falco_libs_thread_table_auto_purging_interval_s; uint32_t m_falco_libs_thread_table_auto_purging_thread_timeout_s; uint64_t m_falco_libs_snaplen; // User supplied base_syscalls, overrides any Falco state engine enforcement. std::unordered_set m_base_syscalls_custom_set; bool m_base_syscalls_all; bool m_base_syscalls_repair; // metrics configs bool m_metrics_enabled; std::string m_metrics_interval_str; uint64_t m_metrics_interval; bool m_metrics_stats_rule_enabled; std::string m_metrics_output_file; uint32_t m_metrics_flags; bool m_metrics_convert_memory_to_mb; bool m_metrics_include_empty_values; std::vector m_plugins; bool m_plugins_hostinfo; // capture configs bool m_capture_enabled; std::string m_capture_path_prefix; capture_mode_t m_capture_mode = capture_mode_t::RULES; uint64_t m_capture_default_duration_ns; // Falco engine engine_kind_t m_engine_mode = engine_kind_t::KMOD; kmod_config m_kmod = {}; modern_ebpf_config m_modern_ebpf = {}; replay_config m_replay = {}; yaml_helper m_config; // // Runtime-Generated values (not user-configurable) // // JSON schema generated from a hardcoded string nlohmann::json m_config_schema; // Timestamp of most recent configuration reload int64_t m_falco_reload_ts{0}; private: void merge_config_files(const std::string& config_name, config_loaded_res& res); void load_yaml(const std::string& config_name); void init_logger(); void load_engine_config(const std::string& config_name); void init_cmdline_options(const std::vector& cmdline_options); void load_cmdline_config_files(const std::vector& cmdline_options); /** * Given a = specifier, set the appropriate option * in the underlying yaml config. can contain '.' * characters for nesting. Currently only 1- or 2- level keys * are supported and only scalar values are supported. */ void set_cmdline_option(const std::string& spec); }; namespace YAML { template<> struct convert { static bool decode(const Node& node, falco_configuration::append_output_config& rhs) { if(!node.IsMap()) { return false; } if(node["match"]) { auto& match = node["match"]; if(match["source"]) { rhs.m_source = match["source"].as(); } if(match["tags"] && match["tags"].IsSequence()) { for(auto& tag : match["tags"]) { if(!tag.IsScalar()) { return false; } rhs.m_tags.insert(tag.as()); } } if(match["rule"]) { rhs.m_rule = match["rule"].as(); } } if(node["extra_output"]) { rhs.m_format = node["extra_output"].as(); } if(node["extra_fields"]) { if(!node["extra_fields"].IsSequence()) { return false; } for(auto& field_definition : node["extra_fields"]) { if(field_definition.IsMap() && field_definition.size() == 1) { YAML::const_iterator def = field_definition.begin(); std::string key = def->first.as(); // it is an error to redefine an existing key if(rhs.m_formatted_fields.count(key) != 0 || rhs.m_raw_fields.count(key) != 0) { return false; } rhs.m_formatted_fields[key] = def->second.as(); } else if(field_definition.IsScalar()) { std::string key = field_definition.as(); // it is an error to redefine an existing key if(rhs.m_formatted_fields.count(key) != 0) { return false; } rhs.m_raw_fields.insert(key); } else { return false; } } } if(node["suggested_output"]) { rhs.m_suggested_output = node["suggested_output"].as(); } return true; } }; template<> struct convert { static Node encode(const falco_configuration::rule_selection_config& rhs) { Node node; Node subnode; if(rhs.m_rule != "") { subnode["rule"] = rhs.m_rule; } if(rhs.m_tag != "") { subnode["tag"] = rhs.m_tag; } if(rhs.m_op == falco_configuration::rule_selection_operation::enable) { node["enable"] = subnode; } else if(rhs.m_op == falco_configuration::rule_selection_operation::disable) { node["disable"] = subnode; } return node; } static bool decode(const Node& node, falco_configuration::rule_selection_config& rhs) { if(!node.IsMap()) { return false; } if(node["enable"]) { rhs.m_op = falco_configuration::rule_selection_operation::enable; const Node& enable = node["enable"]; if(!enable.IsMap()) { return false; } if(enable["rule"]) { rhs.m_rule = enable["rule"].as(); } if(enable["tag"]) { rhs.m_tag = enable["tag"].as(); } } else if(node["disable"]) { rhs.m_op = falco_configuration::rule_selection_operation::disable; const Node& disable = node["disable"]; if(!disable.IsMap()) { return false; } if(disable["rule"]) { rhs.m_rule = disable["rule"].as(); } if(disable["tag"]) { rhs.m_tag = disable["tag"].as(); } } else { return false; } if(rhs.m_rule == "" && rhs.m_tag == "") { return false; } return true; } }; template<> struct convert { // Note that this loses the distinction between init configs // defined as YAML maps or as opaque strings. static Node encode(const falco_configuration::plugin_config& rhs) { Node node; node["name"] = rhs.m_name; node["library_path"] = rhs.m_library_path; node["init_config"] = rhs.m_init_config; node["open_params"] = rhs.m_open_params; return node; } static bool decode(const Node& node, falco_configuration::plugin_config& rhs) { if(!node.IsMap()) { return false; } if(!node["name"]) { return false; } rhs.m_name = node["name"].as(); if(!node["library_path"]) { return false; } rhs.m_library_path = node["library_path"].as(); if(!rhs.m_library_path.empty() && rhs.m_library_path.at(0) != '/') { // prepend share dir if path is not absolute rhs.m_library_path = std::string(FALCO_ENGINE_PLUGINS_DIR) + rhs.m_library_path; } if(node["init_config"] && !node["init_config"].IsNull()) { // By convention, if the init config is a YAML map we convert it // in a JSON object string. This is useful for plugins implementing // the `get_init_schema` API symbol, which right now support the // JSON Schema specific. If we ever support other schema/data types, // we may want to bundle the conversion logic in an ad-hoc class. // The benefit of this is being able of parsing/editing the config as // a YAML map instead of having an opaque string. if(node["init_config"].IsMap()) { nlohmann::json json; YAML::convert::decode(node["init_config"], json); rhs.m_init_config = json.dump(); } else { rhs.m_init_config = node["init_config"].as(); } } if(node["open_params"] && !node["open_params"].IsNull()) { std::string open_params = node["open_params"].as(); rhs.m_open_params = trim(open_params); } return true; } }; template<> struct convert { static Node encode(const falco_configuration::config_files_config& rhs) { Node node; node["path"] = rhs.m_path; node["strategy"] = yaml_helper::strategy_to_string(rhs.m_strategy); return node; } static bool decode(const Node& node, falco_configuration::config_files_config& rhs) { if(!node.IsMap()) { // Single string mode defaults to append strategy rhs.m_path = node.as(); rhs.m_strategy = yaml_helper::STRATEGY_APPEND; return true; } // Path is required if(!node["path"]) { return false; } rhs.m_path = node["path"].as(); // Strategy is not required if(!node["strategy"]) { rhs.m_strategy = yaml_helper::STRATEGY_APPEND; } else { std::string strategy = node["strategy"].as(); rhs.m_strategy = yaml_helper::strategy_from_string(strategy); } return true; } }; } // namespace YAML ================================================ FILE: userspace/falco/event_drops.cpp ================================================ // 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 "event_drops.h" #include "falco_common.h" syscall_evt_drop_mgr::syscall_evt_drop_mgr(): m_num_syscall_evt_drops(0), m_num_actions(0), m_inspector(NULL), m_outputs(NULL), m_next_check_ts(0), m_simulate_drops(false), m_threshold(0) {} syscall_evt_drop_mgr::~syscall_evt_drop_mgr() {} void syscall_evt_drop_mgr::init(std::shared_ptr inspector, std::shared_ptr outputs, const syscall_evt_drop_actions &actions, double threshold, double rate, double max_tokens, bool simulate_drops) { m_inspector = inspector; m_outputs = outputs; m_actions = actions; m_bucket.init(rate, max_tokens); m_threshold = threshold; m_inspector->get_capture_stats(&m_last_stats); m_simulate_drops = simulate_drops; if(m_simulate_drops) { m_threshold = 0; } } bool syscall_evt_drop_mgr::process_event(std::shared_ptr inspector, sinsp_evt *evt) { if(m_next_check_ts == 0) { m_next_check_ts = evt->get_ts() + ONE_SECOND_IN_NS; } if(m_next_check_ts < evt->get_ts()) { scap_stats stats, delta; m_next_check_ts = evt->get_ts() + ONE_SECOND_IN_NS; m_inspector->get_capture_stats(&stats); delta.n_evts = stats.n_evts - m_last_stats.n_evts; delta.n_drops = stats.n_drops - m_last_stats.n_drops; delta.n_drops_buffer = stats.n_drops_buffer - m_last_stats.n_drops_buffer; delta.n_drops_buffer_clone_fork_exit = stats.n_drops_buffer_clone_fork_exit - m_last_stats.n_drops_buffer_clone_fork_exit; delta.n_drops_buffer_execve_exit = stats.n_drops_buffer_execve_exit - m_last_stats.n_drops_buffer_execve_exit; delta.n_drops_buffer_connect_exit = stats.n_drops_buffer_connect_exit - m_last_stats.n_drops_buffer_connect_exit; delta.n_drops_buffer_open_exit = stats.n_drops_buffer_open_exit - m_last_stats.n_drops_buffer_open_exit; delta.n_drops_buffer_dir_file_exit = stats.n_drops_buffer_dir_file_exit - m_last_stats.n_drops_buffer_dir_file_exit; delta.n_drops_buffer_other_interest_exit = stats.n_drops_buffer_other_interest_exit - m_last_stats.n_drops_buffer_other_interest_exit; delta.n_drops_buffer_close_exit = stats.n_drops_buffer_close_exit - m_last_stats.n_drops_buffer_close_exit; delta.n_drops_buffer_proc_exit = stats.n_drops_buffer_proc_exit - m_last_stats.n_drops_buffer_proc_exit; delta.n_drops_scratch_map = stats.n_drops_scratch_map - m_last_stats.n_drops_scratch_map; delta.n_drops_pf = stats.n_drops_pf - m_last_stats.n_drops_pf; delta.n_drops_bug = stats.n_drops_bug - m_last_stats.n_drops_bug; delta.n_preemptions = stats.n_preemptions - m_last_stats.n_preemptions; delta.n_suppressed = stats.n_suppressed - m_last_stats.n_suppressed; delta.n_tids_suppressed = stats.n_tids_suppressed - m_last_stats.n_tids_suppressed; m_last_stats = stats; if(m_simulate_drops) { falco_logger::log(falco_logger::level::INFO, "Simulating syscall event drop"); delta.n_drops++; } if(delta.n_drops > 0) { double ratio = delta.n_drops; // The `n_evts` always contains the `n_drops`. ratio /= delta.n_evts; // When simulating drops the threshold is always zero if(ratio > m_threshold) { m_num_syscall_evt_drops++; // There were new drops in the last second. // If the token bucket allows, perform actions. if(m_bucket.claim(1, evt->get_ts())) { m_num_actions++; return perform_actions(evt->get_ts(), delta, inspector->check_current_engine(MODERN_BPF_ENGINE)); } else { falco_logger::log( falco_logger::level::DEBUG, "Syscall event drop but token bucket depleted, skipping actions"); } } } } return true; } void syscall_evt_drop_mgr::print_stats() { fprintf(stderr, "Syscall event drop monitoring:\n"); fprintf(stderr, " - event drop detected: %lu occurrences\n", m_num_syscall_evt_drops); fprintf(stderr, " - num times actions taken: %lu\n", m_num_actions); } bool syscall_evt_drop_mgr::perform_actions(uint64_t now, const scap_stats &delta, bool bpf_enabled) { std::string rule = "Falco internal: syscall event drop"; std::string msg = rule + ". " + std::to_string(delta.n_drops) + " system calls dropped in last second."; bool ret = true; for(auto &act : m_actions) { switch(act) { case syscall_evt_drop_action::DISREGARD: continue; case syscall_evt_drop_action::LOG: falco_logger::log(falco_logger::level::DEBUG, std::move(msg)); continue; case syscall_evt_drop_action::ALERT: { nlohmann::json output_fields; output_fields["n_evts"] = std::to_string(delta.n_evts); /* Total number of kernel side events actively traced (not including events discarded due to simple consumer mode in eBPF case). */ output_fields["n_drops"] = std::to_string( delta.n_drops); /* Number of all kernel side event drops out of n_evts. */ output_fields["n_drops_buffer_total"] = std::to_string( delta.n_drops_buffer); /* Total number of kernel side drops due to full buffer, includes all categories below, likely higher than sum of syscall categories. */ /* Kernel side drops due to full buffer for categories of system calls. Not all system * calls of interest are mapped into one of the categories. Insights: (1) Identify * statistical properties of workloads (e.g. ratios between categories). (2) Data-driven * optimization opportunity for kernel side filtering and prioritization. (3) Response: * Coarse grained insights into syscalls dropped. (4) Bonus: Cost associated with * syscall category (typically `open` system call category is highest by orders of * magnitude). */ output_fields["n_drops_buffer_clone_fork_exit"] = std::to_string(delta.n_drops_buffer_clone_fork_exit); output_fields["n_drops_buffer_execve_exit"] = std::to_string(delta.n_drops_buffer_execve_exit); output_fields["n_drops_buffer_connect_exit"] = std::to_string(delta.n_drops_buffer_connect_exit); output_fields["n_drops_buffer_open_exit"] = std::to_string(delta.n_drops_buffer_open_exit); output_fields["n_drops_buffer_dir_file_exit"] = std::to_string(delta.n_drops_buffer_dir_file_exit); /* `n_drops_buffer_other_interest_*` Category consisting of other system calls of * interest, not all other system calls that did not match a category from above. Ideal * for a custom category if needed - simply patch switch statement in kernel driver code * (`falcosecurity/libs` repo). */ output_fields["n_drops_buffer_other_interest_exit"] = std::to_string(delta.n_drops_buffer_other_interest_exit); output_fields["n_drops_buffer_close_exit"] = std::to_string(delta.n_drops_buffer_close_exit); output_fields["n_drops_buffer_proc_exit"] = std::to_string(delta.n_drops_buffer_proc_exit); output_fields["n_drops_scratch_map"] = std::to_string( delta.n_drops_scratch_map); /* Number of kernel side scratch map drops. */ output_fields["n_drops_page_faults"] = std::to_string(delta.n_drops_pf); /* Number of kernel side page faults drops (invalid memory access). */ output_fields["n_drops_bug"] = std::to_string( delta.n_drops_bug); /* Number of kernel side bug drops (invalid condition in the kernel instrumentation). */ output_fields["ebpf_enabled"] = std::to_string(bpf_enabled); m_outputs->handle_msg(now, falco_common::PRIORITY_DEBUG, msg, rule, output_fields); continue; } case syscall_evt_drop_action::EXIT: falco_logger::log(falco_logger::level::CRIT, std::move(msg)); falco_logger::log(falco_logger::level::CRIT, "Exiting."); ret = false; continue; default: falco_logger::log(falco_logger::level::ERR, "Ignoring unknown action " + std::to_string(int(act))); continue; } } return ret; } ================================================ FILE: userspace/falco/event_drops.h ================================================ // 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. */ #pragma once #include #include #include #include #include "logger.h" #include "falco_outputs.h" // The possible actions that this class can take upon // detecting a syscall event drop. enum class syscall_evt_drop_action : uint8_t { DISREGARD = 0, LOG, ALERT, EXIT }; using syscall_evt_drop_actions = std::unordered_set; class syscall_evt_drop_mgr { public: syscall_evt_drop_mgr(); virtual ~syscall_evt_drop_mgr(); void init(std::shared_ptr inspector, std::shared_ptr outputs, const syscall_evt_drop_actions &actions, double threshold, double rate, double max_tokens, bool simulate_drops); // Call this for every event. The class will take care of // periodically measuring the scap stats, looking for syscall // event drops, and performing any actions. // // Returns whether event processing should continue or stop (with an error). bool process_event(std::shared_ptr inspector, sinsp_evt *evt); void print_stats(); protected: // Perform all configured actions. bool perform_actions(uint64_t now, const scap_stats &delta, bool bpf_enabled); uint64_t m_num_syscall_evt_drops; uint64_t m_num_actions; std::shared_ptr m_inspector; std::shared_ptr m_outputs; syscall_evt_drop_actions m_actions; token_bucket m_bucket; uint64_t m_next_check_ts; scap_stats m_last_stats; bool m_simulate_drops; double m_threshold; }; ================================================ FILE: userspace/falco/falco.cpp ================================================ // 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 #include #include #include "app/app.h" #include "logger.h" static void display_fatal_err(const std::string &&msg) { /** * If stderr logging is not enabled, also log to stderr. */ if(!falco_logger::log_stderr) { std::cerr << msg; } falco_logger::log(falco_logger::level::ERR, std::move(msg)); } // // ARGUMENT PARSING AND PROGRAM SETUP // int falco_run(int argc, char **argv, bool &restart) { restart = false; std::string errstr; try { if(!falco::app::run(argc, argv, restart, errstr)) { fprintf(stderr, "Error: %s\n", errstr.c_str()); return EXIT_FAILURE; } } catch(std::exception &e) { display_fatal_err("Runtime error: " + std::string(e.what()) + ". Exiting.\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } // // MAIN // int main(int argc, char **argv) { int rc; bool restart; // Generally falco exits when falco_run returns with the rc // returned by falco_run. However, when restart (set by // signal handlers, returned in application::run()) is true, // falco_run() is called again. while((rc = falco_run(argc, argv, restart)) == EXIT_SUCCESS && restart) { } return rc; } ================================================ FILE: userspace/falco/falco_metrics.cpp ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 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 #include "falco_metrics.h" #include "app/state.h" #include #ifdef HAS_JEMALLOC #include #endif namespace fs = std::filesystem; /*! \class falco_metrics \brief Converts metrics provided by the application and Falco libraries into a formatted string for the metrics endpoint. ## Metrics Overview This section explains why looping over inspectors is necessary. Falco utilizes multiple inspectors when loading plugins with an event source. Most metrics should only be retrieved once, ideally by the syscalls inspector if applicable. To maximize metrics retrieval and prevent duplicate data, the syscalls inspector is always positioned at index 0 in the loop when it exists. Wrapper fields: See https://falco.org/docs/concepts/metrics/ - `engine_name` and `event_source` are pushed for each inspector. - All other wrapper fields are agnostic and should be retrieved once. ## Metrics Collection Behavior - `rules_counters_enabled` -> Agnostic; resides in falco; retrieved from the state, not an inspector; only performed once. - `resource_utilization_enabled` -> Agnostic; resides in libs; inspector is irrelevant; only performed once. - `state_counters_enabled` -> Semi-agnostic; resides in libs; must be retrieved by the syscalls inspector if applicable. - `kernel_event_counters_enabled` -> Resides in libs; must be retrieved by the syscalls inspector; not available for other inspectors. - `kernel_event_counters_per_cpu_enabled` -> Resides in libs; must be retrieved by the syscalls inspector; not available for other inspectors. - `libbpf_stats_enabled` -> Resides in libs; must be retrieved by the syscalls inspector; not available for other inspectors. - `plugins_metrics_enabled` -> Must be retrieved for each inspector. - `jemalloc_stats_enabled` -> Agnostic; resides in falco; inspector is irrelevant; only performed once. */ /*! \brief content_type to be returned by the webserver's metrics endpoint. Currently it is the default Prometheus exposition format https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format */ const std::string falco_metrics::content_type_prometheus = "text/plain; version=0.0.4"; // Helper function to convert metric to prometheus text with custom help text static std::string convert_metric_to_text_prometheus_with_deprecation_notice( libs::metrics::prometheus_metrics_converter& converter, const metrics_v2& metric, const std::string& prefix, const std::string& subsystem, const std::map& labels) { // First get the standard prometheus text std::string prometheus_text = converter.convert_metric_to_text_prometheus(metric, prefix, subsystem, labels); // Find the first occurrence of "# HELP" and append the deprecation notice size_t help_pos = prometheus_text.find("# HELP"); if(help_pos != std::string::npos) { // Find the end of the help line size_t help_end = prometheus_text.find('\n', help_pos); if(help_end != std::string::npos) { // Append (DEPRECATED: enter events are no longer tracked in falcosecurity/libs) to the // help text prometheus_text.insert( help_end, " (DEPRECATED: enter events are no longer tracked in falcosecurity/libs)"); } } return prometheus_text; } std::string falco_metrics::falco_to_text_prometheus( const falco::app::state& state, libs::metrics::prometheus_metrics_converter& prometheus_metrics_converter, std::vector& additional_wrapper_metrics) { std::string prometheus_text; // # HELP falcosecurity_falco_version_info https://falco.org/docs/metrics/ // # TYPE falcosecurity_falco_version_info gauge // falcosecurity_falco_version_info{version="0.41.0-100+334ca42"} 1 prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( "version", "falcosecurity", "falco", {{"version", FALCO_VERSION}}); #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) // Note that the rule counter metrics are retrieved from the state, not from any inspector // Distinguish between config and rules files using labels, following Prometheus best // practices: https://prometheus.io/docs/practices/naming/#labels // # HELP falcosecurity_falco_sha256_rules_files_info https://falco.org/docs/metrics/ // # TYPE falcosecurity_falco_sha256_rules_files_info gauge // falcosecurity_falco_sha256_rules_files_info{file_name="falco_rules.yaml",sha256="6f0078862a26528cb50a860f9ebebbfbe3162e5009187089c73cb0cdf91d0b06"} // 1 for(const auto& item : state.config.get()->m_loaded_rules_filenames_sha256sum) { fs::path fs_path = item.first; prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( "sha256_rules_files", "falcosecurity", "falco", {{"file_name", fs_path.filename()}, {"sha256", item.second}}); } // # HELP falcosecurity_falco_sha256_config_files_info https://falco.org/docs/metrics/ // # TYPE falcosecurity_falco_sha256_config_files_info gauge // falcosecurity_falco_sha256_config_files_info{file_name="falco.yaml",sha256="f97de5fa6f513b5e07cd9f29ee9904ee4267cb120ef6501f8555543d5a98dd1c"} // 1 for(const auto& item : state.config.get()->m_loaded_configs_filenames_sha256sum) { fs::path fs_path = item.first; prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( "sha256_config_files", "falcosecurity", "falco", {{"file_name", fs_path.filename()}, {"sha256", item.second}}); } #endif // # HELP falcosecurity_falco_outputs_queue_num_drops_total https://falco.org/docs/metrics/ // # TYPE falcosecurity_falco_outputs_queue_num_drops_total counter // falcosecurity_falco_outputs_queue_num_drops_total 0 if(state.outputs != nullptr) { additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric( "outputs_queue_num_drops", METRICS_V2_MISC, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, state.outputs->get_outputs_queue_num_drops())); } // # HELP falcosecurity_falco_reload_timestamp_nanoseconds https://falco.org/docs/metrics/ // # TYPE falcosecurity_falco_reload_timestamp_nanoseconds gauge // falcosecurity_falco_reload_timestamp_nanoseconds 1748338536592811359 additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric( "reload_ts", METRICS_V2_MISC, METRIC_VALUE_TYPE_S64, METRIC_VALUE_UNIT_TIME_TIMESTAMP_NS, METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT, state.config->m_falco_reload_ts)); if(state.config->m_metrics_flags & METRICS_V2_RULE_COUNTERS) { // rules_counters_enabled const stats_manager& rule_stats_manager = state.engine->get_rule_stats_manager(); const indexed_vector& rules = state.engine->get_rules(); const std::vector>>& rules_by_id = rule_stats_manager.get_by_rule_id(); // Distinguish between rules counters using labels, following Prometheus best // practices: https://prometheus.io/docs/practices/naming/#labels for(size_t i = 0; i < rules_by_id.size(); i++) { auto rule = rules.at(i); auto count = rules_by_id[i]->load(); if(count > 0) { // # HELP falcosecurity_falco_rules_matches_total https://falco.org/docs/metrics/ // # TYPE falcosecurity_falco_rules_matches_total counter // falcosecurity_falco_rules_matches_total{priority="4",rule_name="Read sensitive // file // untrusted",source="syscall",tag_T1555="true",tag_container="true",tag_filesystem="true",tag_host="true",tag_maturity_stable="true",tag_mitre_credential_access="true"} // 32 # HELP falcosecurity_falco_rules_matches_total https://falco.org/docs/metrics/ // # TYPE falcosecurity_falco_rules_matches_total counter // falcosecurity_falco_rules_matches_total{priority="5",rule_name="Terminal shell in // container",source="syscall",tag_T1059="true",tag_container="true",tag_maturity_stable="true",tag_mitre_execution="true",tag_shell="true"} // 1 auto metric = libs::metrics::libsinsp_metrics::new_metric( "rules_matches", METRICS_V2_RULE_COUNTERS, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, count); prometheus_metrics_converter.convert_metric_to_unit_convention(metric); std::map const_labels = { {"rule_name", rule->name}, {"priority", std::to_string(rule->priority)}, {"source", rule->source}, }; std::for_each(rule->tags.cbegin(), rule->tags.cend(), [&const_labels](std::string const& tag) { const_labels.emplace(std::string{"tag_"} + tag, "true"); }); prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( metric, "falcosecurity", "falco", const_labels); } } } #ifdef HAS_JEMALLOC if(state.config->m_metrics_flags & METRICS_V2_JEMALLOC_STATS) { // jemalloc_stats_enabled nlohmann::json j; malloc_stats_print( [](void* to, const char* from) { nlohmann::json* j = (nlohmann::json*)to; *j = nlohmann::json::parse(from); }, &j, "Jmdablxeg"); const auto& j_stats = j["jemalloc"]["stats"]; for(auto it = j_stats.begin(); it != j_stats.end(); ++it) { if(it.value().is_number_unsigned()) { std::uint64_t val = it.value().template get(); std::string key = "jemalloc." + it.key(); auto metric = libs::metrics::libsinsp_metrics::new_metric( key.c_str(), METRICS_V2_JEMALLOC_STATS, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_MEMORY_BYTES, METRIC_VALUE_METRIC_TYPE_MONOTONIC, val); prometheus_metrics_converter.convert_metric_to_unit_convention(metric); prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( metric, "falcosecurity", "falco"); } } } #endif return prometheus_text; } std::string falco_metrics::sources_to_text_prometheus( const falco::app::state& state, libs::metrics::prometheus_metrics_converter& prometheus_metrics_converter, std::vector& additional_wrapper_metrics) { static const char* all_driver_engines[] = {KMOD_ENGINE, MODERN_BPF_ENGINE, SOURCE_PLUGIN_ENGINE, NODRIVER_ENGINE}; static re2::RE2 drops_buffer_pattern("n_drops_buffer_([^_]+(?:_[^_]+)*)_exit$"); static re2::RE2 cpu_pattern("(\\d+)"); std::string prometheus_text; bool agent_info_written = false; bool machine_info_written = false; // Then, source-bound metrics for(const auto& source : state.enabled_sources) { auto source_info = state.source_infos.at(source); auto source_inspector = source_info->inspector; // First thing: list of enabled engine names // Falco wrapper metrics Part A: Repeated for each inspector, accounting for plugins w/ // event sources /* Examples ... # HELP falcosecurity_scap_engine_name_info https://falco.org/docs/metrics/ # TYPE falcosecurity_scap_engine_name_info gauge falcosecurity_scap_engine_name_info{engine_name="source_plugin",evt_source="dummy"} 1 # HELP falcosecurity_scap_engine_name_info https://falco.org/docs/metrics/ # TYPE falcosecurity_scap_engine_name_info gauge falcosecurity_scap_engine_name_info{engine_name="bpf",evt_source="syscall"} 1 */ for(size_t j = 0; j < sizeof(all_driver_engines) / sizeof(const char*); j++) { if(source_inspector->check_current_engine(all_driver_engines[j])) { prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( "engine_name", "falcosecurity", "scap", {{"engine_name", std::string(all_driver_engines[j])}, {"evt_source", source}}); break; } } // Inspectors' metrics collectors // Libs metrics categories // // resource_utilization_enabled // state_counters_enabled // kernel_event_counters_enabled // kernel_event_counters_per_cpu_enabled // libbpf_stats_enabled auto metrics_collector = libs::metrics::libs_metrics_collector(source_inspector.get(), state.config->m_metrics_flags); metrics_collector.snapshot(); auto metrics_snapshot = metrics_collector.get_metrics(); // Source plugin if(source != falco_common::syscall_source) { // Performed repeatedly for each inspectors' libs metrics collector for(auto& metric : metrics_snapshot) { if(metric.flags & METRICS_V2_PLUGINS) { prometheus_metrics_converter.convert_metric_to_unit_convention(metric); prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( metric, "falcosecurity", "plugins"); } } } else { // Source syscall for(auto& metric : metrics_snapshot) { prometheus_metrics_converter.convert_metric_to_unit_convention(metric); std::string prometheus_subsystem = "scap"; if(metric.flags & METRICS_V2_RESOURCE_UTILIZATION) { prometheus_subsystem = "falco"; } if(metric.flags & METRICS_V2_PLUGINS) { prometheus_subsystem = "plugins"; } // raw incoming in form of for example n_evts_cpu_15 or n_drops_cpu_15 if(strncmp(metric.name, "n_evts_cpu", 10) == 0 || strncmp(metric.name, "n_drops_cpu", 11) == 0) // prefix match { std::string name_str(metric.name); std::string cpu_number; if(re2::RE2::PartialMatch(name_str, cpu_pattern, &cpu_number)) { re2::RE2::GlobalReplace(&name_str, cpu_pattern, ""); // possible double __ will be sanitized within libs auto metric_new = libs::metrics::libsinsp_metrics::new_metric( name_str.c_str(), METRICS_V2_KERNEL_COUNTERS_PER_CPU, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, metric.value.u64); const std::map& const_labels = { {"cpu", cpu_number}}; /* Examples ... # HELP falcosecurity_scap_n_evts_cpu_total https://falco.org/docs/metrics/ # TYPE falcosecurity_scap_n_evts_cpu_total counter falcosecurity_scap_n_evts_cpu_total{cpu="7"} 237 # HELP falcosecurity_scap_n_drops_cpu_total https://falco.org/docs/metrics/ # TYPE falcosecurity_scap_n_drops_cpu_total counter falcosecurity_scap_n_drops_cpu_total{cpu="7"} 0 */ prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( metric_new, "falcosecurity", prometheus_subsystem, const_labels); } } else if(strcmp(metric.name, "n_drops_buffer_total") == 0) { // Skip the libs aggregate metric since we distinguish between buffer drops // using labels similar to the rules_matches continue; } else if(strncmp(metric.name, "n_drops_buffer", 14) == 0) // prefix match { std::string drop; std::string name_str(metric.name); if(re2::RE2::FullMatch(name_str, drops_buffer_pattern, &drop)) { auto metric_new = libs::metrics::libsinsp_metrics::new_metric( "n_drops_buffer", METRICS_V2_KERNEL_COUNTERS, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, metric.value.u64); const std::map& const_labels = {{"drop", drop}, {"dir", "exit"}}; /* Examples ... # HELP falcosecurity_scap_n_drops_buffer_total https://falco.org/docs/metrics/ # TYPE falcosecurity_scap_n_drops_buffer_total counter falcosecurity_scap_n_drops_buffer_total{dir="exit",drop="clone_fork"} 0 */ prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( metric_new, "falcosecurity", prometheus_subsystem, const_labels); } } else { prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( metric, "falcosecurity", prometheus_subsystem); } } // Add deprecated enter event metrics with 0 values for backward compatibility static const std::vector deprecated_enter_drops = {"clone_fork", "execve", "connect", "open", "dir_file", "other_interest"}; for(const auto& drop_type : deprecated_enter_drops) { auto metric_new = libs::metrics::libsinsp_metrics::new_metric( "n_drops_buffer", METRICS_V2_KERNEL_COUNTERS, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, 0); // Always 0 for deprecated enter events const std::map& const_labels = {{"drop", drop_type}, {"dir", "enter"}}; // Add deprecation notice to the help text prometheus_text += convert_metric_to_text_prometheus_with_deprecation_notice( prometheus_metrics_converter, metric_new, "falcosecurity", "scap", // Use "scap" subsystem for kernel counters const_labels); } } // Source wrapper metrics Part B: Agnostic, performed only once. if(agent_info_written && machine_info_written) { continue; } const scap_agent_info* agent_info = nullptr; if(!agent_info_written) { agent_info = source_inspector->get_agent_info(); } const scap_machine_info* machine_info = nullptr; if(!machine_info_written) { machine_info = source_inspector->get_machine_info(); } // Not all scap engines report agent and machine infos. // However, recent lib refactors enable a linux lite platform, allowing non-syscall // inspectors to retrieve these metrics if the syscall inspector is unavailable. // We only push these info once. if(agent_info) { prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( "kernel_release", "falcosecurity", "falco", {{"kernel_release", agent_info->uname_r}}); additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric( "start_ts", METRICS_V2_MISC, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_TIME_TIMESTAMP_NS, METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT, agent_info->start_ts_epoch)); auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric( "duration_sec", METRICS_V2_MISC, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_TIME_S_COUNT, METRIC_VALUE_METRIC_TYPE_MONOTONIC, (uint64_t)((now - agent_info->start_ts_epoch) / ONE_SECOND_IN_NS))); agent_info_written = true; } if(machine_info) { prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus( "hostname", "falcosecurity", "evt", {{"hostname", machine_info->hostname}}); additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric( "host_boot_ts", METRICS_V2_MISC, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_TIME_TIMESTAMP_NS, METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT, machine_info->boot_ts_epoch)); additional_wrapper_metrics.emplace_back(libs::metrics::libsinsp_metrics::new_metric( "host_num_cpus", METRICS_V2_MISC, METRIC_VALUE_TYPE_U32, METRIC_VALUE_UNIT_COUNT, METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT, machine_info->num_cpus)); machine_info_written = true; } } // End inspector loop return prometheus_text; } /*! \brief this method takes an application \c state and returns a textual representation of its configured metrics. The current implementation returns a Prometheus exposition formatted string. */ std::string falco_metrics::to_text_prometheus(const falco::app::state& state) { libs::metrics::prometheus_metrics_converter prometheus_metrics_converter; std::string prometheus_text; std::vector additional_wrapper_metrics; // Falco global metrics, once prometheus_text += falco_to_text_prometheus(state, prometheus_metrics_converter, additional_wrapper_metrics); // Metrics for each source prometheus_text += sources_to_text_prometheus(state, prometheus_metrics_converter, additional_wrapper_metrics); for(auto metric : additional_wrapper_metrics) { prometheus_metrics_converter.convert_metric_to_unit_convention(metric); prometheus_text += prometheus_metrics_converter.convert_metric_to_text_prometheus(metric, "falcosecurity", "falco"); } return prometheus_text; } ================================================ FILE: userspace/falco/falco_metrics.h ================================================ // SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2024 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #include "configuration.h" #include namespace falco::app { struct state; } class falco_metrics { public: static const std::string content_type_prometheus; static std::string to_text_prometheus(const falco::app::state& state); private: static std::string falco_to_text_prometheus( const falco::app::state& state, libs::metrics::prometheus_metrics_converter& prometheus_metrics_converter, std::vector& additional_wrapper_metrics); static std::string sources_to_text_prometheus( const falco::app::state& state, libs::metrics::prometheus_metrics_converter& prometheus_metrics_converter, std::vector& additional_wrapper_metrics); }; ================================================ FILE: userspace/falco/falco_outputs.cpp ================================================ // 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 "falco_outputs.h" #include "config_falco.h" #include "formats.h" #include "logger.h" #include "watchdog.h" #include "outputs_file.h" #include "outputs_stdout.h" #if !defined(_WIN32) #include "outputs_program.h" #include "outputs_syslog.h" #endif #if defined(__linux__) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD) #include "outputs_http.h" #endif static const char *s_internal_source = "internal"; falco_outputs::falco_outputs(std::shared_ptr engine, const std::vector &outputs, bool json_output, bool json_include_output_property, bool json_include_tags_property, bool json_include_message_property, bool json_include_output_fields_property, uint32_t timeout, bool buffered, size_t outputs_queue_capacity, bool time_format_iso_8601, const std::string &hostname): m_formats(std::make_unique(engine, json_include_output_property, json_include_tags_property, json_include_message_property, json_include_output_fields_property, time_format_iso_8601)), m_buffered(buffered), m_json_output(json_output), m_time_format_iso_8601(time_format_iso_8601), m_timeout(std::chrono::milliseconds(timeout)), m_hostname(hostname) { for(const auto &output : outputs) { add_output(output); } #ifndef __EMSCRIPTEN__ m_queue.set_capacity(outputs_queue_capacity); m_worker_thread = std::thread(&falco_outputs::worker, this); #endif } falco_outputs::~falco_outputs() { #ifndef __EMSCRIPTEN__ this->stop_worker(); #endif } // This function is called only at initialization-time by the constructor void falco_outputs::add_output(const falco::outputs::config &oc) { std::unique_ptr oo; if(oc.name == "file") { oo = std::make_unique(); } #ifndef _WIN32 else if(oc.name == "program") { oo = std::make_unique(); } #endif else if(oc.name == "stdout") { oo = std::make_unique(); } #ifndef _WIN32 else if(oc.name == "syslog") { oo = std::make_unique(); } #endif #if defined(__linux__) && !defined(__EMSCRIPTEN__) && !defined(MINIMAL_BUILD) else if(oc.name == "http") { oo = std::make_unique(); } #endif else { throw falco_exception("Output not supported: " + oc.name); } std::string init_err; if(oo->init(oc, m_buffered, m_hostname, m_json_output, init_err)) { m_outputs.push_back(std::move(oo)); } else { falco_logger::log(falco_logger::level::ERR, "Failed to init output: " + init_err); } } void falco_outputs::handle_event(sinsp_evt *evt, const std::string &rule, const std::string &source, falco_common::priority_type priority, const std::string &format, std::set &tags, extra_output_field_t &extra_fields) { falco_outputs::ctrl_msg cmsg = {}; cmsg.ts = evt->get_ts(); cmsg.priority = priority; cmsg.source = source; cmsg.rule = rule; cmsg.msg = m_formats->format_event(evt, rule, source, falco_common::format_priority(priority), format, tags, m_hostname, extra_fields); auto fields = m_formats->get_field_values(evt, source, format); for(auto const &ef : extra_fields) { // when formatting for the control message we always want strings, // so we can simply format raw fields as string std::string fformat = ef.second.first; if(fformat.size() == 0) { continue; } if(!(fformat[0] == '*')) { fformat = "*" + fformat; } fields[ef.first] = m_formats->format_string(evt, fformat, source); } cmsg.fields = fields; cmsg.tags.insert(tags.begin(), tags.end()); cmsg.type = ctrl_msg_type::CTRL_MSG_OUTPUT; this->push(cmsg); } void falco_outputs::handle_msg(uint64_t ts, falco_common::priority_type priority, const std::string &msg, const std::string &rule, nlohmann::json &output_fields) { if(!output_fields.is_object()) { throw falco_exception("falco_outputs: output fields must be key-value maps"); } falco_outputs::ctrl_msg cmsg = {}; cmsg.ts = ts; cmsg.priority = priority; cmsg.source = s_internal_source; cmsg.rule = rule; cmsg.fields = output_fields; if(m_json_output) { nlohmann::json jmsg; // Convert the time-as-nanoseconds to a more json-friendly ISO8601. time_t evttime = ts / 1000000000; char time_sec[20]; // sizeof "YYYY-MM-DDTHH:MM:SS" char time_ns[12]; // sizeof ".sssssssssZ" std::string iso8601evttime; strftime(time_sec, sizeof(time_sec), "%FT%T", gmtime(&evttime)); snprintf(time_ns, sizeof(time_ns), ".%09luZ", ts % 1000000000); iso8601evttime = time_sec; iso8601evttime += time_ns; jmsg["output"] = msg; jmsg["priority"] = falco_common::format_priority(priority); jmsg["rule"] = rule; jmsg["time"] = iso8601evttime; jmsg["output_fields"] = output_fields; jmsg["hostname"] = m_hostname; jmsg["source"] = s_internal_source; cmsg.msg = jmsg.dump(); } else { std::string timestr; bool first = true; sinsp_utils::ts_to_string(ts, ×tr, false, true); cmsg.msg = timestr + ": " + falco_common::format_priority(priority) + " " + msg + " ("; for(auto &pair : output_fields.items()) { if(first) { first = false; } else { cmsg.msg += " "; } if(!pair.value().is_primitive()) { throw falco_exception("falco_outputs: output fields must be key-value maps"); } cmsg.msg += pair.key() + "=" + pair.value().dump(); } cmsg.msg += ")"; } cmsg.type = ctrl_msg_type::CTRL_MSG_OUTPUT; this->push(cmsg); } void falco_outputs::cleanup_outputs() { this->push_ctrl(falco_outputs::ctrl_msg_type::CTRL_MSG_CLEANUP); } void falco_outputs::reopen_outputs() { this->push_ctrl(falco_outputs::ctrl_msg_type::CTRL_MSG_REOPEN); } void falco_outputs::stop_worker() { watchdog wd; wd.start([&](void *) -> void { falco_logger::log( falco_logger::level::NOTICE, "output channels still blocked, discarding all remaining notifications\n"); #ifndef __EMSCRIPTEN__ m_queue.clear(); #endif this->push_ctrl(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP); }); wd.set_timeout(m_timeout, nullptr); this->push_ctrl(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP); if(m_worker_thread.joinable()) { m_worker_thread.join(); } } inline void falco_outputs::push_ctrl(ctrl_msg_type cmt) { falco_outputs::ctrl_msg cmsg = {}; cmsg.type = cmt; this->push(cmsg); } inline void falco_outputs::push(const ctrl_msg &cmsg) { #ifndef __EMSCRIPTEN__ if(!m_queue.try_push(cmsg)) { if(m_outputs_queue_num_drops.load() == 0) { falco_logger::log(falco_logger::level::ERR, "Outputs queue out of memory. Drop event and continue on ..."); } m_outputs_queue_num_drops++; } #else for(const auto &o : m_outputs) { process_msg(o.get(), cmsg); } #endif } // todo(leogr,leodido): this function is not supposed to throw exceptions, and with "noexcept", // the program is terminated if that occurs. Although that's the wanted behavior, // we still need to improve the error reporting since some inner functions can throw exceptions. void falco_outputs::worker() noexcept { watchdog wd; wd.start([&](const std::string &payload) -> void { falco_logger::log(falco_logger::level::CRIT, "\"" + payload + "\" output timeout, all output channels are blocked\n"); }); auto timeout = m_timeout; falco_outputs::ctrl_msg cmsg; do { // Block until a message becomes available. #ifndef __EMSCRIPTEN__ m_queue.pop(cmsg); #endif for(const auto &o : m_outputs) { wd.set_timeout(timeout, o->get_name()); try { process_msg(o.get(), cmsg); } catch(const std::exception &e) { falco_logger::log(falco_logger::level::ERR, o->get_name() + ": " + std::string(e.what()) + "\n"); } } wd.cancel_timeout(); } while(cmsg.type != ctrl_msg_type::CTRL_MSG_STOP); } inline void falco_outputs::process_msg(falco::outputs::abstract_output *o, const ctrl_msg &cmsg) { switch(cmsg.type) { case ctrl_msg_type::CTRL_MSG_OUTPUT: o->output(&cmsg); break; case ctrl_msg_type::CTRL_MSG_CLEANUP: case ctrl_msg_type::CTRL_MSG_STOP: o->cleanup(); break; case ctrl_msg_type::CTRL_MSG_REOPEN: o->reopen(); break; default: falco_logger::log(falco_logger::level::DEBUG, "Outputs worker received an unknown message type\n"); } } uint64_t falco_outputs::get_outputs_queue_num_drops() { return m_outputs_queue_num_drops.load(); } ================================================ FILE: userspace/falco/falco_outputs.h ================================================ // 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. */ #pragma once #include #include #include #include "falco_common.h" #include "falco_engine.h" #include "outputs.h" #include "formats.h" #ifndef __EMSCRIPTEN__ #include "tbb/concurrent_queue.h" #endif /*! \brief This class acts as the primary interface between a program and the falco output engine. The falco rules engine is implemented by a separate class falco_engine. All methods in this class are thread-safe. The output framework supports a multi-producer model where messages are stored in a queue and consumed by each configured output asynchronously. */ class falco_outputs { public: falco_outputs(std::shared_ptr engine, const std::vector &outputs, bool json_output, bool json_include_output_property, bool json_include_tags_property, bool json_include_message_property, bool json_include_output_fields_property, uint32_t timeout, bool buffered, size_t outputs_queue_capacity, bool time_format_iso_8601, const std::string &hostname); virtual ~falco_outputs(); /*! \brief Format then send the event to all configured outputs (`evt` is an event that has matched some rule). */ void handle_event(sinsp_evt *evt, const std::string &rule, const std::string &source, falco_common::priority_type priority, const std::string &format, std::set &tags, extra_output_field_t &extra_fields); /*! \brief Format then send a generic message to all outputs. Not necessarily associated with any event. */ void handle_msg(uint64_t now, falco_common::priority_type priority, const std::string &msg, const std::string &rule, nlohmann::json &output_fields); /*! \brief Sends a cleanup message to all outputs. Each output can have an implementation-specific behavior. In general, this is used to flush or clean output buffers. */ void cleanup_outputs(); /*! \brief Sends a message to all outputs that causes them to be closed and reopened. Each output can have an implementation-specific behavior. */ void reopen_outputs(); /*! \brief Return the number of events currently dropped due to failed push attempts into the outputs queue */ uint64_t get_outputs_queue_num_drops(); private: std::unique_ptr m_formats; std::vector> m_outputs; bool m_buffered; bool m_json_output; bool m_time_format_iso_8601; std::chrono::milliseconds m_timeout; std::string m_hostname; enum ctrl_msg_type { CTRL_MSG_STOP = 0, CTRL_MSG_OUTPUT = 1, CTRL_MSG_CLEANUP = 2, CTRL_MSG_REOPEN = 3, }; struct ctrl_msg : falco::outputs::message { ctrl_msg_type type; }; #ifndef __EMSCRIPTEN__ typedef tbb::concurrent_bounded_queue falco_outputs_cbq; falco_outputs_cbq m_queue; #endif std::atomic m_outputs_queue_num_drops = 0; std::thread m_worker_thread; inline void push(const ctrl_msg &cmsg); inline void push_ctrl(ctrl_msg_type cmt); void worker() noexcept; void stop_worker(); void add_output(const falco::outputs::config &oc); inline void process_msg(falco::outputs::abstract_output *o, const ctrl_msg &cmsg); }; ================================================ FILE: userspace/falco/falco_semaphore.h ================================================ // 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 #include #include namespace falco { /** * @brief A simple semaphore implementation. Unfortunately, a standard * semaphore is only available since C++20, which currently we don't target. */ class semaphore { public: /** * @brief Creates a semaphore with the given initial counter value */ explicit semaphore(int c = 0): count(c) {} /** * @brief Increments the internal counter and unblocks acquirers */ inline void release() { std::unique_lock lock(mtx); count++; cv.notify_one(); } /** * @brief Decrements the internal counter or blocks until it can */ inline void acquire() { std::unique_lock lock(mtx); while(count == 0) { cv.wait(lock); } count--; } private: std::mutex mtx; std::condition_variable cv; int count; }; }; // namespace falco ================================================ FILE: userspace/falco/outputs.h ================================================ // 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. */ #pragma once #include #include #include "falco_common.h" #include namespace falco { namespace outputs { // // The way to refer to an output (file, syslog, stdout, etc.) // An output has a name and set of options. // struct config { std::string name; std::map options; }; // // The message to be outputted. It can either refer to: // - an event that has matched some rule, // - or a generic message (e.g., a drop alert). // struct message { uint64_t ts; falco_common::priority_type priority; std::string msg; std::string rule; std::string source; nlohmann::json fields; std::set tags; }; // // This class acts as the primary interface for implementing // a Falco output class. // class abstract_output { public: virtual ~abstract_output() = default; virtual bool init(const config& oc, bool buffered, const std::string& hostname, bool json_output, std::string& err) { m_oc = oc; m_buffered = buffered; m_hostname = hostname; m_json_output = json_output; err = ""; return true; } // Return the output's name as per its configuration. const std::string& get_name() const { return m_oc.name; } // Output a message. virtual void output(const message* msg) = 0; // Possibly close the output and open it again. virtual void reopen() {} // Possibly flush the output. virtual void cleanup() {} protected: config m_oc; bool m_buffered; std::string m_hostname; bool m_json_output; }; } // namespace outputs } // namespace falco ================================================ FILE: userspace/falco/outputs_file.cpp ================================================ // 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 "outputs_file.h" #include #include void falco::outputs::output_file::open_file() { if(!m_buffered) { m_outfile.rdbuf()->pubsetbuf(0, 0); } if(!m_outfile.is_open()) { m_outfile.open(m_oc.options["filename"], std::fstream::app); if(m_outfile.fail()) { throw falco_exception("failed to open output file " + m_oc.options["filename"]); } } } void falco::outputs::output_file::output(const message *msg) { open_file(); m_outfile << msg->msg + "\n"; if(m_oc.options["keep_alive"] != "true") { cleanup(); } } void falco::outputs::output_file::cleanup() { if(m_outfile.is_open()) { m_outfile.close(); } } void falco::outputs::output_file::reopen() { cleanup(); open_file(); } ================================================ FILE: userspace/falco/outputs_file.h ================================================ // 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. */ #pragma once #include "outputs.h" #include #include namespace falco { namespace outputs { class output_file : public abstract_output { void output(const message *msg) override; void cleanup() override; void reopen() override; private: void open_file(); std::ofstream m_outfile; }; } // namespace outputs } // namespace falco ================================================ FILE: userspace/falco/outputs_http.cpp ================================================ // 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 "outputs_http.h" #include "logger.h" #define CHECK_RES(fn) res = res == CURLE_OK ? fn : res static size_t noop_write_callback(void * /*contents*/, size_t size, size_t nmemb, void * /*userp*/) { // We don't want to echo anything. Just return size of bytes ignored return size * nmemb; } bool falco::outputs::output_http::init(const config &oc, bool buffered, const std::string &hostname, bool json_output, std::string &err) { if(!falco::outputs::abstract_output::init(oc, buffered, hostname, json_output, err)) { return false; } m_curl = nullptr; m_http_headers = nullptr; m_max_consecutive_timeouts = static_cast(std::stoi(m_oc.options["max_consecutive_timeouts"]) & 0xFF); CURLcode res = CURLE_FAILED_INIT; m_curl = curl_easy_init(); if(!m_curl) { falco_logger::log( falco_logger::level::ERR, "libcurl failed to initialize the handle: " + std::string(curl_easy_strerror(res))); return false; } if(m_json_output) { m_http_headers = curl_slist_append(m_http_headers, "Content-Type: application/json"); } else { m_http_headers = curl_slist_append(m_http_headers, "Content-Type: text/plain"); } res = curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_http_headers); // if the URL is quoted the quotes should be removed to satisfy libcurl expected format std::string unquotedUrl = m_oc.options["url"]; if(!unquotedUrl.empty() && ((unquotedUrl.front() == '\"' && unquotedUrl.back() == '\"') || (unquotedUrl.front() == '\'' && unquotedUrl.back() == '\''))) { unquotedUrl = libsinsp::filter::unescape_str(unquotedUrl); } CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_URL, unquotedUrl.c_str())); CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_USERAGENT, m_oc.options["user_agent"].c_str())); CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, -1L)); if(m_oc.options["insecure"] == std::string("true")) { CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L)); CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYHOST, 0L)); } if(m_oc.options["mtls"] == std::string("true")) { CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSLCERT, m_oc.options["client_cert"].c_str())); CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSLKEY, m_oc.options["client_key"].c_str())); } if(!m_oc.options["ca_cert"].empty()) { CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_CAINFO, m_oc.options["ca_cert"].c_str())); } else if(!m_oc.options["ca_bundle"].empty()) { CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_CAINFO, m_oc.options["ca_bundle"].c_str())); } else { CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_CAPATH, m_oc.options["ca_path"].c_str())); } if(m_oc.options["echo"] == std::string("false")) { // If echo==true, libcurl defaults to fwrite to stdout, ie: echoing CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, noop_write_callback)); } if(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)); return false; } return true; } void falco::outputs::output_http::output(const message *msg) { CURLcode res = curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, msg->msg.c_str()); uint8_t curl_easy_platform_calls = 0; if(res == CURLE_OK) { do { res = curl_easy_perform(m_curl); curl_easy_platform_calls++; } while(res == CURLE_OPERATION_TIMEDOUT && curl_easy_platform_calls <= m_max_consecutive_timeouts); } if(res != CURLE_OK) { falco_logger::log( falco_logger::level::ERR, "libcurl failed to perform call: " + std::string(curl_easy_strerror(res))); } } void falco::outputs::output_http::cleanup() { curl_easy_cleanup(m_curl); m_curl = nullptr; curl_slist_free_all(m_http_headers); m_http_headers = nullptr; } ================================================ FILE: userspace/falco/outputs_http.h ================================================ // 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. */ #pragma once #include "outputs.h" #include #include namespace falco { namespace outputs { class output_http : public abstract_output { bool init(const config &oc, bool buffered, const std::string &hostname, bool json_output, std::string &err) override; void output(const message *msg) override; void cleanup() override; private: CURL *m_curl; struct curl_slist *m_http_headers; uint8_t m_max_consecutive_timeouts; }; } // namespace outputs } // namespace falco ================================================ FILE: userspace/falco/outputs_program.cpp ================================================ // 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 "outputs_program.h" #include "logger.h" #include #include #include void falco::outputs::output_program::open_pfile() { if(m_pfile == nullptr) { m_pfile = popen(m_oc.options["program"].c_str(), "w"); if(m_pfile == nullptr) { falco_logger::log(falco_logger::level::ERR, "Failed to open program output: " + m_oc.options["program"] + " (error: " + std::string(std::strerror(errno)) + ")"); return; } if(!m_buffered) { setvbuf(m_pfile, NULL, _IONBF, 0); } } } void falco::outputs::output_program::output(const message *msg) { open_pfile(); if(m_pfile != nullptr) { fprintf(m_pfile, "%s\n", msg->msg.c_str()); } if(m_oc.options["keep_alive"] != "true") { cleanup(); } } void falco::outputs::output_program::cleanup() { if(m_pfile != nullptr) { pclose(m_pfile); m_pfile = nullptr; } } void falco::outputs::output_program::reopen() { cleanup(); open_pfile(); } ================================================ FILE: userspace/falco/outputs_program.h ================================================ // 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. */ #pragma once #include "outputs.h" namespace falco { namespace outputs { class output_program : public abstract_output { void output(const message *msg) override; void cleanup() override; void reopen() override; private: void open_pfile(); FILE *m_pfile = nullptr; }; } // namespace outputs } // namespace falco ================================================ FILE: userspace/falco/outputs_stdout.cpp ================================================ // 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 "outputs_stdout.h" #include void falco::outputs::output_stdout::output(const message *msg) { // // By default, the stdout stream is fully buffered or line buffered // (if the stream can be determined to refer to an interactive device, e.g. in a TTY). // Just enable automatic flushing when unbuffered output is desired. // Note that it is set every time since other writings to the stdout can disable it. // if(!m_buffered) { std::cout << std::unitbuf; } std::cout << msg->msg + "\n"; } void falco::outputs::output_stdout::cleanup() { std::cout.flush(); } ================================================ FILE: userspace/falco/outputs_stdout.h ================================================ // 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. */ #pragma once #include "outputs.h" namespace falco { namespace outputs { class output_stdout : public abstract_output { void output(const message *msg) override; void cleanup() override; }; } // namespace outputs } // namespace falco ================================================ FILE: userspace/falco/outputs_syslog.cpp ================================================ // 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 "outputs_syslog.h" #include void falco::outputs::output_syslog::output(const message *msg) { // Syslog output should not have any trailing newline ::syslog(msg->priority, "%s", msg->msg.c_str()); } ================================================ FILE: userspace/falco/outputs_syslog.h ================================================ // 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. */ #pragma once #include "outputs.h" namespace falco { namespace outputs { class output_syslog : public abstract_output { void output(const message *msg) override; }; } // namespace outputs } // namespace falco ================================================ FILE: userspace/falco/stats_writer.cpp ================================================ // 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. */ #ifndef _WIN32 #include #endif #include #include #include #include #include "falco_common.h" #include "stats_writer.h" #include "logger.h" #include "config_falco.h" #include "falco_utils.h" #include #include #ifdef HAS_JEMALLOC #include #endif namespace fs = std::filesystem; // note: ticker_t is an uint16_t, which is enough because we don't care about // overflows here. Threads calling stats_writer::handle() will just // check that this value changed since their last observation. static std::atomic s_timer((stats_writer::ticker_t)0); #if !defined(__APPLE__) && !defined(_WIN32) 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 // 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. bool s_timerid_exists = false; static void timer_handler(int signum) { s_timer.fetch_add(1, std::memory_order_relaxed); } #if defined(_WIN32) bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { return true; } #endif #if defined(__APPLE__) bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { struct sigaction handler = {}; memset(&handler, 0, sizeof(handler)); handler.sa_handler = &timer_handler; if(sigaction(SIGALRM, &handler, NULL) == -1) { err = std::string("Could not set up signal handler for periodic timer: ") + strerror(errno); return false; } struct sigevent sev = {}; /* Create the timer */ sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGALRM; sev.sigev_value.sival_ptr = &s_timerid; return true; } #endif #if defined(EMSCRIPTEN) bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { struct itimerspec timer = {}; struct sigaction handler = {}; memset(&handler, 0, sizeof(handler)); handler.sa_handler = &timer_handler; if(sigaction(SIGALRM, &handler, NULL) == -1) { err = std::string("Could not set up signal handler for periodic timer: ") + strerror(errno); return false; } struct sigevent sev = {}; /* Create the timer */ sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGALRM; sev.sigev_value.sival_ptr = &s_timerid; timer.it_value.tv_sec = interval_msec / 1000; timer.it_value.tv_nsec = (interval_msec % 1000) * 1000 * 1000; timer.it_interval = timer.it_value; return true; } #endif #if defined(__linux__) bool stats_writer::init_ticker(uint32_t interval_msec, std::string& err) { struct itimerspec timer = {}; struct sigaction handler = {}; memset(&handler, 0, sizeof(handler)); handler.sa_handler = &timer_handler; if(sigaction(SIGALRM, &handler, NULL) == -1) { err = std::string("Could not set up signal handler for periodic timer: ") + strerror(errno); return false; } struct sigevent sev = {}; /* Create the timer */ sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGALRM; sev.sigev_value.sival_ptr = &s_timerid; // delete any previously set timer if(s_timerid_exists) { if(timer_delete(s_timerid) == -1) { err = std::string("Could not delete previous timer: ") + strerror(errno); return false; } s_timerid_exists = false; } if(timer_create(CLOCK_MONOTONIC, &sev, &s_timerid) == -1) { err = std::string("Could not create periodic timer: ") + strerror(errno); return false; } s_timerid_exists = true; timer.it_value.tv_sec = interval_msec / 1000; timer.it_value.tv_nsec = (interval_msec % 1000) * 1000 * 1000; timer.it_interval = timer.it_value; if(timer_settime(s_timerid, 0, &timer, NULL) == -1) { err = std::string("Could not set up periodic timer: ") + strerror(errno); return false; } return true; } #endif stats_writer::ticker_t stats_writer::get_ticker() { return s_timer.load(std::memory_order_relaxed); } stats_writer::stats_writer(const std::shared_ptr& outputs, const std::shared_ptr& config, const std::shared_ptr& engine): m_config(config), m_engine(engine) { if(config->m_metrics_enabled) { /* m_outputs should always be initialized because we use it * to extract output-queue stats in both cases: rule output and file output. */ m_outputs = outputs; if(!config->m_metrics_output_file.empty()) { m_file_output.exceptions(std::ofstream::failbit | std::ofstream::badbit); m_file_output.open(config->m_metrics_output_file, std::ios_base::app); m_initialized = true; } if(config->m_metrics_stats_rule_enabled) { m_initialized = true; } } if(m_initialized) { #ifndef __EMSCRIPTEN__ // Adopt capacity for completeness, even if it's likely not relevant m_queue.set_capacity(config->m_outputs_queue_capacity); m_worker = std::thread(&stats_writer::worker, this); #endif } } stats_writer::~stats_writer() { if(m_initialized) { #ifndef __EMSCRIPTEN__ stop_worker(); #endif if(!m_config->m_metrics_output_file.empty()) { m_file_output.close(); } // delete timerID and reset timer #ifdef __linux__ if(s_timerid_exists) { timer_delete(s_timerid); s_timerid_exists = false; } #endif } } void stats_writer::stop_worker() { stats_writer::msg msg; msg.stop = true; push(msg); if(m_worker.joinable()) { m_worker.join(); } } inline void stats_writer::push(const stats_writer::msg& m) { #ifndef __EMSCRIPTEN__ if(!m_queue.try_push(m)) { fprintf(stderr, "Fatal error: Stats queue reached maximum capacity. Exiting.\n"); exit(EXIT_FAILURE); } #endif } void stats_writer::worker() noexcept { stats_writer::msg m; bool use_outputs = m_config->m_metrics_stats_rule_enabled; bool use_file = !m_config->m_metrics_output_file.empty(); auto tick = stats_writer::get_ticker(); auto last_tick = tick; while(true) { // blocks until a message becomes availables #ifndef __EMSCRIPTEN__ m_queue.pop(m); #endif if(m.stop) { return; } tick = stats_writer::get_ticker(); if(last_tick != tick) { m_total_samples++; } last_tick = tick; try { if(use_outputs) { std::string rule = "Falco internal: metrics snapshot"; std::string msg = "Falco metrics snapshot"; m_outputs->handle_msg(m.ts, falco_common::PRIORITY_INFORMATIONAL, msg, rule, m.output_fields); } if(use_file) { nlohmann::json jmsg; jmsg["sample"] = m_total_samples; jmsg["output_fields"] = m.output_fields; m_file_output << jmsg.dump() << std::endl; } } catch(const std::exception& e) { falco_logger::log(falco_logger::level::ERR, "stats_writer (worker): " + std::string(e.what()) + "\n"); } } } stats_writer::collector::collector(const std::shared_ptr& writer): m_writer(writer) {} void add_netinfo_metrics_output_fields(nlohmann::json& output_fields, const std::shared_ptr& inspector) { const auto ipv4_ifinfo = inspector->get_ifaddr_list().get_ipv4_list(); const auto ipv6_ifinfo = inspector->get_ifaddr_list().get_ipv6_list(); // For each interface name, collect the corresponding list of IPv4/IPv6 addresses std::map> ifnames_to_ipv4_addresses; std::map> ifnames_to_ipv6_addresses; for(const auto& ifinfo : *ipv4_ifinfo) { if(ifinfo.m_name == "lo") { continue; } auto it = ifnames_to_ipv4_addresses.find(ifinfo.m_name); auto address = ifinfo.addr_to_string(); if(it == ifnames_to_ipv4_addresses.end()) { ifnames_to_ipv4_addresses.emplace(ifinfo.m_name, std::vector{address}); continue; } it->second.emplace_back(address); } for(const auto& ifinfo : *ipv6_ifinfo) { if(ifinfo.m_name == "lo") { continue; } auto it = ifnames_to_ipv6_addresses.find(ifinfo.m_name); auto address = ifinfo.addr_to_string(); if(it == ifnames_to_ipv6_addresses.end()) { ifnames_to_ipv6_addresses.emplace(ifinfo.m_name, std::vector{address}); continue; } it->second.emplace_back(address); } for(const auto& item : ifnames_to_ipv4_addresses) { auto metric_name = "falco.host_netinfo.interfaces." + item.first + ".protocols.ipv4.addresses"; auto addresses = sinsp_join(item.second.cbegin(), item.second.cend(), ','); output_fields.emplace(metric_name, addresses); } for(const auto& item : ifnames_to_ipv6_addresses) { auto metric_name = "falco.host_netinfo.interfaces." + item.first + ".protocols.ipv6.addresses"; auto addresses = sinsp_join(item.second.cbegin(), item.second.cend(), ','); output_fields.emplace(metric_name, addresses); } } void stats_writer::collector::get_metrics_output_fields_wrapper( nlohmann::json& output_fields, const std::shared_ptr& inspector, const std::string& src, uint64_t num_evts, uint64_t now, double stats_snapshot_time_delta_sec) { static const char* all_driver_engines[] = {KMOD_ENGINE, MODERN_BPF_ENGINE, SOURCE_PLUGIN_ENGINE, NODRIVER_ENGINE}; const scap_agent_info* agent_info = inspector->get_agent_info(); const scap_machine_info* machine_info = inspector->get_machine_info(); // Falco wrapper metrics // /* Wrapper fields useful for statistical analyses and attributions. Always enabled. */ output_fields["evt.time"] = now; /* Some ETLs may prefer a consistent timestamp within output_fields. */ output_fields["falco.reload_ts"] = m_writer->m_config->m_falco_reload_ts; output_fields["falco.version"] = FALCO_VERSION; if(agent_info) { output_fields["falco.start_ts"] = agent_info->start_ts_epoch; output_fields["falco.duration_sec"] = (uint64_t)((now - agent_info->start_ts_epoch) / ONE_SECOND_IN_NS); output_fields["falco.kernel_release"] = agent_info->uname_r; } if(machine_info) { output_fields["evt.hostname"] = machine_info->hostname; /* Explicitly add hostname to log msg in case hostname rule output field is disabled. */ // This line generates a SIGTRAP in zig debug builds if the casting is removed. // It seems caused by the pragma pack for the scap_machine_info structure. output_fields["falco.host_boot_ts"] = (uint64_t)machine_info->boot_ts_epoch; output_fields["falco.host_num_cpus"] = machine_info->num_cpus; } output_fields["falco.outputs_queue_num_drops"] = m_writer->m_outputs->get_outputs_queue_num_drops(); #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) for(const auto& item : m_writer->m_config->m_loaded_rules_filenames_sha256sum) { fs::path fs_path = item.first; std::string metric_name_file_sha256 = fs_path.filename(); metric_name_file_sha256 = "falco.sha256_rules_file." + falco::utils::sanitize_rule_name(metric_name_file_sha256); output_fields[metric_name_file_sha256] = item.second; } for(const auto& item : m_writer->m_config->m_loaded_configs_filenames_sha256sum) { fs::path fs_path = item.first; std::string metric_name_file_sha256 = fs_path.filename(); metric_name_file_sha256 = "falco.sha256_config_file." + falco::utils::sanitize_rule_name(metric_name_file_sha256); output_fields[metric_name_file_sha256] = item.second; } add_netinfo_metrics_output_fields(output_fields, inspector); #endif output_fields["evt.source"] = src; for(size_t i = 0; i < sizeof(all_driver_engines) / sizeof(const char*); i++) { if(inspector->check_current_engine(all_driver_engines[i])) { output_fields["scap.engine_name"] = all_driver_engines[i]; break; } } /* Falco userspace event counters. Always enabled. */ if(m_last_num_evts != 0 && stats_snapshot_time_delta_sec > 0) { /* Successfully processed userspace event rate. */ 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; m_last_num_evts = num_evts; } void stats_writer::collector::get_metrics_output_fields_additional( nlohmann::json& output_fields, double stats_snapshot_time_delta_sec, const std::string& src) { // Falco metrics categories // // rules_counters_enabled if(m_writer->m_config->m_metrics_flags & METRICS_V2_RULE_COUNTERS) { const stats_manager& rule_stats_manager = m_writer->m_engine->get_rule_stats_manager(); const indexed_vector& rules = m_writer->m_engine->get_rules(); output_fields["falco.rules.matches_total"] = rule_stats_manager.get_total().load(); const std::vector>>& rules_by_id = rule_stats_manager.get_by_rule_id(); for(size_t i = 0; i < rules_by_id.size(); i++) { auto rule_count = rules_by_id[i]->load(); if(rule_count == 0 && !m_writer->m_config->m_metrics_include_empty_values) { continue; } auto rule = rules.at(i); std::string rules_metric_name = "falco.rules." + falco::utils::sanitize_rule_name(rule->name); output_fields[rules_metric_name] = rule_count; } } #ifdef HAS_JEMALLOC if(m_writer->m_config->m_metrics_flags & METRICS_V2_JEMALLOC_STATS) { nlohmann::json j; malloc_stats_print( [](void* to, const char* from) { nlohmann::json* j = static_cast(to); *j = nlohmann::json::parse(from); }, &j, "Jmdablxeg"); const auto& j_stats = j["jemalloc"]["stats"]; for(auto it = j_stats.begin(); it != j_stats.end(); ++it) { if(it.value().is_number_unsigned()) { std::uint64_t val = it.value().template get(); if(m_writer->m_config->m_metrics_include_empty_values || val != 0) { std::string key = "falco.jemalloc." + it.key() + "_bytes"; auto metric = libs::metrics::libsinsp_metrics::new_metric( key.c_str(), METRICS_V2_JEMALLOC_STATS, METRIC_VALUE_TYPE_U64, METRIC_VALUE_UNIT_MEMORY_BYTES, METRIC_VALUE_METRIC_TYPE_MONOTONIC, val); #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) if(m_writer->m_config->m_metrics_convert_memory_to_mb && m_writer->m_output_rule_metrics_converter) { m_writer->m_output_rule_metrics_converter ->convert_metric_to_unit_convention(metric); output_fields[metric.name] = metric.value.d; } else { output_fields[metric.name] = metric.value.u64; } #else output_fields[metric.name] = metric.value.u64; #endif } } } } #endif #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) if(m_writer->m_libs_metrics_collectors.find(src) != m_writer->m_libs_metrics_collectors.end() && m_writer->m_output_rule_metrics_converter) { // Libs metrics categories // // resource_utilization_enabled // state_counters_enabled // kernel_event_counters_enabled // libbpf_stats_enabled // Refresh / New snapshot auto& libs_metrics_collector = m_writer->m_libs_metrics_collectors[src]; libs_metrics_collector->snapshot(); auto metrics_snapshot = libs_metrics_collector->get_metrics(); // Cache n_evts and n_drops to derive n_drops_perc. uint64_t n_evts = 0; uint64_t n_drops = 0; uint64_t n_evts_delta = 0; uint64_t n_drops_delta = 0; // Note: Because of possible metric unit conversions, get a non-const ref to the metric. for(auto& metric : metrics_snapshot) { if(metric.name[0] == '\0') { break; } if(m_writer->m_config->m_metrics_convert_memory_to_mb) { m_writer->m_output_rule_metrics_converter->convert_metric_to_unit_convention( metric); } char metric_name[METRIC_NAME_MAX] = "falco."; if((metric.flags & METRICS_V2_LIBBPF_STATS) || (metric.flags & METRICS_V2_KERNEL_COUNTERS) || (metric.flags & METRICS_V2_KERNEL_COUNTERS_PER_CPU)) { strlcpy(metric_name, "scap.", sizeof(metric_name)); } if(metric.flags & METRICS_V2_PLUGINS) { strlcpy(metric_name, "plugins.", sizeof(metric_name)); } strlcat(metric_name, metric.name, sizeof(metric_name)); switch(metric.type) { case METRIC_VALUE_TYPE_U32: if(metric.value.u32 == 0 && !m_writer->m_config->m_metrics_include_empty_values) { break; } output_fields[metric_name] = metric.value.u32; break; case METRIC_VALUE_TYPE_S32: if(metric.value.s32 == 0 && !m_writer->m_config->m_metrics_include_empty_values) { break; } output_fields[metric_name] = metric.value.s32; break; case METRIC_VALUE_TYPE_U64: if(strncmp(metric.name, "n_evts", 7) == 0) { n_evts = metric.value.u64; // Always send high level n_evts related fields, even if zero and configs are // set to exclude empty values. 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) { 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 { output_fields["scap.evts_rate_sec"] = (double)(0); } m_last_n_evts = n_evts; } else if(strncmp(metric.name, "n_drops", 8) == 0) { n_drops = metric.value.u64; // Always send high level n_drops related fields, even if zero and configs are // set to exclude empty values. 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) { 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 { output_fields["scap.evts_drop_rate_sec"] = (double)(0); } m_last_n_drops = n_drops; } if(metric.value.u64 == 0 && !m_writer->m_config->m_metrics_include_empty_values) { break; } output_fields[metric_name] = metric.value.u64; break; case METRIC_VALUE_TYPE_S64: if(metric.value.s64 == 0 && !m_writer->m_config->m_metrics_include_empty_values) { break; } output_fields[metric_name] = metric.value.s64; break; case METRIC_VALUE_TYPE_D: if(metric.value.d == 0 && !m_writer->m_config->m_metrics_include_empty_values) { break; } output_fields[metric_name] = metric.value.d; break; case METRIC_VALUE_TYPE_F: if(metric.value.f == 0 && !m_writer->m_config->m_metrics_include_empty_values) { break; } output_fields[metric_name] = metric.value.f; break; case METRIC_VALUE_TYPE_I: if(metric.value.i == 0 && !m_writer->m_config->m_metrics_include_empty_values) { break; } output_fields[metric_name] = metric.value.i; break; default: break; } } /* n_drops_perc needs to be calculated outside the loop given no field ordering guarantees. * Always send n_drops_perc, even if zero and configs are set to exclude empty values. */ if(n_evts_delta > 0) { output_fields["scap.n_drops_perc"] = (double)((100.0 * n_drops_delta) / n_evts_delta); } else { output_fields["scap.n_drops_perc"] = (double)(0); } } #endif } void stats_writer::collector::collect(const std::shared_ptr& inspector, const std::string& src, uint64_t num_evts) { if(m_writer->has_output()) { #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) if(m_writer->m_libs_metrics_collectors.find(src) == m_writer->m_libs_metrics_collectors.end()) { uint32_t flags = m_writer->m_config->m_metrics_flags; // Note: ENGINE_FLAG_BPF_STATS_ENABLED check has been moved to libs, that is, when // libbpf stats is not enabled in the kernel settings we won't collect them even if the // end user enabled the libbpf stats option if(!inspector->check_current_engine(MODERN_BPF_ENGINE)) { flags &= ~METRICS_V2_LIBBPF_STATS; } // Note: src is static for live captures if(src != falco_common::syscall_source) { flags &= ~(METRICS_V2_KERNEL_COUNTERS | METRICS_V2_KERNEL_COUNTERS_PER_CPU | METRICS_V2_STATE_COUNTERS | METRICS_V2_LIBBPF_STATS); } m_writer->m_libs_metrics_collectors[src] = std::make_unique(inspector.get(), flags); } if(!m_writer->m_output_rule_metrics_converter) { m_writer->m_output_rule_metrics_converter = std::make_unique(); } #endif /* Collect stats / metrics once per ticker period. */ auto tick = stats_writer::get_ticker(); if(tick != m_last_tick) { m_last_tick = tick; auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); uint64_t stats_snapshot_time_delta = 0; if(m_last_now != 0) { stats_snapshot_time_delta = now - m_last_now; } m_last_now = now; double stats_snapshot_time_delta_sec = (stats_snapshot_time_delta / (double)ONE_SECOND_IN_NS); /* Get respective metrics output_fields. */ nlohmann::json output_fields; get_metrics_output_fields_wrapper(output_fields, inspector, src, num_evts, now, stats_snapshot_time_delta_sec); get_metrics_output_fields_additional(output_fields, stats_snapshot_time_delta_sec, src); /* Send message in the queue */ stats_writer::msg msg; msg.ts = now; msg.source = src; msg.output_fields = std::move(output_fields); m_writer->push(msg); } } } ================================================ FILE: userspace/falco/stats_writer.h ================================================ // 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. */ #pragma once #include #include #include #include #ifndef __EMSCRIPTEN__ #include "tbb/concurrent_queue.h" #endif #include "falco_outputs.h" #include "configuration.h" /*! \brief Writes stats samples collected from inspectors into a given output. Users must use a stats_writer::collector in order to collect and write stats into a given stats_writer. This class is thread-safe, and can be shared across multiple stats_writer::collector instances from different threads. */ class stats_writer { public: /*! \brief Value of a ticker that dictates when stats are collected */ typedef uint16_t ticker_t; /*! \brief Collects stats samples from an inspector and uses a writer to print them in a given output. Stats are collected periodically every time the value of stats_writer::get_ticker() changes. This class is not thread-safe. */ class collector { public: /*! \brief Initializes the collector with the given writer */ explicit collector(const std::shared_ptr& writer); /*! \brief Collects one stats sample from an inspector and for the given event source name */ void collect(const std::shared_ptr& inspector, const std::string& src, uint64_t num_evts); private: /*! \brief Collect snapshot metrics wrapper fields as internal rule formatted output fields. */ void get_metrics_output_fields_wrapper(nlohmann::json& output_fields, const std::shared_ptr& inspector, const std::string& src, uint64_t num_evts, uint64_t now, double stats_snapshot_time_delta_sec); /*! \brief Collect the configurable snapshot metrics as internal rule formatted output fields. */ void get_metrics_output_fields_additional(nlohmann::json& output_fields, double stats_snapshot_time_delta_sec, const std::string& src); std::shared_ptr m_writer; // Init m_last_tick w/ invalid value to enable metrics logging immediately after // startup/reload stats_writer::ticker_t m_last_tick = std::numeric_limits::max(); uint64_t m_last_now = 0; uint64_t m_last_n_evts = 0; uint64_t m_last_n_drops = 0; uint64_t m_last_num_evts = 0; }; stats_writer(const stats_writer&) = delete; stats_writer(stats_writer&&) = default; stats_writer& operator=(const stats_writer&) = delete; stats_writer& operator=(stats_writer&&) = default; ~stats_writer(); /*! \brief Initializes a writer. */ stats_writer(const std::shared_ptr& outputs, const std::shared_ptr& config, const std::shared_ptr& engine); /*! \brief Returns true if the writer is configured with a valid output. */ inline bool has_output() const { return m_initialized; } /*! \brief Initializes the ticker with a given interval period defined in milliseconds. Subsequent calls to init_ticker will dismiss the previously-initialized ticker. Internally, this uses a timer signal handler. */ static bool init_ticker(uint32_t interval_msec, std::string& err); /*! \brief Returns the current value of the ticker. This function is thread-safe. */ inline static ticker_t get_ticker(); private: struct msg { msg() {} msg(msg&&) = default; msg& operator=(msg&&) = default; msg(const msg&) = default; msg& operator=(const msg&) = default; bool stop = false; uint64_t ts = 0; std::string source; nlohmann::json output_fields; }; void worker() noexcept; void stop_worker(); inline void push(const stats_writer::msg& m); bool m_initialized = false; uint64_t m_total_samples = 0; std::thread m_worker; std::ofstream m_file_output; #ifndef __EMSCRIPTEN__ tbb::concurrent_bounded_queue m_queue; #endif #if defined(__linux__) and !defined(MINIMAL_BUILD) and !defined(__EMSCRIPTEN__) // Per source map of libs metrics collectors std::unordered_map> m_libs_metrics_collectors; std::unique_ptr m_output_rule_metrics_converter; #endif std::shared_ptr m_outputs; std::shared_ptr m_config; std::shared_ptr m_engine; // note: in this way, only collectors can push into the queue friend class stats_writer::collector; }; ================================================ FILE: userspace/falco/versions_info.cpp ================================================ // 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 "versions_info.h" #include // todo: move string conversion to scap static std::string get_driver_api_version(const std::shared_ptr& s) { auto driver_api_version = s->get_scap_api_version(); unsigned long driver_api_major = PPM_API_VERSION_MAJOR(driver_api_version); unsigned long driver_api_minor = PPM_API_VERSION_MINOR(driver_api_version); unsigned long driver_api_patch = PPM_API_VERSION_PATCH(driver_api_version); char driver_api_version_string[32]; snprintf(driver_api_version_string, sizeof(driver_api_version_string), "%lu.%lu.%lu", driver_api_major, driver_api_minor, driver_api_patch); return std::string(driver_api_version_string); } // todo: move string conversion to scap static inline std::string get_driver_schema_version(const std::shared_ptr& s) { auto driver_schema_version = s->get_scap_schema_version(); unsigned long driver_schema_major = PPM_API_VERSION_MAJOR(driver_schema_version); unsigned long driver_schema_minor = PPM_API_VERSION_MINOR(driver_schema_version); unsigned long driver_schema_patch = PPM_API_VERSION_PATCH(driver_schema_version); char driver_schema_version_string[32]; snprintf(driver_schema_version_string, sizeof(driver_schema_version_string), "%lu.%lu.%lu", driver_schema_major, driver_schema_minor, driver_schema_patch); return std::string(driver_schema_version_string); } falco::versions_info::versions_info(const std::shared_ptr& inspector): plugin_api_version(inspector->get_plugin_api_version()), driver_api_version(get_driver_api_version(inspector)), driver_schema_version(get_driver_schema_version(inspector)) { for(const auto& p : inspector->get_plugin_manager()->plugins()) { plugin_versions[p->name()] = p->plugin_version().as_string(); } } nlohmann::json falco::versions_info::as_json() const { nlohmann::json version_info; version_info["falco_version"] = falco_version; version_info["libs_version"] = libs_version; version_info["plugin_api_version"] = plugin_api_version; version_info["driver_api_version"] = driver_api_version; version_info["driver_schema_version"] = driver_schema_version; version_info["default_driver_version"] = default_driver_version; // note: the 'engine_version' key below must be removed in the next major bump (0.x.y -> 1.0.0) // the two keys are kept for existing tooling that relies on the old key // (falcoctl will match old rules artifacts configs by using this key, and the new ones using // the engine_version_semver key) version_info["engine_version"] = std::to_string(FALCO_ENGINE_VERSION_MINOR); version_info["engine_version_semver"] = engine_version; for(const auto& pv : plugin_versions) { version_info["plugin_versions"][pv.first] = pv.second; } return version_info; } ================================================ FILE: userspace/falco/versions_info.h ================================================ // 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. */ #pragma once #include #include #include #include #include #include "config_falco.h" #include "falco_engine_version.h" namespace falco { /** * @brief Container for the version of Falco components */ struct versions_info { /** * @brief Construct a versions info by using an inspector to obtain * versions about the drivers and the loaded plugins. */ explicit versions_info(const std::shared_ptr& inspector); versions_info(versions_info&&) = default; versions_info& operator=(versions_info&&) = default; versions_info(const versions_info& s) = default; versions_info& operator=(const versions_info& s) = default; /** * @brief Encode the versions info as a JSON object */ nlohmann::json as_json() const; std::string falco_version = FALCO_VERSION; std::string engine_version = FALCO_ENGINE_VERSION; std::string libs_version = FALCOSECURITY_LIBS_VERSION; std::string plugin_api_version; std::string driver_api_version; std::string driver_schema_version; std::string default_driver_version = DRIVER_VERSION; std::unordered_map plugin_versions; }; }; // namespace falco ================================================ FILE: userspace/falco/watchdog.h ================================================ // 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 #include #include #include template class watchdog { public: watchdog(): m_timeout(nullptr), m_is_running(false) {} ~watchdog() { stop(); } void start(std::function cb, std::chrono::milliseconds resolution = std::chrono::milliseconds(100)) { stop(); m_is_running.store(true, std::memory_order_release); m_thread = std::thread([this, cb, resolution]() { const auto no_deadline = time_point{}; timeout_data curr; while(m_is_running.load(std::memory_order_acquire)) { auto t = m_timeout.exchange(nullptr, std::memory_order_acq_rel); if(t) { curr = *t; delete t; } if(curr.deadline != no_deadline && curr.deadline < std::chrono::steady_clock::now()) { cb(curr.payload); curr.deadline = no_deadline; } std::this_thread::sleep_for(resolution); } }); } void stop() { if(m_is_running.load(std::memory_order_acquire)) { m_is_running.store(false, std::memory_order_release); if(m_thread.joinable()) { m_thread.join(); } delete m_timeout.exchange(nullptr, std::memory_order_acq_rel); } } inline void set_timeout(std::chrono::milliseconds timeout, _T payload) noexcept { delete m_timeout.exchange( new timeout_data{std::chrono::steady_clock::now() + timeout, payload}, std::memory_order_release); } inline void cancel_timeout() noexcept { delete m_timeout.exchange(new timeout_data, std::memory_order_release); } private: typedef std::chrono::time_point time_point; struct timeout_data { time_point deadline; _T payload; }; std::atomic m_timeout; std::atomic m_is_running; std::thread m_thread; }; ================================================ FILE: userspace/falco/webserver.cpp ================================================ // 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 "webserver.h" #include "falco_utils.h" #include "falco_metrics.h" #include "app/state.h" #include "versions_info.h" #include falco_webserver::~falco_webserver() { stop(); } void falco_webserver::start(const falco::app::state &state, const falco_configuration::webserver_config &webserver_config) { if(m_running) { throw falco_exception("attempted restarting webserver without stopping it first"); } // allocate and configure server if(webserver_config.m_ssl_enabled) { m_server = std::make_unique(webserver_config.m_ssl_certificate.c_str(), webserver_config.m_ssl_certificate.c_str()); } else { m_server = std::make_unique(); } // configure server m_server->new_task_queue = [webserver_config] { return new httplib::ThreadPool(webserver_config.m_threadiness); }; // setup healthz endpoint m_server->Get(webserver_config.m_k8s_healthz_endpoint, [](const httplib::Request &, httplib::Response &res) { res.set_content("{\"status\": \"ok\"}", "application/json"); }); // setup versions endpoint const auto versions_json_str = falco::versions_info(state.offline_inspector).as_json().dump(); m_server->Get("/versions", [versions_json_str](const httplib::Request &, httplib::Response &res) { res.set_content(versions_json_str, "application/json"); }); // run server in a separate thread if(!m_server->is_valid()) { m_server = nullptr; throw falco_exception("invalid webserver configuration"); } m_failed.store(false, std::memory_order_release); m_server_thread = std::thread([this, webserver_config] { try { this->m_server->listen(webserver_config.m_listen_address, webserver_config.m_listen_port); } catch(std::exception &e) { falco_logger::log(falco_logger::level::ERR, "falco_webserver: " + std::string(e.what()) + "\n"); } this->m_failed.store(true, std::memory_order_release); }); // wait for the server to actually start up // note: is_running() is atomic while(!m_server->is_running() && !m_failed.load(std::memory_order_acquire)) { std::this_thread::yield(); } m_running = true; if(m_failed.load(std::memory_order_acquire)) { stop(); throw falco_exception("an error occurred while starting webserver"); } } void falco_webserver::stop() { if(m_running) { if(m_server != nullptr) { m_server->stop(); } if(m_server_thread.joinable()) { m_server_thread.join(); } m_server = nullptr; m_running = false; } } void falco_webserver::enable_prometheus_metrics(const falco::app::state &state) { if(state.config->m_metrics_enabled && state.config->m_webserver_config.m_prometheus_metrics_enabled) { m_server->Get("/metrics", [&state](const httplib::Request &, httplib::Response &res) { res.set_content(falco_metrics::to_text_prometheus(state), falco_metrics::content_type_prometheus); }); } } ================================================ FILE: userspace/falco/webserver.h ================================================ // 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. */ #pragma once #include "configuration.h" #include #include #include #include namespace falco::app { struct state; } class falco_webserver { public: falco_webserver() = default; virtual ~falco_webserver(); falco_webserver(falco_webserver&&) = delete; falco_webserver& operator=(falco_webserver&&) = delete; falco_webserver(const falco_webserver&) = delete; falco_webserver& operator=(const falco_webserver&) = delete; virtual void start(const falco::app::state& state, const falco_configuration::webserver_config& webserver_config); virtual void stop(); virtual void enable_prometheus_metrics(const falco::app::state& state); private: bool m_running = false; std::unique_ptr m_server = nullptr; std::thread m_server_thread; std::atomic m_failed; };