mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 19:52:08 +00:00
Compare commits
74 Commits
update/cli
...
allow-unkn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca8178c75e | ||
|
|
d8a793030e | ||
|
|
6408270476 | ||
|
|
5a6cbb190c | ||
|
|
959811a503 | ||
|
|
19fe7240e2 | ||
|
|
b76420fe47 | ||
|
|
2883df5808 | ||
|
|
8c4040b610 | ||
|
|
49b8f87db4 | ||
|
|
cd8234d8b3 | ||
|
|
e6b0d2697f | ||
|
|
987ececa54 | ||
|
|
7f4afffe3e | ||
|
|
91bfa379ce | ||
|
|
64a231b962 | ||
|
|
7b030727a2 | ||
|
|
b2eb3ec345 | ||
|
|
b4eb5b87b6 | ||
|
|
a582599778 | ||
|
|
3fb1d207e2 | ||
|
|
4f192e89fa | ||
|
|
07abb89f36 | ||
|
|
7691dba3ff | ||
|
|
c736689f6f | ||
|
|
3bcd2ca70d | ||
|
|
2e443e7660 | ||
|
|
bec5121fa4 | ||
|
|
ee0b7daba0 | ||
|
|
b2bbb265b4 | ||
|
|
0d7068b048 | ||
|
|
7f33b08634 | ||
|
|
c2a05b3e64 | ||
|
|
581d67fa08 | ||
|
|
b7bda6d892 | ||
|
|
5eec26976d | ||
|
|
1916314583 | ||
|
|
8831c7f3c7 | ||
|
|
2cebe052a1 | ||
|
|
05282f3976 | ||
|
|
da4a5b1456 | ||
|
|
36e9c2ba17 | ||
|
|
0c8b4a2127 | ||
|
|
356b3e1451 | ||
|
|
9eb60f04ff | ||
|
|
ff29188cb2 | ||
|
|
4fb7e99c68 | ||
|
|
d03a1f4a9b | ||
|
|
574e7f433b | ||
|
|
f6fa18e7ec | ||
|
|
6beb9838d6 | ||
|
|
9a175cb1db | ||
|
|
32daac3e4d | ||
|
|
0a901e4f52 | ||
|
|
22732e9edb | ||
|
|
6a352338e3 | ||
|
|
f8b66d051b | ||
|
|
c237ddc738 | ||
|
|
9d31164a71 | ||
|
|
f433b449d9 | ||
|
|
44955004e3 | ||
|
|
a9dac551b8 | ||
|
|
df8e4e0545 | ||
|
|
321da3e5bf | ||
|
|
4b34b83739 | ||
|
|
5b558cd600 | ||
|
|
3b7401c2e5 | ||
|
|
aea12f4f3b | ||
|
|
f2637c8600 | ||
|
|
0a14d34e16 | ||
|
|
a1bdf3ed61 | ||
|
|
d3c41c2d97 | ||
|
|
90d71a8e92 | ||
|
|
8eb7d83ee8 |
4
.circleci/OWNERS
Normal file
4
.circleci/OWNERS
Normal file
@@ -0,0 +1,4 @@
|
||||
approvers:
|
||||
- jonahjon
|
||||
reviewers:
|
||||
- jonahjon
|
||||
@@ -452,6 +452,25 @@ jobs:
|
||||
docker build --build-arg FALCO_IMAGE_TAG=master -t falcosecurity/falco-driver-loader:master docker/driver-loader
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
docker push falcosecurity/falco-driver-loader:master
|
||||
# Publish container images to AWS ECR Public
|
||||
"publish/container-images-aws-dev":
|
||||
docker:
|
||||
- image: docker:stable
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: Build and publish falco to AWS
|
||||
command: |
|
||||
apk update
|
||||
apk add --update groff less py-pip
|
||||
pip install awscli
|
||||
FALCO_VERSION=$(cat /build/release/userspace/falco/config_falco.h | grep 'FALCO_VERSION ' | cut -d' ' -f3 | sed -e 's/^"//' -e 's/"$//')
|
||||
docker build --build-arg VERSION_BUCKET=deb-dev --build-arg FALCO_VERSION=${FALCO_VERSION} -t "public.ecr.aws/falcosecurity/falco:master" docker/falco
|
||||
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
|
||||
docker push "public.ecr.aws/falcosecurity/falco:master"
|
||||
# Publish the packages
|
||||
"publish/packages":
|
||||
docker:
|
||||
@@ -518,6 +537,26 @@ jobs:
|
||||
echo ${DOCKERHUB_SECRET} | docker login -u ${DOCKERHUB_USER} --password-stdin
|
||||
docker push "falcosecurity/falco-driver-loader:${CIRCLE_TAG}"
|
||||
docker push "falcosecurity/falco-driver-loader:latest"
|
||||
# Publish container images to AWS ECR Public
|
||||
"publish/container-images-aws":
|
||||
docker:
|
||||
- image: docker:stable
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: Build and publish falco to AWS
|
||||
command: |
|
||||
apk update
|
||||
apk add --update groff less py-pip
|
||||
pip install awscli
|
||||
docker build --build-arg VERSION_BUCKET=deb --build-arg FALCO_VERSION=${CIRCLE_TAG} -t "public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}" docker/falco
|
||||
docker tag "public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}" public.ecr.aws/falcosecurity/falco:latest
|
||||
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/falcosecurity
|
||||
docker push "public.ecr.aws/falcosecurity/falco:${CIRCLE_TAG}"
|
||||
docker push "public.ecr.aws/falcosecurity/falco:latest"
|
||||
workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
@@ -577,7 +616,16 @@ workflows:
|
||||
requires:
|
||||
- "publish/packages-dev"
|
||||
- "tests/driver-loader/integration"
|
||||
- "quality/static-analysis"
|
||||
- "publish/container-images-aws-dev":
|
||||
context: test-infra # contains Falco AWS credentials
|
||||
filters:
|
||||
tags:
|
||||
ignore: /.*/
|
||||
branches:
|
||||
only: master
|
||||
requires:
|
||||
- publish/docker-dev
|
||||
# - "quality/static-analysis" # This is temporarly disabled: https://github.com/falcosecurity/falco/issues/1526
|
||||
release:
|
||||
jobs:
|
||||
- "build/musl":
|
||||
@@ -620,3 +668,12 @@ workflows:
|
||||
only: /.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- "publish/container-images-aws":
|
||||
context: test-infra # contains Falco AWS credentials
|
||||
requires:
|
||||
- "publish/docker"
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,7 +2,6 @@
|
||||
*~
|
||||
*.pyc
|
||||
|
||||
test/falco_tests.yaml
|
||||
test/traces-negative
|
||||
test/traces-positive
|
||||
test/traces-info
|
||||
|
||||
@@ -10,7 +10,7 @@ This is a list of production adopters of Falco (in alphabetical order):
|
||||
|
||||
* [GitLab](https://about.gitlab.com/direction/defend/container_host_security/) - GitLab is a complete DevOps platform, delivered as a single application, fundamentally changing the way Development, Security, and Ops teams collaborate. GitLab Ultimate provides the single tool teams need to find, triage, and fix vulnerabilities in applications, services, and cloud-native environments enabling them to manage their risk. This provides them with repeatable, defensible processes that automate security and compliance policies. GitLab includes a tight integration with Falco, allowing users to defend their containerized applications from attacks while running in production.
|
||||
|
||||
* [League](https://league.com/ca/) - League provides health benefits management services to help employees understand and get the most from their benefits, and employers to provide effective, efficient plans. Falco is used to monitor our deployed services on Kubernetes, protecting against malicious access to containerswhich could lead to leaks of PHI or other sensitive data. The Falco alerts are logged in Stackdriver for grouping and further analysis. In the future, we're hoping for integrations with Prometheus and AlertManager as well.
|
||||
* [League](https://league.com/ca/) - League provides health benefits management services to help employees understand and get the most from their benefits, and employers to provide effective, efficient plans. Falco is used to monitor our deployed services on Kubernetes, protecting against malicious access to containers which could lead to leaks of PHI or other sensitive data. The Falco alerts are logged in Stackdriver for grouping and further analysis. In the future, we're hoping for integrations with Prometheus and AlertManager as well.
|
||||
|
||||
* [Logz.io](https://logz.io/) - Logz.io is a cloud observability platform for modern engineering teams. The Logz.io platform consists of three products — Log Management, Infrastructure Monitoring, and Cloud SIEM — that work together to unify the jobs of monitoring, troubleshooting, and security. We empower engineers to deliver better software by offering the world's most popular open source observability tools — the ELK Stack, Grafana, and Jaeger — in a single, easy to use, and powerful platform purpose-built for monitoring distributed cloud environments. Cloud SIEM supports data from multiple sources, including Falco's alerts, and offers useful rules and dashboards content to visualize and manage incidents across your systems in a unified UI.
|
||||
* https://logz.io/blog/k8s-security-with-falco-and-cloud-siem/
|
||||
@@ -26,5 +26,6 @@ This is a list of production adopters of Falco (in alphabetical order):
|
||||
|
||||
* [Sumo Logic](https://www.sumologic.com/) - Sumo Logic provides a SaaS based log aggregation service that provides dashboards and applications to easily identify and analyze problems in your application and infrastructure. Sumo Logic provides native integrations for many CNCF projects, such as Falco, that allows end users to easily collect Falco events and analyze Falco events on DecSecOps focused dashboards.
|
||||
|
||||
* [Sysdig](https://www.sysdig.com/) Sysdig originally created Falco in 2016 to detect unexpected or suspicious activity using a rules engine on top of the data that comes from the sysdig kernel system call probe. Sysdig provides tooling to help with vulnerability management, compliance, detection, incident response and forensics in Cloud-native environments. Sysdig Secure has extended falco to include: a rule library, the ability to update macros, lists & rules via the user interface and API, automated tuning of rules, and rule creation based on profiling known system behavior. On top of the basic Falco rules, Sysdig Secure implements the concept of a "Security policy" that can comprise several rules which are evaluated for a user-define infrastructure scope like Kubernetes namespaces, OpenShift clusters, deployment workload, cloud regions etc.
|
||||
* [Swissblock Technologies](https://swissblock.net/) At Swissblock we connect the dots by combining cutting-edge algorithmic trading strategies with in-depth market analysis. We route all Falco events to our control systems, both monitoring and logging. Being able to deeply analyse alerts, we can understand what is running on our Kubernetes clusters and check against security policies, specifically defined for each workload. A set of alarms notifies us in case of critical events, letting us react fast. In the near future we plan to build a little application to route Kubernetes internal events directly to Falco, fully leveraging Falco PodSecurityPolicies analyses.
|
||||
|
||||
* [Sysdig](https://www.sysdig.com/) Sysdig originally created Falco in 2016 to detect unexpected or suspicious activity using a rules engine on top of the data that comes from the sysdig kernel system call probe. Sysdig provides tooling to help with vulnerability management, compliance, detection, incident response and forensics in Cloud-native environments. Sysdig Secure has extended Falco to include: a rule library, the ability to update macros, lists & rules via the user interface and API, automated tuning of rules, and rule creation based on profiling known system behavior. On top of the basic Falco rules, Sysdig Secure implements the concept of a "Security policy" that can comprise several rules which are evaluated for a user-defined infrastructure scope like Kubernetes namespaces, OpenShift clusters, deployment workload, cloud regions etc.
|
||||
|
||||
66
CHANGELOG.md
66
CHANGELOG.md
@@ -1,5 +1,71 @@
|
||||
# Change Log
|
||||
|
||||
## v0.27.0
|
||||
|
||||
Released on 2021-01-18
|
||||
|
||||
### Major Changes
|
||||
|
||||
* new: Added falco engine version to grpc version service [[#1507](https://github.com/falcosecurity/falco/pull/1507)] - [@nibalizer](https://github.com/nibalizer)
|
||||
* BREAKING CHANGE: Users who run Falco without a config file will be unable to do that any more, Falco now expects a configuration file to be passed all the times. Developers may need to adjust their processes. [[#1494](https://github.com/falcosecurity/falco/pull/1494)] - [@nibalizer](https://github.com/nibalizer)
|
||||
* new: asynchronous outputs implementation, outputs channels will not block event processing anymore [[#1451](https://github.com/falcosecurity/falco/pull/1451)] - [@leogr](https://github.com/leogr)
|
||||
* new: slow outputs detection [[#1451](https://github.com/falcosecurity/falco/pull/1451)] - [@leogr](https://github.com/leogr)
|
||||
* new: `output_timeout` config option for slow outputs detection [[#1451](https://github.com/falcosecurity/falco/pull/1451)] - [@leogr](https://github.com/leogr)
|
||||
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* build: bump b64 to v2.0.0.1 [[#1441](https://github.com/falcosecurity/falco/pull/1441)] - [@fntlnz](https://github.com/fntlnz)
|
||||
* rules(macro container_started): re-use `spawned_process` macro inside `container_started` macro [[#1449](https://github.com/falcosecurity/falco/pull/1449)] - [@leodido](https://github.com/leodido)
|
||||
* docs: reach out documentation [[#1472](https://github.com/falcosecurity/falco/pull/1472)] - [@fntlnz](https://github.com/fntlnz)
|
||||
* docs: Broken outputs.proto link [[#1493](https://github.com/falcosecurity/falco/pull/1493)] - [@deepskyblue86](https://github.com/deepskyblue86)
|
||||
* docs(README.md): correct broken links [[#1506](https://github.com/falcosecurity/falco/pull/1506)] - [@leogr](https://github.com/leogr)
|
||||
* docs(proposals): Exceptions handling proposal [[#1376](https://github.com/falcosecurity/falco/pull/1376)] - [@mstemm](https://github.com/mstemm)
|
||||
* docs: fix a broken link of README [[#1516](https://github.com/falcosecurity/falco/pull/1516)] - [@oke-py](https://github.com/oke-py)
|
||||
* docs: adding the kubernetes privileged use case to use cases [[#1484](https://github.com/falcosecurity/falco/pull/1484)] - [@fntlnz](https://github.com/fntlnz)
|
||||
* rules(Mkdir binary dirs): Adds exe_running_docker_save as an exception as this rules can be triggerred when a container is created. [[#1386](https://github.com/falcosecurity/falco/pull/1386)] - [@jhwbarlow](https://github.com/jhwbarlow)
|
||||
* rules(Create Hidden Files): Adds exe_running_docker_save as an exception as this rules can be triggerred when a container is created. [[#1386](https://github.com/falcosecurity/falco/pull/1386)] - [@jhwbarlow](https://github.com/jhwbarlow)
|
||||
* docs(.circleci): welcome Jonah (Amazon) as a new Falco CI maintainer [[#1518](https://github.com/falcosecurity/falco/pull/1518)] - [@leodido](https://github.com/leodido)
|
||||
* build: falcosecurity/falco:master also available on the AWS ECR Public registry [[#1512](https://github.com/falcosecurity/falco/pull/1512)] - [@leodido](https://github.com/leodido)
|
||||
* build: falcosecurity/falco:latest also available on the AWS ECR Public registry [[#1512](https://github.com/falcosecurity/falco/pull/1512)] - [@leodido](https://github.com/leodido)
|
||||
* update: gRPC clients can now subscribe to drop alerts via gRCP API [[#1451](https://github.com/falcosecurity/falco/pull/1451)] - [@leogr](https://github.com/leogr)
|
||||
* macro(allowed_k8s_users): exclude cloud-controller-manage to avoid false positives on k3s [[#1444](https://github.com/falcosecurity/falco/pull/1444)] - [@fntlnz](https://github.com/fntlnz)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix(userspace/falco): use given priority in falco_outputs::handle_msg() [[#1450](https://github.com/falcosecurity/falco/pull/1450)] - [@leogr](https://github.com/leogr)
|
||||
* fix(userspace/engine): free formatters, if any [[#1447](https://github.com/falcosecurity/falco/pull/1447)] - [@leogr](https://github.com/leogr)
|
||||
* fix(scripts/falco-driver-loader): lsmod usage [[#1474](https://github.com/falcosecurity/falco/pull/1474)] - [@dnwe](https://github.com/dnwe)
|
||||
* fix: a bug that prevents Falco driver to be consumed by many Falco instances in some circumstances [[#1485](https://github.com/falcosecurity/falco/pull/1485)] - [@leodido](https://github.com/leodido)
|
||||
* fix: set `HOST_ROOT=/host` environment variable for the `falcosecurity/falco-no-driver` container image by default [[#1492](https://github.com/falcosecurity/falco/pull/1492)] - [@leogr](https://github.com/leogr)
|
||||
|
||||
|
||||
### Rule Changes
|
||||
|
||||
* rule(list user_known_change_thread_namespace_binaries): add crio and multus to the list [[#1501](https://github.com/falcosecurity/falco/pull/1501)] - [@Kaizhe](https://github.com/Kaizhe)
|
||||
* rule(Container Run as Root User): new rule created [[#1500](https://github.com/falcosecurity/falco/pull/1500)] - [@Kaizhe](https://github.com/Kaizhe)
|
||||
* rule(Linux Kernel Module injection detected): adds a new rule that detects when an LKM module is injected using `insmod` from a container (typically used by rootkits looking to obfuscate their behavior via kernel hooking). [[#1478](https://github.com/falcosecurity/falco/pull/1478)] - [@d1vious](https://github.com/d1vious)
|
||||
* rule(macro multipath_writing_conf): create and use the macro [[#1475](https://github.com/falcosecurity/falco/pull/1475)] - [@nmarier-coveo](https://github.com/nmarier-coveo)
|
||||
* rule(list falco_privileged_images): add calico/node without registry prefix to prevent false positive alerts [[#1457](https://github.com/falcosecurity/falco/pull/1457)] - [@czunker](https://github.com/czunker)
|
||||
* rule(Full K8s Administrative Access): use the right list of admin users (fix) [[#1454](https://github.com/falcosecurity/falco/pull/1454)] - [@mstemm](https://github.com/mstemm)
|
||||
|
||||
|
||||
### Non user-facing changes
|
||||
|
||||
* chore(cmake): remove unnecessary whitespace patch [[#1522](https://github.com/falcosecurity/falco/pull/1522)] - [@leogr](https://github.com/leogr)
|
||||
* remove stale bot in favor of the new lifecycle bot [[#1490](https://github.com/falcosecurity/falco/pull/1490)] - [@leodido](https://github.com/leodido)
|
||||
* chore(cmake): mark some variables as advanced [[#1496](https://github.com/falcosecurity/falco/pull/1496)] - [@deepskyblue86](https://github.com/deepskyblue86)
|
||||
* chore(cmake/modules): avoid useless rebuild [[#1495](https://github.com/falcosecurity/falco/pull/1495)] - [@deepskyblue86](https://github.com/deepskyblue86)
|
||||
* build: BUILD_BYPRODUCTS for civetweb [[#1489](https://github.com/falcosecurity/falco/pull/1489)] - [@fntlnz](https://github.com/fntlnz)
|
||||
* build: remove duplicate item from FALCO_SOURCES [[#1480](https://github.com/falcosecurity/falco/pull/1480)] - [@leodido](https://github.com/leodido)
|
||||
* build: make our integration tests report clear steps for CircleCI UI [[#1473](https://github.com/falcosecurity/falco/pull/1473)] - [@fntlnz](https://github.com/fntlnz)
|
||||
* further improvements outputs impl. [[#1443](https://github.com/falcosecurity/falco/pull/1443)] - [@leogr](https://github.com/leogr)
|
||||
* fix(test): make integration tests properly fail [[#1439](https://github.com/falcosecurity/falco/pull/1439)] - [@leogr](https://github.com/leogr)
|
||||
* Falco outputs refactoring [[#1412](https://github.com/falcosecurity/falco/pull/1412)] - [@leogr](https://github.com/leogr)
|
||||
|
||||
|
||||
|
||||
## v0.26.2
|
||||
|
||||
Released on 2020-11-10
|
||||
|
||||
@@ -19,6 +19,15 @@ option(BUILD_WARNINGS_AS_ERRORS "Enable building with -Wextra -Werror flags" OFF
|
||||
option(MINIMAL_BUILD "Build a minimal version of Falco, containing only the engine and basic input/output (EXPERIMENTAL)" OFF)
|
||||
option(MUSL_OPTIMIZED_BUILD "Enable if you want a musl optimized build" OFF)
|
||||
|
||||
# We shouldn't need to set this, see https://gitlab.kitware.com/cmake/cmake/-/issues/16419
|
||||
option(EP_UPDATE_DISCONNECTED "ExternalProject update disconnected" OFF)
|
||||
if (${EP_UPDATE_DISCONNECTED})
|
||||
set_property(
|
||||
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PROPERTY EP_UPDATE_DISCONNECTED TRUE)
|
||||
endif()
|
||||
|
||||
|
||||
# Elapsed time
|
||||
# set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") # TODO(fntlnz, leodido): add a flag to enable this
|
||||
|
||||
@@ -176,12 +185,9 @@ if(NOT MINIMAL_BUILD)
|
||||
INSTALL_COMMAND ${CMD_MAKE} COPT="-DNO_FILES" install-lib install-headers PREFIX=${CIVETWEB_SRC}/install "WITH_CPP=1")
|
||||
endif()
|
||||
|
||||
# string-view-lite
|
||||
#string-view-lite
|
||||
include(DownloadStringViewLite)
|
||||
|
||||
# cxxopts
|
||||
include(DownloadCxxOpts)
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
# gRPC
|
||||
include(gRPC)
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
# Process for becoming a maintainer
|
||||
|
||||
* Express interest to the existing maintainers that you or your organization is interested in becoming a
|
||||
maintainer. Becoming a maintainer generally means that you are going to be spending substantial
|
||||
time (>25%) on Falco for the foreseeable future. You should have domain expertise and be extremely
|
||||
proficient in C++. Ultimately your goal is to become a maintainer that will represent your
|
||||
organization.
|
||||
* We will expect you to start contributing increasingly complicated PRs, under the guidance
|
||||
of the existing maintainers.
|
||||
* We may ask you to do some PRs from our backlog.
|
||||
* As you gain experience with the code base and our standards, we will ask you to do code reviews
|
||||
for incoming PRs (i.e., all maintainers are expected to shoulder a proportional share of
|
||||
community reviews).
|
||||
* After a period of approximately 2-3 months of working together and making sure we see eye to eye,
|
||||
the existing maintainers will confer and decide whether to grant maintainer status or not.
|
||||
We make no guarantees on the length of time this will take, but 2-3 months is the approximate
|
||||
goal.
|
||||
|
||||
## Maintainer responsibilities
|
||||
|
||||
* Monitor Slack (delayed response is perfectly acceptable).
|
||||
* Triage GitHub issues and perform pull request reviews for other maintainers and the community.
|
||||
* During GitHub issue triage, apply all applicable [labels](https://github.com/falcosecurity/falco/labels)
|
||||
to each new issue. Labels are extremely useful for future issue follow up. Which labels to apply
|
||||
is somewhat subjective so just use your best judgment.
|
||||
* Make sure that ongoing PRs are moving forward at the right pace or closing them.
|
||||
* Participate when called upon in the security releases. Note that although this should be a rare
|
||||
occurrence, if a serious vulnerability is found, the process may take up to several full days of
|
||||
work to implement. This reality should be taken into account when discussing time commitment
|
||||
obligations with employers.
|
||||
* In general continue to be willing to spend at least 25% of ones time working on Falco (~1.25
|
||||
business days per week).
|
||||
|
||||
## When does a maintainer lose maintainer status
|
||||
|
||||
If a maintainer is no longer interested or cannot perform the maintainer duties listed above, they
|
||||
should volunteer to be moved to emeritus status. In extreme cases this can also occur by a vote of
|
||||
the maintainers per the voting process below.
|
||||
|
||||
# Conflict resolution and voting
|
||||
|
||||
In general, we prefer that technical issues and maintainer membership are amicably worked out
|
||||
between the persons involved. If a dispute cannot be decided independently, the maintainers can be
|
||||
called in to decide an issue. If the maintainers themselves cannot decide an issue, the issue will
|
||||
be resolved by voting. The voting process is a simple majority in which each senior maintainer
|
||||
receives two votes and each normal maintainer receives one vote.
|
||||
|
||||
# Adding new projects to the falcosecurity GitHub organization
|
||||
|
||||
New projects will be added to the falcosecurity organization via GitHub issue discussion in one of the
|
||||
existing projects in the organization. Once sufficient discussion has taken place (~3-5 business
|
||||
days but depending on the volume of conversation), the maintainers of *the project where the issue
|
||||
was opened* (since different projects in the organization may have different maintainers) will
|
||||
decide whether the new project should be added. See the section above on voting if the maintainers
|
||||
cannot easily decide.
|
||||
10
README.md
10
README.md
@@ -21,21 +21,21 @@ Read the [change log](CHANGELOG.md).
|
||||
|
||||
The Falco Project, originally created by [Sysdig](https://sysdig.com), is an incubating [CNCF](https://cncf.io) open source cloud native runtime security tool.
|
||||
Falco makes it easy to consume kernel events, and enrich those events with information from Kubernetes and the rest of the cloud native stack.
|
||||
Falco has a rich rule set of security rules specifically built for Kubernetes, Linux, and cloud-native.
|
||||
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.
|
||||
|
||||
### Installing Falco
|
||||
|
||||
If you would like to run Falco in **production** please adhere to the [official installation guide](https://falco.org/docs/installation/).
|
||||
If you would like to run Falco in **production** please adhere to the [official installation guide](https://falco.org/docs/getting-started/installation/).
|
||||
|
||||
##### Kubernetes
|
||||
|
||||
| Tool | Link | Note |
|
||||
|----------|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
|
||||
| Helm | [Chart Repository](https://github.com/falcosecurity/charts/tree/master/falco#introduction) | The Falco community offers regular helm chart releases. |
|
||||
| Minikube | [Tutorial](https://falco.org/docs/third-party/#minikube) | The Falco driver has been baked into minikube for easy deployment. |
|
||||
| Kind | [Tutorial](https://falco.org/docs/third-party/#kind) | Running Falco with kind requires a driver on the host system. |
|
||||
| GKE | [Tutorial](https://falco.org/docs/third-party/#gke) | We suggest using the eBPF driver for running Falco on GKE. |
|
||||
| Minikube | [Tutorial](https://falco.org/docs/getting-started/third-party/#minikube) | The Falco driver has been baked into minikube for easy deployment. |
|
||||
| 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
|
||||
|
||||
|
||||
17
RELEASE.md
17
RELEASE.md
@@ -28,8 +28,8 @@ Before cutting a release we need to do some homework in the Falco repository. Th
|
||||
|
||||
- Double-check if any hard-coded version number is present in the code, it should be not present anywhere:
|
||||
- If any, manually correct it then open an issue to automate version number bumping later
|
||||
- Versions table in the `README.md` update itself automatically
|
||||
- Generate the change log https://github.com/leodido/rn2md, or https://fs.fntlnz.wtf/falco/milestones-changelog.txt for the lazy people (it updates every 5 minutes)
|
||||
- Versions table in the `README.md` updates itself automatically
|
||||
- Generate the change log https://github.com/leodido/rn2md:
|
||||
- If you review timeout errors with `rn2md` try to generate an GitHub Oauth access token and use `-t`
|
||||
- Add the latest changes on top the previous `CHANGELOG.md`
|
||||
- Submit a PR with the above modifications
|
||||
@@ -69,13 +69,12 @@ Now assume `x.y.z` is the new version.
|
||||
| deb | [](https://dl.bintray.com/falcosecurity/deb/stable/falco-x.y.z-x86_64.deb) |
|
||||
| tgz | [](https://dl.bintray.com/falcosecurity/bin/x86_64/falco-x.y.z-x86_64.deb) |
|
||||
|
||||
| Images |
|
||||
| --------------------------------------------------------------- |
|
||||
| `docker pull docker.io/falcosecurity/falco:_tag_` |
|
||||
| `docker pull docker.io/falcosecurity/falco-driver-loader:_tag_` |
|
||||
| `docker pull docker.io/falcosecurity/falco-no-driver:_tag_` |
|
||||
|
||||
<!-- Copy the relevant part of the changelog here -->
|
||||
| Images |
|
||||
| --------------------------------------------------------------------------- |
|
||||
| `docker pull docker.io/falcosecurity/falco:x.y.z` |
|
||||
| `docker pull public.ecr.aws/falcosecurity/falco:x.y.z` |
|
||||
| `docker pull docker.io/falcosecurity/falco-driver-loader:x.y.z` |
|
||||
| `docker pull docker.io/falcosecurity/falco-no-driver:x.y.z` |
|
||||
|
||||
### Statistics
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
set(CXXOPTS_PREFIX ${CMAKE_BINARY_DIR}/cxxopts-prefix)
|
||||
set(CXXOPTS_INCLUDE ${CXXOPTS_PREFIX}/include)
|
||||
message(STATUS "Using bundled cxxopts in ${CXXOPTS_INCLUDE}")
|
||||
|
||||
ExternalProject_Add(
|
||||
cxxopts
|
||||
PREFIX ${CXXOPTS_PREFIX}
|
||||
GIT_REPOSITORY "https://github.com/jarro2783/cxxopts.git"
|
||||
GIT_TAG "master"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CXXOPTS_PREFIX}/src/cxxopts/include/cxxopts.hpp
|
||||
${CXXOPTS_INCLUDE}/cxxopts.hpp)
|
||||
@@ -10,6 +10,7 @@
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
mark_as_advanced(OPENSSL_BINARY)
|
||||
if(NOT USE_BUNDLED_DEPS)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
message(STATUS "Found openssl: include: ${OPENSSL_INCLUDE_DIR}, lib: ${OPENSSL_LIBRARIES}")
|
||||
@@ -20,6 +21,8 @@ if(NOT USE_BUNDLED_DEPS)
|
||||
message(STATUS "Found openssl: binary: ${OPENSSL_BINARY}")
|
||||
endif()
|
||||
else()
|
||||
mark_as_advanced(OPENSSL_BUNDLE_DIR OPENSSL_INSTALL_DIR OPENSSL_INCLUDE_DIR
|
||||
OPENSSL_LIBRARY_SSL OPENSSL_LIBRARY_CRYPTO)
|
||||
set(OPENSSL_BUNDLE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl")
|
||||
set(OPENSSL_INSTALL_DIR "${OPENSSL_BUNDLE_DIR}/target")
|
||||
set(OPENSSL_INCLUDE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl/include")
|
||||
|
||||
@@ -22,6 +22,7 @@ if(NOT USE_BUNDLED_DEPS)
|
||||
endif()
|
||||
|
||||
# c-ares
|
||||
mark_as_advanced(CARES_INCLUDE CARES_LIB)
|
||||
find_path(CARES_INCLUDE NAMES ares.h)
|
||||
find_library(CARES_LIB NAMES libcares.so)
|
||||
if(CARES_INCLUDE AND CARES_LIB)
|
||||
@@ -31,6 +32,7 @@ if(NOT USE_BUNDLED_DEPS)
|
||||
endif()
|
||||
|
||||
# protobuf
|
||||
mark_as_advanced(PROTOC PROTOBUF_INCLUDE PROTOBUF_LIB)
|
||||
find_program(PROTOC NAMES protoc)
|
||||
find_path(PROTOBUF_INCLUDE NAMES google/protobuf/message.h)
|
||||
find_library(PROTOBUF_LIB NAMES libprotobuf.so)
|
||||
@@ -43,6 +45,7 @@ if(NOT USE_BUNDLED_DEPS)
|
||||
endif()
|
||||
|
||||
# gpr
|
||||
mark_as_advanced(GPR_LIB)
|
||||
find_library(GPR_LIB NAMES gpr)
|
||||
|
||||
if(GPR_LIB)
|
||||
@@ -52,12 +55,16 @@ if(NOT USE_BUNDLED_DEPS)
|
||||
endif()
|
||||
|
||||
# gRPC todo(fntlnz, leodido): check that gRPC version is greater or equal than 1.8.0
|
||||
mark_as_advanced(GRPC_INCLUDE GRPC_SRC
|
||||
GRPC_LIB GRPC_LIBS_ABSOLUTE GRPCPP_LIB GRPC_CPP_PLUGIN)
|
||||
find_path(GRPCXX_INCLUDE NAMES grpc++/grpc++.h)
|
||||
if(GRPCXX_INCLUDE)
|
||||
set(GRPC_INCLUDE ${GRPCXX_INCLUDE})
|
||||
unset(GRPCXX_INCLUDE CACHE)
|
||||
else()
|
||||
find_path(GRPCPP_INCLUDE NAMES grpcpp/grpcpp.h)
|
||||
set(GRPC_INCLUDE ${GRPCPP_INCLUDE})
|
||||
unset(GRPCPP_INCLUDE CACHE)
|
||||
add_definitions(-DGRPC_INCLUDE_IS_GRPCPP=1)
|
||||
endif()
|
||||
find_library(GRPC_LIB NAMES grpc)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
mark_as_advanced(JQ_INCLUDE JQ_LIB)
|
||||
if (NOT USE_BUNDLED_DEPS)
|
||||
find_path(JQ_INCLUDE jq.h PATH_SUFFIXES jq)
|
||||
find_library(JQ_LIB NAMES jq)
|
||||
|
||||
@@ -3,6 +3,7 @@ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/static-analysis-reports)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/static-analysis-reports/cppcheck)
|
||||
|
||||
# cppcheck
|
||||
mark_as_advanced(CPPCHECK CPPCHECK_HTMLREPORT)
|
||||
find_program(CPPCHECK cppcheck)
|
||||
find_program(CPPCHECK_HTMLREPORT cppcheck-htmlreport)
|
||||
|
||||
|
||||
@@ -38,17 +38,6 @@ index 6f51588e..5f9ea84e 100644
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -579,8 +579,8 @@ scap_t* scap_open_udig_int(char *error, int32_t *rc,
|
||||
//
|
||||
// Map the ppm_ring_buffer_info that contains the buffer pointers
|
||||
//
|
||||
- if(udig_alloc_ring_descriptors(&(handle->m_devs[0].m_bufinfo_fd),
|
||||
- &handle->m_devs[0].m_bufinfo,
|
||||
+ if(udig_alloc_ring_descriptors(&(handle->m_devs[0].m_bufinfo_fd),
|
||||
+ &handle->m_devs[0].m_bufinfo,
|
||||
&handle->m_devs[0].m_bufstatus,
|
||||
error) != SCAP_SUCCESS)
|
||||
{
|
||||
@@ -2175,7 +2175,7 @@ int32_t scap_disable_dynamic_snaplen(scap_t* handle)
|
||||
|
||||
const char* scap_get_host_root()
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
#
|
||||
mark_as_advanced(YAMLCPP_INCLUDE_DIR YAMLCPP_LIB)
|
||||
if(NOT USE_BUNDLED_DEPS)
|
||||
find_path(YAMLCPP_INCLUDE_DIR NAMES yaml-cpp/yaml.h)
|
||||
find_library(YAMLCPP_LIB NAMES yaml-cpp)
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
# Falco Dockerfiles
|
||||
|
||||
This directory contains various ways to package Falco as a container and related tools.
|
||||
This directory contains various ways to package Falco as a container and related tools.
|
||||
|
||||
## Currently Supported Images
|
||||
|
||||
| Name | Directory | Description |
|
||||
|---|---|---|
|
||||
| [falcosecurity/falco:latest](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/falco | Falco (DEB built from git tag or from the master) with all the building toolchain. |
|
||||
| [falcosecurity/falco-driver-loader:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:master](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader) | docker/driver-loader | `falco-driver-loader` as entrypoint with the building toolchain. |
|
||||
| [falcosecurity/falco-no-driver:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver), [falcosecurity/falco-no-driver:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver),[falcosecurity/falco-no-driver:master](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver) | docker/no-driver | Falco (TGZ built from git tag or from the master) without the building toolchain. |
|
||||
| [falcosecurity/falco-builder:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-builder) | docker/builder | The complete build tool chain for compiling Falco from source. See [the documentation](https://falco.org/docs/source/) for more details on building from source. Used to build Falco (CI). |
|
||||
| [falcosecurity/falco-tester:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-tester) | docker/tester | Container image for running the Falco test suite. Used to run Falco integration tests (CI). |
|
||||
| [falcosecurity/falco:latest](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/falco | Falco (DEB built from git tag or from the master) with all the building toolchain. |
|
||||
| [falcosecurity/falco-driver-loader:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:master](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader) | docker/driver-loader | `falco-driver-loader` as entrypoint with the building toolchain. |
|
||||
| [falcosecurity/falco-no-driver:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver), [falcosecurity/falco-no-driver:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver),[falcosecurity/falco-no-driver:master](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver) | docker/no-driver | Falco (TGZ built from git tag or from the master) without the building toolchain. |
|
||||
| [falcosecurity/falco-builder:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-builder) | docker/builder | The complete build tool chain for compiling Falco from source. See [the documentation](https://falco.org/docs/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). |
|
||||
| _to not be published_ | docker/local | Built on-the-fly and used by falco-tester. |
|
||||
|
||||
> Note: `falco-builder`, `falco-tester` (and the `docker/local` image 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.
|
||||
|
||||
|
||||
17
falco.yaml
17
falco.yaml
@@ -87,6 +87,23 @@ syscall_event_drops:
|
||||
rate: .03333
|
||||
max_burst: 10
|
||||
|
||||
# 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.
|
||||
# The timeout error will be reported to the log according to the above log_* settings.
|
||||
# Note that the notification will not be discarded from the output queue; thus,
|
||||
# output channels may indefinitely remain blocked.
|
||||
# An output timeout error indeed indicate a misconfiguration issue or I/O problems
|
||||
# that cannot be recovered by Falco and should be fixed by the user.
|
||||
#
|
||||
# The "output_timeout" value specifies the duration in milliseconds to wait before
|
||||
# considering the deadline exceed.
|
||||
#
|
||||
# With a 2000ms default, the notification consumer can block the Falco output
|
||||
# for up to 2 seconds without reaching the timeout.
|
||||
|
||||
output_timeout: 2000
|
||||
|
||||
# A throttling mechanism implemented as a token bucket limits the
|
||||
# rate of falco notifications. This throttling is controlled by the following configuration
|
||||
# options:
|
||||
|
||||
240
proposals/20200828-structured-exception-handling.md
Normal file
240
proposals/20200828-structured-exception-handling.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# Proposal for First Class Structured Exceptions in Falco Rules
|
||||
|
||||
## Summary
|
||||
|
||||
## Motivation
|
||||
|
||||
Almost all Falco Rules have cases where the behavior detected by the
|
||||
rule should be allowed. For example, The rule Write Below Binary Dir
|
||||
has exceptions for specific programs that are known to write below
|
||||
these directories as a part of software installation/management:
|
||||
|
||||
```yaml
|
||||
- rule: Write below binary dir
|
||||
desc: an attempt to write to any file below a set of binary directories
|
||||
condition: >
|
||||
bin_dir and evt.dir = < and open_write
|
||||
and not package_mgmt_procs
|
||||
and not exe_running_docker_save
|
||||
and not python_running_get_pip
|
||||
and not python_running_ms_oms
|
||||
and not user_known_write_below_binary_dir_activities
|
||||
...
|
||||
```
|
||||
In most cases, these exceptions are expressed as concatenations to the original rule's condition. For example, looking at the macro package_mgmt_procs:
|
||||
|
||||
```yaml
|
||||
- macro: package_mgmt_procs
|
||||
condition: proc.name in (package_mgmt_binaries)
|
||||
```
|
||||
|
||||
The result is appending `and not proc.name in (package_mgmt_binaries)` to the condition of the rule.
|
||||
|
||||
A more extreme case of this is the write_below_etc macro used by Write below etc rule. It has tens of exceptions:
|
||||
|
||||
```
|
||||
...
|
||||
and not sed_temporary_file
|
||||
and not exe_running_docker_save
|
||||
and not ansible_running_python
|
||||
and not python_running_denyhosts
|
||||
and not fluentd_writing_conf_files
|
||||
and not user_known_write_etc_conditions
|
||||
and not run_by_centrify
|
||||
and not run_by_adclient
|
||||
and not qualys_writing_conf_files
|
||||
and not git_writing_nssdb
|
||||
...
|
||||
```
|
||||
|
||||
The exceptions all generally follow the same structure--naming a program and a directory prefix below /etc where that program is allowed to write files.
|
||||
|
||||
### Using Appends/Overwrites to Customize Rules
|
||||
|
||||
An important way to customize rules and macros is to use `append: true` to add to them, or `append: false` to define a new rule/macro, overwriting the original rule/macro. Here's an example from Update Package Repository:
|
||||
|
||||
```yaml
|
||||
- list: package_mgmt_binaries
|
||||
items: [rpm_binaries, deb_binaries, update-alternat, gem, pip, pip3, sane-utils.post, alternatives, chef-client, apk, snapd]
|
||||
|
||||
- macro: package_mgmt_procs
|
||||
condition: proc.name in (package_mgmt_binaries)
|
||||
|
||||
- macro: user_known_update_package_registry
|
||||
condition: (never_true)
|
||||
|
||||
- rule: Update Package Repository
|
||||
desc: Detect package repositories get updated
|
||||
condition: >
|
||||
((open_write and access_repositories) or (modify and modify_repositories))
|
||||
and not package_mgmt_procs
|
||||
and not exe_running_docker_save
|
||||
and not user_known_update_package_registry
|
||||
```
|
||||
|
||||
If someone wanted to add additional exceptions to this rule, they could add the following to the user_rules file:
|
||||
|
||||
```yaml
|
||||
- list: package_mgmt_binaries
|
||||
items: [puppet]
|
||||
append: true
|
||||
|
||||
- macro: package_mgmt_procs
|
||||
condition: and not proc.pname=chef
|
||||
append: true
|
||||
|
||||
- macro: user_known_update_package_registry
|
||||
condition: (proc.name in (npm))
|
||||
append: false
|
||||
```
|
||||
|
||||
This adds an 3 different exceptions:
|
||||
* an additional binary to package_mgmt_binaries (because append is true),
|
||||
* adds to package_mgmt_procs, adding an exception for programs spawned by chef (because append is true)
|
||||
* overrides the macro user_known_update_package_registry to add an exception for npm (because append is false).
|
||||
|
||||
### Problems with Appends/Overrides to Define Exceptions
|
||||
|
||||
Although the concepts of macros and lists in condition fields, combined with appending to lists/conditions in macros/rules, is very general purpose, it can be unwieldy:
|
||||
|
||||
* Appending to conditions can result in incorrect behavior, unless the original condition has its logical operators set up properly with parentheses. For example:
|
||||
|
||||
```yaml
|
||||
rule: my_rule
|
||||
condition: (evt.type=open and (fd.name=/tmp/foo or fd.name=/tmp/bar))
|
||||
|
||||
rule: my_rule
|
||||
condition: or fd.name=/tmp/baz
|
||||
append: true
|
||||
```
|
||||
|
||||
Results in unintended behavior. It will match any fd related event where the name is /tmp/baz, when the intent was probably to add /tmp/baz as an additional opened file.
|
||||
|
||||
* A good convention many rules use is to have a clause "and not user_known_xxxx" built into the condition field. However, it's not in all rules and its use is a bit haphazard.
|
||||
|
||||
* Appends and overrides can get confusing if you try to apply them multiple times. For example:
|
||||
|
||||
```yaml
|
||||
macro: allowed_files
|
||||
condition: fd.name=/tmp/foo
|
||||
|
||||
...
|
||||
|
||||
macro: allowed_files
|
||||
condition: and fd.name=/tmp/bar
|
||||
append: true
|
||||
```
|
||||
|
||||
If someone wanted to override the original behavior of allowed_files, they would have to use `append: false` in a third definition of allowed_files, but this would result in losing the append: true override.
|
||||
|
||||
## Solution: Exceptions as first class objects
|
||||
|
||||
To address some of these problems, we will add the notion of Exceptions as top level objects alongside Rules, Macros, and Lists. A rule that supports exceptions must define a new key `exceptions` in the rule. The exceptions key is a list of identifier plus list of tuples of filtercheck fields. Here's an example:
|
||||
|
||||
```yaml
|
||||
- rule: Write below binary dir
|
||||
desc: an attempt to write to any file below a set of binary directories
|
||||
condition: >
|
||||
bin_dir and evt.dir = < and open_write
|
||||
and not package_mgmt_procs
|
||||
and not exe_running_docker_save
|
||||
and not python_running_get_pip
|
||||
and not python_running_ms_oms
|
||||
and not user_known_write_below_binary_dir_activities
|
||||
exceptions:
|
||||
- name: proc_writer
|
||||
fields: [proc.name, fd.directory]
|
||||
- name: container_writer
|
||||
fields: [container.image.repository, fd.directory]
|
||||
comps: [=, startswith]
|
||||
- name: proc_filenames
|
||||
fields: [proc.name, fd.name]
|
||||
comps: [=, in]
|
||||
- name: filenames
|
||||
fields: fd.filename
|
||||
comps: in
|
||||
```
|
||||
|
||||
This rule defines four kinds of exceptions:
|
||||
* proc_writer: uses a combination of proc.name and fd.directory
|
||||
* container_writer: uses a combination of container.image.repository and fd.directory
|
||||
* proc_filenames: uses a combination of process and list of filenames.
|
||||
* filenames: uses a list of filenames
|
||||
|
||||
The specific strings "proc_writer"/"container_writer"/"proc_filenames"/"filenames" are arbitrary strings and don't have a special meaning to the rules file parser. They're only used to link together the list of field names with the list of field values that exist in the exception object.
|
||||
|
||||
proc_writer does not have any comps property, so the fields are directly compared to values using the = operator. container_writer does have a comps property, so each field will be compared to the corresponding exception items using the corresponding comparison operator.
|
||||
|
||||
proc_filenames uses the in comparison operator, so the corresponding values entry should be a list of filenames.
|
||||
|
||||
filenames differs from the others in that it names a single field and single comp operator. This changes how the exception condition snippet is constructed (see below).
|
||||
|
||||
Notice that exceptions are defined as a part of the rule. This is important because the author of the rule defines what construes a valid exception to the rule. In this case, an exception can consist of a process and file directory (actor and target), but not a process name only (too broad).
|
||||
|
||||
Exception values will most commonly be defined in rules with append: true. Here's an example:
|
||||
|
||||
```yaml
|
||||
- list: apt_files
|
||||
items: [/bin/ls, /bin/rm]
|
||||
|
||||
- rule: Write below binary dir
|
||||
exceptions:
|
||||
- name: proc_writer
|
||||
values:
|
||||
- [apk, /usr/lib/alpine]
|
||||
- [npm, /usr/node/bin]
|
||||
- name: container_writer
|
||||
values:
|
||||
- [docker.io/alpine, /usr/libexec/alpine]
|
||||
- name: proc_filenames
|
||||
values:
|
||||
- [apt, apt_files]
|
||||
- [rpm, [/bin/cp, /bin/pwd]]
|
||||
- name: filenames
|
||||
values: [python, go]
|
||||
```
|
||||
|
||||
A rule exception applies if for a given event, the fields in a rule.exception match all of the values in some exception.item. For example, if a program `apk` writes to a file below `/usr/lib/alpine`, the rule will not trigger, even if the condition is met.
|
||||
|
||||
Notice that an item in a values list can be a list. This allows building exceptions with operators like "in", "pmatch", etc. that work on a list of items. The item can also be a name of an existing list. If not present surrounding parantheses will be added.
|
||||
|
||||
Finally, note that the structure of the values property differs between the items where fields is a list of fields (proc_writer/container_writer/proc_filenames) and when it is a single field (procs_only). This changes how the condition snippet is constructed.
|
||||
|
||||
### Implementation
|
||||
|
||||
For exception items where the fields property is a list of field names, each exception can be thought of as an implicit "and not (field1 cmp1 val1 and field2 cmp2 val2 and...)" appended to the rule's condition. For exception items where the fields property is a single field name, the exception can be thought of as an implict "and not field cmp (val1, val2, ...)". In practice, that's how exceptions will be implemented.
|
||||
|
||||
When a rule is parsed, the original condition will be wrapped in an extra layer of parentheses and all exception values will be appended to the condition. For example, using the example above, the resulting condition will be:
|
||||
|
||||
```
|
||||
(<Write below binary dir condition>) and not (
|
||||
(proc.name = apk and fd.directory = /usr/lib/alpine) or (proc.name = npm and fd.directory = /usr/node/bin) or
|
||||
(container.image.repository = docker.io/alpine and fd.directory startswith /usr/libexec/alpine) or
|
||||
(proc.name=apt and fd.name in (apt_files))) or
|
||||
(fd.filename in (python, go))))
|
||||
```
|
||||
|
||||
The exceptions are effectively syntatic sugar that allows expressing sets of exceptions in a concise way.
|
||||
|
||||
### Advantages
|
||||
|
||||
Adding Exception objects as described here has several advantages:
|
||||
|
||||
* All rules will implicitly support exceptions. A rule writer doesn't need to define a user_known_xxx macro and add it to the condition.
|
||||
* The rule writer has some controls on what defines a valid exception. The rule author knows best what is a good exception, and can define the fields that make up the exception.
|
||||
* With this approach, it's much easier to add and manage multiple sets of exceptions from multiple sources. You're just combining lists of tuples of filtercheck field values.
|
||||
|
||||
## Backwards compatibility
|
||||
|
||||
To take advantage of these new features, users will need to upgrade Falco to a version that supports exception objects and exception keys in rules. For the most part, however, the rules file structure is unchanged.
|
||||
|
||||
This approach does not remove the ability to append to exceptions nor the existing use of user_xxx macros to define exceptions to rules. It only provides an additional way to express exceptions. Hopefully, we can migrate existing exceptions to use this approach, but there isn't any plan to make wholesale rules changes as a part of this.
|
||||
|
||||
This approach is for the most part backwards compatible with older Falco releases. To implement exceptions, we'll add a preprocessing element to rule parsing. The main Falco engine is unchanged.
|
||||
|
||||
However, there are a few changes we'll have to make to Falco rules file parsing:
|
||||
|
||||
* Currently, Falco will reject files containing anything other than rule/macro/list top-level objects. As a result, `exception` objects would be rejected. We'll probably want to make a one-time change to Falco to allow arbitrary top level objects.
|
||||
* Similarly, Falco will reject rule objects with exception keys. We'll also probably want to change Falco to allow unknown keys inside rule/macro/list/exception objects.
|
||||
|
||||
|
||||
167
proposals/2021019-libraries-donation.md
Normal file
167
proposals/2021019-libraries-donation.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# OSS Libraries Donation Plan
|
||||
|
||||
## Summary
|
||||
|
||||
Sysdig Inc. intends to donate **libsinsp**, **libscap**, the **kernel module driver** and the **eBPF driver sources** by moving them to the Falco project.
|
||||
|
||||
This means that some parts of the [draios/sysdig](https://github.com/draios/sysdig) repository will be moved to a new GitHub repository called [falcosecurity/libs](https://github.com/falcosecurity/libs).
|
||||
|
||||
This plan aims to describe and clarify the terms and goals to get the donation done.
|
||||
|
||||
## Motivation
|
||||
|
||||
There are two main OSS projects using the libraries and drivers that we are aware of:
|
||||
|
||||
- [sysdig](https://github.com/draios/sysdig) the command line tool
|
||||
- [Falco](https:/github.com/falcosecurity/falco), the CNCF project.
|
||||
|
||||
Since the Falco project is a heavy user of the libraries, a lot more than the sysdig cli tool, Sysdig (the company) decided to donate the libraries and the driver to the Falco community.
|
||||
|
||||
Sysdig (the command line tool) will continue to use the libraries now provided by the Falco community underneath.
|
||||
|
||||
This change is win-win for both parties because of the following reasons:
|
||||
|
||||
- The Falco community owns the source code of the three most important parts of the software it distributes.
|
||||
- Right now it is "only" an engine on top of the libraries. This **donation** helps in making the scope of the Falco project broader. Having the majority of the source code under an **open governance** in the same organization gives the Falco project more contribution opportunities, helps it in **evolving independently** and makes the whole Falco community a strong owner of the processes and decision making regarding those crucial parts.
|
||||
|
||||
- Given the previous point, Sysdig (the command line tool) will benefit from the now **extended contributors base**
|
||||
|
||||
- Sysdig (the company) can now focus on the user experience and user space features
|
||||
|
||||
- **Contributions** to the libraries and drivers will be **easier** to spread across the Falco community
|
||||
|
||||
- By being donated, with their own **release process**, **release artifacts**, and **documentation**, the libraries can now live on their own and possibly be used directly in other projects by becoming fundamental pieces for their success.
|
||||
|
||||
## Goals
|
||||
|
||||
There are many sub-projects and each of them interacts in a different way in this donation.
|
||||
|
||||
Let's see the goals per sub-project.
|
||||
|
||||
### libsinsp
|
||||
|
||||
1. Extract libsinsp from `draios/sysdig/userspace/libsinsp` (keeping the commit history) into [falcosecurity/libs](https://github.com/falcosecurity/libs)
|
||||
|
||||
2. The migration comes first, then we can do additional PRs for the points below so that we do only one thing at a time and keep the history linear
|
||||
|
||||
3. Keep the same code, refactorings will need to be done in subsequent PRs and approved separately
|
||||
|
||||
4. Adapt the CMake and build files
|
||||
|
||||
5. Install [poiana](https://github.com/poiana) and its workflows on it
|
||||
|
||||
6. Define the `OWNERS`
|
||||
|
||||
- Owners are chosen from the current major contributors (considering the past two years) to this project, given their availability, commitment is key
|
||||
|
||||
7. When possible, migrate issues and PRs to the new repository
|
||||
|
||||
8. Distribute the `libsinsp.so` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process
|
||||
|
||||
9. Distribute the `libsinsp.a` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process
|
||||
|
||||
10. Creation of the CI scripts using the Falco CI and Falco Infra
|
||||
|
||||
11. The CI scripts will need to publish the artifacts in the current falcosecurity artifacts repository
|
||||
|
||||
12. Artifacts will be pushed for every tag (release) and for every master merge (development release)
|
||||
|
||||
13. Falco follows a [multi-stage model for adopting new projects](https://github.com/falcosecurity/evolution#falco-project-evolution), in this case we will do an exception since the library is foundational for Falco and it has a very good track record already
|
||||
|
||||
14. This project will go already "Official support" once the donation is completed
|
||||
|
||||
15. Contributing, Code of Conduct, Governance, Security, and Support will be the same as the rest of the organization, find them [here](https://github.com/falcosecurity/.github)
|
||||
|
||||
16. Every other additional change will need to have its own process with a proposal
|
||||
|
||||
17. Implement the release process as described above
|
||||
|
||||
18. Propose a change to Falco repository to use the artifacts produced by the libsinsp release process for the build
|
||||
|
||||
19. Document the API
|
||||
|
||||
### libscap
|
||||
|
||||
1. Extract libscap from `draios/sysdig/userspace/libscap` (keeping the commit history) into [falcosecurity/libs](https://github.com/falcosecurity/libs)
|
||||
|
||||
2. The migration comes first, then we can do additional PRs for the points below so that we do only one thing at a time and keep the history linear
|
||||
|
||||
3. Keep the same code, refactorings will need to be done in subsequent PRs and approved separately
|
||||
|
||||
4. Adapt the CMake and build files
|
||||
|
||||
5. Install [poiana](https://github.com/poiana) and its workflows on it
|
||||
|
||||
6. Define the `OWNERS`
|
||||
|
||||
- Owners are chosen from the current major contributors (considering the past two years) to this project, given their availability, commitment is key
|
||||
|
||||
7. When possible, migrate issues and PRs to the new repository
|
||||
|
||||
8. Distribute the `libscap.so` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process
|
||||
|
||||
9. Distribute the `libscap.a` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process
|
||||
|
||||
10. Creation of the CI scripts using the Falco CI and Falco Infra
|
||||
|
||||
11. The CI scripts will need to publish the artifacts in the current falcosecurity artifacts repository
|
||||
|
||||
12. Artifacts will be pushed for every tag (release) and for every master merge (development release)
|
||||
|
||||
13. Falco follows a [multi-stage model for adopting new projects](https://github.com/falcosecurity/evolution#falco-project-evolution), in this case we will do an exception since the library is foundational for Falco and it has a very good track record already
|
||||
|
||||
14. This project will go already "Official support" once the donation is completed
|
||||
|
||||
15. Contributing, Code of Conduct, Governance, Security, and Support will be the same as the rest of the organization, find them [here](https://github.com/falcosecurity/.github)
|
||||
|
||||
16. Every other additional change will need to have its own process with a proposal
|
||||
|
||||
17. Implement the release process as described above
|
||||
|
||||
18. Propose a change to Falco repository to use the artifacts produced by the libscap release process for the build
|
||||
|
||||
19. Document the API
|
||||
|
||||
### Drivers: Kernel module and eBPF probe
|
||||
|
||||
1. Extract them from `draios/sysdig/driver` (keeping the commit history) into [falcosecurity/libs](https://github.com/falcosecurity/libs)
|
||||
|
||||
2. The migration comes first, then we can do additional PRs for the point below so that we do only one thing at a time and keep the history linear
|
||||
|
||||
3. Keep the same code, refactorings will need to be done in subsequent PRs and approved separately
|
||||
|
||||
4. Adapt the Makefiles and build files
|
||||
|
||||
5. Install [poiana](https://github.com/poiana) and its workflows on it
|
||||
|
||||
6. Define the `OWNERS`
|
||||
|
||||
- Owners are chosen from the current major contributors (considering the past two years) to this project, given their availability, commitment is key
|
||||
|
||||
7. When possible, migrate issues and PRs to the new repository
|
||||
|
||||
8. Falco follows a [multi-stage model for adopting new projects](https://github.com/falcosecurity/evolution#falco-project-evolution), in this case we will do an exception since the library is foundational for Falco and it has a very good track record already. We are just changing maintenance ownership
|
||||
|
||||
9. Contributing, Code of Conduct, Governance, Security, and Support will be the same as the rest of the organization, find them [here](https://github.com/falcosecurity/.github)
|
||||
|
||||
10. Every other additional change will need to have its own process with a proposal
|
||||
|
||||
11. The Falco community already ships driver artifacts using [driverkit](https://github.com/falcosecurity/driverkit) and the [test-infra repository](https://github.com/falcosecurity/test-infra)
|
||||
|
||||
- Adapt the place from which [driverkit](https://github.com/falcosecurity/driverkit) grabs the drivers source
|
||||
|
||||
12. This project will go already "Official support" once the migration is completed.
|
||||
|
||||
### Falco
|
||||
|
||||
1. Adapt the CMake files to point to the new homes for libscap, libsinsp and the drivers
|
||||
|
||||
2. When distributing the deb and rpm, libscap and libsinsp will need to be install dependencies and not anymore compiled into Falco
|
||||
|
||||
### Driverkit
|
||||
|
||||
1. Change the source location for the drivers to point to the new driver repository
|
||||
|
||||
### pdig
|
||||
|
||||
1. The project will need to be adapted to use libscap and libsinsp and the fillers from their new location
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- required_engine_version: 2
|
||||
- required_engine_version: 8
|
||||
|
||||
# Like always_true/always_false, but works with k8s audit events
|
||||
- macro: k8s_audit_always_true
|
||||
@@ -50,12 +50,18 @@
|
||||
vertical_pod_autoscaler_users,
|
||||
cluster-autoscaler,
|
||||
"system:addon-manager",
|
||||
"cloud-controller-manager"
|
||||
"cloud-controller-manager",
|
||||
"eks:node-manager"
|
||||
]
|
||||
|
||||
- rule: Disallowed K8s User
|
||||
desc: Detect any k8s operation by users outside of an allowed set of users.
|
||||
condition: kevt and non_system_user and not ka.user.name in (allowed_k8s_users)
|
||||
condition: kevt and non_system_user
|
||||
exceptions:
|
||||
- name: user_names
|
||||
fields: ka.user.name
|
||||
comps: in
|
||||
values: [allowed_k8s_users]
|
||||
output: K8s Operation performed by user not in allowed list of users (user=%ka.user.name target=%ka.target.name/%ka.target.resource verb=%ka.verb uri=%ka.uri resp=%ka.response.code)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -122,6 +128,10 @@
|
||||
desc: >
|
||||
Detect an attempt to start a pod with a container image outside of a list of allowed images.
|
||||
condition: kevt and pod and kcreate and not allowed_k8s_containers
|
||||
exceptions:
|
||||
- name: image_repos
|
||||
fields: ka.req.pod.containers.image.repository
|
||||
comps: in
|
||||
output: Pod started with container not in allowed list (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -130,7 +140,12 @@
|
||||
- rule: Create Privileged Pod
|
||||
desc: >
|
||||
Detect an attempt to start a pod with a privileged container
|
||||
condition: kevt and pod and kcreate and ka.req.pod.containers.privileged intersects (true) and not ka.req.pod.containers.image.repository in (falco_privileged_images)
|
||||
condition: kevt and pod and kcreate and ka.req.pod.containers.privileged intersects (true)
|
||||
exceptions:
|
||||
- name: image_repos
|
||||
fields: ka.req.pod.containers.image.repository
|
||||
comps: in
|
||||
values: [falco_privileged_images]
|
||||
output: Pod started with privileged container (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -144,7 +159,12 @@
|
||||
desc: >
|
||||
Detect an attempt to start a pod with a volume from a sensitive host directory (i.e. /proc).
|
||||
Exceptions are made for known trusted images.
|
||||
condition: kevt and pod and kcreate and sensitive_vol_mount and not ka.req.pod.containers.image.repository in (falco_sensitive_mount_images)
|
||||
condition: kevt and pod and kcreate and sensitive_vol_mount
|
||||
exceptions:
|
||||
- name: image_repos
|
||||
fields: ka.req.pod.containers.image.repository
|
||||
comps: in
|
||||
values: [falco_sensitive_mount_images]
|
||||
output: Pod started with sensitive mount (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image volumes=%jevt.value[/requestObject/spec/volumes])
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -153,7 +173,12 @@
|
||||
# Corresponds to K8s CIS Benchmark 1.7.4
|
||||
- rule: Create HostNetwork Pod
|
||||
desc: Detect an attempt to start a pod using the host network.
|
||||
condition: kevt and pod and kcreate and ka.req.pod.host_network intersects (true) and not ka.req.pod.containers.image.repository in (falco_hostnetwork_images)
|
||||
condition: kevt and pod and kcreate and ka.req.pod.host_network intersects (true)
|
||||
exceptions:
|
||||
- name: image_repos
|
||||
fields: ka.req.pod.containers.image.repository
|
||||
comps: in
|
||||
values: [falco_hostnetwork_images]
|
||||
output: Pod started using host network (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -166,6 +191,9 @@
|
||||
desc: >
|
||||
Detect an attempt to start a service with a NodePort service type
|
||||
condition: kevt and service and kcreate and ka.req.service.type=NodePort and not user_known_node_port_service
|
||||
exceptions:
|
||||
- name: services
|
||||
fields: [ka.target.namespace, ka.target.name]
|
||||
output: NodePort Service Created (user=%ka.user.name service=%ka.target.name ns=%ka.target.namespace ports=%ka.req.service.ports)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -184,6 +212,9 @@
|
||||
desc: >
|
||||
Detect creating/modifying a configmap containing a private credential (aws key, password, etc.)
|
||||
condition: kevt and configmap and kmodify and contains_private_credentials
|
||||
exceptions:
|
||||
- name: configmaps
|
||||
fields: [ka.target.namespace, ka.req.configmap.name]
|
||||
output: K8s configmap with private credential (user=%ka.user.name verb=%ka.verb configmap=%ka.req.configmap.name config=%ka.req.configmap.obj)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -194,6 +225,10 @@
|
||||
desc: >
|
||||
Detect any request made by the anonymous user that was allowed
|
||||
condition: kevt and ka.user.name=system:anonymous and ka.auth.decision="allow" and not health_endpoint
|
||||
exceptions:
|
||||
- name: user_names
|
||||
fields: ka.user.name
|
||||
comps: in
|
||||
output: Request by anonymous user allowed (user=%ka.user.name verb=%ka.verb uri=%ka.uri reason=%ka.auth.reason))
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -207,6 +242,10 @@
|
||||
# events to be stateful, so it could know if a container named in an
|
||||
# attach request was created privileged or not. For now, we have a
|
||||
# less severe rule that detects attaches/execs to any pod.
|
||||
#
|
||||
# For the same reason, you can't use things like image names/prefixes,
|
||||
# as the event that creates the pod (which has the images) is a
|
||||
# separate event than the actual exec/attach to the pod.
|
||||
|
||||
- macro: user_known_exec_pod_activities
|
||||
condition: (k8s_audit_never_true)
|
||||
@@ -215,6 +254,10 @@
|
||||
desc: >
|
||||
Detect any attempt to attach/exec to a pod
|
||||
condition: kevt_started and pod_subresource and kcreate and ka.target.subresource in (exec,attach) and not user_known_exec_pod_activities
|
||||
exceptions:
|
||||
- name: user_names
|
||||
fields: ka.user.name
|
||||
comps: in
|
||||
output: Attach/Exec to pod (user=%ka.user.name pod=%ka.target.name ns=%ka.target.namespace action=%ka.target.subresource command=%ka.uri.param[command])
|
||||
priority: NOTICE
|
||||
source: k8s_audit
|
||||
@@ -224,10 +267,14 @@
|
||||
condition: (k8s_audit_never_true)
|
||||
|
||||
# Only works when feature gate EphemeralContainers is enabled
|
||||
# Definining empty exceptions just to avoid warnings. There isn't any
|
||||
# great exception for this kind of object, as you'd expect the images
|
||||
# to vary wildly.
|
||||
- rule: EphemeralContainers Created
|
||||
desc: >
|
||||
Detect any ephemeral container created
|
||||
condition: kevt and pod_subresource and kmodify and ka.target.subresource in (ephemeralcontainers) and not user_known_pod_debug_activities
|
||||
exceptions:
|
||||
output: Ephemeral container is created in pod (user=%ka.user.name pod=%ka.target.name ns=%ka.target.namespace ephemeral_container_name=%jevt.value[/requestObject/ephemeralContainers/0/name] ephemeral_container_image=%jevt.value[/requestObject/ephemeralContainers/0/image])
|
||||
priority: NOTICE
|
||||
source: k8s_audit
|
||||
@@ -239,7 +286,12 @@
|
||||
|
||||
- rule: Create Disallowed Namespace
|
||||
desc: Detect any attempt to create a namespace outside of a set of known namespaces
|
||||
condition: kevt and namespace and kcreate and not ka.target.name in (allowed_namespaces)
|
||||
condition: kevt and namespace and kcreate
|
||||
exceptions:
|
||||
- name: services
|
||||
fields: ka.target.name
|
||||
comps: in
|
||||
values: [allowed_namespaces]
|
||||
output: Disallowed namespace created (user=%ka.user.name ns=%ka.target.name)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -279,15 +331,16 @@
|
||||
k8s_image_list
|
||||
]
|
||||
|
||||
- macro: allowed_kube_namespace_pods
|
||||
condition: (ka.req.pod.containers.image.repository in (user_allowed_kube_namespace_image_list) or
|
||||
ka.req.pod.containers.image.repository in (allowed_kube_namespace_image_list))
|
||||
|
||||
# Detect any new pod created in the kube-system namespace
|
||||
- rule: Pod Created in Kube Namespace
|
||||
desc: Detect any attempt to create a pod in the kube-system or kube-public namespaces
|
||||
condition: kevt and pod and kcreate and ka.target.namespace in (kube-system, kube-public) and not allowed_kube_namespace_pods
|
||||
condition: kevt and pod and kcreate and ka.target.namespace in (kube-system, kube-public)
|
||||
output: Pod created in kube namespace (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
|
||||
exceptions:
|
||||
- name: images
|
||||
fields: ka.req.pod.containers.image.repository
|
||||
comps: in
|
||||
values: [user_allowed_kube_namespace_image_list, allowed_kube_namespace_image_list]
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
tags: [k8s]
|
||||
@@ -302,6 +355,9 @@
|
||||
- rule: Service Account Created in Kube Namespace
|
||||
desc: Detect any attempt to create a serviceaccount in the kube-system or kube-public namespaces
|
||||
condition: kevt and serviceaccount and kcreate and ka.target.namespace in (kube-system, kube-public) and response_successful and not trusted_sa
|
||||
exceptions:
|
||||
- name: accounts
|
||||
fields: [ka.target.namespace, ka.target.name]
|
||||
output: Service account created in kube namespace (user=%ka.user.name serviceaccount=%ka.target.name ns=%ka.target.namespace)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -314,6 +370,9 @@
|
||||
desc: Detect any attempt to modify/delete a ClusterRole/Role starting with system
|
||||
condition: kevt and (role or clusterrole) and (kmodify or kdelete) and (ka.target.name startswith "system:") and
|
||||
not ka.target.name in (system:coredns, system:managed-certificate-controller)
|
||||
exceptions:
|
||||
- name: roles
|
||||
fields: [ka.target.namespace, ka.target.name]
|
||||
output: System ClusterRole/Role modified or deleted (user=%ka.user.name role=%ka.target.name ns=%ka.target.namespace action=%ka.verb)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -324,6 +383,10 @@
|
||||
- rule: Attach to cluster-admin Role
|
||||
desc: Detect any attempt to create a ClusterRoleBinding to the cluster-admin user
|
||||
condition: kevt and clusterrolebinding and kcreate and ka.req.binding.role=cluster-admin
|
||||
exceptions:
|
||||
- name: subjects
|
||||
fields: ka.req.binding.subjects
|
||||
comps: in
|
||||
output: Cluster Role Binding to cluster-admin role (user=%ka.user.name subject=%ka.req.binding.subjects)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -332,6 +395,10 @@
|
||||
- rule: ClusterRole With Wildcard Created
|
||||
desc: Detect any attempt to create a Role/ClusterRole with wildcard resources or verbs
|
||||
condition: kevt and (role or clusterrole) and kcreate and (ka.req.role.rules.resources intersects ("*") or ka.req.role.rules.verbs intersects ("*"))
|
||||
exceptions:
|
||||
- name: roles
|
||||
fields: ka.target.name
|
||||
comps: in
|
||||
output: Created Role/ClusterRole with wildcard (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -344,6 +411,10 @@
|
||||
- rule: ClusterRole With Write Privileges Created
|
||||
desc: Detect any attempt to create a Role/ClusterRole that can perform write-related actions
|
||||
condition: kevt and (role or clusterrole) and kcreate and writable_verbs
|
||||
exceptions:
|
||||
- name: roles
|
||||
fields: ka.target.name
|
||||
comps: in
|
||||
output: Created Role/ClusterRole with write privileges (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules)
|
||||
priority: NOTICE
|
||||
source: k8s_audit
|
||||
@@ -352,6 +423,10 @@
|
||||
- rule: ClusterRole With Pod Exec Created
|
||||
desc: Detect any attempt to create a Role/ClusterRole that can exec to pods
|
||||
condition: kevt and (role or clusterrole) and kcreate and ka.req.role.rules.resources intersects ("pods/exec")
|
||||
exceptions:
|
||||
- name: roles
|
||||
fields: ka.target.name
|
||||
comps: in
|
||||
output: Created Role/ClusterRole with pod exec privileges (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -363,12 +438,16 @@
|
||||
- macro: consider_activity_events
|
||||
condition: (k8s_audit_always_true)
|
||||
|
||||
# Activity events don't have exceptions. They do define an empty
|
||||
# exceptions property just to avoid warnings when loading rules.
|
||||
|
||||
- macro: kactivity
|
||||
condition: (kevt and consider_activity_events)
|
||||
|
||||
- rule: K8s Deployment Created
|
||||
desc: Detect any attempt to create a deployment
|
||||
condition: (kactivity and kcreate and deployment and response_successful)
|
||||
exceptions:
|
||||
output: K8s Deployment Created (user=%ka.user.name deployment=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -377,6 +456,7 @@
|
||||
- rule: K8s Deployment Deleted
|
||||
desc: Detect any attempt to delete a deployment
|
||||
condition: (kactivity and kdelete and deployment and response_successful)
|
||||
exceptions:
|
||||
output: K8s Deployment Deleted (user=%ka.user.name deployment=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -385,6 +465,7 @@
|
||||
- rule: K8s Service Created
|
||||
desc: Detect any attempt to create a service
|
||||
condition: (kactivity and kcreate and service and response_successful)
|
||||
exceptions:
|
||||
output: K8s Service Created (user=%ka.user.name service=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -393,6 +474,7 @@
|
||||
- rule: K8s Service Deleted
|
||||
desc: Detect any attempt to delete a service
|
||||
condition: (kactivity and kdelete and service and response_successful)
|
||||
exceptions:
|
||||
output: K8s Service Deleted (user=%ka.user.name service=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -401,6 +483,7 @@
|
||||
- rule: K8s ConfigMap Created
|
||||
desc: Detect any attempt to create a configmap
|
||||
condition: (kactivity and kcreate and configmap and response_successful)
|
||||
exceptions:
|
||||
output: K8s ConfigMap Created (user=%ka.user.name configmap=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -409,6 +492,7 @@
|
||||
- rule: K8s ConfigMap Deleted
|
||||
desc: Detect any attempt to delete a configmap
|
||||
condition: (kactivity and kdelete and configmap and response_successful)
|
||||
exceptions:
|
||||
output: K8s ConfigMap Deleted (user=%ka.user.name configmap=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -417,6 +501,7 @@
|
||||
- rule: K8s Namespace Created
|
||||
desc: Detect any attempt to create a namespace
|
||||
condition: (kactivity and kcreate and namespace and response_successful)
|
||||
exceptions:
|
||||
output: K8s Namespace Created (user=%ka.user.name namespace=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -425,6 +510,7 @@
|
||||
- rule: K8s Namespace Deleted
|
||||
desc: Detect any attempt to delete a namespace
|
||||
condition: (kactivity and non_system_user and kdelete and namespace and response_successful)
|
||||
exceptions:
|
||||
output: K8s Namespace Deleted (user=%ka.user.name namespace=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -433,6 +519,7 @@
|
||||
- rule: K8s Serviceaccount Created
|
||||
desc: Detect any attempt to create a service account
|
||||
condition: (kactivity and kcreate and serviceaccount and response_successful)
|
||||
exceptions:
|
||||
output: K8s Serviceaccount Created (user=%ka.user.name user=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -441,6 +528,7 @@
|
||||
- rule: K8s Serviceaccount Deleted
|
||||
desc: Detect any attempt to delete a service account
|
||||
condition: (kactivity and kdelete and serviceaccount and response_successful)
|
||||
exceptions:
|
||||
output: K8s Serviceaccount Deleted (user=%ka.user.name user=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -449,6 +537,7 @@
|
||||
- rule: K8s Role/Clusterrole Created
|
||||
desc: Detect any attempt to create a cluster role/role
|
||||
condition: (kactivity and kcreate and (clusterrole or role) and response_successful)
|
||||
exceptions:
|
||||
output: K8s Cluster Role Created (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -457,6 +546,7 @@
|
||||
- rule: K8s Role/Clusterrole Deleted
|
||||
desc: Detect any attempt to delete a cluster role/role
|
||||
condition: (kactivity and kdelete and (clusterrole or role) and response_successful)
|
||||
exceptions:
|
||||
output: K8s Cluster Role Deleted (user=%ka.user.name role=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -465,6 +555,7 @@
|
||||
- rule: K8s Role/Clusterrolebinding Created
|
||||
desc: Detect any attempt to create a clusterrolebinding
|
||||
condition: (kactivity and kcreate and clusterrolebinding and response_successful)
|
||||
exceptions:
|
||||
output: K8s Cluster Role Binding Created (user=%ka.user.name binding=%ka.target.name subjects=%ka.req.binding.subjects role=%ka.req.binding.role resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -473,6 +564,7 @@
|
||||
- rule: K8s Role/Clusterrolebinding Deleted
|
||||
desc: Detect any attempt to delete a clusterrolebinding
|
||||
condition: (kactivity and kdelete and clusterrolebinding and response_successful)
|
||||
exceptions:
|
||||
output: K8s Cluster Role Binding Deleted (user=%ka.user.name binding=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -481,6 +573,7 @@
|
||||
- rule: K8s Secret Created
|
||||
desc: Detect any attempt to create a secret. Service account tokens are excluded.
|
||||
condition: (kactivity and kcreate and secret and ka.target.namespace!=kube-system and non_system_user and response_successful)
|
||||
exceptions:
|
||||
output: K8s Secret Created (user=%ka.user.name secret=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -489,6 +582,7 @@
|
||||
- rule: K8s Secret Deleted
|
||||
desc: Detect any attempt to delete a secret Service account tokens are excluded.
|
||||
condition: (kactivity and kdelete and secret and ka.target.namespace!=kube-system and non_system_user and response_successful)
|
||||
exceptions:
|
||||
output: K8s Secret Deleted (user=%ka.user.name secret=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
||||
priority: INFO
|
||||
source: k8s_audit
|
||||
@@ -507,6 +601,7 @@
|
||||
- rule: All K8s Audit Events
|
||||
desc: Match all K8s Audit Events
|
||||
condition: kall
|
||||
exceptions:
|
||||
output: K8s Audit Event received (user=%ka.user.name verb=%ka.verb uri=%ka.uri obj=%jevt.obj)
|
||||
priority: DEBUG
|
||||
source: k8s_audit
|
||||
@@ -521,11 +616,11 @@
|
||||
- list: full_admin_k8s_users
|
||||
items: ["admin", "kubernetes-admin", "kubernetes-admin@kubernetes", "kubernetes-admin@cluster.local", "minikube-user"]
|
||||
|
||||
# This rules detect an operation triggered by an user name that is
|
||||
# included in the list of those that are default administrators upon
|
||||
# cluster creation. This may signify a permission setting too broader.
|
||||
# As we can't check for role of the user on a general ka.* event, this
|
||||
# may or may not be an administrator. Customize the full_admin_k8s_users
|
||||
# This rules detect an operation triggered by an user name that is
|
||||
# included in the list of those that are default administrators upon
|
||||
# cluster creation. This may signify a permission setting too broader.
|
||||
# As we can't check for role of the user on a general ka.* event, this
|
||||
# may or may not be an administrator. Customize the full_admin_k8s_users
|
||||
# list to your needs, and activate at your discrection.
|
||||
|
||||
# # How to test:
|
||||
@@ -535,10 +630,14 @@
|
||||
- rule: Full K8s Administrative Access
|
||||
desc: Detect any k8s operation by a user name that may be an administrator with full access.
|
||||
condition: >
|
||||
kevt
|
||||
and non_system_user
|
||||
kevt
|
||||
and non_system_user
|
||||
and ka.user.name in (full_admin_k8s_users)
|
||||
and not allowed_full_admin_users
|
||||
exceptions:
|
||||
- name: user_names
|
||||
fields: ka.user.name
|
||||
comps: in
|
||||
output: K8s Operation performed by full admin user (user=%ka.user.name target=%ka.target.name/%ka.target.resource verb=%ka.verb uri=%ka.uri resp=%ka.response.code)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
@@ -572,10 +671,13 @@
|
||||
desc: Detect any attempt to create an ingress without TLS certification.
|
||||
condition: >
|
||||
(kactivity and kcreate and ingress and response_successful and not ingress_tls)
|
||||
exceptions:
|
||||
- name: ingresses
|
||||
fields: [ka.target.namespace, ka.target.name]
|
||||
output: >
|
||||
K8s Ingress Without TLS Cert Created (user=%ka.user.name ingress=%ka.target.name
|
||||
namespace=%ka.target.namespace)
|
||||
source: k8s_audit
|
||||
source: k8s_audit
|
||||
priority: WARNING
|
||||
tags: [k8s, network]
|
||||
|
||||
@@ -598,11 +700,15 @@
|
||||
desc: >
|
||||
Detect a node successfully joined the cluster outside of the list of allowed nodes.
|
||||
condition: >
|
||||
kevt and node
|
||||
and kcreate
|
||||
and response_successful
|
||||
and not allow_all_k8s_nodes
|
||||
and not ka.target.name in (allowed_k8s_nodes)
|
||||
kevt and node
|
||||
and kcreate
|
||||
and response_successful
|
||||
and not allow_all_k8s_nodes
|
||||
exceptions:
|
||||
- name: nodes
|
||||
fields: ka.target.name
|
||||
comps: in
|
||||
values: [allowed_k8s_nodes]
|
||||
output: Node not in allowed list successfully joined the cluster (user=%ka.user.name node=%ka.target.name)
|
||||
priority: ERROR
|
||||
source: k8s_audit
|
||||
@@ -612,11 +718,15 @@
|
||||
desc: >
|
||||
Detect an unsuccessful attempt to join the cluster for a node not in the list of allowed nodes.
|
||||
condition: >
|
||||
kevt and node
|
||||
and kcreate
|
||||
and not response_successful
|
||||
and not allow_all_k8s_nodes
|
||||
and not ka.target.name in (allowed_k8s_nodes)
|
||||
kevt and node
|
||||
and kcreate
|
||||
and not response_successful
|
||||
and not allow_all_k8s_nodes
|
||||
exceptions:
|
||||
- name: nodes
|
||||
fields: ka.target.name
|
||||
comps: in
|
||||
values: [allowed_k8s_nodes]
|
||||
output: Node not in allowed list tried unsuccessfully to join the cluster (user=%ka.user.name node=%ka.target.name reason=%ka.response.reason)
|
||||
priority: WARNING
|
||||
source: k8s_audit
|
||||
|
||||
@@ -675,7 +675,8 @@ class FalcoTest(Test):
|
||||
self.check_rules_warnings(res)
|
||||
if len(self.rules_events) > 0:
|
||||
self.check_rules_events(res)
|
||||
self.check_detections(res)
|
||||
if len(self.validate_rules_file) == 0:
|
||||
self.check_detections(res)
|
||||
if len(self.detect_counts) > 0:
|
||||
self.check_detections_by_rule(res)
|
||||
self.check_json_output(res)
|
||||
|
||||
@@ -262,6 +262,7 @@ trace_files: !mux
|
||||
invalid_not_yaml:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rules content is not yaml
|
||||
---
|
||||
This is not yaml
|
||||
@@ -273,6 +274,7 @@ trace_files: !mux
|
||||
invalid_not_array:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rules content is not yaml array of objects
|
||||
---
|
||||
foo: bar
|
||||
@@ -284,6 +286,7 @@ trace_files: !mux
|
||||
invalid_array_item_not_object:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Unexpected element of type string. Each element should be a yaml associative array.
|
||||
---
|
||||
- foo
|
||||
@@ -292,20 +295,10 @@ trace_files: !mux
|
||||
- rules/invalid_array_item_not_object.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_unexpected object:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Unknown rule object: {foo="bar"}
|
||||
---
|
||||
- foo: bar
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_unexpected_object.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_engine_version_not_number:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Value of required_engine_version must be a number
|
||||
---
|
||||
- required_engine_version: not-a-number
|
||||
@@ -317,6 +310,7 @@ trace_files: !mux
|
||||
invalid_yaml_parse_error:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
mapping values are not allowed in this context
|
||||
---
|
||||
this : is : not : yaml
|
||||
@@ -328,6 +322,7 @@ trace_files: !mux
|
||||
invalid_list_without_items:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
List must have property items
|
||||
---
|
||||
- list: bad_list
|
||||
@@ -340,6 +335,7 @@ trace_files: !mux
|
||||
invalid_macro_without_condition:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Macro must have property condition
|
||||
---
|
||||
- macro: bad_macro
|
||||
@@ -352,6 +348,7 @@ trace_files: !mux
|
||||
invalid_rule_without_output:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule must have property output
|
||||
---
|
||||
- rule: no output rule
|
||||
@@ -359,6 +356,8 @@ trace_files: !mux
|
||||
condition: evt.type=fork
|
||||
priority: INFO
|
||||
---
|
||||
1 warnings:
|
||||
Rule no output rule: consider adding an exceptions property to define supported exceptions fields
|
||||
validate_rules_file:
|
||||
- rules/invalid_rule_without_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -366,7 +365,8 @@ trace_files: !mux
|
||||
invalid_append_rule_without_condition:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Rule must have property condition
|
||||
1 errors:
|
||||
Rule must have exceptions or condition property
|
||||
---
|
||||
- rule: no condition rule
|
||||
append: true
|
||||
@@ -378,6 +378,7 @@ trace_files: !mux
|
||||
invalid_append_macro_dangling:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Macro dangling append has 'append' key but no macro by that name already exists
|
||||
---
|
||||
- macro: dangling append
|
||||
@@ -391,6 +392,7 @@ trace_files: !mux
|
||||
invalid_list_append_dangling:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
List my_list has 'append' key but no list by that name already exists
|
||||
---
|
||||
- list: my_list
|
||||
@@ -404,12 +406,15 @@ trace_files: !mux
|
||||
invalid_rule_append_dangling:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule my_rule has 'append' key but no rule by that name already exists
|
||||
---
|
||||
- rule: my_rule
|
||||
condition: evt.type=open
|
||||
append: true
|
||||
---
|
||||
1 warnings:
|
||||
Rule my_rule: consider adding an exceptions property to define supported exceptions fields
|
||||
validate_rules_file:
|
||||
- rules/rule_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -418,7 +423,8 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_contains: |+
|
||||
.*invalid_base_macro.yaml: Ok
|
||||
.*invalid_overwrite_macro.yaml: Compilation error when compiling "foo": Undefined macro 'foo' used in filter.
|
||||
.*invalid_overwrite_macro.yaml: 1 errors:
|
||||
Compilation error when compiling "foo": Undefined macro 'foo' used in filter.
|
||||
---
|
||||
- macro: some macro
|
||||
condition: foo
|
||||
@@ -433,7 +439,8 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_contains: |+
|
||||
.*invalid_base_macro.yaml: Ok
|
||||
.*invalid_append_macro.yaml: Compilation error when compiling "evt.type=execve foo": 17: syntax error, unexpected 'foo', expecting 'or', 'and'
|
||||
.*invalid_append_macro.yaml: 1 errors:
|
||||
Compilation error when compiling "evt.type=execve foo": 17: syntax error, unexpected 'foo', expecting 'or', 'and'
|
||||
---
|
||||
- macro: some macro
|
||||
condition: evt.type=execve
|
||||
@@ -450,6 +457,7 @@ trace_files: !mux
|
||||
invalid_overwrite_macro_multiple_docs:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Compilation error when compiling "foo": Undefined macro 'foo' used in filter.
|
||||
---
|
||||
- macro: some macro
|
||||
@@ -463,6 +471,7 @@ trace_files: !mux
|
||||
invalid_append_macro_multiple_docs:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Compilation error when compiling "evt.type=execve foo": 17: syntax error, unexpected 'foo', expecting 'or', 'and'
|
||||
---
|
||||
- macro: some macro
|
||||
@@ -480,7 +489,8 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_contains: |+
|
||||
.*invalid_base_rule.yaml: Ok
|
||||
.*invalid_overwrite_rule.yaml: Undefined macro 'bar' used in filter.
|
||||
.*invalid_overwrite_rule.yaml: 1 errors:
|
||||
Undefined macro 'bar' used in filter.
|
||||
---
|
||||
- rule: some rule
|
||||
desc: some desc
|
||||
@@ -498,7 +508,8 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_contains: |+
|
||||
.*invalid_base_rule.yaml: Ok
|
||||
.*invalid_append_rule.yaml: Compilation error when compiling "evt.type=open bar": 15: syntax error, unexpected 'bar', expecting 'or', 'and'
|
||||
.*invalid_append_rule.yaml: 1 errors:
|
||||
Compilation error when compiling "evt.type=open bar": 15: syntax error, unexpected 'bar', expecting 'or', 'and'
|
||||
---
|
||||
- rule: some rule
|
||||
desc: some desc
|
||||
@@ -521,6 +532,7 @@ trace_files: !mux
|
||||
invalid_overwrite_rule_multiple_docs:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Undefined macro 'bar' used in filter.
|
||||
---
|
||||
- rule: some rule
|
||||
@@ -530,6 +542,9 @@ trace_files: !mux
|
||||
priority: INFO
|
||||
append: false
|
||||
---
|
||||
2 warnings:
|
||||
Rule some rule: consider adding an exceptions property to define supported exceptions fields
|
||||
Rule some rule: consider adding an exceptions property to define supported exceptions fields
|
||||
validate_rules_file:
|
||||
- rules/invalid_overwrite_rule_multiple_docs.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -552,6 +567,9 @@ trace_files: !mux
|
||||
priority: INFO
|
||||
append: true
|
||||
---
|
||||
2 warnings:
|
||||
Rule some rule: consider adding an exceptions property to define supported exceptions fields
|
||||
Rule some rule: consider adding an exceptions property to define supported exceptions fields
|
||||
validate_rules_file:
|
||||
- rules/invalid_append_rule_multiple_docs.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -559,6 +577,7 @@ trace_files: !mux
|
||||
invalid_missing_rule_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule name is empty
|
||||
---
|
||||
- rule:
|
||||
@@ -573,6 +592,7 @@ trace_files: !mux
|
||||
invalid_missing_list_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
List name is empty
|
||||
---
|
||||
- list:
|
||||
@@ -585,6 +605,7 @@ trace_files: !mux
|
||||
invalid_missing_macro_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Macro name is empty
|
||||
---
|
||||
- macro:
|
||||
@@ -596,8 +617,19 @@ trace_files: !mux
|
||||
|
||||
invalid_rule_output:
|
||||
exit_status: 1
|
||||
stderr_contains: "Runtime error: Error loading rules:.* Invalid output format 'An open was seen %not_a_real_field': 'invalid formatting token not_a_real_field'. Exiting."
|
||||
rules_file:
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Invalid output format 'An open was seen %not_a_real_field': 'invalid formatting token not_a_real_field'
|
||||
---
|
||||
- rule: rule_with_invalid_output
|
||||
desc: A rule with an invalid output field
|
||||
condition: evt.type=open
|
||||
output: "An open was seen %not_a_real_field"
|
||||
priority: WARNING
|
||||
---
|
||||
1 warnings:
|
||||
Rule rule_with_invalid_output: consider adding an exceptions property to define supported exceptions fields
|
||||
validate_rules_file:
|
||||
- rules/invalid_rule_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
@@ -1117,7 +1149,7 @@ trace_files: !mux
|
||||
|
||||
skip_unknown_noevt:
|
||||
detect: False
|
||||
stdout_contains: Skipping rule "Contains Unknown Event And Skipping" that contains unknown filter proc.nobody
|
||||
stdout_contains: Skipping rule "Contains Unknown Event And Skipping". contains unknown filter proc.nobody
|
||||
rules_file:
|
||||
- rules/skip_unknown_evt.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -1130,14 +1162,33 @@ trace_files: !mux
|
||||
|
||||
skip_unknown_error:
|
||||
exit_status: 1
|
||||
stderr_contains: Rule "Contains Unknown Event And Not Skipping" contains unknown filter proc.nobody. Exiting.
|
||||
stderr_contains: |+
|
||||
Could not load rules file.*skip_unknown_error.yaml: 1 errors:
|
||||
rule "Contains Unknown Event And Not Skipping". contains unknown filter proc.nobody
|
||||
---
|
||||
- rule: Contains Unknown Event And Not Skipping
|
||||
desc: Contains an unknown event
|
||||
condition: proc.nobody=cat
|
||||
output: Never
|
||||
skip-if-unknown-filter: false
|
||||
priority: INFO
|
||||
---
|
||||
rules_file:
|
||||
- rules/skip_unknown_error.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
skip_unknown_unspec_error:
|
||||
exit_status: 1
|
||||
stderr_contains: Rule "Contains Unknown Event And Unspecified" contains unknown filter proc.nobody. Exiting.
|
||||
stderr_contains: |+
|
||||
Could not load rules file .*skip_unknown_unspec.yaml: 1 errors:
|
||||
rule "Contains Unknown Event And Unspecified". contains unknown filter proc.nobody
|
||||
---
|
||||
- rule: Contains Unknown Event And Unspecified
|
||||
desc: Contains an unknown event
|
||||
condition: proc.nobody=cat
|
||||
output: Never
|
||||
priority: INFO
|
||||
---
|
||||
rules_file:
|
||||
- rules/skip_unknown_unspec.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -1226,3 +1277,10 @@ trace_files: !mux
|
||||
trace_file: trace_files/cat_write.scap
|
||||
stdout_contains: "2016-08-04T16:17:57.882054739\\+0000: Warning An open was seen"
|
||||
stderr_contains: "^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\+0000"
|
||||
|
||||
unknown_source:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/unknown_source.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
323
test/falco_tests_exceptions.yaml
Normal file
323
test/falco_tests_exceptions.yaml
Normal file
@@ -0,0 +1,323 @@
|
||||
#
|
||||
# Copyright (C) 2016-2020 The Falco Authors..
|
||||
#
|
||||
# This file is part of falco.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
trace_files: !mux
|
||||
|
||||
rule_exception_no_fields:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item ex1: must have fields property with a list of fields
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
priority: error
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_no_fields.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_no_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item must have name property
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- fields: [proc.name, fd.filename]
|
||||
priority: error
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_no_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_no_name:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item must have name property
|
||||
---
|
||||
- rule: My Rule
|
||||
exceptions:
|
||||
- values:
|
||||
- [nginx, /tmp/foo]
|
||||
append: true
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/exceptions/append_item_no_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_unknown_fields:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item ex1: field name not.exist is not a supported filter field
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [not.exist]
|
||||
priority: error
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_unknown_fields.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_comps_fields_len_mismatch:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item ex1: fields and comps lists must have equal length
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
comps: [=]
|
||||
priority: error
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_comps_fields_len_mismatch.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_unknown_comp:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Rule exception item ex1: comparison operator no-comp is not a supported comparison operator
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
comps: [=, no-comp]
|
||||
priority: error
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_unknown_comp.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_fields_values_len_mismatch:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Exception item ex1: fields and values lists must have equal length
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
values:
|
||||
- [nginx]
|
||||
priority: error
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/exceptions/item_fields_values_len_mismatch.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_fields_values_len_mismatch:
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
1 errors:
|
||||
Exception item ex1: fields and values lists must have equal length
|
||||
---
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
priority: error
|
||||
|
||||
- rule: My Rule
|
||||
exceptions:
|
||||
- name: ex1
|
||||
values:
|
||||
- [nginx]
|
||||
append: true
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/exceptions/append_item_fields_values_len_mismatch.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_item_not_in_rule:
|
||||
exit_status: 0
|
||||
stderr_contains: |+
|
||||
1 warnings:
|
||||
Rule My Rule with append=true: no set of fields matching name ex2
|
||||
validate_rules_file:
|
||||
- rules/exceptions/append_item_not_in_rule.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_without_exception:
|
||||
exit_status: 0
|
||||
stderr_contains: |+
|
||||
1 warnings:
|
||||
Rule My Rule: consider adding an exceptions property to define supported exceptions fields
|
||||
validate_rules_file:
|
||||
- rules/exceptions/rule_without_exception.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_no_values:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_no_values.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_one_value:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_one_value.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_one_value:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_append_one_value.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_second_value:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_second_value.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_second_value:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_append_second_value.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_second_item:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_second_item.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_second_item:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_append_second_item.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_third_item:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_third_item.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_third_item:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_append_third_item.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_quoted:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_quoted.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_multiple_values:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_append_multiple.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_comp:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_comp.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_append_comp:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_append_comp.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_values_listref:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_values_listref.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_values_listref_noparens:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_values_listref_noparens.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_values_list:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_values_list.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_single_field:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_single_field.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
rule_exception_single_field_append:
|
||||
detect: False
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/exceptions/rule_exception_single_field_append.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
priority: error
|
||||
|
||||
- rule: My Rule
|
||||
exceptions:
|
||||
- name: ex1
|
||||
values:
|
||||
- [nginx]
|
||||
append: true
|
||||
30
test/rules/exceptions/append_item_no_name.yaml
Normal file
30
test/rules/exceptions/append_item_no_name.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
priority: error
|
||||
|
||||
- rule: My Rule
|
||||
exceptions:
|
||||
- values:
|
||||
- [nginx, /tmp/foo]
|
||||
append: true
|
||||
31
test/rules/exceptions/append_item_not_in_rule.yaml
Normal file
31
test/rules/exceptions/append_item_not_in_rule.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
priority: error
|
||||
|
||||
- rule: My Rule
|
||||
exceptions:
|
||||
- name: ex2
|
||||
values:
|
||||
- [apache, /tmp]
|
||||
append: true
|
||||
25
test/rules/exceptions/item_comps_fields_len_mismatch.yaml
Normal file
25
test/rules/exceptions/item_comps_fields_len_mismatch.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
comps: [=]
|
||||
priority: error
|
||||
26
test/rules/exceptions/item_fields_values_len_mismatch.yaml
Normal file
26
test/rules/exceptions/item_fields_values_len_mismatch.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
values:
|
||||
- [nginx]
|
||||
priority: error
|
||||
23
test/rules/exceptions/item_no_fields.yaml
Normal file
23
test/rules/exceptions/item_no_fields.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
priority: error
|
||||
23
test/rules/exceptions/item_no_name.yaml
Normal file
23
test/rules/exceptions/item_no_name.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- fields: [proc.name, fd.filename]
|
||||
priority: error
|
||||
25
test/rules/exceptions/item_unknown_comp.yaml
Normal file
25
test/rules/exceptions/item_unknown_comp.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [proc.name, fd.filename]
|
||||
comps: [=, no-comp]
|
||||
priority: error
|
||||
24
test/rules/exceptions/item_unknown_fields.yaml
Normal file
24
test/rules/exceptions/item_unknown_fields.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
exceptions:
|
||||
- name: ex1
|
||||
fields: [not.exist]
|
||||
priority: error
|
||||
38
test/rules/exceptions/rule_exception_append_comp.yaml
Normal file
38
test/rules/exceptions/rule_exception_append_comp.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
- name: proc_name_contains
|
||||
fields: [proc.name]
|
||||
comps: [contains]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_name_contains
|
||||
values:
|
||||
- [cat]
|
||||
append: true
|
||||
42
test/rules/exceptions/rule_exception_append_multiple.yaml
Normal file
42
test/rules/exceptions/rule_exception_append_multiple.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
values:
|
||||
- [not-cat]
|
||||
append: true
|
||||
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
values:
|
||||
- [cat]
|
||||
append: true
|
||||
37
test/rules/exceptions/rule_exception_append_one_value.yaml
Normal file
37
test/rules/exceptions/rule_exception_append_one_value.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
values:
|
||||
- [cat]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
values:
|
||||
- [cat]
|
||||
append: true
|
||||
41
test/rules/exceptions/rule_exception_append_second_item.yaml
Normal file
41
test/rules/exceptions/rule_exception_append_second_item.yaml
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
values:
|
||||
- [not-cat]
|
||||
- name: proc_name_cmdline
|
||||
values:
|
||||
- [cat, "cat /dev/null"]
|
||||
- name: proc_name_cmdline_pname
|
||||
values:
|
||||
- [not-cat, "cat /dev/null", bash]
|
||||
append: true
|
||||
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_name_cmdline
|
||||
values:
|
||||
- [not-cat, not-cat]
|
||||
- [cat, "cat /dev/null"]
|
||||
append: true
|
||||
41
test/rules/exceptions/rule_exception_append_third_item.yaml
Normal file
41
test/rules/exceptions/rule_exception_append_third_item.yaml
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
values:
|
||||
- [not-cat]
|
||||
- name: proc_name_cmdline
|
||||
values:
|
||||
- [not-cat, "cat /dev/null"]
|
||||
- name: proc_name_cmdline_pname
|
||||
values:
|
||||
- [cat, "cat /dev/null", bash]
|
||||
append: true
|
||||
34
test/rules/exceptions/rule_exception_comp.yaml
Normal file
34
test/rules/exceptions/rule_exception_comp.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
- name: proc_name_contains
|
||||
fields: [proc.name]
|
||||
comps: [contains]
|
||||
values:
|
||||
- [cat]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
|
||||
28
test/rules/exceptions/rule_exception_no_values.yaml
Normal file
28
test/rules/exceptions/rule_exception_no_values.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
30
test/rules/exceptions/rule_exception_one_value.yaml
Normal file
30
test/rules/exceptions/rule_exception_one_value.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
values:
|
||||
- [cat]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
36
test/rules/exceptions/rule_exception_quoted.yaml
Normal file
36
test/rules/exceptions/rule_exception_quoted.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_name_cmdline
|
||||
values:
|
||||
- [not-cat, not-cat]
|
||||
- [cat, '"cat /dev/null"']
|
||||
append: true
|
||||
34
test/rules/exceptions/rule_exception_second_item.yaml
Normal file
34
test/rules/exceptions/rule_exception_second_item.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
values:
|
||||
- [not-cat]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
values:
|
||||
- [cat, "cat /dev/null"]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
values:
|
||||
- [not-cat, "cat /dev/null", bash]
|
||||
priority: WARNING
|
||||
32
test/rules/exceptions/rule_exception_second_value.yaml
Normal file
32
test/rules/exceptions/rule_exception_second_value.yaml
Normal file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
values:
|
||||
- [not-cat, not-cat]
|
||||
- [cat, "cat /dev/null"]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
priority: WARNING
|
||||
|
||||
30
test/rules/exceptions/rule_exception_single_field.yaml
Normal file
30
test/rules/exceptions/rule_exception_single_field.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_cmdline
|
||||
fields: proc.cmdline
|
||||
comps: in
|
||||
values:
|
||||
- cat /dev/zero
|
||||
- "cat /dev/null"
|
||||
priority: WARNING
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_cmdline
|
||||
fields: proc.cmdline
|
||||
comps: in
|
||||
values:
|
||||
- cat /dev/zero
|
||||
priority: WARNING
|
||||
|
||||
- rule: Open From Cat
|
||||
exceptions:
|
||||
- name: proc_cmdline
|
||||
values:
|
||||
- "cat /dev/null"
|
||||
append: true
|
||||
|
||||
|
||||
34
test/rules/exceptions/rule_exception_third_item.yaml
Normal file
34
test/rules/exceptions/rule_exception_third_item.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name
|
||||
fields: [proc.name]
|
||||
values:
|
||||
- [not-cat]
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
values:
|
||||
- [not-cat, "cat /dev/null"]
|
||||
- name: proc_name_cmdline_pname
|
||||
fields: [proc.name, proc.cmdline, proc.pname]
|
||||
values:
|
||||
- [cat, "cat /dev/null", bash]
|
||||
priority: WARNING
|
||||
29
test/rules/exceptions/rule_exception_values_list.yaml
Normal file
29
test/rules/exceptions/rule_exception_values_list.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
comps: [=, in]
|
||||
values:
|
||||
- [cat, [cat /dev/zero, "cat /dev/null"]]
|
||||
priority: WARNING
|
||||
|
||||
32
test/rules/exceptions/rule_exception_values_listref.yaml
Normal file
32
test/rules/exceptions/rule_exception_values_listref.yaml
Normal file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
- list: cat_cmdlines
|
||||
items: [cat /dev/zero, "cat /dev/null"]
|
||||
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
comps: [=, in]
|
||||
values:
|
||||
- [cat, (cat_cmdlines)]
|
||||
priority: WARNING
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
- list: cat_cmdlines
|
||||
items: [cat /dev/zero, "cat /dev/null"]
|
||||
|
||||
- rule: Open From Cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
exceptions:
|
||||
- name: proc_name_cmdline
|
||||
fields: [proc.name, proc.cmdline]
|
||||
comps: [=, in]
|
||||
values:
|
||||
- [cat, cat_cmdlines]
|
||||
priority: WARNING
|
||||
|
||||
21
test/rules/exceptions/rule_without_exception.yaml
Normal file
21
test/rules/exceptions/rule_without_exception.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- rule: My Rule
|
||||
desc: Some desc
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: Some output
|
||||
priority: error
|
||||
@@ -1 +0,0 @@
|
||||
- foo: bar
|
||||
31
test/rules/unknown_source.yaml
Normal file
31
test/rules/unknown_source.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# Copyright (C) 2021 The Falco Authors.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
- macro: Macro with unknown source
|
||||
condition: some other unknown filter
|
||||
source: unknown-source
|
||||
|
||||
- rule: Rule with unknown source
|
||||
condition: some unknown filter
|
||||
output: some unknown output
|
||||
priority: INFO
|
||||
source: unknown-source
|
||||
|
||||
- rule: open_from_cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
@@ -98,7 +98,7 @@ function run_tests() {
|
||||
# as we're watching the return status when running avocado.
|
||||
set +e
|
||||
TEST_RC=0
|
||||
suites=($SCRIPTDIR/falco_traces.yaml $SCRIPTDIR/falco_tests.yaml $SCRIPTDIR/falco_k8s_audit_tests.yaml $SCRIPTDIR/falco_tests_psp.yaml)
|
||||
suites=($SCRIPTDIR/falco_traces.yaml $SCRIPTDIR/falco_tests.yaml $SCRIPTDIR/falco_k8s_audit_tests.yaml $SCRIPTDIR/falco_tests_psp.yaml $SCRIPTDIR/falco_tests_exceptions.yaml)
|
||||
|
||||
if [ "$SKIP_PACKAGES_TESTS" = false ] ; then
|
||||
suites+=($SCRIPTDIR/falco_tests_package.yaml)
|
||||
|
||||
@@ -302,31 +302,9 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_ev
|
||||
}
|
||||
|
||||
unique_ptr<struct rule_result> res(new rule_result());
|
||||
res->source = "syscall";
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_ls_semaphore);
|
||||
lua_getglobal(m_ls, lua_on_event.c_str());
|
||||
if(lua_isfunction(m_ls, -1))
|
||||
{
|
||||
lua_pushnumber(m_ls, ev->get_check_id());
|
||||
|
||||
if(lua_pcall(m_ls, 1, 3, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error invoking function output: " + string(lerr);
|
||||
throw falco_exception(err);
|
||||
}
|
||||
res->evt = ev;
|
||||
const char *p = lua_tostring(m_ls, -3);
|
||||
res->rule = p;
|
||||
res->source = "syscall";
|
||||
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -2);
|
||||
res->format = lua_tostring(m_ls, -1);
|
||||
lua_pop(m_ls, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw falco_exception("No function " + lua_on_event + " found in lua compiler module");
|
||||
}
|
||||
populate_rule_result(res, ev);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -350,33 +328,50 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_k8s_audit_event(json
|
||||
}
|
||||
|
||||
unique_ptr<struct rule_result> res(new rule_result());
|
||||
res->source = "k8s_audit";
|
||||
|
||||
populate_rule_result(res, ev);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void falco_engine::populate_rule_result(unique_ptr<struct rule_result> &res, gen_event *ev)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_ls_semaphore);
|
||||
lua_getglobal(m_ls, lua_on_event.c_str());
|
||||
if(lua_isfunction(m_ls, -1))
|
||||
{
|
||||
lua_pushnumber(m_ls, ev->get_check_id());
|
||||
|
||||
if(lua_pcall(m_ls, 1, 3, 0) != 0)
|
||||
if(lua_pcall(m_ls, 1, 4, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error invoking function output: " + string(lerr);
|
||||
throw falco_exception(err);
|
||||
}
|
||||
res->evt = ev;
|
||||
const char *p = lua_tostring(m_ls, -3);
|
||||
const char *p = lua_tostring(m_ls, -4);
|
||||
res->rule = p;
|
||||
res->source = "k8s_audit";
|
||||
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -2);
|
||||
res->format = lua_tostring(m_ls, -1);
|
||||
lua_pop(m_ls, 3);
|
||||
res->evt = ev;
|
||||
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -3);
|
||||
res->format = lua_tostring(m_ls, -2);
|
||||
|
||||
// Exception fields are passed back as a table
|
||||
lua_pushnil(m_ls); /* first key */
|
||||
while (lua_next(m_ls, -2) != 0) {
|
||||
// key is at index -2, value is at index
|
||||
// -1. We want the keys.
|
||||
res->exception_fields.insert(luaL_checkstring(m_ls, -2));
|
||||
|
||||
// Remove value, keep key for next iteration
|
||||
lua_pop(m_ls, 1);
|
||||
}
|
||||
|
||||
lua_pop(m_ls, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw falco_exception("No function " + lua_on_event + " found in lua compiler module");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool falco_engine::parse_k8s_audit_json(nlohmann::json &j, std::list<json_event> &evts, bool top)
|
||||
|
||||
@@ -160,6 +160,7 @@ public:
|
||||
std::string source;
|
||||
falco_common::priority_type priority_num;
|
||||
std::string format;
|
||||
std::set<std::string> exception_fields;
|
||||
};
|
||||
|
||||
//
|
||||
@@ -262,6 +263,8 @@ private:
|
||||
std::unique_ptr<falco_sinsp_ruleset> m_sinsp_rules;
|
||||
std::unique_ptr<falco_ruleset> m_k8s_audit_rules;
|
||||
|
||||
void populate_rule_result(unique_ptr<struct rule_result> &res, gen_event *ev);
|
||||
|
||||
//
|
||||
// Here's how the sampling ratio and multiplier influence
|
||||
// whether or not an event is dropped in
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
// The version of rules/filter fields/etc supported by this falco
|
||||
// engine.
|
||||
#define FALCO_ENGINE_VERSION (7)
|
||||
#define FALCO_ENGINE_VERSION (8)
|
||||
|
||||
// This is the result of running "falco --list -N | sha256sum" and
|
||||
// represents the fields supported by this version of falco. It's used
|
||||
|
||||
@@ -60,25 +60,32 @@ int falco_formats::lua_formatter(lua_State *ls)
|
||||
{
|
||||
sinsp_evt_formatter *formatter;
|
||||
formatter = new sinsp_evt_formatter(s_inspector, format);
|
||||
lua_pushnil(ls);
|
||||
lua_pushlightuserdata(ls, formatter);
|
||||
}
|
||||
else
|
||||
{
|
||||
json_event_formatter *formatter;
|
||||
formatter = new json_event_formatter(s_engine->json_factory(), format);
|
||||
lua_pushnil(ls);
|
||||
lua_pushlightuserdata(ls, formatter);
|
||||
}
|
||||
}
|
||||
catch(sinsp_exception &e)
|
||||
catch(exception &e)
|
||||
{
|
||||
luaL_error(ls, "Invalid output format '%s': '%s'", format.c_str(), e.what());
|
||||
}
|
||||
catch(falco_exception &e)
|
||||
{
|
||||
luaL_error(ls, "Invalid output format '%s': '%s'", format.c_str(), e.what());
|
||||
std::ostringstream os;
|
||||
|
||||
os << "Invalid output format '"
|
||||
<< format
|
||||
<< "': '"
|
||||
<< e.what()
|
||||
<< "'";
|
||||
|
||||
lua_pushstring(ls, os.str().c_str());
|
||||
lua_pushnil(ls);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
int falco_formats::lua_free_formatter(lua_State *ls)
|
||||
|
||||
@@ -126,6 +126,31 @@ function set_output(output_format, state)
|
||||
end
|
||||
end
|
||||
|
||||
-- This should be keep in sync with parser.lua
|
||||
defined_comp_operators = {
|
||||
["="]=1,
|
||||
["=="] = 1,
|
||||
["!="] = 1,
|
||||
["<="] = 1,
|
||||
[">="] = 1,
|
||||
["<"] = 1,
|
||||
[">"] = 1,
|
||||
["contains"] = 1,
|
||||
["icontains"] = 1,
|
||||
["glob"] = 1,
|
||||
["startswith"] = 1,
|
||||
["endswith"] = 1,
|
||||
["in"] = 1,
|
||||
["intersects"] = 1,
|
||||
["pmatch"] = 1
|
||||
}
|
||||
|
||||
defined_list_comp_operators = {
|
||||
["in"] = 1,
|
||||
["intersects"] = 1,
|
||||
["pmatch"] = 1
|
||||
}
|
||||
|
||||
-- Note that the rules_by_name and rules_by_idx refer to the same rule
|
||||
-- object. The by_name index is used for things like describing rules,
|
||||
-- and the by_idx index is used to map the relational node index back
|
||||
@@ -253,19 +278,126 @@ function get_lines(rules_lines, row, num_lines)
|
||||
return ret
|
||||
end
|
||||
|
||||
function quote_item(item)
|
||||
|
||||
-- Add quotes if the string contains spaces and doesn't start/end
|
||||
-- w/ quotes
|
||||
if string.find(item, " ") then
|
||||
if string.sub(item, 1, 1) ~= "'" and string.sub(item, 1, 1) ~= '"' then
|
||||
item = "\""..item.."\""
|
||||
end
|
||||
end
|
||||
|
||||
return item
|
||||
end
|
||||
|
||||
function paren_item(item)
|
||||
if string.sub(item, 1, 1) ~= "(" then
|
||||
item = "("..item..")"
|
||||
end
|
||||
|
||||
return item
|
||||
end
|
||||
|
||||
function build_error(rules_lines, row, num_lines, err)
|
||||
local ret = err.."\n---\n"..get_lines(rules_lines, row, num_lines).."---"
|
||||
|
||||
return ret
|
||||
return {ret}
|
||||
end
|
||||
|
||||
function build_error_with_context(ctx, err)
|
||||
local ret = err.."\n---\n"..ctx.."---"
|
||||
return ret
|
||||
return {ret}
|
||||
end
|
||||
|
||||
function validate_exception_item_multi_fields(eitem, context)
|
||||
|
||||
local name = eitem['name']
|
||||
local fields = eitem['fields']
|
||||
local values = eitem['values']
|
||||
local comps = eitem['comps']
|
||||
|
||||
if comps == nil then
|
||||
comps = {}
|
||||
for c=1,#fields do
|
||||
table.insert(comps, "=")
|
||||
end
|
||||
eitem['comps'] = comps
|
||||
else
|
||||
if #fields ~= #comps then
|
||||
return false, build_error_with_context(context, "Rule exception item "..name..": fields and comps lists must have equal length"), warnings
|
||||
end
|
||||
end
|
||||
for k, fname in ipairs(fields) do
|
||||
if not is_defined_filter(fname) then
|
||||
return false, build_error_with_context(context, "Rule exception item "..name..": field name "..fname.." is not a supported filter field"), warnings
|
||||
end
|
||||
end
|
||||
for k, comp in ipairs(comps) do
|
||||
if defined_comp_operators[comp] == nil then
|
||||
return false, build_error_with_context(context, "Rule exception item "..name..": comparison operator "..comp.." is not a supported comparison operator"), warnings
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function validate_exception_item_single_field(eitem, context)
|
||||
|
||||
local name = eitem['name']
|
||||
local fields = eitem['fields']
|
||||
local values = eitem['values']
|
||||
local comps = eitem['comps']
|
||||
|
||||
if comps == nil then
|
||||
eitem['comps'] = "in"
|
||||
comps = eitem['comps']
|
||||
else
|
||||
if type(fields) ~= "string" or type(comps) ~= "string" then
|
||||
return false, build_error_with_context(context, "Rule exception item "..name..": fields and comps must both be strings"), warnings
|
||||
end
|
||||
end
|
||||
if not is_defined_filter(fields) then
|
||||
return false, build_error_with_context(context, "Rule exception item "..name..": field name "..fields.." is not a supported filter field"), warnings
|
||||
end
|
||||
if defined_comp_operators[comps] == nil then
|
||||
return false, build_error_with_context(context, "Rule exception item "..name..": comparison operator "..comps.." is not a supported comparison operator"), warnings
|
||||
end
|
||||
end
|
||||
|
||||
function is_defined_filter(filter)
|
||||
if defined_noarg_filters[filter] ~= nil then
|
||||
return true
|
||||
else
|
||||
bracket_idx = string.find(filter, "[", 1, true)
|
||||
|
||||
if bracket_idx ~= nil then
|
||||
subfilter = string.sub(filter, 1, bracket_idx-1)
|
||||
|
||||
if defined_arg_filters[subfilter] ~= nil then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
dot_idx = string.find(filter, ".", 1, true)
|
||||
|
||||
while dot_idx ~= nil do
|
||||
subfilter = string.sub(filter, 1, dot_idx-1)
|
||||
|
||||
if defined_arg_filters[subfilter] ~= nil then
|
||||
return true
|
||||
end
|
||||
|
||||
dot_idx = string.find(filter, ".", dot_idx+1, true)
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
function load_rules_doc(rules_mgr, doc, load_state)
|
||||
|
||||
local warnings = {}
|
||||
|
||||
-- Iterate over yaml list. In this pass, all we're doing is
|
||||
-- populating the set of rules, macros, and lists. We're not
|
||||
-- expanding/compiling anything yet. All that will happen in a
|
||||
@@ -279,7 +411,7 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
load_state.indices[load_state.cur_item_idx])
|
||||
|
||||
if (not (type(v) == "table")) then
|
||||
return false, build_error_with_context(context, "Unexpected element of type " ..type(v)..". Each element should be a yaml associative array.")
|
||||
return false, build_error_with_context(context, "Unexpected element of type " ..type(v)..". Each element should be a yaml associative array."), warnings
|
||||
end
|
||||
|
||||
v['context'] = context
|
||||
@@ -291,26 +423,31 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
end
|
||||
|
||||
if falco_rules.engine_version(rules_mgr) < v['required_engine_version'] then
|
||||
return false, build_error_with_context(v['context'], "Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr))
|
||||
return false, build_error_with_context(v['context'], "Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr)), warnings
|
||||
end
|
||||
|
||||
elseif (v['macro']) then
|
||||
|
||||
if (v['macro'] == nil or type(v['macro']) == "table") then
|
||||
return false, build_error_with_context(v['context'], "Macro name is empty")
|
||||
return false, build_error_with_context(v['context'], "Macro name is empty"), warnings
|
||||
end
|
||||
|
||||
if v['source'] == nil then
|
||||
v['source'] = "syscall"
|
||||
end
|
||||
|
||||
-- Ignore macros with unknown sources
|
||||
if (v['source'] ~= "syscall" and v['source'] ~= "k8s_audit") then
|
||||
goto next_object
|
||||
end
|
||||
|
||||
if state.macros_by_name[v['macro']] == nil then
|
||||
state.ordered_macro_names[#state.ordered_macro_names+1] = v['macro']
|
||||
end
|
||||
|
||||
for j, field in ipairs({'condition'}) do
|
||||
if (v[field] == nil) then
|
||||
return false, build_error_with_context(v['context'], "Macro must have property "..field)
|
||||
return false, build_error_with_context(v['context'], "Macro must have property "..field), warnings
|
||||
end
|
||||
end
|
||||
|
||||
@@ -323,7 +460,7 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
|
||||
if append then
|
||||
if state.macros_by_name[v['macro']] == nil then
|
||||
return false, build_error_with_context(v['context'], "Macro " ..v['macro'].. " has 'append' key but no macro by that name already exists")
|
||||
return false, build_error_with_context(v['context'], "Macro " ..v['macro'].. " has 'append' key but no macro by that name already exists"), warnings
|
||||
end
|
||||
|
||||
state.macros_by_name[v['macro']]['condition'] = state.macros_by_name[v['macro']]['condition'] .. " " .. v['condition']
|
||||
@@ -338,7 +475,7 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
elseif (v['list']) then
|
||||
|
||||
if (v['list'] == nil or type(v['list']) == "table") then
|
||||
return false, build_error_with_context(v['context'], "List name is empty")
|
||||
return false, build_error_with_context(v['context'], "List name is empty"), warnings
|
||||
end
|
||||
|
||||
if state.lists_by_name[v['list']] == nil then
|
||||
@@ -347,7 +484,7 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
|
||||
for j, field in ipairs({'items'}) do
|
||||
if (v[field] == nil) then
|
||||
return false, build_error_with_context(v['context'], "List must have property "..field)
|
||||
return false, build_error_with_context(v['context'], "List must have property "..field), warnings
|
||||
end
|
||||
end
|
||||
|
||||
@@ -360,7 +497,7 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
|
||||
if append then
|
||||
if state.lists_by_name[v['list']] == nil then
|
||||
return false, build_error_with_context(v['context'], "List " ..v['list'].. " has 'append' key but no list by that name already exists")
|
||||
return false, build_error_with_context(v['context'], "List " ..v['list'].. " has 'append' key but no list by that name already exists"), warnings
|
||||
end
|
||||
|
||||
for j, elem in ipairs(v['items']) do
|
||||
@@ -373,7 +510,11 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
elseif (v['rule']) then
|
||||
|
||||
if (v['rule'] == nil or type(v['rule']) == "table") then
|
||||
return false, build_error_with_context(v['context'], "Rule name is empty")
|
||||
return false, build_error_with_context(v['context'], "Rule name is empty"), warnings
|
||||
end
|
||||
|
||||
if (v['condition'] == nil and v['exceptions'] == nil) then
|
||||
return false, build_error_with_context(v['context'], "Rule must have exceptions or condition property"), warnings
|
||||
end
|
||||
|
||||
-- By default, if a rule's condition refers to an unknown
|
||||
@@ -386,6 +527,18 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
v['source'] = "syscall"
|
||||
end
|
||||
|
||||
-- Ignore rules with unknown sources
|
||||
if (v['source'] ~= "syscall" and v['source'] ~= "k8s_audit") then
|
||||
goto next_object
|
||||
end
|
||||
|
||||
-- Add an empty exceptions property to the rule if not
|
||||
-- defined, but add a warning about defining one
|
||||
if v['exceptions'] == nil then
|
||||
warnings[#warnings + 1] = "Rule "..v['rule']..": consider adding an exceptions property to define supported exceptions fields"
|
||||
v['exceptions'] = {}
|
||||
end
|
||||
|
||||
-- Possibly append to the condition field of an existing rule
|
||||
append = false
|
||||
|
||||
@@ -393,21 +546,95 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
append = v['append']
|
||||
end
|
||||
|
||||
if append then
|
||||
-- Validate the contents of the rule exception
|
||||
if next(v['exceptions']) ~= nil then
|
||||
|
||||
-- For append rules, all you need is the condition
|
||||
for j, field in ipairs({'condition'}) do
|
||||
if (v[field] == nil) then
|
||||
return false, build_error_with_context(v['context'], "Rule must have property "..field)
|
||||
-- This validation only applies if append=false. append=true validation is handled below
|
||||
if append == false then
|
||||
|
||||
for _, eitem in ipairs(v['exceptions']) do
|
||||
|
||||
if eitem['name'] == nil then
|
||||
return false, build_error_with_context(v['context'], "Rule exception item must have name property"), warnings
|
||||
end
|
||||
|
||||
if eitem['fields'] == nil then
|
||||
return false, build_error_with_context(v['context'], "Rule exception item "..eitem['name']..": must have fields property with a list of fields"), warnings
|
||||
end
|
||||
|
||||
if eitem['values'] == nil then
|
||||
-- An empty values array is okay
|
||||
eitem['values'] = {}
|
||||
end
|
||||
|
||||
-- Different handling if the fields property is a single item vs a list
|
||||
local valid, err
|
||||
if type(eitem['fields']) == "table" then
|
||||
valid, err = validate_exception_item_multi_fields(eitem, v['context'])
|
||||
else
|
||||
valid, err = validate_exception_item_single_field(eitem, v['context'])
|
||||
end
|
||||
|
||||
if valid == false then
|
||||
return valid, err
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if append then
|
||||
|
||||
if state.rules_by_name[v['rule']] == nil then
|
||||
if state.skipped_rules_by_name[v['rule']] == nil then
|
||||
return false, build_error_with_context(v['context'], "Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists")
|
||||
return false, build_error_with_context(v['context'], "Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists"), warnings
|
||||
end
|
||||
else
|
||||
state.rules_by_name[v['rule']]['condition'] = state.rules_by_name[v['rule']]['condition'] .. " " .. v['condition']
|
||||
|
||||
if next(v['exceptions']) ~= nil then
|
||||
|
||||
for _, eitem in ipairs(v['exceptions']) do
|
||||
local name = eitem['name']
|
||||
local fields = eitem['fields']
|
||||
local comps = eitem['comps']
|
||||
|
||||
if name == nil then
|
||||
return false, build_error_with_context(v['context'], "Rule exception item must have name property"), warnings
|
||||
end
|
||||
|
||||
-- You can't append exception fields or comps to a rule
|
||||
if fields ~= nil then
|
||||
return false, build_error_with_context(v['context'], "Can not append exception fields to existing rule, only values"), warnings
|
||||
end
|
||||
|
||||
if comps ~= nil then
|
||||
return false, build_error_with_context(v['context'], "Can not append exception comps to existing rule, only values"), warnings
|
||||
end
|
||||
|
||||
-- You can append values. They are added to the
|
||||
-- corresponding name, if it exists. If no
|
||||
-- exception with that name exists, add a
|
||||
-- warning.
|
||||
if eitem['values'] ~= nil then
|
||||
local found=false
|
||||
for _, reitem in ipairs(state.rules_by_name[v['rule']]['exceptions']) do
|
||||
if reitem['name'] == eitem['name'] then
|
||||
found=true
|
||||
for _, values in ipairs(eitem['values']) do
|
||||
reitem['values'][#reitem['values'] + 1] = values
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if found == false then
|
||||
warnings[#warnings + 1] = "Rule "..v['rule'].." with append=true: no set of fields matching name "..eitem['name']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if v['condition'] ~= nil then
|
||||
state.rules_by_name[v['rule']]['condition'] = state.rules_by_name[v['rule']]['condition'] .. " " .. v['condition']
|
||||
end
|
||||
|
||||
-- Add the current object to the context of the base rule
|
||||
state.rules_by_name[v['rule']]['context'] = state.rules_by_name[v['rule']]['context'].."\n"..v['context']
|
||||
@@ -417,7 +644,7 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
|
||||
for j, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
|
||||
if (v[field] == nil) then
|
||||
return false, build_error_with_context(v['context'], "Rule must have property "..field)
|
||||
return false, build_error_with_context(v['context'], "Rule must have property "..field), warnings
|
||||
end
|
||||
end
|
||||
|
||||
@@ -446,16 +673,118 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Remove the context from the table, so the table is exactly what was parsed
|
||||
local context = v['context']
|
||||
v['context'] = nil
|
||||
return false, build_error_with_context(context, "Unknown rule object: "..table.tostring(v))
|
||||
|
||||
arr = build_error_with_context(context, "Unknown top level object: "..table.tostring(v))
|
||||
warnings[#warnings + 1] = arr[1]
|
||||
end
|
||||
|
||||
::next_object::
|
||||
end
|
||||
|
||||
return true, ""
|
||||
return true, {}, warnings
|
||||
end
|
||||
|
||||
-- cond and not ((proc.name=apk and fd.directory=/usr/lib/alpine) or (proc.name=npm and fd.directory=/usr/node/bin) or ...)
|
||||
-- Populates exfields with all fields used
|
||||
function build_exception_condition_string_multi_fields(eitem, exfields)
|
||||
|
||||
local fields = eitem['fields']
|
||||
local comps = eitem['comps']
|
||||
|
||||
local icond = "("
|
||||
|
||||
for i, values in ipairs(eitem['values']) do
|
||||
|
||||
if #fields ~= #values then
|
||||
return nil, "Exception item "..eitem['name']..": fields and values lists must have equal length"
|
||||
end
|
||||
|
||||
if icond ~= "(" then
|
||||
icond=icond.." or "
|
||||
end
|
||||
|
||||
icond=icond.."("
|
||||
|
||||
for k=1,#fields do
|
||||
if k > 1 then
|
||||
icond=icond.." and "
|
||||
end
|
||||
local ival = values[k]
|
||||
local istr = ""
|
||||
|
||||
-- If ival is a table, express it as (titem1, titem2, etc)
|
||||
if type(ival) == "table" then
|
||||
istr = "("
|
||||
for _, item in ipairs(ival) do
|
||||
if istr ~= "(" then
|
||||
istr = istr..", "
|
||||
end
|
||||
istr = istr..quote_item(item)
|
||||
end
|
||||
istr = istr..")"
|
||||
else
|
||||
-- If the corresponding operator is one that works on lists, possibly add surrounding parentheses.
|
||||
if defined_list_comp_operators[comps[k]] then
|
||||
istr = paren_item(ival)
|
||||
else
|
||||
-- Quote the value if not already quoted
|
||||
istr = quote_item(ival)
|
||||
end
|
||||
end
|
||||
|
||||
icond = icond..fields[k].." "..comps[k].." "..istr
|
||||
exfields[fields[k]] = true
|
||||
end
|
||||
|
||||
icond=icond..")"
|
||||
end
|
||||
|
||||
icond = icond..")"
|
||||
|
||||
-- Don't return a trivially empty condition string
|
||||
if icond == "()" then
|
||||
icond = ""
|
||||
end
|
||||
|
||||
return icond, nil
|
||||
|
||||
end
|
||||
|
||||
function build_exception_condition_string_single_field(eitem, exfields)
|
||||
|
||||
local icond = ""
|
||||
|
||||
for i, value in ipairs(eitem['values']) do
|
||||
|
||||
if type(value) ~= "string" then
|
||||
return "", "Expected values array for item "..eitem['name'].." to contain a list of strings"
|
||||
end
|
||||
|
||||
if icond == "" then
|
||||
icond = "("..eitem['fields'].." "..eitem['comps'].." ("
|
||||
else
|
||||
icond = icond..", "
|
||||
end
|
||||
|
||||
exfields[eitem['fields']] = true
|
||||
|
||||
icond = icond..quote_item(value)
|
||||
end
|
||||
|
||||
if icond ~= "" then
|
||||
icond = icond.."))"
|
||||
end
|
||||
|
||||
return icond, nil
|
||||
|
||||
end
|
||||
|
||||
-- Returns:
|
||||
-- - Load Result: bool
|
||||
-- - required engine version. will be nil when load result is false
|
||||
-- - List of Errors
|
||||
-- - List of Warnings
|
||||
function load_rules(sinsp_lua_parser,
|
||||
json_lua_parser,
|
||||
rules_content,
|
||||
@@ -466,6 +795,8 @@ function load_rules(sinsp_lua_parser,
|
||||
replace_container_info,
|
||||
min_priority)
|
||||
|
||||
local warnings = {}
|
||||
|
||||
local load_state = {lines={}, indices={}, cur_item_idx=0, min_priority=min_priority, required_engine_version=0}
|
||||
|
||||
load_state.lines, load_state.indices = split_lines(rules_content)
|
||||
@@ -487,36 +818,42 @@ function load_rules(sinsp_lua_parser,
|
||||
row = tonumber(row)
|
||||
col = tonumber(col)
|
||||
|
||||
return false, build_error(load_state.lines, row, 3, docs)
|
||||
return false, nil, build_error(load_state.lines, row, 3, docs), warnings
|
||||
end
|
||||
|
||||
if docs == nil then
|
||||
-- An empty rules file is acceptable
|
||||
return true, load_state.required_engine_version
|
||||
return true, load_state.required_engine_version, {}, warnings
|
||||
end
|
||||
|
||||
if type(docs) ~= "table" then
|
||||
return false, build_error(load_state.lines, 1, 1, "Rules content is not yaml")
|
||||
return false, nil, build_error(load_state.lines, 1, 1, "Rules content is not yaml"), warnings
|
||||
end
|
||||
|
||||
for docidx, doc in ipairs(docs) do
|
||||
|
||||
if type(doc) ~= "table" then
|
||||
return false, build_error(load_state.lines, 1, 1, "Rules content is not yaml")
|
||||
return false, nil, build_error(load_state.lines, 1, 1, "Rules content is not yaml"), warnings
|
||||
end
|
||||
|
||||
-- Look for non-numeric indices--implies that document is not array
|
||||
-- of objects.
|
||||
for key, val in pairs(doc) do
|
||||
if type(key) ~= "number" then
|
||||
return false, build_error(load_state.lines, 1, 1, "Rules content is not yaml array of objects")
|
||||
return false, nil, build_error(load_state.lines, 1, 1, "Rules content is not yaml array of objects"), warnings
|
||||
end
|
||||
end
|
||||
|
||||
res, errstr = load_rules_doc(rules_mgr, doc, load_state)
|
||||
res, errors, doc_warnings = load_rules_doc(rules_mgr, doc, load_state)
|
||||
|
||||
if (doc_warnings ~= nil) then
|
||||
for idx, warning in pairs(doc_warnings) do
|
||||
table.insert(warnings, warning)
|
||||
end
|
||||
end
|
||||
|
||||
if not res then
|
||||
return res, errstr
|
||||
return res, nil, errors, warnings
|
||||
end
|
||||
end
|
||||
|
||||
@@ -538,8 +875,9 @@ function load_rules(sinsp_lua_parser,
|
||||
-- the items and expand any references to the items in the list
|
||||
for i, item in ipairs(v['items']) do
|
||||
if (state.lists[item] == nil) then
|
||||
items[#items+1] = item
|
||||
items[#items+1] = quote_item(item)
|
||||
else
|
||||
state.lists[item].used = true
|
||||
for i, exp_item in ipairs(state.lists[item].items) do
|
||||
items[#items+1] = exp_item
|
||||
end
|
||||
@@ -556,7 +894,7 @@ function load_rules(sinsp_lua_parser,
|
||||
local status, ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
|
||||
|
||||
if status == false then
|
||||
return false, build_error_with_context(v['context'], ast)
|
||||
return false, nil, build_error_with_context(v['context'], ast), warnings
|
||||
end
|
||||
|
||||
if v['source'] == "syscall" then
|
||||
@@ -572,16 +910,48 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
local v = state.rules_by_name[name]
|
||||
|
||||
local econd = ""
|
||||
|
||||
local exfields = {}
|
||||
|
||||
-- Turn exceptions into condition strings and add them to each
|
||||
-- rule's condition
|
||||
for _, eitem in ipairs(v['exceptions']) do
|
||||
|
||||
local icond, err
|
||||
if type(eitem['fields']) == "table" then
|
||||
icond, err = build_exception_condition_string_multi_fields(eitem, exfields)
|
||||
else
|
||||
icond, err = build_exception_condition_string_single_field(eitem, exfields)
|
||||
end
|
||||
|
||||
if err ~= nil then
|
||||
return false, nil, build_error_with_context(v['context'], err), warnings
|
||||
end
|
||||
|
||||
if icond ~= "" then
|
||||
econd = econd.." and not "..icond
|
||||
end
|
||||
end
|
||||
|
||||
state.rules_by_name[name]['exception_fields'] = exfields
|
||||
|
||||
if econd ~= "" then
|
||||
state.rules_by_name[name]['compile_condition'] = "("..state.rules_by_name[name]['condition']..") "..econd
|
||||
else
|
||||
state.rules_by_name[name]['compile_condition'] = state.rules_by_name[name]['condition']
|
||||
end
|
||||
|
||||
warn_evttypes = true
|
||||
if v['warn_evttypes'] ~= nil then
|
||||
warn_evttypes = v['warn_evttypes']
|
||||
end
|
||||
|
||||
local status, filter_ast, filters = compiler.compile_filter(v['rule'], v['condition'],
|
||||
local status, filter_ast, filters = compiler.compile_filter(v['rule'], v['compile_condition'],
|
||||
state.macros, state.lists)
|
||||
|
||||
if status == false then
|
||||
return false, build_error_with_context(v['context'], filter_ast)
|
||||
return false, nil, build_error_with_context(v['context'], filter_ast), warnings
|
||||
end
|
||||
|
||||
local evtttypes = {}
|
||||
@@ -592,52 +962,22 @@ function load_rules(sinsp_lua_parser,
|
||||
sinsp_rule_utils.check_for_ignored_syscalls_events(filter_ast, 'rule', v['rule'])
|
||||
end
|
||||
|
||||
evttypes, syscallnums = sinsp_rule_utils.get_evttypes_syscalls(name, filter_ast, v['condition'], warn_evttypes, verbose)
|
||||
evttypes, syscallnums = sinsp_rule_utils.get_evttypes_syscalls(name, filter_ast, v['compile_condition'], warn_evttypes, verbose)
|
||||
end
|
||||
|
||||
-- If a filter in the rule doesn't exist, either skip the rule
|
||||
-- or raise an error, depending on the value of
|
||||
-- skip-if-unknown-filter.
|
||||
for filter, _ in pairs(filters) do
|
||||
found = false
|
||||
if not is_defined_filter(filter) then
|
||||
msg = "rule \""..v['rule'].."\": contains unknown filter "..filter
|
||||
warnings[#warnings + 1] = msg
|
||||
|
||||
if defined_noarg_filters[filter] ~= nil then
|
||||
found = true
|
||||
else
|
||||
bracket_idx = string.find(filter, "[", 1, true)
|
||||
|
||||
if bracket_idx ~= nil then
|
||||
subfilter = string.sub(filter, 1, bracket_idx-1)
|
||||
|
||||
if defined_arg_filters[subfilter] ~= nil then
|
||||
found = true
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
dot_idx = string.find(filter, ".", 1, true)
|
||||
|
||||
while dot_idx ~= nil do
|
||||
subfilter = string.sub(filter, 1, dot_idx-1)
|
||||
|
||||
if defined_arg_filters[subfilter] ~= nil then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
|
||||
dot_idx = string.find(filter, ".", dot_idx+1, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
if v['skip-if-unknown-filter'] then
|
||||
if verbose then
|
||||
print("Skipping rule \""..v['rule'].."\" that contains unknown filter "..filter)
|
||||
end
|
||||
goto next_rule
|
||||
if not v['skip-if-unknown-filter'] then
|
||||
return false, nil, build_error_with_context(v['context'], msg), warnings
|
||||
else
|
||||
error("Rule \""..v['rule'].."\" contains unknown filter "..filter)
|
||||
print("Skipping "..msg)
|
||||
goto next_rule
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -716,33 +1056,37 @@ function load_rules(sinsp_lua_parser,
|
||||
-- Ensure that the output field is properly formatted by
|
||||
-- creating a formatter from it. Any error will be thrown
|
||||
-- up to the top level.
|
||||
formatter = formats.formatter(v['source'], v['output'])
|
||||
formats.free_formatter(v['source'], formatter)
|
||||
local err, formatter = formats.formatter(v['source'], v['output'])
|
||||
if err == nil then
|
||||
formats.free_formatter(v['source'], formatter)
|
||||
else
|
||||
return false, nil, build_error_with_context(v['context'], err), warnings
|
||||
end
|
||||
else
|
||||
return false, build_error_with_context(v['context'], "Unexpected type in load_rule: "..filter_ast.type)
|
||||
return false, nil, build_error_with_context(v['context'], "Unexpected type in load_rule: "..filter_ast.type), warnings
|
||||
end
|
||||
|
||||
::next_rule::
|
||||
end
|
||||
|
||||
if verbose then
|
||||
-- Print info on any dangling lists or macros that were not used anywhere
|
||||
for name, macro in pairs(state.macros) do
|
||||
if macro.used == false then
|
||||
print("Warning: macro "..name.." not refered to by any rule/macro")
|
||||
end
|
||||
-- Print info on any dangling lists or macros that were not used anywhere
|
||||
for name, macro in pairs(state.macros) do
|
||||
if macro.used == false then
|
||||
msg = "macro "..name.." not refered to by any rule/macro"
|
||||
warnings[#warnings + 1] = msg
|
||||
end
|
||||
end
|
||||
|
||||
for name, list in pairs(state.lists) do
|
||||
if list.used == false then
|
||||
print("Warning: list "..name.." not refered to by any rule/macro/list")
|
||||
end
|
||||
for name, list in pairs(state.lists) do
|
||||
if list.used == false then
|
||||
msg = "list "..name.." not refered to by any rule/macro/list"
|
||||
warnings[#warnings + 1] = msg
|
||||
end
|
||||
end
|
||||
|
||||
io.flush()
|
||||
|
||||
return true, load_state.required_engine_version
|
||||
return true, load_state.required_engine_version, {}, warnings
|
||||
end
|
||||
|
||||
local rule_fmt = "%-50s %s"
|
||||
@@ -819,7 +1163,14 @@ function on_event(rule_id)
|
||||
-- Prefix output with '*' so formatting is permissive
|
||||
output = "*"..rule.output
|
||||
|
||||
return rule.rule, rule.priority_num, output
|
||||
-- Also return all fields from all exceptions
|
||||
combined_rule = state.rules_by_name[rule.rule]
|
||||
|
||||
if combined_rule == nil then
|
||||
error ("rule_loader.on_event(): could not find rule by name: ", rule.rule)
|
||||
end
|
||||
|
||||
return rule.rule, rule.priority_num, output, combined_rule.exception_fields
|
||||
end
|
||||
|
||||
function print_stats()
|
||||
|
||||
@@ -14,8 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "rules.h"
|
||||
#include "logger.h"
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
@@ -218,6 +219,31 @@ int falco_rules::engine_version(lua_State *ls)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static std::list<std::string> get_lua_table_values(lua_State *ls, int idx)
|
||||
{
|
||||
std::list<std::string> ret;
|
||||
|
||||
if (lua_isnil(ls, idx)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
lua_pushnil(ls); /* first key */
|
||||
while (lua_next(ls, idx-1) != 0) {
|
||||
// key is at index -2, value is at index
|
||||
// -1. We want the values.
|
||||
if (! lua_isstring(ls, -1)) {
|
||||
std::string err = "Non-string value in table of strings";
|
||||
throw falco_exception(err);
|
||||
}
|
||||
ret.push_back(string(lua_tostring(ls, -1)));
|
||||
|
||||
// Remove value, keep key for next iteration
|
||||
lua_pop(ls, 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void falco_rules::load_rules(const string &rules_content,
|
||||
bool verbose, bool all_events,
|
||||
string &extra, bool replace_container_info,
|
||||
@@ -423,7 +449,7 @@ void falco_rules::load_rules(const string &rules_content,
|
||||
lua_pushstring(m_ls, extra.c_str());
|
||||
lua_pushboolean(m_ls, (replace_container_info ? 1 : 0));
|
||||
lua_pushnumber(m_ls, min_priority);
|
||||
if(lua_pcall(m_ls, 9, 2, 0) != 0)
|
||||
if(lua_pcall(m_ls, 9, 4, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
|
||||
@@ -432,20 +458,49 @@ void falco_rules::load_rules(const string &rules_content,
|
||||
throw falco_exception(err);
|
||||
}
|
||||
|
||||
// Either returns (true, required_engine_version), or (false, error string)
|
||||
bool successful = lua_toboolean(m_ls, -2);
|
||||
// Returns:
|
||||
// Load result: bool
|
||||
// required engine version: will be nil when load result is false
|
||||
// array of errors
|
||||
// array of warnings
|
||||
bool successful = lua_toboolean(m_ls, -4);
|
||||
required_engine_version = lua_tonumber(m_ls, -3);
|
||||
std::list<std::string> errors = get_lua_table_values(m_ls, -2);
|
||||
std::list<std::string> warnings = get_lua_table_values(m_ls, -1);
|
||||
|
||||
if(successful)
|
||||
// Concatenate errors/warnings
|
||||
std::ostringstream os;
|
||||
if (errors.size() > 0)
|
||||
{
|
||||
required_engine_version = lua_tonumber(m_ls, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string err = lua_tostring(m_ls, -1);
|
||||
throw falco_exception(err);
|
||||
os << errors.size() << " errors:" << std::endl;
|
||||
for(auto err : errors)
|
||||
{
|
||||
os << err << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(m_ls, 2);
|
||||
if (warnings.size() > 0)
|
||||
{
|
||||
os << warnings.size() << " warnings:" << std::endl;
|
||||
for(auto warn : warnings)
|
||||
{
|
||||
os << warn << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if(!successful)
|
||||
{
|
||||
throw falco_exception(os.str());
|
||||
}
|
||||
|
||||
if (verbose && os.str() != "") {
|
||||
// We don't really have a logging callback
|
||||
// from the falco engine, but this would be a
|
||||
// good place to use it.
|
||||
fprintf(stderr, "When reading rules content: %s", os.str().c_str());
|
||||
}
|
||||
|
||||
lua_pop(m_ls, 4);
|
||||
|
||||
} else {
|
||||
throw falco_exception("No function " + m_lua_load_rules + " found in lua rule module");
|
||||
|
||||
@@ -15,7 +15,6 @@ configure_file(config_falco.h.in config_falco.h)
|
||||
|
||||
set(
|
||||
FALCO_SOURCES
|
||||
cli.cpp
|
||||
configuration.cpp
|
||||
logger.cpp
|
||||
falco_outputs.cpp
|
||||
@@ -35,7 +34,6 @@ set(
|
||||
"${PROJECT_BINARY_DIR}/userspace/falco"
|
||||
"${PROJECT_BINARY_DIR}/driver/src"
|
||||
"${STRING_VIEW_LITE_INCLUDE}"
|
||||
"${CXXOPTS_INCLUDE}"
|
||||
"${YAMLCPP_INCLUDE_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include"
|
||||
@@ -43,7 +41,6 @@ set(
|
||||
|
||||
set(
|
||||
FALCO_DEPENDENCIES
|
||||
cxxopts
|
||||
string-view-lite
|
||||
libyaml
|
||||
b64
|
||||
@@ -126,16 +123,16 @@ target_include_directories(
|
||||
${FALCO_INCLUDE_DIRECTORIES}
|
||||
)
|
||||
|
||||
# if(NOT MINIMAL_BUILD)
|
||||
# add_custom_command(
|
||||
# TARGET falco
|
||||
# COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR}
|
||||
# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
# COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields"
|
||||
# )
|
||||
# else()
|
||||
# message(STATUS "Skipping engine fields checksum when building the minimal Falco.")
|
||||
# endif()
|
||||
if(NOT MINIMAL_BUILD)
|
||||
add_custom_command(
|
||||
TARGET falco
|
||||
COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields"
|
||||
)
|
||||
else()
|
||||
message(STATUS "Skipping engine fields checksum when building the minimal Falco.")
|
||||
endif()
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
add_custom_command(
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2020 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
namespace falco
|
||||
{
|
||||
|
||||
} // namespace falco
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2020 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cxxopts.hpp>
|
||||
|
||||
namespace falco
|
||||
{
|
||||
|
||||
class option_requires_specific_argument_exception : public cxxopts::OptionParseException
|
||||
{
|
||||
public:
|
||||
explicit option_requires_specific_argument_exception(const std::string& option, const std::string& values):
|
||||
OptionParseException("Option " + cxxopts::LQUOTE + option + cxxopts::RQUOTE + " requires an argument equal to " + values)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class option_cannot_be_specified_exception : public cxxopts::OptionParseException
|
||||
{
|
||||
public:
|
||||
explicit option_cannot_be_specified_exception(const std::string& option1, const std::string& option2):
|
||||
OptionParseException("Options " + cxxopts::LQUOTE + option1 + cxxopts::RQUOTE + " and " + cxxopts::LQUOTE + option2 + cxxopts::RQUOTE + " can not be specified together")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class cli
|
||||
{
|
||||
public:
|
||||
cli(int argc, const char** argv):
|
||||
m_argc(argc), m_argv(argv), m_options("falco", "Cloud-Native Runtime Security")
|
||||
{
|
||||
}
|
||||
virtual ~cli()
|
||||
{
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
// These options give some info about Falco (Falco exits).
|
||||
m_options.add_options(
|
||||
"help",
|
||||
{
|
||||
{"h,help", "Print help page."},
|
||||
{"support", "Print support information (version, rules files, etc.)."},
|
||||
{"version", "Print version info."},
|
||||
});
|
||||
|
||||
// These are options responsible for listing Falco elements (Falco exits).
|
||||
m_options.add_options(
|
||||
"list",
|
||||
{
|
||||
{"L", "Show name and description of all rules."},
|
||||
{"l", "Show name and description of a specific rule.", cxxopts::value<std::string>(), "rule name"},
|
||||
{"list", "Show all fields.", cxxopts::value<std::string>()->implicit_value("all"), "sycall|k8s_audit"},
|
||||
{"N", "Show field names only."},
|
||||
});
|
||||
|
||||
// m_options.add_options(
|
||||
// "output",
|
||||
// {
|
||||
// {},
|
||||
// });
|
||||
|
||||
// m_options.add_options(
|
||||
// "input",
|
||||
// {
|
||||
// {},
|
||||
// });
|
||||
|
||||
m_options.add_options(
|
||||
"filtering",
|
||||
{
|
||||
{"D", "Disable any rules with names having the given substring. Can be specified multiple times. Can not be specified with -t.", cxxopts::value<std::vector<std::string>>(), "substring"},
|
||||
{"T", "Disable any rules with a specific tag. Can be specified several times. Can not be specified with -t.", cxxopts::value<std::vector<std::string>>(), "tag"},
|
||||
{"t", "Only run those rules with a specific tag. Can be specified several times. Can not be specified with -T or -D.", cxxopts::value<std::vector<std::string>>(), "tag"},
|
||||
});
|
||||
|
||||
m_result = m_options.parse(m_argc, m_argv);
|
||||
|
||||
process();
|
||||
}
|
||||
|
||||
private:
|
||||
void process()
|
||||
{
|
||||
if(m_result.count("help") && m_result["help"].as<bool>())
|
||||
{
|
||||
std::cout << m_options.help() << std::endl;
|
||||
// todo: print > exit
|
||||
}
|
||||
|
||||
if(m_result.count("support") && m_result["support"].as<bool>())
|
||||
{
|
||||
// todo: argv + config rule filenames > cmdline > print > exit
|
||||
}
|
||||
|
||||
if(m_result.count("version") && m_result["version"].as<bool>())
|
||||
{
|
||||
// todo: print > exit
|
||||
}
|
||||
|
||||
if(m_result.count("L") && m_result["L"].as<bool>())
|
||||
{
|
||||
// todo: engine > print > exit
|
||||
// engine->describe_rule(NULL)
|
||||
}
|
||||
|
||||
if(m_result.count("l"))
|
||||
{
|
||||
// todo: engine > print > exit
|
||||
// engine->describe_rule(m_result["l"].as<string>());
|
||||
}
|
||||
|
||||
if(m_result.count("list"))
|
||||
{
|
||||
auto source = m_result["list"].as<std::string>();
|
||||
// todo: retrieve implicit value
|
||||
if(source.empty() || (source != "syscall" && source != "k8s_audit" && source != "all"))
|
||||
{
|
||||
throw falco::option_requires_specific_argument_exception(
|
||||
"list",
|
||||
cxxopts::LQUOTE + "syscall" + cxxopts::RQUOTE + " or " + cxxopts::LQUOTE + "k8s_audit" + cxxopts::RQUOTE);
|
||||
}
|
||||
|
||||
bool names_only = false;
|
||||
if(m_result.count("N"))
|
||||
{
|
||||
names_only = m_result["N"].as<bool>();
|
||||
}
|
||||
|
||||
// todo: engine + names_only + source
|
||||
// se valore == syscall ==> + [-V]
|
||||
}
|
||||
|
||||
bool count_D = m_result.count("D");
|
||||
bool count_t = m_result.count("t");
|
||||
bool count_T = m_result.count("T");
|
||||
if(count_D > 0)
|
||||
{
|
||||
if(count_t > 0)
|
||||
{
|
||||
throw falco::option_cannot_be_specified_exception("D", "t");
|
||||
}
|
||||
// todo
|
||||
// engine > not exit
|
||||
}
|
||||
if(count_T > 0)
|
||||
{
|
||||
if(count_t > 0)
|
||||
{
|
||||
throw falco::option_cannot_be_specified_exception("T", "t");
|
||||
}
|
||||
// todo
|
||||
// engine > not exit
|
||||
}
|
||||
if(count_t > 0)
|
||||
{
|
||||
// todo
|
||||
// engine > not exit
|
||||
}
|
||||
}
|
||||
|
||||
int m_argc;
|
||||
const char** m_argv;
|
||||
cxxopts::Options m_options;
|
||||
cxxopts::ParseResult m_result;
|
||||
};
|
||||
|
||||
} // namespace falco
|
||||
|
||||
// 3 tipi di azioni
|
||||
// quelle che una volta date devono farlo uscire e non hanno bisogno di nessuna istanza
|
||||
// quelle che hanno bisogno di inspector e/o engine e poi falco esce
|
||||
// quelle che hanno bisogno di inspector e/o engine e poi falco esegue
|
||||
@@ -47,16 +47,6 @@ falco_configuration::~falco_configuration()
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have a configuration file, we just use stdout output and all other defaults
|
||||
void falco_configuration::init(list<string> &cmdline_options)
|
||||
{
|
||||
init_cmdline_options(cmdline_options);
|
||||
|
||||
falco::outputs::config stdout_output;
|
||||
stdout_output.name = "stdout";
|
||||
m_outputs.push_back(stdout_output);
|
||||
}
|
||||
|
||||
void falco_configuration::init(string conf_filename, list<string> &cmdline_options)
|
||||
{
|
||||
string m_config_file = conf_filename;
|
||||
@@ -176,6 +166,8 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
|
||||
falco_logger::set_level(m_log_level);
|
||||
|
||||
m_output_timeout = m_config->get_scalar<uint32_t>("output_timeout", 2000);
|
||||
|
||||
m_notifications_rate = m_config->get_scalar<uint32_t>("outputs", "rate", 1);
|
||||
m_notifications_max_burst = m_config->get_scalar<uint32_t>("outputs", "max_burst", 1000);
|
||||
|
||||
@@ -346,4 +338,4 @@ void falco_configuration::set_cmdline_option(const string &opt)
|
||||
{
|
||||
m_config->set_scalar(keyval.first, keyval.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,7 @@ public:
|
||||
|
||||
bool m_buffered_outputs;
|
||||
bool m_time_format_iso_8601;
|
||||
uint32_t m_output_timeout;
|
||||
|
||||
bool m_grpc_enabled;
|
||||
uint32_t m_grpc_threadiness;
|
||||
|
||||
@@ -43,7 +43,6 @@ limitations under the License.
|
||||
#include "falco_engine.h"
|
||||
#include "config_falco.h"
|
||||
#include "statsfilewriter.h"
|
||||
#include "cli.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
#include "webserver.h"
|
||||
#include "grpc_server.h"
|
||||
@@ -75,102 +74,106 @@ static void restart_falco(int signal)
|
||||
g_restart = true;
|
||||
}
|
||||
|
||||
// //
|
||||
// // Program help
|
||||
// //
|
||||
// static void usage()
|
||||
// {
|
||||
// printf(
|
||||
// "Falco version: " FALCO_VERSION "\n"
|
||||
// "Usage: falco [options]\n\n"
|
||||
// "Options:\n"
|
||||
|
||||
// " -c Configuration file (default " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ")\n"
|
||||
// " -A Monitor all events, including those with EF_DROP_SIMPLE_CONS flag.\n"
|
||||
// " --alternate-lua-dir <path> Specify an alternate path for loading Falco lua files\n"
|
||||
// " -b, --print-base64 Print data buffers in base64.\n"
|
||||
// " This is useful for encoding binary data that needs to be used over media designed to.\n"
|
||||
// " --cri <path> Path to CRI socket for container metadata.\n"
|
||||
// " Use the specified socket to fetch data from a CRI-compatible runtime.\n"
|
||||
// " -d, --daemon Run as a daemon.\n"
|
||||
// " --disable-cri-async Disable asynchronous CRI metadata fetching.\n"
|
||||
// " This is useful to let the input event wait for the container metadata fetch\n"
|
||||
// " to finish before moving forward. Async fetching, in some environments leads\n"
|
||||
// " to empty fields for container metadata when the fetch is not fast enough to be\n"
|
||||
// " completed asynchronously. This can have a performance penalty on your environment\n"
|
||||
// " depending on the number of containers and the frequency at which they are created/started/stopped\n"
|
||||
// " --disable-source <event_source>\n"
|
||||
// " Disable a specific event source.\n"
|
||||
// " Available event sources are: syscall, k8s_audit.\n"
|
||||
// " It can be passed multiple times.\n"
|
||||
// " Can not disable both the event sources.\n"
|
||||
|
||||
// " -e <events_file> Read the events from <events_file> (in .scap format for sinsp events, or jsonl for\n"
|
||||
// " k8s audit events) instead of tapping into live.\n"
|
||||
// #ifndef MINIMAL_BUILD
|
||||
// " -k <url>, --k8s-api <url>\n"
|
||||
// " Enable Kubernetes support by connecting to the API server specified as argument.\n"
|
||||
// " E.g. \"http://admin:password@127.0.0.1:8080\".\n"
|
||||
// " The API server can also be specified via the environment variable FALCO_K8S_API.\n"
|
||||
// " -K <bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>], --k8s-api-cert <bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>]\n"
|
||||
// " Use the provided files names to authenticate user and (optionally) verify the K8S API server identity.\n"
|
||||
// " Each entry must specify full (absolute, or relative to the current directory) path to the respective file.\n"
|
||||
// " Private key password is optional (needed only if key is password protected).\n"
|
||||
// " CA certificate is optional. For all files, only PEM file format is supported. \n"
|
||||
// " Specifying CA certificate only is obsoleted - when single entry is provided \n"
|
||||
// " for this option, it will be interpreted as the name of a file containing bearer token.\n"
|
||||
// " Note that the format of this command-line option prohibits use of files whose names contain\n"
|
||||
// " ':' or '#' characters in the file name.\n"
|
||||
// #endif
|
||||
|
||||
|
||||
|
||||
|
||||
// #ifndef MINIMAL_BUILD
|
||||
// " -m <url[,marathon_url]>, --mesos-api <url[,marathon_url]>\n"
|
||||
// " Enable Mesos support by connecting to the API server\n"
|
||||
// " specified as argument. E.g. \"http://admin:password@127.0.0.1:5050\".\n"
|
||||
// " Marathon url is optional and defaults to Mesos address, port 8080.\n"
|
||||
// " The API servers can also be specified via the environment variable FALCO_MESOS_API.\n"
|
||||
// #endif
|
||||
// " -M <num_seconds> Stop collecting after <num_seconds> reached.\n"
|
||||
|
||||
// " -o, --option <key>=<val> Set the value of option <key> to <val>. Overrides values in configuration file.\n"
|
||||
// " <key> can be a two-part <key>.<subkey>\n"
|
||||
// " -p <output_format>, --print <output_format>\n"
|
||||
// " Add additional information to each falco notification's output.\n"
|
||||
// " With -pc or -pcontainer will use a container-friendly format.\n"
|
||||
// " With -pk or -pkubernetes will use a kubernetes-friendly format.\n"
|
||||
// " With -pm or -pmesos will use a mesos-friendly format.\n"
|
||||
// " Additionally, specifying -pc/-pk/-pm will change the interpretation\n"
|
||||
// " of %%container.info in rule output fields.\n"
|
||||
// " -P, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
|
||||
// " -r <rules_file> Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n"
|
||||
// " Can be specified multiple times to read from multiple files/directories.\n"
|
||||
// " -s <stats_file> If specified, append statistics related to Falco's reading/processing of events\n"
|
||||
// " to this file (only useful in live mode).\n"
|
||||
// " --stats-interval <msec> When using -s <stats_file>, write statistics every <msec> ms.\n"
|
||||
// " This uses signals, so don't recommend intervals below 200 ms.\n"
|
||||
// " Defaults to 5000 (5 seconds).\n"
|
||||
// " -S <len>, --snaplen <len>\n"
|
||||
// " Capture the first <len> bytes of each I/O buffer.\n"
|
||||
// " By default, the first 80 bytes are captured. Use this\n"
|
||||
// " option with caution, it can generate huge trace files.\n"
|
||||
|
||||
|
||||
// " -U,--unbuffered Turn off output buffering to configured outputs.\n"
|
||||
// " This causes every single line emitted by falco to be flushed,\n"
|
||||
// " which generates higher CPU usage but is useful when piping those outputs\n"
|
||||
// " into another process or into a script.\n"
|
||||
// " -u, --userspace Parse events from userspace.\n"
|
||||
// " To be used in conjunction with the ptrace(2) based driver (pdig).\n"
|
||||
// " -V, --validate <rules_file> Read the contents of the specified rules(s) file and exit.\n"
|
||||
// " Can be specified multiple times to validate multiple files.\n"
|
||||
// " -v Verbose output.\n"
|
||||
|
||||
// "\n"
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// Program help
|
||||
//
|
||||
static void usage()
|
||||
{
|
||||
printf(
|
||||
"Falco version: " FALCO_VERSION "\n"
|
||||
"Usage: falco [options]\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help Print this page\n"
|
||||
" -c Configuration file (default " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ")\n"
|
||||
" -A Monitor all events, including those with EF_DROP_SIMPLE_CONS flag.\n"
|
||||
" --alternate-lua-dir <path> Specify an alternate path for loading Falco lua files\n"
|
||||
" -b, --print-base64 Print data buffers in base64.\n"
|
||||
" This is useful for encoding binary data that needs to be used over media designed to.\n"
|
||||
" --cri <path> Path to CRI socket for container metadata.\n"
|
||||
" Use the specified socket to fetch data from a CRI-compatible runtime.\n"
|
||||
" -d, --daemon Run as a daemon.\n"
|
||||
" --disable-cri-async Disable asynchronous CRI metadata fetching.\n"
|
||||
" This is useful to let the input event wait for the container metadata fetch\n"
|
||||
" to finish before moving forward. Async fetching, in some environments leads\n"
|
||||
" to empty fields for container metadata when the fetch is not fast enough to be\n"
|
||||
" completed asynchronously. This can have a performance penalty on your environment\n"
|
||||
" depending on the number of containers and the frequency at which they are created/started/stopped\n"
|
||||
" --disable-source <event_source>\n"
|
||||
" Disable a specific event source.\n"
|
||||
" Available event sources are: syscall, k8s_audit.\n"
|
||||
" It can be passed multiple times.\n"
|
||||
" Can not disable both the event sources.\n"
|
||||
" -D <substring> Disable any rules with names having the substring <substring>. Can be specified multiple times.\n"
|
||||
" Can not be specified with -t.\n"
|
||||
" -e <events_file> Read the events from <events_file> (in .scap format for sinsp events, or jsonl for\n"
|
||||
" k8s audit events) instead of tapping into live.\n"
|
||||
#ifndef MINIMAL_BUILD
|
||||
" -k <url>, --k8s-api <url>\n"
|
||||
" Enable Kubernetes support by connecting to the API server specified as argument.\n"
|
||||
" E.g. \"http://admin:password@127.0.0.1:8080\".\n"
|
||||
" The API server can also be specified via the environment variable FALCO_K8S_API.\n"
|
||||
" -K <bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>], --k8s-api-cert <bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>]\n"
|
||||
" Use the provided files names to authenticate user and (optionally) verify the K8S API server identity.\n"
|
||||
" Each entry must specify full (absolute, or relative to the current directory) path to the respective file.\n"
|
||||
" Private key password is optional (needed only if key is password protected).\n"
|
||||
" CA certificate is optional. For all files, only PEM file format is supported. \n"
|
||||
" Specifying CA certificate only is obsoleted - when single entry is provided \n"
|
||||
" for this option, it will be interpreted as the name of a file containing bearer token.\n"
|
||||
" Note that the format of this command-line option prohibits use of files whose names contain\n"
|
||||
" ':' or '#' characters in the file name.\n"
|
||||
#endif
|
||||
" -L Show the name and description of all rules and exit.\n"
|
||||
" -l <rule> Show the name and description of the rule with name <rule> and exit.\n"
|
||||
" --list [<source>] List all defined fields. If <source> is provided, only list those fields for\n"
|
||||
" the source <source>. Current values for <source> are \"syscall\", \"k8s_audit\"\n"
|
||||
#ifndef MINIMAL_BUILD
|
||||
" -m <url[,marathon_url]>, --mesos-api <url[,marathon_url]>\n"
|
||||
" Enable Mesos support by connecting to the API server\n"
|
||||
" specified as argument. E.g. \"http://admin:password@127.0.0.1:5050\".\n"
|
||||
" Marathon url is optional and defaults to Mesos address, port 8080.\n"
|
||||
" The API servers can also be specified via the environment variable FALCO_MESOS_API.\n"
|
||||
#endif
|
||||
" -M <num_seconds> Stop collecting after <num_seconds> reached.\n"
|
||||
" -N When used with --list, only print field names.\n"
|
||||
" -o, --option <key>=<val> Set the value of option <key> to <val>. Overrides values in configuration file.\n"
|
||||
" <key> can be a two-part <key>.<subkey>\n"
|
||||
" -p <output_format>, --print <output_format>\n"
|
||||
" Add additional information to each falco notification's output.\n"
|
||||
" With -pc or -pcontainer will use a container-friendly format.\n"
|
||||
" With -pk or -pkubernetes will use a kubernetes-friendly format.\n"
|
||||
" With -pm or -pmesos will use a mesos-friendly format.\n"
|
||||
" Additionally, specifying -pc/-pk/-pm will change the interpretation\n"
|
||||
" of %%container.info in rule output fields.\n"
|
||||
" -P, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
|
||||
" -r <rules_file> Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n"
|
||||
" Can be specified multiple times to read from multiple files/directories.\n"
|
||||
" -s <stats_file> If specified, append statistics related to Falco's reading/processing of events\n"
|
||||
" to this file (only useful in live mode).\n"
|
||||
" --stats-interval <msec> When using -s <stats_file>, write statistics every <msec> ms.\n"
|
||||
" This uses signals, so don't recommend intervals below 200 ms.\n"
|
||||
" Defaults to 5000 (5 seconds).\n"
|
||||
" -S <len>, --snaplen <len>\n"
|
||||
" Capture the first <len> bytes of each I/O buffer.\n"
|
||||
" By default, the first 80 bytes are captured. Use this\n"
|
||||
" option with caution, it can generate huge trace files.\n"
|
||||
" --support Print support information including version, rules files used, etc. and exit.\n"
|
||||
" -T <tag> Disable any rules with a tag=<tag>. Can be specified multiple times.\n"
|
||||
" Can not be specified with -t.\n"
|
||||
" -t <tag> Only run those rules with a tag=<tag>. Can be specified multiple times.\n"
|
||||
" Can not be specified with -T/-D.\n"
|
||||
" -U,--unbuffered Turn off output buffering to configured outputs.\n"
|
||||
" This causes every single line emitted by falco to be flushed,\n"
|
||||
" which generates higher CPU usage but is useful when piping those outputs\n"
|
||||
" into another process or into a script.\n"
|
||||
" -u, --userspace Parse events from userspace.\n"
|
||||
" To be used in conjunction with the ptrace(2) based driver (pdig).\n"
|
||||
" -V, --validate <rules_file> Read the contents of the specified rules(s) file and exit.\n"
|
||||
" Can be specified multiple times to validate multiple files.\n"
|
||||
" -v Verbose output.\n"
|
||||
" --version Print version number.\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void display_fatal_err(const string &msg)
|
||||
{
|
||||
@@ -392,7 +395,8 @@ static void print_all_ignored_events(sinsp *inspector)
|
||||
|
||||
static void list_source_fields(falco_engine *engine, bool verbose, bool names_only, std::string &source)
|
||||
{
|
||||
if(!source.empty() && !(source == "syscall" || source == "k8s_audit"))
|
||||
if(source.size() > 0 &&
|
||||
!(source == "syscall" || source == "k8s_audit"))
|
||||
{
|
||||
throw std::invalid_argument("Value for --list must be \"syscall\" or \"k8s_audit\"");
|
||||
}
|
||||
@@ -409,7 +413,7 @@ static void list_source_fields(falco_engine *engine, bool verbose, bool names_on
|
||||
//
|
||||
// ARGUMENT PARSING AND PROGRAM SETUP
|
||||
//
|
||||
int falco_init()
|
||||
int falco_init(int argc, char **argv)
|
||||
{
|
||||
int result = EXIT_SUCCESS;
|
||||
sinsp* inspector = NULL;
|
||||
@@ -510,197 +514,198 @@ int falco_init()
|
||||
set<string> disabled_rule_tags;
|
||||
set<string> enabled_rule_tags;
|
||||
|
||||
// //
|
||||
// // Parse the args
|
||||
// //
|
||||
// while((op = getopt_long(argc, argv,
|
||||
// "hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UuvV:w:",
|
||||
// long_options, &long_index)) != -1)
|
||||
// {
|
||||
// switch(op)
|
||||
// {
|
||||
// case 'h':
|
||||
// usage();
|
||||
// goto exit;
|
||||
// case 'c':
|
||||
// conf_filename = optarg;
|
||||
// break;
|
||||
// case 'A':
|
||||
// all_events = true;
|
||||
// break;
|
||||
// case 'b':
|
||||
// event_buffer_format = sinsp_evt::PF_BASE64;
|
||||
// break;
|
||||
// case 'd':
|
||||
// daemon = true;
|
||||
// break;
|
||||
// case 'D':
|
||||
// substring = optarg;
|
||||
// disabled_rule_substrings.insert(substring);
|
||||
// break;
|
||||
// case 'e':
|
||||
// trace_filename = optarg;
|
||||
// #ifndef MINIMAL_BUILD
|
||||
// k8s_api = new string();
|
||||
// mesos_api = new string();
|
||||
// #endif
|
||||
// break;
|
||||
// case 'F':
|
||||
// list_flds = optarg;
|
||||
// break;
|
||||
// case 'i':
|
||||
// print_ignored_events = true;
|
||||
// break;
|
||||
// #ifndef MINIMAL_BUILD
|
||||
// case 'k':
|
||||
// k8s_api = new string(optarg);
|
||||
// break;
|
||||
// case 'K':
|
||||
// k8s_api_cert = new string(optarg);
|
||||
// break;
|
||||
// #endif
|
||||
// case 'L':
|
||||
// describe_all_rules = true;
|
||||
// break;
|
||||
// case 'l':
|
||||
// describe_rule = optarg;
|
||||
// break;
|
||||
// #ifndef MINIMAL_BUILD
|
||||
// case 'm':
|
||||
// mesos_api = new string(optarg);
|
||||
// break;
|
||||
// #endif
|
||||
// case 'M':
|
||||
// duration_to_tot = atoi(optarg);
|
||||
// if(duration_to_tot <= 0)
|
||||
// {
|
||||
// throw sinsp_exception(string("invalid duration") + optarg);
|
||||
// }
|
||||
// break;
|
||||
// case 'N':
|
||||
// names_only = true;
|
||||
// break;
|
||||
// case 'o':
|
||||
// cmdline_options.push_back(optarg);
|
||||
// break;
|
||||
// case 'P':
|
||||
// pidfilename = optarg;
|
||||
// break;
|
||||
// case 'p':
|
||||
// if(string(optarg) == "c" || string(optarg) == "container")
|
||||
// {
|
||||
// output_format = "container=%container.name (id=%container.id)";
|
||||
// replace_container_info = true;
|
||||
// }
|
||||
// else if(string(optarg) == "k" || string(optarg) == "kubernetes")
|
||||
// {
|
||||
// output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id";
|
||||
// replace_container_info = true;
|
||||
// }
|
||||
// else if(string(optarg) == "m" || string(optarg) == "mesos")
|
||||
// {
|
||||
// output_format = "task=%mesos.task.name container=%container.id";
|
||||
// replace_container_info = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// output_format = optarg;
|
||||
// replace_container_info = false;
|
||||
// }
|
||||
// break;
|
||||
// case 'r':
|
||||
// falco_configuration::read_rules_file_directory(string(optarg), rules_filenames);
|
||||
// break;
|
||||
// case 'S':
|
||||
// snaplen = atoi(optarg);
|
||||
// break;
|
||||
// case 's':
|
||||
// stats_filename = optarg;
|
||||
// break;
|
||||
// case 'T':
|
||||
// disabled_rule_tags.insert(optarg);
|
||||
// break;
|
||||
// case 't':
|
||||
// enabled_rule_tags.insert(optarg);
|
||||
// break;
|
||||
// case 'U':
|
||||
// buffered_outputs = false;
|
||||
// buffered_cmdline = true;
|
||||
// break;
|
||||
// case 'u':
|
||||
// userspace = true;
|
||||
// break;
|
||||
// case 'v':
|
||||
// verbose = true;
|
||||
// break;
|
||||
// case 'V':
|
||||
// validate_rules_filenames.push_back(optarg);
|
||||
// break;
|
||||
// case 'w':
|
||||
// outfile = optarg;
|
||||
// break;
|
||||
// case '?':
|
||||
// result = EXIT_FAILURE;
|
||||
// goto exit;
|
||||
//
|
||||
// Parse the args
|
||||
//
|
||||
while((op = getopt_long(argc, argv,
|
||||
"hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UuvV:w:",
|
||||
long_options, &long_index)) != -1)
|
||||
{
|
||||
switch(op)
|
||||
{
|
||||
case 'h':
|
||||
usage();
|
||||
goto exit;
|
||||
case 'c':
|
||||
conf_filename = optarg;
|
||||
break;
|
||||
case 'A':
|
||||
all_events = true;
|
||||
break;
|
||||
case 'b':
|
||||
event_buffer_format = sinsp_evt::PF_BASE64;
|
||||
break;
|
||||
case 'd':
|
||||
daemon = true;
|
||||
break;
|
||||
case 'D':
|
||||
substring = optarg;
|
||||
disabled_rule_substrings.insert(substring);
|
||||
break;
|
||||
case 'e':
|
||||
trace_filename = optarg;
|
||||
#ifndef MINIMAL_BUILD
|
||||
k8s_api = new string();
|
||||
mesos_api = new string();
|
||||
#endif
|
||||
break;
|
||||
case 'F':
|
||||
list_flds = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
print_ignored_events = true;
|
||||
break;
|
||||
#ifndef MINIMAL_BUILD
|
||||
case 'k':
|
||||
k8s_api = new string(optarg);
|
||||
break;
|
||||
case 'K':
|
||||
k8s_api_cert = new string(optarg);
|
||||
break;
|
||||
#endif
|
||||
case 'L':
|
||||
describe_all_rules = true;
|
||||
break;
|
||||
case 'l':
|
||||
describe_rule = optarg;
|
||||
break;
|
||||
#ifndef MINIMAL_BUILD
|
||||
case 'm':
|
||||
mesos_api = new string(optarg);
|
||||
break;
|
||||
#endif
|
||||
case 'M':
|
||||
duration_to_tot = atoi(optarg);
|
||||
if(duration_to_tot <= 0)
|
||||
{
|
||||
throw sinsp_exception(string("invalid duration") + optarg);
|
||||
}
|
||||
break;
|
||||
case 'N':
|
||||
names_only = true;
|
||||
break;
|
||||
case 'o':
|
||||
cmdline_options.push_back(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
pidfilename = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
if(string(optarg) == "c" || string(optarg) == "container")
|
||||
{
|
||||
output_format = "container=%container.name (id=%container.id)";
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(string(optarg) == "k" || string(optarg) == "kubernetes")
|
||||
{
|
||||
output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id";
|
||||
replace_container_info = true;
|
||||
}
|
||||
else if(string(optarg) == "m" || string(optarg) == "mesos")
|
||||
{
|
||||
output_format = "task=%mesos.task.name container=%container.id";
|
||||
replace_container_info = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
output_format = optarg;
|
||||
replace_container_info = false;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
falco_configuration::read_rules_file_directory(string(optarg), rules_filenames);
|
||||
break;
|
||||
case 'S':
|
||||
snaplen = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
stats_filename = optarg;
|
||||
break;
|
||||
case 'T':
|
||||
disabled_rule_tags.insert(optarg);
|
||||
break;
|
||||
case 't':
|
||||
enabled_rule_tags.insert(optarg);
|
||||
break;
|
||||
case 'U':
|
||||
buffered_outputs = false;
|
||||
buffered_cmdline = true;
|
||||
break;
|
||||
case 'u':
|
||||
userspace = true;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'V':
|
||||
validate_rules_filenames.push_back(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
outfile = optarg;
|
||||
break;
|
||||
case '?':
|
||||
result = EXIT_FAILURE;
|
||||
goto exit;
|
||||
|
||||
// case 0:
|
||||
// if(string(long_options[long_index].name) == "version")
|
||||
// {
|
||||
// printf("Falco version: %s\n", FALCO_VERSION);
|
||||
// printf("Driver version: %s\n", DRIVER_VERSION);
|
||||
// return EXIT_SUCCESS;
|
||||
// }
|
||||
// else if (string(long_options[long_index].name) == "cri")
|
||||
// {
|
||||
// if(optarg != NULL)
|
||||
// {
|
||||
// cri_socket_path = optarg;
|
||||
// }
|
||||
// }
|
||||
// else if (string(long_options[long_index].name) == "disable-cri-async")
|
||||
// {
|
||||
// cri_async = false;
|
||||
// }
|
||||
// else if (string(long_options[long_index].name) == "list")
|
||||
// {
|
||||
// list_flds = true;
|
||||
// if(optarg != NULL)
|
||||
// {
|
||||
// list_flds_source = optarg;
|
||||
// }
|
||||
// }
|
||||
// else if (string(long_options[long_index].name) == "stats-interval")
|
||||
// {
|
||||
// stats_interval = atoi(optarg);
|
||||
// }
|
||||
// else if (string(long_options[long_index].name) == "support")
|
||||
// {
|
||||
// print_support = true;
|
||||
// }
|
||||
// else if (string(long_options[long_index].name) == "disable-source")
|
||||
// {
|
||||
// if(optarg != NULL)
|
||||
// {
|
||||
// disable_sources.insert(optarg);
|
||||
// }
|
||||
// }
|
||||
// else if (string(long_options[long_index].name)== "alternate-lua-dir")
|
||||
// {
|
||||
// if(optarg != NULL)
|
||||
// {
|
||||
// alternate_lua_dir = optarg;
|
||||
// if (alternate_lua_dir.back() != '/') {
|
||||
// alternate_lua_dir += '/';
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
case 0:
|
||||
if(string(long_options[long_index].name) == "version")
|
||||
{
|
||||
printf("Falco version: %s\n", FALCO_VERSION);
|
||||
printf("Driver version: %s\n", DRIVER_VERSION);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "cri")
|
||||
{
|
||||
if(optarg != NULL)
|
||||
{
|
||||
cri_socket_path = optarg;
|
||||
}
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "disable-cri-async")
|
||||
{
|
||||
cri_async = false;
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "list")
|
||||
{
|
||||
list_flds = true;
|
||||
if(optarg != NULL)
|
||||
{
|
||||
list_flds_source = optarg;
|
||||
}
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "stats-interval")
|
||||
{
|
||||
stats_interval = atoi(optarg);
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "support")
|
||||
{
|
||||
print_support = true;
|
||||
}
|
||||
else if (string(long_options[long_index].name) == "disable-source")
|
||||
{
|
||||
if(optarg != NULL)
|
||||
{
|
||||
disable_sources.insert(optarg);
|
||||
}
|
||||
}
|
||||
else if (string(long_options[long_index].name)== "alternate-lua-dir")
|
||||
{
|
||||
if(optarg != NULL)
|
||||
{
|
||||
alternate_lua_dir = optarg;
|
||||
if (alternate_lua_dir.back() != '/') {
|
||||
alternate_lua_dir += '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// }
|
||||
inspector = new sinsp();
|
||||
inspector->set_buffer_format(event_buffer_format);
|
||||
|
||||
@@ -757,8 +762,6 @@ int falco_init()
|
||||
}
|
||||
}
|
||||
|
||||
outputs = new falco_outputs();
|
||||
|
||||
// Some combinations of arguments are not allowed.
|
||||
if (daemon && pidfilename == "") {
|
||||
throw std::invalid_argument("If -d is provided, a pid file must also be provided");
|
||||
@@ -789,7 +792,7 @@ int falco_init()
|
||||
}
|
||||
else
|
||||
{
|
||||
conf_filename = "";
|
||||
throw std::invalid_argument("You must create a config file at " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE " or by passing -c\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -810,7 +813,7 @@ int falco_init()
|
||||
}
|
||||
catch(falco_exception &e)
|
||||
{
|
||||
printf("%s%s\n", prefix.c_str(), e.what());
|
||||
printf("%s%s", prefix.c_str(), e.what());
|
||||
throw;
|
||||
}
|
||||
printf("%sOk\n", prefix.c_str());
|
||||
@@ -831,12 +834,7 @@ int falco_init()
|
||||
}
|
||||
else
|
||||
{
|
||||
config.init(cmdline_options);
|
||||
falco_logger::set_time_format_iso_8601(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) + " (driver version " + std::string(DRIVER_VERSION) + ")\n");
|
||||
falco_logger::log(LOG_INFO, "Falco initialized. No configuration file found, proceeding with defaults\n");
|
||||
throw std::runtime_error("Could not find configuration file at " + conf_filename);
|
||||
}
|
||||
|
||||
if (rules_filenames.size())
|
||||
@@ -867,7 +865,15 @@ int falco_init()
|
||||
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
|
||||
uint64_t required_engine_version;
|
||||
|
||||
engine->load_rules_file(filename, verbose, all_events, required_engine_version);
|
||||
try {
|
||||
engine->load_rules_file(filename, verbose, all_events, required_engine_version);
|
||||
}
|
||||
catch(falco_exception &e)
|
||||
{
|
||||
std::string prefix = "Could not load rules file " + filename + ": ";
|
||||
|
||||
throw falco_exception(prefix + e.what());
|
||||
}
|
||||
required_engine_versions[filename] = required_engine_version;
|
||||
}
|
||||
|
||||
@@ -916,14 +922,14 @@ int falco_init()
|
||||
throw std::runtime_error(string("Could not uname() to find system info: %s\n") + strerror(errno));
|
||||
}
|
||||
|
||||
// for(char **arg = argv; *arg; arg++)
|
||||
// {
|
||||
// if(cmdline.size() > 0)
|
||||
// {
|
||||
// cmdline += " ";
|
||||
// }
|
||||
// cmdline += *arg;
|
||||
// }
|
||||
for(char **arg = argv; *arg; arg++)
|
||||
{
|
||||
if(cmdline.size() > 0)
|
||||
{
|
||||
cmdline += " ";
|
||||
}
|
||||
cmdline += *arg;
|
||||
}
|
||||
|
||||
support["version"] = FALCO_VERSION;
|
||||
support["system_info"]["sysname"] = sysinfo.sysname;
|
||||
@@ -965,13 +971,6 @@ int falco_init()
|
||||
hostname = c_hostname;
|
||||
}
|
||||
|
||||
outputs->init(config.m_json_output,
|
||||
config.m_json_include_output_property,
|
||||
config.m_notifications_rate, config.m_notifications_max_burst,
|
||||
config.m_buffered_outputs,
|
||||
config.m_time_format_iso_8601,
|
||||
hostname);
|
||||
|
||||
if(!all_events)
|
||||
{
|
||||
inspector->set_drop_event_flags(EF_DROP_SIMPLE_CONS);
|
||||
@@ -991,11 +990,6 @@ int falco_init()
|
||||
|
||||
inspector->set_hostname_and_port_resolution_mode(false);
|
||||
|
||||
for(auto output : config.m_outputs)
|
||||
{
|
||||
outputs->add_output(output);
|
||||
}
|
||||
|
||||
if(signal(SIGINT, signal_callback) == SIG_ERR)
|
||||
{
|
||||
fprintf(stderr, "An error occurred while setting SIGINT signal handler.\n");
|
||||
@@ -1081,6 +1075,21 @@ int falco_init()
|
||||
g_daemonized = true;
|
||||
}
|
||||
|
||||
outputs = new falco_outputs();
|
||||
|
||||
outputs->init(config.m_json_output,
|
||||
config.m_json_include_output_property,
|
||||
config.m_output_timeout,
|
||||
config.m_notifications_rate, config.m_notifications_max_burst,
|
||||
config.m_buffered_outputs,
|
||||
config.m_time_format_iso_8601,
|
||||
hostname);
|
||||
|
||||
for(auto output : config.m_outputs)
|
||||
{
|
||||
outputs->add_output(output);
|
||||
}
|
||||
|
||||
if(trace_filename.size())
|
||||
{
|
||||
// Try to open the trace file as a sysdig
|
||||
@@ -1373,23 +1382,13 @@ exit:
|
||||
//
|
||||
// MAIN
|
||||
//
|
||||
int main(int argc, const char **argv)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto cli = new falco::cli(argc, argv);
|
||||
cli->run();
|
||||
}
|
||||
catch(const cxxopts::OptionException &e)
|
||||
{
|
||||
display_fatal_err("Error parsing options: " + string(e.what()) + "\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int rc;
|
||||
|
||||
// g_restart will cause the falco loop to exit, but we
|
||||
// should reload everything and start over.
|
||||
while((rc = falco_init()) == EXIT_SUCCESS && g_restart)
|
||||
while((rc = falco_init(argc, argv)) == EXIT_SUCCESS && g_restart)
|
||||
{
|
||||
g_restart = false;
|
||||
optind = 1;
|
||||
|
||||
@@ -24,6 +24,7 @@ limitations under the License.
|
||||
|
||||
#include "formats.h"
|
||||
#include "logger.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
#include "outputs_file.h"
|
||||
#include "outputs_program.h"
|
||||
@@ -51,18 +52,26 @@ falco_outputs::~falco_outputs()
|
||||
{
|
||||
if(m_initialized)
|
||||
{
|
||||
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it)
|
||||
this->stop_worker();
|
||||
for(auto o : m_outputs)
|
||||
{
|
||||
(*it)->cleanup();
|
||||
delete o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void falco_outputs::init(bool json_output,
|
||||
bool json_include_output_property,
|
||||
uint32_t rate, uint32_t max_burst, bool buffered,
|
||||
bool time_format_iso_8601, string hostname)
|
||||
bool json_include_output_property,
|
||||
uint32_t timeout,
|
||||
uint32_t rate, uint32_t max_burst, bool buffered,
|
||||
bool time_format_iso_8601, std::string hostname)
|
||||
{
|
||||
// Cannot be initialized more than one time.
|
||||
if(m_initialized)
|
||||
{
|
||||
throw falco_exception("falco_outputs already initialized");
|
||||
}
|
||||
|
||||
m_json_output = json_output;
|
||||
|
||||
// Note that falco_formats is already initialized by the engine,
|
||||
@@ -71,17 +80,29 @@ void falco_outputs::init(bool json_output,
|
||||
falco_formats::s_json_output = json_output;
|
||||
falco_formats::s_json_include_output_property = json_include_output_property;
|
||||
|
||||
m_timeout = std::chrono::milliseconds(timeout);
|
||||
|
||||
m_notifications_tb.init(rate, max_burst);
|
||||
|
||||
m_buffered = buffered;
|
||||
m_time_format_iso_8601 = time_format_iso_8601;
|
||||
m_hostname = hostname;
|
||||
|
||||
m_worker_thread = std::thread(&falco_outputs::worker, this);
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
// This function has to be called after init() since some configuration settings
|
||||
// need to be passed to the output plugins. Then, although the worker has started,
|
||||
// the worker is still on hold, waiting for a message.
|
||||
// Thus it is still safe to call add_output() before any message has been enqueued.
|
||||
void falco_outputs::add_output(falco::outputs::config oc)
|
||||
{
|
||||
if(!m_initialized)
|
||||
{
|
||||
throw falco_exception("cannot add output: falco_outputs not initialized yet");
|
||||
}
|
||||
|
||||
falco::outputs::abstract_output *oo;
|
||||
|
||||
@@ -129,6 +150,12 @@ void falco_outputs::handle_event(gen_event *evt, string &rule, string &source,
|
||||
return;
|
||||
}
|
||||
|
||||
falco_outputs::ctrl_msg cmsg = {};
|
||||
cmsg.ts = evt->get_ts();
|
||||
cmsg.priority = priority;
|
||||
cmsg.source = source;
|
||||
cmsg.rule = rule;
|
||||
|
||||
string sformat;
|
||||
if(source == "syscall")
|
||||
{
|
||||
@@ -163,35 +190,38 @@ void falco_outputs::handle_event(gen_event *evt, string &rule, string &source,
|
||||
sformat += " " + format;
|
||||
}
|
||||
|
||||
string msg;
|
||||
msg = falco_formats::format_event(evt, rule, source, falco_common::priority_names[priority], sformat);
|
||||
cmsg.msg = falco_formats::format_event(evt, rule, source, falco_common::priority_names[priority], sformat);
|
||||
cmsg.fields = falco_formats::resolve_tokens(evt, source, sformat);
|
||||
|
||||
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it)
|
||||
{
|
||||
(*it)->output_event(evt, rule, source, priority, sformat, msg);
|
||||
}
|
||||
cmsg.type = ctrl_msg_type::CTRL_MSG_OUTPUT;
|
||||
m_queue.push(cmsg);
|
||||
}
|
||||
|
||||
void falco_outputs::handle_msg(uint64_t now,
|
||||
void falco_outputs::handle_msg(uint64_t ts,
|
||||
falco_common::priority_type priority,
|
||||
std::string &msg,
|
||||
std::string &rule,
|
||||
std::map<std::string, std::string> &output_fields)
|
||||
{
|
||||
std::string full_msg;
|
||||
falco_outputs::ctrl_msg cmsg = {};
|
||||
cmsg.ts = ts;
|
||||
cmsg.priority = priority;
|
||||
cmsg.source = "internal";
|
||||
cmsg.rule = rule;
|
||||
cmsg.fields = output_fields;
|
||||
|
||||
if(m_json_output)
|
||||
{
|
||||
nlohmann::json jmsg;
|
||||
|
||||
// Convert the time-as-nanoseconds to a more json-friendly ISO8601.
|
||||
time_t evttime = now / 1000000000;
|
||||
time_t evttime = ts / 1000000000;
|
||||
char time_sec[20]; // sizeof "YYYY-MM-DDTHH:MM:SS"
|
||||
char time_ns[12]; // sizeof ".sssssssssZ"
|
||||
string iso8601evttime;
|
||||
|
||||
strftime(time_sec, sizeof(time_sec), "%FT%T", gmtime(&evttime));
|
||||
snprintf(time_ns, sizeof(time_ns), ".%09luZ", now % 1000000000);
|
||||
snprintf(time_ns, sizeof(time_ns), ".%09luZ", ts % 1000000000);
|
||||
iso8601evttime = time_sec;
|
||||
iso8601evttime += time_ns;
|
||||
|
||||
@@ -201,15 +231,15 @@ void falco_outputs::handle_msg(uint64_t now,
|
||||
jmsg["time"] = iso8601evttime;
|
||||
jmsg["output_fields"] = output_fields;
|
||||
|
||||
full_msg = jmsg.dump();
|
||||
cmsg.msg = jmsg.dump();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string timestr;
|
||||
bool first = true;
|
||||
|
||||
sinsp_utils::ts_to_string(now, ×tr, false, true);
|
||||
full_msg = timestr + ": " + falco_common::priority_names[priority] + " " + msg + " (";
|
||||
sinsp_utils::ts_to_string(ts, ×tr, false, true);
|
||||
cmsg.msg = timestr + ": " + falco_common::priority_names[priority] + " " + msg + " (";
|
||||
for(auto &pair : output_fields)
|
||||
{
|
||||
if(first)
|
||||
@@ -218,23 +248,95 @@ void falco_outputs::handle_msg(uint64_t now,
|
||||
}
|
||||
else
|
||||
{
|
||||
full_msg += " ";
|
||||
cmsg.msg += " ";
|
||||
}
|
||||
full_msg += pair.first + "=" + pair.second;
|
||||
cmsg.msg += pair.first + "=" + pair.second;
|
||||
}
|
||||
full_msg += ")";
|
||||
cmsg.msg += ")";
|
||||
}
|
||||
|
||||
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it)
|
||||
{
|
||||
(*it)->output_msg(priority, full_msg);
|
||||
}
|
||||
cmsg.type = ctrl_msg_type::CTRL_MSG_OUTPUT;
|
||||
m_queue.push(cmsg);
|
||||
}
|
||||
|
||||
void falco_outputs::cleanup_outputs()
|
||||
{
|
||||
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_CLEANUP);
|
||||
}
|
||||
|
||||
void falco_outputs::reopen_outputs()
|
||||
{
|
||||
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it)
|
||||
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_REOPEN);
|
||||
}
|
||||
|
||||
void falco_outputs::stop_worker()
|
||||
{
|
||||
watchdog<void *> wd;
|
||||
wd.start([&](void *) -> void {
|
||||
falco_logger::log(LOG_NOTICE, "output channels still blocked, discarding all remaining notifications\n");
|
||||
m_queue.clear();
|
||||
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP);
|
||||
});
|
||||
wd.set_timeout(m_timeout, nullptr);
|
||||
|
||||
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP);
|
||||
if(m_worker_thread.joinable())
|
||||
{
|
||||
(*it)->reopen();
|
||||
m_worker_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
inline void falco_outputs::push(ctrl_msg_type cmt)
|
||||
{
|
||||
falco_outputs::ctrl_msg cmsg = {};
|
||||
cmsg.type = cmt;
|
||||
m_queue.push(cmsg);
|
||||
}
|
||||
|
||||
// todo(leogr,leodido): this function is not supposed to throw exceptions, and with "noexcept",
|
||||
// the program is terminated if that occurs. Although that's the wanted behavior,
|
||||
// we still need to improve the error reporting since some inner functions can throw exceptions.
|
||||
void falco_outputs::worker() noexcept
|
||||
{
|
||||
watchdog<std::string> wd;
|
||||
wd.start([&](std::string payload) -> void {
|
||||
falco_logger::log(LOG_CRIT, "\"" + payload + "\" output timeout, all output channels are blocked\n");
|
||||
});
|
||||
|
||||
auto timeout = m_timeout;
|
||||
|
||||
falco_outputs::ctrl_msg cmsg;
|
||||
do
|
||||
{
|
||||
// Block until a message becomes available.
|
||||
m_queue.pop(cmsg);
|
||||
|
||||
for(const auto o : m_outputs)
|
||||
{
|
||||
wd.set_timeout(timeout, o->get_name());
|
||||
try
|
||||
{
|
||||
switch(cmsg.type)
|
||||
{
|
||||
case ctrl_msg_type::CTRL_MSG_OUTPUT:
|
||||
o->output(&cmsg);
|
||||
break;
|
||||
case ctrl_msg_type::CTRL_MSG_CLEANUP:
|
||||
case ctrl_msg_type::CTRL_MSG_STOP:
|
||||
o->cleanup();
|
||||
break;
|
||||
case ctrl_msg_type::CTRL_MSG_REOPEN:
|
||||
o->reopen();
|
||||
break;
|
||||
default:
|
||||
falco_logger::log(LOG_DEBUG, "Outputs worker received an unknown message type\n");
|
||||
}
|
||||
}
|
||||
catch(const exception &e)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, o->get_name() + ": " + string(e.what()) + "\n");
|
||||
}
|
||||
}
|
||||
wd.cancel_timeout();
|
||||
} while(cmsg.type != ctrl_msg_type::CTRL_MSG_STOP);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ limitations under the License.
|
||||
#include "token_bucket.h"
|
||||
#include "falco_engine.h"
|
||||
#include "outputs.h"
|
||||
#include "tbb/concurrent_queue.h"
|
||||
|
||||
//
|
||||
// This class acts as the primary interface between a program and the
|
||||
@@ -39,25 +40,25 @@ public:
|
||||
|
||||
void init(bool json_output,
|
||||
bool json_include_output_property,
|
||||
uint32_t timeout,
|
||||
uint32_t rate, uint32_t max_burst, bool buffered,
|
||||
bool time_format_iso_8601, std::string hostname);
|
||||
|
||||
void add_output(falco::outputs::config oc);
|
||||
|
||||
//
|
||||
// evt is an event that has matched some rule. Pass the event
|
||||
// to all configured outputs.
|
||||
//
|
||||
// Format then send the event to all configured outputs (`evt` is an event that has matched some rule).
|
||||
void handle_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format);
|
||||
|
||||
// Send a generic message to all outputs. Not necessarily associated with any event.
|
||||
// Format then send a generic message to all outputs. Not necessarily associated with any event.
|
||||
void handle_msg(uint64_t now,
|
||||
falco_common::priority_type priority,
|
||||
std::string &msg,
|
||||
std::string &rule,
|
||||
std::map<std::string, std::string> &output_fields);
|
||||
|
||||
void cleanup_outputs();
|
||||
|
||||
void reopen_outputs();
|
||||
|
||||
private:
|
||||
@@ -71,5 +72,28 @@ private:
|
||||
bool m_buffered;
|
||||
bool m_json_output;
|
||||
bool m_time_format_iso_8601;
|
||||
std::chrono::milliseconds m_timeout;
|
||||
std::string m_hostname;
|
||||
|
||||
enum ctrl_msg_type
|
||||
{
|
||||
CTRL_MSG_STOP = 0,
|
||||
CTRL_MSG_OUTPUT = 1,
|
||||
CTRL_MSG_CLEANUP = 2,
|
||||
CTRL_MSG_REOPEN = 3,
|
||||
};
|
||||
|
||||
struct ctrl_msg : falco::outputs::message
|
||||
{
|
||||
ctrl_msg_type type;
|
||||
};
|
||||
|
||||
typedef tbb::concurrent_bounded_queue<ctrl_msg> falco_outputs_cbq;
|
||||
|
||||
falco_outputs_cbq m_queue;
|
||||
|
||||
std::thread m_worker_thread;
|
||||
inline void push(ctrl_msg_type cmt);
|
||||
void worker() noexcept;
|
||||
void stop_worker();
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
#include "config_falco.h"
|
||||
#include "falco_engine_version.h"
|
||||
#include "grpc_server_impl.h"
|
||||
#include "grpc_queue.h"
|
||||
#include "logger.h"
|
||||
@@ -75,6 +76,9 @@ void falco::grpc::server_impl::version(const context& ctx, const version::reques
|
||||
auto& version = *res.mutable_version();
|
||||
version = FALCO_VERSION;
|
||||
|
||||
res.set_engine_version(FALCO_ENGINE_VERSION);
|
||||
res.set_engine_fields_checksum(FALCO_FIELDS_CHECKSUM);
|
||||
|
||||
res.set_major(FALCO_VERSION_MAJOR);
|
||||
res.set_minor(FALCO_VERSION_MINOR);
|
||||
res.set_patch(FALCO_VERSION_PATCH);
|
||||
|
||||
@@ -37,6 +37,21 @@ struct config
|
||||
std::map<std::string, std::string> options;
|
||||
};
|
||||
|
||||
//
|
||||
// The message to be outputted. It can either refer to:
|
||||
// - an event that has matched some rule,
|
||||
// - or a generic message (e.g., a drop alert).
|
||||
//
|
||||
struct message
|
||||
{
|
||||
uint64_t ts;
|
||||
falco_common::priority_type priority;
|
||||
std::string msg;
|
||||
std::string rule;
|
||||
std::string source;
|
||||
map<std::string, std::string> fields;
|
||||
};
|
||||
|
||||
//
|
||||
// This class acts as the primary interface for implementing
|
||||
// a Falco output class.
|
||||
@@ -45,6 +60,8 @@ struct config
|
||||
class abstract_output
|
||||
{
|
||||
public:
|
||||
virtual ~abstract_output() {}
|
||||
|
||||
void init(config oc, bool buffered, std::string hostname)
|
||||
{
|
||||
m_oc = oc;
|
||||
@@ -52,15 +69,19 @@ public:
|
||||
m_hostname = hostname;
|
||||
}
|
||||
|
||||
// Output an event that has matched some rule.
|
||||
virtual void output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg) = 0;
|
||||
// Return the output's name as per its configuration.
|
||||
const std::string get_name() const
|
||||
{
|
||||
return m_oc.name;
|
||||
}
|
||||
|
||||
// Output a generic message. Not necessarily associated with any event.
|
||||
virtual void output_msg(falco_common::priority_type priority, std::string &msg) = 0;
|
||||
// Output a message.
|
||||
virtual void output(const message *msg) = 0;
|
||||
|
||||
// Possibly close the output and open it again.
|
||||
virtual void reopen() {}
|
||||
|
||||
// Possibly flush the output.
|
||||
virtual void cleanup() {}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -31,16 +31,10 @@ void falco::outputs::output_file::open_file()
|
||||
}
|
||||
}
|
||||
|
||||
void falco::outputs::output_file::output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg)
|
||||
{
|
||||
output_msg(priority, msg);
|
||||
}
|
||||
|
||||
void falco::outputs::output_file::output_msg(falco_common::priority_type priority, std::string &msg)
|
||||
void falco::outputs::output_file::output(const message *msg)
|
||||
{
|
||||
open_file();
|
||||
m_outfile << msg + "\n";
|
||||
m_outfile << msg->msg + "\n";
|
||||
|
||||
if(m_oc.options["keep_alive"] != "true")
|
||||
{
|
||||
|
||||
@@ -27,10 +27,7 @@ namespace outputs
|
||||
|
||||
class output_file : public abstract_output
|
||||
{
|
||||
void output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg);
|
||||
|
||||
void output_msg(falco_common::priority_type priority, std::string &msg);
|
||||
void output(const message *msg);
|
||||
|
||||
void cleanup();
|
||||
|
||||
|
||||
@@ -21,44 +21,41 @@ limitations under the License.
|
||||
#include "formats.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
void falco::outputs::output_grpc::output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format,
|
||||
std::string &msg)
|
||||
void falco::outputs::output_grpc::output(const message *msg)
|
||||
{
|
||||
falco::outputs::response grpc_res;
|
||||
|
||||
// time
|
||||
auto timestamp = grpc_res.mutable_time();
|
||||
*timestamp = google::protobuf::util::TimeUtil::NanosecondsToTimestamp(evt->get_ts());
|
||||
*timestamp = google::protobuf::util::TimeUtil::NanosecondsToTimestamp(msg->ts);
|
||||
|
||||
// rule
|
||||
auto r = grpc_res.mutable_rule();
|
||||
*r = rule;
|
||||
*r = msg->rule;
|
||||
|
||||
// source
|
||||
falco::schema::source s = falco::schema::source::SYSCALL;
|
||||
if(!falco::schema::source_Parse(source, &s))
|
||||
if(!falco::schema::source_Parse(msg->source, &s))
|
||||
{
|
||||
throw falco_exception("Unknown source passed to output_grpc::output_event()");
|
||||
throw falco_exception("Unknown source passed to output_grpc::output()");
|
||||
}
|
||||
grpc_res.set_source(s);
|
||||
|
||||
// priority
|
||||
falco::schema::priority p = falco::schema::priority::EMERGENCY;
|
||||
if(!falco::schema::priority_Parse(falco_common::priority_names[priority], &p))
|
||||
if(!falco::schema::priority_Parse(falco_common::priority_names[msg->priority], &p))
|
||||
{
|
||||
throw falco_exception("Unknown priority passed to output_grpc::output_event()");
|
||||
throw falco_exception("Unknown priority passed to output_grpc::output()");
|
||||
}
|
||||
grpc_res.set_priority(p);
|
||||
|
||||
// output
|
||||
auto output = grpc_res.mutable_output();
|
||||
*output = msg;
|
||||
*output = msg->msg;
|
||||
|
||||
// output fields
|
||||
auto &fields = *grpc_res.mutable_output_fields();
|
||||
auto resolvedTkns = falco_formats::resolve_tokens(evt, source, format);
|
||||
for(const auto &kv : resolvedTkns)
|
||||
for(const auto &kv : msg->fields)
|
||||
{
|
||||
fields[kv.first] = kv.second;
|
||||
}
|
||||
@@ -68,9 +65,4 @@ void falco::outputs::output_grpc::output_event(gen_event *evt, std::string &rule
|
||||
*host = m_hostname;
|
||||
|
||||
falco::grpc::queue::get().push(grpc_res);
|
||||
}
|
||||
|
||||
void falco::outputs::output_grpc::output_msg(falco_common::priority_type priority, std::string &msg)
|
||||
{
|
||||
// todo(fntlnz, leodido, leogr) > gRPC does not support subscribing to dropped events yet
|
||||
}
|
||||
@@ -25,10 +25,7 @@ namespace outputs
|
||||
|
||||
class output_grpc : public abstract_output
|
||||
{
|
||||
void output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg);
|
||||
|
||||
void output_msg(falco_common::priority_type priority, std::string &msg);
|
||||
void output(const message *msg);
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
|
||||
@@ -18,13 +18,7 @@ limitations under the License.
|
||||
#include "logger.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
void falco::outputs::output_http::output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg)
|
||||
{
|
||||
output_msg(priority, msg);
|
||||
}
|
||||
|
||||
void falco::outputs::output_http::output_msg(falco_common::priority_type priority, std::string &msg)
|
||||
void falco::outputs::output_http::output(const message *msg)
|
||||
{
|
||||
CURL *curl = NULL;
|
||||
CURLcode res = CURLE_FAILED_INIT;
|
||||
@@ -37,7 +31,7 @@ void falco::outputs::output_http::output_msg(falco_common::priority_type priorit
|
||||
slist1 = curl_slist_append(slist1, "Content-Type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, m_oc.options["url"].c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg->msg.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1L);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
@@ -25,10 +25,7 @@ namespace outputs
|
||||
|
||||
class output_http : public abstract_output
|
||||
{
|
||||
void output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg);
|
||||
|
||||
void output_msg(falco_common::priority_type priority, std::string &msg);
|
||||
void output(const message *msg);
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
|
||||
@@ -31,17 +31,11 @@ void falco::outputs::output_program::open_pfile()
|
||||
}
|
||||
}
|
||||
|
||||
void falco::outputs::output_program::output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg)
|
||||
{
|
||||
output_msg(priority, msg);
|
||||
}
|
||||
|
||||
void falco::outputs::output_program::output_msg(falco_common::priority_type priority, std::string &msg)
|
||||
void falco::outputs::output_program::output(const message *msg)
|
||||
{
|
||||
open_pfile();
|
||||
|
||||
fprintf(m_pfile, "%s\n", msg.c_str());
|
||||
fprintf(m_pfile, "%s\n", msg->msg.c_str());
|
||||
|
||||
if(m_oc.options["keep_alive"] != "true")
|
||||
{
|
||||
|
||||
@@ -25,10 +25,7 @@ namespace outputs
|
||||
|
||||
class output_program : public abstract_output
|
||||
{
|
||||
void output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg);
|
||||
|
||||
void output_msg(falco_common::priority_type priority, std::string &msg);
|
||||
void output(const message *msg);
|
||||
|
||||
void cleanup();
|
||||
|
||||
|
||||
@@ -18,16 +18,10 @@ limitations under the License.
|
||||
#include <iostream>
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
void falco::outputs::output_stdout::output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg)
|
||||
{
|
||||
output_msg(priority, msg);
|
||||
}
|
||||
|
||||
void falco::outputs::output_stdout::output_msg(falco_common::priority_type priority, std::string &msg)
|
||||
void falco::outputs::output_stdout::output(const message *msg)
|
||||
{
|
||||
//
|
||||
// By default, the stdout stream is fully buffered or line buffered
|
||||
// By default, the stdout stream is fully buffered or line buffered
|
||||
// (if the stream can be determined to refer to an interactive device, e.g. in a TTY).
|
||||
// Just enable automatic flushing when unbuffered output is desired.
|
||||
// Note that it is set every time since other writings to the stdout can disable it.
|
||||
@@ -36,7 +30,7 @@ void falco::outputs::output_stdout::output_msg(falco_common::priority_type prior
|
||||
{
|
||||
std::cout << std::unitbuf;
|
||||
}
|
||||
std::cout << msg + "\n";
|
||||
std::cout << msg->msg + "\n";
|
||||
}
|
||||
|
||||
void falco::outputs::output_stdout::cleanup()
|
||||
|
||||
@@ -25,10 +25,7 @@ namespace outputs
|
||||
|
||||
class output_stdout : public abstract_output
|
||||
{
|
||||
void output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg);
|
||||
|
||||
void output_msg(falco_common::priority_type priority, std::string &msg);
|
||||
void output(const message *msg);
|
||||
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
@@ -18,14 +18,8 @@ limitations under the License.
|
||||
#include <syslog.h>
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
void falco::outputs::output_syslog::output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg)
|
||||
{
|
||||
output_msg(priority, msg);
|
||||
}
|
||||
|
||||
void falco::outputs::output_syslog::output_msg(falco_common::priority_type priority, std::string &msg)
|
||||
void falco::outputs::output_syslog::output(const message *msg)
|
||||
{
|
||||
// Syslog output should not have any trailing newline
|
||||
::syslog(priority, "%s", msg.c_str());
|
||||
::syslog(msg->priority, "%s", msg->msg.c_str());
|
||||
}
|
||||
|
||||
@@ -25,10 +25,7 @@ namespace outputs
|
||||
|
||||
class output_syslog : public abstract_output
|
||||
{
|
||||
void output_event(gen_event *evt, std::string &rule, std::string &source,
|
||||
falco_common::priority_type priority, std::string &format, std::string &msg);
|
||||
|
||||
void output_msg(falco_common::priority_type priority, std::string &msg);
|
||||
void output(const message *msg);
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
|
||||
@@ -57,4 +57,7 @@ enum source {
|
||||
k8s_audit = 1;
|
||||
K8s_audit = 1;
|
||||
K8S_audit = 1;
|
||||
INTERNAL = 2;
|
||||
internal = 2;
|
||||
Internal = 2;
|
||||
}
|
||||
|
||||
@@ -36,10 +36,14 @@ message request
|
||||
// its parts as per semver 2.0 specification (https://semver.org).
|
||||
message response
|
||||
{
|
||||
// falco version
|
||||
string version = 1;
|
||||
uint32 major = 2;
|
||||
uint32 minor = 3;
|
||||
uint32 patch = 4;
|
||||
string prerelease = 5;
|
||||
string build = 6;
|
||||
}
|
||||
// falco engine version
|
||||
uint32 engine_version = 7;
|
||||
string engine_fields_checksum = 8;
|
||||
}
|
||||
|
||||
96
userspace/falco/watchdog.h
Normal file
96
userspace/falco/watchdog.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
Copyright (C) 2020 The Falco Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
|
||||
template<typename _T>
|
||||
class watchdog
|
||||
{
|
||||
public:
|
||||
watchdog():
|
||||
m_timeout(nullptr),
|
||||
m_is_running(false)
|
||||
{
|
||||
}
|
||||
|
||||
~watchdog()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void start(std::function<void(_T)> cb,
|
||||
std::chrono::milliseconds resolution = std::chrono::milliseconds(100))
|
||||
{
|
||||
stop();
|
||||
m_is_running.store(true, std::memory_order_release);
|
||||
m_thread = std::thread([this, cb, resolution]() {
|
||||
const auto no_deadline = time_point{};
|
||||
timeout_data curr;
|
||||
while(m_is_running.load(std::memory_order_acquire))
|
||||
{
|
||||
auto t = m_timeout.exchange(nullptr, std::memory_order_release);
|
||||
if(t)
|
||||
{
|
||||
curr = *t;
|
||||
delete t;
|
||||
}
|
||||
if(curr.deadline != no_deadline && curr.deadline < std::chrono::steady_clock::now())
|
||||
{
|
||||
cb(curr.payload);
|
||||
curr.deadline = no_deadline;
|
||||
}
|
||||
std::this_thread::sleep_for(resolution);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if(m_is_running.load(std::memory_order_acquire))
|
||||
{
|
||||
m_is_running.store(false, std::memory_order_release);
|
||||
if(m_thread.joinable())
|
||||
{
|
||||
m_thread.join();
|
||||
}
|
||||
delete m_timeout.exchange(nullptr, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
inline void set_timeout(std::chrono::milliseconds timeout, _T payload) noexcept
|
||||
{
|
||||
delete m_timeout.exchange(new timeout_data{std::chrono::steady_clock::now() + timeout, payload}, std::memory_order_release);
|
||||
}
|
||||
|
||||
inline void cancel_timeout() noexcept
|
||||
{
|
||||
delete m_timeout.exchange(new timeout_data, std::memory_order_release);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::chrono::time_point<std::chrono::steady_clock> time_point;
|
||||
struct timeout_data
|
||||
{
|
||||
time_point deadline;
|
||||
_T payload;
|
||||
};
|
||||
std::atomic<timeout_data *> m_timeout;
|
||||
std::atomic<bool> m_is_running;
|
||||
std::thread m_thread;
|
||||
};
|
||||
Reference in New Issue
Block a user