mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 11:42:06 +00:00
Compare commits
3 Commits
test_moder
...
buffer_dim
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e8bf42ff9 | ||
|
|
a151418270 | ||
|
|
69623e9b93 |
@@ -555,7 +555,7 @@ jobs:
|
||||
name: Build and publish no-driver
|
||||
command: |
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=bin --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=bin-dev --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco-no-driver:x86_64-${CIRCLE_TAG}" \
|
||||
-t falcosecurity/falco-no-driver:x86_64-latest \
|
||||
-t "falcosecurity/falco:x86_64-${CIRCLE_TAG}-slim" \
|
||||
@@ -569,7 +569,7 @@ jobs:
|
||||
name: Build and publish falco
|
||||
command: |
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco:x86_64-${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco:x86_64-latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:x86_64-${CIRCLE_TAG}" \
|
||||
@@ -624,7 +624,7 @@ jobs:
|
||||
name: Build and publish falco
|
||||
command: |
|
||||
cd /tmp/source-arm64/falco
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
docker buildx build --push --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco:aarch64-${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco:aarch64-latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco:aarch64-${CIRCLE_TAG}" \
|
||||
|
||||
142
CHANGELOG.md
142
CHANGELOG.md
@@ -1,147 +1,5 @@
|
||||
# Change Log
|
||||
|
||||
## 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
|
||||
|
||||
@@ -27,14 +27,6 @@ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_SYSTEM_NAME MATCHES "Linux
|
||||
endif()
|
||||
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" OFF)
|
||||
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})
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
if(CPACK_GENERATOR MATCHES "DEB" OR CPACK_GENERATOR MATCHES "RPM")
|
||||
if(CPACK_GENERATOR MATCHES "DEB")
|
||||
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-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-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-plugin.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system")
|
||||
list(APPEND CPACK_INSTALL_COMMANDS "cp scripts/debian/falco.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system")
|
||||
endif()
|
||||
|
||||
if(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/rpm/falco.service _CPack_Packages/${CPACK_TOPLEVEL_TAG}/${CPACK_GENERATOR}/${CPACK_PACKAGE_FILE_NAME}/usr/lib/systemd/system")
|
||||
endif()
|
||||
|
||||
if(CPACK_GENERATOR MATCHES "TGZ")
|
||||
|
||||
@@ -26,8 +26,8 @@ else()
|
||||
# In case you want to test against another driver version (or branch, or commit) just pass the variable -
|
||||
# ie., `cmake -DDRIVER_VERSION=dev ..`
|
||||
if(NOT DRIVER_VERSION)
|
||||
set(DRIVER_VERSION "bb9702d5d3d3358804b1d483e7648dc55a2b7826")
|
||||
set(DRIVER_CHECKSUM "SHA256=447aa085ccedcd649e91f68aefff13d4ca2a9ddc0faa5c4e30dd76d45ae47267")
|
||||
set(DRIVER_VERSION "fd46dd139a8e35692a7d40ab2f0ed2016df827cf")
|
||||
set(DRIVER_CHECKSUM "SHA256=7c14a4b5f282c9e1e4496f5416d73c3132c72954d581e1a737f82ffa0e3a6bdd")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
|
||||
@@ -19,7 +19,7 @@ message(STATUS "Libs version: ${FALCOSECURITY_LIBS_VERSION}")
|
||||
|
||||
ExternalProject_Add(
|
||||
falcosecurity-libs
|
||||
URL "https://github.com/Andreagit97/libs/archive/${FALCOSECURITY_LIBS_VERSION}.tar.gz"
|
||||
URL "https://github.com/falcosecurity/libs/archive/${FALCOSECURITY_LIBS_VERSION}.tar.gz"
|
||||
URL_HASH "${FALCOSECURITY_LIBS_CHECKSUM}"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
||||
@@ -27,8 +27,8 @@ else()
|
||||
# In case you want to test against another falcosecurity/libs version (or branch, or commit) just pass the variable -
|
||||
# ie., `cmake -DFALCOSECURITY_LIBS_VERSION=dev ..`
|
||||
if(NOT FALCOSECURITY_LIBS_VERSION)
|
||||
set(FALCOSECURITY_LIBS_VERSION "bb9702d5d3d3358804b1d483e7648dc55a2b7826")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=447aa085ccedcd649e91f68aefff13d4ca2a9ddc0faa5c4e30dd76d45ae47267")
|
||||
set(FALCOSECURITY_LIBS_VERSION "fd46dd139a8e35692a7d40ab2f0ed2016df827cf")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=7c14a4b5f282c9e1e4496f5416d73c3132c72954d581e1a737f82ffa0e3a6bdd")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
@@ -60,9 +60,6 @@ set(LIBSINSP_DIR "${FALCOSECURITY_LIBS_SOURCE_DIR}")
|
||||
# configure gVisor support
|
||||
set(BUILD_LIBSCAP_GVISOR ${BUILD_FALCO_GVISOR} CACHE BOOL "")
|
||||
|
||||
# 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 "")
|
||||
|
||||
@@ -19,11 +19,11 @@ if(NOT DEFINED PLUGINS_COMPONENT_NAME)
|
||||
set(PLUGINS_COMPONENT_NAME "${CMAKE_PROJECT_NAME}-plugins")
|
||||
endif()
|
||||
|
||||
set(PLUGIN_K8S_AUDIT_VERSION "0.4.0")
|
||||
set(PLUGIN_K8S_AUDIT_VERSION "0.4.0-rc1")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(PLUGIN_K8S_AUDIT_HASH "ded0b5419f40084547620ccc48b19768e5e89457b85cfe8fbe496ca72267a3a4")
|
||||
set(PLUGIN_K8S_AUDIT_HASH "9b77560861ae2b1539a32a542e0b282b4ae83e0a8c26aad7ecefd3e721e9eb99")
|
||||
else() # aarch64
|
||||
set(PLUGIN_K8S_AUDIT_HASH "775cba666612114bc5b0c36f2e3c4557f5adbffcca2d77e72be87c6fcbf51ceb")
|
||||
set(PLUGIN_K8S_AUDIT_HASH "9c7de9a1213dc2e125f1ad2302818e5d34a7c95bfc67532b9d37395c60785d02")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
@@ -39,18 +39,18 @@ install(FILES "${PROJECT_BINARY_DIR}/k8saudit-plugin-prefix/src/k8saudit-plugin/
|
||||
ExternalProject_Add(
|
||||
k8saudit-rules
|
||||
URL "https://download.falco.org/plugins/stable/k8saudit-rules-${PLUGIN_K8S_AUDIT_VERSION}.tar.gz"
|
||||
URL_HASH "SHA256=53948fac0345e718d673142a992ac820135f771141dfaa9719c7575ac8ae6878"
|
||||
URL_HASH "SHA256=f65982fd1c6bc12ae8db833c36127a70252464bd5983fd75c39b91d630eb7f40"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
install(FILES "${PROJECT_BINARY_DIR}/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml" DESTINATION "${FALCO_ETC_DIR}" COMPONENT "${PLUGINS_COMPONENT_NAME}")
|
||||
|
||||
set(PLUGIN_CLOUDTRAIL_VERSION "0.6.0")
|
||||
set(PLUGIN_CLOUDTRAIL_VERSION "0.6.0-rc1")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "80e0c33f30c01a90efb7e9a671d978ff9679c462e3105020238abf31230e49a9")
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "a6c6acf16f7b4acd2b836e2be514346ee15a1e5adce936bd97ab6338d16ad6f9")
|
||||
else() # aarch64
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "a3e739932e66d44be848a68857fa15f56134d5246a1b9ab912c81f91b68fb23f")
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "a6105cb3864a613b3488c60c723163630484bc36b2aa219fb1c730c7735fb5fa")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
@@ -66,18 +66,18 @@ install(FILES "${PROJECT_BINARY_DIR}/cloudtrail-plugin-prefix/src/cloudtrail-plu
|
||||
ExternalProject_Add(
|
||||
cloudtrail-rules
|
||||
URL "https://download.falco.org/plugins/stable/cloudtrail-rules-${PLUGIN_CLOUDTRAIL_VERSION}.tar.gz"
|
||||
URL_HASH "SHA256=e0dccb7b0f1d24b1e526a33ffd973ea5f2ac2879dbc999e119419ebfd24305ff"
|
||||
URL_HASH "SHA256=4df7a0d56300d6077807bc205a8ab7ab3b45c495adcc209c5cca1e8da6fc93c6"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
install(FILES "${PROJECT_BINARY_DIR}/cloudtrail-rules-prefix/src/cloudtrail-rules/aws_cloudtrail_rules.yaml" DESTINATION "${FALCO_ETC_DIR}" COMPONENT "${PLUGINS_COMPONENT_NAME}")
|
||||
|
||||
set(PLUGIN_JSON_VERSION "0.6.0")
|
||||
set(PLUGIN_JSON_VERSION "0.6.0-rc1")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(PLUGIN_JSON_HASH "15fb7eddd978e8bb03f05412e9446e264e4548d7423b3d724b99d6d87a8c1b27")
|
||||
set(PLUGIN_JSON_HASH "7969e4731e529c5a9d9895ee52ec1845d4d1889cfa3562170288bb7a593bf6b9")
|
||||
else() # aarch64
|
||||
set(PLUGIN_JSON_HASH "4db23f35a750e10a5b7b54c9aa469a7587705e7faa22927e941b41f3c5533e9f")
|
||||
set(PLUGIN_JSON_HASH "c19fd1b64228ff95b1dc88d441143017807aa59ba57ae868a5f7db85b93bff99")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
|
||||
@@ -15,7 +15,7 @@ RUN if [ "$TARGETARCH" = "amd64" ] ; then curl -L -o grpcurl.tar.gz \
|
||||
https://github.com/fullstorydev/grpcurl/releases/download/v1.8.6/grpcurl_1.8.6_linux_arm64.tar.gz; \
|
||||
fi;
|
||||
|
||||
RUN dnf install -y python-pip python docker findutils jq unzip sed curl && dnf clean all
|
||||
RUN dnf install -y python-pip python docker findutils jq unzip && dnf clean all
|
||||
ENV PATH="/root/.local/bin/:${PATH}"
|
||||
RUN pip install --user avocado-framework==69.0
|
||||
RUN pip install --user avocado-framework-plugin-varianter-yaml-to-mux==69.0
|
||||
|
||||
@@ -367,7 +367,7 @@
|
||||
desc: Detect any new ssh connection to a host other than those in an allowed group of hosts
|
||||
condition: (inbound_outbound) and ssh_port and not allowed_ssh_hosts
|
||||
enabled: false
|
||||
output: Disallowed SSH Connection (command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
||||
output: Disallowed SSH Connection (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [network, mitre_remote_service]
|
||||
|
||||
@@ -397,7 +397,7 @@
|
||||
(fd.snet in (allowed_outbound_destination_networks)) or
|
||||
(fd.sip.name in (allowed_outbound_destination_domains)))
|
||||
enabled: false
|
||||
output: Disallowed outbound connection destination (command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
||||
output: Disallowed outbound connection destination (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [network]
|
||||
|
||||
@@ -418,7 +418,7 @@
|
||||
(fd.cnet in (allowed_inbound_source_networks)) or
|
||||
(fd.cip.name in (allowed_inbound_source_domains)))
|
||||
enabled: false
|
||||
output: Disallowed inbound connection source (command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
||||
output: Disallowed inbound connection source (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [network]
|
||||
|
||||
@@ -461,7 +461,7 @@
|
||||
and not exe_running_docker_save
|
||||
and not user_known_shell_config_modifiers
|
||||
output: >
|
||||
a shell configuration file has been modified (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid pcmdline=%proc.pcmdline file=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
a shell configuration file has been modified (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pcmdline=%proc.pcmdline file=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
priority:
|
||||
WARNING
|
||||
tags: [file, mitre_persistence]
|
||||
@@ -479,7 +479,7 @@
|
||||
(not proc.name in (shell_binaries))
|
||||
enabled: false
|
||||
output: >
|
||||
a shell configuration file was read by a non-shell program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid file=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
a shell configuration file was read by a non-shell program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
priority:
|
||||
WARNING
|
||||
tags: [file, mitre_discovery]
|
||||
@@ -495,7 +495,7 @@
|
||||
not user_known_cron_jobs
|
||||
enabled: false
|
||||
output: >
|
||||
Cron jobs were scheduled to run (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid
|
||||
Cron jobs were scheduled to run (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
|
||||
file=%fd.name container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
priority:
|
||||
NOTICE
|
||||
@@ -874,7 +874,7 @@
|
||||
and not exe_running_docker_save
|
||||
and not user_known_update_package_registry
|
||||
output: >
|
||||
Repository files get updated (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid pcmdline=%proc.pcmdline file=%fd.name newpath=%evt.arg.newpath container_id=%container.id image=%container.image.repository)
|
||||
Repository files get updated (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pcmdline=%proc.pcmdline file=%fd.name newpath=%evt.arg.newpath container_id=%container.id image=%container.image.repository)
|
||||
priority:
|
||||
NOTICE
|
||||
tags: [filesystem, mitre_persistence]
|
||||
@@ -896,7 +896,7 @@
|
||||
and not user_known_write_below_binary_dir_activities
|
||||
output: >
|
||||
File below a known binary directory opened for writing (user=%user.name user_loginuid=%user.loginuid
|
||||
command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
|
||||
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
|
||||
priority: ERROR
|
||||
tags: [filesystem, mitre_persistence]
|
||||
|
||||
@@ -952,7 +952,7 @@
|
||||
and not user_known_write_monitored_dir_conditions
|
||||
output: >
|
||||
File below a monitored directory opened for writing (user=%user.name user_loginuid=%user.loginuid
|
||||
command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
|
||||
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
|
||||
priority: ERROR
|
||||
tags: [filesystem, mitre_persistence]
|
||||
|
||||
@@ -969,7 +969,7 @@
|
||||
enabled: true
|
||||
output: >
|
||||
Read monitored file via directory traversal (username=%user.name useruid=%user.uid user_loginuid=%user.loginuid program=%proc.name exe=%proc.exepath
|
||||
command=%proc.cmdline pid=%proc.pid parent=%proc.pname file=%fd.name fileraw=%fd.nameraw parent=%proc.pname
|
||||
command=%proc.cmdline parent=%proc.pname file=%fd.name fileraw=%fd.nameraw parent=%proc.pname
|
||||
gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository returncode=%evt.res cwd=%proc.cwd)
|
||||
priority: WARNING
|
||||
tags: [filesystem, mitre_discovery, mitre_exfiltration, mitre_credential_access]
|
||||
@@ -990,7 +990,7 @@
|
||||
enabled: false
|
||||
output: >
|
||||
ssh-related file/directory read by non-ssh program (user=%user.name user_loginuid=%user.loginuid
|
||||
command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository)
|
||||
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository)
|
||||
priority: ERROR
|
||||
tags: [filesystem, mitre_discovery]
|
||||
|
||||
@@ -1276,7 +1276,7 @@
|
||||
- rule: Write below etc
|
||||
desc: an attempt to write to any file below /etc
|
||||
condition: write_etc_common
|
||||
output: "File below /etc opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent=%proc.pname pcmdline=%proc.pcmdline file=%fd.name program=%proc.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)"
|
||||
output: "File below /etc opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent=%proc.pname pcmdline=%proc.pcmdline file=%fd.name program=%proc.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)"
|
||||
priority: ERROR
|
||||
tags: [filesystem, mitre_persistence]
|
||||
|
||||
@@ -1373,7 +1373,7 @@
|
||||
and not known_root_conditions
|
||||
and not user_known_write_root_conditions
|
||||
and not user_known_write_below_root_activities
|
||||
output: "File below / or /root opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent=%proc.pname file=%fd.name program=%proc.name container_id=%container.id image=%container.image.repository)"
|
||||
output: "File below / or /root opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent=%proc.pname file=%fd.name program=%proc.name container_id=%container.id image=%container.image.repository)"
|
||||
priority: ERROR
|
||||
tags: [filesystem, mitre_persistence]
|
||||
|
||||
@@ -1391,7 +1391,7 @@
|
||||
condition: sensitive_files and open_read and server_procs and not proc_is_new and proc.name!="sshd" and not user_known_read_sensitive_files_activities
|
||||
output: >
|
||||
Sensitive file opened for reading by trusted program after startup (user=%user.name user_loginuid=%user.loginuid
|
||||
command=%proc.cmdline pid=%proc.pid parent=%proc.pname file=%fd.name parent=%proc.pname gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
|
||||
command=%proc.cmdline parent=%proc.pname file=%fd.name parent=%proc.pname gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
|
||||
priority: WARNING
|
||||
tags: [filesystem, mitre_credential_access]
|
||||
|
||||
@@ -1461,7 +1461,7 @@
|
||||
and not user_read_sensitive_file_containers
|
||||
output: >
|
||||
Sensitive file opened for reading by non-trusted program (user=%user.name user_loginuid=%user.loginuid program=%proc.name
|
||||
command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)
|
||||
command=%proc.cmdline file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)
|
||||
priority: WARNING
|
||||
tags: [filesystem, mitre_credential_access, mitre_discovery]
|
||||
|
||||
@@ -1485,7 +1485,7 @@
|
||||
and not exe_running_docker_save
|
||||
and not amazon_linux_running_python_yum
|
||||
and not user_known_write_rpm_database_activities
|
||||
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository)"
|
||||
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository)"
|
||||
priority: ERROR
|
||||
tags: [filesystem, software_mgmt, mitre_persistence]
|
||||
|
||||
@@ -1524,7 +1524,7 @@
|
||||
and not user_known_db_spawned_processes
|
||||
output: >
|
||||
Database-related program spawned process other than itself (user=%user.name user_loginuid=%user.loginuid
|
||||
program=%proc.cmdline pid=%proc.pid parent=%proc.pname container_id=%container.id image=%container.image.repository)
|
||||
program=%proc.cmdline parent=%proc.pname container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [process, database, mitre_execution]
|
||||
|
||||
@@ -1535,7 +1535,7 @@
|
||||
desc: an attempt to modify any file below a set of binary directories.
|
||||
condition: bin_dir_rename and modify and not package_mgmt_procs and not exe_running_docker_save and not user_known_modify_bin_dir_activities
|
||||
output: >
|
||||
File below known binary directory renamed/removed (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid
|
||||
File below known binary directory renamed/removed (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
|
||||
pcmdline=%proc.pcmdline operation=%evt.type file=%fd.name %evt.args container_id=%container.id image=%container.image.repository)
|
||||
priority: ERROR
|
||||
tags: [filesystem, mitre_persistence]
|
||||
@@ -1553,7 +1553,7 @@
|
||||
and not exe_running_docker_save
|
||||
output: >
|
||||
Directory below known binary directory created (user=%user.name user_loginuid=%user.loginuid
|
||||
command=%proc.cmdline pid=%proc.pid directory=%evt.arg.path container_id=%container.id image=%container.image.repository)
|
||||
command=%proc.cmdline directory=%evt.arg.path container_id=%container.id image=%container.image.repository)
|
||||
priority: ERROR
|
||||
tags: [filesystem, mitre_persistence]
|
||||
|
||||
@@ -1594,7 +1594,7 @@
|
||||
and not user_known_change_thread_namespace_activities
|
||||
enabled: false
|
||||
output: >
|
||||
Namespace change (setns) by unexpected program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid
|
||||
Namespace change (setns) by unexpected program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
|
||||
parent=%proc.pname %container.info container_id=%container.id image=%container.image.repository:%container.image.tag)
|
||||
priority: NOTICE
|
||||
tags: [process, mitre_privilege_escalation, mitre_lateral_movement]
|
||||
@@ -1741,7 +1741,7 @@
|
||||
and not user_shell_container_exclusions
|
||||
output: >
|
||||
Shell spawned by untrusted binary (user=%user.name user_loginuid=%user.loginuid shell=%proc.name parent=%proc.pname
|
||||
cmdline=%proc.cmdline pid=%proc.pid pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3]
|
||||
cmdline=%proc.cmdline pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3]
|
||||
aname[4]=%proc.aname[4] aname[5]=%proc.aname[5] aname[6]=%proc.aname[6] aname[7]=%proc.aname[7] container_id=%container.id image=%container.image.repository)
|
||||
priority: DEBUG
|
||||
tags: [shell, mitre_execution]
|
||||
@@ -1925,7 +1925,7 @@
|
||||
and not falco_privileged_containers
|
||||
and not user_privileged_containers
|
||||
and not redhat_image
|
||||
output: Privileged container started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag)
|
||||
output: Privileged container started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
||||
priority: INFO
|
||||
tags: [container, cis, mitre_privilege_escalation, mitre_lateral_movement]
|
||||
|
||||
@@ -1949,7 +1949,7 @@
|
||||
and excessively_capable_container
|
||||
and not falco_privileged_containers
|
||||
and not user_privileged_containers
|
||||
output: Excessively capable container started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag cap_permitted=%thread.cap_permitted)
|
||||
output: Excessively capable container started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag cap_permitted=%thread.cap_permitted)
|
||||
priority: INFO
|
||||
tags: [container, cis, mitre_privilege_escalation, mitre_lateral_movement]
|
||||
|
||||
@@ -1995,7 +1995,7 @@
|
||||
and sensitive_mount
|
||||
and not falco_sensitive_mount_containers
|
||||
and not user_sensitive_mount_containers
|
||||
output: Container with sensitive mount started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag mounts=%container.mounts)
|
||||
output: Container with sensitive mount started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag mounts=%container.mounts)
|
||||
priority: INFO
|
||||
tags: [container, cis, mitre_lateral_movement]
|
||||
|
||||
@@ -2015,7 +2015,7 @@
|
||||
desc: >
|
||||
Detect the initial process started by a container that is not in a list of allowed containers.
|
||||
condition: container_started and container and not allowed_containers
|
||||
output: Container started and not in allowed list (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag)
|
||||
output: Container started and not in allowed list (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
||||
priority: WARNING
|
||||
tags: [container, mitre_lateral_movement]
|
||||
|
||||
@@ -2030,7 +2030,7 @@
|
||||
- rule: System user interactive
|
||||
desc: an attempt to run interactive commands by a system (i.e. non-login) user
|
||||
condition: spawned_process and system_users and interactive and not user_known_system_user_login
|
||||
output: "System user ran an interactive command (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid container_id=%container.id image=%container.image.repository)"
|
||||
output: "System user ran an interactive command (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline container_id=%container.id image=%container.image.repository)"
|
||||
priority: INFO
|
||||
tags: [users, mitre_remote_access_tools]
|
||||
|
||||
@@ -2048,7 +2048,7 @@
|
||||
and not user_expected_terminal_shell_in_container_conditions
|
||||
output: >
|
||||
A shell was spawned in a container with an attached terminal (user=%user.name user_loginuid=%user.loginuid %container.info
|
||||
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline pid=%proc.pid terminal=%proc.tty container_id=%container.id image=%container.image.repository)
|
||||
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [container, shell, mitre_execution]
|
||||
|
||||
@@ -2124,7 +2124,7 @@
|
||||
and not user_expected_system_procs_network_activity_conditions
|
||||
output: >
|
||||
Known system binary sent/received network traffic
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid connection=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [network, mitre_exfiltration]
|
||||
|
||||
@@ -2163,7 +2163,7 @@
|
||||
enabled: false
|
||||
output: >
|
||||
Program run with disallowed HTTP_PROXY environment variable
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid env=%proc.env parent=%proc.pname container_id=%container.id image=%container.image.repository)
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline env=%proc.env parent=%proc.pname container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [host, users]
|
||||
|
||||
@@ -2179,7 +2179,7 @@
|
||||
enabled: false
|
||||
output: >
|
||||
Interpreted program received/listened for network traffic
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid connection=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [network, mitre_exfiltration]
|
||||
|
||||
@@ -2190,7 +2190,7 @@
|
||||
enabled: false
|
||||
output: >
|
||||
Interpreted program performed outgoing network connection
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid connection=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [network, mitre_exfiltration]
|
||||
|
||||
@@ -2229,7 +2229,7 @@
|
||||
enabled: false
|
||||
output: >
|
||||
Unexpected UDP Traffic Seen
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid connection=%fd.name proto=%fd.l4proto evt=%evt.type %evt.args container_id=%container.id image=%container.image.repository)
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline connection=%fd.name proto=%fd.l4proto evt=%evt.type %evt.args container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [network, mitre_exfiltration]
|
||||
|
||||
@@ -2289,7 +2289,7 @@
|
||||
and not user_known_non_sudo_setuid_conditions
|
||||
output: >
|
||||
Unexpected setuid call by non-sudo, non-root program (user=%user.name user_loginuid=%user.loginuid cur_uid=%user.uid parent=%proc.pname
|
||||
command=%proc.cmdline pid=%proc.pid uid=%evt.arg.uid container_id=%container.id image=%container.image.repository)
|
||||
command=%proc.cmdline uid=%evt.arg.uid container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [users, mitre_privilege_escalation]
|
||||
|
||||
@@ -2321,7 +2321,7 @@
|
||||
not user_known_user_management_activities
|
||||
output: >
|
||||
User management binary command run outside of container
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4])
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4])
|
||||
priority: NOTICE
|
||||
tags: [host, users, mitre_persistence]
|
||||
|
||||
@@ -2345,7 +2345,7 @@
|
||||
and not fd.name in (allowed_dev_files)
|
||||
and not fd.name startswith /dev/tty
|
||||
and not user_known_create_files_below_dev_activities
|
||||
output: "File created below /dev by untrusted program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid file=%fd.name container_id=%container.id image=%container.image.repository)"
|
||||
output: "File created below /dev by untrusted program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)"
|
||||
priority: ERROR
|
||||
tags: [filesystem, mitre_persistence]
|
||||
|
||||
@@ -2367,7 +2367,7 @@
|
||||
- rule: Contact EC2 Instance Metadata Service From Container
|
||||
desc: Detect attempts to contact the EC2 Instance Metadata Service from a container
|
||||
condition: outbound and fd.sip="169.254.169.254" and container and not ec2_metadata_containers
|
||||
output: Outbound connection to EC2 instance metadata service (command=%proc.cmdline pid=%proc.pid connection=%fd.name %container.info image=%container.image.repository:%container.image.tag)
|
||||
output: Outbound connection to EC2 instance metadata service (command=%proc.cmdline connection=%fd.name %container.info image=%container.image.repository:%container.image.tag)
|
||||
priority: NOTICE
|
||||
tags: [network, aws, container, mitre_discovery]
|
||||
|
||||
@@ -2384,7 +2384,7 @@
|
||||
desc: Detect attempts to contact the Cloud Instance Metadata Service from a container
|
||||
condition: outbound and fd.sip="169.254.169.254" and container and not user_known_metadata_access
|
||||
enabled: false
|
||||
output: Outbound connection to cloud instance metadata service (command=%proc.cmdline pid=%proc.pid connection=%fd.name %container.info image=%container.image.repository:%container.image.tag)
|
||||
output: Outbound connection to cloud instance metadata service (command=%proc.cmdline connection=%fd.name %container.info image=%container.image.repository:%container.image.tag)
|
||||
priority: NOTICE
|
||||
tags: [network, container, mitre_discovery]
|
||||
|
||||
@@ -2412,9 +2412,7 @@
|
||||
quay.io/prometheus-operator/prometheus-operator,
|
||||
k8s.gcr.io/ingress-nginx/kube-webhook-certgen, quay.io/spotahome/redis-operator,
|
||||
registry.opensource.zalan.do/acid/postgres-operator, registry.opensource.zalan.do/acid/postgres-operator-ui,
|
||||
rabbitmqoperator/cluster-operator,
|
||||
falcosecurity/falco-no-driver, docker.io/falcosecurity/falco-no-driver,
|
||||
public.ecr.aws/falcosecurity/falco-no-driver)
|
||||
rabbitmqoperator/cluster-operator)
|
||||
or (k8s.ns.name = "kube-system"))
|
||||
|
||||
- macro: k8s_api_server
|
||||
@@ -2432,7 +2430,7 @@
|
||||
not k8s_containers and
|
||||
k8s_api_server and
|
||||
not user_known_contact_k8s_api_server_activities
|
||||
output: Unexpected connection to K8s API Server from container (command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag connection=%fd.name)
|
||||
output: Unexpected connection to K8s API Server from container (command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag connection=%fd.name)
|
||||
priority: NOTICE
|
||||
tags: [network, k8s, container, mitre_discovery]
|
||||
|
||||
@@ -2448,7 +2446,7 @@
|
||||
- rule: Unexpected K8s NodePort Connection
|
||||
desc: Detect attempts to use K8s NodePorts from a container
|
||||
condition: (inbound_outbound) and fd.sport >= 30000 and fd.sport <= 32767 and container and not nodeport_containers
|
||||
output: Unexpected K8s NodePort Connection (command=%proc.cmdline pid=%proc.pid connection=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
output: Unexpected K8s NodePort Connection (command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
priority: NOTICE
|
||||
tags: [network, k8s, container, mitre_port_knocking]
|
||||
|
||||
@@ -2485,7 +2483,7 @@
|
||||
and not pkg_mgmt_in_kube_proxy
|
||||
output: >
|
||||
Package management process launched in container (user=%user.name user_loginuid=%user.loginuid
|
||||
command=%proc.cmdline pid=%proc.pid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
command=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
priority: ERROR
|
||||
tags: [process, mitre_persistence]
|
||||
|
||||
@@ -2499,7 +2497,7 @@
|
||||
)
|
||||
output: >
|
||||
Netcat runs inside container that allows remote code execution (user=%user.name user_loginuid=%user.loginuid
|
||||
command=%proc.cmdline pid=%proc.pid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
command=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
priority: WARNING
|
||||
tags: [network, process, mitre_execution]
|
||||
|
||||
@@ -2511,7 +2509,7 @@
|
||||
condition: >
|
||||
spawned_process and container and network_tool_procs and not user_known_network_tool_activities
|
||||
output: >
|
||||
Network tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent_process=%proc.pname
|
||||
Network tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname
|
||||
container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
priority: NOTICE
|
||||
tags: [network, process, mitre_discovery, mitre_exfiltration]
|
||||
@@ -2531,7 +2529,7 @@
|
||||
network_tool_procs and
|
||||
not user_known_network_tool_activities
|
||||
output: >
|
||||
Network tool launched on host (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent_process=%proc.pname)
|
||||
Network tool launched on host (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname)
|
||||
priority: NOTICE
|
||||
tags: [network, process, mitre_discovery, mitre_exfiltration]
|
||||
|
||||
@@ -2567,7 +2565,7 @@
|
||||
)
|
||||
output: >
|
||||
Grep private keys or passwords activities found
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid container_id=%container.id container_name=%container.name
|
||||
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline container_id=%container.id container_name=%container.name
|
||||
image=%container.image.repository:%container.image.tag)
|
||||
priority:
|
||||
WARNING
|
||||
@@ -2601,7 +2599,7 @@
|
||||
not trusted_logging_images and
|
||||
not allowed_clear_log_files
|
||||
output: >
|
||||
Log files were tampered (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid file=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
Log files were tampered (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
priority:
|
||||
WARNING
|
||||
tags: [file, mitre_defense_evasion]
|
||||
@@ -2619,7 +2617,7 @@
|
||||
desc: Detect process running to clear bulk data from disk
|
||||
condition: spawned_process and clear_data_procs and not user_known_remove_data_activities
|
||||
output: >
|
||||
Bulk data has been removed from disk (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid file=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
Bulk data has been removed from disk (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
|
||||
priority:
|
||||
WARNING
|
||||
tags: [process, mitre_persistence]
|
||||
@@ -2660,7 +2658,7 @@
|
||||
not var_lib_docker_filepath and
|
||||
not proc.name in (docker_binaries)
|
||||
output: >
|
||||
Shell history had been deleted or renamed (user=%user.name user_loginuid=%user.loginuid type=%evt.type command=%proc.cmdline pid=%proc.pid fd.name=%fd.name name=%evt.arg.name path=%evt.arg.path oldpath=%evt.arg.oldpath %container.info)
|
||||
Shell history had been deleted or renamed (user=%user.name user_loginuid=%user.loginuid type=%evt.type command=%proc.cmdline fd.name=%fd.name name=%evt.arg.name path=%evt.arg.path oldpath=%evt.arg.oldpath %container.info)
|
||||
priority:
|
||||
WARNING
|
||||
tags: [process, mitre_defense_evasion]
|
||||
@@ -2673,7 +2671,7 @@
|
||||
((spawned_process and proc.name in (shred, rm, mv) and proc.args contains "bash_history") or
|
||||
(open_write and fd.name contains "bash_history" and evt.arg.flags contains "O_TRUNC"))
|
||||
output: >
|
||||
Shell history had been deleted or renamed (user=%user.name user_loginuid=%user.loginuid type=%evt.type command=%proc.cmdline pid=%proc.pid fd.name=%fd.name name=%evt.arg.name path=%evt.arg.path oldpath=%evt.arg.oldpath %container.info)
|
||||
Shell history had been deleted or renamed (user=%user.name user_loginuid=%user.loginuid type=%evt.type command=%proc.cmdline fd.name=%fd.name name=%evt.arg.name path=%evt.arg.path oldpath=%evt.arg.oldpath %container.info)
|
||||
priority:
|
||||
WARNING
|
||||
tags: [process, mitre_defense_evasion]
|
||||
@@ -2700,7 +2698,7 @@
|
||||
enabled: false
|
||||
output: >
|
||||
Setuid or setgid bit is set via chmod (fd=%evt.arg.fd filename=%evt.arg.filename mode=%evt.arg.mode user=%user.name user_loginuid=%user.loginuid process=%proc.name
|
||||
command=%proc.cmdline pid=%proc.pid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
command=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
priority:
|
||||
NOTICE
|
||||
tags: [process, mitre_persistence]
|
||||
@@ -2722,7 +2720,7 @@
|
||||
and not exe_running_docker_save
|
||||
enabled: false
|
||||
output: >
|
||||
Hidden file or directory created (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid
|
||||
Hidden file or directory created (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
|
||||
file=%fd.name newpath=%evt.arg.newpath container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
priority:
|
||||
NOTICE
|
||||
@@ -2747,7 +2745,7 @@
|
||||
and remote_file_copy_procs
|
||||
and not user_known_remote_file_copy_activities
|
||||
output: >
|
||||
Remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent_process=%proc.pname
|
||||
Remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname
|
||||
container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
priority: NOTICE
|
||||
tags: [network, process, mitre_lateral_movement, mitre_exfiltration]
|
||||
@@ -2758,7 +2756,7 @@
|
||||
create_symlink and
|
||||
(evt.arg.target in (sensitive_file_names) or evt.arg.target in (sensitive_directory_names))
|
||||
output: >
|
||||
Symlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid target=%evt.arg.target linkpath=%evt.arg.linkpath parent_process=%proc.pname)
|
||||
Symlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline target=%evt.arg.target linkpath=%evt.arg.linkpath parent_process=%proc.pname)
|
||||
priority: WARNING
|
||||
tags: [file, mitre_exfiltration]
|
||||
|
||||
@@ -2768,7 +2766,7 @@
|
||||
create_hardlink and
|
||||
(evt.arg.oldpath in (sensitive_file_names))
|
||||
output: >
|
||||
Hardlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid target=%evt.arg.oldpath linkpath=%evt.arg.newpath parent_process=%proc.pname)
|
||||
Hardlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline target=%evt.arg.oldpath linkpath=%evt.arg.newpath parent_process=%proc.pname)
|
||||
priority: WARNING
|
||||
tags: [file, mitre_exfiltration]
|
||||
|
||||
@@ -2873,14 +2871,14 @@
|
||||
desc: Miners typically connect to miner pools on common ports.
|
||||
condition: net_miner_pool and not trusted_images_query_miner_domain_dns
|
||||
enabled: false
|
||||
output: Outbound connection to IP/Port flagged by https://cryptoioc.ch (command=%proc.cmdline pid=%proc.pid port=%fd.rport ip=%fd.rip container=%container.info image=%container.image.repository)
|
||||
output: Outbound connection to IP/Port flagged by https://cryptoioc.ch (command=%proc.cmdline port=%fd.rport ip=%fd.rip container=%container.info image=%container.image.repository)
|
||||
priority: CRITICAL
|
||||
tags: [network, mitre_execution]
|
||||
|
||||
- rule: Detect crypto miners using the Stratum protocol
|
||||
desc: Miners typically specify the mining pool to connect to with a URI that begins with 'stratum+tcp'
|
||||
condition: spawned_process and (proc.cmdline contains "stratum+tcp" or proc.cmdline contains "stratum2+tcp" or proc.cmdline contains "stratum+ssl" or proc.cmdline contains "stratum2+ssl")
|
||||
output: Possible miner running (command=%proc.cmdline pid=%proc.pid container=%container.info image=%container.image.repository)
|
||||
output: Possible miner running (command=%proc.cmdline container=%container.info image=%container.image.repository)
|
||||
priority: CRITICAL
|
||||
tags: [process, mitre_execution]
|
||||
|
||||
@@ -2910,7 +2908,7 @@
|
||||
- rule: The docker client is executed in a container
|
||||
desc: Detect a k8s client tool executed inside a container
|
||||
condition: spawned_process and container and not user_known_k8s_client_container_parens and proc.name in (k8s_client_binaries)
|
||||
output: "Docker or kubernetes client executed in container (user=%user.name user_loginuid=%user.loginuid %container.info parent=%proc.pname cmdline=%proc.cmdline pid=%proc.pid image=%container.image.repository:%container.image.tag)"
|
||||
output: "Docker or kubernetes client executed in container (user=%user.name user_loginuid=%user.loginuid %container.info parent=%proc.pname cmdline=%proc.cmdline image=%container.image.repository:%container.image.tag)"
|
||||
priority: WARNING
|
||||
tags: [container, mitre_execution]
|
||||
|
||||
@@ -2920,7 +2918,7 @@
|
||||
- rule: Packet socket created in container
|
||||
desc: Detect new packet socket at the device driver (OSI Layer 2) level in a container. Packet socket could be used for ARP Spoofing and privilege escalation(CVE-2020-14386) by attacker.
|
||||
condition: evt.type=socket and evt.arg[0]=AF_PACKET and container and not proc.name in (user_known_packet_socket_binaries)
|
||||
output: Packet socket was created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid socket_info=%evt.args container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
output: Packet socket was created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline socket_info=%evt.args container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
priority: NOTICE
|
||||
tags: [network, mitre_discovery]
|
||||
|
||||
@@ -2951,7 +2949,7 @@
|
||||
enabled: false
|
||||
output: >
|
||||
Network connection outside local subnet
|
||||
(command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id
|
||||
(command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id
|
||||
image=%container.image.repository namespace=%k8s.ns.name
|
||||
fd.rip.name=%fd.rip.name fd.lip.name=%fd.lip.name fd.cip.name=%fd.cip.name fd.sip.name=%fd.sip.name)
|
||||
priority: WARNING
|
||||
@@ -2985,7 +2983,7 @@
|
||||
enabled: false
|
||||
output: >
|
||||
Network connection outside authorized port and binary
|
||||
(command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id
|
||||
(command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id
|
||||
image=%container.image.repository)
|
||||
priority: WARNING
|
||||
tags: [network]
|
||||
@@ -3000,7 +2998,7 @@
|
||||
desc: Detect redirecting stdout/stdin to network connection in container (potential reverse shell).
|
||||
condition: dup and container and evt.rawres in (0, 1, 2) and fd.type in ("ipv4", "ipv6") and not user_known_stand_streams_redirect_activities
|
||||
output: >
|
||||
Redirect stdout/stdin to network connection (user=%user.name user_loginuid=%user.loginuid %container.info process=%proc.name parent=%proc.pname cmdline=%proc.cmdline pid=%proc.pid terminal=%proc.tty container_id=%container.id image=%container.image.repository fd.name=%fd.name fd.num=%fd.num fd.type=%fd.type fd.sip=%fd.sip)
|
||||
Redirect stdout/stdin to network connection (user=%user.name user_loginuid=%user.loginuid %container.info process=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id image=%container.image.repository fd.name=%fd.name fd.num=%fd.num fd.type=%fd.type fd.sip=%fd.sip)
|
||||
priority: NOTICE
|
||||
|
||||
# The two Container Drift rules below will fire when a new executable is created in a container.
|
||||
@@ -3029,7 +3027,7 @@
|
||||
(evt.arg.mode contains "S_IXGRP") or
|
||||
(evt.arg.mode contains "S_IXOTH"))
|
||||
enabled: false
|
||||
output: Drift detected (chmod), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type)
|
||||
output: Drift detected (chmod), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type)
|
||||
priority: ERROR
|
||||
|
||||
# ****************************************************************************
|
||||
@@ -3046,7 +3044,7 @@
|
||||
not user_known_container_drift_activities and
|
||||
evt.rawres>=0
|
||||
enabled: false
|
||||
output: Drift detected (open+create), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type)
|
||||
output: Drift detected (open+create), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type)
|
||||
priority: ERROR
|
||||
|
||||
- list: c2_server_ip_list
|
||||
@@ -3055,7 +3053,7 @@
|
||||
- rule: Outbound Connection to C2 Servers
|
||||
desc: Detect outbound connection to command & control servers
|
||||
condition: outbound and fd.sip in (c2_server_ip_list)
|
||||
output: Outbound connection to C2 server (command=%proc.cmdline pid=%proc.pid connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
||||
output: Outbound connection to C2 server (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
||||
priority: WARNING
|
||||
tags: [network]
|
||||
|
||||
@@ -3090,7 +3088,7 @@
|
||||
- rule: Sudo Potential Privilege Escalation
|
||||
desc: Privilege escalation vulnerability affecting sudo (<= 1.9.5p2). Executing sudo using sudoedit -s or sudoedit -i command with command-line argument that ends with a single backslash character from an unprivileged user it's possible to elevate the user privileges to root.
|
||||
condition: spawned_process and user.uid != 0 and (proc.name=sudoedit or proc.name = sudo) and (proc.args contains -s or proc.args contains -i or proc.args contains --login) and (proc.args contains "\ " or proc.args endswith \)
|
||||
output: "Detect Sudo Privilege Escalation Exploit (CVE-2021-3156) (user=%user.name parent=%proc.pname cmdline=%proc.cmdline pid=%proc.pid %container.info)"
|
||||
output: "Detect Sudo Privilege Escalation Exploit (CVE-2021-3156) (user=%user.name parent=%proc.pname cmdline=%proc.cmdline %container.info)"
|
||||
priority: CRITICAL
|
||||
tags: [filesystem, mitre_privilege_escalation]
|
||||
|
||||
@@ -3100,7 +3098,7 @@
|
||||
spawned_process and container
|
||||
and container.privileged=true
|
||||
and proc.name=debugfs
|
||||
output: Debugfs launched started in a privileged container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag)
|
||||
output: Debugfs launched started in a privileged container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
||||
priority: WARNING
|
||||
tags: [container, cis, mitre_lateral_movement]
|
||||
|
||||
@@ -3124,7 +3122,7 @@
|
||||
and not mount_info
|
||||
and not known_gke_mount_in_privileged_containers
|
||||
and not user_known_mount_in_privileged_containers
|
||||
output: Mount was executed inside a privileged container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag)
|
||||
output: Mount was executed inside a privileged container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
||||
priority: WARNING
|
||||
tags: [container, cis, mitre_lateral_movement]
|
||||
|
||||
@@ -3138,7 +3136,7 @@
|
||||
user.uid != 0 and
|
||||
(evt.rawres >= 0 or evt.res != -1) and
|
||||
not proc.name in (user_known_userfaultfd_processes)
|
||||
output: An userfaultfd syscall was successfully executed by an unprivileged user (user=%user.name user_loginuid=%user.loginuid process=%proc.name command=%proc.cmdline pid=%proc.pid %container.info image=%container.image.repository:%container.image.tag)
|
||||
output: An userfaultfd syscall was successfully executed by an unprivileged user (user=%user.name user_loginuid=%user.loginuid process=%proc.name command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
||||
priority: CRITICAL
|
||||
tags: [syscall, mitre_defense_evasion]
|
||||
|
||||
@@ -3168,7 +3166,7 @@
|
||||
(ingress_remote_file_copy_procs or curl_download) and
|
||||
not user_known_ingress_remote_file_copy_activities
|
||||
output: >
|
||||
Ingress remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pid=%proc.pid parent_process=%proc.pname
|
||||
Ingress remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname
|
||||
container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
priority: NOTICE
|
||||
tags: [network, process, mitre_command_and_control]
|
||||
@@ -3180,7 +3178,7 @@
|
||||
condition:
|
||||
spawned_process and user.uid != 0 and proc.name=pkexec and proc.args = ''
|
||||
output:
|
||||
"Detect Polkit pkexec Local Privilege Escalation Exploit (CVE-2021-4034) (user=%user.loginname uid=%user.loginuid command=%proc.cmdline pid=%proc.pid args=%proc.args)"
|
||||
"Detect Polkit pkexec Local Privilege Escalation Exploit (CVE-2021-4034) (user=%user.loginname uid=%user.loginuid command=%proc.cmdline args=%proc.args)"
|
||||
priority: CRITICAL
|
||||
tags: [process, mitre_privilege_escalation]
|
||||
|
||||
@@ -3203,7 +3201,7 @@
|
||||
desc: Detected Java process downloading a class file which could indicate a successful exploit of the log4shell Log4j vulnerability (CVE-2021-44228)
|
||||
condition: >
|
||||
java_network_read and evt.buffer bcontains cafebabe
|
||||
output: Java process class file download (user=%user.name user_loginname=%user.loginname user_loginuid=%user.loginuid event=%evt.type connection=%fd.name server_ip=%fd.sip server_port=%fd.sport proto=%fd.l4proto process=%proc.name command=%proc.cmdline pid=%proc.pid parent=%proc.pname buffer=%evt.buffer container_id=%container.id image=%container.image.repository)
|
||||
output: Java process class file download (user=%user.name user_loginname=%user.loginname user_loginuid=%user.loginuid event=%evt.type connection=%fd.name server_ip=%fd.sip server_port=%fd.sport proto=%fd.l4proto process=%proc.name command=%proc.cmdline parent=%proc.pname buffer=%evt.buffer container_id=%container.id image=%container.image.repository)
|
||||
priority: CRITICAL
|
||||
tags: [mitre_initial_access]
|
||||
|
||||
@@ -3219,7 +3217,7 @@
|
||||
open_write and container and (fd.name=/proc/self/exe or fd.name startswith /proc/self/fd/) and not docker_procs and not proc.cmdline = "runc:[1:CHILD] init"
|
||||
enabled: false
|
||||
output: >
|
||||
Detect Potential Container Breakout Exploit (CVE-2019-5736) (user=%user.name process=%proc.name file=%fd.name cmdline=%proc.cmdline pid=%proc.pid %container.info)
|
||||
Detect Potential Container Breakout Exploit (CVE-2019-5736) (user=%user.name process=%proc.name file=%fd.name cmdline=%proc.cmdline %container.info)
|
||||
priority: WARNING
|
||||
tags: [container, filesystem, mitre_initial_access]
|
||||
|
||||
@@ -3237,6 +3235,6 @@
|
||||
and not proc.name in (known_binaries_to_read_environment_variables_from_proc_files)
|
||||
output: >
|
||||
Environment variables were retrieved from /proc files (user=%user.name user_loginuid=%user.loginuid program=%proc.name
|
||||
command=%proc.cmdline pid=%proc.pid file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)
|
||||
command=%proc.cmdline file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)
|
||||
priority: WARNING
|
||||
tags: [filesystem, mitre_credential_access, mitre_discovery]
|
||||
|
||||
@@ -15,28 +15,20 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# Systemd
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/systemd/falco-kmod-inject.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/systemd")
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/systemd/falco-kmod.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/systemd")
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/systemd/falco-bpf.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/systemd")
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/systemd/falco-modern-bpf.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/systemd")
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/systemd/falco-plugin.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/systemd")
|
||||
|
||||
# Debian
|
||||
configure_file(debian/postinst.in debian/postinst)
|
||||
configure_file(debian/postrm.in debian/postrm)
|
||||
configure_file(debian/prerm.in debian/prerm)
|
||||
|
||||
# Rpm
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/debian/falco.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/debian")
|
||||
|
||||
configure_file(rpm/postinstall.in rpm/postinstall)
|
||||
configure_file(rpm/postuninstall.in rpm/postuninstall)
|
||||
configure_file(rpm/preuninstall.in rpm/preuninstall)
|
||||
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/rpm/falco.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/rpm")
|
||||
|
||||
configure_file(falco-driver-loader falco-driver-loader @ONLY)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security with ebpf
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
Conflicts=falco-kmod.service
|
||||
Conflicts=falco-modern-bpf.service
|
||||
Conflicts=falco-plugin.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Environment=FALCO_BPF_PROBE=
|
||||
ExecStartPre=/sbin/modprobe falco
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
ExecStopPost=/sbin/rmmod falco
|
||||
UMask=0077
|
||||
TimeoutSec=30
|
||||
RestartSec=15s
|
||||
@@ -21,7 +19,6 @@ ProtectSystem=full
|
||||
ProtectKernelTunables=true
|
||||
RestrictRealtime=true
|
||||
RestrictAddressFamilies=~AF_PACKET
|
||||
StandardOutput=null
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -17,65 +17,27 @@
|
||||
#
|
||||
set -e
|
||||
|
||||
chosen_driver=
|
||||
DKMS_PACKAGE_NAME="@PACKAGE_NAME@"
|
||||
DKMS_VERSION="@DRIVER_VERSION@"
|
||||
NAME="@PACKAGE_NAME@"
|
||||
|
||||
if [ "$1" = "configure" ]; then
|
||||
if [ -x /usr/bin/dialog ]; then
|
||||
# If dialog is installed, create a dialog to let users choose the correct driver for them
|
||||
CHOICE=$(dialog --clear --backtitle "Choose your preferred driver" --title "Falco driver" --menu "Choose one of the following options:" 15 40 4 \
|
||||
1 "Don't start" \
|
||||
2 "Kmod" \
|
||||
3 "eBPF" \
|
||||
4 "Modern eBPF" \
|
||||
5 "Plugin" \
|
||||
2>&1 >/dev/tty)
|
||||
clear
|
||||
case $CHOICE in
|
||||
2)
|
||||
chosen_driver="kmod"
|
||||
;;
|
||||
3)
|
||||
chosen_driver="bpf"
|
||||
;;
|
||||
4)
|
||||
chosen_driver="modern-bpf"
|
||||
;;
|
||||
5)
|
||||
chosen_driver="plugin"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
postinst_found=0
|
||||
|
||||
# If needed, try to load/compile the driver through falco-driver-loader
|
||||
case "$chosen_driver" in
|
||||
"kmod")
|
||||
echo "[POST-INSTALL] Call falco-driver-loader module:\n"
|
||||
falco-driver-loader module
|
||||
;;
|
||||
"bpf")
|
||||
echo "[POST-INSTALL] Call falco-driver-loader bpf:\n"
|
||||
falco-driver-loader bpf
|
||||
;;
|
||||
case "$1" in
|
||||
configure)
|
||||
for DKMS_POSTINST in /usr/lib/dkms/common.postinst /usr/share/$DKMS_PACKAGE_NAME/postinst; do
|
||||
if [ -f $DKMS_POSTINST ]; then
|
||||
$DKMS_POSTINST $DKMS_PACKAGE_NAME $DKMS_VERSION /usr/share/$DKMS_PACKAGE_NAME "" $2
|
||||
postinst_found=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$postinst_found" -eq 0 ]; then
|
||||
echo "ERROR: DKMS version is too old and $DKMS_PACKAGE_NAME was not"
|
||||
echo "built with legacy DKMS support."
|
||||
echo "You must either rebuild $DKMS_PACKAGE_NAME with legacy postinst"
|
||||
echo "support or upgrade DKMS to a more current version."
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
|
||||
if [ -n "$chosen_driver" ]; then
|
||||
echo "[POST-INSTALL] enable falco-$chosen_driver.service:\n"
|
||||
systemctl --system enable "falco-$chosen_driver.service" || true
|
||||
echo "[POST-INSTALL] start falco-$chosen_driver.service:\n"
|
||||
systemctl --system start "falco-$chosen_driver.service" || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
|
||||
if [ -d /run/systemd/system ]; then
|
||||
echo "[POST-INSTALL] trigger deamon-reload:\n"
|
||||
systemctl --system daemon-reload || true
|
||||
if [ -n "$chosen_driver" ]; then
|
||||
echo "[POST-INSTALL] trigger condrestart:\n"
|
||||
# restart falco on upgrade if service is already running
|
||||
systemctl --system condrestart "falco-$chosen_driver.service" || true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -15,18 +15,3 @@
|
||||
# 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 falco services:\n"
|
||||
systemctl --system disable 'falco-kmod.service' || true
|
||||
systemctl --system disable 'falco-bpf.service' || true
|
||||
systemctl --system disable 'falco-modern-bpf.service' || true
|
||||
systemctl --system disable 'falco-plugin.service' || true
|
||||
echo "[POST-REMOVE] trigger deamon-reload:\n"
|
||||
systemctl --system daemon-reload || true
|
||||
fi
|
||||
|
||||
@@ -17,21 +17,8 @@
|
||||
#
|
||||
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
|
||||
|
||||
if [ -d /run/systemd/system ] && [ "$1" = remove ]; then
|
||||
echo "[POST-REMOVE] stop falco services:\n"
|
||||
systemctl --system stop 'falco-kmod.service' || true
|
||||
systemctl --system stop 'falco-bpf.service' || true
|
||||
systemctl --system stop 'falco-modern-bpf.service' || true
|
||||
systemctl --system stop 'falco-plugin.service' || true
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
remove|upgrade|deconfigure)
|
||||
echo "[POST-REMOVE] call falco-driver-loader --clean:\n"
|
||||
falco-driver-loader --clean
|
||||
/usr/bin/falco-driver-loader --clean
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -114,7 +114,8 @@ get_target_id() {
|
||||
# Older CentOS distros
|
||||
OS_ID=centos
|
||||
else
|
||||
return 1
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Overwrite the OS_ID if /etc/VERSION file is present.
|
||||
@@ -163,7 +164,6 @@ get_target_id() {
|
||||
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
flatcar_relocate_tools() {
|
||||
@@ -211,13 +211,7 @@ load_kernel_module_compile() {
|
||||
fi
|
||||
|
||||
# Try to compile using all the available gcc versions
|
||||
for CURRENT_GCC in $(ls "$(dirname "$(which gcc)")"/gcc*); do
|
||||
# Filter away gcc-{ar,nm,...}
|
||||
# Only gcc compiler has `-print-search-dirs` option.
|
||||
${CURRENT_GCC} -print-search-dirs 2>&1 | grep "install:"
|
||||
if [ "$?" -ne "0" ]; then
|
||||
continue
|
||||
fi
|
||||
for CURRENT_GCC in $(which gcc) $(ls "$(dirname "$(which gcc)")"/gcc-* | grep 'gcc-[0-9]\+' | sort -n -r -k 2 -t -); do
|
||||
echo "* Trying to dkms install ${DRIVER_NAME} module with GCC ${CURRENT_GCC}"
|
||||
echo "#!/usr/bin/env bash" > /tmp/falco-dkms-make
|
||||
echo "make CC=${CURRENT_GCC} \$@" >> /tmp/falco-dkms-make
|
||||
@@ -238,14 +232,13 @@ load_kernel_module_compile() {
|
||||
return
|
||||
fi
|
||||
echo "* ${DRIVER_NAME} module found: ${KO_FILE}"
|
||||
echo "* Trying to modprobe"
|
||||
echo "* Trying insmod"
|
||||
chcon -t modules_object_t "$KO_FILE" > /dev/null 2>&1 || true
|
||||
depmod ${KERNEL_RELEASE}
|
||||
if modprobe "${DRIVER_NAME}" > /dev/null 2>&1; then
|
||||
if insmod "$KO_FILE" > /dev/null 2>&1; then
|
||||
echo "* Success: ${DRIVER_NAME} module found and loaded in dkms"
|
||||
exit 0
|
||||
else
|
||||
echo "* Unable to load ${DRIVER_NAME} module"
|
||||
echo "* Unable to insmod ${DRIVER_NAME} module"
|
||||
fi
|
||||
else
|
||||
DKMS_LOG="/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/build/make.log"
|
||||
@@ -260,6 +253,8 @@ load_kernel_module_compile() {
|
||||
}
|
||||
|
||||
load_kernel_module_download() {
|
||||
get_target_id
|
||||
|
||||
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
|
||||
local URL=$(echo "${1}/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" | sed s/+/%2B/g)
|
||||
|
||||
@@ -267,14 +262,11 @@ load_kernel_module_download() {
|
||||
if curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" "${URL}"; then
|
||||
echo "* Download succeeded"
|
||||
chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
|
||||
mkdir -p /lib/modules/${KERNEL_RELEASE}/kernel/drivers/falco/
|
||||
cp ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME} /lib/modules/${KERNEL_RELEASE}/kernel/drivers/falco/falco.ko
|
||||
depmod ${KERNEL_RELEASE}
|
||||
if modprobe "${DRIVER_NAME}" > /dev/null 2>&1; then
|
||||
echo "* Success: ${DRIVER_NAME} module found and loaded with modprobe"
|
||||
if insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}"; then
|
||||
echo "* Success: ${DRIVER_NAME} module found and inserted"
|
||||
exit 0
|
||||
else
|
||||
>&2 echo "Unable to load the prebuilt ${DRIVER_NAME} module"
|
||||
>&2 echo "Unable to insmod the prebuilt ${DRIVER_NAME} module"
|
||||
fi
|
||||
else
|
||||
>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} module"
|
||||
@@ -382,6 +374,8 @@ load_kernel_module() {
|
||||
|
||||
echo "* Looking for a ${DRIVER_NAME} module locally (kernel ${KERNEL_RELEASE})"
|
||||
|
||||
get_target_id
|
||||
|
||||
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
|
||||
echo "* Filename '${FALCO_KERNEL_MODULE_FILENAME}' is composed of:"
|
||||
print_filename_components
|
||||
@@ -389,10 +383,7 @@ load_kernel_module() {
|
||||
if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" ]; then
|
||||
echo "* Found a prebuilt ${DRIVER_NAME} module at ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}, loading it"
|
||||
chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
|
||||
mkdir -p /lib/modules/${KERNEL_RELEASE}/kernel/drivers/falco/
|
||||
cp ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME} /lib/modules/${KERNEL_RELEASE}/kernel/drivers/falco/falco.ko
|
||||
depmod ${KERNEL_RELEASE}
|
||||
modprobe "${DRIVER_NAME}" && echo "* Success: ${DRIVER_NAME} module found and loaded"
|
||||
insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
@@ -553,6 +544,8 @@ load_bpf_probe() {
|
||||
mount -t debugfs nodev /sys/kernel/debug
|
||||
fi
|
||||
|
||||
get_target_id
|
||||
|
||||
BPF_PROBE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.o"
|
||||
echo "* Filename '${BPF_PROBE_FILENAME}' is composed of:"
|
||||
print_filename_components
|
||||
@@ -645,8 +638,6 @@ DRIVER_VERSION=${DRIVER_VERSION:-"@DRIVER_VERSION@"}
|
||||
DRIVER_NAME=${DRIVER_NAME:-"@DRIVER_NAME@"}
|
||||
FALCO_VERSION="@FALCO_VERSION@"
|
||||
|
||||
TARGET_ID="placeholder" # when no target id can be fetched, we try to build the driver from source anyway, using a placeholder name
|
||||
|
||||
DRIVER="module"
|
||||
if [ -v FALCO_BPF_PROBE ]; then
|
||||
DRIVER="bpf"
|
||||
@@ -720,18 +711,6 @@ if [ -z "$source_only" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
get_target_id
|
||||
res=$?
|
||||
if [ $res != 0 ]; then
|
||||
if [ -n "$ENABLE_COMPILE" ]; then
|
||||
ENABLE_DOWNLOAD=
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community. Trying to compile anyway."
|
||||
else
|
||||
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$clean" ]; then
|
||||
if [ -n "$has_opts" ]; then
|
||||
>&2 echo "Cannot use --clean with other options"
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security with plugin
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
Conflicts=falco-kmod.service
|
||||
Conflicts=falco-bpf.service
|
||||
Conflicts=falco-modern-bpf.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=%u
|
||||
User=root
|
||||
ExecStartPre=/sbin/modprobe falco
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
ExecStopPost=/sbin/rmmod falco
|
||||
UMask=0077
|
||||
TimeoutSec=30
|
||||
RestartSec=15s
|
||||
@@ -14,72 +14,18 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
set -e
|
||||
|
||||
chosen_driver=
|
||||
|
||||
if [ $1 -eq 1 ]; then
|
||||
if [ -x /usr/bin/dialog ]; then
|
||||
# If dialog is installed, create a dialog to let users choose the correct driver for them
|
||||
CHOICE=$(dialog --clear --backtitle "Choose your preferred driver" --title "Falco driver" --menu "Choose one of the following options:" 15 40 4 \
|
||||
1 "Don't start" \
|
||||
2 "Kmod" \
|
||||
3 "eBPF" \
|
||||
4 "Modern eBPF" \
|
||||
5 "Plugin" \
|
||||
2>&1 >/dev/tty)
|
||||
clear
|
||||
case $CHOICE in
|
||||
2)
|
||||
chosen_driver="kmod"
|
||||
;;
|
||||
3)
|
||||
chosen_driver="bpf"
|
||||
;;
|
||||
4)
|
||||
chosen_driver="modern-bpf"
|
||||
;;
|
||||
5)
|
||||
chosen_driver="plugin"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
# If needed, try to load/compile the driver through falco-driver-loader
|
||||
case "$chosen_driver" in
|
||||
"kmod")
|
||||
falco-driver-loader module
|
||||
;;
|
||||
"bpf")
|
||||
falco-driver-loader bpf
|
||||
;;
|
||||
esac
|
||||
|
||||
# validate rpm macros by `rpm -qp --scripts <rpm>`
|
||||
# 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 <service>`
|
||||
%systemd_post "falco-$chosen_driver.service"
|
||||
|
||||
# post install mirrored from .deb
|
||||
if [ $1 -eq 1 ]; then
|
||||
if [ -n "$chosen_driver" ]; then
|
||||
systemctl --system enable "falco-$chosen_driver.service" || true
|
||||
systemctl --system start "falco-$chosen_driver.service" || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# post upgrade mirrored from .deb
|
||||
if [ $1 -gt 1 ]; then
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl --system daemon-reload || true
|
||||
if [ -n "$chosen_driver" ]; then
|
||||
# restart falco on upgrade if service is already running
|
||||
systemctl --system condrestart "falco-$chosen_driver.service" || true
|
||||
fi
|
||||
fi
|
||||
mod_version="@DRIVER_VERSION@"
|
||||
dkms add -m falco -v $mod_version --rpm_safe_upgrade
|
||||
if [ `uname -r | grep -c "BOOT"` -eq 0 ] && [ -e /lib/modules/`uname -r`/build/include ]; then
|
||||
dkms build -m falco -v $mod_version
|
||||
dkms install --force -m falco -v $mod_version
|
||||
elif [ `uname -r | grep -c "BOOT"` -gt 0 ]; then
|
||||
echo -e ""
|
||||
echo -e "Module build for the currently running kernel was skipped since you"
|
||||
echo -e "are running a BOOT variant of the kernel."
|
||||
else
|
||||
echo -e ""
|
||||
echo -e "Module build for the currently running kernel was skipped since the"
|
||||
echo -e "kernel source for this kernel does not seem to be installed."
|
||||
fi
|
||||
|
||||
@@ -14,13 +14,3 @@
|
||||
# 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
|
||||
systemctl --system disable 'falco-kmod.service'|| true
|
||||
systemctl --system disable 'falco-bpf.service' || true
|
||||
systemctl --system disable 'falco-modern-bpf.service' || true
|
||||
systemctl --system disable 'falco-plugin.service' || true
|
||||
systemctl --system daemon-reload || true
|
||||
fi
|
||||
|
||||
@@ -14,27 +14,5 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
set -e
|
||||
|
||||
# pre uninstall mirrored from .deb
|
||||
# Currently running falco service uses the driver, so stop it before driver cleanup
|
||||
if [ -d /run/systemd/system ] && [ $1 -eq 0 ]; then
|
||||
systemctl --system stop 'falco-kmod.service' || true
|
||||
systemctl --system stop 'falco-bpf.service' || true
|
||||
systemctl --system stop 'falco-modern-bpf.service' || true
|
||||
systemctl --system stop 'falco-plugin.service' || true
|
||||
fi
|
||||
|
||||
falco-driver-loader --clean
|
||||
|
||||
# validate rpm macros by `rpm -qp --scripts <rpm>`
|
||||
# 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 <service>`
|
||||
%systemd_preun 'falco-kmod.service'
|
||||
%systemd_preun 'falco-bpf.service'
|
||||
%systemd_preun 'falco-modern-bpf.service'
|
||||
%systemd_preun 'falco-plugin.service'
|
||||
/usr/bin/falco-driver-loader --clean
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security with kmod, inject.
|
||||
Documentation=https://falco.org/docs/
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
User=root
|
||||
ExecStart=/sbin/modprobe falco
|
||||
ExecStop=/sbin/rmmod falco
|
||||
@@ -1,29 +0,0 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security with kmod
|
||||
Documentation=https://falco.org/docs/
|
||||
After=falco-kmod-inject.service
|
||||
Requires=falco-kmod-inject.service
|
||||
Conflicts=falco-bpf.service
|
||||
Conflicts=falco-modern-bpf.service
|
||||
Conflicts=falco-plugin.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
UMask=0077
|
||||
TimeoutSec=30
|
||||
RestartSec=15s
|
||||
Restart=on-failure
|
||||
PrivateTmp=true
|
||||
NoNewPrivileges=yes
|
||||
ProtectHome=read-only
|
||||
ProtectSystem=full
|
||||
ProtectKernelTunables=true
|
||||
ReadWritePaths=/sys/module/falco
|
||||
RestrictRealtime=true
|
||||
RestrictAddressFamilies=~AF_PACKET
|
||||
StandardOutput=null
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,26 +0,0 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security with modern ebpf
|
||||
Documentation=https://falco.org/docs/
|
||||
Conflicts=falco-kmod.service
|
||||
Conflicts=falco-bpf.service
|
||||
Conflicts=falco-plugin.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid --modern-bpf
|
||||
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
|
||||
@@ -457,6 +457,11 @@ trace_files: !mux
|
||||
item_name: some macro
|
||||
code: LOAD_ERR_VALIDATE
|
||||
message: "Undefined macro 'foo' used in filter."
|
||||
validate_warnings:
|
||||
- item_type: macro
|
||||
item_name: some macro
|
||||
code: LOAD_UNUSED_MACRO
|
||||
message: "Macro not referred to by any other rule/macro"
|
||||
validate_rules_file:
|
||||
- rules/invalid_overwrite_macro_multiple_docs.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -719,7 +724,7 @@ trace_files: !mux
|
||||
- "Write below etc": 1
|
||||
- "System procs network activity": 1
|
||||
- "Mkdir binary dirs": 1
|
||||
- "System user interactive": 0
|
||||
- "System user interactive": 1
|
||||
- "DB program spawned process": 1
|
||||
- "Non sudo setuid": 1
|
||||
- "Create files below dev": 1
|
||||
|
||||
@@ -62,7 +62,7 @@ traces: !mux
|
||||
falco-event-generator:
|
||||
trace_file: traces-positive/falco-event-generator.scap
|
||||
detect: True
|
||||
detect_level: [ERROR, WARNING, NOTICE, DEBUG]
|
||||
detect_level: [ERROR, WARNING, INFO, NOTICE, DEBUG]
|
||||
detect_counts:
|
||||
- "Write below binary dir": 1
|
||||
- "Read sensitive file untrusted": 3
|
||||
@@ -71,7 +71,7 @@ traces: !mux
|
||||
- "Write below etc": 1
|
||||
- "System procs network activity": 1
|
||||
- "Mkdir binary dirs": 1
|
||||
- "System user interactive": 0
|
||||
- "System user interactive": 1
|
||||
- "DB program spawned process": 1
|
||||
- "Non sudo setuid": 1
|
||||
- "Create files below dev": 1
|
||||
|
||||
@@ -73,15 +73,13 @@ TEST_CASE("Should find event types from filter", "[rule_loader]")
|
||||
set<uint16_t> not_close;
|
||||
set<uint16_t> all_events;
|
||||
set<uint16_t> no_events;
|
||||
|
||||
for(uint32_t i = 2; i < PPM_EVENT_MAX; i++)
|
||||
{
|
||||
// Skip events that are unused.
|
||||
if(sinsp::is_unused_event(i))
|
||||
if(g_infotables.m_event_info[i].flags & EF_UNUSED)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
all_events.insert(i);
|
||||
if(openat_only.find(i) == openat_only.end())
|
||||
{
|
||||
|
||||
@@ -21,11 +21,9 @@ set(FALCO_ENGINE_SOURCE_FILES
|
||||
filter_macro_resolver.cpp
|
||||
filter_evttype_resolver.cpp
|
||||
filter_warning_resolver.cpp
|
||||
stats_manager.cpp
|
||||
rule_loader.cpp
|
||||
rule_loader_reader.cpp
|
||||
rule_loader_collector.cpp
|
||||
rule_loader_compiler.cpp)
|
||||
rule_reader.cpp
|
||||
stats_manager.cpp)
|
||||
|
||||
add_library(falco_engine STATIC ${FALCO_ENGINE_SOURCE_FILES})
|
||||
add_dependencies(falco_engine njson string-view-lite)
|
||||
|
||||
@@ -27,8 +27,7 @@ limitations under the License.
|
||||
#include "falco_engine.h"
|
||||
#include "falco_utils.h"
|
||||
#include "falco_engine_version.h"
|
||||
#include "rule_loader_reader.h"
|
||||
#include "rule_loader_compiler.h"
|
||||
#include "rule_reader.h"
|
||||
|
||||
#include "formats.h"
|
||||
|
||||
@@ -60,7 +59,7 @@ falco_engine::falco_engine(bool seed_rng)
|
||||
falco_engine::~falco_engine()
|
||||
{
|
||||
m_rules.clear();
|
||||
m_rule_collector.clear();
|
||||
m_rule_loader.clear();
|
||||
m_rule_stats_manager.clear();
|
||||
m_sources.clear();
|
||||
}
|
||||
@@ -186,17 +185,15 @@ std::unique_ptr<load_result> falco_engine::load_rules(const std::string &rules_c
|
||||
cfg.replace_output_container_info = m_replace_container_info;
|
||||
cfg.default_ruleset_id = m_default_ruleset_id;
|
||||
|
||||
rule_loader::reader reader;
|
||||
if (reader.read(cfg, m_rule_collector))
|
||||
rule_reader reader;
|
||||
if (reader.load(cfg, m_rule_loader))
|
||||
{
|
||||
for (auto &src : m_sources)
|
||||
{
|
||||
src.ruleset = src.ruleset_factory->new_ruleset();
|
||||
}
|
||||
|
||||
rule_loader::compiler compiler;
|
||||
m_rules.clear();
|
||||
compiler.compile(cfg, m_rule_collector, m_rules);
|
||||
m_rule_loader.compile(cfg, m_rules);
|
||||
}
|
||||
|
||||
if (cfg.res->successful())
|
||||
@@ -346,11 +343,6 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t so
|
||||
|
||||
if(source_idx == m_syscall_source_idx)
|
||||
{
|
||||
if(m_syscall_source == NULL)
|
||||
{
|
||||
m_syscall_source = find_source(m_syscall_source_idx);
|
||||
}
|
||||
|
||||
source = m_syscall_source;
|
||||
}
|
||||
else
|
||||
@@ -392,6 +384,7 @@ std::size_t falco_engine::add_source(const std::string &source,
|
||||
if(source == falco_common::syscall_source)
|
||||
{
|
||||
m_syscall_source_idx = idx;
|
||||
m_syscall_source = find_source(m_syscall_source_idx);
|
||||
}
|
||||
|
||||
return idx;
|
||||
@@ -452,7 +445,7 @@ void falco_engine::read_file(const std::string& filename, std::string& contents)
|
||||
is.open(filename);
|
||||
if (!is.is_open())
|
||||
{
|
||||
throw falco_exception("Could not open " + filename + " for reading");
|
||||
throw falco_exception("Could not open " + filename + " for reading.");
|
||||
}
|
||||
|
||||
contents.assign(istreambuf_iterator<char>(is),
|
||||
@@ -522,7 +515,7 @@ bool falco_engine::check_plugin_requirements(
|
||||
std::string& err) const
|
||||
{
|
||||
err = "";
|
||||
for (const auto &alternatives : m_rule_collector.required_plugin_versions())
|
||||
for (const auto &alternatives : m_rule_loader.required_plugin_versions())
|
||||
{
|
||||
if (!check_plugin_requirement_alternatives(plugins, alternatives, err))
|
||||
{
|
||||
|
||||
@@ -32,7 +32,6 @@ limitations under the License.
|
||||
#include "gen_filter.h"
|
||||
#include "filter_ruleset.h"
|
||||
#include "rule_loader.h"
|
||||
#include "rule_loader_collector.h"
|
||||
#include "stats_manager.h"
|
||||
#include "falco_common.h"
|
||||
#include "falco_source.h"
|
||||
@@ -279,7 +278,7 @@ private:
|
||||
//
|
||||
inline bool should_drop_evt() const;
|
||||
|
||||
rule_loader::collector m_rule_collector;
|
||||
rule_loader m_rule_loader;
|
||||
indexed_vector<falco_rule> m_rules;
|
||||
stats_manager m_rule_stats_manager;
|
||||
|
||||
|
||||
@@ -56,8 +56,6 @@ public:
|
||||
LOAD_UNKNOWN_ITEM
|
||||
};
|
||||
|
||||
virtual ~load_result() = default;
|
||||
|
||||
// The warning code as a string
|
||||
static const std::string& warning_code_str(warning_code ec);
|
||||
|
||||
|
||||
@@ -26,12 +26,6 @@ limitations under the License.
|
||||
*/
|
||||
struct falco_rule
|
||||
{
|
||||
falco_rule(): id(0), priority(falco_common::PRIORITY_DEBUG) {}
|
||||
falco_rule(falco_rule&&) = default;
|
||||
falco_rule& operator = (falco_rule&&) = default;
|
||||
falco_rule(const falco_rule&) = default;
|
||||
falco_rule& operator = (const falco_rule&) = default;
|
||||
|
||||
std::size_t id;
|
||||
std::string source;
|
||||
std::string name;
|
||||
|
||||
@@ -26,23 +26,6 @@ limitations under the License.
|
||||
*/
|
||||
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_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_factory = s.ruleset_factory;
|
||||
filter_factory = s.filter_factory;
|
||||
formatter_factory = s.formatter_factory;
|
||||
return *this;
|
||||
};
|
||||
|
||||
std::string name;
|
||||
std::shared_ptr<filter_ruleset> ruleset;
|
||||
std::shared_ptr<filter_ruleset_factory> ruleset_factory;
|
||||
@@ -53,7 +36,7 @@ struct falco_source
|
||||
// matches an event.
|
||||
mutable falco_rule m_rule;
|
||||
|
||||
inline bool is_field_defined(const std::string& field) const
|
||||
inline bool is_field_defined(std::string field) const
|
||||
{
|
||||
auto *chk = filter_factory->new_filtercheck(field.c_str());
|
||||
if (chk)
|
||||
|
||||
@@ -17,11 +17,12 @@ limitations under the License.
|
||||
#include "filter_evttype_resolver.h"
|
||||
#include <sinsp.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace libsinsp::filter;
|
||||
|
||||
extern sinsp_evttables g_infotables;
|
||||
|
||||
static bool is_evttype_operator(const std::string& op)
|
||||
static bool is_evttype_operator(const string& op)
|
||||
{
|
||||
return op == "==" || op == "=" || op == "!=" || op == "in";
|
||||
}
|
||||
@@ -32,6 +33,7 @@ size_t falco_event_types::get_ppm_event_max()
|
||||
return PPM_EVENT_MAX;
|
||||
}
|
||||
|
||||
|
||||
void filter_evttype_resolver::visitor::inversion(falco_event_types& types)
|
||||
{
|
||||
falco_event_types all_types;
|
||||
@@ -42,7 +44,7 @@ void filter_evttype_resolver::visitor::inversion(falco_event_types& types)
|
||||
}
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::evttypes(const std::string& evtname, falco_event_types& out)
|
||||
void filter_evttype_resolver::visitor::evttypes(string evtname, falco_event_types& out)
|
||||
{
|
||||
// Fill in from 2 to PPM_EVENT_MAX-1. 0 and 1 are excluded as
|
||||
// those are PPM_GENERIC_E/PPME_GENERIC_X
|
||||
@@ -50,7 +52,8 @@ void filter_evttype_resolver::visitor::evttypes(const std::string& evtname, falc
|
||||
for(uint16_t i = 2; i < PPM_EVENT_MAX; i++)
|
||||
{
|
||||
// Skip unused events or events not matching the requested evtname
|
||||
if(!sinsp::is_unused_event(i) && (evtname.empty() || std::string(etable[i].name) == evtname))
|
||||
if(!(etable[i].flags & EF_UNUSED)
|
||||
&& (evtname.empty() || string(etable[i].name) == evtname))
|
||||
{
|
||||
out.insert(i);
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ public:
|
||||
string is passed, all the available evttypes are collected
|
||||
\param out The set to be filled with the evttypes
|
||||
*/
|
||||
inline void evttypes(const std::string& evtname, falco_event_types& out) const
|
||||
inline void evttypes(std::string evtname, falco_event_types& out) const
|
||||
{
|
||||
falco_event_types evt_types;
|
||||
visitor().evttypes(evtname, evt_types);
|
||||
@@ -188,12 +188,6 @@ public:
|
||||
private:
|
||||
struct visitor : public libsinsp::filter::ast::expr_visitor
|
||||
{
|
||||
visitor(): m_expect_value(false) {}
|
||||
visitor(visitor&&) = default;
|
||||
visitor& operator = (visitor&&) = default;
|
||||
visitor(const visitor&) = default;
|
||||
visitor& operator = (const visitor&) = default;
|
||||
|
||||
bool m_expect_value;
|
||||
falco_event_types m_last_node_evttypes;
|
||||
|
||||
@@ -205,6 +199,6 @@ private:
|
||||
void visit(libsinsp::filter::ast::unary_check_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::binary_check_expr* e) override;
|
||||
void inversion(falco_event_types& types);
|
||||
void evttypes(const std::string& evtname, falco_event_types& out);
|
||||
void evttypes(std::string evtname, falco_event_types& out);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -81,12 +81,6 @@ class filter_macro_resolver
|
||||
|
||||
struct visitor : public libsinsp::filter::ast::expr_visitor
|
||||
{
|
||||
visitor() = default;
|
||||
visitor(visitor&&) = default;
|
||||
visitor& operator = (visitor&&) = default;
|
||||
visitor(const visitor&) = delete;
|
||||
visitor& operator = (const visitor&) = delete;
|
||||
|
||||
std::unique_ptr<libsinsp::filter::ast::expr> m_node_substitute;
|
||||
std::unordered_set<std::string>* m_unknown_macros;
|
||||
std::unordered_set<std::string>* m_resolved_macros;
|
||||
|
||||
@@ -151,7 +151,5 @@ public:
|
||||
class filter_ruleset_factory
|
||||
{
|
||||
public:
|
||||
virtual ~filter_ruleset_factory() = default;
|
||||
|
||||
virtual std::shared_ptr<filter_ruleset> new_ruleset() = 0;
|
||||
};
|
||||
|
||||
@@ -48,12 +48,6 @@ public:
|
||||
private:
|
||||
struct visitor : public libsinsp::filter::ast::base_expr_visitor
|
||||
{
|
||||
visitor(): m_is_equality_check(false), m_warnings(nullptr) {}
|
||||
visitor(visitor&&) = default;
|
||||
visitor& operator = (visitor&&) = default;
|
||||
visitor(const visitor&) = delete;
|
||||
visitor& operator = (const visitor&) = delete;
|
||||
|
||||
bool m_is_equality_check;
|
||||
std::set<falco::load_result::warning_code>* m_warnings;
|
||||
|
||||
|
||||
@@ -28,12 +28,7 @@ template <typename T>
|
||||
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;
|
||||
|
||||
/*!
|
||||
\brief Returns the number of elements
|
||||
@@ -73,7 +68,7 @@ public:
|
||||
\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)
|
||||
virtual inline size_t insert(T& entry, const std::string& index)
|
||||
{
|
||||
size_t id;
|
||||
auto prev = m_index.find(index);
|
||||
@@ -94,7 +89,7 @@ public:
|
||||
*/
|
||||
virtual inline T* at(size_t id) const
|
||||
{
|
||||
if (id < m_entries.size())
|
||||
if (id <= m_entries.size())
|
||||
{
|
||||
return (T* const) &m_entries[id];
|
||||
}
|
||||
|
||||
@@ -14,10 +14,27 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "falco_engine.h"
|
||||
#include "falco_utils.h"
|
||||
#include "rule_loader.h"
|
||||
#include "filter_macro_resolver.h"
|
||||
#include "filter_evttype_resolver.h"
|
||||
#include "filter_warning_resolver.h"
|
||||
#include <version.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#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)); } }
|
||||
|
||||
using namespace falco;
|
||||
|
||||
static string s_container_info_fmt = "%container.info";
|
||||
static string s_default_extra_fmt = "%container.name (id=%container.id)";
|
||||
|
||||
using namespace std;
|
||||
using namespace libsinsp::filter;
|
||||
|
||||
static const std::string item_type_strings[] = {
|
||||
"value for",
|
||||
@@ -58,7 +75,7 @@ rule_loader::context::context(const std::string& name)
|
||||
|
||||
rule_loader::context::context(const YAML::Node &item,
|
||||
const item_type item_type,
|
||||
const std::string& item_name,
|
||||
const std::string item_name,
|
||||
const context& parent)
|
||||
{
|
||||
init(parent.name(), position(item.Mark()), item_type, item_name, parent);
|
||||
@@ -73,10 +90,7 @@ rule_loader::context::context(const libsinsp::filter::ast::pos_info& pos,
|
||||
// Contexts based on conditions don't use the
|
||||
// filename. Instead the "name" is just the condition, and
|
||||
// uses a short prefix of the condition.
|
||||
std::string name = "\"" + (
|
||||
condition.length() > 20
|
||||
? condition.substr(0, 20 - 3) + "...\""
|
||||
: condition + "\"");
|
||||
std::string name = "\"" + condition.substr(0, 20) + "...\"";
|
||||
std::replace(name.begin(), name.end(), '\n', ' ');
|
||||
std::replace(name.begin(), name.end(), '\r', ' ');
|
||||
|
||||
@@ -108,7 +122,7 @@ const std::string& rule_loader::context::name() const
|
||||
void rule_loader::context::init(const std::string& name,
|
||||
const position& pos,
|
||||
const item_type item_type,
|
||||
const std::string& item_name,
|
||||
const std::string item_name,
|
||||
const context& parent)
|
||||
{
|
||||
// Copy parent locations
|
||||
@@ -210,12 +224,13 @@ std::string rule_loader::context::snippet(const falco::load_result::rules_conten
|
||||
return "<No context available>\n";
|
||||
}
|
||||
|
||||
size_t from = loc.pos.pos;
|
||||
|
||||
// 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--);
|
||||
for(; from > 0 && from >= snip_content.size(); from--);
|
||||
|
||||
// The snippet is generally the line that contains the
|
||||
// position. So walk backwards from pos to the preceding
|
||||
@@ -225,37 +240,36 @@ std::string rule_loader::context::snippet(const falco::load_result::rules_conten
|
||||
// However, some lines can be very very long, so the walk
|
||||
// forwards/walk backwards is capped at a maximum of
|
||||
// snippet_width/2 characters in either direction.
|
||||
size_t from = pos;
|
||||
for(; from > 0 && snip_content.at(from) != '\n' && (pos - from) < (snippet_width/2); from--);
|
||||
for(; from > 0 && snip_content.at(from) != '\n' && (loc.pos.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++);
|
||||
size_t to = loc.pos.pos;
|
||||
for(; to < snip_content.size()-1 && snip_content.at(to) != '\n' && (to - loc.pos.pos) < (snippet_width/2); to++);
|
||||
|
||||
// Don't include the newlines
|
||||
if(from < snip_content.size() && snip_content.at(from) == '\n')
|
||||
if(snip_content.at(from) == '\n')
|
||||
{
|
||||
from++;
|
||||
}
|
||||
if(to < snip_content.size() && snip_content.at(to) == '\n')
|
||||
if(snip_content.at(to) == '\n')
|
||||
{
|
||||
to--;
|
||||
}
|
||||
|
||||
std::string ret = snip_content.substr(from, to-from+1);
|
||||
|
||||
if(ret.empty())
|
||||
if(snip_content.empty())
|
||||
{
|
||||
return "<No context available>\n";
|
||||
}
|
||||
|
||||
// Replace the initial/end characters with '...' if the walk
|
||||
// forwards/backwards was incomplete
|
||||
if(pos - from >= (snippet_width/2))
|
||||
if(loc.pos.pos - from >= (snippet_width/2))
|
||||
{
|
||||
ret.replace(0, 3, "...");
|
||||
}
|
||||
|
||||
if(to - pos >= (snippet_width/2))
|
||||
if(to - loc.pos.pos >= (snippet_width/2))
|
||||
{
|
||||
ret.replace(ret.size()-3, 3, "...");
|
||||
}
|
||||
@@ -263,10 +277,7 @@ std::string rule_loader::context::snippet(const falco::load_result::rules_conten
|
||||
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";
|
||||
}
|
||||
ret += std::string(loc.pos.pos-from, ' ') + '^' + "\n";
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -510,7 +521,7 @@ const nlohmann::json& rule_loader::result::as_json(const rules_contents_t& conte
|
||||
}
|
||||
|
||||
rule_loader::engine_version_info::engine_version_info(context &ctx)
|
||||
: ctx(ctx), version(0)
|
||||
: ctx(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -525,12 +536,12 @@ rule_loader::plugin_version_info::plugin_version_info(context &ctx)
|
||||
}
|
||||
|
||||
rule_loader::list_info::list_info(context &ctx)
|
||||
: ctx(ctx), used(false), index(0), visibility(0)
|
||||
: ctx(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
rule_loader::macro_info::macro_info(context &ctx)
|
||||
: ctx(ctx), cond_ctx(ctx), used(false), index(0), visibility(0)
|
||||
: ctx(ctx), cond_ctx(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -540,13 +551,520 @@ rule_loader::rule_exception_info::rule_exception_info(context &ctx)
|
||||
}
|
||||
|
||||
rule_loader::rule_info::rule_info(context &ctx)
|
||||
: ctx(ctx), cond_ctx(ctx), output_ctx(ctx), index(0), visibility(0),
|
||||
priority(falco_common::PRIORITY_DEBUG), enabled(true),
|
||||
warn_evttypes(true), skip_if_unknown_filter(false)
|
||||
: ctx(ctx), cond_ctx(ctx), output_ctx(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
rule_loader::rule_load_exception::rule_load_exception(falco::load_result::error_code ec, const std::string& msg, const context& ctx)
|
||||
// todo(jasondellaluce): this breaks string escaping in lists and exceptions
|
||||
static void quote_item(string& e)
|
||||
{
|
||||
if (e.find(" ") != string::npos && e[0] != '"' && e[0] != '\'')
|
||||
{
|
||||
e = '"' + e + '"';
|
||||
}
|
||||
}
|
||||
|
||||
static void paren_item(string& e)
|
||||
{
|
||||
if(e[0] != '(')
|
||||
{
|
||||
e = '(' + e + ')';
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_operator_defined(const string& op)
|
||||
{
|
||||
auto ops = libsinsp::filter::parser::supported_operators();
|
||||
return find(ops.begin(), ops.end(), op) != ops.end();
|
||||
}
|
||||
|
||||
static inline bool is_operator_for_list(const 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, string fmt, string& err)
|
||||
{
|
||||
try
|
||||
{
|
||||
shared_ptr<gen_event_formatter> formatter;
|
||||
formatter = source.formatter_factory->create_formatter(fmt);
|
||||
return true;
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
err = e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void define_info(indexed_vector<T>& 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 <typename T>
|
||||
static inline void append_info(T* prev, T& 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({false, "="});
|
||||
}
|
||||
}
|
||||
THROW(ex.fields.items.size() != ex.comps.items.size(),
|
||||
"Fields and comps lists must have equal length",
|
||||
ex.ctx);
|
||||
for (auto &v : ex.comps.items)
|
||||
{
|
||||
THROW(!is_operator_defined(v.item),
|
||||
std::string("'") + v.item + "' is not a supported comparison operator",
|
||||
ex.ctx);
|
||||
}
|
||||
for (auto &v : ex.fields.items)
|
||||
{
|
||||
THROW(!source.is_field_defined(v.item),
|
||||
std::string("'") + v.item + "' is not a supported filter field",
|
||||
ex.ctx);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
THROW(!source.is_field_defined(ex.fields.item),
|
||||
std::string("'") + ex.fields.item + "' is not a supported filter field",
|
||||
ex.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void build_rule_exception_infos(
|
||||
const vector<rule_loader::rule_exception_info>& exceptions,
|
||||
set<string>& exception_fields,
|
||||
string& condition)
|
||||
{
|
||||
string tmp;
|
||||
for (auto &ex : exceptions)
|
||||
{
|
||||
string icond;
|
||||
if(!ex.fields.is_list)
|
||||
{
|
||||
for (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 (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;
|
||||
string istr;
|
||||
for (auto &field : ex.fields.items)
|
||||
{
|
||||
icond += k == 0 ? "" : " and ";
|
||||
if (values.items[k].is_list)
|
||||
{
|
||||
istr = "(";
|
||||
for (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;
|
||||
}
|
||||
}
|
||||
|
||||
// todo(jasondellaluce): this breaks string escaping in lists
|
||||
static bool resolve_list(string& cnd, const rule_loader::list_info& list)
|
||||
{
|
||||
static string blanks = " \t\n\r";
|
||||
static string delims = blanks + "(),=";
|
||||
string new_cnd;
|
||||
size_t start, end;
|
||||
bool used = false;
|
||||
start = cnd.find(list.name);
|
||||
while (start != string::npos)
|
||||
{
|
||||
// the characters surrounding the name must
|
||||
// be delims of beginning/end of string
|
||||
end = start + list.name.length();
|
||||
if ((start == 0 || delims.find(cnd[start - 1]) != string::npos)
|
||||
&& (end >= cnd.length() || delims.find(cnd[end]) != string::npos))
|
||||
{
|
||||
// shift pointers to consume all whitespaces
|
||||
while (start > 0
|
||||
&& blanks.find(cnd[start - 1]) != string::npos)
|
||||
{
|
||||
start--;
|
||||
}
|
||||
while (end < cnd.length()
|
||||
&& blanks.find(cnd[end]) != string::npos)
|
||||
{
|
||||
end++;
|
||||
}
|
||||
// create substitution string by concatenating all values
|
||||
string sub = "";
|
||||
for (auto &v : list.items)
|
||||
{
|
||||
if (!sub.empty())
|
||||
{
|
||||
sub += ", ";
|
||||
}
|
||||
sub += v;
|
||||
}
|
||||
// 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 void resolve_macros(
|
||||
indexed_vector<rule_loader::macro_info>& macros,
|
||||
shared_ptr<ast::expr>& ast,
|
||||
uint32_t visibility,
|
||||
const rule_loader::context &ctx)
|
||||
{
|
||||
filter_macro_resolver macro_resolver;
|
||||
for (auto &m : macros)
|
||||
{
|
||||
if (m.index < visibility)
|
||||
{
|
||||
macro_resolver.set_macro(m.name, m.cond_ast);
|
||||
}
|
||||
}
|
||||
macro_resolver.run(ast);
|
||||
|
||||
// Note: only complaining about the first unknown macro
|
||||
THROW(!macro_resolver.get_unknown_macros().empty(),
|
||||
std::string("Undefined macro '")
|
||||
+ *macro_resolver.get_unknown_macros().begin()
|
||||
+ "' used in filter.",
|
||||
ctx);
|
||||
|
||||
for (auto &m : macro_resolver.get_resolved_macros())
|
||||
{
|
||||
macros.at(m)->used = true;
|
||||
}
|
||||
}
|
||||
|
||||
// note: there is no visibility order between filter conditions and lists
|
||||
static shared_ptr<ast::expr> parse_condition(
|
||||
string condition,
|
||||
indexed_vector<rule_loader::list_info>& 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
|
||||
{
|
||||
shared_ptr<ast::expr> 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(
|
||||
load_result::LOAD_ERR_COMPILE_CONDITION,
|
||||
e.what(),
|
||||
parsectx);
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_output_substitutions(
|
||||
rule_loader::configuration& cfg,
|
||||
string& out)
|
||||
{
|
||||
if (out.find(s_container_info_fmt) != string::npos)
|
||||
{
|
||||
if (cfg.replace_output_container_info)
|
||||
{
|
||||
out = replace(out, s_container_info_fmt, cfg.output_extra);
|
||||
return;
|
||||
}
|
||||
out = replace(out, s_container_info_fmt, s_default_extra_fmt);
|
||||
}
|
||||
out += cfg.output_extra.empty() ? "" : " " + cfg.output_extra;
|
||||
}
|
||||
|
||||
void rule_loader::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::plugin_version_info::requirement_alternatives>& rule_loader::required_plugin_versions() const
|
||||
{
|
||||
return m_required_plugin_versions;
|
||||
}
|
||||
|
||||
void rule_loader::define(configuration& cfg, engine_version_info& info)
|
||||
{
|
||||
auto v = falco_engine::engine_version();
|
||||
THROW(v < info.version, "Rules require engine version "
|
||||
+ to_string(info.version) + ", but engine version is " + to_string(v),
|
||||
info.ctx);
|
||||
}
|
||||
|
||||
void rule_loader::define(configuration& cfg, plugin_version_info& info)
|
||||
{
|
||||
std::unordered_set<std::string> plugin_names;
|
||||
for (const auto& req : info.alternatives)
|
||||
{
|
||||
sinsp_version plugin_version(req.version);
|
||||
THROW(!plugin_version.m_valid,
|
||||
"Invalid required version '" + req.version
|
||||
+ "' for plugin '" + req.name + "'",
|
||||
info.ctx);
|
||||
THROW(plugin_names.find(req.name) != plugin_names.end(),
|
||||
"Defined multiple alternative version requirements for plugin '"
|
||||
+ req.name + "'",
|
||||
info.ctx);
|
||||
plugin_names.insert(req.name);
|
||||
}
|
||||
m_required_plugin_versions.push_back(info.alternatives);
|
||||
}
|
||||
|
||||
void rule_loader::define(configuration& cfg, list_info& info)
|
||||
{
|
||||
define_info(m_list_infos, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::append(configuration& cfg, list_info& info)
|
||||
{
|
||||
auto prev = m_list_infos.at(info.name);
|
||||
THROW(!prev,
|
||||
"List has 'append' key but no list by that name already exists",
|
||||
info.ctx);
|
||||
prev->items.insert(prev->items.end(), info.items.begin(), info.items.end());
|
||||
append_info(prev, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::define(configuration& cfg, macro_info& info)
|
||||
{
|
||||
define_info(m_macro_infos, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::append(configuration& cfg, macro_info& info)
|
||||
{
|
||||
auto prev = m_macro_infos.at(info.name);
|
||||
THROW(!prev,
|
||||
"Macro has 'append' key but no macro by that name already exists",
|
||||
info.ctx);
|
||||
prev->cond += " ";
|
||||
prev->cond += info.cond;
|
||||
append_info(prev, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::define(configuration& cfg, rule_info& info)
|
||||
{
|
||||
auto source = cfg.sources.at(info.source);
|
||||
if (!source)
|
||||
{
|
||||
cfg.res->add_warning(load_result::LOAD_UNKNOWN_SOURCE,
|
||||
"Unknown source " + info.source + ", skipping",
|
||||
info.ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
auto prev = m_rule_infos.at(info.name);
|
||||
THROW(prev && prev->source != info.source,
|
||||
"Rule has been re-defined with a different source",
|
||||
info.ctx);
|
||||
|
||||
for (auto &ex : info.exceptions)
|
||||
{
|
||||
THROW(!ex.fields.is_valid(),
|
||||
"Rule exception item must have fields property with a list of fields",
|
||||
ex.ctx);
|
||||
validate_exception_info(*source, ex);
|
||||
}
|
||||
|
||||
define_info(m_rule_infos, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::append(configuration& cfg, rule_info& info)
|
||||
{
|
||||
auto prev = m_rule_infos.at(info.name);
|
||||
|
||||
THROW(!prev,
|
||||
"Rule has 'append' key but no rule by that name already exists",
|
||||
info.ctx);
|
||||
THROW(info.cond.empty() && info.exceptions.empty(),
|
||||
"Appended rule must have exceptions or condition property",
|
||||
info.ctx);
|
||||
|
||||
auto source = cfg.sources.at(prev->source);
|
||||
// note: this is not supposed to happen
|
||||
THROW(!source,
|
||||
std::string("Unknown source ") + prev->source,
|
||||
info.ctx);
|
||||
|
||||
if (!info.cond.empty())
|
||||
{
|
||||
prev->cond += " ";
|
||||
prev->cond += info.cond;
|
||||
}
|
||||
|
||||
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::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;
|
||||
}
|
||||
|
||||
rule_loader::rule_load_exception::rule_load_exception(load_result::error_code ec, std::string msg, const context& ctx)
|
||||
: ec(ec), msg(msg), ctx(ctx)
|
||||
{
|
||||
}
|
||||
@@ -557,8 +1075,235 @@ rule_loader::rule_load_exception::~rule_load_exception()
|
||||
|
||||
const char* rule_loader::rule_load_exception::what()
|
||||
{
|
||||
errstr = falco::load_result::error_code_str(ec) + ": "
|
||||
errstr = load_result::error_code_str(ec) + ": "
|
||||
+ msg.c_str();
|
||||
|
||||
return errstr.c_str();
|
||||
}
|
||||
|
||||
void rule_loader::compile_list_infos(configuration& cfg, indexed_vector<list_info>& out) const
|
||||
{
|
||||
string tmp;
|
||||
vector<string> used;
|
||||
for (auto &list : m_list_infos)
|
||||
{
|
||||
list_info v = list;
|
||||
v.items.clear();
|
||||
for (auto &item : list.items)
|
||||
{
|
||||
auto ref = m_list_infos.at(item);
|
||||
if (ref && ref->index < list.visibility)
|
||||
{
|
||||
used.push_back(ref->name);
|
||||
for (auto val : ref->items)
|
||||
{
|
||||
quote_item(val);
|
||||
v.items.push_back(val);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = item;
|
||||
quote_item(tmp);
|
||||
v.items.push_back(tmp);
|
||||
}
|
||||
}
|
||||
v.used = false;
|
||||
out.insert(v, v.name);
|
||||
}
|
||||
for (auto &v : used)
|
||||
{
|
||||
out.at(v)->used = true;
|
||||
}
|
||||
}
|
||||
|
||||
// note: there is a visibility ordering between macros
|
||||
void rule_loader::compile_macros_infos(
|
||||
configuration& cfg,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& out) const
|
||||
{
|
||||
set<string> used;
|
||||
for (auto &m : m_macro_infos)
|
||||
{
|
||||
macro_info entry = m;
|
||||
entry.cond_ast = parse_condition(m.cond, lists, m.cond_ctx);
|
||||
entry.used = false;
|
||||
out.insert(entry, m.name);
|
||||
}
|
||||
|
||||
for (auto &m : out)
|
||||
{
|
||||
resolve_macros(out, m.cond_ast, m.visibility, m.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rule_loader::compile_rule_infos(
|
||||
configuration& cfg,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& macros,
|
||||
indexed_vector<falco_rule>& out) const
|
||||
{
|
||||
string err, condition;
|
||||
set<load_result::warning_code> warn_codes;
|
||||
filter_warning_resolver warn_resolver;
|
||||
for (auto &r : m_rule_infos)
|
||||
{
|
||||
// skip the rule if below the minimum priority
|
||||
if (r.priority > cfg.min_priority)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto source = cfg.sources.at(r.source);
|
||||
// note: this is not supposed to happen
|
||||
|
||||
THROW(!source,
|
||||
std::string("Unknown source ") + 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);
|
||||
}
|
||||
auto ast = parse_condition(condition, lists, r.cond_ctx);
|
||||
resolve_macros(macros, ast, MAX_VISIBILITY, r.ctx);
|
||||
|
||||
// check for warnings in the filtering condition
|
||||
warn_codes.clear();
|
||||
if (warn_resolver.run(ast.get(), warn_codes))
|
||||
{
|
||||
for (auto &w : warn_codes)
|
||||
{
|
||||
cfg.res->add_warning(w, "", r.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// build rule output message
|
||||
rule.output = r.output;
|
||||
if (r.source == falco_common::syscall_source)
|
||||
{
|
||||
apply_output_substitutions(cfg, rule.output);
|
||||
}
|
||||
|
||||
if(!is_format_valid(*cfg.sources.at(r.source), rule.output, err))
|
||||
{
|
||||
throw rule_load_exception(
|
||||
load_result::LOAD_ERR_COMPILE_OUTPUT,
|
||||
err,
|
||||
r.output_ctx);
|
||||
}
|
||||
|
||||
// construct rule definition and compile it to a filter
|
||||
rule.name = r.name;
|
||||
rule.source = r.source;
|
||||
rule.description = r.desc;
|
||||
rule.priority = r.priority;
|
||||
rule.tags = r.tags;
|
||||
|
||||
auto rule_id = out.insert(rule, rule.name);
|
||||
out.at(rule_id)->id = rule_id;
|
||||
|
||||
// This also compiles the filter, and might throw a
|
||||
// falco_exception with details on the compilation
|
||||
// failure.
|
||||
try {
|
||||
source->ruleset->add(*out.at(rule_id), ast);
|
||||
}
|
||||
catch (const falco_exception& e)
|
||||
{
|
||||
// Allow errors containing "nonexistent field" if
|
||||
// skip_if_unknown_filter is true
|
||||
std::string err = e.what();
|
||||
|
||||
if (err.find("nonexistent field") != string::npos &&
|
||||
r.skip_if_unknown_filter)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
load_result::LOAD_UNKNOWN_FIELD,
|
||||
e.what(),
|
||||
r.cond_ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw rule_loader::rule_load_exception(
|
||||
load_result::LOAD_ERR_COMPILE_CONDITION,
|
||||
e.what(),
|
||||
r.cond_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// By default rules are enabled/disabled for the default ruleset
|
||||
if(r.enabled)
|
||||
{
|
||||
source->ruleset->enable(rule.name, true, cfg.default_ruleset_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
source->ruleset->disable(rule.name, true, cfg.default_ruleset_id);
|
||||
}
|
||||
|
||||
// populate set of event types and emit an special warning
|
||||
set<uint16_t> evttypes = { ppm_event_type::PPME_PLUGINEVENT_E };
|
||||
if(rule.source == falco_common::syscall_source)
|
||||
{
|
||||
evttypes.clear();
|
||||
filter_evttype_resolver().evttypes(ast, evttypes);
|
||||
if ((evttypes.empty() || evttypes.size() > 100)
|
||||
&& r.warn_evttypes)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
load_result::LOAD_NO_EVTTYPE,
|
||||
"Rule matches too many evt.type values. This has a significant performance penalty.",
|
||||
r.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rule_loader::compile(configuration& cfg, indexed_vector<falco_rule>& out) const
|
||||
{
|
||||
indexed_vector<list_info> lists;
|
||||
indexed_vector<macro_info> macros;
|
||||
|
||||
// expand all lists, macros, and rules
|
||||
try
|
||||
{
|
||||
compile_list_infos(cfg, lists);
|
||||
compile_macros_infos(cfg, lists, macros);
|
||||
compile_rule_infos(cfg, lists, macros, out);
|
||||
}
|
||||
catch(rule_load_exception &e)
|
||||
{
|
||||
cfg.res->add_error(e.ec, e.msg, e.ctx);
|
||||
}
|
||||
|
||||
// print info on any dangling lists or macros that were not used anywhere
|
||||
for (auto &m : macros)
|
||||
{
|
||||
if (!m.used)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
load_result::LOAD_UNUSED_MACRO,
|
||||
"Macro not referred to by any other rule/macro",
|
||||
m.ctx);
|
||||
}
|
||||
}
|
||||
for (auto &l : lists)
|
||||
{
|
||||
if (!l.used)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
load_result::LOAD_UNUSED_LIST,
|
||||
"List not referred to by any other rule/macro",
|
||||
l.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,24 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "falco_rule.h"
|
||||
#include "falco_source.h"
|
||||
#include "falco_load_result.h"
|
||||
#include "indexed_vector.h"
|
||||
|
||||
namespace rule_loader
|
||||
|
||||
/*!
|
||||
\brief Ruleset loader of the falco engine
|
||||
*/
|
||||
class rule_loader
|
||||
{
|
||||
public:
|
||||
|
||||
class context
|
||||
{
|
||||
public:
|
||||
@@ -64,13 +72,8 @@ namespace rule_loader
|
||||
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(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;
|
||||
@@ -78,18 +81,6 @@ namespace rule_loader
|
||||
|
||||
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
|
||||
@@ -108,10 +99,10 @@ namespace rule_loader
|
||||
std::string item_name;
|
||||
};
|
||||
|
||||
explicit context(const std::string& name);
|
||||
context(const std::string& name);
|
||||
context(const YAML::Node& item,
|
||||
item_type item_type,
|
||||
const std::string& item_name,
|
||||
const std::string item_name,
|
||||
const context& parent);
|
||||
|
||||
// Build a context from a condition expression +
|
||||
@@ -127,11 +118,6 @@ namespace rule_loader
|
||||
|
||||
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;
|
||||
@@ -152,7 +138,7 @@ namespace rule_loader
|
||||
void init(const std::string& name,
|
||||
const position& pos,
|
||||
const item_type item_type,
|
||||
const std::string& item_name,
|
||||
const std::string item_name,
|
||||
const context& parent);
|
||||
|
||||
// A chain of locations from the current item, its
|
||||
@@ -167,16 +153,6 @@ namespace rule_loader
|
||||
|
||||
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;
|
||||
warning& operator = (warning&&) = default;
|
||||
warning(const warning&) = default;
|
||||
warning& operator = (const warning&) = default;
|
||||
|
||||
falco::load_result::warning_code wc;
|
||||
std::string msg;
|
||||
context ctx;
|
||||
@@ -184,16 +160,6 @@ namespace rule_loader
|
||||
|
||||
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;
|
||||
@@ -202,13 +168,8 @@ namespace rule_loader
|
||||
class rule_load_exception : public std::exception
|
||||
{
|
||||
public:
|
||||
rule_load_exception(falco::load_result::error_code ec, const std::string& msg, const context& ctx);
|
||||
rule_load_exception(falco::load_result::error_code ec, std::string msg, const context& ctx);
|
||||
virtual ~rule_load_exception();
|
||||
rule_load_exception(rule_load_exception&&) = default;
|
||||
rule_load_exception& operator = (rule_load_exception&&) = default;
|
||||
rule_load_exception(const rule_load_exception&) = default;
|
||||
rule_load_exception& operator = (const rule_load_exception&) = default;
|
||||
|
||||
const char* what();
|
||||
|
||||
falco::load_result::error_code ec;
|
||||
@@ -226,10 +187,6 @@ namespace rule_loader
|
||||
public:
|
||||
result(const std::string &name);
|
||||
virtual ~result() = 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;
|
||||
@@ -267,17 +224,11 @@ namespace rule_loader
|
||||
explicit configuration(
|
||||
const std::string& cont,
|
||||
const indexed_vector<falco_source>& srcs,
|
||||
const std::string& name)
|
||||
: content(cont), sources(srcs), name(name),
|
||||
default_ruleset_id(0), replace_output_container_info(false),
|
||||
min_priority(falco_common::PRIORITY_DEBUG)
|
||||
std::string name)
|
||||
: content(cont), sources(srcs), name(name)
|
||||
{
|
||||
res.reset(new result(name));
|
||||
}
|
||||
configuration(configuration&&) = default;
|
||||
configuration& operator = (configuration&&) = default;
|
||||
configuration(const configuration&) = delete;
|
||||
configuration& operator = (const configuration&) = delete;
|
||||
|
||||
const std::string& content;
|
||||
const indexed_vector<falco_source>& sources;
|
||||
@@ -296,10 +247,6 @@ namespace rule_loader
|
||||
{
|
||||
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;
|
||||
uint32_t version;
|
||||
@@ -313,12 +260,8 @@ namespace rule_loader
|
||||
struct requirement
|
||||
{
|
||||
requirement() = default;
|
||||
requirement(const std::string& n, const std::string& v):
|
||||
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;
|
||||
@@ -332,10 +275,6 @@ namespace rule_loader
|
||||
plugin_version_info();
|
||||
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;
|
||||
@@ -348,10 +287,6 @@ namespace rule_loader
|
||||
{
|
||||
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;
|
||||
bool used;
|
||||
@@ -368,10 +303,6 @@ namespace rule_loader
|
||||
{
|
||||
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;
|
||||
@@ -390,10 +321,6 @@ namespace rule_loader
|
||||
{
|
||||
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
|
||||
@@ -402,14 +329,6 @@ namespace rule_loader
|
||||
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<entry>& 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<entry> items;
|
||||
@@ -435,10 +354,6 @@ namespace rule_loader
|
||||
{
|
||||
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;
|
||||
@@ -457,4 +372,67 @@ namespace rule_loader
|
||||
bool warn_evttypes;
|
||||
bool skip_if_unknown_filter;
|
||||
};
|
||||
|
||||
virtual ~rule_loader() = default;
|
||||
|
||||
/*!
|
||||
\brief Erases all the internal state and definitions
|
||||
*/
|
||||
virtual void clear();
|
||||
|
||||
/*!
|
||||
\brief Uses the internal state to compile a list of falco_rules
|
||||
*/
|
||||
virtual void compile(configuration& cfg, indexed_vector<falco_rule>& out) const;
|
||||
|
||||
/*!
|
||||
\brief Returns the set of all required versions for each plugin according
|
||||
to the internal definitions.
|
||||
*/
|
||||
virtual const std::vector<plugin_version_info::requirement_alternatives>& required_plugin_versions() const;
|
||||
|
||||
/*!
|
||||
\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_info& info);
|
||||
|
||||
/*!
|
||||
\brief Updates the 'enabled' flag of an existing definition
|
||||
*/
|
||||
virtual void enable(configuration& cfg, rule_info& info);
|
||||
|
||||
private:
|
||||
void compile_list_infos(
|
||||
configuration& cfg,
|
||||
indexed_vector<list_info>& out) const;
|
||||
void compile_macros_infos(
|
||||
configuration& cfg,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& out) const;
|
||||
void compile_rule_infos(
|
||||
configuration& cfg,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& macros,
|
||||
indexed_vector<falco_rule>& out) const;
|
||||
|
||||
uint32_t m_cur_index;
|
||||
indexed_vector<rule_info> m_rule_infos;
|
||||
indexed_vector<macro_info> m_macro_infos;
|
||||
indexed_vector<list_info> m_list_infos;
|
||||
std::vector<plugin_version_info::requirement_alternatives> m_required_plugin_versions;
|
||||
};
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <version.h>
|
||||
|
||||
#include "falco_engine.h"
|
||||
#include "rule_loader_collector.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 <typename T>
|
||||
static inline void define_info(indexed_vector<T>& 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 <typename T>
|
||||
static inline void append_info(T* prev, T& 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 (auto &v : ex.comps.items)
|
||||
{
|
||||
THROW(!is_operator_defined(v.item),
|
||||
std::string("'") + v.item + "' is not a supported comparison operator",
|
||||
ex.ctx);
|
||||
}
|
||||
for (auto &v : ex.fields.items)
|
||||
{
|
||||
THROW(!source.is_field_defined(v.item),
|
||||
std::string("'") + v.item + "' is not a supported filter field",
|
||||
ex.ctx);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
THROW(!source.is_field_defined(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::plugin_version_info::requirement_alternatives>& rule_loader::collector::required_plugin_versions() const
|
||||
{
|
||||
return m_required_plugin_versions;
|
||||
}
|
||||
|
||||
const indexed_vector<rule_loader::list_info>& rule_loader::collector::lists() const
|
||||
{
|
||||
return m_list_infos;
|
||||
}
|
||||
|
||||
const indexed_vector<rule_loader::macro_info>& rule_loader::collector::macros() const
|
||||
{
|
||||
return m_macro_infos;
|
||||
}
|
||||
|
||||
const indexed_vector<rule_loader::rule_info>& 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 < info.version, "Rules require engine version "
|
||||
+ std::to_string(info.version) + ", but engine version is " + std::to_string(v),
|
||||
info.ctx);
|
||||
}
|
||||
|
||||
void rule_loader::collector::define(configuration& cfg, plugin_version_info& info)
|
||||
{
|
||||
std::unordered_set<std::string> plugin_names;
|
||||
for (const auto& req : info.alternatives)
|
||||
{
|
||||
sinsp_version plugin_version(req.version);
|
||||
THROW(!plugin_version.m_valid,
|
||||
"Invalid required version '" + req.version
|
||||
+ "' for plugin '" + req.name + "'",
|
||||
info.ctx);
|
||||
THROW(plugin_names.find(req.name) != plugin_names.end(),
|
||||
"Defined multiple alternative version requirements for plugin '"
|
||||
+ req.name + "'",
|
||||
info.ctx);
|
||||
plugin_names.insert(req.name);
|
||||
}
|
||||
m_required_plugin_versions.push_back(info.alternatives);
|
||||
}
|
||||
|
||||
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,
|
||||
"List has 'append' key but no list by that name already exists",
|
||||
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,
|
||||
"Macro has 'append' key but no macro by that name already exists",
|
||||
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 source = cfg.sources.at(info.source);
|
||||
if (!source)
|
||||
{
|
||||
cfg.res->add_warning(falco::load_result::LOAD_UNKNOWN_SOURCE,
|
||||
"Unknown source " + info.source + ", skipping",
|
||||
info.ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
auto prev = m_rule_infos.at(info.name);
|
||||
THROW(prev && prev->source != info.source,
|
||||
"Rule has been re-defined with a different source",
|
||||
info.ctx);
|
||||
|
||||
for (auto &ex : info.exceptions)
|
||||
{
|
||||
THROW(!ex.fields.is_valid(),
|
||||
"Rule exception item must have fields property with a list of fields",
|
||||
ex.ctx);
|
||||
validate_exception_info(*source, ex);
|
||||
}
|
||||
|
||||
define_info(m_rule_infos, info, m_cur_index++);
|
||||
}
|
||||
|
||||
void rule_loader::collector::append(configuration& cfg, rule_info& info)
|
||||
{
|
||||
auto prev = m_rule_infos.at(info.name);
|
||||
|
||||
THROW(!prev,
|
||||
"Rule has 'append' key but no rule by that name already exists",
|
||||
info.ctx);
|
||||
THROW(info.cond.empty() && info.exceptions.empty(),
|
||||
"Appended rule must have exceptions or condition property",
|
||||
info.ctx);
|
||||
|
||||
auto source = cfg.sources.at(prev->source);
|
||||
// note: this is not supposed to happen
|
||||
THROW(!source,
|
||||
std::string("Unknown source ") + prev->source,
|
||||
info.ctx);
|
||||
|
||||
if (!info.cond.empty())
|
||||
{
|
||||
prev->cond += " ";
|
||||
prev->cond += info.cond;
|
||||
}
|
||||
|
||||
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::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;
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#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<plugin_version_info::requirement_alternatives>& required_plugin_versions() const;
|
||||
|
||||
/*!
|
||||
\brief Returns the list of defined lists
|
||||
*/
|
||||
virtual const indexed_vector<list_info>& lists() const;
|
||||
|
||||
/*!
|
||||
\brief Returns the list of defined macros
|
||||
*/
|
||||
virtual const indexed_vector<macro_info>& macros() const;
|
||||
|
||||
/*!
|
||||
\brief Returns the list of defined rules
|
||||
*/
|
||||
virtual const indexed_vector<rule_info>& 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_info& info);
|
||||
|
||||
/*!
|
||||
\brief Updates the 'enabled' flag of an existing definition
|
||||
*/
|
||||
virtual void enable(configuration& cfg, rule_info& info);
|
||||
|
||||
private:
|
||||
uint32_t m_cur_index;
|
||||
indexed_vector<rule_info> m_rule_infos;
|
||||
indexed_vector<macro_info> m_macro_infos;
|
||||
indexed_vector<list_info> m_list_infos;
|
||||
std::vector<plugin_version_info::requirement_alternatives> m_required_plugin_versions;
|
||||
};
|
||||
|
||||
}; // namespace rule_loader
|
||||
@@ -1,543 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "rule_loader_compiler.h"
|
||||
#include "filter_macro_resolver.h"
|
||||
#include "filter_evttype_resolver.h"
|
||||
#include "filter_warning_resolver.h"
|
||||
|
||||
#define MAX_VISIBILITY ((uint32_t) -1)
|
||||
|
||||
#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";
|
||||
static std::string s_default_extra_fmt = "%container.name (id=%container.id)";
|
||||
|
||||
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_defined(const std::string& op)
|
||||
{
|
||||
auto ops = libsinsp::filter::parser::supported_operators();
|
||||
return find(ops.begin(), ops.end(), op) != ops.end();
|
||||
}
|
||||
|
||||
static inline bool is_operator_for_list(const std::string& op)
|
||||
{
|
||||
auto ops = libsinsp::filter::parser::supported_operators(true);
|
||||
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<gen_event_formatter> formatter;
|
||||
formatter = source.formatter_factory->create_formatter(fmt);
|
||||
return true;
|
||||
}
|
||||
catch(exception &e)
|
||||
{
|
||||
err = e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void build_rule_exception_infos(
|
||||
const std::vector<rule_loader::rule_exception_info>& exceptions,
|
||||
std::set<std::string>& exception_fields,
|
||||
std::string& condition)
|
||||
{
|
||||
std::string tmp;
|
||||
for (auto &ex : exceptions)
|
||||
{
|
||||
std::string icond;
|
||||
if(!ex.fields.is_list)
|
||||
{
|
||||
for (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 (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 (auto &field : ex.fields.items)
|
||||
{
|
||||
icond += k == 0 ? "" : " and ";
|
||||
if (values.items[k].is_list)
|
||||
{
|
||||
istr = "(";
|
||||
for (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;
|
||||
}
|
||||
}
|
||||
|
||||
// todo(jasondellaluce): this breaks string escaping in lists
|
||||
static bool resolve_list(std::string& cnd, const rule_loader::list_info& list)
|
||||
{
|
||||
static std::string blanks = " \t\n\r";
|
||||
static std::string delims = blanks + "(),=";
|
||||
std::string new_cnd;
|
||||
size_t start, end;
|
||||
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
|
||||
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 (auto &v : list.items)
|
||||
{
|
||||
if (!sub.empty())
|
||||
{
|
||||
sub += ", ";
|
||||
}
|
||||
sub += v;
|
||||
}
|
||||
// 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 void resolve_macros(
|
||||
indexed_vector<rule_loader::macro_info>& macros,
|
||||
std::shared_ptr<ast::expr>& ast,
|
||||
uint32_t visibility,
|
||||
const rule_loader::context &ctx)
|
||||
{
|
||||
filter_macro_resolver macro_resolver;
|
||||
for (auto &m : macros)
|
||||
{
|
||||
if (m.index < visibility)
|
||||
{
|
||||
macro_resolver.set_macro(m.name, m.cond_ast);
|
||||
}
|
||||
}
|
||||
macro_resolver.run(ast);
|
||||
|
||||
// Note: only complaining about the first unknown macro
|
||||
THROW(!macro_resolver.get_unknown_macros().empty(),
|
||||
std::string("Undefined macro '")
|
||||
+ *macro_resolver.get_unknown_macros().begin()
|
||||
+ "' used in filter.",
|
||||
ctx);
|
||||
|
||||
for (auto &m : macro_resolver.get_resolved_macros())
|
||||
{
|
||||
macros.at(m)->used = true;
|
||||
}
|
||||
}
|
||||
|
||||
// note: there is no visibility order between filter conditions and lists
|
||||
static std::shared_ptr<ast::expr> parse_condition(
|
||||
std::string condition,
|
||||
indexed_vector<rule_loader::list_info>& 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<ast::expr> 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);
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_output_substitutions(
|
||||
rule_loader::configuration& cfg,
|
||||
std::string& out)
|
||||
{
|
||||
if (out.find(s_container_info_fmt) != std::string::npos)
|
||||
{
|
||||
if (cfg.replace_output_container_info)
|
||||
{
|
||||
out = replace(out, s_container_info_fmt, cfg.output_extra);
|
||||
return;
|
||||
}
|
||||
out = replace(out, s_container_info_fmt, s_default_extra_fmt);
|
||||
}
|
||||
out += cfg.output_extra.empty() ? "" : " " + cfg.output_extra;
|
||||
}
|
||||
|
||||
void rule_loader::compiler::compile_list_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& out) const
|
||||
{
|
||||
std::string tmp;
|
||||
std::vector<std::string> used;
|
||||
for (auto &list : col.lists())
|
||||
{
|
||||
list_info v = list;
|
||||
v.items.clear();
|
||||
for (auto &item : list.items)
|
||||
{
|
||||
const auto ref = col.lists().at(item);
|
||||
if (ref && ref->index < list.visibility)
|
||||
{
|
||||
used.push_back(ref->name);
|
||||
for (auto val : ref->items)
|
||||
{
|
||||
quote_item(val);
|
||||
v.items.push_back(val);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = item;
|
||||
quote_item(tmp);
|
||||
v.items.push_back(tmp);
|
||||
}
|
||||
}
|
||||
v.used = false;
|
||||
out.insert(v, v.name);
|
||||
}
|
||||
for (auto &v : used)
|
||||
{
|
||||
out.at(v)->used = true;
|
||||
}
|
||||
}
|
||||
|
||||
// note: there is a visibility ordering between macros
|
||||
void rule_loader::compiler::compile_macros_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& out) const
|
||||
{
|
||||
for (auto &m : col.macros())
|
||||
{
|
||||
macro_info entry = m;
|
||||
entry.cond_ast = parse_condition(m.cond, lists, m.cond_ctx);
|
||||
entry.used = false;
|
||||
out.insert(entry, m.name);
|
||||
}
|
||||
|
||||
for (auto &m : out)
|
||||
{
|
||||
resolve_macros(out, m.cond_ast, m.visibility, m.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rule_loader::compiler::compile_rule_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& macros,
|
||||
indexed_vector<falco_rule>& out) const
|
||||
{
|
||||
std::string err, condition;
|
||||
std::set<falco::load_result::load_result::warning_code> warn_codes;
|
||||
filter_warning_resolver warn_resolver;
|
||||
for (auto &r : col.rules())
|
||||
{
|
||||
// skip the rule if below the minimum priority
|
||||
if (r.priority > cfg.min_priority)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto source = cfg.sources.at(r.source);
|
||||
// note: this is not supposed to happen
|
||||
|
||||
THROW(!source,
|
||||
std::string("Unknown source ") + 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);
|
||||
}
|
||||
auto ast = parse_condition(condition, lists, r.cond_ctx);
|
||||
resolve_macros(macros, ast, MAX_VISIBILITY, r.ctx);
|
||||
|
||||
// check for warnings in the filtering condition
|
||||
warn_codes.clear();
|
||||
if (warn_resolver.run(ast.get(), warn_codes))
|
||||
{
|
||||
for (auto &w : warn_codes)
|
||||
{
|
||||
cfg.res->add_warning(w, "", r.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// build rule output message
|
||||
rule.output = r.output;
|
||||
if (r.source == falco_common::syscall_source)
|
||||
{
|
||||
apply_output_substitutions(cfg, rule.output);
|
||||
}
|
||||
|
||||
if(!is_format_valid(*cfg.sources.at(r.source), rule.output, err))
|
||||
{
|
||||
throw rule_load_exception(
|
||||
falco::load_result::load_result::LOAD_ERR_COMPILE_OUTPUT,
|
||||
err,
|
||||
r.output_ctx);
|
||||
}
|
||||
|
||||
// construct rule definition and compile it to a filter
|
||||
rule.name = r.name;
|
||||
rule.source = r.source;
|
||||
rule.description = r.desc;
|
||||
rule.priority = r.priority;
|
||||
rule.tags = r.tags;
|
||||
|
||||
auto rule_id = out.insert(rule, rule.name);
|
||||
out.at(rule_id)->id = rule_id;
|
||||
|
||||
// This also compiles the filter, and might throw a
|
||||
// falco_exception with details on the compilation
|
||||
// failure.
|
||||
try {
|
||||
source->ruleset->add(*out.at(rule_id), ast);
|
||||
}
|
||||
catch (const falco_exception& e)
|
||||
{
|
||||
// Allow errors containing "nonexistent field" if
|
||||
// skip_if_unknown_filter is true
|
||||
std::string err = e.what();
|
||||
|
||||
if (err.find("nonexistent field") != std::string::npos &&
|
||||
r.skip_if_unknown_filter)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::load_result::LOAD_UNKNOWN_FIELD,
|
||||
e.what(),
|
||||
r.cond_ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw rule_loader::rule_load_exception(
|
||||
falco::load_result::load_result::LOAD_ERR_COMPILE_CONDITION,
|
||||
e.what(),
|
||||
r.cond_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// By default rules are enabled/disabled for the default ruleset
|
||||
if(r.enabled)
|
||||
{
|
||||
source->ruleset->enable(rule.name, true, cfg.default_ruleset_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
source->ruleset->disable(rule.name, true, cfg.default_ruleset_id);
|
||||
}
|
||||
|
||||
// populate set of event types and emit an special warning
|
||||
std::set<uint16_t> evttypes = { ppm_event_type::PPME_PLUGINEVENT_E };
|
||||
if(rule.source == falco_common::syscall_source)
|
||||
{
|
||||
evttypes.clear();
|
||||
filter_evttype_resolver().evttypes(ast, evttypes);
|
||||
if ((evttypes.empty() || evttypes.size() > 100)
|
||||
&& r.warn_evttypes)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::load_result::LOAD_NO_EVTTYPE,
|
||||
"Rule matches too many evt.type values. This has a significant performance penalty.",
|
||||
r.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rule_loader::compiler::compile(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<falco_rule>& out) const
|
||||
{
|
||||
indexed_vector<list_info> lists;
|
||||
indexed_vector<macro_info> macros;
|
||||
|
||||
// expand all lists, macros, and rules
|
||||
try
|
||||
{
|
||||
compile_list_infos(cfg, col, lists);
|
||||
compile_macros_infos(cfg, col, lists, macros);
|
||||
compile_rule_infos(cfg, col, lists, macros, out);
|
||||
}
|
||||
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 (auto &m : macros)
|
||||
{
|
||||
if (!m.used)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::load_result::LOAD_UNUSED_MACRO,
|
||||
"Macro not referred to by any other rule/macro",
|
||||
m.ctx);
|
||||
}
|
||||
}
|
||||
for (auto &l : lists)
|
||||
{
|
||||
if (!l.used)
|
||||
{
|
||||
cfg.res->add_warning(
|
||||
falco::load_result::LOAD_UNUSED_LIST,
|
||||
"List not referred to by any other rule/macro",
|
||||
l.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rule_loader.h"
|
||||
#include "rule_loader_collector.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;
|
||||
|
||||
/*!
|
||||
\brief Compiles a list of falco rules
|
||||
*/
|
||||
virtual void compile(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<falco_rule>& out) const;
|
||||
|
||||
private:
|
||||
void compile_list_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& out) const;
|
||||
|
||||
void compile_macros_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& out) const;
|
||||
|
||||
void compile_rule_infos(
|
||||
configuration& cfg,
|
||||
const collector& col,
|
||||
indexed_vector<list_info>& lists,
|
||||
indexed_vector<macro_info>& macros,
|
||||
indexed_vector<falco_rule>& out) const;
|
||||
};
|
||||
|
||||
}; // namespace rule_loader
|
||||
|
||||
@@ -14,13 +14,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "rule_reader.h"
|
||||
|
||||
#include "rule_loader_reader.h"
|
||||
|
||||
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(falco::load_result::LOAD_ERR_YAML_VALIDATE, (err), (ctx)); } }
|
||||
#define THROW(cond, err, ctx) { if ((cond)) { throw rule_loader::rule_load_exception(load_result::LOAD_ERR_YAML_VALIDATE, (err), (ctx)); } }
|
||||
|
||||
using namespace falco;
|
||||
|
||||
// Don't call this directly, call decode_val/decode_optional_val instead.
|
||||
template <typename T>
|
||||
@@ -243,7 +241,7 @@ static void read_rule_exceptions(
|
||||
|
||||
static void read_item(
|
||||
rule_loader::configuration& cfg,
|
||||
rule_loader::collector& collector,
|
||||
rule_loader& loader,
|
||||
const YAML::Node& item,
|
||||
const rule_loader::context& parent)
|
||||
{
|
||||
@@ -257,7 +255,7 @@ static void read_item(
|
||||
rule_loader::engine_version_info v(ctx);
|
||||
|
||||
decode_val(item, "required_engine_version", v.version, ctx);
|
||||
collector.define(cfg, v);
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
else if(item["required_plugin_versions"].IsDefined())
|
||||
{
|
||||
@@ -298,7 +296,7 @@ static void read_item(
|
||||
}
|
||||
}
|
||||
|
||||
collector.define(cfg, v);
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["list"].IsDefined())
|
||||
@@ -319,11 +317,11 @@ static void read_item(
|
||||
|
||||
if(append)
|
||||
{
|
||||
collector.append(cfg, v);
|
||||
loader.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.define(cfg, v);
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["macro"].IsDefined())
|
||||
@@ -347,11 +345,11 @@ static void read_item(
|
||||
|
||||
if(append)
|
||||
{
|
||||
collector.append(cfg, v);
|
||||
loader.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.define(cfg, v);
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["rule"].IsDefined())
|
||||
@@ -381,7 +379,7 @@ static void read_item(
|
||||
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx);
|
||||
}
|
||||
read_rule_exceptions(item, v, ctx, append);
|
||||
collector.append(cfg, v);
|
||||
loader.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -396,7 +394,7 @@ static void read_item(
|
||||
!item["priority"].IsDefined())
|
||||
{
|
||||
decode_val(item, "enabled", v.enabled, ctx);
|
||||
collector.enable(cfg, v);
|
||||
loader.enable(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -423,18 +421,18 @@ static void read_item(
|
||||
decode_optional_val(item, "skip-if-unknown-filter", v.skip_if_unknown_filter, ctx);
|
||||
decode_tags(item, v.tags, ctx);
|
||||
read_rule_exceptions(item, v, ctx, append);
|
||||
collector.define(cfg, v);
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rule_loader::context ctx(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent);
|
||||
cfg.res->add_warning(falco::load_result::LOAD_UNKNOWN_ITEM, "Unknown top level item", ctx);
|
||||
cfg.res->add_warning(load_result::LOAD_UNKNOWN_ITEM, "Unknown top level item", ctx);
|
||||
}
|
||||
}
|
||||
|
||||
bool rule_loader::reader::read(rule_loader::configuration& cfg, collector& collector)
|
||||
bool rule_reader::load(rule_loader::configuration& cfg, rule_loader& loader)
|
||||
{
|
||||
std::vector<YAML::Node> docs;
|
||||
try
|
||||
@@ -444,7 +442,7 @@ bool rule_loader::reader::read(rule_loader::configuration& cfg, collector& colle
|
||||
catch(const exception& e)
|
||||
{
|
||||
rule_loader::context ctx(cfg.name);
|
||||
cfg.res->add_error(falco::load_result::LOAD_ERR_YAML_PARSE, e.what(), ctx);
|
||||
cfg.res->add_error(load_result::LOAD_ERR_YAML_PARSE, e.what(), ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -467,7 +465,7 @@ bool rule_loader::reader::read(rule_loader::configuration& cfg, collector& colle
|
||||
{
|
||||
if (!it->IsNull())
|
||||
{
|
||||
read_item(cfg, collector, *it, ctx);
|
||||
read_item(cfg, loader, *it, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,30 +16,22 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "rule_loader.h"
|
||||
#include "rule_loader_collector.h"
|
||||
|
||||
namespace rule_loader
|
||||
{
|
||||
|
||||
/*!
|
||||
\brief Reads the contents of a ruleset
|
||||
*/
|
||||
class reader
|
||||
class rule_reader
|
||||
{
|
||||
public:
|
||||
reader() = default;
|
||||
virtual ~reader() = default;
|
||||
reader(reader&&) = default;
|
||||
reader& operator = (reader&&) = default;
|
||||
reader(const reader&) = default;
|
||||
reader& operator = (const reader&) = default;
|
||||
virtual ~rule_reader() = default;
|
||||
|
||||
/*!
|
||||
\brief Reads the contents of a ruleset and uses a collector to store
|
||||
\brief Reads the contents of a ruleset and uses a loader to store
|
||||
thew new definitions
|
||||
*/
|
||||
virtual bool read(configuration& cfg, collector& loader);
|
||||
virtual bool load(rule_loader::configuration& cfg, rule_loader& loader);
|
||||
};
|
||||
|
||||
}; // namespace rule_loader
|
||||
@@ -34,10 +34,6 @@ class stats_manager
|
||||
public:
|
||||
stats_manager();
|
||||
virtual ~stats_manager();
|
||||
stats_manager(stats_manager&&) = default;
|
||||
stats_manager& operator = (stats_manager&&) = default;
|
||||
stats_manager(const stats_manager&) = default;
|
||||
stats_manager& operator = (const stats_manager&) = default;
|
||||
|
||||
/*!
|
||||
\brief Erases the internal state and statistics data
|
||||
|
||||
@@ -36,7 +36,7 @@ application::run_result application::configure_syscall_buffer_size()
|
||||
uint16_t index = m_state->config->m_syscall_buf_size_preset;
|
||||
if(index < MIN_INDEX || index > MAX_INDEX)
|
||||
{
|
||||
return run_result::fatal("The 'syscall_buf_size_preset' value must be between '" + std::to_string(MIN_INDEX) + "' and '" + std::to_string(MAX_INDEX) + "'\n");
|
||||
return run_result::fatal("The index must be between '" + std::to_string(MIN_INDEX) + "' and '" + std::to_string(MAX_INDEX) + "'\n");
|
||||
}
|
||||
|
||||
/* Sizes from `1 MB` to `512 MB`. The index `0` is reserved, users cannot use it! */
|
||||
@@ -48,24 +48,23 @@ application::run_result application::configure_syscall_buffer_size()
|
||||
long page_size = getpagesize();
|
||||
if(page_size <= 0)
|
||||
{
|
||||
m_state->syscall_buffer_bytes_size = DEFAULT_BYTE_SIZE;
|
||||
falco_logger::log(LOG_WARNING, "Unable to get the system page size through 'getpagesize()'. Try to use the default syscall buffer dimension: " + std::to_string(DEFAULT_BYTE_SIZE) + " bytes\n");
|
||||
falco_logger::log(LOG_WARNING, "Unable to get the system page size through 'getpagesize()'. Try to use the default syscall buffer dimension: " + std::to_string(DEFAULT_BYTE_SIZE) + " bytes.\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
/* Check if the chosen size is a multiple of the page size. */
|
||||
if(chosen_size % page_size != 0)
|
||||
{
|
||||
return run_result::fatal("The chosen 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 'syscall_buf_size_preset' value in the Falco configuration file\n");
|
||||
return run_result::fatal("The chosen size '" + std::to_string(chosen_size) + "' is not a multiple of your system page '" + std::to_string(page_size) + "'. Please choose a greater index.\n");
|
||||
}
|
||||
|
||||
/* Check if the chosen size is greater than `2 * page_size`. */
|
||||
if((chosen_size / page_size) <= 2)
|
||||
{
|
||||
return run_result::fatal("The chosen syscall buffer size '" + std::to_string(chosen_size) + "' is not greater than '2 * " + std::to_string(page_size) + "' where '" + std::to_string(page_size) + "' is your system page size. Please configure a greater 'syscall_buf_size_preset' value in the Falco configuration file\n");
|
||||
return run_result::fatal("The chosen size '" + std::to_string(chosen_size) + "' is not greater than '2 * " + std::to_string(page_size) + "'. Please choose a greater index.\n");
|
||||
}
|
||||
|
||||
m_state->syscall_buffer_bytes_size = chosen_size;
|
||||
falco_logger::log(LOG_INFO, "The chosen syscall buffer dimension is: " + std::to_string(chosen_size) + " bytes (" + std::to_string(chosen_size / (uint64_t)(1024 * 1024)) + " MBs)\n");
|
||||
falco_logger::log(LOG_INFO, "The chosen syscall buffer dimension is: " + std::to_string(chosen_size) + " bytes.\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -33,24 +33,6 @@ void application::configure_interesting_sets()
|
||||
* plus syscalls for Falco default rules.
|
||||
*/
|
||||
m_state->ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set();
|
||||
m_state->ppm_event_info_of_interest = inspector->get_event_set_from_ppm_sc_set(m_state->ppm_sc_of_interest);
|
||||
|
||||
/* Fill-up the set of event infos of interest */
|
||||
for (uint32_t ev = 2; ev < PPM_EVENT_MAX; ev++)
|
||||
{
|
||||
if (!sinsp::is_old_version_event(ev)
|
||||
&& !sinsp::is_unused_event(ev)
|
||||
&& !sinsp::is_unknown_event(ev))
|
||||
{
|
||||
/* So far we only covered syscalls, so we add other kinds of
|
||||
interesting events. In this case, we are also interested in
|
||||
metaevents and in the procexit tracepoint event. */
|
||||
if (sinsp::is_metaevent(ev) || ev == PPME_PROCEXIT_1_E)
|
||||
{
|
||||
m_state->ppm_event_info_of_interest.insert(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In this case we get the tracepoints for the `libsinsp` state and we remove
|
||||
* the `sched_switch` tracepoint since it is highly noisy and not so useful
|
||||
|
||||
@@ -37,7 +37,7 @@ application::run_result application::create_requested_paths()
|
||||
std::ifstream reader(m_options.gvisor_config);
|
||||
if (reader.fail())
|
||||
{
|
||||
return run_result::fatal(m_options.gvisor_config + ": cannot open file");
|
||||
return run_result::fatal(m_options.gvisor_config + ": cannot open file.");
|
||||
}
|
||||
|
||||
nlohmann::json parsed_json;
|
||||
@@ -67,7 +67,7 @@ application::run_result application::create_requested_paths()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state->config->m_grpc_enabled && !m_state->config->m_grpc_bind_address.empty())
|
||||
if (!m_state->config->m_grpc_bind_address.empty())
|
||||
{
|
||||
if(falco::utils::network::is_unix_scheme(m_state->config->m_grpc_bind_address))
|
||||
{
|
||||
@@ -108,4 +108,4 @@ int application::create_dir(const std::string &path)
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -30,24 +30,26 @@ using namespace falco::app;
|
||||
// provided application, and in unregister_signal_handlers it will be
|
||||
// rebound back to the dummy application.
|
||||
|
||||
static application dummy;
|
||||
static std::reference_wrapper<application> s_app = dummy;
|
||||
static int inot_fd;
|
||||
|
||||
static void terminate_signal_handler(int signal)
|
||||
static void signal_callback(int signal)
|
||||
{
|
||||
ASSERT(falco::app::g_terminate.is_lock_free());
|
||||
falco::app::g_terminate.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
|
||||
s_app.get().terminate();
|
||||
}
|
||||
|
||||
static void reopen_outputs_signal_handler(int signal)
|
||||
static void reopen_outputs(int signal)
|
||||
{
|
||||
ASSERT(falco::app::g_reopen_outputs.is_lock_free());
|
||||
falco::app::g_reopen_outputs.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
falco_logger::log(LOG_INFO, "SIGUSR1 received, reopening outputs...\n");
|
||||
s_app.get().reopen_outputs();
|
||||
}
|
||||
|
||||
static void restart_signal_handler(int signal)
|
||||
static void restart_falco(int signal)
|
||||
{
|
||||
ASSERT(falco::app::g_restart.is_lock_free());
|
||||
falco::app::g_restart.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
|
||||
s_app.get().restart();
|
||||
}
|
||||
|
||||
bool application::create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
@@ -72,52 +74,42 @@ bool application::create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
|
||||
application::run_result application::create_signal_handlers()
|
||||
{
|
||||
falco::app::g_terminate.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
|
||||
falco::app::g_restart.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
|
||||
falco::app::g_reopen_outputs.store(APP_SIGNAL_NOT_SET, std::memory_order_seq_cst);
|
||||
|
||||
if (!g_terminate.is_lock_free()
|
||||
|| !g_restart.is_lock_free()
|
||||
|| !g_reopen_outputs.is_lock_free())
|
||||
{
|
||||
falco_logger::log(LOG_WARNING, "Bundled atomics implementation is not lock-free, signal handlers may be unstable\n");
|
||||
}
|
||||
|
||||
run_result ret;
|
||||
if(! create_handler(SIGINT, ::terminate_signal_handler, ret) ||
|
||||
! create_handler(SIGTERM, ::terminate_signal_handler, ret) ||
|
||||
! create_handler(SIGUSR1, ::reopen_outputs_signal_handler, ret) ||
|
||||
! create_handler(SIGHUP, ::restart_signal_handler, ret))
|
||||
if(! create_handler(SIGINT, ::signal_callback, ret) ||
|
||||
! create_handler(SIGTERM, ::signal_callback, ret) ||
|
||||
! create_handler(SIGUSR1, ::reopen_outputs, ret) ||
|
||||
! create_handler(SIGHUP, ::restart_falco, ret))
|
||||
{
|
||||
// we use the if just to make sure we return at the first failed statement
|
||||
return ret;
|
||||
}
|
||||
|
||||
s_app = *this;
|
||||
return ret;
|
||||
}
|
||||
|
||||
application::run_result application::attach_inotify_signals()
|
||||
{
|
||||
if (m_state->config->m_watch_config_files)
|
||||
if (m_state->config->m_watch_config_files)
|
||||
{
|
||||
inot_fd = inotify_init();
|
||||
if (inot_fd == -1)
|
||||
{
|
||||
return run_result::fatal("Could not create inotify handler");
|
||||
return run_result::fatal("Could not create inotify handler.");
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = restart_signal_handler;
|
||||
sa.sa_handler = restart_falco;
|
||||
if (sigaction(SIGIO, &sa, NULL) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to link SIGIO to inotify handler");
|
||||
return run_result::fatal("Failed to link SIGIO to inotify handler.");
|
||||
}
|
||||
|
||||
/* Set owner process that is to receive "I/O possible" signal */
|
||||
if (fcntl(inot_fd, F_SETOWN, getpid()) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to setting owner on inotify handler");
|
||||
return run_result::fatal("Failed to setting owner on inotify handler.");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -127,14 +119,14 @@ application::run_result application::attach_inotify_signals()
|
||||
int flags = fcntl(inot_fd, F_GETFL);
|
||||
if (fcntl(inot_fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to setting flags on inotify handler");
|
||||
return run_result::fatal("Failed to setting flags on inotify handler.");
|
||||
}
|
||||
|
||||
// Watch conf file
|
||||
int wd = inotify_add_watch(inot_fd, m_options.conf_filename.c_str(), IN_CLOSE_WRITE);
|
||||
if (wd == -1)
|
||||
{
|
||||
return run_result::fatal("Failed to watch conf file");
|
||||
return run_result::fatal("Failed to watch conf file.");
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + m_options.conf_filename +"\n");
|
||||
|
||||
@@ -146,7 +138,7 @@ application::run_result application::attach_inotify_signals()
|
||||
{
|
||||
return run_result::fatal("Failed to watch rule file: " + rule);
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + rule +"\n");
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + rule +".\n");
|
||||
}
|
||||
|
||||
// Watch specified rules folders, if any:
|
||||
@@ -160,7 +152,7 @@ application::run_result application::attach_inotify_signals()
|
||||
{
|
||||
return run_result::fatal("Failed to watch rule folder: " + fld);
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder\n");
|
||||
falco_logger::log(LOG_DEBUG, "Watching " + fld +" folder.\n");
|
||||
}
|
||||
}
|
||||
return run_result::ok();
|
||||
@@ -178,5 +170,7 @@ bool application::unregister_signal_handlers(std::string &errstr)
|
||||
errstr = ret.errstr;
|
||||
return false;
|
||||
}
|
||||
|
||||
s_app = dummy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ application::run_result application::daemonize()
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
// error
|
||||
return run_result::fatal("Could not fork");
|
||||
return run_result::fatal("Could not fork.");
|
||||
} else if (pid > 0) {
|
||||
// parent. Write child pid to pidfile and exit
|
||||
std::ofstream pidfile;
|
||||
@@ -54,7 +54,7 @@ application::run_result application::daemonize()
|
||||
// Become own process group.
|
||||
sid = setsid();
|
||||
if (sid < 0) {
|
||||
return run_result::fatal("Could not set session id");
|
||||
return run_result::fatal("Could not set session id.");
|
||||
}
|
||||
|
||||
// Set umask so no files are world anything or group writable.
|
||||
@@ -62,7 +62,7 @@ application::run_result application::daemonize()
|
||||
|
||||
// Change working directory to '/'
|
||||
if ((chdir("/")) < 0) {
|
||||
return run_result::fatal("Could not change working directory to '/'");
|
||||
return run_result::fatal("Could not change working directory to '/'.");
|
||||
}
|
||||
|
||||
// Close stdin, stdout, stderr and reopen to /dev/null
|
||||
|
||||
@@ -46,7 +46,7 @@ void application::configure_output_format()
|
||||
}
|
||||
else if(m_options.gvisor_config != "")
|
||||
{
|
||||
output_format = "container=%container.name (id=%container.id) vpid=%proc.vpid vtid=%thread.vtid";
|
||||
output_format = "container=%container.id pid=%proc.vpid tid=%thread.vtid";
|
||||
replace_container_info = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,9 @@ application::run_result application::load_config()
|
||||
falco_logger::set_time_format_iso_8601(m_state->config->m_time_format_iso_8601);
|
||||
|
||||
// log after config init because config determines where logs go
|
||||
falco_logger::log(LOG_INFO, "Falco version: " + std::string(FALCO_VERSION) + " (" + std::string(FALCO_TARGET_ARCH) + ")\n");
|
||||
if (!m_state->cmdline.empty())
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "CLI args: " + m_state->cmdline);
|
||||
}
|
||||
falco_logger::log(LOG_INFO, "Falco version: " + std::string(FALCO_VERSION) + "\n");
|
||||
falco_logger::log(LOG_INFO, "Falco initialized with configuration file: " + m_options.conf_filename + "\n");
|
||||
falco_logger::log(LOG_INFO, "Falco compiled for: " + std::string(FALCO_TARGET_ARCH) + "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -17,25 +17,8 @@ limitations under the License.
|
||||
#include "application.h"
|
||||
#include <plugin_manager.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
bool application::check_rules_plugin_requirements(std::string& err)
|
||||
{
|
||||
// Ensure that all plugins are compatible with the loaded set of rules
|
||||
// note: offline inspector contains all the loaded plugins
|
||||
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
|
||||
for (const auto &plugin : m_state->offline_inspector->get_plugin_manager()->plugins())
|
||||
{
|
||||
falco_engine::plugin_version_requirement req;
|
||||
req.name = plugin->name();
|
||||
req.version = plugin->plugin_version().as_string();
|
||||
plugin_reqs.push_back(req);
|
||||
}
|
||||
return m_state->engine->check_plugin_requirements(plugin_reqs, err);
|
||||
}
|
||||
|
||||
void application::check_for_ignored_events()
|
||||
{
|
||||
/* Get the events from the rules. */
|
||||
@@ -45,29 +28,51 @@ void application::check_for_ignored_events()
|
||||
|
||||
/* Get the events we consider interesting from the application state `ppm_sc` codes. */
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
std::unordered_set<uint32_t> events(rule_events.begin(), rule_events.end());
|
||||
auto interesting_events = inspector->get_event_set_from_ppm_sc_set(m_state->ppm_sc_of_interest);
|
||||
std::unordered_set<uint32_t> ignored_events;
|
||||
|
||||
auto event_names = inspector->get_events_names(events);
|
||||
for (const auto& n : inspector->get_events_names(m_state->ppm_event_info_of_interest))
|
||||
for(const auto& it : rule_events)
|
||||
{
|
||||
event_names.erase(n);
|
||||
/* If we have the old version of the event we will have also the recent one
|
||||
* so we can avoid analyzing the presence of old events.
|
||||
*/
|
||||
if(sinsp::is_old_version_event(it))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Here we are interested only in syscall events the internal events are not
|
||||
* altered without the `-A` flag.
|
||||
*
|
||||
* TODO: We could consider also the tracepoint events here but right now we don't have
|
||||
* the support from the libraries.
|
||||
*/
|
||||
if(!sinsp::is_syscall_event(it))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the event is not in this set it is not considered by Falco. */
|
||||
if(interesting_events.find(it) == interesting_events.end())
|
||||
{
|
||||
ignored_events.insert(it);
|
||||
}
|
||||
}
|
||||
|
||||
if(event_names.empty())
|
||||
if(ignored_events.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the names of the ignored events and print them. */
|
||||
std::cerr << "Rules match ignored syscall: warning (ignored-evttype):" << std::endl;
|
||||
std::cerr << "Loaded rules match the following events: ";
|
||||
bool first = true;
|
||||
auto event_names = inspector->get_events_names(ignored_events);
|
||||
std::cerr << std::endl << "Rules match ignored syscall: warning (ignored-evttype):" << std::endl;
|
||||
std::cerr << "Loaded rules match the following events:" << std::endl;
|
||||
for(const auto& it : event_names)
|
||||
{
|
||||
std::cerr << (first ? "" : ", ") << it.c_str();
|
||||
first = false;
|
||||
std::cerr << "\t- " << it.c_str() << std::endl;
|
||||
}
|
||||
std::cerr << std::endl << "But these events are not returned unless running falco with -A" << std::endl;
|
||||
std::cerr << "But these events are not returned unless running falco with -A" << std::endl << std::endl;
|
||||
}
|
||||
|
||||
application::run_result application::load_rules_files()
|
||||
@@ -129,10 +134,20 @@ application::run_result application::load_rules_files()
|
||||
}
|
||||
}
|
||||
|
||||
std::string err = "";
|
||||
if (!check_rules_plugin_requirements(err))
|
||||
// Ensure that all plugins are compatible with the loaded set of rules
|
||||
// note: offline inspector contains all the loaded plugins
|
||||
std::string plugin_vers_err = "";
|
||||
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
|
||||
for (const auto &plugin : m_state->offline_inspector->get_plugin_manager()->plugins())
|
||||
{
|
||||
falco_engine::plugin_version_requirement req;
|
||||
req.name = plugin->name();
|
||||
req.version = plugin->plugin_version().as_string();
|
||||
plugin_reqs.push_back(req);
|
||||
}
|
||||
if (!m_state->engine->check_plugin_requirements(plugin_reqs, plugin_vers_err))
|
||||
{
|
||||
return run_result::fatal(err);
|
||||
return run_result::fatal(plugin_vers_err);
|
||||
}
|
||||
|
||||
for (const auto& substring : m_options.disabled_rule_substrings)
|
||||
|
||||
@@ -54,7 +54,7 @@ application::run_result application::open_live_inspector(
|
||||
if (p->caps() & CAP_SOURCING && p->event_source() == source)
|
||||
{
|
||||
auto cfg = m_state->plugin_configs.at(p->name());
|
||||
falco_logger::log(LOG_INFO, "Opening capture with plugin '" + cfg->m_name + "'\n");
|
||||
falco_logger::log(LOG_INFO, "Falco uses the '" + cfg->m_name + "' plugin\n");
|
||||
inspector->open_plugin(cfg->m_name, cfg->m_open_params);
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -67,19 +67,14 @@ application::run_result application::open_live_inspector(
|
||||
//
|
||||
// Falco uses a ptrace(2) based userspace implementation.
|
||||
// Regardless of the implementation, the underlying method remains the same.
|
||||
falco_logger::log(LOG_INFO, "Opening capture with udig\n");
|
||||
falco_logger::log(LOG_INFO, "Starting capture with udig\n");
|
||||
inspector->open_udig();
|
||||
}
|
||||
else if(!m_options.gvisor_config.empty()) /* gvisor engine. */
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Opening capture with gVisor. Configuration path: " + m_options.gvisor_config);
|
||||
falco_logger::log(LOG_INFO, "Enabled event collection from gVisor. Configuration path: " + m_options.gvisor_config);
|
||||
inspector->open_gvisor(m_options.gvisor_config, m_options.gvisor_root);
|
||||
}
|
||||
else if(m_options.modern_bpf) /* modern BPF engine. */
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Opening capture with modern BPF probe");
|
||||
inspector->open_modern_bpf(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
|
||||
}
|
||||
else if(getenv(FALCO_BPF_ENV_VARIABLE) != NULL) /* BPF engine. */
|
||||
{
|
||||
const char *bpf_probe_path = std::getenv(FALCO_BPF_ENV_VARIABLE);
|
||||
@@ -95,23 +90,23 @@ application::run_result application::open_live_inspector(
|
||||
snprintf(full_path, PATH_MAX, "%s/%s", home, FALCO_PROBE_BPF_FILEPATH);
|
||||
bpf_probe_path = full_path;
|
||||
}
|
||||
falco_logger::log(LOG_INFO, "Opening capture with BPF probe. BPF probe path: " + std::string(bpf_probe_path));
|
||||
falco_logger::log(LOG_INFO, "Starting capture with BPF probe. BPF probe path: " + std::string(bpf_probe_path));
|
||||
inspector->open_bpf(bpf_probe_path, m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
|
||||
}
|
||||
else /* Kernel module (default). */
|
||||
{
|
||||
try
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Opening capture with Kernel module");
|
||||
falco_logger::log(LOG_INFO, "Starting capture with Kernel module.");
|
||||
inspector->open_kmod(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
|
||||
}
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
// Try to insert the Falco kernel module
|
||||
falco_logger::log(LOG_INFO, "Trying to inject the Kernel module and opening the capture again...");
|
||||
falco_logger::log(LOG_INFO, "Trying to inject the Kernel module and starting the capture again...");
|
||||
if(system("modprobe " DRIVER_NAME " > /dev/null 2> /dev/null"))
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "Unable to load the driver\n");
|
||||
falco_logger::log(LOG_ERR, "Unable to load the driver.\n");
|
||||
}
|
||||
inspector->open_kmod(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
|
||||
}
|
||||
|
||||
@@ -39,26 +39,25 @@ application::run_result application::print_ignored_events()
|
||||
configure_interesting_sets();
|
||||
|
||||
/* Search for all the ignored syscalls. */
|
||||
std::unordered_set<uint32_t> all_events;
|
||||
for (uint32_t j = 0; j < PPM_EVENT_MAX; j++)
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
std::unordered_set<uint32_t> all_ppm_sc = inspector->get_all_ppm_sc();
|
||||
std::unordered_set<uint32_t> ignored_ppm_sc;
|
||||
|
||||
for(const auto& it : all_ppm_sc)
|
||||
{
|
||||
if (!sinsp::is_old_version_event(j)
|
||||
&& !sinsp::is_unused_event(j)
|
||||
&& !sinsp::is_unknown_event(j))
|
||||
/* If the syscall is not in this set we ignore it. */
|
||||
if(m_state->ppm_sc_of_interest.find(it) == m_state->ppm_sc_of_interest.end())
|
||||
{
|
||||
all_events.insert(j);
|
||||
ignored_ppm_sc.insert(it);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
auto ignored_event_names = inspector->get_events_names(all_events);
|
||||
for (const auto &n : inspector->get_events_names(m_state->ppm_event_info_of_interest))
|
||||
{
|
||||
ignored_event_names.erase(n);
|
||||
}
|
||||
/* Obtain the ignored events names from the ignored syscalls. */
|
||||
auto ignored_events = inspector->get_event_set_from_ppm_sc_set(ignored_ppm_sc);
|
||||
auto event_names = inspector->get_events_names(ignored_events);
|
||||
|
||||
std::cout << "Ignored Event(s):" << std::endl;
|
||||
for(const auto& it : ignored_event_names)
|
||||
for(const auto& it : event_names)
|
||||
{
|
||||
std::cout << "- " << it.c_str() << std::endl;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ application::run_result application::print_page_size()
|
||||
}
|
||||
else
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Your system page size is: " + std::to_string(page_size) + " bytes\n");
|
||||
falco_logger::log(LOG_INFO, "Your system page size is: " + std::to_string(page_size) + " bytes.\n");
|
||||
}
|
||||
return run_result::exit();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ limitations under the License.
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
static std::string read_file(const std::string &filename)
|
||||
static std::string read_file(std::string &filename)
|
||||
{
|
||||
std::ifstream t(filename);
|
||||
std::string str((std::istreambuf_iterator<char>(t)),
|
||||
|
||||
@@ -16,97 +16,17 @@ limitations under the License.
|
||||
|
||||
#include "application.h"
|
||||
|
||||
#include <fields_info.h>
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
struct event_entry
|
||||
{
|
||||
bool is_enter;
|
||||
bool available;
|
||||
std::string name;
|
||||
struct ppm_event_info info;
|
||||
};
|
||||
|
||||
static std::vector<event_entry> get_event_entries(bool include_generics, const std::unordered_set<uint32_t>& available)
|
||||
{
|
||||
event_entry entry;
|
||||
std::vector<event_entry> events;
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
const struct ppm_event_info* etable = inspector->get_event_info_tables()->m_event_info;
|
||||
|
||||
// skip generic events
|
||||
for(uint32_t evt = PPME_GENERIC_X + 1; evt < PPM_EVENT_MAX; evt++)
|
||||
{
|
||||
if (!sinsp::is_old_version_event(evt)
|
||||
&& !sinsp::is_unused_event(evt)
|
||||
&& !sinsp::is_unknown_event(evt))
|
||||
{
|
||||
entry.is_enter = PPME_IS_ENTER(evt);
|
||||
entry.available = available.find(evt) != available.end();
|
||||
entry.name = etable[evt].name;
|
||||
entry.info = etable[evt];
|
||||
events.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (include_generics)
|
||||
{
|
||||
// append generic events
|
||||
const auto generic_syscalls = inspector->get_events_names({PPME_GENERIC_E});
|
||||
for (const auto& name : generic_syscalls)
|
||||
{
|
||||
for(uint32_t evt = PPME_GENERIC_E; evt <= PPME_GENERIC_X; evt++)
|
||||
{
|
||||
entry.is_enter = PPME_IS_ENTER(evt);
|
||||
entry.available = available.find(evt) != available.end();
|
||||
entry.name = name;
|
||||
entry.info = etable[evt];
|
||||
events.push_back(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
application::run_result application::print_syscall_events()
|
||||
{
|
||||
if(m_options.list_syscall_events)
|
||||
{
|
||||
configure_interesting_sets();
|
||||
const auto events = get_event_entries(false, m_state->ppm_event_info_of_interest);
|
||||
|
||||
if(m_options.markdown)
|
||||
{
|
||||
printf("Falco | Dir | Event\n");
|
||||
printf(":-----|:----|:-----\n");
|
||||
}
|
||||
|
||||
for (const auto& e : events)
|
||||
{
|
||||
char dir = e.is_enter ? '>' : '<';
|
||||
if (m_options.markdown)
|
||||
{
|
||||
printf(e.available ? "Yes" : "No");
|
||||
printf(" | %c | **%s**(", dir, e.name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%c %s(", dir, e.name.c_str());
|
||||
}
|
||||
|
||||
for(uint32_t k = 0; k < e.info.nparams; k++)
|
||||
{
|
||||
if(k != 0)
|
||||
{
|
||||
printf(", ");
|
||||
}
|
||||
|
||||
printf("%s %s", param_type_to_string(e.info.params[k].type),
|
||||
e.info.params[k].name);
|
||||
}
|
||||
printf(")\n");
|
||||
}
|
||||
|
||||
// We know this function doesn't hold into the raw pointer value
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
list_events(inspector.get(), m_options.markdown);
|
||||
return run_result::exit();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ application::run_result application::print_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);
|
||||
printf("Driver:\n");
|
||||
printf(" API version: %lu.%lu.%lu\n", driver_api_major, driver_api_minor, driver_api_patch);
|
||||
printf(" Schema version: %lu.%lu.%lu\n", driver_schema_major, driver_schema_minor, driver_schema_patch);
|
||||
printf(" API version: %ld.%ld.%ld\n", driver_api_major, driver_api_minor, driver_api_patch);
|
||||
printf(" Schema version: %ld.%ld.%ld\n", driver_schema_major, driver_schema_minor, driver_schema_patch);
|
||||
printf(" Default driver: %s\n", DRIVER_VERSION);
|
||||
|
||||
return run_result::exit();
|
||||
|
||||
@@ -20,7 +20,6 @@ limitations under the License.
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <atomic>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "falco_utils.h"
|
||||
@@ -98,19 +97,9 @@ application::run_result application::do_inspect(
|
||||
{
|
||||
rc = inspector->next(&ev);
|
||||
|
||||
if (should_reopen_outputs())
|
||||
if(m_state->terminate.load(std::memory_order_seq_cst)
|
||||
|| m_state->restart.load(std::memory_order_seq_cst))
|
||||
{
|
||||
reopen_outputs();
|
||||
}
|
||||
|
||||
if(should_terminate())
|
||||
{
|
||||
terminate();
|
||||
break;
|
||||
}
|
||||
else if(should_restart())
|
||||
{
|
||||
restart();
|
||||
break;
|
||||
}
|
||||
else if(rc == SCAP_TIMEOUT)
|
||||
@@ -228,7 +217,6 @@ void application::process_inspector_events(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
std::shared_ptr<stats_writer> statsw,
|
||||
std::string source, // an empty source represents capture mode
|
||||
application::source_sync_context* sync,
|
||||
application::run_result* res) noexcept
|
||||
{
|
||||
try
|
||||
@@ -276,11 +264,6 @@ void application::process_inspector_events(
|
||||
{
|
||||
*res = run_result::fatal(e.what());
|
||||
}
|
||||
|
||||
if (sync)
|
||||
{
|
||||
sync->finish();
|
||||
}
|
||||
}
|
||||
|
||||
static std::shared_ptr<stats_writer> init_stats_writer(const cmdline_options& opts)
|
||||
@@ -301,7 +284,6 @@ static std::shared_ptr<stats_writer> init_stats_writer(const cmdline_options& op
|
||||
application::run_result application::process_events()
|
||||
{
|
||||
application::run_result res = run_result::ok();
|
||||
bool termination_forced = false;
|
||||
|
||||
// Notify engine that we finished loading and enabling all rules
|
||||
m_state->engine->complete_rule_loading();
|
||||
@@ -318,7 +300,7 @@ application::run_result application::process_events()
|
||||
return res;
|
||||
}
|
||||
|
||||
process_inspector_events(m_state->offline_inspector, statsw, "", nullptr, &res);
|
||||
process_inspector_events(m_state->offline_inspector, statsw, "", &res);
|
||||
m_state->offline_inspector->close();
|
||||
|
||||
// Honor -M also when using a trace file.
|
||||
@@ -331,71 +313,44 @@ application::run_result application::process_events()
|
||||
}
|
||||
else
|
||||
{
|
||||
struct live_context
|
||||
typedef struct
|
||||
{
|
||||
live_context() = default;
|
||||
live_context(live_context&&) = default;
|
||||
live_context& operator = (live_context&&) = default;
|
||||
live_context(const live_context&) = default;
|
||||
live_context& operator = (const live_context&) = default;
|
||||
|
||||
// the name of the source of which events are processed
|
||||
std::string source;
|
||||
// the result of the event processing loop
|
||||
application::run_result res;
|
||||
// if non-null, the thread on which events are processed
|
||||
std::unique_ptr<std::thread> thread;
|
||||
// used for thread synchronization purposes
|
||||
std::unique_ptr<application::source_sync_context> sync;
|
||||
};
|
||||
|
||||
print_enabled_event_sources();
|
||||
} live_context;
|
||||
|
||||
// start event processing for all enabled sources
|
||||
falco::semaphore termination_sem(m_state->enabled_sources.size());
|
||||
std::vector<live_context> ctxs;
|
||||
ctxs.reserve(m_state->enabled_sources.size());
|
||||
for (const auto& source : m_state->enabled_sources)
|
||||
for (auto source : m_state->enabled_sources)
|
||||
{
|
||||
ctxs.emplace_back();
|
||||
auto& ctx = ctxs[ctxs.size() - 1];
|
||||
ctx.source = source;
|
||||
ctx.sync.reset(new application::source_sync_context(termination_sem));
|
||||
auto src_info = m_state->source_infos.at(source);
|
||||
|
||||
auto ctx_idx = ctxs.size();
|
||||
ctxs.emplace_back();
|
||||
ctxs[ctx_idx].source = source;
|
||||
try
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Opening event source '" + source + "'\n");
|
||||
termination_sem.acquire();
|
||||
res = open_live_inspector(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;
|
||||
}
|
||||
|
||||
open_live_inspector(src_info->inspector, source);
|
||||
if (m_state->enabled_sources.size() == 1)
|
||||
{
|
||||
// optimization: with only one source we don't spawn additional threads
|
||||
process_inspector_events(src_info->inspector, statsw, source, ctx.sync.get(), &ctx.res);
|
||||
process_inspector_events(src_info->inspector, statsw, source, &ctxs[ctx_idx].res);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.thread.reset(new std::thread(
|
||||
ctxs[ctx_idx].thread.reset(new std::thread(
|
||||
&application::process_inspector_events,
|
||||
this, src_info->inspector, statsw, source, ctx.sync.get(), &ctx.res));
|
||||
this, src_info->inspector, statsw, source, &ctxs[ctx_idx].res));
|
||||
}
|
||||
}
|
||||
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();
|
||||
ctxs[ctx_idx].res = run_result::fatal(e.what());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -405,47 +360,29 @@ application::run_result application::process_events()
|
||||
// to force all other event streams to termiante too.
|
||||
// We accomulate the errors in a single run_result.
|
||||
size_t closed_count = 0;
|
||||
bool forced_termination = false;
|
||||
while (closed_count < ctxs.size())
|
||||
{
|
||||
// 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();
|
||||
|
||||
if (!res.success && !termination_forced)
|
||||
if (!res.success && !forced_termination)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "An error occurred in an event source, forcing termination...\n");
|
||||
terminate(false);
|
||||
termination_forced = true;
|
||||
terminate();
|
||||
forced_termination = true;
|
||||
}
|
||||
|
||||
for (auto &ctx : ctxs)
|
||||
{
|
||||
if (ctx.sync->finished() && !ctx.sync->joined())
|
||||
if (ctx.thread)
|
||||
{
|
||||
if (ctx.thread)
|
||||
if (!ctx.thread->joinable())
|
||||
{
|
||||
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();
|
||||
continue;
|
||||
}
|
||||
|
||||
falco_logger::log(LOG_DEBUG, "Closing event source '" + ctx.source + "'\n");
|
||||
m_state->source_infos.at(ctx.source)->inspector->close();
|
||||
|
||||
res = run_result::merge(res, ctx.res);
|
||||
ctx.sync->join();
|
||||
closed_count++;
|
||||
ctx.thread->join();
|
||||
ctx.thread = nullptr;
|
||||
}
|
||||
falco_logger::log(LOG_DEBUG, "Closing event source '" + ctx.source + "'\n");
|
||||
m_state->source_infos.at(ctx.source)->inspector->close();
|
||||
res = run_result::merge(res, ctx.res);
|
||||
closed_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,6 @@ limitations under the License.
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
void application::print_enabled_event_sources()
|
||||
{
|
||||
/* Print all enabled sources. */
|
||||
std::string str;
|
||||
for (const auto &s : m_state->enabled_sources)
|
||||
{
|
||||
str += str.empty() ? "" : ", ";
|
||||
str += s;
|
||||
}
|
||||
falco_logger::log(LOG_INFO, "Enabled event sources: " + str + "\n");
|
||||
}
|
||||
|
||||
application::run_result application::select_event_sources()
|
||||
{
|
||||
m_state->enabled_sources = m_state->loaded_sources;
|
||||
@@ -71,5 +59,12 @@ application::run_result application::select_event_sources()
|
||||
return run_result::fatal("Must enable at least one event source");
|
||||
}
|
||||
|
||||
/* Print all enabled sources. */
|
||||
std::ostringstream os;
|
||||
std::copy(m_state->enabled_sources.begin(), m_state->enabled_sources.end(), std::ostream_iterator<std::string>(os, ", "));
|
||||
std::string result = os.str();
|
||||
result.pop_back();
|
||||
falco_logger::log(LOG_INFO, "Enabled event sources: " + result + "\n");
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -70,7 +70,6 @@ application::run_result application::validate_rules_files()
|
||||
|
||||
// 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 : m_options.validate_rules_filenames)
|
||||
@@ -78,10 +77,6 @@ application::run_result application::validate_rules_files()
|
||||
std::unique_ptr<falco::load_result> res;
|
||||
|
||||
res = m_state->engine->load_rules(rc.at(filename), filename);
|
||||
if (!check_rules_plugin_requirements(err))
|
||||
{
|
||||
return run_result::fatal(err);
|
||||
}
|
||||
|
||||
successful &= res->successful();
|
||||
|
||||
@@ -89,30 +84,33 @@ application::run_result application::validate_rules_files()
|
||||
{
|
||||
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";
|
||||
|
||||
// If verbose is true, print the warnings now.
|
||||
if(m_options.verbose)
|
||||
if(summary != "")
|
||||
{
|
||||
fprintf(stderr, "%s\n", res->as_string(true, rc).c_str());
|
||||
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";
|
||||
|
||||
// If verbose is true, print the warnings now.
|
||||
if(m_options.verbose)
|
||||
{
|
||||
fprintf(stderr, "%s\n", res->as_string(true, rc).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,7 @@ namespace app {
|
||||
cmdline_options::cmdline_options()
|
||||
: event_buffer_format(sinsp_evt::PF_NORMAL),
|
||||
gvisor_config(""),
|
||||
list_fields(false),
|
||||
list_plugins(false),
|
||||
list_syscall_events(false),
|
||||
markdown(false),
|
||||
modern_bpf(false),
|
||||
m_cmdline_opts("falco", "Falco - Cloud Native Runtime Security")
|
||||
{
|
||||
define();
|
||||
@@ -159,28 +155,25 @@ void cmdline_options::define()
|
||||
#else
|
||||
("c", "Configuration file. If not specified tries " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ".", cxxopts::value(conf_filename), "<path>")
|
||||
#endif
|
||||
("A", "Monitor all events, including those not interesting to Falco. Please use the -i option to list all ignored events. This option has effect only on live captures.", cxxopts::value(all_events)->default_value("false"))
|
||||
("A", "Monitor all events, including not interesting ones. Please use the `-i` command line option to see the ignored events. This option is implicit when the capture is not live.", cxxopts::value(all_events)->default_value("false"))
|
||||
("b,print-base64", "Print data buffers in base64. This is useful for encoding binary data that needs to be used over media designed to consume this format.")
|
||||
("cri", "Path to CRI socket for container metadata. Use the specified socket to fetch data from a CRI-compatible runtime. If not specified, uses the libs default. This option can be passed multiple times to specify socket to be tried until a successful one is found.", cxxopts::value(cri_socket_paths), "<path>")
|
||||
("cri", "Path to CRI socket for container metadata. Use the specified socket to fetch data from a CRI-compatible runtime. If not specified, uses libs default. It can be passed multiple times to specify socket to be tried until a successful one is found.", cxxopts::value(cri_socket_paths), "<path>")
|
||||
("d,daemon", "Run as a daemon.", cxxopts::value(daemon)->default_value("false"))
|
||||
("disable-cri-async", "Disable asynchronous CRI metadata fetching. This is useful to let the input event wait for the container metadata fetch to finish before moving forward. Async fetching, in some environments leads to empty fields for container metadata when the fetch is not fast enough to be completed asynchronously. This can have a performance penalty on your environment depending on the number of containers and the frequency at which they are created/started/stopped.", cxxopts::value(disable_cri_async)->default_value("false"))
|
||||
("disable-source", "Disable a specific event source. By default, all loaded sources get enabled. Available sources are 'syscall' and all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. This has no offect when reading events from a trace file. Can not disable all event sources. Can not be mixed with --enable-source.", cxxopts::value(disable_sources), "<event_source>")
|
||||
("D", "Disable any rules with names having the substring <substring>. This option can be passed multiple times. Can not be mixed with -t.", cxxopts::value(disabled_rule_substrings), "<substring>")
|
||||
("disable-source", "Disable a specific event source. Available event sources are: syscall or any source from a configured plugin with event sourcing capability. It can be passed multiple times. It has no offect when reading events from a trace file. Can not disable all event sources. Can not be mixed with enable-source.", cxxopts::value(disable_sources), "<event_source>")
|
||||
("D", "Disable any rules with names having the substring <substring>. Can be specified multiple times. Can not be specified with -t.", cxxopts::value(disabled_rule_substrings), "<substring>")
|
||||
("e", "Read the events from a trace file <events_file> in .scap format instead of tapping into live.", cxxopts::value(trace_filename), "<events_file>")
|
||||
("enable-source", "Enable a specific event source. If used, all loaded sources get disabled by default and only the ones passed with this option get enabled. Available sources are 'syscall' and all sources defined by loaded plugins supporting the event sourcing capability. This option can be passed multiple times. This has no offect when reading events from a trace file. Can not be mixed with --disable-source.", cxxopts::value(enable_sources), "<event_source>")
|
||||
("enable-source", "Enable a specific event source. If used, only event sources passed with this options get enabled. Available event sources are: syscall or any source from a configured plugin with event sourcing capability. It can be passed multiple times. It has no offect when reading events from a trace file. Can not be mixed with disable-source.", cxxopts::value(enable_sources), "<event_source>")
|
||||
#ifdef HAS_GVISOR
|
||||
("g,gvisor-config", "Parse events from gVisor using the specified configuration file. A falco-compatible configuration file can be generated with --gvisor-generate-config and can be used for both runsc and Falco.", cxxopts::value(gvisor_config), "<gvisor_config>")
|
||||
("gvisor-generate-config", "Generate a configuration file that can be used for gVisor.", cxxopts::value<std::string>(gvisor_generate_config_with_socket)->implicit_value("/run/falco/gvisor.sock"), "<socket_path>")
|
||||
("gvisor-root", "gVisor root directory for storage of container state. Equivalent to runsc --root flag.", cxxopts::value(gvisor_root), "<gvisor_root>")
|
||||
#endif
|
||||
#ifdef HAS_MODERN_BPF
|
||||
("modern-bpf", "[EXPERIMENTAL] Use BPF modern probe to capture system events.", cxxopts::value(modern_bpf)->default_value("false"))
|
||||
#endif
|
||||
("i", "Print all events that are ignored by default (i.e. without the -A flag) and exit.", cxxopts::value(print_ignored_events)->default_value("false"))
|
||||
#ifndef MINIMAL_BUILD
|
||||
("k,k8s-api", "Enable Kubernetes support by connecting to the API server specified as argument. E.g. \"http://admin:password@127.0.0.1:8080\". The API server can also be specified via the environment variable FALCO_K8S_API.", cxxopts::value(k8s_api), "<url>")
|
||||
("K,k8s-api-cert", "Use the provided files names to authenticate user and (optionally) verify the K8S API server identity. Each entry must specify full (absolute, or relative to the current directory) path to the respective file. Private key password is optional (needed only if key is password protected). CA certificate is optional. For all files, only PEM file format is supported. Specifying CA certificate only is obsoleted - when single entry is provided for this option, it will be interpreted as the name of a file containing bearer token. Note that the format of this command-line option prohibits use of files whose names contain ':' or '#' characters in the file name.", cxxopts::value(k8s_api_cert), "(<bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>])")
|
||||
("k8s-node", "The node name will be used as a filter when requesting metadata of pods to the API server. Usually, this should be set to the current node on which Falco is running. If empty, no filter is set, which may have a performance penalty on large clusters.", cxxopts::value(k8s_node_name), "<node_name>")
|
||||
("k8s-node", "The node name will be used as a filter when requesting metadata of pods to the API server. Usually, it should be set to the current node on which Falco is running. If empty, no filter is set, which may have a performance penalty on large clusters.", cxxopts::value(k8s_node_name), "<node_name>")
|
||||
#endif
|
||||
("L", "Show the name and description of all rules and exit.", cxxopts::value(describe_all_rules)->default_value("false"))
|
||||
("l", "Show the name and description of the rule with name <rule> and exit.", cxxopts::value(describe_rule), "<rule>")
|
||||
@@ -199,19 +192,19 @@ void cmdline_options::define()
|
||||
("plugin-info", "Print info for a single plugin and exit.\nThis includes all descriptivo info like name and author, along with the\nschema format for the init configuration and a list of suggested open parameters.\n<plugin_name> can be the name of the plugin or its configured library_path.", cxxopts::value(print_plugin_info), "<plugin_name>")
|
||||
("p,print", "Add additional information to each falco notification's output.\nWith -pc or -pcontainer will use a container-friendly format.\nWith -pk or -pkubernetes will use a kubernetes-friendly format.\nWith -pm or -pmesos will use a mesos-friendly format.\nAdditionally, specifying -pc/-pk/-pm will change the interpretation of %container.info in rule output fields.", cxxopts::value(print_additional), "<output_format>")
|
||||
("P,pidfile", "When run as a daemon, write pid to specified file", cxxopts::value(pidfilename)->default_value("/var/run/falco.pid"), "<pid_file>")
|
||||
("r", "Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml). This option can be passed multiple times to read from multiple files/directories.", cxxopts::value<std::vector<std::string>>(), "<rules_file>")
|
||||
("r", "Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml). Can be specified multiple times to read from multiple files/directories.", cxxopts::value<std::vector<std::string>>(), "<rules_file>")
|
||||
("s", "If specified, append statistics related to Falco's reading/processing of events to this file (only useful in live mode).", cxxopts::value(stats_filename), "<stats_file>")
|
||||
("stats-interval", "When using -s <stats_file>, write statistics every <msec> ms. This uses signals, so don't recommend intervals below 200 ms. Defaults to 5000 (5 seconds).", cxxopts::value(stats_interval)->default_value("5000"), "<msec>")
|
||||
("S,snaplen", "Capture the first <len> bytes of each I/O buffer. By default, the first 80 bytes are captured. Use this option with caution, it can generate huge trace files.", cxxopts::value(snaplen)->default_value("0"), "<len>")
|
||||
("support", "Print support information including version, rules files used, etc. and exit.", cxxopts::value(print_support)->default_value("false"))
|
||||
("T", "Disable any rules with a tag=<tag>. This option can be passed multiple times. Can not be mized with -t", cxxopts::value<std::vector<std::string>>(), "<tag>")
|
||||
("t", "Only run those rules with a tag=<tag>. This option can be passed multiple times. Can not be mixed with -T/-D.", cxxopts::value<std::vector<std::string>>(), "<tag>")
|
||||
("T", "Disable any rules with a tag=<tag>. Can be specified multiple times. Can not be specified with -t", cxxopts::value<std::vector<std::string>>(), "<tag>")
|
||||
("t", "Only run those rules with a tag=<tag>. Can be specified multiple times. Can not be specified with -T/-D.", cxxopts::value<std::vector<std::string>>(), "<tag>")
|
||||
("U,unbuffered", "Turn off output buffering to configured outputs. This causes every single line emitted by falco to be flushed which generates higher CPU usage but is useful when piping those outputs into another process or into a script.", cxxopts::value(unbuffered_outputs)->default_value("false"))
|
||||
("u,userspace", "Parse events from userspace. To be used in conjunction with the ptrace(2) based driver (pdig)", cxxopts::value(userspace)->default_value("false"))
|
||||
("V,validate", "Read the contents of the specified rules(s) file and exit. This option can be passed multiple times to validate multiple files.", cxxopts::value(validate_rules_filenames), "<rules_file>")
|
||||
("V,validate", "Read the contents of the specified rules(s) file and exit. Can be specified multiple times to validate multiple files.", cxxopts::value(validate_rules_filenames), "<rules_file>")
|
||||
("v", "Verbose output.", cxxopts::value(verbose)->default_value("false"))
|
||||
("version", "Print version number.", cxxopts::value(print_version_info)->default_value("false"))
|
||||
("page-size", "Print the system page size (may help you to choose the right syscall ring-buffer size).", cxxopts::value(print_page_size)->default_value("false"));
|
||||
("page-size", "Print the system page size (may help you to choose the right syscall buffer size).", cxxopts::value(print_page_size)->default_value("false"));
|
||||
|
||||
|
||||
m_cmdline_opts.set_width(140);
|
||||
|
||||
@@ -79,7 +79,6 @@ public:
|
||||
bool verbose;
|
||||
bool print_version_info;
|
||||
bool print_page_size;
|
||||
bool modern_bpf;
|
||||
|
||||
bool parse(int argc, char **argv, std::string &errstr);
|
||||
|
||||
|
||||
@@ -26,41 +26,9 @@ limitations under the License.
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
static inline bool should_take_action_to_signal(std::atomic<int>& v)
|
||||
{
|
||||
// we expected the signal to be received, and we try to set action-taken flag
|
||||
int value = APP_SIGNAL_SET;
|
||||
while (!v.compare_exchange_weak(
|
||||
value,
|
||||
APP_SIGNAL_ACTION_TAKEN,
|
||||
std::memory_order_seq_cst,
|
||||
std::memory_order_seq_cst))
|
||||
{
|
||||
// application already took action, there's no need to do it twice
|
||||
if (value == APP_SIGNAL_ACTION_TAKEN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// signal did was not really received, so we "fake" receiving it
|
||||
if (value == APP_SIGNAL_NOT_SET)
|
||||
{
|
||||
v.store(APP_SIGNAL_SET, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
// reset "expected" CAS variable and keep looping until we succeed
|
||||
value = APP_SIGNAL_SET;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
std::atomic<int> g_terminate(APP_SIGNAL_NOT_SET);
|
||||
std::atomic<int> g_restart(APP_SIGNAL_NOT_SET);
|
||||
std::atomic<int> g_reopen_outputs(APP_SIGNAL_NOT_SET);
|
||||
|
||||
application::run_result::run_result()
|
||||
: success(true), errstr(""), proceed(true)
|
||||
{
|
||||
@@ -71,13 +39,14 @@ application::run_result::~run_result()
|
||||
}
|
||||
|
||||
application::state::state()
|
||||
: loaded_sources(),
|
||||
: restart(false),
|
||||
terminate(false),
|
||||
loaded_sources(),
|
||||
enabled_sources(),
|
||||
source_infos(),
|
||||
plugin_configs(),
|
||||
ppm_sc_of_interest(),
|
||||
tp_of_interest(),
|
||||
syscall_buffer_bytes_size(DEFAULT_DRIVER_BUFFER_BYTES_DIM)
|
||||
tp_of_interest()
|
||||
{
|
||||
config = std::make_shared<falco_configuration>();
|
||||
engine = std::make_shared<falco_engine>();
|
||||
@@ -98,41 +67,29 @@ application::~application()
|
||||
{
|
||||
}
|
||||
|
||||
void application::terminate(bool verbose)
|
||||
void application::terminate()
|
||||
{
|
||||
if (should_take_action_to_signal(falco::app::g_terminate))
|
||||
if(m_state != nullptr)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
|
||||
}
|
||||
m_state->terminate.store(true, std::memory_order_seq_cst);
|
||||
}
|
||||
}
|
||||
|
||||
void application::reopen_outputs(bool verbose)
|
||||
void application::reopen_outputs()
|
||||
{
|
||||
if (should_take_action_to_signal(falco::app::g_reopen_outputs))
|
||||
if(m_state != nullptr && m_state->outputs != nullptr)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "SIGUSR1 received, reopening outputs...\n");
|
||||
}
|
||||
if(m_state != nullptr && m_state->outputs != nullptr)
|
||||
{
|
||||
m_state->outputs->reopen_outputs();
|
||||
}
|
||||
falco::app::g_reopen_outputs.store(APP_SIGNAL_NOT_SET);
|
||||
// note: it is ok to do this inside the signal handler because
|
||||
// in the current falco_outputs implementation this is non-blocking
|
||||
m_state->outputs->reopen_outputs();
|
||||
}
|
||||
}
|
||||
|
||||
void application::restart(bool verbose)
|
||||
void application::restart()
|
||||
{
|
||||
if (should_take_action_to_signal(falco::app::g_restart))
|
||||
if(m_state != nullptr)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
|
||||
}
|
||||
m_state->restart.store(true, std::memory_order_seq_cst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +136,7 @@ bool application::run(std::string &errstr, bool &restart)
|
||||
std::bind(&application::print_ignored_events, this),
|
||||
std::bind(&application::print_syscall_events, this),
|
||||
std::bind(&application::load_config, this),
|
||||
std::bind(&application::create_signal_handlers, this),
|
||||
std::bind(&application::print_plugin_info, this),
|
||||
std::bind(&application::list_plugins, this),
|
||||
std::bind(&application::load_plugins, this),
|
||||
@@ -189,7 +147,6 @@ bool application::run(std::string &errstr, bool &restart)
|
||||
std::bind(&application::validate_rules_files, this),
|
||||
std::bind(&application::load_rules_files, this),
|
||||
std::bind(&application::print_support, this),
|
||||
std::bind(&application::create_signal_handlers, this),
|
||||
std::bind(&application::attach_inotify_signals, this),
|
||||
std::bind(&application::create_requested_paths, this),
|
||||
std::bind(&application::daemonize, this),
|
||||
@@ -238,7 +195,7 @@ bool application::run(std::string &errstr, bool &restart)
|
||||
errstr = res.errstr;
|
||||
}
|
||||
|
||||
restart = should_restart();
|
||||
restart = m_state->restart;
|
||||
|
||||
return res.success;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "semaphore.h"
|
||||
#include "configuration.h"
|
||||
#include "stats_writer.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
@@ -29,29 +28,21 @@ limitations under the License.
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
|
||||
#define APP_SIGNAL_NOT_SET 0 // The signal flag is not set
|
||||
#define APP_SIGNAL_SET 1 // The signal flag has been set
|
||||
#define APP_SIGNAL_ACTION_TAKEN 2 // The signal flag has been set and the application took action
|
||||
|
||||
namespace falco {
|
||||
namespace app {
|
||||
|
||||
// these are used to control the lifecycle of the application
|
||||
// through signal handlers or internal calls
|
||||
extern std::atomic<int> g_terminate;
|
||||
extern std::atomic<int> g_restart;
|
||||
extern std::atomic<int> g_reopen_outputs;
|
||||
|
||||
class application {
|
||||
public:
|
||||
application();
|
||||
virtual ~application();
|
||||
application(application&&) = default;
|
||||
application& operator = (application&&) = default;
|
||||
application(const application&) = delete;
|
||||
application& operator = (const application&) = delete;
|
||||
|
||||
// These are only used in signal handlers. Other than there,
|
||||
// the control flow of the application should not be changed
|
||||
// from the outside.
|
||||
void terminate();
|
||||
void reopen_outputs();
|
||||
void restart();
|
||||
|
||||
bool init(int argc, char **argv, std::string &errstr);
|
||||
|
||||
@@ -90,17 +81,20 @@ private:
|
||||
state();
|
||||
virtual ~state();
|
||||
|
||||
std::atomic<bool> restart;
|
||||
std::atomic<bool> terminate;
|
||||
|
||||
std::shared_ptr<falco_configuration> config;
|
||||
std::shared_ptr<falco_outputs> outputs;
|
||||
std::shared_ptr<falco_engine> engine;
|
||||
|
||||
// The set of loaded event sources (by default, the syscall event
|
||||
// source plus all event sources coming from the loaded plugins)
|
||||
std::unordered_set<std::string> loaded_sources;
|
||||
std::set<std::string> loaded_sources;
|
||||
|
||||
// The set of enabled event sources (can be altered by using
|
||||
// the --enable-source and --disable-source options)
|
||||
std::unordered_set<std::string> enabled_sources;
|
||||
std::set<std::string> enabled_sources;
|
||||
|
||||
// Used to load all plugins to get their info. In capture mode,
|
||||
// this is also used to open the capture file and read its events
|
||||
@@ -116,9 +110,6 @@ private:
|
||||
|
||||
std::string cmdline;
|
||||
|
||||
// Set of events we want the driver to capture
|
||||
std::unordered_set<uint32_t> ppm_event_info_of_interest;
|
||||
|
||||
// Set of syscalls we want the driver to capture
|
||||
std::unordered_set<uint32_t> ppm_sc_of_interest;
|
||||
|
||||
@@ -157,7 +148,7 @@ private:
|
||||
}
|
||||
|
||||
// Failure result that causes the program to stop with an error
|
||||
inline static run_result fatal(const std::string& err)
|
||||
inline static run_result fatal(std::string err)
|
||||
{
|
||||
run_result r;
|
||||
r.success = false;
|
||||
@@ -183,11 +174,6 @@ private:
|
||||
|
||||
run_result();
|
||||
virtual ~run_result();
|
||||
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;
|
||||
@@ -199,67 +185,6 @@ private:
|
||||
bool proceed;
|
||||
};
|
||||
|
||||
// used to synchronize different event source running in parallel
|
||||
class source_sync_context
|
||||
{
|
||||
public:
|
||||
source_sync_context(falco::semaphore& s)
|
||||
: m_finished(false), m_joined(false), m_semaphore(s) { }
|
||||
source_sync_context(source_sync_context&&) = default;
|
||||
source_sync_context& operator = (source_sync_context&&) = default;
|
||||
source_sync_context(const source_sync_context&) = delete;
|
||||
source_sync_context& operator = (const source_sync_context&) = delete;
|
||||
|
||||
inline void finish()
|
||||
{
|
||||
bool v = false;
|
||||
while (!m_finished.compare_exchange_weak(
|
||||
v, true,
|
||||
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<bool> m_finished;
|
||||
// set to true when the result has been collected after finishing
|
||||
std::atomic<bool> m_joined;
|
||||
// used to notify the waiting thread when finished gets set to true
|
||||
falco::semaphore& m_semaphore;
|
||||
};
|
||||
|
||||
// Convenience method. Read a sequence of filenames and fill
|
||||
// in a vector of rules contents.
|
||||
// Also fill in the provided rules_contents_t with a mapping from
|
||||
@@ -350,12 +275,10 @@ private:
|
||||
bool create_handler(int sig, void (*func)(int), run_result &ret);
|
||||
void configure_output_format();
|
||||
void check_for_ignored_events();
|
||||
bool check_rules_plugin_requirements(std::string& err);
|
||||
void format_plugin_info(std::shared_ptr<sinsp_plugin> p, std::ostream& os) const;
|
||||
run_result open_offline_inspector();
|
||||
run_result open_live_inspector(std::shared_ptr<sinsp> inspector, const std::string& source);
|
||||
void add_source_to_engine(const std::string& src);
|
||||
void print_enabled_event_sources();
|
||||
void init_syscall_inspector(std::shared_ptr<sinsp> inspector, const falco::app::cmdline_options& opts);
|
||||
run_result do_inspect(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
@@ -369,7 +292,6 @@ private:
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
std::shared_ptr<stats_writer> statsw,
|
||||
std::string source, // an empty source represents capture mode
|
||||
application::source_sync_context* sync,
|
||||
run_result* res) noexcept;
|
||||
|
||||
/* Returns true if we are in capture mode. */
|
||||
@@ -383,23 +305,6 @@ private:
|
||||
return !m_options.gvisor_config.empty();
|
||||
}
|
||||
|
||||
// used in signal handlers to control the flow of the application
|
||||
void terminate(bool verbose=true);
|
||||
void restart(bool verbose=true);
|
||||
void reopen_outputs(bool verbose=true);
|
||||
inline bool should_terminate()
|
||||
{
|
||||
return g_terminate.load(std::memory_order_seq_cst) != APP_SIGNAL_NOT_SET;
|
||||
}
|
||||
inline bool should_restart()
|
||||
{
|
||||
return g_restart.load(std::memory_order_seq_cst) != APP_SIGNAL_NOT_SET;
|
||||
}
|
||||
inline bool should_reopen_outputs()
|
||||
{
|
||||
return g_reopen_outputs.load(std::memory_order_seq_cst) != APP_SIGNAL_NOT_SET;
|
||||
}
|
||||
|
||||
std::unique_ptr<state> m_state;
|
||||
cmdline_options m_options;
|
||||
bool m_initialized;
|
||||
|
||||
@@ -32,31 +32,13 @@ limitations under the License.
|
||||
using namespace std;
|
||||
|
||||
falco_configuration::falco_configuration():
|
||||
m_json_output(false),
|
||||
m_json_include_output_property(true),
|
||||
m_json_include_tags_property(true),
|
||||
m_notifications_rate(0),
|
||||
m_notifications_max_burst(1000),
|
||||
m_watch_config_files(true),
|
||||
m_buffered_outputs(false),
|
||||
m_time_format_iso_8601(false),
|
||||
m_output_timeout(2000),
|
||||
m_grpc_enabled(false),
|
||||
m_grpc_threadiness(0),
|
||||
m_webserver_enabled(false),
|
||||
m_webserver_threadiness(0),
|
||||
m_webserver_listen_port(8765),
|
||||
m_webserver_k8s_healthz_endpoint("/healthz"),
|
||||
m_webserver_ssl_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_metadata_download_max_mb(100),
|
||||
m_metadata_download_chunk_wait_us(1000),
|
||||
m_metadata_download_watch_freq_sec(1),
|
||||
m_syscall_buf_size_preset(4),
|
||||
m_config(NULL)
|
||||
{
|
||||
}
|
||||
@@ -69,7 +51,7 @@ falco_configuration::~falco_configuration()
|
||||
}
|
||||
}
|
||||
|
||||
void falco_configuration::init(const string& conf_filename, const vector<string> &cmdline_options)
|
||||
void falco_configuration::init(string conf_filename, const vector<string> &cmdline_options)
|
||||
{
|
||||
string m_config_file = conf_filename;
|
||||
m_config = new yaml_configuration();
|
||||
@@ -306,7 +288,7 @@ void falco_configuration::init(const string& conf_filename, const vector<string>
|
||||
/* We put this value in the configuration file because in this way we can change the dimension at every reload.
|
||||
* The default value is `4` -> 8 MB.
|
||||
*/
|
||||
m_syscall_buf_size_preset = m_config->get_scalar<uint16_t>("syscall_buf_size_preset", 4);
|
||||
m_syscall_buf_size_preset = m_config->get_scalar<uint64_t>("syscall_buf_size_preset", 4);
|
||||
|
||||
std::set<std::string> load_plugins;
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ public:
|
||||
falco_configuration();
|
||||
virtual ~falco_configuration();
|
||||
|
||||
void init(const std::string& conf_filename, const std::vector<std::string>& cmdline_options);
|
||||
void init(std::string conf_filename, const std::vector<std::string>& cmdline_options);
|
||||
void init(const std::vector<std::string>& cmdline_options);
|
||||
|
||||
static void read_rules_file_directory(const string& path, list<string>& rules_filenames, list<string> &rules_folders);
|
||||
@@ -270,7 +270,7 @@ public:
|
||||
uint32_t m_metadata_download_watch_freq_sec;
|
||||
|
||||
// Index corresponding to the syscall buffer dimension.
|
||||
uint16_t m_syscall_buf_size_preset;
|
||||
uint64_t m_syscall_buf_size_preset;
|
||||
|
||||
std::vector<plugin_config> m_plugins;
|
||||
|
||||
@@ -371,7 +371,7 @@ namespace YAML {
|
||||
return false;
|
||||
}
|
||||
rhs.m_library_path = node["library_path"].as<std::string>();
|
||||
if(!rhs.m_library_path.empty() && rhs.m_library_path.at(0) != '/')
|
||||
if(rhs.m_library_path.at(0) != '/')
|
||||
{
|
||||
// prepend share dir if path is not absolute
|
||||
rhs.m_library_path = string(FALCO_ENGINE_PLUGINS_DIR) + rhs.m_library_path;
|
||||
@@ -400,8 +400,7 @@ namespace YAML {
|
||||
|
||||
if(node["open_params"] && !node["open_params"].IsNull())
|
||||
{
|
||||
string open_params = node["open_params"].as<std::string>();
|
||||
rhs.m_open_params = trim(open_params);
|
||||
rhs.m_open_params = node["open_params"].as<std::string>();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -24,8 +24,7 @@ syscall_evt_drop_mgr::syscall_evt_drop_mgr():
|
||||
m_inspector(NULL),
|
||||
m_outputs(NULL),
|
||||
m_next_check_ts(0),
|
||||
m_simulate_drops(false),
|
||||
m_threshold(0)
|
||||
m_simulate_drops(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
|
||||
#include <sinsp.h>
|
||||
#include <token_bucket.h>
|
||||
@@ -34,7 +34,7 @@ enum class syscall_evt_drop_action : uint8_t
|
||||
EXIT
|
||||
};
|
||||
|
||||
using syscall_evt_drop_actions = std::unordered_set<syscall_evt_drop_action>;
|
||||
using syscall_evt_drop_actions = std::set<syscall_evt_drop_action>;
|
||||
|
||||
class syscall_evt_drop_mgr
|
||||
{
|
||||
|
||||
@@ -50,7 +50,7 @@ falco_outputs::falco_outputs(
|
||||
uint32_t timeout,
|
||||
bool buffered,
|
||||
bool time_format_iso_8601,
|
||||
const std::string& hostname)
|
||||
std::string hostname)
|
||||
{
|
||||
m_formats.reset(new falco_formats(engine, json_include_output_property, json_include_tags_property));
|
||||
|
||||
@@ -271,7 +271,7 @@ inline void falco_outputs::push(const ctrl_msg& cmsg)
|
||||
void falco_outputs::worker() noexcept
|
||||
{
|
||||
watchdog<std::string> wd;
|
||||
wd.start([&](const std::string& payload) -> void {
|
||||
wd.start([&](std::string payload) -> void {
|
||||
falco_logger::log(LOG_CRIT, "\"" + payload + "\" output timeout, all output channels are blocked\n");
|
||||
});
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
uint32_t timeout,
|
||||
bool buffered,
|
||||
bool time_format_iso_8601,
|
||||
const std::string& hostname);
|
||||
std::string hostname);
|
||||
|
||||
virtual ~falco_outputs();
|
||||
|
||||
|
||||
@@ -128,12 +128,12 @@ void falco::grpc::server::thread_process(int thread_index)
|
||||
}
|
||||
|
||||
void falco::grpc::server::init(
|
||||
const std::string& server_addr,
|
||||
std::string server_addr,
|
||||
int threadiness,
|
||||
const std::string& private_key,
|
||||
const std::string& cert_chain,
|
||||
const std::string& root_certs,
|
||||
const std::string& log_level)
|
||||
std::string private_key,
|
||||
std::string cert_chain,
|
||||
std::string root_certs,
|
||||
std::string log_level)
|
||||
{
|
||||
m_server_addr = server_addr;
|
||||
m_threadiness = threadiness;
|
||||
|
||||
@@ -33,12 +33,12 @@ public:
|
||||
virtual ~server() = default;
|
||||
|
||||
void init(
|
||||
const std::string& server_addr,
|
||||
std::string server_addr,
|
||||
int threadiness,
|
||||
const std::string& private_key,
|
||||
const std::string& cert_chain,
|
||||
const std::string& root_certs,
|
||||
const std::string& log_level
|
||||
std::string private_key,
|
||||
std::string cert_chain,
|
||||
std::string root_certs,
|
||||
std::string log_level
|
||||
);
|
||||
void thread_process(int thread_index);
|
||||
void run();
|
||||
|
||||
@@ -63,7 +63,7 @@ class abstract_output
|
||||
public:
|
||||
virtual ~abstract_output() {}
|
||||
|
||||
void init(const config& oc, bool buffered, const std::string& hostname, bool json_output)
|
||||
void init(config oc, bool buffered, std::string hostname, bool json_output)
|
||||
{
|
||||
m_oc = oc;
|
||||
m_buffered = buffered;
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
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
|
||||
*/
|
||||
semaphore(int c = 0): count(c) {}
|
||||
|
||||
/**
|
||||
* @brief Increments the internal counter and unblocks acquirers
|
||||
*/
|
||||
inline void release()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
count++;
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decrements the internal counter or blocks until it can
|
||||
*/
|
||||
inline void acquire()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
while (count == 0)
|
||||
{
|
||||
cv.wait(lock);
|
||||
}
|
||||
count--;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
int count;
|
||||
};
|
||||
};
|
||||
@@ -67,11 +67,11 @@ public:
|
||||
|
||||
stats_writer(const stats_writer&) = delete;
|
||||
|
||||
stats_writer(stats_writer&&) = default;
|
||||
stats_writer(stats_writer&&) = delete;
|
||||
|
||||
stats_writer& operator=(const stats_writer&) = delete;
|
||||
|
||||
stats_writer& operator=(stats_writer&&) = default;
|
||||
stats_writer& operator=(stats_writer&&) = delete;
|
||||
|
||||
~stats_writer();
|
||||
|
||||
@@ -109,12 +109,6 @@ public:
|
||||
private:
|
||||
struct msg
|
||||
{
|
||||
msg(): stop(false) {}
|
||||
msg(msg&&) = default;
|
||||
msg& operator = (msg&&) = default;
|
||||
msg(const msg&) = default;
|
||||
msg& operator = (const msg&) = default;
|
||||
|
||||
bool stop;
|
||||
scap_stats delta;
|
||||
scap_stats stats;
|
||||
|
||||
Reference in New Issue
Block a user