mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-23 05:02:13 +00:00
Compare commits
1 Commits
dev_docker
...
test/libs/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23d7c6c345 |
@@ -360,7 +360,7 @@ jobs:
|
||||
name: Build and publish falco-driver-loader-dev
|
||||
command: |
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=x86_64-master \
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=master \
|
||||
-t falcosecurity/falco-driver-loader:x86_64-master \
|
||||
-t public.ecr.aws/falcosecurity/falco-driver-loader:x86_64-master \
|
||||
docker/driver-loader
|
||||
@@ -409,7 +409,7 @@ jobs:
|
||||
name: Build and publish falco-driver-loader-dev
|
||||
command: |
|
||||
cd /tmp/source-arm64/falco
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=aarch64-master \
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=master \
|
||||
-t falcosecurity/falco-driver-loader:aarch64-master \
|
||||
-t public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-master \
|
||||
docker/driver-loader
|
||||
@@ -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}" \
|
||||
@@ -579,7 +579,7 @@ jobs:
|
||||
name: Build and publish falco-driver-loader
|
||||
command: |
|
||||
cd /source/falco
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=x86_64-${CIRCLE_TAG} \
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco-driver-loader:x86_64-${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco-driver-loader:x86_64-latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-driver-loader: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}" \
|
||||
@@ -634,7 +634,7 @@ jobs:
|
||||
name: Build and publish falco-driver-loader
|
||||
command: |
|
||||
cd /tmp/source-arm64/falco
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=aarch64-${CIRCLE_TAG} \
|
||||
docker buildx build --push --build-arg FALCO_IMAGE_TAG=${CIRCLE_TAG} \
|
||||
-t "falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG}" \
|
||||
-t "falcosecurity/falco-driver-loader:aarch64-latest" \
|
||||
-t "public.ecr.aws/falcosecurity/falco-driver-loader:aarch64-${CIRCLE_TAG}" \
|
||||
@@ -754,7 +754,6 @@ workflows:
|
||||
- "build-musl"
|
||||
- "build-arm64"
|
||||
- "build-centos7"
|
||||
- "quality-static-analysis"
|
||||
- "tests-integration":
|
||||
requires:
|
||||
- "build-centos7"
|
||||
|
||||
@@ -76,8 +76,6 @@ This is a list of production adopters of Falco (in alphabetical order):
|
||||
|
||||
* [SysFlow](https://sysflow.io) SysFlow is a cloud-native system telemetry framework that focuses on data abstraction, behavioral analytics, and noise reduction. At its core, SysFlow exposes a compact open telemetry format that records workload behaviors by connecting event and flow representations of process control flows, file interactions, and network communications. The resulting abstraction encodes a graph structure that enables provenance reasoning on host and container environments, and fast retrieval of security-relevant information.
|
||||
|
||||
* [StackRox](https://stackrox.io) is the industry’s first Kubernetes-native security platform enabling organizations to build, deploy, and run cloud-native applications securely. The platform works with Kubernetes environments and integrates with DevOps and security tools, enabling teams to operationalize and secure their supply chain, infrastructure, and workloads. StackRox aims to harness containerized applications’ development speed while giving operations and security teams greater context and risk profiling. StackRox leverages cloud-native principles and declarative artifacts to automate DevSecOps best practices.
|
||||
|
||||
## Adding a name
|
||||
|
||||
If you would like to add your name to this file, submit a pull request with your change.
|
||||
|
||||
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})
|
||||
@@ -67,16 +59,16 @@ endif()
|
||||
# This will be used to print the architecture for which Falco is compiled.
|
||||
set(FALCO_TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR})
|
||||
|
||||
if(NOT FALCO_EXTRA_DEBUG_FLAGS)
|
||||
set(FALCO_EXTRA_DEBUG_FLAGS "-D_DEBUG")
|
||||
if(NOT DRAIOS_DEBUG_FLAGS)
|
||||
set(DRAIOS_DEBUG_FLAGS "-D_DEBUG")
|
||||
endif()
|
||||
|
||||
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "debug")
|
||||
set(KBUILD_FLAGS "${FALCO_EXTRA_DEBUG_FLAGS} ${FALCO_EXTRA_FEATURE_FLAGS}")
|
||||
set(KBUILD_FLAGS "${DRAIOS_DEBUG_FLAGS} ${DRAIOS_FEATURE_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_BUILD_TYPE "release")
|
||||
set(KBUILD_FLAGS "${FALCO_EXTRA_FEATURE_FLAGS}")
|
||||
set(KBUILD_FLAGS "${DRAIOS_FEATURE_FLAGS}")
|
||||
add_definitions(-DBUILD_TYPE_RELEASE)
|
||||
endif()
|
||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
@@ -97,7 +89,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "release")
|
||||
set(FALCO_SECURITY_FLAGS "${FALCO_SECURITY_FLAGS} -D_FORTIFY_SOURCE=2")
|
||||
endif()
|
||||
|
||||
set(CMAKE_COMMON_FLAGS "${FALCO_SECURITY_FLAGS} -Wall -ggdb ${FALCO_EXTRA_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
|
||||
set(CMAKE_COMMON_FLAGS "${FALCO_SECURITY_FLAGS} -Wall -ggdb ${DRAIOS_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
|
||||
|
||||
if(BUILD_WARNINGS_AS_ERRORS)
|
||||
set(CMAKE_SUPPRESSED_WARNINGS
|
||||
@@ -109,8 +101,8 @@ endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_COMMON_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "--std=c++0x ${CMAKE_COMMON_FLAGS} -Wno-class-memaccess")
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${FALCO_EXTRA_DEBUG_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${DRAIOS_DEBUG_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${DRAIOS_DEBUG_FLAGS}")
|
||||
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG")
|
||||
@@ -122,11 +114,6 @@ set(DRIVER_NAME "falco")
|
||||
set(DRIVER_DEVICE_NAME "falco")
|
||||
set(DRIVERS_REPO "https://download.falco.org/driver")
|
||||
|
||||
# If no path is provided, try to search the BPF probe in: `home/.falco/falco-bpf.o`
|
||||
# This is the same fallback that we had in the libraries: `SCAP_PROBE_BPF_FILEPATH`.
|
||||
set(FALCO_PROBE_BPF_FILEPATH ".${DRIVER_NAME}/${DRIVER_NAME}-bpf.o")
|
||||
add_definitions(-DFALCO_PROBE_BPF_FILEPATH="${FALCO_PROBE_BPF_FILEPATH}")
|
||||
|
||||
if(NOT DEFINED FALCO_COMPONENT_NAME)
|
||||
set(FALCO_COMPONENT_NAME "${CMAKE_PROJECT_NAME}")
|
||||
endif()
|
||||
@@ -233,5 +220,3 @@ endif()
|
||||
|
||||
# Packages configuration
|
||||
include(CPackConfig)
|
||||
|
||||
add_subdirectory(docker/dev)
|
||||
|
||||
2
OWNERS
2
OWNERS
@@ -3,9 +3,9 @@ approvers:
|
||||
- leogr
|
||||
- jasondellaluce
|
||||
- fededp
|
||||
- andreagit97
|
||||
reviewers:
|
||||
- kaizhe
|
||||
- mfdii
|
||||
emeritus_approvers:
|
||||
- fntlnz
|
||||
- kris-nova
|
||||
|
||||
51
README.md
51
README.md
@@ -7,7 +7,7 @@
|
||||
|
||||
Want to talk? Join us on the [#falco](https://kubernetes.slack.com/messages/falco) channel in the [Kubernetes Slack](https://slack.k8s.io).
|
||||
|
||||
## Latest releases
|
||||
### Latest releases
|
||||
|
||||
Read the [change log](CHANGELOG.md).
|
||||
|
||||
@@ -66,7 +66,7 @@ Falco can also be extended to other data sources by using plugins.
|
||||
Falco has a rich set of security rules specifically built for Kubernetes, Linux, and cloud-native.
|
||||
If a rule is violated in a system, Falco will send an alert notifying the user of the violation and its severity.
|
||||
|
||||
## What can Falco detect?
|
||||
### What can Falco detect?
|
||||
|
||||
Falco can detect and alert on any behavior that involves making Linux system calls.
|
||||
Falco alerts can be triggered by the use of specific system calls, their arguments, and by properties of the calling process.
|
||||
@@ -80,11 +80,11 @@ For example, Falco can easily detect incidents including but not limited to:
|
||||
- A standard system binary, such as `ls`, is making an outbound network connection.
|
||||
- A privileged pod is started in a Kubernetes cluster.
|
||||
|
||||
## Installing Falco
|
||||
### Installing Falco
|
||||
|
||||
If you would like to run Falco in **production** please adhere to the [official installation guide](https://falco.org/docs/getting-started/installation/).
|
||||
|
||||
### Kubernetes
|
||||
##### Kubernetes
|
||||
|
||||
| Tool | Link | Note |
|
||||
|----------|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
|
||||
@@ -93,20 +93,22 @@ If you would like to run Falco in **production** please adhere to the [official
|
||||
| Kind | [Tutorial](https://falco.org/docs/getting-started/third-party/#kind) | Running Falco with kind requires a driver on the host system. |
|
||||
| GKE | [Tutorial](https://falco.org/docs/getting-started/third-party/#gke) | We suggest using the eBPF driver for running Falco on GKE. |
|
||||
|
||||
## Developing
|
||||
### Developing
|
||||
|
||||
Falco is designed to be extensible such that it can be built into cloud-native applications and infrastructure.
|
||||
|
||||
Falco has a [gRPC](https://falco.org/docs/grpc/) endpoint and an API defined in [protobuf](https://github.com/falcosecurity/falco/blob/master/userspace/falco/outputs.proto).
|
||||
The Falco Project supports various SDKs for this endpoint.
|
||||
|
||||
### SDKs
|
||||
##### SDKs
|
||||
|
||||
| Language | Repository |
|
||||
|----------|---------------------------------------------------------|
|
||||
| Go | [client-go](https://github.com/falcosecurity/client-go) |
|
||||
| Rust | [client-rs](https://github.com/falcosecurity/client-rs) |
|
||||
| Python | [client-py](https://github.com/falcosecurity/client-py) |
|
||||
|
||||
## Plugins
|
||||
### Plugins
|
||||
|
||||
Falco comes with a [plugin framework](https://falco.org/docs/plugins/) that extends it to potentially any cloud detection scenario. Plugins are shared libraries that conform to a documented API and allow for:
|
||||
|
||||
@@ -116,18 +118,18 @@ Falco comes with a [plugin framework](https://falco.org/docs/plugins/) that exte
|
||||
The Falco Project maintains [various plugins](https://github.com/falcosecurity/plugins) and provides SDKs for plugin development.
|
||||
|
||||
|
||||
### SDKs
|
||||
##### SDKs
|
||||
|
||||
| Language | Repository |
|
||||
|----------|-------------------------------------------------------------------------------|
|
||||
| Go | [falcosecurity/plugin-sdk-go](https://github.com/falcosecurity/plugin-sdk-go) |
|
||||
|
||||
|
||||
## Documentation
|
||||
### Documentation
|
||||
|
||||
The [Official Documentation](https://falco.org/docs/) is the best resource to learn about Falco.
|
||||
|
||||
## Join the Community
|
||||
### Join the Community
|
||||
|
||||
To get involved with The Falco Project please visit [the community repository](https://github.com/falcosecurity/community) to find more.
|
||||
|
||||
@@ -137,36 +139,23 @@ How to reach out?
|
||||
- [Join the Falco mailing list](https://lists.cncf.io/g/cncf-falco-dev)
|
||||
- [Read the Falco documentation](https://falco.org/docs/)
|
||||
|
||||
## How to contribute
|
||||
|
||||
See the [contributing guide](https://github.com/falcosecurity/.github/blob/main/CONTRIBUTING.md) and the [code of conduct](https://github.com/falcosecurity/evolution/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Security Audit
|
||||
### Contributing
|
||||
|
||||
See the [CONTRIBUTING.md](https://github.com/falcosecurity/.github/blob/master/CONTRIBUTING.md).
|
||||
|
||||
### Security Audit
|
||||
|
||||
A third party security audit was performed by Cure53, you can see the full report [here](./audits/SECURITY_AUDIT_2019_07.pdf).
|
||||
|
||||
## Reporting security vulnerabilities
|
||||
### Reporting security vulnerabilities
|
||||
|
||||
Please report security vulnerabilities following the community process documented [here](https://github.com/falcosecurity/.github/blob/main/SECURITY.md).
|
||||
Please report security vulnerabilities following the community process documented [here](https://github.com/falcosecurity/.github/blob/master/SECURITY.md).
|
||||
|
||||
## License
|
||||
### License Terms
|
||||
|
||||
Falco is licensed to you under the [Apache 2.0](./COPYING) open source license.
|
||||
|
||||
## Project Evolution
|
||||
|
||||
The [falcosecurity/evolution](https://github.com/falcosecurity/evolution) repository is the official space for the community to work together, discuss ideas, and document processes. It is also a place to make decisions. Check it out to find more helpful resources.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Governance](https://github.com/falcosecurity/evolution/blob/main/GOVERNANCE.md)
|
||||
- [Code Of Conduct](https://github.com/falcosecurity/evolution/blob/main/CODE_OF_CONDUCT.md)
|
||||
- [Maintainers Guidelines](https://github.com/falcosecurity/evolution/blob/main/MAINTAINERS_GUIDELINES.md)
|
||||
- [Maintainers List](https://github.com/falcosecurity/evolution/blob/main/MAINTAINERS.md)
|
||||
- [Repositories Guidelines](https://github.com/falcosecurity/evolution/blob/main/REPOSITORIES.md)
|
||||
- [Repositories List](https://github.com/falcosecurity/evolution/blob/main/README.md#repositories)
|
||||
- [Adopters List](https://github.com/falcosecurity/falco/blob/master/ADOPTERS.md)
|
||||
|
||||
|
||||
[1]: https://download.falco.org/?prefix=packages/rpm-dev/
|
||||
[2]: https://download.falco.org/?prefix=packages/rpm/
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
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/debian/falco.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_inject_kmod.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")
|
||||
list(APPEND CPACK_INSTALL_COMMANDS "cp scripts/rpm/falco_inject_kmod.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 "3.0.1+driver")
|
||||
set(DRIVER_CHECKSUM "SHA256=f50003043c804aa21990560de02db42e203ee09d050112a4a5dd2b05f22a8a6c")
|
||||
set(DRIVER_VERSION "6599e2efebce30a95f27739d655d53f0d5f686e4")
|
||||
set(DRIVER_CHECKSUM "SHA256=7cd84fe8a41c25bba9cd7d5d86a87d2483658e367b885ddbd3037aa45404df04")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
@@ -45,4 +45,4 @@ set(DRIVER_NAME "falco")
|
||||
set(DRIVER_PACKAGE_NAME "falco")
|
||||
set(DRIVER_COMPONENT_NAME "falco-driver")
|
||||
|
||||
add_subdirectory(${DRIVER_SOURCE_DIR} ${PROJECT_BINARY_DIR}/driver)
|
||||
add_subdirectory(${DRIVER_SOURCE_DIR} ${PROJECT_BINARY_DIR}/driver)
|
||||
@@ -19,7 +19,7 @@ message(STATUS "Libs version: ${FALCOSECURITY_LIBS_VERSION}")
|
||||
|
||||
ExternalProject_Add(
|
||||
falcosecurity-libs
|
||||
URL "https://github.com/falcosecurity/libs/archive/${FALCOSECURITY_LIBS_VERSION}.tar.gz"
|
||||
URL "https://github.com/geraldcombs/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 "0.9.0")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=5319a1b6a72eba3d9524cf084be5fc2ed81e3e90b3bee8edbe58b8646af0cbcb")
|
||||
set(FALCOSECURITY_LIBS_VERSION "cmake-install-targets")
|
||||
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=31382c88f3e3f0b7310a81cfd9608693e7b3895fbb4c8e7fd59e573ede83ff2f")
|
||||
endif()
|
||||
|
||||
# cd /path/to/build && cmake /path/to/source
|
||||
@@ -49,6 +49,7 @@ if(MUSL_OPTIMIZED_BUILD)
|
||||
add_definitions(-DMUSL_OPTIMIZED)
|
||||
endif()
|
||||
|
||||
set(SCAP_BPF_PROBE_ENV_VAR_NAME "FALCO_BPF_PROBE")
|
||||
set(SCAP_HOST_ROOT_ENV_VAR_NAME "HOST_ROOT")
|
||||
|
||||
if(NOT LIBSCAP_DIR)
|
||||
@@ -60,9 +61,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 "")
|
||||
@@ -87,4 +85,4 @@ endif()
|
||||
|
||||
include(driver)
|
||||
include(libscap)
|
||||
include(libsinsp)
|
||||
include(libsinsp)
|
||||
@@ -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.3.0")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(PLUGIN_K8S_AUDIT_HASH "ded0b5419f40084547620ccc48b19768e5e89457b85cfe8fbe496ca72267a3a4")
|
||||
set(PLUGIN_K8S_AUDIT_HASH "214915fc2a61d147d64aaf4cb29c3fc6a513eda621dad1dfe77f2fd7099b31e1")
|
||||
else() # aarch64
|
||||
set(PLUGIN_K8S_AUDIT_HASH "775cba666612114bc5b0c36f2e3c4557f5adbffcca2d77e72be87c6fcbf51ceb")
|
||||
set(PLUGIN_K8S_AUDIT_HASH "d9b4610714df581043db76ecb4caf3a41aae5494cf61ab8740a3749bfac8457e")
|
||||
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=3913a8c6095794c7de6a97a2a64953a0fa4f87caab014d11b2c8f9221eb77591"
|
||||
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.5.0")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "80e0c33f30c01a90efb7e9a671d978ff9679c462e3105020238abf31230e49a9")
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "ca6c0d087b37090145ef0c92f10d1dd32bb2a08c7bae83cc6fb7a1ba712f3182")
|
||||
else() # aarch64
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "a3e739932e66d44be848a68857fa15f56134d5246a1b9ab912c81f91b68fb23f")
|
||||
set(PLUGIN_CLOUDTRAIL_HASH "f6e12d3bd16ae0f504ed2bb56d13531d15b7d55beb1b63932cbe603cff941372")
|
||||
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=7f88fb6b530f8ee739b65d38a36c69cdc70398576299b90118bd7324dbdb5f46"
|
||||
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.5.0")
|
||||
if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64")
|
||||
set(PLUGIN_JSON_HASH "15fb7eddd978e8bb03f05412e9446e264e4548d7423b3d724b99d6d87a8c1b27")
|
||||
set(PLUGIN_JSON_HASH "b422c4f08bb54ccd384a87c5922e120d5731028c87742ef657cacf936447c202")
|
||||
else() # aarch64
|
||||
set(PLUGIN_JSON_HASH "4db23f35a750e10a5b7b54c9aa469a7587705e7faa22927e941b41f3c5533e9f")
|
||||
set(PLUGIN_JSON_HASH "8358f04325d8a9e9675f38fae8d13a250fb132dcf6741fd0f9830e8c39f48aed")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
|
||||
@@ -25,10 +25,11 @@ else()
|
||||
"--force"
|
||||
"--inconclusive"
|
||||
"--inline-suppr" # allows to specify suppressions directly in source code
|
||||
"--project=${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json" # use the compilation database as source
|
||||
"--quiet"
|
||||
"--xml" # we want to generate a report
|
||||
"--output-file=${CMAKE_CURRENT_BINARY_DIR}/static-analysis-reports/cppcheck/cppcheck.xml" # generate the report under the reports folder in the build folder
|
||||
"-i${CMAKE_CURRENT_BINARY_DIR}"# exclude the build folder
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
)
|
||||
endif() # CPPCHECK
|
||||
|
||||
|
||||
@@ -13,6 +13,5 @@ This directory contains various ways to package Falco as a container and related
|
||||
| [falcosecurity/falco-builder:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-builder) | docker/builder | The complete build tool chain for compiling Falco from source. See [the documentation](https://falco.org/docs/getting-started/source/) for more details on building from source. Used to build Falco (CI). |
|
||||
| [falcosecurity/falco-tester:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-tester) | docker/tester | Container image for running the Falco test suite. Used to run Falco integration tests (CI). |
|
||||
| _not to be published_ | docker/local | Built on-the-fly and used by falco-tester. |
|
||||
| _not to be published_ | docker/dev | Built on-the-fly to test local Falco development. |
|
||||
|
||||
> Note: `falco-builder`, `falco-tester`, `docker/local`, `docker/dev` images are not integrated into the release process because they are development and CI tools that need to be manually pushed only when updated.
|
||||
> Note: `falco-builder`, `falco-tester` (and the `docker/local` image that it's built on the fly) are not integrated into the release process because they are development and CI tools that need to be manually pushed only when updated.
|
||||
|
||||
@@ -9,10 +9,10 @@ shift
|
||||
|
||||
# Build type can be "debug" or "release", fallbacks to "release" by default
|
||||
BUILD_TYPE=$(echo "$BUILD_TYPE" | tr "[:upper:]" "[:lower:]")
|
||||
FALCO_EXTRA_DEBUG_FLAGS=
|
||||
DRAIOS_DEBUG_FLAGS=
|
||||
case "$BUILD_TYPE" in
|
||||
"debug")
|
||||
FALCO_EXTRA_DEBUG_FLAGS="-D_DEBUG -DNDEBUG"
|
||||
DRAIOS_DEBUG_FLAGS="-D_DEBUG -DNDEBUG"
|
||||
;;
|
||||
*)
|
||||
BUILD_TYPE="release"
|
||||
@@ -37,7 +37,7 @@ case "$CMD" in
|
||||
-DBUILD_BPF="$BUILD_BPF" \
|
||||
-DBUILD_WARNINGS_AS_ERRORS="$BUILD_WARNINGS_AS_ERRORS" \
|
||||
-DFALCO_VERSION="$FALCO_VERSION" \
|
||||
-DFALCO_EXTRA_DEBUG_FLAGS="$FALCO_EXTRA_DEBUG_FLAGS" \
|
||||
-DDRAIOS_DEBUG_FLAGS="$DRAIOS_DEBUG_FLAGS" \
|
||||
-DUSE_BUNDLED_DEPS=ON \
|
||||
"$SOURCE_DIR/falco"
|
||||
exit "$(printf '%d\n' $?)"
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# Build a docker container for local development
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set(DEV_DOCKER_CXT ${CMAKE_BINARY_DIR}/docker/dev-docker-ctx)
|
||||
|
||||
# This target prepares the `tar.gz` artifact that will be passed to the dockerfile.
|
||||
add_custom_target(dev-docker-prepare
|
||||
COMMAND mkdir -p ${DEV_DOCKER_CXT}
|
||||
COMMAND "${CMAKE_COMMAND}" --build . --target package
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/falco-${FALCO_VERSION}-${FALCO_TARGET_ARCH}.tar.gz ${DEV_DOCKER_CXT}/falco.tar.gz
|
||||
DEPENDS falco
|
||||
)
|
||||
|
||||
add_custom_target(dev-docker
|
||||
COMMAND docker build
|
||||
--tag falco-nodriver-dev
|
||||
-f ${CMAKE_SOURCE_DIR}/docker/dev/nodriver.Dockerfile
|
||||
${DEV_DOCKER_CXT}
|
||||
DEPENDS dev-docker-prepare
|
||||
)
|
||||
endif()
|
||||
@@ -1,59 +0,0 @@
|
||||
# Falco development image
|
||||
|
||||
This docker image can be easily generated starting from a clean Falco build.
|
||||
|
||||
## 1. Clone the Falco repo ⬇️
|
||||
|
||||
```bash
|
||||
git clone https://github.com/falcosecurity/falco.git
|
||||
```
|
||||
|
||||
## 2. Prepare the build directory 🏗️
|
||||
|
||||
### `falco-runner-image` tag
|
||||
|
||||
The CMake command that we will see in the next section builds Falco locally on your machine, and push it into a docker image, so as you may imagine the final image that will run Falco must have a similar `GLIBC` version to your local one. For this reason, you have to use docker tags.
|
||||
|
||||
The `nodriver.Dockerfile` will use the `falco-runner-image` tag to build the final image as you can see here:
|
||||
|
||||
```dockerfile
|
||||
FROM falco-runner-image AS runner
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
For example, if I build Falco locally on a un `ubuntu:22-04` machine I will instruct docker to use `ubuntu:22-04` as a final running image.
|
||||
|
||||
```bash
|
||||
docker tag ubuntu:22.04 falco-runner-image
|
||||
```
|
||||
|
||||
In this way the `nodriver.Dockerfile` will use `ubuntu:22-04` during the building phase.
|
||||
|
||||
### Cmake command
|
||||
|
||||
Now that we set the `falco-runner-image` tag, we are ready to build our Falco image. Starting from the project root:
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake -DUSE_BUNDLED_DEPS=On -DCREATE_TEST_TARGETS=Off -DCPACK_GENERATOR=TGZ -DFALCO_ETC_DIR=/etc/falco ..
|
||||
make dev-docker
|
||||
```
|
||||
> __Please note__: These cmake options `-DUSE_BUNDLED_DEPS=On -DCREATE_TEST_TARGETS=Off -DCPACK_GENERATOR=TGZ -DFALCO_ETC_DIR=/etc/falco` are the required ones but you can provide additional options to build the image according to your needs (for example you can pass `-DMINIMAL_BUILD=On` if you want a minimal build image or `-DBUILD_FALCO_MODERN_BPF=ON` if you want to include the modern bpf probe inside the image)
|
||||
|
||||
## 3. Run the docker image locally 🏎️
|
||||
|
||||
```bash
|
||||
docker run --rm -i -t \
|
||||
--privileged \
|
||||
-v /var/run/docker.sock:/host/var/run/docker.sock \
|
||||
-v /dev:/host/dev \
|
||||
-v /proc:/host/proc:ro \
|
||||
falco-nodriver-dev
|
||||
```
|
||||
|
||||
If you change something in the Falco source code you can simply rebuild the image with:
|
||||
|
||||
```bash
|
||||
make dev-docker
|
||||
```
|
||||
@@ -1,33 +0,0 @@
|
||||
FROM ubuntu:22.04 AS builder
|
||||
|
||||
COPY ./falco.tar.gz /
|
||||
|
||||
WORKDIR /
|
||||
|
||||
# 1. We remove the Falco directory with the name related to the version and the arch
|
||||
# 2. We remove the source folder
|
||||
# 3. We remove the `falco-driver-loader` binary
|
||||
RUN mkdir falco; \
|
||||
tar -xzf falco.tar.gz -C falco --strip-component 1; \
|
||||
rm -rf /falco/usr/src; \
|
||||
rm /falco/usr/bin/falco-driver-loader
|
||||
|
||||
# the time displayed in log messages and output messages will be in ISO 8601.
|
||||
RUN sed -e 's/time_format_iso_8601: false/time_format_iso_8601: true/' < /falco/etc/falco/falco.yaml > /falco/etc/falco/falco.yaml.new; \
|
||||
mv /falco/etc/falco/falco.yaml.new /falco/etc/falco/falco.yaml
|
||||
|
||||
# Please note: it could be necessary to change this base image according
|
||||
# to the `glibc` version of the machine where you build the tar.gz package
|
||||
# use `docker tag ubuntu:22.04 falco-runner-image` for example
|
||||
FROM falco-runner-image AS runner
|
||||
|
||||
LABEL name="falcosecurity/falco-nodriver-dev"
|
||||
LABEL maintainer="cncf-falco-dev@lists.cncf.io"
|
||||
LABEL usage="docker run -it --rm --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro --name NAME IMAGE"
|
||||
|
||||
COPY --from=builder /falco /
|
||||
|
||||
ENV HOST_ROOT /host
|
||||
ENV HOME /root
|
||||
|
||||
CMD ["/usr/bin/falco", "-o", "time_format_iso_8601=true"]
|
||||
69
falco.yaml
69
falco.yaml
@@ -169,61 +169,6 @@ syscall_event_drops:
|
||||
syscall_event_timeouts:
|
||||
max_consecutives: 1000
|
||||
|
||||
# --- [Description]
|
||||
#
|
||||
# This is an index that controls the dimension of the syscall buffers.
|
||||
# The syscall buffer is the shared space between Falco and its drivers where all the syscall events
|
||||
# are stored.
|
||||
# Falco uses a syscall buffer for every online CPU, and all these buffers share the same dimension.
|
||||
# So this parameter allows you to control the size of all the buffers!
|
||||
#
|
||||
# --- [Usage]
|
||||
#
|
||||
# You can choose between different indexes: from `1` to `10` (`0` is reserved for future uses).
|
||||
# Every index corresponds to a dimension in bytes:
|
||||
#
|
||||
# [(*), 1 MB, 2 MB, 4 MB, 8 MB, 16 MB, 32 MB, 64 MB, 128 MB, 256 MB, 512 MB]
|
||||
# ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
|
||||
# | | | | | | | | | | |
|
||||
# 0 1 2 3 4 5 6 7 8 9 10
|
||||
#
|
||||
# As you can see the `0` index is reserved, while the index `1` corresponds to
|
||||
# `1 MB` and so on.
|
||||
#
|
||||
# These dimensions in bytes derive from the fact that the buffer size must be:
|
||||
# (1) a power of 2.
|
||||
# (2) a multiple of your system_page_dimension.
|
||||
# (3) greater than `2 * (system_page_dimension)`.
|
||||
#
|
||||
# According to these constraints is possible that sometimes you cannot use all the indexes, let's consider an
|
||||
# example to better understand it:
|
||||
# If you have a `page_size` of 1 MB the first available buffer size is 4 MB because 2 MB is exactly
|
||||
# `2 * (system_page_size)` -> `2 * 1 MB`, but this is not enough we need more than `2 * (system_page_size)`!
|
||||
# So from this example is clear that if you have a page size of 1 MB the first index that you can use is `3`.
|
||||
#
|
||||
# Please note: this is a very extreme case just to let you understand the mechanism, usually the page size is something
|
||||
# like 4 KB so you have no problem at all and you can use all the indexes (from `1` to `10`).
|
||||
#
|
||||
# To check your system page size use the Falco `--page-size` command line option. The output on a system with a page
|
||||
# size of 4096 Bytes (4 KB) should be the following:
|
||||
#
|
||||
# "Your system page size is: 4096 bytes."
|
||||
#
|
||||
# --- [Suggestions]
|
||||
#
|
||||
# Before the introduction of this param the buffer size was fixed to 8 MB (so index `4`, as you can see
|
||||
# in the default value below).
|
||||
# You can increase the buffer size when you face syscall drops. A size of 16 MB (so index `5`) can reduce
|
||||
# syscall drops in production-heavy systems without noticeable impact. Very large buffers however could
|
||||
# slow down the entire machine.
|
||||
# On the other side you can try to reduce the buffer size to speed up the system, but this could
|
||||
# increase the number of syscall drops!
|
||||
# As a final remark consider that the buffer size is mapped twice in the process' virtual memory so a buffer of 8 MB
|
||||
# will result in a 16 MB area in the process virtual memory.
|
||||
# Please pay attention when you use this parameter and change it only if the default size doesn't fit your use case.
|
||||
|
||||
syscall_buf_size_preset: 4
|
||||
|
||||
# Falco continuously monitors outputs performance. When an output channel does not allow
|
||||
# to deliver an alert within a given deadline, an error is reported indicating
|
||||
# which output is blocking notifications.
|
||||
@@ -282,10 +227,9 @@ file_output:
|
||||
stdout_output:
|
||||
enabled: true
|
||||
|
||||
# Falco contains an embedded webserver that is used to implement an health
|
||||
# endpoint for checking if Falco is up and running. These config options control
|
||||
# the behavior of that webserver. By default, the webserver is enabled and
|
||||
# the endpoint is /healthz.
|
||||
# Falco contains an embedded webserver that can be used to accept K8s
|
||||
# Audit Events. These config options control the behavior of that
|
||||
# webserver. (By default, the webserver is enabled).
|
||||
#
|
||||
# The ssl_certificate is a combination SSL Certificate and corresponding
|
||||
# key contained in a single file. You can generate a key/cert as follows:
|
||||
@@ -293,10 +237,11 @@ stdout_output:
|
||||
# $ openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
|
||||
# $ cat certificate.pem key.pem > falco.pem
|
||||
# $ sudo cp falco.pem /etc/falco/falco.pem
|
||||
#
|
||||
# It also exposes a healthy endpoint that can be used to check if Falco is up and running
|
||||
# By default the endpoint is /healthz
|
||||
webserver:
|
||||
enabled: true
|
||||
# when threadiness is 0, Falco automatically guesses it depending on the number of online cores
|
||||
threadiness: 0
|
||||
listen_port: 8765
|
||||
k8s_healthz_endpoint: /healthz
|
||||
ssl_enabled: false
|
||||
@@ -351,7 +296,7 @@ http_output:
|
||||
# gRPC server using an unix socket
|
||||
grpc:
|
||||
enabled: false
|
||||
bind_address: "unix:///run/falco/falco.sock"
|
||||
bind_address: "unix:///var/run/falco.sock"
|
||||
# when threadiness is 0, Falco automatically guesses it depending on the number of online cores
|
||||
threadiness: 0
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# The latest Falco Engine version is 9.
|
||||
# Starting with version 8, the Falco engine supports exceptions.
|
||||
# However the Falco rules file does not use them by default.
|
||||
- required_engine_version: 13
|
||||
- required_engine_version: 9
|
||||
|
||||
# Currently disabled as read/write are ignored syscalls. The nearly
|
||||
# similar open_write/open_read check for files being opened for
|
||||
@@ -28,20 +29,13 @@
|
||||
# condition: (syscall.type=read and evt.dir=> and fd.type in (file, directory))
|
||||
|
||||
- macro: open_write
|
||||
condition: (evt.type in (open,openat,openat2) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0)
|
||||
condition: evt.type in (open,openat,openat2) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0
|
||||
|
||||
- macro: open_read
|
||||
condition: (evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0)
|
||||
condition: evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0
|
||||
|
||||
- macro: open_directory
|
||||
condition: (evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='d' and fd.num>=0)
|
||||
|
||||
# Failed file open attempts, useful to detect threat actors making mistakes
|
||||
# https://man7.org/linux/man-pages/man3/errno.3.html
|
||||
# evt.res=ENOENT - No such file or directory
|
||||
# evt.res=EACCESS - Permission denied
|
||||
- macro: open_file_failed
|
||||
condition: (evt.type in (open,openat,openat2) and fd.typechar='f' and fd.num=-1 and evt.res startswith E)
|
||||
condition: evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='d' and fd.num>=0
|
||||
|
||||
- macro: never_true
|
||||
condition: (evt.num=0)
|
||||
@@ -57,32 +51,32 @@
|
||||
condition: (proc.name!="<NA>")
|
||||
|
||||
- macro: rename
|
||||
condition: (evt.type in (rename, renameat, renameat2))
|
||||
condition: evt.type in (rename, renameat, renameat2)
|
||||
|
||||
- macro: mkdir
|
||||
condition: (evt.type in (mkdir, mkdirat))
|
||||
condition: evt.type in (mkdir, mkdirat)
|
||||
|
||||
- macro: remove
|
||||
condition: (evt.type in (rmdir, unlink, unlinkat))
|
||||
condition: evt.type in (rmdir, unlink, unlinkat)
|
||||
|
||||
- macro: modify
|
||||
condition: (rename or remove)
|
||||
condition: rename or remove
|
||||
|
||||
- macro: spawned_process
|
||||
condition: (evt.type in (execve, execveat) and evt.dir=<)
|
||||
condition: evt.type in (execve, execveat) and evt.dir=<
|
||||
|
||||
- macro: create_symlink
|
||||
condition: (evt.type in (symlink, symlinkat) and evt.dir=<)
|
||||
condition: evt.type in (symlink, symlinkat) and evt.dir=<
|
||||
|
||||
- macro: create_hardlink
|
||||
condition: (evt.type in (link, linkat) and evt.dir=<)
|
||||
condition: evt.type in (link, linkat) and evt.dir=<
|
||||
|
||||
- macro: chmod
|
||||
condition: (evt.type in (chmod, fchmod, fchmodat) and evt.dir=<)
|
||||
|
||||
# File categories
|
||||
- macro: bin_dir
|
||||
condition: (fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin))
|
||||
condition: fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin)
|
||||
|
||||
- macro: bin_dir_mkdir
|
||||
condition: >
|
||||
@@ -111,7 +105,7 @@
|
||||
evt.arg.newpath startswith /usr/sbin/)
|
||||
|
||||
- macro: etc_dir
|
||||
condition: (fd.name startswith /etc/)
|
||||
condition: fd.name startswith /etc/
|
||||
|
||||
# This detects writes immediately below / or any write anywhere below /root
|
||||
- macro: root_dir
|
||||
@@ -367,7 +361,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]
|
||||
|
||||
@@ -375,9 +369,11 @@
|
||||
# use the fd.*ip and fd.*ip.name fields to match connection
|
||||
# information against ips, netmasks, and complete domain names.
|
||||
#
|
||||
# To use this rule, you should enable it and
|
||||
# To use this rule, you should modify consider_all_outbound_conns and
|
||||
# populate allowed_{source,destination}_{ipaddrs,networks,domains} with the
|
||||
# values that make sense for your environment.
|
||||
- macro: consider_all_outbound_conns
|
||||
condition: (never_true)
|
||||
|
||||
# Note that this can be either individual IPs or netmasks
|
||||
- list: allowed_outbound_destination_ipaddrs
|
||||
@@ -392,15 +388,17 @@
|
||||
- rule: Unexpected outbound connection destination
|
||||
desc: Detect any outbound connection to a destination outside of an allowed set of ips, networks, or domain names
|
||||
condition: >
|
||||
outbound and not
|
||||
consider_all_outbound_conns and outbound and not
|
||||
((fd.sip in (allowed_outbound_destination_ipaddrs)) or
|
||||
(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]
|
||||
|
||||
- macro: consider_all_inbound_conns
|
||||
condition: (never_true)
|
||||
|
||||
- list: allowed_inbound_source_ipaddrs
|
||||
items: ['"127.0.0.1"']
|
||||
|
||||
@@ -413,12 +411,11 @@
|
||||
- rule: Unexpected inbound connection source
|
||||
desc: Detect any inbound connection from a source outside of an allowed set of ips, networks, or domain names
|
||||
condition: >
|
||||
inbound and not
|
||||
consider_all_inbound_conns and inbound and not
|
||||
((fd.cip in (allowed_inbound_source_ipaddrs)) or
|
||||
(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,29 +458,36 @@
|
||||
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]
|
||||
|
||||
# This rule is not enabled by default, as there are many legitimate
|
||||
# readers of shell config files.
|
||||
# readers of shell config files. If you want to enable it, modify the
|
||||
# following macro.
|
||||
|
||||
- macro: consider_shell_config_reads
|
||||
condition: (never_true)
|
||||
|
||||
- rule: Read Shell Configuration File
|
||||
desc: Detect attempts to read shell configuration files by non-shell programs
|
||||
condition: >
|
||||
open_read and
|
||||
consider_shell_config_reads and
|
||||
(fd.filename in (shell_config_filenames) or
|
||||
fd.name in (shell_config_files) or
|
||||
fd.directory in (shell_config_directories)) and
|
||||
(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]
|
||||
|
||||
- macro: consider_all_cron_jobs
|
||||
condition: (never_true)
|
||||
|
||||
- macro: user_known_cron_jobs
|
||||
condition: (never_true)
|
||||
|
||||
@@ -492,10 +496,10 @@
|
||||
condition: >
|
||||
((open_write and fd.name startswith /etc/cron) or
|
||||
(spawned_process and proc.name = "crontab")) and
|
||||
consider_all_cron_jobs and
|
||||
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 +878,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 +900,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]
|
||||
|
||||
@@ -908,10 +912,7 @@
|
||||
items: [/boot, /lib, /lib64, /usr/lib, /usr/local/lib, /usr/local/sbin, /usr/local/bin, /root/.ssh]
|
||||
|
||||
- macro: user_ssh_directory
|
||||
condition: (fd.name contains '/.ssh/' and fd.name glob '/home/*/.ssh/*')
|
||||
|
||||
- macro: directory_traversal
|
||||
condition: (fd.nameraw contains '../' and fd.nameraw glob '*../*../*')
|
||||
condition: (fd.name glob '/home/*/.ssh/*')
|
||||
|
||||
# google_accounts_(daemon)
|
||||
- macro: google_accounts_daemon_writing_ssh
|
||||
@@ -952,28 +953,10 @@
|
||||
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]
|
||||
|
||||
# ******************************************************************************
|
||||
# * "Directory traversal monitored file read" requires FALCO_ENGINE_VERSION 13 *
|
||||
# ******************************************************************************
|
||||
|
||||
- rule: Directory traversal monitored file read
|
||||
desc: >
|
||||
Web applications can be vulnerable to directory traversal attacks that allow accessing files outside of the web app's root directory (e.g. Arbitrary File Read bugs).
|
||||
System directories like /etc are typically accessed via absolute paths. Access patterns outside of this (here path traversal) can be regarded as suspicious.
|
||||
This rule includes failed file open attempts.
|
||||
condition: (open_read or open_file_failed) and (etc_dir or user_ssh_directory or fd.name startswith /root/.ssh or fd.name contains "id_rsa") and directory_traversal and not proc.pname in (shell_binaries)
|
||||
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
|
||||
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]
|
||||
|
||||
# This rule is disabled by default as many system management tools
|
||||
# like ansible, etc can read these files/paths. Enable it using this macro.
|
||||
|
||||
@@ -990,7 +973,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 +1259,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 +1356,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 +1374,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 +1444,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 +1468,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 +1507,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 +1518,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 +1536,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 +1577,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 +1724,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 +1908,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 +1932,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 +1978,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 +1998,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 +2013,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 +2031,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]
|
||||
|
||||
@@ -2097,7 +2080,7 @@
|
||||
# This list allows for easy additions to the set of commands allowed
|
||||
# to run shells in containers without having to without having to copy
|
||||
# and override the entire run shell in container macro. Once
|
||||
# https://github.com/falcosecurity/falco/issues/255 is fixed this will be a
|
||||
# https://github.com/draios/falco/issues/255 is fixed this will be a
|
||||
# bit easier, as someone could append of any of the existing lists.
|
||||
- list: user_known_shell_spawn_binaries
|
||||
items: []
|
||||
@@ -2124,7 +2107,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]
|
||||
|
||||
@@ -2160,10 +2143,9 @@
|
||||
http_proxy_procs and
|
||||
not allowed_ssh_proxy_env and
|
||||
proc.env icontains HTTP_PROXY
|
||||
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 +2161,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 +2172,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 +2211,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 +2271,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 +2303,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 +2327,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 +2349,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 +2366,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 +2394,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 +2412,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 +2428,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 +2465,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 +2479,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 +2491,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 +2511,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 +2547,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 +2581,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 +2599,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 +2640,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,11 +2653,14 @@
|
||||
((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]
|
||||
|
||||
- macro: consider_all_chmods
|
||||
condition: (always_true)
|
||||
|
||||
- list: user_known_chmod_applications
|
||||
items: [hyperkube, kubelet, k3s-agent]
|
||||
|
||||
@@ -2693,14 +2676,14 @@
|
||||
this means that the application will run with the privileges of the owning user or group respectively.
|
||||
Detect setuid or setgid bits set via chmod
|
||||
condition: >
|
||||
chmod and (evt.arg.mode contains "S_ISUID" or evt.arg.mode contains "S_ISGID")
|
||||
consider_all_chmods and chmod and (evt.arg.mode contains "S_ISUID" or evt.arg.mode contains "S_ISGID")
|
||||
and not proc.name in (user_known_chmod_applications)
|
||||
and not exe_running_docker_save
|
||||
and not user_known_set_setuid_or_setgid_bit_conditions
|
||||
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]
|
||||
@@ -2708,7 +2691,10 @@
|
||||
- list: exclude_hidden_directories
|
||||
items: [/root/.cassandra]
|
||||
|
||||
# The rule is disabled by default.
|
||||
# To use this rule, you should modify consider_hidden_file_creation.
|
||||
- macro: consider_hidden_file_creation
|
||||
condition: (never_true)
|
||||
|
||||
- macro: user_known_create_hidden_file_activities
|
||||
condition: (never_true)
|
||||
|
||||
@@ -2718,11 +2704,11 @@
|
||||
((modify and evt.arg.newpath contains "/.") or
|
||||
(mkdir and evt.arg.path contains "/.") or
|
||||
(open_write and evt.arg.flags contains "O_CREAT" and fd.name contains "/." and not fd.name pmatch (exclude_hidden_directories))) and
|
||||
consider_hidden_file_creation and
|
||||
not user_known_create_hidden_file_activities
|
||||
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 +2733,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 +2744,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 +2754,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 +2859,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,20 +2896,30 @@
|
||||
- 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]
|
||||
|
||||
|
||||
# This rule is enabled by default.
|
||||
# If you want to disable it, modify the following macro.
|
||||
- macro: consider_packet_socket_communication
|
||||
condition: (always_true)
|
||||
|
||||
- list: user_known_packet_socket_binaries
|
||||
items: []
|
||||
|
||||
- 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)
|
||||
condition: evt.type=socket and evt.arg[0]=AF_PACKET and consider_packet_socket_communication 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 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]
|
||||
|
||||
# Change to (always_true) to enable rule 'Network connection outside local subnet'
|
||||
- macro: enabled_rule_network_only_subnet
|
||||
condition: (never_true)
|
||||
|
||||
# Namespaces where the rule is enforce
|
||||
- list: namespace_scope_network_only_subnet
|
||||
items: []
|
||||
@@ -2934,8 +2930,8 @@
|
||||
fd.ip = "0.0.0.0" or
|
||||
fd.net = "127.0.0.0/8"
|
||||
|
||||
# # The rule is disabled by default.
|
||||
# # How to test:
|
||||
# # Change macro enabled_rule_network_only_subnet to condition: always_true
|
||||
# # Add 'default' to namespace_scope_network_only_subnet
|
||||
# # Run:
|
||||
# kubectl run --generator=run-pod/v1 -n default -i --tty busybox --image=busybox --rm -- wget google.com -O /var/google.html
|
||||
@@ -2944,19 +2940,22 @@
|
||||
- rule: Network Connection outside Local Subnet
|
||||
desc: Detect traffic to image outside local subnet.
|
||||
condition: >
|
||||
enabled_rule_network_only_subnet and
|
||||
inbound_outbound and
|
||||
container and
|
||||
not network_local_subnet and
|
||||
k8s.ns.name in (namespace_scope_network_only_subnet)
|
||||
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
|
||||
tags: [network]
|
||||
|
||||
- macro: allowed_port
|
||||
condition: (never_true)
|
||||
|
||||
- list: allowed_image
|
||||
items: [] # add image to monitor, i.e.: bitnami/nginx
|
||||
|
||||
@@ -2977,15 +2976,15 @@
|
||||
- rule: Outbound or Inbound Traffic not to Authorized Server Process and Port
|
||||
desc: Detect traffic that is not to authorized server process and port.
|
||||
condition: >
|
||||
allowed_port and
|
||||
inbound_outbound and
|
||||
container and
|
||||
container.image.repository in (allowed_image) and
|
||||
not proc.name in (authorized_server_binary) and
|
||||
not fd.sport in (authorized_server_port)
|
||||
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 +2999,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.
|
||||
@@ -3011,7 +3010,7 @@
|
||||
# Two things to pay attention to:
|
||||
# 1) In most cases, 'docker cp' will not be identified, but the assumption is that if an attacker gained access to the container runtime daemon, they are already privileged
|
||||
# 2) Drift rules will be noisy in environments in which containers are built (e.g. docker build)
|
||||
# These two rules are not enabled by default.
|
||||
# These two rules are not enabled by default. Use `never_true` in macro condition to enable them.
|
||||
|
||||
- macro: user_known_container_drift_activities
|
||||
condition: (always_true)
|
||||
@@ -3020,6 +3019,7 @@
|
||||
desc: New executable created in a container due to chmod
|
||||
condition: >
|
||||
chmod and
|
||||
consider_all_chmods and
|
||||
container and
|
||||
not runc_writing_exec_fifo and
|
||||
not runc_writing_var_lib_docker and
|
||||
@@ -3028,8 +3028,7 @@
|
||||
((evt.arg.mode contains "S_IXUSR") or
|
||||
(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
|
||||
|
||||
# ****************************************************************************
|
||||
@@ -3045,8 +3044,7 @@
|
||||
not runc_writing_var_lib_docker and
|
||||
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,18 +3098,13 @@
|
||||
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]
|
||||
|
||||
- macro: mount_info
|
||||
condition: (proc.args="" or proc.args intersects ("-V", "-l", "-h"))
|
||||
|
||||
- macro: known_gke_mount_in_privileged_containers
|
||||
condition:
|
||||
(k8s.ns.name = kube-system
|
||||
and container.image.repository = gke.gcr.io/gcp-compute-persistent-disk-csi-driver)
|
||||
|
||||
- macro: user_known_mount_in_privileged_containers
|
||||
condition: (never_true)
|
||||
|
||||
@@ -3122,23 +3115,26 @@
|
||||
and container.privileged=true
|
||||
and proc.name=mount
|
||||
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]
|
||||
|
||||
- macro: consider_userfaultfd_activities
|
||||
condition: (always_true)
|
||||
|
||||
- list: user_known_userfaultfd_processes
|
||||
items: []
|
||||
|
||||
- rule: Unprivileged Delegation of Page Faults Handling to a Userspace Process
|
||||
desc: Detect a successful unprivileged userfaultfd syscall which might act as an attack primitive to exploit other bugs
|
||||
condition: >
|
||||
consider_userfaultfd_activities and
|
||||
evt.type = userfaultfd and
|
||||
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 +3164,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 +3176,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,40 +3199,10 @@
|
||||
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]
|
||||
|
||||
- list: docker_binaries
|
||||
items: [dockerd, containerd-shim, "runc:[1:CHILD]", pause]
|
||||
|
||||
- macro: docker_procs
|
||||
condition: proc.name in (docker_binaries)
|
||||
|
||||
- rule: Modify Container Entrypoint
|
||||
desc: This rule detect an attempt to write on container entrypoint symlink (/proc/self/exe). Possible CVE-2019-5736 Container Breakout exploitation attempt.
|
||||
condition: >
|
||||
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)
|
||||
priority: WARNING
|
||||
tags: [container, filesystem, mitre_initial_access]
|
||||
|
||||
# Application rules have moved to application_rules.yaml. Please look
|
||||
# there if you want to enable them by adding to
|
||||
# falco_rules.local.yaml.
|
||||
|
||||
- list: known_binaries_to_read_environment_variables_from_proc_files
|
||||
items: [scsi_id, argoexec]
|
||||
|
||||
- rule: Read environment variable from /proc files
|
||||
desc: An attempt to read process environment variables from /proc files
|
||||
condition: >
|
||||
open_read and container and (fd.name glob /proc/*/environ)
|
||||
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)
|
||||
priority: WARNING
|
||||
tags: [filesystem, mitre_credential_access, mitre_discovery]
|
||||
|
||||
@@ -22,9 +22,6 @@ configure_file(debian/prerm.in debian/prerm)
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/debian/falco.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/debian")
|
||||
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/debian/falco_inject_kmod.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)
|
||||
@@ -32,9 +29,6 @@ configure_file(rpm/preuninstall.in rpm/preuninstall)
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/rpm/falco.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/rpm")
|
||||
|
||||
file(COPY "${PROJECT_SOURCE_DIR}/scripts/rpm/falco_inject_kmod.service"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/scripts/rpm")
|
||||
|
||||
configure_file(falco-driver-loader falco-driver-loader @ONLY)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
After=falco_inject_kmod.service
|
||||
Requires=falco_inject_kmod.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStartPre=/sbin/modprobe falco
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
ExecStopPost=/sbin/rmmod falco
|
||||
UMask=0077
|
||||
@@ -18,7 +17,6 @@ NoNewPrivileges=yes
|
||||
ProtectHome=read-only
|
||||
ProtectSystem=full
|
||||
ProtectKernelTunables=true
|
||||
ReadWritePaths=/sys/module/falco
|
||||
RestrictRealtime=true
|
||||
RestrictAddressFamilies=~AF_PACKET
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
Before=falco.service
|
||||
Wants=falco.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=root
|
||||
ExecStart=/sbin/modprobe falco
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -41,34 +41,3 @@ case "$1" in
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# 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
|
||||
|
||||
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
|
||||
# This will only remove masks created by d-s-h on package removal.
|
||||
deb-systemd-helper unmask 'falco.service' >/dev/null || true
|
||||
|
||||
# was-enabled defaults to true, so new installations run enable.
|
||||
if deb-systemd-helper --quiet was-enabled 'falco.service'; then
|
||||
# Enables the unit on first installation, creates new
|
||||
# symlinks on upgrades if the unit file has changed.
|
||||
deb-systemd-helper enable 'falco.service' >/dev/null || true
|
||||
else
|
||||
# Update the statefile to add new symlinks (if any), which need to be
|
||||
# cleaned up on purge. Also remove old symlinks.
|
||||
deb-systemd-helper update-state 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl --system daemon-reload >/dev/null || true
|
||||
if [ -n "$2" ]; then
|
||||
_dh_action=restart
|
||||
else
|
||||
_dh_action=start
|
||||
fi
|
||||
deb-systemd-invoke $_dh_action 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -15,25 +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
|
||||
systemctl --system daemon-reload >/dev/null || true
|
||||
fi
|
||||
|
||||
if [ "$1" = "remove" ]; then
|
||||
if [ -x "/usr/bin/deb-systemd-helper" ]; then
|
||||
deb-systemd-helper mask 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "purge" ]; then
|
||||
if [ -x "/usr/bin/deb-systemd-helper" ]; then
|
||||
deb-systemd-helper purge 'falco.service' >/dev/null || true
|
||||
deb-systemd-helper unmask 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -17,14 +17,6 @@
|
||||
#
|
||||
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
|
||||
deb-systemd-invoke stop 'falco.service' >/dev/null || true
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
remove|upgrade|deconfigure)
|
||||
/usr/bin/falco-driver-loader --clean
|
||||
|
||||
@@ -113,17 +113,13 @@ get_target_id() {
|
||||
elif [ -f "${HOST_ROOT}/etc/centos-release" ]; then
|
||||
# Older CentOS distros
|
||||
OS_ID=centos
|
||||
elif [ -f "${HOST_ROOT}/etc/VERSION" ]; then
|
||||
OS_ID=minikube
|
||||
else
|
||||
>&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.
|
||||
# Not sure if there is a better way to detect minikube.
|
||||
if [ -f "${HOST_ROOT}/etc/VERSION" ]; then
|
||||
OS_ID=minikube
|
||||
fi
|
||||
|
||||
case "${OS_ID}" in
|
||||
("amzn")
|
||||
if [[ $VERSION_ID == "2" ]]; then
|
||||
@@ -133,13 +129,8 @@ get_target_id() {
|
||||
fi
|
||||
;;
|
||||
("ubuntu")
|
||||
# Extract the flavor from the kernelrelease
|
||||
# Examples:
|
||||
# 5.0.0-1028-aws-5.0 -> ubuntu-aws-5.0
|
||||
# 5.15.0-1009-aws -> ubuntu-aws
|
||||
if [[ $KERNEL_RELEASE =~ -([a-zA-Z]+)(-.*)?$ ]];
|
||||
then
|
||||
TARGET_ID="ubuntu-${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
|
||||
if [[ $KERNEL_RELEASE == *"aws"* ]]; then
|
||||
TARGET_ID="ubuntu-aws"
|
||||
else
|
||||
TARGET_ID="ubuntu-generic"
|
||||
fi
|
||||
@@ -148,18 +139,6 @@ get_target_id() {
|
||||
KERNEL_RELEASE="${VERSION_ID}"
|
||||
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
|
||||
;;
|
||||
("minikube")
|
||||
TARGET_ID="${OS_ID}"
|
||||
# Extract the minikube version. Ex. With minikube version equal to "v1.26.0-1655407986-14197" the extracted version
|
||||
# will be "1.26.0"
|
||||
if [[ $(cat ${HOST_ROOT}/etc/VERSION) =~ ([0-9]+(\.[0-9]+){2}) ]]; then
|
||||
# kernel version for minikube is always in "1_minikubeversion" format. Ex "1_1.26.0".
|
||||
KERNEL_VERSION="1_${BASH_REMATCH[1]}"
|
||||
else
|
||||
echo "* Unable to extract minikube version from ${HOST_ROOT}/etc/VERSION"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
(*)
|
||||
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
|
||||
;;
|
||||
@@ -634,7 +613,7 @@ if [[ -z "$MAX_RMMOD_WAIT" ]]; then
|
||||
MAX_RMMOD_WAIT=60
|
||||
fi
|
||||
|
||||
DRIVER_VERSION=${DRIVER_VERSION:-"@DRIVER_VERSION@"}
|
||||
DRIVER_VERSION="@DRIVER_VERSION@"
|
||||
DRIVER_NAME=${DRIVER_NAME:-"@DRIVER_NAME@"}
|
||||
FALCO_VERSION="@FALCO_VERSION@"
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
After=falco_inject_kmod.service
|
||||
Requires=falco_inject_kmod.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStartPre=/sbin/modprobe falco
|
||||
ExecStart=/usr/bin/falco --pidfile=/var/run/falco.pid
|
||||
ExecStopPost=/sbin/rmmod falco
|
||||
UMask=0077
|
||||
@@ -18,7 +17,6 @@ NoNewPrivileges=yes
|
||||
ProtectHome=read-only
|
||||
ProtectSystem=full
|
||||
ProtectKernelTunables=true
|
||||
ReadWritePaths=/sys/module/falco
|
||||
RestrictRealtime=true
|
||||
RestrictAddressFamilies=~AF_PACKET
|
||||
StandardOutput=null
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=Falco: Container Native Runtime Security
|
||||
Documentation=https://falco.org/docs/
|
||||
Before=falco.service
|
||||
Wants=falco.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=root
|
||||
ExecStart=/sbin/modprobe falco
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -14,7 +14,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
set -e
|
||||
|
||||
mod_version="@DRIVER_VERSION@"
|
||||
dkms add -m falco -v $mod_version --rpm_safe_upgrade
|
||||
@@ -30,35 +29,3 @@ else
|
||||
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
|
||||
|
||||
# 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.service'
|
||||
|
||||
# post install mirrored from .deb
|
||||
if [ $1 -eq 1 ]; then
|
||||
# This will only remove masks created on package removal.
|
||||
/usr/bin/systemctl --system unmask 'falco.service' >/dev/null || true
|
||||
|
||||
# enable falco on installation
|
||||
# note: DEB postinstall script checks for changed symlinks
|
||||
/usr/bin/systemctl --system enable 'falco.service' >/dev/null || true
|
||||
|
||||
# start falco on installation
|
||||
/usr/bin/systemctl --system start 'falco.service' >/dev/null || true
|
||||
fi
|
||||
|
||||
# post upgrade mirrored from .deb
|
||||
if [ $1 -gt 1 ]; then
|
||||
if [ -d /run/systemd/system ]; then
|
||||
/usr/bin/systemctl --system daemon-reload >/dev/null || true
|
||||
|
||||
# restart falco on upgrade if service is already running
|
||||
/usr/bin/systemctl --system condrestart 'falco.service' >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -14,20 +14,3 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
# post uninstall mirrored from .deb
|
||||
if [ -d /run/systemd/system ] && [ "$1" = 0 ]; then
|
||||
/usr/bin/systemctl --system daemon-reload >/dev/null || true
|
||||
/usr/bin/systemctl --system mask 'falco.service' >/dev/null || true
|
||||
fi
|
||||
|
||||
# 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_postun_with_restart macro expands to
|
||||
# if package upgrade, not uninstall:
|
||||
# `systemd-update-helper mark-restart-system-units <service>`
|
||||
%systemd_postun_with_restart 'falco.service'
|
||||
|
||||
@@ -14,22 +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
|
||||
# stop falco service before uninstall
|
||||
/usr/bin/systemctl --system stop 'falco.service' >/dev/null || true
|
||||
fi
|
||||
|
||||
/usr/bin/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.service'
|
||||
|
||||
15
test/confs/plugins/multiple_source_plugins.yaml
Normal file
15
test/confs/plugins/multiple_source_plugins.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
stdout_output:
|
||||
enabled: true
|
||||
|
||||
plugins:
|
||||
- name: cloudtrail
|
||||
library_path: BUILD_DIR/cloudtrail-plugin-prefix/src/cloudtrail-plugin/libcloudtrail.so
|
||||
init_config: ""
|
||||
open_params: "BUILD_DIR/test/trace_files/plugins/alice_start_instances.json"
|
||||
- name: test_source
|
||||
library_path: BUILD_DIR/test/plugins/libtest_source.so
|
||||
init_config: ""
|
||||
open_params: ""
|
||||
|
||||
# Optional
|
||||
load_plugins: [cloudtrail, test_source]
|
||||
@@ -19,7 +19,6 @@ trace_files: !mux
|
||||
compat_engine_v4_create_disallowed_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -31,7 +30,6 @@ trace_files: !mux
|
||||
|
||||
compat_engine_v4_create_allowed_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -42,7 +40,6 @@ trace_files: !mux
|
||||
compat_engine_v4_create_privileged_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -53,7 +50,6 @@ trace_files: !mux
|
||||
|
||||
compat_engine_v4_create_privileged_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -64,7 +60,6 @@ trace_files: !mux
|
||||
|
||||
compat_engine_v4_create_unprivileged_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -74,7 +69,6 @@ trace_files: !mux
|
||||
compat_engine_v4_create_hostnetwork_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- ./rules/k8s_audit/engine_v4_k8s_audit_rules.yaml
|
||||
@@ -85,7 +79,6 @@ trace_files: !mux
|
||||
|
||||
compat_engine_v4_create_hostnetwork_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -97,7 +90,6 @@ trace_files: !mux
|
||||
user_outside_allowed_set:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -109,7 +101,6 @@ trace_files: !mux
|
||||
|
||||
user_in_allowed_set:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -122,7 +113,6 @@ trace_files: !mux
|
||||
create_disallowed_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -134,7 +124,6 @@ trace_files: !mux
|
||||
|
||||
create_allowed_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -145,7 +134,6 @@ trace_files: !mux
|
||||
create_privileged_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -157,7 +145,6 @@ trace_files: !mux
|
||||
create_privileged_no_secctx_1st_container_2nd_container_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -169,7 +156,6 @@ trace_files: !mux
|
||||
create_privileged_2nd_container_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -180,7 +166,6 @@ trace_files: !mux
|
||||
|
||||
create_privileged_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -190,7 +175,6 @@ trace_files: !mux
|
||||
|
||||
create_unprivileged_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -199,7 +183,6 @@ trace_files: !mux
|
||||
|
||||
create_unprivileged_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -210,7 +193,6 @@ trace_files: !mux
|
||||
create_sensitive_mount_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -222,7 +204,6 @@ trace_files: !mux
|
||||
create_sensitive_mount_2nd_container_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -233,7 +214,6 @@ trace_files: !mux
|
||||
|
||||
create_sensitive_mount_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -243,7 +223,6 @@ trace_files: !mux
|
||||
|
||||
create_unsensitive_mount_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -252,7 +231,6 @@ trace_files: !mux
|
||||
|
||||
create_unsensitive_mount_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -263,7 +241,6 @@ trace_files: !mux
|
||||
create_hostnetwork_pod:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -274,7 +251,6 @@ trace_files: !mux
|
||||
|
||||
create_hostnetwork_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -284,7 +260,6 @@ trace_files: !mux
|
||||
|
||||
create_nohostnetwork_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -293,7 +268,6 @@ trace_files: !mux
|
||||
|
||||
create_nohostnetwork_trusted_pod:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -304,7 +278,6 @@ trace_files: !mux
|
||||
create_nodeport_service:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -316,7 +289,6 @@ trace_files: !mux
|
||||
|
||||
create_nonodeport_service:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -327,7 +299,6 @@ trace_files: !mux
|
||||
create_configmap_private_creds:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -339,7 +310,6 @@ trace_files: !mux
|
||||
|
||||
create_configmap_no_private_creds:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -350,7 +320,6 @@ trace_files: !mux
|
||||
anonymous_user:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -362,7 +331,6 @@ trace_files: !mux
|
||||
pod_exec:
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -374,7 +342,6 @@ trace_files: !mux
|
||||
pod_attach:
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -386,7 +353,6 @@ trace_files: !mux
|
||||
namespace_outside_allowed_set:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -398,7 +364,6 @@ trace_files: !mux
|
||||
|
||||
namespace_in_allowed_set:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -410,7 +375,6 @@ trace_files: !mux
|
||||
create_pod_in_kube_system_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -422,7 +386,6 @@ trace_files: !mux
|
||||
create_pod_in_kube_public_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -434,7 +397,6 @@ trace_files: !mux
|
||||
create_serviceaccount_in_kube_system_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -446,7 +408,6 @@ trace_files: !mux
|
||||
create_serviceaccount_in_kube_public_namespace:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -458,7 +419,6 @@ trace_files: !mux
|
||||
system_clusterrole_deleted:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -470,7 +430,6 @@ trace_files: !mux
|
||||
system_clusterrole_modified:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -482,7 +441,6 @@ trace_files: !mux
|
||||
attach_cluster_admin_role:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -494,7 +452,6 @@ trace_files: !mux
|
||||
create_cluster_role_wildcard_resources:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -506,7 +463,6 @@ trace_files: !mux
|
||||
create_cluster_role_wildcard_verbs:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -518,7 +474,6 @@ trace_files: !mux
|
||||
create_writable_cluster_role:
|
||||
detect: True
|
||||
detect_level: NOTICE
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -530,7 +485,6 @@ trace_files: !mux
|
||||
create_pod_exec_cluster_role:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -542,7 +496,6 @@ trace_files: !mux
|
||||
create_deployment:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -554,7 +507,6 @@ trace_files: !mux
|
||||
delete_deployment:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -566,7 +518,6 @@ trace_files: !mux
|
||||
create_service:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -578,7 +529,6 @@ trace_files: !mux
|
||||
delete_service:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -590,7 +540,6 @@ trace_files: !mux
|
||||
create_configmap:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -602,7 +551,6 @@ trace_files: !mux
|
||||
delete_configmap:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -614,7 +562,6 @@ trace_files: !mux
|
||||
create_namespace:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -628,7 +575,6 @@ trace_files: !mux
|
||||
delete_namespace:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -640,7 +586,6 @@ trace_files: !mux
|
||||
create_serviceaccount:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -652,7 +597,6 @@ trace_files: !mux
|
||||
delete_serviceaccount:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -664,7 +608,6 @@ trace_files: !mux
|
||||
create_clusterrole:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -676,7 +619,6 @@ trace_files: !mux
|
||||
delete_clusterrole:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -688,7 +630,6 @@ trace_files: !mux
|
||||
create_clusterrolebinding:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -700,7 +641,6 @@ trace_files: !mux
|
||||
delete_clusterrolebinding:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -712,7 +652,6 @@ trace_files: !mux
|
||||
create_secret:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -725,7 +664,6 @@ trace_files: !mux
|
||||
create_service_account_token_secret:
|
||||
detect: False
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -735,7 +673,6 @@ trace_files: !mux
|
||||
create_kube_system_secret:
|
||||
detect: False
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -745,7 +682,6 @@ trace_files: !mux
|
||||
delete_secret:
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -756,7 +692,6 @@ trace_files: !mux
|
||||
|
||||
fal_01_003:
|
||||
detect: False
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ../rules/falco_rules.yaml
|
||||
- BUILD_DIR/k8saudit-rules-prefix/src/k8saudit-rules/k8s_audit_rules.yaml
|
||||
@@ -767,7 +702,6 @@ trace_files: !mux
|
||||
json_pointer_correct_parse:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
enable_source: k8s_audit
|
||||
rules_file:
|
||||
- ./rules/k8s_audit/single_rule_with_json_pointer.yaml
|
||||
detect_counts:
|
||||
|
||||
@@ -97,7 +97,6 @@ class FalcoTest(Test):
|
||||
self.all_events = self.params.get('all_events', '*', default=False)
|
||||
self.priority = self.params.get('priority', '*', default='debug')
|
||||
self.addl_cmdline_opts = self.params.get('addl_cmdline_opts', '*', default='')
|
||||
self.enable_source = self.params.get('enable_source', '*', default='')
|
||||
self.rules_file = self.params.get(
|
||||
'rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
|
||||
|
||||
@@ -115,13 +114,6 @@ class FalcoTest(Test):
|
||||
self.json_output = True
|
||||
if not isinstance(self.validate_rules_file, list):
|
||||
self.validate_rules_file = [self.validate_rules_file]
|
||||
|
||||
# can be either empty, a string, or a list
|
||||
if self.enable_source == '':
|
||||
self.enable_source = []
|
||||
else:
|
||||
if not isinstance(self.enable_source, list):
|
||||
self.enable_source = [self.enable_source]
|
||||
|
||||
self.rules_args = ""
|
||||
|
||||
@@ -240,7 +232,7 @@ class FalcoTest(Test):
|
||||
self.grpcurl_res = None
|
||||
self.grpc_observer = None
|
||||
self.grpc_address = self.params.get(
|
||||
'address', 'grpc/*', default='/run/falco/falco.sock')
|
||||
'address', 'grpc/*', default='/var/run/falco.sock')
|
||||
if self.grpc_address.startswith("unix://"):
|
||||
self.is_grpc_using_unix_socket = True
|
||||
self.grpc_address = self.grpc_address[len("unix://"):]
|
||||
@@ -638,15 +630,10 @@ class FalcoTest(Test):
|
||||
if self.trace_file:
|
||||
trace_arg = "-e {}".format(self.trace_file)
|
||||
|
||||
extra_cmdline = ''
|
||||
for source in self.enable_source:
|
||||
extra_cmdline += ' --enable-source="{}"'.format(source)
|
||||
extra_cmdline += ' ' + self.addl_cmdline_opts
|
||||
|
||||
# Run falco
|
||||
cmd = '{} {} {} -c {} {} -o json_output={} -o json_include_output_property={} -o json_include_tags_property={} -o priority={} -v {}'.format(
|
||||
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output,
|
||||
self.json_include_output_property, self.json_include_tags_property, self.priority, extra_cmdline)
|
||||
self.json_include_output_property, self.json_include_tags_property, self.priority, self.addl_cmdline_opts)
|
||||
|
||||
for tag in self.disable_tags:
|
||||
cmd += ' -T {}'.format(tag)
|
||||
|
||||
@@ -285,7 +285,7 @@ trace_files: !mux
|
||||
invalid_not_yaml:
|
||||
exit_status: 1
|
||||
validate_errors:
|
||||
- item_type: rules content
|
||||
- item_type: file
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Rules content is not yaml"
|
||||
@@ -296,7 +296,7 @@ trace_files: !mux
|
||||
invalid_not_array:
|
||||
exit_status: 1
|
||||
validate_errors:
|
||||
- item_type: rules content
|
||||
- item_type: file
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Rules content is not yaml array of objects"
|
||||
@@ -307,7 +307,7 @@ trace_files: !mux
|
||||
invalid_array_item_not_object:
|
||||
exit_status: 1
|
||||
validate_errors:
|
||||
- item_type: rules content item
|
||||
- item_type: item
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_VALIDATE
|
||||
message: "Unexpected element type. Each element should be a yaml associative array."
|
||||
@@ -329,7 +329,7 @@ trace_files: !mux
|
||||
invalid_yaml_parse_error:
|
||||
exit_status: 1
|
||||
validate_errors:
|
||||
- item_type: rules content
|
||||
- item_type: file
|
||||
item_name: ""
|
||||
code: LOAD_ERR_YAML_PARSE
|
||||
message: "yaml-cpp: error at line 1, column 11: illegal map value"
|
||||
@@ -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
|
||||
@@ -1093,7 +1098,7 @@ trace_files: !mux
|
||||
- rules/catchall_order.yaml
|
||||
detect_counts:
|
||||
- open_dev_null: 1
|
||||
dev_null: 6
|
||||
dev_null: 0
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
validate_skip_unknown_noevt:
|
||||
|
||||
@@ -35,7 +35,6 @@ trace_files: !mux
|
||||
stdout_contains: "ct.id"
|
||||
|
||||
detect_create_instance:
|
||||
enable_source: aws_cloudtrail
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
@@ -45,7 +44,6 @@ trace_files: !mux
|
||||
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
|
||||
|
||||
detect_create_instance_bigevent:
|
||||
enable_source: aws_cloudtrail
|
||||
detect: True
|
||||
detect_level: INFO
|
||||
rules_file:
|
||||
@@ -54,9 +52,16 @@ trace_files: !mux
|
||||
- 'Cloudtrail Create Instance': 1
|
||||
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances_bigevent.yaml
|
||||
|
||||
multiple_source_plugins:
|
||||
exit_status: 1
|
||||
stderr_contains: "Can not load multiple plugins with event sourcing capability: 'cloudtrail' already loaded."
|
||||
conf_file: BUILD_DIR/test/confs/plugins/multiple_source_plugins.yaml
|
||||
rules_file:
|
||||
- rules/plugins/cloudtrail_create_instances.yaml
|
||||
|
||||
incompatible_extract_sources:
|
||||
exit_status: 1
|
||||
stderr_contains: "Plugin '.*' has field extraction capability but is not compatible with any known event source"
|
||||
stderr_contains: "Plugin '.*' has field extraction capability but is not compatible with any enabled event source"
|
||||
conf_file: BUILD_DIR/test/confs/plugins/incompatible_extract_sources.yaml
|
||||
rules_file:
|
||||
- rules/plugins/cloudtrail_create_instances.yaml
|
||||
|
||||
@@ -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
|
||||
@@ -123,10 +123,8 @@ traces: !mux
|
||||
# falco-event-generator.scap so the rule is still being tested.
|
||||
run-shell-untrusted:
|
||||
trace_file: traces-positive/run-shell-untrusted.scap
|
||||
detect: True
|
||||
detect: False
|
||||
detect_level: DEBUG
|
||||
detect_counts:
|
||||
- "Run shell untrusted": 1
|
||||
|
||||
system-binaries-network-activity:
|
||||
trace_file: traces-positive/system-binaries-network-activity.scap
|
||||
|
||||
@@ -19,7 +19,7 @@ limitations under the License.
|
||||
#include <string.h>
|
||||
#include <plugin_info.h>
|
||||
|
||||
static const char *pl_required_api_version = PLUGIN_API_VERSION_STR;
|
||||
static const char *pl_required_api_version = "1.0.0";
|
||||
static const char *pl_name_base = "test_extract";
|
||||
static char pl_name[1024];
|
||||
static const char *pl_desc = "Test Plugin For Regression Tests";
|
||||
|
||||
@@ -20,7 +20,7 @@ limitations under the License.
|
||||
|
||||
#include <plugin_info.h>
|
||||
|
||||
static const char *pl_required_api_version = PLUGIN_API_VERSION_STR;
|
||||
static const char *pl_required_api_version = "1.0.0";
|
||||
static uint32_t pl_id = 999;
|
||||
static const char *pl_name = "test_source";
|
||||
static const char *pl_desc = "Test Plugin For Regression Tests";
|
||||
|
||||
@@ -22,7 +22,6 @@ set(
|
||||
engine/test_filter_macro_resolver.cpp
|
||||
engine/test_filter_evttype_resolver.cpp
|
||||
engine/test_filter_warning_resolver.cpp
|
||||
engine/test_plugin_requirements.cpp
|
||||
falco/test_configuration.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -21,20 +21,20 @@ TEST_CASE("is_unix_scheme matches", "[utils]")
|
||||
{
|
||||
SECTION("rvalue")
|
||||
{
|
||||
bool res = falco::utils::network::is_unix_scheme("unix:///run/falco/falco.sock");
|
||||
bool res = falco::utils::network::is_unix_scheme("unix:///var/run/falco.sock");
|
||||
REQUIRE(res);
|
||||
}
|
||||
|
||||
SECTION("std::string")
|
||||
{
|
||||
std::string url("unix:///run/falco/falco.sock");
|
||||
std::string url("unix:///var/run/falco.sock");
|
||||
bool res = falco::utils::network::is_unix_scheme(url);
|
||||
REQUIRE(res);
|
||||
}
|
||||
|
||||
SECTION("char[]")
|
||||
{
|
||||
char url[] = "unix:///run/falco/falco.sock";
|
||||
char url[] = "unix:///var/run/falco.sock";
|
||||
bool res = falco::utils::network::is_unix_scheme(url);
|
||||
REQUIRE(res);
|
||||
}
|
||||
@@ -42,7 +42,7 @@ TEST_CASE("is_unix_scheme matches", "[utils]")
|
||||
|
||||
TEST_CASE("is_unix_scheme does not match", "[utils]")
|
||||
{
|
||||
bool res = falco::utils::network::is_unix_scheme("something:///run/falco/falco.sock");
|
||||
bool res = falco::utils::network::is_unix_scheme("something:///var/run/falco.sock");
|
||||
REQUIRE_FALSE(res);
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -1,269 +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 <memory>
|
||||
#include <catch.hpp>
|
||||
#include "falco_engine.h"
|
||||
|
||||
static void check_requirements(
|
||||
bool expect_success,
|
||||
const std::vector<falco_engine::plugin_version_requirement>& plugins,
|
||||
const std::string& ruleset_content)
|
||||
{
|
||||
std::string err;
|
||||
std::unique_ptr<falco_engine> e(new falco_engine());
|
||||
falco::load_result::rules_contents_t c = {{"test", ruleset_content}};
|
||||
|
||||
auto res = e->load_rules(c.begin()->second, c.begin()->first);
|
||||
if (!res->successful())
|
||||
{
|
||||
if (expect_success)
|
||||
{
|
||||
FAIL(res->as_string(false, c));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e->check_plugin_requirements(plugins, err))
|
||||
{
|
||||
if (expect_success)
|
||||
{
|
||||
FAIL(err);
|
||||
}
|
||||
}
|
||||
else if (!expect_success)
|
||||
{
|
||||
FAIL("unexpected successful plugin requirements check");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("check_plugin_requirements must accept", "[rule_loader]")
|
||||
{
|
||||
SECTION("no requirement")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.1.0"}}, "");
|
||||
}
|
||||
|
||||
SECTION("single plugin")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin newer version")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.2.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.1.0"}, {"json", "0.3.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin multiple versions")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit", "0.2.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin with alternatives")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit-other", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit-other", "0.5.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives with multiple versions")
|
||||
{
|
||||
check_requirements(true, {{"k8saudit-other", "0.7.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 1.0.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.7.0
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("check_plugin_requirements must reject", "[rule_loader]")
|
||||
{
|
||||
SECTION("no plugin loaded")
|
||||
{
|
||||
check_requirements(false, {}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin wrong name")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit2
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin wrong version")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin multiple versions")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.1.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.2.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin with alternatives")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("single plugin with overlapping alternatives")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit
|
||||
version: 0.4.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit-other", "0.5.0"}, {"json3", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.1.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
)");
|
||||
}
|
||||
|
||||
SECTION("multiple plugins with alternatives with multiple versions")
|
||||
{
|
||||
check_requirements(false, {{"k8saudit", "0.7.0"}, {"json2", "0.5.0"}}, R"(
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 0.4.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.4.0
|
||||
- name: json
|
||||
version: 0.3.0
|
||||
alternatives:
|
||||
- name: json2
|
||||
version: 0.1.0
|
||||
- required_plugin_versions:
|
||||
- name: k8saudit
|
||||
version: 1.0.0
|
||||
alternatives:
|
||||
- name: k8saudit-other
|
||||
version: 0.7.0
|
||||
)");
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -42,9 +41,7 @@ using namespace std;
|
||||
using namespace falco;
|
||||
|
||||
falco_engine::falco_engine(bool seed_rng)
|
||||
: m_syscall_source(NULL),
|
||||
m_syscall_source_idx(SIZE_MAX),
|
||||
m_next_ruleset_id(0),
|
||||
: m_next_ruleset_id(0),
|
||||
m_min_priority(falco_common::PRIORITY_DEBUG),
|
||||
m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
m_replace_container_info(false)
|
||||
@@ -60,7 +57,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();
|
||||
}
|
||||
@@ -70,7 +67,7 @@ uint32_t falco_engine::engine_version()
|
||||
return (uint32_t) FALCO_ENGINE_VERSION;
|
||||
}
|
||||
|
||||
const falco_source* falco_engine::find_source(const std::string& name) const
|
||||
falco_source* falco_engine::find_source(const std::string& name)
|
||||
{
|
||||
auto ret = m_sources.at(name);
|
||||
if(!ret)
|
||||
@@ -80,7 +77,7 @@ const falco_source* falco_engine::find_source(const std::string& name) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
const falco_source* falco_engine::find_source(std::size_t index) const
|
||||
falco_source* falco_engine::find_source(std::size_t index)
|
||||
{
|
||||
auto ret = m_sources.at(index);
|
||||
if(!ret)
|
||||
@@ -97,7 +94,7 @@ static std::string fieldclass_key(const gen_event_filter_factory::filter_fieldcl
|
||||
return fld_info.name + fld_info.shortdesc;
|
||||
}
|
||||
|
||||
void falco_engine::list_fields(std::string &source, bool verbose, bool names_only, bool markdown) const
|
||||
void falco_engine::list_fields(std::string &source, bool verbose, bool names_only, bool markdown)
|
||||
{
|
||||
// Maps from field class name + short desc to list of event
|
||||
// sources for which this field class can be used.
|
||||
@@ -105,14 +102,14 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
|
||||
|
||||
// Do a first pass to group together classes that are
|
||||
// applicable to multiple event sources.
|
||||
for(const auto &it : m_sources)
|
||||
for(auto &it : m_sources)
|
||||
{
|
||||
if(source != "" && source != it.name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for(const auto &fld_class : it.filter_factory->get_fields())
|
||||
for(auto &fld_class : it.filter_factory->get_fields())
|
||||
{
|
||||
fieldclass_event_sources[fieldclass_key(fld_class)].insert(it.name);
|
||||
}
|
||||
@@ -124,7 +121,7 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl
|
||||
|
||||
// In the second pass, actually print info, skipping duplicate
|
||||
// field classes and also printing info on supported sources.
|
||||
for(const auto &it : m_sources)
|
||||
for(auto &it : m_sources)
|
||||
{
|
||||
if(source != "" && source != it.name)
|
||||
{
|
||||
@@ -186,26 +183,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);
|
||||
}
|
||||
|
||||
if (cfg.res->successful())
|
||||
{
|
||||
m_rule_stats_manager.clear();
|
||||
for (const auto &r : m_rules)
|
||||
{
|
||||
m_rule_stats_manager.on_rule_loaded(r);
|
||||
}
|
||||
m_rule_loader.compile(cfg, m_rules);
|
||||
}
|
||||
|
||||
return std::move(cfg.res);
|
||||
@@ -248,7 +234,7 @@ void falco_engine::enable_rule(const string &substring, bool enabled, const stri
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
bool match_exact = false;
|
||||
|
||||
for(const auto &it : m_sources)
|
||||
for(auto &it : m_sources)
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
@@ -266,7 +252,7 @@ void falco_engine::enable_rule_exact(const string &rule_name, bool enabled, cons
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
bool match_exact = true;
|
||||
|
||||
for(const auto &it : m_sources)
|
||||
for(auto &it : m_sources)
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
@@ -283,7 +269,7 @@ void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, con
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
for(const auto &it : m_sources)
|
||||
for(auto &it : m_sources)
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
@@ -316,7 +302,7 @@ uint64_t falco_engine::num_rules_for_ruleset(const std::string &ruleset)
|
||||
{
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
uint64_t ret = 0;
|
||||
for (const auto &src : m_sources)
|
||||
for (auto &src : m_sources)
|
||||
{
|
||||
ret += src.ruleset->enabled_count(ruleset_id);
|
||||
}
|
||||
@@ -329,44 +315,28 @@ void falco_engine::evttypes_for_ruleset(std::string &source, std::set<uint16_t>
|
||||
}
|
||||
|
||||
std::shared_ptr<gen_event_formatter> falco_engine::create_formatter(const std::string &source,
|
||||
const std::string &output) const
|
||||
const std::string &output)
|
||||
{
|
||||
return find_source(source)->formatter_factory->create_formatter(output);
|
||||
}
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id)
|
||||
{
|
||||
// note: there are no thread-safety guarantees on the filter_ruleset::run()
|
||||
// method, but the thread-safety assumptions of falco_engine::process_event()
|
||||
// imply that concurrent invokers use different and non-switchable values of
|
||||
// source_idx, which means that at any time each filter_ruleset will only
|
||||
// be accessed by a single thread.
|
||||
|
||||
const falco_source *source;
|
||||
|
||||
if(source_idx == m_syscall_source_idx)
|
||||
{
|
||||
source = m_syscall_source;
|
||||
}
|
||||
else
|
||||
{
|
||||
source = find_source(source_idx);
|
||||
}
|
||||
|
||||
if(should_drop_evt() || !source || !source->ruleset->run(ev, source->m_rule, ruleset_id))
|
||||
falco_rule rule;
|
||||
if(should_drop_evt() || !find_source(source_idx)->ruleset->run(ev, rule, ruleset_id))
|
||||
{
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
|
||||
unique_ptr<struct rule_result> res(new rule_result());
|
||||
res->evt = ev;
|
||||
res->rule = source->m_rule.name;
|
||||
res->source = source->m_rule.source;
|
||||
res->format = source->m_rule.output;
|
||||
res->priority_num = source->m_rule.priority;
|
||||
res->tags = source->m_rule.tags;
|
||||
res->exception_fields = source->m_rule.exception_fields;
|
||||
m_rule_stats_manager.on_event(source->m_rule);
|
||||
res->rule = rule.name;
|
||||
res->source = rule.source;
|
||||
res->format = rule.output;
|
||||
res->priority_num = rule.priority;
|
||||
res->tags = rule.tags;
|
||||
res->exception_fields = rule.exception_fields;
|
||||
m_rule_stats_manager.on_event(rule);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -382,15 +352,7 @@ std::size_t falco_engine::add_source(const std::string &source,
|
||||
// evttype_index_ruleset is the default ruleset implementation
|
||||
std::shared_ptr<filter_ruleset_factory> ruleset_factory(
|
||||
new evttype_index_ruleset_factory(filter_factory));
|
||||
size_t idx = add_source(source, filter_factory, formatter_factory, ruleset_factory);
|
||||
|
||||
if(source == falco_common::syscall_source)
|
||||
{
|
||||
m_syscall_source_idx = idx;
|
||||
m_syscall_source = find_source(m_syscall_source_idx);
|
||||
}
|
||||
|
||||
return idx;
|
||||
return add_source(source, filter_factory, formatter_factory, ruleset_factory);
|
||||
}
|
||||
|
||||
std::size_t falco_engine::add_source(const std::string &source,
|
||||
@@ -407,7 +369,7 @@ std::size_t falco_engine::add_source(const std::string &source,
|
||||
return m_sources.insert(src, source);
|
||||
}
|
||||
|
||||
void falco_engine::describe_rule(string *rule) const
|
||||
void falco_engine::describe_rule(string *rule)
|
||||
{
|
||||
static const char* rule_fmt = "%-50s %s\n";
|
||||
fprintf(stdout, rule_fmt, "Rule", "Description");
|
||||
@@ -428,7 +390,7 @@ void falco_engine::describe_rule(string *rule) const
|
||||
}
|
||||
}
|
||||
|
||||
void falco_engine::print_stats() const
|
||||
void falco_engine::print_stats()
|
||||
{
|
||||
string out;
|
||||
m_rule_stats_manager.format(m_rules, out);
|
||||
@@ -436,7 +398,7 @@ void falco_engine::print_stats() const
|
||||
fprintf(stdout, "%s", out.c_str());
|
||||
}
|
||||
|
||||
bool falco_engine::is_source_valid(const std::string &source) const
|
||||
bool falco_engine::is_source_valid(const std::string &source)
|
||||
{
|
||||
return m_sources.at(source) != nullptr;
|
||||
}
|
||||
@@ -448,7 +410,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),
|
||||
@@ -478,68 +440,53 @@ void falco_engine::interpret_load_result(std::unique_ptr<load_result>& res,
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_plugin_requirement_alternatives(
|
||||
const std::vector<falco_engine::plugin_version_requirement>& plugins,
|
||||
const rule_loader::plugin_version_info::requirement_alternatives& alternatives,
|
||||
|
||||
bool falco_engine::check_plugin_requirements(
|
||||
const std::vector<plugin_version_requirement>& plugins,
|
||||
std::string& err)
|
||||
{
|
||||
for (const auto &req : alternatives)
|
||||
for (const auto &req : m_rule_loader.required_plugin_versions())
|
||||
{
|
||||
bool found = false;
|
||||
for (const auto &plugin : plugins)
|
||||
{
|
||||
if (req.name == plugin.name)
|
||||
if (req.first == plugin.name)
|
||||
{
|
||||
sinsp_version req_version(req.version);
|
||||
found = true;
|
||||
sinsp_version plugin_version(plugin.version);
|
||||
if(!plugin_version.m_valid)
|
||||
{
|
||||
err = "Plugin '" + plugin.name
|
||||
err = "Plugin '" + req.first
|
||||
+ "' has invalid version string '"
|
||||
+ plugin.version + "'";
|
||||
return false;
|
||||
}
|
||||
if (!plugin_version.check(req_version))
|
||||
for (const auto &reqver: req.second)
|
||||
{
|
||||
err = "Plugin '" + plugin.name
|
||||
+ "' version '" + plugin.version
|
||||
+ "' is not compatible with required plugin version '"
|
||||
+ req.version + "'";
|
||||
return false;
|
||||
sinsp_version req_version(reqver);
|
||||
if (!plugin_version.check(req_version))
|
||||
{
|
||||
err = "Plugin '" + plugin.name
|
||||
+ "' version '" + plugin.version
|
||||
+ "' is not compatible with required plugin version '"
|
||||
+ reqver + "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool falco_engine::check_plugin_requirements(
|
||||
const std::vector<plugin_version_requirement>& plugins,
|
||||
std::string& err) const
|
||||
{
|
||||
err = "";
|
||||
for (const auto &alternatives : m_rule_collector.required_plugin_versions())
|
||||
{
|
||||
if (!check_plugin_requirement_alternatives(plugins, alternatives, err))
|
||||
if (!found)
|
||||
{
|
||||
if (err.empty())
|
||||
{
|
||||
for (const auto& req : alternatives)
|
||||
{
|
||||
err += err.empty() ? "" : ", ";
|
||||
err += req.name + " (>= " + req.version + ")";
|
||||
}
|
||||
err = "Plugin requirement not satisfied, must load one of: " + err;
|
||||
}
|
||||
err = "Plugin '" + req.first + "' is required but not loaded";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void falco_engine::complete_rule_loading() const
|
||||
void falco_engine::complete_rule_loading()
|
||||
{
|
||||
for (const auto &src : m_sources)
|
||||
for (auto &src : m_sources)
|
||||
{
|
||||
src.ruleset->on_loading_complete();
|
||||
}
|
||||
@@ -561,7 +508,7 @@ void falco_engine::set_extra(string &extra, bool replace_container_info)
|
||||
m_replace_container_info = replace_container_info;
|
||||
}
|
||||
|
||||
inline bool falco_engine::should_drop_evt() const
|
||||
inline bool falco_engine::should_drop_evt()
|
||||
{
|
||||
if(m_sampling_multiplier == 0)
|
||||
{
|
||||
|
||||
@@ -22,7 +22,6 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -32,7 +31,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"
|
||||
@@ -58,7 +56,7 @@ public:
|
||||
|
||||
// Print to stdout (using printf) a description of each field supported by this engine.
|
||||
// If source is non-empty, only fields for the provided source are printed.
|
||||
void list_fields(std::string &source, bool verbose, bool names_only, bool markdown) const;
|
||||
void list_fields(std::string &source, bool verbose, bool names_only, bool markdown);
|
||||
|
||||
//
|
||||
// Load rules either directly or from a filename.
|
||||
@@ -100,7 +98,7 @@ public:
|
||||
// Internally, this can be used to release unused resources before starting
|
||||
// processing events with process_event().
|
||||
//
|
||||
void complete_rule_loading() const;
|
||||
void complete_rule_loading();
|
||||
|
||||
// Only load rules having this priority or more severe.
|
||||
void set_min_priority(falco_common::priority_type priority);
|
||||
@@ -123,12 +121,12 @@ public:
|
||||
// Print details on the given rule. If rule is NULL, print
|
||||
// details on all rules.
|
||||
//
|
||||
void describe_rule(std::string *rule) const;
|
||||
void describe_rule(std::string *rule);
|
||||
|
||||
//
|
||||
// Print statistics on how many events matched each rule.
|
||||
//
|
||||
void print_stats() const;
|
||||
void print_stats();
|
||||
|
||||
//
|
||||
// Set the sampling ratio, which can affect which events are
|
||||
@@ -167,33 +165,18 @@ public:
|
||||
//
|
||||
// Given an event, check it against the set of rules in the
|
||||
// engine and if a matching rule is found, return details on
|
||||
// the rule that matched. If no rule matched, returns nullptr.
|
||||
// the rule that matched. If no rule matched, returns NULL.
|
||||
//
|
||||
// This method should be invoked only after having initialized and
|
||||
// configured the engine. In particular, invoking this with a source_idx
|
||||
// not previosly-returned by a call to add_source() would cause a
|
||||
// falco_exception to be thrown.
|
||||
// When ruleset_id is provided, use the enabled/disabled status
|
||||
// associated with the provided ruleset. This is only useful
|
||||
// when you have previously called enable_rule/enable_rule_by_tag
|
||||
// with a ruleset string.
|
||||
//
|
||||
// This method is thread-safe only with the assumption that every invoker
|
||||
// uses a different source_idx. Moreover, each invoker must not switch
|
||||
// source_idx in subsequent invocations of this method.
|
||||
// Considering that each invoker is related to a unique event source, it
|
||||
// is safe to assume that each invoker will pass a different event
|
||||
// to this method too, since two distinct sources cannot possibly produce
|
||||
// the same event. Lastly, filterchecks and formatters (and their factories)
|
||||
// that are used to populate the conditions for a given event-source
|
||||
// ruleset must not be reused across rulesets of other event sources.
|
||||
// These assumptions guarantee thread-safety because internally the engine
|
||||
// is partitioned by event sources. However, each ruleset assigned to each
|
||||
// event source is not thread-safe of its own, so invoking this method
|
||||
// concurrently with the same source_idx would inherently cause data races
|
||||
// and lead to undefined behavior.
|
||||
// the returned rule_result is allocated and must be delete()d.
|
||||
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev, uint16_t ruleset_id);
|
||||
|
||||
//
|
||||
// Wrapper assuming the default ruleset.
|
||||
//
|
||||
// This inherits the same thread-safety guarantees.
|
||||
// Wrapper assuming the default ruleset
|
||||
//
|
||||
std::unique_ptr<rule_result> process_event(std::size_t source_idx, gen_event *ev);
|
||||
|
||||
@@ -217,7 +200,7 @@ public:
|
||||
|
||||
// Return whether or not there is a valid filter/formatter
|
||||
// factory for this source.
|
||||
bool is_source_valid(const std::string &source) const;
|
||||
bool is_source_valid(const std::string &source);
|
||||
|
||||
//
|
||||
// Given an event source and ruleset, fill in a bitset
|
||||
@@ -233,10 +216,10 @@ public:
|
||||
// event.
|
||||
//
|
||||
std::shared_ptr<gen_event_formatter> create_formatter(const std::string &source,
|
||||
const std::string &output) const;
|
||||
const std::string &output);
|
||||
|
||||
// The rule loader definition is aliased as it is exactly what we need
|
||||
typedef rule_loader::plugin_version_info::requirement plugin_version_requirement;
|
||||
typedef rule_loader::plugin_version_info plugin_version_requirement;
|
||||
|
||||
//
|
||||
// Returns true if the provided list of plugins satisfies all the
|
||||
@@ -247,7 +230,7 @@ public:
|
||||
//
|
||||
bool check_plugin_requirements(
|
||||
const std::vector<plugin_version_requirement>& plugins,
|
||||
std::string& err) const;
|
||||
std::string& err);
|
||||
|
||||
private:
|
||||
|
||||
@@ -263,23 +246,17 @@ private:
|
||||
|
||||
indexed_vector<falco_source> m_sources;
|
||||
|
||||
const falco_source* find_source(std::size_t index) const;
|
||||
const falco_source* find_source(const std::string& name) const;
|
||||
|
||||
// To allow the engine to be extremely fast for syscalls (can
|
||||
// be > 1M events/sec), we save the syscall source/source_idx
|
||||
// separately and check it explicitly in process_event()
|
||||
const falco_source* m_syscall_source;
|
||||
std::atomic<size_t> m_syscall_source_idx;
|
||||
falco_source* find_source(std::size_t index);
|
||||
falco_source* find_source(const std::string& name);
|
||||
|
||||
//
|
||||
// Determine whether the given event should be matched at all
|
||||
// against the set of rules, given the current sampling
|
||||
// ratio/multiplier.
|
||||
//
|
||||
inline bool should_drop_evt() const;
|
||||
inline bool should_drop_evt();
|
||||
|
||||
rule_loader::collector m_rule_collector;
|
||||
rule_loader m_rule_loader;
|
||||
indexed_vector<falco_rule> m_rules;
|
||||
stats_manager m_rule_stats_manager;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
// The version of rules/filter fields/etc supported by this Falco
|
||||
// engine.
|
||||
#define FALCO_ENGINE_VERSION (15)
|
||||
#define FALCO_ENGINE_VERSION (14)
|
||||
|
||||
// This is the result of running "falco --list -N | sha256sum" and
|
||||
// represents the fields supported by this version of Falco. It's used
|
||||
|
||||
@@ -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,34 +26,13 @@ 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;
|
||||
std::shared_ptr<gen_event_filter_factory> filter_factory;
|
||||
std::shared_ptr<gen_event_formatter_factory> formatter_factory;
|
||||
|
||||
// Used by the filter_ruleset interface. Filled in when a rule
|
||||
// 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,32 +17,31 @@ 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";
|
||||
}
|
||||
|
||||
|
||||
size_t falco_event_types::get_ppm_event_max()
|
||||
void filter_evttype_resolver::visitor::inversion(set<uint16_t>& types)
|
||||
{
|
||||
return PPM_EVENT_MAX;
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::inversion(falco_event_types& types)
|
||||
{
|
||||
falco_event_types all_types;
|
||||
set<uint16_t> all_types;
|
||||
evttypes("", all_types);
|
||||
if (types != all_types) // we don't invert the "all types" set
|
||||
{
|
||||
types = all_types.diff(types);
|
||||
set<uint16_t> diff = types;
|
||||
types.clear();
|
||||
set_difference(
|
||||
all_types.begin(), all_types.end(), diff.begin(), diff.end(),
|
||||
inserter(types, types.begin()));
|
||||
}
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::visitor::evttypes(const std::string& evtname, falco_event_types& out)
|
||||
void filter_evttype_resolver::visitor::evttypes(string evtname, set<uint16_t>& 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 +49,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);
|
||||
}
|
||||
@@ -59,38 +59,42 @@ void filter_evttype_resolver::visitor::evttypes(const std::string& evtname, falc
|
||||
|
||||
void filter_evttype_resolver::evttypes(
|
||||
ast::expr* filter,
|
||||
std::set<uint16_t>& out) const
|
||||
set<uint16_t>& out) const
|
||||
{
|
||||
visitor v;
|
||||
v.m_expect_value = false;
|
||||
v.m_last_node_evttypes.clear();
|
||||
filter->accept(&v);
|
||||
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;});
|
||||
out.insert(v.m_last_node_evttypes.begin(), v.m_last_node_evttypes.end());
|
||||
}
|
||||
|
||||
void filter_evttype_resolver::evttypes(
|
||||
shared_ptr<ast::expr> filter,
|
||||
std::set<uint16_t>& out) const
|
||||
set<uint16_t>& out) const
|
||||
{
|
||||
visitor v;
|
||||
v.m_expect_value = false;
|
||||
v.m_last_node_evttypes.clear();
|
||||
filter.get()->accept(&v);
|
||||
v.m_last_node_evttypes.for_each([&out](uint16_t val){out.insert(val); return true;} );
|
||||
out.insert(v.m_last_node_evttypes.begin(), v.m_last_node_evttypes.end());
|
||||
}
|
||||
|
||||
// "and" nodes evttypes are the intersection of the evttypes of their children.
|
||||
// we initialize the set with "all event types"
|
||||
void filter_evttype_resolver::visitor::visit(ast::and_expr* e)
|
||||
{
|
||||
falco_event_types types;
|
||||
set<uint16_t> types, inters;
|
||||
evttypes("", types);
|
||||
m_last_node_evttypes.clear();
|
||||
for (auto &c : e->children)
|
||||
{
|
||||
falco_event_types inters;
|
||||
inters.clear();
|
||||
c->accept(this);
|
||||
types = types.intersect(m_last_node_evttypes);
|
||||
set_intersection(
|
||||
types.begin(), types.end(),
|
||||
m_last_node_evttypes.begin(), m_last_node_evttypes.end(),
|
||||
inserter(inters, inters.begin()));
|
||||
types = inters;
|
||||
}
|
||||
m_last_node_evttypes = types;
|
||||
}
|
||||
@@ -98,12 +102,12 @@ void filter_evttype_resolver::visitor::visit(ast::and_expr* e)
|
||||
// "or" nodes evttypes are the union of the evttypes their children
|
||||
void filter_evttype_resolver::visitor::visit(ast::or_expr* e)
|
||||
{
|
||||
falco_event_types types;
|
||||
set<uint16_t> types;
|
||||
m_last_node_evttypes.clear();
|
||||
for (auto &c : e->children)
|
||||
{
|
||||
c->accept(this);
|
||||
types.merge(m_last_node_evttypes);
|
||||
types.insert(m_last_node_evttypes.begin(), m_last_node_evttypes.end());
|
||||
}
|
||||
m_last_node_evttypes = types;
|
||||
}
|
||||
|
||||
@@ -20,128 +20,6 @@ limitations under the License.
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
class falco_event_types
|
||||
{
|
||||
private:
|
||||
using vec_t = std::vector<uint8_t>;
|
||||
vec_t m_types{};
|
||||
|
||||
static inline void check_range(uint16_t e)
|
||||
{
|
||||
static const auto enum_max = get_ppm_event_max();
|
||||
if(e > enum_max)
|
||||
{
|
||||
throw std::range_error("invalid event type");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
falco_event_types(falco_event_types&&) = default;
|
||||
falco_event_types(const falco_event_types&) = default;
|
||||
falco_event_types& operator=(falco_event_types&&) = default;
|
||||
falco_event_types& operator=(const falco_event_types&) = default;
|
||||
|
||||
static size_t get_ppm_event_max();
|
||||
|
||||
inline falco_event_types():
|
||||
m_types(get_ppm_event_max() + 1, 0)
|
||||
{
|
||||
}
|
||||
|
||||
inline void insert(uint16_t e)
|
||||
{
|
||||
check_range(e);
|
||||
m_types[e] = 1;
|
||||
}
|
||||
|
||||
void merge(const falco_event_types& other)
|
||||
{
|
||||
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
|
||||
{
|
||||
m_types[i] |= other.m_types[i];
|
||||
}
|
||||
}
|
||||
|
||||
void merge(const std::set<uint16_t>& other)
|
||||
{
|
||||
for(const auto& e : other)
|
||||
{
|
||||
insert(e);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool contains(uint16_t e) const
|
||||
{
|
||||
check_range(e);
|
||||
return m_types[e] != 0;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for(auto& v : m_types)
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool equals(const falco_event_types& other) const
|
||||
{
|
||||
return m_types == other.m_types;
|
||||
}
|
||||
|
||||
falco_event_types diff(const falco_event_types& other)
|
||||
{
|
||||
falco_event_types ret;
|
||||
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
|
||||
{
|
||||
if(m_types[i] == 1 && other.m_types[i] == 0)
|
||||
{
|
||||
ret.m_types[i] = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
falco_event_types intersect(const falco_event_types& other)
|
||||
{
|
||||
falco_event_types ret;
|
||||
for(size_t i = 0; i <= get_ppm_event_max(); ++i)
|
||||
{
|
||||
if(m_types[i] == 1 && other.m_types[i] == 1)
|
||||
{
|
||||
ret.m_types[i] = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void for_each(std::function<bool(uint16_t)> consumer) const
|
||||
{
|
||||
for(uint16_t i = 0; i < m_types.size(); ++i)
|
||||
{
|
||||
if(m_types[i] != 0)
|
||||
{
|
||||
if(!consumer(i))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const falco_event_types& lhs, const falco_event_types& rhs)
|
||||
{
|
||||
return lhs.equals(rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const falco_event_types& lhs, const falco_event_types& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Helper class for finding event types
|
||||
@@ -157,12 +35,9 @@ 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, std::set<uint16_t>& out) const
|
||||
{
|
||||
falco_event_types evt_types;
|
||||
visitor().evttypes(evtname, evt_types);
|
||||
evt_types.for_each([&out](uint16_t val)
|
||||
{out.insert(val); return true; });
|
||||
visitor().evttypes(evtname, out);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -188,14 +63,8 @@ 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;
|
||||
std::set<uint16_t> m_last_node_evttypes;
|
||||
|
||||
void visit(libsinsp::filter::ast::and_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::or_expr* e) override;
|
||||
@@ -204,7 +73,7 @@ private:
|
||||
void visit(libsinsp::filter::ast::list_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::unary_check_expr* e) override;
|
||||
void visit(libsinsp::filter::ast::binary_check_expr* e) override;
|
||||
void inversion(falco_event_types& types);
|
||||
void evttypes(const std::string& evtname, falco_event_types& out);
|
||||
void inversion(std::set<uint16_t>& types);
|
||||
void evttypes(std::string evtname, std::set<uint16_t>& out);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -61,12 +61,12 @@ void filter_macro_resolver::set_macro(
|
||||
m_macros[name] = macro;
|
||||
}
|
||||
|
||||
const unordered_set<string>& filter_macro_resolver::get_unknown_macros() const
|
||||
const set<string>& filter_macro_resolver::get_unknown_macros() const
|
||||
{
|
||||
return m_unknown_macros;
|
||||
}
|
||||
|
||||
const unordered_set<string>& filter_macro_resolver::get_resolved_macros() const
|
||||
const set<string>& filter_macro_resolver::get_resolved_macros() const
|
||||
{
|
||||
return m_resolved_macros;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ limitations under the License.
|
||||
|
||||
#include <filter/parser.h>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
/*!
|
||||
@@ -63,7 +63,7 @@ class filter_macro_resolver
|
||||
substituted during the last invocation of run(). Should be
|
||||
non-empty if the last invocation of run() returned true.
|
||||
*/
|
||||
const std::unordered_set<std::string>& get_resolved_macros() const;
|
||||
const std::set<std::string>& get_resolved_macros() const;
|
||||
|
||||
/*!
|
||||
\brief Returns a set containing the names of all the macros
|
||||
@@ -71,25 +71,19 @@ class filter_macro_resolver
|
||||
A macro remains unresolved if it is found inside the processed
|
||||
filter but it was not defined with set_macro();
|
||||
*/
|
||||
const std::unordered_set<std::string>& get_unknown_macros() const;
|
||||
const std::set<std::string>& get_unknown_macros() const;
|
||||
|
||||
private:
|
||||
typedef std::unordered_map<
|
||||
typedef std::map<
|
||||
std::string,
|
||||
std::shared_ptr<libsinsp::filter::ast::expr>
|
||||
> macro_defs;
|
||||
|
||||
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;
|
||||
std::set<std::string>* m_unknown_macros;
|
||||
std::set<std::string>* m_resolved_macros;
|
||||
macro_defs* m_macros;
|
||||
|
||||
void visit(libsinsp::filter::ast::and_expr* e) override;
|
||||
@@ -101,7 +95,7 @@ class filter_macro_resolver
|
||||
void visit(libsinsp::filter::ast::binary_check_expr* e) override;
|
||||
};
|
||||
|
||||
std::unordered_set<std::string> m_unknown_macros;
|
||||
std::unordered_set<std::string> m_resolved_macros;
|
||||
std::set<std::string> m_unknown_macros;
|
||||
std::set<std::string> m_resolved_macros;
|
||||
macro_defs m_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;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ limitations under the License.
|
||||
#include "falco_engine.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
falco_formats::falco_formats(std::shared_ptr<const falco_engine> engine,
|
||||
falco_formats::falco_formats(std::shared_ptr<falco_engine> engine,
|
||||
bool json_include_output_property,
|
||||
bool json_include_tags_property)
|
||||
: m_falco_engine(engine),
|
||||
@@ -35,7 +35,7 @@ falco_formats::~falco_formats()
|
||||
|
||||
string falco_formats::format_event(gen_event *evt, const std::string &rule, const std::string &source,
|
||||
const std::string &level, const std::string &format, std::set<std::string> &tags,
|
||||
const std::string &hostname) const
|
||||
const std::string &hostname)
|
||||
{
|
||||
string line;
|
||||
|
||||
@@ -132,7 +132,7 @@ string falco_formats::format_event(gen_event *evt, const std::string &rule, cons
|
||||
}
|
||||
|
||||
map<string, string> falco_formats::get_field_values(gen_event *evt, const std::string &source,
|
||||
const std::string &format) const
|
||||
const std::string &format)
|
||||
{
|
||||
std::shared_ptr<gen_event_formatter> formatter;
|
||||
|
||||
|
||||
@@ -24,20 +24,20 @@ limitations under the License.
|
||||
class falco_formats
|
||||
{
|
||||
public:
|
||||
falco_formats(std::shared_ptr<const falco_engine> engine,
|
||||
falco_formats(std::shared_ptr<falco_engine> engine,
|
||||
bool json_include_output_property,
|
||||
bool json_include_tags_property);
|
||||
virtual ~falco_formats();
|
||||
|
||||
std::string format_event(gen_event *evt, const std::string &rule, const std::string &source,
|
||||
const std::string &level, const std::string &format, std::set<std::string> &tags,
|
||||
const std::string &hostname) const;
|
||||
const std::string &hostname);
|
||||
|
||||
map<string, string> get_field_values(gen_event *evt, const std::string &source,
|
||||
const std::string &format) const ;
|
||||
const std::string &format);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<const falco_engine> m_falco_engine;
|
||||
std::shared_ptr<falco_engine> m_falco_engine;
|
||||
bool m_json_include_output_property;
|
||||
bool m_json_include_tags_property;
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
@@ -136,5 +131,5 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<T> m_entries;
|
||||
std::unordered_map<std::string, size_t> m_index;
|
||||
std::map<std::string, size_t> m_index;
|
||||
};
|
||||
|
||||
@@ -515,17 +515,16 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
|
||||
|
||||
size_t idx_len = 0;
|
||||
|
||||
for(const auto &info : get_info().m_fields)
|
||||
for(auto &info : m_info.m_fields)
|
||||
{
|
||||
auto iter = get_aliases().find(info.m_name);
|
||||
if( iter == get_aliases().end())
|
||||
if(m_aliases.find(info.m_name) == m_aliases.end())
|
||||
{
|
||||
throw falco_exception("Could not find alias for field name " + info.m_name);
|
||||
}
|
||||
|
||||
m_uses_paths = info.m_uses_paths;
|
||||
|
||||
auto &al = iter->second;
|
||||
auto &al = m_aliases[info.m_name];
|
||||
|
||||
// What follows the match must not be alphanumeric or a dot
|
||||
if(strncmp(info.m_name.c_str(), str, info.m_name.size()) == 0 &&
|
||||
@@ -693,6 +692,11 @@ size_t json_event_filter_check::parsed_size()
|
||||
}
|
||||
}
|
||||
|
||||
json_event_filter_check::check_info &json_event_filter_check::get_info()
|
||||
{
|
||||
return m_info;
|
||||
}
|
||||
|
||||
void json_event_filter_check::add_extracted_value(const std::string &str)
|
||||
{
|
||||
m_evalues.first.emplace_back(json_event_value(str));
|
||||
@@ -789,9 +793,9 @@ std::string jevt_filter_check::s_jevt_rawtime_field = "jevt.rawtime";
|
||||
std::string jevt_filter_check::s_jevt_value_field = "jevt.value";
|
||||
std::string jevt_filter_check::s_jevt_obj_field = "jevt.obj";
|
||||
|
||||
const jevt_filter_check::check_info &jevt_filter_check::get_info() const
|
||||
jevt_filter_check::jevt_filter_check()
|
||||
{
|
||||
static const check_info info = {"jevt",
|
||||
m_info = {"jevt",
|
||||
"generic ways to access json events",
|
||||
"",
|
||||
{{s_jevt_time_field, "json event timestamp as a string that includes the nanosecond part"},
|
||||
@@ -799,11 +803,6 @@ const jevt_filter_check::check_info &jevt_filter_check::get_info() const
|
||||
{s_jevt_rawtime_field, "absolute event timestamp, i.e. nanoseconds from epoch."},
|
||||
{s_jevt_value_field, "General way to access single property from json object. The syntax is [<json pointer expression>]. The property is returned as a string", IDX_REQUIRED, IDX_KEY},
|
||||
{s_jevt_obj_field, "The entire json object, stringified"}}};
|
||||
return info;
|
||||
}
|
||||
|
||||
jevt_filter_check::jevt_filter_check()
|
||||
{
|
||||
}
|
||||
|
||||
jevt_filter_check::~jevt_filter_check()
|
||||
@@ -1283,77 +1282,71 @@ bool k8s_audit_filter_check::extract_any_privileged(const json &j,
|
||||
return true;
|
||||
}
|
||||
|
||||
const json_event_filter_check::check_info &k8s_audit_filter_check::get_info() const
|
||||
k8s_audit_filter_check::k8s_audit_filter_check()
|
||||
{
|
||||
static const json_event_filter_check::check_info
|
||||
info = {"ka",
|
||||
"Access K8s Audit Log Events",
|
||||
"Fields with an IDX_ALLOWED annotation can be indexed (e.g. ka.req.containers.image[k] returns the image for the kth container). The index is optional--without any index the field returns values for all items. The index must be numeric with an IDX_NUMERIC annotation, and can be any string with an IDX_KEY annotation. Fields with an IDX_REQUIRED annotation require an index.",
|
||||
{{"ka.auditid", "The unique id of the audit event"},
|
||||
{"ka.stage", "Stage of the request (e.g. RequestReceived, ResponseComplete, etc.)"},
|
||||
{"ka.auth.decision", "The authorization decision"},
|
||||
{"ka.auth.reason", "The authorization reason"},
|
||||
{"ka.user.name", "The user name performing the request"},
|
||||
{"ka.user.groups", "The groups to which the user belongs"},
|
||||
{"ka.impuser.name", "The impersonated user name"},
|
||||
{"ka.verb", "The action being performed"},
|
||||
{"ka.uri", "The request URI as sent from client to server"},
|
||||
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
|
||||
{"ka.target.name", "The target object name"},
|
||||
{"ka.target.namespace", "The target object namespace"},
|
||||
{"ka.target.resource", "The target object resource"},
|
||||
{"ka.target.subresource", "The target object subresource"},
|
||||
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
|
||||
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
|
||||
{"ka.req.binding.subject.has_name", "Deprecated, always returns \"N/A\". Only provided for backwards compatibility", IDX_REQUIRED, IDX_KEY},
|
||||
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
|
||||
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
|
||||
{"ka.req.pod.containers.image", "When the request object refers to a pod, the container's images.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.image", "Deprecated by ka.req.pod.containers.image. Returns the image of the first container only"},
|
||||
{"ka.req.pod.containers.image.repository", "The same as req.container.image, but only the repository part (e.g. falcosecurity/falco).", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.image.repository", "Deprecated by ka.req.pod.containers.image.repository. Returns the repository of the first container only"},
|
||||
{"ka.req.pod.host_ipc", "When the request object refers to a pod, the value of the hostIPC flag."},
|
||||
{"ka.req.pod.host_network", "When the request object refers to a pod, the value of the hostNetwork flag."},
|
||||
{"ka.req.container.host_network", "Deprecated alias for ka.req.pod.host_network"},
|
||||
{"ka.req.pod.host_pid", "When the request object refers to a pod, the value of the hostPID flag."},
|
||||
{"ka.req.pod.containers.host_port", "When the request object refers to a pod, all container's hostPort values.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.privileged", "When the request object refers to a pod, the value of the privileged flag for all containers.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.privileged", "Deprecated by ka.req.pod.containers.privileged. Returns true if any container has privileged=true"},
|
||||
{"ka.req.pod.containers.allow_privilege_escalation", "When the request object refers to a pod, the value of the allowPrivilegeEscalation flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.read_only_fs", "When the request object refers to a pod, the value of the readOnlyRootFilesystem flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.run_as_user", "When the request object refers to a pod, the runAsUser uid specified in the security context for the pod. See ....containers.run_as_user for the runAsUser for individual containers"},
|
||||
{"ka.req.pod.containers.run_as_user", "When the request object refers to a pod, the runAsUser uid for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.eff_run_as_user", "When the request object refers to a pod, the initial uid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no uid is specified", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.run_as_group", "When the request object refers to a pod, the runAsGroup gid specified in the security context for the pod. See ....containers.run_as_group for the runAsGroup for individual containers"},
|
||||
{"ka.req.pod.containers.run_as_group", "When the request object refers to a pod, the runAsGroup gid for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.eff_run_as_group", "When the request object refers to a pod, the initial gid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no gid is specified", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.proc_mount", "When the request object refers to a pod, the procMount types for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
|
||||
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.fs_group", "When the request object refers to a pod, the fsGroup gid specified by the security context."},
|
||||
{"ka.req.pod.supplemental_groups", "When the request object refers to a pod, the supplementalGroup gids specified by the security context."},
|
||||
{"ka.req.pod.containers.add_capabilities", "When the request object refers to a pod, all capabilities to add when running the container.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.service.type", "When the request object refers to a service, the service type"},
|
||||
{"ka.req.service.ports", "When the request object refers to a service, the service's ports", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.volumes.hostpath", "When the request object refers to a pod, all hostPath paths specified for all volumes", IDX_ALLOWED, IDX_NUMERIC, true},
|
||||
{"ka.req.volume.hostpath", "Deprecated by ka.req.pod.volumes.hostpath. Return true if the provided (host) path prefix is used by any volume", IDX_ALLOWED, IDX_KEY},
|
||||
{"ka.req.pod.volumes.flexvolume_driver", "When the request object refers to a pod, all flexvolume drivers specified for all volumes", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.volumes.volume_type", "When the request object refers to a pod, all volume types for all volumes", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.resp.name", "The response object name"},
|
||||
{"ka.response.code", "The response code"},
|
||||
{"ka.response.reason", "The response reason (usually present only for failures)"},
|
||||
{"ka.useragent", "The useragent of the client who made the request to the apiserver"}}};
|
||||
return info;
|
||||
m_info = {"ka",
|
||||
"Access K8s Audit Log Events",
|
||||
"Fields with an IDX_ALLOWED annotation can be indexed (e.g. ka.req.containers.image[k] returns the image for the kth container). The index is optional--without any index the field returns values for all items. The index must be numeric with an IDX_NUMERIC annotation, and can be any string with an IDX_KEY annotation. Fields with an IDX_REQUIRED annotation require an index.",
|
||||
{{"ka.auditid", "The unique id of the audit event"},
|
||||
{"ka.stage", "Stage of the request (e.g. RequestReceived, ResponseComplete, etc.)"},
|
||||
{"ka.auth.decision", "The authorization decision"},
|
||||
{"ka.auth.reason", "The authorization reason"},
|
||||
{"ka.user.name", "The user name performing the request"},
|
||||
{"ka.user.groups", "The groups to which the user belongs"},
|
||||
{"ka.impuser.name", "The impersonated user name"},
|
||||
{"ka.verb", "The action being performed"},
|
||||
{"ka.uri", "The request URI as sent from client to server"},
|
||||
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
|
||||
{"ka.target.name", "The target object name"},
|
||||
{"ka.target.namespace", "The target object namespace"},
|
||||
{"ka.target.resource", "The target object resource"},
|
||||
{"ka.target.subresource", "The target object subresource"},
|
||||
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
|
||||
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
|
||||
{"ka.req.binding.subject.has_name", "Deprecated, always returns \"N/A\". Only provided for backwards compatibility", IDX_REQUIRED, IDX_KEY},
|
||||
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
|
||||
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
|
||||
{"ka.req.pod.containers.image", "When the request object refers to a pod, the container's images.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.image", "Deprecated by ka.req.pod.containers.image. Returns the image of the first container only"},
|
||||
{"ka.req.pod.containers.image.repository", "The same as req.container.image, but only the repository part (e.g. falcosecurity/falco).", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.image.repository", "Deprecated by ka.req.pod.containers.image.repository. Returns the repository of the first container only"},
|
||||
{"ka.req.pod.host_ipc", "When the request object refers to a pod, the value of the hostIPC flag."},
|
||||
{"ka.req.pod.host_network", "When the request object refers to a pod, the value of the hostNetwork flag."},
|
||||
{"ka.req.container.host_network", "Deprecated alias for ka.req.pod.host_network"},
|
||||
{"ka.req.pod.host_pid", "When the request object refers to a pod, the value of the hostPID flag."},
|
||||
{"ka.req.pod.containers.host_port", "When the request object refers to a pod, all container's hostPort values.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.privileged", "When the request object refers to a pod, the value of the privileged flag for all containers.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.container.privileged", "Deprecated by ka.req.pod.containers.privileged. Returns true if any container has privileged=true"},
|
||||
{"ka.req.pod.containers.allow_privilege_escalation", "When the request object refers to a pod, the value of the allowPrivilegeEscalation flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.read_only_fs", "When the request object refers to a pod, the value of the readOnlyRootFilesystem flag for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.run_as_user", "When the request object refers to a pod, the runAsUser uid specified in the security context for the pod. See ....containers.run_as_user for the runAsUser for individual containers"},
|
||||
{"ka.req.pod.containers.run_as_user", "When the request object refers to a pod, the runAsUser uid for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.eff_run_as_user", "When the request object refers to a pod, the initial uid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no uid is specified", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.run_as_group", "When the request object refers to a pod, the runAsGroup gid specified in the security context for the pod. See ....containers.run_as_group for the runAsGroup for individual containers"},
|
||||
{"ka.req.pod.containers.run_as_group", "When the request object refers to a pod, the runAsGroup gid for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.eff_run_as_group", "When the request object refers to a pod, the initial gid that will be used for all containers. This combines information from both the pod and container security contexts and uses 0 if no gid is specified", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.containers.proc_mount", "When the request object refers to a pod, the procMount types for all containers", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
|
||||
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.fs_group", "When the request object refers to a pod, the fsGroup gid specified by the security context."},
|
||||
{"ka.req.pod.supplemental_groups", "When the request object refers to a pod, the supplementalGroup gids specified by the security context."},
|
||||
{"ka.req.pod.containers.add_capabilities", "When the request object refers to a pod, all capabilities to add when running the container.", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.service.type", "When the request object refers to a service, the service type"},
|
||||
{"ka.req.service.ports", "When the request object refers to a service, the service's ports", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.volumes.hostpath", "When the request object refers to a pod, all hostPath paths specified for all volumes", IDX_ALLOWED, IDX_NUMERIC, true},
|
||||
{"ka.req.volume.hostpath", "Deprecated by ka.req.pod.volumes.hostpath. Return true if the provided (host) path prefix is used by any volume", IDX_ALLOWED, IDX_KEY},
|
||||
{"ka.req.pod.volumes.flexvolume_driver", "When the request object refers to a pod, all flexvolume drivers specified for all volumes", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.req.pod.volumes.volume_type", "When the request object refers to a pod, all volume types for all volumes", IDX_ALLOWED, IDX_NUMERIC},
|
||||
{"ka.resp.name", "The response object name"},
|
||||
{"ka.response.code", "The response code"},
|
||||
{"ka.response.reason", "The response reason (usually present only for failures)"},
|
||||
{"ka.useragent", "The useragent of the client who made the request to the apiserver"}}};
|
||||
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, k8s_audit_filter_check::alias> &k8s_audit_filter_check::get_aliases() const
|
||||
{
|
||||
static const std::unordered_map<std::string, k8s_audit_filter_check::alias>
|
||||
aliases = {
|
||||
{
|
||||
m_aliases = {
|
||||
{"ka.auditid", {{"/auditID"_json_pointer}}},
|
||||
{"ka.stage", {{"/stage"_json_pointer}}},
|
||||
{"ka.auth.decision", {{"/annotations/authorization.k8s.io~1decision"_json_pointer}}},
|
||||
@@ -1411,11 +1404,7 @@ const std::unordered_map<std::string, k8s_audit_filter_check::alias> &k8s_audit_
|
||||
{"ka.response.code", {{"/responseStatus/code"_json_pointer}}},
|
||||
{"ka.response.reason", {{"/responseStatus/reason"_json_pointer}}},
|
||||
{"ka.useragent", {{"/userAgent"_json_pointer}}}};
|
||||
return aliases;
|
||||
}
|
||||
|
||||
k8s_audit_filter_check::k8s_audit_filter_check()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
k8s_audit_filter_check::~k8s_audit_filter_check()
|
||||
@@ -1486,14 +1475,14 @@ std::list<gen_event_filter_factory::filter_fieldclass_info> json_event_filter_fa
|
||||
|
||||
for(auto &chk: m_defined_checks)
|
||||
{
|
||||
const json_event_filter_check::check_info &info = chk->get_info();
|
||||
json_event_filter_check::check_info &info = chk->get_info();
|
||||
gen_event_filter_factory::filter_fieldclass_info cinfo;
|
||||
|
||||
cinfo.name = info.m_name;
|
||||
cinfo.desc = info.m_desc;
|
||||
cinfo.shortdesc = info.m_shortdesc;
|
||||
|
||||
for(const auto &field : info.m_fields)
|
||||
for(auto &field : info.m_fields)
|
||||
{
|
||||
gen_event_filter_factory::filter_field_info info;
|
||||
info.name = field.m_name;
|
||||
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
};
|
||||
|
||||
json_event_filter_check();
|
||||
virtual ~json_event_filter_check() = 0;
|
||||
virtual ~json_event_filter_check();
|
||||
|
||||
virtual int32_t parse_field_name(const char *str, bool alloc_state, bool needed_for_filtering);
|
||||
void add_filter_value(const char *str, uint32_t len, uint32_t i = 0);
|
||||
@@ -197,7 +197,7 @@ public:
|
||||
// brackets (e.g. ka.image[foo])
|
||||
size_t parsed_size();
|
||||
|
||||
virtual const check_info &get_info() const = 0;
|
||||
check_info &get_info();
|
||||
|
||||
//
|
||||
// Allocate a new check of the same type. Must be overridden.
|
||||
@@ -260,9 +260,9 @@ protected:
|
||||
//
|
||||
// The version of parse_field_name in this base class will
|
||||
// check a field specification against all the aliases.
|
||||
virtual const std::unordered_map<std::string, alias> &get_aliases() const = 0;
|
||||
std::map<std::string, struct alias> m_aliases;
|
||||
|
||||
//check_info m_info;
|
||||
check_info m_info;
|
||||
|
||||
// The actual field name parsed in parse_field_name.
|
||||
std::string m_field;
|
||||
@@ -315,18 +315,11 @@ public:
|
||||
|
||||
int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) final;
|
||||
|
||||
json_event_filter_check *allocate_new() override;
|
||||
const check_info &get_info() const override;
|
||||
json_event_filter_check *allocate_new();
|
||||
|
||||
protected:
|
||||
|
||||
bool extract_values(json_event *jevt) final;
|
||||
const std::unordered_map<std::string, alias> &get_aliases() const override
|
||||
{
|
||||
static std::unordered_map<std::string, alias> a;
|
||||
return a;
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
|
||||
@@ -347,12 +340,9 @@ public:
|
||||
k8s_audit_filter_check();
|
||||
virtual ~k8s_audit_filter_check();
|
||||
|
||||
json_event_filter_check *allocate_new() override;
|
||||
json_event_filter_check *allocate_new();
|
||||
|
||||
const check_info &get_info() const override;
|
||||
const std::unordered_map<std::string, alias> &get_aliases() const override;
|
||||
|
||||
// Extract all images/image repositories from the provided containers
|
||||
// Extract all images/image repositories from the provided containers
|
||||
static bool extract_images(const nlohmann::json &j,
|
||||
json_event_filter_check &jchk);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,167 +16,68 @@ 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:
|
||||
// The kinds of items that can be in rules
|
||||
// content. These generally map to yaml items but a
|
||||
// few are more specific (e.g. "within condition
|
||||
// expression", "value for yaml node", etc.)
|
||||
enum item_type {
|
||||
VALUE_FOR = 0,
|
||||
EXCEPTIONS,
|
||||
EXCEPTION,
|
||||
EXCEPTION_VALUES,
|
||||
EXCEPTION_VALUE,
|
||||
RULES_CONTENT,
|
||||
RULES_CONTENT_ITEM,
|
||||
REQUIRED_ENGINE_VERSION,
|
||||
REQUIRED_PLUGIN_VERSIONS,
|
||||
REQUIRED_PLUGIN_VERSIONS_ENTRY,
|
||||
REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE,
|
||||
LIST,
|
||||
LIST_ITEM,
|
||||
MACRO,
|
||||
MACRO_CONDITION,
|
||||
RULE,
|
||||
RULE_CONDITION,
|
||||
CONDITION_EXPRESSION,
|
||||
RULE_OUTPUT,
|
||||
RULE_OUTPUT_EXPRESSION,
|
||||
RULE_PRIORITY
|
||||
};
|
||||
|
||||
static const std::string& item_type_as_string(enum item_type it);
|
||||
|
||||
static const size_t default_snippet_width = 160;
|
||||
|
||||
struct position
|
||||
{
|
||||
position() : pos(0), line(0), column(0) {};
|
||||
explicit position(const YAML::Mark& mark) : pos(mark.pos), line(mark.line), column(mark.column) {};
|
||||
~position() = default;
|
||||
position(position&&) = default;
|
||||
position& operator = (position&&) = default;
|
||||
position(const position&) = default;
|
||||
position& operator = (const position&) = default;
|
||||
|
||||
int pos;
|
||||
int line;
|
||||
int column;
|
||||
};
|
||||
|
||||
struct location
|
||||
{
|
||||
location(): item_type(context::item_type::VALUE_FOR) {}
|
||||
location(
|
||||
const std::string& n,
|
||||
const position& p,
|
||||
context::item_type i,
|
||||
const std::string& in):
|
||||
name(n), pos(p), item_type(i), item_name(in) {}
|
||||
location(location&&) = default;
|
||||
location& operator = (location&&) = default;
|
||||
location(const location&) = default;
|
||||
location& operator = (const location&) = default;
|
||||
|
||||
// A name for the content this location refers
|
||||
// to. Will generally be a filename, can also
|
||||
// refer to a rule/macro condition when the
|
||||
// location points into a condition string.
|
||||
std::string name;
|
||||
|
||||
// The original location in the document
|
||||
position pos;
|
||||
YAML::Mark mark;
|
||||
|
||||
// The kind of item at this location
|
||||
// (e.g. "list", "macro", "rule", "exception", etc)
|
||||
context::item_type item_type;
|
||||
std::string item_type;
|
||||
|
||||
// The name of this item (e.g. "Write Below Etc",
|
||||
// etc).
|
||||
std::string item_name;
|
||||
};
|
||||
|
||||
explicit context(const std::string& name);
|
||||
context(const std::string& name);
|
||||
context(const YAML::Node& item,
|
||||
item_type item_type,
|
||||
const std::string& item_name,
|
||||
const std::string item_type,
|
||||
const std::string item_name,
|
||||
const context& parent);
|
||||
|
||||
// Build a context from a condition expression +
|
||||
// parser position. This does not use the original
|
||||
// yaml content because:
|
||||
// - YAML block indicators will remove whitespace/newlines/wrapping
|
||||
// from the YAML node containing the condition expression.
|
||||
// - When compiling, the condition expression has expanded
|
||||
// macro and list references with their values.
|
||||
context(const libsinsp::filter::ast::pos_info& pos,
|
||||
const std::string& condition,
|
||||
const context& parent);
|
||||
|
||||
virtual ~context() = default;
|
||||
|
||||
context(context&&) = default;
|
||||
context& operator = (context&&) = default;
|
||||
context(const context&) = default;
|
||||
context& operator = (const context&) = default;
|
||||
|
||||
// Return the content name (generally filename) for
|
||||
// this context
|
||||
const std::string& name() const;
|
||||
|
||||
// Return a snippet of the provided rules content
|
||||
// corresponding to this context.
|
||||
// Uses the provided rules_contents to look up the original
|
||||
// rules content for a given location name.
|
||||
// (If this context has a non-empty alt_content, it
|
||||
// will be used to create the snippet, ignoring the
|
||||
// provided rules_contents).
|
||||
std::string snippet(const falco::load_result::rules_contents_t& rules_contents, size_t snippet_width = default_snippet_width) const;
|
||||
|
||||
std::string as_string();
|
||||
nlohmann::json as_json();
|
||||
|
||||
private:
|
||||
void init(const std::string& name,
|
||||
const position& pos,
|
||||
const item_type item_type,
|
||||
const std::string& item_name,
|
||||
const context& parent);
|
||||
std::string name;
|
||||
|
||||
// A chain of locations from the current item, its
|
||||
// parent, possibly older ancestors.
|
||||
std::vector<location> m_locs;
|
||||
|
||||
// If non-empty, this content will be used when
|
||||
// creating snippets. Used for contexts involving
|
||||
// condition expressions.
|
||||
std::string alt_content;
|
||||
};
|
||||
|
||||
struct warning
|
||||
{
|
||||
warning(): wc(falco::load_result::warning_code::LOAD_UNKNOWN_SOURCE), ctx("no-filename-given") {}
|
||||
warning(
|
||||
falco::load_result::warning_code w,
|
||||
const std::string& m,
|
||||
const context& c): wc(w), msg(m), ctx(c) {}
|
||||
warning(warning&&) = default;
|
||||
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 +85,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 +93,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 +112,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 +149,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 +172,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;
|
||||
@@ -310,35 +182,16 @@ namespace rule_loader
|
||||
*/
|
||||
struct plugin_version_info
|
||||
{
|
||||
struct requirement
|
||||
{
|
||||
requirement() = default;
|
||||
requirement(const std::string& n, const std::string& v):
|
||||
name(n), version(v) { }
|
||||
requirement(requirement&&) = default;
|
||||
requirement& operator = (requirement&&) = default;
|
||||
requirement(const requirement&) = default;
|
||||
requirement& operator = (const requirement&) = default;
|
||||
|
||||
std::string name;
|
||||
std::string version;
|
||||
};
|
||||
|
||||
typedef std::vector<requirement> requirement_alternatives;
|
||||
|
||||
// This differs from the other _info structs by having
|
||||
// a default constructor. This allows it to be used
|
||||
// by falco_engine, which aliases the type.
|
||||
plugin_version_info();
|
||||
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;
|
||||
std::string name;
|
||||
std::string version;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -348,10 +201,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 +217,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 +235,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 +243,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 +268,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 +286,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::map<std::string, std::set<std::string>> 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::map<std::string, std::set<std::string>> 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>
|
||||
@@ -36,7 +34,7 @@ static void decode_val_generic(const YAML::Node& item, const char *key, T& out,
|
||||
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
|
||||
THROW(val.IsNull(), std::string("Mapping for key '") + key + "' is empty", ctx);
|
||||
|
||||
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx);
|
||||
rule_loader::context valctx(val, "value for", key, ctx);
|
||||
THROW(!val.IsScalar(), "Value is not a scalar value", valctx);
|
||||
THROW(val.Scalar().empty(), "Value must be non-empty", valctx);
|
||||
|
||||
@@ -74,13 +72,13 @@ static void decode_seq(const YAML::Node& item, const char *key,
|
||||
|
||||
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
|
||||
|
||||
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, key, ctx);
|
||||
rule_loader::context valctx(val, "value for", key, ctx);
|
||||
THROW(!val.IsSequence(), "Value is not a sequence", valctx);
|
||||
|
||||
T value;
|
||||
for(const YAML::Node& v : val)
|
||||
{
|
||||
rule_loader::context ictx(v, rule_loader::context::LIST_ITEM, "", valctx);
|
||||
rule_loader::context ictx(v, "list item", "", valctx);
|
||||
THROW(!v.IsScalar(), "sequence value is not scalar", ictx);
|
||||
THROW(!YAML::convert<T>::decode(v, value), "Can't decode YAML sequence value", ictx);
|
||||
inserter(value);
|
||||
@@ -130,7 +128,7 @@ static void decode_exception_info_entry(
|
||||
|
||||
THROW(!val.IsDefined(), std::string("Item has no mapping for key '") + key + "'", ctx);
|
||||
|
||||
rule_loader::context valctx(val, rule_loader::context::VALUE_FOR, (key == NULL ? "" : key), ctx);
|
||||
rule_loader::context valctx(val, "value for", (key == NULL ? "" : key), ctx);
|
||||
|
||||
if (val.IsScalar())
|
||||
{
|
||||
@@ -141,10 +139,10 @@ static void decode_exception_info_entry(
|
||||
if (val.IsSequence())
|
||||
{
|
||||
out.is_list = true;
|
||||
rule_loader::rule_exception_info::entry tmp;
|
||||
for(const YAML::Node& v : val)
|
||||
{
|
||||
rule_loader::rule_exception_info::entry tmp;
|
||||
rule_loader::context lctx(v, rule_loader::context::EXCEPTION, "", valctx);
|
||||
rule_loader::context lctx(v, "list exception entry", "", valctx);
|
||||
|
||||
// Optional is always false once you get past the outer values
|
||||
optional = false;
|
||||
@@ -198,7 +196,7 @@ static void read_rule_exceptions(
|
||||
return;
|
||||
}
|
||||
|
||||
rule_loader::context exes_ctx(exs, rule_loader::context::EXCEPTIONS, "", parent);
|
||||
rule_loader::context exes_ctx(exs, "exceptions", "", parent);
|
||||
|
||||
THROW(!exs.IsSequence(), "Rule exceptions must be a sequence", exes_ctx);
|
||||
|
||||
@@ -207,13 +205,13 @@ static void read_rule_exceptions(
|
||||
// Make a temp context to verify simple properties
|
||||
// about the exception.
|
||||
std::string name;
|
||||
rule_loader::context tmp(ex, rule_loader::context::EXCEPTION, "", exes_ctx);
|
||||
rule_loader::context tmp(ex, "exception", "", exes_ctx);
|
||||
|
||||
THROW(!ex.IsMap(), "Rule exception must be a mapping", tmp);
|
||||
decode_val(ex, "name", name, tmp);
|
||||
|
||||
// Now use a real context including the exception name.
|
||||
rule_loader::context ex_ctx(ex, rule_loader::context::EXCEPTION, name, parent);
|
||||
rule_loader::context ex_ctx(ex, "exception", name, parent);
|
||||
rule_loader::rule_exception_info v_ex(ex_ctx);
|
||||
v_ex.name = name;
|
||||
|
||||
@@ -225,12 +223,12 @@ static void read_rule_exceptions(
|
||||
const YAML::Node& exvals = ex["values"];
|
||||
if (exvals.IsDefined())
|
||||
{
|
||||
rule_loader::context vals_ctx(exvals, rule_loader::context::EXCEPTION_VALUES, "", ex_ctx);
|
||||
rule_loader::context vals_ctx(exvals, "exception values", "", ex_ctx);
|
||||
THROW(!exvals.IsSequence(),
|
||||
"Rule exception values must be a sequence", vals_ctx);
|
||||
for (auto &val : exvals)
|
||||
{
|
||||
rule_loader::context vctx(val, rule_loader::context::EXCEPTION_VALUE, "", vals_ctx);
|
||||
rule_loader::context vctx(val, "exception value", "", vals_ctx);
|
||||
rule_loader::rule_exception_info::entry v_ex_val;
|
||||
|
||||
decode_exception_values(val, v_ex_val, vctx);
|
||||
@@ -243,26 +241,26 @@ 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)
|
||||
{
|
||||
rule_loader::context tmp(item, rule_loader::context::RULES_CONTENT_ITEM, "", parent);
|
||||
rule_loader::context tmp(item, "item", "", parent);
|
||||
THROW(!item.IsMap(), "Unexpected element type. "
|
||||
"Each element should be a yaml associative array.", tmp);
|
||||
|
||||
if (item["required_engine_version"].IsDefined())
|
||||
{
|
||||
rule_loader::context ctx(item, rule_loader::context::REQUIRED_ENGINE_VERSION, "", parent);
|
||||
rule_loader::context ctx(item, "required_engine_version", "", parent);
|
||||
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())
|
||||
{
|
||||
const YAML::Node& req_plugin_vers = item["required_plugin_versions"];
|
||||
rule_loader::context ctx(req_plugin_vers, rule_loader::context::REQUIRED_PLUGIN_VERSIONS, "", parent);
|
||||
rule_loader::context ctx(req_plugin_vers, "required_plugin_versions", "", parent);
|
||||
|
||||
THROW(!req_plugin_vers.IsSequence(),
|
||||
"Value of required_plugin_versions must be a sequence",
|
||||
@@ -270,45 +268,28 @@ static void read_item(
|
||||
|
||||
for(const YAML::Node& plugin : req_plugin_vers)
|
||||
{
|
||||
rule_loader::plugin_version_info::requirement r;
|
||||
|
||||
// Use a temp context until we can get a name
|
||||
rule_loader::context tmp(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, "", ctx);
|
||||
std::string name;
|
||||
rule_loader::context tmp(plugin, "plugin version", "", ctx);
|
||||
THROW(!plugin.IsMap(), "Plugin version must be a mapping", tmp);
|
||||
decode_val(plugin, "name", r.name, tmp);
|
||||
rule_loader::context pctx(plugin, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ENTRY, r.name, ctx);
|
||||
decode_val(plugin, "name", name, tmp);
|
||||
|
||||
rule_loader::context pctx(plugin, "plugin version", name, ctx);
|
||||
rule_loader::plugin_version_info v(pctx);
|
||||
decode_val(plugin, "version", r.version, pctx);
|
||||
v.alternatives.push_back(r);
|
||||
decode_val(plugin, "version", v.version, pctx);
|
||||
v.name = name;
|
||||
|
||||
const YAML::Node& alternatives = plugin["alternatives"];
|
||||
if(alternatives.IsDefined())
|
||||
{
|
||||
THROW(!alternatives.IsSequence(),
|
||||
"Value of plugin version alternatives must be a sequence",
|
||||
pctx);
|
||||
for (const auto &req : alternatives)
|
||||
{
|
||||
tmp = rule_loader::context(req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, "", pctx);
|
||||
THROW(!req.IsMap(), "Plugin version alternative must be a mapping", tmp);
|
||||
decode_val(req, "name", r.name, tmp);
|
||||
tmp = rule_loader::context(req, rule_loader::context::REQUIRED_PLUGIN_VERSIONS_ALTERNATIVE, r.name, pctx);
|
||||
decode_val(req, "version", r.version, tmp);
|
||||
v.alternatives.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
collector.define(cfg, v);
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["list"].IsDefined())
|
||||
{
|
||||
std::string name;
|
||||
// Using tmp context until name is decoded
|
||||
rule_loader::context tmp(item, rule_loader::context::LIST, "", parent);
|
||||
rule_loader::context tmp(item, "list", "", parent);
|
||||
decode_val(item, "list", name, tmp);
|
||||
|
||||
rule_loader::context ctx(item, rule_loader::context::LIST, name, parent);
|
||||
rule_loader::context ctx(item, "list", name, parent);
|
||||
rule_loader::list_info v(ctx);
|
||||
|
||||
bool append = false;
|
||||
@@ -319,21 +300,21 @@ 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())
|
||||
{
|
||||
std::string name;
|
||||
// Using tmp context until name is decoded
|
||||
rule_loader::context tmp(item, rule_loader::context::MACRO, "", parent);
|
||||
rule_loader::context tmp(item, "macro", "", parent);
|
||||
decode_val(item, "macro", name, tmp);
|
||||
|
||||
rule_loader::context ctx(item, rule_loader::context::MACRO, name, parent);
|
||||
rule_loader::context ctx(item, "macro", name, parent);
|
||||
rule_loader::macro_info v(ctx);
|
||||
v.name = name;
|
||||
|
||||
@@ -341,17 +322,17 @@ static void read_item(
|
||||
decode_val(item, "condition", v.cond, ctx);
|
||||
|
||||
// Now set the proper context for the condition now that we know it exists
|
||||
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::MACRO_CONDITION, "", ctx);
|
||||
v.cond_ctx = rule_loader::context(item["condition"], "condition", "", ctx);
|
||||
|
||||
decode_optional_val(item, "append", append, ctx);
|
||||
|
||||
if(append)
|
||||
{
|
||||
collector.append(cfg, v);
|
||||
loader.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.define(cfg, v);
|
||||
loader.define(cfg, v);
|
||||
}
|
||||
}
|
||||
else if(item["rule"].IsDefined())
|
||||
@@ -359,10 +340,10 @@ static void read_item(
|
||||
std::string name;
|
||||
|
||||
// Using tmp context until name is decoded
|
||||
rule_loader::context tmp(item, rule_loader::context::RULE, "", parent);
|
||||
rule_loader::context tmp(item, "rule", "", parent);
|
||||
decode_val(item, "rule", name, tmp);
|
||||
|
||||
rule_loader::context ctx(item, rule_loader::context::RULE, name, parent);
|
||||
rule_loader::context ctx(item, "rule", name, parent);
|
||||
rule_loader::rule_info v(ctx);
|
||||
v.name = name;
|
||||
|
||||
@@ -378,10 +359,10 @@ static void read_item(
|
||||
decode_optional_val(item, "condition", v.cond, ctx);
|
||||
if(item["condition"].IsDefined())
|
||||
{
|
||||
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx);
|
||||
v.cond_ctx = rule_loader::context(item["condition"], "condition", "", ctx);
|
||||
}
|
||||
read_rule_exceptions(item, v, ctx, append);
|
||||
collector.append(cfg, v);
|
||||
loader.append(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -396,7 +377,7 @@ static void read_item(
|
||||
!item["priority"].IsDefined())
|
||||
{
|
||||
decode_val(item, "enabled", v.enabled, ctx);
|
||||
collector.enable(cfg, v);
|
||||
loader.enable(cfg, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -404,17 +385,17 @@ static void read_item(
|
||||
|
||||
// All of these are required
|
||||
decode_val(item, "condition", v.cond, ctx);
|
||||
v.cond_ctx = rule_loader::context(item["condition"], rule_loader::context::RULE_CONDITION, "", ctx);
|
||||
v.cond_ctx = rule_loader::context(item["condition"], "condition", "", ctx);
|
||||
|
||||
decode_val(item, "output", v.output, ctx);
|
||||
v.output_ctx = rule_loader::context(item["output"], rule_loader::context::RULE_OUTPUT, "", ctx);
|
||||
v.output_ctx = rule_loader::context(item["output"], "output", "", ctx);
|
||||
|
||||
decode_val(item, "desc", v.desc, ctx);
|
||||
decode_val(item, "priority", priority, ctx);
|
||||
|
||||
v.output = trim(v.output);
|
||||
v.source = falco_common::syscall_source;
|
||||
rule_loader::context prictx(item["priority"], rule_loader::context::RULE_PRIORITY, "", ctx);
|
||||
rule_loader::context prictx(item["priority"], "priority value", "", ctx);
|
||||
THROW(!falco_common::parse_priority(priority, v.priority),
|
||||
"Invalid priority", prictx);
|
||||
decode_optional_val(item, "source", v.source, ctx);
|
||||
@@ -423,18 +404,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);
|
||||
rule_loader::context ctx(item, "unknown", "", parent);
|
||||
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 +425,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 +448,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
|
||||
@@ -45,48 +45,37 @@ void stats_manager::format(
|
||||
out += "Rule counts by severity:\n";
|
||||
for (size_t i = 0; i < m_by_priority.size(); i++)
|
||||
{
|
||||
auto val = m_by_priority[i].get()->load();
|
||||
if (val > 0)
|
||||
if (m_by_priority[i] > 0)
|
||||
{
|
||||
falco_common::format_priority(
|
||||
(falco_common::priority_type) i, fmt, true);
|
||||
transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
|
||||
out += " " + fmt + ": " + to_string(val) + "\n";
|
||||
out += " " + fmt;
|
||||
out += ": " + to_string(m_by_priority[i]) + "\n";
|
||||
}
|
||||
}
|
||||
out += "Triggered rules by rule name:\n";
|
||||
for (size_t i = 0; i < m_by_rule_id.size(); i++)
|
||||
{
|
||||
auto val = m_by_rule_id[i].get()->load();
|
||||
if (val > 0)
|
||||
if (m_by_rule_id[i] > 0)
|
||||
{
|
||||
out += " " + rules.at(i)->name + ": " + to_string(val) + "\n";
|
||||
out += " " + rules.at(i)->name;
|
||||
out += ": " + to_string(m_by_rule_id[i]) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stats_manager::on_rule_loaded(const falco_rule& rule)
|
||||
{
|
||||
while (m_by_rule_id.size() <= rule.id)
|
||||
{
|
||||
m_by_rule_id.emplace_back();
|
||||
m_by_rule_id[m_by_rule_id.size() - 1].reset(new atomic<uint64_t>(0));
|
||||
}
|
||||
while (m_by_priority.size() <= (size_t) rule.priority)
|
||||
{
|
||||
m_by_priority.emplace_back();
|
||||
m_by_priority[m_by_priority.size() - 1].reset(new atomic<uint64_t>(0));
|
||||
}
|
||||
}
|
||||
|
||||
void stats_manager::on_event(const falco_rule& rule)
|
||||
{
|
||||
if (m_by_rule_id.size() <= rule.id
|
||||
|| m_by_priority.size() <= (size_t) rule.priority)
|
||||
if (m_by_rule_id.size() <= rule.id)
|
||||
{
|
||||
throw falco_exception("rule id or priority out of bounds");
|
||||
m_by_rule_id.resize(rule.id + 1, (uint64_t) 0);
|
||||
}
|
||||
m_total.fetch_add(1, std::memory_order_relaxed);
|
||||
m_by_rule_id[rule.id]->fetch_add(1, std::memory_order_relaxed);
|
||||
m_by_priority[(size_t) rule.priority]->fetch_add(1, std::memory_order_relaxed);
|
||||
if (m_by_priority.size() <= (size_t) rule.priority)
|
||||
{
|
||||
m_by_priority.resize((size_t) rule.priority + 1, (uint64_t) 0);
|
||||
}
|
||||
m_total++;
|
||||
m_by_rule_id[rule.id]++;
|
||||
m_by_priority[(size_t) rule.priority]++;
|
||||
}
|
||||
|
||||
@@ -18,26 +18,17 @@ limitations under the License.
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include "falco_rule.h"
|
||||
#include "indexed_vector.h"
|
||||
|
||||
/*!
|
||||
\brief Manager for the internal statistics of the rule engine.
|
||||
The on_event() is thread-safe and non-blocking, and it can be used
|
||||
concurrently across many callers in parallel.
|
||||
All the other methods are not thread safe.
|
||||
\brief Manager for the internal statistics of the rule engine
|
||||
*/
|
||||
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
|
||||
@@ -45,29 +36,19 @@ public:
|
||||
virtual void clear();
|
||||
|
||||
/*!
|
||||
\brief Callback for when a new rule is loaded in the engine.
|
||||
Rules must be passed through this method before submitting them as
|
||||
an argument of on_event().
|
||||
*/
|
||||
virtual void on_rule_loaded(const falco_rule& rule);
|
||||
|
||||
/*!
|
||||
\brief Callback for when a given rule matches an event.
|
||||
This method is thread-safe.
|
||||
\throws falco_exception if rule has not been passed to
|
||||
on_rule_loaded() first
|
||||
\brief Callback for when a given rule matches an event
|
||||
*/
|
||||
virtual void on_event(const falco_rule& rule);
|
||||
|
||||
/*!
|
||||
\brief Formats the internal statistics into the out string.
|
||||
\brief Formats the internal statistics into the out string
|
||||
*/
|
||||
virtual void format(
|
||||
const indexed_vector<falco_rule>& rules,
|
||||
std::string& out) const;
|
||||
|
||||
private:
|
||||
atomic<uint64_t> m_total;
|
||||
std::vector<std::unique_ptr<atomic<uint64_t>>> m_by_priority;
|
||||
std::vector<std::unique_ptr<atomic<uint64_t>>> m_by_rule_id;
|
||||
uint64_t m_total;
|
||||
std::vector<uint64_t> m_by_priority;
|
||||
std::vector<uint64_t> m_by_rule_id;
|
||||
};
|
||||
@@ -20,7 +20,7 @@ set(
|
||||
app_actions/create_signal_handlers.cpp
|
||||
app_actions/daemonize.cpp
|
||||
app_actions/init_falco_engine.cpp
|
||||
app_actions/init_inspectors.cpp
|
||||
app_actions/init_inspector.cpp
|
||||
app_actions/init_clients.cpp
|
||||
app_actions/init_outputs.cpp
|
||||
app_actions/list_fields.cpp
|
||||
@@ -37,14 +37,9 @@ set(
|
||||
app_actions/print_support.cpp
|
||||
app_actions/print_syscall_events.cpp
|
||||
app_actions/print_version.cpp
|
||||
app_actions/print_page_size.cpp
|
||||
app_actions/compute_syscall_buffer_size.cpp
|
||||
app_actions/select_event_sources.cpp
|
||||
app_actions/start_grpc_server.cpp
|
||||
app_actions/start_webserver.cpp
|
||||
app_actions/validate_rules_files.cpp
|
||||
app_actions/create_requested_paths.cpp
|
||||
app_actions/configure_interesting_sets.cpp
|
||||
configuration.cpp
|
||||
logger.cpp
|
||||
falco_outputs.cpp
|
||||
@@ -53,7 +48,7 @@ set(
|
||||
outputs_stdout.cpp
|
||||
outputs_syslog.cpp
|
||||
event_drops.cpp
|
||||
stats_writer.cpp
|
||||
statsfilewriter.cpp
|
||||
falco.cpp
|
||||
)
|
||||
|
||||
@@ -67,10 +62,9 @@ set(
|
||||
"${YAMLCPP_INCLUDE_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include"
|
||||
)
|
||||
|
||||
list(APPEND FALCO_INCLUDE_DIRECTORIES "${FALCO_EXTRA_INCLUDE_DIRS}")
|
||||
|
||||
set(
|
||||
FALCO_DEPENDENCIES
|
||||
string-view-lite
|
||||
|
||||
@@ -1,71 +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 "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
/* These indexes could change over the Falco releases. */
|
||||
#define MIN_INDEX 1
|
||||
#define MAX_INDEX 10
|
||||
#define DEFAULT_BYTE_SIZE 1 << 23
|
||||
|
||||
application::run_result application::configure_syscall_buffer_size()
|
||||
{
|
||||
/* We don't need to compute the syscall buffer dimension if we are in capture mode or if the
|
||||
* the syscall source is not enabled.
|
||||
*/
|
||||
if(is_capture_mode() || m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end())
|
||||
{
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
/* Sizes from `1 MB` to `512 MB`. The index `0` is reserved, users cannot use it! */
|
||||
std::vector<uint32_t> vect{0, 1 << 20, 1 << 21, 1 << 22, DEFAULT_BYTE_SIZE, 1 << 24, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29};
|
||||
|
||||
uint64_t chosen_size = vect[index];
|
||||
|
||||
/* If the page size is not valid we return here. */
|
||||
long page_size = getpagesize();
|
||||
if(page_size <= 0)
|
||||
{
|
||||
m_state->syscall_buffer_bytes_size = DEFAULT_BYTE_SIZE;
|
||||
falco_logger::log(LOG_WARNING, "Unable to get the system page size through 'getpagesize()'. Try to use the default syscall buffer dimension: " + std::to_string(DEFAULT_BYTE_SIZE) + " bytes\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
m_state->syscall_buffer_bytes_size = chosen_size;
|
||||
falco_logger::log(LOG_INFO, "The chosen syscall buffer dimension is: " + std::to_string(chosen_size) + " bytes (" + std::to_string(chosen_size / (uint64_t)(1024 * 1024)) + " MBs)\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -1,61 +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 "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
void application::configure_interesting_sets()
|
||||
{
|
||||
/// TODO: in the next future we need to change the interface of `enforce_simple_ppm_sc_set`
|
||||
/// and `enforce_sinsp_state_tp` APIs, they shouldn't require an inspector to be called!
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
|
||||
/* Please note: here we fill these 2 sets because we are interested in only some features, if we leave
|
||||
* them empty `libsinsp` will fill them with all the available syscalls and all the available tracepoints!
|
||||
*/
|
||||
|
||||
/* Here the `libsinsp` state set is not enough, we need other syscalls used in the rules,
|
||||
* so we use the `simple_set`, this `simple_set` contains all the syscalls of the `libsinsp` state
|
||||
* plus syscalls for Falco default rules.
|
||||
*/
|
||||
m_state->ppm_sc_of_interest = inspector->enforce_simple_ppm_sc_set();
|
||||
m_state->ppm_event_info_of_interest = inspector->get_event_set_from_ppm_sc_set(m_state->ppm_sc_of_interest);
|
||||
|
||||
/* Fill-up the set of event infos of interest */
|
||||
for (uint32_t ev = 2; ev < PPM_EVENT_MAX; ev++)
|
||||
{
|
||||
if (!sinsp::is_old_version_event(ev)
|
||||
&& !sinsp::is_unused_event(ev)
|
||||
&& !sinsp::is_unknown_event(ev))
|
||||
{
|
||||
/* So far we only covered syscalls, so we add other kinds of
|
||||
interesting events. In this case, we are also interested in
|
||||
metaevents and in the procexit tracepoint event. */
|
||||
if (sinsp::is_metaevent(ev) || ev == PPME_PROCEXIT_1_E)
|
||||
{
|
||||
m_state->ppm_event_info_of_interest.insert(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In this case we get the tracepoints for the `libsinsp` state and we remove
|
||||
* the `sched_switch` tracepoint since it is highly noisy and not so useful
|
||||
* for our state/events enrichment.
|
||||
*/
|
||||
m_state->tp_of_interest = inspector->enforce_sinsp_state_tp();
|
||||
m_state->tp_of_interest.erase(SCHED_SWITCH);
|
||||
}
|
||||
@@ -1,111 +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 "application.h"
|
||||
#include "falco_utils.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef CPPPATH_SEP
|
||||
#ifdef _MSC_VER
|
||||
#define CPPPATH_SEP "\\"
|
||||
#else
|
||||
#define CPPPATH_SEP "/"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
application::run_result application::create_requested_paths()
|
||||
{
|
||||
if(!m_options.gvisor_config.empty())
|
||||
{
|
||||
// This is bad: parsing gvisor config to get endpoint
|
||||
// to be able to auto-create the path to the file for the user.
|
||||
std::ifstream reader(m_options.gvisor_config);
|
||||
if (reader.fail())
|
||||
{
|
||||
return run_result::fatal(m_options.gvisor_config + ": cannot open file");
|
||||
}
|
||||
|
||||
nlohmann::json parsed_json;
|
||||
std::string gvisor_socket;
|
||||
try
|
||||
{
|
||||
parsed_json = nlohmann::json::parse(reader);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
return run_result::fatal(m_options.gvisor_config + ": cannot parse JSON: " + e.what());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
gvisor_socket = parsed_json["trace_session"]["sinks"][0]["config"]["endpoint"];
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
return run_result::fatal(m_options.gvisor_config + ": failed to fetch config.endpoint: " + e.what());
|
||||
}
|
||||
|
||||
int ret = create_dir(gvisor_socket);
|
||||
if (ret != 0)
|
||||
{
|
||||
return run_result::fatal(gvisor_socket + ": " + strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_state->config->m_grpc_enabled && !m_state->config->m_grpc_bind_address.empty())
|
||||
{
|
||||
if(falco::utils::network::is_unix_scheme(m_state->config->m_grpc_bind_address))
|
||||
{
|
||||
auto server_path = m_state->config->m_grpc_bind_address.substr(
|
||||
falco::utils::network::UNIX_SCHEME.length()
|
||||
);
|
||||
int ret = create_dir(server_path);
|
||||
if(ret != 0)
|
||||
{
|
||||
return run_result::fatal(server_path + ": " + strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: eventually other files written by Falco whose destination is
|
||||
// customizable by users, must be handled here.
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
int application::create_dir(const std::string &path)
|
||||
{
|
||||
// Properly reset errno
|
||||
errno = 0;
|
||||
|
||||
istringstream f(path);
|
||||
string path_until_token;
|
||||
string s;
|
||||
// Create all the subfolder stopping at last token (f.eof());
|
||||
// Examples:
|
||||
// "/tmp/foo/bar" -> "", "tmp", "foo" -> mkdir("/") + mkdir("/tmp/") + midir("/tmp/foo/")
|
||||
// "tmp/foo/bar" -> "tmp", "foo" -> mkdir("tmp/") + midir("tmp/foo/")
|
||||
while (getline(f, s, *CPPPATH_SEP) && !f.eof()) {
|
||||
path_until_token += s + CPPPATH_SEP;
|
||||
int ret = mkdir(path_until_token.c_str(), 0600);
|
||||
if (ret != 0 && errno != EEXIST)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -30,24 +30,23 @@ 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);
|
||||
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);
|
||||
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);
|
||||
s_app.get().restart();
|
||||
}
|
||||
|
||||
bool application::create_handler(int sig, void (*func)(int), run_result &ret)
|
||||
@@ -72,52 +71,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 +116,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 +135,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 +149,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 +167,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
|
||||
|
||||
@@ -22,17 +22,15 @@ application::run_result application::init_clients()
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
// k8s and mesos clients are useful only if syscall source is enabled
|
||||
if (m_state->enabled_sources.find(falco_common::syscall_source) == m_state->enabled_sources.end())
|
||||
if (!is_syscall_source_enabled())
|
||||
{
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
auto inspector = m_state->source_infos.at(falco_common::syscall_source)->inspector;
|
||||
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download max size to " + to_string(m_state->config->m_metadata_download_max_mb) + " MB\n");
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download chunk wait time to " + to_string(m_state->config->m_metadata_download_chunk_wait_us) + " μs\n");
|
||||
falco_logger::log(LOG_DEBUG, "Setting metadata download watch frequency to " + to_string(m_state->config->m_metadata_download_watch_freq_sec) + " seconds\n");
|
||||
inspector->set_metadata_download_params(m_state->config->m_metadata_download_max_mb * 1024 * 1024, m_state->config->m_metadata_download_chunk_wait_us, m_state->config->m_metadata_download_watch_freq_sec);
|
||||
m_state->inspector->set_metadata_download_params(m_state->config->m_metadata_download_max_mb * 1024 * 1024, m_state->config->m_metadata_download_chunk_wait_us, m_state->config->m_metadata_download_watch_freq_sec);
|
||||
|
||||
//
|
||||
// Run k8s, if required
|
||||
@@ -55,7 +53,7 @@ application::run_result application::init_clients()
|
||||
*k8s_api_cert_ptr = k8s_cert_env;
|
||||
}
|
||||
}
|
||||
inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, m_options.verbose);
|
||||
m_state->inspector->init_k8s_client(k8s_api_ptr, k8s_api_cert_ptr, k8s_node_name_ptr, m_options.verbose);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -67,12 +65,12 @@ application::run_result application::init_clients()
|
||||
// passes a pointer but the inspector does
|
||||
// *not* own it and does not use it after
|
||||
// init_mesos_client() returns.
|
||||
inspector->init_mesos_client(&(m_options.mesos_api), m_options.verbose);
|
||||
m_state->inspector->init_mesos_client(&(m_options.mesos_api), m_options.verbose);
|
||||
}
|
||||
else if(char* mesos_api_env = getenv("FALCO_MESOS_API"))
|
||||
{
|
||||
std::string mesos_api_copy = mesos_api_env;
|
||||
inspector->init_mesos_client(&mesos_api_copy, m_options.verbose);
|
||||
m_state->inspector->init_mesos_client(&mesos_api_copy, m_options.verbose);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "application.h"
|
||||
#include <plugin_manager.h>
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
@@ -46,7 +47,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;
|
||||
}
|
||||
|
||||
@@ -56,80 +57,47 @@ void application::configure_output_format()
|
||||
}
|
||||
}
|
||||
|
||||
void application::add_source_to_engine(const std::string& src)
|
||||
{
|
||||
auto src_info = m_state->source_infos.at(src);
|
||||
std::shared_ptr<gen_event_filter_factory> filter_factory = nullptr;
|
||||
std::shared_ptr<gen_event_formatter_factory> formatter_factory = nullptr;
|
||||
|
||||
if (src == falco_common::syscall_source)
|
||||
{
|
||||
filter_factory = std::shared_ptr<gen_event_filter_factory>(
|
||||
new sinsp_filter_factory(src_info->inspector.get()));
|
||||
formatter_factory = std::shared_ptr<gen_event_formatter_factory>(
|
||||
new sinsp_evt_formatter_factory(src_info->inspector.get()));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &filterchecks = m_state->source_infos.at(src)->filterchecks;
|
||||
filter_factory = std::shared_ptr<gen_event_filter_factory>(
|
||||
new sinsp_filter_factory(src_info->inspector.get(), filterchecks));
|
||||
formatter_factory = std::shared_ptr<gen_event_formatter_factory>(
|
||||
new sinsp_evt_formatter_factory(src_info->inspector.get(), filterchecks));
|
||||
}
|
||||
|
||||
if(m_state->config->m_json_output)
|
||||
{
|
||||
formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
|
||||
}
|
||||
|
||||
src_info->engine_idx = m_state->engine->add_source(
|
||||
src, filter_factory, formatter_factory);
|
||||
}
|
||||
|
||||
application::run_result application::init_falco_engine()
|
||||
{
|
||||
// add all non-syscall event sources in engine
|
||||
for (const auto& src : m_state->loaded_sources)
|
||||
{
|
||||
if (src != falco_common::syscall_source)
|
||||
{
|
||||
// we skip the syscall as we want it to be the one added for last
|
||||
// in the engine. This makes the source index assignment easier.
|
||||
add_source_to_engine(src);
|
||||
}
|
||||
}
|
||||
|
||||
// add syscall as last source
|
||||
add_source_to_engine(falco_common::syscall_source);
|
||||
|
||||
// note: in capture mode, we can assume that the plugin source index will
|
||||
// be the same in both the falco engine and the sinsp plugin manager.
|
||||
// This assumption stands because the plugin manager stores sources in a
|
||||
// vector, and the syscall source is appended in the engine *after* the sources
|
||||
// coming from plugins. The reason why this can't work with live mode,
|
||||
// is because in that case event sources are scattered across different
|
||||
// inspectors. Since this is an implementation-based assumption, we
|
||||
// check this and return an error to spot regressions in the future.
|
||||
if (is_capture_mode())
|
||||
{
|
||||
auto manager = m_state->offline_inspector->get_plugin_manager();
|
||||
for (const auto &p : manager->plugins())
|
||||
{
|
||||
if (p->caps() & CAP_SOURCING)
|
||||
{
|
||||
bool added = false;
|
||||
auto source_idx = manager->source_idx_by_plugin_id(p->id(), added);
|
||||
auto engine_idx = m_state->source_infos.at(p->event_source())->engine_idx;
|
||||
if (!added || source_idx != engine_idx)
|
||||
{
|
||||
return run_result::fatal("Could not add event source in the engine: " + p->event_source());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure_output_format();
|
||||
|
||||
// Create "factories" that can create filters/formatters for syscalls
|
||||
|
||||
// libs requires raw pointer, we should modify libs to use reference/shared_ptr
|
||||
std::shared_ptr<gen_event_filter_factory> syscall_filter_factory(new sinsp_filter_factory(m_state->inspector.get()));
|
||||
|
||||
// libs requires raw pointer, we should modify libs to use reference/shared_ptr
|
||||
std::shared_ptr<gen_event_formatter_factory> syscall_formatter_factory(new sinsp_evt_formatter_factory(m_state->inspector.get()));
|
||||
|
||||
m_state->syscall_source_idx = m_state->engine->add_source(falco_common::syscall_source, syscall_filter_factory, syscall_formatter_factory);
|
||||
|
||||
if(m_state->config->m_json_output)
|
||||
{
|
||||
syscall_formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
|
||||
}
|
||||
|
||||
for(const auto &src : m_options.disable_sources)
|
||||
{
|
||||
if (m_state->enabled_sources.find(src) == m_state->enabled_sources.end())
|
||||
{
|
||||
return run_result::fatal("Attempted disabling unknown event source: " + src);
|
||||
}
|
||||
m_state->enabled_sources.erase(src);
|
||||
}
|
||||
|
||||
// todo(jasondellaluce,leogr): change this once we attain multiple active source
|
||||
if(m_state->enabled_sources.empty())
|
||||
{
|
||||
return run_result::fatal("At least one event source needs to be enabled");
|
||||
}
|
||||
|
||||
/* 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 sources: " + result + "\n");
|
||||
|
||||
m_state->engine->set_min_priority(m_state->config->m_min_priority);
|
||||
|
||||
return run_result::ok();
|
||||
|
||||
58
userspace/falco/app_actions/init_inspector.cpp
Normal file
58
userspace/falco/app_actions/init_inspector.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright (C) 2022 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
application::run_result application::init_inspector()
|
||||
{
|
||||
m_state->inspector->set_buffer_format(m_options.event_buffer_format);
|
||||
|
||||
// If required, set the CRI paths
|
||||
for (auto &p : m_options.cri_socket_paths)
|
||||
{
|
||||
if (!p.empty())
|
||||
{
|
||||
m_state->inspector->add_cri_socket_path(p);
|
||||
}
|
||||
}
|
||||
|
||||
// Decide whether to do sync or async for CRI metadata fetch
|
||||
m_state->inspector->set_cri_async(!m_options.disable_cri_async);
|
||||
|
||||
//
|
||||
// If required, set the snaplen
|
||||
//
|
||||
if(m_options.snaplen != 0)
|
||||
{
|
||||
m_state->inspector->set_snaplen(m_options.snaplen);
|
||||
}
|
||||
|
||||
if(!m_options.all_events)
|
||||
{
|
||||
// Drop EF_DROP_SIMPLE_CONS kernel side
|
||||
m_state->inspector->set_simple_consumer();
|
||||
// Eventually, drop any EF_DROP_SIMPLE_CONS event
|
||||
// that reached userspace (there are some events that are not syscall-based
|
||||
// like signaldeliver, that have the EF_DROP_SIMPLE_CONS flag)
|
||||
m_state->inspector->set_drop_event_flags(EF_DROP_SIMPLE_CONS);
|
||||
}
|
||||
|
||||
m_state->inspector->set_hostname_and_port_resolution_mode(false);
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -1,195 +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 "application.h"
|
||||
#include <unordered_set>
|
||||
#include <plugin_manager.h>
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
void application::init_syscall_inspector(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
const falco::app::cmdline_options& opts)
|
||||
{
|
||||
inspector->set_buffer_format(opts.event_buffer_format);
|
||||
|
||||
// If required, set the CRI paths
|
||||
for (auto &p : opts.cri_socket_paths)
|
||||
{
|
||||
if (!p.empty())
|
||||
{
|
||||
inspector->add_cri_socket_path(p);
|
||||
}
|
||||
}
|
||||
|
||||
// Decide whether to do sync or async for CRI metadata fetch
|
||||
inspector->set_cri_async(!opts.disable_cri_async);
|
||||
|
||||
//
|
||||
// If required, set the snaplen
|
||||
//
|
||||
if(opts.snaplen != 0)
|
||||
{
|
||||
inspector->set_snaplen(opts.snaplen);
|
||||
}
|
||||
|
||||
if(!opts.all_events)
|
||||
{
|
||||
configure_interesting_sets();
|
||||
}
|
||||
|
||||
inspector->set_hostname_and_port_resolution_mode(false);
|
||||
}
|
||||
|
||||
static bool populate_filterchecks(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
const std::string& source,
|
||||
filter_check_list& filterchecks,
|
||||
std::unordered_set<std::string>& used_plugins,
|
||||
std::string& err)
|
||||
{
|
||||
std::vector<const filter_check_info*> info;
|
||||
for(const auto& p : inspector->get_plugin_manager()->plugins())
|
||||
{
|
||||
if (!(p->caps() & CAP_EXTRACTION))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if some fields are overlapping on this event sources
|
||||
info.clear();
|
||||
filterchecks.get_all_fields(info);
|
||||
for (auto &info : info)
|
||||
{
|
||||
for (int32_t i = 0; i < info->m_nfields; i++)
|
||||
{
|
||||
// check if one of the fields extractable by the plugin
|
||||
// is already provided by another filtercheck for this source
|
||||
std::string fname = info->m_fields[i].m_name;
|
||||
for (auto &f : p->fields())
|
||||
{
|
||||
if (std::string(f.m_name) == fname)
|
||||
{
|
||||
err = "Plugin '" + p->name()
|
||||
+ "' supports extraction of field '" + fname
|
||||
+ "' that is overlapping for source '" + source + "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add plugin filterchecks to the event source
|
||||
filterchecks.add_filter_check(sinsp_plugin::new_filtercheck(p));
|
||||
used_plugins.insert(p->name());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
application::run_result application::init_inspectors()
|
||||
{
|
||||
std::string err;
|
||||
std::unordered_set<std::string> used_plugins;
|
||||
const auto& all_plugins = m_state->offline_inspector->get_plugin_manager()->plugins();
|
||||
|
||||
for (const auto &src : m_state->loaded_sources)
|
||||
{
|
||||
auto src_info = m_state->source_infos.at(src);
|
||||
|
||||
// in capture mode, every event source uses the offline inspector.
|
||||
// in live mode, we create a new inspector for each event source
|
||||
src_info->inspector = is_capture_mode()
|
||||
? m_state->offline_inspector
|
||||
: std::make_shared<sinsp>();
|
||||
|
||||
// handle syscall and plugin sources differently
|
||||
// todo(jasondellaluce): change this once we support extracting plugin fields from syscalls too
|
||||
if (src == falco_common::syscall_source)
|
||||
{
|
||||
init_syscall_inspector(src_info->inspector, m_options);
|
||||
continue;
|
||||
}
|
||||
|
||||
// load and init all plugins compatible with this event source
|
||||
// (if in capture mode, all plugins will be inited on the same inspector)
|
||||
for (const auto& p : all_plugins)
|
||||
{
|
||||
std::shared_ptr<sinsp_plugin> plugin = nullptr;
|
||||
auto config = m_state->plugin_configs.at(p->name());
|
||||
auto is_input = p->caps() & CAP_SOURCING && p->event_source() == src;
|
||||
|
||||
if (is_capture_mode())
|
||||
{
|
||||
// in capture mode, every plugin is already registered
|
||||
// in the offline inspector by the load_plugins action
|
||||
plugin = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
// in live mode, for the inspector assigned to the given
|
||||
// event source, we must register the plugin supporting
|
||||
// that event source and also plugins with field extraction
|
||||
// capability that are compatible with that event source
|
||||
if (is_input || (p->caps() & CAP_EXTRACTION && p->is_source_compatible(src)))
|
||||
{
|
||||
plugin = src_info->inspector->register_plugin(config->m_library_path);
|
||||
}
|
||||
}
|
||||
|
||||
// init the plugin, if we registered it into an inspector
|
||||
// (in capture mode, this is true for every plugin)
|
||||
if (plugin)
|
||||
{
|
||||
if (!plugin->init(config->m_init_config, err))
|
||||
{
|
||||
return run_result::fatal(err);
|
||||
}
|
||||
if (is_input)
|
||||
{
|
||||
auto gen_check = src_info->inspector->new_generic_filtercheck();
|
||||
src_info->filterchecks.add_filter_check(gen_check);
|
||||
}
|
||||
used_plugins.insert(plugin->name());
|
||||
}
|
||||
}
|
||||
|
||||
// populate filtercheck list for this inspector
|
||||
if (!populate_filterchecks(
|
||||
src_info->inspector,
|
||||
src,
|
||||
src_info->filterchecks,
|
||||
used_plugins,
|
||||
err))
|
||||
{
|
||||
return run_result::fatal(err);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// check if some plugin with field extraction capability remains unused
|
||||
for (const auto& p : all_plugins)
|
||||
{
|
||||
if(used_plugins.find(p->name()) == used_plugins.end()
|
||||
&& p->caps() & CAP_EXTRACTION
|
||||
&& !(p->caps() & CAP_SOURCING && p->is_source_compatible(p->event_source())))
|
||||
{
|
||||
return run_result::fatal("Plugin '" + p->name()
|
||||
+ "' has field extraction capability but is not compatible with any known event source");
|
||||
}
|
||||
}
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -27,34 +27,117 @@ application::run_result application::load_plugins()
|
||||
return run_result::fatal("Can not load/use plugins with musl optimized build");
|
||||
}
|
||||
#endif
|
||||
auto empty_src_info = state::source_info{};
|
||||
|
||||
// Initialize the set of loaded event sources.
|
||||
// By default, the set includes the 'syscall' event source
|
||||
m_state->source_infos.clear();
|
||||
m_state->source_infos.insert(empty_src_info, falco_common::syscall_source);
|
||||
m_state->loaded_sources = { falco_common::syscall_source };
|
||||
// The only enabled event source is syscall by default
|
||||
m_state->enabled_sources = {falco_common::syscall_source};
|
||||
|
||||
// Initialize map of plugin configs
|
||||
m_state->plugin_configs.clear();
|
||||
|
||||
// Initialize the offline inspector. This is used to load all the configured
|
||||
// plugins in order to have them available every time we need to access
|
||||
// their static info. If Falco is in capture mode, this inspector is also
|
||||
// used to open and read the trace file
|
||||
m_state->offline_inspector.reset(new sinsp());
|
||||
|
||||
// Load all the configured plugins
|
||||
std::string err = "";
|
||||
std::shared_ptr<sinsp_plugin> loaded_plugin = nullptr;
|
||||
for(auto &p : m_state->config->m_plugins)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Loading plugin '" + p.m_name + "' from file " + p.m_library_path + "\n");
|
||||
auto plugin = m_state->offline_inspector->register_plugin(p.m_library_path);
|
||||
m_state->plugin_configs.insert(p, plugin->name());
|
||||
falco_logger::log(LOG_INFO, "Loading plugin (" + p.m_name + ") from file " + p.m_library_path + "\n");
|
||||
auto plugin = m_state->inspector->register_plugin(p.m_library_path);
|
||||
if (!plugin->init(p.m_init_config, err))
|
||||
{
|
||||
return run_result::fatal(err);
|
||||
}
|
||||
|
||||
if(plugin->caps() & CAP_SOURCING)
|
||||
{
|
||||
auto sname = plugin->event_source();
|
||||
m_state->source_infos.insert(empty_src_info, sname);
|
||||
m_state->loaded_sources.insert(sname);
|
||||
if (!is_capture_mode())
|
||||
{
|
||||
// todo(jasondellaluce): change this once we support multiple enabled event sources
|
||||
if(loaded_plugin)
|
||||
{
|
||||
return run_result::fatal("Can not load multiple plugins with event sourcing capability: '"
|
||||
+ loaded_plugin->name()
|
||||
+ "' already loaded");
|
||||
}
|
||||
loaded_plugin = plugin;
|
||||
m_state->enabled_sources = {plugin->event_source()};
|
||||
m_state->inspector->set_input_plugin(p.m_name, p.m_open_params);
|
||||
}
|
||||
|
||||
// Init filtercheck list for the plugin's source and add the
|
||||
// event-generic filterchecks
|
||||
auto &filterchecks = m_state->plugin_filter_checks[plugin->event_source()];
|
||||
filterchecks.add_filter_check(m_state->inspector->new_generic_filtercheck());
|
||||
|
||||
// Factories that can create filters/formatters for the event source of the plugin.
|
||||
std::shared_ptr<gen_event_filter_factory> filter_factory(new sinsp_filter_factory(m_state->inspector.get(), filterchecks));
|
||||
std::shared_ptr<gen_event_formatter_factory> formatter_factory(new sinsp_evt_formatter_factory(m_state->inspector.get(), filterchecks));
|
||||
if(m_state->config->m_json_output)
|
||||
{
|
||||
formatter_factory->set_output_format(gen_event_formatter::OF_JSON);
|
||||
}
|
||||
|
||||
// note: here we assume that the source index will be the same in
|
||||
// both the falco engine and the sinsp plugin manager. This assumption
|
||||
// stands because the plugin manager stores sources in a vector, and
|
||||
// the syscall source is appended in the engine *after* the sources
|
||||
// coming from plugins. Since this is an implementation-based
|
||||
// assumption, we check this and return an error to spot
|
||||
// regressions in the future. We keep it like this for to avoid the
|
||||
// overhead of additional mappings at runtime, but we may consider
|
||||
// mapping the two indexes under something like std::unordered_map in the future.
|
||||
bool added = false;
|
||||
auto source_idx = m_state->inspector->get_plugin_manager()->source_idx_by_plugin_id(plugin->id(), added);
|
||||
auto source_idx_engine = m_state->engine->add_source(plugin->event_source(), filter_factory, formatter_factory);
|
||||
if (!added || source_idx != source_idx_engine)
|
||||
{
|
||||
return run_result::fatal("Could not add event source in the engine: " + plugin->event_source());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the plugins with extractor capability and add them to the
|
||||
// filtercheck list of their compatible sources
|
||||
std::vector<const filter_check_info*> filtercheck_info;
|
||||
for(const auto& p : m_state->inspector->get_plugin_manager()->plugins())
|
||||
{
|
||||
if (!(p->caps() & CAP_EXTRACTION))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool used = false;
|
||||
for (auto &it : m_state->plugin_filter_checks)
|
||||
{
|
||||
// check if the event source is compatible with this plugin
|
||||
if (p->is_source_compatible(it.first))
|
||||
{
|
||||
// check if some fields are overlapping on this event sources
|
||||
filtercheck_info.clear();
|
||||
it.second.get_all_fields(filtercheck_info);
|
||||
for (auto &info : filtercheck_info)
|
||||
{
|
||||
for (int32_t i = 0; i < info->m_nfields; i++)
|
||||
{
|
||||
// check if one of the fields extractable by the plugin
|
||||
// is already provided by another filtercheck for this source
|
||||
std::string fname = info->m_fields[i].m_name;
|
||||
for (auto &f : p->fields())
|
||||
{
|
||||
if (std::string(f.m_name) == fname)
|
||||
{
|
||||
return run_result::fatal(
|
||||
"Plugin '" + p->name()
|
||||
+ "' supports extraction of field '" + fname
|
||||
+ "' that is overlapping for source '" + it.first + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add plugin filterchecks to the event source
|
||||
it.second.add_filter_check(sinsp_plugin::new_filtercheck(p));
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
if (!used)
|
||||
{
|
||||
return run_result::fatal("Plugin '" + p->name()
|
||||
+ "' has field extraction capability but is not compatible with any enabled event source");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,57 +17,56 @@ limitations under the License.
|
||||
#include "application.h"
|
||||
#include <plugin_manager.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
bool application::check_rules_plugin_requirements(std::string& err)
|
||||
{
|
||||
// Ensure that all plugins are compatible with the loaded set of rules
|
||||
// note: offline inspector contains all the loaded plugins
|
||||
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
|
||||
for (const auto &plugin : m_state->offline_inspector->get_plugin_manager()->plugins())
|
||||
{
|
||||
falco_engine::plugin_version_requirement req;
|
||||
req.name = plugin->name();
|
||||
req.version = plugin->plugin_version().as_string();
|
||||
plugin_reqs.push_back(req);
|
||||
}
|
||||
return m_state->engine->check_plugin_requirements(plugin_reqs, err);
|
||||
}
|
||||
|
||||
void application::check_for_ignored_events()
|
||||
{
|
||||
/* Get the events from the rules. */
|
||||
std::set<uint16_t> rule_events;
|
||||
std::string source = falco_common::syscall_source;
|
||||
m_state->engine->evttypes_for_ruleset(source, rule_events);
|
||||
|
||||
/* Get the events we consider interesting from the application state `ppm_sc` codes. */
|
||||
std::set<uint16_t> evttypes;
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
std::unordered_set<uint32_t> events(rule_events.begin(), rule_events.end());
|
||||
sinsp_evttables* einfo = inspector->get_event_info_tables();
|
||||
const struct ppm_event_info* etable = einfo->m_event_info;
|
||||
|
||||
auto event_names = inspector->get_events_names(events);
|
||||
for (const auto& n : inspector->get_events_names(m_state->ppm_event_info_of_interest))
|
||||
std::string source = falco_common::syscall_source;
|
||||
m_state->engine->evttypes_for_ruleset(source, evttypes);
|
||||
|
||||
// Save event names so we don't warn for both the enter and exit event.
|
||||
std::set<std::string> warn_event_names;
|
||||
|
||||
for(auto evtnum : evttypes)
|
||||
{
|
||||
event_names.erase(n);
|
||||
if(evtnum == PPME_GENERIC_E || evtnum == PPME_GENERIC_X)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!sinsp::simple_consumer_consider_evtnum(evtnum))
|
||||
{
|
||||
std::string name = etable[evtnum].name;
|
||||
if(warn_event_names.find(name) == warn_event_names.end())
|
||||
{
|
||||
warn_event_names.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(event_names.empty())
|
||||
// Print a single warning with the list of ignored events
|
||||
if (!warn_event_names.empty())
|
||||
{
|
||||
return;
|
||||
std::string skipped_events;
|
||||
bool first = true;
|
||||
for (const auto& evtname : warn_event_names)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
skipped_events += evtname;
|
||||
first = false;
|
||||
} else
|
||||
{
|
||||
skipped_events += "," + evtname;
|
||||
}
|
||||
}
|
||||
fprintf(stderr,"Rules match ignored syscall: warning (ignored-evttype):\n loaded rules match the following events: %s;\n but these events are not returned unless running falco with -A\n", skipped_events.c_str());
|
||||
}
|
||||
|
||||
/* Get the names of the ignored events and print them. */
|
||||
std::cerr << "Rules match ignored syscall: warning (ignored-evttype):" << std::endl;
|
||||
std::cerr << "Loaded rules match the following events: ";
|
||||
bool first = true;
|
||||
for(const auto& it : event_names)
|
||||
{
|
||||
std::cerr << (first ? "" : ", ") << it.c_str();
|
||||
first = false;
|
||||
}
|
||||
std::cerr << std::endl << "But these events are not returned unless running falco with -A" << std::endl;
|
||||
}
|
||||
|
||||
application::run_result application::load_rules_files()
|
||||
@@ -129,10 +128,19 @@ 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
|
||||
std::string plugin_vers_err = "";
|
||||
std::vector<falco_engine::plugin_version_requirement> plugin_reqs;
|
||||
for (const auto &plugin : m_state->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)
|
||||
@@ -162,13 +170,11 @@ application::run_result application::load_rules_files()
|
||||
m_state->engine->enable_rule_by_tag(m_options.enabled_rule_tags, true);
|
||||
}
|
||||
|
||||
/* Reading a scap file we have no concepts of ignored events we read all we need. */
|
||||
if(!m_options.all_events && !is_capture_mode())
|
||||
if(!m_options.all_events)
|
||||
{
|
||||
/* Here we have already initialized the application state with the interesting syscalls,
|
||||
* so we have to check if any event types used by the loaded rules are not considered by
|
||||
* Falco interesting set.
|
||||
*/
|
||||
// For syscalls, see if any event types used by the
|
||||
// loaded rules are ones with the EF_DROP_SIMPLE_CONS
|
||||
// label.
|
||||
check_for_ignored_events();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,115 +18,94 @@ limitations under the License.
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <plugin_manager.h>
|
||||
|
||||
#include "application.h"
|
||||
|
||||
/* DEPRECATED: we will remove it in Falco 0.34. */
|
||||
#define FALCO_BPF_ENV_VARIABLE "FALCO_BPF_PROBE"
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
application::run_result application::open_offline_inspector()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_state->offline_inspector->open_savefile(m_options.trace_filename);
|
||||
falco_logger::log(LOG_INFO, "Reading system call events from file: " + m_options.trace_filename + "\n");
|
||||
return run_result::ok();
|
||||
}
|
||||
catch (sinsp_exception &e)
|
||||
{
|
||||
return run_result::fatal("Could not open trace filename " + m_options.trace_filename + " for reading: " + e.what());
|
||||
}
|
||||
}
|
||||
typedef std::function<void(std::shared_ptr<sinsp> inspector)> open_t;
|
||||
|
||||
application::run_result application::open_live_inspector(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
const std::string& source)
|
||||
application::run_result application::open_inspector()
|
||||
{
|
||||
try
|
||||
// Notify engine that we finished loading and enabling all rules
|
||||
m_state->engine->complete_rule_loading();
|
||||
|
||||
if(is_capture_mode())
|
||||
{
|
||||
if (source != falco_common::syscall_source) /* Plugin engine */
|
||||
// Try to open the trace file as a
|
||||
// capture file first.
|
||||
try {
|
||||
m_state->inspector->open(m_options.trace_filename);
|
||||
falco_logger::log(LOG_INFO, "Reading system call events from file: " + m_options.trace_filename + "\n");
|
||||
}
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
for (const auto& p: inspector->get_plugin_manager()->plugins())
|
||||
return run_result::fatal("Could not open trace filename " + m_options.trace_filename + " for reading: " + e.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_options.userspace)
|
||||
{
|
||||
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");
|
||||
inspector->open_plugin(cfg->m_name, cfg->m_open_params);
|
||||
return run_result::ok();
|
||||
}
|
||||
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
|
||||
//
|
||||
// Falco uses a ptrace(2) based userspace implementation.
|
||||
// Regardless of the implementation, the underlying method remains the same.
|
||||
m_state->inspector->open_udig();
|
||||
}
|
||||
return run_result::fatal("Can't open inspector for plugin event source: " + source);
|
||||
}
|
||||
else if (m_options.userspace) /* udig engine. */
|
||||
{
|
||||
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
|
||||
//
|
||||
// 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");
|
||||
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);
|
||||
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);
|
||||
char full_path[PATH_MAX];
|
||||
/* If the path is empty try to load the probe from the default path. */
|
||||
if(strncmp(bpf_probe_path, "", 1) == 0)
|
||||
else if(m_options.gvisor_config != "")
|
||||
{
|
||||
const char *home = std::getenv("HOME");
|
||||
if(!home)
|
||||
{
|
||||
return run_result::fatal("Cannot get the env variable 'HOME'");
|
||||
}
|
||||
snprintf(full_path, PATH_MAX, "%s/%s", home, FALCO_PROBE_BPF_FILEPATH);
|
||||
bpf_probe_path = full_path;
|
||||
falco_logger::log(LOG_INFO, "Enabled event collection from gVisor. Configuration path: " + m_options.gvisor_config);
|
||||
m_state->inspector->open_gvisor(m_options.gvisor_config, m_options.gvisor_root);
|
||||
}
|
||||
falco_logger::log(LOG_INFO, "Opening 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
|
||||
else
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Opening capture with Kernel module");
|
||||
inspector->open_kmod(m_state->syscall_buffer_bytes_size, m_state->ppm_sc_of_interest, m_state->tp_of_interest);
|
||||
m_state->inspector->open();
|
||||
}
|
||||
catch(sinsp_exception &e)
|
||||
}
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
// If syscall input source is enabled and not through userspace instrumentation
|
||||
if (is_syscall_source_enabled() && !m_options.userspace)
|
||||
{
|
||||
// Try to insert the Falco kernel module
|
||||
falco_logger::log(LOG_INFO, "Trying to inject the Kernel module and opening the capture again...");
|
||||
if(system("modprobe " DRIVER_NAME " > /dev/null 2> /dev/null"))
|
||||
{
|
||||
falco_logger::log(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);
|
||||
m_state->inspector->open();
|
||||
}
|
||||
else
|
||||
{
|
||||
return run_result::fatal(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (sinsp_exception &e)
|
||||
|
||||
/// TODO: we can add a method to the inspector that tells us what
|
||||
/// is the underline engine used. Right now we print something only
|
||||
/// in case of BPF engine
|
||||
if(m_state->inspector->is_bpf_enabled())
|
||||
{
|
||||
return run_result::fatal(e.what());
|
||||
falco_logger::log(LOG_INFO, "Falco is using the BPF probe\n");
|
||||
}
|
||||
|
||||
// This must be done after the open
|
||||
if (!m_options.all_events)
|
||||
if(!m_options.all_events)
|
||||
{
|
||||
inspector->start_dropping_mode(1);
|
||||
m_state->inspector->start_dropping_mode(1);
|
||||
}
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
bool application::close_inspector(std::string &errstr)
|
||||
{
|
||||
if(m_state->inspector != nullptr)
|
||||
{
|
||||
m_state->inspector->close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,51 +18,54 @@ limitations under the License.
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
/// TODO: probably in the next future would be more meaningful to print the ignored syscalls rather than
|
||||
/// the ignored events, or maybe change the name of the events since right now they are almost the same of
|
||||
/// the syscalls.
|
||||
application::run_result application::print_ignored_events()
|
||||
void application::print_all_ignored_events()
|
||||
{
|
||||
/* If the option is true we print the events ignored with Falco `-A`, otherwise
|
||||
* we return immediately.
|
||||
*/
|
||||
if(!m_options.print_ignored_events)
|
||||
{
|
||||
return run_result::ok();
|
||||
}
|
||||
std::unique_ptr<sinsp> inspector(new sinsp());
|
||||
sinsp_evttables* einfo = inspector->get_event_info_tables();
|
||||
const struct ppm_event_info* etable = einfo->m_event_info;
|
||||
const struct ppm_syscall_desc* stable = einfo->m_syscall_info_table;
|
||||
|
||||
/* Fill the application syscall and tracepoint sets.
|
||||
* The execution will be interrupted after this call so
|
||||
* we don't care if we populate these sets even if the `-A` flag
|
||||
* is not set.
|
||||
*/
|
||||
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::set<string> ignored_event_names;
|
||||
for(uint32_t j = 0; j < PPM_EVENT_MAX; j++)
|
||||
{
|
||||
if (!sinsp::is_old_version_event(j)
|
||||
&& !sinsp::is_unused_event(j)
|
||||
&& !sinsp::is_unknown_event(j))
|
||||
if(!sinsp::simple_consumer_consider_evtnum(j))
|
||||
{
|
||||
all_events.insert(j);
|
||||
std::string name = etable[j].name;
|
||||
// Ignore event names NA*
|
||||
if(name.find("NA") != 0)
|
||||
{
|
||||
ignored_event_names.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
for(uint32_t j = 0; j < PPM_SC_MAX; j++)
|
||||
{
|
||||
ignored_event_names.erase(n);
|
||||
if(!sinsp::simple_consumer_consider_syscallid(j))
|
||||
{
|
||||
std::string name = stable[j].name;
|
||||
// Ignore event names NA*
|
||||
if(name.find("NA") != 0)
|
||||
{
|
||||
ignored_event_names.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Ignored Event(s):" << std::endl;
|
||||
for(const auto& it : ignored_event_names)
|
||||
printf("Ignored Event(s):");
|
||||
for(auto it : ignored_event_names)
|
||||
{
|
||||
std::cout << "- " << it.c_str() << std::endl;
|
||||
printf(" %s", it.c_str());
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
return run_result::exit();
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
application::run_result application::print_ignored_events()
|
||||
{
|
||||
if(m_options.print_ignored_events)
|
||||
{
|
||||
print_all_ignored_events();
|
||||
return run_result::exit();
|
||||
}
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
@@ -1,37 +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 "application.h"
|
||||
|
||||
using namespace falco::app;
|
||||
|
||||
application::run_result application::print_page_size()
|
||||
{
|
||||
if(m_options.print_page_size)
|
||||
{
|
||||
long page_size = getpagesize();
|
||||
if(page_size <= 0)
|
||||
{
|
||||
return run_result::fatal("\nUnable to get the system page size through 'getpagesize()'\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "Your system page size is: " + std::to_string(page_size) + " bytes\n");
|
||||
}
|
||||
return run_result::exit();
|
||||
}
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -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,19 +20,16 @@ limitations under the License.
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <atomic>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "falco_utils.h"
|
||||
#include "event_drops.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
#include "webserver.h"
|
||||
#endif
|
||||
#include "stats_writer.h"
|
||||
#include "statsfilewriter.h"
|
||||
#include "application.h"
|
||||
#include "falco_outputs.h"
|
||||
#include "token_bucket.h"
|
||||
#include "app_cmdline_options.h"
|
||||
|
||||
#include <plugin_manager.h>
|
||||
|
||||
@@ -41,32 +38,19 @@ using namespace falco::app;
|
||||
//
|
||||
// Event processing loop
|
||||
//
|
||||
application::run_result application::do_inspect(
|
||||
std::shared_ptr<sinsp> inspector,
|
||||
const std::string& source, // an empty source represents capture mode
|
||||
std::shared_ptr<stats_writer> statsw,
|
||||
syscall_evt_drop_mgr &sdropmgr,
|
||||
bool check_drops_and_timeouts,
|
||||
uint64_t duration_to_tot_ns,
|
||||
uint64_t &num_evts)
|
||||
application::run_result application::do_inspect(syscall_evt_drop_mgr &sdropmgr,
|
||||
uint64_t duration_to_tot_ns,
|
||||
uint64_t &num_evts)
|
||||
{
|
||||
int32_t rc;
|
||||
sinsp_evt* ev;
|
||||
stats_writer::collector stats_collector(statsw);
|
||||
StatsFileWriter writer;
|
||||
uint64_t duration_start = 0;
|
||||
uint32_t timeouts_since_last_success_or_msg = 0;
|
||||
std::size_t source_idx;
|
||||
bool source_idx_found = false;
|
||||
token_bucket rate_limiter;
|
||||
bool rate_limiter_enabled = m_state->config->m_notifications_rate > 0;
|
||||
bool source_engine_idx_found = false;
|
||||
bool is_capture_mode = source.empty();
|
||||
bool syscall_source_engine_idx = m_state->source_infos.at(falco_common::syscall_source)->engine_idx;
|
||||
std::size_t source_engine_idx = 0;
|
||||
std::vector<std::string> source_names = inspector->get_plugin_manager()->sources();
|
||||
source_names.push_back(falco_common::syscall_source);
|
||||
if (!is_capture_mode)
|
||||
{
|
||||
source_engine_idx = m_state->source_infos.at(source)->engine_idx;
|
||||
}
|
||||
|
||||
// if enabled, init rate limiter
|
||||
if (rate_limiter_enabled)
|
||||
@@ -76,19 +60,24 @@ application::run_result application::do_inspect(
|
||||
m_state->config->m_notifications_max_burst);
|
||||
}
|
||||
|
||||
// reset event counter
|
||||
num_evts = 0;
|
||||
|
||||
// init drop manager if we are inspecting syscalls
|
||||
if (check_drops_and_timeouts)
|
||||
sdropmgr.init(m_state->inspector,
|
||||
m_state->outputs, // drop manager has its own rate limiting logic
|
||||
m_state->config->m_syscall_evt_drop_actions,
|
||||
m_state->config->m_syscall_evt_drop_threshold,
|
||||
m_state->config->m_syscall_evt_drop_rate,
|
||||
m_state->config->m_syscall_evt_drop_max_burst,
|
||||
m_state->config->m_syscall_evt_simulate_drops);
|
||||
|
||||
if (m_options.stats_filename != "")
|
||||
{
|
||||
sdropmgr.init(inspector,
|
||||
m_state->outputs, // drop manager has its own rate limiting logic
|
||||
m_state->config->m_syscall_evt_drop_actions,
|
||||
m_state->config->m_syscall_evt_drop_threshold,
|
||||
m_state->config->m_syscall_evt_drop_rate,
|
||||
m_state->config->m_syscall_evt_drop_max_burst,
|
||||
m_state->config->m_syscall_evt_simulate_drops);
|
||||
string errstr;
|
||||
|
||||
if (!writer.init(m_state->inspector, m_options.stats_filename, m_options.stats_interval, errstr))
|
||||
{
|
||||
return run_result::fatal(errstr);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -96,21 +85,26 @@ application::run_result application::do_inspect(
|
||||
//
|
||||
while(1)
|
||||
{
|
||||
rc = inspector->next(&ev);
|
||||
|
||||
if (should_reopen_outputs())
|
||||
rc = m_state->inspector->next(&ev);
|
||||
|
||||
writer.handle();
|
||||
|
||||
if(m_state->reopen_outputs)
|
||||
{
|
||||
reopen_outputs();
|
||||
falco_logger::log(LOG_INFO, "SIGUSR1 received, reopening outputs...\n");
|
||||
m_state->outputs->reopen_outputs();
|
||||
m_state->reopen_outputs = false;
|
||||
}
|
||||
|
||||
if(should_terminate())
|
||||
if(m_state->terminate)
|
||||
{
|
||||
terminate();
|
||||
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
|
||||
break;
|
||||
}
|
||||
else if(should_restart())
|
||||
else if (m_state->restart)
|
||||
{
|
||||
restart();
|
||||
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
|
||||
break;
|
||||
}
|
||||
else if(rc == SCAP_TIMEOUT)
|
||||
@@ -119,7 +113,8 @@ application::run_result application::do_inspect(
|
||||
{
|
||||
timeouts_since_last_success_or_msg++;
|
||||
if(timeouts_since_last_success_or_msg > m_state->config->m_syscall_evt_timeout_max_consecutives
|
||||
&& check_drops_and_timeouts)
|
||||
&& is_syscall_source_enabled()
|
||||
&& !is_gvisor_enabled())
|
||||
{
|
||||
std::string rule = "Falco internal: timeouts notification";
|
||||
std::string msg = rule + ". " + std::to_string(m_state->config->m_syscall_evt_timeout_max_consecutives) + " consecutive timeouts without event.";
|
||||
@@ -153,32 +148,7 @@ application::run_result application::do_inspect(
|
||||
//
|
||||
// Event read error.
|
||||
//
|
||||
return run_result::fatal(inspector->getlasterr());
|
||||
}
|
||||
|
||||
// if we are in live mode, we already have the right source engine idx
|
||||
if (is_capture_mode)
|
||||
{
|
||||
source_engine_idx = syscall_source_engine_idx;
|
||||
if (ev->get_type() == PPME_PLUGINEVENT_E)
|
||||
{
|
||||
// note: here we can assume that the source index will be the same
|
||||
// in both the falco engine and the sinsp plugin manager. See the
|
||||
// comment in init_falco_engine.cpp for more details.
|
||||
source_engine_idx = inspector->get_plugin_manager()->source_idx_by_plugin_id(*(int32_t *)ev->get_param(0)->m_val, source_engine_idx_found);
|
||||
if (!source_engine_idx_found)
|
||||
{
|
||||
return run_result::fatal("Unknown plugin ID in inspector: " + std::to_string(*(int32_t *)ev->get_param(0)->m_val));
|
||||
}
|
||||
}
|
||||
|
||||
// for capture mode, the source name can change at every event
|
||||
stats_collector.collect(inspector, source_names[source_engine_idx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// for live mode, the source name is constant
|
||||
stats_collector.collect(inspector, source);
|
||||
return run_result::fatal(m_state->inspector->getlasterr());
|
||||
}
|
||||
|
||||
// Reset the timeouts counter, Falco successfully got an event to process
|
||||
@@ -195,17 +165,35 @@ application::run_result application::do_inspect(
|
||||
}
|
||||
}
|
||||
|
||||
if(check_drops_and_timeouts && !sdropmgr.process_event(inspector, ev))
|
||||
if(!sdropmgr.process_event(m_state->inspector, ev))
|
||||
{
|
||||
return run_result::fatal("Drop manager internal error");
|
||||
}
|
||||
|
||||
if(!ev->simple_consumer_consider() && !m_options.all_events)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
source_idx = m_state->syscall_source_idx;
|
||||
if (ev->get_type() == PPME_PLUGINEVENT_E)
|
||||
{
|
||||
// note: here we can assume that the source index will be the same
|
||||
// in both the falco engine and the sinsp plugin manager. See the
|
||||
// comment in load_plugins.cpp for more details.
|
||||
source_idx = m_state->inspector->get_plugin_manager()->source_idx_by_plugin_id(*(int32_t *)ev->get_param(0)->m_val, source_idx_found);
|
||||
if (!source_idx_found)
|
||||
{
|
||||
return run_result::fatal("Unknown plugin ID in inspector: " + std::to_string(*(int32_t *)ev->get_param(0)->m_val));
|
||||
}
|
||||
}
|
||||
|
||||
// As the inspector has no filter at its level, all
|
||||
// events are returned here. Pass them to the falco
|
||||
// engine, which will match the event against the set
|
||||
// of rules. If a match is found, pass the event to
|
||||
// the outputs.
|
||||
unique_ptr<falco_engine::rule_result> res = m_state->engine->process_event(source_engine_idx, ev);
|
||||
unique_ptr<falco_engine::rule_result> res = m_state->engine->process_event(source_idx, ev);
|
||||
if(res)
|
||||
{
|
||||
if (!rate_limiter_enabled || rate_limiter.claim())
|
||||
@@ -224,233 +212,47 @@ application::run_result application::do_inspect(
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
double duration;
|
||||
scap_stats cstats;
|
||||
uint64_t num_evts = 0;
|
||||
syscall_evt_drop_mgr sdropmgr;
|
||||
bool is_capture_mode = source.empty();
|
||||
bool check_drops_timeouts = is_capture_mode
|
||||
|| (source == falco_common::syscall_source && !is_gvisor_enabled());
|
||||
|
||||
duration = ((double)clock()) / CLOCKS_PER_SEC;
|
||||
|
||||
*res = do_inspect(inspector, source, statsw, sdropmgr, check_drops_timeouts,
|
||||
uint64_t(m_options.duration_to_tot*ONE_SECOND_IN_NS),
|
||||
num_evts);
|
||||
|
||||
duration = ((double)clock()) / CLOCKS_PER_SEC - duration;
|
||||
|
||||
inspector->get_capture_stats(&cstats);
|
||||
|
||||
if(m_options.verbose)
|
||||
{
|
||||
if (source == falco_common::syscall_source)
|
||||
{
|
||||
fprintf(stderr, "Driver Events:%" PRIu64 "\nDriver Drops:%" PRIu64 "\n",
|
||||
cstats.n_evts,
|
||||
cstats.n_drops);
|
||||
}
|
||||
|
||||
fprintf(stderr, "%sElapsed time: %.3lf, Captured Events: %" PRIu64 ", %.2lf eps\n",
|
||||
(is_capture_mode ? "" : ("("+source+") ").c_str()),
|
||||
duration,
|
||||
num_evts,
|
||||
num_evts / duration);
|
||||
}
|
||||
|
||||
if (check_drops_timeouts)
|
||||
{
|
||||
sdropmgr.print_stats();
|
||||
}
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
*res = run_result::fatal(e.what());
|
||||
}
|
||||
|
||||
if (sync)
|
||||
{
|
||||
sync->finish();
|
||||
}
|
||||
}
|
||||
|
||||
static std::shared_ptr<stats_writer> init_stats_writer(const cmdline_options& opts)
|
||||
{
|
||||
auto statsw = std::make_shared<stats_writer>();
|
||||
if (!opts.stats_filename.empty())
|
||||
{
|
||||
std::string err;
|
||||
if (!stats_writer::init_ticker(opts.stats_interval, err))
|
||||
{
|
||||
throw falco_exception(err);
|
||||
}
|
||||
statsw.reset(new stats_writer(opts.stats_filename));
|
||||
}
|
||||
return statsw;
|
||||
}
|
||||
|
||||
application::run_result application::process_events()
|
||||
{
|
||||
application::run_result res = run_result::ok();
|
||||
bool termination_forced = false;
|
||||
syscall_evt_drop_mgr sdropmgr;
|
||||
// Used for stats
|
||||
double duration;
|
||||
scap_stats cstats;
|
||||
uint64_t num_evts = 0;
|
||||
run_result ret;
|
||||
|
||||
// Notify engine that we finished loading and enabling all rules
|
||||
m_state->engine->complete_rule_loading();
|
||||
duration = ((double)clock()) / CLOCKS_PER_SEC;
|
||||
|
||||
// Initialize stats writer
|
||||
auto statsw = init_stats_writer(m_options);
|
||||
ret = do_inspect(sdropmgr,
|
||||
uint64_t(m_options.duration_to_tot*ONE_SECOND_IN_NS),
|
||||
num_evts);
|
||||
|
||||
// Start processing events
|
||||
if(is_capture_mode())
|
||||
duration = ((double)clock()) / CLOCKS_PER_SEC - duration;
|
||||
|
||||
m_state->inspector->get_capture_stats(&cstats);
|
||||
|
||||
if(m_options.verbose)
|
||||
{
|
||||
res = open_offline_inspector();
|
||||
if (!res.success)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
fprintf(stderr, "Driver Events:%" PRIu64 "\nDriver Drops:%" PRIu64 "\n",
|
||||
cstats.n_evts,
|
||||
cstats.n_drops);
|
||||
|
||||
process_inspector_events(m_state->offline_inspector, statsw, "", nullptr, &res);
|
||||
m_state->offline_inspector->close();
|
||||
|
||||
// Honor -M also when using a trace file.
|
||||
// Since inspection stops as soon as all events have been consumed
|
||||
// just await the given duration is reached, if needed.
|
||||
if(m_options.duration_to_tot > 0)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds(m_options.duration_to_tot));
|
||||
}
|
||||
fprintf(stderr, "Elapsed time: %.3lf, Captured Events: %" PRIu64 ", %.2lf eps\n",
|
||||
duration,
|
||||
num_evts,
|
||||
num_evts / duration);
|
||||
}
|
||||
else
|
||||
|
||||
// Honor -M also when using a trace file.
|
||||
// Since inspection stops as soon as all events have been consumed
|
||||
// just await the given duration is reached, if needed.
|
||||
if(is_capture_mode() && m_options.duration_to_tot > 0)
|
||||
{
|
||||
struct live_context
|
||||
{
|
||||
live_context() = default;
|
||||
live_context(live_context&&) = default;
|
||||
live_context& operator = (live_context&&) = default;
|
||||
live_context(const live_context&) = default;
|
||||
live_context& operator = (const live_context&) = default;
|
||||
|
||||
// the name of the source of which events are processed
|
||||
std::string source;
|
||||
// the result of the event processing loop
|
||||
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();
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.thread.reset(new std::thread(
|
||||
&application::process_inspector_events,
|
||||
this, src_info->inspector, statsw, source, ctx.sync.get(), &ctx.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();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// wait for event processing to terminate for all sources
|
||||
// if a thread terminates with an error, we trigger the app termination
|
||||
// to force all other event streams to termiante too.
|
||||
// We accomulate the errors in a single run_result.
|
||||
size_t closed_count = 0;
|
||||
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)
|
||||
{
|
||||
falco_logger::log(LOG_INFO, "An error occurred in an event source, forcing termination...\n");
|
||||
terminate(false);
|
||||
termination_forced = true;
|
||||
}
|
||||
|
||||
for (auto &ctx : ctxs)
|
||||
{
|
||||
if (ctx.sync->finished() && !ctx.sync->joined())
|
||||
{
|
||||
if (ctx.thread)
|
||||
{
|
||||
if (!ctx.thread->joinable())
|
||||
{
|
||||
// thread has finished executing but
|
||||
// we already joined it, so we skip to the next one.
|
||||
// technically, we should never get here because
|
||||
// ctx.joined should already be true at this point
|
||||
continue;
|
||||
}
|
||||
ctx.thread->join();
|
||||
}
|
||||
|
||||
falco_logger::log(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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(m_options.duration_to_tot));
|
||||
}
|
||||
|
||||
m_state->engine->print_stats();
|
||||
sdropmgr.print_stats();
|
||||
|
||||
return res;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,75 +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 "application.h"
|
||||
|
||||
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;
|
||||
|
||||
// event sources selection is meaningless when reading trace files
|
||||
if (is_capture_mode())
|
||||
{
|
||||
return run_result::ok();
|
||||
}
|
||||
|
||||
if (!m_options.enable_sources.empty() && !m_options.disable_sources.empty())
|
||||
{
|
||||
return run_result::fatal("You can not mix --enable-source and --disable-source");
|
||||
}
|
||||
|
||||
if (!m_options.enable_sources.empty())
|
||||
{
|
||||
m_state->enabled_sources.clear();
|
||||
for(const auto &src : m_options.enable_sources)
|
||||
{
|
||||
if (m_state->loaded_sources.find(src) == m_state->loaded_sources.end())
|
||||
{
|
||||
return run_result::fatal("Attempted enabling an unknown event source: " + src);
|
||||
}
|
||||
m_state->enabled_sources.insert(src);
|
||||
}
|
||||
}
|
||||
else if (!m_options.disable_sources.empty())
|
||||
{
|
||||
for(const auto &src : m_options.disable_sources)
|
||||
{
|
||||
if (m_state->loaded_sources.find(src) == m_state->loaded_sources.end())
|
||||
{
|
||||
return run_result::fatal("Attempted disabling an unknown event source: " + src);
|
||||
}
|
||||
m_state->enabled_sources.erase(src);
|
||||
}
|
||||
}
|
||||
|
||||
if(m_state->enabled_sources.empty())
|
||||
{
|
||||
return run_result::fatal("Must enable at least one event source");
|
||||
}
|
||||
|
||||
return run_result::ok();
|
||||
}
|
||||
@@ -27,14 +27,8 @@ application::run_result application::start_webserver()
|
||||
if(!is_capture_mode() && m_state->config->m_webserver_enabled)
|
||||
{
|
||||
std::string ssl_option = (m_state->config->m_webserver_ssl_enabled ? " (SSL)" : "");
|
||||
falco_logger::log(LOG_INFO, "Starting health webserver with threadiness "
|
||||
+ to_string(m_state->config->m_webserver_threadiness)
|
||||
+ ", listening on port "
|
||||
+ to_string(m_state->config->m_webserver_listen_port)
|
||||
+ ssl_option + "\n");
|
||||
|
||||
falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(m_state->config->m_webserver_listen_port) + ssl_option + "\n");
|
||||
m_state->webserver.start(
|
||||
m_state->config->m_webserver_threadiness,
|
||||
m_state->config->m_webserver_listen_port,
|
||||
m_state->config->m_webserver_k8s_healthz_endpoint,
|
||||
m_state->config->m_webserver_ssl_certificate,
|
||||
|
||||
@@ -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,24 @@ 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 those with EF_DROP_SIMPLE_CONS flag.", 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>")
|
||||
("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>")
|
||||
("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. Can not disable all event sources.", 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 <events_file> in .scap format instead of tapping into live.", cxxopts::value(trace_filename), "<events_file>")
|
||||
#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-generate-config", "Generate a configuration file that can be used for gVisor.", cxxopts::value<std::string>(gvisor_generate_config_with_socket)->implicit_value("/tmp/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,20 +191,18 @@ 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"));
|
||||
|
||||
("version", "Print version number.", cxxopts::value(print_version_info)->default_value("false"));
|
||||
|
||||
m_cmdline_opts.set_width(140);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ public:
|
||||
bool disable_cri_async;
|
||||
std::vector<std::string> disable_sources;
|
||||
std::vector<std::string> disabled_rule_substrings;
|
||||
std::vector<std::string> enable_sources;
|
||||
std::string trace_filename;
|
||||
std::string gvisor_config;
|
||||
std::string gvisor_generate_config_with_socket;
|
||||
@@ -78,8 +77,6 @@ public:
|
||||
std::vector<std::string> validate_rules_filenames;
|
||||
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,17 +39,14 @@ application::run_result::~run_result()
|
||||
}
|
||||
|
||||
application::state::state()
|
||||
: loaded_sources(),
|
||||
enabled_sources(),
|
||||
source_infos(),
|
||||
plugin_configs(),
|
||||
ppm_sc_of_interest(),
|
||||
tp_of_interest(),
|
||||
syscall_buffer_bytes_size(DEFAULT_DRIVER_BUFFER_BYTES_DIM)
|
||||
: restart(false),
|
||||
terminate(false),
|
||||
reopen_outputs(false),
|
||||
enabled_sources({falco_common::syscall_source})
|
||||
{
|
||||
config = std::make_shared<falco_configuration>();
|
||||
engine = std::make_shared<falco_engine>();
|
||||
offline_inspector = std::make_shared<sinsp>();
|
||||
inspector = std::make_shared<sinsp>();
|
||||
outputs = nullptr;
|
||||
}
|
||||
|
||||
@@ -98,41 +63,27 @@ 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 = true;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
m_state->reopen_outputs = true;
|
||||
}
|
||||
}
|
||||
|
||||
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 = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,36 +125,33 @@ bool application::run(std::string &errstr, bool &restart)
|
||||
std::list<std::function<run_result()>> run_steps = {
|
||||
std::bind(&application::print_help, this),
|
||||
std::bind(&application::print_version, this),
|
||||
std::bind(&application::print_page_size, this),
|
||||
std::bind(&application::print_generated_gvisor_config, this),
|
||||
std::bind(&application::print_ignored_events, this),
|
||||
std::bind(&application::print_syscall_events, this),
|
||||
std::bind(&application::create_signal_handlers, this),
|
||||
std::bind(&application::load_config, this),
|
||||
std::bind(&application::init_inspector, this),
|
||||
std::bind(&application::print_plugin_info, this),
|
||||
std::bind(&application::list_plugins, this),
|
||||
std::bind(&application::load_plugins, this),
|
||||
std::bind(&application::init_inspectors, this),
|
||||
std::bind(&application::init_falco_engine, this),
|
||||
std::bind(&application::list_fields, this),
|
||||
std::bind(&application::select_event_sources, this),
|
||||
std::bind(&application::list_plugins, this),
|
||||
std::bind(&application::validate_rules_files, this),
|
||||
std::bind(&application::load_rules_files, this),
|
||||
std::bind(&application::print_ignored_events, 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),
|
||||
std::bind(&application::init_outputs, this),
|
||||
std::bind(&application::init_clients, this),
|
||||
std::bind(&application::configure_syscall_buffer_size, this),
|
||||
#ifndef MINIMAL_BUILD
|
||||
std::bind(&application::start_grpc_server, this),
|
||||
std::bind(&application::start_webserver, this),
|
||||
#endif
|
||||
std::bind(&application::open_inspector, this),
|
||||
std::bind(&application::process_events, this)
|
||||
};
|
||||
|
||||
std::list<std::function<bool(std::string &)>> teardown_steps = {
|
||||
std::bind(&application::close_inspector, this, _1),
|
||||
std::bind(&application::unregister_signal_handlers, this, _1),
|
||||
#ifndef MINIMAL_BUILD
|
||||
std::bind(&application::stop_grpc_server, this, _1),
|
||||
@@ -238,7 +186,7 @@ bool application::run(std::string &errstr, bool &restart)
|
||||
errstr = res.errstr;
|
||||
}
|
||||
|
||||
restart = should_restart();
|
||||
restart = m_state->restart;
|
||||
|
||||
return res.success;
|
||||
}
|
||||
|
||||
@@ -16,42 +16,30 @@ limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "semaphore.h"
|
||||
#include "configuration.h"
|
||||
#include "stats_writer.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
#include "grpc_server.h"
|
||||
#include "webserver.h"
|
||||
#include "indexed_vector.h"
|
||||
#endif
|
||||
|
||||
#include "app_cmdline_options.h"
|
||||
|
||||
#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);
|
||||
|
||||
@@ -69,65 +57,32 @@ private:
|
||||
// standalone class to allow for a bit of separation between
|
||||
// application state and instance variables, and to also defer
|
||||
// initializing this state until application::init.
|
||||
struct state
|
||||
{
|
||||
// Holds the info mapped for each loaded event source
|
||||
struct source_info
|
||||
{
|
||||
// The index of the given event source in the state's falco_engine,
|
||||
// as returned by falco_engine::add_source
|
||||
std::size_t engine_idx;
|
||||
// The filtercheck list containing all fields compatible
|
||||
// with the given event source
|
||||
filter_check_list filterchecks;
|
||||
// The inspector assigned to this event source. If in capture mode,
|
||||
// all event source will share the same inspector. If the event
|
||||
// source is a plugin one, the assigned inspector must have that
|
||||
// plugin registered in its plugin manager
|
||||
std::shared_ptr<sinsp> inspector;
|
||||
};
|
||||
|
||||
struct state {
|
||||
state();
|
||||
virtual ~state();
|
||||
|
||||
bool restart;
|
||||
bool terminate;
|
||||
bool reopen_outputs;
|
||||
|
||||
std::shared_ptr<falco_configuration> config;
|
||||
std::shared_ptr<falco_outputs> outputs;
|
||||
std::shared_ptr<falco_engine> engine;
|
||||
std::shared_ptr<sinsp> inspector;
|
||||
std::set<std::string> enabled_sources;
|
||||
|
||||
// The set of loaded event sources (by default, the syscall event
|
||||
// source plus all event sources coming from the loaded plugins)
|
||||
std::unordered_set<std::string> loaded_sources;
|
||||
// The event source index that correspond to "syscall"
|
||||
std::size_t syscall_source_idx;
|
||||
|
||||
// The set of enabled event sources (can be altered by using
|
||||
// the --enable-source and --disable-source options)
|
||||
std::unordered_set<std::string> enabled_sources;
|
||||
|
||||
// Used to load all plugins to get their info. In capture mode,
|
||||
// this is also used to open the capture file and read its events
|
||||
std::shared_ptr<sinsp> offline_inspector;
|
||||
|
||||
// List of all the information mapped to each event source
|
||||
// indexed by event source name
|
||||
indexed_vector<source_info> source_infos;
|
||||
|
||||
// List of all plugin configurations indexed by plugin name as returned
|
||||
// by their sinsp_plugin::name method
|
||||
indexed_vector<falco_configuration::plugin_config> plugin_configs;
|
||||
// All filterchecks created by plugins go in this
|
||||
// list. If we ever support multiple event sources at
|
||||
// the same time, this, and the factories created in
|
||||
// init_inspector/load_plugins, will have to be a map
|
||||
// from event source to filtercheck list.
|
||||
std::map<std::string, filter_check_list> plugin_filter_checks;
|
||||
|
||||
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;
|
||||
|
||||
// Set of tracepoints we want the driver to capture
|
||||
std::unordered_set<uint32_t> tp_of_interest;
|
||||
|
||||
// Dimension of the syscall buffer in bytes.
|
||||
uint64_t syscall_buffer_bytes_size;
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
falco::grpc::server grpc_server;
|
||||
std::thread grpc_server_thread;
|
||||
@@ -157,7 +112,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;
|
||||
@@ -166,28 +121,8 @@ private:
|
||||
return r;
|
||||
}
|
||||
|
||||
// Merges two run results into one
|
||||
inline static run_result merge(const run_result& a, const run_result& b)
|
||||
{
|
||||
auto res = ok();
|
||||
res.proceed = a.proceed && b.proceed;
|
||||
res.success = a.success && b.success;
|
||||
res.errstr = a.errstr;
|
||||
if (!b.errstr.empty())
|
||||
{
|
||||
res.errstr += res.errstr.empty() ? "" : "\n";
|
||||
res.errstr += b.errstr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
run_result();
|
||||
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 +134,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
|
||||
@@ -309,7 +183,7 @@ private:
|
||||
run_result attach_inotify_signals();
|
||||
run_result daemonize();
|
||||
run_result init_falco_engine();
|
||||
run_result init_inspectors();
|
||||
run_result init_inspector();
|
||||
run_result init_clients();
|
||||
run_result init_outputs();
|
||||
run_result list_fields();
|
||||
@@ -317,7 +191,7 @@ private:
|
||||
run_result load_config();
|
||||
run_result load_plugins();
|
||||
run_result load_rules_files();
|
||||
run_result create_requested_paths();
|
||||
run_result open_inspector();
|
||||
run_result print_generated_gvisor_config();
|
||||
run_result print_help();
|
||||
run_result print_ignored_events();
|
||||
@@ -325,11 +199,7 @@ private:
|
||||
run_result print_support();
|
||||
run_result print_syscall_events();
|
||||
run_result print_version();
|
||||
run_result print_page_size();
|
||||
run_result process_events();
|
||||
run_result select_event_sources();
|
||||
void configure_interesting_sets();
|
||||
application::run_result configure_syscall_buffer_size();
|
||||
#ifndef MINIMAL_BUILD
|
||||
run_result start_grpc_server();
|
||||
run_result start_webserver();
|
||||
@@ -346,33 +216,21 @@ private:
|
||||
#endif
|
||||
|
||||
// Methods called by the above methods
|
||||
int create_dir(const std::string &path);
|
||||
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 print_all_ignored_events();
|
||||
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,
|
||||
const std::string& source, // an empty source represents capture mode
|
||||
std::shared_ptr<stats_writer> statsw,
|
||||
syscall_evt_drop_mgr &sdropmgr,
|
||||
bool check_drops_and_timeouts,
|
||||
uint64_t duration_to_tot_ns,
|
||||
uint64_t &num_evts);
|
||||
void 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,
|
||||
run_result* res) noexcept;
|
||||
run_result do_inspect(syscall_evt_drop_mgr &sdropmgr,
|
||||
uint64_t duration_to_tot_ns,
|
||||
uint64_t &num_events);
|
||||
|
||||
inline bool is_syscall_source_enabled() const
|
||||
{
|
||||
return m_state->enabled_sources.find(falco_common::syscall_source)
|
||||
!= m_state->enabled_sources.end();
|
||||
}
|
||||
|
||||
/* Returns true if we are in capture mode. */
|
||||
inline bool is_capture_mode() const
|
||||
{
|
||||
return !m_options.trace_filename.empty();
|
||||
@@ -383,23 +241,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,12 @@ 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 +50,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();
|
||||
@@ -226,15 +207,10 @@ void falco_configuration::init(const string& conf_filename, const vector<string>
|
||||
falco_logger::log_syslog = m_config->get_scalar<bool>("log_syslog", true);
|
||||
|
||||
m_webserver_enabled = m_config->get_scalar<bool>("webserver.enabled", false);
|
||||
m_webserver_threadiness = m_config->get_scalar<uint32_t>("webserver.threadiness", 0);
|
||||
m_webserver_listen_port = m_config->get_scalar<uint32_t>("webserver.listen_port", 8765);
|
||||
m_webserver_k8s_healthz_endpoint = m_config->get_scalar<string>("webserver.k8s_healthz_endpoint", "/healthz");
|
||||
m_webserver_ssl_enabled = m_config->get_scalar<bool>("webserver.ssl_enabled", false);
|
||||
m_webserver_ssl_certificate = m_config->get_scalar<string>("webserver.ssl_certificate", "/etc/falco/falco.pem");
|
||||
if(m_webserver_threadiness == 0)
|
||||
{
|
||||
m_webserver_threadiness = falco::utils::hardware_concurrency();
|
||||
}
|
||||
|
||||
std::list<string> syscall_event_drop_acts;
|
||||
m_config->get_sequence(syscall_event_drop_acts, "syscall_event_drops.actions");
|
||||
@@ -303,11 +279,6 @@ void falco_configuration::init(const string& conf_filename, const vector<string>
|
||||
throw logic_error("Error reading config file(" + m_config_file + "): metadata download watch frequency seconds must be an unsigned integer > 0");
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
std::set<std::string> load_plugins;
|
||||
|
||||
bool load_plugins_node_defined = m_config->is_defined("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);
|
||||
@@ -250,7 +250,6 @@ public:
|
||||
std::string m_grpc_root_certs;
|
||||
|
||||
bool m_webserver_enabled;
|
||||
uint32_t m_webserver_threadiness;
|
||||
uint32_t m_webserver_listen_port;
|
||||
std::string m_webserver_k8s_healthz_endpoint;
|
||||
bool m_webserver_ssl_enabled;
|
||||
@@ -269,9 +268,6 @@ public:
|
||||
uint32_t m_metadata_download_chunk_wait_us;
|
||||
uint32_t m_metadata_download_watch_freq_sec;
|
||||
|
||||
// Index corresponding to the syscall buffer dimension.
|
||||
uint16_t m_syscall_buf_size_preset;
|
||||
|
||||
std::vector<plugin_config> m_plugins;
|
||||
|
||||
private:
|
||||
@@ -371,7 +367,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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user